# OMP adapter plan

Status: **ratified clean cutover** (2026-07-14)

`omp-spt` is the SPT harness adapter for
[Oh My Pi](https://github.com/can1357/oh-my-pi). The supported product is a
native OMP terminal session with SPT messaging and lifecycle behavior supplied
by an OMP extension. The inherited alternate-harness plugin, hook/injection
stack, RPC bridge, launcher aliases, and provider-wrapper profiles are not
compatibility surfaces. ADRs
[0008](docs/adr/0008-omp-native-product-boundary.md) through
[0013](docs/adr/0013-release-gate-stops-at-the-adapter-boundary.md) are the
ratified boundary.

## Product architecture

```text
spt-core broker PTY
        │
        └── omp-spt launch-omp
                 │ validates and execs/spawns
                 ▼
          native OMP TUI
                 │ loads
                 ▼
       packaged omp-spt extension
                 │
                 ├── bind/listen/state/session-end
                 ├── serialized delivery custody
                 └── correlated reply or explicit failure
```

The process whose interface the operator sees owns the PTY. The launch shim may
resolve and validate the real OMP executable, but it must not become a second
terminal application. Every hosted endpoint is therefore attachable as the
same native OMP TUI whether it is currently attended or unattended.
<!-- [doc->REQ-OMP-NATIVE-TUI] -->

Bare `omp` lookup is not trusted: another executable may own that basename.
Resolution must identify a genuine Oh My Pi binary, fail loudly on ambiguity or
collision, and preserve OMP's terminal ownership after launch.
<!-- [doc->REQ-OMP-EXECUTABLE-RESOLUTION] -->

## Public SPT contract

The adapter uses only the two seams in the published
[SPT integration checklist](https://sabermage.github.io/spt-releases/harness-contract/integration-checklist.html):
the declarative manifest and imperative `spt api` commands. No private
spt-core source, SDK, state schema, or transport implementation is an adapter
dependency.

### Register

- `[adapter]` identifies `omp-spt`, declares `kind = "harness"`, and advertises
  only `ReadyAgent` and `LiveAgent`.
- `host_binaries = ["omp"]` is the bind-time harness match. The adapter helper
  binary is intentionally absent so it cannot collide with or impersonate the
  hosted TUI.
- `shortcut_basename = "omp"` brands picker-generated launchers.
- The manifest validates against the published schema and then passes the
  public `spt adapter add` cross-field validation.
<!-- [doc->REQ-DIST-MANIFEST-SCHEMA] -->

### Start and resume

- `[session.self]` starts `launch-omp` with the packaged extension.
- `[session.resume]` uses the same path plus OMP's native resume argument.
- `SPT_ENDPOINT_ID` is broker-injected. Once OMP reports its actual session id,
  the extension performs the public `api bind <id> --set-session-id <sid>`
  startup half.
- The endpoint/session association is immutable while the endpoint is alive.
  In-TUI switch, branch, new-session, and resume actions are blocked. Changing
  sessions requires stopping and relaunching the endpoint with an explicit
  native-resume target.
<!-- [doc->REQ-OMP-SESSION-IMMUTABLE] -->

### Run

spt-core owns durable delivery custody. Listener events enter OMP immediately
through its native message rail, while active-only messages remain in core until
`api poll --include-deferred` surfaces them at an active model boundary. The
extension does not merge those channels or infer cross-channel chronology.
Assistant output remains local; a peer receives output only through explicit
`spt send` or shortform use.
<!-- [doc->REQ-OMP-CORE-DELIVERY] -->

Each accepted peer message opens an ordinary OMP turn with a short
`<msg from="…"/>` user-visible stub. The complete SPT `<EVENT …>…</EVENT>`
envelope is supplied as context for that same turn. Peer text remains opaque
content; a message beginning with an OMP slash command cannot become local
operator input.
<!-- [doc->REQ-OMP-MESSAGE-CONTEXT] -->

OMP lifecycle events drive honest `busy`/`idle` state. Listener, state, and poll
failures preserve local OMP work after the endpoint is established. The cyan
identity remains visible with ` · comms recovering...`; listener recovery uses
capped indefinite backoff, state recovery converges on current OMP truth, and
polling retries only at a real model boundary. Initial bind failure remains loud.
<!-- [doc->REQ-OMP-COMMS-RECOVERY] -->

### End

Normal OMP shutdown causes the extension to release the listener and call the
public session-end seam. Listener exhaustion uses the same teardown path before
closing OMP. SPT retains spool and history according to its published lifecycle
contract.

## ReadyAgent and LiveAgent

`omp-spt` supports exactly two endpoint types:

- **ReadyAgent** — native OMP plus extension-owned messaging, without a Psyche.
- **LiveAgent** — the same native endpoint with the manifest's go-live gate and
  bounded `[session.psyche_resume]` turns.

The Psyche is not a resident second endpoint. Each invocation is one bounded
headless OMP turn using the core-written context file and its own private OMP
session directory, then exits. OMP subagents are not advertised as independent
SPT Workers.
<!-- [doc->REQ-OMP-READY-LIVE] -->
<!-- [doc->REQ-PSYCHE-EPHEMERAL-SHIM] -->

## History, digest, and continuity

OMP session JSONL is authoritative:

```text
~/.omp/agent/sessions/<encoded-cwd>/<timestamp>_<session-id>.jsonl
```

- `[history] strategy = "fetcher"` locates the session by id and streams its
  OMP JSONL for the bounded summarizer.
  <!-- [doc->REQ-HISTORY-FETCHER] -->
- `[digest] strategy = "fetcher"` maps OMP records to SPT's neutral
  `{role,text,tool,ts}` NDJSON contract. Delivered user-facing messages remain
  turn-opening input records so digest cursors retain turn granularity.
  <!-- [doc->REQ-DIST-DIGEST-EXTRACTOR] -->
- `[session.echo_commune]` self-locates history and prints one bounded context
  delta on stdout; spt-core owns the file drop, ingest, and deletion.
  <!-- [doc->REQ-SESSION-ECHO-COMMUNE] -->

Commune and signoff drops live under project-local `.spt/`. The published
v0.29.0 echo-commune contract resolves a relative watched directory against the
endpoint's recorded working directory, never the daemon cwd, and warns rather
than guessing when no endpoint cwd exists. The adapter's higher
`min_spt_core_version` of **0.31.0** is required for the identity-preserving
`api listen --session-id` listener path.
<!-- [doc->REQ-OMP-CONTINUITY-DROPS] -->

## Distribution and update

The adapter id, binary, and repository are all `omp-spt` /
`BigscreenVR/omp-spt`. A multi-platform `adapter.spt` contains the shared
manifest and extension plus the platform tool binary. `[update]` uses the
published `gh_release` avenue. There is no secondary plugin reconciliation or
reload step.
<!-- [doc->REQ-DIST-ADAPTER-RELEASE] -->

## Skill delivery gap

No OMP-native command/skill distribution path has been proven for this
adapter. Consequently, no command skills are shipped or advertised. Generic
SPT operations remain available through the public `spt` CLI. Adding OMP
commands later requires an explicit OMP extension registration and packaging
contract plus focused tests; a foreign plugin mechanism is not an acceptable
substitute.

## Release verification

Deterministic checks cover the extension, launch resolver, manifest/schema,
Psyche, history, digest, echo-commune, and archive shape. The release gate then
uses one disposable **same-node** endpoint to prove:

1. fresh native bringup and attachable TUI;
2. message → ordinary OMP turn → correlated reply;
3. honest busy/idle state and graceful shutdown;
4. native resume and immutable in-TUI binding;
5. bounded listener recovery and fail-closed teardown;
6. both ReadyAgent and LiveAgent behavior.

Cross-node transport is not an adapter gate. Once same-node delivery has crossed
the public SPT message seam, subnet transport belongs entirely to spt-core and
adds no OMP harness contract.

## Completion status

| Slice | Status |
|---|---|
| OMP-native product boundary | Accepted — ADR-0008 |
| Native TUI for every endpoint | Accepted — ADR-0009 |
| Custody and bounded listener recovery | Accepted — ADR-0010 |
| Immutable endpoint/session binding | Accepted — ADR-0011 |
| Project-local `.spt/` continuity | Accepted — ADR-0012 |
| Same-node adapter release gate | Accepted — ADR-0013 |
| Foreign plugin/hooks, RPC bridge, compatibility profiles | Removed from supported scope |
| OMP-native skill distribution | Public-contract gap; not shipped |
