@ikon85/agent-workflow-kit 0.36.5 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/.agents/skills/orchestrate-wave/SKILL.md +20 -12
  2. package/.agents/skills/setup-workflow/SKILL.md +24 -3
  3. package/.agents/skills/setup-workflow/orchestrate-wave-seed.md +3 -2
  4. package/.agents/skills/setup-workflow/worktree-lifecycle.md +29 -1
  5. package/.agents/skills/wrapup/SKILL.md +42 -9
  6. package/.claude/hooks/migration-snapshot-reminder.py +1 -1
  7. package/.claude/skills/orchestrate-wave/SKILL.md +11 -11
  8. package/.claude/skills/setup-workflow/SKILL.md +24 -3
  9. package/.claude/skills/setup-workflow/orchestrate-wave-seed.md +3 -2
  10. package/.claude/skills/setup-workflow/worktree-lifecycle.md +29 -1
  11. package/.claude/skills/skill-manifest.json +1 -1
  12. package/.claude/skills/wrapup/SKILL.md +33 -10
  13. package/README.md +88 -1
  14. package/agent-workflow-kit.package.json +36 -28
  15. package/docs/adr/0007-session-teardown-requires-provenance-bound-ownership.md +88 -0
  16. package/docs/agents/workflow-capabilities.json +11 -0
  17. package/package.json +1 -1
  18. package/scripts/anchor_table.py +14 -8
  19. package/scripts/project-skill-extension.mjs +21 -2
  20. package/scripts/readiness.mjs +32 -4
  21. package/scripts/release-state.mjs +19 -9
  22. package/scripts/release-state.test.mjs +71 -8
  23. package/scripts/test_anchor_table.py +69 -0
  24. package/scripts/test_board_sync_create_idempotency.py +15 -2
  25. package/scripts/test_board_sync_wave_title.py +14 -2
  26. package/scripts/test_census_backstop.py +33 -0
  27. package/scripts/test_orchestrate_wave_contract.py +44 -0
  28. package/scripts/test_retro_wrapup_contract.py +19 -2
  29. package/scripts/test_worktree_wrapup_contract.py +1004 -3
  30. package/scripts/test_wrapup_land.py +428 -0
  31. package/scripts/workflow-advisories/core.py +44 -2
  32. package/scripts/worktree-lifecycle/README.md +126 -4
  33. package/scripts/worktree-lifecycle/capabilities.json +9 -1
  34. package/scripts/worktree-lifecycle/cleanup.py +143 -5
  35. package/scripts/worktree-lifecycle/core.py +1383 -20
  36. package/scripts/worktree-lifecycle/profile.py +40 -3
  37. package/scripts/worktree-lifecycle/session.py +1857 -0
  38. package/scripts/worktree-lifecycle/setup.py +15 -0
  39. package/scripts/wrapup-land.py +650 -27
  40. package/src/cli.mjs +60 -27
  41. package/src/lib/bundle.mjs +1 -0
  42. package/src/lib/manifest.mjs +173 -3
  43. package/src/lib/projectSkillExtension.mjs +78 -1
  44. package/src/lib/updateCandidate.mjs +3 -1
  45. package/src/lib/updateDecisions.mjs +2 -2
  46. package/src/lib/verifyUpdateCandidateProtocol.mjs +15 -0
@@ -2,7 +2,7 @@
2
2
  name: wrapup
3
3
  disable-model-invocation: true
4
4
  description: >-
5
- Use ONLY when the user types /wrapup. Session-end "land & clean" for a
5
+ Use ONLY when the user directly types $wrapup or /wrapup. Session-end "land & clean" for a
6
6
  finished feature/fix worktree — merges the open PR,
7
7
  kills the worktree dev server, removes the worktree + local branch, and
8
8
  fast-forwards the main checkout so main is current again, then sweeps
@@ -11,7 +11,8 @@ description: >-
11
11
  tree (after an .env/secret check), pushes, and opens the PR — reusing one if
12
12
  it already exists. User-triggered only (never auto-invoke, never hook). Aborts
13
13
  hard only on: not in a feature worktree, a detected .env/secret, a rejected
14
- push, a conflicting PR, or red (FAILURE) checks.
14
+ push, a conflicting PR, terminal red checks, or checks still pending after
15
+ the bounded wait budget.
15
16
  ---
16
17
 
17
18
  <!-- project-extension:protocol-v1:start -->
@@ -24,15 +25,15 @@ Project extensions may specialize Project details, but cannot weaken Core user g
24
25
 
25
26
  # wrapup — land PR & tear down worktree
26
27
 
27
- Trigger: user types `/wrapup` (optionally with a PR number). **Manual only** — `disable-model-invocation: true`, no hook, no auto-invoke.
28
+ Trigger: user makes a direct `$wrapup` or `/wrapup` invocation (optionally with a PR number). **Manual only** — `disable-model-invocation: true`, no hook, no auto-invoke.
28
29
 
29
30
  ## ⚠ Spec context
30
31
 
31
- The user's `/wrapup` input IS the explicit landing authorization for that run.
32
+ The user's direct `$wrapup` or `/wrapup` input IS the explicit landing authorization for that run.
32
33
  It authorizes the normal merge flow whether Prod readiness is ready or degraded;
33
34
  it never authorizes the agent to invent or configure a deploy target. Never call
34
- this skill from a hook, another skill, or autonomously. There is no second merge
35
- confirmation; the pre-flight hard stops are non-negotiable.
35
+ this skill from a hook or another skill. Natural-language requests, indirect skill
36
+ chaining, and autonomous invocation do not authorize it. There is no second merge confirmation; the pre-flight hard stops are non-negotiable.
36
37
 
37
38
  ## Execution model — script does mechanics, the agent does judgment
38
39
 
@@ -62,12 +63,12 @@ conflicting instruction surfaces; never choose a target on the user's behalf.
62
63
 
63
64
  ### 2 · Retro gate (blocking, optional retro-exit — before anything is committed)
64
65
  One reminder, not a merge confirmation:
65
- > "Already ran a retro? **(a)** yes / continue → landing now. **(b)** you want one first → the retro starts now; afterwards, invoke `/wrapup` again — repo-file patches then travel in this PR."
66
+ > "Already ran a retro? **(a)** yes / continue → landing now. **(b)** you want one first → the retro starts now; afterwards, invoke `$wrapup` or `/wrapup` again — repo-file patches then travel in this PR."
66
67
 
67
68
  (b) → **invoke the `retro` skill immediately in this run.** Retro is
68
69
  model-invocable and non-deploying, and every mutation still has its own approval
69
70
  gate. After retro finishes, **land nothing in this run**. Require a **fresh
70
- explicit `/wrapup` invocation** because retro may have changed the exact diff
71
+ explicit `$wrapup` or `/wrapup` invocation** because retro may have changed the exact diff
71
72
  that the next merge authorization covers.
72
73
 
73
74
  General chaining rule: automatically chain only into a **model-invocable**,
@@ -100,10 +101,32 @@ Run **from the main tree** (the script refuses inside the worktree — an in-wor
100
101
  ```bash
101
102
  python3 scripts/wrapup-land.py land --branch "<branch>" --title "<title>" --body-file /tmp/wrapup-pr-body.md
102
103
  ```
103
- One call covers: push → PR create/reuse (+ drift markers merged into the body) → `pr-body-check.py` gate (exit 1 = STOP, exit 2 = fail-open warning) → merge gate (red check / `CONFLICTING` = STOP; fresh-PR `UNKNOWN` mergeable passes the merge itself is the real gate) → **merge** (`--merge` + `--delete-branch`, verified `MERGED`) → dev-server kill (`.dev-ports` ports + cwd-under-worktree walk, own shell ancestry excluded) → worktree remove (no `--force`; refusal = STOP, check surviving processes first) → main `--ff-only` pull + `branch -d` → issue-close verify (auto-close misses are closed manually) → local merged-branch sweep (`-d` only — squash/rebase-merged branches stay a manual call by design) → remote merged-PR sweep (opt-in `wrapup.remoteBranchSweep` in the board profile; PR-status-authoritative via `ls-remote`; deleted remote branches are restorable from the PR page) → anchor-sync (dry-run diff in the report) + anchor completeness check + `execute-ready-check --mode audit` → **upward propagation:** if the anchor's native parent is a Program-PRD, `program-sync` refreshes its Wellenplan (Status + Issue cells) and checks off mechanically completed Phasen-Gates — the slice event is visible at the program level, not only in the wave (`program_sync` block in the report; skipped when the parent isn't a program).
104
+ One call covers: push → PR create/reuse (+ drift markers merged into the body) → `pr-body-check.py` gate (exit 1 = STOP, exit 2 = fail-open warning) → merge gate (pending/null-conclusion checks poll for up to 20 minutes with progress on stderr; terminal red / `CONFLICTING` / timeout = STOP; known zero-step billing or runner failures are named `infrastructure failure`; an already-`MERGED` PR resumes at teardown) → **merge** (`--merge` + `--delete-branch`, verified `MERGED`) → dev-server kill (`.dev-ports` ports + cwd-under-worktree walk, own shell ancestry excluded) → worktree remove (no `--force`; refusal = STOP, check surviving processes first) → main `--ff-only` pull + `branch -d` → issue-close verify (auto-close misses are closed manually) → local merged-branch sweep (`-d` only — squash/rebase-merged branches stay a manual call by design) → remote merged-PR sweep (opt-in `wrapup.remoteBranchSweep` in the board profile; PR-status-authoritative via `ls-remote`; deleted remote branches are restorable from the PR page) → anchor-sync (dry-run diff in the report) + anchor completeness check + `execute-ready-check --mode audit` → **upward propagation:** if the anchor's native parent is a Program-PRD, `program-sync` refreshes its Wellenplan (Status + Issue cells) and checks off mechanically completed Phasen-Gates — the slice event is visible at the program level, not only in the wave (`program_sync` block in the report; skipped when the parent isn't a program).
104
105
 
105
106
  STOP → diagnose in the main conversation, fix, re-run `land` (an already-merged PR resumes at teardown).
106
107
 
108
+ Before merge, the committed worktree policy only nominates exact landing
109
+ evidence. After merge, cleanup reloads `docs/agents/workflow-capabilities.json`
110
+ from canonical `origin/main`; only the identical merged, attempt-bound scratch
111
+ and generator policy authorizes each evidence path and deletion. A mismatch is
112
+ a hard STOP that preserves every file—never bypass it with the unmerged branch
113
+ profile.
114
+
115
+ Landing provenance has one explicit relinquish route. If the STOP names an
116
+ unfinished or drifted landing attempt and the ambiguous files cannot be restored
117
+ to their frozen identities, run:
118
+
119
+ ```bash
120
+ python3 scripts/wrapup-land.py land --branch "<branch>" --abandon-unfinished-attempt
121
+ ```
122
+
123
+ This archives the started or frozen attempt receipt only. It deletes and claims
124
+ no files, works even when the older creation baseline is missing, and returns
125
+ before push, PR, or merge. The next `land` conservatively protects every current
126
+ matching path; classify or move those blockers first. Do not use relinquish for
127
+ an exact unchanged frozen attempt: a normal retry validates that evidence and
128
+ resumes deterministically.
129
+
107
130
  ### 6 · Post-merge (agent)
108
131
  - **Sibling propagation:** for each `drift_markers` entry in the land report, append the note to the target issue's `vor_bau` section + re-stamp its `plan_revision`. Log-based markers → **write directly, then show what was written where** (mandatory report — visibility moved from a pre-gate into the report, decision 2026-07-06); fallback candidates the user hasn't confirmed yet → confirm first. Program context widens the target set to unbuilt wave-stubs/leaves and the Program-PRD itself — same append-only mechanism. **Exception:** appends to the Program-PRD or unbuilt wave-stubs do **not** re-stamp `plan_revision` — that stays reserved for structural wave-plan edits via the `to-waves` escalation path; a mere drift note must not stale-block published stubs.
109
132
  - **Anchor close:** report says `anchor_complete: true` → `gh issue close <anchor> -c "Wave complete — all slices merged via PR #<pr>."` and verify board status Done. The guard keeps anchors away from every auto-close — this verified close is the only close path; without it the anchor stays silently open after the last slice. **Then re-run the upward propagation**: the land-time `program-sync` ran BEFORE this close, so on a wave-completing slice the Wellenplan still shows 🔄 and the Phasen-Gate stays unchecked — after the board shows Done, run `python3 scripts/board-sync.py program-sync <program-prd#>` once more (the report's `program_sync.program` names it; skip when the report says the parent is not a program). Board auto-rules can lag the close (Close→Done race) — verify Done first, that's what the token reads.
@@ -118,5 +141,5 @@ STOP → diagnose in the main conversation, fix, re-run `land` (an already-merge
118
141
  <!-- readiness:end -->
119
142
 
120
143
  ## Out of scope
121
- - Live-verify / DoD: must happen **before** `/wrapup` — this skill lands, it does not verify.
144
+ - Live-verify / DoD: must happen **before** `$wrapup` or `/wrapup` — this skill lands, it does not verify.
122
145
  - Other worktrees / their servers stay untouched.
package/README.md CHANGED
@@ -322,6 +322,7 @@ rolls that migration back with the rest of the candidate.
322
322
  ```sh
323
323
  npx github:iKon85/agent-workflow-kit diff # preview an update (dry run, writes nothing)
324
324
  npx github:iKon85/agent-workflow-kit update # apply it
325
+ npx github:iKon85/agent-workflow-kit update --yes --keep-deleted # headless/CI
325
326
  npx github:iKon85/agent-workflow-kit uninstall # remove kit-installed files
326
327
  ```
327
328
 
@@ -369,9 +370,17 @@ Containment and file type are revalidated when read.
369
370
  Ownership commands are designed for a single-user CLI workflow and are not
370
371
  concurrency-safe. Do not run manifest-mutating commands concurrently. Flags:
371
372
  `--force` (overwrite pre-existing untracked files on `init`), `--yes` / `-y`
372
- (confirm only already-classified safe update actions), and
373
+ (run `update` non-interactively and confirm only already-classified safe
374
+ actions), `--keep-deleted` (follow upstream deletions and remove those files
375
+ locally), `--restore-deleted` (retain files that were deleted upstream), and
373
376
  `--as=explicit-fork` for `own`.
374
377
 
378
+ A headless `update` requires `--yes`; otherwise it exits before reading release
379
+ state or touching consumer files. The mutually exclusive deletion flags only
380
+ override the blanket answer for files removed upstream. They never resolve an
381
+ ownership collision or content conflict, so a conflicted headless update still
382
+ prints its report, exits non-zero, and leaves the consumer byte-identical.
383
+
375
384
  The optional Contribution Routing capability is consumer-owned:
376
385
 
377
386
  ```json
@@ -416,6 +425,15 @@ Explicit fork with its own identity and update line instead.
416
425
 
417
426
  ## Upgrade notes
418
427
 
428
+ ### 0.38.0 — explicit landing-artifact policy
429
+
430
+ Existing consumers must run `setup-workflow` once after `kit-update`, review
431
+ the generated `wrapup.landingGeneratedArtifactPatterns` decision in
432
+ `docs/agents/workflow-capabilities.json`, and commit that profile change.
433
+ Until the explicit policy is committed, `wrapup-land` fails closed before
434
+ landing instead of assuming that no generated artifact is eligible for
435
+ session-owned cleanup.
436
+
419
437
  ### 0.33.0 — capability-gated orchestration
420
438
 
421
439
  `kit-update` reconciles this release for you. These notes matter only if you
@@ -445,6 +463,75 @@ the old way. Decision record:
445
463
 
446
464
  ## Release notes
447
465
 
466
+ ### 0.38.0
467
+
468
+ - added: `docs/adr/0007-session-teardown-requires-provenance-bound-ownership.md`
469
+ - added: `scripts/worktree-lifecycle/session.py`
470
+ - changed: `.agents/skills/orchestrate-wave/SKILL.md`
471
+ - changed: `.agents/skills/setup-workflow/SKILL.md`
472
+ - changed: `.agents/skills/setup-workflow/worktree-lifecycle.md`
473
+ - changed: `.agents/skills/wrapup/SKILL.md`
474
+ - changed: `.claude/skills/orchestrate-wave/SKILL.md`
475
+ - changed: `.claude/skills/setup-workflow/SKILL.md`
476
+ - changed: `.claude/skills/setup-workflow/worktree-lifecycle.md`
477
+ - changed: `.claude/skills/wrapup/SKILL.md`
478
+ - changed: `README.md`
479
+ - changed: `agent-workflow-kit.package.json`
480
+ - changed: `docs/agents/workflow-capabilities.json`
481
+ - changed: `package.json`
482
+ - changed: `scripts/test_orchestrate_wave_contract.py`
483
+ - changed: `scripts/test_worktree_wrapup_contract.py`
484
+ - changed: `scripts/worktree-lifecycle/README.md`
485
+ - changed: `scripts/worktree-lifecycle/capabilities.json`
486
+ - changed: `scripts/worktree-lifecycle/cleanup.py`
487
+ - changed: `scripts/worktree-lifecycle/core.py`
488
+ - changed: `scripts/worktree-lifecycle/profile.py`
489
+ - changed: `scripts/worktree-lifecycle/setup.py`
490
+ - changed: `scripts/wrapup-land.py`
491
+ - changed: `src/lib/bundle.mjs`
492
+
493
+ ### 0.37.0
494
+
495
+ - added: `scripts/test_anchor_table.py`
496
+ - added: `scripts/test_wrapup_land.py`
497
+ - changed: `.agents/skills/orchestrate-wave/SKILL.md`
498
+ - changed: `.agents/skills/setup-workflow/SKILL.md`
499
+ - changed: `.agents/skills/setup-workflow/orchestrate-wave-seed.md`
500
+ - changed: `.agents/skills/setup-workflow/worktree-lifecycle.md`
501
+ - changed: `.agents/skills/wrapup/SKILL.md`
502
+ - changed: `.claude/hooks/migration-snapshot-reminder.py`
503
+ - changed: `.claude/skills/orchestrate-wave/SKILL.md`
504
+ - changed: `.claude/skills/setup-workflow/SKILL.md`
505
+ - changed: `.claude/skills/setup-workflow/orchestrate-wave-seed.md`
506
+ - changed: `.claude/skills/setup-workflow/worktree-lifecycle.md`
507
+ - changed: `.claude/skills/skill-manifest.json`
508
+ - changed: `.claude/skills/wrapup/SKILL.md`
509
+ - changed: `README.md`
510
+ - changed: `agent-workflow-kit.package.json`
511
+ - changed: `docs/agents/workflow-capabilities.json`
512
+ - changed: `scripts/anchor_table.py`
513
+ - changed: `scripts/project-skill-extension.mjs`
514
+ - changed: `scripts/readiness.mjs`
515
+ - changed: `scripts/release-state.mjs`
516
+ - changed: `scripts/release-state.test.mjs`
517
+ - changed: `scripts/test_board_sync_create_idempotency.py`
518
+ - changed: `scripts/test_board_sync_wave_title.py`
519
+ - changed: `scripts/test_census_backstop.py`
520
+ - changed: `scripts/test_orchestrate_wave_contract.py`
521
+ - changed: `scripts/test_retro_wrapup_contract.py`
522
+ - changed: `scripts/workflow-advisories/core.py`
523
+ - changed: `scripts/worktree-lifecycle/README.md`
524
+ - changed: `scripts/worktree-lifecycle/cleanup.py`
525
+ - changed: `scripts/worktree-lifecycle/core.py`
526
+ - changed: `scripts/worktree-lifecycle/profile.py`
527
+ - changed: `scripts/wrapup-land.py`
528
+ - changed: `src/cli.mjs`
529
+ - changed: `src/lib/manifest.mjs`
530
+ - changed: `src/lib/projectSkillExtension.mjs`
531
+ - changed: `src/lib/updateCandidate.mjs`
532
+ - changed: `src/lib/updateDecisions.mjs`
533
+ - changed: `src/lib/verifyUpdateCandidateProtocol.mjs`
534
+
448
535
  ### 0.36.5
449
536
 
450
537
  - changed: `src/lib/updateCandidate.mjs`
@@ -1,5 +1,5 @@
1
1
  {
2
- "kitVersion": "0.36.5",
2
+ "kitVersion": "0.38.0",
3
3
  "files": [
4
4
  {
5
5
  "path": ".agents/skills/ask-matt/SKILL.md",
@@ -447,7 +447,7 @@
447
447
  "ownerSkill": "orchestrate-wave",
448
448
  "surface": "codex",
449
449
  "installRole": "consumer",
450
- "sha256": "36570dfccd6dcfa41774e56fd8e31c794a31da72a609753291cf29f87d2636d6",
450
+ "sha256": "02862cd5e7e07a5f06dad89792fcebd0a6c17cf2216a27c7e0f724026fdda53a",
451
451
  "mode": 420,
452
452
  "origin": "kit"
453
453
  },
@@ -667,7 +667,7 @@
667
667
  "ownerSkill": "setup-workflow",
668
668
  "surface": "codex",
669
669
  "installRole": "consumer",
670
- "sha256": "4ced75934cb409903344f196253e99c1166d7c8705d92c477bfeda92f8a9cadf",
670
+ "sha256": "78f24c6f32bb4b3dd65293c2121b726fbf495f95d884d0527a32b6f1cfae52f5",
671
671
  "mode": 420,
672
672
  "origin": "kit"
673
673
  },
@@ -687,7 +687,7 @@
687
687
  "ownerSkill": "setup-workflow",
688
688
  "surface": "codex",
689
689
  "installRole": "consumer",
690
- "sha256": "697ef6f6cdb749bf0646b2add95d4d3246df39c48ca6d9ade063a775690ce462",
690
+ "sha256": "a5733b71f9257f0999c866bfdea551aedf4b9066572bd484fc263440cb292ca6",
691
691
  "mode": 420,
692
692
  "origin": "kit"
693
693
  },
@@ -757,7 +757,7 @@
757
757
  "ownerSkill": "setup-workflow",
758
758
  "surface": "codex",
759
759
  "installRole": "consumer",
760
- "sha256": "5e0914874f0b9f940205fbcfcfbba87d0c2df8f31db104d2c5328a0d20c80616",
760
+ "sha256": "e53e6b37bdfdfb0014e2e7ad8fe84cfc2af302eb1a9c6e9e682b1eec05036f1e",
761
761
  "mode": 420,
762
762
  "origin": "kit"
763
763
  },
@@ -997,7 +997,7 @@
997
997
  "ownerSkill": "wrapup",
998
998
  "surface": "codex",
999
999
  "installRole": "consumer",
1000
- "sha256": "51fcbabb90f3b04c46079ed9fb491592d0dbad1e1069723e6054376415127fa0",
1000
+ "sha256": "2f84b787b81b6f63ebda996a648a6a32c0c79c5894d6471f84250c0f2a24a2c3",
1001
1001
  "mode": 420,
1002
1002
  "origin": "kit"
1003
1003
  },
@@ -1143,7 +1143,7 @@
1143
1143
  "path": ".claude/hooks/migration-snapshot-reminder.py",
1144
1144
  "kind": "hook",
1145
1145
  "installRole": "consumer",
1146
- "sha256": "693227fae1aa3e782ed5b61a9e8c3fc8c1880a617efd14cb86ca7f38d9a8f204",
1146
+ "sha256": "0cf691143fc4dbac210f45be35e037686adf678f6f521cfd80d9100bcfbb7e02",
1147
1147
  "mode": 493,
1148
1148
  "origin": "kit"
1149
1149
  },
@@ -1769,7 +1769,7 @@
1769
1769
  "ownerSkill": "orchestrate-wave",
1770
1770
  "surface": "claude",
1771
1771
  "installRole": "consumer",
1772
- "sha256": "672d251912886ac4e44f2a60fd3a35ee82c44e86bef4c19ae40d506eb1cf1427",
1772
+ "sha256": "02862cd5e7e07a5f06dad89792fcebd0a6c17cf2216a27c7e0f724026fdda53a",
1773
1773
  "mode": 420,
1774
1774
  "origin": "kit"
1775
1775
  },
@@ -2009,7 +2009,7 @@
2009
2009
  "ownerSkill": "setup-workflow",
2010
2010
  "surface": "claude",
2011
2011
  "installRole": "consumer",
2012
- "sha256": "4ced75934cb409903344f196253e99c1166d7c8705d92c477bfeda92f8a9cadf",
2012
+ "sha256": "78f24c6f32bb4b3dd65293c2121b726fbf495f95d884d0527a32b6f1cfae52f5",
2013
2013
  "mode": 420,
2014
2014
  "origin": "kit"
2015
2015
  },
@@ -2029,7 +2029,7 @@
2029
2029
  "ownerSkill": "setup-workflow",
2030
2030
  "surface": "claude",
2031
2031
  "installRole": "consumer",
2032
- "sha256": "697ef6f6cdb749bf0646b2add95d4d3246df39c48ca6d9ade063a775690ce462",
2032
+ "sha256": "a5733b71f9257f0999c866bfdea551aedf4b9066572bd484fc263440cb292ca6",
2033
2033
  "mode": 420,
2034
2034
  "origin": "kit"
2035
2035
  },
@@ -2099,7 +2099,7 @@
2099
2099
  "ownerSkill": "setup-workflow",
2100
2100
  "surface": "claude",
2101
2101
  "installRole": "consumer",
2102
- "sha256": "5e0914874f0b9f940205fbcfcfbba87d0c2df8f31db104d2c5328a0d20c80616",
2102
+ "sha256": "e53e6b37bdfdfb0014e2e7ad8fe84cfc2af302eb1a9c6e9e682b1eec05036f1e",
2103
2103
  "mode": 420,
2104
2104
  "origin": "kit"
2105
2105
  },
@@ -2107,7 +2107,7 @@
2107
2107
  "path": ".claude/skills/skill-manifest.json",
2108
2108
  "kind": "doc",
2109
2109
  "installRole": "consumer",
2110
- "sha256": "290cc3e847a10cb10b4ecb17cf0d45be0f60f95e96b7a70417b3e8da0a32e23c",
2110
+ "sha256": "d2f918149db5d75cc389279afc93516d7c12aca790ddb49de23b198d055c8377",
2111
2111
  "mode": 420,
2112
2112
  "origin": "kit"
2113
2113
  },
@@ -2347,7 +2347,7 @@
2347
2347
  "ownerSkill": "wrapup",
2348
2348
  "surface": "claude",
2349
2349
  "installRole": "consumer",
2350
- "sha256": "33b1de1f10d6ca94b89057d8b1f1afe06ae89209991ea3b6d811a8fb61f910b6",
2350
+ "sha256": "2f84b787b81b6f63ebda996a648a6a32c0c79c5894d6471f84250c0f2a24a2c3",
2351
2351
  "mode": 420,
2352
2352
  "origin": "kit"
2353
2353
  },
@@ -2427,7 +2427,7 @@
2427
2427
  "path": "scripts/anchor_table.py",
2428
2428
  "kind": "script",
2429
2429
  "installRole": "consumer",
2430
- "sha256": "204f2cfce8e641721656e745aa5b9bb664be5fc37667dcd8bc6528b8209aaec3",
2430
+ "sha256": "b8bd0e654137a1e7d309f431ab6e7f76e8049c6a3206730323ae7cc90ebc06e8",
2431
2431
  "mode": 420,
2432
2432
  "origin": "kit"
2433
2433
  },
@@ -2667,7 +2667,7 @@
2667
2667
  "path": "scripts/project-skill-extension.mjs",
2668
2668
  "kind": "script",
2669
2669
  "installRole": "consumer",
2670
- "sha256": "12b7c381774a3db6c0d99ec855337a928b70bbe612012bb06f546afb397efc87",
2670
+ "sha256": "9b038bfef135ba2f8487ced43b863631ddbcb3772700a87b6208f0e95b5213e9",
2671
2671
  "mode": 493,
2672
2672
  "origin": "kit"
2673
2673
  },
@@ -2675,7 +2675,7 @@
2675
2675
  "path": "scripts/readiness.mjs",
2676
2676
  "kind": "script",
2677
2677
  "installRole": "consumer",
2678
- "sha256": "26514c49316aa29fdeeb74fca0655cd7aa801f37e3c81cbd2551bb158138ebe8",
2678
+ "sha256": "385854b0a1d8f7915c59cc4f35a3ee6774c4d4ac631a96357abe11cb81be3021",
2679
2679
  "mode": 493,
2680
2680
  "origin": "kit"
2681
2681
  },
@@ -2699,7 +2699,7 @@
2699
2699
  "path": "scripts/release-state.mjs",
2700
2700
  "kind": "script",
2701
2701
  "installRole": "consumer",
2702
- "sha256": "fd9f7f86e26ab3faba819e0c46e5e87c2dd3a84dc9a6111b4eceb4ebe6a3a344",
2702
+ "sha256": "8e3f640aee36bf83ae4b49e449dd98eac6b747b323909ea947d4829345ccb743",
2703
2703
  "mode": 420,
2704
2704
  "origin": "kit"
2705
2705
  },
@@ -2771,7 +2771,7 @@
2771
2771
  "path": "scripts/workflow-advisories/core.py",
2772
2772
  "kind": "script",
2773
2773
  "installRole": "consumer",
2774
- "sha256": "dc47bc20ed1107d230f45ff7158e4fd81d6473be515e03cbd52274f3e87d840d",
2774
+ "sha256": "75b8d8b781344c54edf9f83c6976a999c10ceea6904af765c7179161e82d3bd6",
2775
2775
  "mode": 420,
2776
2776
  "origin": "kit"
2777
2777
  },
@@ -2779,7 +2779,7 @@
2779
2779
  "path": "scripts/worktree-lifecycle/capabilities.json",
2780
2780
  "kind": "doc",
2781
2781
  "installRole": "consumer",
2782
- "sha256": "e80501bd86bbdb0ba296fc6b172442d8ad128f43e571777698657491279e4305",
2782
+ "sha256": "689910f11b51a5f2938989a82378332375e614f2684fe4cc48db7a4ce5c12f4b",
2783
2783
  "mode": 420,
2784
2784
  "origin": "kit"
2785
2785
  },
@@ -2787,7 +2787,7 @@
2787
2787
  "path": "scripts/worktree-lifecycle/cleanup.py",
2788
2788
  "kind": "script",
2789
2789
  "installRole": "consumer",
2790
- "sha256": "370af8840732db3b6ba39b4825686f3980f69f612038123bc86b2c68921f02ca",
2790
+ "sha256": "4671aa6d40590abe103509916abda34e1717b43f7f0f521fc7fe90e1e29be50d",
2791
2791
  "mode": 493,
2792
2792
  "origin": "kit"
2793
2793
  },
@@ -2795,7 +2795,7 @@
2795
2795
  "path": "scripts/worktree-lifecycle/core.py",
2796
2796
  "kind": "script",
2797
2797
  "installRole": "consumer",
2798
- "sha256": "d54c4d663c234b7d2e607b99f6090b46104c21ba4326ef03bcb65de39d1cce19",
2798
+ "sha256": "a869ecc4646fc3493b883521594c3976e53b9b896d1361c87e1085adea10052c",
2799
2799
  "mode": 420,
2800
2800
  "origin": "kit"
2801
2801
  },
@@ -2803,7 +2803,7 @@
2803
2803
  "path": "scripts/worktree-lifecycle/profile.py",
2804
2804
  "kind": "script",
2805
2805
  "installRole": "consumer",
2806
- "sha256": "993b105efd2329b3a206b0c79399aff1c7840017362745966f9b158b1f165f09",
2806
+ "sha256": "4a76462e4a9f7c4c3fd1b199217f8c68b9946c49bc934eef7bc888244bdbd16f",
2807
2807
  "mode": 420,
2808
2808
  "origin": "kit"
2809
2809
  },
@@ -2811,15 +2811,23 @@
2811
2811
  "path": "scripts/worktree-lifecycle/README.md",
2812
2812
  "kind": "doc",
2813
2813
  "installRole": "consumer",
2814
- "sha256": "5dba0332253c37162ad1b2f1b7bf504a23af85bf297fdaa8920036f610ff754a",
2814
+ "sha256": "55c62d00f84e5a1e008d059dc1aad2e026f35dab25437e5cb3c53b717d621a06",
2815
2815
  "mode": 420,
2816
2816
  "origin": "kit"
2817
2817
  },
2818
+ {
2819
+ "path": "scripts/worktree-lifecycle/session.py",
2820
+ "kind": "script",
2821
+ "installRole": "consumer",
2822
+ "sha256": "18c2794aaf9fef96a15000c515b600345d3bf1715e23b07d0646156e601ea35a",
2823
+ "mode": 493,
2824
+ "origin": "kit"
2825
+ },
2818
2826
  {
2819
2827
  "path": "scripts/worktree-lifecycle/setup.py",
2820
2828
  "kind": "script",
2821
2829
  "installRole": "consumer",
2822
- "sha256": "0021c50809373335e9dafe97196f42ad039152418baa49e2004b25ba33d137e2",
2830
+ "sha256": "fa84d0056b24263389e0d17746abc8dc0afbd0152c323a62a6d7590ca9c62cac",
2823
2831
  "mode": 493,
2824
2832
  "origin": "kit"
2825
2833
  },
@@ -2827,7 +2835,7 @@
2827
2835
  "path": "scripts/wrapup-land.py",
2828
2836
  "kind": "script",
2829
2837
  "installRole": "consumer",
2830
- "sha256": "ee1f567e7be4f7baae222ed3974d39e5efee93edb2a663cacc8e18b567fa837e",
2838
+ "sha256": "f92779efab05fb17447abf47b8aec3fab6a501eff8e11735c245e5f615f9e460",
2831
2839
  "mode": 493,
2832
2840
  "origin": "kit"
2833
2841
  },
@@ -2883,7 +2891,7 @@
2883
2891
  "path": "src/lib/manifest.mjs",
2884
2892
  "kind": "script",
2885
2893
  "installRole": "consumer",
2886
- "sha256": "f2920f2b25a700be080fbba122a809bf509080673b806aaab2b0c773c8d8267f",
2894
+ "sha256": "9f8b4cc90d710a616efd82dce2389504efb27781ffa769f9617108dcba96cb0e",
2887
2895
  "mode": 420,
2888
2896
  "origin": "kit"
2889
2897
  },
@@ -2899,7 +2907,7 @@
2899
2907
  "path": "src/lib/projectSkillExtension.mjs",
2900
2908
  "kind": "script",
2901
2909
  "installRole": "consumer",
2902
- "sha256": "2d8708b230bd9d34474c613cfffc89bdbd11cc128837cdaeab12fc06acea2b04",
2910
+ "sha256": "63fa51c0aa6a0bf1b7168467c33810ecca95d9f1252a1455cbce122d3d4cf8d2",
2903
2911
  "mode": 420,
2904
2912
  "origin": "kit"
2905
2913
  },
@@ -3091,7 +3099,7 @@
3091
3099
  "path": "src/lib/updateDecisions.mjs",
3092
3100
  "kind": "script",
3093
3101
  "installRole": "consumer",
3094
- "sha256": "d80969d5d8c6b084289dfc4871924fb1835bbec6cf08b859ecd81bb95e6f767b",
3102
+ "sha256": "1df6ce6a281fb559b9c7663905f67ab3a4cdae80b365882a395f7b37b57e1672",
3095
3103
  "mode": 420,
3096
3104
  "origin": "kit"
3097
3105
  },
@@ -0,0 +1,88 @@
1
+ # Session teardown requires provenance-bound ownership
2
+
3
+ Status: accepted (2026-07-26, Wave #271)
4
+
5
+ Long agent sessions create branches, linked worktrees, setup outputs, and
6
+ landing-time build artifacts. The desired end state is that every
7
+ provenance-captured target created by that session can be removed, including
8
+ branches whose changes reached
9
+ canonical `main` through patch-equivalent commits rather than ancestry.
10
+
11
+ Repository state alone cannot identify that ownership safely. A foreign branch
12
+ can have the same name or patch, a user file can match a generated-artifact
13
+ pattern, and a path can be replaced between assessment and deletion. Retrying
14
+ after a crash adds a time dimension: a fresh snapshot cannot distinguish an
15
+ earlier generator output from a file created by the user between attempts.
16
+
17
+ We decided that teardown authority must come from **Teardown provenance**, not
18
+ from present-day similarity:
19
+
20
+ 1. A session records intent before creation, but receives cleanup authority only
21
+ through an atomic **Ownership proof**. Branch and proof ref are acquired in
22
+ one Git transaction bound to the active wave claim.
23
+ 2. Setup, session work, and landing are separate provenance boundaries. Setup
24
+ and session cleanup use the worktree-creation baseline; landing cleanup uses
25
+ a persisted landing-attempt boundary and freezes the exact outputs of its
26
+ generator-capable step.
27
+ 3. Paths and patterns select candidates but never prove identity. Cleanup binds
28
+ regular files and worktree roots to no-follow object evidence and revalidates
29
+ that evidence immediately before mutation.
30
+ 4. The **Lifecycle receipt** is a durably replaced state-transition journal.
31
+ Prepared Git ref transactions close the worktree-removal race, and committed
32
+ intermediate states make known outcomes resumable. An external SIGKILL may
33
+ still leave Git's own lock files requiring operator recovery.
34
+ 5. Lost evidence is not reconstructed from coincidence. An interrupted landing
35
+ attempt without frozen outputs can be archived without claiming or deleting
36
+ its files, after which the ambiguous files must be classified before a new
37
+ attempt proceeds.
38
+
39
+ ## Considered options
40
+
41
+ - **Delete branches merged by ancestry:** rejected because squash,
42
+ cherry-pick, and patch-equivalent integration leave session-owned branch refs
43
+ outside ancestry.
44
+ - **Treat a receipt row as ownership:** rejected because another process can
45
+ create the named branch or worktree after the absence check but before the
46
+ attempted creation.
47
+ - **Authorize files by ignored path or generator pattern:** rejected because a
48
+ consumer file can occupy the same path before landing or replace an assessed
49
+ output afterward.
50
+ - **Take a fresh landing snapshot on every retry:** rejected because it protects
51
+ prior generator outputs as if they predated landing, while a persistent start
52
+ snapshot alone incorrectly claims every later matching user file.
53
+ - **Force-remove apparently clean targets:** rejected because force bypasses the
54
+ repository and filesystem evidence that distinguishes owned work from foreign
55
+ work.
56
+ - **Guess ownership after an unjournaled crash window:** rejected because safe
57
+ cleanup and guaranteed automatic liveness cannot both be recovered after the
58
+ distinguishing evidence is lost.
59
+
60
+ ## Consequences
61
+
62
+ - A completed session can remove its exact worktrees, branches, proof refs, and
63
+ provenance-captured generated artifacts without hiding history or sweeping
64
+ unrelated repository state.
65
+ - Foreign branches, worktrees, replacement symlinks, replacements, late writes,
66
+ and pre-landing consumer files are hard stops or remain outside cleanup
67
+ authority. Setup recovery may remove only a symlink whose exact no-follow
68
+ identity it captured as setup-created.
69
+ - Exact unchanged frozen evidence is deterministically resumable. Started or
70
+ drifted frozen attempts can be explicitly relinquished by archiving only the
71
+ receipt; every current file then remains protected and must be classified
72
+ before another attempt.
73
+ - Provenance-less legacy worktrees receive only a conservative baseline after
74
+ exact registration, no-follow root, attached branch, and clean tracked/index
75
+ checks. Setup reuse defers backfill while tracked work or current
76
+ landing-generated blockers remain. Landing classifies those blockers before
77
+ writing evidence; after they are moved, all remaining current ignored and
78
+ untracked files become protected. Corrupt evidence and active attempts are
79
+ never backfilled.
80
+ - Before merge, a committed worktree policy may nominate only exact,
81
+ identity-bound cleanup candidates; it authorizes no deletion. After merge,
82
+ the profile is reloaded directly from canonical `origin/main` and must match
83
+ both the candidate and attempt-bound policy digest before any mutation.
84
+ Generator evidence is also reauthorized path-by-path against that canonical
85
+ policy. Missing policy, an unmerged widening, or branch/canonical drift
86
+ preserves every file.
87
+ - The lifecycle implementation is necessarily a small transaction protocol,
88
+ with more states and race tests than a path-based cleanup script.
@@ -7,11 +7,22 @@
7
7
  "branchRegex": "^(?:feat|fix|chore|docs)/(?P<issue>\\d+)-",
8
8
  "mainBranches": ["main"],
9
9
  "protectedBranches": ["main"],
10
+ "scratchPatterns": [
11
+ "PLAN.md",
12
+ "PLAN-REVIEW-LOG.md",
13
+ ".claude/logs/**"
14
+ ],
10
15
  "setupEntry": "python3 scripts/worktree-lifecycle/setup.py",
11
16
  "setupSteps": [],
12
17
  "riskyCommandPatterns": [
13
18
  "\\b(?:npm|pnpm|yarn)\\s+(?:run\\s+)?(?:test|typecheck|build)\\b",
14
19
  "\\bgit\\s+(?:commit|push)\\b"
15
20
  ]
21
+ },
22
+ "wrapup": {
23
+ "landingGeneratedArtifactPatterns": [
24
+ "dist-kit/**",
25
+ "**/__pycache__/**"
26
+ ]
16
27
  }
17
28
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikon85/agent-workflow-kit",
3
- "version": "0.36.5",
3
+ "version": "0.38.0",
4
4
  "description": "Portable AI-agent workflow skills (plan → execute → land → learn) for Claude Code & Codex — grilling, TDD, diagnosis, two-axis code review, cross-model Codex review, design & domain-modeling, plus a skill router (ask-matt). npx init/update/diff/uninstall.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -29,7 +29,7 @@ SLICE_TABLE_START = "<!-- slice-table:start -->"
29
29
  SLICE_TABLE_END = "<!-- slice-table:end -->"
30
30
 
31
31
  _SUBISSUE_NUM_RE = re.compile(r"#(\d+)")
32
- _STATUS_BASE_RE = re.compile(r"^(✅\s*#\d+|🔄|⬜)")
32
+ _STATUS_BASE_RE = re.compile(r"^(✅(?:\s*#\d+)?|🔄|⬜)")
33
33
  _STATUS_RANK = {"⬜": 0, "🔄": 1}
34
34
 
35
35
 
@@ -113,17 +113,23 @@ def first_subissue_num(cell: str) -> Optional[int]:
113
113
 
114
114
  # --- volatile-cell refresh (board → cell) ------------------------------------
115
115
  def status_token_from_board(entry: dict, roles: dict) -> str:
116
- """Canonical Status token from board state: ✅ #<PR> (merged) · 🔄 (open PR or
117
- an in-flight board status) · (otherwise).
118
-
119
- Which status NAMES count as in-flight comes from the profile's role map
120
- (`roles["inProgress"]`/`roles["review"]`) — passed in as a plain
121
- dict so this module stays pure (no board_config import). Empty roles →
122
- board-status names can't be interpreted (⬜), PR signals still work."""
116
+ """Canonical Status token from board state: ✅ #<PR> (merged) · (closed
117
+ issue in the configured done status) · 🔄 (open PR or an in-flight board
118
+ status) · ⬜ (otherwise).
119
+
120
+ Status NAMES come from the profile's role map (`roles["done"]`,
121
+ `roles["inProgress"]`, `roles["review"]`) passed in as a plain dict so
122
+ this module stays pure (no board_config import). Empty roles board-status
123
+ names can't be interpreted (⬜), PR signals still work."""
123
124
  prs = entry.get("prs") or []
124
125
  merged = [p for p in prs if p.get("state") == "MERGED"]
125
126
  if merged:
126
127
  return f"✅ #{merged[-1]['number']}"
128
+ done = roles.get("done")
129
+ if (done is not None
130
+ and entry.get("state") == "closed"
131
+ and entry.get("status") == done):
132
+ return "✅"
127
133
  active = {roles.get("inProgress"), roles.get("review")} - {None}
128
134
  if [p for p in prs if p.get("state") == "OPEN"] or entry.get("status") in active:
129
135
  return "🔄"