From arcforge
Transforms design documents into structured XML specs with formal acceptance criteria. Use when converting design docs to specs or when spec quality needs improvement.
How this skill is triggered — by the user, by Claude, or both
Slash command
/arcforge:arc-refiningThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**NO INVENTION WITHOUT AUTHORIZATION. PRESERVE EVERY PRIOR DELTA. NEVER WRITE AUTHORITATIVE STATE ON BLOCK.**
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:
${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.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).
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).
/arc-brainstorming first)arc-syncing-spec job (separate, opt-in workflow), not refiner 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.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.
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."completed" status → proceed with the new iteration."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).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.
Validate the design doc programmatically:
node -e "
const { parseDesignDoc, validateDesignDoc } = require('${ARCFORGE_ROOT}/scripts/lib/sdd-utils');
const parsed = parseDesignDoc('docs/plans/<spec-id>/<date>/design.md');
const result = validateDesignDoc(parsed);
console.log(JSON.stringify(result, null, 2));
"
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.valid is false with only WARNINGs — proceed but surface the warnings to the user.valid is true — proceed.After input validation passes, load vision and ledger context (read-only), then validate them.
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.
node -e "
const { parseVision, validateVision, parseDecisionLedger, validateDecisionLedger,
getHeadLedgerContent, parseDecisionLedgerContent,
checkSpecDecisionGraph } = require('${ARCFORGE_ROOT}/scripts/lib/sdd-utils');
const fs = require('fs');
// Vision gate — no-op when spec vision absent.
const productVisionPath = 'product/vision.md';
const specVisionPath = 'specs/<spec-id>/vision.md';
const productParsed = fs.existsSync(productVisionPath) ? parseVision(productVisionPath) : null;
const specParsed = fs.existsSync(specVisionPath) ? parseVision(specVisionPath) : null;
const visionResult = validateVision(productParsed, specParsed);
if (!visionResult.valid) {
console.log(JSON.stringify(visionResult, null, 2));
process.exit(1);
}
// Ledger gate — no-op when absent.
const ledgerPath = 'specs/<spec-id>/decisions.yml';
const current = parseDecisionLedger(ledgerPath);
if (current !== null) {
const headContent = getHeadLedgerContent(ledgerPath, process.cwd());
const previous = parseDecisionLedgerContent(headContent);
const result = validateDecisionLedger(current, previous);
if (!result.valid) {
console.log(JSON.stringify(result, null, 2));
process.exit(1);
}
}
// Graph gate — spec↔decision↔anchor consistency (D6 P2, S10). No-op when absent.
const specXmlPath = 'specs/<spec-id>/spec.xml';
const specXmlContent = fs.existsSync(specXmlPath) ? fs.readFileSync(specXmlPath, 'utf8') : null;
const graphResult = checkSpecDecisionGraph({
specXmlContent,
ledger: current,
productVision: productParsed,
specVision: specParsed,
});
if (!graphResult.valid) {
console.log(JSON.stringify(graphResult, null, 2));
process.exit(1);
}
console.log('vision+ledger+graph gate: OK');
"
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).
Check the filesystem for a prior spec at the canonical path:
specs/<spec-id>/spec.xml exists → this is an iteration; the design doc must contain Context + Change Intent sections.specs/<spec-id>/spec.xml does 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.
specs/<spec-id>/_pending-conflict.md.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:
windowSec: 60; Q&A row says "use windowMs for consistency" → axis 2 fires.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).
On any axis firing — BLOCK. Print to terminal:
(a) keep requirement A, drop B; (b) keep B, drop A; (c) widen scope so both hold under disjoint conditions.(a) keep design wording, edit Q&A row; (b) accept Q&A answer, edit design; (c) make the axis configurable so both stances coexist.(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):
node -e "
const { writeConflictMarker } = require('${ARCFORGE_ROOT}/scripts/lib/sdd-utils');
writeConflictMarker('<spec-id>', {
axis_fired: '<1|2|3>',
conflict_description: '<specific design line ranges and Q&A row q_ids involved>',
candidate_resolutions: [
'(a) <first candidate>',
'(b) <second candidate>'
],
user_action_prompt: 'Run /arc-brainstorming iterate <spec-id> to resolve this conflict.'
});
"
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):
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.
Build the complete spec.xml and all specs/<spec-id>/details/*.xml in memory before writing any file to disk. This is the two-pass write pattern: build in memory → validate → write atomically only if valid.
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:
<trace> cites the qualitative phrase in design.md.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.
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:
status: accepted ledger entry.ratified_by on any entry without the arcforge ratify command having run).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.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:
git add specs/<spec-id>/decisions.yml
git commit -m "docs: draft proposed ledger entry for <spec-id>"
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.
spec_version = previous version + 1supersedes = <spec-id>:v<previous-version>source/design_path and source/design_iteration point to the NEW design doc<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.
<overview>
... identity fields ...
<delta version="2" iteration="2026-05-10">
<added ref="fr-as-007" />
<modified ref="fr-as-002" />
</delta>
<delta version="3" iteration="2026-06-01">
<added ref="fr-as-009" />
<removed ref="fr-as-001">
<reason>Required — why removed</reason>
<migration>Optional — how integrations adapt</migration>
</removed>
<renamed ref_old="fr-as-002" ref_new="fr-auth-002">
<reason>Optional — semantic change explanation</reason>
</renamed>
</delta>
</overview>
Rules for the new (current sprint's) delta:
delta.version MUST equal the new spec_versiondelta.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.
Before Phase 6 output validation, re-read each requirement's <description> against each <criterion> (and against sibling criteria). Two failure modes to flag:
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)candidate_resolutions: 1–3 concrete user-pickable resolutionsuser_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.
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:
writeConflictMarker (same pattern as Phase 4 block shown above), setting axis_fired: '3'.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.
Before writing any file to disk, validate the in-memory spec:
node -e "
const fs = require('fs');
const { parseSpecHeader, validateSpecHeader } = require('${ARCFORGE_ROOT}/scripts/lib/sdd-utils');
const xml = fs.readFileSync('_draft_spec.xml', 'utf-8');
const parsed = parseSpecHeader(xml);
const result = validateSpecHeader(parsed);
console.log(JSON.stringify(result, null, 2));
"
Phase 6 runs two checks:
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.
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.6b — Axis-3 mechanical authorization check (R3-axis block, writes conflict file):
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.
Before writing files, confirm:
<trace> element<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)<detail_file path="..."> references point to files that will be written<spec-id>:v<N><delta> element present and well-formed (when prior spec exists): version/iteration match, all refs resolve<delta> is preserved verbatim — <overview> contains the full ascending sequence<delta> is the last child of <overview>validateSpecHeader returns no ERROR)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))<trace> source = invention)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.
After generating or updating specs:
git add specs/<spec-id>/
git commit -m "docs: refine spec for <spec-id>"
Hand off to /arc-planning — the planner reads specs/<spec-id>/spec.xml and the latest <delta> (via parsed.latest_delta) to scope the planning sprint.
✅ refiner complete
<spec-id><overview>: N (new delta appended as last child)specs/<spec-id>/spec.xml + N detail files (committed)/arc-planning⚠️ refiner blocked
<spec-id>specs/<spec-id>/_pending-conflict.md (ephemeral handoff — brainstorming reads and deletes it)/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.
npx claudepluginhub gregoryho/arcforge --plugin arcforgeGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.