---
name: changes-review-loop
description: '[Code Quality] Use when you need to combine /changes-review + /fix in a recursive loop — each round runs /changes-review (report-only) to surface validated findings over a fixed diff scope, then /fix to resolve them, then loops again with a FRESH full /changes-review over the CHANGED diff until one complete pass produces zero validated findings (nothing left to fix).'
---

> Codex compatibility note:
>
> - Invoke repository skills with `$skill-name` in Codex; this mirrored copy rewrites legacy Claude `/skill-name` references.
> - Task tracker mandate: BEFORE executing any workflow or skill step, create/update task tracking for all steps and keep it synchronized as progress changes.
> - User-question prompts mean to ask the user directly in Codex.
> - Ignore Claude-specific mode-switch instructions when they appear.
> - Strict execution contract: when a user explicitly invokes a skill, execute that skill protocol as written.
> - Subagent authorization: when a skill is user-invoked or AI-detected and its protocol requires subagents, that skill activation authorizes use of the required `spawn_agent` subagent(s) for that task.
> - Do not skip, reorder, or merge protocol steps unless the user explicitly approves the deviation first.
> - For workflow skills, execute each listed child-skill step explicitly and report step-by-step evidence.
> - If a required step/tool cannot run in this environment, stop and ask the user before adapting.

<!-- CODEX:PROJECT-REFERENCE-LOADING:START -->

## Codex Project-Reference Loading (No Hooks)

Codex uses static project-reference loading instead of runtime-injected project docs.
When coding, planning, debugging, testing, or reviewing, open project docs explicitly using this routing.

**Always read:**

- `docs/project-config.json` (project-specific paths, commands, modules, and workflow/test settings)
- `docs/project-reference/docs-index-reference.md` (routes to the full `docs/project-reference/*` catalog)
- `docs/project-reference/lessons.md` (always-on guardrails and anti-patterns)

**Missing/stale context route:** If `docs/project-config.json`, the docs index, `lessons.md`, `CLAUDE.md`, `AGENTS.md`, or any task-required reference doc is missing or stale, auto-run `$project-init` or the narrow setup route (`$project-config`, `$docs-init`, `$scan-all`, `$scan --target=<key>`, `$claude-md-init`) before ordinary project-specific work. If Codex mirrors or `AGENTS.md` are missing/stale, ask the user to run `$sync-codex`; do not auto-run it.

**Situation-based docs:**

- Project structure/architecture/tech-stack/deployment/setup (any layer — backend, frontend, or infra): `project-structure-reference.md`
- Backend/CQRS/API/domain/entity changes: `backend-patterns-reference.md`, `domain-entities-reference.md`
- Frontend/UI/styling/design-system: `frontend-patterns-reference.md`, `scss-styling-guide.md`, `design-system/README.md`
- Spec authoring, `docs/specs/` pathing, or TC format: `feature-spec-reference.md`, `spec-system-reference.md`, `spec-principles.md`
- Behavior/public-contract changes or spec-test-code sync: `workflow-spec-test-code-cycle-reference.md` plus the spec docs above
- Derived spec indexes/ERDs/reimplementation guides: `spec-system-reference.md` and source Feature Specs under `docs/specs/`
- Integration test implementation/review: `integration-test-reference.md`
- E2E test implementation/review: `e2e-test-reference.md`
- Code review/audit work: `code-review-rules.md` plus domain docs above based on changed files

Do not read all docs blindly. Start from `docs-index-reference.md`, then open only relevant files for the task.

<!-- CODEX:PROJECT-REFERENCE-LOADING:END -->

<!-- PROMPT-ENHANCE:STEP-TASK-ANCHOR:START -->

> **[BLOCKING]** Execute skill steps in declared order. NEVER skip, reorder, or merge steps without explicit user approval.
> **[BLOCKING]** Before each step or sub-skill call, update task tracking: set `in_progress` when step starts, set `completed` when step ends.
> **[BLOCKING]** Every completed/skipped step MUST include brief evidence or explicit skip reason.
> **[BLOCKING]** If Task tools are unavailable, create and maintain an equivalent step-by-step plan tracker with the same status transitions.

<!-- PROMPT-ENHANCE:STEP-TASK-ANCHOR:END -->

## Quick Summary

**Goal:** Drive a diff scope to a **clean pass** by pairing `$changes-review` with `$fix` in a recursive loop — each round runs `$changes-review` INLINE in **report-only mode** to surface validated findings over a fixed diff scope, then `$fix` to resolve them at the owning layer, then loops again with a FRESH full `$changes-review` over the CHANGED diff — stopping when a complete `$changes-review` pass clears the round's exit bar: **zero validated findings** in rounds 1-2, and **zero validated CRITICAL/HIGH/MEDIUM** from round 3 (LOW-only ENDS the loop, deferred not fixed).

**Summary:**

- **Each round = `$changes-review` (report-only) + validate + `$fix`** — the loop runs `$changes-review` as a review-PRODUCER that stops after its raw findings report (the documented `$workflow-review-changes` boundary: _"stop after the report; parent step 2 owns validation"_, `changes-review/SKILL.md:52,204`); the loop then owns the validation gate (`$why-review --validate-findings`) and the dedicated `$fix` half, so `$changes-review` never self-validates or self-fixes; one without the other never converges.
- **Steps (in order):** (0) resolve diff scope + Goal Contract → (0b) bind the convergence loop (protocol loop primary + optional `/goal` accelerator) → (1) round loop { run `$changes-review` report-only INLINE → `$why-review --validate-findings` on the report → run `$fix` on the VALIDATED findings at the owning layer → log iteration } → (2) converge when a fresh review clears the round's exit bar (zero findings rounds 1-2; zero CRITICAL/HIGH/MEDIUM round 3+) OR escalate on non-progress → (3) terminal `$docs-update` + recap.
- **Convergence:** stop ONLY when a **fresh full** `$changes-review` over the CURRENT (post-fix) diff clears the round's exit bar — not a stale clean report predating the last fix.
- **Severity floor — from round 3, LOW stops blocking.** Rounds 1-2 converge on **zero validated findings** (any severity). **From round 3 the bar is zero validated CRITICAL/HIGH/MEDIUM — a round whose validated findings are ALL LOW ENDS the loop.** Never open another round to fix LOW alone; list every deferred LOW in the recap and Goal Contract instead, and NEVER re-tier a real CRITICAL/HIGH/MEDIUM down to LOW to reach the exit. Severity tiers per `SYNC:severity-rubric`.
- **Inline invariant:** run `$changes-review` and `$why-review` via the skill invocation, NEVER the `spawn_agent` tool — they self-bind their OWN review-loop obligations (and a session `/goal` gate WHEN available, `changes-review/SKILL.md:197-223`, `why-review/SKILL.md:57-76`), which a sub-agent cannot own or carry back to this loop. `$changes-review`'s own dimensional reviewers (its Phase 0.7 sub-agents) stay sub-agents by its design, so context stays bounded.
- **Apply ONLY validated findings:** every finding is validated to the ≥85% survival bar via `$why-review --validate-findings` before `$fix` touches it; the loop applies THOSE at the lowest owning layer (Entity > Service > Handler), routed by change type — NEVER unvalidated findings.
- **Scope base is FIXED; the working tree grows.** Recompute the diff scope each round (`branch-diff base` ∪ current uncommitted changes) so convergence is measured against a stable subject as fixes accumulate.
- **Bounded:** round cap default 3; blocking findings not shrinking across 2 rounds, or cap hit with CRITICAL/HIGH/MEDIUM still open → **STOP & escalate** by asking the user directly. Increasing findings → STOP (fixes regressing).

**Why this skill exists (READ FIRST — it is the whole justification):** standalone `$changes-review` COUPLES find + fix inside one skill invocation (its Phase 7 self-fix and Phase -1 self-recursive loop, `changes-review/SKILL.md:53,197-223`) — the review and the fix share one context, one lens, and one confirmation bias. This loop **decouples** them: it runs `$changes-review` purely as a finder (report-only, the documented `$workflow-review-changes` boundary where it stops after the report and the caller owns fixing, `changes-review/SKILL.md:29,204,558,578`), validates the findings, then hands them to a **dedicated `$fix`** half with its own intelligent routing at the lowest owning layer, and then re-runs a **fresh full** `$changes-review` over the changed diff. The value is the clean finder/fixer split plus the fresh-full re-review each round: it catches **fix-induced regressions** the coupled inner loop can rationalize away, and it lets `$fix` own the fix mechanics instead of the reviewer patching its own findings. Without this outer loop, "changes-review found issues, then something fixed them" ships those fixes without an independent fresh review.

**Workflow:** resolve diff scope + Goal Contract → bind the convergence loop (protocol loop + optional `/goal` accelerator) → **round loop** { run `$changes-review` report-only INLINE → `$why-review --validate-findings` → run `$fix` on the validated findings at owning layer → log iteration } → converge when a fresh full review clears the round's exit bar (zero validated findings rounds 1-2; zero validated CRITICAL/HIGH/MEDIUM round 3+) → terminal `$docs-update` + recap.

**Key Rules:**

- **Each round pairs `$changes-review` (find) + `$fix` (resolve).** The loop runs `$changes-review` in report-only mode so it produces findings but does NOT self-validate or self-fix; the loop's `$why-review --validate-findings` gate and `$fix` are the halves that validate and land the change. A round is incomplete until BOTH have run (or the review returned zero findings).
- **MUST run INLINE in the main session — NEVER dispatch `$changes-review` or `$why-review` as a sub-agent.** They self-bind their own review-loop obligations (and a session `/goal` gate when available, `changes-review/SKILL.md:197-223`); as a sub-agent that in-session guarantee is silently lost. This loop skill therefore also runs inline. (`$changes-review`'s internal Phase 0.7 dimensional reviewers remain sub-agents by its own design — that is bounded and correct.)
- **Report-only invocation is mandatory.** Tell `$changes-review` to run as a review producer: do its full dimensional review + Phase 6 `$why-review --validate-findings` gate, then STOP before Phase 7 self-fix / Phase 7.5 holistic / Phase 8 docs-update. The loop owns fixing (Step 1.3) and the terminal docs-update (Step 3). If a `$changes-review` invocation cannot be constrained to report-only in this environment and self-fixes anyway, fall back to detecting fixes-applied per round (like `$workflow-review-changes-loop`) and skip the redundant `$fix` half for that round — never double-fix.
- **Convergence = a fresh full `$changes-review` over the post-fix diff clears the round's exit bar.** Rounds 1-2: zero validated findings. **Round 3+: zero validated CRITICAL/HIGH/MEDIUM — LOW-only converges** (record the LOWs as deferred, do not fix them). A clean report produced BEFORE the latest fix landed does NOT count — re-review the changed diff.
- **The severity floor bounds ITERATION, never the standard.** It ends the loop; it never authorizes shipping a known CRITICAL/HIGH/MEDIUM, never lowers the ≥85% finding-survival bar, and never applies to a binary gate (a failing test is a failure, not a LOW finding).
- **`$fix` applies ONLY validated findings**, at the lowest owning layer, routed by change type (code → `$fix` with its `--target` intelligent routing, or a direct edit at Entity/Service; spec-drift → `$spec [update]` + `$spec [mode=tests]`; docs → `$docs-update`; missing coverage → `$integration-test`; honor each finding's dual-feedback ledger, `changes-review/SKILL.md:530`). NEVER apply an unvalidated or demoted finding.
- **The diff scope base is FIXED across rounds; its content changes as fixes land.** Recompute `{scope}` = branch-diff base ∪ current uncommitted changes each round so the base merge-base never moves and convergence is measured against a stable subject.
- **Round cap (default 3)** and **blocking-findings-not-shrinking / increasing → STOP & escalate** by asking the user directly. NEVER loop open-ended. Cap exhaustion escalates only when CRITICAL/HIGH/MEDIUM remain — a LOW-only round converges via the severity floor.

---

## First Principle — Convergence, Not Motion

> A round that changes the diff is progress **only if** the next fresh review finds fewer things to fix.
> The loop exists to reach a fixed point (no blocking findings), not to keep editing the code.
> The bar tightens by round: everything blocks in rounds 1-2; from round 3 only CRITICAL/HIGH/MEDIUM block, so a LOW-only round is the fixed point.
> If findings stop shrinking, that is a signal to **escalate**, not to spin another round.

---

## Step 0 — Resolve Diff Scope + Goal Contract (FIRST ACTION)

1. **Parse the review scope** from the user prompt into a stable, reusable scope string — exactly the diff kinds `$changes-review` resolves (`changes-review/SKILL.md:96-104`). It has two parts UNIONed:
    - **Branch-diff base** — a branch-to-branch or PR diff (e.g. `git diff develop...HEAD`, three-dot: changes on the feature branch since it forked from `develop`) so the base is a **fixed merge-base**, not a moving target. If the prompt names a commit range, capture it the same way.
    - **Current changes** — the uncommitted working-tree changes (`git status --porcelain`, `git diff` + `git diff --staged`).
    - **Scope string (recompute each round):** `{branch-diff base} ∪ {current uncommitted changes}`. The base commit is fixed for the whole loop; the uncommitted set legitimately grows as fixes land.
    - If the prompt names no branch diff (pure "current changes" review), the scope is just the working-tree changes — the loop still applies. **NEVER silently convert the diff source type.**
2. **Resolve/create the Goal Contract** per `SYNC:goal-contract-satisfaction-loop` (`plans/goals/{YYMMDD-HHmm}-{slug}/goal.md`, template `.claude/templates/goal-contract-template.md`). Its single **required** Success Criterion:
    > _A fresh full `$changes-review` over `{scope}` clears the round's exit bar: **rounds 1-2** → **zero validated findings** (no finding of any severity survives the loop's `$why-review --validate-findings` gate); **round 3+** → **zero validated CRITICAL/HIGH/MEDIUM findings**, with any remaining LOW findings recorded as deferred rather than fixed._
    > Record the round cap (default 3) and the scope string in **Constraints**.

## Step 0b — Bind the Convergence Loop (protocol-first; `/goal` is an optional accelerator)

The convergence loop is bound by TWO layers. The **protocol loop (Steps 1–2) is the BINDING mechanism** and MUST be self-driven by you, the running agent, on every host — with or without any command or hook. The **`/goal` command is an OPTIONAL accelerator** layered on top; it is never the primary mechanism, and its absence NEVER weakens the loop. This mirrors the project rule that hooks/trackers are accelerators only — correctness must not depend on them.

**1. Protocol loop — ALWAYS binding (hook/command-independent).** You are personally responsible for not stopping until the loop converges or bounded-escalates. This binds Claude, Codex, and Copilot equally, whether or not `/goal` exists:

> Repeatedly run `$changes-review` report-only INLINE over `{scope}` (recomputed each round). After each review, validate its findings with `$why-review --validate-findings`, apply every VALIDATED finding's fix via `$fix` at its owning layer, then re-run a FRESH full `$changes-review` over the CHANGED diff. Do NOT stop while the last review still produced validated findings that BLOCK at the current round's bar. Converge when a fresh full `$changes-review` clears that bar: **rounds 1-2** → zero validated findings; **round 3+** → zero validated CRITICAL/HIGH/MEDIUM (LOW-only ENDS the loop, with the LOWs recorded as deferred). Cap at `{N=5}` rounds; if blocking findings do not shrink across 2 consecutive rounds, findings increase, or the cap is hit with CRITICAL/HIGH/MEDIUM still open → STOP and escalate by asking the user directly. Never loop open-ended.

Treat this as a standing obligation you re-read at every Step 2 checkpoint — NOT a one-time note you can rationalize away after the first fix cycle. The Goal Contract's required Success Criterion (Step 0) is its durable, host-independent record.

**2. `/goal` command — invoke as an accelerator WHEN AVAILABLE.** If a `/goal` command exists and you are permitted to run it in this environment, ALSO invoke it (a real tool/command call, NOT a paraphrase, NOT a Goal Contract file substituted for it) with the SAME condition, so a session Stop hook mechanically enforces the loop:

```
/goal changes-review convergence loop: repeatedly run $changes-review report-only INLINE over {scope} (recomputed each round). After each review, validate its findings with $why-review --validate-findings, apply every VALIDATED finding's fix via $fix at its owning layer, then re-run a FRESH full $changes-review over the CHANGED diff. If blocking validated findings>0 → apply fixes and run another round; if a fresh full $changes-review clears the round's bar (rounds 1-2: zero validated findings; round 3+: zero validated CRITICAL/HIGH/MEDIUM, LOW-only counts as clear with the LOWs recorded as deferred) → CONVERGED, run the terminal $docs-update and clear the gate. Do NOT stop while the last review still produced blocking validated findings. Cap at {N=5} rounds; if blocking findings do not shrink across 2 consecutive rounds, findings increase, or the cap is hit with CRITICAL/HIGH/MEDIUM still open → STOP and escalate by asking the user directly. Never loop open-ended.
```

The `/goal` Stop hook blocks stopping until the condition holds and auto-clears when met — do not tell the user to clear it.

**If `/goal` is unavailable, unregistered, or not permitted** (e.g. Codex/Copilot, or a Claude run without the command): DO NOT error, DO NOT block, and DO NOT invent a stand-in gate. Record ONE line in the Goal Contract — `/goal accelerator unavailable — loop bound by protocol (Steps 1–2) + this Goal Contract` — and proceed. The protocol loop above plus the Goal Contract are the same gate, enforced by discipline instead of a hook.

> **Nested gates (by design, safe):** each inner `$changes-review` round would self-bind its OWN Phase -1 review-loop obligation — but the loop runs it in **report-only mode**, where Phase -1 is deferred to the caller (the `$workflow-review-changes` boundary, `changes-review/SKILL.md:204`), so no inner self-fix gate is installed; THIS outer loop owns the single convergence gate. Each inner `$why-review --validate-findings` self-clears when that round's findings are all adjudicated. All self-clear on satisfaction — no orphaned gate. Do NOT tell the user to clear either.

## Step 1 — Round Loop (`$changes-review` → validate → `$fix` → log)

Each round couples the two halves — **review to find, fix to resolve.** For each round `R` (starting at 1), do ALL of:

1. **Snapshot before:** record the working-tree fingerprint — `git status --porcelain` + `git diff --stat`. This is the fixes-applied baseline for the round and the objective backstop for convergence detection (Step 2).
2. **Run `$changes-review` report-only INLINE** on the recomputed `{scope}` via the skill invocation (NEVER the `spawn_agent` tool). Direct it to run as a review PRODUCER: perform its full dimensional review and STOP after writing its findings report — do NOT run its Phase 6 validation, Phase 7 self-fix, Phase 7.5 holistic, or Phase 8 docs-update. This is exactly the `$workflow-review-changes` boundary where the caller (this loop) owns validation and fixing, so `$chan