Skip to content

S2.3.5 — withTasksChecklist merger, and the checklist on both capability paths - #340

Merged
dev-pmallapp merged 3 commits into
story/47-s2-3-execution-skillsfrom
task/315-with-tasks-checklist
Sep 21, 2026
Merged

dev-pmallapp merged 3 commits into
story/47-s2-3-execution-skillsfrom
task/315-with-tasks-checklist

Conversation

@dev-pmallapp

Copy link
Copy Markdown
Owner

Refs #315. Refs #47. Build targets 1 and 2. Anchors CLAIM-47.7, CLAIM-47.1. Cases 2 and 12.

No Closes directive, deliberately. NEVER-47.8 — this Story's own anti-claim — forbids a task PR carrying one, and CONTRIBUTING.md:415 records that it would never fire against a story branch anyway.

The defect, measured before anything was changed

At a6ce084, packages/core/src/gh/sub-issues.ts:296-304 built gh issue edit <parent> --repo … --body <checklist> where the body was tasksChecklist's output — only ## Tasks\n\n- [ ] #905 …. The Story's entire body was replaced by its own checklist.

--- emitted `gh issue edit` --body, in full ---
"## Tasks\n\n- [ ] #905\n- [ ] #906"
--- does it retain ANY of the Story body? ---
  ## Problem           retained: false
  ## Claims            retained: false
  CLAIM-99.1           retained: false
  a reviewer reads     retained: false

packages/core/test/gh-sub-issues.test.ts:165-169 asserted the first four argv elements and that the last contained ## Tasks. Both are true of a body that has destroyed everything else. A passing test that is wrong.

What shipped

withTasksChecklist(body, items) — build target 1, modelled on withParentLine one file over: it inserts into an existing body, it is idempotent, and it refuses the ambiguous case rather than guessing.

Behaviour Why
Rewrites only the leading run of entry lines, never the whole section Real Story bodies put load-bearing prose inside ## Tasks — see below
Refuses a body with two ## Tasks sections, naming the count Two checklists are two answers to "what are this Story's tasks"
Refuses entries split by prose, naming the line The structure is asserted, not assumed — #323's lesson
Inherits a tick the caller is silent about; an explicit checked wins A tick is body content too
Rebuilds items through safeOwnValue, never a spread A spread runs a hostile getter; this module requires 0 throws

parentBody is required on both paths and has no default. A default of "" would make the destructive behaviour the fallback for a caller who simply forgot — the defect with a nicer signature. A genuinely empty Story body is stated as "".

Build target 2: the checklist edit is emitted on the GraphQL path too. At a6ce084 that path returned exactly one command and no checklist, so CLAIM-47.1's clause "adds a ## Tasks checklist to the Story" was unreachable on the primary path. Both paths now build the edit through one helper, so they cannot drift on what "adds a checklist" means.

The finding, and it came from the one survivor

M11 — widening the section boundary from ^#{1,2} to ^#{1,3} — survived the first pass, changing not one assertion. Chasing it down found the real defect:

Every real Story body carries load-bearing prose INSIDE the ## Tasks section, after the entries. #47 carries its dependency ordering and the "must not be built twice" ruling there. #293 carries its sequencing rulings there — and on #293 that section runs to the end of the body, so no following heading would stop a section-wide rewrite.

The merger as first written replaced the section from its heading to the next H1/H2. Against #293 it would have deleted every one of those rulings. The fix for a body-destroying defect had reproduced the defect one level down, inside itself, with the full suite green across it.

A green suite over the corpus you thought of is not evidence about the corpus that exists. Every fixture had prose around the checklist; not one had prose inside it, which is what every real Story has.

Mutations — 13 run, 11 killed, 2 void

# Mutation Verdict
M1 Case 12. The merge becomes an overwrite — the a6ce084 defect restored KILLED
M2 The append path drops the body it was appending to KILLED
M3 The rewrite drops everything after the section VOID — pattern gone; M13 carries its intent
M4 Every listed box treated as ticked KILLED
M5 Tick inheritance removed: a human's ticks silently cleared KILLED
M6 Two ## Tasks sections stop being refused KILLED
M7 The section's trailing blanks swallowed VOID — pattern gone
M8 parentBody silently defaults to "" KILLED
M9 Build target 2 undone KILLED
M10 safeOwnValue replaced by direct reads KILLED
M11 An H3 inside the section becomes a boundary SURVIVED, then KILLED
M12 The structure assertion made vacuous KILLED
M13 The rewrite swallows the whole section, prose and all KILLED

M3 and M7 are VOID, not survivors — their patterns stopped existing when the code they targeted was replaced. A mutation whose pattern does not match has measured nothing.

Case 12 is a forward-dated kill and the pre-landing state is recorded as a measured gap, never as a pass. At a6ce084 there was no preservation logic to delete — grep -c withTasksChecklist over packages/core/src returns 0 — so the mutation was inapplicable there, not surviving. M1 pays the kill now.

Two process findings recorded in the artifact

A revert is a mutating operation. The mutation script's git checkout -- <path> ran against a dirty tree and destroyed the section fix before it was committed. Its own post-revert byte-identity assertion fired on mutation 1 rather than at the end of a 13-mutation round; it now refuses to start while those files are dirty.

The task-branch recipe pushes to the story branch. git checkout -b task/… origin/story/… sets the upstream to the story branch, and with push.default = tracking a plain git push -u origin task/… lands three commits directly on it. It did. story/47-s2-3-execution-skills was restored to a6ce084 and the commits re-pushed to their own ref. The existing task/196-revert-direct-push branch suggests this is the second occurrence.

What this does not close

Chain, measured at db04726

bun test        917 tests / 903 pass / 0 fail / 14 skip / 3241 expect()
bun run lint    0 violations across 95 files
bun run typecheck   clean
bun run skill-lint  4 SKILL.md + 12 contract files, 0 errors, 0 warnings
bun run claim-lint  114 files, 0 errors, 1 warning
IAI_PURITY_CHILD=1        9 pass
IAI_BINDING_SWEEP_CHILD=1 5 pass
scripts/verify-required-checks.sh   PASS, six contexts
scripts/verify-workflow-hygiene.sh  PASS

Baseline at a6ce084: 903 tests / 889 pass / 3179 expect(). The expect() total becomes 3243 once the evidence artifact lands — #299, and the delta is a property of the corpus an artifact joins, verified rather than assumed.

Evidence: docs/evidence/315-20260920T112814Z.md

The Story body was replaced by the checklist, not merged with it:
planSubIssueAttach passed tasksChecklist's output as the whole --body of a
gh issue edit on the parent. The shipped test asserted the first four argv
elements and that the last contained the heading, both of which are true of
a body that had destroyed everything else.

withTasksChecklist(body, items) is the merger, modelled on withParentLine:
it rewrites the ## Tasks section in place, preserves everything around it,
inherits a tick the caller is silent about, and refuses a body carrying two
## Tasks sections rather than guessing which one is the checklist.

parentBody is required on both paths and has no default, because a default
of "" would make the destructive behaviour the fallback for a caller who
simply forgot. The checklist edit is now emitted on the GraphQL path too,
where CLAIM-47.1's clause was previously unreachable.

Refs #315, #47
…t it

Mutation M11 survived the first round: widening the section boundary to H3
changed no assertion. Chasing it down found that every real Story body puts
load-bearing prose INSIDE ## Tasks, after the entries — #47's dependency
ordering and its "must not be built twice" ruling, #293's sequencing
rulings — and that on #293 the section runs to the end of the body, so a
section-wide rewrite deletes all of it with no following heading to stop it.

The merger rewrote the whole section. That is the defect this task exists to
remove, reproduced one level down inside the fix for it.

The rewrite now covers the leading run of entry lines and nothing else, and
the structure is asserted rather than assumed: entries split by prose are
refused with the line named, because taking "the first contiguous run" on
trust is how #323's audit parser read the right rows by accident.

Refs #315, #47
13 mutations, 11 killed, 2 void. The one first-pass survivor found that
every real Story body carries load-bearing prose inside ## Tasks, which a
section-wide rewrite would have deleted from #293.

Refs #315, #47
@dev-pmallapp
dev-pmallapp marked this pull request as ready for review September 20, 2026 11:42
@dev-pmallapp
dev-pmallapp merged commit fcd91c7 into story/47-s2-3-execution-skills Sep 21, 2026
14 checks passed
@dev-pmallapp
dev-pmallapp deleted the task/315-with-tasks-checklist branch September 21, 2026 03:42
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