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

Decision

prism_start, prism_checkpoint, and prism_wrap MUST NOT carry pending signals in their response payloads. These three “session lifecycle” verbs are exempt from the SPEC-037 per-verb piggyback drain. Pending signals are delivered via either (a) the explicit prism_signals_pending poll, or (b) implicit piggyback on any non-lifecycle bootstrapped verb call. This rule is captured as a one-line principle in PRISM.md and elaborated in SPEC-041. Implementation modifies mcp/server.py:1649-1661 to remove the explicit drain from prism_start, and updates the require_bootstrap decorator at mcp/server.py:237-265 to skip _piggyback_drain for all three lifecycle verbs.

Rationale

Three concerns motivate the separation: 1. Single responsibility. A session-lifecycle verb has one job — manage session state (begin / persist / end). Mixing in messaging delivery couples two unrelated concerns; every future change to either has to reason about both. The verb contract grows opaque to vibe-coding callers, who then write code expecting messaging guarantees from verbs that should have made only session guarantees. 2. Race correctness on prism_start specifically. Identity-targeted signals queued before a new session existed are stale by definition. They may have been intended for the prior (now-wrapped) session of the same identity, not the fresh one. Delivering them on prism_start silently re-routes them. Forcing the agent to call any other verb first (or prism_signals_pending explicitly) creates an opportunity for stale-signal hygiene to apply. (This concern does not apply to wrap or checkpoint — those run after registration. Symmetry of the principle alone justifies exempting them.) 3. Composition with hd_exec. SPEC-040 introduces hd_exec as a non-start session verb that DOES drain via piggyback. The asymmetry is intentional and load-bearing: a pickup is a continuation (inherits unread messages); a fresh start is a beginning (does not). The distinction collapses if both verbs multiplex messaging.

Alternatives Considered

Drain on all bootstrapped verbs uniformly (status quo before this ADR). Rejected — couples session lifecycle to messaging, creates the prism_start race condition, drives opaque contract growth. Practical hazard: caller code grew to expect signals on bootstrap, which would make future protocol changes (push-based delivery, lease-based ownership) breaking changes. Drain only on prism_start (narrow fix). Rejected — leaves the asymmetry where two of three lifecycle verbs (checkpoint, wrap) still multiplex while one (start) doesn’t. Hard to defend the line. Frank flagged “session lifecycle is session lifecycle” — any cherry-picking erodes the principle. Drain on transitions (start, wrap) but not steady-state (checkpoint). Rejected — arbitrary split. Checkpoint is a lifecycle verb regardless of whether it’s a transition or a save-point. TTL on queued signals to prevent indefinite accumulation. Deferred, not rejected. Current scope is correctness (no signal loss). Hygiene is a separate concern; if queue growth becomes problematic, a future spec adds per-type TTL.
Last modified on April 27, 2026