feat(dashboard): surface recovery state in task view (#1043)#1119
feat(dashboard): surface recovery state in task view (#1043)#1119shaun0927 wants to merge 2 commits into
Conversation
Surface read-only TaskRun NEEDS_HELP metadata and active handoff context in the terminal task view so operators can see why automation paused and how to resume without digging through JSON files. Constraint: Stack on #955 dashboard task view and read #1039/#1040 metadata defensively from disk because those branches may merge in a different order. Rejected: Dashboard resume/cancel controls | recovery state must remain read-only and MCP tools own mutation. Confidence: high Scope-risk: moderate Directive: Keep dashboard recovery readers best-effort and non-throwing; never add writes or hotkey mutations to this pane. Tested: npm test -- --runTestsByPath tests/dashboard/views/ledger-views.test.ts --runInBand Tested: npm run build Tested: npm run lint:changed Co-authored-by: OmX <omx@oh-my-codex.dev>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 18f70a1fdf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| async function readTaskRuns(root: string, limit: number): Promise<TaskRunRecovery[]> { | ||
| const entries = await readdirSafe(root); | ||
| const rows: TaskRunRecovery[] = []; | ||
| for (const entry of entries.slice(0, limit)) { |
There was a problem hiding this comment.
Apply TaskRun limit after timestamp ordering
The loop truncates entries before any ordering, then sorts only the truncated subset by requested_at. Because fs.readdir does not provide a reliable recency order, dashboards with more than limit run folders can omit the most recent NEEDS_HELP runs entirely, so operators may not see current recovery state. Collect all readable rows first, sort by requested_at, and only then apply the limit.
Useful? React with 👍 / 👎.
| async function readHandoffs(root: string, limit: number): Promise<HandoffRecovery[]> { | ||
| const entries = await readdirSafe(root); | ||
| const rows: HandoffRecovery[] = []; | ||
| for (const entry of entries.slice(0, limit)) { |
There was a problem hiding this comment.
Apply handoff limit after expiration ordering
This code also slices directory entries before sorting by expires_at, so only an arbitrary subset is ranked. In environments with many handoff records, active or soon-expiring handoffs can be dropped before sorting and never shown in the recovery panel, which defeats the new visibility feature. Read all candidate rows, sort them, then apply limit.
Useful? React with 👍 / 👎.
Resolve the stacked dashboard test conflict by preserving both recovery-pane read-only coverage and ledger refresh timer coverage. Constraint: PR #1119 targets the dashboard-ledger-views branch, not develop, and only the shared ledger-views test file conflicted. Rejected: Taking either side wholesale | that would drop either recovery state coverage or dashboard refresh guard coverage. Confidence: high Scope-risk: narrow Directive: Keep dashboard stack conflict fixes test-only unless source conflicts require owner review. Tested: npx jest tests/dashboard/views/ledger-views.test.ts --runInBand --forceExit; git diff --check for touched file. Not-tested: Full GitHub Actions matrix. Co-authored-by: OmX <omx@oh-my-codex.dev>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a6a60e47a1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const recoveryLines = this.renderRecovery(data.recovery, width); | ||
| const visibleRows = Math.max(0, size.rows - 7 - recoveryLines.length); | ||
| for (const t of data.tasks.slice(0, visibleRows)) { |
There was a problem hiding this comment.
Render recovery pane even when no task rows are present
Callers can provide recovery metadata independently of the task ledger (for example, when TaskRun/handoff files exist but tasks is empty), but renderRecovery(...) is only reached inside the else branch for non-empty tasks. In that state the view shows only the empty-task hint and silently drops NEEDS_HELP/ACTIVE recovery signals, which defeats the operator visibility this feature adds.
Useful? React with 👍 / 👎.
| truncate(` cursor=${run.current_cursor || '—'} resume=${run.resume_hint || '—'}`, width - 4), | ||
| width, | ||
| )); | ||
| const evidence = (run.evidence || []).slice(0, 3).map(item => `${item.kind}:${item.ref}`).join(', ') || '—'; |
There was a problem hiding this comment.
Guard evidence entries before dereferencing fields
The reader casts meta.last_evidence to typed objects without validating each element, and rendering unconditionally accesses item.kind/item.ref. If the on-disk JSON contains null/undefined/non-object entries (e.g., partial or malformed metadata), this line throws and can break dashboard rendering instead of degrading gracefully.
Useful? React with 👍 / 👎.
Progress / Review status
Auto-refreshed 2026-05-13 — owner comments cleaned up to reduce review noise.
feat/1043-dashboard-recovery→feat/865-dashboard-ledger-viewsa6a60e4— Compose dashboard recovery tests with ledger refresh testsOwner comment cleanup: 0 issue + 0 inline review comments deleted. Outstanding feedback from automated/external reviewers above is unchanged.
Summary
NEEDS_HELPreason/resume hint/cursor/evidence/checkpoint metadata when present.Closes #1043.
Stacked on #955 /
feat/865-dashboard-ledger-views; merge #911 then #955 first. This PR reads #1039/#1040 metadata defensively from disk so merge order stays flexible.Direction / duplicate check before implementation
Success criteria
NEEDS_HELPTaskRuns render distinctly in the task dashboard recovery panel.Verification performed
npm test -- --runTestsByPath tests/dashboard/views/ledger-views.test.ts --runInBandnpm run buildnpm run lint:changedReal OpenChrome verification after merge
https://the-internet.herokuapp.com/login.oc_task_run_needs_helpwith reasonManual login required before continuing, a current cursor, and a resume hint.NEEDS_HELP, the exact reason, cursor URL, resume hint, and evidence refs.oc_handoff_startfor the same run with a safe before snapshot.ACTIVE.Out of scope