Use when converting design documents to structured specs, when spec quality is below threshold, or when requirements need formal acceptance criteria
Installation
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Use when converting design documents to structured specs, when spec quality is below threshold, or when requirements need formal acceptance criteria
Refiner
Iron Law
NO INVENTION WITHOUT AUTHORIZATION. PRESERVE EVERY PRIOR DELTA. NEVER WRITE AUTHORITATIVE STATE ON BLOCK.
Every criterion the refiner emits MUST trace to a design phrase or a user Q&A row — invention from training-data inference is forbidden. No overwrite of earlier <delta> elements. No refiner-report.md artifact. No escape hatch from the DAG completion gate. On R3 axis block: write only _pending-conflict.md (the explicit ephemeral exception per fr-rf-015), exit non-zero, no authoritative state (spec.xml, details/). On non-R3 blocks: terminal output only, exit non-zero, zero filesystem state. If you find yourself wanting to fill an unbound axis with a "sensible default", trim history, write a block report, or add a --force flag, stop and surface the underlying need to the user instead.
Third authorization source (attended-only). A status: accepted ledger entry in specs/<spec-id>/decisions.yml that carries ratified_by (minted by arcforge ratify) is a valid authorization source for a concrete MUST — but ONLY in attended mode (ARCFORGE_MODE=attended). In unattended mode this third source cannot be produced by the agent, and the three legal moves (see Phase 5's "No invention without authorization" section and the Mode-Split subsection) are unchanged. Authorization granularity is the VALUE, not the decision-id: citing an accepted decision authorizes ONLY the exact values listed in its authorized_values that the human confirmed at ratify, never arbitrary values derived from the decision's prose. A <trace>D-NNN:value</trace> cites a specific value from authorized_values; that exact value must appear in the accepted entry's list.
REQUIRED BACKGROUND:
Read ${ARCFORGE_ROOT}/scripts/lib/sdd-schemas/spec.md before producing any spec.xml (primary form) — it carries the canonical identity-header schema (required fields, supersedes format, delta-element rules), auto-generated from ${ARCFORGE_ROOT}/scripts/lib/sdd-utils.js's SPEC_HEADER_RULES. The CLI alternative node "${ARCFORGE_ROOT}/scripts/lib/print-schema.js" spec produces equivalent content. This is the single source of truth — no templates, no hand-authored examples, no drift.
references/spec-structure.md — supplementary field tables for per-spec directory layout and detail-file requirement rules. Load when about to write files in Phase 5.
Overview
Transform design documents into structured XML specifications. The spec becomes Source of Truth — downstream skills read it directly, never the design doc. The refiner is the central transformation: raw source (design.md) → live contract (spec.xml).
Boundary
arc-refining owns formalizing design docs into authoritative specs/<spec-id>/spec.xml and details/*.xml. It does not own completion-claim verification (that is arc-verifying) and it does not own post-implementation spec sync or spec/code drift reconciliation (that is the optional, separate, future arc-syncing-spec workflow — never folded into the SessionStart bootstrap or the arc-using router).
When NOT to Use
No design doc exists yet (run /arc-brainstorming first)
Task is small enough that a structured spec is overhead
Spec is already authoritative and the work is reconciling it with post-implementation reality — that is the arc-syncing-spec job (separate, opt-in workflow), not refiner
Core Rules
ask, don't assume — if unclear, ask the user; never invent requirements
source of truth — spec.xml is authoritative; downstream skills quote it, never the design doc
checklist validation — complete quality checklist before writing any files
iterative refinement — ask 2–3 clarifying questions per iteration
R2 unidirectional — refiner MUST NOT write to docs/plans/. On non-R3 blocks (DAG gate, design-doc validation, identity-header validation), refiner writes nothing — terminal output and non-zero exit only. On R3 axis blocks (Phase 4 axis-1/2/3, Phase 5.5a self-contradiction, Phase 5.5b axis-3-LLM, Phase 6b mechanical-auth-check), refiner writes ONLY the ephemeral specs/<spec-id>/_pending-conflict.md per fr-rf-015 — never spec.xml, never details/. The Iron Law's "NEVER WRITE AUTHORITATIVE STATE ON BLOCK" governs both cases.
Phase 0 — Locate Inputs
If the user has not provided a spec-id, scan specs/ and docs/plans/ to present available targets and ask the user to choose.
Once you have the spec-id, locate the design doc at docs/plans/<spec-id>/<date>/design.md.
Phase 1 — DAG Completion Gate (when prior spec exists)
Before producing a new spec version, verify that the prior sprint is complete. This gate lives in the refiner — not the planner — so the wiki layer can never reach an inconsistent state where spec_version is at v(N+1) while v(N)'s DAG is still running.
No prior spec → skip this phase. v1 formalization has no prior sprint to be incomplete.
Prior spec exists → run the gate check:
node -e "
const { checkDagStatus } = require('${ARCFORGE_ROOT}/scripts/lib/sdd-utils');
const status = checkDagStatus('specs/<spec-id>/dag.yaml');
if (status === null) {
console.log('No dag.yaml — proceed (legal: refined but not yet planned).');
} else if (status.incomplete === 0) {
console.log('All', status.total, 'epics complete — proceed with new iteration.');
} else {
console.log('BLOCKED:', status.incomplete, 'of', status.total, 'epics still incomplete:');
for (const e of status.incompleteEpics) console.log(' -', e.id, '(' + e.status + ')');
console.log('Complete current sprint before iterating.');
process.exit(1);
}
"
Three outcomes:
checkDagStatus returns null (no dag.yaml exists) → proceed. Legal state: user refined but did not yet run the planner.
All epics in "completed" status → proceed with the new iteration.
Any epic NOT in "completed" status → BLOCK. Print the incomplete epic list to terminal, print "Complete current sprint before iterating.", exit non-zero. Write no files (no spec.xml, no details/, no report).
No escape hatch
When the gate blocks, the user has exactly two paths forward:
a. Complete the remaining epics in the current sprint (status → completed), then re-run refiner.
b. Abandon the entire spec by deleting specs/<spec-id>/ (a filesystem action — not an arcforge primitive), then start over.
There is no --force flag, no abandoned epic status, no environment-variable override, no partial abandonment mechanism. Partial abandonment would corrupt dag.yaml status semantics (from "actual execution state" to "what the user wishes were the state"), polluting every downstream tool that reads it. If you find yourself wanting to add an escape hatch, stop and surface the underlying need to the user instead.
If valid is false and any issue has level: 'ERROR' — BLOCK. Print the issues to terminal, exit non-zero, write no files. Do not write any report file.
If valid is false with only WARNINGs — proceed but surface the warnings to the user.
If valid is true — proceed.
Phase 2.5 — Vision and Decision-Ledger Context (D6)
After input validation passes, load vision and ledger context (read-only), then validate them.
2.5a — Load Context
If specs/<spec-id>/vision.md exists, read it as context — it describes the product's long-horizon intent and informs which design changes align with the stated vision. If specs/<spec-id>/decisions.yml exists, read it as context — the existing ledger entries show which decisions are already recorded and their status.
Both files are read-only inputs to the refiner. Do NOT write to vision.md. Do NOT edit existing ledger entries; the B2 immutability hook will deny any such write.
No-op rule: when specs/<spec-id>/decisions.yml, specs/<spec-id>/vision.md, and specs/<spec-id>/spec.xml are absent, the gate MUST PASS. Only present files are validated. This gate does not write any files. On ERROR: BLOCK — print issues to terminal, exit non-zero, write no files (per fr-rf-015-ac2: non-R3 block, terminal output only).
Phase 3 — Detect Behavior Context
Check the filesystem for a prior spec at the canonical path:
specs/<spec-id>/spec.xmlexists → this is an iteration; the design doc must contain Context + Change Intent sections.
specs/<spec-id>/spec.xmldoes not exist → this is the first formalization (v1); the design doc carries prose with problem / solution / requirements / scope.
This is one refiner behavior with conditional fields based on filesystem state — not two modes. There is no mode parameter, no path-style label, no greek-letter framing. The filesystem is the single source of truth for which sections to expect.
When a prior spec exists, the design doc MUST have both Context and Change Intent sections. Missing either is ERROR — block with: "Iteration design doc must have Context and Change Intent sections — re-run brainstorming with the prior spec in scope."
When the design doc's date folder is older than or equal to the spec's recorded design_iteration, produce a WARNING: "design iteration <date> is not newer than spec source <spec-date> — this may be a stale design doc."
Before drafting the spec, read the design doc and the brainstorming Q&A decision-log in full, then check three axes. R3 fires when ANY axis surfaces a contradiction.
Axis 1 — design.md internal contradictions.
Contradictory requirements within the design. Print both requirement IDs to terminal — e.g., REQ-A requires "sessions expire after 15 minutes" and REQ-B requires "sessions never expire" is an axis-1 contradiction; terminal output MUST name both REQ-A and REQ-B with a pointer to specs/<spec-id>/_pending-conflict.md.
When prior spec exists: contradiction between new design requirements and existing spec requirements.
Broken dependencies (requirements that depend on removed requirements).
Axis 2 — design.md ↔ user Q&A answers.
If design says X and a user Q&A row says ¬X, the conflict is unresolved. Refiner does not silently pick one — silently picking either side is forbidden even if the Q&A answer is more recent than the design. Terminal output MUST cite both the design line range and the Q&A row q_id. Examples:
Design says max=32; Q&A row says "make 32 the default but configurable via flag" → axis 2 fires.
The refiner has no authorization to pick. Authoring windowMs: 60000 (or any reconciled middle ground) without surfacing the conflict is the failure mode this axis catches.
Axis 3 — spec-draft coverage (deferral and invention).
Every criterion the refiner is about to draft must trace to a (design phrase ∪ Q&A row) source. A criterion with no such source is invention; under R3, it does not belong in the spec.
Deferral signals in Q&A ("use defaults", "covered.", "skip", "you decide", and similar) DO NOT authorize concrete MUSTs. A deferred axis is unbound, not "implicitly authorized via training-data common practice".
On any axis firing — BLOCK. Print to terminal:
Which axis fired (1, 2, or 3) and a one-line description of the conflict.
The specific design line ranges and Q&A row q_ids involved (so the user can locate them without re-reading the whole design).
1–3 candidate resolutions the user can pick from when re-running brainstorming. Provide AT LEAST 1 and AT MOST 3 — the writer enforces this range. Examples per axis:
Axis 1: (a) keep requirement A, drop B; (b) keep B, drop A; (c) widen scope so both hold under disjoint conditions.
Axis 2: (a) keep design wording, edit Q&A row; (b) accept Q&A answer, edit design; (c) make the axis configurable so both stances coexist.
Axis 3: (a) downgrade the criterion to SHOULD/MAY citing design's qualitative phrase; (b) leave the axis unbound; (c) ask user to specify a concrete value in a new design iteration.
Before exiting non-zero, MUST write the conflict handoff file (fr-rf-015-ac1):
The schema source of truth is PENDING_CONFLICT_RULES (from ${ARCFORGE_ROOT}/scripts/lib/sdd-utils). The file is written at specs/<spec-id>/_pending-conflict.md. It is ephemeral — brainstorming Phase 0 reads it as Change Intent seed (fr-bs-008), then deletes it on successful new-design write. Refiner does NOT clean it up.
MUST NOT write _pending-conflict.md for non-R3-axis blocks (fr-rf-015-ac2):
DAG completion gate failure (fr-rf-012) → terminal output only, exit non-zero, no file written.
Identity-header validation errors (fr-rf-010-ac1 through fr-rf-010-ac4) → terminal output only, exit non-zero, no file written.
These are pipeline-mechanical or programmer-error blocks, not axis contradictions. Their output channel is terminal only.
Exit non-zero. Write no authoritative files — no spec.xml, no details/, no report. The _pending-conflict.md is the only file written. The user routes through /arc-brainstorming iterate <spec-id> to author a new dated design.md (R1-authorized), refiner re-runs against the new design, no stale state to clean up.
Ask at least 2–3 clarifying questions when gaps or ambiguities (not contradictions) surface — gaps are unbound axes (legal under axis 3 by leaving the axis unbound), not R3 triggers.
Phase 5 — Draft Spec In Memory (Two-Pass Write)
Build the complete spec.xml and all specs/<spec-id>/details/*.xmlin memory before writing any file to disk. This is the two-pass write pattern: build in memory → validate → write atomically only if valid.
No invention without authorization
Refiner MUST NOT author criteria from training-data inference. When a design phrase is qualitative ("rate-limited", "fast", "secure") or a Q&A row defers ("use defaults", "covered.", "skip", "you decide"), the legitimate refiner moves are exactly three:
Preserve design's qualitative phrasing as SHOULD/MAY. The source phrase is in design.md — that is the authorization. SHOULD/MAY signals "non-binding hint" without inventing a concrete number. The <trace> cites the qualitative phrase in design.md.
Leave the axis unbound. No criterion at all on that axis. Downstream stages (planner, implementing) may surface the unbound axis as a planning question; refiner does not pre-answer it.
BLOCK with candidate resolutions. When ambiguity is large enough that neither (1) nor (2) is honest — for example, the design's qualitative phrase is so vague that any SHOULD wording would itself be invention — route the user through brainstorming via Phase 4's block flow.
Inventing a concrete MUST from training-data common practice ("most rate-limiters use 60-second windows, so MUST window=60s") is not on this list. It violates the Iron Law's first clause.
Deferral signals (ac2). A Q&A row carries deferral_signal=true when its user_answer_verbatim matches one of the canonical deferral phrases — the four canonical phrases per DECISION_LOG_RULES.deferral_signal_canonical_phrases are: "use defaults", "covered.", "skip", "you decide". When deferral_signal=true, the corresponding axis is unbound. Deferral does NOT authorize a concrete MUST derived from training-data common practice — the same three legitimate moves apply. A deferred answer means the user deliberately left the axis open; refiner has no authorization to pre-fill it.
Every concrete MUST must be sourced (ac3). For every concrete MUST the refiner is about to author, it MUST be able to point to a non-deferral source — either a design phrase that contains the concrete value, or a Q&A row whose user_answer_verbatim contains the concrete value with deferral_signal=false. If no such source exists, the criterion is invention and MUST NOT be authored; use one of the three legitimate moves instead. This rule is the runtime invariant that mechanicalAuthorizationCheck (in ${ARCFORGE_ROOT}/scripts/lib/sdd-validators.js) verifies at Phase 6 — every concrete MUST in the produced spec will be checked mechanically, so any invention the LLM drafts here will be caught and cause a block downstream.
Field tables (identity header, per-spec directory layout, detail-file requirement rules, unchanged-requirements rule) are in references/spec-structure.md — already listed under REQUIRED BACKGROUND above. The decision logic below (wiki-style delta accumulation, version increment semantics) stays here.
Mode-Split: Unattended vs. Attended
The refiner reads ARCFORGE_MODE from the environment (set by the calling environment — NOT agent self-report). Any value other than attended is treated as UNATTENDED. The default when the variable is absent is UNATTENDED.
UNATTENDED path (default, byte-for-byte the same as before D6). On a deferral or unbound/qualitative axis, the three legal moves above apply without change: downgrade to SHOULD/MAY, leave unbound, or BLOCK via _pending-conflict.md. In addition, the refiner MUST NOT take any of these actions in unattended mode:
MUST NOT write a status: accepted ledger entry.
MUST NOT self-ratify (i.e., set ratified_by on any entry without the arcforge ratify command having run).
MUST NOT treat a D-NNN: decision-trace as authorization for a concrete MUST unless the referenced entry already has status: accepted and ratified_by — which the agent itself cannot produce in unattended mode.
No "agent self-labels low-risk → auto-accept" escape hatch exists.
ATTENDED path (ARCFORGE_MODE=attended) — draft-then-ratify. When the refiner is in attended mode and encounters a deferral signal ("you decide", "use defaults", "covered.", "skip") or an unbound/qualitative axis:
Draft a status: proposed ledger entry in specs/<spec-id>/decisions.yml with authorized_values as a structured list (not prose). This records what the refiner would do but does NOT authorize any concrete MUST yet.
Commit the drafted ledger entry before stopping, so the proposed decision survives the session boundary:
Stop and instruct the human: print "Run arcforge ratify <spec-id> D-NNN to authorize the proposed values before this criterion can be written as a MUST."
Do NOT author the concrete MUST until a ratified (status: accepted + ratified_by) entry exists in the ledger.
Once ratification has occurred (the human has run arcforge ratify), the refiner may cite <trace>D-NNN:value</trace> where value is an exact item from authorized_values — the value the human confirmed at ratify.
The refiner NEVER mints an accepted entry itself and NEVER authors the concrete MUST without a pre-existing ratified entry. The attended deferral clause (fr-rf-013) is a scoped addition to the unattended path: the unattended behavior is unchanged; the attended path adds a draft-then-ratify exit rather than unconditionally leaving the axis unbound or blocking.
For how attended mode is opted into, how the human resolves and runs arcforge ratify under a plugin install, and where draft-then-ratify sits in the end-to-end pipeline, see ${ARCFORGE_ROOT}/docs/guide/sdd-pipeline.md.
Version Increment (when prior spec exists)
spec_version = previous version + 1
supersedes = <spec-id>:v<previous-version>
source/design_path and source/design_iteration point to the NEW design doc
Delta Elements — Wiki-Style Accumulation
<overview> accumulates all <delta> elements ever written. Each iteration appends one new <delta> as the last child of <overview>. Refiner MUST preserve every prior <delta> element verbatim — no overwrite, no merge, no deduplication, no rewrite. Earlier deltas are historical record, frozen at the moment they were appended.
delta.iteration MUST equal the new source/design_iteration
<added> and <modified> ref MUST correspond to a real requirement in current detail files
<added> and <modified> items SHOULD carry a decision attribute pointing to the relevant ledger entry when a specs/<spec-id>/decisions.yml entry exists that authorized the addition or change. Example: <added ref="fr-foo-001" decision="D-003" />. Use the D-id of the proposed or accepted ledger entry that captures the rationale. Omit the attribute when no ledger entry covers this item.
<removed> MUST include a <reason> child; <migration> is optional. The implementer LLM reads both as teardown guidance — phrase them with that reader in mind, not just for human archive purposes.
<renamed> is body-unchanged only — ref_new MUST exist in current detail files; semantic changes use <removed> + <added>, never <renamed> + <modified>.
For the first formalization (no prior spec): no <delta> element. Its absence signals "plan all requirements" to the planner.
For v2+: the new delta is appended after the prior delta(s). The resulting sequence MUST be ordered ascending by version. Both parsed.deltas (full array) and parsed.latest_delta (highest version) are exposed by parseSpecHeader for downstream consumers.
Phase 5.5 hosts two independent checks. Both can block; their block behaviors differ.
5.5a — Self-Contradiction Sub-Pass
Before Phase 6 output validation, re-read each requirement's <description> against each <criterion> (and against sibling criteria). Two failure modes to flag:
Scope mismatch. Description says "the system handles X" (covering both success and failure paths), but ACs only test the success path. The description's scope and the AC set's coverage diverge — readers will infer requirements that the spec does not actually test. Remediation hint: "widen ACs to cover failure path, or narrow description to match ACs."
RFC-2119 verb mismatch. Description uses MUST but a sibling AC for the same axis uses SHOULD (or vice versa). The verb's strength must be consistent across description and ACs for the same axis. Mismatches signal copy-paste drift between drafting passes. Remediation hint: "align verbs across description and ACs for the same axis."
If any requirement fails this sub-pass — BLOCK (R3 enforcement severity). Print to terminal: requirement ID, the specific scope or verb mismatch, and the relevant remediation hint above. Exit non-zero. Write no authoritative files — no spec.xml, no details/. Phase 5.5 findings MUST NOT be downgraded to WARNING — a WARN would let the spec ship with internal contradictions.
Before exiting non-zero, MUST write the conflict handoff file (fr-rf-014-ac5): call writeConflictMarker (recipe above in Phase 4) with these values:
axis_fired: '3'
conflict_description: '<requirement ID>: <specific scope or verb mismatch> — <remediation hint from ac1/ac2>' (ac1: widen/narrow scope; ac2: align verbs)
user_action_prompt: 'Run /arc-brainstorming iterate <spec-id> to resolve this conflict.'
This is the single recovery surface for every R3 BLOCK — self-contradiction is not exempted from the handoff.
5.5b — Axis-3 LLM Judgment Pass
Re-read each criterion in the in-memory draft and verify it traces to a (design phrase ∪ Q&A row) citable source. This is the LLM-judgment layer of axis 3 (the mechanical layer runs at Phase 6 via mechanicalAuthorizationCheck — Phase 5.5b is LLM judgment, Phase 6 is the mechanical follow-up over <trace> elements). Criteria with no citable source trigger BLOCK per fr-rf-001 axis 3.
If any criterion has no traceable source — BLOCK (write conflict file, per fr-rf-015-ac1, R3 enforcement severity). Phase 5.5 findings MUST NOT be downgraded to WARNING. Before exiting non-zero:
Print to terminal: which criterion has no source, and the 1–3 candidate resolutions.
Exit non-zero. Write no authoritative files — no spec.xml, no details/.
This sub-pass is independent of Phase 4's three axes. Phase 4 catches conflicts between the design inputs; Phase 5.5 catches the spec-to-be contradicting itself (5.5a) or having invented criteria (5.5b). Both 5.5a and 5.5b write _pending-conflict.md — single recovery surface for every R3 BLOCK.
6a — Identity-header validation (non-R3 block, per fr-cc-if-002):
validateSpecHeader verifies the identity-header contract (fr-cc-if-002): spec_id, spec_version, status, source, and scope must all be present and well-formed. Any missing or malformed field is ERROR.
If validateSpecHeader returns any level: 'ERROR' — BLOCK (no conflict file, per fr-rf-015-ac2). Print all findings with remediation guidance to terminal, exit non-zero, write no files (no spec.xml, no details/, no report file). Do NOT write _pending-conflict.md — header validation errors are schema/programmer errors, not axis contradictions.
WARNINGs are surfaced to the user but do not block writing.
node -e "
const fs = require('fs');
const { mechanicalAuthorizationCheck, writeConflictMarker, parseDecisionLedger } = require('${ARCFORGE_ROOT}/scripts/lib/sdd-utils');
const result = mechanicalAuthorizationCheck(
fs.readFileSync('_draft_spec.xml', 'utf-8'),
'docs/plans/<spec-id>/<date>/design.md',
'docs/plans/<spec-id>/<date>/decision-log.yml',
parseDecisionLedger('specs/<spec-id>/decisions.yml')
);
if (!result.valid) {
console.log(JSON.stringify(result.unauthorized_traces, null, 2));
writeConflictMarker('<spec-id>', {
axis_fired: '3',
conflict_description: 'Mechanical authorization check failed: ' +
result.unauthorized_traces.map(t => t.trace_value + ' (' + t.reason + ')').join('; '),
candidate_resolutions: [
'(a) Add authorizing source to design.md for the flagged criterion.',
'(b) Downgrade the criterion to SHOULD/MAY citing design qualitative phrase.',
'(c) Remove the criterion — the axis is unbound without an authorizing source.'
],
user_action_prompt: 'Run /arc-brainstorming iterate <spec-id> to resolve this conflict.'
});
process.exit(1);
}
"
If mechanicalAuthorizationCheck returns valid: false — BLOCK (write conflict file, per fr-rf-015-ac1). Call writeConflictMarker with axis_fired: '3', then exit non-zero. Write no authoritative files.
If both checks pass: write all files atomically: spec.xml and all details/*.xml in a single operation. Partial writes (spec.xml written but details/ incomplete) MUST NOT occur.
Quality Checklist
Before writing files, confirm:
every requirement has at least one acceptance criterion with a <trace> element
every <trace> points to a real source — a design.md line range that contains the cited content, or a Q&A row q_id whose user_answer_verbatim contains the cited content (no invention from training data)
every concrete MUST has an authorizing source — design phrase or non-deferral Q&A answer; deferral signals do NOT authorize concrete MUSTs
no vague language — use MUST/SHOULD/MAY per RFC 2119
all requirement IDs are unique across all detail files
all <detail_file path="..."> references point to files that will be written
for v2+: supersedes field present; format <spec-id>:v<N>
new <delta> element present and well-formed (when prior spec exists): version/iteration match, all refs resolve
every prior <delta> is preserved verbatim — <overview> contains the full ascending sequence
new <delta> is the last child of <overview>
Phase 4 three axes all clean (design internal, design ↔ Q&A, criterion coverage)
Phase 5.5a sub-pass clean (no description ↔ AC scope or verb mismatches)
Phase 5.5b axis-3 LLM judgment clean (every criterion has a citable source)
Phase 6a identity-header validation passed (validateSpecHeader returns no ERROR)
Phase 6b mechanical authorization check passed (mechanicalAuthorizationCheck returns valid: true — every <trace> resolves to design.md content, decision-log content, or a ratified ledger value (specs/<spec-id>/decisions.yml entry with status: accepted + ratified_by, cited value in authorized_values))
user confirms: "Is this spec complete?"
Red Flags — Stop
"user seems happy, let's proceed"
"I can make reasonable assumptions about missing requirements"
"we can refine this in implementation"
"skipping clarifying questions"
"I'll just write a quick refiner-report.md so the user knows what blocked"
"I'll force past the DAG gate this once"
"I'll trim the older deltas — they're noise"
"user said 'use defaults' / 'covered.' / 'skip', so I'll pick a sensible value"
"training data shows most systems do X, so MUST X"
"design says X and Q&A says ¬X, but Q&A is more recent so I'll use that" (axis 2 silent pick)
"the criterion is obviously implied by the design's wording" (no <trace> source = invention)
"this scope mismatch between description and ACs is minor, ship it"
All mean: stop. Keep asking until checklist complete and user confirms. On R3 axis block: write _pending-conflict.md then exit non-zero — no authoritative files. On all other blocks: terminal + exit only — zero files. No escape hatch from the gate. Prior deltas are preserved verbatim. No invention from training data; no silent picks across design/Q&A conflicts; no shipping a spec that contradicts itself.
issues listed to terminal with requirement IDs, issue types, remediation
files written (R3 axis-1/2/3 blocks, Phase 5.5a, Phase 5.5b): specs/<spec-id>/_pending-conflict.md (ephemeral handoff — brainstorming reads and deletes it)
files written (all other blocks): none (no spec.xml, no details/, no report)
exit: non-zero
action: for R3 axis blocks → run /arc-brainstorming iterate <spec-id> to resolve. For other blocks → address issues then re-run refiner.
There is no refiner-report.md artifact. Block behavior is intentionally transient — terminal output + non-zero exit, no authoritative filesystem state. The _pending-conflict.md is the only permitted artifact on R3 axis blocks; it is ephemeral (brainstorming deletes it). Clean retry semantics: resolve the conflict (or fix the design doc / finish the sprint), re-run.