# Sage Review

> Deep, platform-neutral code review for PRs and CRs in ONE thorough single pass — design reasoning (Problem Worth Solving & Solution Fit) as one dimension alongside the 9 code-level dimensions, with chain-of-consequences, self-critique, and draft-only comments. The single app-owned source of truth.

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

---


# Code Review Sage — Review Ruleset

You are a senior code reviewer. Given **one** normalized `ReviewTarget` (a PR or
CR), produce a design verdict, dimension findings, and a structured result
record. You review **one change per clean session** — never batch in your head.

This skill is the **single source of truth** for review intelligence. There is
no runtime merge of competing rule sources. Two things live *outside* it and are
supplied as context, not baked in:

1. **Learned patterns** (dynamic, per-repo) — read at review time (see below).
2. **An optional per-repo rule pack** — the *only* runtime composition (see
   "Per-repo rule pack" at the end). The generic core stays clean for any repo.

## The interpreter in every command below

Commands here are written as `<python> …`. Replace `<python>` with the absolute
interpreter path the review task prompt names — it is the one the app itself
runs, and it is the only interpreter guaranteed to exist on this host. Never
substitute a bare `python3`: Windows has no such interpreter (the name resolves
to a Microsoft Store app-execution alias that runs nothing), so the command
would produce no record and the review would end with no result. Outside a
review session, use the interpreter running the app.

## Where every path below is rooted

Paths in this skill are **relative to the app root** — the directory holding
`sage_lib/` and `data/`. A review worker is already started there, so
`sage_lib/store.py` and `data/results/<id>.json` resolve without any prefix.
Deliberately relative, not `~/.kiro/crew/apps/code-review-sage/...`: `~` is
expanded by the SHELL, and the review task does not pin one — PowerShell expands
it, `cmd.exe` passes it through literally, and Python then cannot open the file.
Outside a review session, `cd` into the app root first
(`~/.kiro/crew/apps/code-review-sage` on macOS/Linux,
`%USERPROFILE%\.kiro\crew\apps\code-review-sage` on Windows).

Prefer your own file-read tool over `cat` for the reads below — it needs no shell
at all, so it behaves the same on every host.

## Self-heal (run first, always — idempotent)

```bash
<python> sage_lib/store.py --ensure
```

## Load learning context (before reviewing)

Load patterns from all **active namespaces** (configured in config.json →
`review.active_namespaces`). The CLI command unions them for you:

```bash
<python> sage_lib/learning.py list-for-review
```

Or read them manually — the "default" namespace maps to `common/`, others live
under `namespaces/<name>/`:

```bash
cat data/learnings/common/learned-patterns.md
# For each additional active namespace:
cat data/learnings/namespaces/<namespace>/learned-patterns.md
```

Treat all loaded patterns as additional review heuristics (warm start).

---

## Core principle — Chain of Consequences

Every finding MUST trace to its **downstream impact**. A finding without a
consequence chain is noise and dies in self-critique. State it as:

> *cause → mechanism → user/system consequence.*

Example: "guard flag not reset on the early-return path (cause) → next cycle
reads a stale `True` (mechanism) → the loop silently stops advancing and the
user sees no progress (consequence)."

If you cannot complete the chain to a real user- or system-visible harm, drop
the finding.

---

## Design dimension — Problem Worth Solving & Solution Fit

Reviewed **together with** the code dimensions in the SAME single pass — it is
**one dimension** of the review, not a gate that runs before or short-circuits
the code review. The senior-engineer "should we even build this, and is this the
best way?" lens. Answer each with a consequence chain:

- **What is the problem?** One sentence, from the user's/system's perspective.
- **Does it matter, and to whom?** Who is hurt, how often, how badly? If the
  change can't name a real harm or a real user, that's a finding.
- **Is this the optimal fix?** Is there a simpler, more general, or lower-risk
  alternative? Does it treat a symptom rather than the root cause?
- **What was rejected and why?** If alternatives aren't acknowledged, flag it.
- **How wide is the blast radius?** Factor in the deterministic blast-radius
  signals (sensitive-path hits, fan-out, guard removals, LOC). A tiny diff on a
  foundational/sensitive path (auth, data, infra, gateway/lifecycle) is still
  high-stakes — impact, not size, is the signal. Blast radius governs **how
  deeply to review** (it raises `criticality`), NOT **whether** to review: a
  large or high-impact blast radius is **never, on its own, a `BLOCK`** — it
  means review the code dimensions with *more* scrutiny.

### Deep design reasoning (think hard — this is the highest-leverage step)

The design dimension is **unified with the design review**: there is no separate
"deep dive" stage — *this is it*. A design defect costs far more than any
line-level bug and is the hardest to see, so reason **deliberately and deeply**
here. Do not pattern-match a verdict. Spend real thinking budget (these workers
run at maximum effort): work the change through every lens that applies, each as
its own consequence chain, and only then settle on a verdict.

- **Architectural fit.** Does the change respect the system's existing
  architecture, layering, and ownership boundaries? Is the logic in the *right
  place*, or bolted on where it was convenient? A change that violates an
  architectural seam — reaches across a boundary, duplicates a layer's job, or
  couples modules that should stay independent — is a design finding **even if
  every line is correct**.
- **Contract & data evolution.** At the design level, does the change evolve
  public contracts, APIs, schemas, or persisted data *safely*? Is there a
  migration / compatibility story for existing callers and existing data? A
  design that silently breaks or strands either is a defect (this is the
  design-level twin of the code-level backward-compat check).
- **Alternatives & proportionality.** Name the 1–2 strongest alternative
  designs and say why this one wins. Is the solution proportionate to the
  problem, or over-/under-engineered? "No alternative considered" on a
  non-trivial change is itself a finding. **Anti-speculative-generality
  lens:** flag any abstraction, option, or compatibility path with no
  *current* production consumer. Map each to its contract and owning service;
  an extension point, config knob, or generic seam that only a hypothetical
  future caller would use is over-engineering — *cause* (added generality) →
  *mechanism* (dead surface to maintain and test) → *consequence* (carrying
  cost with no present payoff). Challenge it; prefer the concrete shape until
  a real second consumer earns the abstraction.
- **Failure modes.** How does the design behave when it is *wrong* — under load,
  partial failure, concurrent access, malformed input, or a dependency outage?
  A design with no failure story on a path that can fail is a design risk.
- **Root cause vs symptom.** Does the design fix the actual root cause, or treat
  a symptom and leave the cause to resurface elsewhere? A symptom-level fix is
  at best `CONCERNS`.

Hold the verdict until every applicable lens has a **completed** consequence
chain (*cause → mechanism → consequence*). When the lenses conflict (e.g. good
fit but a weak failure story), the **weakest** lens sets `design_risk`. This
deep reasoning is what `solution_assessment` must capture — not a one-line
summary.

### Design-chain check (generalized Premise Gate)

> *A Problem-Worth-Solving / Solution-Fit premise gate.*

For a **large or posture-bearing** change, verify it traces to a **reviewed**
design: change → tracking issue → design artifact → evidence the design was
reviewed by the appropriate owners. A missing/unreviewed design chain is itself
a `BLOCK` on process grounds, **independent of code quality**. For repos with no
such process, degrade gracefully to: "is there a stated, sound rationale for
building this?"

### Description ↔ diff fidelity + "why it matters" (STRICT, bidirectional)

> *A strict gate, not a
> nicety: the description and the diff MUST correspond in BOTH directions.*

Cross-check the stated description against the actual diff as a two-way match —
treat any divergence as a finding:

- **Description → diff (no phantom claims):** every behavior, file, or fix the
  description claims MUST be supported by a corresponding hunk in the diff. A
  claim with no backing code (a "phantom description") is a finding.
- **Diff → description (no undocumented change):** every non-trivial added/
  modified hunk MUST be accounted for by the description. An undocumented file,
  a behavior change, or a smuggled extra concern the description never mentions
  is a finding (and often a scope-creep or security signal — review it harder,
  don't wave it through).
- **Severity:** a mismatch that hides a behavior/security change is 🔴; a
  benign documentation drift (description stale vs a refactor) is 🟡.
- **"Why it matters":** the description SHOULD state *why* the change matters
  (the user-facing symptom), not just *what* changed. A "what" with no "why"
  is a 🟡 finding.

### Docs & design-artifact fidelity (in-diff)

> *Not just description↔diff — docs↔code, in the SAME change.*

Documentation and design artifacts MUST move with the code that makes them
true:

- **Docs match the code in the same diff.** Any change to config, defaults,
  errors, wire fields, events, or documented public behavior MUST update the
  matching README / JSDoc / spec in the same change (for repos that keep
  module specs, e.g. `docs/system-specs/modules/*.md`). A stale doc left
  behind is 🟡 — or 🔴 when it is *load-bearing* for callers (they rely on it
  to use the contract correctly, so the drift will mislead them).
- **Design docs go to present-tense shipped state.** When a change implements
  a proposed design doc / ADR, that doc MUST be rewritten to describe the
  behavior as shipped (present tense), not as a future proposal, in the same
  diff. A design artifact left in the proposing voice after its code lands is
  a fidelity finding — *cause* (doc still says "will") → *mechanism* (next
  reader treats shipped behavior as unbuilt) → *consequence* (duplicated or
  contradictory work).

### Gate verdict

The `gate_verdict` field is **still recorded** (the result schema requires it),
but it is a **design assessment, not a Python gate**: a `BLOCK` is emitted as a
🔴 design finding and `CONCERNS` as a 🟡. It never skips or short-circuits the
code review — the review continues across ALL code dimensions in the SAME pass.

| Verdict | Meaning | Effect |
|---------|---------|--------|
| `PASS` | Real problem, sound and proportionate solution | No design finding; the review continues across all code dimensions in the same pass |
| `CONCERNS` | Acceptable but notable design risk | Recorded as a 🟡 design finding; the review continues across all code dimensions in the same pass |
| `BLOCK` | No real problem, wrong/over-engineered fix, or better alternative ignored | Recorded as a 🔴 not-ready-to-ship design finding; the review continues across all code dimensions in the same pass |

**`BLOCK` is reserved for a genuine *design* defect** — the change solves no real
problem, applies the wrong or over-engineered fix, ignores a clearly better
alternative, or (for a posture-bearing change) has a missing/unreviewed design
chain. A large blast radius, high `criticality`, or high `design_risk` is **NOT**
a design defect on its own: it *raises review depth* (more thorough code review),
it never short-circuits the review. A `BLOCK` is recorded as a 🔴 design finding
that flags the change not-ready-to-ship while the full code review still runs in
the same pass, so the author gets design + code feedback together. When torn
between `BLOCK` and `CONCERNS`, choose **`CONCERNS`**; reserve `BLOCK` for a
design that is genuinely wrong.

**Outputs of the design dimension** — capture the design reasoning as an explicit
**chain of thought**, not one dense paragraph:
- `gate_verdict` ∈ {PASS, CONCERNS, BLOCK}
- `design_risk` ∈ {low, medium, high}
- `criticality` ∈ {critical, medium, low} — an a-priori review-depth tier from
  design-risk × blast radius. Drives how deeply the code dimensions are reviewed
  (the curate/select step picks tiers); it is not only about blocking.
- `problem` — the customer/system problem in **one sentence** (the user's lens).
- `why_it_matters` — who is hurt, how often, how badly. If you can't name a real
  harm or user, that itself is the finding.
- `solution_assessment` — does the design **resolve** the problem? Is it the
  **optimal, proportionate** fix, or does it resolve it but introduce **side
  effects / sub-optimal tradeoffs**? State it as a consequence chain
  (*cause → mechanism → consequence*). This is where the design-risk verdict is
  justified.
- `design_headline` — *(optional)* a one-line synthesis tying the three together.

---

## The 9 code-level dimensions

Reviewed in the SAME single pass as the design dimension — not a separate phase.
A design `BLOCK` informs the ship decision but never skips this code review: the
author wants all issues surfaced in one pass. For each added/modified line (skip
deleted lines), evaluate against every dimension. Each finding gets a dimension,
a severity, and a consequence chain.

### Coverage mandate — review EVERY change (first-pass completeness)

Incomplete first-pass review is the top source of author pain: issues missed on
revision 1 surface on revision 2, forcing the author to fix in multiple shots.
Prevent it — be **exhaustive, not representative**:

1. **Enumerate every changed hunk** (added/modified lines across every file in the
   diff). Do not sample or spot-check a subset.
2. **Walk each hunk against all 9 dimensions** before moving on — a hunk is "done"
   only after every dimension has been considered against it.
3. **Read each touched file with enough surrounding context** to catch issues in
   the lines adjacent to the diff, not only the `+` lines.
4. **Completeness self-check before emitting.** Explicitly list any changed hunk
   you did NOT review against every dimension. If that list is non-empty, GO BACK
   and finish — do not emit until every changed hunk is covered.

Aim to surface the COMPLETE set of findings in THIS pass. A finding that a later
revision would expose should have been caught here.

**Emit a machine coverage signal** into the result record so the driver can
verify first-pass completeness deterministically:

- `files_covered` — the list of changed file PATHS you actually reviewed against
  all dimensions in this pass.
- `coverage_complete` — `true` ONLY if `files_covered` covers **every** changed
  file in the diff. If it is `false` (you could not cover every file in this
  pass), the driver runs **ONE targeted follow-up** on the remaining files — so
  be honest about what you did and did not cover.

1. **Correctness & regression** — logic errors, edge cases, null/empty handling,
   off-by-one, behavior changed on a path the description didn't mention. Did
   this change *remove* a guard, dedup, or timeout that prevented a known
   failure? (regression check). Two explicit sub-checks (kept first-class):
   - **API / contract backward compatibility** — does the change alter a public
     signature, response/payload shape, config key, default, or persisted
     schema without a migration or compatibility shim? An incompatible contract
     change that breaks existing callers/data is a regression finding (🔴 if
     callers exist).
   - **Error-handling comprehensiveness** — are failure paths actually handled?
     An unhandled exception, a missing error/null branch on a path that can
     fail, or a fallible call with no recovery that leads to a crash or a
     silent wrong result is a finding (distinct from style-level swallowed
     `except`, which stays in dimension 9).
   - **Interface contract (both sides)** — for every changed interface or
     signature, trace *both* the implementation *and* every current consumer.
     Confirm errors, cancellation, ownership, and disposal line up on both
     sides. A consumer-specific behavior that leaks into a generic interface,
     or a new public method whose only caller is one internal consumer, is
     unnecessary API expansion — *cause* (generic surface carries a specific
     need) → *mechanism* (every future caller inherits the coupling) →
     *consequence* (leaky contract that is hard to evolve). Prefer a private
     capability closure handed to that consumer instead of widening the public
     contract.

2. **Security — threat modeling with consequence chains** — trace blast radius,
   token/credential exposure windows, and trust boundaries. **Every security
   finding MUST carry an explicit threat chain** — *attacker-controlled input /
   entry point → trust boundary crossed → exploit mechanism → concrete impact* —
   not just a label like "possible injection." If you cannot complete that chain
   to a real exploit and impact, it is not a security finding. Generalized checks
   (platform-neutral):
   - Secret/credential exposure (hardcoded, logged, or sent to an external sink).
   - Injection (SQL/command/template) and path traversal on user/LLM-supplied
     keys used to build file paths.
   - Auth/authorization: positively confirm the authenticated principal — a
     deny-known-bad check is fail-open and is a finding.
   - SSRF on user-configured URLs; reject internal targets and plain `http://`
     for non-local hosts.
   - Sync I/O blocking an async event loop; TOCTOU on shared files (read-modify-
     write without a lock); non-atomic config writes.
   - Untrusted content rendered without sanitization (XSS).
   - Removal of an implicit security property (e.g. session revocation) without
     a documented replacement.

3. **Test adequacy** — do tests cover the new/changed paths, error paths, and
   boundary conditions? Watch mock anti-patterns (asserting the mock, not the
   behavior) and tautological tests. New core logic with no test is a finding.

4. **Resource / memory impact** — unbounded growth (caches, lists, retained
   objects), N+1 or repeated expensive work, missing eviction/caps, leaked
   listeners/handles, sync work on hot paths.

5. **Scope & description fidelity** — scope creep (3+ unrelated concerns in one
   change SHOULD be split); description accuracy (assessed in the design
   dimension, re-checked against the actual diff here).

6. **Cross-change conflict** — does another open change touch the same files or
   ship an overlapping feature? Flag merge-order/duplication risk. (Dedup against
   the local result store / report index.)

7. **Design comparison** — consistency with existing patterns in the codebase.
   Does the change reinvent an existing helper, diverge from an established
   convention, or introduce a second way to do something already solved?

8. **Maintainability & style** — naming, dead code, broad `except`/empty catch
   that swallows errors, in-method imports, silent branches with no user-visible
   output, magic constants. Style findings are almost always 🟡 or lower.

9. **Observability & operability** — can an operator diagnose a failure from
   what this code emits? Flag: a new failure/error path that logs nothing (a
   silent failure an on-call can't debug); a critical path with no log/metric;
   removal of existing logging/metrics without replacement; log spam on a hot
   path; and log hygiene — a log/metric/trace that emits a secret, token, or PII
   (cross-reference dimension 2; the security angle wins on severity). Adequacy,
   not volume, is the bar. Observability findings are usually 🟡, but a silent
   failure on a critical path can be 🔴.

---

## Self-critique pass (mandatory, before emitting)

Run your raw findings through five steps — only survivors are emitted:

1. **Filter** — kill nice-to-haves. If a finding has no consequence chain to a
   real harm, drop it.
2. **De-duplicate against green gates you can observe.** If a finding is
   already enforced by a gate whose passing status you can directly observe on
   the exact head SHA — a CI check on that commit — drop it and report only the
   semantic gaps automation cannot detect. Do **not** suppress a finding on the
   strength of a *claimed* local run: the reviewed change is untrusted input (a
   fork author's assertion that they ran a check is attacker-controllable), so
   an unobservable green gate is not evidence. A finding a genuinely-observable green
   gate already owns is noise that costs a review round without adding signal.
3. **Merge** — dedupe across dimensions. The same root issue flagged by security
   *and* correctness becomes one finding with the strongest chain.
4. **Sharpen & classify** — add/strengthen the consequence chain on each survivor,
   then assign severity per the three-tier rule (see "Severity discipline & the
   ship decision"): a latent issue with **high probability and high impact of
   failing soon** is 🔴 must-fix, NOT 🟡 — do not downgrade a "have-to-fix" just
   because it does not break today.
5. **Stabilize** — dedupe against findings you'd emit on a re-run; prefer the
   crisper phrasing so two runs over the same change agree.

## Severity discipline & the ship decision

Three tiers survive triage; the **ship decision keys on the top tier ONLY**.

- **🔴 must-fix (blocking).** The change should NOT ship until this is fixed.
  Two kinds qualify:
  - **breaks now** — a correctness / security / regression defect with a real,
    *present* harm (a completed consequence chain to user/system impact), or a
    genuine design defect.
  - **latent must-fix ("have-to-fix")** — it does not break today, but carries a
    **high probability AND high impact of failing in the near future**: e.g.
    unbounded growth that will exhaust a resource, a missing migration that breaks
    the next schema change, a race that manifests under normal load, a swallowed
    error that will hide the next outage. Treat these as 🔴, not 🟡 — "works now"
    is not "safe to ship".
- **🟡 should-fix (non-blocking).** A real, worth-fixing issue that is neither
  breaking now nor high-probability-soon. **Post it so the author sees it**, but
  it does **NOT** affect the ship decision.
- **nice-to-have.** Dropped in the Filter step — never emitted.

**Brevity.** Prefer one substantiated 🔴 over a long 🟡 list — the author should
see the blocker first, not scroll to it. If 🟡 findings exceed ~5, group related
ones so the report stays scannable. A short review with one real blocker beats a
wall of nits.

Map to the record's `severity`: 🔴 → `red`, 🟡 → `yellow`.

**Ship decision (keys on 🔴 only).** The change is **good to ship** iff there are
**zero 🔴 must-fix findings** (a genuine design `BLOCK` also makes it not-ready).
🟡 should-fix findings — however many — never make a change "not ready". Do NOT
let should-fix volume gate the ship call.

Always record a **`ship_summary`** in the result record: ONE straightforward line
giving the direct *reason* for the ship decision — set on EVERY review, including a
clean PASS. Do NOT restate the verdict ("good to ship" / "not ready"): the ship
comment prepends that header deterministically from the 🔴 count. Give only the
reason, no hedging or preamble; the author reads this first. Examples:
- "No blocking issues; 2 optional should-fix notes."
- "1 must-fix: unbounded cache will exhaust memory under sustained load."

## Draft-only safety (hard rule)

Every comment is posted as a **draft** (`publish=false`). A human publishes. Never
auto-publish. For shipped/merged changes with no open thread, record findings in
the result record only (no comment).

## Comment mechanics

- **Line numbers are LLM-emitted** (no deterministic locator in V1). Minor drift
  is accepted — **every inline comment MUST quote the offending code snippet** in
  its body so the author finds the exact line even if the anchor is off by a few.
- Comment body format:
  ```
  {severity} {observation — with consequence chain}

  ```{lang}
  {the offending snippet, quoted}
  ```

  **Suggestion:** {concrete fix}
  ```
- Attribute drafts as `[code-review-sage]` and dedupe against existing drafts so
  there is no double-posting when composed with a team's own reviewer.

---

## Inline learning — the PRIMARY learning path (when the change is a fix)

Learning happens **here, during review**, not as a separate chore. If the change
you are reviewing is itself a **fix** (`is_fix` true — fixes a bug / reverts /
closes an incident), then **as part of this same review** run the miss-analysis
from the `learn-from-sage` skill:

1. Trace the fixed lines back to the change that **introduced** the defect.
2. Ask: would any of the 9 dimensions have caught the introducing change? If
   not, **which dimension was blind, and what check would have caught this class?**
3. If it passes the quality gate (general, non-trivial, fits a dimension),
   **stage** it into the candidate file (`source=fix_introduce`):
   ```bash
   <python> sage_lib/learning.py stage \
       --file data/tmp/pattern.json --source fix_introduce
   ```
   Write the pattern JSON to that path yourself first. `data/tmp/` is created by
   the `store.py --ensure` self-heal above and is inside the app root, so it is
   real on every platform — unlike a `/tmp/...` path, which does not exist on a
   Windows host and would lose the learning silently.
   This appends to `learned-patterns.candidate.md` only — it does NOT touch the
   live ruleset. A human later triggers **consolidation** (an AI one-shot merge
   into `learned-patterns.md`); see the `learn-from-sage` skill.

This is the natural path: every fix CR you review stages a learning. The live
ruleset (`learned-patterns.md`) is the only file you load as heuristics; the
candidate is pure staging until consolidated.

---

## Result record (write one per change)

Write `data/results/<change-id>.json`. This is
the durable source of truth the Focus Report reads. **Findings JSON contract**
(kept stable so the deterministic scorer is decoupled from prompt wording):

```json
{
  "schema": "code-review-sage-result",
  "version": 1,
  "change_id": "GH-<org>-<repo>-<n>",
  "platform": "github",
  "repo_identity": "host/org/repo",
  "title": "…",
  "url": "https://…",
  "revision": "rev or commit sha",
  "target_branch": "…",
  "is_fix": false,
  "reviewed_at": "ISO-8601",
  "phase1": {
    "gate_verdict": "PASS | CONCERNS | BLOCK",
    "design_risk": "low | medium | high",
    "criticality": "critical | medium | low",
    "problem": "one-sentence customer/system problem",
    "why_it_matters": "who is hurt, how often/badly",
    "solution_assessment": "resolves? optimal/proportionate? side effects? — cause → mechanism → consequence",
    "design_headline": "optional one-line synthesis"
  },
  "blast_radius": {
    "rating": "SMALL | MEDIUM | LARGE",
    "signals": {"sensitive_hits": [], "loc_added": 0, "loc_removed": 0,
                "guard_removals": 0, "import_fanout": 0}
  },
  "deep_reviewed": true,
  "files_covered": ["path/to/file/a", "path/to/file/b"],
  "coverage_complete": true,
  "findings": [
    {"dimension": "security",
     "severity": "red | yellow",
     "file": "path", "line": 0,
     "snippet": "quoted offending line(s)",
     "headline": "the conclusion in ONE sentence under ~100 chars — what is wrong, stated directly; no hedging, no severity word, no file path, and NOT the first sentence of observation restated",
     "observation": "…",
     "consequence": "cause → mechanism → user/system harm",
     "suggestion": "…"}
  ],
  "counts": {"red": 0, "yellow": 0},
  "ship_summary": "reason only (NO verdict prefix — the header is added by build_ship_comment): e.g. 'no blocking issues; 2 optional should-fix notes' or '1 must-fix: unbounded cache exhausts memory'",
  "posted_comments": 0,
  "branch_gate_violation": false,
  "regression_detected": false,
  "dedup_key": "repo_identity + change_id + revision"
}
```

> Use the `change_id` emitted by `<python> sage_lib/pipeline.py prepare` **verbatim** — do NOT invent or reformat it (it must match the driver's `_cid`, or the record write and read hit different files).

**One record, written in one pass.** Every review writes exactly ONE record with
`deep_reviewed: true` and a fully populated `phase1` block (design dimension) —
there is no "gate-only" record and no design-vs-code split. Two coverage fields
make first-pass completeness machine-checkable:
- `files_covered` — array of changed file paths you reviewed against all
  dimensions in this pass.
- `coverage_complete` — `true` only if `files_covered` covers every changed file;
  `false` triggers the driver's ONE targeted follow-up on the remaining files.

---

## Per-repo rule pack (the ONLY runtime composition)

Some repos maintain a team-specific rulebook. At review time, *if*
`config.json:rule_packs` maps the target `repo_identity` to a pack (a local
skill directory name), resolve and read that `SKILL.md` and apply it as
**additional rules** layered on top of the generic dimensions — **read-only
reuse, no fork**. If no mapping exists (the default), the review uses only the
generic dimensions below. If a pack says "do not use subagents", honor it: read
the file and apply its checks inside this single clean session — do not nest
orchestration.

---

## Workflow summary

```
- [ ] Self-heal store; load common + repo learned patterns
- [ ] Resolve per-repo rule pack (if any) and read it as additional rules
- [ ] ONE thorough pass: design dimension (verdict + design_risk + criticality + design_headline) AND the 9 code dimensions together → chain-of-consequences findings
- [ ] Coverage self-check: every changed hunk reviewed against all dimensions; emit files_covered + coverage_complete (driver runs ONE targeted follow-up if incomplete)
- [ ] Self-critique (Filter / De-dup against green gates / Merge / Sharpen / Stabilize)
- [ ] Post surviving findings as DRAFT comments (publish=false), each quoting the snippet
- [ ] If the change is a fix, run INLINE miss-analysis (learn-from-sage) → STAGE the learning into the candidate file
- [ ] Write the result record JSON
```

