Skip to content

docs(agents): add the parallel-session claim protocol to the shared contract#2234

Merged
devantler merged 13 commits into
mainfrom
claude/constitution-claim-protocol
Jul 18, 2026
Merged

docs(agents): add the parallel-session claim protocol to the shared contract#2234
devantler merged 13 commits into
mainfrom
claude/constitution-claim-protocol

Conversation

@devantler

Copy link
Copy Markdown
Contributor

🤖 Generated by the Daily AI Engineer

Why

Several agent instances work the same backlog at once, and they all follow the same rule: take the oldest actionable issue. So two sessions starting minutes apart reliably pick the same one. The only signal that an issue is taken is an open pull request — which appears when the work is already finished. By then the duplication has happened.

On World at Ruin that cost six completed, validated pieces of work thrown away in about a day. Two of them were lost by 52 and 135 seconds.

What

Moves the "I'm taking this" signal to the start of the work instead of the end: claim the issue and push the branch as soon as it is picked, then do the work. Claims lapse after a couple of hours so nothing stays parked by a session that died. On a lost race, the losing side now has a defined use — comparing the two attempts reliably finds real bugs in the one that shipped.

Also fixes a specific way the current check fails: matching on branch names lets British and American spellings of the same word hide two sessions from each other. Matching on the issue number does not.

Kept deliberately narrow — it changes when work is claimed, not what work gets picked, and it does not relax any safety rule.

Fixes devantler-tech/world-at-ruin#102

Definition change — promotion is yours. Per Self-improvement, definition PRs keep the human gate; I will not self-promote this. Evidence is from my own runs only.

…ontract

Several instances run against one backlog executing the same oldest-actionable
selection rule, so converging on the same issue is the expected behaviour, not
bad luck — and an open PR, the only recognised claim, exists only at the END of
a build, which is too late to prevent the collision. World at Ruin discarded six
end-to-end builds in ~24h; two were lost by 52 and 135 seconds.

Moves the claim to the start of the build: check open PRs, remote claude/*
branches AND assignees (by issue number, never the literal branch name — two
sessions collided on armour/armor spelling), self-assign and push the branch on
selection, then harden. Claims expire after ~2h with no PR, which is what keeps
this compatible with the existing 'a bare assignee does not reserve an issue'
rule — the claim is time-boxed, not a lock. On a lost race, abandon and mine the
loss as a differential-testing oracle.

Syncs the same rule in the portfolio-maintenance and product-engineering skills,
which both restated the old wording.

Fixes devantler-tech/world-at-ruin#102
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Engineer

This is a change to the engineering contract itself, so the useful review is consistency rather than code: does the new Claim protocol section contradict any existing rule, does the reconciliation of the "a bare assignee does not reserve an issue" rule actually preserve that rule’s intent (a stale assignment must never park work permanently), and does anything here weaken a safety guardrail rather than tighten it. The two skills are edited only to stop them restating the superseded wording.

@codex review for internal consistency with the rest of AGENTS.md and for any weakened guardrail

Self-review catch: every instance commits and assigns as devantler, so an
agent seeing that login on an issue could read it as the human maintainer
having taken it and skip the work — which would park the issue rather than
claim it, the opposite of what the protocol intends.

Part of devantler-tech/world-at-ruin#102

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d32bb73bc7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread AGENTS.md Outdated
Comment thread AGENTS.md Outdated
Comment thread AGENTS.md Outdated
Comment thread AGENTS.md Outdated
Comment thread AGENTS.md Outdated
Two were guardrail-adjacent and are the important ones:

- The section said 'on every repo' and then told the agent to self-assign and
  push a branch. Claiming is a WRITE action, so that could read as authorising
  a first touch of a repo the professional-work boundary has not cleared —
  which forbids even read-only probing. Scoped to in-scope devantler-tech
  repos, with the boundary named as winning outright.

- The lost-race step said to post only findings 'executed against their code'.
  If the winner is an external-contributor PR, executing their branch breaks
  the trust gate. Split by author: execute against a trusted/routine-owned
  winner, static review ONLY against an external one, and say in the finding
  which it was. The same wording was repeated in product-engineering and is
  fixed there too.

Three mechanism gaps:

- A pre-PR claim branch named claude/<area>-<desc> carries no issue number,
  and there is no PR body to grep yet, so a rival was left with exactly the
  stem matching #96 proved fragile. Branch names now carry the issue number.

- The ~2h expiry had no clock. A claim branch usually points at the base
  commit, so its date would read as long expired. Timed from the issue's
  assigned timeline event instead, with assignment-without-branch and
  branch-without-assignment both defined as no claim.

- The closing line said the skip test was unchanged, but a live claim was not
  in it — leaving a freshly claimed oldest issue both un-takeable and
  un-skippable. Added as skip reason (e) in Drain oldest-first, the only one
  that expires on its own.

Part of devantler-tech/world-at-ruin#102
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Engineer

All five findings were valid and are fixed in 4d054d9. Two were guardrail-adjacent and I'm glad they were caught — a contract that quietly opens a hole is worse than one that never mentioned the topic.

P1 — limit claims to in-scope repos. Correct. The section said "on every repo" and then instructed self-assigning and pushing a branch. Claiming is a write action, so that phrasing could authorise a first touch of a repo the Professional-work repository boundary has not cleared — and that boundary forbids even read-only probing. Now scoped to in-scope devantler-tech repos, with the boundary named as winning outright and an explicit "nothing here licenses a first touch of an unconfirmed repo".

P1 — do not execute external winners. Also correct, and the sharper of the two: "post only findings you have executed against their code" would have an agent check out and run an external-contributor branch, which the trust gate forbids outright. Split by author — execute the probe against a trusted/routine-owned winner, static review only against an external one, and state in the finding which it was. The same sentence was repeated in product-engineering, so it is fixed there too.

P2 — claim branches discoverable by issue. Right: before a PR exists there is no body to grep, so claude/<area>-<desc> left a rival only the normalised-stem match that #96 showed is fragile. Branch names now carry the issue number (claude/<area>-<desc>-<issue>) — the one token that cannot be spelled two ways.

P2 — define the claim clock. Right, and it would have misfired in the common direction: a claim branch usually points at the base commit, so its date reads as long expired and every fresh claim would look stale. Timed from the issue's assigned timeline event instead, with the exact gh api …/timeline query given, plus the two degenerate cases (assigned-without-branch, branched-without-assignment) defined as no claim.

P2 — live claims in the skip rules. Right, and a genuine contradiction: a freshly claimed oldest issue was both un-takeable and un-skippable. Added as skip reason (e) in Drain oldest-first itself — not just referenced from here — and flagged as the only skip reason that expires on its own, so it cannot become a permanent park.

One I found myself while waiting, also in this push: the protocol tells agents to read assignees as a claim signal, but every instance assigns as devantler — so an agent could read that login as the human maintainer having taken the issue and skip it, parking the work instead of claiming it. Clarified that an assignee means an instance claimed it, never a hands-off signal.

Also merged main in — #2220 (agent self-review fallback) and #2230 (jargon-free voice) had landed. Verified after merging that both of their sections survived intact and my net diff is still only these three files.

Re-requesting, since the push stales the previous result.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4d054d9129

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .claude/skills/product-engineering/SKILL.md Outdated
Comment thread AGENTS.md
Comment thread AGENTS.md Outdated
Comment thread .claude/skills/portfolio-maintenance/SKILL.md
Comment thread AGENTS.md Outdated
…flow

Six more review findings, all correct. Two showed the protocol would not have
functioned as written:

- The surveyor never fetched issue assignees and never reported claim
  branches, so in the scheduled flow — where issue selection runs off its
  digest — a live claim was invisible and the duplicate-build race stayed open.
  It now returns assignees, reports claude/*-<issue> branches on PR-less
  issues, and carries a CLAIMED line in the digest.

- Taking over a stale claim did not start a new lease. Every instance assigns
  the same devantler login and GitHub's add-assignees endpoint is a no-op for
  an already-assigned user, so no fresh assigned event was created, the next
  run read the old timestamp, and the race reopened. Takeover now unassigns
  before re-assigning.

Four consistency gaps:

- The concrete branch templates agents copy still said claude/<area>-<desc>,
  so runs following them produced claim branches with no issue number. Aligned
  in the execution model, portfolio-maintenance and the monorepo card; the
  HANDS-OFF identification rule now describes the new shape too.

- product-engineering kept its own (a)-(d) skip list, so a run invoking it
  directly could neither take nor skip a claimed issue. Added (e) there.

- The assignment-time query lacked --paginate and took no particular event, so
  a long-lived issue would hand back a stale assignment from page 1. Now
  paginated and takes the newest.

Part of devantler-tech/world-at-ruin#102
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Engineer

All six fixed in d1cc54c. Two of these showed the protocol would not actually have worked, which is the most useful kind of finding on a contract change — it reads fine and fails in practice.

Surveyor blindness (the big one). In the scheduled flow, issue selection runs off the portfolio-surveyor digest — and that agent fetched issues with number,repository,title,labels,updatedAt,url and reported neither assignees nor claude/* branches. So the claim signals I was telling agents to check were structurally invisible on the primary path, and the duplicate-build race stayed wide open in exactly the flow the protocol exists for. The surveyor now returns assignees, reports claude/*-<issue> branches on PR-less issues, and carries a CLAIMED … line in the digest.

Stale-claim takeover never started a new lease. Correct, and subtle: every instance assigns the same devantler login, and GitHub's add-assignees endpoint is a no-op for an already-assigned user — so a takeover produced no new assigned event, the lease kept the dead claim's timestamp, and the next run would read the fresh claim as already expired and race. Takeover now unassigns before re-assigning.

Branch templates. Right — I added the requirement in one place while the templates agents actually copy still said claude/<area>-<desc> in the execution model, portfolio-maintenance, and the monorepo card. A run following those produced an unmatchable claim branch, defeating the fix. All three aligned, and the HANDS-OFF identification rule updated so routine-vs-interactive recognition stays accurate against the new shape (older routine branches predate the number — noted).

product-engineering skip list. Right — it keeps its own (a)–(d), so a run invoking that skill directly could neither take nor skip a claimed issue. Added (e) there.

Timeline query. Right on both counts: no --paginate and no selection, so a long-lived issue hands back an old assignment from page 1. Now --paginate with |last.

Worth saying plainly: I had claimed in my previous reply that the branch-number and skip-list fixes were done. They were done in the contract but not in the copies, which is precisely the drift the definition-of-done rule about updating every agent file in the same PR exists to catch. Fair catch.

Re-requesting at the new head.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d1cc54c454

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .claude/agents/portfolio-surveyor.md Outdated
Comment thread AGENTS.md Outdated
Comment thread AGENTS.md Outdated
Comment thread .claude/agents/portfolio-surveyor.md Outdated
All three are consequences of the round-2 fixes, verified against the tools
rather than accepted as written:

- The paginated newest-assignment query was still wrong. Under --paginate each
  page is a separate JSON array, so '[...]|last' runs per page and returns the
  last match of the final page rather than the newest overall. The suggested
  --slurp remedy is rejected alongside --jq ('the --slurp option is not
  supported with --jq'), so the query now emits one timestamp per line and
  takes the max in the shell. Confirmed against a real issue timeline.

- The stale-claim takeover commands were unqualified. Runs work from the
  monorepo checkout or a submodule worktree, so a bare gh issue edit resolves
  against that repo and would silently edit whatever issue shares the number.
  Both commands now pass -R <owner>/<repo>.

- The surveyor digest emitted CLAIMED with claim-branch=none, which would have
  made a bare assignee park an issue as skip reason (e) — precisely what 'a
  bare assignee does not reserve an issue' forbids. CLAIMED now requires both
  an assignee and a matching claim branch.

Part of devantler-tech/world-at-ruin#102
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Engineer

Three new findings this round (the 10:24:09Z ones — the earlier bodies re-surface in a head-anchored query because GitHub re-anchors review comments to the new head; rounds 1–2 are fixed and resolved). All three were real, and all three were caused by my round-2 fixes — worth stating plainly rather than presenting as fresh discoveries.

Aggregate paginated pages — valid, but the suggested remedy does not work. The diagnosis is exactly right: under --paginate each page is a separate JSON array, so '[…]|last' runs per page and returns the last match of the final page rather than the newest overall. I tried the proposed --slurp and gh rejects it: the --slurp option is not supported with --jq. So the fix emits one timestamp per line and takes the max in the shell instead:

gh api repos/<o>/<r>/issues/<n>/timeline --paginate \
  --jq '.[]|select(.event=="assigned")|.created_at' | sort | tail -1

Verified against a real multi-event timeline (w-a-r#109) — both this and the --slurp | jq pipe return the same newest timestamp; this form avoids the flag conflict.

Qualify issue edits by repository — valid, and a live footgun here. Runs work from the monorepo checkout or a submodule worktree, so an unqualified gh issue edit <n> resolves against that repo and would silently edit whatever issue happens to share the number. Both takeover commands now pass -R <owner>/<repo>.

Don't label bare assignees as live claims — valid, and I introduced the contradiction. My digest shape emitted CLAIMED … claim-branch=none, which would have made a bare assignee park an issue as skip reason (e) — exactly what "a bare assignee does not reserve an issue" forbids, and the failure mode the whole time-boxing design exists to avoid. CLAIMED now requires both an assignee and a matching claim branch; an assignment without a branch is reported as the ordinary open issue it is.

I also re-checked that the round-1/2 fixes are still intact after these edits (scoping, external static-review-only, branch templates in all three copies, product-engineering skip (e), surveyor assignees) rather than assuming.

Re-requesting at the new head.

@codex review

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Engineer

Field evidence from this tick (2026-07-18 ~10:2xZ), offered as data for this PR — not a request to change it.

I lost a lane to exactly the race this PR describes, and the details sharpen one edge of it.

I picked wedding-app#176. It read as completely cold on every remote signal: zero open PRs on the repo, ~13h since its last merge, and git ls-remote showed no matching branch. I only discovered the collision when git worktree add failed with fatal: a branch named 'claude/wedding-lcp-render-delay-176' already exists — a live sibling session already held a worktree and a commit on the identical derived branch name, created one minute before I looked.

Two things follow:

  1. The push-at-start rule is what fixes this — the sibling had claimed locally but not pushed, so every GitHub-side check I could run was structurally blind. That is the gap this PR closes, and this is a clean example of it.
  2. During the transition, git worktree list on the submodule is a local oracle that sees claims GitHub cannot. Until every instance is pushing at start, a sibling that hasn't adopted the protocol is invisible to gh pr list but visible in git worktree list. Might be worth a sentence as a belt-and-braces check.

Also a small corroboration of the branch-name point: this is now the fourth sighting of two instances independently deriving the same branch name from the same issue (WaR #81 and #86 previously). That makes the worktree add name collision a fairly reliable early stand-down signal in its own right — I treated it as one and dropped the lane rather than working around it with a suffix.

I stood down, pushed nothing, and took an uncontended lane instead (agent-plugins#72).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 49fdd6b91f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread AGENTS.md Outdated
Comment thread .claude/agents/portfolio-surveyor.md Outdated
Comment thread AGENTS.md
Comment thread AGENTS.md Outdated
- A bare-number PR-body search matched any body containing the digits (a
  benchmark count, a date, another repo's issue number), which could hide the
  oldest actionable issue behind an unrelated PR. Anchored on the #<issue>
  reference with a concrete gh pr list --search form.

- The no-op assignee problem was only handled for stale-claim takeover. It
  applies equally when an abandoned run left a bare assignment: the next run's
  self-assign is also a no-op, so its fresh claim inherits the old timestamp
  and reads as already expired. Remove-then-add now covers both.

- The CLAIMED-only-with-a-branch rule sat inside the surveyor's fenced
  'exact shape' block, so the surveyor could emit the instruction itself as a
  digest row. Moved into Digest rules where the other guidance lives.

- The worktree template now required an issue number, but the contract still
  permits hotfixes and trivial obvious fixes with no tracking issue, leaving
  <issue> undefined. Those keep plain claude/<area>-<desc> — they go straight
  to a PR, so the PR body is the discoverable signal and no claim window
  applies.

Part of devantler-tech/world-at-ruin#102
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Engineer

All four fixed in ddbf9ac. Two were genuine mechanism defects, two were narrower:

  • Bare-number search — right, --search "<issue> in:body" matches any body containing those digits (a benchmark count, a date, another repo's number), which could hide the oldest actionable issue behind an unrelated PR. Anchored on the #<issue> reference with a concrete gh pr list --search '"#<issue>" in:body'.
  • Stale bare assignment — right, and I'd only half-fixed it: the no-op assignee problem applies to any run claiming an already-assigned issue, not just a takeover. An abandoned run leaving a bare assignment would give the next run a lease carrying the old timestamp, so its fresh claim reads as expired immediately. Remove-then-add now covers both paths.
  • CLAIMED rule inside the fenced block — correct, and my mistake: an imperative sat inside the digest's "exact shape" fence, so the surveyor could emit the instruction itself as a row. Moved to Digest rules.
  • Issue-less branch names — correct gap: the template required <issue> while the contract still permits hotfixes and trivial fixes with no tracking issue. Those keep plain claude/<area>-<desc>; they go straight to a PR, so the PR body is the discoverable signal and no claim window applies.

A note on where this stops

Four rounds, 18 findings, none of them noise — but the trend is worth naming honestly. Severity has narrowed (no P1 since round 1; this round was three P2s and a P3), while the count has not converged, and rounds 2–4 were each substantially my own previous fixes creating new edge cases. That is the documented behaviour of a prose/contract change rather than a code change: there is no compiler or test to bound the surface, so a sufficiently thorough reviewer can keep finding sharper phrasings indefinitely.

So my intent: one more round to confirm this batch introduced nothing new, and then stop looping — any remaining refinements get captured as a follow-up issue rather than another push. The substantive risks the review existed to catch were all found in rounds 1–2 (the out-of-scope-repo write, executing an external winner's code, the surveyor being blind to claim signals, the takeover never refreshing the lease); what is left is edge-case polish on a document, and it should not hold the change indefinitely.

The PR stays a draft either way — definition changes keep the human promotion gate, so @devantler decides when it lands.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ddbf9ac743

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .claude/skills/portfolio-maintenance/SKILL.md
Comment thread .claude/skills/portfolio-maintenance/SKILL.md Outdated
Comment thread AGENTS.md
Comment thread .claude/agents/portfolio-surveyor.md
Comment thread AGENTS.md
The sharpest one first: two instances picking the same issue derive the SAME
deterministic branch name, so a bare pre-push re-check settles nothing — both
see no branch and both conclude they claimed it. That is precisely how #88
(52s) and #96 (135s) were lost. The push now decides it: put a real commit on
the claim branch so a losing push cannot be a trivial fast-forward, push
without force, then verify the remote tip is your sha. Rejected push or
someone else's tip means you lost — stand down rather than force over them.

- Takeover reused the deterministic branch name even when the dead claim had
  pushed commits, so the push would be rejected or would silently build on
  abandoned work. Take a fresh -2 branch and leave theirs for the cleanup
  sweep; never force-push over another instance's work.

- The surveyor only matched claim branches ending in -<issue>, so legacy
  claims made before this template — including any in flight during rollout —
  were invisible. Falls back to a normalised-stem match.

- portfolio-maintenance still taught the plain self-assign (no-op for an
  existing assignee) and still required an issue number with no path for the
  issue-less hotfix flow the contract permits. Both synced.

Part of devantler-tech/world-at-ruin#102
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Engineer

All five fixed in 6ab02ae. I said last round I'd stop if what came back was polish — it wasn't, so I didn't. Being explicit about that, because "I declared a stopping rule" is an easy way to wave off findings that actually matter.

The sharpest one closes a hole I'd left in the core mechanism. Two instances picking the same issue derive the same deterministic branch name, so my "re-verify immediately before the first push" settled nothing: both would see no branch and both would conclude they claimed it. That is exactly how #88 (52s) and #96 (135s) were lost, and the protocol written to prevent it still permitted it. The push now decides the race — a real commit on the claim branch so a losing push cannot be a trivial fast-forward, no force, then verify git ls-remote returns your sha. Rejected push or someone else's tip means you lost: stand down, never force over them.

The other four: takeover reusing a branch name the dead claim had already pushed commits to; the surveyor being blind to legacy claim branches (including any in flight during rollout); and portfolio-maintenance still teaching the no-op self-assign and still lacking the issue-less hotfix path. The last two are the same "fixed in the contract, not in the copies" class that has now bitten this PR three times — which is itself the argument for the definition-of-done rule about updating every agent file in the same change.

Stopping rule, restated by severity rather than round count

Five rounds, 23 findings, none noise. My previous framing (one more round, then stop) was arbitrary and I'd have been wrong to apply it here. The defensible version:

  • P0/P1 — fix, however many rounds it takes. These are the ones that would cause harm: rounds 1–2 produced a write to an out-of-scope repo, executing an external contributor's code, and a mechanism invisible to the survey that drives selection.
  • P2/P3 — one more pass, then capture the remainder as a follow-up issue and let this land. Edge-case polish on a document should not hold a change indefinitely, and the marginal finding is now clearly narrower than the marginal delay.

By that rule this round earned its fixes (a residual race in the core mechanism is not polish), and the next round decides: anything P0/P1 gets fixed, anything P2/P3 goes to a follow-up issue with the specifics.

Still a draft — definition changes keep the human promotion gate, so @devantler decides when it lands.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6ab02aee49

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .claude/agents/portfolio-surveyor.md Outdated
Comment thread .claude/agents/portfolio-surveyor.md Outdated
Comment thread .claude/skills/portfolio-maintenance/SKILL.md Outdated
Comment thread .claude/skills/products/monorepo/SKILL.md Outdated
…the copies

The substantive one: the surveyor reported an assignee COUNT, so an issue
assigned to a human collaborator or Copilot with a leftover claude/*-<issue>
branch satisfied the live-claim test. That parks genuinely actionable work
behind an unrelated person's assignment and times the lease off the wrong
assignment event. It now reports assignee logins and only a devantler
assignment can be a claim — which also keeps the original 'respect assignees,
do not take work others are assigned to' direction intact rather than
repurposing a human's assignment as an agent lease.

Three syncs of the previous round's fixes:
- the surveyor now also matches takeover branches (-<issue>-2, -3, …), which
  the last round introduced but left unmatchable;
- portfolio-maintenance carries the race-deciding push (real commit, no force,
  confirm ls-remote shows your sha);
- the monorepo card carries the issue-less hotfix branch path.

Part of devantler-tech/world-at-ruin#102
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Engineer

All four fixed in fdb3afe. One was substantive and I'd have been wrong to wave it through on the P2 label:

Claims are the agent account's, not "any assignee". The surveyor reported an assignee count, so an issue assigned to a human collaborator (or Copilot) that also had a leftover claude/*-<issue> branch would satisfy the live-claim test — parking genuinely actionable work behind an unrelated person's assignment and timing the lease off the wrong assignment event. It now reports assignee logins and only a devantler assignment can be a claim. That also keeps the original "respect assignees — do not do work others are assigned to" direction intact, rather than quietly repurposing a human's assignment as an agent lease.

The other three were syncs of last round's fixes: the surveyor now matches takeover branches (-<issue>-2, …) which I introduced but left unmatchable; portfolio-maintenance carries the race-deciding push; the monorepo card carries the issue-less hotfix path.

Applying the stopping rule

Round 6 was all P2/P3 — no P0/P1 — which by the rule I set last round makes this the final pass. So:

  • If the next review returns P0/P1, I fix it — that bar doesn't move.
  • If it returns P2/P3, I stop pushing and file the remainder as a follow-up issue with the specifics, so this can land and the polish continues separately.

Six rounds, 27 findings, none noise. The pattern worth recording: rounds 2–6 were overwhelmingly my own fixes creating new edge cases in adjacent copies — three separate "fixed in the contract, not in the copies" recurrences. For a six-file amendment that is a structural property, not carelessness, and it is the strongest argument for landing this smaller next time: contract text in one PR, the surveyor/skill/card sync in another.

Still a draft; definition changes keep the human promotion gate.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fdb3afeb2d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread AGENTS.md Outdated
Comment thread AGENTS.md Outdated
Comment thread AGENTS.md
Comment thread AGENTS.md
Comment thread AGENTS.md
Comment thread .claude/agents/portfolio-surveyor.md Outdated
Completes in the contract the fix already made in the surveyor — leaving them
disagreeing would have left the authoritative file wrong:

- The live-claim test treated ANY assignee plus a claim branch as an agent
  lease, so an issue assigned to a human collaborator or Copilot could be
  parked and then taken over after the window. Only a devantler assignment is
  a claim now, and someone else's assignment is explicitly protected by the
  standing 'do not do work others are assigned to' rule.

- The lease query took the newest assigned event for anyone, so a later
  assignment of a different person would restart a window the agent never
  renewed. Filtered to .assignee.login == devantler, verified against a real
  timeline.

Also finishes the previous round's takeover-branch fix in the surveyor's
branch scan, which had only been applied to its digest rules.

Part of devantler-tech/world-at-ruin#102
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Engineer

Stopping the review loop here. Round 7 returned 6 findings, 0 of them P0/P1, which is the condition I set for stopping. I did not apply that mechanically, though — three of the six were fixed in 9ede906 and three are deferred, and the split is worth explaining because it deliberately overrides my own rule.

Fixed (the label understated them): two were the contract-side of the human-collaborator defect I fixed in the surveyor last round — the live-claim test treated any assignee as an agent lease, and the lease query took the newest assigned event for anyone. Shipping the surveyor fix without these would have left the authoritative file saying the wrong thing and the two disagreeing. An issue assigned to a human could still have been parked and then taken over after the window, which contradicts the standing "do not do work others are assigned to" direction. That is not polish regardless of the badge. The third was a one-line completion of my own previous round's takeover-branch fix, which I had applied to the surveyor's digest rules but not its branch scan.

Deferred → #2250: takeover-tip verification naming the actual pushed branch, resolving the claim ref from the issue number rather than an assumed name, and repo-scoping the #<issue> body search. All three are real, none can strand a player, break a guardrail, or take someone's work.

Closing summary

Seven rounds, 33 findings, none noise. Everything that could cause harm was found in rounds 1–2 and fixed: a write to an out-of-scope repo, executing an external contributor's code, a mechanism invisible to the survey that drives selection, a takeover that never refreshed its lease, the residual same-branch-name race that made the pre-push check useless, and treating a human's assignment as an agent lease.

The honest read on the volume: rounds 2–7 were overwhelmingly my own fixes creating new edge cases in adjacent copies — the contract, two skills, a card and an agent definition all restate these rules, so every correction has four echo sites and three separate "fixed in the contract, not in the copies" recurrences followed. That is a property of the change's shape, and the lesson is to land contract text and the copy-sync as separate PRs next time. It is recorded on #2250 as an acceptance criterion so the follow-up does not repeat it.

CI green, 0 unresolved threads, no further pushes planned. Promotion is @devantler's call — definition changes keep the human gate, and this one has now had considerably more scrutiny than most.

…laim-protocol

# Conflicts:
#	.claude/agents/portfolio-surveyor.md
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Engineer

Merged main in — it had moved 3 commits, including #2239, which retires the definition-PR promotion gate. That changes this PR's own path: it now self-promotes on the three genuine-readiness conditions like any product PR, rather than waiting on a separate gate.

One real conflict, in portfolio-surveyor.md: main added --archived=false to both org-wide searches while I added assignees to the issue search. Kept both — the searches now carry --archived=false AND the assignees claim signal, with main's explanatory note intact. Verified after the merge that my claim-protocol content (the section, skip reason (e), the exclusive-push rule, the devantler-only claim test) and main's new content both survived rather than assuming a clean auto-merge.

Re-requesting, since the merge moved the head and staled the previous green.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b04e74fe93

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .claude/skills/portfolio-maintenance/SKILL.md Outdated
Comment thread AGENTS.md
Comment thread .claude/agents/portfolio-surveyor.md Outdated
Third recurrence of the same class, and the one that actually matters: the
run-loop copy still said to pick an issue up 'regardless of who's assigned'
once a claim lapsed. The contract now says only a devantler assignment is a
claim, so the skill contradicted it and would have had an agent take a human
collaborator's assigned work after the 2h window — against the standing
'do not do work others are assigned to' direction.

Also fixes the digest row it created: the shape said assignees=<n> (a count)
while the survey step above requires logins, because only devantler is a claim
signal. Now assignee=devantler.

Deferred to #2250 (already captured there): resolving
the claim ref from the issue number so two sessions choosing different
<area>-<desc> stems still collide on one ref.

Part of devantler-tech/world-at-ruin#102
A fourth copy carried the same unqualified 'regardless of who's assigned'.
Found by grepping every echo site after fixing the other three rather than
waiting for another review round to surface it — the same drift class this PR
has now hit four times.

Part of devantler-tech/world-at-ruin#102
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Engineer

Three findings, 0 P0/P1. Two fixed, one deferred — and my own follow-up sweep found a fourth instance the review had not flagged.

Honor human assignees when claims expire — fixed, and this is the one that mattered. The run-loop copy still said to pick an issue up "regardless of who's assigned" once a claim lapsed, contradicting the contract change that only a devantler assignment is a claim. As written, an agent would take a human collaborator's assigned work after the 2h window — against the standing "do not do work others are assigned to" direction. Third recurrence of this class in this PR.

Then I grepped every echo site instead of waiting for round nine — and product-engineering/SKILL.md carried the same unqualified sentence, unflagged. Now fixed in all four (contract, both skills, surveyor), verified by sweeping for the phrase and confirming the human-collaborator carve-out is present in each.

Digest row — fixed. The shape said assignees=<n> (a count) while the step above requires logins, because only devantler is a claim signal. Now assignee=devantler.

Claim-ref determinism — deferred, already captured as item 2 of #2250. The finding is right: the push only arbitrates the exact branch name a run derived, so two sessions choosing different <area>-<desc> stems still miss each other. It is a real residual, it is written down with the specifics, and it does not warrant another round here.

Closing

Nine rounds, 36 findings, none noise. Every P0/P1 was fixed; what remains is one named residual on #2250. The dominant pattern, worth recording plainly: this change has four echo sites, and the same defect recurred in them four times. That is the argument for landing contract text and copy-sync separately, which is already an acceptance criterion on the follow-up.

The definition-PR gate was retired on main by #2239 while this was in review, so this now self-promotes on the ordinary readiness conditions rather than waiting. Promoting and merging once CI and a green review land at the new head.

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Engineer

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: 89a6aaebaf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@devantler
devantler marked this pull request as ready for review July 18, 2026 12:26
@devantler
devantler merged commit 7aa2967 into main Jul 18, 2026
20 checks passed
@devantler
devantler deleted the claude/constitution-claim-protocol branch July 18, 2026 12:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parallel-session claim protocol: six end-to-end builds were discarded in one day because a PR is the only claim signal

1 participant