From 0e31d738f8d018c35de56abf66ea521fd20e63c0 Mon Sep 17 00:00:00 2001 From: Michael Johnston Date: Sat, 15 Aug 2026 14:16:34 -0700 Subject: [PATCH 1/4] Let separate slices of one issue run in parallel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A claim was treated as ownership of the whole work item: "add no second claim", "one item carries one active intent", any non-completed claim is active, and an issue's claim completes only when the issue closes. Together those rules turned an issue into a lock. Issue #97 hit it — an active remove-tabs-show-review-ids slice blocked a separate slice of the same issue from starting — while the user normally runs three to five threads and wants different pieces of one roadmap issue to progress at once. Make a claim own one bounded slice instead. Several claims may be active on an issue when their slices are separate; a pull request is one slice, so it normally carries one. Stage 3 now compares slices from the summary, touch points, and branch already in the claim, and asks only when the slices are the same or plausibly overlap. The post-publication read treats a concurrent claim as a collision on overlap rather than on count. A slice's claim completes when that slice lands, while the issue and its Project status stay In Progress until the issue itself is done. Babysit reads the claim whose slice its pull request delivers, and on merge completes only that claim, leaving sibling slices untouched. No new coordination machinery: no owner tokens, elections, timestamp winners, self-demotion, snapshot loops, locks, retries, or extra claim fields. The eval test that encoded the serialized rule now covers both sides of the new boundary, and its assertions no longer pin exact line wrapping. --- .agents/skills/babysit/SKILL.md | 6 ++- .agents/skills/babysit/references/merge.md | 22 +++++---- .agents/skills/start-issue/SKILL.md | 56 ++++++++++++++-------- test/start-issue-evals.test.ts | 49 ++++++++++++++++++- 4 files changed, 98 insertions(+), 35 deletions(-) diff --git a/.agents/skills/babysit/SKILL.md b/.agents/skills/babysit/SKILL.md index 184d97cf..33ab3600 100644 --- a/.agents/skills/babysit/SKILL.md +++ b/.agents/skills/babysit/SKILL.md @@ -22,8 +22,10 @@ merge` merges the exact green head with the repository's enabled method and verifies the merge. Read the slice boundary before the first fix: the addressed issue's acceptance -criteria and the work-intent comment carry the observable evidence that ends -this slice and the actors, scenarios, variants, and extensions it leaves out. +criteria and this pull request's work-intent claim carry the observable evidence +that ends this slice and the actors, scenarios, variants, and extensions it +leaves out. An issue can carry several claims for slices running in parallel, so +read the one whose slice this pull request delivers. When neither records one, state the boundary you will use in the first response and proceed. Ask before the first triage when the ambiguity would change how a finding is sorted. The boundary is the authority for triage. diff --git a/.agents/skills/babysit/references/merge.md b/.agents/skills/babysit/references/merge.md index 70d9fbe7..11485cc8 100644 --- a/.agents/skills/babysit/references/merge.md +++ b/.agents/skills/babysit/references/merge.md @@ -13,15 +13,17 @@ URL. Run `done` so every matching local review reaches Done; zero matching reviews is success. Report a lookup or Markover failure without weakening the verified GitHub merge result. -When the merged pull request carries a work-intent claim, it completes with the -merge, so set its phase to `completed`. Then refresh the issue conversation for -comments added during the merge before recommending next steps or closing it, -and account for every remaining adjunct item. If work remains, prepare concrete -next steps for the report; the issue's own claim stays as it is. If the issue -is complete, perform the remaining state-only housekeeping, set its claim to -`completed` as well, verify the issue and its trackers reflect completion, and -prepare an archive-ready conclusion. +Complete the claim for the merged slice: the pull request's own claim when it +carries one, otherwise the claim on the addressed issue whose slice this pull +request delivered. Set that claim's phase to `completed` and leave every +sibling claim alone — other slices of the same issue may still be running. + +Then refresh the issue conversation for comments added during the merge before +recommending next steps or closing it, and account for every remaining adjunct +item. If work remains, prepare concrete next steps for the report. If the issue +is complete, perform the remaining state-only housekeeping, verify the issue and +its trackers reflect completion, and prepare an archive-ready conclusion. **Complete when:** the merge is verified, matching Markover reviews are Done, -every completed claim says so, and the issue and its trackers match the real -state. +the merged slice's claim says `completed` with its siblings untouched, and the +issue and its trackers match the real state. diff --git a/.agents/skills/start-issue/SKILL.md b/.agents/skills/start-issue/SKILL.md index 59204739..a72670f0 100644 --- a/.agents/skills/start-issue/SKILL.md +++ b/.agents/skills/start-issue/SKILL.md @@ -6,10 +6,12 @@ description: Use before implementation when the user asks to start or take over # Start Issue Starting work means making it visible before changing it: one **work item** on -GitHub, the **tracker** the user reads, and one **claim** other agents can see. -The interview then fixes the **slice boundary** — the evidence that ends this -slice and what it leaves out — so babysit and the tripwire have something -finite to compare against. Complete the stages in order. +GitHub, the **tracker** the user reads, and a **claim** other agents can see on +the slice you are taking. A claim owns one bounded slice rather than the whole +item, so separate slices of one issue can run at the same time. The interview +then fixes the **slice boundary** — the evidence that ends this slice and what +it leaves out — so babysit and the tripwire have something finite to compare +against. Complete the stages in order. Enter this workflow when the user asks to start, take over, implement untracked repository work, or record work. Diagnosing a failure, explaining behavior, @@ -94,11 +96,19 @@ plausible overlap has been assessed or raised with the user. ## 3. Claim it **When the target already carries an active claim** — any claim whose phase is -not `completed` — show it to the user and ask whether this run continues it, -takes it over, or belongs on a different item, before attaching or claiming -anything. Add no second claim. Edit another run's claim only after the user -says that run has stopped, and preserve its intent data when taking it over. -One item carries one active intent. +not `completed` — compare its slice with the one you are taking before +attaching or claiming anything, reading its summary, touch points, and branch: + +- Clearly separate slices: claim yours and continue. Another claim on the item + is not by itself a reason to ask. +- Clearly the same slice: show that claim and ask whether this run continues + it, takes it over, or belongs on a different item. One slice carries one + active claim. +- Plausible overlap the live evidence does not settle: show both slices and ask. + +Edit another run's claim only after the user says that run has stopped, and +preserve its intent data when taking it over. A pull request is one slice, so +it normally carries one claim. Attach the target to the tracker set if it is not already attached, and move each mapped Project to `In Progress`. An already-correct value is a no-op. @@ -118,8 +128,9 @@ An item belongs to many Projects but one milestone. Treat every other Project field, milestone property, and repository label as read-only; the claim carries the rest. -With no active claim on the target, post one claim comment and maintain it by -editing that exact comment ID: +With no active claim on your slice, post one claim comment and maintain it by +editing that exact comment ID rather than a sibling slice's claim on the same +item: ````markdown @@ -146,12 +157,14 @@ keep the phase truthful — `implementing` only after implementation is authorized. After posting, read the target's own claim comments once more — that comment -thread only, not the trackers. When more than one active claim is present, -pause, show the collision, and let the user resolve it before implementation. -Two runs pausing is a good outcome; do not invent a winner. +thread only, not the trackers. A claim that appeared while you were writing +collides only when its slice overlaps or may overlap yours; two disjoint +slices both continue. On a real overlap, pause, show both slices, and let the +user resolve it before implementation. Two runs pausing is a good outcome; +do not invent a winner. **Complete when:** the target is attached, mapped Projects show `In Progress`, -one truthful claim exists, and no unresolved collision remains. +this slice has one truthful claim, and no unresolved overlap remains. ## 4. Interview @@ -198,16 +211,17 @@ Projects, and the milestone aligned with the real state: `In Progress`. - `review`: the slice is with babysit or the user; record the handoff in the summary and leave mapped Projects `In Progress`. -- `completed`: the owned work is finished, and mapped Projects move to `Done`. - A direct pull request completes when it merges. An issue completes when the - issue closes, so a merged pull request that leaves issue work open keeps it - `In Progress`. +- `completed`: this slice's work is finished. A direct pull request's claim + completes when it merges; an issue slice's claim completes when that slice + lands, even while sibling slices continue. Mapped Projects move to `Done` + only when the item itself is done, so an issue with slices still running + stays `In Progress`. Keep the milestone attached throughout; its progress changes when the item closes. -Re-read the claim before resuming after an interruption, and before entering a -surface it does not declare. When implementation materially changes the +Re-read your claim before resuming after an interruption, and before entering +a surface it does not declare. When implementation materially changes the summary, touch points, dependencies, or branch, update the claim and reassess overlap for the newly added surface before working inside it. diff --git a/test/start-issue-evals.test.ts b/test/start-issue-evals.test.ts index aa9de686..a07e403c 100644 --- a/test/start-issue-evals.test.ts +++ b/test/start-issue-evals.test.ts @@ -160,7 +160,28 @@ test('branch-only guidance is progressively disclosed', () => { assert.doesNotMatch(markoverReference, /open ''/) }) -test('duplicate claims are detected and handed to the user without an election', () => { +test('a claim owns one slice, so disjoint slices of an issue run in parallel', () => { + assert.match( + skillSource, + /A claim owns one bounded slice rather than the whole/ + ) + assert.match( + skillSource, + /Clearly separate slices: claim yours and continue\./ + ) + assert.match( + skillSource, + /collides only when its slice overlaps or may overlap yours/ + ) + assert.match( + skillSource, + /even while sibling slices continue/ + ) + assert.doesNotMatch(skillSource, /Add no second claim|One item carries one active intent/) + assert.doesNotMatch(skillSource, /more than one active claim is present/) +}) + +test('an overlapping slice is detected and handed to the user without an election', () => { const existingClaim = skillSource.indexOf( '**When the target already carries an active claim**' ) @@ -170,11 +191,35 @@ test('duplicate claims are detected and handed to the user without an election', assert.match(skillSource, /any claim whose phase is\nnot `completed`/) assert.match( skillSource, - /read the target's own claim comments once more[\s\S]*pause, show the collision[\s\S]*do not invent a winner/ + /Clearly the same slice: show that claim and ask/ + ) + assert.match( + skillSource, + /Plausible overlap the live evidence does not settle: show both slices and ask\./ + ) + assert.match( + skillSource, + /read the target's own claim comments once more[\s\S]*pause, show both slices[\s\S]*do not invent a winner/ ) assert.doesNotMatch(skillSource, /owner token|earliest `created_at`|self-demot/i) }) +test('merging one slice leaves its sibling claims alone', () => { + const merge = fs.readFileSync( + path.join(root, '.agents/skills/babysit/references/merge.md'), + 'utf8' + ) + assert.match( + merge, + /Complete the claim for the merged slice[\s\S]*sibling claim alone/ + ) + assert.doesNotMatch(merge, /`completed` as well/) + assert.match( + fs.readFileSync(path.join(root, '.agents/skills/babysit/SKILL.md'), 'utf8'), + /An issue can carry several claims for slices running in parallel/ + ) +}) + test('root guidance owns the terminal-friendly Markover handoff', () => { assert.match(agentsSource, /^`open ''`$/m) assert.match(agentsSource, /best-effort Markdown link and raw review ID/) From 34f8c24591e3d75127ffd8bb473c90f0b0caff43 Mon Sep 17 00:00:00 2001 From: Michael Johnston Date: Sat, 15 Aug 2026 16:45:00 -0700 Subject: [PATCH 2/4] Compare complete slice boundaries --- .agents/skills/start-issue/SKILL.md | 3 ++- test/start-issue-evals.test.ts | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.agents/skills/start-issue/SKILL.md b/.agents/skills/start-issue/SKILL.md index a72670f0..7b971acc 100644 --- a/.agents/skills/start-issue/SKILL.md +++ b/.agents/skills/start-issue/SKILL.md @@ -97,7 +97,8 @@ plausible overlap has been assessed or raised with the user. **When the target already carries an active claim** — any claim whose phase is not `completed` — compare its slice with the one you are taking before -attaching or claiming anything, reading its summary, touch points, and branch: +attaching or claiming anything, reading its summary, touch points, `done-when`, +`excludes`, and branch: - Clearly separate slices: claim yours and continue. Another claim on the item is not by itself a reason to ask. diff --git a/test/start-issue-evals.test.ts b/test/start-issue-evals.test.ts index a07e403c..7aae8df8 100644 --- a/test/start-issue-evals.test.ts +++ b/test/start-issue-evals.test.ts @@ -169,6 +169,10 @@ test('a claim owns one slice, so disjoint slices of an issue run in parallel', ( skillSource, /Clearly separate slices: claim yours and continue\./ ) + assert.match( + skillSource, + /reading its summary, touch points, `done-when`,\n`excludes`, and branch/ + ) assert.match( skillSource, /collides only when its slice overlaps or may overlap yours/ From a67ddc78d4c7cdba74308e265ab9d4d7356e430c Mon Sep 17 00:00:00 2001 From: Michael Johnston Date: Sat, 15 Aug 2026 16:51:55 -0700 Subject: [PATCH 3/4] Check every active slice claim --- .agents/skills/start-issue/SKILL.md | 19 ++++++++++--------- test/start-issue-evals.test.ts | 15 +++++++++------ 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/.agents/skills/start-issue/SKILL.md b/.agents/skills/start-issue/SKILL.md index 7b971acc..a84e495a 100644 --- a/.agents/skills/start-issue/SKILL.md +++ b/.agents/skills/start-issue/SKILL.md @@ -95,17 +95,18 @@ plausible overlap has been assessed or raised with the user. ## 3. Claim it -**When the target already carries an active claim** — any claim whose phase is -not `completed` — compare its slice with the one you are taking before -attaching or claiming anything, reading its summary, touch points, `done-when`, +**When the target already carries active claims**, compare the slice you are +taking with every claim whose phase is not `completed` before attaching or +claiming anything, reading each claim's summary, touch points, `done-when`, `excludes`, and branch: -- Clearly separate slices: claim yours and continue. Another claim on the item - is not by itself a reason to ask. -- Clearly the same slice: show that claim and ask whether this run continues - it, takes it over, or belongs on a different item. One slice carries one - active claim. -- Plausible overlap the live evidence does not settle: show both slices and ask. +- Clearly separate from every active claim: claim yours and continue. Another + claim on the item is not by itself a reason to ask. +- Clearly the same as an active claim: show that claim and ask whether this run + continues it, takes it over, or belongs on a different item. One slice + carries one active claim. +- Plausible overlap with any active claim that the live evidence does not + settle: show both slices and ask. Edit another run's claim only after the user says that run has stopped, and preserve its intent data when taking it over. A pull request is one slice, so diff --git a/test/start-issue-evals.test.ts b/test/start-issue-evals.test.ts index 7aae8df8..3d32630c 100644 --- a/test/start-issue-evals.test.ts +++ b/test/start-issue-evals.test.ts @@ -167,11 +167,11 @@ test('a claim owns one slice, so disjoint slices of an issue run in parallel', ( ) assert.match( skillSource, - /Clearly separate slices: claim yours and continue\./ + /Clearly separate from every active claim: claim yours and continue\./ ) assert.match( skillSource, - /reading its summary, touch points, `done-when`,\n`excludes`, and branch/ + /reading each claim's summary, touch points, `done-when`,\n`excludes`, and branch/ ) assert.match( skillSource, @@ -187,19 +187,22 @@ test('a claim owns one slice, so disjoint slices of an issue run in parallel', ( test('an overlapping slice is detected and handed to the user without an election', () => { const existingClaim = skillSource.indexOf( - '**When the target already carries an active claim**' + '**When the target already carries active claims**' ) const firstTrackerWrite = skillSource.indexOf('Attach the target to the tracker set') assert.ok(existingClaim >= 0) assert.ok(firstTrackerWrite > existingClaim) - assert.match(skillSource, /any claim whose phase is\nnot `completed`/) assert.match( skillSource, - /Clearly the same slice: show that claim and ask/ + /every claim whose phase is not `completed`/ ) assert.match( skillSource, - /Plausible overlap the live evidence does not settle: show both slices and ask\./ + /Clearly the same as an active claim: show that claim and ask/ + ) + assert.match( + skillSource, + /Plausible overlap with any active claim that the live evidence does not\n settle: show both slices and ask\./ ) assert.match( skillSource, From e676c7c6cebd5864f6768c53324100149d53954f Mon Sep 17 00:00:00 2001 From: Michael Johnston Date: Sat, 15 Aug 2026 18:44:34 -0700 Subject: [PATCH 4/4] Fix slice-claim assertion lint --- test/start-issue-evals.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/start-issue-evals.test.ts b/test/start-issue-evals.test.ts index 3d32630c..97342fd5 100644 --- a/test/start-issue-evals.test.ts +++ b/test/start-issue-evals.test.ts @@ -202,7 +202,7 @@ test('an overlapping slice is detected and handed to the user without an electio ) assert.match( skillSource, - /Plausible overlap with any active claim that the live evidence does not\n settle: show both slices and ask\./ + /Plausible overlap with any active claim that the live evidence does not\n {2}settle: show both slices and ask\./ ) assert.match( skillSource,