Repository navigation
fix(aidd-pm): give a spec gap one home, and refuse only one written elsewhere - #969
Merged
Merged
Conversation
The TBD marker had a form but no place, so two runs on the same feature could put the same gap in different sections. Gaps are now listed once under a required Open Questions section, and a residual question makes the spec invalid. The new criterion weighs nothing, so the pass threshold is unchanged. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
The Deliver zone wrote this plan and its phase, so it is committed with the work it describes. It carries the backlog link to #626 and the five decisions the implementation was not free to re-make, including the two the ticket got wrong: SKILL.md needs no change, and the validator gains no weighted criterion. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
…et refine clear entries Review of a4d185a found two gaps. First, the rule and both test rows said "no required section carries a marker". Context and Stakeholders are optional, so a gap listed under Open Questions and copied into Context passed every check while reproducing the second run measured in #626. The rule now reads "every other section", in tbd-marker.md and in both test rows. Second, refine only listed what was still unanswered. With the marker no longer inside its field, rewriting a field no longer deleted it, so one stale entry could invalidate a spec whose content was complete. Step 4 now removes answered entries first and writes None when none remain, with a test row. Also drops the placeholder bullet from the spec template and the criterion's self-description from the validator. Weights and threshold are unchanged. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
A checker found the plan carrying a false reason: check-doc-duplication.js scans only docs, the two memory banks and the READMEs, so it could never have failed a sentence shared inside a skill. The decision stands on one fact, one home; the reason did not. The same round found one noun written four ways across the skill, the broadest of which now blocks validation. The plan fixes the unit: a gap is an unresolved decision a required section depends on, and what no required section depends on is not a gap. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
… spec-wide Finding 1: the spec skill named one unit four ways (unresolved decision, gap, missing required field, field still unanswered). tbd-marker.md now defines a gap once, as an unresolved decision a required section depends on, excluding what no required section depends on and implementation detail. 01-build, 02-refine, the template and the validator point at it. Finding 2: the open_questions criterion now states that no TBD marker appears outside Open Questions anywhere in the spec, Context included, so an evaluator reading the yml alone has one reading. Weights and pass_threshold are unchanged. Also: 02-refine removes an entry only once its decision is fully answered, and rewrites it to the remaining question otherwise. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
The validator now refuses both cases phase-1 named and never ran: a residual entry, and a None section with a marker under Context. Placement and the measured case hold. The thin-request runs failed their criterion. Two of three still ask which fields the CSV must carry, which no required section depends on. The unit bounds where a gap is written, not which questions are raised. Also repairs a sentence in tbd-marker.md that read "nowhere else, anywhere in the spec". Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
The heading was hardcoded in five files. The template owns it now, and nothing else names it. Cutting the text also closed the over-ask the previous evidence caught: on the same thin request, drafts went from 5, 4 and 6 entries with CSV field lists and refusal wording to 2, 4 and 2 with none. The rule did not change, only its length. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
It said answered twice and rewrite twice. Three clauses, three behaviours, no word spent on a second pass. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
…draft The gap rules were lean and the text around them was not, so the skill still read as mostly prose. 01-build loses a quarter of its words, 02-refine and the template guidance the rest: the restated sub-bullet, the parenthetical listing the optional sections the template already marks, the three example verbs, and the two paragraphs explaining the backlog declaration. Every rule is kept. A fresh context given the shortened step 6 still writes backlog-link.json for a named ticket, writes none for no ticket, and leaves an existing file untouched for a different one. A fourth draft of the thin request listed three gaps, none of them presentation detail. This goes past the issue, on the user asking for the existing text to be cut too. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
A checker measured what the last cut cost, and two of the three were real. Refine lost "None when none remains", so resolving the last gap left an empty section 3 times out of 3, and the validator refuses that. The validator lost the definition of a gap, so an evaluator reading the yml alone supplied its own and refused a spec whose content was complete. Refine never linked the template, so on a spec carrying no such section it invented the heading in lower case 3 times out of 3. All three now pass 3 of 3. 01-build gets back what written_by holds, which its own test row requires. The journey no longer claims the cut caused the shorter drafts: that cut changed several clauses at once, so length was never isolated. It reports what is measured instead, including 0 markers outside the section across 22 drafts and None on a fully answered request 3 times out of 3. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
Eight sessions reading SKILL.md from a translated sandbox and following it. Placement holds: the section exists 8 of 8, no marker outside it 8 of 8. Zero does not: a request answering every required field still lists entries 3 of 3, and the new hard threshold then refuses that spec. The pasted-text runs were optimistic. They measured None 3 of 3 where the router measures 0 of 3, because the router reads more files and asks more. Also records why the first installed-skill attempt measured nothing: the Skill tool resolves to the user-scope install whatever a project declares, and a project marketplace does not register in a headless session. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
…pen does not Measured through the installed tree: a request answering every required field still lists 3, 2 and 2 entries, always the column set and when the seven days start. Neither is a decision a required section depends on. So the threshold added here refused specs where nothing was missing, and before this branch an extra question cost nothing. open_questions_unresolved becomes open_questions_outside_their_section. A listed gap is valid, a gap written in any other section is not, which is the defect the issue measured. Driving to zero moves to the template as the intention it is, and to #625 as the only thing that will enforce it. Also closes four record findings: the plan claimed one home for a definition the validator must repeat, phase-1 still framed the rule on required sections, 01-build had lost what a backlog item is and the one-support rule, and two files named two authorities for one check. Measured, three runs each: a listed gap valid 3/3, a gap under Context invalid 3/3, None valid 3/3. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
…draft The backlog declaration was a contract inside a step, so it moves to its own reference and the step becomes one line. Four duplications go with it: the template already gives headers, bullets and a non-goals section, so the transversal rule saying to was redundant; the drop-implementation-detail clause is already transversal; the validator repeated the plan boundary its own hard threshold carries; and two files named two authorities for one section check. The assets lose their padding. The template keeps one placeholder bullet per list instead of two and states each rule once. The validator reads as a checklist again, every criterion one line, arithmetic untouched: required 90, total 100, pass_threshold 90, five hard thresholds. 1379 words to 1050, with a section and a reference added since. Measured on the rebuilt tree: every required section present 4 of 4, the section exists 4 of 4, no marker outside it 4 of 4, and the three validator cases unchanged at 2 of 2 each. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
The JSON was a scaffold inside a reference, so it moves to assets/backlog-link-template.json beside the spec template, and the reference keeps only the rules. The guard that pins the taught field names follows it. Tested through the tree, which the declaration never had been and refine never had at all: a named ticket writes the file with all three fields, no ticket writes none, an existing file for another item is left untouched, and refine drops a resolved entry then reads None on the last. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
…ts heading The template shipped `## Stakeholders (optional)`, and a drafted spec carried the parenthesis into its own heading. Optionality belongs to the template that teaches it, not to the artefact. Tested through the tree: no drafted spec carries it, 6 runs out of 6. Two paths nothing had exercised, both green. From a PRD, 3 runs out of 3 carry every required section and lift the PRD own open question as the one gap. On a second model, 3 runs out of 3 place every gap in the section, which settles that the over-asking is not one model habit: 6, 7 and 7 entries against 6 to 8. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
blafourcade
marked this pull request as ready for review
October 7, 2026 13:36
…ced block CI caught what no local gate could: the spec skill ships its backlog-link shape as an asset now, and this guard extracted the first fenced json block out of the action, which no longer has one. It read null and failed, exactly as a guard should. The loader accepts both forms, the asset bytes or the fenced block, so each skill still feeds its own example through the real adapter. Worth noting: the pre-push gate globs cli/**, so a plugins-only change runs no cli test locally, while cli-ci.yml runs them. That is the gap this slipped through. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
The loader docblock restated its own body, and the header docblock explained which skill ships which form, which the two paths say. One line stays: why the real adapter and not a stand-in parser, the only thing the code cannot show. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
This was referenced Oct 7, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🎯 What & why
The
TBD:marker had a form and no home, so two runs on the same feature put the same gap in different sections. A spec now has one place for its open questions.🛠️ How it works
spec-template.md:29 owns the section and is the only file that names its heading. tbd-marker.md:5 defines what belongs there, and both actions point at it rather than restating it.
A gap is an unresolved decision a required section depends on. spec-validator.yml:24 repeats that definition, the one deliberate exception to one fact, one home: its evaluator reads the yml alone, and without the definition there, one supplied its own and refused a spec whose content was complete.
What invalidates a spec is a gap written outside the section, not one still open. The first version refused any open gap. Measured through the installed tree, a request answering every required field still lists two or three entries — the CSV column set, when the seven days start — so that rule refused complete work. Before this branch an extra question cost nothing;
open_questions_unresolvedwould have made it a refusal.SKILL.mdis untouched. Its transversal rule names no placement, so there is nothing to retarget. #626 lists the file as affected; it is not.Past the issue, on the author asking for the existing text to be cut too: the skill goes from 1379 to 1050 words. The backlog declaration left step 6 for backlog-link.md, its shape became an asset, and the guard that pins the taught field names follows it.
🧪 How to verify
aidd translate . --to claude --out <dir>, then draft a spec in that tree from a request that leaves a decision open: the gap lands under the open questions section and nowhere else.pnpm exec lefthook run pre-commitopen_questions_outside_their_sectionhas no reader in the pipeline until fix(orchestrator): SDLC Frame hands a TBD-laden spec to Deliver unchecked #625 lands.journey.mdcites counts whose raw outputs live in a session scratchpad, not in the repository.🔗 Linked issue
Closes #626
✅ I certify
🤖 Generated with Claude Code
https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z