# Phase 35: Psyche Sync — Cross-Machine Context Backup via Private gh Repo - Discussion Log

> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.

**Date:** 2026-05-22
**Phase:** 35-psyche-sync-cross-machine-context-backup-via-private-gh-repo
**Areas discussed:** Conflict resolution policy, Sync trigger timing + throttling, Setup UX (auto vs manual), Failure visibility + recovery

---

## Conflict Resolution Policy

### Q1: Two machines commit to same agent branch before either syncs. After pull, what should SPT do?

| Option | Description | Selected |
|--------|-------------|----------|
| Rebase + file-level last-write-wins | `git pull --rebase`; `-X theirs` for conflicts; sessions.log custom merge driver | ✓ |
| Fast-forward only, bail on divergence | `--ff-only`; abandon sync; require manual resolve | |
| Merge with -X ours (local always wins) | `--no-rebase -X ours`; local clobbers remote | |
| You decide | Recommend rebase + LWW | |

**User's choice:** Rebase + file-level LWW (Recommended).
**Notes:** Payload files are full-rewrites, so newest-commit-wins matches user intent.

### Q2: Sessions.log JSONL deduped by (uuid, trigger). Cross-machine append + rebase loses entries. Handle how?

| Option | Description | Selected |
|--------|-------------|----------|
| Custom merge driver `jsonl-union` | Register via .gitattributes; union + re-dedup | |
| Switch sessions.log to append-only | Drop in-place rewrite; dedup on read | |
| Accept the loss | .gitignore sessions.log; per-machine forensics only | ✓ |
| You decide | Recommend merge driver | |

**User's choice:** Accept the loss (free-text).
**Notes:** "In most spt ecosystems, agents are run by a single user. It's highly unlikely that the same live agent will be active on two different machines simultaneously." Single-user assumption is load-bearing.

### Q3: Broken rebase state (corrupt, killed mid-flight). Recovery posture?

| Option | Description | Selected |
|--------|-------------|----------|
| Auto-abort + retry next cycle | `git rebase --abort`; stash; idempotent | ✓ |
| Bail + surface in doctor | Stop syncing; require `$LIVE doctor --repair` | |
| Nuke + reclone from remote | `worktree remove --force` + re-add; loses uncommitted | |
| You decide | Recommend auto-abort | |

**User's choice:** Auto-abort + retry (Recommended).
**Notes:** Matches Phase 24 D-02 soft-fail posture.

---

## Sync Trigger Timing + Throttling

### Q1: Where does sync run? UserPromptSubmit (pull-fresh) or post-commit (push-new) or both?

| Option | Description | Selected |
|--------|-------------|----------|
| Split: pull on UserPromptSubmit, push on commit | Two narrow trigger points | ✓ (refined) |
| Unified: pull-then-push on every commit only | Skip UserPromptSubmit | |
| Unified: pull-then-push on UserPromptSubmit only | Skip post-commit | |
| You decide | Recommend split | |

**User's choice:** Refined — "pull on UserPromptSubmit, pull-then-push on commit." Commit path always pulls first then pushes against fresh remote.
**Notes:** Defensive — commit-time pull guarantees push is against fresh remote even if UserPromptSubmit's async pull was slow.

### Q2: UserPromptSubmit fires every turn. Throttle the pull how?

| Option | Description | Selected |
|--------|-------------|----------|
| Time-budget skip (60s) | mtime check; skip recent | |
| Fire-and-forget async (no skip) | Background spawn every prompt | ✓ |
| Block every prompt | Sync; max freshness, worst latency | |
| You decide | Hybrid async + brief block | |

**User's choice:** Fire-and-forget async.
**Notes:** Simplest; detached subprocess won't block hook return.

### Q3: Sync scope per cycle?

| Option | Description | Selected |
|--------|-------------|----------|
| Active agent + active project only | `a-{self_id}` + `p-{cwd_project}` | ✓ |
| All branches | `git push --all` / `git fetch --all` per cycle | |
| Active + periodic `--all` sweep | Active hot path + boot sweep | |
| You decide | Active hot + boot sweep | |

**User's choice:** Active only (Recommended).

### Q4: Subprocess timeout for gh/git sync ops?

| Option | Description | Selected |
|--------|-------------|----------|
| 10s hard cap | Generous budget; kill on timeout | |
| 30s hard cap | More tolerant of slow networks | ✓ |
| No timeout | Rely on gh defaults | |
| You decide | 10s cap | |

**User's choice:** 30s.
**Notes:** Mobile tether / corp VPN tolerance.

---

## Setup UX (auto vs manual)

### Q1: When does auto path fire AskUserQuestion?

| Option | Description | Selected |
|--------|-------------|----------|
| On first `$LIVE start` after upgrade with gh present | Once acked, never re-prompt | |
| On every `$LIVE start` until acked | Re-prompt until answered | ✓ (refined) |
| Only via `/psyche-sync-setup` skill | Drop SC1 entirely | |
| You decide | Recommend once-per-machine | |

**User's choice:** Refined free-text — "on every `$LIVE start` with gh present, IF user hasn't declined ('No') AND IF not already set up. Options: Yes/No/Remind me later. Setup flow must create the repo using gh CLI, or direct user to repo creation URL if gh auth lacks repo creation scope."
**Notes:** Three-option AskUserQuestion; setup also handles repo creation (not just adoption of existing).

### Q2: On 'Yes', what does SPT do?

| Option | Description | Selected |
|--------|-------------|----------|
| Create repo + wire all worktrees + initial push | `gh repo create` + `remote add` per wt + `push --all` | ✓ |
| Create repo + wire only on first sync per branch | Lazier; deferred per-branch | |
| Create repo, defer wiring to user-driven re-launch | Tell user to restart | |
| You decide | Eager wire-all on accept | |

**User's choice:** Create repo + wire all + initial push (Recommended).

### Q3: Settings.json schema?

| Option | Description | Selected |
|--------|-------------|----------|
| Flat: sync_enabled, sync_acked, sync_declined, remote_url, last_setup_ts | Five top-level booleans + url | |
| Nested `sync` block, same five fields | Namespaced | ✓ (combined) |
| Enum state: `sync_state: unset|enabled|declined|remind-later` | Single state field + url + ts | ✓ (combined) |
| You decide | Enum state | |

**User's choice:** Combined options 2+3 — nested `sync` namespace with enum state. 12h cooldown for Remind-later.
**Notes:** Cleanest of both — namespaced AND state-machine-style.

### Q4: `/psyche-sync-setup` skill scope?

| Option | Description | Selected |
|--------|-------------|----------|
| Skill = unified entry; auto path routes through it | One implementation, two framings | ✓ |
| Skill = recovery-only | Auto path runs its own create+wire | |
| Skill = teach-only | Print instructions; SPT adopts result later | |
| You decide | Unified entry | |

**User's choice:** Unified entry (Recommended).

### Addendum (free-text on Q4 "more questions?")

**User's note:** "If user runs the manual setup skill while gh or git aren't installed, agent should use AskUserQuestion — how do they want to install? 'agent handles it' (with bash/powershell), 'show download links' (user installs), 'cancel'."
**Captured as:** D-13 step 2 in CONTEXT.md.

---

## Failure Visibility + Recovery

### Q1: When sync fails (network, auth, repo deleted, timeout), where does it surface?

| Option | Description | Selected |
|--------|-------------|----------|
| Doctor only + stderr trace | Hot path silent; SPT_TRACE=1 stderr; doctor table | ✓ |
| Doctor + UserPromptSubmit notice after N failures | Inject `<sync_warning>` after 3 fails | |
| Inline stderr always | Single-line warning per failure | |
| You decide | Doctor + rate-limited stderr | |

**User's choice:** Doctor only + stderr trace (Recommended).

### Q2: Auto-disable after sustained failure?

| Option | Description | Selected |
|--------|-------------|----------|
| Soft-disable after N consecutive failures | New `failing` state; require re-setup | |
| Never auto-disable; keep retrying forever | Doctor shows failures only | |
| Throttle backoff (exponential), no full disable | 1m→5m→30m→1h→6h max; self-recovering | ✓ (refined) |
| You decide | Throttle backoff | |

**User's choice:** Refined — "exponential throttle timeout. So no auto-retries, but grow the amount of minimum downtime after each successive failure."
**Notes:** Backoff is a GATE, not a timer. SPT doesn't spontaneously retry; next natural trigger checks the gate.

### Q3: Repo deleted on github.com — behavior?

| Option | Description | Selected |
|--------|-------------|----------|
| Detect 404, transition to `failing`, surface in doctor | Honors user agency; re-setup required | ✓ |
| Auto-recreate the repo on 404 | Self-healing; risks undoing intentional delete | |
| Treat 404 same as transient backoff | No special handling | |
| You decide | 404 short-circuits to `failing` | |

**User's choice:** Detect 404 → `failing` state (Recommended).

### Q4: Auth-related failures (token expired, gh not logged in, scope insufficient)?

| Option | Description | Selected |
|--------|-------------|----------|
| Distinct `auth-failing` state + doctor hint to run `gh auth login` | Separate state, separate remedy | |
| Same `failing` state as repo-deleted | Collapse hard failures | |
| Doctor + auto-retry on next `$LIVE start` | No state tracking | |
| You decide | Distinct `auth-failing` state | |

**User's choice:** Free-text — "`gh` CLI probably isn't used after setup, as `git push` can be used from then on. No special case for auth failure."
**Notes:** Important architectural correction: gh is setup-only; runtime sync uses plain `git push/pull` against the configured remote. Auth failures collapse to ordinary git failures → backoff (D-18). Simplified D-16 in CONTEXT.md.

---

## Claude's Discretion

- AskUserQuestion framing strings on auto-detect vs manual paths.
- Whether `gh auth setup-git` is invoked once globally vs per-worktree.
- Backoff delay schedule tunable defaults.
- Subprocess detachment mechanism specifics (Windows `CREATE_NO_WINDOW` + `DETACHED_PROCESS` vs Unix `setsid`).
- Where in `tracked::commit_agent_payload` the post-commit sync hook fires (exact line).
- Doctor row collapse rules (one summary row when all branches clean).
- Whether to add a `$LIVE list` sync-state indicator per-agent.

## Deferred Ideas

- Custom merge driver for sessions.log union semantics (revisit if multi-user scenarios appear).
- `--all` sweep on `$LIVE start` boot (revisit if dormant-agent staleness becomes painful).
- Smart pull-coalescing on UserPromptSubmit (revisit if rate-limit / network cost becomes observable).
- Per-machine UUID for "last touched by machine X" doctor surface.
- GitHub Enterprise / self-hosted remote support.
- Repo name customization (currently locked to `spt-agent-storage`).
- `$LIVE doctor --repair-sync` shortcut for common recovery cases.
- Multi-account gh support.
- `$LIVE list` sync-state indicator (visual polish).
