feat(sessions): surface blocked and stale session run statuses#92697
feat(sessions): surface blocked and stale session run statuses#92697Hidetsugu55 wants to merge 2 commits into
Conversation
Add two run statuses to make stuck and orphaned sessions recognizable in sessions.list and the Control UI: - blocked: the blocked-liveness watchdog now maps to a distinct terminal status instead of being folded into "failed". Everywhere that treated blocked as failed for behavior (session rotation reuse, subagent reconciliation, terminal-status guards, subagent historical state) keeps the same behavior; only the surfaced status changes. - stale: a display-only projection computed by sessions.list for rows that still claim "running" but have no live run evidence in this gateway (no tracked run, no in-process embedded run, no live subagent run, no ACP session, and not updated within a grace window). Never persisted. Wired the projection into the sessions.list active-run-flags pass, widened the session run status unions (gateway, session tools, Control UI), added statusBlocked/statusStale i18n keys (English fallback across locales), and documented both statuses in docs/concepts/session-tool.md. Tests: new projectSessionRowRunStatus suite, blocked subagent reconciliation + historical-state parity, blocked liveness lifecycle mapping, sessions_list status passthrough, and Control UI badge rendering.
|
Codex review: needs real behavior proof before merge. Reviewed June 30, 2026, 8:23 PM ET / 00:23 UTC. Summary PR surface: Source +197, Tests +235, Docs +13. Total +445 across 59 files. Reproducibility: yes. for the review finding at source level: the PR implementation excludes ACP rows from stale projection, while the added session-tool docs do not mention that exception. The feature itself is proof-based rather than a failing current-main bug report. 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:
Mantis proof suggestion Risk before merge
Maintainer options:
Next step before merge
Security Review findings
Review detailsBest possible solution: Land only after the ACP docs exception is fixed, visible UI proof or an explicit proof override covers the badges, conflicts are resolved, and maintainers accept the additive status contract while keeping the broader activity-state API tracked separately. Do we have a high-confidence way to reproduce the issue? Yes for the review finding at source level: the PR implementation excludes ACP rows from stale projection, while the added session-tool docs do not mention that exception. The feature itself is proof-based rather than a failing current-main bug report. Is this the best way to solve the issue? Mostly yes: deriving Full review comments:
Overall correctness: patch is correct AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 5a73361ed2b3. Label changesLabel justifications:
Evidence reviewedPR surface: Source +197, Tests +235, Docs +13. Total +445 across 59 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
|
…tus-stale-blocked # Conflicts: # ui/src/i18n/.i18n/ar.meta.json # ui/src/i18n/.i18n/de.meta.json # ui/src/i18n/.i18n/es.meta.json # ui/src/i18n/.i18n/fa.meta.json # ui/src/i18n/.i18n/fr.meta.json # ui/src/i18n/.i18n/id.meta.json # ui/src/i18n/.i18n/it.meta.json # ui/src/i18n/.i18n/ja-JP.meta.json # ui/src/i18n/.i18n/ko.meta.json # ui/src/i18n/.i18n/nl.meta.json # ui/src/i18n/.i18n/pl.meta.json # ui/src/i18n/.i18n/pt-BR.meta.json # ui/src/i18n/.i18n/th.meta.json # ui/src/i18n/.i18n/tr.meta.json # ui/src/i18n/.i18n/uk.meta.json # ui/src/i18n/.i18n/vi.meta.json # ui/src/i18n/.i18n/zh-CN.meta.json # ui/src/i18n/.i18n/zh-TW.meta.json
|
This pull request has been automatically marked as stale due to inactivity. |
|
Closing due to inactivity. |
Summary
Adds two session run statuses so stuck and orphaned sessions are recognizable in
sessions.listand the Control UI:blocked— the blocked-liveness watchdog now maps to a distinct terminal status instead of being folded intofailed. A run that stalled waiting on something is no longer indistinguishable from a genuine failure.stale— a display-only projection computed atsessions.listtime for a row that still claimsrunningbut has no live-run evidence in this gateway (no Control UI-tracked run, no in-process embedded run for its sessionId/key, no live sub-agent run, not an ACP session, and not updated within a grace window). This is the signature of a run orphaned by a crash or restart. It is never written back to the session store.Behavior
blockedis persisted by the gateway lifecycle mapping (AgentRunTerminalReason "blocked"→"blocked"). Every place that previously treated blocked-as-failedfor behavior keeps that behavior, so this is a surfacing change, not a control-flow change:resolveTerminalMainSessionTranscriptRegistryCheck)resolveCompletionFromSessionEntry→ error outcome)isTerminalSessionStatusin both config and gateway)historicalvsinterruptedrun-state derivationstaleis derived byprojectSessionRowRunStatus, invoked from the existingsessions.listactive-run-flags pass. Only therunning→staletransition changes a value; every other status passes through untouched. Grace window is 5 minutes, sized so a brief lull between turns never flips a healthy run.GatewaySessionRow), the session tools (SessionRunStatus), and the Control UI.staleis excluded from the persistedSessionEntry["status"]type so it cannot be written to a store; the lifecycle derivation is typed to only ever yield persistable statuses.blockedwith the failed tone andstalewith the idle tone (a soft warning, not an alarm). NewstatusBlocked/statusStalei18n keys added to the English source bundle and propagated to all locales (English fallback, tracked in the locale metadata).Not in this PR (follow-ups)
statusBlocked/statusStale— currently English fallback across the 18 non-English locales, which is the standard tracked-fallback state.Tests
src/gateway/server-methods/session-active-runs.test.ts(new):projectSessionRowRunStatus— stale projection, every live-run keep-alive signal (tracked run, embedded run by id/key, live sub-agent, ACP), grace window, missingupdatedAt, and terminal pass-through.src/agents/subagent-session-reconciliation.test.ts(new):blockedpersisted status → error completion, freshness gating, running non-resolution.src/gateway/session-lifecycle-state.test.ts: blocked-liveness lifecycle error →blockedstatus.src/gateway/session-utils.subagent.test.ts: ablockedregistry-only sub-agent run reads ashistorical(parity with the prior failed-folding).src/agents/tools/sessions-list-tool.test.ts:stale/blockedpass throughsessions_list; unknown status values are dropped.ui/src/ui/views/sessions.test.ts: Control UI badge label/tone/aria forstaleandblocked.pnpm check --include-test-typesgreen;pnpm check:docsgreen (broken_links=0);pnpm ui:i18n:checkclean for all locales.Real behavior proof
Behavior or issue addressed: A session row that still claims
runningbut has no live run in this gateway (an orphan from a crash/restart) must be surfaced asstalebysessions.listwithout being persisted, while a freshly-updated running row and terminal rows are untouched; and a run ended by the blocked-liveness watchdog must persist asblockedrather thanfailed.Real environment tested: macOS (Apple Silicon) checkout of this branch, Node v25.9.0, isolated
OPENCLAW_STATE_DIR=/tmp/oc-prC-proof. The production functions ran in-process with no test doubles:listSessionsFromStore(gateway row assembly),projectSessionRowRunStatus(the exact per-row projection thesessions.listhandler runs, fed by the reallistActiveEmbeddedRunSessionIds/Keys), andderivePersistedSessionLifecyclePatch(gateway lifecycle → persisted status).Exact steps or command run after this patch:
The harness builds a real session store with three rows — a
runningrow last updated 6 min ago (past the 5 min grace), arunningrow updated 30 s ago, and adonerow — then runs thesessions.listprojection over them with no live runs registered, and separately derives the persisted status for a blocked-liveness lifecycle event and an aborted control event.Evidence after fix:
Observed result after fix: The orphaned running row (no live evidence, past the grace window) projected to
stale, while the freshly-updated running row stayedrunningand the terminaldonerow was untouched — so the projection changes exactly the orphaned row and nothing else. The blocked-liveness lifecycle event persisted asblocked; the aborted control event persisted askilled, confirming the new mapping is specific to blocked liveness and not a blanket change.What was not tested: A full live gateway driving an end-to-end model turn that crashes mid-run and then renders the stale badge in a real browser (the projection and the badge rendering are covered separately by the harness above and the Control UI unit tests); Windows runtime behavior.