feat: in-app git sync (s keybind) #17

Merged
fiatcode merged 9 commits from feat/git-sync into main 2026-06-17 18:11:00 +07:00
Owner

Brings the graph's git sync in-house. Previously sync lived in an external fish function (weft-sync.fish); now it's a first-class, observable action inside the TUI — weft's second deliberate disk-mutating surface alongside internal/edit/.

What it does

Press s (page view) to run commit → pull --rebase → push on the --graph repo, asynchronously:

  • ⟳ syncing…✓ synced, or ✗ <stage> failed — see weft.log on failure.
  • Reindex on pull: when the pull changes the working tree, the graph index rebuilds so pulled pages appear immediately.
  • Clean bail on conflict: weft never edits a conflicted tree — git output is appended to weft.log and you resolve in a shell.
  • Single-flight: a second s mid-sync is a no-op.

Design highlights

  • New internal/sync/ package shells out to system git (no go-git). Returns a Result{Committed, Pulled, Pushed, Stage, Output, Err}.
  • No editor coupling: the in-app editor is modal, so disk is authoritative whenever s is reachable — sync just operates on the working tree.
  • Runner is injected on App so view tests stub it (never shell out to git); sync-package tests use throwaway repos with a local bare remote (no network, never touches the real graph).
  • Documented in the help overlay.

Provenance

  • Spec: docs/superpowers/specs/2026-06-17-git-sync-design.md
  • Plan: docs/superpowers/plans/2026-06-17-git-sync.md
  • Built via subagent-driven development: per-task spec+quality review, a whole-branch final review, and a fix that corrected a latent Result bug (Committed/Pulled were dropped on mid-sequence failure paths).

go vet ./... && go test ./... green (7 packages).

Brings the graph's git sync in-house. Previously sync lived in an external fish function (`weft-sync.fish`); now it's a first-class, observable action inside the TUI — weft's second deliberate disk-mutating surface alongside `internal/edit/`. ## What it does Press `s` (page view) to run **commit → pull --rebase → push** on the `--graph` repo, asynchronously: - `⟳ syncing…` → `✓ synced`, or `✗ <stage> failed — see weft.log` on failure. - **Reindex on pull:** when the pull changes the working tree, the graph index rebuilds so pulled pages appear immediately. - **Clean bail on conflict:** weft never edits a conflicted tree — git output is appended to `weft.log` and you resolve in a shell. - **Single-flight:** a second `s` mid-sync is a no-op. ## Design highlights - New `internal/sync/` package shells out to system `git` (no go-git). Returns a `Result{Committed, Pulled, Pushed, Stage, Output, Err}`. - **No editor coupling:** the in-app editor is modal, so disk is authoritative whenever `s` is reachable — sync just operates on the working tree. - Runner is injected on `App` so view tests stub it (never shell out to git); sync-package tests use throwaway repos with a local bare remote (no network, never touches the real graph). - Documented in the help overlay. ## Provenance - Spec: `docs/superpowers/specs/2026-06-17-git-sync-design.md` - Plan: `docs/superpowers/plans/2026-06-17-git-sync.md` - Built via subagent-driven development: per-task spec+quality review, a whole-branch final review, and a fix that corrected a latent `Result` bug (Committed/Pulled were dropped on mid-sequence failure paths). `go vet ./... && go test ./...` green (7 packages).
- Rewrite drainFor to run tea.Batch children concurrently (mirrors the
  real Bubble Tea runtime); a slow 3-second setHint tick no longer blocks
  the fast syncDoneMsg, cutting TestSyncKeyShowsSyncingHintAndRunsRunner
  from ~3s to <1ms.
- Strengthen TestSyncPulledTriggersReindex: drain for indexLoadedMsg to
  prove buildIndexCmd() is actually wired into the Pulled branch.
- Rename errTest → errSyncTest to avoid a future package-level collision.
fix: preserve Committed/Pulled in sync Result on mid-sequence failure
Some checks failed
CI / vet-and-test (pull_request) Failing after 31s
d4d75d7002
The fail() helper returned a fresh Result{}, dropping Committed/Pulled
on the add/commit/pull failure paths — so a pull conflict after a
successful local commit reported Committed:false. Replace it with a
failWith closure that carries the running res forward, matching the
push-failure path. Add assertions locking both invariants: commit
survives a failed pull; a clean no-advance sync does not report Pulled.
feat: bind git sync to S (shift+s) instead of s
Some checks failed
CI / vet-and-test (pull_request) Failing after 23s
ebed523a3f
Capitalized to match weft's convention where heavier, consequential
actions are shift-keyed (E external editor, R reindex, T todos) and to
make a remote-pushing action a deliberate keypress rather than an easy
mis-key. Updates the keybind, its tests, the help overlay + golden, and
the design spec.
test: give sync test repo a local git identity for CI
All checks were successful
CI / vet-and-test (pull_request) Successful in 25s
f928574f5f
Run()'s production 'git commit' inherits the ambient environment, so the
sync tests passed locally (developer has a global git identity) but failed
in CI, which has no user.name/user.email configured:
  TestRunDirtyTreeCommitsAndPushes / TestRunConflictHaltsAtPull —
  'Author identity unknown'. Set a local identity on the throwaway work
tree after clone so the commit path never depends on global git config.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
fiatcode/weft!17
No description provided.