feat(a2a): orphaned-run revival seam — reconcile re-attaches instead of terminal-failing - #66
Conversation
…of terminal-failing (TJC-1494) A serve restart/recycle drops in-memory runs; the tasks/get reconcile safety net then terminal-failed the task even when the underlying work (e.g. a Claude Managed Agents session) was still running. Add a public A2AOrphanedRunReviver hook: reconcileOrphaned consults it before failing — Some(run) re-attaches under the task's reserved runtime slot with the standard run lifecycle (failure -> terminal failed status, finish + registry cleanup), None or a reviver error falls back to the existing terminal failure. ResultManager's history-repair message is now optional (revived runs have no in-flight PreparedRun). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-1494) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review: orphaned-run revival seamSolid, well-scoped change with careful concurrency reasoning and genuinely good docstrings. The A few things worth considering: Design / correctness
Test coverageThe 5 new tests are well-chosen (success, decline, reviver-crash, one-run-under-race, revived-run-crash) and the race test correctly asserts the fork count via a gated promise. Two
Both are reachable by having the reviver's Nits
Overall a careful, minimal seam that preserves the existing safety net's convergence guarantees. The concurrency invariants check out. My only substantive ask is to ensure the consumer bounds the revived run's lifetime given point (1), and ideally add the terminal-race test from the coverage section. |
|
Live consumer validation of the seam on TJC-LP/tjc-agents#115 (dev, orcagent): serve container recycled mid-run → first 🤖 Generated with Claude Code |
Problem
A serve restart/recycle drops in-memory runs. The
tasks/getdurable-completion safety net (reconcileOrphaned) then terminal-fails the task — "Task interrupted: no active run … Resend the message to retry" — even when the underlying work is still running (e.g. a Claude Managed Agents session, which survives serve recycles by design). Downstream (TJC-LP/tjc-agents TJC-1494): a Modal infra replacement of a CMA serve container false-failed 4 long email-driven runs in one day while their sessions kept working.Change
New public
A2AOrphanedRunReviverhook, consulted byreconcileOrphanedbefore failing a non-terminal task with no runtime bus:Some(run)→ the server reserves the task's runtime slot (the existing registry mutex, so concurrent polls revive at most one run), re-loads the task to honor a concurrent terminal/cancel write, and forksrun(publisher)with the exact same lifecycle as a normal execution (catchAll→ terminal failed status;ensuring→ finish + registry cleanup). The poll returns the task stillworking.Noneor a failedrevive→ the existing terminal orphan failure, verbatim.Supporting changes:
ResultManager.userMessageis nowOption[A2AMessage]— revived runs have no in-flightPreparedRun; the history-repair message is reconstructed from persisted history (startRevivedRun).startExecution/startRevivedRunshare the fork/failure/cleanup wrapper (forkManagedRun) — no behavior change on the normal path.A2ARolenowderives CanEqual(consumers compiling with-language:strictEqualitycouldn't compare roles).A2AServerCoreConfig.orphanedRunReviver(defaultNone) + JVMA2AServerLive.Configfield. JS config inherits the default — no JS-side surface change.Tests
A2AServerCoreSpec(+5, runs on both platforms): revive re-attaches and completes; decline → terminal failure; reviver crash → terminal failure; concurrent polls fork exactly one run; a crashed revived run terminal-fails with the error detail. Full suites green: JVM 143/143, JS 190/190.Consumer: TJC-LP/tjc-agents wires this for CMA agents (watch-only re-attach to the live session). Needs a release once merged so tjc-agents can pin it.
🤖 Generated with Claude Code