| name | understand |
| position | none |
| description | Detail one capability that /vision seeded — promote its grounding from directional to detailed, create and detail its functionalities, set the capability's own NFR + compliance needs, and roll those needs up into the product profile, firming the box and recording a decision for any out-of-box move. The product-manager step of the ProductOS strategy pipeline, after /vision and before /shape — the last detailing step. Opens no delivery issue. |
| user-invocable | true |
understand
Take a capability that /vision seeded — named, directional, no functionalities — and
detail it. /understand is the product-manager step, the last detailing step:
promote the capability's capability.md from the directional stage to the detailed stage,
create its functionalities (a spine entry plus a detailed functionality.md each), set
the capability's own concrete NFR + compliance needs, then roll those per-capability needs
into the product profile. On a still-directional box the roll-up establishes the box and
firms it to set; on an already-set box, a need that exceeds the committed ceiling halts
for a human decision that moves the box and records why. One capability per run.
Pipeline position: none. /understand is a MIDDLE play of the strategy pipeline (vision → understand → shape → roadmap): it expects to run on the branch /vision already started, injects no start-change head and no close sequence, stops when its work is done, and leaves the branch as-is for the next play to pick up. The close belongs to /roadmap. It writes the persistent product model directly, on the already-started branch. (#437)
Compiled From
This play was compiled from the understand ICE (reference/ice.md) by play-editor
(#466 Batch C, Level 3 rollout per ADR 025; #467 Batch B — the checkpoint upgraded to a
conditional learned gate, see standards/rules/gate-config.md). Intent defines
constraints (C1–C11) and failure conditions (F1–F12); the expectation defines success
scenarios (S1–S5), 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 and the step order. You delegate the
domain work — grounded detailing and functionality authoring — to the product-os-keeper
agent via a JSON contract over files on disk, and you run the mechanical work (the shape
linter, the content-quality eval, the monotonic-up roll-up, the allowlisted persist, and
the post-apply checks) through bundled scripts and an isolated judge. You never write the
grounding docs or the spine yourself, you never lower a box dimension, and you never
persist before the single checkpoint (C9) resolves — a typed approval, a recorded config
skip, or a recorded policy auto-pass — which also carries approval of any out-of-box
box-move (C8).
Forbidden: hand-writing grounding docs or spine entries; touching any capability,
functionality, or domain other than the target and its new functionalities (C10);
prioritizing — writing a slice or an epic, or flipping the capability to active (C2, that
is /shape); persisting by any route other than scripts/apply_understand.py; persisting
before the checkpoint resolves; closing COMPLETED without the stop-condition verdict
reading held (C11/F11).
Agent boundaries:
| Agent | Domain | Skill it invokes | Phases |
|---|
product-os-keeper | Recover the capability's KB shelf (re-route), then detail the capability (directional → detailed), author its functionalities, and set its NFR + compliance needs grounded in that shelf | search-kb, enrich-capability-ice | Enrich |
product-os-keeper is the single domain agent this play uses (1 of the ≤5
budget). No utility agents are needed — git/issue machinery is absent (position none).
The content-quality judge always runs as an isolated, clean-context sub-agent (optionally
on a configured different model) — never the orchestrator's own context.
Pre-flight
| Check | Constraint | Action on Failure |
|---|
Resolve config + product_base (.garura/core/config.yaml) | — | Hard halt |
Resolve grounding-eval.judge (optional model override) | C4 | Default: sub-agent on the session model |
Target capability exists in the spine and is a directional seed (detail: directional) | C1 | Hard halt (REC1) |
Resolve the pre-flight facts mechanically with the bundled resolver — do not derive
them by inference. /understand has no branch or issue (position none):
python3 scripts/preflight.py --play understand --config .garura/core/config.yaml
It returns one JSON object of facts. /understand reads product_base (the live model —
the spine _spine.yaml and the grounding docs), stm_base (drafts + evidence), the
resolved grounding-eval.judge config, and evidence_record (the D1 gate). The target
capability is a runtime input (the play argument, e.g. /understand checkout); the play
resolves its entry in _spine.yaml. If the capability is absent or is not a directional
seed, hard halt (C1/REC1) — /understand details a seeded capability, it never seeds.
The run's working root (<working> below) is {stm_base}_shaping/understand/<capability>/
— the routing, the draft, the roll-up report, the applied record (apply-manifest.json),
and the captured post-apply checks (apply-checks.json) all live under it; the stop
condition evaluates against it. Status markers live at
{stm_base}_shaping/understand/status/.
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}_shaping/understand/status/session-stamp-understand.json" \
--cwd "$(pwd)" --branch "$(git branch --show-current)"
Resume check: if {stm_base}_shaping/understand/status/<capability>.json exists,
resume — skip completed steps, reset any in-progress step to pending, continue from
the first incomplete.
Task DAG
Create ALL tasks immediately after resolving config — before any domain work.
The play owns this DAG; the agent must not edit its top-level tasks.
[T1] Enrich (detail + functionalities) blockedBy: []
[T2] Validate the draft blockedBy: [T1]
[T3] Roll up the box blockedBy: [T1]
[T4] Checkpoint (approval) blockedBy: [T2, T3]
[T5] Persist blockedBy: [T4]
[T6] Verify persisted 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: Enrich
Step 1 — Enrich (detail + functionalities) · Owner: product-os-keeper · Depends on: pre-flight
The persisted model does not record which KB shelf a capability came from (/vision
kept that only in STM). So the agent first re-routes the capability to its KB shelf
with search-kb, then invokes enrich-capability-ice to detail it — promote the
capability.md to the detailed stage, author a functionality.md per functionality it
identifies, set the capability's nfr_needs + compliance_needs, and emit the implied
per-dimension levels the roll-up consumes — all to a draft folder under STM:
{
"task": "re-route this capability to its KB shelf, then detail it (directional->detailed), author its functionalities, set its nfr_needs; emit implied NFR levels",
"inputs": { "capability": "<id + slug + path to its directional capability.md>",
"product_base": "<product_base>" },
"outputs": { "routing": "<working>/routing.yaml",
"draft_dir": "<working>/draft/",
"enrich_manifest": "<working>/draft/enrich-manifest.yaml" }
}
The agent passes the shelf search-kb returned to enrich-capability-ice as its
kb_domain, so the grounding is the shelf the router actually resolved. It returns the
contract with the output paths on disk — never inline content. The skill reads the live
model read-only and writes only the draft (the detailed capability.md, the
functionality.md docs, the spine delta _spine.yaml, and the manifest).
SE-1 (F1/C1): the target capability was resolved and is a directional seed — an absent
or non-seed capability halted the run at pre-flight (REC1).
SE-2 (C6): the enrich-manifest.yaml records grounded_in, and it matches the shelf
search-kb routed to in routing.yaml — the detail is grounded in the recovered shelf.
Phase: Validate + Roll up
Step 2 — Validate the draft · Owner: play · Depends on: Step 1
Run the two guards over the draft before the checkpoint — shape first, then content.
The draft spine is a DELTA (only the target capability + its new functionalities), so the
draft docs are linted on their own here; the full spine↔doc consistency (parent refs
resolving against the domain) is checked post-apply in Step 6 against the merged live spine.
python3 scripts/lint_grounding.py --root <working>/draft/product-os
Then run the content-quality eval over the grounding docs the draft adds or changes
(the detailed capability.md and every new functionality.md) as a concurrent
read-only fan-out (standards/rules/concurrent-fanout.md): dispatch one isolated,
clean-context judge sub-agent PER doc, ALL IN ONE CONCURRENT BATCH — each handed the judge
prompt (standards/rules/grounding-eval.md), its doc, and the kind's per-section guidance
(it sees neither the brief nor the author's reasoning), on the model from
grounding-eval.judge.model. The three safety conditions hold: each judge only READS its
own doc, writes only its own verdict JSON, and no judge depends on another. Join —
wait for every verdict to return before gating any. Then gate every returned verdict
(order-stable, so the outcome is identical to a serial run):
python3 scripts/grounding_gate.py --verdict <verdict.json>
SE-3 (F2/C3): lint_grounding.py exits 0 over the draft docs — the detailed
capability.md conforms to the capability template's detailed stage and every
functionality.md conforms to the functionality template. (Full spine↔doc consistency,
including parent-ref resolution, is verified post-apply in Step 6 against the merged live
spine.)
SE-4 (F3/C4): the content-quality eval gate (grounding_gate.py) passes for EVERY
grounding doc — each section self-explains at product-manager altitude and clears the
stranger test.
SE-5 (F4/C2): the draft promotes the capability (its spine entry is detail: detailed)
and every functionality it names carries both a spine functionalities entry and a
functionality.md (lint_grounding.py link + stage checks).
SE-6 (F5/C5): every nfr_needs.<dimension> on the capability is concrete — a level, a
measurable target, and a gate — and the needs sit on the capability, not globally.
On any GAP, apply the matching recovery (REC2–REC6) and re-run before the checkpoint — a
content-eval fail (SE-4) is REC3: rewrite the doc to the judge's cited fixes and re-judge.
Step 3 — Roll up the box · Owner: play · Depends on: Step 1
Roll the implied levels into the profile, monotonic-up. The script reads the box from the
live spine and branches on state: a directional box is established and firmed to set
(no moves); a set box yields an out-of-box move for any dimension or compliance regime
the need exceeds:
python3 scripts/rollup_box.py --spine <product_base>/product-os/_spine.yaml \
--enrich-manifest <working>/draft/enrich-manifest.yaml \
--out-profile <working>/draft/proposed-profile.yaml \
--out-report <working>/rollup.json
SE-7 (F6/C7): the roll-up report shows state_after: set and never lowers a dimension
(each proposed level is the max of the current box and the need). establishing: true on a
directional box yields no box-moves.
Note — partial gates are expected. On the first firm-up, dimensions no capability has
touched yet keep their directional level; the box fills in as more capabilities are
understood. This is incremental firm-up, not a defect.
Phase: Checkpoint (conditional gate, C9)
Step 4 — Human review (class: standard, conditional) · Owner: play · Depends on: Steps 2–3
This checkpoint is a conditional gate (#467) per standards/rules/gate-config.md —
/understand is one of the eleven conditional document plays. Resolve it first match
wins: pinned (n/a here) → gates.plays.understand → the learned policy →
gates.classes.standard → gates.default (absent ⇒ on). For the policy lookup,
classify the draft-vs-live change shape first:
python3 scripts/classify_change.py --play understand --draft <working>/draft \
--live <product_base> --out <working>/shape.json
Look the shape key up in the config-resolved policy (gates.conditional.policy):
auto-pass iff the shape is in the policy's auto: block AND not in never_auto:
AND Step 2 stands with no blocking finding (a lint_grounding.py gap or a
grounding_gate.py content-eval fail). On auto-pass, do NOT wait: record
gate auto-passed by learned policy (shape: <shape-key>, policy v<version>) as a
Checkpoint Decisions row, include the draft's diff summary in the run record, append
the crossing's live-eval ledger line, and proceed to Step 5:
python3 scripts/gate_eval.py append --ledger <gates.conditional.ledger> --play understand \
--issue <strategy issue> --shape <shape-key> --predicted auto --human auto_pass \
--policy-version <policy version> --ts <run ts>
Anything else resolves the gate on (an explicit gates.plays.understand: off instead
records gate skipped by config (<resolution path>) as a Checkpoint Decisions row and
proceeds — box-move approvals ride this same gate, so the recorded skip or auto-pass
stands in evidence for them too). When on, present the detailed capability, its
functionalities, the per-capability NFR needs, and the profile changes inline — for
each box-move in rollup.json, render its own explicit line item (the dimension,
the from→to levels, and the product-level decision (ADR) it will create), distinct from
the grounding approval — render the approval prompt
(standards/templates/approval-prompt.md) and wait for the typed response. Approve →
continue to persist; cancel → halt with nothing written to the model. Then append the
crossing's live-eval ledger line with the human's real action:
python3 scripts/gate_eval.py append --ledger <gates.conditional.ledger> --play understand \
--issue <strategy issue> --shape <shape-key> --predicted gate \
--human <approved_clean|approved_edited|rejected> --ts <run ts>
<strategy issue> is the strategy-pipeline issue the run's branch carries (opened by
/vision's start-change). <gates.conditional.ledger> / <gates.conditional.policy>
resolve from config gates.conditional (defaults .garura/core/gate-evals.jsonl /
.garura/core/gate-policy.yaml); <policy version> is the policy file's version:
field. <run ts> is the run's own UTC timestamp, derived the same way the close
derives ts (date -u), passed by the orchestrator.
SE-8 (F7/C8/C9): when the box is set and out_of_box: true, each move is resolved
here before any write — Step 5 is the sole writer and depends on this step, and every
box-move appears as its own line item, so an out-of-box need is never persisted without
an explicit, un-rubber-stamped approval (or, when the gate resolves off by config or
auto-passes by learned policy, without the recorded skip/auto-pass row standing in
evidence for it).
SE-13 (F12): every crossing of this gate appended exactly one live-eval ledger line
(shape, predicted gate|auto, the human's real action or auto_pass), and an
auto-pass fired only for a shape the policy lists in auto: (and not in never_auto:)
with no blocking finding standing.
Phase: Apply
Step 5 — Persist · Owner: play · Depends on: Step 4
First snapshot the live spine (cp <product_base>/product-os/_spine.yaml <working>/spine-before.yaml)
so Step 6 can diff. Then persist on a fixed allowlist. apply_understand.py overwrites the
target capability's doc (the promotion), flips its spine entry to detail: detailed and
sets its nfr_needs/compliance_needs, adds its new functionality entries + docs, writes
the rolled-up profile, and writes one decision per approved box-move — and it refuses to
touch any other capability (keyed to --capability-ref). Step 6 verifies the allowlist held.
python3 scripts/apply_understand.py --draft <working>/draft --product-base <product_base> \
--proposed-profile <working>/draft/proposed-profile.yaml --rollup-report <working>/rollup.json \
--capability-ref <capability id> --decided-by /understand --date "$(date -u +%Y-%m-%d)" \
--out-manifest <working>/apply-manifest.json
Step 6 — Verify persisted · Owner: play · Depends on: Step 5
Diff the before/after spine and check the written decisions — capture the checker's JSON
output; its ok field is the stop condition's D3 input:
python3 scripts/check_apply.py --manifest <working>/apply-manifest.json \
--spine-before <working>/spine-before.yaml \
--spine-after <product_base>/product-os/_spine.yaml --capability-ref <capability id> \
> <working>/apply-checks.json
python3 scripts/lint_grounding.py --root <product_base>/product-os \
--spine <product_base>/product-os/_spine.yaml
SE-9 (F8): there is one product-level decision per box-move, each carrying its
dimension, its from→to, and status accepted.
SE-10 (F9/C10): the spine diff is confined to the target capability (its entry + doc),
its new functionalities, the profile, and decisions — no other capability, functionality, or
domain changed.
SE-11 (F10): no slice and no epic was written, and the capability's status was not
flipped to active — prioritization is /shape's.
SE-12 (F11/C11): the close is stop-condition gated — check_stop_condition.py over the
baked stop-condition.yaml (D1 the applied record apply-manifest.json exists; D2 it
stamps changed.profile: true; D3 the captured apply-checks.json reads ok: true) must
read held before any COMPLETED close; a run whose persist or verification did not land
closes HALTED, never COMPLETED (REC11).
Phase: Scenario Validation
Step 7 — Scenario evals · Owner: play · Depends on: Step 6
- SCE-1 (S1 — product manager, detail + functionalities): the capability is detailed
(its
capability.md promoted, its entry detail: detailed, with nfr_needs) and its
functionalities are created (each a spine entry + a functionality.md), all clean on
lint_grounding.py and passing the content-eval gate, and the stop-condition verdict
reads held.
- SCE-2 (S2 — product strategist, first firm-up): on a
directional box, the roll-up
established and firmed it to set with no decision written this run.
- SCE-3 (S3 — architect, out-of-box move): on a
set box exceeded on a dimension, the
run halted at the checkpoint, and on approval the box rose with a product-level accepted
decision recording the from→to.
- SCE-4 (S4 — product owner, non-destructive): the before/after spine diff shows changes
only to the target capability, its new functionalities, the profile, and decisions, and no
dimension was lowered.
- SCE-5 (S5 — reviewer, the checkpoint): the detailed capability, its functionalities,
the NFR needs, and each box-move (dimension, from→to, ADR) appeared inline, and no
product-model file was written before approval — or, on the auto-pass path (a
policy-listed shape), the gate resolved with no wait and the recorded auto-pass, the
appended ledger line, and the diff summary stand in the approval's place.
Phase: Evidence & Close
Step 8 — Close · Owner: play · Depends on: Step 7
Run the Standard Play Close. /understand is a product-scoped play (no issue) — use
the product-scoped evidence base and slug. Evidence recording is play-only and
config-gated per the D1 evidence rule (standards/rules/evidence-recording.md).
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_stamp=$(python3 scripts/session_stamp.py --phase close \
--marker "${stm_base}_shaping/understand/status/session-stamp-understand.json")
python3 scripts/check_stop_condition.py \
--manifest "<play-dir>/stop-condition.yaml" \
--base "${stm_base}_shaping/understand/<capability>/" \
--out "${stm_base}_shaping/understand/status/stop-condition-understand.yaml"
sc_exit=$?
python3 scripts/distill_gate_policy.py --ledger "<gates.conditional.ledger>" --policy "<gates.conditional.policy>" --streak <gates.conditional.streak> --project "<project name from config>" || true
/understand is product-scoped: evidence_base="${product_base}_evidence/understand/"
and slug="${product_slug}" (the capability's slug, e.g. checkout).
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 — fix the state per REC11 (re-run the
allowlisted persist, or re-capture the post-apply verification) and re-evaluate; the close
stays HALTED until the verdict reads held. An unevaluable verdict is never a pass.
Step C1 — Write evidence file. Gated by the resolved evidence.record flag (global
- per-play
evidence.plays.understand; 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 understand, run_id understand-${ts},
product_slug, started_at/completed_at, status per C0, exit_reason; artifacts produced: the
detailed capability.md, the new functionality.md docs, the spine entries written, the
firmed profile, any decision records, the apply manifest, the captured apply-checks.json,
the stop-condition verdict; the content-eval verdicts; step and scenario eval results
SE-1…SE-13 / SCE-1…SCE-5; checkpoint decision from Step 4 including each approved box-move
(or the gate skipped by config / gate auto-passed by learned policy row when the gate
resolved without a wait) plus the gate ledger line(s) appended this run; 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: ## understand Delivered — ${product_slug}, the Run Summary table, the Pipeline
Steps table from the task DAG, the Artifacts Produced table (the detailed capability, its
functionalities, the firmed profile, any decisions), Next Steps (run /shape to prioritize
the detailed functionalities into end-to-end verticals and epics), and a pointer to
$evidence_dest. Always emitted; never gated.
Scenario Validation
| Scenario | Persona | Eval |
|---|
| S1 — detail + functionalities | product manager | SCE-1 |
| S2 — first firm-up | product strategist | SCE-2 |
| S3 — out-of-box move | architect | SCE-3 |
| S4 — non-destructive | product owner | SCE-4 |
| S5 — the checkpoint | reviewer | SCE-5 |
Recovery
| For | Trigger | Direction | Handoff |
|---|
| F1 | the target capability is absent or not a directional seed | halt and ask for a valid seeded capability (run /vision first) before proceeding | human |
| F2 | a grounding doc fails its template/shape, or a spine entry fails the schema or spine↔doc consistency | re-emit the failing doc or spine entry to conform and restore consistency before the checkpoint | autonomous |
| F3 | a grounding doc fails the content-quality eval | rewrite the failing doc to the judge's cited fixes — raise each flagged section to a self-explaining, product-manager-altitude statement — and re-judge until the gate passes | autonomous |
| F4 | the capability stayed directional, or a functionality lacks its entry or its doc | complete the promotion (flip the entry to detail: detailed) and emit the missing functionality entry or doc before the checkpoint | autonomous |
| F5 | an NFR need has no level/target/gate, or needs were written off the capability | re-draft each need on the capability as a concrete level + target + gate | autonomous |
| F6 | a profile dimension was lowered, or the box firmed to a state other than set | recompute the roll-up monotonic-up (max per dimension) and firm to set | autonomous |
| F7 | an out-of-box need against a set box was persisted with no approved decision | revert the box change, surface the move at the checkpoint, persist only after a human approves it | human |
| F8 | a box-move lacks its decision record, or a decision omits its dimension or from→to | write or complete the product-level decision for each move with its dimension and from→to before persisting | autonomous |
| F9 | a capability, functionality, or domain other than the target was changed | restore the other artifact and re-run writing only the target capability and its functionalities, after a human confirms the restore | human |
| F10 | a slice, an epic, or a capability status flip was written | strip the prioritization over-reach — remove the slice/epic, reset the status — leaving only /understand's detailing scope | autonomous |
| F11 | the run is about to close COMPLETED with the Done means unmet | close HALTED (stop_condition_unmet) with the unmet clauses named; fix the state — re-run the allowlisted persist, or re-capture the post-apply verification — and re-evaluate; the close stays HALTED until the verdict reads held | autonomous |
| F12 | a conditional-gate crossing left no live-eval ledger line, or an auto-pass fired for a shape the policy does not list as auto (or that carried a blocking finding) | re-append the missing ledger line for the recorded crossing; when the auto-pass was unearned, re-run the gate as a live wait — render the approval prompt and wait for the typed response — before proceeding | autonomous |
Pause and Resume
Steps run top to bottom. On entry, resolve config, resolve the target capability from
the play argument or the in-progress draft, check the status marker, skip completed
steps, reset any in-progress step to pending, and continue. A fresh start with no marker
runs everything and creates the marker at Step 1.
Compilation Metadata
| Field | Value |
|---|
| fingerprint | sha256:823da1a56b52de5d4c80f642eed9c6d5ebf165d1d6b657351afd6590fb0b526e (of reference/ice.md) |
| compiled_by | play-editor (#467 Batch B); prior: play-editor (#466 Batch C) |
| pipeline_position | none |
| position_exception | middle of the strategy pipeline — runs on the branch /vision started; the close belongs to /roadmap (#437) |
| workflow_structure | A (single checkpoint carrying box-move approvals — class: standard, conditional gate per gate-config.md #467; stop-condition gated close) |
| stop_condition | stop-condition.yaml (D1–D3), gate live at Step C0 |
| domain_agents | 1 (product-os-keeper) |
| utility_agents | 0 |
| skills_used | search-kb, enrich-capability-ice |
| scripts | 11 (preflight.py, lint_grounding.py, grounding_gate.py, rollup_box.py, apply_understand.py, check_apply.py, classify_change.py, gate_eval.py, distill_gate_policy.py, check_stop_condition.py, session_stamp.py) |
| step_evals | 13 (SE-1…SE-13) |
| scenario_evals | 5 (SCE-1…SCE-5) |
| recovery_entries | 12 (one per failure condition; 9 autonomous / 3 human) |
Recompiled note (#467 Batch B): checkpoint upgraded to a conditional learned gate;
see gate-config.md.
Direct-edit deviation note (#468 Stage 5, concurrent fan-out): the Step 2
content-quality judge loop was changed from a serial per-doc loop to a concurrent
read-only fan-out — one isolated judge per doc dispatched in one batch, joined before
gating (standards/rules/concurrent-fanout.md). Execution-timing change only: each judge
stays isolated and read-only, writes only its own verdict, and the gate still runs over
every verdict, so coverage (SE-4) is unchanged. No constraint, failure, scenario, or eval
touched — the ICE (reference/ice.md) and fingerprint stand. play-creator emits the same
fan-out form so a rebuild converges.