Skip to content

D follow-ups required by the F1 runner lifecycle contract #51

Description

@mchwang

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

  1. 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, settled never resolves, and shutdown waits forever with the Store open. 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.)
  2. Asynchronous launch. The Claude and Codex start calls do their Docker setup synchronously (createVendorNetwork, profile creation and startProfileInvocation use execFileSync/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, and settled must resolve only after setup cleanup.
  3. Owner, attempt and allocation labels on every resource. InvocationInput gains runnerOwner, 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 carries io.codeboost.runner, io.codeboost.attempt and io.codeboost.allocation: containers, networks, egress proxies, task-storage volumes and keepers.
  4. 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. exportTaskDiff and removeTaskFilesystems must accept these handles, because ownership today lives in a process-local WeakMap that 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.
  5. Abortable preparation helpers. createTaskClone (git/clone.ts, execFileSync) and prepareTaskFilesystems (agents/container/storage.ts, execFileSync/spawnSync) block the event loop and cannot be cancelled. Add asynchronous variants that take an AbortSignal, 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

  1. Bounded task-volume export. F cannot read Docker volumes from the host. Add exportTaskDiff(filesystems | recoveryHandle, maxBytes): it runs git diff against the last codeboost commit in a read-only container and returns at most 1 MiB. It must accept an AbortSignal and stop, and await, its own container and subprocesses when the signal aborts, because F gives each export a 60-second deadline (maxBytes bounds only the returned data, not the Docker work). F saves this as partial output for stopped attempts.

Done when

  • agents/contract.ts and 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions