# Phase 25.4 — Smoke Probe Report

**Purpose:** end-to-end verification that the Phase 25.4 nested-migration writers no longer create flat psyche/worker perch dirs as side effects.

**Method:** before-and-after comparison of `.planning/quick/260523-paw-perch-audit-psyche-worker-flat-vs-nested/probe.ps1` output against the operator's `%LOCALAPPDATA%\spt\owlery` filesystem.

**Source:** CONTEXT.md D-01 + D-02 + D-05 + D-07; RESEARCH.md §3 (pre-deploy evidence).

---

## Pre-Deploy Probe (2026-05-23 18:21 PT — captured at RESEARCH time)

Source: RESEARCH.md §3 (Section 3 — On-Disk Evidence). Active live agent at capture time: `todlando`.

### todlando-psyche (FLAT) — every file fresh, active writer

```
DIR: …\owlery\todlando-psyche  dir-mtime=2026-05-23 18:18:13
    2026-05-22 16:54  <DIR>  inbox            (created at first boot; rarely re-touched)
    2026-05-23 17:18  0      .has-messages    (FRESH, ~1h)
    2026-05-23 18:11  0      .idle-ready      (FRESH, ~10min)
    2026-05-22 16:55  0      .more-done       (stale)
    2026-05-23 18:11  315    info.json        (FRESH — written by poll.rs every iter)
    2026-05-23 17:18  0      ready            (FRESH)
    2026-05-23 17:18  36864  spool.db         (FRESH — WAL active)
    2026-05-23 18:11  44     status           (FRESH — poll.rs status line)
    2026-05-23 17:18  139    wrapper-state.json (FRESH — claude.rs:201 publish)
```

### todlando/nested/todlando-psyche (NESTED) — fixtures only, no live writer

```
DIR: …\owlery\todlando\nested\todlando-psyche  dir-mtime=2026-05-23 17:18:21
    2026-04-19 04:06  <DIR>  inbox            (one month old)
    2026-05-22 03:28  316    info.json        (one day old — start.rs nested initial write)
    2026-05-23 17:18  0      ready            (~1h — refreshed by start.rs at last boot? See below)
    2026-05-21 17:54  12288  spool.db         (two days old)
    2026-05-22 03:15  44     status           (one day old)
    2026-05-22 01:55  114    wrapper-state.json (one day old)
```

The `ready` file at the nested path was refreshed at 17:18 — coinciding
with the LAST `$LIVE start` (or the post-handoff publish via
`start.rs:524 / 834`). The wrapper's loop-exit predicate at
`wrapper/mod.rs:1168` is reading THAT file. Nothing in the steady-state
poll loop refreshes the nested ready file. **Survives only because no
code path deletes it.**

### Workers — flat dominant, nested fragmentary

Flat (7 dirs, last hour activity):
```
todlando-w81/  .owl-aware (17:28)  spool.db (17:28)
todlando-w82/  .owl-aware (17:29)  spool.db (17:29)
todlando-w83/  .owl-aware (17:48)  spool.db (17:48)
todlando-w84/  .owl-aware (17:52)  spool.db (17:52)
todlando-w85/  .owl-aware (17:57)  spool.db (17:57)
todlando-w86/  .owl-aware (17:59)  spool.db (17:59)
todlando-w87/  .owl-aware (18:04)  spool.db (18:04)
```

These flat dirs have NO `info.json` and NO `ready` file —
`enumerate_perches::push_if_perch` skips them silently, so they don't
appear in `live list`. They are pure side-effect of hook_check.rs:141
(`.owl-aware`) + spool.rs:12-18 (`spool.db` autocreate).

Nested (2 dirs, w85 and w87 only):
```
todlando/nested/todlando-w85/  info.json (17:57)  ready (17:57)  inbox/  — fully formed nested perch
todlando/nested/todlando-w87/  info.json (18:04)  ready (18:04)  inbox/  — fully formed nested perch
```

w81-w84 and w86 have NO nested counterpart. So `spawn_worker_perch` only
fired (or only succeeded) for w85 and w87 — the other 5 workers got their
flat dirs from spool/inbox auto-create paths WITHOUT a properly registered
perch ever existing. Implication: those subagents never got
`spawn_worker_perch` called for them (or it failed silently). **Worth
investigating as a separate issue** — Open Question 1.

### Other agents (offline at time of research)

- `doyle` (offline, last started 5/22): has `nested/doyle-psyche/` with
  matching flat counterpart from before the most recent migration. No
  active writer.
- `webber` (offline): nested-only `nested/webber-psyche/`. **Pure nested
  case** — confirms the migration DID land for some agents at some point;
  current writes lost the nested-aware shape.
- `dunsen` (offline, last started 5/23 17:17): flat `dunsen/` Self-level
  only. No psyche dir at either layer (psyche was cleaned up by the soft
  stop).

---

## Post-Deploy Probe (2026-05-23 22:01:54 PT — captured after Phase 25.4 v1.11.10 deploy + `$LIVE start probetest` + subagent spawn)

Probe output captured to `post-probe.txt`. Reproduced inline:

```
ROOT: C:\Users\decid\AppData\Local\spt\owlery

=== TOP LEVEL ===

Name           LastWriteTime
----           -------------
deployah       5/23/2026 7:39:30 PM
doyle          5/23/2026 7:39:30 PM
dunsen         5/23/2026 10:01:02 PM
executor       5/23/2026 7:39:30 PM
mica           5/23/2026 7:39:30 PM
probetest      5/23/2026 10:01:54 PM
todlando       5/23/2026 7:39:30 PM
webber         5/23/2026 10:01:02 PM
witty          5/23/2026 7:39:30 PM
.registry      5/23/2026 9:53:58 PM
.session-cache 5/23/2026 10:00:52 PM

=== FRESH FILES (last 2h) under owlery (recursive) ===
     0.8 min  C:\Users\decid\AppData\Local\spt\owlery\probetest\.idle-ready
       1 min  C:\Users\decid\AppData\Local\spt\owlery\probetest\spool.db
       1 min  C:\Users\decid\AppData\Local\spt\owlery\.session-cache
     1.2 min  C:\Users\decid\AppData\Local\spt\owlery\probetest\.working-counter
     7.9 min  C:\Users\decid\AppData\Local\spt\owlery\.registry
     7.9 min  C:\Users\decid\AppData\Local\spt\owlery\probetest\status
     7.9 min  C:\Users\decid\AppData\Local\spt\owlery\probetest\info.json
     7.9 min  C:\Users\decid\AppData\Local\spt\owlery\probetest\nested\probetest-psyche\.idle-ready
     7.9 min  C:\Users\decid\AppData\Local\spt\owlery\probetest\nested\probetest-psyche\status
     7.9 min  C:\Users\decid\AppData\Local\spt\owlery\probetest\nested\probetest-psyche\info.json
     7.9 min  C:\Users\decid\AppData\Local\spt\owlery\probetest\nested\probetest-psyche\spool.db
     7.9 min  C:\Users\decid\AppData\Local\spt\owlery\probetest\nested\probetest-psyche\wrapper-state.json
     8.1 min  C:\Users\decid\AppData\Local\spt\owlery\probetest\nested\probetest-psyche\ready
     8.1 min  C:\Users\decid\AppData\Local\spt\owlery\probetest\ready
    10.4 min  C:\Users\decid\AppData\Local\spt\owlery\dunsen\.idle-ready
    89.4 min  C:\Users\decid\AppData\Local\spt\owlery\dunsen\info.json

=== FLAT PSYCHE DIRS ===
   <empty>

=== FLAT WORKER DIRS (-w[N]) ===
   <empty>

=== NESTED CHILD DIRS (owlery/*/nested/*/) ===
DIR: C:\Users\decid\AppData\Local\spt\owlery\probetest\nested\probetest-psyche  dir-mtime=05/23/2026 22:01:54
    5/23/2026 9:53:49 PM  <DIR>  inbox
    5/23/2026 9:53:58 PM  0  .idle-ready
    5/23/2026 9:53:58 PM  181  info.json
    5/23/2026 9:53:49 PM  0  ready
    5/23/2026 9:53:58 PM  12288  spool.db
    5/23/2026 9:53:58 PM  45  status
    5/23/2026 9:53:58 PM  138  wrapper-state.json
(+ doyle/nested/doyle-psyche, todlando/nested/todlando-psyche, todlando/nested/todlando-w85, webber/nested/webber-psyche — all pre-existing per CONTEXT D-05; old mtimes confirm no fresh flat-writer side effects)
```

---

## Diff Annotations

### New test agent (`probetest`) — expectations

- [x] `probetest-psyche/` flat dir does NOT exist — confirmed empty `=== FLAT PSYCHE DIRS ===`
- [x] `probetest/nested/probetest-psyche/` EXISTS with fresh files (info.json, ready, status, inbox/, spool.db, .idle-ready, wrapper-state.json — all mtimes ≤8.1 min)
- [x] `probetest-wN/` flat worker dirs do NOT exist — confirmed empty `=== FLAT WORKER DIRS ===`
- [~] `probetest/nested/probetest-wN/` not observed for spawned subagent — but ALSO not observed flat → migrated writers correctly produce zero flat dirs; spawn_worker_perch evidently did not fire a fully-formed nested perch for the one-shot subagent. Carry over RESEARCH Open Q1 (worker-perch asymmetry) as separate investigation — independent of Phase 25.4 GOAL (no flat side-effect dirs).

### RESEARCH-era orphans (expected to persist per CONTEXT D-05)

- [x] No fresh files inside `todlando-psyche/` or any `todlando-w*` — all such dirs cleaned up from top-level listing (only `todlando/` Self dir remains; nested entries have mtimes ≥1 day old)
- [x] `doyle/nested/doyle-psyche` and `webber/nested/webber-psyche` show no fresh writes — old mtimes intact

### Worker-mystery (RESEARCH Open Q1) resolution

- [ ] Worker-perch asymmetry persists at low signal — one-shot subagent did not create either flat OR nested perch dir. File follow-up under `.planning/debug/<DATE>-worker-perch-asymmetry-persists.md` per CONTEXT D-07. Out of Phase 25.4 GOAL scope: phase asserts NO flat side effects, which holds. Whether spawn_worker_perch fires reliably is a separate registration question.

### Follow-up fixes applied during probe (in-scope finishing)

- [x] **`.more-done` gate predicate** (`src/owl/hook_idle.rs:137`) — flipped from flat `owlery::ready_file(psyche_id)` to nested `perch_path::resolve_ready_file(psyche_id, Explicit(owl_id))`. Pre-fix: gate always failed post-25.4 (flat ready file never exists) → sentinel never written → wrapper echo-commune fires silently dead. Verified via reprobe: `.more-done` sentinel landed at `probetest/nested/probetest-psyche/.more-done` after Stop hook fired on v1.11.11 binary. Commit `48b61be`.
- [x] **Stop hook version-change early-return** (`src/owl/hook_idle.rs::run`) — dropped `VersionPrompt::Emitted => return` short-circuit. Pre-fix: any Stop hook firing a version-change emission skipped both `set_idle_ready` and `spawn_echo_commune_if_live` (both pure filesystem sentinel writers, orthogonal to spool/stdout) → `.more-done` dropped for one fire per version transition. Commit `72b4263`. Verified by reasoning; operator accepted without re-proof (sentinel already present for current session from prior fire under v1.11.11).

### Why was this missed by the Phase 25.4 planner?

Plan 25.4-04 listed `src/owl/hook_idle.rs` in `files_modified` and migrated the `.more-done` WRITER at line 153. The READER predicate at line 137 — `owlery::ready_file(psyche_id).exists()` — went unmigrated because:

1. **Reader audit was call-site scoped, not file-scoped.** RESEARCH Class-E enumerated direct path-composition reader calls (`info_file`, `perch_dir`, `ready_file`). It captured discrete sites but did not require: "for every file we touch the writer in, sweep all sibling readers in the same function." A gate predicate is a degenerate reader (boolean `.exists()` on a composed path) that the audit's call-site index treated as low-risk.
2. **Plan 04 vs Plan 06 split routed the reader to the wrong plan.** Plan 04 = lower-layer writer migration. Plan 06 = eight Class-E reader sites. The gate predicate sat inside Plan 04's file but was conceptually Plan 06 work. Neither plan claimed it. It fell between the seams.
3. **Tests pinned the broken contract.** The pre-fix test helper `touch_psyche_ready` wrote the ready sentinel at the FLAT path — exactly matching the flat-gate. The test setup secretly co-evolved with the bug, so all 7 tests passed against a gate that would never fire in production.
4. **No integration test exercised the .more-done writer end-to-end.** The integration suite added in Plan 25.4-07 (`perch_path_no_flat_psyche.rs`, `perch_path_no_flat_worker.rs`) asserts the NEGATIVE invariant (no flat dirs created). It does not assert the POSITIVE invariant (`.more-done` actually fires when conditions are met). A POSITIVE-side smoke would have caught this in CI.

**Mitigation for next phase of this kind:** when migrating a writer, grep the SAME file for `owlery::*_file(`, `owlery::perch_dir(`, `owlery::info_file(` predicates and gate calls — not just the writer call. Add to PATTERNS.md if recurrence likely.

The Stop hook version-change early-return bug (commit `72b4263`) was unrelated to Phase 25.4 — pure Phase 34 over-broad ordering invariant that happened to surface during 25.4 reprobe.

---

## Resolution

**Status:** PASS (with 2 in-scope finishing fixes applied during checkpoint loop)

**Notes:**
- Phase 25.4 GOAL — "psyche/worker writers no longer create flat perch dirs as side effects" — confirmed by probe: zero flat psyche dirs, zero flat worker dirs, all probetest writes nested-only.
- Two seam bugs caught and fixed in-checkpoint: `.more-done` gate predicate (Phase 25.4 miss) + Stop hook version-change early-return (Phase 34 unrelated coupling). Both committed to `main` and deployed (v1.11.11, v1.11.12). Plugin version bumped twice during finishing.
- RESEARCH Open Q1 (worker-perch asymmetry) carried forward as separate investigation — orthogonal to Phase 25.4 GOAL.
- Operator approved close without third reprobe; the gate-predicate fix has positive proof (sentinel observed at nested path), the early-return fix has reasoned justification + downstream tests intact.
