---
slug: fire-ec-empty-slices
status: resolved
trigger: "doyle.log:1866 FIRE-EC came back with slices unpopulated"
created: 2026-06-02
updated: 2026-06-02
---

# Debug: FIRE-EC echo_commune emits empty live-context / project-context slices

## Symptoms

- **Expected:** A FIRE_ECHO_COMMUNE_NOW dispatch (source=clear) re-emits an
  echo_commune envelope to Self with the same populated `<live-context>` and
  `<project-context>` slices that a normal commune carries.
- **Actual:** The echo_commune envelope arrives with BOTH slices empty:
  `<live-context></live-context>` and `<project-context></project-context>`.
- **Error / log evidence:** `C:\Users\decid\AppData\Local\spt\logs_latest\doyle.log`
  - 1859 `[FIRE-EC] received FIRE_ECHO_COMMUNE_NOW control message`
  - 1860 `[FIRE-EC] dispatching: prior_session=c52b0bc0-... forward_to_self=true source=clear`
  - 1861 `[FIRE-EC] spawning _echo-commune --force prior_session=c52b0bc0-... forward_to_self=true source=clear`
  - 1862 `[ECHO] spawned pid=63156`
  - 1866 `--- ECHO_COMMUNE ... pid=61076 self_session=c52b0bc0-... fresh_session=0db5bd9c-... exit=0 stdout_bytes=1673 excerpt_bytes=64349 excerpt_lines=25 excerpt_dropped_errors=0 excerpt_source_error=ok ---`
  - 1871 `[MSG] from=<EVENT type="echo_commune" from="doyle-p...`
  - 1880-1883 emitted envelope: `<live-context></live-context>` + `<project-context></project-context>` — BOTH EMPTY
- **Timeline:** Observed 2026-06-02 ~05:12 on live agent `doyle` (project spt-core,
  branch dev-freeform). The dispatch source=clear (post-/clear FIRE-EC path).
- **Reproduction:** FIRE_ECHO_COMMUNE_NOW with source=clear, forward_to_self=true,
  after a /clear (prior_session set, fresh_session spawned).

## Key contrast (smoking gun candidates)

- A *normal* commune just before (log 1800-1848) emitted FULLY POPULATED
  live-context + project-context.
- The FIRE-EC echo_commune (exit=0, stdout_bytes=1673, excerpt_bytes=64349) reported
  success yet wrote empty slices — so the failure is in slice POPULATION/forwarding,
  not the spawn or the LLM excerpt capture (excerpt_source_error=ok, 64KB captured).
- Repeated line: `[LIVE-CONTEXT-POST-WRITE] skip no-project-slice self_id=doyle
  project_resolves=true prompt_had_project_ctx=false` (log 1852 and 1887) — fires both
  after the populated commune and after the empty echo. `prompt_had_project_ctx=false`
  may be the relevant branch: the echo path may not be threading the captured slices
  into the post-write persistence / forwarded envelope.

## Code entry points

- `src/live/wrapper/mod.rs` — emits `[LIVE-CONTEXT-POST-WRITE] skip no-project-slice`,
  the FIRE-EC dispatch, `_echo-commune --force`, and ECHO_COMMUNE excerpt capture.
- Related prior work: Phase 25.3 (project-context envelope persistence/encoding defects).
- Distinct from existing echo debug sessions (double-envelope, gate-poll-exit,
  orphan-grace) — this is specifically empty slices on the forwarded FIRE-EC echo.

## Current Focus

- hypothesis: CONFIRMED (revised). The FIRE-EC source=clear path reuses the cadence
  echo-commune's DELTA-summary haiku prompt. The haiku received full prior state
  (CURRENT_LIVE_CONTEXT + CURRENT_PROJECT_CONTEXT, both populated) AND a 64KB delta
  excerpt, then DELIBERATELY emitted empty slice bodies because — per the prompt's
  delta-merge instruction — nothing in the excerpt was new relative to the snapshot.
  Empty slices are the prompt-sanctioned "in-project-but-quiet" signal. The envelope
  faithfully forwards what the haiku produced. Root cause is the prompt/intent mismatch
  for the clear-path, not a slice-threading or persistence bug.
- next_action: root cause found — see Resolution. Fix not yet applied.

## Evidence

- timestamp: 2026-06-02 — Slice assembly traced. The forwarded echo_commune
  envelope body is NOT read from disk; it is the literal `[COMMUNE]...[/COMMUNE]`
  body the haiku child emits (`src/owl/echo_commune.rs:1365-1443`,
  `compose_echo_commune_payload` + `dispatch_commune_markers`). Whatever slice
  bodies the haiku writes are what Self receives.
- timestamp: 2026-06-02 — Forensic jsonl of the haiku session
  (`~/.claude/projects/C--Users-decid-AppData-Local-spt-psyches-tracked/0db5bd9c-...jsonl`)
  shows the haiku's verbatim output:
  `"Nothing new to report. [COMMUNE]<live-context></live-context><project-context></project-context>[/COMMUNE]"`
  and its reasoning: *"The echo-input shows B2 completion and CI validation ... which
  the CURRENT_LIVE_CONTEXT already captured in full ... post-facto confirmation of work
  already described in the snapshot."* This is the haiku correctly following the
  delta-merge prompt (`echo_commune.rs:1146-1173`).
- timestamp: 2026-06-02 — The haiku's INPUT prompt contained BOTH populated
  `CURRENT_LIVE_CONTEXT` and `CURRENT_PROJECT_CONTEXT` blocks (verified in the jsonl).
  So `build_current_context_blocks` (`echo_commune.rs:916-959`) worked and the project
  resolved. The empty output is not caused by a missing snapshot or unresolved project.
- timestamp: 2026-06-02 — FIRE-EC clear path threads NO source distinction into
  `_echo-commune`. `fire_echo_commune_force` (`src/live/wrapper/mod.rs:1657-1668`) →
  `fire_echo_commune_inner` → `build_inner_argv` (`src/live/wrapper/echo_fire.rs:323-342`)
  builds argv `[_echo-commune, --force, --forward-to-self, session_uuid, self_id, psyche_id]`.
  No `--source clear|compact` flag exists (the mod.rs:1647-1650 doc comment explicitly
  notes source is logged-only, never passed to argv). So `run_echo_commune` cannot
  switch prompt intent for the clear path even though it should.
- timestamp: 2026-06-02 — `parse_two_slice` (`src/common/envelope.rs:84-93`) treats
  an empty-bodied `<live-context></live-context>` as `Some("")`, NOT `None`. The empty
  string then short-circuits as an empty slice through `route_two_slice_with_precedence`.
  The forwarded envelope therefore literally carries the empty tags the haiku produced.

## Eliminated

- timestamp: 2026-06-02 — ELIMINATED: excerpt capture failure. `excerpt_bytes=64349`,
  `excerpt_source_error=ok`, `excerpt_lines=25` — the 64KB delta excerpt was captured
  from the prior session jsonl (`prior_session=c52b0bc0`) successfully.
- timestamp: 2026-06-02 — ELIMINATED: spawn / wait failure. `exit=0`, fresh haiku
  session UUID parsed (`fresh_session=0db5bd9c`), `stdout_bytes=1673` — the subprocess
  ran cleanly.
- timestamp: 2026-06-02 — ELIMINATED: slice-threading / disk-read bug in the forward
  path. The envelope body is the haiku's literal output, not a disk read; the haiku
  produced empty bodies on purpose.
- timestamp: 2026-06-02 — ELIMINATED: `prompt_had_project_ctx=false` (mod.rs LIVE-CONTEXT-POST-WRITE)
  as the proximate cause. That is the INBOUND wrapper-side persistence skip and is a
  separate concern; the haiku's outbound prompt DID receive a populated project block.
- timestamp: 2026-06-02 — ELIMINATED: unresolved project name. The resolver chain
  (`resolve_self_project_name_via_info_cwd`) produced `spt-core`; the prompt carried
  `CURRENT_PROJECT_CONTEXT (name=...)`.

## Resolution

- root_cause: The FIRE-EC `source=clear` re-emission path reuses the cadence
  echo-commune's DELTA-summary haiku prompt (`src/owl/echo_commune.rs:1146-1173`,
  "Produce a delta summary — what is NEW in the excerpt relative to CURRENT_LIVE_CONTEXT
  ... empty is the in-project-but-quiet signal"). After a /clear, the prior session's
  work is already reflected in the on-disk snapshot, so the delta is genuinely empty
  and the haiku faithfully emits `<live-context></live-context><project-context></project-context>`.
  The clear-path INTENT is a full re-hydration of Self (re-emit current context after a
  context wipe), not a delta — but `source` is never threaded into `_echo-commune`'s
  argv (`fire_echo_commune_force` logs it but drops it; no `--source` flag in
  `build_inner_argv`), so `run_echo_commune` cannot select a re-emit prompt. The
  proximate cause is a prompt/intent mismatch, not a forwarding or persistence defect.

- fix: APPLIED in the single atomic commit titled "fix: thread FIRE-EC source
  into echo-commune so clear re-hydration re-emits context" (HEAD of branch main
  at resolution time; sha 7539af8 prior to any later amend). All three
  recommended directions implemented:
    1. **Threaded `source` into the echo-commune invocation.** Added optional
       `#[arg(long)] source: Option<String>` to the `EchoCommune` clap command
       (`src/cli.rs`, before the positionals — backward-compat callers without
       `--source` still parse). Destructured + forwarded in `src/owl/mod.rs`.
       `build_inner_argv` / `fire_echo_commune_inner` (`src/live/wrapper/echo_fire.rs`)
       gained a `source: Option<&str>` param that pushes `--source <value>` as a flag
       pair BEFORE the positionals when `Some(_)`. `fire_echo_commune_force`
       (`src/live/wrapper/mod.rs`) now threads its `source: &str` through as
       `Some(source)`. The cadence caller (`fire_echo_commune_if_due`) passes `None`
       (keeps the delta prompt). The orphan path (`src/live/wrapper/orphan.rs`) was
       left UNCHANGED — it builds argv via its own `build_orphan_fire_args` (it never
       called `fire_echo_commune_inner`), so its Test E/E2/H regression assertions and
       the deliberate `--forward-to-self` absence (D9) are preserved.
    2. **Re-emit prompt variant.** Extracted `build_echo_commune_prompt` (pure,
       unit-testable) in `src/owl/echo_commune.rs`. `source == Some("clear")` builds a
       RE-EMIT prompt instructing the haiku to echo CURRENT_LIVE_CONTEXT /
       CURRENT_PROJECT_CONTEXT back VERBATIM (full re-hydration, no delta, no empty
       slices when a snapshot block was inlined). `Some("compact")` / `None` / any
       other value keeps the existing DELTA prompt unchanged. Same `[COMMUNE]`
       two-slice envelope contract and the same "emit BOTH only when a project block
       was inlined" rule in both branches.
    3. **Defensive snapshot fallback.** Added `two_slice_effectively_empty` (treats
       `Some("")`/whitespace-only the same as missing) and `snapshot_two_slice_body`
       (rebuilds a `[COMMUNE]` two-slice body from `build_current_context_blocks`).
       `dispatch_commune_markers` gained a `self_body_override` closure: when
       `forward_to_self` AND the haiku slices are effectively empty, the Self leg is
       re-composed from the on-disk snapshot. The Psyche-bundle leg always keeps the
       haiku's literal output. Independent belt-and-suspenders guard on top of Part 2.
  Tests: updated all `build_inner_argv` call sites for the new param + added
  `--source clear` (before positionals) / `--source` absent (None) argv cases;
  added `--source clear`/`--source compact`/no-`--source` clap parse coverage;
  updated the mod.rs source-introspection test to the new 4-arg call string
  (`self.fire_echo_commune_inner(prior_uuid, true, forward_to_self, Some(source))`);
  added a `fire_ec_empty_slices_tests` module asserting prompt selection (clear →
  re-emit/VERBATIM, no "delta summary"; None/compact → delta) plus snapshot-fallback
  unit + dispatch-integration cases. `cargo build --release` clean; `cargo test --lib`
  green (980 pass). NOTE: three pre-existing, UNRELATED failures exist on the pristine
  main tree and are NOT introduced by this fix — `psyche_md_forbids_projects_write_tool_calls`
  (an un-applied debug-doc fix; the literal sentence was never added to psyche.md),
  `deferred_send_does_not_wake_idle_poll` (native-poll timing flake), and the
  release-only `current_version_honors_override_in_debug_builds` (debug_assertions-gated).
  Also fixed a pre-existing compile break in `tests/native_wrapper_state_retry.rs`
  (three `WrapperHandoffState` initializers missing the `pulse_psyche` field added by
  commit 3616ed1) so `cargo test` can compile the integration crates at all.

- cycles: 1 (investigation, conclusive) + 1 (fix)
