Skip to main content
Status: accepted · ADR-35 · Filed 2026-04-30

Title

Two-session model: Work Session (User-level) + Agent Session (process-level)

Decision

Introduce two distinct session concepts in the schema and verb surface:
  1. Work Session — a User-level day-of-work boundary. One row per user per logical work-day. Auto-created on first agent_session of the day for a given user; ends explicitly via prism_work_session_end or rolls over at midnight. May span multiple machines (Frank on mini1 morning + mini3 afternoon = same Work Session). Used for daily roll-up analytics, retros, signal history grouping. Not a routing filter.
  2. Agent Session — a process-level binding for one agent on one machine. One row per prism_startprism_wrap cycle. Bound to a specific machine + process. Heartbeat-tracked. Backend uses for master election, current-delivery target lookup, audit trail. Not a routing filter.
agent_sessions.work_session_id FK ties each process-level session to the user’s day-of-work. The legacy controller_registrations table is renamed agent_sessions and gains the work_session_id FK plus the agent_id FK (per ADR on agent_id normalization).

Rationale

The legacy model conflated three concepts under one session_id:
  • The agent’s identity (which agent is this signal for? — now agent_id)
  • The current process binding (which mcp-node should I push to? — now agent_session_id)
  • The user’s day-of-work grouping (whose day’s-work is this? — now work_session_id)
Three pressures forced separation:
  1. Routing continuity across restarts. Solved by agent_id keying (separate ADR). Process-level session can churn freely without losing the routing target.
  2. Daily roll-up boundary. No clean way to ask “show me all signals from Frank’s day” today — agent_sessions span midnight in messy ways and don’t compose across machines. Work Session is the natural unit.
  3. Cross-machine continuity. Frank moving from mini1 to mini3 mid-day should preserve the work-day’s audit context even though every agent gets a new agent_session_id on restart. Work Session FK gives this for free.
The work_session does not affect routing — channel naming uses agent_id; subscribe authorization uses tenant_id/org_id/project_id. Work Session is a higher-level grouping for human consumption, like a calendar day vs an HTTP request. Treating them as the same thing was the bug. The verb surface is intentionally minimal in v1: prism_work_session_start (explicit override; auto-created if not called), prism_work_session_end (explicit close; auto-rolls at midnight if not called). No reach into project lifecycle, no required UX. The column exists, the FK exists, the auto-creation runs — analytics consumers can build on it later.

Alternatives Considered

  1. One session concept (today’s session_id), keep conflating. Status quo; surfaced as broken via the SPEC-054 port miss + bell+count regression. Rejected.
  2. Three explicit sessions: Day Session + Process Session + Connection Session. Adds a Connection layer between WS lifecycle and process lifetime. Considered; rejected — the WS reconnect cycle is short-lived and process-bounded, no need for its own row.
  3. Work Session as a label/tag on agent_sessions, not a separate table. Smaller schema delta but loses the ability to attach explicit started_at / ended_at / notes. Rejected; the entity is real enough to deserve its own row.

Status

accepted

References

  • SPEC-056 §Two-session model
  • Memory: feedback_heartbeat_out_of_band (related — separate channels for separate concerns)
Last modified on May 3, 2026