Skip to content

feat(status): collapse session status to five glanceable states (#332)#349

Open
harshitsinghbhandari wants to merge 4 commits into
mainfrom
feat/session-status-five-states
Open

feat(status): collapse session status to five glanceable states (#332)#349
harshitsinghbhandari wants to merge 4 commits into
mainfrom
feat/session-status-five-states

Conversation

@harshitsinghbhandari

Copy link
Copy Markdown
Collaborator

Implements the resolved design in #332: the dashboard's job is to let you scan a wall of agents and tell at a glance which need you. Today one raw signal expands into 13 SessionStatus values run through three independent frontend groupings, most collapsing to the same move, so they add states to scan without changing the decision. This derives the states from the distinct moves instead.

The five states

State Means Your move
Working the agent is actively running leave it alone
Needs input the agent is blocked on you respond
Ready a clean PR waits on you (mergeable / approved / review-required) merge it / go review it
Stalled the agent will not finish on its own (hung, never booted, or stopped with unfinished work) get it moving
Idle nothing happening, or finished (incl. merged / terminated) nothing

What changed

Backend (single source of truth):

  • service/session/status.goderiveStatus now reads activity.state + raw PR buckets (clean / unfinished / neutral) directly, never a pre-collapsed status, so active-vs-PR precedence resolves the right way. First-match-wins ordering: terminated → needs_input → stalled → working → ready → idle.
  • New stalled() detector: never-booted (boot grace), hung mid-run (hangTimeout), and stopped-with-unfinished-work. noSignalGracebootGrace, bumped 90s → 120s. hangTimeout is the one open number (TBD per the spec); set to 10m as a false-positive-averse starting point.
  • Blocked stacked children keep their problem signals (unfinished) but have their readiness suppressed.
  • domain/status.go — vocabulary reduced from 13 to 5. Dropped the worst-wins severity ladder, aggregatePRStatus, prPipelineStatus, statusSeverity, anyMerged.

Frontend (render verbatim):

  • Deleted workerDisplayStatus, attentionZone, sessionNeedsAttention, workerStatusPulses, and their types/tables.
  • Added one shared STATUS_META (+statusOrder) that the pill (topbar + inspector), board columns, sidebar dot, and card badge all read, so the surfaces can no longer disagree.
  • Liveness (sessionIsActive, done-bar archive, restore, orchestrator liveness) now keys off a carried isTerminated fact rather than the removed merged/terminated statuses. The wire already carries isTerminated; no API/openapi enum change needed (status is a plain string).
  • Board is now 5 columns (Needs input · Stalled · Ready · Working · Idle); terminated sessions archive in the existing done bar.

Net LOC

-237 LOC (346 insertions, 583 deletions), excluding the regenerated routeTree.gen.ts and lockfiles. Net negative as required.

Verification

  • Backend: go build ./... clean; go test -race ./... green except two pre-existing internal/adapters/runtime/zellij tests (TestRuntimeIntegrationUsesExactSessionParsing, TestAttachCommandDisablesPaneFrames) that are specific to the local zellij 0.44.3 binary and untouched by this change.
  • Frontend: tsc --noEmit clean; vitest run 131 passed.

Closes #332.

harshitsinghbhandari and others added 3 commits June 20, 2026 23:28
Implements ReverbCode#332. Replaces the 13-value SessionStatus expanded
through three frontend groupings with five states derived once, server-side:
Working, Needs input, Ready, Stalled, Idle. Each maps to one distinct human
move; finer PR detail stays in the inspector.

Backend: deriveStatus now reads activity.state plus raw PR buckets (clean /
unfinished / neutral) directly rather than a pre-collapsed status, so
active-wins resolves correctly. Adds a hang detector (HANG_TIMEOUT) and a
stopped-with-unfinished-work case, renames noSignalGrace -> bootGrace
(90s -> 120s), and drops the worst-wins severity ladder.

Frontend: deletes workerDisplayStatus, attentionZone, sessionNeedsAttention,
and workerStatusPulses; every surface (pill, board columns, sidebar dot, card
badge) now reads one shared STATUS_META. Liveness moves onto a carried
isTerminated fact instead of the merged/terminated statuses.

Net -237 LOC (346 insertions, 583 deletions, excluding generated + lockfiles).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Replace em dashes in authored comments/strings with plain punctuation
  (activityDetail copy + workspace.ts doc comments) per the repo style rule.
- Sweep stale "no_signal" prose in service.go, lifecycle/manager.go,
  daemon/lifecycle_wiring.go, and activitydispatch/dispatch.go to the new
  "never-booted / broken-pipeline stall" vocabulary; the signalCapable
  plumbing they describe is unchanged.
- Note the intentional degenerate-stack (cyclic all-clean) -> Idle behavior
  in prMoves, replacing the old force-aggregate "never go dark" fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@harshitsinghbhandari

Copy link
Copy Markdown
Collaborator Author

Thanks for the thorough review. No blocking issues; addressed the actionable nits in 728c8dd:

  • Em dashes (hard repo style rule): fixed the activityDetail "Stalled" copy and the doc comments I authored in workspace.ts. Left the pre-existing PRCard "—" empty-value placeholders and the timeline separator alone, since they predate this PR and aren't prose I produced.
  • Stale no_signal prose (nit chore: scaffold backend/ and frontend/ skeletons for rewrite #1): swept the six comments in service.go, lifecycle/manager.go, daemon/lifecycle_wiring.go, and activitydispatch/dispatch.go to the new "never-booted / broken-pipeline stall" vocabulary. The signalCapable plumbing they describe is unchanged. (Left session_store.go's "zero = no signal received yet" as-is; it describes the zero-time convention, not the removed status name.)
  • Degenerate-stack fallback (behavior feat(backend): Lifecycle Manager + Session Manager lane #2): added a comment in prMoves making the cyclic-all-clean → Idle behavior an explicit, conscious choice. Not adding a guard: it requires a cycle of mergeable PRs each targeting the next's branch with no root, which can't arise from a real branch topology, and the old force-aggregate was defensive code for a case that can't occur.

Conscious sign-offs on the non-blocking behavior notes:

Backend go build + touched-package go test -race green; frontend tsc + vitest (131) green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant