Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
48d7b08
feat(review): add review:explain-change with a digest policy and an i…
kyle-sexton Oct 3, 2026
27d36b8
fix(review): close explain-change's output-path and self-config holes
kyle-sexton Oct 3, 2026
48d3c19
fix(review): treat case-variant overlays as tracked in explain-change
kyle-sexton Oct 3, 2026
24eedc6
Merge origin/main into feat/1217-review-explain-change
kyle-sexton Oct 3, 2026
cfaaaba
fix(review): add stub evals and avoid a typos hit in the explain-chan…
kyle-sexton Oct 3, 2026
0047e1c
Merge origin/main into feat/1217-review-explain-change
kyle-sexton Oct 3, 2026
b10026d
Merge origin/main into feat/1217-review-explain-change
kyle-sexton Oct 3, 2026
91f51b9
fix(review): read explain-change team config at the base commit and g…
kyle-sexton Oct 3, 2026
5a2ae65
merge main
cursoragent Oct 3, 2026
509abc8
fix(review): refuse an explain-change overlay inside a .claude submodule
kyle-sexton Oct 3, 2026
65a9855
Merge branch 'feat/1217-review-explain-change' of ssh://github.com/me…
kyle-sexton Oct 3, 2026
c221d9e
Merge origin/main into feat/1217-review-explain-change
kyle-sexton Oct 3, 2026
0dc4e08
Merge origin/main into feat/1217-review-explain-change
kyle-sexton Oct 3, 2026
62337da
docs(review): name the submodule guard in the digest overlay paragraph
kyle-sexton Oct 3, 2026
be6fc89
Merge origin/main into feat/1217-review-explain-change
kyle-sexton Oct 3, 2026
feb2d03
Merge remote-tracking branch 'origin/main' into feat/1217-review-expl…
kyle-sexton Oct 3, 2026
7003422
Merge remote-tracking branch 'origin/main' into feat/1217-review-expl…
kyle-sexton Oct 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ plugin manifests and kept in sync by CI. Never hand-edit it; the category vocabu
## Quality

- [`mcp-tools`](../plugins/mcp-tools): Two MCP audits. audit scores the tool definitions of a server you build against MCP-specification, Anthropic tool-design, and Claude-Code client criteria in a per-tool PASS/WARN/FAIL scorecard (Python, TypeScript, .NET). audit-posture inventories the MCP servers your Claude Code configuration runs and flags supply-chain risks such as floating package versions, without running any server.
- [`review`](../plugins/review): Code-review toolkit: six reviewer agents, read-only over the reviewed code (code, security, architecture, doc drift, build/test/lint, CI-log audit), plus orchestration skills for the quality gate, fan-out, and enforceability audit (/review:audit-enforceability), an offered HTML pull-request explainer (/review:pr-explainer), a fan-out sweep workflow (/review:fanout-sweep), and CI lane commands (/review:code-review, /review:security-review) for org reusable workflows.
- [`review`](../plugins/review): Code-review toolkit: six reviewer agents, read-only over the reviewed code (code, security, architecture, doc drift, build/test/lint, CI-log audit), plus orchestration skills for the quality gate, fan-out, and enforceability audit (/review:audit-enforceability), a pull-request change digest with an interactive view (/review:explain-change), a fan-out sweep workflow (/review:fanout-sweep), and CI lane commands (/review:code-review, /review:security-review) for org reusable workflows.
- [`codebase-health`](../plugins/codebase-health): Repo-wide drift audit between docs, config, code, and architecture: verifies every factual claim against reality via parallel subagent fan-out, severity-rates findings, and reports read-only, delegating remediation to the implementation/verification lanes. Audit dimensions are configurable through a tracked .claude/codebase-health.md config file written by the setup skill.
- [`code-metrics`](../plugins/code-metrics): Read-only code measures for a change, with cited references and no verdict: lines per file (audit-size), cyclomatic, cognitive, and Halstead complexity (audit-complexity), duplication (audit-duplication), per-function coverage and CRAP from existing lcov, Cobertura, coverage.py, or Go artifacts (audit-coverage), TypeScript and Python type debt (audit-type-debt), metric literacy (principles), and setup. Uses only collectors already installed; never installs or runs tests.
- [`discipline`](../plugins/discipline): Correctors that re-anchor a rule, audit work in flight, and fix drift: do-your-research (and -deep), follow-our-standards, point-dont-copy, reason-dont-recite, tighten-your-output, recheck-against-upstream (and -deep), pick-for-the-problem, mind-your-maxims, script-the-deterministic-work, use-your-skills, reuse-or-replace, scrutinize-dont-coast. sweep-all runs them as one batch. wait-what re-pitches a message that did not land. hold-my-hand shows work one phase at a time.
Expand Down
3 changes: 3 additions & 0 deletions docs/conventions/config-cascade/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,7 @@ conformance.
| `instruction-placement` | team on conflict (policy-floor) | per-key |
| `overengineering` | team on conflict for protected keys | per-key |
| `multi-agent` | later layer; the docs block over `.claude/multi-agent.yaml` in the team layer | per-key |
| `review-digest` | later layer; the docs block over `.claude/review-digest.json` in the team layer; `--policy` over every layer | per-key (lists replace) |

<!-- END GENERATED: config-cascade semantics -->

Expand Down Expand Up @@ -522,6 +523,7 @@ its conformance cell.
| `instruction-placement` | `.claude/instruction-placement.md` | all three | team on conflict (policy-floor) | per-key | conforms; per-key override (suppression entries merge per `finding_id`), plus policy-floor inversion: the team layer wins a direct conflict and a personal-only entry is reported `personal-only, not applied`, since a decline removes a placement proposal from every future report and a personal layer hiding one the team never accepted is the weakening this class prevents. `suppressions` is the surface's only key today; the plugin's `userConfig` dials stay personal and are never keys here. Written (team layer only) by `/instruction-placement:realign` behind its per-item gate, read by `/instruction-placement:audit` and `/instruction-placement:delta`. Keys owned by the plugin's `reference/consumer-config.md`; suppression-entry keys by [`finding-suppression`](../finding-suppression/README.md) |
| `overengineering` | `.claude/overengineering.md` | all three | team on conflict for protected keys | per-key | conforms; per-key override, plus policy-floor inversion on two key groups: the protected-categories set and the suppression entries (which merge per `finding_id`). On both, the team layer wins a direct conflict, personal layers may extend or tighten only, and a personal contribution is named in the report: a gitignored overlay emptying the protected set would defeat the plugin's FLAG-FOR-HUMAN cap on security-class artifacts, and a personal-only suppression is the same weakening `audit-pass` prevents above. Narrowing or emptying the protected set stays available on the tracked layer, spelled one category at a time so the diff names each protection dropped. The threshold and observation-window keys take ordinary refinement. Keys owned by the plugin's `reference/consumer-config.md`; suppression-entry keys by [`finding-suppression`](../finding-suppression/README.md) |
| `multi-agent` | team: the `yaml config` block in `docs/conventions/multi-agent.md`, else `.claude/multi-agent.yaml`; user-global `~/.claude/multi-agent.yaml`; overlay `.claude/multi-agent.local.yaml` | all three | later layer; the docs block over `.claude/multi-agent.yaml` in the team layer | per-key | conforms to the [location axis](#location-of-the-team-layer-a-docs-convention-file-or-claudename) ([ADR 0044](../../adr/0044-default-structured-team-config-to-a-docs-convention-file-with-a-claude-fallback.md)): the docs block wins, `.claude/multi-agent.yaml` is read only when the docs file holds no block, a note names both paths when both exist, and two blocks in one file make the team layer invalid. Rule 5 holds: a layer that does not parse or names another schema is skipped and named, and an unknown key or a value outside its allowed set is reported and inert. No policy-floor class: every key is a routing dial, and turning the fan-out guard off is a documented opt-in on whichever layer sets it. Keys owned by [`plugins/multi-agent/reference/config.md`](../../../plugins/multi-agent/reference/config.md); resolved by `plugins/multi-agent/scripts/resolve-roles.sh`, read by `/multi-agent:route`, `/multi-agent:audit-defaults` and `/multi-agent:setup check`; written by `/multi-agent:setup apply` (any of the three layers, after a preview and an explicit yes) |
| `review-digest` | team: the `json config` block in `docs/conventions/review-digest.md`, else `.claude/review-digest.json`; user-global `~/.claude/review-digest.json`; overlay `.claude/review-digest.local.json` | all three | later layer; the docs block over `.claude/review-digest.json` in the team layer; `--policy` over every layer | per-key (lists replace) | conforms to the [location axis](#location-of-the-team-layer-a-docs-convention-file-or-claudename) ([ADR 0044](../../adr/0044-default-structured-team-config-to-a-docs-convention-file-with-a-claude-fallback.md)): the docs block wins with a warning naming both paths, and two blocks make the team layer invalid. Rule 5 holds: a layer that does not parse is named and skipped, an invalid value is reported and ignored, and an unknown key is inert. Declared deviation from the per-layer verdicts: an untracked team layer is reported and resolved as absent rather than stopping, and an overlay that is not gitignored is reported and still applied, since every key only decides when a reader is offered a view. No policy-floor class. Keys owned by [`review-digest`](../review-digest.md); resolved by `plugins/review/skills/explain-change/scripts/digest-policy.mjs`, which also reads the `rendered-views` `medium` key |
Comment thread
kyle-sexton marked this conversation as resolved.

### Root rule by surface

Expand All @@ -541,6 +543,7 @@ model-run skill with no reader script, so the rule lives in the skill text.
| `docs-naming` | implements | `scripts/resolve-config.sh` sources its `lib/config-root.sh` copy and classifies `--root` (else the git toplevel of the current directory) against `--home`; `paths` reports team and overlay as not-applicable at a `home` or `non-repo` root, and a team or overlay path that is the user-global file is read once |
| `ai-slop` | implements | `skills/audit/scripts/detect.sh` sources its `lib/config-root.sh` copy and classifies its root (`CLAUDE_PROJECT_DIR`, else `git rev-parse --show-toplevel`, else `pwd`) before the team and overlay reads; a team or overlay file that is the user-global file is read once |
| `multi-agent` | implements | `scripts/resolve-roles.sh` sources its `lib/config-root.sh` copy and classifies `--root` (else `CLAUDE_PROJECT_DIR`, else the git toplevel) against `--home`; team and overlay are reported not-applicable at a `home` or `non-repo` root, and a team or overlay path that is the user-global file is read once; `/multi-agent:setup apply --layer team` and `--layer local` refuse at a `home` or `non-repo` root |
| `review-digest` | implements | `skills/explain-change/scripts/digest-policy.mjs` classifies its root inline (`CLAUDE_PROJECT_DIR`, else the nearest directory holding `.git`) and skips team and overlay when the root is the home directory, above it, or not a working tree, and any team or overlay path that is the user-global file; it does not source the resolver |
| `attribution` | implements | `skills/audit/scripts/lib.sh` sources its `lib/config-root.sh` copy; `cfg_layers_init` skips team and overlay unless `config_root_classify` returns `repo`, and skips a layer that `config_root_paths_same` matches to the user-global file |
| `code-metrics` | not yet | `scripts/resolve-config.py`: `git rev-parse --show-toplevel`, else the current directory; `CLAUDE_PROJECT_DIR` is not consulted |
| `disk-hygiene` | not yet | the clean engine takes the team file from `--project-dir`; no root classification |
Expand Down
7 changes: 7 additions & 0 deletions docs/conventions/rendered-views/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
Notable changes to the rendered-views contract. The contract is not
versioned; this log records each change to it.

## The digest lane is `review:explain-change`, 2026-10-03

- **`review:pr-explainer` is renamed `review:explain-change` (#1217).** The digest lane
builds an interactive page through the shared builder from a checked-in template, ships
`medium: file`, and keeps the planned `artifact` default behind its own review. The
escape-helper bullet and the thin-skill example name the new lane.

## The first interactive emitter, 2026-10-03

- **`education:illustrate` replaces `education:eli5` on the escape-helper emitter list
Expand Down
9 changes: 5 additions & 4 deletions docs/conventions/rendered-views/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ Two sentences reconcile this with the local-first residence decision:
priced fleet sweep deliberately migrates them (tracked as a deferred-work issue).

One new lane is an exception to sentence 1, recorded here: the pull-request digest
lane (`review:pr-explainer` today, `review:explain-change` once #5835 C1 lands) takes
lane (`review:explain-change`) ships `medium: file` and takes
`medium: artifact` as its default only after that lane's own external-publication
review signs off. An operator who wants the digest local sets `medium: file`
in their personal layer (`~/.claude/rendered-views.md` or the repo overlay); the
Expand Down Expand Up @@ -469,8 +469,9 @@ the checked-in helper in the third bullet instead of this skeleton alone.
string through `lib/html-escape.mjs` (the same path inside each adopting plugin,
generated and drift-gated by `scripts/sync-shared-copies.sh`). The page carries the generator marker
`validateRenderedPage` checks, so a page assembled without the helper is detectable.
`/review:pr-explainer` and `/education:quiz-me` are on that gate. Such a lane is K2 (see
Content classes); the shared builder carries the same helper and adds the interactive profile.
`/education:quiz-me` is on that gate, and `/review:explain-change` builds through the
shared builder. Such a lane is K2 (see Content classes); the shared builder carries the
same helper and adds the interactive profile.
- Escaping reaches text and quoted-attribute positions and nothing else. A value that
lands in URL position (`href`, `src`, `action`, `formaction`, SVG `xlink:href`) is
checked against a scheme allowlist BEFORE it is escaped: `javascript:` and `data:`
Expand Down Expand Up @@ -622,7 +623,7 @@ which is another cost of copying.
- It never makes a view the record: the markdown record stays authoritative everywhere.
- It adds no generic HTML skill, one whose job is "make a page" for any content. Thin
intent-named skills are allowed: a skill named for what the reader is trying to do
(`review:pr-explainer` explains a pull request) may emit a view as its deliverable,
(`review:explain-change` explains a pull request) may emit a view as its deliverable,
owning its genre's page shape and reusing the shared builder and chrome.
`visualization:visualize` stays a router that owns no craft.
- It does not migrate the grandfathered surfaces' ladder or `medium`: that sweep is
Expand Down
78 changes: 78 additions & 0 deletions docs/conventions/review-digest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Review Digest Convention

When `/review:explain-change` builds or offers a change digest for a pull request. This file is
the owner doc for the `review-digest` cascade concern and this repository's team layer for it: the
config block below is what the skill reads here.

## The policy

`digest_policy` takes one of three values:

- `off`: the digest is never offered or built unasked.
- `offer` (the default): the skill offers the digest when any trigger below fires, and stays
quiet when none does.
- `always`: the skill builds the digest when the pull request is marked ready. At any other
point it behaves as `offer`.

A reader who invokes the skill directly has asked for the digest. That request is the explicit
argument tier, so the skill builds it whatever the policy says.

Whatever the policy, the digest never posts to the pull request, never comments on it, and never
sets a check status. It does not gate merge.

## The triggers

Under `offer`, any one of these fires the offer:

| Trigger | Fires when | Key |
|---|---|---|
| files | the pull request changes more than `max_files` files | `max_files` |
| changed-lines | additions plus deletions exceed `max_changed_lines` | `max_changed_lines` |
| blast-radius | the assessed blast radius is one of `blast_radius` | `blast_radius` |
| risk-path | a changed path matches one of the `risk_paths` globs | `risk_paths` |
| label | the pull request carries the `opt_in_label` label | `opt_in_label` |

`risk_paths` globs use `**` for any number of directories and `*` or `?` within one path
segment. A change to any file this convention or the rendered-views `medium` key reads fires
risk-path whatever `risk_paths` holds. An empty `opt_in_label` turns the label trigger off. The blast radius comes from the
plan or a `/review:quality-gate downstream` pass, as LOW, MEDIUM, HIGH, or CRITICAL.

## The keys and their layers

The surface is JSON. Layers resolve per the
[config-cascade convention](config-cascade/README.md), per-key override, a later layer replacing
an earlier one key by key:

1. user-global `~/.claude/review-digest.json`
2. team: the `json config` block in `docs/conventions/review-digest.md`, else
`.claude/review-digest.json`
3. overlay `.claude/review-digest.local.json`

The team layer, and the rendered-views team file, are read from the pull request's base commit
(`baseRefOid`), never the working tree, so a checked-out pull request cannot configure its own
digest. When that commit is not in the clone, the team layer is skipped with a warning. The
overlay applies only when untracked, in any letter case, and is refused when `.claude` or the
overlay is a symlink, or when `.claude` is itself a tracked entry (a submodule or a tracked file) or
holds a `.git`; one that is not gitignored is reported and still applied.

An explicit `--policy` argument beats every layer. An unknown key is inert, and an invalid value
is reported and ignored. Lists replace whole. No key is policy-floor: each one only decides when a
reader is offered a view.

Where a built page goes is the `medium` key of the
[rendered-views concern](rendered-views/README.md#the-rendered-views-cascade-concern), not a key
here. The skill's shipped default is `file`.

The block below holds the shipped defaults, so this repository runs on them. A test holds it equal
to the skill's own defaults.

```json config
{
"digest_policy": "offer",
"max_files": 5,
"max_changed_lines": 200,
"blast_radius": ["HIGH", "CRITICAL"],
"risk_paths": [".github/workflows/**", "**/hooks/**", "**/migrations/**"],
"opt_in_label": "explain-change"
}
```
3 changes: 2 additions & 1 deletion docs/skill-cheat-sheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,9 @@ owned by [docs/catalog-taxonomy.md](catalog-taxonomy.md).
| [`/plugin-quality:audit`](../plugins/plugin-quality/skills/audit/SKILL.md) | `plugin-quality` | Behavioral audit of a plugin component ending in a maintainer work item |
| [`/review:audit-enforceability`](../plugins/review/skills/audit-enforceability/SKILL.md) | `review` | Propose the cheapest deterministic rung for each review finding |
| [`/review:code-review`](../plugins/review/skills/code-review/SKILL.md) | `review` | Org CI code-review lane command for a GitHub pull request |
| [`/review:explain-change`](../plugins/review/skills/explain-change/SKILL.md) | `review` | Change digest for a pull request, markdown record plus an interactive view |
| [`/review:fanout`](../plugins/review/skills/fanout/SKILL.md) | `review` | Fan review out across every reviewer surface into one ranked report |
| [`/review:pr-explainer`](../plugins/review/skills/pr-explainer/SKILL.md) | `review` | Offered HTML explainer for a pull request, markdown record kept |
| [`/review:pr-explainer`](../plugins/review/skills/pr-explainer/SKILL.md) | `review` | Renamed to /review:explain-change; one-release stub |
| [`/review:quality-gate`](../plugins/review/skills/quality-gate/SKILL.md) | `review` | Single-lens review checkpoint routed to the matching reviewer |
| [`/review:security-review`](../plugins/review/skills/security-review/SKILL.md) | `review` | Org CI security-review lane command for a GitHub pull request |
| [`/skill-quality:check`](../plugins/skill-quality/skills/check/SKILL.md) | `skill-quality` | Static QA gate for skill frontmatter, caps, and evals |
Expand Down
Loading
Loading