Files
Frank 54f5555470 chore: run live-sync heartbeat hourly
Operator decision at review: 15 min is aggressive for config churn; hourly
keeps the stale-window acceptable (<=1 h) with a fifth of the wakeups.
Timer unit, tier law, and failure-table wording aligned.
2026-10-18 16:28:50 -07:00

3.1 KiB

type, title, description, tags, verified, timestamp
type title description tags verified timestamp
reference live-sync heartbeat 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.
git
automation
systemd
deployment
fleet
2026-10-18 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 § 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:

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 (≤1 h stale window)
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