---
phase: 25.1-revise-commune-signoff-skill-docs-echo-commune-instruction-t
plan: 01
subsystem: live-context
tags: [rust, sentinel, psyche-download, two-slice, d-25.1-04]

# Dependency graph
requires:
  - phase: 25
    provides: "download_payload() single-producer D-14 ordering (memformat -> live_context -> project -> Pending) and D-15 inheritance into download_payload_for_injection"
  - phase: 24.1
    provides: "derive_current_repo_names() cwd_project resolution"
  - phase: 24
    provides: "owlery::project_worktree_path() forked-repo layout"
provides:
  - "framework-emitted <project-context-resolved name=\"...\"/> sentinel in psyche-download output"
  - "deterministic grep target for Self-side in-project detection (eliminates LLM-judgment heuristic)"
  - "stable contract surface for Wave 2 plans (02-06) doc rewrites"
affects:
  - 25.1-02
  - 25.1-03
  - 25.1-04
  - 25.1-05
  - 25.1-06

# Tech tracking
tech-stack:
  added: []
  patterns:
    - "Self-closing XML sentinel emission gated by same conditions as the data it announces"
    - "Single-producer inheritance via D-15 (download_payload_for_injection wraps download_payload)"

key-files:
  created:
    - .planning/phases/25.1-revise-commune-signoff-skill-docs-echo-commune-instruction-t/25.1-01-SUMMARY.md
  modified:
    - src/live/context.rs

key-decisions:
  - "Shape A (XML self-closing sentinel) chosen over Shape B (H2 header) — symmetric with existing <psyche-stamp/> / <current/> tags, zero collision with Psyche-LLM-authored H2s in project body"
  - "Sentinel gated by SAME three guards as project body push (cwd_project + file exists + read succeeds) — presence/absence is a hard signal, no separate failure modes"
  - "No edit to download_payload_for_injection — inherits sentinel for free via Phase 25 D-15 single-producer rule"

patterns-established:
  - "Sentinel-gated-by-data: framework-emitted markers MUST share the guard set of the data they announce; partial-emission breaks the presence/absence contract"

requirements-completed: []

# Metrics
duration: 6min
completed: 2026-05-22
---

# Phase 25.1 Plan 01: Sentinel Introduction Summary

**Framework-emitted `<project-context-resolved name="..."/>` sentinel in `download_payload()` enables Self to detect in-project status from psyche-download output via deterministic grep, eliminating LLM-judgment heuristics for D-25.1-04.**

## Performance

- **Duration:** 6 min (353 s)
- **Started:** 2026-05-22T07:13:40Z
- **Completed:** 2026-05-22T07:19:33Z
- **Tasks:** 1 (TDD — RED + GREEN commits)
- **Files modified:** 1 (`src/live/context.rs`)

## Accomplishments

- Sentinel `<project-context-resolved name="{project_name}"/>\n` now emits at `src/live/context.rs:466` (inside the in-project guard block at lines 460-473), immediately BEFORE the existing `out.push_str(&content);` project body push.
- Two new unit tests pin both branches:
  - `download_payload_emits_project_context_resolved_sentinel_when_in_project` (`src/live/context.rs:2354`) — asserts substring presence with embedded project name AND D-14 ordering invariants (`live body < sentinel < project body`).
  - `download_payload_omits_sentinel_when_no_project_file` (`src/live/context.rs:2422`) — asserts substring absence when no project file exists; regression-guards live body push.
- Doc-comment block (lines 449-459) extended to call out the sentinel rationale alongside D-14 / D-09 / D-15 commentary.
- `cargo build --release` succeeds with no new warnings introduced in `src/live/context.rs`.
- All 10 `live::context::tests::download_payload_*` tests pass (8 pre-existing regression guards + 2 new); 1 pre-existing `#[ignore]` test unaffected.
- Full `live::context::tests` module: 56 passed / 0 failed / 1 ignored (clean).
- `download_payload_for_injection` inherits the sentinel automatically via Phase 25 D-15 single-producer rule — no separate edit, existing `download_payload_for_injection_strips_pulse_log` test continues to pass.

## Task Commits

1. **Task 1 RED — failing tests for sentinel** — `6a71c6d` (test)
2. **Task 1 GREEN — sentinel emission** — `5717e2b` (feat)

_TDD task — REFACTOR phase skipped as no cleanup needed; implementation is minimal (one `push_str` + one `format!` macro)._

## Files Created/Modified

- `src/live/context.rs` — Sentinel emission at line 466 inside `download_payload()`. Doc-comment extension at lines 449-459. Two new tests at lines 2336-2452 in `mod tests`.
- `.planning/phases/25.1-revise-commune-signoff-skill-docs-echo-commune-instruction-t/25.1-01-SUMMARY.md` — this file.

## Decisions Made

- **Shape A (self-closing XML tag) preferred over Shape B (H2 header)** per RESEARCH §"Open Question Resolution" recommendation — symmetric with existing `<psyche-stamp/>` / `<current/>` convention; zero collision with Psyche-LLM-authored H2s in the project body.
- **Sentinel placed BEFORE `out.push_str(&content)`** — Self greps it as a header, not buried in body text.
- **No edit to `download_payload_for_injection`** — Phase 25 D-15 single-producer rule means it inherits the sentinel by delegating to `download_payload`. Existing `download_payload_for_injection_strips_pulse_log` regression test continues to pass, confirming the strip_pulse_log line-anchor (`## Pulse Log (gen`) does not collide with the new sentinel.

## Deviations from Plan

**None — plan executed exactly as written.** All acceptance criteria met:

- `grep -c 'project-context-resolved' src/live/context.rs` = 5 (>= 3 required: 1 emission site + 2 test fn names + 2 assertion lines).
- `grep -c 'out.push_str.*project-context-resolved' src/live/context.rs` = 1 (>= 1 required — sentinel emission line uses single-line `format!` macro per RESEARCH §"Code Examples").
- `cargo build --release` exit 0.
- All 10 `download_payload_*` tests green (incl. 2 new + 4 pre-existing regression guards listed in plan).
- Negative criteria: `psyche.md`, `src/owl/resume.rs`, `src/common/envelope.rs`, `src/owl/echo_commune.rs`, and `plugin/spt/skills/` all unmodified — confirmed via `git diff --name-only HEAD <path>`.

Minor formatting note: initial multi-line `out.push_str(&format!(\n    "<project-context-resolved name=\"{}\"/>\n",\n    project_name\n));` was reformatted to single-line `out.push_str(&format!("<project-context-resolved name=\"{}\"/>\n", project_name));` so the plan's `grep 'out.push_str.*project-context-resolved'` acceptance regex matches on one line. Semantics identical; pure formatting.

## Issues Encountered

**Pre-existing flaky parallel-test failures (out-of-scope):** A full `cargo test --lib` run produces 38-41 failures fluctuating between runs in modules outside `live::context` (e.g., `owl::resume::tests::inject_active_perch_context_*`, `owl::echo_commune::tests::*`, `owl::plugin_session_start::*`). These tests PASS in isolation. This matches the known issue documented in PROJECT.md "Next Milestone Goals" → *"21 unrelated parallel-test failures in Phase 18/28/30/32 surfaces (pass in isolation; cleanup quick task candidate)"* and STATE.md backlog. Failure count is non-deterministic across runs (38 vs 41 observed) — classic env-isolation race signature, not a regression introduced by this plan. **All `live::context::tests` are green** (56/56 incl. all `download_payload_*` tests).

## TDD Gate Compliance

Plan declared `tdd="true"` on Task 1; gate sequence verified in git log:

1. RED — `test(25.1-01)` at `6a71c6d` (1 file, +117 lines; tests authored, both failing per cargo test output).
2. GREEN — `feat(25.1-01)` at `5717e2b` (1 file, +12 lines; sentinel emission added; both new tests now passing alongside the 8 pre-existing `download_payload_*` regression guards).
3. REFACTOR — skipped (implementation minimal; no cleanup needed).

Fail-fast rule respected: RED phase confirmed both new tests failed before implementation; no proceed-on-passing.

## Next Phase Readiness

- **Wave 2 plans (02-06) unblocked.** Each doc-rewrite plan now has a stable grep target (`<project-context-resolved`) to teach in their skill markdown:
  - Self-side detection prose: *"After `$LIVE psyche-download`, grep the output for `<project-context-resolved`. If present, you are in a tracked project — include `<project-context>`. If absent, omit it."*
  - Concrete contract: framework-emitted, gated by data presence, attribute-carrying for the project name.
- **No deploy needed for this plan.** Wave 2 doc edits will batch in; the milestone deploy cadence (per STATE.md and ROADMAP.md) is end-of-milestone via `DEPLOY.ps1`, not per-phase.
- **Backward compat:** Sentinel is additive emission — no parse-side requirement. Live agents booted on older binaries continue to work; they simply do not see the sentinel until their next `cargo build --release` + handoff cycle.

## Self-Check: PASSED

Verification ran:

- `[ -f src/live/context.rs ]` → FOUND
- `git log --oneline --all | grep -q 6a71c6d` → FOUND (RED commit)
- `git log --oneline --all | grep -q 5717e2b` → FOUND (GREEN commit)
- `grep -c 'project-context-resolved' src/live/context.rs` → 5 (>= 3 expected)
- `grep -c 'out.push_str.*project-context-resolved' src/live/context.rs` → 1 (>= 1 expected)
- Negative-criteria diffs (psyche.md, resume.rs, envelope.rs, echo_commune.rs, plugin/spt/skills/) → all empty
- `cargo build --release` → exit 0
- `cargo test --lib live::context::tests::download_payload_emits_project_context_resolved_sentinel_when_in_project` → 1 passed
- `cargo test --lib live::context::tests::download_payload_omits_sentinel_when_no_project_file` → 1 passed
- `cargo test --lib live::context::tests::download_payload` → 10 passed / 0 failed / 1 ignored

---
*Phase: 25.1-revise-commune-signoff-skill-docs-echo-commune-instruction-t*
*Completed: 2026-05-22*
