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
| Skill | Documents | Templates require | Hit? |
|---|---|---|---|
10-ideation.md (3 mentions) | ideation.md | idea.md (full-feature), research-question.md (spike) | yes — twice |
50-testing.md (3 mentions) | testing.md | test-results.md | yes |
60-deployment.md (3 mentions) | deployment.md | deployment-record.md | yes (app-operational-skills) |
70-maintenance.md (2 mentions) | maintenance.md | maintenance-plan.md | not 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_artifacts — idea.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.