[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71FYW40ZSPDDC69WN5ZFWK5":3,"skill-same:01M71FYW40ZSPDDC69WN5ZFWK5":101},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":51,"commitSha":52,"skillDirectory":53,"forksCount":14,"repositoryHtmlUrl":54,"repositoryHomepageUrl":6,"ownerHtmlUrl":55,"ownerBlogUrl":56,"ownerDescription":57,"updatedAt":58,"install":59,"resources":72},"01M71FYW40ZSPDDC69WN5ZFWK5","review-changes",null,"ALWAYS invoke this skill when reviewing working changes on a branch against a base ref. NEVER review changes by hand-formatting JSON or by reading persisted review artifacts directly.","outcomeeng\u002Fplugins","outcomeeng","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F257335685?v=4","01M71FRVRF36XYR3HRSCGFHK2R","Outcome Engineering","01M71FRVS4BZG17DWANFJ7EPNY",1,"MIT","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fproduct-tree\u002Fskills\u002Freview-changes\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"review changes","review","changes","formatting","artifacts","Bash","when","invoke","base","grep","reading","write","read","scripts","json","run","verify","never","python3","branch","2026-11-10 10:12:30",false,"---\nname: review-changes\ndescription: ALWAYS invoke this skill when reviewing working changes on a branch against a base ref. NEVER review changes by hand-formatting JSON or by reading persisted review artifacts directly.\nallowed-tools:\n  - Bash(python3 \"${CLAUDE_SKILL_DIR}\u002Fscripts\u002Freview_run.py\":*)\n  - Bash(SPX_VERIFY_*=* python3 \"${CLAUDE_SKILL_DIR}\u002Fscripts\u002Freview_run.py\":*)\n  - Read\n  - Grep\n  - Glob\n  - Write\n---\n\n\u003Cobjective>\nThe raw run token that `finish` prints for a sealed `spx journal --type review` run.\n\u003C\u002Fobjective>\n\n\u003Cinputs>\n\nReview scope is discovered from the current worktree. Reusable gate evidence requires the exact current version to be committed and the worktree to be clean. Explicit advisory feedback may include staged, unstaged, or untracked work, and the resulting review supplies no reusable gate evidence. The input may supply the resolved scope as literal `SPX_VERIFY_BASE_REF`, `SPX_VERIFY_HEAD_REF`, `SPX_VERIFY_BRANCH`, `SPX_VERIFY_TARGET_KIND`, and `SPX_VERIFY_PULL_REQUEST_NUMBER` assignments, each written as a prefix on the single `review_run.py start` command line, because a shell export does not persist across Bash calls. `SPX_VERIFY_BASE_REF` and `SPX_VERIFY_HEAD_REF` select a non-default range; the other three record branch and target identity.\n\n\u003C\u002Finputs>\n\n\u003Capi_surface>\n\nInvoke only the bundled runner:\n\n```bash\n[SPX_VERIFY_*=\u003Cvalue> ...] python3 \"${CLAUDE_SKILL_DIR}\u002Fscripts\u002Freview_run.py\" start\npython3 \"${CLAUDE_SKILL_DIR}\u002Fscripts\u002Freview_run.py\" append-scope --state \"\u003CstatePath>\" \"\u003Cchanged-file>\"\npython3 \"${CLAUDE_SKILL_DIR}\u002Fscripts\u002Freview_run.py\" append-finding --state \"\u003CstatePath>\" \u003C \"\u003Cfinding-file>\"\npython3 \"${CLAUDE_SKILL_DIR}\u002Fscripts\u002Freview_run.py\" finish --state \"\u003CstatePath>\" --unread \"\u003Cunread-path>\"\npython3 \"${CLAUDE_SKILL_DIR}\u002Fscripts\u002Freview_run.py\" finish --state \"\u003CstatePath>\" --unread-file \"\u003Cunread-list-file>\"\n```\n\nThe bracketed `SPX_VERIFY_*=\u003Cvalue>` assignments are optional and appear only when the input supplies them, each as a literal prefix on the `start` command line; omit the brackets. Pass `--unread \"\u003Cunread-path>\"` once for each changed file Claude did not read, and omit it when Claude read every changed file. When the unread set is large, write one changed path per line to a payload file with the file-write tool, in the scratch directory `\u003Cfinding_payload>` names, under a file name unique to this agent and run such as `unread-\u003CrunToken>.txt`, and pass that file once as `--unread-file \"\u003Cunread-list-file>\"` with the path exactly as the file-write result reports it; `--unread-file` marks each listed path unread exactly as one `--unread` argument does, and the two forms combine.\n\n`start` fetches a git-derived or `origin\u002F` base and refuses a head behind the fetched `origin\u002F\u003Cbase>` tip before any journal exists — the dedicated stale-base exit code with a `stale-base` JSON diagnostic on stderr and no run state; a supplied local ref or commit is compared as given — and otherwise computes the diff bundle, opens the review journal, appends the scope-entered event, and returns JSON containing `statePath`, `runToken`, `diffPath`, `manifestPath`, `changedFiles`, `generatedExtents`, `generatedDeclarationChanged`, and `renames`. Report the refusal as a `stale-base` block carrying the diagnostic verbatim and stop.\n\n`changedFiles` lists a renamed file once under its new path. `renames` lists each rename with its `path`, its `former_path`, and `content_unchanged`, true when git detected a rename of identical content. `generatedExtents` is read from `spx\u002Flocal\u002Fgenerated-sources.toml` as committed at the scope's base ref (`git show \u003Cbase-ref>:spx\u002Flocal\u002Fgenerated-sources.toml`), never from the working tree, and is empty when the base lacks the file; it lists, per relation, the changed `files` that the relation's whole-file output pattern matches together with the relation's `sources`, `generator`, and `regenerate` command. A file bounded only by a relation's `begin` and `end` markers is an ordinary changed file. A relation whose declared sources or generator the changeset also changes carries `flagged` true and names them in `changed_sources` and `changed_generator`. When `spx\u002Flocal\u002Fgenerated-sources.toml` is among the changed paths, `generatedDeclarationChanged` is true and every listed relation carries `flagged` true with `flagged_reason` naming that declaration: the changeset changes the declaration that would exempt its files, so nothing is exempt by declaration, every changed file is ordinary, and the run-completed event records `generatedDeclarationChanged` with that reason and lists no `skippedGenerated` file.\n\n`append-scope` appends one scope-advanced event for a changed file only after Claude has read that file; a file Claude has not read gets no scope-advanced event.\n\n`append-finding` reads one finding JSON object from its standard input, which the input redirect from `\u003Cfinding-file>` supplies, wraps it in the journal event envelope, and appends it. The runner does not perform the full review finding schema or citation validation; `spx journal append` is the authoritative event boundary.\n\n`finish` refuses, with a non-zero exit, while a changed file is neither advanced nor marked unread, except a declared generated extent and a pure rename, which `finish` accounts for itself and which need no `append-scope` and no unread mark. Otherwise it reads the journal prefix, appends the terminal run-completed event with review status, finding counts, each unread file, each declared generated extent skipped by declaration with its relation's regenerate command, and each rename without content change, and seals the run `failed` when any file is marked unread, never `approved`. A run whose only unexamined files are declared generated extents and pure renames seals by its findings. On a successful `finish` it prints the raw run token. The runner removes its scratch storage on every `finish` exit and on every non-zero exit of `start`, `append-scope`, or `append-finding`, so a failed verb, a coverage refusal included, ends the run and a later verb finds no state.\n\nWhen any runner verb exits non-zero, stop and surface its stderr, except for the single `append-finding` rerun `\u003Cfinding_payload>` directs, which follows a shell diagnostic that names an absent finding file and so a runner that never ran and left its state in place; the command boundary in `\u003Cconstraints>` admits no repair outside the runner. A verb or payload-file write that the native permission policy refuses ends the run as `\u003Cconstraints>` directs, not as a stderr surface.\n\n\u003C\u002Fapi_surface>\n\n\u003Creview_materials>\n\nAfter `start`, read:\n\n```text\n${CLAUDE_SKILL_DIR}\u002Freferences\u002Freview-prompt.md\n\u003CdiffPath>\n```\n\nUse `manifestPath` and `changedFiles` for navigation, but treat the diff file as the review input. `Grep` and `Glob` serve two purposes, each read-only and bounded to a relationship the prompt declares: discovery of the unchanged consumers of a changed governing declaration, bounded to exactly the declared relationships the prompt's `\u003Creview_scope>` consumers-of-changed-truth item enumerates; and location of the `ISSUES.md` of each governing node of the changed files, bounded to the governing-node relationship the prompt's `\u003Ctracked_debt>` section defines. Repository-root review policy files are not part of this skill's review context; the bundled reference prompt is the only prompt authority. Repository-local review rules belong in the repository's Product Tree, decisions, root `{{! file('root_guide', 'codex') !}}` or `{{! file('root_guide', 'claude') !}}`, and loaded governing skill files.\n\n\u003C\u002Freview_materials>\n\n\u003Cworkflow>\n\n1. Run `start` and parse the returned JSON.\n2. Load the prompt reference and diff bundle.\n3. Examine every changed file and every emitted diff section, applying the prompt's `\u003Cadversarial_probes>` before treating a file as clean. After Claude has read a changed file, call `append-scope` for that file; never call it for a file Claude sampled or skipped. Judge a declared generated extent through its relation's `sources` and `generator` and never read it as authored work; call neither `append-scope` nor `--unread` for it, and call neither for a pure rename. When a `generatedExtents` entry is `flagged` with `changed_sources` or `changed_generator`, the changeset changes the relation's sources or generator, so Claude may read that relation's extents as evidence of the generator change, and a defect they show resolves to the named `changed_sources` or `changed_generator`. When an entry carries a `flagged_reason`, its files are ordinary changed files: examine each and call `append-scope` after reading it.\n4. For each changed governing declaration, discover and read its unchanged consumers per the prompt's `\u003Creview_scope>` consumers-of-changed-truth item. A contradiction there is a finding cited at the consumer's location; consumers are not `append-scope` units — the journal's scope coverage remains the changed-file set.\n5. Before judging any `debt` finding, locate and read the `ISSUES.md` of each governing node of the changed files per the prompt's `\u003Ctracked_debt>` section.\n6. When a finding is raised, immediately write that one finding JSON object to its own scratch file and pass it to `append-finding` through the input redirect, following `\u003Cfinding_payload>`. Do not collect findings into a later batch. Set the finding's `unit` and `tracked` fields as the prompt's `\u003Cfinding_shape>` and `\u003Ctracked_debt>` direct.\n7. When review is complete, call `finish`, passing `--unread` for each changed file Claude did not read, or `--unread-file` when that set is large. Exclude declared generated extents and pure renames from the unread set.\n8. Output only the raw `runToken` that `finish` printed.\n\n\u003C\u002Fworkflow>\n\n\u003Cfinding_payload>\n\nA finding is a payload: it carries code spans, quotes, and JSON.\n\n- ALWAYS: write each finding with the file-write tool to a file in the scratch directory the harness names for this session, under a file name unique to this agent and run, such as `finding-\u003CrunToken>-\u003Cn>.json`. Where the harness names no scratch directory, use the one the hosting environment designates, such as `$RUNNER_TEMP` on a hosted runner.\n- ALWAYS: copy the scratch directory the harness names into each finding path character for character — a path rebuilt by hand differs from the written file by one character, and the runner then reads no file.\n- ALWAYS: when a file-write result reports a path that the scratch directory does not contain by whole path components, stop before `append-finding` and name the reported path.\n- ALWAYS: pass the file to `append-finding` with the input redirect `\u003C \u003Cfinding-file>`, with the path exactly as the file-write result reports it, written out literally, never held in a shell variable or expanded.\n- ALWAYS: when `append-finding` exits non-zero with a diagnostic that names the finding file as absent, run `append-finding` once more with the path copied from the file-write result; on a second failure, stop and report the exit, the stderr, and the run token.\n- NEVER: send a finding through a heredoc, a pipe, or an inline argument.\n- NEVER: remove a finding file; the harness clears its scratch directory.\n\n\u003C\u002Ffinding_payload>\n\n\u003Cconstraints>\n\n- NEVER run validation, tests, evals, coverage, lint, typecheck, or any deterministic verification command. Deterministic verification has already passed before this review starts; this skill provides agentic judgment by reading the diff and loaded review context.\n- NEVER invoke `spx journal`, `git`, `mktemp`, `rm`, `date`, `printf`, `compute_diff.py`, `journal_emit.py`, or `review_result.py` directly. The runner is the only command boundary.\n- NEVER write review-result files, rendered Markdown artifacts, or durable state outside `spx journal`. The runner-owned diff bundle and state file are scratch input for the active invocation only; the only files Claude writes are a finding payload per `\u003Cfinding_payload>` and an unread-list payload per `\u003Capi_surface>`.\n- When the native permission policy refuses a run-recording write — a runner `start`, `append-scope`, `append-finding`, or `finish` invocation, the journal it appends to, a finding payload file, or an unread-list payload file — stop at that write, retry it no more, write to no other location, and return a blocked result in place of the `runToken`: `blocked` with `diagnostic: configuration error` naming the refused path. The host grants the paths; this skill never widens its own access. The one `append-finding` rerun `\u003Cfinding_payload>` allows for an absent finding file is separate from this stop.\n- NEVER load a review prompt other than `${CLAUDE_SKILL_DIR}\u002Freferences\u002Freview-prompt.md` — one prompt keeps every review run comparable.\n- NEVER read `REVIEW.md`, `REVIEW.example.md`, or another repository-root review prompt.\n- NEVER append praise, acknowledgements, open questions, verdicts, or prose summaries to the review stream — it carries findings only.\n- NEVER render, summarize, count, or restate findings in the output. The sealed journal prefix is the review authority.\n\n\u003C\u002Fconstraints>\n\n\u003Cfailure_modes>\n\n**Runner failure bypassed the command boundary.** A runner verb exited non-zero, and Claude attempted to repair the journal through direct `spx journal` or scratch-file commands. Stop on the failed verb and surface its stderr; the runner remains the only command boundary.\n\n**A refused run-recording write was worked around.** The native permission policy refused a runner verb or a finding file, and Claude retried or wrote the finding to another location. Stop at the refused write and return the blocked configuration error naming the refused path.\n\n**A retyped finding path sealed a review with no verdict.** Claude rebuilt the finding file path by hand, `append-finding` read no file, and the run ended without recording the finding. Copy the path from the file-write result, and rerun once only when the diagnostic names the file as absent.\n\n**A clean review became a finding.** Claude appended a no-findings comment or synthetic finding before `finish`. Append no finding object for a clean review; `finish` records zero finding counts in the terminal event and returns the raw run token.\n\n**A partial run was reported as complete.** `finish` failed before sealing, and Claude returned the earlier `runToken`. Report the non-zero exit and stderr; only the token printed by a successful `finish` is a completed review result.\n\n**Sampled files were recorded as read.** Claude sampled a large changeset, appended scope for all 322 changed files, and sealed an approval over files it never opened. Append scope only for a file Claude read, and mark every other changed file with `finish --unread`, which seals the run `failed`.\n\n**Generated output was reviewed as authored work.** Claude read every file under a regenerated tree, raised findings in the output, and spent the scope on files the declaration already attributes to a generator. Judge a declared generated extent through its relation's sources, record no scope for it, and cite a finding about generated content at the source; read the extents as evidence only when the relation is `flagged` for changed sources or generator.\n\n**A long unread list broke the command line.** Claude passed hundreds of `--unread` arguments and the command exceeded the shell's limits. Write the paths to a file with the file-write tool and pass it once as `--unread-file`.\n\n**A universal claim was read without being executed.** Claude inspected every changed line and accepted \"exhaustive\" and \"mutually exclusive\" as prose. The claim failed for constructed boundary members absent from the corpus. Apply the counterexample probe in `\u003Cadversarial_probes>` before completing that file's scope.\n\n**Changed governing truth stopped at the diff boundary.** Claude compared changed specifications with their governors while leaving unchanged language renderings and shipped auditors unread. Those consumers contradicted the new rule. Follow declared derivation edges outward per the prompt's `\u003Creview_scope>` consumers-of-changed-truth item and report each contradiction at the consumer.\n\n\u003C\u002Ffailure_modes>\n\n\u003Csuccess_criteria>\n\n- [ ] The sealed journal prefix ends in a terminal run-completed event that records review status, finding counts, each unread file by name, each declared generated extent skipped by declaration with its regenerate command, and each rename without content change.\n- [ ] Every changed file that is neither a declared generated extent nor a pure rename is either advanced after being read or named unread, so no unread file is sealed `approved`.\n- [ ] The final output contains no rendered findings, count line, verdict, or summary.\n- [ ] A non-zero runner exit is reported with its stderr instead of a partial review result.\n- [ ] A permission-refused run-recording write yields a blocked result with `diagnostic: configuration error` naming the refused path, with no retry and no write elsewhere.\n\n\u003C\u002Fsuccess_criteria>\n","\u003Cobjective>\nThe raw run token that `finish` prints for a sealed `spx journal --type review` run.\n\u003C\u002Fobjective>\n\n\u003Cinputs>\n\nReview scope is discovered from the current worktree. Reusable gate evidence requires the exact current version to be committed and the worktree to be clean. Explicit advisory feedback may include staged, unstaged, or untracked work, and the resulting review supplies no reusable gate evidence. The input may supply the resolved scope as literal `SPX_VERIFY_BASE_REF`, `SPX_VERIFY_HEAD_REF`, `SPX_VERIFY_BRANCH`, `SPX_VERIFY_TARGET_KIND`, and `SPX_VERIFY_PULL_REQUEST_NUMBER` assignments, each written as a prefix on the single `review_run.py start` command line, because a shell export does not persist across Bash calls. `SPX_VERIFY_BASE_REF` and `SPX_VERIFY_HEAD_REF` select a non-default range; the other three record branch and target identity.\n\n\u003C\u002Finputs>\n\n\u003Capi_surface>\n\nInvoke only the bundled runner:\n\n```bash\n[SPX_VERIFY_*=\u003Cvalue> ...] python3 \"${CLAUDE_SKILL_DIR}\u002Fscripts\u002Freview_run.py\" start\npython3 \"${CLAUDE_SKILL_DIR}\u002Fscripts\u002Freview_run.py\" append-scope --state \"\u003CstatePath>\" \"\u003Cchanged-file>\"\npython3 \"${CLAUDE_SKILL_DIR}\u002Fscripts\u002Freview_run.py\" append-finding --state \"\u003CstatePath>\" \u003C \"\u003Cfinding-file>\"\npython3 \"${CLAUDE_SKILL_DIR}\u002Fscripts\u002Freview_run.py\" finish --state \"\u003CstatePath>\" --unread \"\u003Cunread-path>\"\npython3 \"${CLAUDE_SKILL_DIR}\u002Fscripts\u002Freview_run.py\" finish --state \"\u003CstatePath>\" --unread-file \"\u003Cunread-list-file>\"\n```\n\nThe bracketed `SPX_VERIFY_*=\u003Cvalue>` assignments are optional and appear only when the input supplies them, each as a literal prefix on the `start` command line; omit the brackets. Pass `--unread \"\u003Cunread-path>\"` once for each changed file Claude did not read, and omit it when Claude read every changed file. When the unread set is large, write one changed path per line to a payload file with the file-write tool, in the scratch directory `\u003Cfinding_payload>` names, under a file name unique to this agent and run such as `unread-\u003CrunToken>.txt`, and pass that file once as `--unread-file \"\u003Cunread-list-file>\"` with the path exactly as the file-write result reports it; `--unread-file` marks each listed path unread exactly as one `--unread` argument does, and the two forms combine.\n\n`start` fetches a git-derived or `origin\u002F` base and refuses a head behind the fetched `origin\u002F\u003Cbase>` tip before any journal exists — the dedicated stale-base exit code with a `stale-base` JSON diagnostic on stderr and no run state; a supplied local ref or commit is compared as given — and otherwise computes the diff bundle, opens the review journal, appends the scope-entered event, and returns JSON containing `statePath`, `runToken`, `diffPath`, `manifestPath`, `changedFiles`, `generatedExtents`, `generatedDeclarationChanged`, and `renames`. Report the refusal as a `stale-base` block carrying the diagnostic verbatim and stop.\n\n`changedFiles` lists a renamed file once under its new path. `renames` lists each rename with its `path`, its `former_path`, and `content_unchanged`, true when git detected a rename of identical content. `generatedExtents` is read from `spx\u002Flocal\u002Fgenerated-sources.toml` as committed at the scope's base ref (`git show \u003Cbase-ref>:spx\u002Flocal\u002Fgenerated-sources.toml`), never from the working tree, and is empty when the base lacks the file; it lists, per relation, the changed `files` that the relation's whole-file output pattern matches together with the relation's `sources`, `generator`, and `regenerate` command. A file bounded only by a relation's `begin` and `end` markers is an ordinary changed file. A relation whose declared sources or generator the changeset also changes carries `flagged` true and names them in `changed_sources` and `changed_generator`. When `spx\u002Flocal\u002Fgenerated-sources.toml` is among the changed paths, `generatedDeclarationChanged` is true and every listed relation carries `flagged` true with `flagged_reason` naming that declaration: the changeset changes the declaration that would exempt its files, so nothing is exempt by declaration, every changed file is ordinary, and the run-completed event records `generatedDeclarationChanged` with that reason and lists no `skippedGenerated` file.\n\n`append-scope` appends one scope-advanced event for a changed file only after Claude has read that file; a file Claude has not read gets no scope-advanced event.\n\n`append-finding` reads one finding JSON object from its standard input, which the input redirect from `\u003Cfinding-file>` supplies, wraps it in the journal event envelope, and appends it. The runner does not perform the full review finding schema or citation validation; `spx journal append` is the authoritative event boundary.\n\n`finish` refuses, with a non-zero exit, while a changed file is neither advanced nor marked unread, except a declared generated extent and a pure rename, which `finish` accounts for itself and which need no `append-scope` and no unread mark. Otherwise it reads the journal prefix, appends the terminal run-completed event with review status, finding counts, each unread file, each declared generated extent skipped by declaration with its relation's regenerate command, and each rename without content change, and seals the run `failed` when any file is marked unread, never `approved`. A run whose only unexamined files are declared generated extents and pure renames seals by its findings. On a successful `finish` it prints the raw run token. The runner removes its scratch storage on every `finish` exit and on every non-zero exit of `start`, `append-scope`, or `append-finding`, so a failed verb, a coverage refusal included, ends the run and a later verb finds no state.\n\nWhen any runner verb exits non-zero, stop and surface its stderr, except for the single `append-finding` rerun `\u003Cfinding_payload>` directs, which follows a shell diagnostic that names an absent finding file and so a runner that never ran and left its state in place; the command boundary in `\u003Cconstraints>` admits no repair outside the runner. A verb or payload-file write that the native permission policy refuses ends the run as `\u003Cconstraints>` directs, not as a stderr surface.\n\n\u003C\u002Fapi_surface>\n\n\u003Creview_materials>\n\nAfter `start`, read:\n\n```text\n${CLAUDE_SKILL_DIR}\u002Freferences\u002Freview-prompt.md\n\u003CdiffPath>\n```\n\nUse `manifestPath` and `changedFiles` for navigation, but treat the diff file as the review input. `Grep` and `Glob` serve two purposes, each read-only and bounded to a relationship the prompt declares: discovery of the unchanged consumers of a changed governing declaration, bounded to exactly the declared relationships the prompt's `\u003Creview_scope>` consumers-of-changed-truth item enumerates; and location of the `ISSUES.md` of each governing node of the changed files, bounded to the governing-node relationship the prompt's `\u003Ctracked_debt>` section defines. Repository-root review policy files are not part of this skill's review context; the bundled reference prompt is the only prompt authority. Repository-local review rules belong in the repository's Product Tree, decisions, root `{{! file('root_guide', 'codex') !}}` or `{{! file('root_guide', 'claude') !}}`, and loaded governing skill files.\n\n\u003C\u002Freview_materials>\n\n\u003Cworkflow>\n\n1. Run `start` and parse the returned JSON.\n2. Load the prompt reference and diff bundle.\n3. Examine every changed file and every emitted diff section, applying the prompt's `\u003Cadversarial_probes>` before treating a file as clean. After Claude has read a changed file, call `append-scope` for that file; never call it for a file Claude sampled or skipped. Judge a declared generated extent through its relation's `sources` and `generator` and never read it as authored work; call neither `append-scope` nor `--unread` for it, and call neither for a pure rename. When a `generatedExtents` entry is `flagged` with `changed_sources` or `changed_generator`, the changeset changes the relation's sources or generator, so Claude may read that relation's extents as evidence of the generator change, and a defect they show resolves to the named `changed_sources` or `changed_generator`. When an entry carries a `flagged_reason`, its files are ordinary changed files: examine each and call `append-scope` after reading it.\n4. For each changed governing declaration, discover and read its unchanged consumers per the prompt's `\u003Creview_scope>` consumers-of-changed-truth item. A contradiction there is a finding cited at the consumer's location; consumers are not `append-scope` units — the journal's scope coverage remains the changed-file set.\n5. Before judging any `debt` finding, locate and read the `ISSUES.md` of each governing node of the changed files per the prompt's `\u003Ctracked_debt>` section.\n6. When a finding is raised, immediately write that one finding JSON object to its own scratch file and pass it to `append-finding` through the input redirect, following `\u003Cfinding_payload>`. Do not collect findings into a later batch. Set the finding's `unit` and `tracked` fields as the prompt's `\u003Cfinding_shape>` and `\u003Ctracked_debt>` direct.\n7. When review is complete, call `finish`, passing `--unread` for each changed file Claude did not read, or `--unread-file` when that set is large. Exclude declared generated extents and pure renames from the unread set.\n8. Output only the raw `runToken` that `finish` printed.\n\n\u003C\u002Fworkflow>\n\n\u003Cfinding_payload>\n\nA finding is a payload: it carries code spans, quotes, and JSON.\n\n- ALWAYS: write each finding with the file-write tool to a file in the scratch directory the harness names for this session, under a file name unique to this agent and run, such as `finding-\u003CrunToken>-\u003Cn>.json`. Where the harness names no scratch directory, use the one the hosting environment designates, such as `$RUNNER_TEMP` on a hosted runner.\n- ALWAYS: copy the scratch directory the harness names into each finding path character for character — a path rebuilt by hand differs from the written file by one character, and the runner then reads no file.\n- ALWAYS: when a file-write result reports a path that the scratch directory does not contain by whole path components, stop before `append-finding` and name the reported path.\n- ALWAYS: pass the file to `append-finding` with the input redirect `\u003C \u003Cfinding-file>`, with the path exactly as the file-write result reports it, written out literally, never held in a shell variable or expanded.\n- ALWAYS: when `append-finding` exits non-zero with a diagnostic that names the finding file as absent, run `append-finding` once more with the path copied from the file-write result; on a second failure, stop and report the exit, the stderr, and the run token.\n- NEVER: send a finding through a heredoc, a pipe, or an inline argument.\n- NEVER: remove a finding file; the harness clears its scratch directory.\n\n\u003C\u002Ffinding_payload>\n\n\u003Cconstraints>\n\n- NEVER run validation, tests, evals, coverage, lint, typecheck, or any deterministic verification command. Deterministic verification has already passed before this review starts; this skill provides agentic judgment by reading the diff and loaded review context.\n- NEVER invoke `spx journal`, `git`, `mktemp`, `rm`, `date`, `printf`, `compute_diff.py`, `journal_emit.py`, or `review_result.py` directly. The runner is the only command boundary.\n- NEVER write review-result files, rendered Markdown artifacts, or durable state outside `spx journal`. The runner-owned diff bundle and state file are scratch input for the active invocation only; the only files Claude writes are a finding payload per `\u003Cfinding_payload>` and an unread-list payload per `\u003Capi_surface>`.\n- When the native permission policy refuses a run-recording write — a runner `start`, `append-scope`, `append-finding`, or `finish` invocation, the journal it appends to, a finding payload file, or an unread-list payload file — stop at that write, retry it no more, write to no other location, and return a blocked result in place of the `runToken`: `blocked` with `diagnostic: configuration error` naming the refused path. The host grants the paths; this skill never widens its own access. The one `append-finding` rerun `\u003Cfinding_payload>` allows for an absent finding file is separate from this stop.\n- NEVER load a review prompt other than `${CLAUDE_SKILL_DIR}\u002Freferences\u002Freview-prompt.md` — one prompt keeps every review run comparable.\n- NEVER read `REVIEW.md`, `REVIEW.example.md`, or another repository-root review prompt.\n- NEVER append praise, acknowledgements, open questions, verdicts, or prose summaries to the review stream — it carries findings only.\n- NEVER render, summarize, count, or restate findings in the output. The sealed journal prefix is the review authority.\n\n\u003C\u002Fconstraints>\n\n\u003Cfailure_modes>\n\n**Runner failure bypassed the command boundary.** A runner verb exited non-zero, and Claude attempted to repair the journal through direct `spx journal` or scratch-file commands. Stop on the failed verb and surface its stderr; the runner remains the only command boundary.\n\n**A refused run-recording write was worked around.** The native permission policy refused a runner verb or a finding file, and Claude retried or wrote the finding to another location. Stop at the refused write and return the blocked configuration error naming the refused path.\n\n**A retyped finding path sealed a review with no verdict.** Claude rebuilt the finding file path by hand, `append-finding` read no file, and the run ended without recording the finding. Copy the path from the file-write result, and rerun once only when the diagnostic names the file as absent.\n\n**A clean review became a finding.** Claude appended a no-findings comment or synthetic finding before `finish`. Append no finding object for a clean review; `finish` records zero finding counts in the terminal event and returns the raw run token.\n\n**A partial run was reported as complete.** `finish` failed before sealing, and Claude returned the earlier `runToken`. Report the non-zero exit and stderr; only the token printed by a successful `finish` is a completed review result.\n\n**Sampled files were recorded as read.** Claude sampled a large changeset, appended scope for all 322 changed files, and sealed an approval over files it never opened. Append scope only for a file Claude read, and mark every other changed file with `finish --unread`, which seals the run `failed`.\n\n**Generated output was reviewed as authored work.** Claude read every file under a regenerated tree, raised findings in the output, and spent the scope on files the declaration already attributes to a generator. Judge a declared generated extent through its relation's sources, record no scope for it, and cite a finding about generated content at the source; read the extents as evidence only when the relation is `flagged` for changed sources or generator.\n\n**A long unread list broke the command line.** Claude passed hundreds of `--unread` arguments and the command exceeded the shell's limits. Write the paths to a file with the file-write tool and pass it once as `--unread-file`.\n\n**A universal claim was read without being executed.** Claude inspected every changed line and accepted \"exhaustive\" and \"mutually exclusive\" as prose. The claim failed for constructed boundary members absent from the corpus. Apply the counterexample probe in `\u003Cadversarial_probes>` before completing that file's scope.\n\n**Changed governing truth stopped at the diff boundary.** Claude compared changed specifications with their governors while leaving unchanged language renderings and shipped auditors unread. Those consumers contradicted the new rule. Follow declared derivation edges outward per the prompt's `\u003Creview_scope>` consumers-of-changed-truth item and report each contradiction at the consumer.\n\n\u003C\u002Ffailure_modes>\n\n\u003Csuccess_criteria>\n\n- [ ] The sealed journal prefix ends in a terminal run-completed event that records review status, finding counts, each unread file by name, each declared generated extent skipped by declaration with its regenerate command, and each rename without content change.\n- [ ] Every changed file that is neither a declared generated extent nor a pure rename is either advanced after being read or named unread, so no unread file is sealed `approved`.\n- [ ] The final output contains no rendered findings, count line, verdict, or summary.\n- [ ] A non-zero runner exit is reported with its stderr instead of a partial review result.\n- [ ] A permission-refused run-recording write yields a blocked result with `diagnostic: configuration error` naming the refused path, with no retry and no write elsewhere.\n\n\u003C\u002Fsuccess_criteria>\n",{"name":5,"description":7,"allowed-tools":44},[45,46,47,48,49,50],"Bash(python3 \"${CLAUDE_SKILL_DIR}\u002Fscripts\u002Freview_run.py\":*)","Bash(SPX_VERIFY_*=* python3 \"${CLAUDE_SKILL_DIR}\u002Fscripts\u002Freview_run.py\":*)","Read","Grep","Glob","Write","main","978d51c51d68c99cffb2ba48f7cad9d2f07e5738","src\u002Fplugins\u002Fproduct-tree\u002Fskills\u002Freview-changes","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins","https:\u002F\u002Fgithub.com\u002Foutcomeeng","https:\u002F\u002Foutcome.engineering","Spec-driven development for AI-assisted engineering","2026-11-12 11:34:11",{"source":8,"skillName":5,"commands":60,"prompt":70,"zipAvailable":71},[61,64,67],{"tool":62,"command":63},"npx","npx skills add outcomeeng\u002Fplugins --skill review-changes",{"tool":65,"command":66},"bunx","bunx skills add outcomeeng\u002Fplugins --skill review-changes",{"tool":68,"command":69},"pnpm","pnpm dlx skills add outcomeeng\u002Fplugins --skill review-changes","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fproduct-tree\u002Fskills\u002Freview-changes\u002FSKILL.md",true,[73,79,85,89,93,97],{"path":74,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":77,"sourceHtmlUrl":78},"references\u002Freview-prompt.md","REFERENCE","参考文档",15900,"https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fproduct-tree\u002Fskills\u002Freview-changes\u002Freferences\u002Freview-prompt.md",{"path":80,"resourceType":81,"resourceTypeDesc":82,"sizeBytes":83,"sourceHtmlUrl":84},"scripts\u002F__init__.py","SCRIPT","脚本",254,"https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fproduct-tree\u002Fskills\u002Freview-changes\u002Fscripts\u002F__init__.py",{"path":86,"resourceType":81,"resourceTypeDesc":82,"sizeBytes":87,"sourceHtmlUrl":88},"scripts\u002Fcompute_diff.py",28693,"https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fproduct-tree\u002Fskills\u002Freview-changes\u002Fscripts\u002Fcompute_diff.py",{"path":90,"resourceType":81,"resourceTypeDesc":82,"sizeBytes":91,"sourceHtmlUrl":92},"scripts\u002Fjournal_emit.py",25151,"https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fproduct-tree\u002Fskills\u002Freview-changes\u002Fscripts\u002Fjournal_emit.py",{"path":94,"resourceType":81,"resourceTypeDesc":82,"sizeBytes":95,"sourceHtmlUrl":96},"scripts\u002Freview_result.py",29951,"https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fproduct-tree\u002Fskills\u002Freview-changes\u002Fscripts\u002Freview_result.py",{"path":98,"resourceType":81,"resourceTypeDesc":82,"sizeBytes":99,"sourceHtmlUrl":100},"scripts\u002Freview_run.py",45309,"https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fproduct-tree\u002Fskills\u002Freview-changes\u002Fscripts\u002Freview_run.py",[102,108,142,157,173,186],{"skillNo":103,"name":104,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":105,"categories":106,"keywords":107,"publishedAt":39},"01M71G25RZTPQG9KJ00JCHC1F7","plugin","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Ftemplates\u002Fplugin\u002FSKILL.md",[],[104],{"skillNo":109,"name":110,"nameZh":6,"description":111,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":112,"categories":113,"keywords":124,"publishedAt":39},"01M71G23DCX3SV60H63HBBKQ6J","sanitize-powerpoint","ALWAYS invoke this skill when sanitizing, cleaning up, auditing, or aligning a PowerPoint (.pptx) deck — slide-master and layout structure, layout type attributes, stray fonts, non-theme colors, or layout naming. NEVER hand-edit pptx XML without this skill.","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fwork\u002Fskills\u002Fsanitize-powerpoint\u002FSKILL.md",[114,119],{"categoryNo":115,"categoryName":116,"taxonomyType":117,"taxonomyTypeDesc":118},"01JBSEEDCAT000000000000003","演示文稿","SCENARIO","使用场景",{"categoryNo":120,"categoryName":121,"taxonomyType":122,"taxonomyTypeDesc":123},"01JBSEEDCAT000000000000034","行政与办公人员","PROFESSION","适用职业",[125,126,127,128,129,130,131,36,132,133,134,135,136,137,138,139,140,141,26,25],"sanitize powerpoint","powerpoint","sanitize","fonts","type","cleaning","structure","attributes","theme","pptx","naming","layout","deck","up","edit","xml","master",{"skillNo":143,"name":144,"nameZh":6,"description":145,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":146,"categories":147,"keywords":148,"publishedAt":39},"01M71G21PQKBCDECHGVYSKPJW5","draw-excalidraw","ALWAYS invoke this skill when creating Excalidraw diagrams, visualizing workflows, architectures, or concepts. NEVER generate Excalidraw JSON without this skill.","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fwork\u002Fskills\u002Fdraw-excalidraw\u002FSKILL.md",[],[149,150,151,152,153,36,26,154,155,25,156,33],"draw excalidraw","excalidraw","draw","diagrams","architectures","generate","workflows","concepts",{"skillNo":158,"name":159,"nameZh":6,"description":160,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":161,"categories":162,"keywords":166,"publishedAt":39},"01M71G1ZMNGFD37RZYM620XM2C","typescript-test-standards","TypeScript test standards enforced across all skills. Loaded by other skills, not invoked directly.","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Ftypescript\u002Fskills\u002Ftypescript-test-standards\u002FSKILL.md",[163],{"categoryNo":164,"categoryName":165,"taxonomyType":117,"taxonomyTypeDesc":118},"01JBSEEDCAT000000000000008","测试与质量",[167,168,169,170,171,31,172],"typescript","typescript test standards","test","standards","false","across",{"skillNo":174,"name":175,"nameZh":6,"description":176,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":177,"categories":178,"keywords":182,"publishedAt":39},"01M71G1XBH0X1VCTR7269MDGSY","typescript-code-standards","TypeScript code standards enforced across all skills. Loaded by other skills, not invoked directly.","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Ftypescript\u002Fskills\u002Ftypescript-code-standards\u002FSKILL.md",[179],{"categoryNo":180,"categoryName":181,"taxonomyType":122,"taxonomyTypeDesc":123},"01JBSEEDCAT000000000000030","开发工程师",[167,183,184,185,170,171,31,172],"code","typescript code standards","code standards",{"skillNo":187,"name":188,"nameZh":6,"description":189,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":190,"categories":191,"keywords":192,"publishedAt":39},"01M71G1TQ3VN45SJ75DFMJDB9W","typescript-architecture-standards","TypeScript ADR conventions enforced across architect and auditor skills. Loaded by other skills, not invoked directly.","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Ftypescript\u002Fskills\u002Ftypescript-architecture-standards\u002FSKILL.md",[],[193,170,194,195,167,196,197,172,31,198,171,199],"architecture standards","typescript architecture standards","architecture","adr","architect","auditor","conventions"]