# Phase 35.3: psyche-sync-setup-ux-pass-error-display-doctor-partial-docs - Context

**Gathered:** 2026-05-29
**Status:** Ready for planning

<domain>
## Phase Boundary

Operator-experience pass on the `psyche-sync` surface. Originally a polish-only
bundle of four defects (Issues 3–6 from `.planning/debug/sync-setup-data-loss.md`):
error formatting, doctor partial-state visibility, exit-code docs, recovery docs.

**Scope expanded during discussion** (user-directed, 2026-05-29) to add a fifth,
**behavioral** fix: non-git host projects never sync their tracked context. This
shifts the phase from "polish-only, no behavior change" to "polish + one sync-
coverage fix." See **D-09** and the scope-shift note in Deferred Ideas.

Sync-engine reconciliation logic (data-loss, per-ref push, bootstrap determinism)
is OUT — that landed in Phase 35.2 and must not be re-touched here.
</domain>

<decisions>
## Implementation Decisions

### Issue 4 — Error display (locked by seed, no discussion)
- **D-01:** One-character flip `{:?}` → `{}` at `src/owl/psyche_sync_setup.rs:70`.
  Both `SyncError` Display (`sync.rs:410-423`, all 6 variants) and the nested
  `GitError` Display (`git.rs:447-456`, all 4 variants) are verified clean and
  human-readable. The flip fully resolves the Debug-repr leak — no Display-impl
  changes needed.

### Issue 5 — Exit-1 documentation (locked by seed, no discussion)
- **D-02:** Add a `**1**` bullet to the "Interpret the exit code" list in
  `plugin/spt/skills/psyche-sync-setup/SKILL.md` (currently lists 0/2/3/4/5 — 5 is
  scope-fallback, 2/3/4 are gh prereqs). Phrasing: generic `accept_flow` failure;
  surface the (now human-readable) error line and offer `$OWL doctor`.

### Issue 6 — Doctor partial-state evidence
- **D-03:** **Probe-only** for this phase. In `doctor.rs::check_sync_status`, when
  `state == Unset`, probe `seed/.git/config` for an `origin` remote AND
  `git ls-remote origin` success → surface a partial-setup row. **No SyncSettings
  schema change** (backward-compat is a hard constraint — shared users).
- **D-04:** The `ls-remote` probe MUST be timeout-bounded (reuse the existing
  timeout-wrapped git helper used by 35.2 reconcile). On timeout/failure of
  `ls-remote`, degrade to "origin configured locally, remote unverified" rather
  than blocking or hanging doctor. Doctor stays fast and non-fatal.
- **D-05 [deferred]:** `accept_flow_attempt_ts` persistence (distinguishing "never attempted"
  from "attempted but settings-write failed") is **deferred** — see Deferred Ideas.
  Probe gives the actionable signal now without schema churn.

### Issue 6 — Doctor row UX
- **D-06:** Severity = **Warn**, not Fail. Partial setup is recoverable; Fail stays
  reserved for `state == Failing`. Don't perturb doctor's overall exit semantics.
- **D-07:** Row message points the operator at the idempotent re-run:
  `"partial setup — origin configured but sync state=Unset (accept_flow likely
  failed at settings write); re-run /spt:psyche-sync-setup to converge."`
  Re-run is safe (D-13 idempotency).

### Issue 3 — Recovery doc home & depth
- **D-08:** Recovery lives **inline in SKILL.md** — no sibling doc / ADR. Single
  source of truth in the skill the operator already invokes; don't fragment.
  Depth = **minimal + pointer**, because 35.2 automated reconciliation makes
  residual manual recovery rare. Document, in order: (a) re-run setup (idempotent),
  (b) `$OWL doctor` to inspect partial state, (c) `--disable` escape hatch + re-setup.
  Mention `seed/` inspection + manual `rebase` as a one-line last-resort pointer
  ONLY — not a full procedure. The Phase 35.2 per-ref dispatcher output
  (`PUSHED`/`RECONCILED`/`DIVERGED`/`PUSH_FAILED`/`PROBE_FAILED` tags) obsoletes the
  old `git update-ref`-against-bare-repo workaround.

### Issue 7 (NEW, user-added) — Non-git projects never sync tracked context
- **D-09:** **In scope for 35.3.** Symptom (user report): on `$LIVE start` for a host
  project that is NOT a git repository, gh/git detection reports a false negative,
  and `psyches/tracked/projects/<project-name>/` and its context files are never
  synced. This is a sync-COVERAGE bug (context silently not backed up), distinct
  from 35.2's data-LOSS bug.
- **D-10 [informational]:** **Root cause is UNCONFIRMED — researcher must investigate before planning.** (RESOLVED by 35.3-RESEARCH.md: confirmed `project_name_from_cwd_path` git-ancestor contract; tracked via D-09/D-11.)
  The tracked project worktree (`projects/{name}/` on branch `p-{name}`) lives in the
  shared `seed/` repo and is git-versioned regardless of the host project, so the
  "no host git repo → no sync" link is not yet explained. Researcher investigation
  pointers (see code_context): project-name resolution fallback, the sync-prompt
  gate, and any live-start path that conflates host-git-repo presence with sync
  eligibility.
- **D-11:** Fix intent: a non-git host project MUST still get its tracked context
  synced (it already gets a versioned worktree). The gh/false-negative detection
  must not gate tracked-context sync on host-repo presence.

### Claude's Discretion
- User said "agent decides all using best judgment" for the four original gray
  areas — D-03 through D-08 are Claude's calls, recorded above. Planner/researcher
  may refine probe mechanics (D-04) and exact row wording (D-07) if research
  surfaces a better approach, but the lean/no-schema-change and Warn-severity
  intents are locked.

### Error-format test scope
- **D-12:** Add a lightweight unit test asserting each `SyncError` (and nested
  `GitError`) Display variant produces no `{`/Debug-struct syntax — a cheap
  regression guard locking the D-01 flip against future reversion. Full snapshot
  fixture not required.
</decisions>

<canonical_refs>
## Canonical References

**Downstream agents MUST read these before planning or implementing.**

### Diagnosis & source
- `.planning/debug/sync-setup-data-loss.md` — root-cause investigation; Issues 3–6
  definitions, fix shapes (lines ~19-31, 71-72, 86-91, 118-121, 183-189).
- `.planning/seeds/psyche-sync-setup-ux-pass-error-display-doctor-partial-docs.md` —
  the seed bundling the four original fixes (file:line targets + deployment notes).
- `.planning/ROADMAP.md` §"Phase 35.3" (lines ~825-831) — goal + requirement IDs
  (SYNC-ERR-DISP-01, SYNC-DOCTOR-PARTIAL-01, SYNC-DOC-EXIT1-01, SYNC-DOC-RECOVERY-01).
  NOTE: `.planning/REQUIREMENTS.md` does not exist — requirement text lives inline
  in ROADMAP only.

### Touchpoints — error display (Issue 4 / D-01)
- `src/owl/psyche_sync_setup.rs:69-72` — the `eprintln!("... {:?}", e)` + `exit(1)`.
- `src/common/sync.rs:400-423` — `SyncError` enum + Display (verified complete).
- `src/common/git.rs:435-456` — `GitError` enum + Display (verified clean).

### Touchpoints — doctor partial-state (Issue 6 / D-03..D-07)
- `src/owl/doctor.rs:1115-1165` — `check_sync_status` collapse rule + state-keyed
  match (the Unset branch at 1144 is where the probe row hangs off).

### Touchpoints — docs (Issues 3 & 5 / D-02, D-08)
- `plugin/spt/skills/psyche-sync-setup/SKILL.md` §"Interpret the exit code"
  (~lines 88-103) and §"To disable" (~117+).

### Touchpoints — non-git sync bug (Issue 7 / D-09..D-11)
- `src/common/git.rs:409-418` — `git_project_basename`: None outside a repo →
  caller falls back to cwd basename.
- `src/owl/sync_prompt.rs:52-66` — `should_emit_sync_prompt`: gates on
  `gh_present` + state only (NOT git). `deliver_sync_prompt_to_perch` ~114-139.
- `src/common/sync.rs:171-188` — `gh_present()` probe (500ms-bounded `gh --version`).
- `src/live/start.rs:422-432` — live-start sync-prompt delivery hook.
- `src/common/tracked.rs:724-730, 1466-1499` — `ensure_project_worktree` /
  project commit_payload path; D-02 "missing-git fallback: degrade silently"
  (lines 16, 73-80) is the likely culprit region to investigate.

### Carried from Phase 35.2
- `.planning/phases/35-psyche-sync-cross-machine-context-backup-via-private-gh-repo/`
  — per-ref dispatcher: `accept_flow` → `(String, Vec<PerRefOutcome>)`; push tags
  `PUSHED`/`RECONCILED`/`DIVERGED`/`PUSH_FAILED`/`PROBE_FAILED` (CLAUDE.md §Conventions).
</canonical_refs>

<code_context>
## Existing Code Insights

### Reusable Assets
- `SyncError` / `GitError` Display impls — already complete and human-readable;
  D-01 just routes through them. No new formatting code.
- 35.2's timeout-wrapped git helpers (used by reconcile/ls-remote probes) — reuse
  for the doctor `ls-remote origin` probe (D-04) instead of a raw subprocess.
- Doctor `DiagResult` / `DiagStatus::{Pass,Warn,Fail}` rows — the partial-state
  row is just another `Warn` entry appended in `check_sync_status`.

### Established Patterns
- Doctor collapse rule (doctor.rs:1115-1129): short-circuits per-branch surface when
  `state != Enabled`. The new partial-state probe must slot in WITHOUT breaking that
  rule — it's an extra row under the global Unset case, not a new code path.
- D-02 "missing-git fallback: degrade silently to non-versioned writes"
  (tracked.rs:16) — this silent-degrade is the suspected mechanism behind Issue 7:
  a non-git host may be tripping a degrade path that also suppresses tracked-project
  sync. Researcher must confirm.
- SKILL.md exit-code interpretation is a documented contract the skill layer reads;
  exit 1 is currently undocumented (Issue 5).

### Integration Points
- `check_sync_status` (doctor) — append partial-state Warn row.
- `psyche_sync_setup.rs:70` — error print.
- SKILL.md — exit-code list + recovery subsection.
- Issue 7 fix point is TBD pending root-cause (likely live-start sync gating and/or
  the tracked-write degrade path).
</code_context>

<specifics>
## Specific Ideas

- Doctor row wording locked verbatim in D-07.
- Recovery doc ordering locked in D-08 (re-run → doctor → --disable → seed/rebase
  last-resort pointer).
- User's Issue 7 framing (quoted): "gh detection fails + reports false negative (on
  i.e. $LIVE start) for any projects that don't have a git repository → the
  `psyches/tracked/projects/<project-name>/` folder and context files within are
  never synced. need to fix, it's a UX bug."
</specifics>

<deferred>
## Deferred Ideas

- **`accept_flow_attempt_ts` in SyncSettings** — persist a pre-push attempt timestamp
  so doctor can distinguish "never attempted" from "attempted but settings-write
  failed" (a stronger signal than the D-03 probe). Deferred from 35.3 to avoid a
  settings-schema change under the shared-users backward-compat constraint. Revisit
  if the probe proves insufficient in practice.
- **Scope-shift note:** Issue 7 (D-09) was added to 35.3 by user direction. It is a
  behavioral fix, unlike the original polish-only bundle. If root-cause investigation
  (D-10) reveals it's larger than a localized gating fix, planner should consider
  splitting it into its own phase (e.g., 35.4) rather than bloating 35.3.

</deferred>

---

*Phase: 35.3-psyche-sync-setup-ux-pass-error-display-doctor-partial-docs*
*Context gathered: 2026-05-29*
