Skip to content

Cross-lane claim races are never arbitrated β€” a lane-neutral claim ref, with four proven trapsΒ #2302

Description

@devantler

πŸ€– Generated by the Daily AI Engineer

Split out of #2292 on a pre-committed trigger: the claim mechanism drew a new P1 in each of two consecutive review rounds and started requiring changes in a third file, so it ships on its own rather than growing that PR further.

Evidence β€” the hole is PRE-EXISTING, not introduced by the third lane

The claim protocol's rule 4 settles a race by having both instances derive the same branch name, so one non-force push is refused. That premise is already false: each instance writes its own namespace, and codex/* has been in live use alongside claude/* for some time β€” measured 2026-07-20: 17 codex/* PRs on ksail, 3 on world-at-ruin, 3 cursor/* on monorepo. Two different instances racing one issue therefore both push cleanly and both believe they won. Cross-lane races have never been arbitrated; adding a third lane widens an existing hole rather than creating one.

The recorded cost of unarbitrated races is high: six end-to-end builds discarded in ~24 hours on world-at-ruin, one lost by 52 seconds and one by 135.

Design

Arbitrate on a lane-neutral agent-claim/<issue> ref that every instance derives identically from the issue number alone, before creating its lane-specific work branch.

Four traps, all proven by execution β€” do not re-derive these

  1. Arbitration works β€” two clones, second writer's non-force push is rejected, git ls-remote returns the winner's sha. βœ…
  2. Never judge the push by its exit status. git push … | tail returns exit 0 on a rejected push (the pipeline reports tail's status). Only the ls-remote tip comparison is safe.
  3. Identical claim commits collide. Every instance commits as devantler, so a fixed message on a shared base in the same second yields a byte-identical commit β€” reproduced exactly: two clones both produced e0c73d68fed2075216b62023f8147c411929d3da, identical down to the signature. Both pushes succeed, both read the tip as theirs, arbitration silently fails in the close race it exists for. A nonce fixes it. ⚠️ A first fixture did not reproduce this because the clones had different parents β€” anchor both on origin/main or the negative result looks like refutation.
  4. An unretired claim ref is a permanent lock. It is not a PR head and no sweep enumerates it, so one run crashing between claiming and opening its PR rejects every later push while every later instance correctly stands down β€” the issue becomes unworkable forever. Proven RED/GREEN: A claims and crashes β†’ B is rejected β†’ B retires the stale ref and claims β†’ C still correctly loses to B β†’ tip equals B.

Open design questions this must answer

  • Nonce portability. uuidgen is not guaranteed on every runner; if the substitution fails the message goes back to fixed and trap 3 returns. Needs a source guaranteed across macOS, the Linux review environment and the cloud sandbox, and must fail closed if none is available.
  • Atomic propagation. .claude/skills/portfolio-maintenance/SKILL.md (~lines 407–414) still tells every run to push the lane branch as the claim and scans only claude/*. The contract and that procedure must change together, or runs keep bypassing the new ref.
  • Retirement without a sweep. Nothing enumerates agent-claim/*; the retirement rules must stand on their own evidence gate.

Acceptance criteria

  • Lane-neutral claim ref specified in AGENTS.md and the run-loop skill, in one change
  • Nonce source portable across all three runtimes, failing closed when unavailable
  • Retirement on PR open, plus evidence-gated stale takeover (no open PR for the issue + tip past the lease)
  • RED/GREEN test covering all four traps above
  • The ⚠️ KNOWN HOLE note added to rule 4 by feat(ai-engineer): onboard the Cursor Automation cloud instance as the third laneΒ #2292 is removed when this lands

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    Status
    πŸ‘€ In Review

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions