Skip to content

fix: unify session state and lifecycle presentation#2980

Draft
whoisasx wants to merge 13 commits into
mainfrom
codex/issue-2950-session-state
Draft

fix: unify session state and lifecycle presentation#2980
whoisasx wants to merge 13 commits into
mainfrom
codex/issue-2950-session-state

Conversation

@whoisasx

Copy link
Copy Markdown
Collaborator

Summary

This PR makes AO's session state model consistent from backend facts through every frontend surface.

  • Active agent work now wins over non-terminal SCM state, so a session immediately returns to Working when its agent resumes.
  • Agent-process exit is no longer conflated with AO/tmux termination.
  • Codex gets reliable process-exit reporting plus runtime/reaper reconciliation while preserving the interactive shell after Codex exits.
  • Board placement, card badges, topbar activity, and sidebar dots now consume the correct canonical source.
  • Idle/Working, Ready/Merged, Archive, semantic status colors, merge termination preferences/actions, and PR activity timing are consolidated into one implementation.

Closes #2950

Covers the narrower reproduction in #2944.

Supersedes the overlapping implementations in #2924, #2840, and #2841.

Root cause

AO already persisted the necessary facts independently:

activity_state   agent runtime activity
is_terminated    AO/runtime lifecycle fact
PR facts         SCM lifecycle, CI, review, comments, mergeability
status           derived display state

The inconsistencies came from two places:

  1. Backend status derivation evaluated PR state before active agent work and converted an exited activity signal into termination.
  2. Frontend components independently interpreted status/activity, so the board, sidebar dot, topbar pill, and cards could disagree even when they received the same session data.

Codex added a third gap: its hooks expose turn boundaries but no reliable session-end event, so the agent process could exit while tmux stayed alive and AO continued showing the last activity state.

Final state precedence

Priority Durable/runtime facts Derived display state Board placement
1 Terminated + any PR merged merged Archive
2 Terminated + no merged PR terminated Archive
3 Live + agent active working Working
4 Live + agent exited exited Needs you
5 Live + waiting/blocked needs_input Needs you
6 Live + idle + actionable PR problem Existing PR status Needs you
7 Live + idle + open/draft/reviewing PR Existing PR status In review
8 Live + idle + approved/mergeable PR Existing PR status Ready to merge
9 Live + idle + merged PR merged Merged
10 Live + idle + no PR idle Idle

status remains derived at service read time and is never persisted. Existing stacked/multi-PR worst-wins aggregation and no_signal behavior remain intact.

Backend changes

Lifecycle and state derivation

  • Added exited as a real derived session status.
  • Reordered derivation so active work wins over every non-terminal SCM/input state.
  • Preserved activity=exited, isTerminated=false for an exited agent inside a still-live terminal runtime.
  • Kept explicit/runtime termination as the terminal lifecycle boundary.
  • Clear stale tool-flight/permission state when an agent exits.
  • Preserve write safety: session guard refuses delivery/nudges after agent exit because the pane may contain a shell.
  • Keep failed or unknown runtime probes inconclusive rather than treating them as death.

Codex process-exit detection

  • Added an internal AO process supervisor for adapters without a reliable exit hook; Codex opts into it.
  • Persisted a per-launch ID and require it on process-exit activity events to fence stale observations.
  • Added tmux and ConPTY supervised-process inspection as the reaper fallback.
  • Added startup reconciliation for exits that occurred while the daemon was stopped.
  • Preserved the existing keep-alive shell after the supervised Codex command returns, so the terminal remains interactive.

Merged-session policy

  • Added persisted terminateOnPrMerge session policy and API endpoints.
  • Route opted-in merge completion through Session Manager's normal kill path so teardown finishes before terminal facts are written.
  • Keep merged sessions live when the preference is disabled.
  • Expose manual termination for merged live sessions.

PR activity timing

  • Persist and expose stateChangedAt for normalized PR lifecycle state.
  • Carry timestamps through storage, session summaries, OpenAPI, and generated frontend schema.

Integration with current main

  • Merged fix(lifecycle): durably deliver worker-idle completions to the orchestrator #2836's durable worker-idle outbox and active-turn steering work.
  • Preserved both the worker-idle recovery sweep and this PR's runtime/workload reconciliation.
  • Start SCM observation only after the completion terminator is wired.
  • Renumbered this branch's migrations after main's new 0025:
    • 0026_pr_state_changed_at.sql
    • 0027_add_session_terminate_on_pr_merge.sql
    • 0028_add_session_runtime_launch_id.sql

Frontend changes

One presentation model

  • Centralized activity, status, attention-zone, card, dot, and timeline presentation in session-presentation.ts.
  • Board/card surfaces use backend-derived session.status.
  • Topbar and sidebar use raw session.activity through the same helper.
  • Unknown data remains a visible, non-animated frontend fallback and emits telemetry.
  • Merged-but-live sessions remain active/navigable until their runtime is actually terminated.

Board and Archive

  • Replaced the old hidden idle stack with a split Idle / Working lane.
  • Idle is the primary upper region; Working appears below only while active sessions exist.
  • Added a split Ready to merge / Merged lane with distinct ready and completed colors.
  • Kept live merged sessions in the board and moved only terminated sessions into Archive.
  • Added persistent Archive grid/row controls, defaulting to grid.
  • Improved restore/terminate actions and compacted archive spacing.

Session lifecycle controls

  • Added the terminate-on-merge preference to the Summary inspector.
  • Added manual terminate actions in Summary and merged board cards.
  • Consolidated API mutation, telemetry, invalidation, and errors into one termination hook.
  • Keep merged live sessions in the sidebar until explicit or policy-driven termination.

Activity, colors, and workspace UX

  • Added role-specific dark/light status tokens for Working, Needs you, In review, Ready, Merged, Idle, Exited, Terminated, and Unknown.
  • Only active work pulses; idle, attention, exited, terminated, and unknown indicators remain static.
  • Show PR draft/open/merged transition times and links in the Summary activity timeline.
  • Open session pages with Summary alongside the terminal by default.
  • Added collapsed-sidebar hover preview, native titlebar alignment transitions, and tighter dashboard/board/sidebar/Summary spacing.
  • Reduced compact PR-state label sizing and cleaned session-card separators.

Cleanup

  • Removed an unused exit-detection enum value and redundant merged-card style override.
  • Reused the shared termination hook in the topbar instead of maintaining a second mutation path.
  • Hardened kill errors when a transport failure has no HTTP response.
  • Removed the stale assumption that every merged session is inactive.

Related PRs

Those PRs can be closed after this consolidated PR is accepted to avoid reviewing and merging overlapping implementations independently.

Validation

  • cd backend && go test -p 1 ./... - all backend packages passed.
  • cd frontend && npm run typecheck - passed.
  • cd frontend && npm test -- --run --maxWorkers=1 - 73 files, 886 tests passed.
  • Focused post-merge lifecycle, daemon, Codex, tmux, ConPTY, SQLite, state-presentation, topbar, sidebar, board, inspector, and command-palette suites passed.
  • git diff --check - passed.

Screenshots

Visual evidence will be attached in a follow-up PR comment.

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.

Unify session-state precedence, preserve agent exit vs tmux termination, and standardize status colors

1 participant