Skip to content
Back to skills

Check Change

ASecurity

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 check-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...

  • 3 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
toolspythongobashnodegit

Works with

  • terminal
  • cli

Security analysis

A100/100

Pro scans all 11 files and shows the line behind each finding

Scanned November 7, 2026

npx -y skills add intent-driven-ai/garura --skill check-change --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Check Change?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Check Change
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/intent-driven-ai-check-change/badge)](https://www.skillsdirectory.com/skills/intent-driven-ai-check-change)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: check-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 check-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
---

# check-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
check-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 → check-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 check-change ICE (`reference/ice.md`) by edit-play
(#443 redesign; #467 Batch C gate ruling; #484 scripted chain; #496 grounding fan-out; #860 installed tools). Intent defines constraints (C1–C12) and failure
conditions (F1–F13); 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 edit-play.
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 `change-reviewer` resolves each
category's treatment from the `knowledge/review/` shelf, which it copies into short-term
memory through the memory tool, and writes it into `categories.yaml`. You read the treatment
only from `categories.yaml` (C2). Layer 1 lints the changed parts where they sit in the
repository under review, with the installed tools in `.garura/core/tools/` (C12). 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 |
|-------|--------|-------------------------|--------|-----------|
| `template-manager` | Hand out the Next-line map (`standards/rules/pipeline-next.md`) before Step 2, and the close's templates | the memory tool | Assessment, Close | Utility |
| `change-reviewer` | (Assessment) assess work categories from the diff, write each category's treatment into `categories.yaml`, and copy the memory the review shares (the review shelf, the PR severity taxonomy); (Review) design-ground **one** design-bearing category from committed sources — dispatched once per design-bearing category as a concurrent read-only fan-out (C11) | the memory tool; `git show <base>:<path>` | Assessment, Review | Domain |
| `quality-auditor` | Run the standards linter (`check-pr-quality`) against the diff for the standards-driven categories, with the PR severity taxonomy copy that Step 2 made | `check-pr-quality` | 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 the standards set via `standards_order` | 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 check-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 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). It names no memory folder: the check-knowledge shelf
comes through the memory tool in Step 2 (C2), and the Next-line map from `template-manager`
at the end of Step 1 (C12). Record 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` from config into `context.yaml`. No agent
(C10). Standards *resolution per finding* is owned downstream by `check-pr-quality`; this step
binds the diff, the changed paths, the base ref and the standards_order:

```
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`, `changed_paths`, `changed_files_reported`, and
`base`. It names no memory folder. `changed_paths` comes from the diff headers, so a PR of
any size gives every path. When the host's file count and the count of paths in the diff do
not agree, the script writes no context file and exits 3: stop the review and show its message.

Then, before Step 2, dispatch `template-manager` for the Next-line map (C12). Its copy is the
one `change-reviewer` gets as an input and every play-linter run gets as `--next-map`;
`change-reviewer` does not fetch it.

    { "task":    "load these templates into the issue's short-term memory",
      "inputs":  { "issue": <issue>, "templates": ["standards/rules/pipeline-next.md"] },
      "outputs": { "out_dir": "{stm_base}{issue}/context/templates/",
                   "manifest": "{stm_base}{issue}/context/templates/manifest-check-change.json" },
      "task_id": "load-next-map" }

`next_map` is `{stm_base}{issue}/context/templates/standards/rules/pipeline-next.md`. When
`template-manager` returns `template_missing`, the play-linter runs record
`did not run: no Next-line map` and the review goes on.

**Step 2 — Assess Categories** · Owner: `change-reviewer` · Depends on: Step 1
This one dispatch copies all the memory the review shares, so no later dispatch writes a
shared file (C11): the `knowledge/review/` shelf (`**/knowledge/review/*.md`) into
`review_shelf`, and the PR severity taxonomy (`**/standards/rules/pr.md`) into
`severity_taxonomy`, each through the memory tool into the issue's short-term memory, with
each source in the manifest beside them (C2). The agent then reads the diff and derives the
work categories actually present — judging each changed file by its content, consulting the
playbook copies to recognise seeded categories but never limited to them. It writes
`categories.yaml`: each present category, the changed paths that evidence it, the governing
playbook id (or `null` + "playbook missing" for a new category), and the category's
treatment from that playbook — the layers that apply, the linters to run, and the runnable
checks.

    {
      "task":    "assess the work categories present in the diff; cite changed-path evidence and the governing playbook; write each category's treatment",
      "inputs":  { "context": "<working>/context.yaml", "diff": "<working>/pr.diff",
                   "next_map": "{stm_base}{issue}/context/templates/standards/rules/pipeline-next.md" },
      "outputs": { "categories": "<working>/categories.yaml",
                   "review_shelf": "{stm_base}{issue}/context/memory/knowledge/review/",
                   "severity_taxonomy": "{stm_base}{issue}/context/memory/standards/rules/pr.md" }
    }

**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`, read its treatment from its entry there (never from
a playbook copy), 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 `check-pr-quality` against the diff →
  `quality-findings.yaml`. For harness/memory the play runs the installed structural linters
  the treatment names, from the project root, on the changed parts where they sit in the
  repository under review (C12). When `changed_paths` holds a path under `core/components`:

  ```
  node .garura/core/tools/lint-components/index.js --layout source --target core/components
  python3 .garura/core/tools/lint-play/lint_play.py <the changed play's SKILL.md> --next-map "<next_map>"
  ```

  with the play linter run once for each changed play, on its `plays/<play>/SKILL.md` under
  `core/components`. Else:

  ```
  node .garura/core/tools/lint-components/index.js --layout installed --target .claude
  python3 .garura/core/tools/lint-play/lint_play.py .claude/skills/<play>/SKILL.md --next-map "<next_map>"
  ```

  with the play linter run once for each changed installed play. `<next_map>` is the copy
  `template-manager` handed out at the end of Step 1, so the Next-line check runs. Only a
  violation whose file is in `changed_paths` becomes a finding; count the others, and record
  the count as "there before". When an installed tool is not there, record `not installed`
  for that check; when a tool exits with a crash, record `did not run: <first error line>`.
  Neither is a pass, and neither stops the review. Every tool runs from its installed place;
  reading the repository under review is not running Garura's source. 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/**`,
  `standards/rules/**`, read through the memory tool) and checks conformance; grounding reads are read-only and never
  from diff-added content. Each agent writes the extra memory it copies only under
  `{stm_base}{issue}/context/memory/grounding-<category>/`, with its own `manifest.json` there,
  and reads the shared copies Step 2 made, so no two dispatches write one file. 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",
                   "memory_dir": "{stm_base}{issue}/context/memory/grounding-<cat>/" }
    }

The standards linter (a single dispatch across all standards-driven categories):

    {
      "task":    "run the standards linter against the diff for the standards-driven categories, with the PR severity taxonomy copy Step 2 made as severity_taxonomy_path",
      "inputs":  { "context": "<working>/context.yaml", "categories": "<working>/categories.yaml",
                   "severity_taxonomy_path": "{stm_base}{issue}/context/memory/standards/rules/pr.md" },
      "outputs": { "quality_findings": "<working>/quality-findings.yaml" }
    }

**SE-2 (F2/C2):** each reviewed category's treatment is sourced from its `knowledge/review/`
playbook and written by `change-reviewer` into its `categories.yaml` entry (the entry cites the
playbook id, and the manifest names its memory source); the play read the treatment only from
`categories.yaml` — no treatment is carried in the play body, the play read no playbook copy,
and no memory folder was read by its path.
**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` and only its own
`grounding-<category>/` memory folder and manifest (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).
**SE-13 (F13/C12):** every Layer 1 linter that ran was an installed tool under
`.garura/core/tools/` (the command lines recorded in the findings show it), run on the layout
where the changed parts sit (source when `changed_paths` holds a path under `core/components`,
else installed); every Layer 1 finding names a file in `changed_paths`, and the other
violations are recorded as a "there before" count; each play-linter run carried `--next-map`
with the copy `template-manager` handed out, and its output shows the
`next-command (pipeline-next)` check; each tool that was not installed has a `not installed`
entry, each tool that crashed has a `did not run: <first error line>` entry, and the review
went on.

### 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 `check-pr-quality` 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.check-change` → `gates.classes.standard` → `gates.default`
(absent ⇒ on). Per the #467 ruling `gates.plays.check-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`:
  `decision: approve | reject` and `decided_by: human` (C9).
- **Off — the recommendation becomes the decision.** Do not wait. Record
  `gate skipped by config (gates.plays.check-change)` as a Checkpoint Decisions row in
  the evidence. Write `decision.yaml` directly from `recommended_verdict.yaml`:
  `decision:` its `verdict` **verbatim** (approve | reject), `decided_by: harness`, and the
  citing (blocking) findings (C9). 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.check-change):
<decision>` so no reader mistakes it for a human review (C8, F10). Then run the bundled
`post_verdict.py` — it reads `decision.yaml` first and posts nothing (exit 2) unless the
record holds a decision (approve | reject) and its decider (harness | human), and the
comment's first line names that same decision and decider (C9, #536);
then it posts the comment (via `platform_adapter.py`), writes `review/posted.json`
`{posted: true, comment_url, decision, decided_by, decision_recorded: true}`, 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" \
    --decision "{stm_base}{issue}/review/decision.yaml" \
    --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 decision recorded with its decider
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, and
`decision_recorded: true` with the `decision` and `decided_by` of `decision.yaml`; 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 (shared; see ../_shared/play-close.md) ---
# Run the close in ../_shared/play-close.md. Its Step L loads the templates through
# template-manager and sets ts and evidence_dest; then run this play's own lines below;
# then Steps C0-C2.
#   evidence_base, slug (project-scoped play):
#     evidence_base="${stm_base}${issue}/evidence/check-change/"   ; slug="#${issue}"

# 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.check-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 `check-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 the Next-line map (`$next_map`, from `template-manager`) and emit `**Next:** /<command> — <why>.` (omitted when the mapped command is null), per `play-close.md`. Fill the `delivery-report.md` slots and output the
report: `## check-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 or from a playbook copy | have `change-reviewer` re-resolve the category's treatment from the `knowledge/review/` shelf into `categories.yaml`, and read it from there | 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 |
| F13 | a Layer 1 linter ran from Garura's source or on the wrong layout, a violation outside `changed_paths` became a finding, memory was read by a path, the play linter ran without the Next-line map, or a missing or crashed tool was passed over or halted the run | re-run Layer 1 with the installed tools in `.garura/core/tools/` on the layout where the changed parts sit, keep as findings only the violations in `changed_paths` (count the rest as "there before"), get the Next-line map from `template-manager`, and record `not installed` or `did not run: <first error line>` for a tool that is missing or crashed | 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:0205d7b77d832711125bf40ce6160b962a15749106c914a8a5ef68eecb8807f7 (of `reference/ice.md`) |
| compiled_by | edit-play (#466 Batch A; #467 Batch C; #484 scripted chain; #496 grounding fan-out; #536 Done means; #860 installed tools) |
| 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 | 1 (template-manager — the Next-line map before Step 2 and the close's templates; mechanical PR/host I/O runs in bundled scripts) |
| skills_reused | check-pr-quality; installed tools .garura/core/tools/lint-components and .garura/core/tools/lint-play |
| knowledge_consumed | through the memory tool: knowledge/review/ (category playbooks) and standards/rules/pr.md (severity taxonomy), copied once in Step 2; standards/rules/pipeline-next.md (the Next-line map, from template-manager, for the play linter); 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 | 13 (SE-1…SE-13) |
| scenario_evals | 6 (SCE-1…SCE-6) |
| recovery_entries | 13 (one per failure condition; F8 hands to the human gate, the rest autonomous) |

Files in this skill

  • SKILL.md34.5 KB
  • reference/ice.md16.9 KB
  • scripts/check_stop_condition.py5 KB
  • scripts/compute_verdict.py3 KB
  • scripts/fetch_pr_context.py9.4 KB
  • scripts/platform_adapter.py19.9 KB
  • scripts/post_verdict.py5.3 KB
  • scripts/preflight.py7 KB
  • scripts/session_stamp.py4.8 KB
  • scripts/test_fetch_pr_context.py9.9 KB
  • stop-condition.yaml758 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.