Skip to content

v1.10.2 ·

The skills were lying: six places the instructions and the code disagreed

The problem

A skill is what an agent obeys. When a skill documents an artifact name, a file format, or a CLI behaviour that the code does not implement, the agent does what the skill says and the gate rejects it — and the failure reads like the agent forgot something rather than like the instructions were wrong.

Six divergences exist today. Four artifact-name mismatches (three of which cost real detours in a single session, 2026-08-25; the fourth has simply not been reached), the findings-format mismatch, and — found by this instance's own design review — two skills documenting CLI commands that do not work.

Artifact names — skills hardcode a name the templates do not require

SkillDocumentsTemplates requireHit?
10-ideation.md (3 mentions)ideation.mdidea.md (full-feature), research-question.md (spike)yes — twice
50-testing.md (3 mentions)testing.mdtest-results.mdyes
60-deployment.md (3 mentions)deployment.mddeployment-record.mdyes (app-operational-skills)
70-maintenance.md (2 mentions)maintenance.mdmaintenance-plan.mdnot yet

The root cause is that a skill names one fixed filename while the template is the authority and varies per template — ideation alone wants idea.md or research-question.md depending on which template is in play, so no single hardcoded name can be correct.

A second-order consequence: the traceability gate reads ac-coverage blocks from testing.md only (in packages/cli/src/traceability/gate-criterion.ts), while every template requires test-results.md. So the documented escape hatch for indirect AC coverage is unreachable as written — worked around twice this session by writing both files.

Review findings — the gate cannot parse what the CLI itself scaffolds

parseFindingsFromMarkdown (in packages/cli/src/commands/transition.ts) requires a single heading line:

## F1: Some title [critical] [open]

But 80-review.md documents, and commands/review.ts scaffolds, a bullet form:

## Finding 1
- **Severity:** critical
- **Section:** …
- **Issue:** …
- **Suggestion:** …
- **Status:** open

A review performed exactly as documented parses to zero findings, so checkReviewGate blocks the transition with "no findings exist" while the file sits there full of them. Hit twice this session; worked around by rewriting every heading by hand.

How it could be solved

The obvious fix was to correct the filenames — change ideation.md to idea.md and move on. That was rejected, because no single filename is correct. A full-feature instance's ideation phase requires idea.md; a spike requires research-question.md. Naming either one leaves the same bug with new spelling. So the skills now describe the artifact by its role and point at the template: "the artifacts your template's ideation phase declares in required_artifacts", with the common names given as examples rather than as the rule.

Renaming the template artifacts instead — making every template agree on ideation.md — was rejected harder. It would break every completed instance on disk, and it treats a documentation error as a reason to migrate data.

The second decision was whether to write a parser. The design originally specified adding a bullet-form recognizer to the review gate, and that turned out to be the same mistake the instance existed to fix: a complete, tested bullet parser already lived in commands/review.ts, exported, with fifteen passing tests. The real defect was never absence — it was duplication. Two parsers existed for one format and the gate was wired to the weaker one. Writing a third grammar would have been this instance's own bug class, so the fix became a deletion instead: one parser, called from both places.

That change had a consequence worth stating plainly, because it decided how the status vocabulary works. Matching a finding's status against the exact vocabulary open|resolved|accepted-risk|rejected looked obviously right and was measurably wrong: 185 of the 229 status lines already on disk carry trailing prose, like resolved (requirements.md rev 2, 2026-08-18). Under exact matching, four out of five real findings simply vanished — and status === 'open' never matched anything, which means a critical open finding could not block the gate it exists to block. The parser now matches the leading word and keeps the remainder as a note. An unrecognised status is treated as open, and an unrecognised severity as critical: when the input is ambiguous, the safe direction is to block, not to wave work through.

Making the review scaffold parseable created the opposite risk — that an untouched placeholder would satisfy the gate as though a review had happened. Leaving the scaffold unparseable was rejected (it is what hid real findings in the first place), and so was counting it. Instead the placeholder is flagged by two independent sentinels, so rewording one does not silently disarm the guard, and a round-trip test asserts the live scaffold still flags — a reword fails the test rather than the gate. Checking the gate per required artifact rather than over a pooled set closed the last hole: one real finding anywhere used to unblock every unreviewed artifact in the phase.

The final choice was about coverage claims. When both the template-declared artifact and a legacy testing.md exist, their ac-coverage entries could be unioned or ranked. Union loses: a stale claim in the old file would keep a criterion marked covered after the current artifact had dropped it — coverage that nothing asserts any more. Precedence wins instead, and the ignored file is named in a warning, so the situation is visible rather than silent.

How AIDLC solves it

The skills told you to write files the templates never asked for. 10-ideation.md said to produce ideation.md; every template requires idea.md, or research-question.md for a spike. The same gap sat in the testing, deployment, maintenance, idea-capture, and requirements skills. An agent following the instructions produced a file the gate could not see, then the gate reported a missing artifact nobody had been told to write.

Two skills also documented commands that do not exist. aidlc cost report <instance> has no report subcommand — the working form is aidlc cost <instance>. aidlc gate deployment puts a phase name in the slot reserved for an instance name, so it fails looking for an instance called "deployment". Both are corrected, which makes six divergences fixed, not the three the seed ideas described.

Skills now describe the artifact by its role and point at the template: "the artifacts your template's ideation phase declares in required_artifactsidea.md for the feature templates, research-question.md for spike". Naming a different fixed filename would have left the same bug with new spelling, because no single name is correct for both.

Underneath, three code defects that made the same class of mismatch invisible:

The review gate read findings with the wrong parser. Two parsers existed for one format and the gate was wired to the weaker one, so bullet-form findings — the form aidlc review actually writes — were invisible to it. parseFindingsFromMarkdown is deleted; the gate now calls the tested parser in commands/review.ts. Worse, that parser matched status against an exact vocabulary, and 185 of 229 status bullets on disk carry trailing prose like resolved (rev 2, 2026-08-18). Under exact matching, status === 'open' never matched — a critical open finding could not block the gate. Status and severity now match the leading token and keep the remainder as a note; an unrecognised value is treated as open/critical, never skipped.

The scaffolded placeholder could pass for a review. Making the scaffold parseable would otherwise let unreviewed work through, so it is flagged by two independent sentinels, and the gate is evaluated per required artifact instead of over a pooled set. Previously one real finding anywhere unblocked every unreviewed artifact in the phase.

The ac-coverage escape hatch was unreachable. The traceability gate read a hardcoded testing.md while every template requires test-results.md. It now resolves the testing phase's required artifacts from the instance's template, honouring project-local template overrides, with testing.md kept readable for instances that used the old workaround. Where both exist the template-declared file wins and the legacy one is reported as ignored — precedence, not union, so a stale claim cannot keep a criterion covered after the current artifact drops it.

Guarding all of it: test/skill-code-contract.test.ts asserts that no skill documents an artifact no template requires, that every aidlc … command a skill mentions resolves on the built program, and that the documented file formats round-trip through the real parsers. It runs in the normal suite.