# LIFECYCLE-PLAN — host lifecycle wave (v1.0.3)

> JIT plan (AGENTS.md). Scope: three operator-reported field issues, one wave.
> Gate: host `cargo test` + rust workspace tests + app JVM tests + assembleDebug/Release
> + `traceable-reqs check` green, then release v1.0.3 per docs/RELEASING.md.

## Field evidence (2026-07-07, all live-probed against spt-core release binaries)

1. **`spt endpoint shutdown|suspend mobile-gw` doesn't stop the gateway.** The verbs
   record registry status `Suspended` and touch nothing else — the presence badge rides
   the host's `api listen` child, which spt-core never kills (docs: teardown happens
   "when the template process exits"; no exit contract exists for hosted binaries).
   The host never looks at the registry, so it relays forever. Probe trail: shutdown →
   `NO_EDGE` (edge already recorded), `endpoint list --json` shows `status: "Suspended"`
   while the human list shows `ONLINE + CONTROLLED` off the live listener.
2. **`spt adapter update mobile` can't swap a running host.** Windows locks the running
   exe; nothing stops the host around an update. Also: the daemon does NOT relaunch a
   hosted process that exits (probed: host killed → no respawn), so the host must arrange
   its own successor.
3. **Duplicate digest turns on the phone** (perri view, operator screenshot): the open
   turn arrives with `partial: true` and **no `input_seq`** (probed live; both view twins
   and their fixtures wrongly assumed `input_seq` is always present). The 15s liveness
   belt re-syncs with `--after <cursor>`; the unseqed turn matches no merge slot and is
   re-appended every tick — and once it closes (gaining a seq), the stale unseqed copies
   can never be replaced.

## Tasks

- **T1 — suspend honor** (`REQ-HOST-SUSPEND-HONOR`): host polls its own registry status
  (`spt endpoint list --json`) every LIFECYCLE_CHECK period in the drain loop and before
  every re-listen; `Suspended` → announce + full exit. `run` startup wakes a `Suspended`
  endpoint (`spt endpoint wake <id>`) — explicit run is operator intent to be up.
- **T2 — in-place update** (`REQ-HOST-INPLACE-UPDATE`): at `run` startup the host frees
  its canonical exe path (rename running image → `-prev`, copy back to canonical), then
  watches the canonical file; a changed mtime = update landed → spawn a detached
  relauncher (`spt endpoint run --adapter mobile --id <id> --start`, breakaway from the
  job) and exit. Fresh generation cleans the stale `-prev`.
- **T3 — job-object guard** (`REQ-HAZARD-LISTEN-ORPHAN`): kill-on-close Windows job wraps
  the host; every spawned `spt` child (listen, digest --follow) dies with it — no orphan
  listener can hold the endpoint ONLINE. Relauncher spawns with breakaway. Core-side
  `--parent-pid` watch (SPT-CORE-NEEDS §5) stays belt-and-suspenders.
- **T4 — digest dup fix** (`REQ-HAZARD-DUP-ROWS`): both view twins
  (`rust/link-client/src/digest.rs`, `app/…/DigestViewState.kt`) drop stale unseqed
  (open/partial) turns before a snapshot merge — the snapshot window is authoritative for
  the open tail; seqed dedup stays exact. Fixtures corrected to the probed wire shape.

## Open notes

- Automated int test for T3 (kill host → assert child died) is owed; this wave ships
  impl + live verification (see KNOWN-HAZARDS 4.1).
- v1.0.2 → v1.0.3 update itself still needs the manual stop (the running 1.0.2 binary
  honors nothing yet); v1.0.3 → v1.0.4 is the first fully in-place update.
