Skip to content

feat(dashboard): surface session-ended-before-Stop and Stop-hook-timed-out in Activity#250

Merged
thejustinwalsh merged 2 commits into
mainfrom
feat/run-history-reasons
Jun 23, 2026
Merged

feat(dashboard): surface session-ended-before-Stop and Stop-hook-timed-out in Activity#250
thejustinwalsh merged 2 commits into
mainfrom
feat/run-history-reasons

Conversation

@thejustinwalsh

@thejustinwalsh thejustinwalsh commented Jun 23, 2026

Copy link
Copy Markdown
Owner

User description

Summary

Closes #242

Surface the two specific abnormal-termination reasons (session-ended-before-Stop, Stop-hook-timed-out) in the Activity view's run-history rows with human-readable labels and tooltips. Follow-up from PR #233, which explicitly punted this UI work as a follow-up (see PR #233 "Punted as follow-up" section).

Status

  • Phase 1 (all): migration + dispatcher write + wire type + UI render + tests

What changed

  • packages/dispatcher/src/db/migrations/012_workflows_end_reason.sql — new end_reason TEXT column on workflows
  • packages/dispatcher/src/workflow-record.tsendReason?: string in WorkflowPatch + PATCH_COLUMNS
  • packages/dispatcher/src/workflows/recommender.ts — writes "session-ended-before-Stop" / "Stop-hook-timed-out" via updateWorkflow before throwing
  • packages/dispatcher/src/workflows/implementation.ts — same, threaded via optional workflowId arg through awaitNextStop / resolveBareStop / enforceVerifyOnDone
  • packages/dashboard/src/wire.tsendReason: string | null on RunSummary
  • packages/dashboard/src/db-deps.tslistRuns() projects end_reasonendReason
  • packages/dashboard/src/app/components/Activity.tsxRunRow renders a neutral Badge + title tooltip; null → no chip; unknown tokens show the raw value
  • packages/dashboard/test/activity.test.tsx — 4 new SSR unit tests
  • packages/dashboard/test/runs-deps.test.ts — 1 new DB-level projection test

Why these changes

The state column only conveys compensated/failed — it doesn't tell the operator why the run failed. The two reasons surfaced here (session-ended-before-Stop and Stop-hook-timed-out) are the most actionable: one indicates a crashed/killed tmux session before the Stop hook fired; the other indicates the Stop hook exceeded its configured timeout. Both were already thrown as error messages but never persisted in the DB. Adding a dedicated end_reason column (not meta_json — the docs call that "scratch") lets the dashboard project it cleanly.

The reason is written before throwing so the saga's compensation (compensated state write) doesn't clobber it — both writes go to different columns.

Verification

bun test          # FULL repo suite
bun run typecheck
bun run lint

Full-suite output (bun test)

Migration 012 bumps the schema version 11→12, which touches full-chain latest-version assertions in db.test.ts and db-scripts.test.ts. Those are updated (partial-migration assertions through002→2 / through010→10 left intact, and the through010 fixture filter widened to copy only migrations ≤ 010 so the v10 "pre-index world" holds). The full suite is green:

 1489 pass
 0 fail
 3765 expect() calls
Ran 1489 tests across 137 files. [100.71s]

bun run typecheck → clean (tsc --noEmit, no output).
bun run lint → clean (oxlint --fix --deny-warnings, no output).

Targeted suites (subset of the above)

bun test packages/dashboard               # 128 pass, 0 fail
bun test packages/dashboard/test/activity.test.tsx   # 9 pass (4 new end-reason tests)
bun test packages/dashboard/test/runs-deps.test.ts   # 5 pass (1 new projection test)
bun test packages/dispatcher/test/db.test.ts         # schema-version chain → 12

Acceptance criteria

Stumbling points

  • The reason strings in the issue title use hyphens (session-ended-before-Stop) while the existing error message strings in the code use spaces ("session ended before Stop hook"). Confirmed the hyphen-separated tokens are the correct canonical form for the DB column — they match the issue title and are machine-readable.
  • The DB doesn't have a dedicated end_reason column (the issue says reasons "appear in the raw run data" — they actually only existed as thrown error messages). Added a migration.
  • No Radix Tooltip component is vendored in app/components/ui/. Used title attribute on the Badge — works in SSR, accessible, correct for this use case.

Suggested CLAUDE.md updates

None — the tooltip convention (use title when no Radix Tooltip is vendored) and the meta_json vs. dedicated-column distinction are already derivable from reading the existing code. The dashboard CLAUDE.md already tells agents to check what's vendored.

Decisions

See planning/issues/242/decisions.md for the full rationale on: reason token format, title vs. Radix Tooltip, end_reason as a column vs. meta_json, and the workflowId optional-arg pattern.


PR Type

Bug fix, Enhancement


Description

  • Add end_reason column to workflows (migration 012) and write reason tokens before throwing in recommender and implementation workflows

  • Race the recommender's Stop wait against session liveness via awaitStopOrSessionEnd, so dead sessions fail fast instead of stalling

  • Surface session-ended-before-Stop and Stop-hook-timed-out as human-readable badges with tooltips in the Activity dashboard

  • Bump schema version to 12 and update assertions across dispatcher and CLI tests

  • Document design decisions and implementation plan for the end‑reason feature


Diagram Walkthrough

flowchart LR
  A["Recommender / Implementation workflow"] -- "session ends or Stop times out" --> B["Write end_reason token"]
  B --> C["DB column end_reason"]
  C --> D["listRuns() projection"]
  D --> E["RunSummary.endReason"]
  E --> F["Activity.tsx renders Badge + tooltip"]
  A -- "Race liveness with `awaitStopOrSessionEnd`" --> G["Detect dead tmux session quickly"]
  G --> B
Loading

File Walkthrough

Relevant files
Configuration changes
3 files
012_workflows_end_reason.sql
Add `end_reason` column to workflows table                             
+7/-0     
db.test.ts
Update schema version assertions to 12                                     
+12/-9   
db-scripts.test.ts
Bump expected schema version in db-scripts test                   
+1/-1     
Bug fix
2 files
recommender.ts
Race session liveness and write end_reason before throw   
+2/-0     
implementation.ts
Thread `workflowId` to write end_reason on abnormal Stop 
+18/-2   
Enhancement
4 files
workflow-record.ts
Add `endReason` to WorkflowPatch and PATCH_COLUMNS             
+8/-0     
Activity.tsx
Render end‑reason badges with tooltips in run rows             
+36/-0   
db-deps.ts
Project `end_reason` from DB into RunSummary                         
+3/-1     
wire.ts
Add `endReason` field to RunSummary wire type                       
+9/-0     
Tests
2 files
activity.test.tsx
Add tests for known, null, and unknown end‑reason rendering
+40/-0   
runs-deps.test.ts
Test endReason projection for normal and abnormal runs     
+30/-2   
Documentation
2 files
decisions.md
Document design decisions for end‑reason tokens and rendering
+59/-0   
plan.md
Add implementation plan for issue 242                                       
+44/-0   

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@thejustinwalsh, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 50 minutes and 9 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses rolling per-developer review limits. Reviews become available again as older review attempts age out of the rolling limit window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c9bf07f7-f72e-460a-9e92-60d05594f021

📥 Commits

Reviewing files that changed from the base of the PR and between 22bcb78 and 670da7c.

📒 Files selected for processing (13)
  • packages/cli/test/db-scripts.test.ts
  • packages/dashboard/src/app/components/Activity.tsx
  • packages/dashboard/src/db-deps.ts
  • packages/dashboard/src/wire.ts
  • packages/dashboard/test/activity.test.tsx
  • packages/dashboard/test/runs-deps.test.ts
  • packages/dispatcher/src/db/migrations/012_workflows_end_reason.sql
  • packages/dispatcher/src/workflow-record.ts
  • packages/dispatcher/src/workflows/implementation.ts
  • packages/dispatcher/src/workflows/recommender.ts
  • packages/dispatcher/test/db.test.ts
  • planning/issues/242/decisions.md
  • planning/issues/242/plan.md

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@thejustinwalsh

Copy link
Copy Markdown
Owner Author

/review

@c0der3view

c0der3view Bot commented Jun 23, 2026

Copy link
Copy Markdown

PR Reviewer Guide 🔍

(Review updated until commit 670da7c)

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis ✅

242 - PR Code Verified

Compliant requirements:

  • The run history view in the dashboard recognises and renders session-ended-before-Stop with a human-readable label and a tooltip explaining what it means.
  • The run history view recognises and renders Stop-hook-timed-out with a human-readable label and a tooltip explaining what it means.
  • Both reasons are covered by a Playwright smoke or unit snapshot test so they do not regress silently.

Requires further human verification:

  • bun run typecheck and bun run lint clean; existing dashboard tests pass.
⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
🔀 No multiple PR themes
⚡ No major issues detected

@c0der3view

c0der3view Bot commented Jun 23, 2026

Copy link
Copy Markdown

Persistent review updated to latest commit 99850be

@c0der3view

c0der3view Bot commented Jun 23, 2026

Copy link
Copy Markdown

PR Code Suggestions ✨

No code suggestions found for the PR.

…d-out in Activity

Add `end_reason TEXT` to the `workflows` table (migration 012) and wire it end-to-end:

- Dispatcher writes `"session-ended-before-Stop"` / `"Stop-hook-timed-out"` via
  `updateWorkflow` just before throwing in both `recommender.ts` (inline, `ctx` in
  scope) and `implementation.ts` (`awaitNextStop` / `resolveBareStop` /
  `enforceVerifyOnDone` accept an optional `workflowId` arg so the write is
  conditional and test-safe).
- `workflow-record.ts` gains `endReason?: string` in `WorkflowPatch` + `PATCH_COLUMNS`.
- `db-deps.ts` `listRuns()` projects `end_reason` → `endReason` on `RunSummary`.
- `wire.ts` `RunSummary` adds `endReason: string | null` with TSDoc.
- `Activity.tsx` `RunRow` renders a neutral `Badge` with an HTML `title` tooltip for
  the two known reasons; unknown tokens show the raw value; null → no chip.

Tests: 4 new SSR unit tests in `activity.test.tsx` (label + tooltip presence, null
omission, unknown-reason fallback) and 1 new DB-level projection test in
`runs-deps.test.ts`. All 128 dashboard tests + all dispatcher workflow tests pass.

Closes #242
…2, bump version assertions

Migration 012 hand-inserted its own `schema_version` row, but migrations
003–011 don't — the runner records the version via `INSERT OR IGNORE` (db.ts).
Removed the redundant insert to match the convention.

Bumping the schema to 12 broke full-chain latest-version assertions in two
packages that targeted dashboard/dispatcher-workflow runs missed:
- packages/dispatcher/test/db.test.ts — 7 full-chain `toBe(11)` → `toBe(12)`
  (the partial through002 → 2 and through010 → 10 assertions are unchanged).
- packages/cli/test/db-scripts.test.ts — restore-roundtrip `toBe(11)` → `toBe(12)`.

The through010 partial-migration test filtered migrations by `!startsWith("011_")`,
which let 012 slip into the v10 fixture dir — widened it to copy only NNN ≤ 010
so the "pre-index world" stays at v10 regardless of later migrations.

Full `bun test`: 1489 pass, 0 fail. typecheck + lint clean.
@thejustinwalsh thejustinwalsh force-pushed the feat/run-history-reasons branch from 6319048 to 670da7c Compare June 23, 2026 15:56
@thejustinwalsh thejustinwalsh marked this pull request as ready for review June 23, 2026 15:58
@thejustinwalsh thejustinwalsh added the ready-for-review All phases done and verified — PR ready for final human review and merge label Jun 23, 2026
@thejustinwalsh thejustinwalsh merged commit 2efe61a into main Jun 23, 2026
2 checks passed
@thejustinwalsh thejustinwalsh deleted the feat/run-history-reasons branch June 23, 2026 15:58
@c0der3view

c0der3view Bot commented Jun 23, 2026

Copy link
Copy Markdown

Persistent review updated to latest commit 670da7c

@c0der3view

c0der3view Bot commented Jun 23, 2026

Copy link
Copy Markdown

PR Code Suggestions ✨

No code suggestions found for the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-review All phases done and verified — PR ready for final human review and merge Review effort 2/5

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(dashboard): surface session-ended-before-Stop and Stop-hook-timed-out reasons in recommender run-history UI

1 participant