Repository navigation
S2.3.1 — task-create - #345
Merged
Merged
Conversation
Build target 3. CLAIM-47.1's real input is the Design's ## Build Targets table, not binding.unitOfWork — the seeded wording is a type error, since UnitSpec is five readonly string fields declared singular and validated with isPlainObject. Restated at the gate as G1. THE COLUMN IS FOUND BY NAME. Measured over the 12 Designs on disk, the table ships in three header shapes; 41.md, 293.md and 47.md lead with a # column, putting Target in position 2. A positional reader returns '1', '2', '3' for a third of the corpus — row numbers plausible enough to survive review. The test asserts both shapes are really present, or the by-name lookup would be untested in practice. The parser refuses rather than copes: missing section, renamed column, ragged row, absent separator, header with no rows. #323's M5 is the precedent — a parser that tolerates the structure it depends on cannot report a violation of it. Blocked by: was already rendered inside renderPrBody. CLAIM-47.2 puts the same directive on task issue bodies, so the FORM is extracted to gh/blocked-by.ts on first reuse rather than on the third. pr.ts's tests are untouched and green — the regression guard that the move is behaviour-preserving — and a new test asserts both consumers emit the same line rather than merely similar ones. Refs #48, #47
…ut to cost Build target 8a. The body passes all 18 skill-lint rules first time and cites 4 discretionary references, inside the cap of 5. THE REST OF THIS COMMIT IS THE POINT OF #344. Adding one SKILL.md turned 20 tests red across the harness: PINNED_SKILL_COUNT in two places, two hardcoded 4s in transcription-gap, case 6's threshold, the DOMAIN_RESOLVING_SKILLS partition, the scenario roster's uncovered-skills assertion, two roster-length pins, case 8's identity-key mapping, and the transcription audit's denominator. None of it is named by S2.3's Design or by #48, because #293 owned the harness and this Story was told not to pre-empt it — a deferral that expired when #293 closed. The transcription is written but NOT claimed faithful. It and the body come from the same task, so a faithfulness verdict here is self-attestation, which is exactly what #322 declined to do. Its five audit rows are recorded UNAUDITED and the independent audit is tracked by #344. Case 8's ROW_CLASS keys on the verbatim cell text and caught a real drift: the map said 'Does the Story already carry' where the body says 'Does the Story body already carry'. A looser key would have classified the wrong row and stayed green. Refs #48, #47, #344
M3 — the H2 section bound was never the binding one: in every fixture the leading-contiguous-run scan stopped at the same place. The two diverge only where a Build Targets section carries NO table and a later section does, and there the unbounded parser returns another section's table as the build targets. Confident, wrong, plausible. Now pinned. M5 — the rows.length === 0 guard was unreachable: table.length < 3 already rejects a header and separator with nothing after. Removed, with a note, rather than left as a comment that reads like a check. M8 and M11 — both in the transcription, both surviving the full suite AND a green skill-harness, because the roster scenario always supplies the domain label and never asserts the seeded body survives. M11 IS THE ONE THAT MATTERS: it reproduces, inside this transcription, the exact defect #315 existed to fix — building the ## Tasks section and sending it as the whole body. Against the roster's one-line seed nothing observable changed, which is how the original shipped. The new test uses a body carrying a ruling INSIDE ## Tasks, which is what every real Story has. The harness proves the transcription is idempotent. It does not prove it is correct, and those are different claims. Round: 11 mutations, 7 killed on the first pass, 4 chased down. Refs #48
Three of the five Done-when bullets were not satisfied against the tree. Bullet 5 asked for a `## Re-entry` table whose every mutating step is preceded by its read. Parenting is a mutating step and its path is chosen by `subIssueCapabilityProbe` -- a read that appeared nowhere in the body. The probe is now Phase 0 item 6 and Re-entry row 3, the transcription reads it before the create loop, and the fake forge MODELS it rather than answering `unmodelled`: that exit code is documented as "a gap in the double, never a forge refusal" and reading it as "absent" would be exactly the silent-permanent-fallback failure sub-issues.ts:15-27 names. The fake honours the feature header too, so the hazard is tested, not just stated. Bullet 3 asked that the checklist MERGE. The transcription skipped: once `## Tasks` existed it wrote nothing, under a comment claiming "MERGED" and crediting #315's merger it never called. On the re-run the body itself names -- "after a gate ruling adds a row" -- the new task opened and its checklist entry never arrived, on every subsequent run. Both paths now go through `withTasksChecklist`, guarded so a no-op merge issues no write. Bullet 2's body promised that an invented claim anchor "fails claim-lint". It cannot: `anchor-dangling` is scoped to `anchors_to` cells in docs/test-plans/*.md and claim-lint never reads an issue body. The bullet now says model-judged, and says where the anchor IS checked. Also: the row-1 gate was a substring check that 47.md's own prose would have passed; it is the parser's verdict now. Eight mutations run, four survived -- the parser gate, the absent-capability branch, the header hazard and the graphql classifier were all unpinned -- and eight tests close them. 47.md's "11 of 11 Designs" was stale at 12. Refs #47
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.
Build targets 3 and 8a of
docs/design/stories/47.md. AnchorsCLAIM-47.1,CLAIM-47.2,CLAIM-47.4.Evidence:
docs/evidence/48-20261001T041135Z.md.The whole chain was green and the task was not done
This task was resumed from an interrupted session. Three implementation commits were in, the tree was clean, and every gate passed on the first run — 1228 tests, 0 failures, lint, typecheck, skill-lint and claim-lint clean, harness verdict pass.
Three of the five Done-when bullets were still unsatisfied. Only resolving each bullet against the tree surfaced them. A suite proves the code does what the tests say; it cannot prove the tests say what the issue asked for.
Bullet 2 — the body promised an enforcement that does not exist
SKILL.mdclosed its anchoring requirement with "a task anchored to an invented identifier failsclaim-lint." It does not.anchor-danglingis scoped toanchors_tocells indocs/test-plans/*.md(packages/core/src/guards/claim-lint.ts:404-435), and claim-lint's scope is four directories on disk (scripts/claim-lint.ts:58) — it never reads an issue body. A task citingCLAIM-47.99would have passed silently, forever.Now stated as model-judged, not tool-checked, naming where the anchor genuinely is checked.
Bullet 3 — the merge was a skip wearing a comment that said "MERGED"
The transcription never called
withTasksChecklist, and once## Tasksexisted it wrote nothing at all — under a comment claiming the checklist was "MERGED into it" and crediting #315. A test pinned the skip as intended.The body names the exact case that breaks: "after a gate ruling adds a row to the Design's table." The new row's task issue opens — the create keys on title — then the body step sees
## Tasksand returns. The checklist never gains the entry, on that run or any run after it.Both branches go through #315's merger now, its refusal is a hard failure rather than a guess, and the write is guarded so a no-op merge issues no call — which is what keeps
run-2-mutatedhonest rather than merely green.Bullet 5 — the one mutating step with no read in front of it
Parenting is a mutation whose path is chosen by a read,
subIssueCapabilityProbe(packages/core/src/gh/sub-issues.ts:63-80).SKILL.mdsaidsub-issues.ts"owns both paths and the choice between them" and never mentioned the probe — no Phase 0 item, no## Re-entryrow. The transcription hardcoded the fallback.That read is not a formality.
sub-issues.ts:15-27records it: on 2026-09-01 asubIssuesquery on #243 returned empty and read as the fallback path when all three of its tasks were real sub-issues. Without the feature header the API returns no nodes and no error.unmodelledis not an answer. The obvious fix — treat a non-zero probe exit asabsent— is the bug:fake-forge.ts:371-383documents that exit code as "a gap in the double, never a forge refusal." So the probe is genuinely modelled, and the fake honours the feature header — the same probe sent without it answersabsenton a fake configuredpresent. The 2026-09-01 hazard is a test now, not a comment.Row 3 added, old rows 3/4/5 renumbered, both paths scenario'd with distinct seed hashes.
The mutation round, and why four survivors is the result
8 run, 7 killed, 1 deliberate non-kill. M1–M3 died on the first pass. M4, M5, M6 and M7 all survived — every one a behaviour added by this session with no test behind it: the parser gate, the absent-capability branch, the feature-header hazard, and the graphql mutation classifier. Eight tests close them, each verified by re-applying its mutation and confirming the new test goes red.
M4 matters most: the row-1 gate was an
includes("## Build Targets")substring check, which47.md's own prose passes at:70and:466.test/build-targets.test.ts:140-147had already named the hazard and nothing held the transcription to it. It is the parser's verdict now.M8 is a non-kill on purpose — it guards a vacuous-truth case unreachable with 12 Designs on disk.
Case 17 is NOT CLAIMED
Its denominator is "3 of 3, one artifact row per skill" over the three new S2.3 bodies. Only
task-createexists. #48 can discharge at most a third and therefore claims none. The tool-checked half — the row count read off each body at run time — is green.CLAIM-293.11stays deferred to #344, alltask-createaudit rows UNAUDITED. Two of this task's three findings were divergences between the body and its transcription, which is the argument for an independent audit, not against it. The audit's denominator moved 21 → 22 and its five stalepath:linecitations were re-derived.Verification
1251 tests / 1237 pass / 0 fail / 14 skip / 4624
expect()· lint 0 across 132 files · typecheck clean · skill-lint 5 + 12, 0 errors, 18 rules · claim-lint 132 files, 0 errors, 1 warning (pre-existing,docs/design/stories/9.md) · skill-harness 9 scenarios / 5 skills, verdict pass.The baseline was red.
cd74902had 55 failing tests — the roster, the pinned skill count and the injection roster all expected a fifth skill that did not yet exist. Delta: +178 tests, +1036expect(), +1 skill, +1## Re-entryrow.Also:
47.md:491said "11 of 11 Designs parse" with 12 on disk, and one loop over the real corpus had no non-zero denominator guard. Both fixed.Refs #48, #47