---
name: presence-dead-shows-online
description: "A remote node paints another node's DEAD endpoints as ONLINE — dead perch gossiped Dormant, picker maps remote Dormant→Online"
metadata: 
  node_type: memory
  type: reference
  originSessionId: 712ec375-e0f7-42d6-afab-1b6b84a87330
---

**Symptom (confirmed 2026-06-27):** node B's picker shows ALL of node A's endpoints as green/ONLINE when they're dead. Same perch reads Offline on its HOME node but Online on a remote node.

**Root (two sites, deterministic chain):**
1. `crates/spt-daemon/src/registryhost.rs:397-405` — the gossiped per-endpoint `Status`: `if is_perch_alive {...} else { Status::Dormant }`. A **not-alive perch is advertised as `Dormant`** (re-stamped every gossip round with a fresh epoch → never ages to Offline).
2. `crates/spt/src/picker/data.rs:266-270` — a REMOTE endpoint's `registry::Status::Active | Dormant => EpStatus::Online`, `_ => Offline`. So **remote `Dormant` → green ONLINE**, collapsing the Active/Dormant distinction. (Locally, `row_status(alive=false) → Offline` — hence the home-vs-remote disagreement.)

**Design intent GATED vs resting.rs + CONTEXT.md (operator-corrected, key):** active/dormant is the MULTI-INSTANCE ROUTING differentiator — an endpoint runs on N nodes (cross-node sync); **active** = the bare-`id` routing target, **dormant** = a WARM non-target sibling ("driving ling@laptop makes ling@desktop dormant", resting.rs:97; active→dormant on AttentionShift/Detach). **suspended** = COLD (session closed, resumable-on-wake) while its NODE is UP. **offline** = NODE DOWN — RestState has NO Offline; a live node ONLY self-gossips active/dormant/suspended; Offline is remote-inferred via epoch-lease eviction. Consolidated into CONTEXT.md "instance state" glossary.

**Fix (REQ-PRESENCE-LIVENESS-TRUTH, v0.17.0 W4) — PRIMARY is registryhost `else` only:** not-bound-alive → live-but-**unbound** (live broker session; is_perch_alive bound-gated, reuse is_perch_unbound) → Active/Dormant (warm); **else (cold, no live session — but node is UP since this daemon gossips) → SUSPENDED**, NOT Dormant (needs warm), NOT Offline (node up; never self-gossiped). This ALONE kills false-ONLINE (picker `_` arm → gray). Picker `Dormant→Online` is CORRECT (warm = available routing fallback); a distinct glyph is OPTIONAL UX. **My earlier "dead→Offline" was WRONG** — Offline=node-down, but the node is UP → Suspended. Two corrections deep: doyle wrote "dead→Offline" then "picker co-equal bug"; operator grounded both → dead-on-live-node=Suspended, picker is fine. FLAGGED design-Q: Detach→Dormant means a single detached instance is also Dormant — autonomous-headless Active-vs-Dormant is a separate clarification (non-blocking). Kin: [[v0170-robust-join-design]], [[gate-against-documented-design]]. GOTCHA: dead test perches on HFENDULEAM linger on disk → list_self_perch_ids re-gossips every round.
