Skip to content

S2.3 — Execution skills - #313

Draft
dev-pmallapp wants to merge 22 commits into
mainfrom
story/47-s2-3-execution-skills
Draft

dev-pmallapp wants to merge 22 commits into
mainfrom
story/47-s2-3-execution-skills

Conversation

@dev-pmallapp

Copy link
Copy Markdown
Owner

S2.3 — the three execution verbs (task-create, task-do, task-verify). Design only so far; the gate is open and blocking.

docs/design/stories/47.md: ten-section spine, twelve claims, ten build targets, six decisions, four open gate questions.

Do not merge, and do not write skill bodies, until the gate is ruled

Four questions on the ## iai-gate comment of #47. G1 and G4 are load-bearing. docs/design/03-workflow.md:271 — an agent may never author a decision.

The Story seeds SIX claims, not five

CLAIM-47.6 is in the issue body and at docs/milestones/M2.md:136-137. No prior summary has counted it.

Verdicts

Claim Verdict
CLAIM-47.1 UNIMPLEMENTABLE AS WORDED — binding.unitOfWork is not a list
CLAIM-47.2 UNIMPLEMENTABLE AS WORDED — zero cycle-detection code; the plural breaks NEVER-21.7
CLAIM-47.3 PARTIALLY SUPPORTED — refusal already built; no git layer
CLAIM-47.4 PARTIALLY SUPPORTED — inherits CLAIM-41.8 until #293 lands
CLAIM-47.5 PARTIALLY SUPPORTED — shipped code names this claim defective by file and line
CLAIM-47.6 PARTIALLY SUPPORTED — doctrine exists, reader does not
  • binding.unitOfWork is UnitSpec — five readonly string fields, declared singular, validated with isPlainObject. for (const item of …) throws. Four sources name the Design's ## Build Targets table instead, including the issue body eleven lines below the claim.
  • packages/core/src/evidence/template.ts:16-26 says CLAIM-47.5 "will not work as literally written" and its runtime error names docs/milestones/M2.md:132 — this claim's first line — as defective. S1.4 left that message as a warning two milestones in advance.
  • A live defect: planSubIssueAttach replaces the parent Story's whole body with the checklist. Its test passes because it asserts the wrong thing, and mutation testing cannot find it — there is no logic to delete.
  • Finding 18 is discharged (The three-reference cap is unsatisfiable: every skill needs four #287 raised the budget to 5 discretionary; story-design sits exactly at 5).
  • "iAI never merges" is at CONTRIBUTING.md:416 — fifth distinct number, with two stale citations live in tracked files.

Chain

903 tests / 889 pass / 0 fail / 14 skip / 3170 expect() measured at 5884a40 plus this branch's commit · lint 0 across 95 files · typecheck clean · skill-lint 0 errors · claim-lint 113 files, 0 errors, 1 warning (the expected docs/design/stories/9.md one).

#293's Design is cited by issue number, never by path — the two Stories are independent and may merge in either order.

Refs #47

… as written

Writes docs/design/stories/47.md: ten-section spine, twelve claims, ten build
targets, six decisions and four open gate questions.

The issue seeds SIX claims, not five. No prior summary of this Story has
counted CLAIM-47.6.

Measured at d59a958, verdicts:

- CLAIM-47.1 UNIMPLEMENTABLE. binding.unitOfWork is UnitSpec -- one object of
  five prose strings, validated with isPlainObject, declared singular. There
  is no item to iterate. Four sources name the Design's ## Build Targets
  table as the real enumeration, including the issue body eleven lines below
  the claim that contradicts it.
- CLAIM-47.2 UNIMPLEMENTABLE. Zero cycle-detection code exists anywhere, and
  Blocked by: is emit-only with no parser -- though the sibling Parent: line
  has both. The plural "lines" contradicts NEVER-21.7 and Decision 8 of
  stories/21.md. Only the ordering clause is real, and it is already enforced
  by a comment that named this claim two milestones early.
- CLAIM-47.3 PARTIALLY SUPPORTED, and widened. The default-branch refusal
  shipped in S1.3 citing M2.md:126-128 by line -- the first seeded clause in
  M2 built before its Story existed. But "cut from the story branch" forbids
  the stacking shape branch-and-pr-model.md:15 mandates.
- CLAIM-47.4 inherits CLAIM-41.8's restatement per stories/41.md:564-565.
- CLAIM-47.5 PARTIALLY SUPPORTED. evidence/template.ts refuses {n} at
  construction and its runtime error names M2.md:132 -- this claim's first
  line -- as defective. S1.4 predicted this Story's defect two milestones in
  advance and left the message as the warning.
- CLAIM-47.6 PARTIALLY SUPPORTED. Doctrine exists; no reader does.

An unrecorded live defect, found by implementing: planSubIssueAttach passes
the checklist as the whole --body, replacing the parent Story's entire body.
No withTasksChecklist merger exists though withParentLine is the exact
analogue one file over. The test asserts four argv elements and never checks
the body survives -- a passing test that is wrong, which mutation testing
cannot find because there is no logic to delete.

Finding 18 is DISCHARGED: #287 raised the budget to 3 baseline free plus 5
discretionary. story-design sits exactly at 5.

"iAI never merges" is at CONTRIBUTING.md:416 -- the fifth distinct number.
Two stale citations are live in tracked files, one of them a Reconciliation
row announcing it had corrected that very citation.

#293's Design is cited by issue number, never by path: the two Stories are
independent and may merge in either order. Citing it by path made this file
fail path-dangling on main and cascaded to 11 test failures. Finding 38's
precedent -- de-path the name in the prose.

Chain re-derived on main + this file: 903 tests / 889 pass / 0 fail / 14 skip
/ 3170 expect() at 5884a40 plus this commit, lint 0 across 95 files,
typecheck clean, skill-lint 0 errors, claim-lint 113 files 0 errors 1 warning
(the expected 9.md one). design-spine scans 11 Designs / 114 sections.

Refs #47
Ruled by the repository owner on 2026-09-07. iAI did not author the decision
and did not choose between the shapes (docs/design/03-workflow.md:271); this
commit relays it with attribution.

| G1 | restate CLAIM-47.1 to the Design's ## Build Targets table | YES |
| G2 | drop the plural Blocked by: lines; cycles out of scope with an owner | YES to both |
| G3 | close the task issue at verify time, not after merge | VERIFY TIME |
| G4 | git/argv layer shared with #293's adapter port | SHARED |

G3 was the one question measurement could not settle, and it was ruled
deliberately rather than by default. The cost is accepted and stated: a task
issue closes while its PR is still unmerged, because a human merges and
task-verify cannot wait on a human and still complete its own claim.

G3 GENERATED WORK THIS DESIGN DID NOT HAVE A ROW FOR. gh-operations.md:56 and
branch-and-pr-model.md:53 both say "close after merge, citing the merge
commit". The ruling contradicts both, so both must be restated. Added as
Build Target 11 -- the sixth consecutive Story to under-count enforcement,
and the first where a GATE RULING generated the uncounted work rather than
the milestone's indicative table. Worth naming as a new instance of the
pattern: a ruling is an edit to the repository, not only to the Design.

CLAIM-47.5 now states the ruled closure timing. Decision 5 is retitled -- the
contradiction no longer "goes to" the gate, it was answered there.

Chain unchanged: 903 tests / 889 pass / 0 fail / 14 skip / 3170 expect(),
lint 0 across 95 files, typecheck clean, skill-lint 0 errors, claim-lint 113
files 0 errors 1 warning (the expected 9.md one).

Refs #47
Writes docs/test-plans/47-plan.md against the RULED Design at 8039d53.

20 cases / 18 P0 / 2 P1 / 0 P2, in seven tables. Coverage verified in BOTH
directions: 12 claims defined, 12 anchored, 0 uncovered, 0 dangling.

Corpus: 11 real / 8 synthetic / 1 none. The real majority is possible because
this Story's subject already exists on disk -- eleven Designs to parse, twelve
references, seven skill bodies, and an argv layer whose refusals shipped two
milestones early.

Five cases carry the weight:

- Case 1 parses ## Build Targets over every Design on disk. CLAIM-47.1 was a
  TYPE ERROR, not a phrasing problem: binding.unitOfWork is a UnitSpec, five
  readonly string fields, declared singular.
- Case 2 asserts the Story body SURVIVES the checklist merge. At HEAD it does
  not. Case 12 is its mutation, and it KILLS NOTHING until Build Target 1
  lands -- there is no body-preservation logic to delete. Recorded as a
  forward-dated kill with the pre-landing run stated as a measured gap, per
  the S2.1 taxonomy, never as a pass. Amendment 1 records it.
- Case 5 asserts {n} is refused AND that the message still names it.
  evidence/template.ts aimed that warning two milestones forward at whoever
  implemented CLAIM-47.5; a generic error would decay it to nothing.
- Case 10 asserts the label transition emits exactly one command in all three
  branches, including ZERO on a no-op. Second occurrence of a defect
  labels.ts:16-26 already restated for CLAIM-21.3, in that file's own comment.
- Case 14 is the #307 partition at SEVEN. The three new verbs must each be
  classified deliberately or the equality fails -- by design.

Case 20 covers Build Target 11, which gate ruling G3 GENERATED: both
references still say "close after merge, citing the merge commit" and the
ruling contradicts them.

Case 13 records the trap that will bite the author: `## iai-evidence` written
literally is an instant duplicate-contract error, because it contains
SENTINEL_NAMESPACE_PREFIX. CLAIM-47.5 quotes it; the body may not.

Denominators: testplan-corpus 9 -> 10 plans, 49 -> 56 case tables, 220 -> 240
cases. claim-lint 113 -> 114 files, 0 errors, 1 warning (the expected 9.md).

expect() UNCHANGED at 3170, confirming the +0 prediction for a plan a second
time.

#293 is cited by issue number, never by path -- the two Stories are
independent and may merge in either order.

Refs #47
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 force-pushed the story/47-s2-3-execution-skills branch from b63258c to a6ce084 Compare September 20, 2026 11:40
dev-pmallapp and others added 15 commits September 21, 2026 09:12
S2.3.5 — `withTasksChecklist` merger, and the checklist on both capability paths
The block at docs/design/02-roles.md:276-282 had five copies in the tree —
four prose bodies and a private hardFailure() in risk-mandate.ts — and a
rule too loose to police any of them: /(?:- .+\n?)+/ accepts any bullets in
any order, which is why the subject-line divergence went unnoticed.

packages/core/src/guards/hard-failure.ts holds the spec once. A TypeScript
renderer produces conformant blocks; skill-lint's new hard-failure-block
rule validates prose against the same exported constants. One contract,
two enforcement directions.

risk-mandate.ts's private copy is deleted and rewired. Its test is
UNTOUCHED and still green — it pins the output string verbatim, so it is
the regression guard that the swap is behaviour-preserving.

The subject line is an open slot keyed Milestone/Story/Goal per gate
ruling G-a on #47: the fixed '- Story: #{story}' was unsatisfiable for
goal-create, which runs before any Story exists. All four shipped bodies
pass the new rule unchanged.

Refs #316, #47
…rule fires

checkEvidenceMandate is the reader the doctrine at
references/evidence-artifacts.md:140-144 never had. It RESOLVES NOTHING: it
takes an already-determined resolution and decides what to do about it, and
a static assertion pins that its own source imports no fs and no path.

That seam is what makes the task shippable. Build target 4's resolver
cannot be built as specified (#341): 21 of 54 artifacts carry no Claims row
and the other 33 use five incompatible notations, over an immutable corpus.
Only one direction is sound — a claim id in ZERO artifacts is certainly
uncovered — and a refusal needs only that one. Case 8 is recorded PARTIALLY
discharged per gate ruling G-b, carrying the positive direction to BT4.

The negative fixtures each assert TWO things: the old loose regex ACCEPTED
the malformation, and the new rule rejects it. Without the first, they
would only show that some rule fires on some garbage; with it, each one
names a defect that would have shipped green — a missing Action-line
prefix, a subject key outside the vocabulary, transposed Expected/Found.

docs/design/02-roles.md's template restated per G-a: the subject is an open
slot keyed Milestone/Story/Goal, and {remedy} is the trailing imperative
only.

Refs #316, #47, #341
Mutation M5 deleted the Action line's invariant-prefix check entirely --
startsWith(HARD_FAILURE_ACTION_PREFIX) became startsWith("- Action:") --
and the suite stayed green. The negative fixture meant to pin that check
still failed the mutant, but by accident: its Action line is SHORTER than
the 36-character prefix, so the surviving .slice(PREFIX.length) ran off the
end and yielded "", which the blank-remainder branch rejected regardless.
A length accident was standing in for a content comparison.

The added fixture miscases one word and keeps the line long enough that the
slice yields a non-blank remainder either way, so the only thing that can
reject it is a real comparison against the invariant. It is asserted to be
longer than the prefix, and its remainder asserted non-blank, so the
accident cannot come back silently.

Round: 12 mutations, 12 killed -- 11 on the first pass, M5 after this fix.

Refs #316
Verdict PASS. 963 tests / 949 pass / 0 fail / 14 skip, measured at the
parent commit b0616b9; the +2 expect() and +1 claim-lint file this artifact
itself adds are stated rather than measured, per #299, and both predictions
were confirmed after it landed.

12 mutations, 12 killed. M5 is the round's return: it deleted the Action
line's invariant check and the suite stayed green, because the fixture
pinning that check was shorter than the prefix and was being rejected by a
length accident instead of a content comparison.

Case 8 is PARTIALLY discharged per gate ruling G-b, carrying the positive
direction to build target 4 and #341.

Refs #316
…-emitter

S2.3.6 — The hard-failure block emitter
…rulings

BT4's row said 'map a task's anchored CLAIM-{story}.{n} to a Design claim
and to an artifact on disk'. The second half cannot be built: 21 of 54
artifacts carry no Claims row, the other 33 use five incompatible
notations, and docs/evidence/ is immutable, so neither gap can ever close.

Restated to what is buildable — reconstruct the EXPECTED artifact from the
Design and test plan, then check for its existence. It never parses
docs/evidence/ to discover what a claim covers.

That inversion is the point, not a detail. Reading coverage out of the
artifacts means a heterogeneous corpus yields confident wrong answers.
Reconstructing the expectation and checking for it means an unexpected
corpus yields a miss, which is a refusal, which is the safe direction for
CLAIM-47.6.

Also records G-a, G-b and G-c in a second Gate section, with the trigger:
#316's issue body was false in two of three clauses, and resolving each
against the tree before believing it is what surfaced G-a.

Refs #341, #47, #316
…ment

#341 — Build target 4 resolves from the Design side, not from the artifacts
…e seam job

S2.6 landed on main at dab09cc. story/47 was 39 commits behind and, more
importantly, had no packages/exec — which is the port gate ruling G4 binds
#49 task-do to. #49 could not have been built on this branch without it,
and the alternative is the second git layer G4 and G3 both forbid.

It also brings packages/harness and the seam CI job, so this branch's PRs
now report 8 checks rather than 7. That is the second precondition #334
needs before seam can be promoted to a required context.
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
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