Skip to content

feat(runner): settled terminal event + idempotent finalizer (§0.1)#5

Merged
casabre merged 1 commit into
mainfrom
feat/settled-finalizer
Jul 3, 2026
Merged

feat(runner): settled terminal event + idempotent finalizer (§0.1)#5
casabre merged 1 commit into
mainfrom
feat/settled-finalizer

Conversation

@casabre

@casabre casabre commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Third §0.1 increment — the one Runner-port change from the design (§3.2).

Why

ProcessRunner.cancel() emits neither done nor error, and the close handler early-returns when cancelled — so a finalizer subscribed to done/error would leak on the cancel path (worktree/slot/span cleanup skipped). Adding a single terminal signal fixes this and gives every terminal path one guaranteed cleanup hook.

What

  • Runner port — additive settled event carrying a TerminalReason (succeeded | failed | cancelled | timed-out). No existing event changed; done/error still fire before settled when they occur.
  • ProcessRunner — guarded _settle() wired into every terminal path: spawn-failure, no-stdout, child error, close (reason from cancelled/exit code), timeout, idle, and cancel-before-start. Emits at most once.
  • AgentTaskExecutor — subscribes settled as the idempotent finalizer (evicts the active runner). The seam where worktree.dispose()/slot-release attach later. Publishes no A2A event on settled (event stream unchanged).

Gates

typecheck ✅ · lint ✅ · test:coverage ✅ (372 tests, 100%) · build ✅. New: settled contract tests (succeeded/failed/cancelled/once) + executor finalizer cleanup.

🤖 Generated with Claude Code

Introduce a `settled` event on the Runner port that fires exactly once per run,
on EVERY terminal path — including cancellation, which today emits neither
`done` nor `error` (ProcessRunner.cancel() emitted nothing, so a done/error-only
finalizer would leak on cancel). `done`/`error` still fire before `settled` when
they occur; `settled` carries a TerminalReason (succeeded | failed | cancelled |
timed-out).

- ProcessRunner: guarded _settle() wired into spawn-failure, no-stdout, child
  error, close (reason from cancelled/exit code), timeout, idle, and
  cancel-before-start. Emits at most once.
- AgentTaskExecutor: subscribes `settled` as the idempotent finalizer (evicts the
  active runner) — the seam where worktree.dispose()/slot-release attach later.
  Publishes no A2A event on settled (preserves the event stream).

Contract-additive to the Runner port; no existing event changed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@casabre casabre merged commit 22c679c into main Jul 3, 2026
10 checks passed
@casabre casabre deleted the feat/settled-finalizer branch July 3, 2026 21:51
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