---
phase: quick-260527-s8l
plan: 01
subsystem: tracked-worktree + echo_commune project-context routing
tags: [bug-fix, silent-failure, worktree, p-branch, sync, defense-in-depth]
requirements:
  - "S8L-01: p-* project branches must receive commits and propagate to origin (COMPLETE)"
  - "S8L-02: ensure_worktree must self-recover from pre-populated non-empty target dirs (COMPLETE)"
dependency-graph:
  requires: [tracked::ensure_project_worktree, tracked::commit_project_payload, common::git::run_git_checked]
  provides: ["p-{project_name} branches now receive commits", "self-healing recovery for pre-populated dirs"]
  affects: [src/owl/echo_commune.rs, src/common/tracked.rs]
tech-stack:
  added: []
  patterns: ["materialize-then-write ordering", "salvage/retry/rollback recovery"]
key-files:
  created: []
  modified:
    - src/owl/echo_commune.rs
    - src/common/tracked.rs
decisions:
  - "Reorder, don't band-aid: every project-write site materializes the worktree FIRST."
  - "Salvage path is SCOPED — fires only on second-attempt `already exists` + missing .git + non-empty dir. Healthy installs hit the fast-path long before this code runs."
  - "Recursive-tree restore is out of scope — the bug only ever produced flat `.md` files at the top level."
  - "Rollback on salvage failure: rename salvage dir back to original name so install isn't left half-broken."
metrics:
  duration: ~22 min
  completed: 2026-05-27
  tasks: 3
  commits: 2 code + 1 docs (orchestrator)
  new_tests: 2
---

# Quick Task 260527-s8l: Fix p-* project branches never receiving commits

## One-liner

Reordered `ensure_project_worktree` BEFORE `fs::write` at three project-write sites in `echo_commune.rs`, plus added a defense-in-depth salvage path to `tracked::ensure_worktree` that self-heals pre-populated dirs without operator action.

## Root cause

From `.planning/debug/p-branches-not-pushed-origin.md` Resolution section:

> `route_two_slice` (`src/owl/echo_commune.rs:780-822`) writes the project `.md` file into `psyches/tracked/projects/<project>/` BEFORE calling `commit_project_payload`. This pre-population causes the subsequent `git worktree add` (in `ensure_worktree`, `src/common/tracked.rs:418-468`) to fail with `'...' already exists` on BOTH the primary and fallback attempts. The worktree never materializes, the commit never fires, and the project branch stays pinned at the seed-init SHA. The error is swallowed by route_two_slice with a `(payload on disk)` warning so the failure is silent in normal operation.

Confirmed empirically by the new regression test on unmodified production code:

```
route_two_slice: commit_project_payload for claude_skill_owl/... failed: worktree add failed:
Preparing worktree (checking out 'p-claude_skill_owl')
fatal: '../projects/claude_skill_owl' already exists
 (payload on disk)
```

Cross-machine project-context sync (the entire reason `spt-agent-storage` exists for projects) has never worked since project worktrees were introduced.

## Fix surface

Three production project-write sites in `src/owl/echo_commune.rs`, plus one defense-in-depth helper in `src/common/tracked.rs`:

| Site | Function | Lines (orig) | Change |
|------|----------|--------------|--------|
| 1 | `route_two_slice_outcome` project arm | ~501-526 | `ensure_project_worktree` first; on Err return `SliceWriteState::IoError`; on Ok use returned PathBuf as `proj_dir`. Dead `fs::create_dir_all` removed. |
| 2 | `route_two_slice_with_precedence` project arm | ~588-612 | Same reorder; preserved `write_with_precedence` + `Suppressed`/`IoError` branches. |
| 3 | `route_project_slot` (called from `route_two_slice` live-error branch + main path) | ~792-800 | `ensure_project_worktree` first; on Err `eprintln!` + soft-fail `Ok(())` (preserves PROJECT.md commit-posture). |
| 4 | `tracked::ensure_worktree` salvage path | new `try_salvage_worktree` helper | When second-attempt `worktree add` fails with `already exists` AND `wt.exists()` AND `!wt.join(".git").exists()`: rename aside → retry add → restore top-level entries → remove empty salvage dir. Rollback on retry failure. |

Verified NON-bugs (planner-flagged, intentionally NOT edited):
- `src/live/signoff.rs` — production signoff project-writes delegate to `route_two_slice_with_precedence` (Site 2 above), so the fix inherits transitively.
- `src/live/context.rs` — only test helpers and a read-path. No production project-write site exists here.

## Regression tests

1. `owl::echo_commune::tests::route_two_slice_project_worktree_materializes_dotgit_marker`
   - Calls `route_two_slice` with `<live-context>...</live-context><project-context>...</project-context>`.
   - Asserts: `wt/.git` exists, `wt/<id>.md` exists with the body marker, `git log p-{project_name}` shows >=2 commits (seed init + project payload).
   - **RED gate confirmed** against pre-fix code: panicked on missing `.git` marker with the exact `(payload on disk)` warning visible in stderr.

2. `common::tracked::tests::ensure_project_worktree_salvages_pre_populated_dir`
   - Pre-creates `projects/salvagetest/stray.md` (no `.git`), then calls `ensure_project_worktree("salvagetest")`.
   - Asserts: `.git` materialized; `stray.md` restored with `STRAY CONTENT MARKER` body intact; no `*.salvage-*` sibling left under `projects/`.
   - **RED gate confirmed** against pre-fix code: `Err(WorktreeFailed)` with `'../projects/salvagetest' already exists`.

## Commits

| SHA | Subject |
|-----|---------|
| `4866fb9` | `fix(s8l): reorder ensure_project_worktree before fs::write in echo_commune` |
| `2eb23eb` | `fix(s8l): add salvage path to ensure_worktree for pre-populated targets` |
| _(pending)_ | `docs(s8l): summarize p-branch commit fix + update STATE` — orchestrator handles in Step 8 |

Both code commits carry the trailer `Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>`.

## Verification

- `cargo build --release` — exit 0.
- `cargo test --lib -- --test-threads=1` — **951 passed; 0 failed; 5 ignored**. Includes both new regression tests.
- Focused: `cargo test --lib -- --test-threads=1 route_two_slice` — 15 passed (all existing + new).
- Focused: `cargo test --lib -- --test-threads=1 ensure_project_worktree` — 2 passed (existing + new salvage).
- Focused: `cargo test --lib -- --test-threads=1 common::tracked` — 72 passed (full tracked.rs module).
- Integration sanity: `cargo test --test handoff_integration` — 19 passed.
- Integration sanity: `cargo test --test hook_chain` — 7 passed.

## Deviations from plan

### Out-of-scope discovery

`cargo test` full-suite (all integration targets) fails to compile `tests/native_wrapper_state_retry.rs` due to a missing `pulse_psyche` field in `WrapperHandoffState` initializers — introduced in commit `3616ed1` (`feat(live): default --period 480 + new --pulse-psyche flag`) AFTER the test was last edited in `844d903`. This is pre-existing breakage unrelated to s8l (wrapper-state evolution, not project-context routing). Logged to `deferred-items.md` in this quick-task dir; a trivial follow-up commit adding `pulse_psyche: None` to the three initializers will clear it.

**Impact on this task:** None. The s8l regression tests and 951 lib tests + relevant integration suites all pass. The pre-existing breakage was confirmed orthogonal by checking the `pulse_psyche` field's introduction history.

No Rule-1/2/3 deviations.

## Out-of-scope reminder

Per planner constraint: the user's local broken install (`psyches/tracked/projects/claude_skill_owl/` currently exists without `.git` and contains stale `.md` files) is **NOT** auto-repaired by this task. The salvage path in `tracked.rs` will repair it automatically on next access (any commune carrying `<project-context>` triggers `ensure_project_worktree` → salvage fires → `.git` materialized → existing `.md` files restored → commit lands → push to origin).

## Deploy reminder

This task **does NOT trigger** `docs/DEPLOY.ps1`. The user runs `powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1 [-Bump patch|minor|major]` after verifying the fix locally. Per project memory: per-phase deploys are discouraged; bundle with the next milestone-level deploy.

## Self-Check: PASSED

- `src/owl/echo_commune.rs` modified — verified by `git log --name-only 4866fb9` showing the file.
- `src/common/tracked.rs` modified — verified by `git log --name-only 2eb23eb` showing the file.
- Commit `4866fb9` — verified by `git log --oneline -5` listing it second-newest after `2eb23eb`.
- Commit `2eb23eb` — verified by `git log --oneline -5` listing it newest.
- `route_two_slice_project_worktree_materializes_dotgit_marker` — passes in `cargo test --lib -- route_two_slice`.
- `ensure_project_worktree_salvages_pre_populated_dir` — passes in `cargo test --lib -- ensure_project_worktree`.
- Trailers — both code commits carry `Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>` (verified via `git log -1 --format=%B`).
