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
[Bug]: a Markdown heading inside the Plan Approval section silently ends it — the [Approval Fingerprint] / [Answer] below are ignored and the error blames the fingerprint #1022
latestPlanApproval in aidlc-testing-posture.ts delimits the Plan Approval section by Markdown headings only, and any heading — at any level — closes it. A heading written inside that section (e.g. an explanatory ### Why re-approval is needed) makes the parser drop the [Approval Fingerprint]: and/or [Answer]: lines below it. The resulting failure blames the fingerprint or the answer and never mentions the heading, so the actual cause is effectively invisible.
Nothing in code-generation.md Step 3 or in stage-protocol.md states that the Plan Approval section is heading-delimited, so authoring a heading there is not a documented violation.
Verified against v2 @ a277af2 (AIDLC_VERSION = 2.7.1).
Mechanism (read from source)
Line numbers are from the shipped .claude/tools/ copy at 2.7.1; the functions are named so they can be located if the numbers drift.
Location
What it does
aidlc-testing-posture.ts:150MARKDOWN_HEADING_RE
/^(#{1,6})[ \t]+(.+?)[ \t]*#*[ \t]*$/ — matches every heading level
on every heading line it reassignsinPlanApproval = isPlanApprovalLabel(headingText…). There is no "still inside the section" branch, and no comparison of heading levels — a ### under a ## Plan Approval sets inPlanApproval = false.
same, line 891
if (!inPlanApproval) continue; — from that point on, ANSWER_TAG_RE (line 151) and FINGERPRINT_TAG_RE (lines 152-153) are never evaluated
aidlc-testing-posture.ts:922questionsFileApprovalFingerprint → :1055codeGenerationApprovalArtifacts (recordedFingerprint, line 1110) → :1533codeGenerationPlanApprovalQuestionEvidence
consumer chain
same, lines 1568-1572
if (artifacts.recordedFingerprint !== artifacts.expectedFingerprint) throw new Error("Plan Approval fingerprint does not match the active intent, target, directive epoch, plan, instructions, and Testing Contract")
same, lines 1573-1578
throw new Error("Plan Approval questions file must contain exactly [Answer]: …")
both call codeGenerationPlanApprovalQuestionEvidence, so both prescribed commands hit this
So a heading in the section makes recordedFingerprint come back null, which fails the equality at line 1568 and produces the fingerprint error — while the [Approval Fingerprint]: line is present in the file and equals what aidlc-testing-posture.ts fingerprint just printed.
Two different symptoms, depending on where the heading sits
Measured by calling the exported readers directly (script below):
Questions file content
questionsFileApprovalFingerprint
questionsFileApproved
questionsFileHasPendingPlanApproval
## Plan Approval + fingerprint + [Answer]: A. Approve Plan
fingerprint
true
false
same, with ### Why re-approval is neededabove the fingerprint
null
false
false
same, with #### Optionsbetween the fingerprint and the answer
fingerprint
false
false
same, with the heading replaced by **Why re-approval is needed**
fingerprint
true
false
same, with a > blockquote note
fingerprint
true
false
same, with a - bullet note
fingerprint
true
false
Row 2 fails at line 1568 (fingerprint error). Row 3 fails at line 1573 (must contain exactly [Answer]: Approve Plan) even though the answer is literally in the file. Only headings do this; blockquotes and lists are fine.
Isolated reproduction (no workflow state required)
// probe.ts — run with: bun run probe.tsimport{questionsFileApproved,questionsFileApprovalFingerprint,}from"./.claude/tools/aidlc-testing-posture.ts";constFP="sha256:"+"a".repeat(64);consttail=`\n[Approval Fingerprint]: ${FP}\n\nA. Approve Plan\nB. Request Changes\n\n[Answer]: A. Approve Plan\n`;constclean=`## Plan Approval\n\nApprove this exact Code Generation plan?\n${tail}`;constheading=`## Plan Approval\n\nApprove this exact Code Generation plan?\n\n### Why re-approval is needed\n\nThe plan changed after the previous approval.\n${tail}`;constbold=`## Plan Approval\n\nApprove this exact Code Generation plan?\n\n**Why re-approval is needed**\n\nThe plan changed after the previous approval.\n${tail}`;for(const[name,body]of[["clean",clean],["heading",heading],["bold",bold]]asconst){console.log(name,{fingerprint: questionsFileApprovalFingerprint(body),approved: questionsFileApproved(body),});}
Run any scope to CONSTRUCTION code-generation Step 3, up to the point where the stage file says to write the hash "into the Plan Approval section".
In <record>/…/code-generation/code-generation-questions.md, add one explanatory Markdown heading inside the Plan Approval section, above [Approval Fingerprint]: — for example ### Why re-approval is needed.
Run the prescribed bun .claude/tools/aidlc-log.ts decision --stage code-generation --checkpoint plan-approval --questions-file … --session … --decision … --options "Approve Plan,Request Changes" --unit ….
It fails with Plan Approval fingerprint does not match the active intent, target, directive epoch, plan, instructions, and Testing Contract, although the fingerprint line is present and matches the value aidlc-testing-posture.ts fingerprint printed a moment earlier.
Change nothing except the heading — make it a bold paragraph, **Why re-approval is needed** — and the same command succeeds.
What we observed vs. what we read. Steps 4 and 5 are what we hit in a real run: a heading added to explain a re-approval broke the checkpoint, and converting it to a bold paragraph fixed it. The causal chain in the Mechanism section, the two-symptom table, and the isolated script are from reading and executing the shipped sources afterwards — not inferred from the run.
Why this is expensive to diagnose
The message names six inputs (intent, target, directive epoch, plan, instructions, Testing Contract). The questions file's structure is not one of them, and the file's own content looks correct on inspection.
Step 3 of aidlc-common/stages/construction/code-generation.md says to write the hash "into the Plan Approval section" and gives no constraint on the section's internal structure. stage-protocol.md's questions-file rules say nothing about it either. A conductor adding a heading is following the documentation.
Internal inconsistency worth noting
aidlc-unit.ts reads the same questions file with whole-file, non-section-scoped regexes: /^\[Approval Fingerprint\]:\s*(sha256:[0-9a-f]{64})\s*$/m (line 1617) and /^\[Answer\]:\s*A\.\s*Approve Plan\s*$/m (line 1664). Those match regardless of the heading. So one reader can consider the questions file well-formed while latestPlanApproval sees an empty section — the two readers do not agree on what "the Plan Approval section" is.
Suggested directions (not a patch)
Do not let a nested heading close the section. Track the opening heading's level and close only on a heading of the same or shallower level (or only on another top-level question heading). A ### under ## Plan Approval would then stay inside, which is what a Markdown author reasonably expects.
At minimum, document the constraint in code-generation.md Step 3 and in the questions-file rules of stage-protocol.md, and consider aligning aidlc-unit.ts with whichever scoping rule is chosen.
(1) and (2) are complementary; (2) is valuable independently of whether (1) is adopted.
Environment
v2 @ a277af2, AIDLC_VERSION = 2.7.1, Claude Code harness, dist/claude
macOS (darwin 25.5.0), bun 1.4.0
Parser behaviour measured by calling questionsFileApproved / questionsFileApprovalFingerprint / questionsFileHasPendingPlanApproval directly; the wiring from those readers to the two error messages was read from source, not executed end to end.
Summary
latestPlanApprovalinaidlc-testing-posture.tsdelimits the Plan Approval section by Markdown headings only, and any heading — at any level — closes it. A heading written inside that section (e.g. an explanatory### Why re-approval is needed) makes the parser drop the[Approval Fingerprint]:and/or[Answer]:lines below it. The resulting failure blames the fingerprint or the answer and never mentions the heading, so the actual cause is effectively invisible.Nothing in
code-generation.mdStep 3 or instage-protocol.mdstates that the Plan Approval section is heading-delimited, so authoring a heading there is not a documented violation.Verified against
v2@a277af2(AIDLC_VERSION = 2.7.1).Mechanism (read from source)
Line numbers are from the shipped
.claude/tools/copy at 2.7.1; the functions are named so they can be located if the numbers drift.aidlc-testing-posture.ts:150MARKDOWN_HEADING_RE/^(#{1,6})[ \t]+(.+?)[ \t]*#*[ \t]*$/— matches every heading levelaidlc-testing-posture.ts:855-902latestPlanApprovalinPlanApproval = isPlanApprovalLabel(headingText…). There is no "still inside the section" branch, and no comparison of heading levels — a###under a## Plan ApprovalsetsinPlanApproval = false.if (!inPlanApproval) continue;— from that point on,ANSWER_TAG_RE(line 151) andFINGERPRINT_TAG_RE(lines 152-153) are never evaluatedaidlc-testing-posture.ts:922questionsFileApprovalFingerprint→:1055codeGenerationApprovalArtifacts(recordedFingerprint, line 1110) →:1533codeGenerationPlanApprovalQuestionEvidenceif (artifacts.recordedFingerprint !== artifacts.expectedFingerprint) throw new Error("Plan Approval fingerprint does not match the active intent, target, directive epoch, plan, instructions, and Testing Contract")throw new Error("Plan Approval questions file must contain exactly [Answer]: …")aidlc-log.ts:262-268(decision),aidlc-log.ts:614-618(answer)codeGenerationPlanApprovalQuestionEvidence, so both prescribed commands hit thisSo a heading in the section makes
recordedFingerprintcome backnull, which fails the equality at line 1568 and produces the fingerprint error — while the[Approval Fingerprint]:line is present in the file and equals whataidlc-testing-posture.ts fingerprintjust printed.Two different symptoms, depending on where the heading sits
Measured by calling the exported readers directly (script below):
questionsFileApprovalFingerprintquestionsFileApprovedquestionsFileHasPendingPlanApproval## Plan Approval+ fingerprint +[Answer]: A. Approve Plantruefalse### Why re-approval is neededabove the fingerprintnullfalsefalse#### Optionsbetween the fingerprint and the answerfalsefalse**Why re-approval is needed**truefalse>blockquote notetruefalse-bullet notetruefalseRow 2 fails at line 1568 (fingerprint error). Row 3 fails at line 1573 (
must contain exactly [Answer]: Approve Plan) even though the answer is literally in the file. Only headings do this; blockquotes and lists are fine.Isolated reproduction (no workflow state required)
Output on
a277af2/ bun 1.4.0:In-workflow reproduction
code-generationStep 3, up to the point where the stage file says to write the hash "into the Plan Approval section".<record>/…/code-generation/code-generation-questions.md, add one explanatory Markdown heading inside the Plan Approval section, above[Approval Fingerprint]:— for example### Why re-approval is needed.bun .claude/tools/aidlc-log.ts decision --stage code-generation --checkpoint plan-approval --questions-file … --session … --decision … --options "Approve Plan,Request Changes" --unit ….Plan Approval fingerprint does not match the active intent, target, directive epoch, plan, instructions, and Testing Contract, although the fingerprint line is present and matches the valueaidlc-testing-posture.ts fingerprintprinted a moment earlier.**Why re-approval is needed**— and the same command succeeds.What we observed vs. what we read. Steps 4 and 5 are what we hit in a real run: a heading added to explain a re-approval broke the checkpoint, and converting it to a bold paragraph fixed it. The causal chain in the Mechanism section, the two-symptom table, and the isolated script are from reading and executing the shipped sources afterwards — not inferred from the run.
Why this is expensive to diagnose
--resume), code-generation: the Plan Approval fingerprint is unsatisfiable after any compliant execute+review cycle #987 (plan bytes mutated by checkbox ticks and the## Reviewappend), [Bug]: Code Generation pre-planning source floor can become permanently unsatisfiable #981 (source floor). Searching the message leads to those theories first; none of them apply here, and none of the documented remedies (fresh process,--resume, reverting the plan) moves this one.aidlc-common/stages/construction/code-generation.mdsays to write the hash "into the Plan Approval section" and gives no constraint on the section's internal structure.stage-protocol.md's questions-file rules say nothing about it either. A conductor adding a heading is following the documentation.Internal inconsistency worth noting
aidlc-unit.tsreads the same questions file with whole-file, non-section-scoped regexes:/^\[Approval Fingerprint\]:\s*(sha256:[0-9a-f]{64})\s*$/m(line 1617) and/^\[Answer\]:\s*A\.\s*Approve Plan\s*$/m(line 1664). Those match regardless of the heading. So one reader can consider the questions file well-formed whilelatestPlanApprovalsees an empty section — the two readers do not agree on what "the Plan Approval section" is.Suggested directions (not a patch)
###under## Plan Approvalwould then stay inside, which is what a Markdown author reasonably expects.foundis true butfingerprint/answerisnull, a whole-file scan can cheaply tell whether the tag exists further down; if it does, say so — e.g.Plan Approval fingerprint found outside the Plan Approval section: the heading on line N ends the section. This alone converts the failure from an unguided hunt into a one-line fix, and it also disambiguates this cause from 2.6.124: code-generation plan-approval livelock — /aidlc --resume resets the approval epoch it demands #975 / [Bug]: Code Generation pre-planning source floor can become permanently unsatisfiable #981 / code-generation: the Plan Approval fingerprint is unsatisfiable after any compliant execute+review cycle #987, which share the current wording.code-generation.mdStep 3 and in the questions-file rules ofstage-protocol.md, and consider aligningaidlc-unit.tswith whichever scoping rule is chosen.(1) and (2) are complementary; (2) is valuable independently of whether (1) is adopted.
Environment
v2@a277af2,AIDLC_VERSION = 2.7.1, Claude Code harness,dist/claudequestionsFileApproved/questionsFileApprovalFingerprint/questionsFileHasPendingPlanApprovaldirectly; the wiring from those readers to the two error messages was read from source, not executed end to end.