---
name: psyche-ephemeral-redesign
description: "Psyche invocation redesign (2026-07-04): resident wrapper → ephemeral run-to-completion shim. REBUILT against doyle's W1/W2 contract FREEZE (single role, event-on-stdin, {psyche_context}-presence discriminator, exit-95 reseed); committed on branch feat/f030-psyche-ephemeral, gates green, publish PARKS on the F-030 core sha. v0.14.0."
metadata: 
  node_type: memory
  type: project
  originSessionId: fd0a5f34-2e68-404b-b6f6-e440c02f0a22
---

Work order (doyle brief 9e7583cc, 2026-07-04, operator design review; full text saved in session scratchpad `doyle-psyche-redesign-brief.md`): resident `claude-spt psyche` wrapper is legacy heritage — replace with ephemeral shim; daemon drives every turn (psyche_init first, NEW `[session.psyche_resume]` per event, {psyche_prompt} carries the event). Chain: perri plans → doyle gates → perri builds+publishes on greenlight. Non-goal: zero spt-core changes (gap ⇒ STOP + classify, F-027-style).

**Plan = `PSYCHE-EPHEMERAL-PLAN.md` (repo root), submitted to doyle's gate.** Key decisions: D1 shim stays claude-spt subcommand; **D2 linchpin: uuid_inject — daemon fills {session_id} into both roles, CC headless verified accepting `--session-id <uuid>` + `-r <sid>` → init ADOPTS the sid, resume resumes it, NO capture-and-report / no [identity] post_spawn needed** (precondition = sid fill is CC-valid UUID, verified counter-43 INT-2); D3 init template argv-identical to today (skew-safe), psyche_resume new table w/ four keys + env_remove pair; D4 loud-nonzero, no silent reseed, thrash guard deleted (core C3(b) owns crashloop); D5 blank-event guard stays in shim.

**GATE VERDICT (doyle 69276f6c, 2026-07-04): CONDITIONAL PASS → edits applied, awaiting PASS stamp.** Q1: NO core drives per-event psyche_resume — brief premise-3 overstated; re-entry machinery (resume_psyche/run_psyche_turn/psyche_turn_and_relay) built+unit-tested core-side with ZERO production callers; resident wrapper IS today's driver → **F-030 core wave (post-v0.24.0) wires the driver; floor = that release; v0.14.0 trails; JOINT wave, this plan = adapter half.** Q1b NEW: today's {session_id} fill = PARENT session's sid (would collide) → F-030 mints psyche-OWN sid persisted on perch record, fills both roles; reseed = fresh sid + psyche_init. Q2: perch core-owned already (spawn_and_bind binds nested perch every spawn); runner's minting = redundant legacy; **shim must NOT mint**. Q3: D4 confirmed; reseed trigger = F-030 detail. Q4a: feature gap not docs drift (docs land w/ F-030); Q4b: real published-docs bug, folded into F-030. **Sequencing: BUILD now (core-independent), PUBLISH holds for F-030 + live-verify window.** Verdict saved: scratchpad doyle-gate-verdict-69276f6c.md.

**PASS STAMP (456b7c34) → BUILT + PUSHED 2026-07-04: plan @a36e8a5, build @df402b3.** Shim shipped (single-spawn planner, --resume mode, blank-guard, PSYCHE_TURN_FAIL loud-fail, no spt/perch touch — unit-asserted); [session.psyche_resume] declared (env_remove mirrored, KH §2.5 both tables); REQ-PSYCHE-EPHEMERAL-SHIM green (impl+unit+int tag); KH §2.6 resolved-by-design (one_spawn_then_exit successor unit anchors thrash REQ); live-relay int residency assert INVERTED to ephemeral (nested-perch + no-lingering-shim, command-line matched — old image-name match was ambiguous vs translate). Gates all green. **PARKED: (1) doyle's F-030 sha → floor pin + v0.14.0 cut+publish, (2) live-verify window → commune→ingest→resume→synthesis e2e + RC confirmatory re-runs (SEAM-2 ask-2 iso leg-labeled, wake-survival e2e).** Manifest version/floor deliberately untouched until publish.

Hazard dispositions: KH §2.6/REQ-HAZARD-PSYCHE-HOST-THRASH → resolved-by-design + successor invariant unit (one-spawn-then-exit); §2.5 env_remove mirrored to both tables; §2.2 unchanged. New REQ-PSYCHE-EPHEMERAL-SHIM before build. Rollout: **v0.14.0**, migration note = bounce live agents once (old wrappers run from .live-bin until session end). Payoff: ephemeral shim ⇒ updates take effect next invocation ⇒ dissolves .live-bin copy-dance rationale (core follow-on). E2E (commune→ingest→resume→synthesis through shim) rides the next live-verify window with the RC re-runs. Related: [[checkpoint-wake-broken-postclear-forcenative]], [[f027-trust-seam-groundtruth]], [[live-perch-monitor-reachability]].

**CONTRACT FREEZE + REBUILD (2026-07-04, doyle W1/W2 psyche-turn contract 944a74b0 + evidence answers).** The freeze CONFIRMED D2 (psyche-OWN minted sid) but INVERTED two parked pillars and COLLAPSED the two-path design into ONE spawned role — so df402b3 was rebuilt fresh on branch `feat/f030-psyche-ephemeral` (@fb94917 shim/manifest/reqs/test; @f272343 KH honesty pass). Final contract deltas vs df402b3: (1) event moves argv `--prompt`→**STDIN** ({psyche_prompt} retired); (2) `--resume` flag → **{psyche_context}-presence discriminator** (non-empty=fresh adopt `--session-id`; empty=continue `--resume`; {psyche_context} is a SOFT key — core fills empty on continue [W3 amendment], guarantees first-turn non-empty via a seed marker, so non-empty⟺fresh by construction); (3) prompt→claude via **claude's OWN stdin** (win32 ~32k cap; core flagged {psyche_context_file} as a W4 candidate — hold for explicit freeze if it lands); (4) claude stdout **CAPTURED + passed through** as sole outbound + non-empty ack (core backstops EmptyOutput); (5) **detach=false** captured (was detach=true Stdio::null); (6) **psyche_init NEVER spawned** — kept as live-capability GATE only (noop-safe); (7) reseed = reserved **EXIT 95** sole discriminator (counter-proposed vs stderr marker, doyle ACCEPTED + todlando pre-authorized; core matches TurnError::Failed{status_code:Some(95)}), every other nonzero keeps custody; session-gone signature = claude "No conversation found with session ID" (confirmed on-box). Q1(B): prompt = mind + blank line + event, no de-dup (channels disjoint by source). manifest-schema ACCEPTS psyche_resume+detach=false (no schema finding). all gates green. Manifest carries a LOUD PUBLISH-BLOCKER (floor must bump to the F-030/W-wave core sha before tag; still 0.23.0 only because unpublished).

**W4 AMENDMENT — {psyche_context_file} (doyle 9e6863d0, committed @fd41985).** The mind moved from an argv VALUE to a FILE path: `{psyche_context}` REMOVED, `{psyche_context_file}` = an always-filled path to a core-written mind file the shim READS (kills the win32 ~32k cmdline cap — ~20KB minds). Shim delta = a read-file prefix; is_first_turn/compose_prompt/turn_cmd operate byte-for-byte on the file CONTENT. **BINDING INVARIANT (doyle-pinned, core unit-asserted): ContinueExisting writes the file TRULY EMPTY, NO sentinel ever; non-empty body is EXCLUSIVELY FreshWithPreload.** File-read failure = generic keep-custody fault before any spawn, never exit 95. New REQ-PSYCHE-CONTEXT-FILE-INDIRECTION (impl+unit: real-temp-file read drives discriminator, missing-file=Err). {home}/{subnet} collapsed to a single {subnet} (ignored on argv). The W4 file supersedes the W3 soft-key on the adapter side — no {psyche_context} value remains.

**CORE SHAs / FLOOR TIMING (doyle):** W3 sha = 4ae0b92 (soft-key + code-95 flip LANDED, gated 1542/1542) — reference only, my branch is past it on W4. W4 (context-file + subnet) building core-side next; W5 (transition-orphan) + W6 follow. **W5 STRANDED-SHAPE (todlando adffae28, my analysis sent): on a dirty upgrade a <=v0.24.0 RESIDENT wrapper is left running. Its fate is deterministic on ONE thing — what POST-W3 `spt ready <parent>-psyche --once` (the wrapper's sole per-cycle blocking poll, poll_cmd) returns: NON-SUCCESS (error/READY_FAIL/perch-gone) → self-exits terminal (exit 0/4) = option (i) DOCUMENT; SUCCESS-but-unpulsed (perch bound, blocks forever / slow-timeout re-poll) → HANGS/slow-loops FOREVER, never exits (ThrashGuard needs <2s completed cycles; a blocking poll is neither) = option (ii) legacy-resident SWEEP. My earlier "exit-4 terminal" relay was ONLY the READY_FAIL branch — NOT the general shape; the dangerous branch is the invisible HANG (no exit for psyche_host_error, no CPU — KH §2.6 class one level up). LEAN option (ii) unless core guarantees the psyche-perch poll returns non-success post-W3. **RULED option (ii) by doyle (my branch map dispositive) — todlando confirmed branch (B): post-W3 `spt ready <parent>-psyche --once` REGISTERS cleanly (F-030 W4 @535489a nested_parent_home resolution succeeds without --subnet — the W4 irony: it UPGRADED the legacy wrapper from loud-refuse/exit-4 to register-then-block-FOREVER) → HANG. W5 builds a CORE-side legacy-resident sweep (one-shot brain-start, positive-match-only, residue-clearing). NO adapter code. My cmdline discriminator for the sweep guard: kill `claude-spt psyche … --prompt` (legacy resident) NOT `--psyche-context-file` (current v0.14.0 shim) — unambiguous across the version boundary. v0.14.0 release-notes "bounce once" note becomes OPTIONAL (sweep auto-reaps).**

**F-030 CORE DONE @3ab635a → v0.25.0 cut (counter 45) → RC-GREEN (2026-07-04).** RC acceptance leg run against the STAGED v0.25.0 (sha256 0202742115…491 == published byte) in an SPT_HOME-ISOLATED v0.25.0 daemon (box 0.24.0 pid 349468 untouched). ALL LEGS GREEN. **BOUNDARY LESSON (founding constraint; doyle c6641414): I first built the RC peeking core internals (psyche-custody.json path, PSYCHE_RESEED brain-stderr grep, custody pre-seed) = VIOLATION. "internals handed = surface it, don't found on it" (= gap-is-a-finding). Corrected + doyle-approved method: (c) exit-95 = ISOLATION real claude `--resume <nonexistent>`→shim exits 95; (a)/(b)/(d) = ISOLATION with a fake `claude.EXE` (Rust Command::new won't run a .bat) on PATH logging my shim's OWN invocation argv (non-empty ctx→--session-id, 0-byte→--resume); live JOINT = v0.25.0 daemon drives my shim fresh+continue, perch NO psyche-host:FAILED on public `endpoint list`. Partition: core-writes-file=doyle-e2e × shim-discriminates=fake-claude-isolation × compose=live-perch-healthy.** RIG RECIPE: rc-rigbin/spt.exe=staged (POSIX PATH form or git-bash ignores it!); rc-adapter/manifest.toml with [update] STRIPPED (else gh-release-pending→ADAPTER_SKIP, daemon won't host); seed needs --session-id; echo-gate = `spt api echo-gate set <id> --token <BOUND-token>` (auth; seed→listen rig has empty info.json.session_id); the ephemeral shim is SUB-SECOND — process-polling/WMI can't catch it, fake-claude isolation is the instrument. **NEXT: doyle final gate → deployah flips v0.25.0 → I pin min_spt_core=published-v0.25.0-sha, two-platform pack, publish claude-spt v0.14.0, live-verify.** **The REAL floor sha for the PUBLISH-BLOCKER comes at RELEASE CUT (post-W6) — do NOT pin the floor before then.** Branch feat/f030-psyche-ephemeral: fb94917 shim + f272343 KH + fd41985 W4. **STILL PARKS: release-cut floor sha → floor pin + two-platform pack + v0.14.0 publish + live-verify e2e.** Aligned through W4, reachable for W5/W6.
