Read-only survey of agent-runtime 0.174.0 against laude-institute/headlong's persistent-agency loop, driven by a measured failure in discovery-lab.
The measured problem
Across every director-v2 run with a ledger (discovery-lab, 2026-08-22..24), 8 of 8 steer-continuity edges died with runtime-has-no-inbox. Directors direct only at spawn; mid-run supervision has never delivered a message.
Finding 1: that is a config omission, not a runtime bug
Scope.send (src/runtime/supervise/scope.ts:1067-1073) delivers only if the child executor exposes deliver. The sandbox executor builds an inbox only under the opt-in branch (src/runtime/supervise/runtime.ts:1379-1400, if (seam.steering)); the fallthrough singleShotDriver has no deliver key. Same for provider. Nothing in src/ sets steering by default; the only sites are tests (tests/runtime/mid-flight-steering.test.ts:262-268).
Proposal: either default steering on for sandbox/provider workers with a documented box-held-across-turns cost, or make steer_agent refuse loudly at spawn time when the target has no inbox, instead of reporting delivery failure after the fact. Today a director can spend a whole run steering into a void and only the ledger notices.
Finding 2: no persistent idle loop exists
Every loop is run-until-settled: tool-loop.ts:203-235 returns the moment the model replies with no tool call; supervisor.ts:699-734 tears down all live children when root act() returns. No heartbeat/tick/backoff anywhere (the only idle read-models, progress.ts:80-83 and stop-rules no-progress/all-stalled, END runs on idleness).
Headlong's liveness is a timer with exponential backoff (5s doubling to a 300s cap, reset by any real event; measured $1-2/hour idle). Smallest transplant: an onIdle hook in runBrainLoop (tool-loop.ts:218) returning 'settle' | { resumeAfterMs }, implemented in coordination-driver.ts:1032 as drain inbox -> wait on the coordination bus or a backoff timer via existing Scope.wait('timer') (scope.ts:1091) -> push an operator turn and continue; journal each sleep as waiting/woken so restart resumes the schedule; plus a wait coordination verb so a brain can choose to sleep (today await_event burns an inference turn per 15s poll). Constraints: must live inside act() or the join barrier kills the tree; must not extend the pool deadline (scope.ts:1108-1110); idle turns should be excluded from the equal-compute unit (tool-loop.ts:154).
Finding 3: journal fork/merge is partial
Journals are append-only with parent pointers (durable/spawn-journal.ts, types.ts:1049-1270); sandbox box fork exists (sandbox-lineage.ts:158-215); no history fork/merge. Smallest: forkTree(srcRoot, upToSeq, newRoot) copying the seq <= upToSeq prefix plus a forked-from event; merge as a union of settlements keyed by assignmentId/key with a merged-from event. Blobs are content-addressed so nothing copies. Risks: re-seq for replaySpawnTree, bridge session re-attach via assignmentId (supervise.ts:206-216), budget double-count (supervisor.ts:769-790).
What NOT to import from headlong
Its "self-improvement by fork, test, merge" and goal generation are README text with no code path (the dedicated thinkers were built and removed in Aug 2026). agent-runtime already has what it lacks: promotionGate (paired bootstrap, minPairedTasks=6), approval-gated activation (improvement-cycle.ts:988-1007), and the invariant that the measured thing never writes its own grade (wave-types.ts:396-401). Keep promotion outside act().
Full seam survey with file:line evidence is in the discovery-lab session log; happy to attach.
Read-only survey of agent-runtime 0.174.0 against laude-institute/headlong's persistent-agency loop, driven by a measured failure in discovery-lab.
The measured problem
Across every director-v2 run with a ledger (discovery-lab, 2026-08-22..24), 8 of 8 steer-continuity edges died with
runtime-has-no-inbox. Directors direct only at spawn; mid-run supervision has never delivered a message.Finding 1: that is a config omission, not a runtime bug
Scope.send(src/runtime/supervise/scope.ts:1067-1073) delivers only if the child executor exposesdeliver. The sandbox executor builds an inbox only under the opt-in branch (src/runtime/supervise/runtime.ts:1379-1400,if (seam.steering)); the fallthroughsingleShotDriverhas nodeliverkey. Same forprovider. Nothing insrc/setssteeringby default; the only sites are tests (tests/runtime/mid-flight-steering.test.ts:262-268).Proposal: either default
steeringon for sandbox/provider workers with a documented box-held-across-turns cost, or makesteer_agentrefuse loudly at spawn time when the target has no inbox, instead of reporting delivery failure after the fact. Today a director can spend a whole run steering into a void and only the ledger notices.Finding 2: no persistent idle loop exists
Every loop is run-until-settled:
tool-loop.ts:203-235returns the moment the model replies with no tool call;supervisor.ts:699-734tears down all live children when rootact()returns. No heartbeat/tick/backoff anywhere (the only idle read-models,progress.ts:80-83and stop-rulesno-progress/all-stalled, END runs on idleness).Headlong's liveness is a timer with exponential backoff (5s doubling to a 300s cap, reset by any real event; measured $1-2/hour idle). Smallest transplant: an
onIdlehook inrunBrainLoop(tool-loop.ts:218) returning'settle' | { resumeAfterMs }, implemented incoordination-driver.ts:1032as drain inbox -> wait on the coordination bus or a backoff timer via existingScope.wait('timer')(scope.ts:1091) -> push an operator turn and continue; journal each sleep aswaiting/wokenso restart resumes the schedule; plus awaitcoordination verb so a brain can choose to sleep (todayawait_eventburns an inference turn per 15s poll). Constraints: must live insideact()or the join barrier kills the tree; must not extend the pool deadline (scope.ts:1108-1110); idle turns should be excluded from the equal-compute unit (tool-loop.ts:154).Finding 3: journal fork/merge is partial
Journals are append-only with parent pointers (
durable/spawn-journal.ts,types.ts:1049-1270); sandbox box fork exists (sandbox-lineage.ts:158-215); no history fork/merge. Smallest:forkTree(srcRoot, upToSeq, newRoot)copying theseq <= upToSeqprefix plus aforked-fromevent; merge as a union of settlements keyed byassignmentId/keywith amerged-fromevent. Blobs are content-addressed so nothing copies. Risks: re-seq forreplaySpawnTree, bridge session re-attach viaassignmentId(supervise.ts:206-216), budget double-count (supervisor.ts:769-790).What NOT to import from headlong
Its "self-improvement by fork, test, merge" and goal generation are README text with no code path (the dedicated thinkers were built and removed in Aug 2026). agent-runtime already has what it lacks:
promotionGate(paired bootstrap,minPairedTasks=6), approval-gated activation (improvement-cycle.ts:988-1007), and the invariant that the measured thing never writes its own grade (wave-types.ts:396-401). Keep promotion outsideact().Full seam survey with file:line evidence is in the discovery-lab session log; happy to attach.