---
name: adapter-glue-model-boundary
description: "Adapters (harness AND shell) interface with spt-core via manifest + binary ONLY — never add adapter-aware source to spt-core, never place real adapters in-tree"
metadata: 
  node_type: memory
  type: feedback
  originSessionId: 82459163-f234-40c3-8afa-90e074af5065
---

User stopped D8 work (2026-06-04) when I (a) placed the real notify shell adapter in-tree and (b) hardcoded its "notify" capability name in the daemon. Both reverted same day: adapter moved to standalone SaberMage/spt-shell-notify (public, self-contained, hand-decoded wire format); daemon-side render became the [session.notif] manifest template seam generalized to shell adapters (vocabulary-agnostic).

**Why:** The adapter model's entire point is zero source integration — the manifest declares everything, the binary speaks only the public `spt api` surface + documented wire format. The mock stays the only adapter-shaped artifact in-tree (PRD R-DOCS-2). spt-core knowing a shell vocabulary verb = a privileged capability baked into core = the model broken.

**How to apply:** Before any adapter-touching work: real adapters get their own repo (spt-core CI may add a thin cross-repo hook for E2E); any daemon-side behavior keyed to an adapter must be driven by a manifest declaration spt-core honors generically, never by a name/verb spt-core knows. When a plan says "via the template/manifest", build the manifest seam — don't drift to whatever machinery is freshest in context.

**THE OTHER DIRECTION — spt-core → adapter (doyle drift, v0.25.0 RC 2026-07-05, operator-corrected):** the boundary cuts BOTH ways. Building/testing an adapter must consume ONLY published docs + binary + public `spt api` verbs + perch state — NEVER spt-core SOURCE (file:line, internal file paths like the raw psyche-custody.json location, internal stderr markers). I drifted hard: orchestrating perri's RC I fed her lifecycle.rs/perch.rs line numbers, the internal custody-json path, and a `PSYCHE_RESEED` brain-STDERR grep. **CONTEXT.md:243 is the binding principle: "a harness reads PERCH STATE, never brain stderr"** — the psyche_host_error info.json field (rendered by `spt endpoint list`/`whoami`) is THE public psyche-health signal; brain stderr is invisible-to-harness by design. TRIAGE TREE for "adapter has an impl/test problem" (operator standing): (1) not reading published docs closely → point at the doc SECTION; (2) genuine docs gap → amend docs; (3) worst: spt-core missing a feature for legacy parity → amend core. ALWAYS check against CONTEXT.md (design source of truth). In this case the docs were COMPLETE (MANIFEST.md:114-126 + docs-site manifest.md:45-52 fully specify the psyche-turn contract incl. the boundary-clean reseed observable: exit-95 → next turn's {psyche_context_file} goes non-empty) — verdict was (my orchestration error), NOT a gap/missing-feature. Correct adapter-RC pattern: observe at the SHIM boundary (the adapter's OWN argv — e.g. the {psyche_context_file} path core HANDS it via the key, not core's layout) + public verbs + perch state; adapter-obligation legs (exit-95) = adapter UNIT test in isolation; core's REACTION = core's own e2e, not the adapter's to re-verify. POST-RELEASE DOC-CLARITY SEED: MANIFEST.md:126 names "PSYCHE_RESEED:<id> marker" without stating it's operator-LOG-only (not adapter-consumable) — could tempt a reader (it tempted me) to scrape stderr; add one sentence per CONTEXT.md:243. Non-blocking.
