# Verify

> Run comprehensive triage-first verification pipeline with specialized skills. Detects scope, discovers toolchain, triages files to relevant skills, runs static analysis, invokes review skills in parallel, exercises the app, and produces a unified report. Supports interactive, report-only, and auto-fix modes.

- Skill: `niekcandaele/verify` (Agent Skill)
- Install (CLI): `npx skillmds@latest add niekcandaele/verify`
- Raw SKILL.md: https://api.skillmd.com/api/skills/niekcandaele/verify/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: niekcandaele (https://skillmd.com/u/niekcandaele)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/niekcandaele/verify

---


# Verify Changes — Triage-First Pipeline

## Goal

Run comprehensive verification before considering changes complete. This skill detects what changed, triages files to relevant skills, runs static analysis, invokes review skills in parallel, exercises the app end-to-end, and produces a unified report with actionable findings.

## Phase 0: Parse Arguments & Mode

Parse `$ARGUMENTS` for:

**Mode** (`--mode=`):
- `interactive` (default): Full pipeline → report → interactive triage → plan → fix
- `report-only`: Full pipeline → report → STOP
- `auto-fix`: Full pipeline → report → auto-accept severity >= threshold → plan → fix → STOP

**Depth** (`--depth=`):
- `light` (default): one independent judgement review plus empirical evidence
- `deep`: the full judgement fan-out

**Composition** — what the pipeline runs, by depth:

| | `light` (default) | `deep` |
|---|---|---|
| Triage (Phase 3) | Job A only | Job A + Job B |
| Judgement review | **one**: `codex-reviewer`, or `reviewer` when Codex is unavailable | `reviewer`, `codex-reviewer`, `comment-review`, `qa`, `ux-reviewer` |
| Deterministic | `static-analysis`, `tester` | `static-analysis`, `tester` |
| Empirical | `exerciser`, carrying its own visual lens | `exerciser`, plus `visual-verify` when triage gates it in |
| Diagnostic | `debugger` on failure | `debugger` on failure |
| Plan completeness | when a plan resolves | when a plan resolves |

This table is the single source of truth for composition; callers point here rather than
restating it.

**The two depths are different bets, not different amounts of the same bet.** Deep buys more
lenses on one diff: five reviewers, each with its own specialism, each free to open a line of
inquiry the others would miss. Light buys model diversity and empirical evidence instead: one
review from a genuinely different model where the machine has one, plus starting the
application and actually using the feature. Both keep the checks that cannot wander — a
linter and a test runner report what came back and never widen their own remit.

Light is the default because the caller is usually a model that has already reviewed and
tested its own work. Stacking five more judgement reviewers on top of that does not converge:
each round fixes the previous round's findings, and reviewers meeting a diff that only grew
open new inquiries into code earlier rounds already cleared. Every fix becomes fresh surface
to review.

Deep exists for the change where that cost is worth paying — the integrated result of a whole
epic, an unusually risky diff, a human who asks for it. Ask for it explicitly; nothing selects
it on your behalf.

**Scope Control:**
- `--scope=staged`: Verify only staged changes
- `--scope=unstaged`: Verify only unstaged modified files
- `--scope=branch`: Verify all changes in current branch vs base
- `--scope=all`: Verify entire codebase (comprehensive audit — skips triage, runs all agents on everything)
- `--files="file1,file2"`: Verify specific files only
- `--module=path`: Verify specific module/directory
- Default (no scope arg): Auto-detect from git state
- `--base=<ref>`: Exact baseline ref for `--scope=branch`, for example
  `origin/release`. Reject it with every other scope. Callers that know the PR target must
  pass it; do not replace it with a default-branch guess.

**Other Options:**
- `--skip-ux`: Skip UX review for pure backend changes. **Valid only at `--depth=deep`.** At
  light there is no `ux-reviewer` to skip, so accepting the flag would let a caller believe it
  suppressed something that was never going to run. Reject it as an argument error rather than
  treating it as a no-op.
- `--skip-visual`: Skip visual review for changes the human has already eyeballed (e.g. copy-only). Valid at both depths: at deep it skips `visual-verify`, at light it suppresses the exerciser's visual lens. Visual review is also auto-skipped when `visual-verify` is not among the available skills (defensive — it is normally installed globally, and a project may shadow it with its own copy to customize capture conventions).
- `--auto-fix-threshold=N`: Minimum severity for auto-fix mode (default: 3)
- `--plan-file=<path>`: Explicit path to a plan file for completeness checking. If not provided, discover the plan from context — check if a plan is visible in conversation history (e.g., invoked from player-coach which read a plan, or a plan was created/discussed earlier in this session). If a plan is found from either source, resolve its contents for the plan completeness check in Phase 6.

**Carry-forward across verification runs:**

A caller that runs verification repeatedly against one change — `player-coach` is the
obvious one — can supply what earlier runs already decided. Without it every run is the
first run: fresh reviewers re-audit a diff that only grows, and each round opens a new
line of inquiry into code the previous rounds already cleared. The findings never
converge; they wander.

- `--ledger=<path>`: a run ledger to read (see `player-coach`'s
  `references/run-ledger.md`). Read-only — verify never writes it.
- `--since=<sha>`: the head the last verification run approved. Requires `--ledger`.
  Defines the **delta**.
- `--no-carry-forward`: supply the ledger for reporting but review as though it were the
  first run. This is how the confirming full audit before approval is requested.

With no `--ledger`, verify behaves exactly as it always has, so it stays usable standalone.

**Epic context:**

- `--epic-context=<path>`: a file describing the epic this change is one issue of — what is
  already done, what this issue is, and what is still to come. Read-only, and passed straight
  through to the judgement reviewers. It exists so a reviewer can tell the difference between
  work that was forgotten and work that is scheduled, which is the distinction a reviewer
  looking at one issue of an epic has no way to make on its own.

**Output Format:**
- `--format=`: `markdown` (default) or `json`. When `json`, the report is serialized as a JSON object conforming to the adversary CLI schema (see Phase 8c)
- `--output=<path>`: File path to write the JSON report to. Required when `--format=json`. The Write tool is used to write the file.

## Phase 1: Scope Detection

Determine what files/changes to verify.

**1. Parse User-Specified Scope (if provided):**
- Check `$ARGUMENTS` for `--scope=`, `--files=`, or `--module=` flags
- If specified, use that exact scope
- Skip auto-detection

For explicit branch scope, resolve the comparison before listing files:

```bash
HEAD_SHA=$(git rev-parse HEAD)
BASE_REF="$exact_base_ref" # the parsed --base value, or the resolved default
# Without --base, resolve origin/<provider default branch>; only then fall back to main/master.
BASE_REMOTE=${BASE_REF%%/*}
if git remote get-url "$BASE_REMOTE" >/dev/null 2>&1; then
  BASE_BRANCH=${BASE_REF#*/}
  git fetch "$BASE_REMOTE" \
    "+refs/heads/$BASE_BRANCH:refs/remotes/$BASE_REMOTE/$BASE_BRANCH"
fi
git rev-parse --verify "$BASE_REF^{commit}"
MERGE_BASE=$(git merge-base "$BASE_REF" "$HEAD_SHA")
git diff --name-status "$MERGE_BASE" "$HEAD_SHA"
git diff -U0 "$MERGE_BASE" "$HEAD_SHA" -- "$scoped_file"
```

Set task-specific `exact_base_ref` and `scoped_file` values before running the snippet.

Never compare branch scope to a stale local branch when its `origin/` ref exists. Preserve
`BASE_REF` exactly as supplied in the report, while recording the resolved `MERGE_BASE`
and `HEAD_SHA` separately. An unknown base ref or failed merge-base calculation is a
verification error; silently choosing a different base would verify the wrong change.

**2. Auto-Detect Scope (default behavior):**

Priority order:
1. **Staged changes exist?** → Scope to staged files only
2. **Unstaged changes exist?** → Scope to modified files only
3. **Branch has commits ahead of base?** → Scope to branch changes
4. **No changes detected?** → Report "nothing to verify" and STOP

**Git Commands for Scope Detection:**

```bash
# Check for staged changes
git diff --cached --name-only

# Check for unstaged changes
git diff --name-only

# Check for branch changes
if git remote get-url origin >/dev/null 2>&1; then
  git fetch origin
fi
BASE_REF=$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null)
if [ -z "$BASE_REF" ]; then
  DEFAULT_BRANCH=$(git remote show origin 2>/dev/null | sed -n 's/^[[:space:]]*HEAD branch: //p')
  if [ -n "$DEFAULT_BRANCH" ] &&
     git rev-parse --verify "origin/$DEFAULT_BRANCH^{commit}" >/dev/null 2>&1; then
    BASE_REF="origin/$DEFAULT_BRANCH"
  elif git rev-parse --verify 'origin/main^{commit}' >/dev/null 2>&1; then
    BASE_REF=origin/main
  elif git rev-parse --verify 'origin/master^{commit}' >/dev/null 2>&1; then
    BASE_REF=origin/master
  elif git rev-parse --verify 'main^{commit}' >/dev/null 2>&1; then
    BASE_REF=main
  elif git rev-parse --verify 'master^{commit}' >/dev/null 2>&1; then
    BASE_REF=master
  else
    echo 'No provider default, origin/main, origin/master, main, or master base exists' >&2
    exit 1
  fi
fi
MERGE_BASE=$(git merge-base "$BASE_REF" HEAD)
git diff --name-only "$MERGE_BASE" HEAD

# Get line ranges for changed files
git diff --cached -U0 -- "$scoped_file"  # staged
git diff -U0 -- "$scoped_file"           # unstaged
git diff -U0 "$MERGE_BASE" HEAD -- "$scoped_file" # branch
```

**3. Build Scope Context:**

Create a list of files in scope with status:
- `file.ts` (modified, lines 45-67, 89-102)
- `new-file.ts` (added, entire file)
- `old-file.ts` (deleted)

Store as `SCOPE_CONTEXT` for passing to agents.

Also build a machine-usable `SCOPE_METADATA` block for agents that need exact diff reconstruction:
- `scope_mode`: `staged`, `unstaged`, `branch`, `files`, `module`, `all`, or the resolved auto-detected mode
- `base_ref`: exact baseline ref/commit used for the scoped diff
- `compare_ref`: exact comparison target (`HEAD`, `INDEX`, `WORKTREE`, or explicit ref)
- `path_filter`: exact scoped paths, or `ALL_SCOPED_FILES`
- `diff_command`: exact git diff command used to define the scope
- `merge_base`: exact merge-base hash for branch scope, otherwise empty
- `head_sha`: full `git rev-parse HEAD` SHA for branch scope, otherwise the full HEAD SHA
  observed when verification began
- `included_files`: ordered file/status records admitted to the scope
- `excluded_files`: ordered path/reason records explicitly excluded; use
  `ALL_OTHER_FILES (outside selected scope)` when paths were not enumerated

With `--since=<sha>`, add three more fields describing what changed since the last
verified head. They are **additive**: every field above keeps its meaning and the full
scoped diff is still what reviewers receive.
- `delta_base_sha`: the `--since` value
- `delta_files`: `git diff --name-status $DELTA_BASE $HEAD`
- `delta_hunks`: the same comparison at `-U0`

`SCOPE_METADATA` is the source of truth for any skill that needs to reconstruct the selected diff.

**4. Format Scope for Agents:**

```
VERIFICATION SCOPE:
Files in scope:
- src/auth/login.ts (modified, lines 45-67, 89-102)
- src/auth/middleware.ts (modified, lines 12-34)
- tests/auth/login.test.ts (added, entire file)

CRITICAL SCOPE CONSTRAINTS:
- ONLY flag issues in code that was ADDED or MODIFIED in these files/lines
- DO NOT flag issues in surrounding context or old code unless it blocks the new changes
- DO NOT flag issues in other files not listed above
- Focus exclusively on the quality of the NEW or CHANGED code

Exception: You MAY flag issues in old code IF:
1. The new changes directly interact with or depend on that old code
2. The old code issue is causing the new code to be incorrect
3. The old code issue creates a blocker for the new functionality

Git commands to see your scoped changes:
git diff HEAD -- <scoped-files>
git diff --cached -- <scoped-files>
```

Example machine-readable metadata:
```text
SCOPE_METADATA:
- scope_mode: unstaged
- base_ref: INDEX
- compare_ref: WORKTREE
- path_filter: src/auth/login.ts,src/auth/middleware.ts
- diff_command: git diff -- src/auth/login.ts src/auth/middleware.ts
- merge_base:
- head_sha: 9d8f...full SHA
- included_files: src/auth/login.ts,src/auth/middleware.ts
- excluded_files: ALL_OTHER_FILES (outside selected scope)
```

## Phase 2: Load Engineer Skill

Many repositories carry an **engineer skill** — a `<repo>-engineer` skill that
documents how to build, test, and run that specific repository. Find it two ways,
in this order:

1. Look among the skills available in this session for one whose name ends in
   `-engineer`. This is the reliable check for whether one exists.
2. Locate its directory on disk, because later phases must hand sub-agents a real
   path — a sub-agent cannot resolve a skill by name, and most harnesses expose
   names rather than paths:

```bash
ls -d .*/skills/*-engineer/ */skills/*-engineer/ 2>/dev/null | grep -v '^\.\./'
```

**If one is available:**
- Read its SKILL.md
- Read key reference files it points to (TESTING.md, architecture docs, etc.)
- Extract: test commands, build commands, linter commands, architecture notes
- Store as `ENGINEER_CONTEXT` — this is pre-verified knowledge from `/setup-engineer`
- Store the directory from step 2 as `ENGINEER_SKILL_DIR`. If the skill is loaded
  but the glob found nothing (it lives outside the repository), say so in the
  report and leave `ENGINEER_SKILL_DIR` empty — sub-agents then fall back to the
  summary in `ENGINEER_CONTEXT` instead of reading the files themselves.
- Check for `VERIFICATION.md` in the same skill directory — if it exists, read it and store as `CUSTOM_GATES`
  - Extract **Exerciser Gates** → will be passed to exerciser in Phase 7c
  - Extract **Review Gates** → will be passed to review agents in Phase 5/6
- The discovery phase (Phase 3) will validate these commands and only discover what's missing

**If there is no engineer skill:**
- `ENGINEER_CONTEXT` is empty
- `CUSTOM_GATES` is empty
- `ENGINEER_SKILL_DIR` is empty
- The discovery phase will do full discovery from scratch

## Phase 3: Discovery & Triage

Launch ONE fast, read-only exploration sub-agent with two jobs in a single prompt.

**At `--depth=light`, send Job A only.** Job B produces two things and light consumes
neither: per-skill file assignments, when the single judgement reviewer wants the whole
scoped diff anyway, and the `ux-reviewer` / `visual-verify` gating decision, when neither
skill is in the light composition and the visual lens gates itself on what the exerciser
actually did. Reading every changed file to produce a routing table nobody reads is exactly
the kind of cost light exists to avoid. Job A stays either way — `static-analysis` and
`tester` need `TOOLCHAIN`.
(On Claude Code that is the `Explore` agent type; any read-only sub-agent will do.)

### Job A: Discover Project Toolchain

**If engineer skill exists:** Validate that the provided commands still work and discover anything missing:
```
The engineer skill provides these commands:
- Test: {test_command}
- Build: {build_command}
- Lint: {lint_commands}

Verify each command exists (which/type check). For any that fail, discover alternatives.
Discover any additional linters/type-checkers not covered by the engineer skill.
```

**If no engineer skill:** Full discovery:
```
Discover the project's toolchain:
- Test command (npm test, pytest, cargo test, go test, etc.)
- Build command (npm run build, cargo build, go build, etc.)
- Linter commands (eslint, pylint, clippy, golangci-lint, etc.)
- Type-checker commands (tsc --noEmit, mypy, etc.)

Check package.json scripts, Makefile targets, CI config, pyproject.toml, Cargo.toml, go.mod.
Output concrete commands that can be executed.
```

### Job B: Triage Changed Files

**Deep depth only** — see the note above Job A.

Triage does two things: assign files to focus each skill, and decide whether the two
user-facing skills apply at all.

```
Read each changed file (not just the extension — look at actual content).

ALWAYS-ON SKILLS — these run on every verification, no exceptions.
Assign each the full scoped file list.

- reviewer          Business logic, architecture, patterns, security, robustness,
                    over-engineering. Needs breadth across the whole diff.
- codex-reviewer    General second-opinion pass, not a specialist router target.
- comment-review    Ephemeral references and history comments appear in any changed file.
- qa                Test coverage assessment for everything that changed.
- tester            Any code change could affect tests.

GATED SKILLS — these run only if the scope contains surfaces they can review.
Decide applies: true/false for each, and state your reason either way.

- ux-reviewer   APPLIES if the scope touches ANY user-facing surface:
                UI components, CLI output or help text, user-facing strings,
                error messages, API response messages, user-visible log output.
                Assign the specific user-facing files.

                SKIP only when ALL of those are absent — pure backend,
                infrastructure, or internal refactoring with no user-visible
                surface whatsoever.

                WHEN IN DOUBT, APPLY. A wasted ux-reviewer run costs tokens;
                a missed one ships a confusing error message to users.

- visual-verify APPLIES if the scope contains UI-rendering files where the change
                affects what a user sees on the rendered page — components
                (.astro, .tsx, .vue, .svelte), pages, layouts, templates, CSS,
                design tokens, public assets the page depends on for rendering.
                Assign those files.

                SKIP if none are present. NOT JSON config, NOT server-side
                handlers without UI side effects, NOT pure copy strings
                (those are ux-reviewer's).

A file can be assigned to multiple skills.

Output a JSON-like mapping:
{
  "skill_assignments": {
    "reviewer": ["all scoped files"],
    "codex-reviewer": ["all scoped files"],
    "comment-review": ["all scoped files"],
    "qa": ["all scoped files"],
    "tester": ["all"],
    "ux-reviewer": ["UI/CLI/user-facing files"],
    "visual-verify": ["UI-rendering files"]
  },
  "gating": {
    "ux-reviewer": { "applies": true,  "reason": "CLI help text modified in src/cli/help.ts" },
    "visual-verify": { "applies": false, "reason": "no UI-rendering files in scope" }
  },
  "toolchain": {
    "test": "npm test",
    "build": "npm run build",
    "lint": ["npx eslint", "npx tsc --noEmit"]
  }
}
```

**Explicit user overrides** force a skip regardless of what triage decided:
- `--skip-ux` → `ux-reviewer` is skipped.
- `--skip-visual` → `visual-verify` is skipped. Use this when you have already eyeballed the rendered change in a browser and want to suppress the duplicate review. Visual review is the design-quality gate, not just a bug check — it asks "would a designer ship this?"

`visual-verify` is also skipped if it is not among the available skills (defensive — it is normally installed globally and always present; a project may shadow it with its own copy to customize capture conventions).

**Output:** Store the skill assignments and gating decisions as `TRIAGE_RESULT` and toolchain commands as `TOOLCHAIN`.

At light, Job B did not run. Set `TRIAGE_RESULT.skill_assignments` to the full scoped file
list for every skill in the light composition, and leave its gating decisions empty. Every
later reference to `TRIAGE_RESULT.skill_assignments` then resolves without branching on depth.

## Phase 4: Static Analysis

Invoke `static-analysis` (a fast, cheap model is enough — it runs commands and reports output) with:
- The scoped file list from Phase 1
- The linter/type-checker commands discovered in Phase 3

```
SCOPED FILES:
{scope file list}

COMMANDS TO RUN:
{each linter/type-checker command from TOOLCHAIN}

Run each command on the scoped files. Parse output into structured findings.
Report only findings in scoped files.
```

Wait for results. Store as `STATIC_SUMMARY`.

## Phase 5: Build Context Bundle

Assemble a compact context bundle (~50-100 lines) for review skills:

```
CONTEXT_BUNDLE:

VERIFICATION SCOPE:
{SCOPE_CONTEXT from Phase 1}

SCOPE METADATA:
{SCOPE_METADATA from Phase 1}

{If --ledger was supplied:}
KNOWN FINDINGS (decided by previous verification runs on this change):

| ID | Location | Title | Root cause | Sev | Disposition | Reason |
|----|----------|-------|------------|-----|-------------|--------|
{one row per ledger finding that is not `fixed`}

DELTA SINCE LAST VERIFIED HEAD: {delta_base_sha}
{delta_files, one per line, with changed line ranges}

{If --epic-context was supplied:}
EPIC CONTEXT:
{the file's contents — completed issues, this issue, remaining issues}

Work whose natural home is one of the remaining issues is deferred, not reported: name it on
a DEFERRED_TO_EPIC line instead of filing a finding. Anything at severity 9-10, anything in
the security class, and any defect this change itself introduces is reported normally
regardless of what the backlog says.

OUTPUT ADDITION:
DEFERRED_TO_EPIC: <issue> — <one sentence>    (or "none")

ENGINEER SKILL SUMMARY:
{Brief summary from ENGINEER_CONTEXT, or "No engineer skill found — toolchain discovered via exploration"}
{If engineer skill exists: "Reference files available at {ENGINEER_SKILL_DIR} — read TESTING.md, ARCHITECTURE.md etc. for your domain"}

STATIC ANALYSIS SUMMARY:
{STATIC_SUMMARY from Phase 4 — just the findings table, not raw output}

TOOLCHAIN:
- Test: {command}
- Build: {command}
- Lint: {commands}

CUSTOM REVIEW GATES:
{Review Gates from VERIFICATION.md, or "None defined"}
These are repo-maintainer-defined requirements. If any rule falls in your review domain,
report PASS/FAIL for it. Failed gates should be reported as severity 9 findings.

DIFF STAT:
{output of: git diff --stat [scope args]}
```

**Keep this compact.** Skills read files themselves — the bundle just tells them where to look and what's already known.

## Phase 6: Launch Review Skills (Parallel)

Invoke the applicable review skills **in parallel** — on Claude Code, a single
message with multiple Skill tool calls.

**If your harness cannot run skills concurrently**, run them one at a time in the
order listed below and concatenate their reports before Phase 7. The pipeline is
correct either way; it is only slower. Do not drop skills to save time — the
report's value comes from the combination.

**If this phase is running one reviewer at a time on a harness that supports
sub-agents, suspect a concurrency cap before suspecting this skill.** Measured on
the Codex CLI: the same fan-out took 359 s of spawn-to-spawn at the default cap
and 43 s once the cap was raised, with no change to these instructions.
**Harness bindings** at the end of this skill records where that cap lives.

### At `--depth=deep`

**Always run, every time:** `reviewer`, `codex-reviewer`, `comment-review`, `qa`, `tester`. Triage focuses these with file assignments but never suppresses them — for correctness-facing review, a missed regression costs more than an extra skill run.

**Run if triage says they apply:** `ux-reviewer` and `visual-verify` (see Phase 3 Job B gating). `--skip-ux` / `--skip-visual` override triage and force a skip.

### At `--depth=light` (default)

**Always run, every time:** `tester`, and exactly **one** judgement reviewer chosen like this:

```bash
which codex
```

On success, invoke `codex-reviewer`. Otherwise invoke `reviewer`. Record which one ran — the
report must say, because a reader cannot otherwise tell what kind of review the change got.

**Never resolve this fallback inside `codex-reviewer`.** That skill's entire value is model
diversity; a substitute review running the same model hidden behind its name would report
`codex-reviewer: COMPLETED` for a pass that had no diversity in it at all. The choice belongs
here, where it is visible in the composition and in the report.

`reviewer` is the more expensive of the two — it is the broadest review in the suite and its
cost tier is the most capable model available. On a machine without Codex, light is one
expensive agent rather than one cheap one. That is still one agent, and it is still the right
substitute: the alternative is a change nobody independently reviewed.

**Never run at light:** `comment-review`, `qa`, `ux-reviewer`, `visual-verify`. These are not
failures or skips-for-cause — they are outside the composition. Phase 7 records them
accordingly.

`exerciser` runs at both depths in Phase 7c, and at light it carries its own visual lens.

**Model routing is the harness's call.** No skill pins a model — the right one
depends on how gnarly the change is, and only the orchestrator knows that. What
each skill needs, as a rough cost signal:

- **Most capable model** — `reviewer`. Comprehensive review across design,
  architecture, coherence, hardening, security, and over-engineering. This is the one
  that benefits most from raw capability.
- **General-purpose model** — `codex-reviewer`, `comment-review`, `qa`,
  `ux-reviewer`, `exerciser`, `visual-verify`. Judgment calls, but bounded ones.
- **Fast, cheap model** — `tester`, `static-analysis`. These run commands and
  report what came back; they are not asked to reason about the result.

**Each skill prompt includes:**
1. The `CONTEXT_BUNDLE` from Phase 5
2. Per-skill file list from `TRIAGE_RESULT.skill_assignments`
3. Static findings relevant to their domain
4. Instruction to read engineer skill reference files for their domain if available
5. Skill-specific instructions (see templates below)

For `codex-reviewer`, `SCOPE_METADATA` is authoritative. It must not infer scope mode from filenames or prose when exact metadata is available.

### Carry-forward rules (only when `--ledger` was supplied without `--no-carry-forward`)

Append this to the prompt of the **judgment** reviewers — `reviewer`, `codex-reviewer`,
`qa`, `comment-review`, `ux-reviewer`, `visual-verify`. Never to `tester`,
`static-analysis`, or `exerciser`: those run commands and report what came back, and a
machine does not drift the way a fresh reader does.

```
CARRY-FORWARD RULES

Tier A — regression lens, full scoped diff, always. You have the whole branch diff and you
review all of it for anything the DELTA broke. A defect the delta introduced or made
reachable is reportable at full severity even at a location listed in KNOWN FINDINGS: an
accepted finding covers the exact state its root cause describes, never the file, the
function, or the subsystem.

Tier B — new audit territory. Do not open a new line of inquiry into code that is unchanged
since DELTA BASE and that KNOWN FINDINGS does not mention. Previous rounds already reviewed
that code against this threshold and dispositioned what they found. Widening the audit a
little further each round is how a loop that was one round from done ends up reporting on a
different subsystem every time.

Matching a known finding:
- Matches a KNOWN finding AND the code there is unchanged since DELTA BASE
  -> do not report it as a finding. List it under REAFFIRMED with the known ID and one sentence.
- Matches a KNOWN finding BUT the code there changed in the delta
  -> report it normally, at full severity. It is a new finding about new code.
- Has new evidence of concrete harm — a failing test, a reproduction, an exploit path
  -> report it normally AND list it under REOPENED with that evidence.

Never suppressible, whatever KNOWN FINDINGS says: anything at severity 9 or 10, and
anything in the security class.

OUTPUT ADDITIONS:
REAFFIRMED: <known id> — <one sentence>    (or "none")
REOPENED:   <known id> — <harm evidence>   (or "none")
```

**The orchestrator never filters.** Do not drop a reported finding because it matches the
ledger. Suppression is a judgement a reviewer makes with the whole diff in front of it, and
every instance of it is visible as a `REAFFIRMED` row. A quiet string-match filter in the
pipeline would be undetectable exactly when it was wrong.

### Skill Prompt Templates

**For each skill, the prompt follows this structure:**

```
{CONTEXT_BUNDLE}

YOUR ASSIGNED FILES:
{files from TRIAGE_RESULT.skill_assignments for this skill}

RELEVANT STATIC FINDINGS:
{filtered findings from STATIC_SUMMARY relevant to this skill's domain}

{If engineer skill exists:}
ENGINEER SKILL REFERENCE:
Reference files are available at {ENGINEER_SKILL_DIR}
Read files relevant to your domain (e.g., TESTING.md for tester, architecture docs for reviewer).

{Skill-specific instructions...}

OUTPUT FORMAT: For each issue found, provide:
- Title (short description)
- Severity (1-10, where 1=trivial, 10=critical)
- Location (file:line)
- Description (what the issue is and why it matters)
```

**Skill-specific instruction blocks:**

**reviewer:**
```
Comprehensive review across all five dimensions:
1. Design & Code Quality: design adherence, over-engineering, AI slop, test integrity, structural completeness
2. Architecture: module boundaries, dependency direction, god objects, abstraction opportunities, coupling
3. Coherence: reinvented wheels, pattern violations, convention mismatches, documentation drift, dead code
4. Hardening: invalid inputs, error paths, inconsistent validation, orphaned references, state transitions
5. Security: injection, auth/authz, multi-tenant isolation, data exposure, crypto

Research the project's structure, patterns, and security approach BEFORE evaluating changes.
Focus on: 'Is this change well-designed, structurally sound, pattern-consistent, robust, and secure?'

Work the over-engineering lens in Dimension 1 deliberately — it is easy to review only
for what is missing and never for what should be cut. Tag those findings
(delete / stdlib / native / yagni / shrink), verify each proposed replacement actually
exists before reporting it, and close the report with the net-lines metric.
```

**codex-reviewer:**
```
Run the local Codex CLI as an independent second-opinion reviewer.
Shell out to the local Codex CLI — the point is a second engine's opinion, not your own analysis again.
Follow the codex-reviewer skill's own execution procedure exactly; it owns the invocation, the completion detection, and the timeout. Do not substitute your own — this prompt is scope and context, not a runbook.
Use `SCOPE_METADATA` as the source of truth for scope reconstruction.
Adapt the verify scope into a temporary diff-only workspace under /tmp so Codex reviews only the intended changes.
Do NOT infer staged vs unstaged vs branch vs path-filtered scope from assigned files or prose if `SCOPE_METADATA` says otherwise.
If exact reconstruction from `SCOPE_METADATA` is not possible, report PATCH_CONSTRUCTION_FAILED instead of reviewing an approximate diff.
If Codex is unavailable (missing CLI, auth missing, network blocked, sandbox blocked), report BLOCKED status with a short factual reason.
If scope is `--scope=all`, report SKIPPED_UNSUPPORTED_SCOPE rather than attempting a whole-codebase audit.
Normalize Codex output into: title, severity, location, description.
```

**comment-review:**
```
Review ONLY comments and docstrings added or modified in the scoped diff
(plus pre-existing comments the changes make stale).
Flag: ephemeral review-ID references (VI-N, CI-N, "per review feedback"),
historical change-narration ("previously", "now we", "replaced X with Y"),
stale comments contradicting the code, reviewer-appeasement, and redundant restatement.
Comments must describe the current code and its intent — git owns history.
Code correctness, design, and prose docs are out of scope — other skills own those.
Normalize findings into: title, severity (floor 5, cap 6 — the floor is deliberate),
location, category (tag), description including a concrete rewrite (or "delete").
If comments are clean, report COMPLETED with zero findings.
```

**tester:**
```
Run the full test suite using: {test command from TOOLCHAIN}
Report exact pass/fail counts.
If tests cannot run, report what prevented execution.
For EACH failure: title, severity, location, error message, and whether it's IN-SCOPE or OUT-OF-SCOPE.
```

**ux-reviewer:**
```
ONLY test user-facing changes in the scoped files.
Do not audit the entire UI/CLI for issues.
Focus on the UX of what changed in this scope.
Test any UI, CLI output, error messages, or API responses that were modified.
```

**qa:**
```
Evaluate whether the scoped changes are adequately tested.
Assess test quality, mock usage, and test type appropriateness.
Adapt expectations to codebase testing maturity.
Focus on: 'Are these changes well-tested with good tests?'
```

**visual-verify:** *(only invoked if the skill is present in the available skills list and `--skip-visual` was not passed)*
```
Read the engineer skill for screenshot mechanics (wrapper command, dev URLs, viewport conventions) — typically VISUAL.md or the engineer SKILL.md.
For each UI-rendering file in YOUR ASSIGNED FILES, identify at least one route that renders it (grep imports for components; page files render directly).
Take screenshots at desktop (1440-wide, full-page) AND mobile (390-wide, full-page). If responsive sizing changed, sample boundary widths too.
Open every PNG with `Read` and apply the holistic articulation step described in the skill: write 2-3 sentences describing the composition in designer's terms (balance, weight, hierarchy, rhythm, alignment, density, color, typography). Anything in that paragraph that reads as a complaint becomes a finding.
Focus on: 'Would a designer ship this?' — not 'is anything imperfect?'
If no UI-rendering files in scope or no running app discoverable, return STATUS: SKIPPED with a one-line factual reason — same convention as codex-reviewer.
Output structured findings (Title / Severity / Location / Category / Description) per the skill's reviewer-mode protocol.
```

### Plan Completeness Check (Parallel, Conditional)

This check runs in parallel with the review skills above. It has no dependency on their output — it only needs the plan and the branch diff.

**If a plan was resolved** (via `--plan-file` flag or discovered from conversation context):

Invoke a general-purpose agent with:

```
PLAN FILE:
{plan file contents}

BRANCH DIFF:
{output of the diff command from SCOPE_METADATA, or `git diff {base}...HEAD`}

SCOPE CONTEXT:
{SCOPE_CONTEXT from Phase 1}

You are checking whether the implementation is complete relative to the plan.

Instructions:
- Identify all phases, sections, and steps described in the plan
- For each, determine whether the branch diff contains changes that implement it
- A phase is "addressed" if the diff contains changes that clearly correspond to its requirements
- If significant portions of the plan are unimplemented, emit a single finding:
  - Title: "Plan incomplete — only phase N of M implemented" (or similar descriptive summary)
  - Severity: 8
  - Description: List which phases/sections are implemented and which are missing, with brief reasoning
  - Sources: ["plan-completeness"]
  - Location: null (omit)
- If the plan appears fully implemented, emit NO finding (do not emit a success finding)

OUTPUT FORMAT: For each issue found, provide:
- Title (short description)
- Severity (1-10, where 1=trivial, 10=critical)
- Location (file:line or null)
- Description (what the issue is and why it matters)
```

**If no plan was resolved** (no flag, nothing in context): skip this check entirely, no finding emitted.

## Phase 7: Collect Results + Conditional Agents

### 7a. Wait for all Phase 6 skills to complete

This is one wait covering the whole set, not one wait per skill.

**A skill that produced no result is `NOT_RUN`, never `COMPLETED`.** If it returned
empty, hit a provider quota or rate limit, timed out, or was refused a start,
record `NOT_RUN` with the observed reason. A review that never ran and a review
that found nothing are opposite outcomes, and only one of them is good news —
filing the first as the second turns a hole in the pipeline into a clean bill of
health.

**A skill outside the current depth's composition is `SKIPPED`, never `NOT_RUN`.** Record it
as `SKIPPED` with the reason `not in light composition`. `NOT_RUN` has to keep meaning "this
should have run and did not" — that is the entire point of the rule above, and reusing it for
"this was never part of the plan" would hide a real hole behind an expected one. Running the reviewers concurrently makes quota refusals more likely, not
less, so this distinction earns its keep.

Collect structured findings from each skill. Extract ONLY:
- Title
- Severity (1-10)
- Location (file:line)
- Category (skill-specific)
- Description

**Discard investigation narratives.** Keep the orchestrator context lean.

For `codex-reviewer`, also collect skill status if no findings were produced:
- `COMPLETED`
- `BLOCKED`
- `SKIPPED_UNSUPPORTED_SCOPE`

For `visual-verify`, also collect skill status if no findings were produced:
- `COMPLETED`
- `SKIPPED` with reason (`NO_UI_FILES_IN_SCOPE`, `NO_RUNNING_APP_DISCOVERABLE`, `NO_SCREENSHOT_MECHANISM`). SKIPPED is expected behavior, not a warning — it just means there was nothing visual in scope.

**Codex BLOCKED handling:** If `codex-reviewer` reports BLOCKED, this is a significant event — the independent second-model review did not run. Flag it prominently in the report:
- In `report-only` mode: Include BLOCKED status with high visibility in the Agent Results Summary and add a prominent warning after the summary table.
- In `interactive` mode: Use `AskUserQuestion` to ask: "Codex review was BLOCKED ({reason}). Continue without Codex review, or stop to resolve?"
- `SKIPPED_UNSUPPORTED_SCOPE` is expected for `--scope=all` and is not flagged as a warning.

### 7b. Conditional: debugger

If tester OR exerciser — or, at deep, ux-reviewer — reported failures (severity 7+):

`debugger` runs at both depths. It is a diagnostic that fires on an observed failure, not a
reviewer that goes looking for one, so it cannot widen an audit the way a judgement skill can.
Callers depend on it: `player-coach` can only open a quarantine entry on a debugger
determination, so dropping it at light would silently disable quarantine.

Invoke `debugger` with:
```
VERIFICATION SCOPE CONTEXT:
{SCOPE_CONTEXT}

FAILURES TO INVESTIGATE:
{list of failures from tester/ux/exerciser}

Analyze the root cause of these failures.
Focus on failures caused by the scoped changes.
If failures are unrelated to scope, note that explicitly.

For each failure, determine whether the scoped changes caused it or whether it was already
there. Check out {scope.mergeBase} in a scratch worktree and run the same command against it.
Report, per failure:
  PRE_EXISTING: yes | no | undetermined
  COMMAND: <the exact command you ran>
  EVIDENCE: <output excerpt showing the same failure signature at the merge base>
  TOUCHED_BY_DIFF: <paths in the failure's stack that appear in the scoped diff, or none>
```

That extra suite run costs one execution per distinct failure signature, once. It buys back
far more than it costs: without it, an environment failure that has nothing to do with the
change gets re-diagnosed from scratch on every round it appears in, at full suite price
each time, and gets re-reported at a severity that dominates whatever the round was
actually about.

### 7c. Always: exerciser with issue verification

Runs **after** the Phase 6 reviews, not in parallel with them — it needs the collected issue list as input.

Invoke `exerciser` with:
```
{CONTEXT_BUNDLE}

Exercise the changes end-to-end:
1. Read the engineer skill at {ENGINEER_SKILL_DIR} if one was found — follow its instructions for starting the environment, authenticating, and interacting with services
2. Start the full local environment (app + all backing services)
3. Determine exercise strategy based on change type:
   - Frontend/UI changes → use Playwright to navigate and interact
   - API/backend changes → make actual A

…(truncated)
