Skip to content

S2.3.1 — task-create - #345

Merged
dev-pmallapp merged 5 commits into
story/47-s2-3-execution-skillsfrom
task/48-task-create
Oct 1, 2026
Merged

dev-pmallapp merged 5 commits into
story/47-s2-3-execution-skillsfrom
task/48-task-create

Conversation

@dev-pmallapp

Copy link
Copy Markdown
Owner

Build targets 3 and 8a of docs/design/stories/47.md. Anchors CLAIM-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.md closed its anchoring requirement with "a task anchored to an invented identifier fails claim-lint." It does not. anchor-dangling is scoped to anchors_to cells in docs/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 citing CLAIM-47.99 would 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 ## Tasks existed 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 ## Tasks and 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-mutated honest 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.md said sub-issues.ts "owns both paths and the choice between them" and never mentioned the probe — no Phase 0 item, no ## Re-entry row. The transcription hardcoded the fallback.

That read is not a formality. sub-issues.ts:15-27 records it: on 2026-09-01 a subIssues query 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.

unmodelled is not an answer. The obvious fix — treat a non-zero probe exit as absent — is the bug: fake-forge.ts:371-383 documents 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 answers absent on a fake configured present. 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, which 47.md's own prose passes at :70 and :466. test/build-targets.test.ts:140-147 had 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-create exists. #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.11 stays deferred to #344, all task-create audit 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 stale path:line citations 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. cd74902 had 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, +1036 expect(), +1 skill, +1 ## Re-entry row.

Also: 47.md:491 said "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

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
@dev-pmallapp dev-pmallapp mentioned this pull request Oct 1, 2026
5 tasks
@dev-pmallapp
dev-pmallapp merged commit 592a54c into story/47-s2-3-execution-skills Oct 1, 2026
8 checks passed
@dev-pmallapp
dev-pmallapp deleted the task/48-task-create branch October 1, 2026 05:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants