Repository navigation
Closed
Description
Lane D follow-up to D5 (#50). The F1 runner lifecycle contract (#49, docs/implementation/runner-lifecycle.md) needs these D changes. D5 merged the real-Docker isolation gate but did not change agents/contract.ts, so none of them exist yet.
The F1 implementation can start against fakes now. It must not merge until items 1–5 land. Item 6 is needed before F2's writable attempts.
Before the F1 implementation merges
- Settlement always ends. The supervisor retries unfinished container, network and setup cleanup every second with no limit (
agents/adapters/supervisor.ts; D5's hand-off doc says "The supervisor retries cleanup until then"). If Docker is unreachable,settlednever resolves, and shutdown waits forever with theStoreopen. Either bound the retries and settle with a terminal cleanup-failure result, or prove in the real-Docker suite that every cleanup path ends, including when the daemon is unreachable. (F1 decision 4 depends on this.) - Asynchronous launch. The Claude and Codex start calls do their Docker setup synchronously (
createVendorNetwork, profile creation andstartProfileInvocationuseexecFileSync/spawnSync). While that runs, the server cannot begin shutdown or cancel. The start call must return its handle at once and run setup asynchronously inside the handle:cancel()must work during setup, andsettledmust resolve only after setup cleanup. - Owner, attempt and allocation labels on every resource.
InvocationInputgainsrunnerOwner, a 32-character hex ID that F creates once per database. Every allocator that runs before an invocation also takes it, together with the attempt ID and a caller-provided allocation ID:prepareTaskFilesystems(which takes no owner today) and vendor network creation. Every resource carriesio.codeboost.runner,io.codeboost.attemptandio.codeboost.allocation: containers, networks, egress proxies, task-storage volumes and keepers. - Scoped crash recovery. Add
recoverLeftovers(runnerOwner): Promise<RecoveryReport>. It acts only on resources labelled with that owner: it stops and removes agent containers, egress proxies and networks. It keeps task storage whole (volumes plus keeper) and returns an authenticated recovery handle for each, registered in D's ownership map after checking the labels.exportTaskDiffandremoveTaskFilesystemsmust accept these handles, because ownership today lives in a process-localWeakMapthat is empty after a restart. It rejects with a bounded diagnostic if anything cannot be stopped or removed. Unlabelled resources from older builds are reported, never removed. - Abortable preparation helpers.
createTaskClone(git/clone.ts,execFileSync) andprepareTaskFilesystems(agents/container/storage.ts,execFileSync/spawnSync) block the event loop and cannot be cancelled. Add asynchronous variants that take anAbortSignal, spawn their subprocesses in their own process group, expose the group ID, and settle only after those processes have exited.
Before F2's writable attempts
- Bounded task-volume export. F cannot read Docker volumes from the host. Add
exportTaskDiff(filesystems | recoveryHandle, maxBytes): it runsgit diffagainst the last codeboost commit in a read-only container and returns at most 1 MiB. It must accept anAbortSignaland stop, and await, its own container and subprocesses when the signal aborts, because F gives each export a 60-second deadline (maxBytesbounds only the returned data, not the Docker work). F saves this as partial output for stopped attempts.
Done when
agents/contract.tsand the storage, network and adapter APIs expose the items above, with contract tests.- The real-Docker suite covers: cleanup against an unreachable daemon ending in settlement; cancel during asynchronous setup; recovery for two databases on one machine touching only its own labelled resources; recovery handles being accepted by export and removal after a simulated restart; and aborting clone and storage allocation with a child that ignores SIGTERM.
Refs: #49 (contract, sections "Shutdown" and "Launch"), #50 (D5), #22.
Metadata
Metadata
Assignees
Labels
No labels
Activity
Update F1 contract baseline for merged D5; link D prerequisites (#51)
F1: runner lifecycle and state-holder contract (#49)
Add F1e (#60) and the #51 merge condition to the F lane row
Bind Ask answers to their attempt and keep cleanup ownership
Record Ask storage left at shutdown and keep Ask off until it is gone
Reconcile the design plan with the code (#52)
Keep Ask off after a setup failure that leaves unidentifiable storage
Bound Ask against unsettled lane D cleanup and scan all D labels
Close the store on failed Ask cleanup; private locks; keep lock after…
Run Ask in the lane D agent container (#54)
Address review suggestions: shared Docker ID check, accurate bounds
D: bound cleanup retries so settlement always ends (#51 item 1) (#69)
D: return start handles at once and run Docker setup inside them (#51 …
D: label every resource with its runner, attempt and allocation (#51 …
Scan every Docker type for all codeboost labels; update the lifecycle…
D: scoped crash recovery with storage recovery handles (#51 item 4) (#72
D: abortable clone and storage preparation in process groups (#51 ite…
mchwang commentedon Sep 28, 2026
Status (2026-09-28): every item needed before the F1 implementation merges is done.
F1 is no longer blocked on lane D. The F1 stack's own order is #74 (F1b) → #57 (F1c) → #59 (F1d) → #60 (F1e); F1a (#53) is merged.
What keeps this issue open: only item 6, the bounded task-volume diff export. It is needed before F2's writable attempts (F2b, #68), not before F1. The related operations for inspecting changes and making runner commits are tracked in #66.
Merge pull request #76 from codeabovelab/feat/d-task-diff-export
D: real-Docker export through a recovery handle after a restart (#51) (…