Skip to content

feat: keep an autonomous session working while the person is away - #1101

Merged
REPPL merged 64 commits into
mainfrom
feat/away-mode-stop-check
Oct 23, 2026
Merged

REPPL merged 64 commits into
mainfrom
feat/away-mode-stop-check

Conversation

@REPPL

@REPPL REPPL commented Oct 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

This fixes the critical lapse in which an autonomous session sat idle overnight with work it could do alone. It delivers the first slice of the autonomous-agents bundle.

The person marks themselves away with abcd mode autonomous [--until <time>] and ends it with abcd mode back. While the mode holds:

  • A session can't end its turn silently with work it owns still owed. A plugin Stop hook reads the work: its live reservations, its own pull requests that are behind or failing, its own unpushed branches, and stacked branches whose base is still open. It sends the session back to the next item. "Own" covers branches the session created, including ones made with a plain git worktree add, so both instances of the incident are caught. The away mode grants no push rights: where an item needs a push, the session pushes only if the person's own standing instruction allows it, and otherwise queues it.
  • A waiting session must have a timely wake. A scheduled wake counts only if it fires before its reservations need renewing, or within 30 minutes while work is owed. An unchanged owed set is released by a wake at most twice in a row.
  • Questions are routed by the act. A question to the person is refused. A product or technical choice becomes a recorded decision, and an act only the person may approve is queued for their return.
  • The mode never lives only in context. It is repeated at every session start, compaction and prompt, and the person sees one line on every prompt.

Safety:

  • in a checkout abcd does not manage, every hook returns at once, with no output, no state and no git;
  • mode back succeeds whatever state the record is in;
  • the stop check fails open with a notice inside its time budget;
  • a block is written as hook feedback, so the host shows no hook-error notice.

Evidence

  • Every step's tests were watched failing first, including an end-to-end test with real git and a stub forge.
  • Live Claude Code captures are recorded in the spec:
    • before the change, a session with unpushed work stops after "Hello";
    • with the mode, it is sent back until the host's cap;
    • present mode makes no forge call;
    • a real compaction re-injects the mode;
    • cron wake times match the host's listed next run;
    • an unmanaged repository is untouched.
  • Three plan review rounds and one diff review round, each by an Opus and Sonnet pair, were run by the session owning the coordination intent. The diff review's six majors are each fixed with a test, and its hand check says ship.
  • The docs-fidelity review held on seven sentences, all fixed, then promoted.

Decisions are recorded on delegation, citing the product thinker's standing rulings, under dcn-26101809* to dcn-261018103*. The ADR for the invariant 7 amendment is adr-2610181003541997 (proposed).

Delivers: itd-2610180912189125
Resolves: iss-2610170619247185
Refs: spc-2610180914469489
Assisted-by: Claude:claude-opus-5-5

REPPL added 30 commits October 18, 2026 10:17
…estions

The critical overnight stall is fixed first rather than deferred, on the
product thinker's standing rulings; its smallest slice is its own intent.
The slice's design questions and the checklist draft's obvious ones are
answered from the record and the host's documented hook behaviour.

Refs: iss-2610170619247185
Refs: itd-2610170638184486
Assisted-by: Claude:claude-opus-5-5
The away mode held by abcd and repeated at every start, compaction and
prompt, a Stop hook that refuses to end a turn with owed work while the
person is away, and the question reroute become their own planned intent
with a seven-step spec ending in a live session capture. The ready gate
passes.

Refs: iss-2610170619247185
Refs: itd-2610180912189125
Assisted-by: Claude:claude-opus-5-5
…ng walk

Fourteen Given-When-Then criteria derived from the press release and the
product thinker's decisions, marked as a draft to walk on Thursday, and the
scope split names the first slice that builds on the draft.

Refs: itd-2610170638184486
Assisted-by: Claude:claude-opus-5-5
Refs: itd-2610170638184486
Refs: itd-2610180912189125
Assisted-by: Claude:claude-opus-5-5
The plan review of 2026-10-18 (an Opus and Sonnet pair) returned fix the
plan first. Every blocker, major and moderate is applied as a plan edit:
"own" work without a window, read from the reflog, a run's lanes and
this copy's reservations; a clearing rule for stacked branches; a stop
allowed while away only with a wake whose next fire time precedes the
earliest renewal due; questions routed by the act, with the acts only
the person may approve queued; the away record changed under a lock and
capped at seven days; present mode reading disk only; a StopFailure
entry. The return report, the status line and the bare mode print move
back to the checklist draft, and the slice is re-estimated at five days.
Eight new decisions record the refinements; the spec carries a review
round note listing each finding's disposition.

Refs: iss-2610170619247185
Refs: itd-2610180912189125
Refs: itd-2610170638184486
Assisted-by: Claude:claude-opus-5-5
Only the person sets the away mode now: guard entries refuse an agent's
every route to it that the guard reads, and the mode's push rights are
narrow, with the default branch, tags, cuts, other people's pull requests
and review-only branches excluded. The wake deadline gains a ceiling and
the host's lateness, behind comes from the forge's merge state, created
branches are recorded as own, stale and review-only branches are not, the
person-only acts are completed, and a lapsed window points at the verb
that ends it. Nine refining decisions and a proposed ADR for the network
read record it; the slice is re-estimated at eight days.

Refs: itd-2610180912189125
Refs: iss-2610170619247185
Assisted-by: Claude:claude-opus-5-5
The away mode now grants no push rights: the person's own standing
instruction decides what is committed, pushed and opened, as before, and
an owed item that needs a push is pushed only if that instruction allows,
otherwise queued and handed back for the window. The guard entries that
made setting the mode person-only, the guard's reading of go run and
built binaries as abcd, the forge listing's reuse cache and the fuller
mode back print move to the checklist draft as named remaining work. The
created-branch recorder, the ownership, link and stale rules, the wake
deadline, the merge-state reading and the person-only acts stay, so
round 2's blockers stay fixed. Re-estimated at 5.75 days over seven
steps, down from eight. Two decisions record it, the first superseding
round 2's choice (a); the invariant 7 ADR drops the cache while proposed.

Refs: itd-2610180912189125
Refs: itd-2610170638184486
Refs: iss-2610170619247185
Assisted-by: Claude:claude-opus-5-5
…third review

A queued push now hands nothing back unless the person ran abcd mode
autonomous --hold-pushes, and then only a verified branch's unpushed
item; a qualifying wake releases an unchanged set of work the session can
do itself at most twice in a row. The cheap minors are written in, a
Review round 3 section lists each finding, and the size is re-estimated
at 7.5 days of work.

Refs: itd-2610180912189125
Assisted-by: Claude:claude-opus-5-5
…heck

# Conflicts:
#	.abcd/development/research/notes/2026-08-15-decomposition-calibration.md
internal/core/autonomy keeps one away record per repository on the
machine, in the away store keyed on the root commit, changed only under
fsutil's file lock with a re-read inside it. A record or lock that is
not a regular file this account owns that no one else can write, within
64 KiB, reads as not away and refuses a writer, naming the file. A
record lapses at its end, seven days at most; repeated identical stops
collapse into one entry. Record and Owed are fixed here so the
owed-work and stop-verdict lanes can build against them.

Refs: itd-2610180912189125
Assisted-by: Claude:claude-opus-5-5
RefuseParkWhileAway refuses product-thinker and facilitator while the
away mode holds, naming abcd decide and abcd mode queue; managed is
never refused and the three-word vocabulary is unchanged.

Refs: itd-2610180912189125
Assisted-by: Claude:claude-opus-5-5
The away verbs reach autonomy from the CLI and the plugin page:
autonomous [--until <time>] [--hold-pushes] sets the record, queue keeps
a step for the person's return, and back prints one line per queued
step, hand-back and stop, records who ended it, and closes it. While
the record holds, mode product-thinker and facilitator are refused
naming abcd decide. Every refusal exits 2 with nothing written. The
verb gains its own brief chapter, as surface_coverage requires once it
has sub-verbs, and the derived reference and appendix are regenerated.

Refs: itd-2610180912189125
Assisted-by: Claude:claude-opus-5-5
Step 1 of the overnight-stall plan is landed; its Settled here section
records the readings the build lane chose where the plan was silent,
for review.

Refs: itd-2610180912189125
Assisted-by: Claude:claude-opus-5-5
While the away record holds, the session-start hook prints the away
block on every source, compaction included, and the prompt hook adds it
to every prompt outside the rules loader's dedup ledger. The block says
the mode grants no rights, lists the acts only the person may approve,
routes every other question to abcd decide, and allows abcd mode back
only on the person's word. The person sees one line on every prompt
naming who set it, from which copy, the steps queued and items handed
back, and abcd mode back; a lapsed, unclosed record holding a queued
step or a recorded stop shows one line naming that verb instead. The
session-start hook notes each session's start copy in the away store,
first note kept, so a hook run from a worktree the session made
resolves to the start copy. The prompt hook reads the record through
the drain's root-commit lookup, so it still makes one a prompt.

Refs: itd-2610180912189125
Assisted-by: Claude:claude-opus-5-5
…branch reading

The guard's pre-tool hook reads each shell command for the branches it
creates (git worktree add -b|-B, git switch -c|-C, git checkout -b|-B,
git branch <b>, a bare git worktree add, git worktree add on an existing
branch) and carries a record across git branch -m|-M, before the guard
judges it. A created name is kept only when no branch of that name exists;
names match whole. The list is per working copy, keyed on the copy key, in
the away store, written under the store's lock, capped at the hundred most
recent. It records whether or not the person is away and refuses nothing:
a store it cannot write leaves one stderr line.

Refs: itd-2610180912189125
Assisted-by: Claude:claude-opus-5-5
Judge is the stop verdict, pure over the away record, the owed set, the
host's payload and the session's state: a prose question is blocked
once; owed work blocks, naming the next item, unless a qualifying wake
holds the session waiting, and a wake releases an unchanged set of
capped items at most twice in a row; with nothing owed the session
takes the next ready item or schedules a look. A wake qualifies when
its latest fire time, read by abcd's own five-field cron parser with
the host's lateness added, falls before the deadline: the earliest
renewal due less the skew, and at most half an hour on while work is
owed. stop_hook_active alone drives the count of consecutive blocks,
and the block passing the host's cap is recorded as a stop possibly
forced by it. A hand-back is accepted only under --hold-pushes and only
for an unpushed branch's item. The session's state is kept per session
id in the copy's local tier. Owed gains NextRenewal and HandedBack.

Refs: itd-2610180912189125
Assisted-by: Claude:claude-opus-5-5
While the away record holds, the guard's question gate refuses every
call to the host's question tool before any other check, abcd's or
another tool's, chipped or not, with one reason: an act only the person
may approve is queued with abcd mode queue, and every other question is
decided and recorded with abcd decide. No mode is set from a chip and
nothing is marked open. The list and the route live in
autonomy.PersonOnlyActs and autonomy.QuestionRoute, so the away block
and the stop check can repeat the same text. An away record the store
refuses reads as not away, and the hook says so with exit 1.

Refs: itd-2610180912189125
Assisted-by: Claude:claude-opus-5-5
The guard chapter, the guard page and the mode page say the gate
refuses every question while away with one reason routing it by the
act, sets no mode, and that nothing detects the act mechanically.

Refs: itd-2610180912189125
Assisted-by: Claude:claude-opus-5-5
…hoices

Refs: itd-2610180912189125
Assisted-by: Claude:claude-opus-5-5
The away block and the stop check's prose-question block now carry
autonomy.QuestionRoute, the question reroute's one statement of the
acts only the person may approve, instead of a list of their own. A
placeholder file stands in for the reroute's question.go until its
branch merges, with exactly its text, and is deleted then.

Refs: itd-2610180912189125
Assisted-by: Claude:claude-opus-5-5
autonomy.ReadOwed(ctx, ReadOptions) is the stop check's one seam onto owed
work. While the person is present it reads disk only: renewals due and
unpushed own branches. While away it adds one listing of the open pull
requests under a ten-second deadline, for failing, behind (the forge's
merge state, DIRTY as behind), stacked and uncarried work, and fails open
to the disk kinds, naming the pull requests as not read.

A branch is own through this copy's reflog or its created-branch list
while it holds a recent commit of this checkout's or a live reservation
links it, as a lane of a live run, or as the head of the person's pull
request a reservation under this copy's key links by head branch or a
closing trailer. A stacked branch is judged by the clearing rule, review
and receipts included. With --hold-pushes a queued step hands back a
verified branch's unpushed item, recorded once per branch and head.

landing.GHContext binds the forge client to a caller's deadline; brief
invariant 7 gains the away mode's one listing per check.

Refs: itd-2610180912189125
Assisted-by: Claude:claude-opus-5-5
…e's choices

Refs: itd-2610180912189125
Assisted-by: Claude:claude-opus-5-5
abcd hook stop maps the Stop payload onto the verdict: it reads the
session's start copy from the session-start note, the away record, the
owed set through one seam the owed reader fills, and the session's
state, then answers {"decision":"block","reason":...} on a block, a
systemMessage naming owed work while the person is present, and
nothing otherwise. It records accepted hand-backs and the stop passing
the host's cap (CLAUDE_CODE_STOP_HOOK_BLOCK_CAP raises it) under the
store's lock. A malformed payload, an unreadable record or session
state, or a lock not had allows the stop with a stderr line. abcd hook
stop-failure records a turn ended on an API error, with its type, while
the record holds, and writes nothing while the person is present. The
manifest gains Stop (30-second timeout) and StopFailure entries whose
scripts never download the binary, and the brief's hook paragraphs name
them.

Refs: itd-2610180912189125
Assisted-by: Claude:claude-opus-5-5
Steps 2 and 5 of the overnight-stall plan are landed; the plan's
Settled here section records the readings build lane 1 chose where the
plan was silent, for review, among them the owed reader's seam and the
placeholder for the question reroute's text.

Refs: itd-2610180912189125
Assisted-by: Claude:claude-opus-5-5
Step 6, the question reroute, lands beside steps 2 and 5. Its
question.go carries the text lane 1's question_placeholder.go stood in
for, word for word, so the placeholder is removed in this merge to keep
the tree building.

Assisted-by: Claude:claude-opus-5-5
Steps 3 and 4, the created-branch recorder and the owed-work reader,
land beside steps 2, 5 and 6. Three conflicts, each kept both ways: the
guard chapter keeps the question gate's away paragraph and adds the
recorder's section after it; the autonomy package doc keeps the verdict's
section and adds the branch list and the reader after it; the spec keeps
every lane's landed line and Settled here entry, lane 2's after step 6's.

Assisted-by: Claude:claude-opus-5-5
ReadOwed now returns Owed.NextRenewal, the earliest renewal due among
the live reservations under this copy's key, which the wake deadline
needs, and moves a queued push's unpushed item into Owed.HandedBack
instead of recording it. The verdict accepts a hand-back and the stop
hook records what it accepted, so a hand-back has one owner and is never
recorded for an item the verdict puts back among the owed.

Assisted-by: Claude:claude-opus-5-5
The Stop hook's seam now defaults to autonomy.ReadOwed and is asked with
autonomy.ReadOptions, replacing lane 1's local stand-in, which read
nothing owed. An end-to-end test runs the hook against a real checkout
with an own branch holding unpushed commits and a stub forge client: away,
the stop is blocked with the push sentence that defers to the person's
standing instruction; present, it is allowed and the forge never asked.

Assisted-by: Claude:claude-opus-5-5
The created-branch recorder now resolves its copy through the
session-start note (autonomy.ResolveCopy), falling back to the payload
cwd's worktree when the session has no note, so a branch a session cuts
after changing into a worktree it made is recorded under the copy the
stop check reads.

Assisted-by: Claude:claude-opus-5-5
…gin is known

adr-2610181003541997, still proposed, is amended in place: the stop
check's one listing covers every open pull request when the person's
forge login is known, since the reader needs other people's pull
requests to keep them out of the session's own, and the person's own
alone when it is not. Invariant 7's clause and the spec's design text say
the same. The spec records the integration's choices: the placeholder
removed in the merge, the stop hook as the one owner of the hand-back
record, the wired seam and the recorder keyed on the start copy.

Assisted-by: Claude:claude-opus-5-5
REPPL added 14 commits October 18, 2026 14:42
…hes as not own

Two minors of the diff review:
- The carried check accepted a fork's or another person's pull request
  naming the reservation, which findOwn excludes; it now applies the
  same filter, so such a pull request carries nothing.
- Sibling copies on one machine share the author email, so a branch this
  copy only checked out once to review read as its own. A branch from the
  reflog that another worktree has checked out now is that worktree's,
  unless this copy's sessions recorded creating it or a linked pull
  request names it.

Assisted-by: Claude:claude-opus-5-5
The spec gains the "Diff review round 1" table, one row per finding with
its fix and tests, the fix round's choices under "Settled here", and the
hooks reference read again: the Stop feedback form and StopFailure's
input. The mode command page says `back` ends the mode whatever state
the record is in, and that a full queue refuses.

Assisted-by: Claude:claude-opus-5-5
The away hooks still asked git for the checkout's root before finding no
.abcd/ there. They now look for the nearest .git entry first, with no git:
a .git folder with no .abcd/ beside it is left untouched, and only a
managed-looking checkout, or a linked worktree's .git file, is asked of
git. The release orchestrator's rule: abcd changes nothing about how an
unmanaged repository behaves.

Assisted-by: Claude:claude-opus-5-5
A docs-fidelity review of the stall slice found seven sentences the code
contradicts. `mode back` never refuses on the record's or lock's state, so
its sentence and the mode chapter now say it ends the mode whatever that
state, and setting the mode carries a record merely past 64 KiB. The
StopFailure shim names the API-error stop it did not record, not the stop
check. The branch recorder is gated on abcd managing the checkout, not on
the local tier. The guard hook also writes the mode and the open-question
marker on an admitted abcd question. The away-record warning status appears
only where the question would otherwise pass silently, an undecodable
question payload fails open before the away check, and the on-disk behind
check covers every pushed branch of the session's own when the pull-request
listing fails.

Assisted-by: Claude:claude-opus-5-5
While the person is marked away, a session that tries to end its turn
with work it owns still owed is sent back to it; a waiting session
needs a wake in time to keep its reservations; a question is routed by
the act to a recorded decision or the person's return queue; and the
away record is repeated at every start, compaction and prompt.

Delivers: itd-2610180912189125
Resolves: iss-2610170619247185
Assisted-by: Claude:claude-opus-5-5
TestAwayHooksLeaveUnmanagedRepoAlone joined the home folder's name by
hand, which the home-name boundary refuses in a test; the fixture now
follows the name when it changes.

Assisted-by: Claude:claude-opus-5-5
The away mode added 04-surfaces/26-mode.md without joining briefDocs, so
the brief-surface crosscheck would not read it. It is pinned in
chapter-slug order, between memory and reading.

Assisted-by: Claude:claude-opus-5-5
Eleven exported functions in internal/core/autonomy were reached only
from their own package, or from tests in the CLI package: begin,
createdFile, latestFire, openStore, owedKinds, parseCron, parseUntil,
qualifyingWake, resolveWho, sessionStatePath, and wakeDeadline (renamed
from Deadline so it does not collide with the deadline locals it is
assigned to). PersonOnlyActs is gone: its own test reads the list
directly, and the guard test now holds the refusal to QuestionRoute, which
carries every act. The CLI tests that opened the store or built a record
by hand now go through Resolve and SetAway, the paths the verbs take.

Assisted-by: Claude:claude-opus-5-5
…o baseline

Run alone, the test counted the guard's once-per-process git version
check as the recorder's work.

Assisted-by: Claude:claude-opus-5-5
@REPPL
REPPL enabled auto-merge October 18, 2026 17:18
The literal tripped the secret scan's generic-api-key rule on the commit
that added it; that commit's line is fingerprinted as history residue.

Assisted-by: Claude:claude-opus-5-5
@REPPL
REPPL added this pull request to the merge queue Oct 18, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 18, 2026
@REPPL
REPPL added this pull request to the merge queue Oct 22, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 22, 2026
REPPL added 3 commits October 22, 2026 21:01
Main changed gitutil.RootCommit to return (string, error), so a failed
git call is never read as a repository with no root commit. The away
store, written against the old signature, no longer built on the merge
result, and the merge queue dropped the PR twice for it.

openStore now stops with git's failure rather than ErrNoRootCommit, as
lab and the implement loop do. ResolveCopy treats a noted copy git
cannot answer for as unconfirmed and acts from cwd's worktree, as it
already did when Toplevel failed there.

Assisted-by: Claude:claude-opus-5-5
The review pair on the RootCommit adaptation found that the Stop hook
allows a stop with nothing on stderr when git does not answer for the
root commit. The behaviour predates the adaptation; it is filed rather
than changed on the release's critical path.

Refs: iss-2610222021318139
Assisted-by: Claude:claude-opus-5-5
@REPPL
REPPL enabled auto-merge October 22, 2026 22:09
@REPPL
REPPL added this pull request to the merge queue Oct 22, 2026
Merged via the queue into main with commit 3e8cd68 Oct 23, 2026
14 of 15 checks passed
@REPPL
REPPL deleted the feat/away-mode-stop-check branch October 23, 2026 00:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant