# Seed: psyche-sync-setup second-machine attach destroys remote agent-branch heads

**Source:** https://paste.rs/47EE5 (captured 2026-05-28)
**Severity:** HIGH — data loss + setup failure on legitimate second-machine attach
**Surface:** v1.11.21 (current live)
**Diagnosed in:** `.planning/debug/sync-setup-data-loss.md`

## The Problem

`psyche-sync-setup` on a SECOND machine attaching to an existing private `spt-agent-storage` repo:

1. Overwrites prior-machine writes on agent branches (`a-<agent_id>`) without warning — first-machine commune/signoff history disappears from the remote.
2. Fails to push `main` due to per-machine independent root commits causing non-fast-forward rejection. Setup creates the divergence it then refuses to resolve.

Reporter's framing was "force-push semantics"; actual mechanism is unrelated-history non-FF on `git push --all` (per-ref). No `--force` flag in src/ — the data-loss surface relies entirely on GitHub's server-side non-FF check. Any future config change (branch-protection off, operator-added `--force-with-lease`) silently destroys writes.

## Root Causes (Two Coordinated Defects)

### RC-1: Zero Remote-State Reconciliation Before Seeding Push
`src/common/sync.rs::accept_flow`:
- Step 1 (`sync.rs:489-515`): `gh repo create` treats "already exists" stderr as idempotent fall-through with NO read of existing remote refs.
- Step 5 (`sync.rs:561-568`): single `git -C {seed} push --all origin` — no `--force`, no prior `fetch`, no per-branch ancestor check, no per-ref outcome observability.
- `git push --all` is per-ref: client-only branches succeed while divergent refs reject, leaving the remote in a partially-corrupted state and `accept_flow` exiting nonzero (the undocumented exit-1 path).

### RC-2: Non-Deterministic Empty-Seed Bootstrap
`src/common/tracked.rs::ensure_seed` cold path (lines 198-260):
- `commit-tree {empty_tree} -m "init: tracked seed"` (line 238-250) takes no `-p` parent, no `--date` lock, no `GIT_COMMITTER_DATE` / `GIT_AUTHOR_DATE` env override → fresh wall-clock SHA per machine.
- `ensure_worktree` (line 418-431) roots every agent branch `a-{id}` in the local seed's `main` HEAD. Machine-B agent branches share NO history with machine-A's.
- Result: every shared ref is non-FF by construction across machines.

## The Fix (Two Coordinated Changes)

### Fix 1: Reconcile Before Push (RC-1)
- `accept_flow`: after `gh repo create` resolves, run `git -C {seed} fetch origin`.
- Per-local-branch loop: `git merge-base --is-ancestor` check. Diverged refs → `rebase -X theirs origin/{branch}` (matches `pull_branch` policy elsewhere in `sync.rs`).
- Replace `push --all` with per-branch `push origin {branch}:{branch}` for observable per-ref outcomes.
- Dispatcher reports per-ref rescued vs needs-attention rows so operator sees what reconciled.

### Fix 2: Deterministic Bootstrap (RC-2)
- `ensure_seed` cold path: lock `GIT_COMMITTER_DATE` + `GIT_AUTHOR_DATE` envs (e.g. `1970-01-01T00:00:00Z`) on the `commit-tree` invocation.
- Combined with already-locked identity (`spt-bootstrap <spt@local>`) + empty-tree content → byte-identical `main` SHA across machines.
- Existing installs with wall-clock bootstraps absorbed by Fix 1's reconciliation path as "another divergent main".

## Deployment Notes

- Single coordinated rollout via standard GSD workflow (`/gsd-plan-phase`)
- Atomic commits per fix subsection for independent revertability
- Test surface: cross-machine attach simulation (two separate `SPT_HOME` roots pushing to a single bare-repo fixture)
- Migration: existing wall-clock bootstraps reconcile organically through Fix 1 — no explicit migration command needed

## Related (NOT this bug — separate threads, queued for 35.3 UX pass)
- Issue 3 (recovery doc gap for exit 1) — `git update-ref` workaround obsoleted by per-ref dispatcher output once Fix 1 lands
- Issue 4 (`{:?}` Debug repr leak at `psyche_sync_setup.rs:70`) — Display impl already exists at `sync.rs:400-413`
- Issue 5 (SKILL.md missing exit code 1) — doc-only
- Issue 6 (doctor "sync: not configured" after successful pushes) — `check_sync_status` partial-state blindness at `doctor.rs:1130-1145`

This bug = `accept_flow` push step + `ensure_seed` bootstrap. Distinct surfaces from the UX-pass bundle.
