Development record
Before an agent starts on an item, it reserves it on the shared remote, and everyone who works on the project sees the reservation
Typed links: refines itd-2609221656373558 (the shipped in-run claim, whose lease shape, two-hour default, back-off refusal and liveness judgement this record carries to the shared remote; the judgement moves into one function both use) and itd-2609211116005482 (the pick's exclusions gain "held by another person's reservation" and the view's age); supersedes itd-33 (agent coordination through a local active-work.json, which decision 1 rules out); related_issues iss-2609020716570699 (the duplicated re-fixes and pull requests this record exists to prevent); related_intents itd-2609091416295622 (the read-only sibling-worktree listing; prose cross-reference to the 2026-09-09 ruling, which gave it its own record), itd-2609091416304128 (the resolve-time refusal; prose cross-reference to the same ruling), itd-2609091014076309 (the machine-scoped worktree store), itd-2609150819440345 (the wider register of running sessions, kept for later; its decision 4 is this record's transport), itd-2610031259176838 (outside contributors, who cannot push a reservation branch), itd-118 (the home of tombstone-branch deletion); related_adrs adr-2609091248200336 (a tool never writes in space it was not handed) and adr-2610031352212697 (reservations are branches on the shared remote, nothing kept on one computer is the register, and reservation text is data; accepted). Prose cross-reference, not a typed link, because no schema field carries the relation (iss-2609091256264547): iss-2608220750029993, session presence, which this record answers for records and not for sessions.
On words. Every surface a person reads says reservation. Inside the record and the code a reservation is a claim, the name the shipped in-run implement claim already uses; the two words name one thing.
Press Release
Two people's agents never start the same piece of work. When an agent starts on an item, abcd reserves it for the person the agent works for. The reservation is a small branch on the project's shared copy that nobody ever merges: Everyone who works on the project sees it, whichever computer they work on, and so does an agent working from a cloud session. If someone else already holds the item, or the files the work will touch, abcd says who and until when, and the agent takes something else. A reservation lasts two hours and is renewed every hour while the work goes on, so one left behind by a closed laptop frees itself. It stops counting the moment the work is merged. Starting a build reserves the item, and a person working by hand reserves with one short command.
"There are two of us now, on two computers, each running agents, and sometimes a cloud session as well," said Maya, an autonomous-development practitioner. "My agents could only see what my own machine knew, so they would happily start an item my colleague's agents were halfway through. Now they see the reservation before they begin, and the one that loses finds out in seconds, not after a night's work. Moving a piece of work from one of my copies to another is one flag."
Why This Matters
A second person now works on the project, from their own computer, and the repository is public; a cloud session can join the work with its own copy. Everything abcd used to keep agents apart lived on one computer: The shared run's lease file, the sibling-worktree listing, the notes sessions leave each other. None of it reaches the second person or the cloud session. Their agents and ours can each start the same item, and the first anyone hears of it is when the second set of changes collides at merge, after the work is written, tested and reviewed. That is the collision this record has carried since 2026-09-01; it is now certain to recur, because the participants share nothing but the remote.
The autonomous runs taught the same thing from inside one account (the multi-agent coordination synthesis of 2026-10-03, 2026-10-03-multi-agent-coordination-sota-and-run-lessons.md, which ranks the run lessons and the state of the art):
- A reservation taken by one atomic check works. The shipped
implement claim(an exclusive file create with a two-hour lease) saw no collisions inside one account. But it is per account and per run: A peer outside the run still re-fixed two issues the run held. - Only shared, durable records carry work across sessions. Habits held in one session's prose died at every handover; only the decision log carried authority, and the product thinker could not see which session ran the run.
- Seeing records is not seeing who works on them. The listing shows what a peer's tree holds, not who is working on what, so agents mistook each other for foreign peers.
- The merge is the one serial point. Coordination that only bites at merge is coordination that bites after the cost is spent.
- Every channel a peer can write to is untrusted input. The state of the art's incidents of 2026 are agents following instructions planted in issue text, comments and branch names.
The state of the art names the same primitive this record adopts: A reservation taken by a create-only compare-and-swap on a git ref, a lease with an expiry, a re-check at merge time (fencing), and the files the work will touch declared up front, because concurrent agent pull requests that touch one file conflict often.
How the record got here
The record began as the remedy of iss-2609020716570699: On one night of the 2026-09-01 run a peer session re-fixed two issues a paused branch had already fixed, and two of its pull requests duplicated merged work; the resolution gate refused the push, the first moment anything said no. The recorded remedy was a claimed_by stamp on the record, pushed alone, plus a duplicate guard and a resolve-time refusal. On 2026-09-09 two local near-collisions (a record nearly minted twice, a worktree nearly created over a peer's) widened the draft to cover sessions on one machine.
Two adversarial reviews of the widened draft then found five things, and the ruling of 2026-09-09 in the decision log split the record in three: The listing (itd-2609091416295622, shipped) and the resolve-time refusal (itd-2609091416304128) went their own way, and this record kept the claim, marked not ready. The findings, and what each becomes under this design:
- A stamp on an issue record is invisible to an older reader. The issue schema is a closed allow-list (
internal/core/issueschema/issueschema.go, mirrored byrecord_schemaininternal/core/lint/schema.go), so a stamp needed a two-release migration. This design writes no field into any record, so the migration is gone with the stamp. - The refusals fired after the work. Here the reservation is taken when a build starts,
build nextpasses over a reserved item, and the land stage re-checks. - No staleness threshold was safe in both directions. Here nothing detects liveness: A lease expires unless renewed, bounded by the lease in one direction and by the hourly renewal in the other, and merged work releases its reservation without any step.
- The pushed half cost a merge-queue pass per claim, fifteen to sixteen minutes measured on this repository. A reservation branch opens no pull request and enters no queue.
- Every collision on record was covered without a claim. That held while every session shared one machine. A second person or a cloud session has no sibling worktree to list, and the resolve-time refusal fires only after their work has merged.
Three homes were weighed for a lease on one machine: The shared .git, the machine-scoped ~/.abcd.noindex/ lane keyed on the root commit, and the host's session registry (a source through hooks, never a surface, per itd-22). All three are one computer's knowledge, which decision 1 rules out as the source of truth.
The reviews and the planning interview (2026-10-03)
Two adversarial reviews of this rewrite followed, design and feasibility, then record discipline; both returned NEEDS-WORK, and their evidence is in the synthesis's Review evidence section. Review 1 found the primitive right and the transport wrong: A hosted cloud session's git proxy pushes branches only (HTTP 403 on a ref outside refs/heads/); abcd's hazard registry and the cloud proxy rule out lease flags and deletions, while a plain push is already the compare-and-swap; the committed pre-push hook refused every claim push; the merge queue merges after the fence; the claimer sets every time; and the lease arithmetic must have one home. Review 2 found the grounds unreadable to the second person, the criteria compound, and logged rulings reversed only inside the draft; the synthesis was promoted, the criteria split, and the rulings logged. The planning interview then answered the pre-pass's questions: Decisions 12 to 32 below.
What's In Scope
How the two specs are realised (decision 38). abcd intent plan mints spec 1, whose ## Steps list the spec-1 work below first and the spec-2 work after it. When spec 1's steps have landed, abcd spec close <spc-N> --remainder <slug> mints spec 2, carrying the unlanded steps. The intent ships when spec 2 closes.
Spec 1: Reserve, renew, release, the listing and the hook.
- A reservation is a branch nobody merges,
refs/heads/abcd-reserved/<record-id>onorigin, holding a chain of commits whose every tree is the empty tree (4b825dc642cb6eb9a060e54bf8d69288fbee4904). abcd writes each commit withgit commit-tree, so the caller's HEAD, index and working tree are never touched and a detached HEAD makes no difference. Each commit's message carries trailers: The record id; the holder, the person's GitHub username; the copy, an opaque working-copy key naming the checkout the reservation was made from (never a path or a host); the footprint;claimed_at(when this commit was written);expires_at;released_aton a tombstone; andAssisted-by: abcd:<version>, which abcd writes itself becausecommit-treeruns noprepare-commit-msghook. Author and committer are the person's git identity. No record file gains a field. - The holder is a person, named by their GitHub username. abcd reads it from a no-reply address in
user.email(<id>+<username>@users.noreply.github.comor<username>@users.noreply.github.com), or else from a value set once per computer in the user-level~/.abcd.noindex/config.json, never from the repository. With neither, every write is refused, naming both sources. A laptop and a cloud session of one person resolve to one username and count as one person. - A reservation also records the copy it was made from (decision 33). The working-copy key is a random value minted once per checkout and kept in that checkout's local tier, so it names neither a path nor a host. The same copy continues and renews freely, so a fresh agent in the same folder carries on. Another copy of the same person, on a second computer or in a cloud session, is refused while the reservation is live, with the message "you are already building <id> on another computer, until <expiry>". To continue elsewhere on purpose, the person passes
--take-over(onabcd reserveandabcd build), which pushes a child naming the new copy; the old copy's renewal and land fence then refuse. The person stays the unit for the cap, the per-person count and the identity. - Every act is a plain push, never a lease flag or a deletion. The verb first fetches the reservation branch, judges its tip, then pushes:
- Reserve: A parentless commit; the remote refuses it as non-fast-forward when the branch exists, and the verb then re-reads and refuses naming the holder.
- Renew: A fast-forward child, when the tip names this person and this copy.
- Take over from one's own other copy: With
--take-over, a fast-forward child naming this copy, when the tip names this person and another copy; the output names the copy it replaced. - Take over: A fast-forward child of a tip whose effective expiry, judged by the taker's clock, passed at least five minutes ago; the output names the holder and expiry it replaced.
- Release: A fast-forward tombstone child carrying
released_at, withexpires_atset to it, when the tip names this person and this copy. - A reservation another person holds is refused with the back-off exit, naming the holder and the effective expiry.
- A footprint is required. Every reservation names the repository-relative paths the work expects to change; one without a footprint is refused.
- One liveness judgement. The effective expiry is
min(expires_at, claimed_at + 24h), 24 hours being the shippedMaxLease; a longer lease is refused. A tip that cannot be parsed, or exceeds the message size cap, is reported as unreadable, held until its commit date plus the two-hour default lease, and then open to a takeover that names what it replaced. A reservation stops counting the moment its record is terminal on the default branch as last fetched (refs/remotes/origin/<default>), whatever its expiry. One function holds this judgement, and the shipped in-runimplement claimcalls it too. - The verbs.
abcd reserve <record-id> --footprint <path>…andabcd reserve <record-id> --releasecover the work no build or drain starts, each a push by its documented meaning;abcd reserveis an agent's verb (decision 39), listed with the verbs agents and hosts call.abcd peersfetches+refs/heads/abcd-reserved/*:refs/remotes/origin/abcd-reserved/*with pruning and lists every reservation with its holder, footprint, effective expiry and state (live, expired, released, unreadable), and each person's count of live reservations. - Only
origin. A checkout whose remotes do not include one namedoriginis refused, naming the remotes found. - Every failure refuses loudly and writes no local reservation: No remote; no write permission; offline; a remote or proxy that rejects the push. Each names its cause and says that no reservation was made.
- The pre-push hook exempts reservation chains, narrowly.
.githooks/pre-pushpasses an update torefs/heads/abcd-reserved/*without a preflight receipt only when every commit in the pushed range (every commit, on a create) has the empty tree; anything else under that prefix is gated as today. The non-fast-forward refusal stays and covers reservation branches too. No reservation push skips the hooks. - Public safety. A reservation names no host and no home path; the message passes the outbound policy (no session URL, no tool footer). A reservation is parsed by its trailer keys and rendered as quoted data, never handed to an agent as instructions; an unknown key is ignored. Footprint paths are trailer values only and never reach a shell or a refspec. The branch name is built only from a record id that passes the record-id grammar. The message is size-capped, and the verb refuses to write a larger one.
Spec 2: The land fence, the cap, the footprint refusal, the renders, and build and drain.
abcd build <itd-N>reserves the intent when it starts, with the footprint its spec's## Footprintnames, andabcd build <itd-N> --releasegives it back.abcd drainreserves each issue it takes, with the issue'sfound_atpath as its footprint. A run with no footprint to give is refused at the reservation, naming what is missing. These pushes join each verb's documented meaning.- Renewal during a run.
implement steprenews the run's reservation whenever half its lease has passed (every hour for the two-hour default); its documented meaning gains the reservation read and write. - The land fence. Inside
implement step: Immediately before arming the merge (or leaving the pull request for a person), the land stage fetches the reservation branch and refuses unless the tip still names this person and this copy, naming the person or the other copy that holds it now; when it is, the stage renews it with a lease of at least the merge queue'scheck_response_timeout_minutesfrom.abcd/work/rulesets/main-protection.json(45 here) plus a margin, or a day where no mirror exists (decision 40), and keeps renewing at half-lease while the merge waits. Once the record is terminal on the default branch the reservation has stopped counting; a later step tidies up with a tombstone, and leaves and reports a tip that is no longer this person's. - A per-person cap. The verb refuses a reservation that would take one person past ten live reservations, naming the cap and their live reservations.
- Footprint overlap is refused. A new reservation whose footprint shares a path with another live reservation is refused, naming that reservation, and the agent takes other work.
- Renders read only the disk.
abcd <record-id>, the board,build <itd-N>andbuild nextreadrefs/remotes/origin/abcd-reserved/*as last fetched, through the shared liveness function, and say how old that view is, or that nothing has been fetched.build nextpasses over an item another person holds, naming the reservation and the view's age in its exclusion;build <itd-N>refuses it at thepeerscheck. The "as of last fetch" reader and its age line are the same ones the resolve-time refusal (itd-2609091416304128) uses. - Names never collide. The prefix
abcd-reserved/is disjoint from the implement loop's lane prefixbuild/, and the land stage's branch removal never touches a reservation branch.
Records to touch at ship
- Brief invariant 10 gains one clause: A git branch push is not remote configuration (decision 12).
- Brief invariant 7's documented-meaning entries:
reserve,build,drain,peersandimplement step. - A new surface page for the reservation verb in
.abcd/development/brief/04-surfaces/, with its README row;34-build.mdcorrected (the in-run claim within one account, the reservation across people);08-abcd.mdand27-implement.md. commands/reserve.md(new),commands/build.md,commands/drain.md,commands/peers.md,commands/implement.mdandcommands/abcd.md.AGENTS.md's concurrent-sessions and attribution sections; the.githooks/pre-pushheader; the verification matrix; a reservation-pushknown_goodininternal/core/guard/defaults/guard.json.docs/reference/terminology.mdgains "reservation";ACKNOWLEDGEMENTS.mdgains any source the build adopts.- adr-2610031352212697 moves to accepted.
What's Out of Scope
- The wider session register, saying which sessions run and what each holds: itd-2609150819440345, kept as a draft for later.
- Outside contributors, who cannot push a branch to the remote: itd-2610031259176838.
- The resolve-time refusal of a record already terminal on the default branch: itd-2609091416304128.
- Deleting reservation branches. Tombstones are deleted by itd-118's sweep from an ordinary machine, since the cloud proxy rejects deletions.
- Keeping one person's sessions in one copy apart. Any session in the copy that made a reservation may renew or release it; sessions sharing one run are kept apart by the in-run
implement claim, unchanged. - Judging that two records describe one observation: itd-87.
- Moving, listing or reclaiming worktrees: itd-2609091014076309.
- Any field on a record file. No
claimed_bystamp, no schema change, no migration. - A second backend. One transport, the reservation branch.
- A lock. Anyone with write access can push to or delete a reservation branch from an ordinary machine, and the rulesets here target only the default branch; a reservation is advisory among trusted writers.
- Detecting whether a session is alive. Expiry, renewal and the derived release replace it.
- Implicit network traffic. No render, gate, hook or session start fetches or pushes a reservation.
- A shared remote with another name than
origin, and any change to the user's git configuration.
Mechanism
We expect two people's agents, and a cloud session's, to stop starting the same item because the reservation is taken by one atomic compare-and-swap on the one place they all share, a branch on the remote every participant can push, at the moment work starts (a build or a drain reserves as it begins), and because the verbs that choose work (build next, build <itd-N>) read it from disk; we expect the footprint refusal to keep apart work on different items that would touch the same files, and the land fence to catch a reservation lost mid-work before the merge is armed. We expect a person's own second computer or cloud session to be stopped the same way, because the reservation names the copy it was made from, while --take-over keeps a deliberate move one flag away. We expect it to cost little enough that nobody routes round it, because a reservation is one plain push of one empty commit, seconds, with no pull request and no queue pass, and because merged work releases itself. It is falsified if, after it ships, two people's runs work one item, or overlapping files, in parallel although both reserved; if live work regularly loses its reservation to a takeover because renewal does not keep pace; if a cloud session cannot reserve; or if the footprint refusal turns away so much work that people widen footprints or skip the verb.
Scope Conditions
- Holds for participants with write access to the shared remote, all pushing to one remote named
origin. Someone who cannot push a branch cannot reserve, and is out of scope here. - Holds while the remote, and any proxy in front of it, accepts fast-forward branch pushes under
abcd-reserved/. The hosted cloud session's proxy does, by the 2026-10-03 probe; the branch shows in the forge's branch list and may raise a compare-and-pull-request banner, costs the product thinker accepted. - Hypothesis until a real push proves it: A reservation branch starts no workflow, because every
on: pushtrigger in.github/workflows/filters on the default branch or release tags. It is argued from the workflow definitions, not observed. - Holds while the remote stays under the forge's limit of 5,000 branches (repository limits). Every reserved record leaves one tombstoned branch until itd-118's sweep deletes it.
- Holds among trusted writers. Anyone with write access can push onto or delete a reservation branch from an ordinary machine, and nothing here prevents it.
- Holds while each person's computers resolve to one GitHub username. A person whose laptop and cloud session resolve differently counts as two people, and their own sessions refuse each other.
- Holds while the participants' clocks agree to within minutes. Expiry is judged by the reader's clock against times the claimer wrote; the five-minute takeover margin absorbs small skew, and the 24-hour clamp bounds a claimer whose clock or
expires_atis far ahead. - Holds while renewal runs at least once per lease.
implement steprenews only when it is called; a run whose steps are more than an hour apart can lose its reservation to a taker, and the land fence then refuses its merge. - The cap and the footprint refusal are judged on the view fetched just before the push. Two reservations on different records pushed within the same seconds can both pass, though they overlap or together exceed the cap.
- The fence covers the merge queue's wait only while
implement stepkeeps renewing; the derived release then covers the merge itself, so a merged record never needs a step to stop counting. - Renders and
build's peer check are only as fresh as the last fetch of the reservation branches, byabcd peersor by anygit fetchoforiginunder the default refspec. A reservation taken since then is invisible to them, and the reserving push itself refuses a second reservation on the same item. - A reservation's contents are public. Anyone who can read the repository can fetch the branches, so a reservation carries only what a commit would already publish.
- A cloud session, running on a hosted machine with its own clone, holds the same position as a second person's machine. It sees only what is on the remote, and nothing in this machine's local tier, run store or desktop notes.
Acceptance Criteria
Confirmed by the product thinker at the planning interview, 2026-10-03: person-visible criteria walked one by one (C2, C4, C5, C8, C12–C13, C15, C16, C27, C34–C39, C42, C47, C48, C50), technical criteria accepted as lists; C4, C20 and the new C52–C53 walked one by one after the take-over ruling and confirmed; C4 and C20 revised by decision 33 (take-over). One Given-When-Then and one verdict per bullet; each is testable with a local bare repository standing in for the remote and further clones standing in for a second person, a second computer and a cloud session. P and Q are two people with different GitHub usernames; C52 and C53 were added with decision 33 and are numbered after the rest so the confirmed numbering stands.
Spec 1: Reserve, renew, release, the listing and the hook.
- Given a bare repository as
originwith noabcd-reserved/iss-Nbranch and P's checkout on a detached HEAD, when P runsabcd reserve iss-N --footprint a.go, thenrefs/heads/abcd-reserved/iss-Npoints at a parentless empty-tree commit, authored and committed by P's git identity, whose trailers carry the record id, P's username, the footprint,claimed_at, anexpires_attwo hours later andAssisted-by: abcd:<version>, with P's HEAD, index andgit status --porcelainunchanged. - Given P holds a live reservation on
iss-N, when Q reservesiss-N, then Q is refused with the back-off exit, naming P's username and the effective expiry, and the branch is unchanged. - Given Q fetched before P reserved
iss-N, when Q reservesiss-N, then the bare repository rejects Q's parentless push as non-fast-forward, Q's verb re-reads and refuses naming P, and Q writes no reservation anywhere. - Given P holds a reservation made from one working copy, when a fresh agent session in the same copy reserves
iss-N, then it pushes a fast-forward child with a laterexpires_at. - Given the tip of
abcd-reserved/iss-Nis Q's, when P runsabcd reserve iss-N --release, then P is refused naming Q and nothing is pushed. - Given P holds a reservation, when P runs
abcd reserve iss-N --release, then a fast-forward tombstone child carryingreleased_at, withexpires_atequal to it, is pushed and no branch is deleted. - Given a reservation branch whose tip is a tombstone, when Q reserves
iss-N, then Q's reservation succeeds as a fast-forward child of the tombstone. - Given P's reservation reached its effective expiry more than five minutes ago by Q's clock, when Q reserves
iss-N, then Q's verb judges the expiry itself, pushes a fast-forward child, and names P and the expiry it replaced. - Given P's reservation reached its effective expiry less than five minutes ago by Q's clock, when Q reserves
iss-N, then Q is refused naming P. - Given a tip whose
expires_atis thirty days after itsclaimed_at, when any verb judges it, then its effective expiry isclaimed_atplus 24 hours. - Given a request for a lease longer than 24 hours, when the verb runs, then it refuses before pushing.
- Given a tip whose trailers carry instruction-shaped text, an unknown key and a malformed
expires_at, whenabcd peerslists it, then every value is shown as quoted data, the unknown key is ignored, and the reservation is reported as unreadable and held until its commit date plus two hours. - Given an unreadable tip whose commit date plus two hours and five minutes has passed, when Q reserves the record, then Q takes it over as a fast-forward child and the output names the unreadable commit it replaced.
- Given a tip whose message exceeds the size cap, when any verb reads it, then it is reported as unreadable.
- Given P's live reservation on
iss-Nandiss-Ninresolved/onrefs/remotes/origin/mainas last fetched, when any verb judges the reservation, then it counts as released. - Given P holds reservations on
iss-Nandiss-M, when Q runsabcd peers, then Q'srefs/remotes/origin/abcd-reserved/*holds both and the listing shows each one's holder, footprint, effective expiry and state, with P's live count as two. - Given
user.emailis1234+octo@users.noreply.github.com, when P reserves, then the holder trailer isocto. - Given
user.emailis not a no-reply address and~/.abcd.noindex/config.jsonnames the usernameocto, when P reserves, then the holder trailer isocto. - Given neither
user.emailnor~/.abcd.noindex/config.jsongives a username and the repository's own.abcd/configuration names one, when P reserves, then the verb refuses naming the two user-level sources and pushes nothing. - Given a laptop copy resolving to
octothrough~/.abcd.noindex/config.jsonholds a live reservation oniss-N, and a cloud copy whoseuser.emailisocto's no-reply address, when the cloud session reservesiss-Nwithout--take-over, then it is refused with "you are already building iss-N on another computer, until <expiry>" and nothing is pushed. - Given a checkout whose only remote is named
upstream, when the verb runs, then it refuses namingupstreamas the remote found. - Given an
originwhose path does not exist, when the verb runs, then it fails naming the cause and says no reservation was made. - Given a bare repository whose
pre-receivehook rejectsrefs/heads/abcd-reserved/*, standing in for a missing write permission and for a proxy that rejects the push, when the verb runs, then it fails quoting the rejection and says no reservation was made. - Given this repository's committed pre-push hook, when the verb pushes a reserve, a renewal, a takeover and a release, then the hook passes each without a preflight receipt and without
--no-verify. - Given this repository's committed pre-push hook, when a push to
refs/heads/abcd-reserved/*includes a commit whose tree is not the empty tree, then the hook refuses it for want of a receipt. - Given this repository's committed pre-push hook, when a non-fast-forward push to any branch, a reservation branch included, is attempted, then the hook refuses it.
- Given no footprint, when P runs
abcd reserve iss-N, then the verb refuses and pushes nothing. - Given the reservation commits, every render and the
--jsonpayloads, when they are scanned, then none names a host or carries a home path and the outbound policy finds no session URL or tool footer. - Given a footprint path containing shell metacharacters, a colon and an asterisk, when P reserves with it, then it is stored verbatim as a trailer value and appears in no git or shell argument.
- Given a reservation whose message would exceed the size cap, when the verb would write it, then the verb refuses and pushes nothing.
- Given an argument that is not a valid record id, when the verb runs, then it refuses before forming any ref name.
- Given the shipped
implement claimand the reservation, when each judges whether a claim is live, expired or unreadable, then both call one shared function and theimplement claimtests pass unchanged. - Given a cloud clone whose git identity is the person's no-reply address, when
scripts/check-attribution.shreads its reservation commits, then it passes.
Spec 2: The land fence, the cap, the footprint refusal, the renders, and build and drain.
- Given no reservation on planned intent
itd-Xwhose spec names a footprint, when P runsabcd build itd-X, thenabcd-reserved/itd-Xholds P's reservation with the spec's footprint. - Given planned intent
itd-Xwhose spec carries no## Footprint, when P runsabcd build itd-X, then it is refused at the reservation, naming the missing footprint. - Given P's run holds the reservation on
itd-X, when P runsabcd build itd-X --release, then a tombstone child is pushed. - Given a drain whose rule takes
iss-N, whenabcd drainstarts its lane, thenabcd-reserved/iss-Nholds P's reservation with the issue'sfound_atpath as its footprint. - Given Q's last-fetched view shows P's live reservation on planned intent
itd-X, when Q runsbuild next, thenitd-Xappears underexcluded, naming P's reservation, its expiry and the view's age. - Given Q's last-fetched view shows P's live reservation on planned intent
itd-X, when Q runsbuild itd-X, then it is refused at thepeerscheck naming P. - Given a run holding its reservation, half of whose lease has passed since the tip's
claimed_at, whenimplement stepruns, then it renews the reservation with a fast-forward child and logs the renewal. - Given a run holding its reservation, less than half of whose lease has passed, when
implement stepruns, then it pushes nothing for the reservation. - Given a lane at land whose reservation tip is now Q's, when
implement stepreaches the arm step, then it refuses naming Q and arms nothing. - Given a lane at land whose reservation tip is still P's and a ruleset mirror whose
check_response_timeout_minutesis 45, whenimplement stepreaches the arm step, then it renews with a lease of at least 45 minutes plus the margin before arming. - Given a lane at land in a repository with no ruleset mirror, when
implement stepreaches the arm step, then it renews with a day's lease,lease.Max(revised by decision 40 on 2026-10-15; it first read "with the default two-hour lease"). - Given a lane whose record is terminal on the default branch and whose reservation tip is P's, when
implement stepcompletes the landing, then it pushes a tombstone child. - Given a lane whose record is terminal on the default branch and whose reservation tip is Q's, when
implement stepcompletes the landing, then it reports Q and leaves the branch as it is. - Given P holds ten live reservations, when P reserves an eleventh record, then the verb refuses naming the cap and P's live reservations, and pushes nothing.
- Given Q's live reservation names
internal/x/a.goin its footprint, when P reservesiss-Mwith a footprint that includesinternal/x/a.go, then P is refused, naming Q's reservation and the shared path. - Given the zero-network harness and a checkout that has fetched the reservations, when
abcd <record-id>, the board,build <itd-N>andbuild nextrun, then none makes a network request and each shows the view's age. - Given a checkout that has never fetched the reservations, when
abcd <record-id>renders, then it says nothing has been fetched rather than that the item is free. - Given a run whose lane branch is
build/<run>and a reservation branchabcd-reserved/<record-id>, when the land stage removes the lane's branch, then the reservation branch is untouched. - Given P's laptop copy holds a live reservation on
iss-N, when P's cloud session runsabcd reserve iss-N --take-over, then it pushes a fast-forward child naming the cloud copy, and the output names the laptop copy it replaced. - Given P's laptop lane at land whose reservation P's cloud copy has taken over, when
implement stepreaches the arm step on the laptop, then it refuses naming the other copy and arms nothing.
Decisions
Ruled by the product thinker on 2026-10-03:
- Coordination is visible to every participant. A second person now works on the project and the repository is public, so nothing kept only on one computer (the local tier, a note on the desktop, same-account messaging) is the source of truth for who holds what. Such channels may only speed up notice.
- The marker lives on the shared remote and abcd lists it. As first ruled, it was a ref the forge's website does not show; decision 10 narrows that to a branch, which the website does show. It covers people with write access to the remote; outside contributors are the follow-up itd-2610031259176838.
- "A today, B later." This record is settled and built today; the wider session register, itd-2609150819440345, stays a draft for later.
- A cloud session working in parallel is covered. In the product thinker's words: "A cloud session ([a hosted session] running on a hosted machine with its own clone) counts as one more participant that shares only the remote; it reserves, sees and honours claims exactly as a second person's machine does."
Agent-proposed on 2026-10-03, following from decision 2; the transport questions (7, 8 and how each act is pushed) are settled by evidence in review 1, and the rest were confirmed at the planning interview:
- The refusal surface is before the work. The reservation is taken when work starts and refuses an item another person holds;
build nextpasses over a reserved item andbuild <itd-N>refuses it; the land stage re-checks before arming the merge. - Liveness is expiry plus renewal. A reservation lapses two hours after its last renewal unless renewed hourly, its expiry clamped to 24 hours; nothing tries to detect that a session has died.
- The price is one plain push. Seconds, with no pull request and no merge-queue pass. Settled by evidence in review 1; the no-workflow part is a hypothesis (decision 21).
- The lease lives on the shared remote, as a reservation branch, not on any one machine. Settled by evidence in review 1 and ruled in decision 10.
- There is no stamp. No record file carries a reservation field, so the two-release schema migration the 2026-09-09 review required does not arise.
Ruled by the product thinker on 2026-10-03, after review 1:
- The marker is a branch,
refs/heads/abcd-claim/<record-id>as ruled, not a hidden ref, because a hosted cloud session's git proxy accepts pushes of branches only (the 2026-10-03 probe got HTTP 403 onrefs/abcd/*while branch pushes succeeded, and the host's description of the proxy says it rejects branch deletions and anything but a branch). This narrows decision 2's "hidden" wording rather than deleting it. Accepted costs: The branch shows in the forge's branch list, may show a compare-and-pull-request banner, and needs occasional clean-up. One backend only; no hybrid. The prefix is refined by decision 20. - Build everything today. The land fence, the footprint handling, the board and
abcd <record-id>renders and the malformed-reservation handling are all in; no criterion is cut, and the criteria the reviews found defective are fixed instead.
Ruled by the technical facilitator (the person) on 2026-10-03, at the planning interview:
- A reservation push is not remote configuration (pre-pass Q1, invariant 10).
implement stepmay renew a reservation by itself. The change that builds this adds one clause to invariant 10 saying a git branch push is not remote configuration; it is listed under Records to touch at ship, and the invariant is not edited now. - Release is derived (Q3). A reservation stops counting as soon as its record is terminal on the default branch: Readers derive the release, and no post-merge step is needed. The tombstone after the merge is a tidy-up, not the release; expiry stays for abandoned work.
- An unreadable tip keeps its grace (Q4). It is held until its commit date plus two hours, then open to a takeover that names what it replaced, on the precedent of the shipped
implement claim. The principle "unrecognised input never writes" gives way here, because refusing every write on an unreadable tip would let one malformed push hold a record for ever: No verb deletes a reservation branch today and a cloud session cannot delete one, while a takeover is a fast-forward child that keeps the unreadable commit in the history, so nothing is lost or reinterpreted. - A per-person cap of ten live reservations, enforced by the verb (Q5).
- The footprint is required, and overlap refuses (Q6). A reservation without a footprint is refused; one whose footprint shares a path with another live reservation is refused, naming the other reservation, and the agent takes other work. This replaces "overlap is reported, not refused".
- The holder is the person (Q20). Any agent or session of the same person may renew or release that person's reservations; only other people are refused. Two of one person's own sessions are kept apart by the in-run
implement claim, not by this. The opaque session key is therefore dropped from the reservation. Refined by decision 33, which records the copy and refuses the same person's other copy. - A person is their GitHub username (Q22). It is read from the no-reply address, or from a value set once per computer in the user-level
~/.abcd/config.json, never the repository; a laptop and a cloud session then count as one person. A session with neither source is refused. - Two specs under this one intent, both built today (Q7). Spec 1 is reserve, renew, release, the listing and the pre-push hook exemption; spec 2 is the land fence, the cap, the footprint refusal, the renders and the build and drain integration. The intent ships when both close.
- The verbs and the word (reworked from the person's own idea).
abcd build <itd-N>reserves the intent when it starts, andabcd build <itd-N> --releasegives it back;abcd drainreserves each issue it takes; a small top-levelabcd reserve <record-id>andabcd reserve <record-id> --releasecover hand work (refined by decision 39: an agent's verb);abcd peerslists every reservation. The word a person reads is "reservation" everywhere; the record keeps "claim" as the internal name. The branch prefix isabcd-reserved/, the facilitator's preference, so the forge's branch list reads in the same plain word; the record holds no reason forabcd-claim/beyond its first use in decision 10 and the decision log's line of the same day, which this refines.
Decided without a question at the planning interview, each the one defensible answer on the record:
- The no-workflow claim is a hypothesis (Q2) until a real push proves it, and sits in the scope conditions; the 5,000-branch limit cites the forge's repository-limits documentation.
- This record refines the shipped in-run claim (Q8, Q11), written as
refines: [itd-2609221656373558]: The liveness judgement moves into one function both use, and the in-run claim's store and lease shape stay. - The state-of-the-art path is build-native (Q9): The primitive is git itself, a compare-and-swap on a branch, so nothing is adopted or wrapped.
- The register and the outside-contributor follow-up stay separate (Q12, Q13), as already ruled; they will read reservations through the same liveness function.
- The mailbox draft is untouched (Q14). itd-2609151838312703 stays as it is; this record's decision 1 governs any local channel, which may speed up notice and never decide who holds what.
- The resolve-time refusal stays separate (Q15); the two share the "as of last fetch" reader and its age line at build time.
- This record refines
build next(Q16), itd-2609211116005482: The pick's exclusions gain "held by another person's reservation", with the view's age. - Tombstone deletion lives in itd-118 (Q17); both records stay.
- The fence's lease comes from the ruleset mirror (Q18):
check_response_timeout_minutes(45) in.abcd/work/rulesets/main-protection.json; with no mirror, the default lease. - The
.gitrule covers files abcd invents (Q19). "Read it, never write it" forbids abcd's own files under.git/; git's own refs and objects, written bygit fetchandgit commit-tree, are outside it. - Only
origin(Q21). Any other remote layout is refused, naming the remotes found. - No username, no reservation (Q22, with decision 18). A session whose username resolves from neither source is refused.
Ruled by the product thinker on 2026-10-03, during the criteria walk (Product Q15):
- A reservation records the person and the copy; another copy needs
--take-over. This refines decision 17. A reservation records the person and the working copy it was made from, as an opaque working-copy key, never a path or a host. The same copy continues and renews freely, so a fresh agent in the same folder carries on. Another copy of the same person (a second computer or a cloud session) is refused while the reservation is live, with the message "you are already building <id> on another computer, until <expiry>". To continue elsewhere on purpose, the person passes--take-over(onabcd reserveandabcd build), which pushes a child naming the new copy, and the old copy's land fence then refuses its merge. The person remains the unit for the cap, the per-person count and the username identity. Criteria C4 and C20 are revised to match, and C52 and C53 added.
Further decisions of 2026-10-03, at the planning interview:
- The press release is confirmed as written (the product thinker). The take-over detail stays in What's In Scope and the Mechanism.
- The Mechanism is confirmed as written (the product thinker); its sentence on the person's own other copy was added with decision 33.
- The Scope Conditions are confirmed as written (the product thinker).
- Impact is additive (the product thinker).
abcd intent plan --impact additivestamps it; it is not hand-written. - Two specs, through the verbs that exist (decided without a question).
intent planmints spec 1, whose## Stepslist the spec-1 work first and the spec-2 work after; when spec 1's steps land,spec close <spc-N> --remainder <slug>mints spec 2, carrying the unlanded steps; the intent ships when spec 2 closes. This realises decision 19. abcd reserveis an agent's verb (the product thinker, 2026-10-12: "I suggest /abcd:reserve becomes an agent-only verb"). This refines decision 20, which had it cover hand work: the verb is listed in the agents-and-hosts block and hidden from a person's/abcd:menu, it stays runnable from the CLI, and a person sees reservations throughabcd peersand the record and board renders.
Ruled by the product thinker on 2026-10-15, at about 20:15 BST (iss-2610152020234609):
- A pull request waiting on a person holds the reservation for a day, mirror or not. This amends decision 29, whose "with no mirror, the default lease" no longer holds: the land fence renews for
lease.Maxwhether or not a ruleset mirror exists, because a two-hour lapse let a colleague's drain redo work that was only waiting to merge. Criterion C44 is revised to match, and so is the land fence's line under What's In Scope.
Open Questions
None open.
Prior Art
- iss-2609020716570699: The source record, with the 2026-09-01 collisions and the original stamp-and-push remedy.
- itd-2609221656373558 and
internal/core/implement/claim.go: The shipped in-run claim; exclusive create, a two-hour lease bounded by a 24-hourMaxLease, a back-off exit and a grace for an unparseable claim. This record refines it. - itd-2609150819440345, decision 4: The product thinker's choice of transport, recorded on the register before this record was rewritten around it.
- The multi-agent coordination synthesis of 2026-10-03 (
2026-10-03-multi-agent-coordination-sota-and-run-lessons.md): The run lessons and the state of the art. Path: build-native. The primitive is git itself (a create-only compare-and-swap on a ref, as in a sixteen-agent compiler run coordinated through git push conflicts), with leases and fencing re-checked at merge, per-reservation path footprints, and every peer channel treated as untrusted data. - Reviews 1 and 2 of 2026-10-03 (design and feasibility; record discipline), summarised in the synthesis's Review evidence section: The cloud probe, the scratch bare-repository experiments behind the plain-push compare-and-swap, and the hook, fence, clock, duplication and record-placement findings.
- The 2026-09-09 split, in the decision log: The five review findings recorded above.
.githooks/pre-push,internal/core/guard/defaults/guard.jsonandinternal/core/implement/loop/land.go: The hook a reservation push meets, the registry that rules out lease flags, and the land stage the fence sits in.- Brief invariant 7: Implicit operations never touch the network; every reservation push and fetch belongs to a verb that says so.
- Loud staging: Its letter is about unwired code announcing itself; this record applies its spirit, an explicit refusal and never a half-working stub, to a reservation that cannot reach the remote.
- itd-22: The host-profile seam; the reason the host's session registry is never the reservation's home.
Audit Notes
Fidelity review — receipt rcp-0d4b99e46049 (verifier abcd:intent-auditor claude-opus-5-5).
Provenance: abcd:intent-auditor@claude-opus-5-5 · rubric_hash sha256:effa65b3e9e88ff29433b443ec2be159522a8b0b71cf1434526514aa61edb13e · prompt_hash sha256:3a7ad73072cb8c25d771f230d2b81e47224d3a7e97f5f6ad34613b1afcb97185 Input attestations: diff:c43b46bfb47f9b0cd995f0cccabdb403afa556f9..c9d94ebd1c34e4eb40bfa3bb57b8a75da43b6f36@sha256:c41642d22fc6aafc895a5392c95bf8a3793adc34ec437d59dd295b52886917d6; merge:b9bf7bc6ac9a2e48cbdf5ec41c7f44302d9fdaf9@-; merge:5801ad0ab1e6d25f2b268b7c190550022877f116@-; merge:dcdbafa7f40c4e5dad482a4f170c0f2f7eb456fe@-; merge:c9d94ebd1c34e4eb40bfa3bb57b8a75da43b6f36@-; repository:18170d03fab7435c8001999973e4093002226e20@-; intent:.abcd/development/intents/shipped/itd-2609091034175565-nothing-tells-an-agent-that-a-record-it.md@sha256:ee3f93226513c196021578dd5dbcae5bcb77e8a60432626c14083a6f2369a34e; spec:.abcd/development/specs/closed/spc-2610031753359849-nothing-tells-an-agent-that-a-record-it.md@sha256:ac0b2f1396b513a60c335651156965c256298ff30b73c8f13fe6d829f5b287d0; request:.abcd/.work.local/reviews/rcp-0d4b99e46049.request.md@sha256:e6e63c32458c8bc96f53ba8dc2d2bf23ef8a996c14c25db1ebc7b7ce7f3fa757;
Acceptance rollup: MET 52 · MET_WITH_CONCERNS 1 · NOT_MET 0 · INCONCLUSIVE 0
Per-criterion verdicts:
- ac-1 — MET: TestAReservationIsAParentlessEmptyTreeCommitAndLeavesTheCheckoutAlone reserves from a detached HEAD with a dirty and staged tree against a bare origin and asserts a parentless commit on the empty tree at refs/heads/abcd-reserved/iss-5, authored and committed as P's git identity, whose trailers carry iss-5, octo, a.go, claimed 14:00Z, expiring 16:00Z and Assisted-by: abcd:< version>, with HEAD, index and porcelain status unchanged and no local reservation branch. evidence: internal/core/claim/remote_test.go:142 — "p.git("checkout", "-q", "--detach", "HEAD")" evidence: internal/core/claim/remote_test.go:163 — "tree != "4b825dc642cb6eb9a060e54bf8d69288fbee4904"" evidence: internal/core/claim/remote_test.go:171 — ""Assisted-by: abcd:dev"" evidence: internal/core/claim/remote_test.go:176 — "h != head || i != index || s != status"
- ac-2 — MET: Q's reserve over P's live reservation is refused as ErrBackOff naming octo and the effective expiry, origin's tip is unmoved, and even --take-over cannot move another person's live reservation; the CLI maps the back-off to exit 3. evidence: internal/core/claim/remote_test.go:195 — "want := "iss-5 is reserved by octo until 2026-10-03T16:00:00Z; back off and take other work"" evidence: internal/core/claim/remote_test.go:199 — "if got := w.remoteTip("iss-5"); got != tip {" evidence: internal/surface/cli/reserve.go:292 — "return &exitError{Code: 3, Msg: msg}"
- ac-3 — MET: Both shapes of the race are driven (git's client rejecting the advertised tip, and origin's own compare-and-swap refusing a push that landed while Q's was received): lostRace re-fetches, re-judges and refuses with the back-off naming octo, origin's tip stays P's and Q holds no local reservation ref. evidence: internal/core/claim/remote_test.go:254 — "want := "iss-5 was reserved by octo while this push was on its way, until 2026-10-03T16:00:00Z; back off and take other work"" evidence: internal/core/claim/remote_test.go:266 — "if local := q.git("for-each-ref", "refs/heads/abcd-reserved/"); local != "" {" evidence: internal/core/claim/remote.go:298 — "return backOff("%s was reserved by %s while this push was on its way, until %s; back off and take other work","
- ac-4 — MET: A second reserve from the same checkout, which shares its copy key, 30 minutes later is ActRenewed: a child of the first commit whose Abcd-Expires-At moves from 16:00Z to 16:30Z. evidence: internal/core/claim/remote_test.go:283 — "if err != nil || res.Act != ActRenewed {" evidence: internal/core/claim/remote_test.go:287 — "if w.parentOf(tip) != first.Commit || !res.ExpiresAt.After(first.ExpiresAt) ||" evidence: internal/core/claim/remote_test.go:288 — "Abcd-Expires-At: 2026-10-03T16:30:00Z"
- ac-5 — MET: P's release over Q's tip is refused naming quinn and saying nothing was pushed, and origin's tip is unmoved. evidence: internal/core/claim/remote_test.go:325 — "err.Error() != "iss-5 is reserved by quinn, not by you; nothing was pushed"" evidence: internal/core/claim/remote_test.go:328 — "if w.remoteTip("iss-5") != tip {" evidence: internal/core/claim/remote.go:436 — "fmt.Sprintf("%s is reserved by %s, not by you; %s", p.id, j.Holder, nothingPushed))"
- ac-6 — MET: P's release pushes a child of the reservation with the subject 'abcd reservation released' and Abcd-Expires-At equal to Abcd-Released-At (14:20Z); the branch remains (the committed hook refuses any deletion under the prefix), and a second release reports already_released and pushes nothing. evidence: internal/core/claim/remote_test.go:349 — "if tip == "" || w.parentOf(tip) != first.Commit || !strings.HasPrefix(msg, "abcd reservation released: iss-5") ||" evidence: internal/core/claim/remote_test.go:350 — "Abcd-Released-At: 2026-10-03T14:20:00Z" evidence: .githooks/pre-push:432 — "a branch under $row_prefix ends by a push, never a deletion"
- ac-7 — MET: After P's tombstone, Q's reserve is ActReserved as a child of the tombstone commit (the released row of the push table). evidence: internal/core/claim/remote_test.go:372 — "if err != nil || res.Act != ActReserved || w.parentOf(w.remoteTip("iss-5")) != rel.Commit {" evidence: internal/core/claim/remote.go:372 — "return res, m, j.Commit, nil"
- ac-8 — MET: At P's effective expiry plus the five-minute margin by Q's injected clock, Q's reserve is ActTakenOver as a child of P's tip, Replaced naming octo and the 16:00Z expiry; the text render prints the replaced holder and the lapse time. evidence: internal/core/claim/remote_test.go:386 — "w.set(t0.Add(2*time.Hour + TakeoverMargin))" evidence: internal/core/claim/remote_test.go:388 — "res.Replaced.Holder != "octo" ||" evidence: internal/surface/cli/reserve.go:365 — "replaced: %s's reservation, which lapsed at %s"
- ac-9 — MET: One second inside the margin Q's reserve is refused with the back-off naming octo, the lapse and the retry time, and the branch is unmoved. evidence: internal/core/claim/remote_test.go:406 — "w.set(t0.Add(2*time.Hour + TakeoverMargin - time.Second))" evidence: internal/core/claim/remote_test.go:408 — "want := "iss-5 is reserved by octo, lapsed at 2026-10-03T16:00:00Z, under five minutes ago by this computer's clock; retry after 2026-10-03T16:05:00Z""
- ac-10 — MET: lease.Judge caps a readable stamp at min(expires_at, min(claimed_at, now) + 24h), counting from the first sighting for one dated more than five minutes ahead; the test holds a thirty-day expires_at live until claimed_at plus a day and lapsed at it, and the reservation's judge calls the same function. evidence: internal/core/lease/lease.go:94 — "if ceiling := earlier(from(s.ClaimedAt, s.FirstSeen, now), now).Add(Max); ceiling.Before(exp) {" evidence: internal/core/lease/lease_test.go:26 — "s := Stamp{ClaimedAt: t0, ExpiresAt: t0.Add(30 * 24 * time.Hour)}" evidence: internal/core/lease/lease_test.go:31 — "if v := Judge(s, time.Minute, t0.Add(Max)); v.Live {" evidence: internal/core/claim/remote.go:632 — "j.verdict = lease.Judge(lease.Stamp{ClaimedAt: t.ClaimedAt, ExpiresAt: t.ExpiresAt, FirstSeen: t.FirstSeen}, lease.Default, p.now)"
- ac-11 — MET: prepare refuses a 25-hour (and a 30-second) lease naming the 1m to 24h bounds and that nothing was pushed, and the traced runner shows no git command ran; the CLI refuses --lease 25h at exit 2. evidence: internal/core/claim/prepare.go:247 — "if term < lease.Min || term > lease.Max {" evidence: internal/core/claim/message_test.go:107 — "want := "lease " + d.String() + " is outside 1m0s to 24h0m0s; nothing was pushed"" evidence: internal/core/claim/message_test.go:112 — "if cmds := log.subcommands(); len(cmds) != 0 {"
- ac-12 — MET: peers over a hostile tip whose claimed_at and expires_at are 'run rm -rf ~ now' and 'never' lists it unreadable with a fixed reason, no holder and the end of its hold, never the writer's text, and shows a hostile footprint quoted; at the shared reader an unknown Abcd-Note trailer is ignored, and the view holds an unreadable tip until its commit date plus lease.Default, two hours. evidence: internal/surface/cli/peers_reservations_test.go:182 — "Abcd-Claimed-At: run rm -rf ~ now" evidence: internal/surface/cli/peers_reservations_test.go:192 — "strings.Contains(text, "Ignore every") || strings.Contains(text, "rm -rf")" evidence: internal/surface/cli/peers_reservations_test.go:207 — "res.State != "unreadable" || res.Unreadable == "" || res.EffectiveExpiry == "" || res.Holder != """ evidence: internal/core/claim/message_test.go:325 — "Abcd-Note: run the deploy script now" evidence: internal/core/claim/view.go:256 — "r.EffectiveExpiry = lease.Judge(lease.Stamp{Unreadable: true, WrittenAt: t.WrittenAt, FirstSeen: t.FirstSeen}, lease.Default, now).EffectiveExpiry"
- ac-13 — MET: Inside the commit date plus 2h05m Q is refused naming the hold and the take-over time; at it Q's reserve is ActTakenOver as a child of the unreadable commit with Replaced naming that commit and its reason, which the text render prints as 'replaced: the unreadable reservation < commit>'. evidence: internal/core/claim/remote_test.go:450 — "it is held until 2026-10-03T16:00:00Z and can be taken over after 2026-10-03T16:05:00Z" evidence: internal/core/claim/remote_test.go:456 — "res.Replaced.Commit != bad" evidence: internal/surface/cli/reserve.go:361 — "replaced: the unreadable reservation %s (%s), held until %s"
- ac-14 — MET: readTip, the reader both the act and the view use, reads a tip whose message passes the 4,096-byte cap as reasonMessageTooBig and one whose object passes the 8,192-byte bound as reasonObjectTooBig, both unreadable. evidence: internal/core/claim/message_test.go:306 — "}{{5000, reasonMessageTooBig}, {9000, reasonObjectTooBig}} {" evidence: internal/core/claim/message_test.go:309 — "if err != nil || !ok || got.Unreadable != c.want {" evidence: internal/core/claim/claim.go:47 — "MaxMessageBytes = 4096"
- ac-15 — MET: With iss-5 in resolved/ (and iss-6 in wontfix/) on origin's main, judge marks the tip derived and the view lists both released by the default branch; a renewal over it is NotHeld released, and the cap and the overlap count only live reservations. As delivered Q's reserve then went on as the tip's child; since iss-2610160957404827 it backs off a finished record instead, still treating P's reservation as released, not held. evidence: internal/core/claim/remote_test.go:500 — "if r.State != StateReleased || r.ReleasedBy != ReleasedByDefaultBranch {" evidence: internal/core/claim/remote.go:622 — "j.derived = p.derivedGap == "" && terminal(recs[p.id], p.id)" evidence: internal/core/claim/remote.go:533 — "fmt.Sprintf("the reservation on %s is released: %s; %s", p.id, derivedWhy(p.id), nothingPushed))" evidence: internal/core/claim/crowd.go:59 — "if r.State == StateLive && r.Record != p.id {"
- ac-16 — MET: A third clone's peers --json, after P reserved iss-5 and itd-7 (and released iss-8) and Q reserved iss-6, lists all four from refs/remotes/origin/abcd-reserved/* with holder, footprint, effective expiry and state, counts octo's live reservations as 2, stamps the fetch, and prunes a branch origin dropped. evidence: internal/surface/cli/peers_reservations_test.go:110 — ""iss-5": {"octo", "live", []string{"a.go"}}," evidence: internal/surface/cli/peers_reservations_test.go:127 — "if got.LiveByHolder["octo"] != 2 || got.LiveByHolder["quinn"] != 1 || len(got.LiveByHolder) != 2 {" evidence: internal/surface/cli/peers_reservations_test.go:161 — "t.Fatalf("iss-6 is still listed after origin dropped it: %+v", res)" evidence: internal/core/claim/claim.go:43 — "TrackingPrefix = "refs/remotes/" + Remote + "/abcd-reserved/""
- ac-17 — MET: prepare resolves octo from both no-reply forms and writes Abcd-Holder: octo; the resolver reads the no-reply login first. evidence: internal/core/claim/message_test.go:144 — "for _, email := range []string{"1234+octo@users.noreply.github.com", "octo@users.noreply.github.com"} {" evidence: internal/core/claim/message_test.go:150 — "if p.holder != "octo" ||" evidence: internal/core/claim/holder.go:152 — "if u := NoReplyUsername(who.Email); u != "" && validUsername(u) {"
- ac-18 — MET: With user.email pat@example.com and ~/.abcd.noindex/config.json naming octo, prepare's holder is octo; the machine key is the resolver's second source, read from the machine layer only. evidence: internal/core/claim/message_test.go:160 — "r := newCheckout(t, "pat@example.com")" evidence: internal/core/claim/message_test.go:163 — "if err != nil || p.holder != "octo" {" evidence: internal/core/claim/holder.go:155 — "u, err := machineHolder(o.home())"
- ac-19 — MET_WITH_CONCERNS: Realised as delivered: at c9d94ebd1, the end of the delivered range, a reserve with neither source was refused naming user.email and the machine config.json and saying the repository's own configuration is never read, and the test asserted that over a repository naming mallory. Concern: PR #1037 (iss-2610152046379890, merged after this delivery under the 'no manual steps' ruling) added the session's forge token as a third source, so at HEAD such a reserve takes the holder from gh when it answers, and otherwise refuses naming only the token's failure class, never the two user-level sources; the repository's value is still never used and nothing is pushed, but the intent's In Scope and decision 32 still state the two-source refusal. evidence: c9d94ebd1:internal/core/claim/message_test.go:129 — "want := "cannot tell which GitHub user you are: user.email is not a GitHub no-reply address, and " + abcdhome.Display("config.json") + " sets no " +" evidence: c9d94ebd1:internal/core/claim/holder.go:116 — "return "", refuse("cannot tell which GitHub user you are: user.email is not a GitHub no-reply address, and %s sets no %s; "+" evidence: internal/core/claim/holder.go:170 — "return tokenHolder(root, o)" evidence: internal/core/claim/message_test.go:178 — "want := "could not work out whose GitHub account this session uses: " + errForgeNoToken.Error() + "; nothing was sent or pushed"" evidence: internal/core/claim/holder_test.go:219 — "func TestRepoConfigCannotBlockTokenFallback(t *testing.T) {"
- ac-20 — MET: A laptop clone resolving octo through the machine config holds iss-5; a cloud clone with octo's no-reply address is refused with the back-off and 'you are already building iss-5 on another computer, until 2026-10-03T16:00:00Z', followed by the --take-over hint, and the branch is unmoved.
evidence: internal/core/claim/remote_test.go:514 — "laptop := w.clone("pat@example.com", machineConfig(t,
{"reservation": {"github_username": "octo"}}))" evidence: internal/core/claim/remote_test.go:521 — "want := "you are already building iss-5 on another computer, until 2026-10-03T16:00:00Z; pass --take-over to continue here"" evidence: internal/core/claim/remote.go:403 — "return Result{}, message{}, "", backOff("you are already building %s on another computer, until %s; pass --take-over to continue here"," - ac-21 — MET: With its only remote renamed upstream the act refuses naming upstream as the remote found, with no remote it says so, and nothing reaches origin. evidence: internal/core/claim/remote_test.go:564 — "want := "reservations live on the remote named origin, and this checkout's remotes are: upstream; nothing was pushed"" evidence: internal/core/claim/remote_test.go:572 — "if w.remoteTip("iss-5") != "" {"
- ac-22 — MET: An origin pointing at a path that does not exist fails at the fetch quoting git's reason (the gone.git path) and ending 'no reservation was made'; no ref and no fetch stamp is left. evidence: internal/core/claim/remote_test.go:583 — "if !errors.Is(err, ErrRefused) || !strings.HasPrefix(err.Error(), "could not read the reservations from origin: ") ||" evidence: internal/core/claim/remote_test.go:584 — "!strings.Contains(err.Error(), "gone.git") || !strings.HasSuffix(err.Error(), "; no reservation was made") {" evidence: internal/core/claim/remote_test.go:587 — "if refs := p.git("for-each-ref", "refs/heads/abcd-reserved/", TrackingPrefix); refs != "" {"
- ac-23 — MET: A bare origin whose pre-receive refuses refs/heads/abcd-reserved/* makes the act fail as 'origin refused the reservation push:' quoting the hook's own message and ending 'no reservation was made', leaving no ref. evidence: internal/core/claim/remote_test.go:607 — "if !errors.Is(err, ErrRefused) || !strings.HasPrefix(err.Error(), "origin refused the reservation push: ") ||" evidence: internal/core/claim/remote_test.go:608 — "!strings.Contains(err.Error(), "the proxy refuses abcd-reserved pushes") || !strings.HasSuffix(err.Error(), "; no reservation was made") {" evidence: internal/core/claim/remote_test.go:611 — "if w.remoteTip("iss-5") != "" || p.git("for-each-ref", "refs/heads/abcd-reserved/") != "" {"
- ac-24 — MET: The fixture installs the committed .githooks/pre-push and pushes a parentless empty-tree reserve, a renewal, a take-over and a tombstone with git push --porcelain, the verb's own push shape (pushTo), with hooks on and no receipt; each lands and the preflight never runs. The verb makes that push through gitutil.Net, which runs the repository's hooks, with no --no-verify. evidence: internal/surface/cli/githook_prepush_reserve_test.go:60 — "if out, err := c.push("--porcelain", "origin", next+":"+reservedRef); err != nil {" evidence: internal/surface/cli/githook_prepush_test.go:69 — "for _, rel := range []string{".githooks/pre-push", "scripts/preflight-receipt.sh"} {" evidence: internal/core/claim/remote.go:256 — "return []string{"push", "--porcelain", Remote, sha + ":" + ref}" evidence: internal/gitutil/net.go:39 — "The repository's hooks run (the pre-push gate is the point)"
- ac-25 — MET: Ordinary history pushed under the prefix, an empty-tree tip over a non-empty commit, and a non-empty-tree child of a reservation each meet 'no passing preflight'; reservation_shape requires every commit in the pushed range to have the empty tree. evidence: internal/surface/cli/githook_prepush_reserve_test.go:82 — "if err == nil || !strings.Contains(out, "no passing preflight") {" evidence: .githooks/pre-push:195 — "reservation_shape() {" evidence: .githooks/pre-push:198 — "only_empty_trees "$local_sha""
- ac-26 — MET: A forced push replacing a reservation tip with an unrelated empty-tree chain is refused as non-fast-forward and origin keeps the first reservation; the guard runs before the receipt-less exemption for every branch. evidence: internal/surface/cli/githook_prepush_reserve_test.go:128 — "if err == nil || !strings.Contains(out, "non-fast-forward") {" evidence: .githooks/pre-push:448 — "echo "pre-push: refusing non-fast-forward (force) push to '$branch'." >&2" evidence: .githooks/pre-push:456 — "if receiptless_row "$branch" && shape_check "$row_shape" "$remote_sha" "$local_sha"; then"
- ac-27 — MET: abcd reserve iss-5 with no --footprint exits 2 with the footprint refusal and origin holds nothing for iss-5. evidence: internal/surface/cli/reserve_surface_test.go:119 — "want := "abcd reserve: a reservation names the files the work will change: pass --footprint < path> once per path; nothing was pushed"" evidence: internal/surface/cli/reserve_surface_test.go:123 — "if tip := w.remoteTip(t, "iss-5"); tip != "" {" evidence: internal/core/claim/prepare.go:230 — "if !release && !req.Release && !req.Renew && len(req.Footprint) == 0 {"
- ac-28 — MET: The written commit object carries no home, checkout path or host and is authored by the person's identity; the message passes the outbound policy, which refuses one carrying a session URL; reserve's text, --json and refusal renders under a temporary HOME carry no home path or host, and peers' failure render is checked for home paths. evidence: internal/core/claim/message_test.go:412 — "for _, leak := range []string{home, r.Root(), host} {" evidence: internal/core/claim/message_test.go:420 — "if err := checkMessage(r.Root(), p.reservation().text()); err != nil {" evidence: internal/surface/cli/reserve_surface_test.go:263 — "if len(host) >= 4 && strings.Contains(out, host) {" evidence: internal/surface/cli/peers_reservations_test.go:254 — "noHomePath(t, w.home, raw+stderr)"
- ac-29 — MET: A footprint carrying ; | & $( ) backticks, a colon, an asterisk and quotes is written and read back verbatim, and the traced git runner shows it in no argument; it travels only in the message on standard input. evidence: internal/core/claim/message_test.go:381 — "if log.anyArgContains("$(e)") || log.anyArgContains("refs/heads/main*") {" evidence: internal/core/claim/message_test.go:386 — "if err != nil || !ok || got.Unreadable != "" || !slices.Equal(got.Footprint, []string{hostile, "ok.go"}) {"
- ac-30 — MET: Two hundred long footprint paths are refused with the cap message after only config reads, and writeCommit measures again and refuses without writing an object. evidence: internal/core/claim/message_test.go:246 — "bytes, over the 4096-byte cap: name fewer or shorter footprint paths; nothing was pushed" evidence: internal/core/claim/message_test.go:265 — "if after := r.Git("count-objects"); after != before {"
- ac-31 — MET: Eleven malformed arguments (wrong case, other families, padding, path and refspec shapes, an escape) are refused as not a reservable id before any git command; reservableID admits only CitedIDRe ids canonicalised to iss- or itd- (and, since a later spec, a rel-vX.Y.Z subject) before refName is formed. evidence: internal/core/claim/message_test.go:122 — "for _, arg := range []string{"", "ISS-5", "adr-5", "spc-5", "iss-", "iss-0", "itd-5 ", "iss-5/../main"," evidence: internal/core/claim/message_test.go:132 — "if cmds := log.subcommands(); len(cmds) != 0 {" evidence: internal/core/claim/claim.go:124 — "if !recordid.CitedIDRe.MatchString(arg) {"
- ac-32 — MET: The in-run claim's judge calls lease.Judge, as the reservation's judge does, and its DefaultLease, MinLease and MaxLease are the lease package's; TestClaimJudgesThroughTheSharedLease adds the shared clamp, and the in-run claim tests were last edited on 2026-10-04, before the 2026-10-12 refactor, so they run unchanged. evidence: internal/core/implement/claim.go:427 — "return lease.Judge(lease.Stamp{ClaimedAt: c.ClaimedAt, ExpiresAt: c.ExpiresAt}, UnreadableClaimGrace, now)" evidence: internal/core/claim/remote.go:632 — "j.verdict = lease.Judge(lease.Stamp{ClaimedAt: t.ClaimedAt, ExpiresAt: t.ExpiresAt, FirstSeen: t.FirstSeen}, lease.Default, p.now)" evidence: internal/core/implement/lease_test.go:25 — "func TestClaimJudgesThroughTheSharedLease(t *testing.T) {" evidence: git log -- internal/core/implement/claim_test.go — "a1ed114bd Sun Oct 4 08:59:13 2026 +0100 feat!: rename abcd's home to ~/.abcd.noindex and stop while ~/.abcd stands"
- ac-33 — MET: A reserve, a renewal and a release authored by a no-reply identity and carrying Assisted-by: abcd:< version> pass scripts/check-attribution.sh commits over their range with 'check-attribution: clean'. evidence: internal/core/claim/gates_test.go:65 — "cmd := exec.Command("bash", filepath.Join(root, "scripts", "check-attribution.sh"), "commits", "origin/main", tip)" evidence: internal/core/claim/gates_test.go:68 — "if out, err := cmd.CombinedOutput(); err != nil || !strings.Contains(string(out), "check-attribution: clean") {"
- ac-34 — MET: Start on a planned intent whose spec names packages internal/widget and commands/ pushes P's reservation with footprint [internal/widget, commands], records reserved_at and a reservation note, and a resume renews it as the tip's child; abcd build --json names the reservation it took. evidence: internal/core/implement/loop/build_reserve_test.go:220 — "if fp := footprintOf(t, bare, "itd-10"); !slices.Equal(fp, []string{"internal/widget", "commands"}) {" evidence: internal/core/implement/loop/build_reserve_test.go:224 — "if tip.holder != "octo" || tip.released {" evidence: internal/surface/cli/build_reserve_surface_test.go:39 — "if !strings.HasPrefix(started.Reservation, "reserved itd-8 on origin for this copy until ") ||"
- ac-35 — MET: A spec with no ## Footprint refuses the start at the reservation stage, not as contention, naming spc-1 and the missing section and that nothing was started; no run tier is created and origin holds no reservation. evidence: internal/core/implement/loop/build_reserve_test.go:292 — "want := "spc-1 has no footprint under ## Footprint to reserve itd-10 with; fill its packages line, then run abcd build again; nothing was started"" evidence: internal/core/implement/loop/build_reserve_test.go:297 — "if tip := originReservation(t, bare, reservedRef); tip.commit != "" {"
- ac-36 — MET: abcd build itd-8 --release after a build pushes a tombstone child of the run's reservation, says the run's state is unchanged, leaves the state file byte-identical, and a second release reports already_released. evidence: internal/surface/cli/build_reserve_surface_test.go:54 — "if code != 0 || !strings.Contains(out, "released the reservation on itd-8") || !strings.Contains(out, "the run's state is unchanged") {" evidence: internal/surface/cli/build_reserve_surface_test.go:61 — "if parent := gitCmd(t, w.origin, "rev-parse", tomb+"^"); parent != reserved {" evidence: internal/surface/cli/build_reserve_surface_test.go:64 — "if after, err := os.ReadFile(statePath); err != nil || string(after) != string(before) {"
- ac-37 — MET: The drain reserves the issue whose lane it opens, as P from this copy, with its found_at path (commands/drain.md, and AGENTS.md from AGENTS.md:3 with the locator dropped), records it in the run, and reserves no issue whose lane has not opened. evidence: internal/core/implement/loop/drain_reserve_test.go:69 — "if fp := footprintOf(t, bare, secondIssue); !slices.Equal(fp, []string{"commands/drain.md"}) {" evidence: internal/core/implement/loop/drain_reserve_test.go:96 — "if fp := footprintOf(t, bare, eligibleIssue); !slices.Equal(fp, []string{"AGENTS.md"}) {"
- ac-38 — MET: With Q's live reservation on itd-10 fetched into P's view, the candidates exclude itd-10 at the peers check with a reason naming quinn, the expiry and 'as of the last fetch under a minute ago'; the CLI's build next --json excludes the reserved intent from disk alone.
evidence: internal/core/implement/loop/build_reserve_test.go:71 — "if e.Check != CheckPeers || !strings.Contains(e.Reason, "reserved by quinn until ") ||"
evidence: internal/core/implement/loop/build_reserve_test.go:72 — "!strings.Contains(e.Reason, "as of the last fetch under a minute ago") {"
evidence: internal/surface/cli/reservation_render_test.go:183 — "if code != 2 || !strings.Contains(raw,
"id": "itd-8") || !strings.Contains(raw, "reserved by octo until ") {" - ac-39 — MET: Start over Q's fetched reservation is refused at the check stage, peers check, as contention, naming quinn and the view's age, never suggesting --take-over, creating no run and leaving origin's tip unmoved. evidence: internal/core/implement/loop/build_reserve_test.go:123 — "if r.Stage != "check" || r.Check != CheckPeers || !r.Contention {" evidence: internal/core/implement/loop/build_reserve_test.go:126 — "if !strings.Contains(r.Reason, "reserved by quinn until ") || !strings.Contains(r.Reason, "as of the last fetch") {"
- ac-40 — MET: A step 61 minutes into the run's two-hour reservation pushes a child of the tip with a fresh two-hour lease, logs reservation_renewed with the new expiry in the run record, and still performs its stage. evidence: internal/core/implement/loop/reservation_test.go:176 — "reserveRun(t, f.repo, f.runID, "itd-10", now.Add(-61*time.Minute), "internal/widget")" evidence: internal/core/implement/loop/reservation_test.go:181 — "if after.commit == before.commit || after.parent != before.commit {" evidence: internal/core/implement/loop/reservation_test.go:187 — "if note := recordNote(t, f.repo, f.runID, StageReservationRenewed); !strings.Contains(note, "itd-10") || !strings.Contains(note, after.expires.Format(time.RFC3339)) {"
- ac-41 — MET: A step 30 minutes into the lease leaves origin's tip unchanged and logs no reservation stage. evidence: internal/core/implement/loop/reservation_test.go:199 — "reserveRun(t, f.repo, f.runID, "itd-10", now.Add(-30*time.Minute), "internal/widget")" evidence: internal/core/implement/loop/reservation_test.go:202 — "if after := originReservation(t, f.bare, reservedRef); after.commit != before.commit {"
- ac-42 — MET: After P's lapsed reservation is taken over by Q, the step at the arming is refused as contention naming quinn's reservation and expiry, no pr merge runs, the lane stays unarmed, and Q's tip is untouched; landFence runs at the top of landArm. evidence: internal/core/implement/loop/reservation_test.go:272 — "want := "the reservation on itd-10 is now quinn's, until " + qTip.expires.Format(time.RFC3339) + ", so this lane does not arm its merge"" evidence: internal/core/implement/loop/reservation_test.go:249 — "if strings.Contains(f.ghLog(t), "pr merge") {" evidence: internal/core/implement/loop/land.go:722 — "holds, notice, err := landFence(c, *lane, fenceLease)"
- ac-43 — MET: Under a mirror whose queue timeout is 45 minutes (and 150, 3000), the fence renews P's reservation as the tip's child with a 24-hour lease, well past 45 minutes plus the margin, before the rule decides whether to arm; since the 2026-10-14 review the fence takes lease.Max under any mirror rather than the timeout plus a margin. evidence: internal/core/implement/loop/reservation_test.go:349 — "{"45", 24 * time.Hour}," evidence: internal/core/implement/loop/reservation_test.go:361 — "if after.parent != before.commit || after.expires.Sub(after.claimed) != tc.lease || after.claimed.Before(now) {" evidence: internal/core/implement/loop/land.go:722 — "holds, notice, err := landFence(c, *lane, fenceLease)"
- ac-44 — MET: With no ruleset mirror the fence renews for 24 hours, lease.Max, and leaves the pull request open for a person (decision 40, delivered in PR #1034). evidence: internal/core/implement/loop/parked_reserve_test.go:192 — ""no mirror":" evidence: internal/core/implement/loop/parked_reserve_test.go:201 — "if tip.expires.Sub(tip.claimed) != 24*time.Hour {" evidence: internal/core/implement/loop/reservation.go:216 — "const fenceLease = lease.Max"
- ac-45 — MET: The fixture's lane commits the spec close (Delivers: itd-10) and its head is pushed to origin's main, so the intent is shipped there; the next step marks the lane done and pushes a tombstone child of the fenced reservation, its note saying it released it. evidence: internal/core/implement/loop/reservation_test.go:459 — "if !tomb.released || tomb.parent != held.commit || tomb.holder != "octo" {" evidence: internal/core/implement/loop/reservation_test.go:462 — "if note := landNote(t, f); !strings.Contains(note, "released the reservation on itd-10") {" evidence: internal/core/implement/loop/land_test.go:230 — "f.repo.Git("push", "-q", "origin", head+":refs/heads/main")" evidence: internal/core/implement/loop/land_test.go:260 — ""Delivers: itd-10""
- ac-46 — MET: Q takes the fenced reservation over past its day plus the margin; after the merge the step completes the landing, leaves Q's tip as it stands, and its note names the reservation left with quinn. evidence: internal/core/implement/loop/reservation_test.go:509 — "if got := originReservation(t, f.bare, reservedRef); got.commit != qTip.commit {" evidence: internal/core/implement/loop/reservation_test.go:512 — "if note := landNote(t, f); !strings.Contains(note, "left the reservation on itd-10") || !strings.Contains(note, "quinn") {"
- ac-47 — MET: With ten live reservations an eleventh is refused (ErrRefused, a CapError holding the ten) naming the cap and iss-1..iss-10, and origin gets nothing, while Q is not refused by P's ten; the cap is MaxLive, ten, unless the person's own machine setting reservation.max_live (added after this delivery) changes it. evidence: internal/core/claim/capoverlap_test.go:34 — "want := "you hold 10 live reservations, the most one person may hold: iss-1, iss-10, iss-2, iss-3, iss-4, iss-5, " +" evidence: internal/core/claim/capoverlap_test.go:46 — "t.Fatalf("a reservation over the cap reached origin: %s", tip)" evidence: internal/core/claim/crowd.go:17 — "const MaxLive = 10"
- ac-48 — MET: With iss-1 reserved by octo on commands/reserve.md and internal/core/claim, quinn's reserves sharing an equal path, a file beneath a reserved directory or a directory over a reserved file are each refused with the back-off naming the shared path quoted, iss-1, octo and its expiry, and nothing reaches origin.
evidence: internal/core/claim/capoverlap_test.go:130 — "want :=
"+ c.path +" is in the footprint of iss-1+ held" evidence: internal/core/claim/capoverlap_test.go:134 — "if tip := w.remoteTip(c.id); tip != "" {" evidence: internal/core/claim/crowd.go:112 — "return backOff("%s is in the footprint of %s, reserved by %s until %s%s; take other work; %s"," - ac-49 — MET: After Q's fetch, origin is moved away, HTTP requests are counted and gh is stubbed: abcd iss-5 and the board show octo's reservation 'as of the last fetch', build itd-8 is refused at the peers check (exit 3) and build next excludes itd-8, a reservation made after the fetch is not seen, the fetch stamp is unmoved, and no request or gh call is made; build next's age wording is asserted at the loop level (ac-38).
evidence: internal/surface/cli/reservation_render_test.go:101 — "if err := os.Rename(w.origin, w.origin+".away"); err != nil {"
evidence: internal/surface/cli/reservation_render_test.go:131 — "!strings.HasPrefix(r.Text, "live, reserved by octo until ") || !strings.Contains(r.Text, ", as of the last fetch ") {"
evidence: internal/surface/cli/reservation_render_test.go:166 — "if !strings.HasPrefix(reserved, "reserved by octo until ") || !strings.Contains(reserved, ", as of the last fetch ") {"
evidence: internal/surface/cli/reservation_render_test.go:178 — "if code != 3 || !strings.Contains(raw,
"check": "peers") || !strings.Contains(raw, "reserved by octo until ") ||" evidence: internal/surface/cli/reservation_render_test.go:190 — "if n := atomic.LoadInt32(&hits); n != 0 {" - ac-50 — MET: With no fetch stamped in a checkout that has an origin, Describe renders the reservation as 'unknown, nothing has been fetched here;
abcd peersfetches', never none, and a tip a plain git fetch left is shown with its age unknown. evidence: internal/core/record/reservation_test.go:54 — "d.Reservation.Text != "unknown, nothing has been fetched here;abcd peersfetches" {" evidence: internal/core/claim/render.go:137 — "return "as of a fetch of unknown age"" - ac-51 — MET: After the landing removes the lane's build/ branch, origin still holds abcd-reserved/itd-10, the checkout's remote-tracking ref for it remains, and no local branch exists under the reservation prefix. evidence: internal/core/implement/loop/reservation_test.go:476 — "if got := f.repo.Git("for-each-ref", "refs/heads/"+l.Branch); got != "" {" evidence: internal/core/implement/loop/reservation_test.go:479 — "if got := originReservation(t, f.bare, reservedRef); got.commit == "" {" evidence: internal/core/claim/claim.go:41 — "// The prefix is disjoint from the implement loop's lane prefix build/."
- ac-52 — MET: The cloud copy's reserve with TakeOver is ActTakenOver as a child of the laptop's commit whose Abcd-Copy is the cloud copy's key, Replaced naming octo's other copy; the CLI prints 'took over iss-5' and 'your copy on another computer' and never a copy key. evidence: internal/core/claim/remote_test.go:545 — "if err != nil || res.Act != ActTakenOver || res.Replaced == nil || res.Replaced.Copy != CopyAnother || res.Replaced.Holder != "octo" {" evidence: internal/core/claim/remote_test.go:553 — "if w.parentOf(tip) != first.Commit || !strings.Contains(w.remoteMessage(tip), "Abcd-Copy: "+cloudKey) {" evidence: internal/surface/cli/reserve_surface_test.go:228 — "if code != 0 || !strings.Contains(text, "took over iss-5") || !strings.Contains(text, "your copy on another computer") {"
- ac-53 — MET: After P's second computer takes the reservation over with TakeOver, the laptop lane's step at the arming is refused as contention, 'moved to your copy on another computer', and nothing is armed. evidence: internal/core/implement/loop/reservation_test.go:289 — "if res, err := claim.Reserve(p2, claim.Request{Record: "itd-10", Footprint: []string{"internal/widget"}, TakeOver: true}, clockAt(now.Add(-time.Minute))); err != nil || res.Act != claim.ActTakenOver {" evidence: internal/core/implement/loop/reservation_test.go:293 — "if want := "the reservation on itd-10 moved to your copy on another computer, so this lane does not arm its merge here"; r.Reason != want {"
Gap audit:
- honoured:
- When an agent starts on an item, abcd reserves it for the person the agent works for: a build at its start, a drain as each lane opens. evidence: internal/core/implement/loop/build_reserve_test.go:220 — "if fp := footprintOf(t, bare, "itd-10"); !slices.Equal(fp, []string{"internal/widget", "commands"}) {" evidence: internal/core/implement/loop/drain_reserve_test.go:69 — "if fp := footprintOf(t, bare, secondIssue); !slices.Equal(fp, []string{"commands/drain.md"}) {"
- The reservation is a branch on the shared remote that nobody merges, a chain of empty-tree commits that leaves the caller's HEAD, index and working tree alone. evidence: internal/core/claim/claim.go:42 — "RefPrefix = "refs/heads/abcd-reserved/"" evidence: internal/core/claim/remote_test.go:163 — "tree != "4b825dc642cb6eb9a060e54bf8d69288fbee4904"" evidence: internal/core/claim/remote_test.go:176 — "h != head || i != index || s != status"
- If someone else already holds the item, or the files the work will touch, abcd says who and until when, and the agent takes something else.
evidence: internal/core/claim/remote_test.go:195 — "want := "iss-5 is reserved by octo until 2026-10-03T16:00:00Z; back off and take other work""
evidence: internal/core/claim/capoverlap_test.go:130 — "want :=
"+ c.path +" is in the footprint of iss-1+ held" evidence: internal/core/implement/loop/build_reserve_test.go:71 — "if e.Check != CheckPeers || !strings.Contains(e.Reason, "reserved by quinn until ") ||" - Everyone who works on the project sees the reservation, from any computer or clone, as last fetched and with the view's age. evidence: internal/surface/cli/peers_reservations_test.go:127 — "if got.LiveByHolder["octo"] != 2 || got.LiveByHolder["quinn"] != 1 || len(got.LiveByHolder) != 2 {" evidence: internal/surface/cli/reservation_render_test.go:131 — "!strings.HasPrefix(r.Text, "live, reserved by octo until ") || !strings.Contains(r.Text, ", as of the last fetch ") {"
- A reservation is renewed at half its lease while the work goes on, and one left behind lapses and can be taken over. evidence: internal/core/implement/loop/reservation_test.go:181 — "if after.commit == before.commit || after.parent != before.commit {" evidence: internal/core/claim/remote_test.go:388 — "res.Replaced.Holder != "octo" ||"
- It stops counting the moment the work is merged: a record terminal on origin's default branch releases its reservation, with no step needed. evidence: internal/core/claim/remote_test.go:500 — "if r.State != StateReleased || r.ReleasedBy != ReleasedByDefaultBranch {"
- The one that loses finds out in seconds: a lost race is refused naming the winner and writes nothing. evidence: internal/core/claim/remote_test.go:254 — "want := "iss-5 was reserved by octo while this push was on its way, until 2026-10-03T16:00:00Z; back off and take other work""
- Moving a piece of work from one of a person's copies to another is one flag, --take-over. evidence: internal/surface/cli/reserve_surface_test.go:228 — "if code != 0 || !strings.Contains(text, "took over iss-5") || !strings.Contains(text, "your copy on another computer") {" evidence: internal/core/claim/remote_test.go:553 — "if w.parentOf(tip) != first.Commit || !strings.Contains(w.remoteMessage(tip), "Abcd-Copy: "+cloudKey) {"
- One liveness judgement serves the in-run implement claim and the reservation. evidence: internal/core/implement/claim.go:427 — "return lease.Judge(lease.Stamp{ClaimedAt: c.ClaimedAt, ExpiresAt: c.ExpiresAt}, UnreadableClaimGrace, now)" evidence: internal/core/claim/remote.go:632 — "j.verdict = lease.Judge(lease.Stamp{ClaimedAt: t.ClaimedAt, ExpiresAt: t.ExpiresAt, FirstSeen: t.FirstSeen}, lease.Default, p.now)"
- The pre-push hook exempts reservation chains narrowly and still refuses a non-fast-forward push. evidence: .githooks/pre-push:195 — "reservation_shape() {" evidence: internal/surface/cli/githook_prepush_reserve_test.go:128 — "if err == nil || !strings.Contains(out, "non-fast-forward") {"
- diverged:
- A reservation lasts two hours: once a lane reaches land the fence renews it for a day, lease.Max, mirror or not (decision 40, amending decision 29 and C44 after a two-hour lapse let a colleague's drain redo work only waiting to merge), and the queue's check timeout is no longer read. evidence: internal/core/implement/loop/reservation.go:216 — "const fenceLease = lease.Max" evidence: internal/core/implement/loop/parked_reserve_test.go:201 — "if tip.expires.Sub(tip.claimed) != 24*time.Hour {" evidence: .abcd/work/issues/resolved/iss-2610152020234609-fence-holds-a-day-without-a-mirror.md:19 — "a pull request the land step leaves waiting on a person holds its reservation on the shared remote for a day, mirror or not"
- A person working by hand reserves with one short command: decision 39 made abcd reserve an agent's verb, kept off a person's /abcd: menu though still runnable from the CLI, and a person sees reservations on the board and a record's page. evidence: commands/reserve.md:6 — "user-invocable: false"
- With neither a no-reply user.email nor the machine key, every write is refused naming both sources (In Scope, decision 32, C19): after this delivery the act asks the session's forge token third, reserves under its account when gh answers, and otherwise refuses naming only the token's failure; the intent's text was not amended. evidence: internal/core/claim/holder.go:170 — "return tokenHolder(root, o)" evidence: internal/core/claim/message_test.go:178 — "want := "could not work out whose GitHub account this session uses: " + errForgeNoToken.Error() + "; nothing was sent or pushed"" evidence: internal/core/claim/holder_test.go:219 — "func TestRepoConfigCannotBlockTokenFallback(t *testing.T) {"
- A cloud session reserves, sees and honours reservations exactly as a second person's machine does (decision 4): as delivered it reserved only once its clone's identity named the person; a hosted clone carries the host's vendor identity (the 2026-10-15 probe) and was refused for want of a username until the token fallback merged after this delivery (PR #1037). evidence: c9d94ebd1:internal/core/claim/holder.go:116 — "return "", refuse("cannot tell which GitHub user you are: user.email is not a GitHub no-reply address, and %s sets no %s; "+" evidence: .abcd/work/issues/resolved/iss-2610152046379890-a-hosted-cloud-session-s-clone-carries.md:21 — "A hosted cloud session's clone carries the host's default vendor identity as its git author, not the person's"
- The land fence catches a reservation lost mid-work before the merge is armed (Mechanism): it does, but as delivered it ran only at arming, after the lane's push and pull request, so a drain lane paused past its lease and taken over by another account became a duplicate public pull request; a push fence and drain-move renewals were added after this delivery (iss-2610161015393523, major). evidence: .abcd/work/issues/resolved/iss-2610161015393523-a-drain-s-in-progress-lane-is-renewed.md:20 — "the land fence runs only at arming, after the push and the pull request, so the duplicate becomes a public pull request" evidence: internal/core/implement/loop/land.go:722 — "holds, notice, err := landFence(c, *lane, fenceLease)"
- The footprint refusal keeps apart work on different items that would touch the same files (Mechanism): as delivered, directory footprints turned away whole packages of work, one account holding internal/core/implement/loop blocking 32 eligible issues, and a same-person renewal had to drop files it changes; later records narrowed the check (iss-2610152125101468). evidence: .abcd/work/issues/resolved/iss-2610152125101468-a-reservation-footprint-can-hold-a-whole.md:22 — "while one account holds internal/core/implement/loop, every other account is blocked from all 32 eligible issues under it" evidence: .abcd/work/issues/resolved/iss-2610152125101468-a-reservation-footprint-can-hold-a-whole.md:20 — "the messaging reservation's renewal was refused twice and had to leave out .githooks/pre-push, the ruleset mirror and the generated CLI reference, which it does change"
- A per-person cap of ten live reservations (decision 15): since iss-2610170717515674 the cap is the person's own machine setting reservation.max_live, 1 to 100, ten when unset. evidence: internal/core/claim/crowd.go:13 — "// MaxLive is the most live reservations one person may hold unless their" evidence: internal/core/claim/maxlive.go:12 — "reservation.max_live in ~/.abcd.noindex/config.json, the machine layer"
- missing: (none)
Scope-condition dispositions:
- cond-2610031753354810 — survived: Reservations live on origin alone: a checkout without a remote named origin is refused naming the remotes found, and a remote that refuses the push, standing for a missing write permission, makes no reservation. evidence: internal/core/claim/claim.go:38 — "Remote = "origin"" evidence: internal/core/claim/remote_test.go:564 — "want := "reservations live on the remote named origin, and this checkout's remotes are: upstream; nothing was pushed"" evidence: internal/core/claim/remote_test.go:607 — "!strings.HasPrefix(err.Error(), "origin refused the reservation push: ")"
- cond-2610031753352651 — survived: Every act is one plain fast-forward branch push under refs/heads/abcd-reserved/; the forge's ruleset there refuses only deletion and non-fast-forward, and this checkout's view as last fetched holds 138 reservation branches pushed to the real origin. The hosted proxy's acceptance still rests on the 2026-10-03 probe; nothing in the repository re-observes it. evidence: internal/core/claim/remote.go:256 — "return []string{"push", "--porcelain", Remote, sha + ":" + ref}" evidence: .abcd/work/rulesets/reservations-protection.json:24 — ""type": "non_fast_forward"" evidence: refs/remotes/origin/abcd-reserved/iss-2610100649471088 — "abcd reservation: iss-2610100649471088"
- cond-2610031753357820 — untested: Every on: push trigger still filters on main or v* tags, and the one workflow added since, the reservation sweep, runs on a schedule and by hand, so the argument still stands; but nothing in the delivery or the repository records a real reservation push and the runs it did or did not start, which the condition names as its proof. evidence: .github/workflows/auto-release.yml:39 — "branches: [main]" evidence: .github/workflows/attribution.yml:52 — "branches: [main]" evidence: .github/workflows/release.yml:29 — "- 'v*'" evidence: .github/workflows/reservation-sweep.yml:27 — "schedule:"
- cond-2610031753357092 — survived: Each record has one reservation branch, a release adds a tombstone and deletes nothing, and origin as last fetched holds 150 branches, 138 of them reservations, far under 5,000; since this delivery a daily sweep (iss-2610142200389875, not itd-118) deletes spent ones. evidence: internal/core/claim/remote_test.go:349 — "if tip == "" || w.parentOf(tip) != first.Commit || !strings.HasPrefix(msg, "abcd reservation released: iss-5") ||" evidence: refs/remotes/origin/abcd-reserved/iss-2610100649471088 — "abcd reservation: iss-2610100649471088" evidence: .github/workflows/reservation-sweep.yml:28 — "- cron: "37 4 * * *""
- cond-2610031753351641 — narrowed: Reservations stay advisory among writers: the holder is a self-declared trailer and any writer can append a child to another's branch. But the delivery made deletion and force refused: the forge's 'reservations protection' ruleset refuses deletion and non-fast-forward under refs/heads/abcd-reserved/** for everyone but the sweep App, and the committed pre-push hook refuses a deletion there. narrowing: Holds for fast-forward appends only: any writer can still push a child naming any holder onto a reservation branch; deleting or rewriting one is refused by the forge ruleset (bypassed only by the sweep App) and, for a push through the hooks, by the pre-push hook. evidence: .abcd/work/rulesets/reservations-protection.json:21 — ""type": "deletion"" evidence: .abcd/work/rulesets/reservations-protection.json:4 — ""actor_id": "RESERVATION_SWEEP_APP_ID"," evidence: .githooks/pre-push:432 — "a branch under $row_prefix ends by a push, never a deletion" evidence: .abcd/development/specs/closed/spc-2610031753359849-nothing-tells-an-agent-that-a-record-it.md:778 — "people who share push access in good faith. Any writer with push access"
- cond-2610031753358669 — survived: One username is one person: a laptop resolving octo through the machine config and a cloud clone resolving it through the no-reply address are judged one person, two copies, and usernames compare without regard to case. evidence: internal/core/claim/remote_test.go:521 — "want := "you are already building iss-5 on another computer, until 2026-10-03T16:00:00Z; pass --take-over to continue here"" evidence: internal/core/claim/holder.go:48 — "func sameHolder(a, b string) bool { return strings.EqualFold(a, b) }"
- cond-2610031753352100 — survived: Expiry is judged by the reader's clock against the writer's times with a five-minute take-over margin; the day clamp counts from the earlier of the claim and the reader's now (and from the first sighting for a claim dated more than five minutes ahead), and the code names the residual bound for a holder whose clock runs behind. evidence: internal/core/claim/claim.go:61 — "TakeoverMargin = lease.Skew" evidence: internal/core/lease/lease.go:33 — "const Skew = 5 * time.Minute" evidence: internal/core/lease/lease.go:94 — "if ceiling := earlier(from(s.ClaimedAt, s.FirstSeen, now), now).Add(Max); ceiling.Before(exp) {" evidence: internal/core/claim/claim.go:57 — "// A holder whose clock runs behind by more than half its lease plus this"
- cond-2610031753358608 — survived: Renewal happens only when something runs it, a step at half the lease (and, since the 2026-10-14 review, a receipt and each drain move over a parked lane); a reservation left to lapse is taken over and the land fence then refuses the merge, the failure the multi-account drain investigation of 2026-10-16 met (iss-2610161015393523). evidence: internal/core/implement/loop/reservation_test.go:181 — "if after.commit == before.commit || after.parent != before.commit {" evidence: internal/core/implement/loop/reservation_test.go:265 — "reserveRun(t, f.repo, f.runID, "itd-10", now.Add(-130*time.Minute), "internal/widget")" evidence: internal/core/implement/loop/reservation_test.go:272 — "want := "the reservation on itd-10 is now quinn's, until " + qTip.expires.Format(time.RFC3339) + ", so this lane does not arm its merge""
- cond-2610031753354769 — survived: The cap and the overlap are judged by crowding after the act's own fetch and before the commit and push, on that view, with no compare-and-swap across records. evidence: internal/core/claim/remote.go:169 — "gap, err := fetch(root, p.now, noReservation, reservations)" evidence: internal/core/claim/remote.go:196 — "if err := crowding(p, j, &res); err != nil {" evidence: internal/core/claim/crowd.go:48 — "// Both are judged on the view just fetched, not compared-and-swapped: each"
- cond-2610031753356382 — narrowed: The derived release covers the merge itself as assumed. But decision 40 made the fence's own renewal a day, mirror or not, and later renewals keep that term, so the merge queue's wait no longer depends on implement step renewing during the first day. narrowing: The queue's wait depends on continued renewal (a step, a receipt, or a drain move over the parked lane) only past the day the fence's own lease.Max renewal holds; within that day the fence covers it alone, and the derived release covers the merge. evidence: internal/core/implement/loop/reservation.go:216 — "const fenceLease = lease.Max" evidence: internal/core/implement/loop/parked_reserve_test.go:201 — "if tip.expires.Sub(tip.claimed) != 24*time.Hour {" evidence: internal/core/claim/remote_test.go:500 — "if r.State != StateReleased || r.ReleasedBy != ReleasedByDefaultBranch {"
- cond-2610031753358785 — survived: Renders and the build's peers check read refs/remotes/origin/abcd-reserved/* from disk: a reservation made after the last fetch reads as none, a view a plain git fetch left is shown with its age unknown, and the reserving push itself refuses the same item as a lost race. evidence: internal/surface/cli/reservation_render_test.go:146 — "if r := rec.Reservation; r == nil || r.State != "none" {" evidence: internal/core/claim/render.go:137 — "return "as of a fetch of unknown age"" evidence: internal/core/claim/remote_test.go:254 — "want := "iss-5 was reserved by octo while this push was on its way, until 2026-10-03T16:00:00Z; back off and take other work""
- cond-2610031753359470 — survived: A reservation carries the record id, a username, a random copy key, repository-relative paths, UTC times and the abcd label; the written commit carries no home, checkout path or host, and the message passes the outbound policy before it is written. evidence: internal/core/claim/message_test.go:412 — "for _, leak := range []string{home, r.Root(), host} {" evidence: internal/core/claim/message_test.go:420 — "if err := checkMessage(r.Root(), p.reservation().text()); err != nil {" evidence: internal/core/claim/message_test.go:31 — "Abcd-Copy: 9f2c41d07be35a6c80e1f4b2a7d39c55"
- cond-2610031753355764 — narrowed: The cloud copy sees only origin: the reservation lives on origin, each clone keeps its own copy key, and the tests' cloud clone, carrying the person's no-reply address, is judged exactly as a second machine. But the 2026-10-15 probe found a hosted clone carries the host's vendor identity, and as delivered the holder resolver then refused for want of a username unless a person set user.email or the machine key. narrowing: As delivered it holds for a cloud clone whose git identity is the person's GitHub no-reply address or whose home sets reservation.github_username; a hosted clone with the host's vendor identity could not reserve until the forge-token holder, merged after this delivery (PR #1037, iss-2610152046379890), named the person from the session's token. evidence: internal/core/claim/remote_test.go:515 — "cloud := w.clone("1234+octo@users.noreply.github.com", "")" evidence: c9d94ebd1:internal/core/claim/holder.go:116 — "return "", refuse("cannot tell which GitHub user you are: user.email is not a GitHub no-reply address, and %s sets no %s; "+" evidence: .abcd/work/issues/resolved/iss-2610152046379890-a-hosted-cloud-session-s-clone-carries.md:21 — "A hosted cloud session's clone carries the host's default vendor identity as its git author, not the person's" evidence: internal/core/claim/holder.go:170 — "return tokenHolder(root, o)"
Grounds
- pursued: A second person joined today, and agents on two computers plus a cloud session now work on the project at once. We expect no item to be worked twice in the first month after it ships; one duplicate pull request or one issue fixed twice by two people would show it wrong.
Frontmatter
| id | itd-2609091034175565 |
|---|---|
| type | intent |
| lifecycle | shipped |
| kind | standalone |
| severity | minor |
.abcd/development/intents/shipped/itd-2609091034175565-nothing-tells-an-agent-that-a-record-it.md
open on GitHub ↗ · commit history ↗
References9
- 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-33Multiple Agents Coordinate On Intent And Spec Work Without Duplicating Effort Or Producing Competing Artefactssupersedes
- 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 worktreerelated
- itd-2609091014076309Session and agent worktrees live in a machine-scoped store that abcd lists and reclaims, never beside the user's own projectsrelated
- itd-2609091416295622A session sees the records its peers hold before it mints or fixes onerelated
- itd-2609091416304128capture resolve refuses a record the default branch has already closed, judged by the last fetchrelated
- 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-2610031259176838Outside contributors reserve work through a draft pull request that abcd honoursrelated
- itd-2610170634394993Headless agents in parallel coordinate through the shared remote, not conversationrelated
Referenced by27
- adr-2610031352212697Claims are branches on the shared remote; nothing kept on one computer is the register; claim text is datarelated
- 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
- adr-2610222026262980Coordination between headless agents is bounded by deadlines, fenced by the claim's tip, version-checked, and done under each session's own credentialrelated
- iss-2608220750029993Session-presence detection for shared checkouts: each live session leases a marker (session id, intended branch, started-at) in the checkout, and the pre-commit gate warns when another live session's lease is present — the armed-detector rung of the AGENTS.md concurrent-session conventions, which are vigilance-only until this ships. Lease home needs thought: the local tier is per-worktree by design, and the hazard is precisely two sessions in ONE worktree, so the lease belongs to the checkout, not the branch.related
- 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.related
- iss-2609240646546286An `abcd implement claim` holds one record, and a lane is often a cluster of records. In autonomous run A the second session's first lane fixed three issues and captured and fixed a fourth under one claim; the other records were unguarded against the first session for the lane's whole life, and only the two orchestrators' agreement kept a second lane off them. The second session cannot close the gap by claiming each record either, because it is refused a second live claim. Wanted: a cluster form of the claim, several record ids in one claim or a lane key the records are declared under, refused whole when any member is held elsewhere.related
- iss-2610262032111048The mental model says a standalone intent ships as one spec; itd-2609091034175565, planned alone, is delivered through two specs.related
- iss-2610262037516372The reservation intent refuses any remote layout without origin; the build chapter reserves nothing in such a checkout and none of its checks refuses the run.related
- iss-2610262037561390The reservation intent's land fence renews for the merge queue's timeout plus a margin where a mirror exists; the implement chapter renews it for a day, with a mirror or without one.related
- iss-261026203959075704-universal-patterns keeps multi-agent claims in `.abcd/coordination/` with local-only state; itd-2609091034175565 makes the reservation branch on origin the one record of who holds what.related
- iss-2610262040016204itd-2609091034175565 says a person sees reservations through `abcd peers`; the README lists `peers` as a verb for agents and hosts only.related
- iss-2610262043379064itd-2610031259176838 tells people about 'the claim branch'; itd-2609091034175565 ruled the person-facing word is reservation and the branch prefix abcd-reserved/related
- iss-2610262043385436itd-2609091034175565 hands reservation tombstone deletion to itd-118, whose scope covers PR branches and local residue onlyrelated
- iss-2610262044322326The worktree-block intent assigns typed claims to itd-33, but the shipped reservation intent delivered the claimrelated
- iss-2610262045435815itd-2609231434459890 says the pre-push hook runs make preflight; itd-2609091034175565 has the pre-push hook gate on a preflight receiptrelated
- iss-2610262045489203itd-2609091034175565 refuses any footprint overlap with a live reservation; itd-2610161016394967 lets generated files and a person's own overlap throughrelated
- iss-2610262045503347--release lands a held lane on implement step but gives back a reservation on buildrelated
- iss-2610262045516387itd-2609091416295622 defines peers as a read-only view of this checkout's common dir; itd-2609091034175565 has peers fetch origin and list every person's reservationsrelated
- itd-2609091416295622A session sees the records its peers hold before it mints or fixes onebuilt on by
- 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.built on by
- 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 reservationbuilt on by
- itd-2610031259176838Outside contributors reserve work through a draft pull request that abcd honoursbuilt on by
- itd-2610120738075547Work handed to a cloud session comes back, or says why notbuilt on by
- itd-2610170634394993Headless agents in parallel coordinate through the shared remote, not conversationbuilt on by
- spc-2610031753359849Reservations on the shared remote: `abcd reserve`, one liveness judgement, and the build, drain and land integrationimplemented by
mentions42
- adr-5Brief is the current state; no version label, no archive directory
- coordination-lives-on-the-shared-remoteCoordination lives on the shared remote, never only in conversation
- dcn-26090900000000052026-09-09 — itd-2609091034175565, the claim intent, is split into three records (maintainer, ruling after two adversarial reviews of the widened draft, design/feasibility and record-discipline). The reviews found the expensive parts unsound as drafted: a `claimed_by` stamp on an issue record is invisible to any peer on an older binary, because `issueschema.Known` is a closed allow-list whose reader refuses and skips a record carrying an unknown key and `record_schema` mirrors the refusal into the gate, and version skew is the steady state here (five plugin-cache vintages beside a `go run` checkout); the write-verb refusals fire after the fix is written, because `AGENTS.md` puts `capture resolve` in the same change as the fix; no staleness threshold is safe in both directions for two sessions in one worktree, since the worktree-exists and branch-merged tests are identical for both and a host may fire session-end on a context clear with the human still present; and the pushed half costs a merge-queue pass per claim, measured at fifteen to sixteen minutes on this repository's merge-group `ci` leg. Every collision on record is one of two claim-free shapes. The ruling: itd-2609091416295622 carries the read-only sibling-worktree ledger diff over `git worktree list --porcelain` (no claim, no lease, no hook, no threshold; the piece that ships soonest); itd-2609091416304128 carries `capture resolve` and `capture wontfix` refusing a record terminal at the local `origin/main` ref as last fetched, stating the ref's age and fetching nothing, with the same judgement rendered read-only on `abcd <record-id>`; itd-2609091034175565 keeps the claim, the lease and the refusals as a draft marked not ready, carrying the refusal-surface, liveness and pushed-price questions as open questions and the two-release stamp migration as a hard constraint. The existing record stays the claim so the `promoted_from` trail from iss-2609020716570699 — whose remedy is the claim and the pushed half — stays true rather than being retitled onto a listing the issue never proposed; the two new records reach the issue through `related_intents` on the issue and on themselves, and every relation the schema cannot type is stated in prose as a prose cross-reference (iss-2609091256264547). All three sit in `drafts/`; adoption is the interview's. Rejected: re-scoping the existing record to the listing, which keeps the trail mechanically and breaks it semantically; a fifth relation word for "split from", which is the vague form the decomposition principle forbids.
- dcn-26100300000000012026-10-03 — Reserving work across people (product thinker, at an interactive interview, with a second person now working on this public repository). Rulings: anything kept on one computer (the local tier, a desktop note, same-account messaging) is never the record of who works on what, because the other person never sees it; an item is reserved by a reservation branch on the shared remote, `abcd-reserved/<record-id>`, which abcd lists and nobody merges, chosen over a hidden ref because a hosted cloud session can push branches only (probe of 2026-10-03 and the host's documentation); a hosted cloud session takes part exactly as a second person's machine does; itd-2609091034175565 is planned and built today with every acceptance criterion, and the session register itd-2609150819440345 stays a draft for later; outside contributors, who cannot push a claim branch, are the follow-up itd-2610031259176838, not urgent. The ruling of 2026-09-22 ("Cross-account and cross-machine agent communication stays out of the run") covered the run of 2026-09-23 only, confirmed by the product thinker today, so nothing is overturned. The 2026-09-09 ruling's two-release migration for a `claimed_by` stamp does not arise, because no stamp is written on any record (decided without a question: one defensible answer). The research and both reviews are filed as research/notes/2026-10-03-multi-agent-coordination-sota-and-run-lessons.md.
- dcn-26101610441876732026-10-16 — The drain bundle's pre-pass answers (facilitator-on-delegation, drain-many-accounts): brief invariant 7's sentences naming what drain and implement step fetch and push are amended, in each building step that adds an act, to name the per-move fetch, the reservation before a judgement, the hand-back tombstone, the renewal of the lane in progress, the parked lanes' pull-request listing, the fork question and the sync's fetch of the default branch; brief invariant 19 gains a clause that an issue whose related_intents names an intent is not taken; a hand-back published on origin is the record of a judgement kept on the reservation, not a field classification written onto the issue, so invariant 19's re-derivation stands; and each member keeps both against its siblings itd-82, itd-2609091034175565 and itd-2609151838312703, building on the first two (grounds: the invariants are the record of what may touch the network and what a machine may take, so they move with the surface, as the principle spec-moves-with-the-surface requires).
- dcn-26101807203626922026-10-18 — A dependency is a Depends-On line in the pull request's body (facilitator-on-delegation, itd-2610170634394993 planning prep): a branch's wait on another change is written as Depends-On: #N, which land writes from the agent's declaration and the pass enforces, and the session's short status line mirrors it as a hint only; a field on the claim is not used, since a reservation is per record and per person while a dependency belongs to a change, and the line's exact spelling is plumbing for the brief (grounds: docs.mergify.com/merge-protections/builtin; Zuul's Depends-On footer; itd-2609091034175565)
- dcn-26101807203662822026-10-18 — The coordination pass runs on demand (facilitator-on-delegation, itd-2610170634394993 planning prep): land, build, drain and status run the pass from the records they have just fetched before they act; there is no timer lease and no process that must stay alive, and every deadline and expiry is derived when the records are read, as a reservation's release already is (grounds: the draft's research note on controllers; kubernetes.io/docs/concepts/architecture/controller; brief invariant 7; itd-2609151838312703 decision 14; itd-2609091034175565 decision 13)
- dcn-26101807203901662026-10-18 — Pushes stay well inside the forge's rate (facilitator-on-delegation, itd-2610170634394993 planning prep): renewals stay at half the two-hour lease, hourly, the short status line is pushed only when it changes, and a refused push backs off, so ten sessions stay far below six pushes a minute (grounds: itd-2609091034175565; internal/core/claim/renewdue.go; docs.github.com repository-limits)
- dcn-26101807203977742026-10-18 — One central-view line per working copy (facilitator-on-delegation, itd-2610170634394993 planning prep): a session in the view is a working copy, keyed on the reservation's existing copy key, since the checkout is the unit of isolation (grounds: itd-2609091034175565 decision 33; AGENTS.md concurrent sessions; internal/core/claim/message.go)
- dcn-26101807205480662026-10-18 — A waiting session wakes before half its lease (facilitator-on-delegation, itd-2610170634394993 planning prep): since no process stays alive and a branch is rebuilt only when its agent asks to land, a session waiting on a dependency wakes, through the checklist draft's timed wake, at an interval shorter than half its reservations' lease, so an idle waiter's reservations are renewed rather than lapsing after two hours and being taken over (grounds: itd-2609091034175565 two-hour lease, renewed at half; itd-2610170638184486 timed wake; the draft's take-over criterion)
- iss-1launch phase ownership contradicts across the record: phase-1-ahoy.md and build-sequence.md make install plus launch the first milestone, but 04-surfaces/04-launch.md says full launch builds in Phase 5. ADR-bound.
- iss-2release-version source of truth conflicts: roadmap README tracks releases by plugin.json; ADR-31 derives versions from shipped intents; 04-launch.md still describes --version and phase-completion tiering. ADR-bound.
- iss-3phase membership and lifecycle directories disagree: planned intents (itd-20, itd-24, itd-29, itd-46) appear in no phase Scope, while draft itd-43 is scoped into Phase 0/1 yet listed later-phase. ADR-bound.
- iss-4phase count mismatch: 04-scope.md, 03-out-of-scope.md and 06-delivery README say six phases (Phase 0-5), but roadmap phases README has Phase 0-6 with Phase 6 as lifeboat round-trip.
- iss-5disembark and embark status banners point to the wrong phase: 02-disembark.md says Phase 4, 03-embark.md says Phase 5, but phase-6-lifeboat.md owns both.
- iss-6command inventory mismatch: 04-scope.md lists seven user-facing commands; 04-surfaces README lists nine (adds /abcd and /abcd:reflect).
- iss-803-out-of-scope.md omits current drafts itd-74 and itd-75 while including scoped itd-43; regenerate from the actual draft corpus.
- iss-10Phase 0 user-command wording contradicts: phase-0-substrate.md says Phase 0 carries /abcd:intent review; phases README says Phase 0 has no user-facing command.
- iss-2609091256264547The decomposition discipline requires a cross-record link to be typed with one of four relations, supersedes or reverses or duplicates or refines, and never a vague related. Only the first of those four exists. supersedes is schema-known through recordHandleFields and is carried by fifty-eight decision records and three others, while reverses, duplicates and refines appear in no schema and in no committed record anywhere in the tree: the field list a record may carry is related_adrs, related_intents, builds_on and blocked_by, and a record carrying a key outside the known set is dropped by the reader rather than reported, so writing one of the three missing words would make the record invisible to every surface that reads it. The consequence is that an author following the rule literally either writes a link the tooling silently discards or writes the relation in prose and calls it typed, and the corpus shows the second: records assert a typed link in a heading and carry no frontmatter edge, which reads as done to a human and is invisible to every graph walker. That is the shape enforcement-claims-are-facts refuses, a convention naming a mechanism that does not exist, and it is load-bearing here because the decomposition protocol is the documented gate until its automated rung ships. Fix candidates, none chosen: implement the three missing relations as known fields so the rule can be followed; or narrow the rule to the vocabulary the schema implements and say what an author does with a relation the four words cannot express; or record that prose is the sanctioned form for the three and stop calling them typed. Detector: a record asserting a typed relation in its body carries a frontmatter edge naming the same target, and a relation the schema cannot express is refused at write time rather than dropped in silence.
- iss-2610100649471088abcd lint docs --json prints two JSON documents on a blocker, and names its finding fields differently from abcd lint
- iss-2610142200389875The reservations ruleset forbids deleting refs/heads/abcd-reserved/** with no bypass, so every spent reservation branch stays on the forge forever, counting against its per-repository branch limits and against the intent's 'needs occasional clean-up' (decision 10 of itd-2609091034175565); the rulesets README notes the itd-118 sweep needs the ruleset amended first.
- iss-2610152020234609The product thinker ruled on 2026-10-15, at about 20:15 BST: a pull request the land step leaves waiting on a person holds its reservation on the shared remote for a day, mirror or not. Today the land fence (fenceLease in internal/core/implement/loop/reservation.go) renews for lease.Max only where the checkout has a ruleset mirror (.abcd/work/rulesets/), and for the two-hour lease.Default otherwise, per decision 29 and criterion C44 of itd-2609091034175565 (spc-2610031753359849). Without a mirror, a pull request that is only waiting to merge loses its reservation two hours after the fence unless something renews it, and a colleague's drain can then take the record and redo the work.
- iss-2610152046379890A hosted cloud session's clone carries the host's default vendor identity as its git author, not the person's: the cloud probe of 2026-10-15 found it in the clone's git config, while the forge credited the push to the person's own account through the token. The product thinker ruled the same day that this vendor identity must not be shown anywhere, in no commit, ref, trailer or output, and that no person should have to set it by hand ('Who has to remember what? No manual steps.'). Session announcements (itd-2609151838312703) already fall back to the token's login for their own commits; every other commit a cloud session makes still carries the vendor identity until its setup sets the person's.
- iss-2610152125101468A reservation footprint can hold a whole directory (docs, hooks, internal/surface/cli), and the overlap check then refuses every other reservation naming any file inside it. Generated files that every verb-adding branch regenerates, such as docs/reference/cli/commands.md, sit inside such directories, so two unrelated intents held by the same person from two copies could not both declare an honest footprint: on 2026-10-15 the messaging reservation's renewal was refused twice and had to leave out .githooks/pre-push, the ruleset mirror and the generated CLI reference, which it does change.
- iss-2610160608451996itd-2609091034175565's press release persona quote says 'My laptop and my cloud session count as me, so they never lock each other out', but the shipped reservations refuse a second copy of the same person and name --take-over; the v0.15.0 release page dropped the quote for that reason
- iss-2610160957404827abcd drain reads the issue ledger of the checkout it runs in and never checks whether that checkout is behind origin's default branch: from a primary checkout 185 commits behind main, the dry run listed 162 eligible issues from the stale ledger, many since fixed or changed, and a run would judge, reserve and work from that list.
- iss-2610161015393523A drain's in-progress lane is renewed only when its own step or receipt runs, so if the host pauses the lane for more than the two-hour default lease another account takes the reservation over five minutes after expiry and works the same issue; the land fence runs only at arming, after the push and the pull request, so the duplicate becomes a public pull request.
- iss-2610170717515674The reservation cap (MaxLive, 10, in internal/core/claim/crowd.go) counts every live reservation one person holds across all of their sessions and copies, so one person running seven sessions on one account hit it all night: a session's fix waited for slots, two drain issues went unreserved, and sessions released and re-took holds to make room.
- iss-2610251558434724docs/reference/terminology.md: three crosswalk positions no longer fit their own label definitions once the stale facts are corrected, and need a ruling rather than an edit. Durable execution stays WATCHING although the pacing intent (itd-2609201925079472, shipped) checkpoints lanes to their branches and abcd build resumes a run from its state file (internal/core/implement/loop/ratelimit.go:3-20, loop.go:825; internal/surface/cli/build.go:103). Agent2Agent stays WATCHING although its tracking record itd-33 is superseded by itd-2609091034175565, so no live entry tracks it as the label requires (terminology.md:13-14). Orchestration says the orchestration-substrate role is declined while abcd's own implement loop sequences a run's agents and gates each stage on its receipt (internal/core/implement/loop/budget.go:11-13).
- iss-2610281402204506The site's history pass dates a record from every commit it walks, branch sides included, and reads a merge commit's diff against its first parent. A branch that merges main the usual way, with the branch as first parent, so carries a commit whose diff shows every record main moved since the branch was cut, dated the day of that merge. Once such a branch lands, those records read as having entered their folders on that day. The home page features the most recently shipped intent whose audit is met, so the featured quotation can change for reasons the landing diff does not contain, and the site's banned-token gate then judges a different press release. On 2026-10-28 an unpushed merge of main into the ideate-librarian bundle branch, from 23 Oct, re-dated main's shipped intents. That made itd-2609091034175565 the featured intent, its press release tripped present_tense/no-longer, and site-render failed on a branch whose diff touched no shipped intent. The branch was rebuilt with main as the merge's first parent and passed.
- itd-5Every Agent Prompt Carries Its Version, Earns Its Lock, And Survives Injection
- itd-7Lifeboats Carry RepoPrompt Workspace Definitions Forward
- itd-8Lifeboats Carry Source Code, Not Just Lessons
- itd-10Full Removal When You Mean It
- itd-22abcd Reaches Any Harness Through an Adaptor
- itd-87A Finding That Comes Back After You Closed It Tells You Something, And abcd Stops Throwing It Away
- itd-2609211116005482`abcd build next` picks the readiest planned intent itself, and writes down why
- itd-2609221656373558Two sessions share one run, and the run measures which way of dividing the work is best
- itd-2610161016394967Drains on several accounts share the work
- spc-5folder-classification
- spc-2610120941441282a-person-hands-an-item-from-their
- spc-2610230758581403several-headless-agents-work-one