Development record
A session left to work on never stops while it has work
Summary
Delivers itd-2610180912189125, the first slice of the checklist draft (itd-2610170638184486) and the fix for iss-2610170619247185. Trimmed after review round 2 to what closes the issue (dcn-2610181017056307), and narrowed by review round 3 (dcn-2610181035355741, dcn-2610181035361997). Three parts:
- The away record.
abcd mode autonomous [--until <time>] [--hold-pushes],abcd mode backandabcd mode queue "<step>"keep one away record per repository on the machine, changed only under a lock and lapsing at its end, seven days at most. The session-start hook (every source,compactincluded) and the prompt hook repeat it to the agent while it holds, and the prompt hook shows the person one line saying so, namingabcd mode back. The mode grants no rights: the person's own standing instruction stays the authority to commit, push and open or merge a pull request (dcn-2610181016510153). - The stop check. A plugin Stop hook runs
abcd hook stop. While the record holds, a session is only working or waiting: a turn's end is blocked while owed work exists, naming the next item, and is allowed only with a wake that fires, the host's lateness included, within half an hour while work is owed and before the session's reservations need renewing, and a wake releases an unchanged set of work the session can do itself at most twice in a row. The host's cap of eight consecutive continuations is the outer bound. A StopFailure hook records a turn the host ends on an API error. While the person is present the check reads disk only and points the work out. - The question reroute. While the record holds, the guard refuses every
question on the host's question tool. An act only the person may approve
goes to the queue; every other question is decided and recorded with
abcd decide.abcd mode backprints the queue and the recorded stops, one line each.
Every behaviour is built test-first: each step names its tests, written and watched failing before its code. The last step is a live Claude Code session capture, because the hook mechanics are the host's and a unit test cannot prove them (principle evaluate-at-the-user-surface).
Moved out by review round 1 (dcn-2610180937181929) and by the trim after
round 2 (dcn-2610181016510153, dcn-2610181017056307), back to the checklist
draft as named remaining work: the report shown once on the person's return,
the status line, the bare abcd mode print, making the away mode the
person's act alone (the guard entries, their other routes and the refused
attempts), the guard's reading of go run ./cmd/abcd and built binaries as
abcd, and the forge listing's reuse cache. "Trim after round 2" below
lists each.
Design
The host mechanics this rests on
Read on 2026-10-18 at https://code.claude.com/docs/en/hooks (the old docs.claude.com hooks page redirects there):
- Stop. The hook "runs when the main Claude Code agent has finished
responding" and not on a user interrupt. Its input carries
stop_hook_active, "true when Claude Code is already continuing as a result of a stop hook", andlast_assistant_message, the text of Claude's final response. It also carriesbackground_tasks(each withid,typesuch asshell,subagent,monitororworkflow, andstatus) andsession_crons(each withid,scheduleas a cron expression such as0 9 * * 1-5,recurringandprompt); both are present only when the host's task registry is reachable. Output{"decision": "block", "reason": …}prevents the stop. Claude Code caps this at eight consecutive continuations, overriding the next block; the count resets each time Claude calls a tool, andCLAUDE_CODE_STOP_HOOK_BLOCK_CAPraises it. Read again on 2026-10-18 ("Stop decision control"):hookSpecificOutput.additionalContextis "Non-error feedback for Claude. The conversation continues so Claude can act on it, but unlikedecision: "block"it is shown in the transcript as hook feedback rather than a hook error"; use it "when the hook is working as designed", and it "keeps the conversation going through the same loop protections asdecision: "block", namely thestop_hook_activeinput and the 8-consecutive-continuation cap, but the transcript labels itStop hook feedbackand no hook error notification is shown". The stop check blocks with{"hookSpecificOutput":{"hookEventName":"Stop","additionalContext":…}}(Diff review round 1, item 7). - StopFailure. Fires instead of Stop "when the turn ends due to an API
error"; its matcher is the error type (
rate_limit,overloaded,server_errorand others). It cannot block, and its output is ignored. Its input carrieserror, "the error type and is used for matcher filtering", with optionalerror_detailsandlast_assistant_message(here the API error string); "StopFailure hooks have no decision control".abcd hook stop-failurereadserror. - SessionStart. The matcher values include
compact("auto or manual compaction"). Plain stdout, orhookSpecificOutput.additionalContext, is added to Claude's context. - UserPromptSubmit. Plain stdout or
additionalContextis added beside the prompt, as a system reminder. - Universal output.
systemMessageis a "warning message shown to the user". - Where a prompt comes from. The prompt hook also fires on turns the host starts on its own and on a message another session sends, and its input has no field naming the prompt's origin, so a hook cannot tell the person's word from a relayed one.
Read on 2026-10-18 at https://code.claude.com/docs/en/scheduled-tasks:
- Scheduled tasks. "All times are interpreted in your local timezone",
not UTC. A recurring task "starts each run late by up to half the gap to
the next run, and by at most 30 minutes", except
*/5 * * * *, which runs 15 seconds sooner; a one-shot task set for:00or:30runs up to 90 seconds early, and one set for any other minute is not shifted. Day of week takes0or7for Sunday, and when both day fields are restricted either matches, "standard vixie-cron semantics".CronListrows show each task's next run.
abcd's hook manifest already registers SessionStart with no matcher, so
compaction already runs abcd hook session-start. It has no Stop or
StopFailure entry, and its PreToolUse entry matches the shell tool and the
host's question tool.
The away record
- Where it lives. One JSON file per repository on the machine, in the
away store,
~/.abcd.noindex/away/<root-sha>/, keyed on the repository's root commit as the worktree store is (dcn-2610180913399499), with a lock file beside it. The same folder holds each working copy's list of branches it created (dcn-2610181003463367). Everything in it is written through fsutil's atomic writer. - What it holds. Who set it: the holder name reservations cache, without
a network read, falling back to git's
user.name, and the working copy it was set from. It also holds the start, the end, whether the person said pushes wait (--hold-pushes), the queue of steps for the person, the items the stop check handed back (branch and head), the stops recorded (with work owed, with no next look, or on an API error with its type, each at the cap labelled "possibly forced by the cap"; a stop identical to the last one recorded is kept as that entry with a count and the last time, so a wake every ten minutes cannot push the record past its 64 KiB check), and, once ended by word, who ended it and from which working copy. - The lock (dcn-2610180937181736). Every read-modify-write (
mode autonomous,mode back,mode queue, a recorded stop, a recorded branch) holdsfsutil.WithFileLockon the lock file and re-reads the record inside it. The stop check reads the record and lists the forge outside the lock, then takes it, re-reads, and records its stop only if the record still holds, so a check that began while away never writes away back afterabcd mode back. A lock not had within two seconds: the verbs refuse naming it, and the hooks allow the stop and say so on stderr. - Ownership and permissions. The record and the lock file are used only
when each is a regular file this account owns that no one else can write,
within 64 KiB: the checks
rules.jsonalready gets. A record that fails them, or does not parse, reads as not away (failing open, with a stderr line), and a verb asked to change it refuses naming the file. - When it ends. It holds while now is before its end. It ends with
abcd mode back, or when its time passes. A human prompt never ends it (dcn-2610180913399042). - The stale-record cap. An open-ended record ends seven days after it was
set, and
--untilmay name no later time; a later one is refused with nothing written. A run of several days, like the standing run of 2026-10-15 through 2026-10-17, fits; a forgotten record lapses. - Seen by the person. While the record holds, the prompt hook adds a
one-line
systemMessageon every prompt: away until when, set by whom and from which working copy, "N steps queued, M items handed back", and the literalabcd mode backas the way to end it (dcn-2610181035355741). The away block tells the agent to runabcd mode backonly on the person's word in its session (dcn-2610181003474765). - A lapsed window with anything unread. While a record whose end has
passed is not yet closed and holds a queued step or a recorded stop, the
prompt hook shows the person one line on every prompt counting them and
naming
abcd mode back, until that verb closes the record (dcn-2610181003474765). - Ending it.
abcd mode backprints one plain line per queued step, per handed-back item and per recorded stop (its label and count included), records who ended the mode and from which working copy, and ends the record; on a record whose time has passed it prints the same and closes it. Nothing more is printed: the report on return moved to the checklist draft. - The badge. The three-word badge in
internal/core/modeis untouched. While the record holds,abcd mode product-thinkerandabcd mode facilitatorare refused, namingabcd decide, since a badge parked on the person is a question waiting for them (dcn-2610180913431493).
Who sets it, and what it grants
It grants nothing (dcn-2610181016510153, superseding dcn-2610181003456444). The person's own standing instruction stays the authority to commit, push, and open or merge a pull request, exactly as before the mode existed. The away mode only keeps a session working while owed work exists and turns its questions into recorded decisions or queued steps; it is never treated as permission. The away block says so, in those words.
Who sets it. The person, with their own command (at a terminal, or with
! in the session), or a session on the person's word in that session, as
dcn-2610180913399042 first said. Nothing checks which: the prompt hook cannot
tell the person's message from one another session relays. Because the mode
grants no rights, a mode set on a relayed word only keeps sessions working
and deciding instead of asking, and the person sees who set it, from which
working copy, and abcd mode back on their next prompt.
Pushes held (dcn-2610181035355741). --hold-pushes is the person's word
that pushes wait for their return. It only narrows what sessions do and grants
nothing, so dcn-2610181016510153 stands: like the mode, nothing can check who
ran it, and set on a relayed word it can at worst make sessions queue pushes
they could have sent. It is the only thing that lets a queued push hand an
item back (Owed work, below).
Why the guard entries left the slice. Round 2's choice (a) made setting
the mode the person's act alone, because the mode then carried push rights.
The OS sandbox of spc-2610161751056096 fences only the agents abcd's runner
launches, never the person's own interactive sessions, which are the ones
that stalled. There, the guard entries would only ever be a mistake filter
(adr-42, dcn-2610171112384712), so their two days would buy a trust claim the plan cannot make.
With no push rights in the mode there is nothing for them to protect in this
slice. Making the mode the person's act alone (the guard entry, its other
routes, the store's shell-write and file-tool refusals, the refused
attempts, and the capture's checks of the ! path), and the guard reading
go run ./cmd/abcd and built binaries as abcd for every entry, the ledger
flip included, are named remaining work of itd-2610170638184486; the second
is a security-relevant widening that gets its own review there.
Owed work
Owed work is four kinds (dcn-2610180913416683, refined by dcn-2610180936568358, dcn-2610180936571036 and, in review round 2, dcn-2610181003463367, dcn-2610181003467233, dcn-2610181003464379 and dcn-2610181003488355):
- a live reservation under this copy's key whose renewal is due, or whose work no open pull request carries yet;
- an open pull request of the session's own that is behind its base or failing its checks;
- a branch of its own with commits not pushed;
- a branch of its own stacked on a change that is still open, while it needs work (the clearing rule below).
- What "own" means. A branch is the session's own when one of these
holds (dcn-2610181003463367):
- this working copy's HEAD reflog has checked it out;
- the session created it: when the guard's pre-tool hook sees this session
run
git worktree add -b|-B <b>,git switch -c|-C <b>,git checkout -b|-B <b>orgit branch <b>, and no branch named<b>exists yet, it records<b>under this copy's key in the away store, under the store's lock, whether or not the person is away. A baregit worktree add <path>records the branch git names after the folder,git worktree add <path> <existing>records<existing>, andgit branch -m|-M <old> <new>carries a record from<old>to<new>; branch names match as whole tokens, and the file is capped (the builder sets the bound, oldest dropped). The hook only records; it refuses nothing, and a failed write leaves the command allowed with a stderr line. This is how the stacked drain branches of 2026-10-17 (feat/drain-step11and its siblings) are seen: cut with a plaingit worktree add, they had no upstream, no entry in this copy's reflog, no run state and no record id in their names. A record made for a command that then failed names a branch that does not exist, and is ignored; - it is the branch of a lane in a run state this copy's local tier holds
(
.abcd/.work.local/run/<run-id>/state.json), while that run's reservation is live under this copy's key; - it is the head branch of an open pull request linked to a reservation under this copy's key (the link below).
- Live, not stale. A branch owned through the reflog or a recorded
creation counts only while it holds a commit authored by this checkout's
user.emailwithin the last 14 days, or a live reservation under this copy's key links it. A finished or abandoned run's lanes never count. So the five lanes of 2026-10-12 this checkout still holds, from runs that stopped at implement and never landed, are not owed forever, and the nothing-owed row stays reachable. - Never own. The default branch; a tag; a branch whose open pull request another person authored (the listing's author is not the person); and a branch this copy only checked out to review, which the authorship rule above leaves out, since it holds none of this checkout's commits. A sibling copy's branch is none of the routes above, so it never holds this session.
- Which copy. A session's copy is the worktree it started in: the
session-start hook notes the worktree root of its payload's
cwdper session id in the away store, and the recorder and the stop check read the copy key, local tier and session state from that root, so a session that changes directory into a worktree it just made keeps its copy key; a session with no note uses its payloadcwd's worktree. - The reservation filter and link (dcn-2610181003467233). A reservation
counts only when its copy key is this copy's (the view's
Copy == CopyThis). A pull request carries a reservation's work when the record id appears in its head branch's name or on aResolves:orDelivers:line of its body, matched on word boundaries, so iss-5 never matches iss-50. ARefs:line, which the issue-resolution gate (RS004) requires of any pull request that only names an issue, carries the work only when the head branch is already own by another route, and never makes a branch own: a sibling copy's pull request that mentions the record neither leaves this session waiting nor becomes its branch. A renewal is due halfway between the reservation's latest claim or renewal and its expiry. - Behind (dcn-2610181003464379). While away, a pull request is behind
when the listing's merge state (
mergeStateStatus) isBEHIND, which the forge measures against its base as it stands now, not against the last-fetched default branch, which nothing refreshes overnight.DIRTY(a conflicting pull request) is owed as behind too, andUNKNOWNreads as not read. GitHub reportsBEHINDonly where the base requires branches to be up to date, so the live capture's lab base requires it. Only a branch with no pull request is compared on disk, with the last-fetched default branch, and the reason names when that ref was last fetched. - Finding a stacked branch. An own branch is stacked when its open pull request's base is not the default branch, or, with no pull request, when the nearest other own branch whose tip is an ancestor of its tip is not contained in the last-fetched default branch.
- The clearing rule for a stacked branch (dcn-2610180936571036, refined
by dcn-2610181003488355). One whose base is still open is owed only while
any of these holds: it is behind its base (its merge state, or with no
pull request its base's tip on disk); its checks are failing; a review of
its head is outstanding (the listing's review decision is
CHANGES_REQUESTED, or for a run's lane its run state records no passing review of its head), since the coordination prep's decision that a declared dependency constrains only the landing (dcn-2610180905291297) says its review runs now; or it is not verified. Verified means a preflight receipt naming its head in any worktree (landing.Receipt) where the repository mints receipts (a receipt folder exists in one of its worktrees); in a repository without them, its pull request's checks passing in the listing; with no pull request there either, it is judged on the other conditions alone and the reason says it was not verified. Otherwise it is waiting on its base, which is not owed: only its landing waits. - Unpushed. An own branch with commits not on its upstream, or, with no upstream configured, not in any remote-tracking ref. A branch whose upstream is gone (merged and deleted on the remote, a squash merge included) never counts.
- Queued pushes, and the one hand-back (dcn-2610181035355741, refining
dcn-2610181016510153). The mode grants no push, so an item whose next act
is a push (an unpushed branch, a reservation whose work no pull request
carries, a behind or failing pull request once its local fix is committed)
may be one the session has no standing instruction to send.
- By default a queued push hands nothing back. The item stays owed and the stop stays blocked; a session with no standing instruction to push ends at the host's cap, and the stop is recorded for the person.
- Only with
--hold-pushesdoes a queued step hand anything back. A step naming an own branch, on the same word-boundary match as the link, removes only that branch's unpushed item, and only while the disk shows the rest clear: its head contains its base's last-fetched tip (the default branch, or a stacked branch's base branch as it stands on disk), and a preflight receipt names the head (landing.Receipt) where the repository mints receipts. Where it mints none, the clearing rule's equivalent, passing checks in the listing, can only name a pushed commit, so nothing is handed back there. - Never handed back: a pull request that is behind (or
DIRTY) or failing, which the forge reads so until the push; a stacked branch the clearing rule still owes; a review outstanding; a reservation named by its record id alone; a renewal. So a behind pull request whose merge is committed locally stays owed with pushes held and ends at the cap, recorded: the instance of 2026-10-16 shows up in the morning instead of passing as nothing owed. - The conditions are re-read at every check. The stop check records each
item it hands back (branch and head) in the away record under its lock,
once per branch and head, for the person's line and
abcd mode back.
- What is read, and when (invariant 7,
adr-2610181003541997). While the person is present the check reads disk
only: renewals due (reservations as last fetched,
claim.LastFetched) and unpushed own branches. It never calls the forge, so no turn's end touches the network. While away it adds at most onegh pr listof the open pull requests, every open one when the person's forge login is known (so a branch whose pull request another person authored is left out of "own"), the person's own alone when it is not (head and base branch, author, body, merge state, review decision, check rollup; each field among thoseinternal/ghcontractpins) under a ten-second deadline. On a failure or timeout it fails open to the disk kinds, naming the pull requests as not read: a branch with a pull request is compared on disk with the last-fetched default branch, naming the fetch time, and a reservation is never reported as uncarried, so no duplicate pull request is opened. There is no cache: a run of eight blocks costs up to eight listings, each bounded by the deadline (the reuse cache is remaining work of the checklist draft). Brief invariant 7 gains this listing as the away mode's documented meaning, as the drain's listing is, by adr-2610181003541997. - Order. The next item is the first of: a renewal due, a failing pull request, a behind one, a stacked branch, an unpushed branch, unhanded work.
The stop verdict
autonomy.Judge(record, owed, payload, session, now) Verdict is pure.
The allowed states while away (dcn-2610180936577720): working (the stop is blocked) or waiting (the stop is allowed with a qualifying wake, whose next fire time is the next look). abcd never allows a stop with work owed and no qualifying wake; "stopped" happens only when the host's cap overrides a block, and is then recorded. Stopped is allowed, and flagged, only while the person is present.
The deadline (dcn-2610181003455731, refining dcn-2610180936579498). The
earlier of: the earliest renewal due among the live reservations under this
copy's key, minus lease.Skew; and now plus a ceiling, 30 minutes while work
is owed and one hour when none is. The ceiling closes the long-lease hole: a
release reservation's 24-hour lease puts its renewal 12 hours on, and a
0 7 * * * cron seen at 21:00 would otherwise qualify and leave the session
idle for ten hours with work owed.
The qualifying wake. A session_crons entry qualifies when its latest
fire time is no later than the deadline. The latest fire time is the next
time its schedule matches after now, read in the machine's local time as the
host reads it, plus the most the host may run it late: for a recurring task,
half the gap from that match to the following one, at most 30 minutes, and
nothing for exactly */5 * * * *; a one-shot task is taken as matched, since
the host only runs one early. So an hourly cron whose next match is 3 minutes
before the deadline does not qualify (it may run 30 minutes late), and with
owed work the longest a session can wait is the 30-minute ceiling.
The next fire time comes from a five-field cron parser of abcd's own (minute,
hour, day of month, month, day of week; lists, ranges, steps, a-b/n and
*; 7 is Sunday as 0 is). It follows vixie cron, as the host says it does:
when both day fields are restricted either matches, and a day field written
from * (*/n included) counts as unrestricted for that rule. It steps
minute by minute in absolute time, converting each minute to local wall-clock
time, from now to the deadline plus an hour (for the following match), so at
most a few hundred steps; a wall-clock minute skipped by a daylight-saving
change never matches, and one repeated by it matches at its first
occurrence. An expression it cannot parse never qualifies. A background task
never counts on its own while away. No new dependency.
The session's state. One file per session id in the local tier
(.abcd/.work.local/autonomy/sessions/<session-id>.json; an id outside
[A-Za-z0-9_-]{1,128} is refused and the stop allowed with a stderr line)
holds the state, the next look, the owed-set fingerprint, the last question
message's hash, the count of consecutive blocks, and the wake count with its
own fingerprint of the capped items (below).
The wake count (dcn-2610181035361997, refining dcn-2610180936577720 and dcn-2610181003455731). The capped items are the owed kinds the session can act on itself: a pull request that is behind or failing, an unpushed branch, a stacked branch the clearing rule still owes, and a reservation whose work no pull request carries (which the review's list did not name, but opening that pull request is the same kind of act as a push). A renewal that is only due, and an item waiting only on the forge (a stacked branch whose one unmet condition is its checks still running), are not capped. An allow by row 3 while any capped item is owed adds one to the wake count; a change in the capped items' fingerprint sets it to zero. The count compares the capped items alone, so a renewal falling due or done neither resets it nor adds to it.
While away, the rows are read in order and the first that matches decides:
| # | condition | verdict |
|---|---|---|
| 1 | last_assistant_message ends on a line ending in ?, not yet blocked for this message |
block with the reroute's reason (working) |
| 2 | owed, set changed or first block this session | block: next item and count (working) |
| 3 | owed, set unchanged, qualifying wake, and no capped item owed or the wake count below two | allow (waiting; next look kept; the wake count adds one if a capped item is owed) |
| 4 | owed, set unchanged, qualifying wake, a capped item owed and the wake count at two | block: next item; the set has not moved over two wakes, so a wake no longer releases this stop (working) |
| 5 | owed, set unchanged, no qualifying wake | block: next item, and if it cannot move, take another or schedule a look before the deadline (working) |
| 6 | none owed, qualifying wake | allow (waiting; next look kept) |
| 7 | none owed, no qualifying wake | block: take the next ready item first (reserve it), and only if none can be taken alone, schedule a look before the deadline (working) |
So a */10 * * * * cron set once and an unchanged behind pull request give
two waiting spells of up to ten minutes each, then blocks until the item
moves or the host's cap records the stop; each later wake starts a turn that
is blocked the same way, and the identical stops collapse into one recorded
entry with a count.
- An item that needs a push (dcn-2610181016510153, refined by
dcn-2610181035355741). Without
--hold-pushesthe reason says: do the local work, then push if your standing instruction allows; queue only if the person has said pushes wait, and a push you may not send stays owed, so queue it withabcd mode queue "<step>"for the person and take another item meanwhile. With--hold-pushesit says: the person has said pushes wait; finish the local work (the merge from the base, the preflight), then queue the push naming the branch. It never says the away mode allows the push. stop_hook_activedecides no row, so both values are covered by every row. It alone drives the count (dcn-2610181003484631, refining dcn-2610180936577720): a block with itfalsesets the count to one, and with ittrueadds one. A change in the owed set no longer resets the count, since the host's count does not: in the round-2 scenario a renewal falling due at the sixth block would have reset abcd to one, and the host's override at its ninth would have found abcd at three, unrecorded. The block whose count passes the cap (eight, orCLAUDE_CODE_STOP_HOOK_BLOCK_CAPwhen set) records "stopped with work owed" (rows 2, 4 and 5) or "stopped with no next look" (row 7) in the away record, labelled "possibly forced by the cap", because the host's count also resets on a tool call abcd cannot see. The count so errs toward recording a stop that did not happen, never toward missing one, and step 7 checks where the host's count resets.- Present. With owed work (the disk kinds only), allow with a
systemMessagenaming the work (stopped, flagged); with none, allow silently. No forge call. - The front door.
abcd hook stopmaps the payload and writes the host's JSON. A malformed payload, an unreadable record or a lock not had allows the stop and says so on stderr, following the guard's fail-open-loud contract. A stop is never blocked on abcd's own fault. The Stop entry inhooks/hooks.jsoncarries a timeout of 30 seconds, room for the ten-second listing and the git reads. - StopFailure (dcn-2610180937175629).
abcd hook stop-failure, while the record holds, records "stopped on an API error" with the error type, under the lock. It cannot continue the session; the scope conditions name the gap.
The question reroute
questionGate (internal/surface/cli/guard_question.go) reads the away
record first. While it holds, it denies every question-tool call, abcd's or
another tool's, with one reason whatever the chip (dcn-2610180937177502).
The classification is by the act asked about, which the reason lists, so the
agent routes it (dcn-2610181003477194, extended by dcn-2610181016510153):
- Queued for the person with
abcd mode queue "<step>", carrying on with other work meanwhile, is a question about an act only the person may approve:- an approval or ruling the repository binds to the person per act: an
embargo close's
--approve <digest>, whichcommands/embargo.mdsays is passed only after the person, in this session, has read the text and said yes, and never supplied on the agent's own; the embargo triage verdict ("the verdict is the person's decision",commands/embargo.md); the drain's hand-back ("a hand-back is a person's decision",commands/drain.md); the confirmed apply verb for remote configuration (brief invariant 10); a release sign-off; - pushing, opening or merging a pull request without a standing instruction of the person's that allows it; the away mode is never that instruction;
- publishing anything, changing a repository's visibility, or posting a comment or message outward, other than a pull request a standing instruction allows;
- adding a dependency;
- a secret, a credential or an App;
- a step only a website or a browser can do;
- a destructive act: deleting what the session did not create, overwriting uncommitted work, a force push, discarding a peer's work;
- anything that spends money;
- any other act a command page or a brief invariant names as the person's.
- an approval or ruling the repository binds to the person per act: an
embargo close's
- Decided and recorded with
abcd decide, with its grounds and how to reverse it, is every other question.
A release sign-off is a cut, or a step of one, that asks for the person by name, or that no standing instruction of theirs allows. A standing permission for automatic cuts stands; the away mode adds none.
Nothing detects the act mechanically. The agent classifies its own question from the refusal's list, and the guard's existing entries enforce only what they name, a force push and a skipped hook among them; the rest of the list rests on the agent following it, as the person-only rules on the command pages already do.
The away block repeats this list and that the mode grants nothing. No mode is set and no question is marked open.
Settled here
Choices made while building step 1 on 2026-10-18 where this record is silent, each the narrowest reading that meets the step. Each is the build lane's choice, recorded for review; none changes a test the step names.
- 2026-10-18 (lane's choice, for review): Setting the mode again keeps
what is unread.
abcd mode autonomouson a record that holds, or that lapsed and is not yet closed, writes a new record (who set it, from which copy, a new start and end,--hold-pushesas given this time) and carries over its queued steps, handed-back items and recorded stops, since nothing has printed them yet. The seven-day cap counts from the new start: setting the mode is a deliberate act, and the cap is there for a forgotten record. A closed record's items, already printed byabcd mode back, are not carried. - 2026-10-18 (lane's choice, for review):
abcd mode queueneeds a record that holds. With none, or one lapsed, it is refused (exit 2) and the reason says to ask the person instead, since there is no return to queue for. A step's whitespace is folded so it prints as one line, and a step over 500 characters is refused, so one step cannot fill the 64 KiB record. - 2026-10-18 (lane's choice, for review): Who set it. The holder name
reservations resolve with no network read (
claim.ReaderHolder), else the name git would author a commit with (identity.EffectiveIdentity, which readsuser.nameafter git's own overrides), else nothing, shown as "someone git does not name". The working copy is its root as git names it, shown in the tilde form. - 2026-10-18 (lane's choice, for review): Every refusal is exit 2 with nothing written, a lock not had within two seconds included: each is "this invocation cannot be honoured", as the badge's refusals are.
- 2026-10-18 (lane's choice, for review): The lock is checked on every read, not only on writes. A lock file that is a symlink, another account's, writable by others or over 64 KiB reads as not away for the hooks as well as refusing the verbs, as TestRecordOwnershipFailsOpen names. Reading never creates the store; the first write creates its folders owner-only.
- 2026-10-18 (lane's choice, for review): "Shown in the away block" is
Record.Terms. Step 1 has no away block yet (step 2 prints it), so the record's plain statement (until when, set by whom from which copy, that pushes wait when--hold-pushesis set, that the mode grants no rights, and how it ends) isRecord.Terms, whichabcd mode autonomousprints and step 2's block can repeat. - 2026-10-18 (lane's choice, for review): Sibling files go through the
store. The created-branch list (step 3) and the session's start-copy
note (step 2) live in the same folder;
Store.ReadFileandStore.WriteFile, called insideStore.WithLock, give them the record's ownership, size and atomic-write terms. Their names and shapes are those steps' to choose. - 2026-10-18 (lane's choice, for review): The badge's refusal reads the
record at the verb.
mode.RefuseParkWhileAwayis pure and the CLI reads the away record for it, sointernal/core/modekeeps no knowledge of the away store; the guard's chip-set path is left to step 6, whose gate denies every question while away before that path is reached. - 2026-10-18 (lane's choice, for review):
abcd modegets its own brief chapter,04-surfaces/26-mode.md, beyond the step's named packages. Once the verb registers sub-verbs, record-lint'ssurface_coveragerule requires a chapter named for it with a sub-verbs table, so the away mode's surface text lives there, the register's row 26 points at it, and08-abcd.mdkeeps the badge and points at the new chapter. The three sub-verbs' sentences and the queue's worked example go ininternal/core/surface, as every visible verb's do. - 2026-10-18 (step 6 lane's choice, for review): The away check comes before decoding. "Every question-tool call" is read literally: a questions field the check cannot read is refused with the same reason while away, rather than running on the loud fail-open status, since a question nobody is there to answer stalls the session whatever its shape. A payload whose outer JSON does not decode still fails open, because the hook then has no working directory to find the record by.
- 2026-10-18 (step 6 lane's choice, for review): The reason names none of
the asking rules. Its head line says the person is away and that no
question is put to them whatever its chip and whoever wrote it, then
autonomy.QuestionRoute: the queue route with the person-only acts, the decide route, and that nothing detects the act. A reason that also listed the question's field findings would invite a redraft instead of a reroute. - 2026-10-18 (step 6 lane's choice, for review): The text lives in
internal/core/autonomy.PersonOnlyActsandQuestionRouteare additive exports beside the record, so step 2's away block and step 5's row 1 ("block with the reroute's reason") can repeat the same list rather than a copy that drifts. - 2026-10-18 (step 6 lane's choice, for review): An unreadable away record is loud at the question gate. A record or lock the store refuses reads as not away, the question is checked as if the person were present, and one line names the record: folded into the reason when the gate denies, and with exit 1 (the guard's fail-open-loud status) when it would otherwise admit silently. Where the gate already answered on stdout with the rows note, it keeps exit 0, since exit 1 would drop the note. Outside a checkout, with no root commit or no home, nobody is away and nothing is said.
- 2026-10-18 (step 6 lane's choice, for review): The badge's chip-set path
needs no code of its own. The away check returns the deny before the
chip is read, so no mode is set and no question marked open while away;
TestAwayQuestionSetsNoMode holds it. Beyond the step's named packages,
commands/guard.md,commands/mode.mdand the guard hook's help text gain a sentence each, since each describes the gate and would otherwise misstate it while away. - 2026-10-18 (lane 2's choice, steps 3 and 4, for review): The
created-branch list holds a hundred names, the oldest dropped
(
autonomy.MaxCreatedBranches): a hundred names at git's 255-byte limit stay well within the store's 64 KiB per file. A rename keeps the record's place and time. - 2026-10-18 (lane 2's choice, for review): The recorder reads literal
words only, and runs before the registry loads. A word a substitution or
a variable prints makes the command no act;
git -C <dir>is stepped over, not followed, and every name is judged against the session copy's repository. It runs ahead of the registry, so a disabled or unloaded registry does not stop a branch being recorded.git worktree add <path> <commit-ish>records the name when it is a branch or resolves to nothing (git then creates it), never a tag or an object name;--detachand--orphanrecord nothing;git branch -c|-Cis recorded as a creation. - 2026-10-18 (lane 2's choice, for review): The copy is the payload
cwd's worktree until step 2's start-copy note lands. The recorder resolves it in one function (awayCopyRootininternal/surface/cli) for step 2 to route through its note, andautonomy.ReadOwedtakes the copy root from its caller (ReadOptions.Root). - 2026-10-18 (lane 2's choice, for review): The away listing lists every
open pull request, not only the person's, so a branch whose pull request
another person authored can be told apart: the person is the holder name
reservations resolve without a network read (
claim.ReaderHolder), and when that is unknown the listing asks--author @meand that rule is not applied. At most 200 are listed. The ADR's decision says "the person's open pull requests"; invariant 7's clause says "the open pull requests". - 2026-10-18 (lane 2's choice, for review): "Recent" is a commit beyond
the default branch whose author email is this checkout's, made or
rewritten (the later of its author and committer dates) within 14 days.
A live reservation links a branch by its name too (the record id on word
boundaries), not only through a pull request. The reflog route reads both
ends of each
checkout: moving from A to Bline and the branch checked out now. - 2026-10-18 (lane 2's choice, for review): A stacked branch whose base
has moved on is still found without a pull request: the branch they share
a commit with, outside the default branch, is its base when that commit
was the other branch's own tip by its reflog (set there other than by the
branch's creation, which tells a base from a branch cut from it). A stacked
branch is one
stackeditem naming what it needs, never a separate behind or failing item; a stacked pull request with no checks reported, where no receipts are minted, is not verified and owed. - 2026-10-18 (lane 2's choice, for review): When the listing fails, "a
branch with a pull request" is read as an own branch with an upstream that
is not gone, compared on disk with the last-fetched default branch; the
fetch time is the default branch's reflog, else
claim.LastFetched. - 2026-10-18 (lane 2's choice, for review): A release reservation
(
rel-…) is never owed as uncarried work, since no pull request carries a release; its renewal due still counts. - 2026-10-18 (lane 2's choice, for review): A queued step names a branch
as a whole name in which
.,_,/and-continue the name, sofeat/pis never found infeat/p-2: stricter than the record id's word boundary, where-ends the id sofix/iss-5-xnames iss-5.ReadOwedrecords each hand-back itself (Store.RecordHandBack), and the unpushed item's reason is the fact alone; the push wording is the verdict's (step 5). - 2026-10-18 (lane 2's choice, for review): The run states are read
leniently, for their lanes alone, rather than through the loop's strict
decoder: a run counts while a spec step is pending or a lane is not done,
and a lane counts while its stage is not done, discarded or handed back.
landing.GHContextwas added besidelanding.GHto bind the forge client to the ten-second deadline, and the guard'sabcd guardandabcd guard hooksentences now name the branch record they write.
Choices made while building steps 2 and 5 on 2026-10-18, by build lane 1, each recorded for review; none changes a test the steps name.
- 2026-10-18 (lane 1's choice, for review): The start-copy note. The
session-start hook notes a session's start worktree in the away store's
sessions.jsononly in a checkout with.abcd/at its root, and only the first time for a session id, so a compaction or resume run after the session changed into a worktree it made keeps the start. Notes older than 30 days, and past 256, are dropped.autonomy.ResolveCopyreads it back for the stop check and for step 3's recorder. - 2026-10-18 (lane 1's choice, for review): The prompt hook's read. It reads the away record through the live drain's root-commit lookup, so the router still makes one such lookup a prompt, and appends the away block after the rules, the memory notes and the status block. The block's words come from the owner-only home store, never a tracked file, which is why the session-start hook may print it on stdout where its notices stay on stderr.
- 2026-10-18 (lane 1's choice, for review): The person's lines. While
the record holds: "abcd: away until <time>, set by <who> from <copy>; N
steps queued, M items handed back.
abcd mode backends it." (with "pushes wait for you" under--hold-pushes). Lapsed and unclosed: one line counting the queued steps and recorded stops, as the plan names, not the hand-backs. Both areRecord.PersonLineandRecord.LapsedLine. - 2026-10-18 (lane 1's choice, for review): The owed seam. The plan
names no reader, so the stop hook takes the owed set through one function
value,
stopOwedReaderininternal/surface/cli/hook_stop.go, shaped as step 4'sautonomy.ReadOwed(asked with the start copy, the record, the home and the clock; the record not holding means disk only). Until step 4's reader fills it, it reads nothing owed. Recording a hand-back is idempotent per branch and head, so the reader and the front door may both record one. - 2026-10-18 (lane 1's choice, for review): Two fields on
Owed.NextRenewal, the earliest renewal due among the live reservations under this copy's key, due or not yet, which the deadline needs and no owed item carries; andHandedBack, the items a queued step handed back, which the verdict judges. Both are additive. - 2026-10-18 (lane 1's choice, for review): The hand-back at the
verdict.
Judgeaccepts a hand-back only while away, under--hold-pushes, and only of an unpushed branch's item naming its branch and head; any other item the reader hands back is put back among the owed items. The front door records each accepted one under the lock. - 2026-10-18 (lane 1's choice, for review): Rows the plan leaves open. The prose-question block (row 1) does not take the owed set as seen, so the next check names it; at the cap it records nothing, since the plan names rows 2, 4, 5 and 7. A block past the cap leaves the session's state "stopped". The push sentence goes with an unpushed branch, an uncarried reservation, and a behind or failing pull request, the items whose next act the plan calls a push. The next look is the earliest-matching qualifying wake; a recurring task with no following match by the deadline plus an hour is taken as up to 30 minutes late.
- 2026-10-18 (lane 1's choice, for review): The parser's edges. Names
(
MON),@forms and a step on a single value are not read, so they never qualify. The day-of-month and day-of-week "written from*" rule takes a field starting with*. - 2026-10-18 (lane 1's choice, for review): abcd's own faults allow.
Present, the session's state is neither read nor written. Away, a session
state that cannot be read or saved, and an owed set the reader could not
return, allow the stop with a stderr line, as a malformed payload does. A
StopFailure error type outside
[a-z][a-z0-9_]{0,63}is recorded asunknown.hooks/stop.shandhooks/stop-failure.shnever download the binary, ashooks/subagent-stop.shdoes not, and exit 1 when it is missing, never the Stop event's blocking 2; the brief's hook paragraphs name them among the exceptions. - 2026-10-18 (lane 1's choice, for review): One text for the person-only
acts. The away block and the prose-question block carry
autonomy.QuestionRoute(), step 6's statement of the list. Until that lane's branch merges,internal/core/autonomy/question_placeholder.gostands in for itsquestion.gowith exactly its text; it is deleted at the merge.
Choices made while integrating the three lanes on 2026-10-18, each recorded for review; none changes a test a step names.
- 2026-10-18 (integration's choice, for review): The placeholder went in
the merge. Step 6's
question.goand lane 1'squestion_placeholder.godeclared the same names word for word, so the placeholder was removed in the merge commit that brought step 6 in, to keep every commit building. - 2026-10-18 (integration's choice, for review): The stop hook owns the
hand-back record. Lane 2's reader recorded a hand-back as it found one,
and lane 1's front door recorded each the verdict accepted.
ReadOwednow records nothing: it moves the handed-back item fromOwed.ItemsintoOwed.HandedBack, the verdict accepts it (Verdict.HandBacks), and the stop hook records what was accepted under the lock, once per branch and head. One owner, and an item the verdict puts back among the owed is never recorded as handed back. This replaces lane 2's "ReadOwedrecords each hand-back itself" and lane 1's "the reader and the front door may both record one" above.ReadOwedalso fillsOwed.NextRenewal, present or away, from the same halfway point its renewal items use. - 2026-10-18 (integration's choice, for review): The seam is wired. The
stop hook's
stopOwedReaderdefaults toautonomy.ReadOwedand is asked withautonomy.ReadOptions; lane 1's stand-in type is gone. - 2026-10-18 (integration's choice, for review): The recorder keys on the
start copy.
awayCopyRootresolves the copy throughautonomy.ResolveCopywith the hook payload's session id, falling back to the payload cwd's worktree when the session has no note or the store cannot be opened, so the branch list and the stop check read the same copy's key. - 2026-10-18 (integration's choice, for review): The ADR says what the listing covers. adr-2610181003541997, still proposed, now says the listing covers every open pull request when the person's forge login is known, and the person's own alone when it is not, as lane 2's reader does and invariant 7's clause now says too; lane 2's note above on the mismatch is answered by it.
- 2026-10-18 (fix round 1's choice, for review): "Managed" reaches a
linked worktree's main worktree. Every away hook and the recorder act
only where
.abcd/is a directory at the checkout's root or, for a linked worktree, at its main worktree's root (autonomy.Managed). The second half keeps a session that changed into a worktree it made (whose branch may predate.abcd/, or whose.abcd/was never committed) checked; onegit rev-parse --git-common-diris the only cost, paid only in a linked worktree with no.abcd/. A checkout whose.gitis a folder with no.abcd/beside it is told from the marker alone, with no git. The recorder now asks the same question instead of the local tier, which a fresh worktree lacks. - 2026-10-18 (fix round 1's choice, for review): A landed no-upstream
branch is told by a merge first.
git cherryalone misses a squash of several commits (its patch matches none of them), so the reader first merges the branch into the last-fetched default branch without writing anything (merge-tree --write-tree, merge drivers replaced as the conflict probe replaces them): no change is landed, a change is owed. Only a conflicting merge falls togit cherry, landed when every commit is upstream by patch id, and otherwise a note. So a genuinely unpushed branch whose change conflicts with the default branch is a note, not an item: the phantom-stop failure the review found is worse than this one, which other items (its reservation, uncarried) still cover. - 2026-10-18 (fix round 1's choice, for review): The record's parts have
their own bounds. Stops past 24 KiB (JSON) let the one seen least
recently go, and hand-backs past 8 KiB the oldest, each counted
(
dropped_stops,dropped_handed_back) and printed bymode back; the queue past 24 KiB refuses the next step (ErrQueueFull, exit 2), since a queued step is the person's to read and is never cut. Only Begin carrying, or Back closing, a record already past 64 KiB lets queued steps go (dropped_queue), and Back prints every line first. Three fields are added to the record, omitted when zero;schema_versionstays 1. - 2026-10-18 (fix round 1's choice, for review): Back replaces what it
cannot trust. A record that does not parse, names another schema or is
not safely this account's (a symlink, writable by others, another
account's) is replaced by a closed record through the store's atomic
writer, which replaces a link rather than writing through it, and nothing
from it is printed. A record with parts of the wrong shape is read for the
rest. A lock not had within its two seconds, or not safe to take, is gone
around: the write is atomic, and a re-read catches a writer that held the
lock and wrote after, closing again (three tries).
autonomousandqueuestill refuse such a record, namingmode back. - 2026-10-18 (fix round 1's choice, for review): A due renewal's deadline
is its lapse. The reader reports
Owed.WakeBy: each live reservation's renewal while not yet due, and its lapse once due, the earliest. The deadline reads it less the skew (falling back toNextRenewalfor a set built without it), so a renewal only due releases on a wake that fires before the lease lapses, as "Owed work" has it. - 2026-10-18 (fix round 1's choice, for review): The budget counts from
the hook's start. The owed read has 14 seconds from the hook's start
(the root lookups included); the forge listing's ten seconds sit inside
it. A reading cut short sets
Owed.Incomplete;Judgethen allows the stop with a line naming what was not read and, while away, records it ascheck-incomplete. This changes the design in one respect: under heavy load a stop with work owed may now be allowed, recorded and shown, where before the hook was killed at 30 seconds and the stop went unrecorded.
Footprint
- packages: internal/core/autonomy, internal/core/mode, internal/core/guard, internal/surface/cli, hooks, commands/mode.md, commands/guard.md, .abcd/development/brief/02-constraints/03-invariants.md, .abcd/development/brief/04-surfaces/08-abcd.md, .abcd/development/brief/04-surfaces/17-guard.md
- tests: internal/core/autonomy, internal/core/mode, internal/core/guard, internal/surface/cli
Steps
- The away record, its store, its lock and its verbs
- packages: internal/core/autonomy, internal/core/mode, internal/surface/cli, commands/mode.md, .abcd/development/brief/04-surfaces/08-abcd.md
- tests: internal/core/autonomy, internal/core/mode, internal/surface/cli
- Watched failing first: TestAwayRecordSharedAcrossWorktrees (
abcd mode autonomous --until 09:00writes one record in the away store that a sibling worktree reads, with who set it and from which copy); TestUntilClockTimeAndTimestamp; TestUntilRefusedPastOrNoTierOrNoCheckout (nothing written); TestStaleRecordCapSevenDays (an open-ended record reads as ended seven days on, and--untilbeyond that is refused); TestModeBackPrintsQueueAndStops (one plain line per queued step and per recorded stop, the "possibly forced by the cap" label included, and the ender and copy recorded; also on a record whose time has passed); TestRecordPastEndReadsEnded; TestQueueAppends; TestHoldPushesRecorded (--hold-pushesis kept in the record, shown in the away block, and grants nothing); TestRepeatedStopCollapses (a stop identical to the last recorded one adds to its count and last time instead of a new entry); TestConcurrentWritesUnderLock (twomode queuecalls racing keep both steps, and a stop recorded aftermode backre-reads inside the lock and does not turn the mode back on); TestRecordOwnershipFailsOpen (a record or lock that is a symlink, not this account's, writable by others, or over 64 KiB reads as not away, and a verb refuses naming it); TestBadgeRefusedWhileAway; the badge's three-word vocabulary test passes unchanged. The plugin page and the brief'sabcd modesurface text describe the three verbs and--hold-pushesand say the mode grants no rights, the person's standing instruction deciding what is pushed, andgo generate ./internal/surface/clirefreshes the derived appendix. This step also fixes the shapes ofRecordand ofOwed(the owed items, each with its kind, whether it is capped, and its branch or record id) ininternal/core/autonomy, so steps 3 and 4 and steps 5 and 6 can run in two lanes against them. - landed: 2e859ad4b
- Repeated to the agent, shown to the person
- packages: internal/surface/cli
- tests: internal/surface/cli
- Watched failing first: TestSessionStartAwayBlockEverySource (
startup,resumeandcompactprint the away block while the record holds, nothing when it does not, and the block says the mode grants nothing, carries the person-only act list and the rule to runabcd mode backonly on the person's word); TestPromptRouterAwayBlockEveryPrompt (a second prompt in the same session too, outside the loader's dedup ledger); TestPromptRouterAwaySystemMessage (one line the person sees on every prompt while it holds, naming who set it, from which copy, the count of queued steps and of handed-back items, and the literalabcd mode back); TestSessionStartNotesCopy (the session's start worktree is noted per session id, and a later hook run from a worktree the session made reads the start copy's key); TestPromptRouterLapsedWindowLine (a lapsed, unclosed record with a queued step or a recorded stop shows one line namingabcd mode backon every prompt untilmode backcloses it, and one with neither shows nothing); all inside the hook's existing deadline. - landed: 472488938
- The created-branch recorder
- packages: internal/core/guard, internal/core/autonomy, internal/surface/cli, commands/guard.md, .abcd/development/brief/04-surfaces/17-guard.md
- tests: internal/core/guard, internal/core/autonomy, internal/surface/cli
- Watched failing first: TestBranchRecorderOnCreate (
git worktree add -b feat/x ../x,git switch -c feat/yandgit checkout -B feat/zrecord the branch under this copy's key, whether or not the person is away, only when no branch of that name exists, andgit checkout -b mainon an existingmainrecords nothing); TestBranchRecorderOtherForms (git branch feat/w, a baregit worktree add ../vrecordingv, andgit worktree add ../u feat/uon an existing branch are recorded;git branch -m feat/w feat/w2carries the record;feat/xis not matched insidefeat/x2; the file stays within its cap); TestBranchRecorderNeverRefuses (the command is allowed whatever the recorder does, and a store it cannot write leaves a stderr line); TestBranchRecorderUnderLock (two recordings racing keep both). commands/guard.md and the brief's guard chapter say the hook records a created branch and refuses nothing for it. - landed: 710b9626b
- Owed work, and invariant 7's clause
- packages: internal/core/autonomy, .abcd/development/brief/02-constraints/03-invariants.md
- tests: internal/core/autonomy
- Watched failing first, against real git repositories in temp folders
and a fake forge listing pinned to
internal/ghcontract's fields: TestOwnBranchFromReflogWithoutWindow (checked out before the mode was set, with a recent commit of this checkout's, still own); TestOwnBranchFromRecordedCreation (a branch cut withgit worktree add -b, absent from this copy's reflog, with no upstream and no record id, is own once recorded); TestOwnBranchFromLiveRunLane (a lane of a run whose reservation is live under this copy's key is own; a lane of a run whose reservation lapsed, or of a finished run, is not); TestOwnBranchFromLinkedReservation; TestStaleOwnBranchNotOwed (a reflog or recorded branch whose newest commit of this checkout's is older than 14 days, with no live reservation, is not owed); TestReviewCheckoutNotOwn (a branch checked out to review, holding only another author's commits, and the head of a pull request another person authored, are never own); TestSiblingBranchNotOwn; TestReservationCopyKeyFilter (a sibling copy's live reservation is not owed); TestReservationLinkByHeadOrClosingTrailer (the record id in the head branch or aResolves:/Delivers:line links; a green linked pull request is not owed until renewal is due); TestRecordIDWordBoundary (iss-5 does not match iss-50 in a branch name or a trailer); TestRefsLineCarriesOnlyOwnBranch (aRefs:line on a sibling copy's pull request neither carries the work nor makes its branch own; on an own branch's pull request it carries it); TestBehindFromMergeState (a pull request whose merge state isBEHINDis owed while the local default branch is stale and shows it current; oneDIRTYis owed as behind; oneCLEANis not;UNKNOWNreads as not read); TestStackedBranchFoundWithoutPullRequest; TestStackedBranchClearingRule (owed while behind its base, failing, with a review outstanding, or unverified; waiting on its base otherwise); TestClearingRuleReview (CHANGES_REQUESTEDin the listing, and a run lane with no passing review of its head, are owed); TestClearingRuleWithoutReceipts (in a repository with no receipt folder, passing checks verify; with no pull request the reason says it was not verified); TestUnpushedSkipsGoneUpstream (a squash-merged branch whose upstream is gone is not owed); TestQueuedPushHandsItemBack (without--hold-pushesa queued step hands nothing back, the unpushed branch staying owed; with it, a step naming an unpushed branch whose head contains the base's last-fetched tip and has a receipt hands back that branch's unpushed item, and the hand-back is recorded once per branch and head; with it, none is handed back when the head lacks the base tip, has no receipt, or the repository mints no receipts; nor a pull request behind,DIRTYor failing whose fix is committed locally; nor a stacked branch the clearing rule owes; nor one withCHANGES_REQUESTED; nor a reservation queued by record id alone; nor a renewal due; iss-5 queued does not hand back iss-50); TestPresentReadsDiskOnly (zero forge calls while present); TestForgeListingDeadline (a hung listing ends at ten seconds, and the check fails open to the disk kinds with a "not read" note: a branch with a pull request is compared on disk naming the fetch time, and a reservation is never reported as uncarried); TestOwedOrderFixed. Brief invariant 7 gains the away mode's one listing per check and its deadline, citing adr-2610181003541997. - landed: 1ad31a522
- The stop verdict, the Stop and StopFailure hooks
- packages: internal/core/autonomy, internal/surface/cli, hooks
- tests: internal/core/autonomy, internal/surface/cli
- Watched failing first: TestJudgeRows (each of the seven away rows and
both present rows, each with
stop_hook_activetrue and false); TestWakeReleasesUnchangedSetTwice (with*/10 * * * *and an unchanged behind pull request, the first two stops with the wake are allowed and the third is blocked, and so is each later one; the same for failing, unpushed, stacked-not-clear and uncarried items; a set of only a renewal due, or of a stacked branch waiting only on running checks, keeps releasing; a renewal falling due between the wakes neither resets nor adds to the count; a change in the capped items resets it; the count and its fingerprint are read back from the session's state file by a new hook process); TestJudgeEscapeBoundReproducesNoStall (owed, unchanged,true, no wake: blocked, not allowed); TestJudgeCapRecordsStop (the block passing the cap, and an override from the environment, records the stop labelled "possibly forced by the cap"); TestCountResetsOnlyOnInactive (a change in the owed set at the sixth block does not reset the count, and the ninth consecutive block still records the stop; a block withstop_hook_activefalse sets it to one); TestPushItemReasonDefersToStandingInstruction (without--hold-pushesthe reason for an unpushed branch says to push if the standing instruction allows, to queue only if the person has said pushes wait, and that a queued push stays owed; with it, to finish the local work and then queue; neither names the away mode as permission); TestDeadlineCeiling (with work owed and no reservation, now plus 30 minutes; with nothing owed, now plus one hour; with a renewal due sooner, that minus the skew); TestCronNextFire (lists, ranges, steps, both day fields, an unparsable expression never qualifies); TestCronDayOfWeekSeven (7 matches Sunday as 0 does); TestCronRangeStep (10-50/20matches minutes 10, 30 and 50); TestCronStarStepInDayField (0 9 */2 * 1matches only days that are both odd-numbered and Monday, since a field written from*counts as unrestricted, while0 9 1 * 1matches either); TestCronDaylightSaving (in Europe/London,30 1 * * *has no match on the spring-forward day and matches once, at its first occurrence, on the fall-back day); TestQualifyingWakeDistantCron (a release reservation with a 24-hour lease claimed at 20:00, and0 7 * * *seen at 21:00: does not qualify); TestQualifyingWakeJitter (an hourly cron whose next match is 3 minutes before the deadline does not qualify;*/5 * * * *and*/10 * * * *do; a one-shot at the deadline does); TestQualifyingWakeRecurringCron (*/30 * * * *with nothing owed does); TestQualifyingWakeAfterLeaseLapse (a wake past the earliest renewal due minus the skew does not); TestBackgroundTaskNotAWake (ashelltask runningsleep 36000or atail -fdoes not count); TestNextLookStored; TestProseQuestionBlockedOnce; TestNothingOwedReason (names taking the next ready item first); TestSessionIDShape; TestHookStopOutput (a block only on a block, written since Diff review round 1 as the Stop feedbackhookSpecificOutput.additionalContext, asystemMessagewhile present; a malformed payload, an unreadable record or a held lock allows with a stderr line); TestHookStopFailureRecords (the error type recorded while away, nothing written while present);hooks/hooks.jsongains Stop and StopFailure entries, the Stop entry with a 30-second timeout, andhooks/stop.shandhooks/stop-failure.shtake the same bootstrap and home checks ashooks/subagent-stop.sh(the hook-shape tests cover them);go generate ./internal/surface/clirefreshes the derived appendix. - landed: 5d754f95b
- The question reroute
- packages: internal/surface/cli, internal/core/autonomy, .abcd/development/brief/04-surfaces/17-guard.md
- tests: internal/surface/cli, internal/core/autonomy
- Watched failing first: TestAwayQuestionDeniedOneReason (a Product-chip,
a Tech-chip, a Setup-chip and an unchipped question, and another tool's,
get the same reason); TestAwayReasonListsPersonOnlyActs (the reason
names every act in the list: the embargo
--approvedigest, the embargo triage verdict, the drain's hand-back, the remote-configuration apply verb, a release sign-off and its meaning, pushing, opening or merging a pull request without a standing instruction, an outward comment or message, and the catch-all; that the away mode is no standing instruction; and the route toabcd mode queuefor those and toabcd decidefor the rest); TestAwayQuestionSetsNoMode (no mode set, no marker written); TestPresentQuestionGateUnchanged. The brief's guard chapter says what the gate does while away, and that nothing detects the act mechanically. - landed: 2d27610df
- The live session capture
- packages: none (evidence only, kept in the local tier)
- tests: a live Claude Code session with the plugin built from the branch
- In a lab checkout, seed one behind pull request checked out before the
mode is set, one stacked branch made with
git worktree add -bin the session, and one unpushed branch, and record the transcript. It must show: the mode set and the away block on start, with the person's line naming who set it andabcd mode back; a manual/compactfollowed by the block again; an attempted stop blocked naming the behind pull request; a reply with no tool call blocked again rather than allowed; the stacked branch's item; the unpushed branch's reason deferring to the standing instruction, and, with none given and no--hold-pushes, the push queued and the item still named; with--hold-pushesand a receipt for its head, the item handed back and the person's line counting it; a cron set for the next morning refused as a wake and one inside the deadline accepted, with abcd's computed fire time compared with thenext runthe host'sCronListrow shows; a*/10 * * * *cron with the owed set unchanged releasing twice and the third stop blocked; nine blocks in a row with no tool call, then the same with one tool call in between, to see where the host's count resets; a question refused with the one reason; the window left to lapse with a queued step, and the next prompt's line namingabcd mode back; andabcd mode backprinting the queue and the recorded stops. Repeat with the mode off to show the stop allowed and flagged with no forge call. Capture before and after the change on the same checkout, as the release lesson on host fixes requires. - landed: d0249f356
Live captures
Run on 2026-10-18, 13:44 to 14:02 BST, with Claude Code 2.1.302
(claude-opus-5-5), against the branch at f403910ac. API spend for all the
runs together: about 1.70 USD.
The harness. Everything sits in one scratch folder, <scratch>, outside
every working tree:
<scratch>/pluginisgit archive f403910ac. Its binary rungabcdis a wrapper that runs<scratch>/abcd-stall, which isgo build ./cmd/abcdat f403910ac, withHOME=<scratch>/home, so the away store and every other file of abcd's lands there. The before capture uses<scratch>/plugin-beforeand<scratch>/abcd-before, the same build at the branch's merge base 646f2fb04.<scratch>/repois the lab checkout. It holdsCLAUDE.mdwith abcd's marker block,.abcd/config.jsonand a gitignored local tier. Its bare origin is<scratch>/origin.git.feat/xwas made in this checkout withgit checkout -b, so its HEAD reflog holds it, then pushed once, then given a second commit byuser.emailthat was not pushed. The checkout then went back tomain.<scratch>/pathbincomes first on PATH. It holds a stubghthat logs every call and answerspr listwith[], so no real forge is touched. It also holds the sameabcdwrapper, for the session's ownabcd mode queue.claude -pruns with--plugin-dir,--setting-sources project,local,--output-format stream-json --verbose --include-hook-events,--no-session-persistence,--permission-prompts none, a narrow--allowedTools(nevergit push, so no capture shows a push clearing an item) and--max-budget-usd 3(<scratch>/run-session.sh). The runs that need several prompts in one session, or/compact, use stream-json input through<scratch>/drive.py, which sends each prompt only after the turn before it has ended.- Transcripts are
<scratch>/transcripts/<n>.jsonl, with.stderrbeside each, and<scratch>/summarise.pyturns each into<n>.summary.txt. Nothing under the account's own Claude configuration was edited.
The person's ! command and a relayed ! are not checked here. Those checks
moved to the checklist draft with the guard entries ("Trim after round 2").
0. Before the change (0-before). The merge-base plugin, the same lab and
the same owed feat/x, with the prompt "Say hello and stop." There is no Stop
hook: the session answers "Hello!" and the turn ends (turns=1). This is the
stall.
1. Present, no away record (1-present). The prompt is "Say hello and
stop." SessionStart and the prompt hook add nothing. The Stop hook allows the
stop with {"systemMessage":"abcd: stopped with work owed (1 item); next: branch feat/x has commits not pushed …"}, and the host shows it as "Stop
says: …". The stub gh was never called. Verdict: as designed.
2. Away, no standing instruction to push (2-away). The record was set
with abcd mode autonomous --until 15:45 (two hours ahead), then the same
prompt was sent.
- SessionStart:startup returns the
## AWAYblock, with "grants no rights", the person-only list and "Runabcd mode backonly on the person's word in this session". - The prompt hook's
systemMessageis "abcd: away until 2026-10-18 15:45 BST, set by Alice from repo; 0 steps queued, 0 items handed back.abcd mode backends it.", and the host shows it to the person. - The first Stop is blocked:
{"decision":"block","reason":"abcd: the person is away and work is owed (1 item). Next: branch feat/x has commits not pushed … Do the local work, then push if your standing instruction allows; queue only if the person has said pushes wait. A push you may not send stays owed: queue it with abcd mode queue …"}. - Claude goes on. It says the away mode is no standing instruction and runs
abcd mode queue "Push branch feat/x …". - With no
--hold-pushes, the queued push hands nothing back. The next stop is blocked again, adding "If it cannot move, take another or schedule a look before 14:15 BST", which is the 30-minute ceiling. - Every later reply has no tool call, and each one is blocked rather than allowed. After ten blocks the host prints "A hook blocked the turn from ending 9 consecutive times — overriding and ending turn."
- The stub
ghlogged tenpr list … --author @mecalls, one per check. - The away record holds the queued step and one stop,
work-owed,possibly_forced_by_cap: true, with count 2. The two are blocks 9 and 10: abcd's count ran 1 to 10, sincestop_hook_activestayed true across the tool call.
Verdict: as designed. One block, a tool call, then nine blocks with no tool call before the host overrode the stop. So the host's count resets on a tool call and overrides at the ninth block since, which is the eighth continuation. abcd's count does not see that reset, so it records the cap one block early as well as at the real one. That errs toward recording, as the spec says.
3. A question while away. Print mode does not offer the host's question
tool. AskUserQuestion is missing from the session's tool list even with
--tools AskUserQuestion,Bash,Read, and missing again in stream-json input
mode. The live refusal by the host was therefore not captured.
- Live (
3-question): asked to use AskUserQuestion, the session cannot. It follows the away block instead: it decides the choice itself, intends to record it withabcd decide, queues the push, and is blocked to the cap again. That is ten moreghcalls for ten checks. - Hook level (
3b-question-hook): an AskUserQuestion PreToolUse payload was sent throughsh <scratch>/plugin/hooks/pre-tool-use.sh, the command the host runs. It returnspermissionDecision: "deny"with "Blocked by the abcd guard (question tool): the person is away (abcd mode autonomous), so no question is put to them, whatever its chip and whoever wrote it. Route the question by the act it asks about …", followed by the full person-only list and the route toabcd decide.
Verdict: the reroute text is right at the hook. The host side remains to capture in an interactive session.
4. Compaction re-injection (4-compact). Stream-json input sent "Say
hello and stop." and then /compact.
- The first stop is blocked, and Claude queues the push.
- The second stop is blocked and names the deadline "before 14:19 BST".
Claude then calls CronCreate with a one-shot
13 14 18 10 *, and the host answers "Next run: Oct 18 2:13pm". - The third stop is allowed silently. The session state reads
waiting,next_look2026-10-18T14:13:00+01:00(the host's own next run) andwakes: 1. /compact(triggermanual) runs PreCompact, and then SessionStart:compact returns the## AWAYblock again.
Verdict: as designed, captured live.
5. Cron wakes, compared with the host (5-cron). Three prompts went to one
session, each sent after the turn before it ended.
- Turn 1. CronCreate
0 7 * * *. The host's CronList row is "Every day at 7:00 AM (recurring) [session-only], next run Oct 19 7:30am". abcd computes the same latest fire time: the 07:00 match plus the host's lateness, at most 30 minutes. The first stop is blocked (the first block of the session). The second, with only the morning cron, is refused as a wake: it is blocked with "schedule a look before 14:21 BST". Claude then adds*/10 * * * *; the host lists "next run Oct 18 2:01pm" (13:51 now). abcd's bound is the 14:00 match plus half the gap, 14:05, before the 14:21 deadline, and the host's 14:01 falls inside it. That stop is allowed (wake 1). - Turn 2, with the owed set unchanged: allowed (wake 2).
- Turn 3: blocked with "The set has not moved over two wakes, so a wake no longer releases this stop: move the item, or take another." Then nine one-word replies with no tool call. The host overrides at the ninth block, and abcd's count reaches nine at that same block and records the stop.
- 13
ghcalls for 13 checks.
A first attempt sent all three prompts at once. The host merged them into one turn, which gave nine blocks in a row with no tool call and the host's override at the ninth. Its transcript was overwritten by the rerun, and its one recorded stop stays in the record's count.
Verdict: as designed. The */10 wake released twice and the third stop was
blocked, and abcd's fire times agree with what the host lists.
6. --hold-pushes, a stacked branch, the hand-back (6-hold-stacked).
- Setup.
abcd mode autonomous --until 14:01 --hold-pushes(eight minutes ahead; the queue and the stop were kept). A hand-made receipt file namedfeat/x's head went into.abcd/.work.local/preflight-receipts/. - Prompt 1. The session runs
git worktree add -b feat/stacked <scratch>/wt-stacked feat/x. The pre-tool hook records the branch. - First stop.
feat/xis already handed back: an earlier queued step names it, its head containsorigin/mainand has a receipt. Only the new branch is owed: "work is owed (1 item). Next: branch feat/stacked has commits not pushed … With --hold-pushes the person has said pushes wait: finish the local work …, then queue the push naming the branch, as `abcd mode queue "push feat/stacked"`." - The stacked condition itself is not owed: the receipt and an equal base tip meet the clearing rule. So the item shown is the stacked branch's unpushed one.
- Claude queues "push feat/stacked", and the next stop hands that back too. Nothing is owed, so row 7 blocks with "take the next ready item first … schedule a look before 14:54 BST", which is the one-hour ceiling.
- Claude creates a one-shot cron for 14:37 (host: "Next run: Oct 18 2:37pm"), and the stop is allowed.
- Prompt 2. The person's line reads "pushes wait for you; 4 steps queued, 2 items handed back". The stop is allowed silently.
Verdict: as designed.
7. The window lapsed (7-lapsed, after 14:01).
- The prompt hook shows "abcd: the away window ended at 2026-10-18 14:01 BST
with 4 steps queued and 2 stops recorded, not yet read.
abcd mode backprints them and closes it." - The stop is checked as present: allowed with "stopped with work owed (2 items); next: branch feat/stacked has commits not pushed …".
- No
ghcall.
8. StopFailure (8-stopfailure-hook, hook level). Triggering a real API
error was not possible cheaply, so a payload {"hook_event_name": "StopFailure","error":"rate_limit",…} was sent through sh <scratch>/plugin/hooks/stop-failure.sh while away. It exits 0 with no output,
and the record gains {"kind":"api-error","error_type":"rate_limit"}. A
live StopFailure was not captured, so the host's field name is not proven
here.
9. abcd mode back (9-mode-back, run by hand). It prints one line per
queued step (four), per handed-back item ("handed back: feat/x at
512f75b1df8a", the same for feat/stacked), and per recorded stop ("stopped
with work owed, possibly forced by the cap: … (6 times, first … 13:46 BST,
last … 13:52 BST)", "stopped on an API error (rate_limit) …"). It ends with
"the away window ended at 2026-10-18 14:01 BST; closed by Alice from repo".
Not captured, and why.
- The behind pull request: the stub forge answers
[], and a real lab base that requires up-to-date branches was out of reach here. - The host's question refusal live: print mode has no question tool (above).
- A live StopFailure.
- A push clearing an item: pushes were withheld on purpose.
Seen in passing, none of them blocking.
- The item's text repeats itself: "branch feat/x has commits not pushed (feat/x has commits not pushed)", in the reason, in the person's message and in the recorded stop.
- The host marks the first block of each turn with a notification "Stop hook
error occurred · ctrl+o to see", although the hook exited 0 with outcome
successand empty stderr. That wording is the host's, but a person sees it. - The stops' count overstates the forced stops: 6 recorded against 4 the host forced, from the early record when a tool call reset the host's count.
- Bounded by the cap, the blocked session repeats itself. In
3-questionit called the hook "faulty" for keeping a queued push owed, which is the designed behaviour without--hold-pushes.
Re-capture after diff review round 1 (9962906df)
Run on 2026-10-18, 15:05 to 15:10 BST, with the same Claude Code and model,
against the branch at 9962906df. The binary <scratch>/abcd-stall and the
plugin copy <scratch>/plugin (a fresh git archive 9962906df, with the same
wrapper rung) were rebuilt. The away store and the lab's local tier were
cleared, feat/stacked and its worktree removed, so feat/x again has one
unpushed commit (512f75b) and the lab is the managed checkout it was
(.abcd/config.json beside .git). API spend for the four runs: about
0.72 USD. Transcripts: <scratch>/transcripts/2-away-r2, 2b-away-hold-r2,
1-present-r2 and 3-unmanaged-r2, each as .jsonl, .stderr and
.summary.txt.
2-away-r2 (away record set with abcd mode autonomous --until 17:05,
prompt "Say hello and stop.").
- The stop is still prevented. The Stop hook answers
{"hookSpecificOutput": {"hookEventName":"Stop","additionalContext":"abcd: the person is away and work is owed (1 item). Next: branch feat/x has commits not pushed. Do the local work, then push if your standing instruction allows; …"}}, and the session goes on to queue the push. Thirteen Stop hooks ran and none ended the turn until the host's cap: "A hook blocked the turn from ending 9 consecutive times — overriding and ending turn." - The output uses
hookSpecificOutput.additionalContext, with nodecisionorreasonfield. - The host shows no "Stop hook error occurred":
hook errorandStop hook feedbackmatch nothing in the stream-json or the stderr (0 and 0). The onenotificationin the stream is the cap's own (stop-hook-block-cap). - The item names
feat/xonce: "Next: branch feat/x has commits not pushed." The doubled "(feat/x has commits not pushed)" of the earlier run is gone. - The stub
ghlogged 13pr listcalls for 13 Stop hooks, one per check. - The model's own
abcd decidecall was denied by the narrow--allowedTools(apermission_deniedevent); the hook has no part in that.
2b-away-hold-r2 (the same, with --hold-pushes). Without --hold-pushes
the reason only carries the generic abcd mode queue "<step>" placeholder,
so the branch quoting is checked here. The first Stop output ends: "queue the
push naming the branch, as abcd mode queue 'push feat/x'." The branch is a
single quoted shell word. No hook error (0 matches), 10 Stop hooks, 10 gh
calls, ended by the host's cap.
1-present-r2 (no away record). The stop is allowed. The hook returns
{"systemMessage":"abcd: stopped with work owed (1 item); next: branch feat/x has commits not pushed."}, shown as the informational "Stop says: …". No
hook error, and the stub gh was never called (0 calls).
3-unmanaged-r2 (a copy of the lab in a sibling folder with .abcd/
removed and feat/x still unpushed; the away record from the lab was in
force). The Stop hook prints nothing (exit=0, empty output), the prompt hook
adds nothing, the turn ends after one reply ("Hello!"), no abcd text appears
in the stream, and the stub gh was never called.
Verdict: the block holds, the host's error notice is gone, and the item text no longer repeats. One thing to know: a Stop that follows the queued push, when nothing is owed, still continues the session ("take the next ready item first …"), since that wording is a continuation prompt.
Size
Re-estimated after review round 3 at 7.5 working days of work, up from the 5.75 written after the trim (dcn-2610181017056307):
| step | days | after the trim |
|---|---|---|
| 1. The away record, its store, its lock and its verbs | 1.0 | 0.75 |
| 2. Repeated to the agent, shown to the person | 0.75 | 0.5 |
| 3. The created-branch recorder | 0.5 | 0.25 |
| 4. Owed work, and invariant 7's clause | 2.25 | 1.75 |
| 5. The stop verdict, the Stop and StopFailure hooks | 1.75 | 1.5 |
| 6. The question reroute | 0.5 | 0.5 |
| 7. The live session capture | 0.75 | 0.5 |
| total | 7.5 | 5.75 |
Round 3 adds: in step 1 --hold-pushes, the collapsed repeated stops and
fixing the Record and Owed shapes; in step 2 the counts on the person's
line and the session's start-copy note; in step 3 the other branch-creating
forms, the rename and the cap; in step 4 the hand-back's base-tip and
receipt conditions with their real-git cases, DIRTY and UNKNOWN, and the
fail-open terms; in step 5 the wake count and its row; in step 7 the
hand-back and wake-count checks. The rest of the rise is the Sonnet
reviewer's point that 5.75 days was thin for about 60 tests on real-git
fixtures: that reviewer planned about seven days before these additions, the
Opus reviewer found the elapsed figure credible, and 7.5 is the honest
figure with both.
Why not four. About four working days of work is not reachable by
scoping alone. Steps 3 to 5 carry 4.5 days, and every rule in them answers a
review finding: the created-branch, stale-ownership and Refs: rules
(round-2 blockers 3 to 5), the merge state (blocker 2), the deadline and
cron cases (blocker 1), and the hand-back and wake count (round-3 F1 and
F2). Cutting any of them reopens a finding, and the stacked-branch rules are
the issue's second instance. The elapsed time can be shorter: Judge is
pure over the owed set, so once step 1 fixes Record and Owed, steps 3
and 4 (2.75 days) run in one lane while steps 2, 5 and 6 (3.0 days) run in
another, which puts the critical path at about 4.75 days with the capture
after both. Whether to run two lanes is the release orchestrator's choice.
Round 2 estimated eight days for seven steps, round 1 five days for six; the first plan wrote no size down.
Review round 1
On 2026-10-18 an Opus and Sonnet pair reviewed the plan as frozen at 82b928305, with the verdict "fix the plan first". Each item and how it was handled:
| # | finding | handled |
|---|---|---|
| 1 | the ownership rule misses both instances; stacked-branch clearing | fixed: "own" without a window, from the reflog, a run's lanes or a linked reservation; the clearing rule; stacked found without a pull request; gone upstreams skipped (dcn-2610180936568358, dcn-2610180936571036) |
| 2 | the escape bound reproduces the stall | fixed: an unchanged owed set is allowed only with a qualifying wake, else blocked again, the host's cap the outer bound with the stop recorded; stop_hook_active decides no row, so the missing row is covered (dcn-2610180936577720) |
| 3 | a wake's timing is never checked | fixed: next fire time from abcd's own cron parser, before the earliest renewal due minus the skew; background tasks never count; distant, recurring and stale-shell rows tested (dcn-2610180936579498) |
| 4 | the reroute approves acts only the person may approve | fixed: classified by the act with the listed person-only acts, the embargo --approve digest included; the chip no longer routes (dcn-2610180937177502) |
| 5 | stopping while away contradicts the coordination decision (dcn-2610180720541206) | fixed: while away only working or waiting with a qualifying wake; the next look stored in the session's state (dcn-2610180936577720) |
| 6 | concurrent writes lose updates | fixed: fsutil.WithFileLock with a re-read inside it; ownership and permission checks; an unreadable record fails open (dcn-2610180937181736) |
| 7 | a stale record traps the person | fixed in part: capped at seven days rather than 24 hours, since the record's standing run of 2026-10-15 to 2026-10-17 would not fit 24 hours and requiring --until contradicts the open-ended ruling; the person sees a line on every prompt rather than past a threshold; the agent runs abcd mode back only on the person's word (dcn-2610180937181736) |
| 8 | the return list reads one working copy's decisions | moved: the return list leaves the slice with the split, and the fix (abcd decide appending each id to the away record under its lock) moves with it (dcn-2610180937181929) |
| 9 | present mode undefined; per-turn forge call | fixed: present reads disk only with no forge call; away listing under a ten-second deadline with a cache (dcn-2610180936571036) |
| 10 | reservations: no link, no copy filter | fixed: the record id in the head branch or a body trailer links them; only this copy's key counts (dcn-2610180936568358) |
| 11 | StopFailure and prose questions | fixed: a StopFailure entry records the stop and its type, the gap named in the scope conditions; a prose question is blocked once (dcn-2610180937175629) |
| 12 | waiting does not look for other work | fixed: the nothing-owed reason says to take the next ready item first (dcn-2610180936577720) |
| 13 | a refused session can lift away mode for all | fixed in part: abcd mode back records who ended it and from which copy, and the away block allows it only on the person's word; showing it on return moved with the return report (dcn-2610180937181736, dcn-2610180937181929) |
| size | no size written; five days more honest | fixed: re-estimated at five days, per step above |
| split | move step 6 and the return half of step 5 | made: the return report, the status line, the bare print and their brief text moved to the checklist draft; kept here, because the slice's own behaviour needs them, are abcd mode back printing the queue, invariant 7's clause and the shipped verbs' docs (dcn-2610180937181929) |
Review round 2
On 2026-10-18 an Opus and Sonnet pair re-reviewed the plan as frozen at d04bc92fc, with the verdict "fix the plan first". They found round 1's items 2, 5, 6, 11, 12 and 13 fixed, item 7 sufficient, and items 8, 9 and 10 narrowed by the findings below. Each item and how it was handled:
| # | finding | handled |
|---|---|---|
| (a) | away mode as the person's ask to push is not safe: the prompt hook cannot tell a relayed message from the person's, so an agent could grant itself and every copy seven days of push rights | fixed: guard entries refuse every route to setting the mode the guard reads (the binary by any of its names, a go run of cmd/abcd, sh -c, a script file, a shell write into the away store, the file tools), so the person sets it with ! or at a terminal; refused attempts are recorded and shown; the grant is narrow and its exclusions stated (never the default branch, a tag, a release cut, another person's pull request, a review-only branch, a force push); the guard is called a mistake filter, the OS fence the boundary for launched agents only (dcn-2610171112384712); the capture checks the ! path and a relayed !, with the no-push fallback named if they break the premise (dcn-2610181003456444) |
| (b) | the cap count can miss a forced stop when the owed set changes | fixed: the count resets only on stop_hook_active false, a stop at the cap is labelled "possibly forced by the cap", and the capture checks where the host resets (dcn-2610181003484631) |
| (c) | cron in local time is correct; add a capture check | fixed: the capture compares abcd's computed fire time with the host's listed next run (dcn-2610181003455731) |
| 1 | the wake deadline still allows the stall, through long leases and the host's lateness | fixed: the deadline is the earlier of the renewal due minus the skew and now plus 30 minutes while owed (one hour when not); a recurring cron's fire time adds half its gap, at most 30 minutes; TestQualifyingWakeDistantCron is pinned to a 24-hour lease; daylight saving, day-of-week 7, a-b/n and */n in a day field are tested (dcn-2610181003455731) |
| 2 | "behind" is measured against a default branch nothing refreshes overnight | fixed: while away, behind is the listing's merge state; only a branch with no pull request is compared on disk, naming the fetch time (dcn-2610181003464379) |
| 3 | instance 2 is still missed: the stacked drain branches have no upstream, reflog entry, run state or record id | fixed: the pre-tool hook records a branch the session creates (git worktree add -b, git switch -c, git checkout -b) under this copy's key in the away store, only when no branch of that name exists; the capture seeds one (dcn-2610181003463367) |
| 4 | a Refs: line is a mention, not a carrier, and iss-5 matches iss-50 |
fixed: linked by head branch, Resolves: or Delivers: on word boundaries; a Refs: line carries only for an already-own branch and never confers ownership (dcn-2610181003467233) |
| 5 | stale ownership leaves abandoned lanes and pruned branches owed forever | fixed: a run lane counts only while its run's reservation is live under this copy's key, finished runs' lanes never; a reflog or recorded branch only with a commit of this checkout's in the last 14 days or a live linked reservation (dcn-2610181003463367) |
| 6 | the person-only list is incomplete | fixed: the embargo triage verdict, the drain's hand-back, the remote-configuration apply verb, a push or merge beyond own branches, outward comments and messages, and a catch-all added; release sign-off defined beside the standing permission for automatic cuts; stated that nothing detects the act mechanically and the existing guards enforce only what they name (dcn-2610181003477194) |
| 7 | the queue goes unread when the window lapses | fixed: a lapsed, unclosed record with anything unread shows a line naming abcd mode back on every prompt until closed, and the away line carries the literal verb (dcn-2610181003474765) |
| minor | the clearing rule needs abcd's preflight receipt | fixed: where a repository mints no receipts, passing checks verify; with no pull request the reason says unverified (dcn-2610181003488355) |
| minor | the clearing rule leaves out review | fixed: an outstanding review (CHANGES_REQUESTED, or a run lane with no passing review of its head) keeps a stacked branch owed (dcn-2610181003488355) |
| minor | the intent still lists a superseded decision | fixed: dcn-2610180913431985 removed from the intent's list; its successor dcn-2610180937181929 stays |
| minor | mint an ADR for the invariant 7 amendment | made: adr-2610181003541997, proposed |
| size | not a finding; re-estimated for the fixes | eight working days, step by step above |
Trim after round 2
On 2026-10-18 the release orchestrator ruled that the plan take the fallback and be trimmed to what closes iss-2610170619247185, aiming at about four working days, because the security and bug-fix cut waits on this slice. The reviewer added which items stay. Two decisions record it: dcn-2610181016510153 (the away mode grants no push rights; supersedes dcn-2610181003456444) and dcn-2610181017056307 (the trim and what moved).
What stays is what serves the three things the issue needs: (i) a
session with owed work cannot end its turn silently (the Stop hook, the
ownership rules, the qualifying wake, the cap handling); (ii) questions
reroute by the act (decided with abcd decide, or queued when only the
person may approve); (iii) the away record (set, ended, lapsing,
re-injected at start, compaction and prompt, with its lock).
What moved to itd-2610170638184486, as named remaining work, and why:
| moved | why |
|---|---|
the guard entry refusing an agent's abcd mode autonomous, and its routes through sh -c and script files |
the mode grants no rights now, so there is nothing for it to guard in this slice; and the OS fence (spc-2610161751056096) never covers the person's interactive sessions, so it could not become a boundary for the sessions that stalled |
the guard reading go run ./cmd/abcd and built abcd-<goos>-<arch> binaries as abcd, for every entry, the ledger flip included |
a security-relevant widening of what every entry matches, which deserves its own review in the checklist work, not a ride on this cut |
| the shell-write and file-tool refusals on the away store, and the PreToolUse matcher gaining the file tools | they only protected the person-only setting |
the refused attempts: recorded, shown on the next prompt, listed by abcd mode back |
they only reported the person-only setting |
the capture's checks of the person's ! command and of a relayed ! |
they tested the push-rights premise the fallback removes; the checklist work needs them when it makes the mode person-only |
| the away forge listing's ten-minute reuse cache and its stale-cache fallback | tuning: each away check makes one listing under its ten-second deadline, and a failed read judges from disk, naming the pull requests as not read, so nothing is hidden; a run of eight blocks costs up to eight bounded listings |
abcd mode back printing more than one plain line per queued step and per recorded stop |
the report on return is already the checklist draft's; the plain lines keep the queue read |
Added by the fallback (dcn-2610181016510153): the reason for an item that
needs a push tells the session to push if its standing instruction allows
and otherwise to queue the push, never treating the away mode as
permission; a queued step that names the item's branch or record id hands it
back for the window, a branch only while it holds commits to push; and the
person-only acts gain pushing, opening or merging a pull request without a
standing instruction. Without the hand-back, a session with no standing
instruction to push would be blocked on its unpushed branch until the host's
cap. Review round 3 found that hand-back an exemption the session could
grant itself, and narrowed it to --hold-pushes and a verified branch's
unpushed item alone (below).
Each round-2 item after the trim:
| # | round-2 finding | state after the trim |
|---|---|---|
| (a) | away mode as the person's ask to push is not safe | closed by the fallback the review named: the mode grants no push rights, so a relayed word cannot grant any; the guard entries that answered it the other way moved out with dcn-2610181003456444, which dcn-2610181016510153 supersedes. A mode set on a relayed word only keeps sessions working and deciding, and the person sees who set it on the next prompt |
| (b) | the cap count can miss a forced stop | kept: the count resets only on stop_hook_active false, the stop at the cap is labelled "possibly forced by the cap", and the capture checks where the host resets (step 5, step 7) |
| (c) | cron in local time; a capture check | kept: the capture compares abcd's fire time with the host's CronList next run (step 7) |
| 1 | the wake deadline still allows the stall | kept whole: the ceiling, the host's lateness, the 24-hour-lease case and the daylight-saving, day-7, a-b/n and */n cases (step 5) |
| 2 | "behind" against a stale default branch | kept: the forge's merge state while away, disk only for a branch with no pull request (step 4); the listing loses only its reuse cache, and invariant 7's clause and adr-2610181003541997, amended while proposed, say one listing per check |
| 3 | instance 2 missed: the stacked drain branches | kept, in any mode: the pre-tool hook records a created branch, now its own step 3, and it refuses nothing; the capture seeds one (step 7) |
| 4 | Refs: as a carrier; iss-5 matching iss-50 |
kept: linked by head branch or a closing trailer on word boundaries, a Refs: line carrying only for an already-own branch (step 4) |
| 5 | stale ownership | kept: run lanes only while their reservation is live, reflog and recorded branches only with a commit of this checkout's in 14 days (step 4) |
| 6 | the person-only list is incomplete | kept, and extended by the fallback: pushing, opening or merging a pull request without a standing instruction joins it, replacing "a push or merge beyond the session's own branches" (step 6) |
| 7 | the queue goes unread when the window lapses | kept: the every-prompt line and the lapsed-window line, both naming abcd mode back; the lapsed line now counts queued steps and recorded stops, the refused attempts having moved (step 2) |
| minor | the clearing rule needs abcd's receipt | kept: passing checks verify where no receipts are minted (step 4) |
| minor | the clearing rule leaves out review | kept: an outstanding review keeps a stacked branch owed (step 4) |
| minor | the intent lists a superseded decision | unchanged, still fixed; dcn-2610181003456444 is now superseded too and the intent says so |
| minor | mint an ADR for invariant 7 | kept: adr-2610181003541997, proposed, amended to drop the cache from its decision |
| size | re-estimate | 5.75 days, step by step under Size; about four is the elapsed time with two lanes, not the work |
Round-1 items the trim touches: item 9 (the per-turn forge call) stays fixed, since present mode makes no call and the away call is bounded, though no longer cached; item 13 keeps the recorded ender, while showing it on return had already moved; items 6, 7 and 11 are kept whole.
No blocker is reopened. Nothing the review made essential to (i) to (iii) was cut. The items kept against the trim's pressure, and why, are listed in dcn-2610181017056307: each is the fix of a finding above.
Review round 3
On 2026-10-18 an Opus and Sonnet pair reviewed the plan as frozen at 600c109d6, with the verdict "ready to build once F1 and F2 are in". They found the round-2 mapping, the ADR and invariant 7's clause, the recorder and the two-lane estimate sound. Each item and how it was handled:
| # | finding | handled |
|---|---|---|
| F1 | the queued-push hand-back lets the session exempt itself, replaying the first instance: a behind pull request merged locally and queued was handed back and stayed behind all night; it also covered stacked branches, reservations and renewals, and the person's line did not count it | fixed: by default a queued push hands nothing back, the item stays owed and the cap records the stop; only abcd mode autonomous --hold-pushes, which narrows and grants nothing (dcn-2610181016510153 stands), allows a hand-back, and then only of a branch's unpushed item while its head contains the base's last-fetched tip and has a receipt (or the clearing rule's equivalent, which an unpushed head never meets); never a behind, failing or under-review item, a reservation alone or a renewal; the person's line says "N steps queued, M items handed back"; TestQueuedPushHandsItemBack covers each case, the no-flag case included (dcn-2610181035355741) |
| F2 | a cheap wake keeps a stall alive: a */10 cron set once releases an unchanged owed set at every wake |
fixed: an unchanged set of capped items (behind, failing, unpushed, stacked-not-clear, and an uncarried reservation, added as the same kind of act) is released by a qualifying wake at most twice in a row, then blocked, the cap recording the stop; a renewal only due or an item waiting on running checks keeps releasing; the count and its fingerprint live in the session's state file in the local tier; TestWakeReleasesUnchangedSetTwice; repeated identical stops collapse into one entry so the record stays within 64 KiB (dcn-2610181035361997) |
| minor | the behind kind: DIRTY and UNKNOWN |
handed to the builder, and written into Owed work and TestBehindFromMergeState: DIRTY owed as behind, UNKNOWN not read |
| minor | the recorder's forms: git branch <b>, a bare git worktree add <path>, git worktree add <path> <existing>, a record carried across git branch -m or -M, whole-token names, a capped file |
handed to the builder, and written into "own" and TestBranchRecorderOtherForms; the cap's bound is the builder's |
| minor | the copy key from a worktree's working directory | handed to the builder, and written into Owed work ("Which copy") and TestSessionStartNotesCopy; the note's shape is the builder's |
| minor | fail-open when the listing fails, stated in the spec and the ADR | handed to the builder, and written into "What is read", TestForgeListingDeadline and adr-2610181003541997: a branch with a pull request compared on disk naming the fetch time, a reservation never reported as uncarried |
| minor | a Stop hook timeout of about 30 seconds | handed to the builder, and written into the front door and step 5 |
| minor | Record and Owed fixed in step 1 so the lanes can run in parallel |
handed to the builder, and written into step 1 and Size |
| size | Sonnet: 5.75 days thin, about seven; Opus: 3.75 days elapsed credible | re-estimated at 7.5 days of work with round 3's additions, about 4.75 elapsed in two lanes (Size) |
No fourth review round: the two deltas are checked on the next commit by hand.
Diff review round 1
On 2026-10-18 an Opus and Sonnet pair reviewed the built slice's diff at f403910ac (against e4cca1e38), with the verdict "fix first", every major reproduced. The fix round added the release orchestrator's three hard requirements to majors 1, 3 and 6. Each item and how it was handled:
| # | finding | handled |
|---|---|---|
| major 1 | the Stop hook acted in every git repository, not only managed ones | fixed: Stop, StopFailure, the session-start and prompt away paths and the branch recorder return at once outside a managed checkout (autonomy.Managed), with no output, no file under abcd's home, no session note or state, and no git at all where the checkout's .git is a folder (the nearest .git entry is found without git); TestHookStopUnmanagedRepoSilent, TestAwayHooksLeaveUnmanagedRepoAlone (93d34a1ef, e1d64652a, 1eec1addc) |
| major 2 | a squash-merged branch with no upstream read as unpushed for 14 days | fixed: a merge into the default branch that changes nothing is landed, a conflicting one falls to git cherry, and the undecidable is a note; TestNoUpstreamLandedBranchNotOwed, TestNoUpstreamUnlandedBranchOwed, TestNoUpstreamUndecidedBranchNoted (546c22e40) |
| major 3 | a full record kept the person from ending the mode | fixed: AddStop collapses an identical stop anywhere and rotates past its bound; hand-backs rotate; the queue refuses past its bound; Back ends a record past its bound, unparsable, partly unparsable, not safely this account's or behind a busy or unsafe lock, printing what it can and naming the rest; Begin carries a full record fitted; TestAddStopCollapsesAnywhere, TestRecordStaysWithinBoundInUse, TestBackEndsAnyRecord, TestBackWithLockHeld, TestBeginCarriesAFullRecord, TestModeBackEndsAnUnreadableRecord (e101881f2) |
| major 4 | a failed git worktree add <path> <existing> recorded a peer's branch |
fixed at record time: a name a worktree has checked out is skipped; TestBranchRecorderSkipsBranchCheckedOutElsewhere (fa7f6a8f2). At read time a reflog branch another worktree holds is not own (minor 4's fix); a recorded one stays own, since a worktree the session made holds its branch by design |
| major 5 | a row-1 block past the cap recorded nothing | fixed: work owed when anything is, else no next look; TestRowOnePastCapRecordsStop (e0d16ae76) |
| major 6 | the stop check overran the hook's 30-second timeout | fixed: every git call of the read is bound to a 14-second budget from the hook's start; the stack-base search is linear; a reading cut short allows the stop, named and recorded; TestStackBaseSearchLinear, TestReadOwedHonoursDeadline, TestIncompleteReadAllows, TestHookStopBudgetFailsOpen (31b5ac337). A design change, stated under Settled here |
| 7 | decision: "block" shows "Stop hook error occurred" |
fixed after reading the hooks reference again (quoted under the host mechanics): a block is hookSpecificOutput.additionalContext; TestHookStopBlockIsFeedback (060d25335); the live capture re-run is the orchestrator's |
| 8 | the reason repeated itself | fixed at the source: the reader's reason is the item's words; TestReasonSaysItOnce and the end-to-end exact line (e0d16ae76, e1d64652a) |
| minor 1 | a due renewal never released on a wake | fixed: the deadline reads Owed.WakeBy; TestDueRenewalReleasesOnWake builds the set with the reader, and TestWakeReleasesUnchangedSetTwice no longer uses an impossible state (3e4676f32) |
| minor 2 | the 200-listing truncation was silent | fixed: a note, and no reservation reported uncarried from a truncated listing; TestForgeListingAtLimitTruncated (231a6984f) |
| minor 3 | carried counted other people's pull requests | fixed: the carried check applies findOwn's fork and author filter; TestCarriedOnlyByOwnPullRequest (29f62682b) |
| minor 4 | the review-only exclusion leaned on author email | fixed for the case the machine can tell: a reflog branch another worktree has checked out is not own; TestReviewCheckoutSameEmailNotOwn (29f62682b). A sibling's branch no worktree holds any more still reads as own on one email: nothing on disk tells it apart |
| minor 5 | a branch name in a suggested shell command | fixed: each interpolated word is one shell word (shellquote.Single unless plain), the renewal's abcd reserve too; names were already cleaned by termsafe; TestSuggestedQueueCommandQuotesBranch (e0d16ae76) |
| minor 6 | hook stop had no recover |
fixed: a panic allows the stop with a stderr line, and StopFailure records nothing; TestHookStopPanicAllows (61f1e0e40) |
| minor 7 | overcounted recorded stops | left as the spec allows, labelled "possibly forced by the cap" |
| minor 8 | SubagentStop under --no-session-persistence |
outside the slice; not handled in this round |
| own | the end-to-end test made its bare origin by hand | fixed: gittest.InitBare, which TestNoTestMakesABareRepositoryByHand requires (3b8b14fac) |
Frontmatter
| id | spc-2610180914469489 |
|---|---|
| type | spec |
| lifecycle | closed |
.abcd/development/specs/closed/spc-2610180914469489-a-session-told-to-work-on-while-its.md
open on GitHub ↗ · commit history ↗
References1
- itd-2610180912189125A session left to work on never stops while it has workimplements
mentions38
- adr-42The guard's parse layer is a mistake filter, not a security boundary — matching is two-tier, and completeness is abandoned as a goal
- adr-2610181003541997The away mode's stop check reads the forge once, as its documented meaning
- dcn-26101711123847122026-10-17 — The headless approver closes the arithmetic routes a review found by reading the text as bash will read it where that is cheap: it joins a backslash-newline before scanning, refuses an alias or alias expansion turned on, a $'…' string that writes a byte by its code, and a builtin whose operands bash may read as arithmetic spelled with a quote or a backslash in its name; it reads a substring offset on any parameter and an array index only in an assignment, an expansion or a name a builtin is handed, so a regular expression's character class is no longer refused. It stops there by decision, not by oversight: on the default branch a launched role is granted the shell with no approver at all, so every route the approver misses is one the run has today; reading a shell line well enough to refuse every hidden command is not finite; and the approver stays a mistake filter (adr-42), with the boundary for launched agents the OS sandbox spec (spc-2610161751056096). Grounds: the default branch grants Bash in the implementer and reviewer roles (internal/core/implement/loop/drive.go). Refines dcn-2610170944153720.
- dcn-26101807205412062026-10-18 — A session's mode shows on its central-view line (facilitator-on-delegation, itd-2610170634394993 planning prep): the line carries the session's state from the checklist draft's fixed set: working; waiting, with what it waits on and when it next looks; or stopped, which is allowed only while the person is present and is flagged. While the person is away a session with work never stops, and one with nothing it can do is waiting with its next look, so no session reads as idle. This follows the product thinker's ruling that the person must never wrongly believe agents are working or waiting (grounds: the draft's decision 6; itd-2610170638184486 modes)
- dcn-26101809052912972026-10-18 — A declared dependency constrains only the landing (facilitator-on-delegation, itd-2610170634394993 planning prep): a branch that waits on an open pull request is held only from being queued; its merge from the default branch, its fixes, its review and its preflight run now, and only its landing waits for the dependency, so the stop check counts a stacked branch with an open base as owed work (grounds: iss-2610170619247185, second instance; the draft's first acceptance criterion; the decision that a waiting branch is rebuilt by merging)
- dcn-26101809133990422026-10-18 — The away mode is a record of the person's absence, set by a verb and ended only by its time or by their word (facilitator-on-delegation, itd-2610180912189125 planning): abcd mode autonomous [--until <time>] records who set it (the holder name reservations already resolve from caller-local facts), when, and when it ends, where --until takes a clock time (its next occurrence, local time) or a full timestamp and a time already past is refused; without --until it is open-ended until abcd mode back. It sits beside the waiting-on badge, whose three words stay a closed set, and is not a fourth word. A human prompt during the window does not end it, since a brief return overrode the instruction on 2026-10-16 and a prompt from a peer looks the same. An agent sets it on the person's word in its session, and one set covers every working copy, so no peer has to relay it. This restates the product thinker's 2026-10-17 ruling that they say they are away, until a set time or open-ended, and say when they are back (grounds: itd-2610170638184486 decisions; iss-2610170619247185; brief invariant 10; internal/core/mode)
- dcn-26101809133994992026-10-18 — The away mode is held once per repository on the machine, not per working copy (facilitator-on-delegation, itd-2610180912189125 planning): the record lives in abcd's machine store under the repository's root commit, beside the worktree store's key, so every working copy of the repository and every session in it reads the same record; the issue's remedy words it per checkout, but the stall began with an instruction relayed by a peer between sessions in separate working copies, and the person's absence is a fact about them, not about one copy. It is not machine-wide, so a repository the person never left running is unaffected. A session on another computer or in the cloud does not see it until the checklist draft's shared options land (grounds: iss-2610170619247185 what happened; AGENTS.md worktree store; principle the-users-directory-is-theirs)
- dcn-26101809134166832026-10-18 — Owed work is four kinds, read from disk and one forge listing (facilitator-on-delegation, itd-2610180912189125 planning): a session owes work it can do alone when it has (1) a live reservation whose renewal is due or whose work no open pull request carries yet; (2) an open pull request of its own that is behind the default branch or failing its checks; (3) a branch of its own with commits not pushed; (4) a branch of its own stacked on a change that is still open, whose merge from the default branch, fixes, review and preflight are owed now, since a declared dependency holds only its landing. A branch is the session's own when its working copy's own HEAD history has had it checked out since the window began, so a sibling copy's branches never count. The reservations are read as last fetched, behind against the last-fetched default branch, and the pull requests' states from one listing of the person's open pull requests through the forge client per stop check, which becomes part of the away mode's documented meaning in brief invariant 7, as the drain's listing is; a failed listing falls back to the disk kinds and says so. The next item is the first of: a renewal due, a failing pull request, a behind one, a stacked branch, an unpushed branch, unhanded work (grounds: iss-2610170619247185 remedy and second instance; the coordination prep's decision that a declared dependency constrains only the landing; brief invariant 7; internal/core/claim)
- dcn-26101809134314932026-10-18 — While the person is away no question waits for them (facilitator-on-delegation, itd-2610180912189125 planning): while the away mode holds, the guard refuses every question on the host's question tool, abcd's or another tool's, because a question nobody is there to answer stalls the session. A product or technical question is refused with a reason telling the agent to decide it, record it with abcd decide marked as decided on delegation with its grounds and how to reverse it, and carry on; a step only the person's hands can do (a browser step, a secret, a new App) is refused with a reason telling the agent to queue it with abcd mode queue "<step>" and carry on with other work. abcd mode product-thinker or facilitator is refused the same way. Decisions in the window are not asked first, technical trade-offs included, since the product thinker ruled on 2026-10-17 that in an autonomous run the agent decides and records and only steps that need them on the website wait (grounds: iss-2610170619247185 remedy; itd-2610170638184486 framing; internal/surface/cli guard question check)
- dcn-26101809134319852026-10-18 — The person's return shows two lists derived from what already exists (facilitator-on-delegation, itd-2610180912189125 planning): when the away mode ends, by its time passing or abcd mode back, the next prompt shows once the steps queued for the person's hands and the decisions recorded in the window, each with its text, which the reroute asks to carry its grounds and how to reverse it, plus any stop recorded with work owed. The decisions are the decision records whose time-ordered ids fall inside the window, so no second store of decisions is kept; the queue is the only new list and lives in the away record. The draft's richer lists for the person's return stay in itd-2610170638184486 (grounds: principle one-canonical-primitive; adr-45 time-ordered ids; itd-2610170638184486 press release)
- dcn-26101809365683582026-10-18 — A session's own branches are named by three facts with no window bound, and only this copy's reservations count (facilitator-on-delegation, itd-2610180912189125 review round 1; refines dcn-2610180913416683): a branch other than the default branch is the session's own when (a) this working copy's HEAD reflog has checked it out at any time, (b) it is the branch of a lane recorded in a run state this copy's local tier holds (the implement loop's lanes, which build and drain cut with git worktree add, so they never reach the coordinating copy's reflog), or (c) it is the head branch of an open pull request linked to a live reservation held under this copy's key. A reservation counts only when its copy key is this copy's, so a sibling copy's reservation never holds this session. A pull request carries a reservation's work when the record id appears in its head branch's name or on a Refs:, Resolves: or Delivers: line of its body. The window bound is dropped because both instances of iss-2610170619247185 escaped it: the pull requests that fell behind were opened and last checked out before any window began, and the stacked drain lanes lived in worktrees of their own (grounds: the plan review of 2026-10-18 by an Opus and Sonnet pair, findings 1 and 10; internal/core/claim copy key; internal/core/implement/loop lane worktrees; iss-2610170619247185)
- dcn-26101809365710362026-10-18 — A stacked branch is owed only while it needs work, and the forge is read only while the person is away, under a deadline (facilitator-on-delegation, itd-2610180912189125 review round 1; refines dcn-2610180913416683): an own branch is stacked when its open pull request's base is not the default branch, or, with no pull request, when the nearest other own branch whose tip is an ancestor of its tip is not contained in the last-fetched default branch. A stacked branch whose base is still open is owed only while it is behind its base's tip or the last-fetched default branch, its checks are failing, or no preflight receipt names its head in any worktree (the receipts the landing step already reads); otherwise it is waiting on its base, which is not owed. An unpushed branch has commits not on its upstream or, with none configured, not in any remote-tracking ref; a branch whose upstream is gone (merged and deleted on the remote, a squash merge included) is never counted. While the person is present the check reads disk only (a reservation's renewal due and unpushed own branches) and never calls the forge, so brief invariant 7 holds on every turn. While away it makes at most one pull-request listing under a ten-second deadline; a listing younger than ten minutes is reused rather than repeated, and on a failure or timeout one younger than half the default lease is used with its age named, else the disk kinds alone with the pull requests named as not read (grounds: the plan review of 2026-10-18, findings 1 and 9; brief invariant 7; internal/core/landing receipt lookup)
- dcn-26101809365777202026-10-18 — While the person is away a session is only working or waiting, and abcd never allows a stop with work owed (facilitator-on-delegation, itd-2610180912189125 review round 1; supersedes the bound in dcn-2610180913413137 and refines dcn-2610180913421109): while away a stop is allowed only when the session has a qualifying wake and either nothing is owed or the owed set is unchanged since this session's last block; it then reads waiting, and the wake's next fire time is kept as its next look in the session's own state file in the local tier, beside the owed-set fingerprint and a count of consecutive blocks. Every other stop while away is blocked, whatever stop_hook_active says, naming the next item, or with nothing owed asking the session to take the next ready item first and only then schedule its next look. The host's cap of eight consecutive continuations, or the cap its documented environment variable sets, is the outer bound, and the block abcd counts as reaching it records stopped with work owed in the away record. The old row that allowed an unchanged owed set once stop_hook_active was true reproduced the stall, since a reply of awaiting the person with no tool call ended the turn until morning. This matches the coordination prep's decision that while the person is away a session with work never stops and one with nothing it can do is waiting with its next look, and the draft's ruling that stopped is allowed only while the person is present (grounds: the plan review of 2026-10-18, findings 2, 5 and 12; code.claude.com/docs/en/hooks Stop input and continuation cap; itd-2610170638184486 decisions)
- dcn-26101809365794982026-10-18 — A wake qualifies only when its next fire time falls before the session's earliest renewal due, and a background task never counts (facilitator-on-delegation, itd-2610180912189125 review round 1; refines dcn-2610180913421109): a session_crons entry qualifies when the next time its cron expression matches after now, read in the machine's local time, is no later than the earliest renewal due among the live reservations under this copy's key minus the lease skew allowance, a renewal being due halfway between a reservation's claim or last renewal and its expiry; with no live reservation it must fire within half the default lease. The next fire time comes from a small five-field cron parser of abcd's own (minute, hour, day of month, month, day of week, with lists, ranges, steps and the wildcard), since the host documents schedule as a cron expression and a new dependency would need sign-off; an expression it cannot parse never qualifies. A background task does not count on its own while the person is away, since a shell task such as a long sleep or a tail that never ends reports as in flight however distant its wake. So a cron at 09:00 seen at 21:00, or a wake that fires after a lease lapses, no longer passes (grounds: the plan review of 2026-10-18, finding 3; code.claude.com/docs/en/hooks session_crons and background_tasks fields; internal/core/lease; AGENTS.md new dependencies need sign-off)
- dcn-26101809371756292026-10-18 — An API-error stop is recorded, and a turn ending on a prose question while away is blocked once (facilitator-on-delegation, itd-2610180912189125 review round 1): the plugin's hook manifest gains a StopFailure entry, which the host fires instead of Stop when a turn ends on an API error such as a rate limit and whose output it ignores, so it cannot continue the session; while the person is away abcd records the stop and the host's error type in the away record, and the slice's scope conditions name that such a turn is recorded, not resumed, except by a wake the session had already scheduled. While away, a stop whose last_assistant_message ends on a line ending in a question mark is blocked once per message with the reroute's reason (decide and record it, or queue it when it is an act only the person may approve), before the verdict's other rows apply (grounds: the plan review of 2026-10-18, finding 11; code.claude.com/docs/en/hooks StopFailure and Stop input; iss-2610170619247185, whose session ended its turn on a question)
- dcn-26101809371775022026-10-18 — While the person is away a question is routed by the act it asks about, not by its chip (facilitator-on-delegation, itd-2610180912189125 review round 1; supersedes the chip routing in dcn-2610180913431493): the guard still refuses every question on the host's question tool while the away mode holds, now with one reason whatever the chip. It tells the agent to queue with abcd mode queue, and carry on with other work, a question about any act only the person may approve: an approval the repository binds to the person per act (an embargo close's --approve digest, which commands/embargo.md says is never supplied unless the person has read the text and said yes in that session; a release sign-off); publishing anything or changing a repository's visibility; adding a dependency; a secret, a credential or an App; a step only a website or a browser can do; a destructive act (deleting what the session did not create, overwriting uncommitted work, a force push, discarding a peer's work); and anything that spends money. Every other question is decided and recorded with abcd decide, with its grounds and how to reverse it. The away mode, set on the person's word, is their ask for the session to commit and push its own branches and open their pull requests, which is the owed work itself; merging follows the repository's own rule. The Setup chip marks the setup interview's questions (visibility, keys, oracles), not a step only the person can take, so routing by chip would have sent an embargo approval, or the creation of a new App asked under a Product chip, to be decided by the agent (grounds: the plan review of 2026-10-18, finding 4; commands/embargo.md approval one per act; brief invariant 22; AGENTS.md new dependencies and commits; internal/surface/cli guard question chips)
- dcn-26101809371817362026-10-18 — The away record is changed only under a lock, read only when it is safely the account's, and capped at seven days (facilitator-on-delegation, itd-2610180912189125 review round 1; refines dcn-2610180913399042): every read-modify-write of the record (setting, ending, queueing, a recorded stop) holds fsutil.WithFileLock on a lock file beside it and re-reads the record inside the lock, so a stop check that read away before a slow forge listing cannot write away back after the person ended it, and two queued steps cannot lose one; the forge listing runs outside the lock, and a lock not had within two seconds allows the stop and says so on stderr. The record and its lock are used only when each is a regular file this account owns that no one else can write, within 64 KiB, the checks rules.json already gets; a record failing them, or failing to parse, reads as not away, failing open, and a verb asked to change it refuses naming the file. An open-ended record ends seven days after it was set, and --until may name no later time, so a forgotten record lapses while a run of several days, such as the standing run of 2026-10-15 through 2026-10-17, still fits; a 24-hour cap would have ended that run midway, and requiring --until would contradict the product thinker's ruling that the mode may be open-ended. While the record holds, the prompt hook adds a one-line systemMessage the person sees on every prompt naming the away mode, its end, who set it and how to end it; the away block tells the agent to run abcd mode back only on the person's word in its session; and abcd mode back records who ended the mode and from which working copy (grounds: the plan review of 2026-10-18, findings 6, 7 and 13; internal/fsutil file lock; AGENTS.md rules.json ownership checks; iss-2610170619247185 standing run; the draft's ruling that the state is always shown plainly)
- dcn-26101809371819292026-10-18 — The person's return report, the status line and the bare abcd mode print move from the slice back to the checklist draft (facilitator-on-delegation, itd-2610180912189125 review round 1; supersedes dcn-2610180913431985 for the slice, and the status-line and bare-print halves of dcn-2610180913445272): both reviewers of 2026-10-18 recommended the split, so the security and bug-fix cut waits only on the away record, the stop check and the question reroute. The spec's step 6 and the return half of its step 5 leave the slice: the report shown once on the first prompt after the window ends (the queue, the window's decisions and the recorded stops); the window's decisions gathered from every working copy by abcd decide appending each id to the away record under its lock (review finding 8); showing on return who ended the mode (finding 13); the status line naming the away mode and its end; a bare abcd mode printing the record, the queue and the owed items; and the brief's surface text for them. They become named remaining work in the scope of itd-2610170638184486. The slice keeps what its own behaviour needs: abcd mode back prints the queue and the recorded stops as it ends the record, so a queued step is never unread; brief invariant 7 gains the away mode's forge listing; and the plugin page and the brief's surface chapters describe the verbs and hooks the slice ships, so nothing it ships is undocumented. The slice is re-estimated at five working days (grounds: the plan review of 2026-10-18, size and split; the coordination prep's decision that the cut waits on this slice alone; AGENTS.md wired or it isn't done; brief invariant 7)
- dcn-26101810034557312026-10-18 — A wake qualifies only when it fires, the host's lateness included, within a short ceiling (facilitator-on-delegation, itd-2610180912189125 review round 2, blocker 1 and choice (c); refines dcn-2610180936579498): the deadline is the earlier of the earliest renewal due among this copy's live reservations minus lease.Skew, and now plus a ceiling of 30 minutes while work is owed or one hour when none is, because a release reservation's 24-hour lease put its renewal 12 hours on and let a 07:00 cron seen at 21:00 qualify. The fire time compared with it is the next match, read in the machine's local time, plus the latest the host may run it: for a recurring task, half the gap to its following match, at most 30 minutes, and nothing for `*/5 * * * *`; a one-shot task is taken as matched, since the host only ever runs one early. The parser follows vixie cron, as the host says it does: a day field written from `*` (`*/n`) counts as unrestricted for the either-day rule, 7 is Sunday as 0 is, and `a-b/n` steps a range. The live capture compares the computed fire time with the next run the host lists (grounds: plan review round 2, blocker 1 and choice (c); the host's scheduled-tasks page on jitter, local time and day-of-week; the release reservation's default lease in the reserve verb)
- dcn-26101810034564442026-10-18 — Only the person sets the away mode, its push rights are narrow, and the guard's refusal is a mistake filter (facilitator-on-delegation, itd-2610180912189125 review round 2, choice (a); refines dcn-2610180913399042, replacing its clause that an agent sets the mode on the person's word): the prompt hook cannot tell the person's message from one another session relays, and its input names no origin, so a relayed word could let an agent grant itself, and every copy, push rights. So the guard refuses every route to setting the mode that it reads: `abcd mode autonomous` by the binary on PATH, the plugin root's binary or a built `abcd-<goos>-<arch>`; a `go run` of a package ending in `cmd/abcd`; any of them inside `sh -c` or in a script file a shell runs (adr-2610091150447054); a shell write into the away store under `~/.abcd.noindex/away/`; and an Edit or Write tool call on a path inside it. The person sets the mode with their own `!` command, which the host runs directly without going through Claude and so without the pre-tool hook, or at a terminal. A refused attempt is recorded in the store and shown to the person on the next prompt. What the mode grants is the session committing and pushing its own branches and opening their pull requests; never the default branch, a tag, a release cut, another person's pull request, a branch this copy only checked out to review, or a force push; merging follows the repository's own rule. This is chosen over the fallback, and is enough for now, because the grant only governs an agent that follows refusals: one that ignores them can push without any mode, so no shape of the mode adds a boundary inside the person's own session; the guard stops a refusal-following agent from granting itself on a relayed word; the rights are narrow, and every push is reversible and visible on the forge; and the record (who set it and from which copy) and the recorded refusals leave a trail the person reads on return. The guard stays a mistake filter (adr-42, and the decision of 2026-10-17 on the default branch that keeps the headless approver one, naming the OS fence as the boundary). That fence (spc-2610161751056096) is the boundary only for agents abcd's runner launches, whose writes to abcd's home it confines to their own worktree; it does not fence the person's own interactive session, where the stall happened, so there the narrow grant and the trail are the mitigation. The fallback (the mode grants no push rights, and the person's own standing instruction stays the authority to push) applies instead if the live capture shows that a message relayed from another session can run as a `!` command, or that an agent's tool call can reach the host's shell mode (grounds: plan review round 2, choice (a), and the coordinator's two addenda; the host's interactive-mode page, which says shell mode runs commands directly without going through Claude and outside the sandbox that applies to Claude's commands; the abcd-source-ledger-flip guard entry; adr-2610161621415708)
- dcn-26101810034633672026-10-18 — A branch is the session's own only while it is live and this copy's, and a branch the session creates is recorded as its own (facilitator-on-delegation, itd-2610180912189125 review round 2, blockers 3 and 5 and choice (a)'s exclusions; refines dcn-2610180936568358): when the guard's pre-tool hook sees this session run `git worktree add -b|-B <b>`, `git switch -c|-C <b>` or `git checkout -b|-B <b>`, it records `<b>` under this copy's key in the away store, under its lock, and only when no branch of that name exists yet, whether or not the person is away, because the stacked drain branches of 2026-10-17 were cut with a plain `git worktree add` before any window and carried no upstream, reflog entry, run state or record id. Stale ownership is bounded: a run's lane counts only while its run's reservation is live under this copy's key, and a finished or abandoned run's lanes never count; a branch owned through the reflog or a recorded creation counts only while it holds a commit authored by this checkout's `user.email` within the last 14 days, or a live reservation under this copy's key links it. Never own: the default branch, a tag, a branch whose open pull request another person authored, and a branch this copy only checked out to review, which the authorship rule leaves out (grounds: plan review round 2, blockers 3 and 5; lanes are cut from the default branch; the 60 run states and the five unlanded lanes of 2026-10-12 in this checkout)
- dcn-26101810034643792026-10-18 — While the person is away, a pull request is behind when the forge says so (facilitator-on-delegation, itd-2610180912189125 review round 2, blocker 2; refines dcn-2610180936571036): the stop check reads "behind" from the merge state (`mergeStateStatus` BEHIND) in its one pull-request listing, not from the last-fetched default branch, which nothing refreshes overnight, so a pull request that fell behind after the last fetch still reads as owed. Only a branch with no pull request is compared on disk, and the reason names when that ref was last fetched (grounds: plan review round 2, blocker 2; the pinned pull-request fields in internal/ghcontract)
- dcn-26101810034672332026-10-18 — A pull request carries a reservation's work only through its head branch or a closing trailer (facilitator-on-delegation, itd-2610180912189125 review round 2, blocker 4; refines dcn-2610180936568358): the link is the record id in the pull request's head branch name, or on a `Resolves:` or `Delivers:` line of its body, matched on word boundaries so that iss-5 never matches iss-50. A `Refs:` line, which the issue-resolution gate requires of any pull request that only names an issue, carries the work only when the head branch is already this copy's own by another route, and never makes a branch own, so a sibling copy's pull request that mentions the record neither leaves this session waiting nor becomes its branch (grounds: plan review round 2, blocker 4; the RS004 rule in scripts/check-issue-resolution.sh)
- dcn-26101810034747652026-10-18 — A window that lapsed with anything unread points the person at abcd mode back (facilitator-on-delegation, itd-2610180912189125 review round 2, blocker 7; refines dcn-2610180937181736): while a record whose end has passed is not yet closed and holds a queued step, a recorded stop or a refused attempt, the prompt hook shows the person one line on every prompt that counts them and names the literal `abcd mode back`, until that verb closes the record; the line shown while away carries the literal `abcd mode back` too, so the person never has to recall the verb (grounds: plan review round 2, blocker 7; dcn-2610180937181929, which kept the queue's print in the slice so that no queued step goes unread)
- dcn-26101810034771942026-10-18 — The acts only the person may approve are completed, end in a catch-all, and are classified by the agent (facilitator-on-delegation, itd-2610180912189125 review round 2, blocker 6; refines dcn-2610180937177502): the list the question reroute queues gains the embargo triage verdict (commands/embargo.md), the drain's hand-back (commands/drain.md), the confirmed apply verb for remote configuration (brief invariant 10), a push or merge beyond the session's own branches (the default branch, a tag, a release cut, another person's pull request, a branch only checked out to review), and a comment or message posted outward other than the session's own pull request, and it ends with any act a command page or a brief invariant names as the person's. A release sign-off is a cut, or a step of one, that asks for the person by name or that no standing instruction of theirs allows; a standing permission for automatic cuts stands, and the away mode adds none. Nothing detects the act mechanically: the agent classifies its own question from the refusal's list, and the guard's existing entries enforce only what they name, a force push and a skipped hook among them (grounds: plan review round 2, blocker 6; commands/embargo.md; commands/drain.md; brief invariant 10)
- dcn-26101810034846312026-10-18 — abcd's count of blocks resets only when the host is not already continuing (facilitator-on-delegation, itd-2610180912189125 review round 2, choice (b); refines dcn-2610180936577720): the count is set to one on a block with `stop_hook_active` false and adds one with it true; a change in the owed set no longer resets it, since the host's count does not, and a reset abcd made on one could leave a stop the host forced unrecorded. A stop recorded at the cap is labelled "possibly forced by the cap", because the host's count also resets on a tool call abcd cannot see, and the live capture checks where the host's count resets (grounds: plan review round 2, choice (b); the host's hooks page on the Stop cap)
- dcn-26101810034883552026-10-18 — A stacked branch's clearing rule includes review and works without abcd's preflight (facilitator-on-delegation, itd-2610180912189125 review round 2, minor findings; refines dcn-2610180936571036): a stacked branch whose base is still open is also owed while a review of its head is outstanding, that is, while the away listing shows the forge's review decision as CHANGES_REQUESTED or, for a run's lane, its run state records no passing review of its head, since the coordination prep's decision that a declared dependency constrains only the landing says its review runs now. Verified means a preflight receipt naming its head where the repository mints receipts (a receipt folder exists in one of its worktrees); in a repository without them, its pull request's checks passing in the away listing; and with no pull request there either, the branch is judged on being behind and on review alone, and the reason says it was not verified (grounds: plan review round 2, minor findings; the receipt lookup in internal/core/landing)
- dcn-26101810165101532026-10-18 — The away mode grants no push rights: the person's own standing instruction stays the authority to commit, push, and open or merge a pull request, exactly as before the mode existed (facilitator-on-delegation on the release orchestrator's ruling, itd-2610180912189125 after plan review round 2; supersedes dcn-2610181003456444, round 2's choice (a), and with it the guard entries that made setting the mode the person's act alone). The OS sandbox of spc-2610161751056096 fences only the agents abcd's runner launches, never the person's own interactive sessions, so for the sessions that actually stalled on 2026-10-16 the guard entries would never become a boundary, and their two days would buy a trust claim the plan cannot make. The fallback dcn-2610181003456444 already named is taken now, not on the live capture's evidence. Where the stop check finds owed work that needs a push (an unpushed branch, a reservation whose work no pull request carries, a behind or failing pull request once its local fix is committed), its reason tells the session to push if its standing instruction allows, and otherwise to queue the push with abcd mode queue for the person's return; a queued step that names the item's branch or record id, on word boundaries, hands that item back to the person for the rest of the window (a branch only while it holds commits its upstream lacks, so only the push waits), and the stop check stops naming it. The away mode is never treated as permission. The person-only acts gain pushing, opening or merging a pull request without a standing instruction that allows it. Who sets the mode returns to dcn-2610180913399042: the person, or a session on their word in that session; since the mode grants nothing, a mode set on a relayed word only keeps sessions working and deciding instead of asking, and the person sees who set it, from which working copy, and the literal abcd mode back on their next prompt. Making the mode the person's act alone moves to itd-2610170638184486 as named remaining work: the guard entry refusing an agent's abcd mode autonomous, its other routes (sh -c and script files), the shell-write and file-tool refusals on the away store, the refused attempts recorded and shown, the capture's checks of the person's ! command and of a relayed !, and the guard reading go run ./cmd/abcd and built abcd-<goos>-<arch> binaries as abcd for every entry, the ledger flip included, a security-relevant widening that gets its own review there (grounds: the release orchestrator's ruling of 2026-10-18 and the reviewer's addendum, both recommending the fallback; spc-2610161751056096 and adr-2610161621415708, which fence launched agents only; iss-2610170619247185, whose standing instruction already allowed pushes and auto-merge; the fallback clause of dcn-2610181003456444)
- dcn-26101810170563072026-10-18 — The overnight-stall slice is trimmed to what closes iss-2610170619247185 and nothing else (facilitator-on-delegation on the release orchestrator's ruling, itd-2610180912189125 after plan review round 2; refines dcn-2610180937181929): it keeps (i) a session with owed work cannot end its turn silently, through the Stop hook stop check with the ownership rules, the qualifying wake and the cap handling the reviews required; (ii) questions rerouted by the act, every other question decided with abcd decide and the person-only acts queued; and (iii) the away record set, ended, lapsing after at most seven days, re-injected at start, compaction and every prompt, under its lock. Moved to itd-2610170638184486 as named remaining work: the person-only guard step (dcn-2610181016510153), with the guard's reading of go run ./cmd/abcd and built binaries as abcd; the ten-minute reuse cache of the away forge listing and its stale-cache fallback, so each away check makes at most one listing under its ten-second deadline and a failed read judges from disk naming the pull requests as not read (adr-2610181003541997, still proposed, amended to match); abcd mode back printing more than one plain line per queued step and per recorded stop (any grouping, formatting or window summary); and the refused-attempt lines on the prompt. Kept despite the trim, because a review finding's fix rests on each: the created-branch list recorded in any mode (round-2 blocker 3, the drain's stacked branches, instance 2 of the issue); the Refs rule and word-boundary matching (blocker 4); the stale-ownership bounds (blocker 5); the wake deadline with the host's lateness and the cron cases (blocker 1); the forge's merge state for behind, with the one bounded listing and invariant 7's clause (blocker 2); the person-only acts list (blocker 6); the every-prompt line and the lapsed-window line naming abcd mode back (blocker 7); the block count and its possibly-forced label (round-2 (b)); the clearing rule's review and receipt terms (round-2 minors); the record's lock, ownership checks and seven-day cap (round-1 findings 6 and 7); the recorded ender (round-1 finding 13); the StopFailure record and the prose-question block (round-1 finding 11); and the badge verbs refused while away, which keeps a question from waiting on the person through the badge (dcn-2610180913431493). Re-estimated at 5.75 working days over seven steps, down from eight; about four is not reachable without reopening blockers 1 to 5, since the owed-work reading and the stop verdict with its cron parser carry 3.5 days between them and every rule in them answers a finding; the verdict is pure over the owed set, so two lanes could bring the elapsed time to about four days if the release orchestrator chooses (grounds: the release orchestrator's ruling of 2026-10-18 and the reviewer's addendum listing what stays; the spec's review rounds 1 and 2; AGENTS.md wired or it isn't done)
- dcn-26101810353557412026-10-18 — A queued push hands nothing back unless the person said pushes wait, and then only the unpushed item of a verified branch (facilitator-on-delegation, itd-2610180912189125 plan review round 3, finding F1; refines dcn-2610181016510153, whose grant of no push rights stands): by default a step queued with abcd mode queue hands nothing back, so the item stays owed and the stop stays blocked; the reason for an item that needs a push says to push if the standing instruction allows and to queue only if the person has said pushes wait, and a session with no instruction to push ends at the host's cap with the stop recorded for the person. A hand-back happens only when the away record carries the person's word that pushes wait, set with abcd mode autonomous --hold-pushes. The flag narrows what sessions do and grants nothing, so set on a relayed word it can at worst make sessions queue pushes they could have sent. Even then a queued step naming an own branch, on word boundaries, removes only that branch's unpushed item, and only while the disk shows the rest clear: its head contains its base's last-fetched tip, and a preflight receipt names the head where the repository mints receipts; where none are minted the clearing rule's equivalent, passing checks in the listing, can name only a pushed commit, so nothing is handed back there. It never removes a pull request that is behind or failing, a stacked branch the clearing rule still owes, a review outstanding, a reservation named by its record id alone, or a renewal, so a behind pull request whose merge is committed locally stays owed with pushes held and ends at the cap, recorded. The stop check records each hand-back in the away record under its lock, abcd mode back prints them, and the every-prompt line counts the queued steps and the handed-back items. TestQueuedPushHandsItemBack covers each case, the no-flag case handing nothing back (grounds: plan review round 3, finding F1, raised by both reviewers: the hand-back as written let a session exempt itself, replaying the first instance of iss-2610170619247185, where a behind pull request stayed behind all night; and it crossed the coordination intent's decisions that a stacked branch is cleared only by its merge and receipt and that a reservation's renewal must not lapse)
- dcn-26101810353619972026-10-18 — A qualifying wake releases an unchanged set of work the session can do itself at most twice in a row (facilitator-on-delegation, itd-2610180912189125 plan review round 3, finding F2; refines dcn-2610180936577720 and dcn-2610181003455731): while away, a stop whose owed set is unchanged is allowed with a qualifying wake at most twice while any capped item stays unchanged, and the third such stop is blocked naming the next item; the block counts toward the host's cap, whose forced stop is recorded. The capped items are the kinds the session can act on itself: a pull request that is behind or failing, an unpushed branch, a stacked branch the clearing rule still owes, and a reservation whose work no pull request carries, which the review's list did not name but which is the same kind of act as a push. A renewal that is only due, and an item waiting only on the forge, such as checks still running, keep releasing on a qualifying wake. The count compares the capped items alone, so a renewal falling due or done neither resets it nor adds to it, and a change in the capped items resets it; the count and a fingerprint of the capped items are kept in the session's state file in the local tier (.abcd/.work.local/autonomy/sessions/<session-id>.json), and TestWakeReleasesUnchangedSetTwice shows the third consecutive unchanged stop with a wake blocked. Repeated identical recorded stops are kept as one entry with a count and the last time, so a cron that wakes the session every ten minutes cannot grow the away record past its 64 KiB check, which would read as not away (grounds: plan review round 3, finding F2: with a */10 cron set once, each wake found the set unchanged and was allowed, and a behind pull request stayed behind for eleven hours, meeting the intent's falsifier)
- 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-50Adopt Decided/Implemented as the fixed value set for the wiring-status field on brief surface rows. The convention serves the spec-moves-with-the-surface practice: a surface row that names a verb must state mechanically whether the verb is merely decided or actually implemented, because it was the absence of exactly this state that produced the empty-shipped/ incident, where a surface documented as shipped had nothing behind it. Detector: the spec-moves-with-the-surface cross-check reads the wiring-status field mechanically, so any row missing the field or carrying a value outside the two-token vocabulary fails the check; acceptance is the cross-check parsing every surface row without a free-text fallback.
- iss-2610170619247185An agent session told to work autonomously overnight sat idle for about ten of the eleven and a half hours between 20:48 BST on 2026-10-16 and 07:13 BST on 2026-10-17, while it held owed work it could have done alone.
- iss-2610262038139122The surfaces register counts six hidden hook entrypoints; the configuration chapter wires two more, `hook stop` and `hook stop-failure`.
- itd-201Every question abcd's agents put to a human is asked one at a time, in plain language, with options that widen
- itd-2610170638184486Every session keeps a checklist on disk, so no run stalls and no stall spreads
- spc-2610161751056096a-person-who-leaves-abcd-to-run-on-its