--- type: reference title: live-sync heartbeat description: Auto-commit daemon for machine-written paths in the command-center checkout — the deployment is live, so symlinks make config churn dirty tracked files continuously. tags: - git - automation - systemd - deployment - fleet verified: 2026-10-18 timestamp: 2026-10-18 --- # live-sync heartbeat The command-center checkout IS the deployment, and several tracked files are live state: the harness writes memories and managed-skills, the operator edits `config.yml`/`models.yml` through the `cfg://` surface, and desktop GUIs (Noctalia, niri) rewrite dotfiles through symlinks. Before Refs #11 this machine churn just piled up unpushed — one disk holding the only copy of ~61 auto-learned skills, a settings fix stranded locally, and merge-refusals because the dirty tree collided with incoming work. `scripts/live-sync.sh` is the pressure valve: a heartbeat that commits the machine's own writes on a schedule. Tier law (which paths it may touch) lives in [AGENTS.md](../../AGENTS.md) § Live-sync tiers. ## Contract (black-box) Run = one pass: flock → branch guard (`main` only) → stage allowlisted dirty tiers → commit (hooks armed) → fetch + merge `origin/main` → push → reap stale `wt/*` worktrees. Exit `0` = quiet heartbeat (synced/no-op/reaped); exit `1` = an alert was raised. Pinned by `tests/live-sync.bats`. Hard properties: - **Allowlist only.** Task dirt (`plugins/`, `omp-docs/`, prose) is never staged — merge conflicts with in-flight work become impossible by construction. - **Hooks stay armed.** A secret-gate refusal aborts the commit, leaves changes staged for a human, and alerts. Never `--no-verify`, never force. - **Never fights the remote.** Merge conflict → `merge --abort`, local commit kept, alert. Retries are automatic next tick. - **Branch guard.** On a non-`main` checkout it refuses and alerts — same rule as the doctrine ("never end a session off main"). - **Polite reaper.** `wt/wt-*` worktrees untouched for 7+ days are removed with their `wt/*` branch; dirty or process-occupied worktrees are skipped. ## Alerts `notify()` writes stderr always; pushes to ntfy when the topic file `~/.config/ntfy/live-sync` (mode 600, topic text only) exists, throttled to one push per hour. Test seams: `LIVE_SYNC_REPO`, `LIVE_SYNC_NOTIFY`, `LIVE_SYNC_NTFY_TOPIC_FILE`, `LIVE_SYNC_AGE_DAYS`. ## Install Units ship in `dotfiles/systemd-user/.config/systemd/user/` and propagate with the usual dotfiles chain: ```bash systemctl --user enable --now live-sync.timer # after the unit is linked systemctl --user start live-sync.service # one manual heartbeat journalctl --user -u live-sync.service -n 20 # observe ``` ## Failure modes | Event | Result | |-------|--------| | Harness mid-write | commits a valid snapshot; remainder lands next heartbeat | | Gitea down | commit safe locally; push retries next tick (≤15 min stale window) | | Same file edited on two boxes | real merge conflict → ntfy → human resolves (visible, not silent) | | Feature branch checked out in main checkout | refuses + alerts; protects the never-end-off-main rule | | Secret shape in machine-written content | hook blocks, changes stay staged, alert |