---
name: v0250-symmetric-event-postentersettle
description: "2026-07-18 v0.25.0 SHIPPED — type-agnostic <EVENT> stub delivery (notify/user-msg/gateway) + post-enter settle; published private+mirror, NOT yet node-applied"
metadata: 
  node_type: memory
  type: project
  originSessionId: 01a548be-ed5c-45bf-a7c7-2da0bacbd94c
  modified: 2026-07-19T03:18:50.793Z
---

**2026-07-18 — v0.25.0 SHIPPED** (0.24.1→0.25.0, floor UNCHANGED 0.27.0, no new core seam — all published-surface). Operator sprint "SYMMETRIC-EVENT" (docs/plans/SYMMETRIC-EVENT-PLAN.md). Commit 7486709 on main; tag v0.25.0; GH release live on BigscreenVR/claude-spt-bs with `adapter.spt` asset; public mirror pushed (SaberMage/claude-spt, commit 32c2c09). **No cplugs skeleton push** — binary+strings only, no plugin.json/skill/hook structural change.

**Three operator asks:**

1. **Post-enter settle (REQ-DIST-IDLE-POST-ENTER-SETTLE).** translate.rs: new `POST_ENTER_MS=50` const, inserted after every TERMINAL `enter` (before `{commit}`) in commands_for_clear / commands_for_msg_stub / commands_for_event / commands_for_boundary. Purpose: the submit registers before the InjectFloor release / buffered-controller flush → kills the "leftover message" residue class. GUARD: `commands_for_boundary` adds it only `if !v.is_empty()` — the mid-sequence rename enter (when a wake half follows) keeps its 150ms BOUNDARY_BRIDGE_MS, never double-settled; bare-commit adds none. Idle-delivery choreography is now 7 commands (was 6).

2. **Type-agnostic stub delivery (REQ-STUB-GENERAL-EVENT).** translate.rs `stub_delivery`: DROPPED the `type=="msg"` gate → notify / user-msg (gateway/local-CLI user-authority, from `*-gw`) / alarm / commune / reply / any unknown type now stub+park like a peer msg (idle agent receives them the SAME way — parked body drains via render_frames as `<sptc_messages from=…>`). Stub shape unchanged (`<msg from="…"/>` for ALL types — type distinction rides the parked body, `stub::is_msg_stub` untouched). **NON-EMPTY-body GUARD added**: `type="file_drop"` (path attr, empty body) and other attr-only envelopes stay on the full-type path so render_frames (from+body only) doesn't strand their attrs. Control envelopes (wake/rename/role/fire) + full_inject still intercepted earlier in dispatch_event. **CONSEQUENCE**: hook.rs UPS update-nudge now scans PARKED bodies + poll frames (`update_src`), since an spt-update notify can arrive via the idle route now (reverses the old rationale that kept notify full-typing so the poll-only nudge saw it). Touches [[v0180-items-3-5-and-f032]]'s REQ-UPDATE-NUDGE-VERSION-GATED.

3. **SessionStart NON-draining — verify-only, NO code change.** `handle_session_start` has no `api poll` / no `drain_msg_park`; it only marks `state idle` (bind:913, clear:950). Setting idle while spooled unreads exist IS the idle route the operator wants (daemon drains spool → translate → stub+park → next UPS). Only `api poll` sites are UPS(1279) + PreToolUse(1483); the brief path (perch_brief/psyche_download) pulls static briefs + resume context, never the spool. Confirmed correct.

**Envelope taxonomy — doyle source-grounded (spt-proto event.rs / spt-daemon notif.rs, 2026-07-18):** ONE grammar `<EVENT type="…" from="…" [attrs]>body</EVENT>`, parser reads `type` generically (N-1 safe). Types on an agent perch: msg, user-msg (user-authority, identity-gated — can't forge the tag), notify (attrs from/notif_id/subnet; notif_id=`<node_hex>:<counter>`; from e.g. spt-update), alarm (target-time/current-time), commune, echo_commune (note attr), reply, file_drop (path attr, EMPTY body). Shell channel (`api poll <shell-id> --link`) is a DISTINCT MAC'd transport — carved out, never reaches the translate binary. "gateway" is an ENDPOINT type, not a message type — its sends arrive as `type="user-msg"`. NO priority attr, NO per-message seq (seq is transport-chunk-only on `<EVENT-PART>`).

**Gates:** 321 tests green, traceable-reqs check green (2 new REQs tagged impl+unit), clippy clean (the one let-else warning is pre-existing at hook.rs:1390, not mine). **DOGFOOD on the packaged binary + spt 0.38.0**: translate-proof of a `type="notify" from="spt-update"` envelope emitted `<msg from="spt-update"/>` (stubbed, not full-typed) with `ctrl+s·50·text·70·enter·50·commit` (7 cmds, commit:yes) → TRANSLATE_PROOF_OK. All 3 triples rebuilt fresh (win msvc + linux gnu + linux musl — CAUGHT a stale musl from 2026-07-16 before packaging; rebuilt via zigbuild).

**OPEN / owed:** (a) **NOT node-applied** — left to operator (`spt adapter update claude-spt` + bounce endpoints; changelog says "takes effect on next start"). (b) FIELD VERIFY owed at a live window (int stage deferred on both REQs): a live idle notify/gateway send to a spt-hosted endpoint types only the stub and the stub turn carries the full body; and no leftover residue post-submit. Never cut on unit green alone — silent delivery loss is the failure mode.

Related: [[v0200-stub-delivery-shipped]] (ADR-0007 stub base this generalizes), [[v0180-items-3-5-and-f032]] (update-nudge origin), [[live-delivery-model-and-dupmsg]].
