Skip to content

S2.3 — Execution skills #47

Open
4 of 7 issues completed
Open

Description

@dev-pmallapp

Story

Milestone: M2 — Universal lifecycle
Unit of work: one Tier-1 skill
Size: L
Design: docs/milestones/M2.md — S2.3

Write task-create, task-do and task-verify. These are the highest-traffic
verbs in the system and the ones most likely to corrupt state, so idempotency and
explicit task closure are load-bearing, not polish.

Acceptance criteria (these seed the Design claims):

  • CLAIM-47.1: task-create opens one sub-issue per binding.unitOfWork item,
    each anchored to at least one CLAIM-{story}.{n}, labelled type:task and the
    parent's domain:*, and adds a ## Tasks checklist to the Story.
  • CLAIM-47.2: Dependencies appear as Blocked by: #N lines matching the parent
    checklist order; a cyclic dependency is refused with the hard-failure
    block rather than partially created.
  • CLAIM-47.3: task-do creates task/{n}-{slug} cut from the story branch,
    commits with #{issue}: prefixes, and opens a draft PR targeting the
    story branch; a PR targeting the default branch is refused before creation.
  • CLAIM-47.4: Re-running task-do on a task that already has a branch, commits
    and a draft PR creates zero new branches, zero new PRs and zero duplicate
    commits.
  • CLAIM-47.5: task-verify writes docs/evidence/{n}-{ts}.md, posts
    ## iai-evidence, marks the PR ready, applies status:resolved in one
    gh issue edit paired with --remove-label, and closes the issue
    explicitly.
  • CLAIM-47.6: task-verify refuses to resolve a task whose anchored CLAIM-{story}.{n} has
    no evidence on disk, emitting the hard-failure block.

Verification: A fixture Story is driven from task-create through
task-verify twice; the second pass mutates nothing; the task issues are closed
even though no closing keyword fired, and each carries evidence on disk.


Next steps

/iai:story-design  <this issue>
/iai:story-test-plan <this issue>
/iai:task-create   <this issue>

The task list below is indicative, taken from docs/milestones/M2.md.
The authoritative list is produced by /iai:task-create from the Build Targets
table in this Story's Design and may differ once the design is written.

Created by scripts/bootstrap-stories.py from docs/milestones/M2.md.


Tasks

Six tasks, in dependency order. The milestone's indicative table named four; #315, #316 and #317 are net-new, and #317 exists because gate ruling G3 generated work the Design's first draft had no row for. Seventh consecutive Story to exceed its indicative table.

#51 is closed as out of scope — references/workflow-states.md:76-79 records that nothing in M1 or M2 implements the Story roll-up, and it is anchored to none of CLAIM-47.1–.12. Reopen it when a Story owns it.

The git/argv layer in #49 is shared with #293's adapter port, per gate rulings G4 here and G3 there, ruled in the same sitting. It must not be built twice.

Activity

  1. added
    type:storyOne end-to-end deliverable outcome
    domain:devSoftware development pack
    iaiCreated or managed by iAI
    on Aug 25, 2026
  2. dev-pmallapp commented on Sep 7, 2026

    @dev-pmallapp
    OwnerAuthor

    iai-design

    Design: docs/design/stories/47.md — pinned at 8039d533290a4d71e3832b61715e9a064bb72874 (gate ruling applied), on story/47-s2-3-execution-skills.

    Twelve claims registered: CLAIM-47.1–.7, NEVER-47.8–.10, CLAIM-47.11–.12. Ten build targets, six decisions, four open gate questions.

    This 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 of S2.3 has counted it.

    Verdicts on the seeded claims, measured at d59a958

    Claim Verdict Core reason
    CLAIM-47.1 UNIMPLEMENTABLE AS WORDED binding.unitOfWork is not a list. Nothing to iterate
    CLAIM-47.2 UNIMPLEMENTABLE AS WORDED Zero cycle-detection code; the plural contradicts a shipped anti-claim
    CLAIM-47.3 PARTIALLY SUPPORTED The refusal is already built and tested; the git layer is not
    CLAIM-47.4 PARTIALLY SUPPORTED Inherits CLAIM-41.8's restatement until #293 lands
    CLAIM-47.5 PARTIALLY SUPPORTED Shipped code names this claim defective, by file and line
    CLAIM-47.6 PARTIALLY SUPPORTED The doctrine exists; the reader does not

    1. binding.unitOfWork has no items. packages/core/src/binding/domain.ts:111-117 declares UnitSpec as five readonly string fields, and :225 declares the field singular. packages/core/src/binding/validate.ts:70-76 validates it with isPlainObject, not Array.isArray. It is a definition of what one unit is, not an enumeration of the units in this Story — and it carries the same prose-threshold defect domain.ts:102-110 already records for size, plus a categorical one size does not have. All five domain packages are 5-line stubs; the only real binding is nullBinding, whose noun is "nothing".

    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.

    2. Shipped code already names CLAIM-47.5 defective. packages/core/src/evidence/template.ts:16-26 says in terms: "M2's CLAIM-47.5 will not work as literally written, and whoever implements it hits a loud error naming {n}", and the runtime message at :108-116 states docs/milestones/M2.md:132 is defective. :132 is this claim's first line. S1.4 predicted this Story's defect two milestones in advance and left the error message as the warning.

    3. Cycle detection has no foundation at all. No topological sort, no visited set, no adjacency structure anywhere in the tree — and Blocked by: is emit-only with no parser, though the sibling Parent: line has both a regex and a reader. You cannot detect a cycle in edges nobody reads. Separately, the plural "Blocked by: #N lines" contradicts Decision 8 of docs/design/stories/21.md:213 and NEVER-21.7, which fix it as one comma-joined line.

    4. CLAIM-47.3 is the counter-example worth recording. Its refusal clause shipped in S1.3 — packages/core/src/gh/pr.ts:152-157, whose header comment cites docs/milestones/M2.md:126-128 by line. This is the first seeded clause in M2 built before its Story existed, against five consecutive Stories of under-counted enforcement. But "cut from the story branch" forbids the stacking shape references/branch-and-pr-model.md:15 mandates.

    A live defect, found by implementing

    planSubIssueAttach replaces the Story's entire body with the checklist. packages/core/src/gh/sub-issues.ts:296-304 passes checklist.value as the whole --body. No withTasksChecklist merger exists, though withParentLine (:163) is the exact idempotent analogue one file over. packages/core/test/gh-sub-issues.test.ts:165-169 asserts the first four argv elements and that the last contains the heading — nothing asserts the existing body survives.

    A passing test that is wrong. Mutation testing cannot find it, because there is no body-preservation logic to delete. CLAIM-47.7's mutation kills nothing at HEAD, and that is the finding.

    And on the other path there is no checklist at all: when the capability is "present", planSubIssueAttach returns exactly one command. The clause is destructive on the fallback path and unreachable on the primary one.

    Two corrections to standing notes

    • Handoff finding 18 is DISCHARGED. The three-reference cap is unsatisfiable: every skill needs four #287 raised the cap: docs/design/01-skill-hierarchy.md:472 now reads "at most 5 references of its own" with the three baselines uncounted, cross-checked by test/skill-lint.test.ts:671-679 parsing the sentence. 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: references/branch-and-pr-model.md:108 and :137, and docs/design/stories/41.md:241. The first is a Reconciliation row announcing it had corrected that very citation. Nothing verifies that a :NN citation still says what the citing document claims #304's sixth request, and the first instance inside a contract document a skill is told to read.

    Chain

    903 tests / 889 pass / 0 fail / 14 skip / 3170 expect() measured at 5884a40 plus 942a91a2bc6cbac8c7392f5d7965647840d97bee · 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) · design-spine 11 Designs / 114 sections.

    #293's Design is cited by issue number, never by path. The two Stories are independent and may merge in either order; a path citation 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.

    The gate is open — see the ## iai-gate comment.


    Gate RULED, 2026-09-07 — ruling applied

    Decided by the repository owner (@dev-pmallapp). iAI did not author the decision — docs/design/03-workflow.md:271. All four questions accepted as recommended (G1, G2, G3, G4). G3 ruled close AT VERIFY TIME, which added Build Target 11.

    This sentinel is repinned to 8039d533290a4d71e3832b61715e9a064bb72874, the commit that carries the applied ruling. The text the approval was given against remains addressable at 942a91a2bc6cbac8c7392f5d7965647840d97bee.

    See the ## iai-gate comment, updated in place, for the full ruling.

  3. dev-pmallapp commented on Sep 7, 2026

    @dev-pmallapp
    OwnerAuthor

    iai-gate

    RULED — 2026-09-07. All four questions accepted as recommended.

    Decided by the repository owner (@dev-pmallapp). iAI did not author this
    decision, did not choose between the shapes, and did not originate any part of
    it — docs/design/03-workflow.md:271. This comment relays the ruling with
    attribution and is the same comment that carried the questions; it was updated
    in place, not stacked.

    # Question Ruling
    G1 Restate CLAIM-47.1 to read the Design's ## Build Targets table rather than "per binding.unitOfWork item"? YES
    G2 Drop CLAIM-47.2's plural Blocked by: lines, and move cycle refusal out of scope with a named owner? YES to both
    G3 Does task-verify close the task issue at verify time, or after the human merges? CLOSE AT VERIFY TIME
    G4 Is the git/argv execution layer shared with #293's adapter port, or built separately? SHARED

    Applied at 8039d533290a4d71e3832b61715e9a064bb72874.

    G3 was the only one measurement could not settle, and it was ruled deliberately

    CLAIM-47.5 says verify time. references/gh-operations.md:56 and references/branch-and-pr-model.md:53 both say close after merge, citing the merge commit. These cannot both hold.

    The ruling is verify time, and the cost is accepted and recorded: a task issue closes while its PR is still unmerged. The reason is structural — a human merges (CONTRIBUTING.md:416), so an after-merge rule means task-verify can never complete its own claim in one run.

    The ruling generated work this Design had no row for. Both references now contradict the ruled behaviour and must be restated. That is Build Target 11, added by this commit.

    This is 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: a ruling is an edit to the repository, not only to the Design.

    Why G1 was not a matter of taste

    CLAIM-47.1 is a type error, not an awkward phrasing. binding.unitOfWork is UnitSpec — five readonly string fields, declared singular, validated with isPlainObject. for (const item of binding.unitOfWork) throws. The binding is still read after the restatement, for what it actually contains: the noun and the prose sizing bound, which is exactly what docs/design/01-skill-hierarchy.md:96 gives task-create.

    G4 was ruled alongside #293's G3

    Both questions are about the same seam. One port, owned by whichever Story lands first, consumed by the other. Two ports would be the duplicate-contract defect arriving before either exists — the exact shape of scripts/bootstrap-github.sh hand-writing argv that packages/core/src/gh/milestones.ts already builds.

    Next

    Chain green at the applied commit: 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 docs/design/stories/9.md one).

    gate:approved applied. Next: /iai:story-test-plan 47, then /iai:task-create 47 — eleven build targets now, not ten.

  4. added a commit that references this issue on Sep 7, 2026
  5. added a commit that references this issue on Sep 7, 2026
  6. dev-pmallapp commented on Sep 7, 2026

    @dev-pmallapp
    OwnerAuthor

    iai-test-plan

    Test plan: docs/test-plans/47-plan.md — pinned at 56eee9d3c0a58693dd01b0499faa7370bc152a28, written against the ruled Design at 8039d53.

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

    Corpus: 11 real / 8 synthetic / 1 none

    A real majority, unusual for this repository and possible because this Story's subject already exists on disk: eleven Designs to parse, twelve reference documents, seven skill bodies, and an argv layer whose refusals shipped two milestones early.

    The five cases that carry the weight

    Case Why it matters
    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
    2 + 12 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, because there is no body-preservation logic to delete
    5 Asserts {n} is refused and that the message still names it. packages/core/src/evidence/template.ts aimed that warning two milestones forward at whoever implemented CLAIM-47.5; a generic error would decay it to nothing
    10 The label transition emits exactly one command in all three branches, including zero on a no-op. Second occurrence of a defect packages/core/src/gh/labels.ts:16-26 already restated for CLAIM-21.3, in that file's own comment
    14 The #307 partition at seven. The three new verbs must each be classified deliberately or the equality fails — by design

    Two things the author must know before writing a body

    • ## iai-evidence written literally is an instant duplicate-contract error — it contains SENTINEL_NAMESPACE_PREFIX. CLAIM-47.5 quotes it; the skill body may not. All four shipped bodies dodge this by naming the sentinel through references/evidence-artifacts.md. Case 13 enforces it.
    • task-do may carry no domain routing form, though it dispatches to the pack's leaf skill. Same case.

    Case 12 is a survivor on purpose, and it is recorded as one

    It cannot kill before Build Target 1 lands. Recording it as killed would be false; recording it as a plain survivor would imply the fix was wrong. The resolution is S2.1's taxonomy: simulate the future state and record a forward-dated kill, with the pre-landing run stated as a measured gap. Amendment 1 of the plan records this rather than applying it silently.

    Case 20 exists because the gate ruling generated work

    G3 ruled closure at verify time, which contradicts references/gh-operations.md:56 and references/branch-and-pr-model.md:53 — both still say close after merge, citing the merge commit. Both must be restated. That is Build Target 11, which neither the milestone nor the Design's first draft had a row for.

    Sixth consecutive Story to under-count enforcement, and the first where a ruling generated the uncounted work.

    Deviations from the milestone

    Four of six seeded criteria are asserted differently, all ruled at the gate — see the plan's Deviations table. The sharpest is CLAIM-47.5: a shipped module in packages/core already names docs/milestones/M2.md:132 as defective, by file and line, in a runtime error string. The milestone was not restated to match the code; the code was written knowing the milestone was wrong.

    Chain

    903 tests / 889 pass / 0 fail / 14 skip / 3170 expect() · lint 0 across 95 files · typecheck clean · skill-lint 0 errors · claim-lint 114 files, 0 errors, 1 warning (the expected docs/design/stories/9.md one).

    Denominators: testplan-corpus 9 → 10 plans, 49 → 56 case tables, 220 → 240 cases. expect() unchanged, confirming the +0 prediction for a plan a second time.

    Next: /iai:task-create 47 — 11 build targets against 4 bootstrapped stubs (#48, #49, #50, #51), so task-create must reconcile onto them and add the rest.

  7. added 3 commits that reference this issue on Sep 20, 2026
  8. dev-pmallapp commented on Sep 23, 2026

    @dev-pmallapp
    OwnerAuthor

    Gate ruling — G-a, G-b, G-c (for #316, Build Target 7)

    Three questions were raised before #316 started, because research found its issue body wrong in two load-bearing places. All three are now ruled.

    G-a — the hard-failure block's subject line: the template is wrong, and the slot is an ordered vocabulary

    docs/design/02-roles.md:278 specifies a fixed - Story: #{story}. Two shipped bodies cannot satisfy it: goal-create emits - Goal: <id> and story-create emits - Milestone: <milestone>. goal-create runs before any Story exists, so - Story: # is literally unfillable there.

    Ruled: the subject line is an open slot, not a fixed key. Its key is drawn from an ordered vocabulary, broadest to narrowest:

    Milestone → Story → Goal

    Consequences:

    • docs/design/02-roles.md's template is corrected to show the slot rather than - Story:.
    • The renderer takes the subject as a {kind, value} pair, kind constrained to that vocabulary.
    • The tightened lint rule requires the subject key to be one of those three, and rejects any other.
    • All four shipped bodies stay green — goal-create Goal, story-create Milestone, story-design and story-test-plan Story. No body is reconciled under this ruling.

    G-b — case 8 may be recorded as partially discharged

    Case 8 of docs/test-plans/47-plan.md anchors CLAIM-47.6. Its refusal half needs only the sound direction — a claim-id token absent from every artifact is certainly uncovered. The converse (token present ⇒ covered) requires range parsing and belongs to Build Target 4, which #316 does not own and which is independently blocked (see G-c).

    Ruled: #316 records case 8 as partially discharged, with an explicit carry row naming BT4 as the owner of the positive direction. Precedent: docs/evidence/44-*.md:15 records a claim "partially discharges NEVER-41.7", and 41-*.md:16 records three claims explicitly not discharged.

    Binding constraint: the sound-direction scan must not ship as a resolver. It is not exported under any name suggesting general claim resolution, and the refusal site takes an already-determined resolution as a supplied parameter, reading nothing from disk — the seam packages/core/src/guards/risk-mandate.ts:58-60 already uses.

    G-c — BT4's corpus problem is raised as its own issue

    Measured over the 54 artifacts in docs/evidence/: 21 carry no | Claims | row at all; the remaining 33 use at least five incompatible notations (…, en-dash with abbreviated endpoint, .., spaced en-dash, partial-coverage qualifiers) plus the retired ISC-* namespace. #287 has two artifacts, so the mapping is not 1:1. docs/evidence/ is immutable (NEVER-194.6), so the 21 gaps can never be backfilled.

    Ruled: BT4 cannot be built as the Design specifies. A separate issue is raised recording the measurement, so whoever owns BT4 gets a ruling on reconstructing the mapping from the Design/plan side rather than from the artifacts.

    Two corrections to #316's body, applied separately

    • "No module emits it" is false — packages/core/src/guards/risk-mandate.ts:48-56 emits one today.
    • "no lint rule requires it" is false — test/skill-lint.test.ts:1369 requires the block in every real skill body.

    The second matters most: #316's first Done-when bullet proposed reconciling the four bodies to cite rather than restate the block, which a currently-green test forbids, and whose assertions encode CLAIM-41.5 and case 12 of 41-plan.md — a closed Story with committed evidence. That bullet's second branch is taken instead: the bodies keep restating the block, and this ruling is the recorded reason.

    Refs #316, #47

  9. added a commit that references this issue on Sep 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:devSoftware development packgate:approvedHuman decision recorded; pipeline may proceediaiCreated or managed by iAIrung:compiledev 1/4: builds cleanstatus:in-progressActively being workedtype:storyOne end-to-end deliverable outcome

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions