fix(agents): reconcile stale restart-aborted subagent runs instead of resurrecting them (fixes #90766)#91611
Conversation
… resurrecting them restore reconciliation only treated a run as stale-unended when abortedLastRun was not true, so restart-aborted runs were exempted from the shared liveness policy and auto-recovered no matter how old. After long gateway downtime this resurrected hours-old aborted subagent runs. Drop the exemption so the existing isStaleUnendedSubagentRun window (2h or run timeout+grace) applies to aborted runs too; fresh restart-aborted runs are not yet stale and still recover. Fixes openclaw#90766 Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
Codex review: needs real behavior proof before merge. Reviewed June 21, 2026, 12:35 PM ET / 16:35 UTC. Summary PR surface: Source +2, Tests +42. Total +44 across 2 files. Reproducibility: yes. at source level. Current main has both implementation and test evidence that a three-hour restart-aborted restored subagent run remains recoverable instead of stale-pruned; I did not run tests because this review is read-only. Review metrics: 1 noteworthy metric.
Stored data model Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Security Review findings
Review detailsBest possible solution: Land one canonical stale subagent recovery fix that applies the shared liveness cutoff at restore and direct recovery boundaries, preserves fresh and timeout-extended recovery, updates docs, and includes redacted restart-path proof. Do we have a high-confidence way to reproduce the issue? Yes at source level. Current main has both implementation and test evidence that a three-hour restart-aborted restored subagent run remains recoverable instead of stale-pruned; I did not run tests because this review is read-only. Is this the best way to solve the issue? No as submitted. Removing the restore exemption is necessary, but the best fix also gates direct orphan recovery, updates the public docs, and proves the restart path with redacted real or faithful output. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against c2ee9b0be8ae. Label changesLabel justifications:
Evidence reviewedPR surface: Source +2, Tests +42. Total +44 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
Closing this. On re-evaluation against current Stale / mis-targeted. This branch is ~3900 commits behind More importantly, the behavior it removes is intentional. The So removing the exemption reverses a shipped, tested design and would break that test. Whether stale restart-aborted runs should age out (#90766) versus stay recoverable is an owner-level product decision, not a drop-in contributor fix — and #90766 is currently labeled Closing rather than force a behavior reversal that contradicts the current intended design. If a maintainer decides #90766's age-out behavior is wanted, the right shape is an owner-driven change against current |
Summary
agent.waiton a run that is no longer relevant.resolveSubagentRunOrphanReason(subagent-registry-helpers.ts) only classifies a run asstale-unended-runwhenabortedLastRun !== true. So restart-aborted runs were exempted from the shared stale-liveness policy and preserved indefinitely, then auto-recovered no matter how old.isStaleUnendedSubagentRunpolicy (STALE_UNENDED_SUBAGENT_RUN_MS = 2h, or the run's timeout + grace) applies to aborted runs too. A long-downtime aborted run is now reconciled (pruned) instead of resurrected.evaluateSubagentRecoveryGate, 2-minute window) and non-aborted stale pruning are untouched. No new config or stale window — it reuses the existing liveness policy.Change Type
Scope
Linked Issue
User-visible / Behavior Changes
After long downtime, the gateway no longer auto-resumes subagent runs that were aborted more than the stale-liveness window ago; they are reconciled out of the registry. Fresh restart-aborted runs (within the window) still recover as before.
Security Impact
Evidence
reconciles stale unended restored runs even when restart-aborted (#90766)(3-hour aborted run → pruned, noagent.wait) fails with the exemption restored. Newrecovers fresh restart-aborted runs that are not yet stale (#90766)(1-minute aborted run → still recovered) stays green throughout, proving fresh restart recovery is preserved.subagent-registry.persistence.test.ts,subagent-orphan-recovery.test.ts,subagent-registry-queries.test.ts(+ co-located). oxlint + oxfmt clean.Human Verification
restartRegistry(); a 1-minute aborted run is still kept and resumed viaagent.wait; non-aborted stale pruning and the re-wedge gate are unchanged.Compatibility / Migration
AI assistance
AI-assisted (Claude Code). Tested locally (92 tests + lint/format). Author understands the change. GitHub Codex review covers the AI-review pass.
Real behavior proof
Behavior addressed: restart/orphan recovery no longer resurrects subagent runs aborted longer ago than the shared stale-liveness window; those are reconciled instead, while fresh restart-aborted runs still recover.
Real environment tested: Local unit + source verification on Node 22.22.1 (macOS). No live multi-hour gateway-downtime run.
Exact steps or command run after this patch:
node node_modules/vitest/vitest.mjs run src/agents/subagent-registry.persistence.test.ts src/agents/subagent-orphan-recovery.test.ts src/agents/subagent-registry-queries.test.tsplus oxlint + oxfmt on the touched files.Evidence after fix: 92 tests pass including the updated stale-aborted-pruning test and the new fresh-aborted-recovery test; re-adding the
abortedLastRun !== trueexemption turns the stale-aborted test red (it gets resurrected) while the fresh test stays green.Observed result after fix: a 3-hour-old aborted run is pruned from the registry (
callGatewaynot called); a 1-minute-old aborted run is recovered (agent.wait, kept in the registry).What was not tested: a live gateway restart after multi-hour downtime against a real registry (verified at unit/source level only).
Risks and Mitigations