Review an open change thoroughly and against something outside the change itself: assess what work categories the diff contains, resolve each category''s review treatment from the review-knowledge memory shelf, review each through its layers (objective linters, then design-grounding from committed sources for design-bearing categories), consolidate findings that each cite their basis, and stop at one decision gate resolved per gate-config — the reviewer owns the approve/reject verdict when th...
Installs into .claude/skills of the current project.
Are you the author of Review Change?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/kapilvirenahuja-review-change)
---
name: review-change
position: end
description: 'Review an open change thoroughly and against something outside the change itself: assess what work categories the diff contains, resolve each category''s review treatment from the review-knowledge memory shelf, review each through its layers (objective linters, then design-grounding from committed sources for design-bearing categories), consolidate findings that each cite their basis, and stop at one decision gate resolved per gate-config — the reviewer owns the approve/reject verdict when the gate is on; when off the computed recommendation is recorded and posted as a clearly marked harness verdict. The gate (third step) of the end sequence in the ProductOS command model. Use when a PR is ready to be reviewed and a verdict is needed.'
user-invocable: true
---
# review-change
Review an open change so the merge decision rests on something **outside** the change. Assess
the work categories the diff contains, resolve each category's treatment from the
review-knowledge memory shelf, review each through up to two layers (objective linters, then
design-grounding for design-bearing categories), consolidate findings that each cite a basis,
and stop at one decision gate resolved per gate-config: the reviewer owns the verdict when
the gate is on; when off, the computed recommendation becomes the decision, marked as a
harness verdict — either way it is posted to the PR.
**Pipeline position: end** (third step of the end sequence: commit-change →
propose-change → review-change → merge-change). The D2 pipeline-position rule injects this
sequence into any play declared `position: end`. Independent, invokable play; ordering is
enforced by pre-flight (an open PR must exist).
## Compiled From
This play was compiled from the review-change ICE (`reference/ice.md`) by play-editor
(#443 redesign; #467 Batch C gate ruling; #484 scripted chain; #496 grounding fan-out). Intent defines constraints (C1–C11) and failure
conditions (F1–F12); the expectation defines success scenarios (S1–S6), a Done means
(D1–D3, baked to `stop-condition.yaml`), and one recovery entry per failure condition.
To modify this play, update `reference/ice.md` and recompile with play-editor.
Do NOT edit this file manually — it is a compiled artifact.
## Role
You are the orchestrator. You own the workflow, the step order, and the one decision gate.
You delegate the agentic assessment and the design-grounding to `change-reviewer` and the
standards linter to `quality-auditor` — genuine judgment, via JSON contracts over files on
disk. The mechanical PR/host I/O (context bind, verdict post) runs in bundled scripts, not
an agent (C10, #484). You presume **no** work categories and **no** review
treatment: the categories are assessed every run, and each category's treatment is resolved
from the `knowledge/review/` shelf. You run only the layers and steps that apply to the
categories present. You never invent the verdict — you compute a *recommendation*; the gate
resolution (C8) decides who owns the decision: the human when the gate is on, the
recommendation itself (recorded as a harness verdict) when it is off.
**The guiding rule: the change under review is never its own standard.**
**Forbidden:** presuming or hardcoding a category list or a review treatment; grounding a
design principle in the branch's own new content; running a full-repo scan (reads outside the
diff are allowed *only* to reconstruct committed grounding, and are read-only);
auto-deciding the verdict while the gate resolves on; recording an off-gate decision that
differs from the computed recommendation, or posting any decision that does not name its
decider (harness vs human); leaving a verdict silent or unposted; reporting a finding with
no cited basis.
**Agent boundaries:**
| Agent | Domain | Skill/script it invokes | Phases | Role type |
|-------|--------|-------------------------|--------|-----------|
| `change-reviewer` | (Assessment) assess work categories from the diff; (Review) design-ground **one** design-bearing category from committed sources — dispatched once per design-bearing category as a concurrent read-only fan-out (C11) | reads `knowledge/review/` shelf; `git show <base>:<path>` | Assessment, Review | Domain |
| `quality-auditor` | Run the standards linter (`quality-check-scoped`) against the diff for the standards-driven categories | `quality-check-scoped` | Review | Domain |
The mechanical PR/host I/O has **no agent** (C10): `fetch_pr_context.py` binds the PR context
(Step 1) and `post_verdict.py` posts the decision + commits the run artifacts (Step 6), both
via `platform_adapter.py`.
Domain agents: 2 (`change-reviewer`, `quality-auditor`) — `change-reviewer` is dispatched
once for assessment and then **once per design-bearing category** for grounding, in one
concurrent batch (C11, `standards/rules/concurrent-fanout.md`). The verdict **recommendation**
is a deterministic script; the verdict **decision** follows the gate resolution — the human's
when the gate is on, the recommendation recorded as a harness verdict when it is off (C8).
## Pre-flight
| Check | Constraint | Action on Failure |
|-------|-----------|-------------------|
| Resolve working base + config (`.garura/core/config.yaml`) | — | Hard halt |
| An open PR exists for the branch | — | Hard halt |
| Platform CLI available (`gh`) | C8 | Hard halt |
| Resolve standards set + review-knowledge shelf path via `standards_order` / `ltm` | C2 | Hard halt |
| Fetch PR diff + `changed_paths` + base ref (the only files in scope) | C5 | Hard halt |
Resolve the deterministic pre-flight facts with the bundled resolver — config tokens,
`branch`, `issue`, `evidence_record`:
```
python3 scripts/preflight.py --play review-change \
--config .garura/core/config.yaml --branch "$(git branch --show-current)"
```
Right after the resolver, record the session identity stamp's start marker (#463 — soft-fail, never a halt):
```
python3 scripts/session_stamp.py --phase start \
--marker "{stm_base}{issue}/status/session-stamp-review-change.json" \
--cwd "$(pwd)" --branch "$(git branch --show-current)"
```
The open-PR check and `gh` availability are live host reads; the standards set, the
review-knowledge shelf path, and the PR diff/`changed_paths`/base ref are resolved by the
bundled `fetch_pr_context.py` (Step 1, via `platform_adapter.py`) — no agent (C10). Record
the resolved shelf path, the standards set, the PR's `changed_paths`, and the base ref;
everything downstream is bounded to those paths.
## Task DAG
Create ALL tasks immediately after resolving the working base — before any domain work.
The play owns this DAG; agents must not edit its top-level tasks.
```
[T1] Bind PR Context blockedBy: []
[T2] Assess Categories blockedBy: [T1]
[T3] Review Per Category (layers) blockedBy: [T2]
[T4] Consolidate + Recommend Verdict blockedBy: [T3]
[T5] Decision Gate (config-resolved) blockedBy: [T4]
[T6] Post Decision to PR blockedBy: [T5]
[T7] Scenario Validation blockedBy: [T6]
[T8] Close blockedBy: [T7]
```
Mark each task in-progress before its step and completed right after its eval passes. No
runtime reordering. On resume, skip completed and reset in-progress to pending.
## Workflow
### Phase: Assessment
**Step 1 — Bind PR Context** · Owner: play (script) · Depends on: pre-flight
Run the bundled `fetch_pr_context.py` — it fetches the PR diff/changed-paths/base-ref (via
`platform_adapter.py`) and binds `standards_order` + the resolved `knowledge/review/` shelf
path from config into `context.yaml`. No agent (C10). Standards *resolution per finding* is
owned downstream by `quality-check-scoped`; this step binds the diff, the changed paths, the
base ref, the standards_order, and the review-shelf path:
```
python3 scripts/fetch_pr_context.py --config .garura/core/config.yaml \
--out-context "<working>/context.yaml" --out-diff "<working>/pr.diff"
```
`context.yaml` records `standards_order`, the `review_shelf` path, `changed_paths`, and
`base`.
**Step 2 — Assess Categories** · Owner: `change-reviewer` · Depends on: Step 1
The agent reads the diff and derives the work categories actually present — judging each
changed file by its content, consulting the `knowledge/review/` shelf to recognise seeded
categories but never limited to them. It writes `categories.yaml`: each present category, the
changed paths that evidence it, and the governing playbook id (or `null` + "playbook missing"
for a new category).
{
"task": "assess the work categories present in the diff; cite changed-path evidence and the governing playbook",
"inputs": { "context": "<working>/context.yaml", "diff": "<working>/pr.diff",
"review_shelf": "<resolved>/knowledge/review/" },
"outputs": { "categories": "<working>/categories.yaml" }
}
**SE-1 (F1/C1):** every category in `categories.yaml` cites the changed paths that put it
there, and the set was derived from the diff — no presumed/hardcoded category list was used
(an absent category is simply not listed).
### Phase: Review
**Step 3 — Review Per Category** · Owner: play (dispatch) · Depends on: Step 2
For each category in `categories.yaml`, resolve its treatment from its `knowledge/review/`
playbook, then run **only the layers that apply**:
- **Layer 1 — linters.** For the standards-driven categories (code, config, tests, docs-prose)
the play dispatches `quality-auditor` to run `quality-check-scoped` against the diff →
`quality-findings.yaml`. For harness/memory the play runs the bundled structural linters the
playbook names (`lint_play.py`, `lint-components`) and folds their output into findings. For
categories whose linter is a known gap (ADRs, config-map path-existence), the play performs
the playbook's objective checks and records the basis. Where a category has a **runnable**
check (a test suite, a language linter), the play executes it — it is not merely read.
- **Layer 2 — design-grounding (concurrent read-only fan-out, C11).** Take the design-bearing
categories from `categories.yaml` (harness, memory, any new design-bearing category) and
dispatch **one `change-reviewer` per design-bearing category in a single concurrent batch**
(one message, N dispatches — concurrent read-only fan-out: each grounds only its own category
from committed sources, writes only its own `design-findings-<category>.yaml`, no grounding
agent depends on another; the safety conditions hold and the batch is **joined** before Step 4
reads any output — see `standards/rules/concurrent-fanout.md`). Each agent reconstructs its
category's principles from committed sources (base ref, `docs/philosophy/**`, `docs/adr/**`,
`memory/standards/rules/**`) and checks conformance; grounding reads are read-only and never
from diff-added content. The batch is bounded (the design-bearing set fixed by Step 2) and its
outputs are collected in a stable category order, so the result is identical to a serial run.
A single design-bearing category is a fan-out of width one.
A category whose categories.yaml entry is absent has its layers skipped — that is the expected
path, not a failure.
One grounding dispatch per design-bearing category `<cat>`, all sent as one concurrent batch:
{
"task": "design-ground ONLY the '<cat>' category from committed sources; emit conformance findings",
"inputs": { "context": "<working>/context.yaml", "categories": "<working>/categories.yaml",
"category": "<cat>", "diff": "<working>/pr.diff" },
"outputs": { "design_findings": "<working>/design-findings-<cat>.yaml" }
}
The standards linter (a single dispatch across all standards-driven categories):
{
"task": "run the standards linter against the diff for the standards-driven categories",
"inputs": { "context": "<working>/context.yaml", "categories": "<working>/categories.yaml" },
"outputs": { "quality_findings": "<working>/quality-findings.yaml" }
}
**SE-2 (F2/C2):** each reviewed category's treatment is sourced from its `knowledge/review/`
playbook (the record cites the playbook id) — no treatment is carried in the play body.
**SE-3 (F3/C3):** the layers executed equal the union of the applicable layers for the
categories present — no layer ran for an absent category, and no applicable layer was skipped.
**SE-4 (F4/C4):** every grounded principle across the `design-findings-<category>.yaml` set
cites a committed/external source; none cites a diff-added file (the branch is not its own standard).
**SE-5 (F5/C5):** every path read for review is within `changed_paths`, except grounding reads
which are read-only and committed — no full-repo scan ran.
**SE-7 (F7/C7):** for each in-scope category with a runnable check, the check was executed and
its result recorded; none was merely read.
**SE-12 (F12/C11):** design-grounding ran as a concurrent read-only fan-out — the count of
grounding dispatches equals the count of design-bearing categories in `categories.yaml`; each
wrote only its own `design-findings-<category>.yaml` (distinct paths, no overlap); the batch was
joined before Step 4 read any of them; and the collected result is order-stable (a serial re-run
would produce the same consolidated findings).
### Phase: Decision
**Step 4 — Consolidate + Recommend Verdict** · Owner: play (script) · Depends on: Step 3
The play assembles its agents' outputs — `quality-findings.yaml`, the joined
`design-findings-<category>.yaml` set (read in stable category order after the fan-out barrier),
and any bundled-linter findings — into one `findings.yaml` grouped by category and severity — every
finding already classified P1–P4 (by `quality-check-scoped` for standards findings, by the
`pr.md` taxonomy for design findings) and carrying a cited basis. It then computes the
**recommended** verdict — a fixed rule, no agent:
```
python3 scripts/compute_verdict.py --findings <working>/findings.yaml --threshold <conf>
```
It emits `recommended_verdict.yaml` (recommendation + reason + cited blocking findings +
counts): **reject** if any P1 finding (or a runnable suite is red), otherwise **approve**.
This is an input to the human gate, not the decision.
**SE-6 (F6/C6):** every finding in `findings.yaml` carries a P1–P4 class **and** a cited basis
— a linter rule id or a grounded principle with its committed source.
**Step 5 — Decision Gate (class: standard)** · Owner: play · Depends on: Step 4
This checkpoint is a config switch per `standards/rules/gate-config.md` (C8, #466/#467):
resolve `gates.plays.review-change` → `gates.classes.standard` → `gates.default`
(absent ⇒ on). Per the #467 ruling `gates.plays.review-change` is `off`; flipping the
config back to `on` restores the human wait unchanged. `review-pr.bypass` remains a
SEPARATE, pre-existing config (whether a reject blocks the downstream merge), untouched
by this switch.
- **On — the human decides.** Present, in plain language: the categories assessed; for
each design-bearing category, the principles grounded and their committed sources; the
consolidated findings grouped by category and severity, each with its basis; and the
recommended verdict with its reason. Then output the summary and **wait for the
reviewer's typed approve or reject** (do NOT use AskUserQuestion; do NOT proceed on the
recommendation alone). Record the reviewer's decision verbatim to `decision.yaml`
(`decided_by: human`).
- **Off — the recommendation becomes the decision.** Do not wait. Record
`gate skipped by config (gates.plays.review-change)` as a Checkpoint Decisions row in
the evidence. Write `decision.yaml` directly from `recommended_verdict.yaml`:
`decided_by: harness`, the recommendation **verbatim**, and the citing (blocking)
findings. A `reject` outcome stands exactly as a human reject would — fix the cited
findings and re-raise. The verdict is still never silent and never unposted (C9).
**Step 6 — Post Decision to PR** · Owner: play (script) · Depends on: Step 5
Assemble the decision comment (decision + the cited findings + routing) into a file — the
comment ALWAYS names its decider: a human decision is posted as the reviewer's verdict; an
off-gate decision leads with `Harness verdict (gate off per gates.plays.review-change):
<decision>` so no reader mistakes it for a human review (C8, F10). Then run the bundled
`post_verdict.py` — it posts the comment (via `platform_adapter.py`), writes
`review/posted.json` `{posted: true, comment_url}`, and commits+pushes the review run
artifacts, so the PR updates with its own review record. No agent (C10):
```
python3 scripts/post_verdict.py --config .garura/core/config.yaml \
--pr-number "<pr number>" --comment-file "<working>/decision-comment.md" \
--issue <issue> --artifacts-dir "{stm_base}{issue}/review/" \
--out "<working>/posted.json"
```
Whether a `reject` hard-blocks the downstream merge is governed by config
(`review-pr.bypass`), unchanged from prior behavior. A verdict rendered AND posted counts as
done regardless of approve/reject (C9).
**SE-8 (F8/C8):** the recommended verdict was computed; when the gate resolved on, the
recorded decision equals the reviewer's typed input; a verdict comment authored by this
run exists on the PR after the step.
**SE-9 (F9/C9):** `posted.json` reads `posted: true` with a comment URL; the run artifacts
are on the remote branch; the stop-condition verdict reads held before the close may read
COMPLETED.
**SE-10 (F10/C8):** when the gate resolved off, `decision.yaml` reads `decided_by: harness`
and equals the computed recommendation verbatim with its citing findings, and the posted
comment identifies its decider (harness vs human).
**SE-11 (F11/C10):** the PR context bind (Step 1) and the verdict post + artifact
commit/push (Step 6) ran as the bundled `fetch_pr_context.py` / `post_verdict.py` (via
`platform_adapter.py`), not an agent dispatch — no `repo-orchestrator` was invoked for the
mechanical PR/host work.
### Phase: Scenario Validation
**Step 7 — Scenario Evals** · Owner: play · Depends on: Step 6
- **SCE-1 (S1 — mixed PR):** the assessed category set reflects the diff's content; every
present category ran its applicable layers; no absent-category layer ran; posted.json
records the comment URL; the run artifacts are committed and pushed to the branch; the
stop-condition verdict reads held.
- **SCE-2 (S2 — single-kind PR):** only the applicable layers ran; design-grounding and test
execution were skipped — not failed — when their categories were absent; posted.json
records the comment URL; the run artifacts are committed and pushed to the branch; the
stop-condition verdict reads held.
- **SCE-3 (S3 — external grounding):** every grounding source recorded is committed/external;
none is a diff-added file.
- **SCE-4 (S4 — blocking finding):** with a P1 finding, the recommended verdict is reject
citing it; the decision followed the gate resolution (human's input when on; the harness
reject, marked as such, when off); the posted decision equals the recorded decision and
names its decider.
- **SCE-5 (S5 — cited basis):** every finding in the consolidated report names a linter rule
or a grounded principle as its basis.
- **SCE-6 (S6 — multi-category grounding):** when the diff holds more than one design-bearing
category, the number of grounding dispatches equals the number of design-bearing categories,
each wrote only its own `design-findings-<category>.yaml`, the batch was joined before
consolidation read any of them, and the consolidated result is order-stable.
### Phase: Evidence & Close
**Step 8 — Close** · Owner: play · Depends on: Step 7
Run the Standard Play Close. Evidence recording is play-only and config-gated per the D1
evidence rule (`standards/rules/evidence-recording.md`).
```bash
# --- Standard Play Close (canonical; see standards/rules/play-close.md) ---
# Path tokens resolved at pre-flight (resolve here if not already):
# ltm_project_target = yq '.ltm.project-target' .garura/core/config.yaml
# evidence_base, slug (project-scoped play):
# evidence_base="${stm_base}${issue}/evidence/review-change/" ; slug="#${issue}"
evidence_template=$(cat "${ltm_project_target}standards/templates/evidence-file.md")
delivery_template=$(cat "${ltm_project_target}standards/templates/delivery-report.md")
ts=$(date -u +%Y%m%d-%H%M%S)
evidence_dest="${evidence_base}${ts}.md"
mkdir -p "$(dirname "$evidence_dest")"
# Session identity stamp (#463) — close phase; start phase ran at pre-flight
session_stamp=$(python3 scripts/session_stamp.py --phase close \
--marker "${stm_base}${issue}/status/session-stamp-review-change.json")
# Stop-condition gate (#464) — Step C0: this play carries a baked manifest, so the
# gate is LIVE. Evaluate the Done means against the run's STM as the close's
# authoritative input.
python3 scripts/check_stop_condition.py \
--manifest "<play-dir>/stop-condition.yaml" \
--base "${stm_base}${issue}/" \
--out "${stm_base}${issue}/status/stop-condition-review-change.yaml"
sc_exit=$? # 0 held · 1 unmet · 2 error
```
**Step C0 — bind the verdict.** `sc_exit == 0` (held) permits `status: COMPLETED`.
Anything else closes `HALTED` with `exit_reason: stop_condition_unmet` and the evidence's
Stop Condition section names every unmet clause. An unevaluable verdict is never a pass.
**Step C1 — Write evidence file.** Gated by the resolved `evidence.record` flag (global +
per-play `evidence.plays.review-change`; first match wins, absent ⇒ record). When false,
skip the write and record `evidence skipped (record=false)` in the report's pointer line.
Otherwise fill the `evidence-file.md` slots (play `review-change`, run_id
`review-change-${ts}`, issue, started_at/completed_at, status per C0, exit_reason; artifacts
produced: `context.yaml`, `categories.yaml`, `quality-findings.yaml`, `design-findings.yaml`,
`findings.yaml`, `recommended_verdict.yaml`, `decision.yaml`, `posted.json` (with the PR
comment URL), the stop-condition verdict; step/scenario eval results; the recorded decision
(with its `decided_by`) as the terminal status; checkpoint decisions (incl. any
`gate skipped by config` row);
commit reference = the run-artifacts commit SHA; the session identity stamp fields from
$session_stamp (#463): session_id, ledger_file, ledger_start_offset, ledger_end_offset (null
when unresolved — never blocks the close); and stop_condition per C0 with the Stop Condition
section filled) and write to `$evidence_dest`. Do NOT hand-author the body.
**Step C2 — Render delivery report.** Also render the **Next** line: resolve this play in `standards/rules/pipeline-next.md` and emit `**Next:** /<command> — <why>. Or run /next to see all recommended actions.` (only /next pointer, or omit, when the mapped command is null), per `play-close.md`. Fill the `delivery-report.md` slots and output the
report: `## review-change Delivered — #${issue}` (lead with the human's decision), the Run
Summary table, the Pipeline Steps table from the task DAG, the Artifacts Produced table, Next
Steps (on approve: run merge-change; on reject: fix the cited findings and re-raise), and a
pointer to `$evidence_dest`. Always emitted; never gated.
```bash
# --- end Standard Play Close ---
```
## Scenario Validation
| Scenario | Persona | Eval |
|----------|---------|------|
| S1 — mixed PR | reviewer | SCE-1 |
| S2 — single-kind PR | reviewer | SCE-2 |
| S3 — external grounding | reviewer | SCE-3 |
| S4 — blocking finding | reviewer | SCE-4 |
| S5 — cited basis | reviewer | SCE-5 |
| S6 — multi-category grounding | reviewer | SCE-6 |
## Recovery
| For | Trigger | Direction | Handoff |
|-----|---------|-----------|---------|
| F1 | categories presumed or hardcoded | re-run the category assessment from the diff and discard any presumed list | autonomous |
| F2 | treatment taken from the play body | re-resolve the category's treatment from the `knowledge/review/` shelf | autonomous |
| F3 | a layer ran for an absent category, or an applicable layer was skipped | recompute applicability from the assessed categories — skip absent-category layers, run every applicable one | autonomous |
| F4 | grounding drawn from the branch's own new content | re-ground the design from committed/external sources only and re-check conformance | autonomous |
| F5 | the review expanded beyond the diff | re-scope to the diff; keep any outside reads read-only and grounding-only | autonomous |
| F6 | a finding has no cited basis | attach the finding's basis (linter rule or grounded principle) or drop the finding | autonomous |
| F7 | a runnable check was read but not executed | execute the check and fold its result into the findings | autonomous |
| F8 | the verdict was auto-decided while the gate was on, or the decision was not posted | hold at the human gate, record the reviewer's decision verbatim, and post it to the PR | human |
| F9 | the run is about to close COMPLETED with the Done means unmet (verdict never posted, posted.json or run artifacts missing) | post the verdict / fix the run state (write posted.json, commit+push the run artifacts), then re-evaluate the stop condition; the close stays HALTED until the verdict reads held | autonomous |
| F10 | with the gate off, `decision.yaml` differs from the computed recommendation, or the posted comment does not identify its decider | rewrite `decision.yaml` from `recommended_verdict.yaml` (`decided_by: harness`, recommendation verbatim, citing findings) and re-post the comment with the decider named | autonomous |
| F11 | a mechanical PR/host op (context bind, or verdict post + artifact commit/push) ran through an agent instead of its bundled script | route it through `fetch_pr_context.py` / `post_verdict.py` (via `platform_adapter.py`) and remove the agent dispatch | autonomous |
| F12 | design-grounding ran as a serial monolithic pass, or a grounding agent crossed category scope / wrote a shared output, or the batch wasn't joined before consolidation | re-dispatch grounding as a concurrent read-only fan-out — one read-only `change-reviewer` per design-bearing category, each scoped to its category and its own `design-findings-<category>.yaml`, joined before consolidation | autonomous |
## Pause and Resume
Steps run top to bottom. On entry, resolve the PR from the branch, check the status
marker, skip completed steps, reset any in-progress step to pending, and continue. When the
gate resolves on, the decision gate (Step 5) is never auto-resolved on resume — if it has
not been answered, re-present and wait; when it resolves off, resume re-derives the
decision from the recommendation exactly as a first pass would. A re-run re-posts the
current decision rather than adding a conflicting second one.
## Compilation Metadata
| Field | Value |
|-------|-------|
| fingerprint | sha256:b4adce1338cabd935698234cf8157be971f9e8c589d48e2f5865733c99167cdd (of `reference/ice.md`) |
| compiled_by | play-editor (#466 Batch A; #467 Batch C; #484 scripted chain; #496 grounding fan-out) |
| pipeline_position | end |
| workflow_structure | A (multi-agent; agentic assessment → concurrent read-only design-grounding fan-out (one agent per design-bearing category) → consolidation; config-resolved verdict gate; gated close) |
| stop_condition | stop-condition.yaml (D1–D3), gate live at Step C0 |
| domain_agents | 2 (change-reviewer — assessment ×1, grounding ×N design-bearing categories in a concurrent batch; quality-auditor) |
| utility_agents | 0 (#484 — mechanical PR/host I/O moved to bundled scripts) |
| skills_reused | quality-check-scoped, lint_play.py, lint-components |
| knowledge_consumed | knowledge/review/ (category playbooks); standards/rules/pr.md (severity taxonomy); standards/rules/concurrent-fanout.md (Layer-2 fan-out); standards_order set |
| scripts | 7 (preflight.py — pre-flight facts; compute_verdict.py — recommended verdict over classified findings; check_stop_condition.py — Done-means gate; session_stamp.py — #463 identity stamp; platform_adapter.py — code-host adapter; fetch_pr_context.py — bind PR context; post_verdict.py — post decision + commit artifacts) |
| step_evals | 12 (SE-1…SE-12) |
| scenario_evals | 6 (SCE-1…SCE-6) |
| recovery_entries | 12 (one per failure condition; F8 hands to the human gate, the rest autonomous) |
## Recompile note (#496, grounding fan-out)
Intent change via `reference/ice.md` → recompile. Added C11 (design-grounding runs as a
concurrent read-only fan-out per `standards/rules/concurrent-fanout.md`: one read-only
`change-reviewer` per design-bearing category, dispatched as one batch after assessment fixes
the category set, joined before consolidation), F12, REC12, SE-12, and S6/SCE-6. Step 3 Layer 2
changed from a single monolithic grounding dispatch (`design-findings.yaml`) to a per-category
fan-out (`design-findings-<category>.yaml`, distinct output paths); Step 4 now joins and reads
that set in stable category order. The assess-first barrier (Step 2) and the consolidate-last
barrier (Step 4) are unchanged, so the outcome is identical to a serial run — concurrency changes
only wall-clock, and a single design-bearing category is a fan-out of width one. No new agent
(same `change-reviewer`, dispatched N times), no new script. domain_agents stays 2; step_evals
11 → 12; scenario_evals 5 → 6; recovery_entries 11 → 12. The fold of assessment into grounding
was considered and dropped (Occam: it saves only the ~30s assessment round-trip on single-category
PRs, cannot coexist with the fan-out, and would add a permanent branch — see #496). Fingerprint
recomputed over the edited ICE.
## Recompile note (#484, scripted chain)
Intent change via `reference/ice.md` → recompile. Added C10 (mechanical PR/host I/O runs in
bundled scripts, never agent dispatch), F11, REC10, SE-11. Step 1 (bind PR context) and Step
6 (post verdict + commit/push artifacts) no longer dispatch `repo-orchestrator`; they run
`fetch_pr_context.py` and `post_verdict.py` via `platform_adapter.py` (canonical copies in
`play-creator/references/`, stamped here). The category assessment + design-grounding
(`change-reviewer`) and the standards linter (`quality-auditor`) STAY agent-run — genuine
judgment. Utility agents 1 → 0; scripts 4 → 7. Guarantees (categories assessed from the diff,
treatment from the shelf, grounding from committed sources, cited findings, human/harness
decision per gate, verdict posted) unchanged. Fingerprint recomputed over the edited ICE.
## Direct-edit deviation note (#434, harness-led sweep)
Superseded by the #443 redesign below. The prior note recorded moving the verdict from an LLM
agent into `compute_verdict.py`; that script is retained, now as the *recommendation* engine
feeding the human gate.
## Direct-edit deviation note (#443, review-change redesign)
Intent change (via `reference/ice.md` → play-editor, not a hand-edit): review-change moved from
a single-pass autonomous scripted gate to a thorough, category-aware, externally-grounded
review. The play now assesses work categories agentically (`change-reviewer` → `categories.yaml`),
resolves each category's treatment from the new `knowledge/review/` memory shelf, reviews each
through up to two layers (objective linters; design-grounding from committed sources for
design-bearing categories), runs only the applicable layers, consolidates findings that each
cite a basis, and ends at one mandatory human gate that owns the approve/reject verdict.
`compute_verdict.py` is retained as the recommendation input (no longer the decision);
`preflight.py` is unchanged. New domain agent `change-reviewer` added (categorization +
design-grounding); `quality-auditor` reused for the standards linter. The fingerprint reflects
the rewritten `reference/ice.md`.
## Direct-edit deviation note (#463, session identity stamp)
Non-intent change: the Standard Play Close gained the session identity stamp — `scripts/session_stamp.py` (canonical copy: `play-creator/references/session_stamp.py`) runs `--phase start` at pre-flight and `--phase close` in the close block; the evidence frontmatter carries session_id / ledger_file / ledger_start_offset / ledger_end_offset. Source of truth: `standards/rules/play-close.md`; play-creator emits the same lines so a rebuild converges. No constraint, failure, scenario, or eval changed; the fingerprint stands.
## Recompile note (#467 Batch C, chain gates off)
Intent change via `reference/ice.md` → play-editor: the Step 5 gate constraint (C8) now
resolves per gate-config (per-play `gates.plays.review-change` off per the #467 ruling).
When off, the recommendation from `compute_verdict.py` BECOMES the recorded decision —
`decision.yaml` carries `decided_by: harness`, the recommendation verbatim, and the citing
findings; the comment posted to the PR is clearly marked as a harness verdict; a REJECT
stands exactly as a human reject would (fix the cited findings, re-raise;
`review-pr.bypass` untouched). The verdict is still never silent and never unposted (C9
unchanged). New F10 + REC10 + SE-10 guard the off path (decision ≡ recommendation; the
posted comment names its decider). Checkpoint machinery is kept: flipping the config back
to `on` restores the human wait unchanged. No new scripts. The fingerprint reflects the
updated ICE.