---
status: fixed
trigger: "probetest.log line 109 — ECHO-FIRE orphan-path fired immediately after binary handoff (iter 1) while Self listener was only transiently unreachable; grace recheck 21s later confirmed Self alive and suppressed INIT_SIGNOFF, but the echo-commune had already fired and been delivered (line 110, line 120)"
created: 2026-05-23T22:55:00-07:00
updated: 2026-05-23T23:10:00-07:00
---

## Current Focus

hypothesis: CONFIRMED — In `src/live/wrapper/orphan.rs::check_orphan`, the Phase 29 Plan 04 orphan-path ECHO-FIRE block (lines 167-199) executes BEFORE the 5-second grace-period sleep + still_gone recheck (lines 201-225). A transient false-orphan signal (Self listener momentarily unreachable during binary handoff, or any other brief probe failure) therefore fires `_echo-commune --force` and delivers a phantom echo_commune envelope to the Psyche inbox even when Self recovers within the grace window and INIT_SIGNOFF is correctly suppressed. The grace-period guard introduced by quick-260520-mth was retrofitted around INIT_SIGNOFF delivery only — the synchronous ECHO-FIRE added later by Phase 29 Plan 04 inherited the unguarded position above the sleep.
test: log timeline reconstruction (probetest.log lines 102-115) + direct source reading of orphan.rs + new source-order regression test `test_sync_fire_argv_appears_after_still_gone_recheck_and_recovery_return` (passes, pins new ordering)
expecting: ECHO-FIRE log emits AFTER `still_gone` recheck returns true, never before
next_action: none — fix applied + 14 orphan_fire_tests pass + 147 wrapper tests pass + 898 lib tests pass

## Symptoms

expected: When the Psyche wrapper completes a binary handoff and starts inner-poll iteration 1, `check_orphan` should either (a) observe Self alive and return false silently, OR (b) observe Self apparently gone, sleep 5s, recheck — if Self recovered, log "Self parent recovered" and return false WITHOUT firing ECHO-FIRE or INIT_SIGNOFF.
actual: ECHO-FIRE fires unconditionally on the first `self_alive == false` reading, BEFORE the grace period sleep. The synchronous fire blocks ~19s waiting for the haiku subprocess (line 109 → line 113 = 21s wall time). When the grace recheck then succeeds (line 115), the wrapper logs "Self parent recovered" and skips INIT_SIGNOFF — but the phantom echo-commune envelope has already been composed and delivered (line 110 ECHO_COMMUNE block; line 120 wrapper drains its own freshly-emitted envelope).
errors: none (silent semantic violation — the ECHO-FIRE succeeds technically; it just shouldn't have run)
reproduction: trigger a binary handoff (or any other transient Self-perch probe failure) while the Self listener is mid-cycle. New wrapper boots, iteration 1 calls `check_orphan`, evaluates `self_alive` against a momentarily-stale `parent_pid` liveness probe → enters orphan branch → fires ECHO-FIRE → sleeps 5s → recovers.
started: Phase 29 Plan 04 (AUTO-EC-02) — when the synchronous orphan-path `_echo-commune --force` was added to `check_orphan`. The grace guard already existed (per quick-260520-mth) but only covered compose+deliver of INIT_SIGNOFF.

## Evidence

- timestamp: 2026-05-23T22:50:20
  source: C:\Users\decid\AppData\Local\spt\logs_latest\probetest.log:102-115
  finding: |
    102: [22:50:20] poll exited code=2 stderr=HANDOFF_DEFER:probetest-psyche
    105: [22:50:20] HANDOFF wrapper: new pid=47612, exiting
    106: [22:50:20] wrapper started gen=1 ... handoff_hydrated=true
    108: [22:50:20] poll iteration 1 starting
    109: [22:50:20] [ECHO-FIRE] orphan-path: spawning _echo-commune --force (no timeout)
    110: --- ECHO_COMMUNE 2026-05-23T22:50:39-07:00 ... exit=0 stdout_bytes=2830 ...
    113: [22:50:41] [ECHO-FIRE] orphan-path: exit=0 stdout_bytes=0 stderr_bytes=245
    114: [22:50:41] Self parent session is gone ... entering grace period before INIT_SIGNOFF
    115: [22:50:46] Self parent recovered during grace period, continuing normally
    120: [22:50:46] [MSG] from=<EVENT type="echo_commune" from="probete: ""

  Note line 109 fires BEFORE line 114 (grace-period entry). Line 115 confirms the orphan signal was transient — the entire ECHO-FIRE was redundant work that also redelivered a noise echo_commune envelope to Self (line 120).

- timestamp: source-read
  source: C:\Users\decid\Documents\projects\claude_skill_owl\src\live\wrapper\orphan.rs:163-225 (pre-fix)
  finding: |
    Line 163-165: `if self_alive { return false; }` — early return when alive.
    Line 167-199: ECHO-FIRE block (read uuid, log "spawning", dispatch_orphan_fire, log result). RUNS UNCONDITIONALLY when self_alive was false.
    Line 201: log "Self parent session is gone ... entering grace period"
    Line 209: `std::thread::sleep(Duration::from_secs(5))` — grace sleep
    Line 211-221: `let still_gone = !ready_exists || !is_alive(parent_pid)`
    Line 222-225: `if !still_gone { log("recovered"); return false }` — early return on recovery
    Line 226+: compose_init_signoff_payload + deliver_body_anonymous (guarded correctly behind still_gone).

  The ECHO-FIRE block (167-199) sat OUTSIDE the still_gone guard. Moved to between the still_gone confirmation and the compose call.

## Resolution

### Root Cause

In `src/live/wrapper/orphan.rs::check_orphan`, the Phase 29 Plan 04 AUTO-EC-02 synchronous orphan-path ECHO-FIRE block was placed BEFORE the quick-260520-mth grace recheck. The grace guard correctly suppresses spurious INIT_SIGNOFF delivery when `self_alive` reads false transiently (e.g. during a binary handoff window where the Self listener is momentarily mid-restart), but the ECHO-FIRE block was inserted above the grace sleep at line 209, so it fires unconditionally on the FIRST `self_alive == false` reading regardless of whether Self recovers.

In the observed probetest run, the Psyche wrapper handed off from v1.11.12 → v1.11.13 at 22:50:20. The new wrapper's iteration-1 `check_orphan` evaluated `self_alive` against a `parent_pid` that briefly appeared dead (or a `ready` file that briefly didn't exist) during the handoff cross-over, fired ECHO-FIRE synchronously (21 seconds of blocking work), then completed the grace recheck which found Self alive and recovered — but the phantom echo-commune envelope had already been composed, delivered to the Psyche perch, and would be drained back into the resume prompt one iteration later (line 120).

This is a placement bug, not a semantic bug — the ECHO-FIRE logic itself is correct; it just needed to run AFTER `still_gone` is confirmed true.

### Fix (applied)

`src/live/wrapper/orphan.rs::check_orphan` — moved the ECHO-FIRE block from above the grace sleep to between the still_gone confirmation log line and the compose+deliver block. New ordering:

1. Check `self_alive` — unchanged
2. If alive, return false — unchanged
3. Log "entering grace period"
4. Sleep 5s grace
5. Compute `still_gone`
6. If recovered, log "Self parent recovered during grace period, continuing normally" and return false
7. Log "Self parent confirmed gone after grace period, delivering INIT_SIGNOFF"
8. **[MOVED]** ECHO-FIRE block — fires only when teardown is certain
9. Compose + deliver INIT_SIGNOFF
10. Return `classify_orphan_return(true)`

This preserves the Phase 29 Plan 04 design intent ("fire echo-commune as the final delta-capture before INIT_SIGNOFF") while making it grace-aware.

### Regression test (added)

`src/live/wrapper/orphan.rs::orphan_fire_tests::test_sync_fire_argv_appears_after_still_gone_recheck_and_recovery_return`:

Asserts via `include_str!` byte-index that inside the production region of `orphan.rs`:
- `let still_gone =` recheck binding appears BEFORE `build_orphan_fire_args(&uuid,` (the ECHO-FIRE call site)
- `Self parent recovered during grace period` log line appears BEFORE `build_orphan_fire_args(&uuid,`

If a future refactor accidentally moves the ECHO-FIRE back above the grace sleep, this test fails first with a pointer to this debug session.

### Existing tests unaffected

All pre-existing source-order tests remain green because the new placement still satisfies their invariants:
- Test A: build_orphan_fire_args call still precedes compose_init_signoff_payload ✓
- Test B: build_orphan_fire_args call still follows `if self_alive {` guard ✓
- Test E2: production region still contains no quoted `"--forward-to-self"` literal ✓
- quick-260520-mth source-order tests: sleep + still_gone still precede compose/deliver ✓

### Test results

- `cargo test --lib orphan_fire_tests -- --test-threads=1` — 14 passed, 0 failed
- `cargo test --lib wrapper -- --test-threads=1` — 147 passed, 0 failed
- `cargo test --lib -- --test-threads=1` — 898 passed, 0 failed, 5 ignored
