@ikon85/agent-workflow-kit 0.34.1 → 0.34.3

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.
@@ -11,6 +11,41 @@ A discipline for hard bugs. Skip phases only when explicitly justified.
11
11
 
12
12
  When exploring the codebase, read `CONTEXT.md` (if it exists) to get a clear mental model of the relevant modules, and check ADRs in the area you're touching.
13
13
 
14
+ ## Phase 0 — Claim the tracked issue
15
+
16
+ Applies only when the bug arrives as a tracked issue. A bug reported in chat has
17
+ nothing to claim; go straight to Phase 1.
18
+
19
+ <!-- issue-claim:start -->
20
+ **Claim the issue before you build.** A worktree, branch, or PR check only sees
21
+ this machine, and only once someone has pushed — the claim on the issue itself
22
+ is the only signal a second session, a second machine, or a cloud agent can
23
+ read.
24
+
25
+ 1. **Check first.** Read the issue's assignee and its claim comments. An
26
+ `<!-- agent-claim: ... -->` marker you did not plant, or an assignee that is
27
+ not you, is a **foreign claim**: **STOP**, report the claimed branch and
28
+ worktree, and ask the user how to proceed. Take the issue over only on their
29
+ word, and never delete a foreign claim. A claim whose branch and worktree no
30
+ longer exist is stale — say that and let the user decide; do not assume it is
31
+ dead.
32
+ 2. **Plant your claim, before the first edit.** Assign the issue to yourself
33
+ where the tracker supports it, and post the claim marker as a comment:
34
+ `<!-- agent-claim: branch=<branch>; worktree=<absolute-path>; date=<YYYY-MM-DD> -->`
35
+ Branch and worktree are the payload — without them a colliding session sees
36
+ the collision but cannot find the work in progress.
37
+ 3. **Release.** A PR that references the issue **supersedes** the claim, so
38
+ landing needs no extra step. If you abandon or hand back the work, remove
39
+ your own claim (unassign plus a one-line `claim released` comment) before you
40
+ stop.
41
+
42
+ The concrete commands are your tracker's — `docs/agents/issue-tracker.md`,
43
+ §Pickup claim. If that layer documents no claim convention, fall back to the two
44
+ generic operations above: self-assign, plus the marker comment.
45
+ <!-- issue-claim:end -->
46
+
47
+ Building the Phase-1 loop is already work: claim before it, not after.
48
+
14
49
  ## Phase 1 — Build a feedback loop
15
50
 
16
51
  **This is the skill.** Everything else is mechanical. If you have a **tight** pass/fail signal for the bug — one that goes red on _this_ bug — you will find the cause; bisection, hypothesis-testing, and instrumentation all just consume it. If you don't have one, no amount of staring at code will save you.
@@ -6,6 +6,39 @@ disable-model-invocation: true
6
6
 
7
7
  Implement the work described by the user in the PRD or issues.
8
8
 
9
+ ## Pickup — claim the issue first
10
+
11
+ Applies whenever the work comes from a tracked issue. Work handed over as plain
12
+ text or a local plan has nothing to claim; skip this section then.
13
+
14
+ <!-- issue-claim:start -->
15
+ **Claim the issue before you build.** A worktree, branch, or PR check only sees
16
+ this machine, and only once someone has pushed — the claim on the issue itself
17
+ is the only signal a second session, a second machine, or a cloud agent can
18
+ read.
19
+
20
+ 1. **Check first.** Read the issue's assignee and its claim comments. An
21
+ `<!-- agent-claim: ... -->` marker you did not plant, or an assignee that is
22
+ not you, is a **foreign claim**: **STOP**, report the claimed branch and
23
+ worktree, and ask the user how to proceed. Take the issue over only on their
24
+ word, and never delete a foreign claim. A claim whose branch and worktree no
25
+ longer exist is stale — say that and let the user decide; do not assume it is
26
+ dead.
27
+ 2. **Plant your claim, before the first edit.** Assign the issue to yourself
28
+ where the tracker supports it, and post the claim marker as a comment:
29
+ `<!-- agent-claim: branch=<branch>; worktree=<absolute-path>; date=<YYYY-MM-DD> -->`
30
+ Branch and worktree are the payload — without them a colliding session sees
31
+ the collision but cannot find the work in progress.
32
+ 3. **Release.** A PR that references the issue **supersedes** the claim, so
33
+ landing needs no extra step. If you abandon or hand back the work, remove
34
+ your own claim (unassign plus a one-line `claim released` comment) before you
35
+ stop.
36
+
37
+ The concrete commands are your tracker's — `docs/agents/issue-tracker.md`,
38
+ §Pickup claim. If that layer documents no claim convention, fall back to the two
39
+ generic operations above: self-assign, plus the marker comment.
40
+ <!-- issue-claim:end -->
41
+
9
42
  Use /tdd where possible, at pre-agreed seams.
10
43
 
11
44
  Run typechecking regularly, single test files regularly, and the full test suite once at the end.
@@ -23,11 +23,20 @@ never publishes to a registry or creates a GitHub release directly.
23
23
  recommend Major, additions recommend Minor, and changed content recommends
24
24
  Patch.
25
25
 
26
- 2. Ask the user to confirm exactly one target Semver. Do not change release
27
- metadata before confirmation. A recommendation is advice; the confirmed
28
- target is authority.
26
+ 2. Resolve authority for exactly one target Semver:
29
27
 
30
- 3. Prepare that exact version:
28
+ - Normally, ask the user to confirm the target before changing release
29
+ metadata. A recommendation is advice; the confirmed target is authority.
30
+ - When the caller carries an **explicit AFK end-to-end mandate** whose scope
31
+ includes release preparation, accept the deterministic recommendation as
32
+ the target for **reversible metadata preparation** and record that choice.
33
+ Do not turn a narrower build-only or single-action request into this
34
+ authority.
35
+
36
+ Both routes authorize one prepared target only. Neither authorizes
37
+ publication.
38
+
39
+ 3. Prepare that authorized exact version:
31
40
 
32
41
  ```sh
33
42
  npm run release:prepare -- --version <confirmed-version>
@@ -45,8 +54,9 @@ never publishes to a registry or creates a GitHub release directly.
45
54
 
46
55
  5. After merge, report the exact integrated version and commit as
47
56
  `awaiting-tag`. Merging integrates the prepared release; it cannot start
48
- publication. Ask the user for a second, explicit confirmation to publish
49
- that exact `v<version>`. The earlier Semver confirmation authorized metadata
57
+ publication. Creating and pushing the annotated `v<version>` tag requires a
58
+ **separate explicit confirmation** to publish that exact version. The
59
+ earlier Semver confirmation or AFK preparation mandate authorized metadata
50
60
  preparation, not publication.
51
61
 
52
62
  6. After that confirmation, verify that the package version on current
@@ -62,6 +62,16 @@ node scripts/readiness.mjs check --skill orchestrate-wave --json
62
62
  change — current gate, last green milestone, next step, visible blocker/risk. A
63
63
  still-running tool session is stated as such; never wait silently for the final
64
64
  result.
65
+ - **Authorization scope is sticky and gates are local.** A single-Wave mandate
66
+ authorizes only that Wave. A later explicit whole-Program mandate authorizes all
67
+ planned Waves in that Program and overrides a default per-Wave authorization
68
+ sentence in the Program-PRD; record that scope once and **Do not re-ask at each
69
+ Wave boundary**. A manual or irreversible gate pauses only the gated action:
70
+ continue every dependency-safe reversible or read-only step inside the
71
+ authorized scope, including preparation for later Waves. A persistent goal
72
+ must not be marked `blocked` while meaningful authorized safe work remains
73
+ anywhere in that scope. None of this carries authorization into the gated
74
+ irreversible action itself.
65
75
  - **Degrade by subtraction, never by improvisation.** The defined failure paths:
66
76
  - **Slice stays red after escalation** (2-strikes rule, see Routing): pull the
67
77
  slice — do not merge it (or revert its merge), pull every dependent that
@@ -180,6 +190,14 @@ Before each slice, bind **(a) inline vs delegate** and **(b) tier + effort** und
180
190
  Standing rules. Tiny mechanical work may stay inline.
181
191
  For Path A/B, create one worktree per agent from wave HEAD, using `§Setup` or
182
192
  `git worktree add <path> -b feat/<anchor>-<slug>`.
193
+ - **Claim each slice issue at builder launch — the wave claim is not a slice
194
+ claim.** It guards the anchor on this machine only; the slice issue is the
195
+ grabbable unit a second machine or cloud agent sees. Before dispatch, skip and
196
+ report any slice already carrying a foreign claim (assignee or
197
+ `<!-- agent-claim: ... -->` comment this run did not plant) — never overwrite
198
+ one. Otherwise assign it to yourself and post
199
+ `<!-- agent-claim: branch=<slice-branch>; worktree=<builder-worktree>; date=<YYYY-MM-DD> -->`
200
+ so the claim names the WIP. Commands: `docs/agents/issue-tracker.md`, §Pickup claim.
183
201
  - **Build the prompt from [`references/builder-contract.md`](references/builder-contract.md)**
184
202
  — fill the slots with the slice's VERBATIM What+AC, plan decision, recon
185
203
  file:line map and consume-only lines; never paraphrase (paraphrase drift has
@@ -190,7 +208,8 @@ For Path A/B, create one worktree per agent from wave HEAD, using `§Setup` or
190
208
 
191
209
  **Done when:** every dispatched slice reported back with commit SHA + green
192
210
  package tests + green fast gate (`§Builder Commands`) — or a STOP item you resolved
193
- (answered / re-dispatched) before integration.
211
+ (answered / re-dispatched) before integration · every dispatched slice issue
212
+ carries this run's claim, every foreign-claimed slice skipped and reported.
194
213
 
195
214
  ## Phase 3 — Serial integration
196
215
 
@@ -302,6 +321,9 @@ written · merge order documented.
302
321
  `wave-active/<anchor>`, call `releaseWaveClaim` with this run's owner after
303
322
  success or abort; its owner check, not a broad `wave-active/*` pattern,
304
323
  authorizes cleanup, and a foreign marker is left untouched.
324
+ - **Release this run's slice claims.** A landed slice's PR supersedes its claim;
325
+ for a pulled or abandoned slice remove the claim this run planted (unassign +
326
+ `claim released` comment) so it stays grabbable. Foreign ones stay untouched.
305
327
  - **Before removing any slice worktree, read its `ANNAHMEN.md`** (an assumptions
306
328
  log, gitignored at worktree-root) and propagate each build-time assumption marker
307
329
  to the sibling issue it carries. A hand-driven multi-PR / migration landing does
@@ -331,7 +353,8 @@ written · merge order documented.
331
353
  - Leave slice worktrees for the user to inspect, or note they're
332
354
  post-merge-removable.
333
355
 
334
- **Done when:** no orphan process · this run's claim removed · ANNAHMEN propagated ·
356
+ **Done when:** no orphan process · this run's wave claim removed · this run's
357
+ unlanded slice claims released · ANNAHMEN propagated ·
335
358
  anchor reconciled + leaf closes verified · anchor closure decided and documented ·
336
359
  Program-PRD propagation completed or explicitly skipped · final report lists
337
360
  landed/pulled slices as **X of Y**.
@@ -23,6 +23,17 @@ Create a GitHub issue.
23
23
 
24
24
  Run `gh issue view <number> --comments`.
25
25
 
26
+ ## Pickup claim
27
+
28
+ Used by `/implement`, `/diagnose`, and `/orchestrate-wave`: the visible claim
29
+ that keeps a second session, machine, or cloud agent off an issue already being
30
+ built. The marker carries the WIP location, the assignee makes the claim visible
31
+ in list views.
32
+
33
+ - **Check** (before any edit): `gh issue view <n> --json assignees,comments --jq '{assignees: [.assignees[].login], claims: [.comments[].body | select(contains("<!-- agent-claim:"))]}'` — a foreign assignee or a marker this session did not plant is a foreign claim: stop and report it, never overwrite or delete it.
34
+ - **Claim**: `gh issue edit <n> --add-assignee @me`, then post the marker as a comment — `gh issue comment <n> --body '<!-- agent-claim: branch=<branch>; worktree=<absolute-path>; date=<YYYY-MM-DD> -->'`.
35
+ - **Release**: a PR referencing the issue supersedes the claim — no extra step when the work lands. On abandon: `gh issue edit <n> --remove-assignee @me` plus `gh issue comment <n> --body "claim released"`.
36
+
26
37
  ## Wayfinding operations
27
38
 
28
39
  Used by `/wayfinder`. The **map** is a single issue with **child** issues as tickets.
@@ -22,6 +22,17 @@ Create a GitLab issue.
22
22
 
23
23
  Run `glab issue view <number> --comments`.
24
24
 
25
+ ## Pickup claim
26
+
27
+ Used by `/implement`, `/diagnose`, and `/orchestrate-wave`: the visible claim
28
+ that keeps a second session, machine, or cloud agent off an issue already being
29
+ built. The marker carries the WIP location, the assignee makes the claim visible
30
+ in list views.
31
+
32
+ - **Check** (before any edit): `glab issue view <n> --comments` (or `-F json`) — a foreign assignee, or an `<!-- agent-claim: ... -->` note this session did not plant, is a foreign claim: stop and report it, never overwrite or delete it.
33
+ - **Claim**: `glab issue update <n> --assignee @me`, then post the marker as a note — `glab issue note <n> --message '<!-- agent-claim: branch=<branch>; worktree=<absolute-path>; date=<YYYY-MM-DD> -->'`.
34
+ - **Release**: a merge request referencing the issue supersedes the claim — no extra step when the work lands. On abandon, drop yourself with the `/unassign @<your-user>` quick action (`glab issue note <n> --message "/unassign @<your-user>"`) and post a `claim released` note.
35
+
25
36
  ## Wayfinding operations
26
37
 
27
38
  Used by `/wayfinder`. The **map** is a single issue with **child** issues as tickets.
@@ -18,6 +18,17 @@ Create a new file under `.scratch/<feature-slug>/` (creating the directory if ne
18
18
 
19
19
  Read the file at the referenced path. The user will normally pass the path or the issue number directly.
20
20
 
21
+ ## Pickup claim
22
+
23
+ Used by `/implement`, `/diagnose`, and `/orchestrate-wave`: the visible claim
24
+ that keeps a second session, machine, or cloud agent off an issue already being
25
+ built. There is no assignee field here, so the marker line carries the whole
26
+ claim — commit it, otherwise no other checkout can see it.
27
+
28
+ - **Check** (before any edit): read the issue file and grep it for `<!-- agent-claim:`. A marker this session did not plant is a foreign claim: stop and report it, never overwrite or delete it.
29
+ - **Claim**: set `Status: claimed` and add one marker line directly under it — `<!-- agent-claim: branch=<branch>; worktree=<absolute-path>; date=<YYYY-MM-DD> -->` — then commit and push the issue file so other checkouts see the claim.
30
+ - **Release**: a PR referencing the issue supersedes the claim. On abandon, delete your own marker line, reset `Status:` to its pre-pickup role, and commit.
31
+
21
32
  ## Wayfinding operations
22
33
 
23
34
  Used by `/wayfinder`. The **map** is a file with one **child** file per ticket.
@@ -11,6 +11,41 @@ A discipline for hard bugs. Skip phases only when explicitly justified.
11
11
 
12
12
  When exploring the codebase, read `CONTEXT.md` (if it exists) to get a clear mental model of the relevant modules, and check ADRs in the area you're touching.
13
13
 
14
+ ## Phase 0 — Claim the tracked issue
15
+
16
+ Applies only when the bug arrives as a tracked issue. A bug reported in chat has
17
+ nothing to claim; go straight to Phase 1.
18
+
19
+ <!-- issue-claim:start -->
20
+ **Claim the issue before you build.** A worktree, branch, or PR check only sees
21
+ this machine, and only once someone has pushed — the claim on the issue itself
22
+ is the only signal a second session, a second machine, or a cloud agent can
23
+ read.
24
+
25
+ 1. **Check first.** Read the issue's assignee and its claim comments. An
26
+ `<!-- agent-claim: ... -->` marker you did not plant, or an assignee that is
27
+ not you, is a **foreign claim**: **STOP**, report the claimed branch and
28
+ worktree, and ask the user how to proceed. Take the issue over only on their
29
+ word, and never delete a foreign claim. A claim whose branch and worktree no
30
+ longer exist is stale — say that and let the user decide; do not assume it is
31
+ dead.
32
+ 2. **Plant your claim, before the first edit.** Assign the issue to yourself
33
+ where the tracker supports it, and post the claim marker as a comment:
34
+ `<!-- agent-claim: branch=<branch>; worktree=<absolute-path>; date=<YYYY-MM-DD> -->`
35
+ Branch and worktree are the payload — without them a colliding session sees
36
+ the collision but cannot find the work in progress.
37
+ 3. **Release.** A PR that references the issue **supersedes** the claim, so
38
+ landing needs no extra step. If you abandon or hand back the work, remove
39
+ your own claim (unassign plus a one-line `claim released` comment) before you
40
+ stop.
41
+
42
+ The concrete commands are your tracker's — `docs/agents/issue-tracker.md`,
43
+ §Pickup claim. If that layer documents no claim convention, fall back to the two
44
+ generic operations above: self-assign, plus the marker comment.
45
+ <!-- issue-claim:end -->
46
+
47
+ Building the Phase-1 loop is already work: claim before it, not after.
48
+
14
49
  ## Phase 1 — Build a feedback loop
15
50
 
16
51
  **This is the skill.** Everything else is mechanical. If you have a **tight** pass/fail signal for the bug — one that goes red on _this_ bug — you will find the cause; bisection, hypothesis-testing, and instrumentation all just consume it. If you don't have one, no amount of staring at code will save you.
@@ -6,6 +6,39 @@ disable-model-invocation: true
6
6
 
7
7
  Implement the work described by the user in the PRD or issues.
8
8
 
9
+ ## Pickup — claim the issue first
10
+
11
+ Applies whenever the work comes from a tracked issue. Work handed over as plain
12
+ text or a local plan has nothing to claim; skip this section then.
13
+
14
+ <!-- issue-claim:start -->
15
+ **Claim the issue before you build.** A worktree, branch, or PR check only sees
16
+ this machine, and only once someone has pushed — the claim on the issue itself
17
+ is the only signal a second session, a second machine, or a cloud agent can
18
+ read.
19
+
20
+ 1. **Check first.** Read the issue's assignee and its claim comments. An
21
+ `<!-- agent-claim: ... -->` marker you did not plant, or an assignee that is
22
+ not you, is a **foreign claim**: **STOP**, report the claimed branch and
23
+ worktree, and ask the user how to proceed. Take the issue over only on their
24
+ word, and never delete a foreign claim. A claim whose branch and worktree no
25
+ longer exist is stale — say that and let the user decide; do not assume it is
26
+ dead.
27
+ 2. **Plant your claim, before the first edit.** Assign the issue to yourself
28
+ where the tracker supports it, and post the claim marker as a comment:
29
+ `<!-- agent-claim: branch=<branch>; worktree=<absolute-path>; date=<YYYY-MM-DD> -->`
30
+ Branch and worktree are the payload — without them a colliding session sees
31
+ the collision but cannot find the work in progress.
32
+ 3. **Release.** A PR that references the issue **supersedes** the claim, so
33
+ landing needs no extra step. If you abandon or hand back the work, remove
34
+ your own claim (unassign plus a one-line `claim released` comment) before you
35
+ stop.
36
+
37
+ The concrete commands are your tracker's — `docs/agents/issue-tracker.md`,
38
+ §Pickup claim. If that layer documents no claim convention, fall back to the two
39
+ generic operations above: self-assign, plus the marker comment.
40
+ <!-- issue-claim:end -->
41
+
9
42
  Use /tdd where possible, at pre-agreed seams.
10
43
 
11
44
  Run typechecking regularly, single test files regularly, and the full test suite once at the end.
@@ -23,11 +23,20 @@ never publishes to a registry or creates a GitHub release directly.
23
23
  recommend Major, additions recommend Minor, and changed content recommends
24
24
  Patch.
25
25
 
26
- 2. Ask the user to confirm exactly one target Semver. Do not change release
27
- metadata before confirmation. A recommendation is advice; the confirmed
28
- target is authority.
26
+ 2. Resolve authority for exactly one target Semver:
29
27
 
30
- 3. Prepare that exact version:
28
+ - Normally, ask the user to confirm the target before changing release
29
+ metadata. A recommendation is advice; the confirmed target is authority.
30
+ - When the caller carries an **explicit AFK end-to-end mandate** whose scope
31
+ includes release preparation, accept the deterministic recommendation as
32
+ the target for **reversible metadata preparation** and record that choice.
33
+ Do not turn a narrower build-only or single-action request into this
34
+ authority.
35
+
36
+ Both routes authorize one prepared target only. Neither authorizes
37
+ publication.
38
+
39
+ 3. Prepare that authorized exact version:
31
40
 
32
41
  ```sh
33
42
  npm run release:prepare -- --version <confirmed-version>
@@ -45,8 +54,9 @@ never publishes to a registry or creates a GitHub release directly.
45
54
 
46
55
  5. After merge, report the exact integrated version and commit as
47
56
  `awaiting-tag`. Merging integrates the prepared release; it cannot start
48
- publication. Ask the user for a second, explicit confirmation to publish
49
- that exact `v<version>`. The earlier Semver confirmation authorized metadata
57
+ publication. Creating and pushing the annotated `v<version>` tag requires a
58
+ **separate explicit confirmation** to publish that exact version. The
59
+ earlier Semver confirmation or AFK preparation mandate authorized metadata
50
60
  preparation, not publication.
51
61
 
52
62
  6. After that confirmation, verify that the package version on current
@@ -70,6 +70,16 @@ node scripts/readiness.mjs check --skill orchestrate-wave --json
70
70
  change — current gate, last green milestone, next step, visible blocker/risk. A
71
71
  still-running tool session is stated as such; never wait silently for the final
72
72
  result.
73
+ - **Authorization scope is sticky and gates are local.** A single-Wave mandate
74
+ authorizes only that Wave. A later explicit whole-Program mandate authorizes all
75
+ planned Waves in that Program and overrides a default per-Wave authorization
76
+ sentence in the Program-PRD; record that scope once and **Do not re-ask at each
77
+ Wave boundary**. A manual or irreversible gate pauses only the gated action:
78
+ continue every dependency-safe reversible or read-only step inside the
79
+ authorized scope, including preparation for later Waves. A persistent goal
80
+ must not be marked `blocked` while meaningful authorized safe work remains
81
+ anywhere in that scope. None of this carries authorization into the gated
82
+ irreversible action itself.
73
83
  - **Degrade by subtraction, never by improvisation.** The defined failure paths:
74
84
  - **Slice stays red after escalation** (2-strikes rule, see Routing): pull the
75
85
  slice — do not merge it (or revert its merge), pull every dependent that
@@ -188,6 +198,14 @@ Before each slice, bind **(a) inline vs delegate** and **(b) tier + effort** und
188
198
  Standing rules. Tiny mechanical work may stay inline.
189
199
  For Path A/B, create one worktree per agent from wave HEAD, using `§Setup` or
190
200
  `git worktree add <path> -b feat/<anchor>-<slug>`.
201
+ - **Claim each slice issue at builder launch — the wave claim is not a slice
202
+ claim.** It guards the anchor on this machine only; the slice issue is the
203
+ grabbable unit a second machine or cloud agent sees. Before dispatch, skip and
204
+ report any slice already carrying a foreign claim (assignee or
205
+ `<!-- agent-claim: ... -->` comment this run did not plant) — never overwrite
206
+ one. Otherwise assign it to yourself and post
207
+ `<!-- agent-claim: branch=<slice-branch>; worktree=<builder-worktree>; date=<YYYY-MM-DD> -->`
208
+ so the claim names the WIP. Commands: `docs/agents/issue-tracker.md`, §Pickup claim.
191
209
  - **Build the prompt from [`references/builder-contract.md`](references/builder-contract.md)**
192
210
  — fill the slots with the slice's VERBATIM What+AC, plan decision, recon
193
211
  file:line map and consume-only lines; never paraphrase (paraphrase drift has
@@ -198,7 +216,8 @@ For Path A/B, create one worktree per agent from wave HEAD, using `§Setup` or
198
216
 
199
217
  **Done when:** every dispatched slice reported back with commit SHA + green
200
218
  package tests + green fast gate (`§Builder Commands`) — or a STOP item you resolved
201
- (answered / re-dispatched) before integration.
219
+ (answered / re-dispatched) before integration · every dispatched slice issue
220
+ carries this run's claim, every foreign-claimed slice skipped and reported.
202
221
 
203
222
  ## Phase 3 — Serial integration
204
223
 
@@ -310,6 +329,9 @@ written · merge order documented.
310
329
  `wave-active/<anchor>`, call `releaseWaveClaim` with this run's owner after
311
330
  success or abort; its owner check, not a broad `wave-active/*` pattern,
312
331
  authorizes cleanup, and a foreign marker is left untouched.
332
+ - **Release this run's slice claims.** A landed slice's PR supersedes its claim;
333
+ for a pulled or abandoned slice remove the claim this run planted (unassign +
334
+ `claim released` comment) so it stays grabbable. Foreign ones stay untouched.
313
335
  - **Before removing any slice worktree, read its `ANNAHMEN.md`** (an assumptions
314
336
  log, gitignored at worktree-root) and propagate each build-time assumption marker
315
337
  to the sibling issue it carries. A hand-driven multi-PR / migration landing does
@@ -339,7 +361,8 @@ written · merge order documented.
339
361
  - Leave slice worktrees for the user to inspect, or note they're
340
362
  post-merge-removable.
341
363
 
342
- **Done when:** no orphan process · this run's claim removed · ANNAHMEN propagated ·
364
+ **Done when:** no orphan process · this run's wave claim removed · this run's
365
+ unlanded slice claims released · ANNAHMEN propagated ·
343
366
  anchor reconciled + leaf closes verified · anchor closure decided and documented ·
344
367
  Program-PRD propagation completed or explicitly skipped · final report lists
345
368
  landed/pulled slices as **X of Y**.
@@ -23,6 +23,17 @@ Create a GitHub issue.
23
23
 
24
24
  Run `gh issue view <number> --comments`.
25
25
 
26
+ ## Pickup claim
27
+
28
+ Used by `/implement`, `/diagnose`, and `/orchestrate-wave`: the visible claim
29
+ that keeps a second session, machine, or cloud agent off an issue already being
30
+ built. The marker carries the WIP location, the assignee makes the claim visible
31
+ in list views.
32
+
33
+ - **Check** (before any edit): `gh issue view <n> --json assignees,comments --jq '{assignees: [.assignees[].login], claims: [.comments[].body | select(contains("<!-- agent-claim:"))]}'` — a foreign assignee or a marker this session did not plant is a foreign claim: stop and report it, never overwrite or delete it.
34
+ - **Claim**: `gh issue edit <n> --add-assignee @me`, then post the marker as a comment — `gh issue comment <n> --body '<!-- agent-claim: branch=<branch>; worktree=<absolute-path>; date=<YYYY-MM-DD> -->'`.
35
+ - **Release**: a PR referencing the issue supersedes the claim — no extra step when the work lands. On abandon: `gh issue edit <n> --remove-assignee @me` plus `gh issue comment <n> --body "claim released"`.
36
+
26
37
  ## Wayfinding operations
27
38
 
28
39
  Used by `/wayfinder`. The **map** is a single issue with **child** issues as tickets.
@@ -22,6 +22,17 @@ Create a GitLab issue.
22
22
 
23
23
  Run `glab issue view <number> --comments`.
24
24
 
25
+ ## Pickup claim
26
+
27
+ Used by `/implement`, `/diagnose`, and `/orchestrate-wave`: the visible claim
28
+ that keeps a second session, machine, or cloud agent off an issue already being
29
+ built. The marker carries the WIP location, the assignee makes the claim visible
30
+ in list views.
31
+
32
+ - **Check** (before any edit): `glab issue view <n> --comments` (or `-F json`) — a foreign assignee, or an `<!-- agent-claim: ... -->` note this session did not plant, is a foreign claim: stop and report it, never overwrite or delete it.
33
+ - **Claim**: `glab issue update <n> --assignee @me`, then post the marker as a note — `glab issue note <n> --message '<!-- agent-claim: branch=<branch>; worktree=<absolute-path>; date=<YYYY-MM-DD> -->'`.
34
+ - **Release**: a merge request referencing the issue supersedes the claim — no extra step when the work lands. On abandon, drop yourself with the `/unassign @<your-user>` quick action (`glab issue note <n> --message "/unassign @<your-user>"`) and post a `claim released` note.
35
+
25
36
  ## Wayfinding operations
26
37
 
27
38
  Used by `/wayfinder`. The **map** is a single issue with **child** issues as tickets.
@@ -18,6 +18,17 @@ Create a new file under `.scratch/<feature-slug>/` (creating the directory if ne
18
18
 
19
19
  Read the file at the referenced path. The user will normally pass the path or the issue number directly.
20
20
 
21
+ ## Pickup claim
22
+
23
+ Used by `/implement`, `/diagnose`, and `/orchestrate-wave`: the visible claim
24
+ that keeps a second session, machine, or cloud agent off an issue already being
25
+ built. There is no assignee field here, so the marker line carries the whole
26
+ claim — commit it, otherwise no other checkout can see it.
27
+
28
+ - **Check** (before any edit): read the issue file and grep it for `<!-- agent-claim:`. A marker this session did not plant is a foreign claim: stop and report it, never overwrite or delete it.
29
+ - **Claim**: set `Status: claimed` and add one marker line directly under it — `<!-- agent-claim: branch=<branch>; worktree=<absolute-path>; date=<YYYY-MM-DD> -->` — then commit and push the issue file so other checkouts see the claim.
30
+ - **Release**: a PR referencing the issue supersedes the claim. On abandon, delete your own marker line, reset `Status:` to its pre-pickup role, and commit.
31
+
21
32
  ## Wayfinding operations
22
33
 
23
34
  Used by `/wayfinder`. The **map** is a file with one **child** file per ticket.
package/README.md CHANGED
@@ -387,6 +387,28 @@ the old way. Decision record:
387
387
 
388
388
  ## Release notes
389
389
 
390
+ ### 0.34.3
391
+
392
+ - changed: `.agents/skills/diagnose/SKILL.md`
393
+ - changed: `.agents/skills/implement/SKILL.md`
394
+ - changed: `.agents/skills/orchestrate-wave/SKILL.md`
395
+ - changed: `.agents/skills/setup-workflow/issue-tracker-github.md`
396
+ - changed: `.agents/skills/setup-workflow/issue-tracker-gitlab.md`
397
+ - changed: `.agents/skills/setup-workflow/issue-tracker-local.md`
398
+ - changed: `.claude/skills/diagnose/SKILL.md`
399
+ - changed: `.claude/skills/implement/SKILL.md`
400
+ - changed: `.claude/skills/orchestrate-wave/SKILL.md`
401
+ - changed: `.claude/skills/setup-workflow/issue-tracker-github.md`
402
+ - changed: `.claude/skills/setup-workflow/issue-tracker-gitlab.md`
403
+ - changed: `.claude/skills/setup-workflow/issue-tracker-local.md`
404
+
405
+ ### 0.34.2
406
+
407
+ - changed: `.agents/skills/kit-release/SKILL.md`
408
+ - changed: `.agents/skills/orchestrate-wave/SKILL.md`
409
+ - changed: `.claude/skills/kit-release/SKILL.md`
410
+ - changed: `.claude/skills/orchestrate-wave/SKILL.md`
411
+
390
412
  ### 0.34.1
391
413
 
392
414
  - changed: `.agents/skills/kit-release/SKILL.md`
@@ -1,5 +1,5 @@
1
1
  {
2
- "kitVersion": "0.34.1",
2
+ "kitVersion": "0.34.3",
3
3
  "files": [
4
4
  {
5
5
  "path": ".agents/skills/ask-matt/SKILL.md",
@@ -137,7 +137,7 @@
137
137
  "ownerSkill": "diagnose",
138
138
  "surface": "codex",
139
139
  "installRole": "consumer",
140
- "sha256": "38ae64634933b42a6fea8b480c1962fc494a2dc08d7b07b0ab9a114eaeaa631e",
140
+ "sha256": "8a6b1036968146beff15a03e03c7b0f7d71ab65a666201ee8e1588c484f32ce6",
141
141
  "mode": 420,
142
142
  "origin": "kit"
143
143
  },
@@ -267,7 +267,7 @@
267
267
  "ownerSkill": "implement",
268
268
  "surface": "codex",
269
269
  "installRole": "consumer",
270
- "sha256": "5b55552c4a0d90fe15c9ee44abce4498faa03b73e332024e30ab69c2b7065770",
270
+ "sha256": "dff8c4bd4a1cfd376bc5fdd9d97641301d61055fd9155bde50c1e9634a6015d2",
271
271
  "mode": 420,
272
272
  "origin": "kit"
273
273
  },
@@ -367,7 +367,7 @@
367
367
  "ownerSkill": "kit-release",
368
368
  "surface": "codex",
369
369
  "installRole": "maintainer",
370
- "sha256": "168879858199f19f124e8bc3722f430f65b44b1fb6409d9c2112e6ec02d9b561",
370
+ "sha256": "7e08d5115d34f36da08491fa3bb8d5f4ca45e2908560e8f8c924cf3fd619d7f9",
371
371
  "mode": 420,
372
372
  "origin": "kit"
373
373
  },
@@ -447,7 +447,7 @@
447
447
  "ownerSkill": "orchestrate-wave",
448
448
  "surface": "codex",
449
449
  "installRole": "consumer",
450
- "sha256": "3a281805aee83a771675f8f80b9bd9dee3c1b2f2b2ca58001b37d8565a7d9d7d",
450
+ "sha256": "4c40d95e773649ba87e5010740180b66682a18cdf98ba7d8b7907397ea212ed0",
451
451
  "mode": 420,
452
452
  "origin": "kit"
453
453
  },
@@ -627,7 +627,7 @@
627
627
  "ownerSkill": "setup-workflow",
628
628
  "surface": "codex",
629
629
  "installRole": "consumer",
630
- "sha256": "5f5ceed4ec89d8eae8f1199d08b4948e9627eef7ce128e1858b208511c726e52",
630
+ "sha256": "6c896543b1a1a50ca3f591d63e364a3f0d624b8ae02ef93183e3665a27248d3c",
631
631
  "mode": 420,
632
632
  "origin": "kit"
633
633
  },
@@ -637,7 +637,7 @@
637
637
  "ownerSkill": "setup-workflow",
638
638
  "surface": "codex",
639
639
  "installRole": "consumer",
640
- "sha256": "7a72f72bc4c42401e6bfefb1253d87a2970b633d5f9e9bf975c5d486813cacd2",
640
+ "sha256": "be08d8e86f4573049c9cd809cde9417a334d364f3e823548bcd54136b1da4b01",
641
641
  "mode": 420,
642
642
  "origin": "kit"
643
643
  },
@@ -647,7 +647,7 @@
647
647
  "ownerSkill": "setup-workflow",
648
648
  "surface": "codex",
649
649
  "installRole": "consumer",
650
- "sha256": "e0dd9835e3658909132058e9a5fdd851e972220bbadaf78a57e3c6220d470922",
650
+ "sha256": "b4bf369b9c7791b9b9c76438aaa4cd9a07a0860fcc6bb9b52eebe12c6427ccbb",
651
651
  "mode": 420,
652
652
  "origin": "kit"
653
653
  },
@@ -1359,7 +1359,7 @@
1359
1359
  "ownerSkill": "diagnose",
1360
1360
  "surface": "claude",
1361
1361
  "installRole": "consumer",
1362
- "sha256": "2cdcb0ff5699dfeda8f8f9f487a8290d97d44176ddb6942638f4a9a32ecbaaad",
1362
+ "sha256": "44edd6a5af6429fd711ddbb3a4123b51a9c497573c147c2fc7b874e6e2a1336c",
1363
1363
  "mode": 420,
1364
1364
  "origin": "kit"
1365
1365
  },
@@ -1579,7 +1579,7 @@
1579
1579
  "ownerSkill": "implement",
1580
1580
  "surface": "claude",
1581
1581
  "installRole": "consumer",
1582
- "sha256": "5b55552c4a0d90fe15c9ee44abce4498faa03b73e332024e30ab69c2b7065770",
1582
+ "sha256": "dff8c4bd4a1cfd376bc5fdd9d97641301d61055fd9155bde50c1e9634a6015d2",
1583
1583
  "mode": 420,
1584
1584
  "origin": "kit"
1585
1585
  },
@@ -1679,7 +1679,7 @@
1679
1679
  "ownerSkill": "kit-release",
1680
1680
  "surface": "claude",
1681
1681
  "installRole": "maintainer",
1682
- "sha256": "168879858199f19f124e8bc3722f430f65b44b1fb6409d9c2112e6ec02d9b561",
1682
+ "sha256": "7e08d5115d34f36da08491fa3bb8d5f4ca45e2908560e8f8c924cf3fd619d7f9",
1683
1683
  "mode": 420,
1684
1684
  "origin": "kit"
1685
1685
  },
@@ -1759,7 +1759,7 @@
1759
1759
  "ownerSkill": "orchestrate-wave",
1760
1760
  "surface": "claude",
1761
1761
  "installRole": "consumer",
1762
- "sha256": "fb244997866b4ad01db7c7de8a22467d4c8399f030d236a517b6b9cc958d9177",
1762
+ "sha256": "24c90d6048f04df29c0031cd6b643ab433536c8a176dd4983111c6be6e0fcb19",
1763
1763
  "mode": 420,
1764
1764
  "origin": "kit"
1765
1765
  },
@@ -1959,7 +1959,7 @@
1959
1959
  "ownerSkill": "setup-workflow",
1960
1960
  "surface": "claude",
1961
1961
  "installRole": "consumer",
1962
- "sha256": "5f5ceed4ec89d8eae8f1199d08b4948e9627eef7ce128e1858b208511c726e52",
1962
+ "sha256": "6c896543b1a1a50ca3f591d63e364a3f0d624b8ae02ef93183e3665a27248d3c",
1963
1963
  "mode": 420,
1964
1964
  "origin": "kit"
1965
1965
  },
@@ -1969,7 +1969,7 @@
1969
1969
  "ownerSkill": "setup-workflow",
1970
1970
  "surface": "claude",
1971
1971
  "installRole": "consumer",
1972
- "sha256": "7a72f72bc4c42401e6bfefb1253d87a2970b633d5f9e9bf975c5d486813cacd2",
1972
+ "sha256": "be08d8e86f4573049c9cd809cde9417a334d364f3e823548bcd54136b1da4b01",
1973
1973
  "mode": 420,
1974
1974
  "origin": "kit"
1975
1975
  },
@@ -1979,7 +1979,7 @@
1979
1979
  "ownerSkill": "setup-workflow",
1980
1980
  "surface": "claude",
1981
1981
  "installRole": "consumer",
1982
- "sha256": "e0dd9835e3658909132058e9a5fdd851e972220bbadaf78a57e3c6220d470922",
1982
+ "sha256": "b4bf369b9c7791b9b9c76438aaa4cd9a07a0860fcc6bb9b52eebe12c6427ccbb",
1983
1983
  "mode": 420,
1984
1984
  "origin": "kit"
1985
1985
  },
@@ -2,6 +2,10 @@
2
2
 
3
3
  Status: accepted (2026-07-22, issue #204)
4
4
 
5
+ Clarified 2026-07-25 by issue #239: an explicit AFK end-to-end mandate may
6
+ authorize deterministic reversible release preparation, but never the
7
+ publication tag itself.
8
+
5
9
  The release workflow currently publishes whenever a merge to `main` changes
6
10
  `package.json`. That keeps the canonical branch and npm close together, but it
7
11
  also makes an ordinary merge an irreversible public action whose consequence is
@@ -36,7 +40,12 @@ We decided that integration and publication are separate transitions:
36
40
  ## Consequences
37
41
 
38
42
  - A prepared version may temporarily exist on `main` in an `awaiting-tag` state.
39
- - An AFK wave may create its own tag after all pre-authorized landing gates pass;
40
- the next program wave still requires separate authorization.
43
+ - An explicit AFK Wave/Program mandate may cover the deterministic reversible
44
+ metadata preparation and landing that it names. It never by itself authorizes
45
+ creating or pushing the publication tag; that irreversible action requires a
46
+ separate explicit confirmation.
47
+ - A single-Wave mandate does not authorize the next Program Wave. A later
48
+ explicit whole-Program mandate does authorize all planned Waves and remains in
49
+ force across their boundaries.
41
50
  - Reruns reconcile the same tag and never infer that a red workflow means
42
51
  nothing was published.
@@ -10,3 +10,14 @@ Issues and PRDs for this repo live in `iKon85/agent-workflow-kit` as GitHub issu
10
10
  - Route board writes, status/wave/cluster field edits, dependencies, and sub-issue links through `scripts/board-sync.py`, not bare `gh project` commands.
11
11
  - Board state is authoritative for workflow status; GitHub issues remain the durable content source.
12
12
 
13
+ ## Pickup claim
14
+
15
+ Used by `/implement`, `/diagnose`, and `/orchestrate-wave` before the first
16
+ edit. Board status `In Progress` is the human-facing signal; the claim below is
17
+ the machine-readable one, and it is the only one that names the branch and
18
+ worktree a colliding session would have to find.
19
+
20
+ - **Check**: `gh issue view <n> --json assignees,comments --jq '{assignees: [.assignees[].login], claims: [.comments[].body | select(contains("<!-- agent-claim:"))]}'` — a foreign assignee or a marker this session did not plant is a foreign claim: stop, report the claimed branch/worktree, and leave it alone.
21
+ - **Claim**: `gh issue edit <n> --add-assignee @me`, then `gh issue comment <n> --body '<!-- agent-claim: branch=<branch>; worktree=<absolute-path>; date=<YYYY-MM-DD> -->'`. Board status stays a `scripts/board-sync.py` write, never a bare `gh project` call.
22
+ - **Release**: the slice PR (`closes #<n>` / `Part of #<anchor>`) supersedes the claim, so `/wrapup` needs no extra step. On abandon: `gh issue edit <n> --remove-assignee @me` plus a `claim released` comment.
23
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikon85/agent-workflow-kit",
3
- "version": "0.34.1",
3
+ "version": "0.34.3",
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": {
@@ -184,6 +184,18 @@ test('both release skill surfaces name only the owned scoped npm package', async
184
184
  }
185
185
  });
186
186
 
187
+ test('an explicit AFK scope can authorize reversible preparation but never publication', async () => {
188
+ const claude = await readFile(join(REPO, '.claude/skills/kit-release/SKILL.md'), 'utf8');
189
+ const codex = await readFile(join(REPO, '.agents/skills/kit-release/SKILL.md'), 'utf8');
190
+ assert.equal(codex, claude);
191
+ for (const body of [claude, codex]) {
192
+ assert.match(body, /explicit AFK end-to-end mandate/i);
193
+ assert.match(body, /deterministic recommendation/i);
194
+ assert.match(body, /reversible metadata preparation/i);
195
+ assert.match(body, /annotated `v<version>` tag[\s\S]*separate explicit confirmation/i);
196
+ }
197
+ });
198
+
187
199
  test('maintainer docs and the accepted ADR agree that merge integrates and an annotated tag publishes', async () => {
188
200
  const paths = [
189
201
  'CLAUDE.md',
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env python3
2
+ """Issue-claim-on-pickup contract (#231).
3
+
4
+ A skill that ACCEPTS a tracked issue for building must leave a claim on the
5
+ issue itself before it builds — the local worktree/branch/PR guard only sees
6
+ the same machine, and only until someone pushes. The claim is what a second
7
+ session, a second machine, or a cloud agent can actually see.
8
+
9
+ Enforced here:
10
+
11
+ - `implement` and `diagnose` carry one byte-identical claim block on both
12
+ surfaces, and it sits before their first build instruction.
13
+ - The block covers all three legs: check for a foreign claim (STOP), plant a
14
+ claim naming branch + worktree, release it.
15
+ - `orchestrate-wave` claims each slice issue at builder-launch time and
16
+ releases only its own slice claims.
17
+ - Every tracker seed and this repo's own tracker layer document the concrete
18
+ claim / check / release operations, so the tracker-neutral skill prose
19
+ resolves to real commands.
20
+
21
+ Run: python3 scripts/test_issue_claim_contract.py
22
+ """
23
+ from __future__ import annotations
24
+
25
+ import unittest
26
+ from pathlib import Path
27
+
28
+ ROOT = Path(__file__).resolve().parents[1]
29
+ SURFACES = (".claude/skills", ".agents/skills")
30
+ PICKUP_SKILLS = ("implement", "diagnose")
31
+ START = "<!-- issue-claim:start -->"
32
+ END = "<!-- issue-claim:end -->"
33
+ MARKER = "<!-- agent-claim:"
34
+ TRACKER_DOC = "docs/agents/issue-tracker.md"
35
+ TRACKER_SEEDS = (
36
+ "setup-workflow/issue-tracker-github.md",
37
+ "setup-workflow/issue-tracker-gitlab.md",
38
+ "setup-workflow/issue-tracker-local.md",
39
+ )
40
+ # The first instruction that already assumes the build has started.
41
+ FIRST_BUILD_ANCHOR = {
42
+ "implement": "Use /tdd where possible",
43
+ "diagnose": "## Phase 1 — Build a feedback loop",
44
+ }
45
+
46
+
47
+ def skill_body(surface: str, skill: str) -> str:
48
+ return (ROOT / surface / skill / "SKILL.md").read_text(encoding="utf-8")
49
+
50
+
51
+ def claim_block(body: str) -> str:
52
+ return body.split(START, 1)[1].split(END, 1)[0]
53
+
54
+
55
+ def section(body: str, heading: str) -> str:
56
+ """Return one second-level Markdown section, excluding the next one."""
57
+ start = body.index(heading) + len(heading)
58
+ end = body.find("\n## ", start)
59
+ return body[start:] if end == -1 else body[start:end]
60
+
61
+
62
+ class ClaimBlockContract(unittest.TestCase):
63
+ def test_pickup_skills_carry_exactly_one_claim_block_on_both_surfaces(self):
64
+ for skill in PICKUP_SKILLS:
65
+ for surface in SURFACES:
66
+ with self.subTest(skill=skill, surface=surface):
67
+ body = skill_body(surface, skill)
68
+ self.assertEqual(body.count(START), 1)
69
+ self.assertEqual(body.count(END), 1)
70
+ self.assertLess(body.index(START), body.index(END))
71
+
72
+ def test_claim_block_is_byte_identical_across_skills_and_surfaces(self):
73
+ blocks = {
74
+ (skill, surface): claim_block(skill_body(surface, skill))
75
+ for skill in PICKUP_SKILLS
76
+ for surface in SURFACES
77
+ }
78
+ first = blocks[(PICKUP_SKILLS[0], SURFACES[0])]
79
+ for key, block in blocks.items():
80
+ with self.subTest(key=key):
81
+ self.assertEqual(block, first)
82
+
83
+ def test_claim_block_covers_check_plant_and_release(self):
84
+ block = claim_block(skill_body(SURFACES[0], PICKUP_SKILLS[0]))
85
+ # Check leg — a foreign claim stops the pickup, and is never removed.
86
+ self.assertIn("foreign claim", block)
87
+ self.assertIn("STOP", block)
88
+ self.assertIn("never delete a foreign claim", block)
89
+ # Plant leg — the marker must carry branch AND worktree, or a colliding
90
+ # session cannot find the work in progress.
91
+ self.assertIn(MARKER, block)
92
+ self.assertIn("branch=", block)
93
+ self.assertIn("worktree=", block)
94
+ # Release leg — the PR supersedes it; an abandoned claim is removed.
95
+ self.assertIn("supersedes", block)
96
+ self.assertIn("abandon", block)
97
+
98
+ def test_claim_block_resolves_through_the_tracker_layer_with_a_fallback(self):
99
+ block = claim_block(skill_body(SURFACES[0], PICKUP_SKILLS[0]))
100
+ self.assertIn(TRACKER_DOC, block)
101
+ self.assertIn("fall back", block)
102
+
103
+ def test_claim_is_planted_before_the_first_build_instruction(self):
104
+ for skill in PICKUP_SKILLS:
105
+ for surface in SURFACES:
106
+ with self.subTest(skill=skill, surface=surface):
107
+ body = skill_body(surface, skill)
108
+ self.assertLess(
109
+ body.index(END), body.index(FIRST_BUILD_ANCHOR[skill])
110
+ )
111
+
112
+
113
+ class OrchestrateWaveSliceClaimContract(unittest.TestCase):
114
+ def test_dispatch_claims_each_slice_issue_at_builder_launch(self):
115
+ for surface in SURFACES:
116
+ with self.subTest(surface=surface):
117
+ dispatch = section(
118
+ skill_body(surface, "orchestrate-wave"),
119
+ "## Phase 2 — Dispatch one wave in parallel",
120
+ )
121
+ self.assertIn(MARKER, dispatch)
122
+ self.assertIn("slice issue", dispatch)
123
+ self.assertIn("branch=", dispatch)
124
+ self.assertIn("worktree=", dispatch)
125
+
126
+ def test_wave_claim_is_not_treated_as_a_slice_claim(self):
127
+ for surface in SURFACES:
128
+ with self.subTest(surface=surface):
129
+ dispatch = section(
130
+ skill_body(surface, "orchestrate-wave"),
131
+ "## Phase 2 — Dispatch one wave in parallel",
132
+ )
133
+ self.assertIn("wave claim", dispatch)
134
+
135
+ def test_cleanup_releases_only_this_runs_slice_claims(self):
136
+ for surface in SURFACES:
137
+ with self.subTest(surface=surface):
138
+ cleanup = section(
139
+ skill_body(surface, "orchestrate-wave"),
140
+ "## Phase 6 — Cleanup + close",
141
+ )
142
+ self.assertIn("slice claim", cleanup)
143
+ self.assertIn("this run", cleanup)
144
+
145
+
146
+ class TrackerLayerClaimOperations(unittest.TestCase):
147
+ def documents(self) -> dict[str, str]:
148
+ docs = {TRACKER_DOC: (ROOT / TRACKER_DOC).read_text(encoding="utf-8")}
149
+ for surface in SURFACES:
150
+ for seed in TRACKER_SEEDS:
151
+ path = ROOT / surface / seed
152
+ docs[str(path.relative_to(ROOT))] = path.read_text(encoding="utf-8")
153
+ return docs
154
+
155
+ def test_every_tracker_layer_defines_claim_check_and_release(self):
156
+ for name, body in self.documents().items():
157
+ with self.subTest(document=name):
158
+ self.assertIn("## Pickup claim", body)
159
+ claim = section(body, "## Pickup claim")
160
+ self.assertIn(MARKER, claim)
161
+ self.assertIn("branch=", claim)
162
+ self.assertIn("worktree=", claim)
163
+ # a read side (detect a foreign claim) and a release side
164
+ self.assertIn("Check", claim)
165
+ self.assertIn("Release", claim)
166
+
167
+
168
+ if __name__ == "__main__":
169
+ unittest.main()
@@ -95,6 +95,14 @@ BEHAVIORAL_PARITY = {
95
95
  "Program-PRD",
96
96
  "program sync",
97
97
  ),
98
+ "program authorization continuity": (
99
+ "explicit whole-Program mandate",
100
+ "all planned Waves",
101
+ "Do not re-ask at each Wave boundary",
102
+ "gated action",
103
+ "meaningful authorized safe work",
104
+ "must not be marked `blocked`",
105
+ ),
98
106
  }
99
107
 
100
108
 
@@ -175,7 +183,11 @@ class OrchestrateWaveContract(unittest.TestCase):
175
183
  ):
176
184
  self.assertIn(fragment, self.skill)
177
185
 
178
- self.assertLessEqual(len(self.skill.splitlines()), 345)
186
+ # Skeleton-size ratchet: detail belongs in references/, not here. 345 ->
187
+ # 360 carried the sticky Program-authorization rule; 360 -> 368 adds the
188
+ # per-slice pickup claim (#231). Both are phase-level responsibilities
189
+ # the reference files cannot carry — they gate dispatch itself.
190
+ self.assertLessEqual(len(self.skill.splitlines()), 368)
179
191
 
180
192
  def test_registry_ownership_distinguishes_safe_from_eager_registries(self):
181
193
  prose = " ".join(self.skill.split())