Development record
Claims are branches on the shared remote; nothing kept on one computer is the register; claim text is data
Context
On 2026-10-03 a second person began working on abcd from their own computer, the repository was public, and a hosted cloud session with its own clone was admitted as one more participant. Everything abcd used to keep agents apart lived on one computer: The shared run's exclusive-create lease under the machine-scoped run store (itd-2609221656373558), the sibling-worktree listing, and notes the sessions left each other. None of it reaches the second person or the cloud session, so their agents and ours could each start the same item, and the first anyone would hear of it is the collision at merge (iss-2609020716570699).
The product thinker ruled the same day that coordination must be visible to
every participant, that a claim is a marker on the shared remote which abcd
lists, and that reserving one item is built first
(itd-2609091034175565,
decisions 1 to 4;
itd-2609150819440345,
decision 4). The state of the art and the run lessons behind those rulings
are in
2026-10-03-multi-agent-coordination-sota-and-run-lessons.md.
Two adversarial reviews of the design followed, and their evidence fixed the transport rather than leaving it to preference:
- A hosted cloud session's git proxy rejects branch deletions and pushes of
anything other than a branch; a probe on 2026-10-03 got HTTP 403 on a ref
under
refs/abcd/while branch pushes succeeded. A ref outsiderefs/heads/cannot include the cloud session. - abcd's hazard registry refuses every
--force-with-leasepush, and the proxy refuses deletions, so neither a lease flag nor a deletion is available. A plain push already compares and swaps: a parentless commit over an existing branch is rejected as non-fast-forward, and two children of one parent admit exactly one. - The committed pre-push hook demands a preflight receipt for every new commit, so it refuses a claim push today; an exemption by branch name alone would let code past the receipt gate.
- A claim's text is written by whoever has write access and is read by agents, and the 2026 incidents in the state of the art are agents following instructions planted in exactly such channels.
- Anyone with write access can push to or delete any branch outside the default branch, which is all this repository's rulesets protect.
Brief invariant 7 already holds that implicit operations never touch the network (adr-38).
Decision
We will hold a claim on a record as a branch on the shared remote,
refs/heads/abcd-reserved/<record-id> (a person reads it as a reservation; the record and the code call it a claim), and nowhere else:
- The branch is the transport, and the only one. A claim branch is a chain of commits with the empty tree, which nobody merges and abcd lists. It is a branch because a hosted cloud session can push nothing else. No hidden-ref backend sits beside it. Every claim act is a plain fast-forward push: a parentless create, a renewal or takeover as a child, and a release as a tombstone child; abcd uses no lease flag and deletes no claim branch.
- Nothing kept on one computer is the source of truth for who holds what. The local tier, the machine-scoped run store, a desktop note and same-account messaging may speed up notice; the claim branch on the remote decides. A claim that cannot reach the remote is refused loudly and is never recorded locally as if it were shared.
- Claim text is data. A claim is parsed by its known trailer keys and rendered as quoted values; an unknown key is ignored, a malformed or oversized claim is reported as unreadable, and no part of a claim is ever handed to an agent as instructions. Footprint paths never reach a shell or a refspec, and the branch name is built only from a valid record id.
- Claims are advisory among trusted writers. A claim refuses nothing at the forge: Any writer can push to it or delete it. abcd's verbs honour it, expiry is judged by the reader, and the land stage re-checks it before arming a merge.
- The pre-push exemption covers empty-tree claim commits only. The
repository's pre-push hook passes an update to
refs/heads/abcd-reserved/*without a preflight receipt only when every commit in the pushed range has the empty tree; every other push, and every non-fast-forward push, is gated as before. - Every network act is an explicit verb.
abcd reserveand its release,abcd build,abcd drainandabcd peerspush or fetch reservations because that is their documented meaning, andimplement stepreads and writes the run's reservation because its documented meaning is extended to say so; a branch push is not remote configuration. A reservation stops counting once its record is terminal on the default branch, so no step after the merge is needed. Every render, gate, hook and session start reads the claims only as last fetched, from disk.
Alternatives Considered
- A ref outside
refs/heads/(refs/abcd/claims/<record-id>), hidden from the forge's website. The product thinker's first choice; rejected on the cloud probe, because the cloud session could neither claim nor release. - Both a hidden ref and a branch. Rejected: Two backends are two bug surfaces, and the hidden ref buys nothing a cloud session can use.
- A lease kept on one machine (the run store, the shared
.git, or the host's session registry). Rejected by the visibility ruling: The second person never sees it. - A
claimed_bystamp on the record, pushed through a pull request. Rejected: An older reader skips a record with an unknown key, so it needed a two-release migration, and each claim paid a merge-queue pass of fifteen to sixteen minutes. - A forge claim (an issue assignment or a draft pull request). Kept for outside contributors, who cannot push a branch (itd-2610031259176838); it needs a forge client and token that a cloud session lacks.
Consequences
- A claim costs one push of seconds, with no pull request, queue pass or workflow run, since every push trigger filters on the default branch or release tags.
- Claim branches show in the forge's branch list and may raise a compare-and-pull-request banner; every claimed record leaves a tombstoned branch, so the clean-up sweep of itd-118 is owed before the forge's 5,000-branch limit matters.
- A malicious or careless writer can delete or overwrite a claim; the design accepts this among trusted writers and does not defend against it.
- Brief invariant 7's documented-meaning entries must name the reservation
verbs and
implement step's reservation write, and invariant 10 gains one clause saying a git branch push is not remote configuration (the technical facilitator's ruling of 2026-10-03, itd-2609091034175565 decision 12); both land in the change that builds the reservation, not before. - The shipped
implement claimand the remote claim judge liveness through one function, so the lease arithmetic has one home.
Frontmatter
| id | adr-2610031352212697 |
|---|---|
| type | adr |
| status | accepted |
| date | 2026-10-03 |
.abcd/development/decisions/adrs/2610031352212697-claims-are-branches-on-the-shared-remote.md
open on GitHub ↗ · commit history ↗
References6
- adr-38Implicit checks are disk-only — the network answers only an explicit askrelated
- adr-2609091248200336A tool never creates directories in user-owned project space; the store's location binds now and its verbs bind when the store shipsrelated
- itd-2609091034175565Before an agent starts on an item, it reserves it on the shared remote, and everyone who works on the project sees the reservationrelated
- itd-2609150819440345Which session holds which worktree, branch or record is coordinated entirely by conversation, so every new session repeats a handshake that nothing records. A session joining work in progress has no way to ask what is already claimed: it messages the peers it can see, waits for replies, and rebuilds a picture that the sessions before it had already built and did not write down. One measured encounter cost four messages and about fifteen minutes before any work began, and the picture it produced is not durable, so the session after that pays again. The convention that a diff you did not make is a peer's work depends on knowing who the peers are and what they hold, which is precisely the thing no artefact carries. The repository already records this gap for the narrow case of detecting a peer session before mutating git state; the wider case is claim rather than presence, and the two want the same substrate. Whatever holds it should be as cheap to write as it is to read, because a coordination record nobody updates is worse than the chat it replaced.related
- itd-2609221656373558Two sessions share one run, and the run measures which way of dividing the work is bestrelated
- itd-2610031259176838Outside contributors reserve work through a draft pull request that abcd honoursrelated
Referenced by4
- adr-2610120937435856A cloud session's unchecked work goes only to a declared work-in-progress prefix that never reaches the default branchrelated
- adr-2610150729333337A status branch moves without a preflight receiptrelated
- adr-2610161940004937Spent reservation branches are deleted only by one GitHub App the ruleset namesrelated
- itd-2609151838312703Sessions under any account, on any machine or in the cloud announce what they are doing through short fixed-form announcements on the shared remote, a person reads everyone's status with one command, and what is really a hold stays a reservationrelated
mentions4
- iss-2609020716570699Nothing tells an agent that a record it is about to fix has been claimed or resolved by another session until the resolution gate refuses the push. In one night a peer session re-fixed two issues a paused branch also fixed, and two of its open PRs duplicate merged work. The claim signal that worked in every published multi-agent run is the repository itself: a claim written into the open record (claimed_by: account and harness, branch) and pushed alone through the queue before any fix starts, so a losing race is a push rejection; plus a duplicate-guard required check that fails a PR whose Resolves trailer names a record already resolved on origin/main; plus capture resolve refusing a record that is already terminal on the fetched origin/main. Folder membership is already the status signal, so the claim extends the one canonical primitive rather than adding a lock file that rots. Refines iss-2608220750029993.
- itd-118Merged work leaves no residue: abcd-managed repos delete a PR branch on merge, and abcd tidies the stale local branch, tracking ref, and worktree
- itd-2610161016394967Drains on several accounts share the work
- spc-2610031753359849Reservations on the shared remote: `abcd reserve`, one liveness judgement, and the build, drain and land integration