You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On a zero-Unit run (a scope where Units Generation is SKIP), a per-unit stage that declares summary_confirmation: required can never be completed. The engine writes the stage's artifacts — including its <stage>-questions.md — to the stage-level path, but the summary-confirmation guard looks for that questions file only under per-unit paths. The file exists, the human answered it, the receipt was minted; the guard still refuses with "its question flow has no <stage>-questions.md file".
The zero-Unit escape hatch already exists in the codebase (usesStageLevelPerUnitArtifacts) and is applied by the artifact resolver — it is simply not applied to the questions-file scanner.
Mechanism
Everything below is on a277af218f0d (current main).
1. The writer resolves the zero-Unit path.resolveArtifactInstances (core/tools/aidlc-artifact-resolution.ts:219-251): for a per-unit stage, when usesStageLevelPerUnitArtifacts(scope, stateContent) is true it returns a single instance at join(record, owner.phase, owner.slug, filename) — i.e. <record>/construction/<stage>/…, with no Unit segment. code-generation.md documents this shape explicitly ("with no synthetic Unit segment").
usesStageLevelPerUnitArtifacts (core/tools/aidlc-lib.ts:21910-21915) is exactly the zero-Unit predicate:
2. The reader does not.summaryQuestionFiles (core/tools/aidlc-lib.ts:6971-6997) branches only on isPerUnitStage(stage) (:113-115, i.e. for_each: unit-of-work):
It takes no scope/stateContent parameter, so it cannot consult usesStageLevelPerUnitArtifacts at all. On a zero-Unit run the artifacts sit at construction/<stage>/, which is itself a child of construction/ — so readdirSync returns the stage slug as a pseudo-unit and the scanner probes construction/<stage>/<stage>/, which never exists.
3. The guard refuses before the escape hatch is ever consulted. In checkSummaryConfirmationEvidence (core/tools/aidlc-lib.ts:7023):
:7049 — let questions = summaryQuestionFiles(projectDir, stage); → []
:7056-7066 — questions.length === 0 and summary_confirmation === "required" → returns {ok:false, message: "Refusing to complete \"<stage>\": its question flow has no <stage>-questions.md file. …"}
:7067-7079 — the only call to usesStageLevelPerUnitArtifacts in this function, reached only after the refusal above, and it guards a different concern (the bolt-DAG unit-coverage check).
4. Writer/reader asymmetry.summaryQuestionEvidence (core/tools/aidlc-log.ts:160-200) accepts any *-questions.md inside the active record via --questions-file, validates its [Answer]: line, hashes it, and mints SUMMARY_CONFIRMATION_RECORDED with Questions File / Questions SHA-256. So the receipt is recorded successfully, pointing at the stage-level path, and only the verifier is unable to find it.
5. Three blocking call sites, all with options.unit === undefined (there is no unit in a zero-Unit run):
Review cannot start, the gate cannot open, and completion is refused — so there is no ordering of the sanctioned steps that gets through.
Affected scopes
Derived from the compiled stage-graph.json + scope-grid.json. Per-unit stages (for_each: unit-of-work) that declare summary_confirmation: required: functional-design, nfr-requirements, nfr-design, infrastructure-design. (code-generation declares no summary_confirmation, so it returns early at aidlc-lib.ts:7045 and is unaffected.)
Shipped scopes with units-generation: SKIP and at least one of those four EXECUTE:
3 of the 11 shipped scopes, plus any composed scope with the same shape. poc, bugfix and express are zero-Unit too but skip all four stages, so they do not hit this.
Steps to Reproduce
# 1. Install the Claude Code distribution into an empty git repo
mkdir demo &&cd demo && git init
cp -r /path/to/aidlc-workflows/dist/claude/.claude/ .claude/
cp -r /path/to/aidlc-workflows/dist/claude/aidlc/ aidlc/
# 2. Create a refactor-scope intent (units-generation SKIP, functional-design EXECUTE)
bun .claude/tools/aidlc-utility.ts intent-create \
--scope refactor --label "demo" --arguments "tidy up a small module"# 3. Run through to functional-design. Because Units Generation was skipped, the# engine resolves this stage's artifacts to the stage-level path, so the# questions file is written to:# aidlc/spaces/default/intents/<record>/construction/functional-design/functional-design-questions.md# 4. Answer the questions and record the consolidated-summary checkpoint.# This SUCCEEDS — the receipt binds to the stage-level path above:
bun .claude/tools/aidlc-log.ts decision \
--stage functional-design --checkpoint summary-confirmation \
--questions-file aidlc/spaces/default/intents/<record>/construction/functional-design/functional-design-questions.md \
--decision "Does this all look correct before I generate the artifact?" \
--options "Looks correct,Request changes" --details "Looks correct"# 5. Generate the artifacts, then try any of the three sanctioned next steps:
bun .claude/tools/aidlc-log.ts review --stage functional-design \
--reviewer aidlc-architecture-reviewer-agent --iteration 1
bun .claude/tools/aidlc-state.ts gate-start functional-design
Expected Behavior
summaryQuestionFiles finds the questions file the engine itself told the stage to write. On a zero-Unit run the guard should verify the single stage-level questions file at <record>/<phase>/<stage>/<stage>-questions.md — the same path resolveArtifactInstances returns — and the freshness/hash/artifact-ordering checks should proceed against it normally.
Actual Behavior
Step 5 fails, repeatedly and identically, regardless of what the questions file contains:
Cannot start review for "functional-design": its question flow has no
functional-design-questions.md file. Create and answer the stage questions, then
record the consolidated summary checkpoint before generating artifacts.
Refusing to complete "functional-design": its question flow has no
functional-design-questions.md file. Create and answer the stage questions, then
record the consolidated summary checkpoint before generating artifacts.
…while <record>/construction/functional-design/functional-design-questions.md exists, carries exactly one [Answer]: Looks correct under ## Consolidated Summary Confirmation, and is named verbatim in the SUMMARY_CONFIRMATION_RECORDED row the engine itself wrote minutes earlier:
The message's own remedy ("create and answer the stage questions, then record the consolidated summary checkpoint") is not actionable — all three of those steps had already succeeded.
What we observed (separate from the code reading above): on a real zero-Unit run we recorded five of these refusals across aidlc-log review and aidlc-state gate-start. The only thing that got us past it was AIDLC_SKIP_SUMMARY_CONFIRMATION_GUARD=1 (aidlc-lib.ts:6917-6919), which is a blunt instrument: it also disables the receipt-freshness check, the questions-file hash binding, and the post-confirmation artifact-write check for the rest of the workflow. Jumping past the stage is worse — it marks the stage [S] (skipped) in aidlc-state.md, which misrepresents what happened.
AI-DLC Version
v2
Release / Commit
a277af218f0d (current main at time of filing).
AI-DLC Phase
Construction
Harness
Claude Code
AI Model
Claude Opus 5
Environment
OS: macOS (Darwin 25.5.0)
Scope: composed scope with units-generation — SKIP, functional-design — EXECUTE (reproducible on the stock refactor scope)
Zero-Unit: no unit-of-work.md, no Unit DAG
Possible fix directions
Thread the escape hatch into the scanner (smallest change). Give summaryQuestionFiles the stateContent that checkSummaryConfirmationEvidence already holds and short-circuit the per-unit branch:
This mirrors resolveArtifactResolution's branch exactly, so writer and reader agree by construction. Callers already pass stateContent at all three blocking sites (aidlc-log.ts:1703, aidlc-state.ts:3364, and the orchestrate report path).
Whichever route, a regression test at the checkSummaryConfirmationEvidence level covering units-generation: SKIP + a summary_confirmation: required per-unit stage would pin it.
Description
On a zero-Unit run (a scope where Units Generation is
SKIP), a per-unit stage that declaressummary_confirmation: requiredcan never be completed. The engine writes the stage's artifacts — including its<stage>-questions.md— to the stage-level path, but the summary-confirmation guard looks for that questions file only under per-unit paths. The file exists, the human answered it, the receipt was minted; the guard still refuses with "its question flow has no<stage>-questions.mdfile".The zero-Unit escape hatch already exists in the codebase (
usesStageLevelPerUnitArtifacts) and is applied by the artifact resolver — it is simply not applied to the questions-file scanner.Mechanism
Everything below is on
a277af218f0d(currentmain).1. The writer resolves the zero-Unit path.
resolveArtifactInstances(core/tools/aidlc-artifact-resolution.ts:219-251): for a per-unit stage, whenusesStageLevelPerUnitArtifacts(scope, stateContent)is true it returns a single instance atjoin(record, owner.phase, owner.slug, filename)— i.e.<record>/construction/<stage>/…, with no Unit segment.code-generation.mddocuments this shape explicitly ("with no synthetic Unit segment").usesStageLevelPerUnitArtifacts(core/tools/aidlc-lib.ts:21910-21915) is exactly the zero-Unit predicate:2. The reader does not.
summaryQuestionFiles(core/tools/aidlc-lib.ts:6971-6997) branches only onisPerUnitStage(stage)(:113-115, i.e.for_each: unit-of-work):It takes no
scope/stateContentparameter, so it cannot consultusesStageLevelPerUnitArtifactsat all. On a zero-Unit run the artifacts sit atconstruction/<stage>/, which is itself a child ofconstruction/— soreaddirSyncreturns the stage slug as a pseudo-unit and the scanner probesconstruction/<stage>/<stage>/, which never exists.3. The guard refuses before the escape hatch is ever consulted. In
checkSummaryConfirmationEvidence(core/tools/aidlc-lib.ts:7023)::7049—let questions = summaryQuestionFiles(projectDir, stage);→[]:7056-7066—questions.length === 0andsummary_confirmation === "required"→ returns{ok:false, message: "Refusing to complete \"<stage>\": its question flow has no <stage>-questions.md file. …"}:7067-7079— the only call tousesStageLevelPerUnitArtifactsin this function, reached only after the refusal above, and it guards a different concern (the bolt-DAG unit-coverage check).4. Writer/reader asymmetry.
summaryQuestionEvidence(core/tools/aidlc-log.ts:160-200) accepts any*-questions.mdinside the active record via--questions-file, validates its[Answer]:line, hashes it, and mintsSUMMARY_CONFIRMATION_RECORDEDwithQuestions File/Questions SHA-256. So the receipt is recorded successfully, pointing at the stage-level path, and only the verifier is unable to find it.5. Three blocking call sites, all with
options.unit === undefined(there is no unit in a zero-Unit run):core/tools/aidlc-log.ts:1702aidlc-log.ts review --stage <s> --reviewer <a> --iteration 1Cannot start review for "<s>": …core/tools/aidlc-state.ts:3348-3368(verifySummaryConfirmationPrecondition)aidlc-state.ts gate-start <s>Refusing to complete "<s>": …core/tools/aidlc-orchestrate.ts(report)aidlc-orchestrate.ts report --stage <s> --result …Refusing to complete "<s>": …Review cannot start, the gate cannot open, and completion is refused — so there is no ordering of the sanctioned steps that gets through.
Affected scopes
Derived from the compiled
stage-graph.json+scope-grid.json. Per-unit stages (for_each: unit-of-work) that declaresummary_confirmation: required:functional-design,nfr-requirements,nfr-design,infrastructure-design. (code-generationdeclares nosummary_confirmation, so it returns early ataidlc-lib.ts:7045and is unaffected.)Shipped scopes with
units-generation: SKIPand at least one of those fourEXECUTE:refactorfunctional-designinfranfr-requirements,nfr-design,infrastructure-designsecurity-patchnfr-requirements3 of the 11 shipped scopes, plus any composed scope with the same shape.
poc,bugfixandexpressare zero-Unit too but skip all four stages, so they do not hit this.Steps to Reproduce
Expected Behavior
summaryQuestionFilesfinds the questions file the engine itself told the stage to write. On a zero-Unit run the guard should verify the single stage-level questions file at<record>/<phase>/<stage>/<stage>-questions.md— the same pathresolveArtifactInstancesreturns — and the freshness/hash/artifact-ordering checks should proceed against it normally.Actual Behavior
Step 5 fails, repeatedly and identically, regardless of what the questions file contains:
…while
<record>/construction/functional-design/functional-design-questions.mdexists, carries exactly one[Answer]: Looks correctunder## Consolidated Summary Confirmation, and is named verbatim in theSUMMARY_CONFIRMATION_RECORDEDrow the engine itself wrote minutes earlier:The message's own remedy ("create and answer the stage questions, then record the consolidated summary checkpoint") is not actionable — all three of those steps had already succeeded.
What we observed (separate from the code reading above): on a real zero-Unit run we recorded five of these refusals across
aidlc-log reviewandaidlc-state gate-start. The only thing that got us past it wasAIDLC_SKIP_SUMMARY_CONFIRMATION_GUARD=1(aidlc-lib.ts:6917-6919), which is a blunt instrument: it also disables the receipt-freshness check, the questions-file hash binding, and the post-confirmation artifact-write check for the rest of the workflow. Jumping past the stage is worse — it marks the stage[S](skipped) inaidlc-state.md, which misrepresents what happened.AI-DLC Version
v2
Release / Commit
a277af218f0d(currentmainat time of filing).AI-DLC Phase
Construction
Harness
Claude Code
AI Model
Claude Opus 5
Environment
units-generation — SKIP,functional-design — EXECUTE(reproducible on the stockrefactorscope)unit-of-work.md, no Unit DAGPossible fix directions
Thread the escape hatch into the scanner (smallest change). Give
summaryQuestionFilesthestateContentthatcheckSummaryConfirmationEvidencealready holds and short-circuit the per-unit branch:This mirrors
resolveArtifactResolution's branch exactly, so writer and reader agree by construction. Callers already passstateContentat all three blocking sites (aidlc-log.ts:1703,aidlc-state.ts:3364, and the orchestratereportpath).Resolve the questions file through
resolveArtifactInstancesinstead of by directory walk, so there is one code path deciding where a per-unit stage's files live. More invasive, but removes the whole class of drift — [Bug]: traceability sensor can never pass on zero-Unit code-generation (poc/bugfix/refactor/security-patch/express) #1011 is the same class of defect in a different component (aidlc-sensor-traceability.ts extractUnitNamederiving a Unit from a path that has none).Whichever route, a regression test at the
checkSummaryConfirmationEvidencelevel coveringunits-generation: SKIP+ asummary_confirmation: requiredper-unit stage would pin it.Relationship to existing issues
options.unit-bearing path for kind-vacuous units. fix: kind-vacuous units no longer deadlock per-unit summary-confirmation routing #912's vacuity filter is present ata277af2(aidlc-orchestrate.ts:5295,:7151-7155) and does not touch the directory the scanner walks — verified that it does not cover this case.## Reviewappend as the post-confirmation artifact write #856 (open) are in the same function but concern the post-receipt artifact-write scan, not the questions-file scan. Orthogonal — a fix here should leave both alone.readdirSync(<record>/construction)above is exactly the "enumerate everyconstruction/child as a unit" pattern that PR calls out. Moving units toconstruction/units/<unit>/changes the directory this scanner walks but does not by itself add the zero-Unit branch — so the two want sequencing.Happy to open a PR for direction 1 with a regression test if that is the preferred shape.