feat: background git fetch for connected repos (auto-refresh v1)#213
Open
NovakPAai wants to merge 1 commit into
Open
feat: background git fetch for connected repos (auto-refresh v1)#213NovakPAai wants to merge 1 commit into
NovakPAai wants to merge 1 commit into
Conversation
Adds per-project "Auto-fetch on new chat" toggle, a manual refresh
button on each project card, and a global "Fetch all on codbash start"
toggle. Runs `git fetch --all --prune` in the background — never touches
the working tree, never blocks the HTTP server.
Goal: when a session starts, the LLM sees current origin/<branch> so
new branches start from a fresh base and continuations don't pile on
top of stale state.
Triggers:
- Manual: click the refresh button on a project card.
- New chat: when the per-project toggle is on, /trigger + /wait
(timeoutMs: 2000) fire before /api/launch. Session opens after fetch
finishes, or after 2s with stale refs (graceful degradation).
- Service start: bin/cli.js wires the known-roots gate then calls
repoRefreshManager.initOnStartup() on process.nextTick.
Backend:
- src/repo-refresh.js — singleton via createRepoRefreshManager(opts);
state machine idle <-> fetching <-> error, single-flight per gitRoot,
semaphore max 4, 60s timeout with SIGTERM -> 2s grace -> SIGKILL.
- src/repo-refresh-routes.js — 4 routes under /api/repo-refresh/*
(state, trigger, wait, settings). Body cap 1 MiB, /wait timeoutMs
clamped to 10s, asyncHandler sends 500 on uncaught throws.
- src/atomic.js — atomicWriteJson(path, obj, {mode}) — tmp + fsync +
rename with cleanup on rename failure. Settings written 0o600;
existing disk caches in data.js retrofitted (closes deferred MEDIUM
from PR #212 about non-atomic writes).
- Persistence: ~/.codedash/refresh-settings.json. Corrupt file ->
defaults + warning, file left untouched. Debounced 500ms saves.
- Known-roots gate: getKnownGitRoots() = loadProjects() U session
git_roots, 5s TTL cache. Wired into manager via
setKnownGitRootsProvider(); initOnStartup refuses to fetch paths
not in the known set. Defends against settings-file injection.
- Credential redaction: https://user:token@host stripped from any
captured stderr before storing in lastError (which is exposed via
/state to the browser).
Frontend:
- Per-project card: status badge (Fetching / Updated 2 min ago /
Refresh failed: <msg>), refresh button (28x28 visual, 44x44 hit
area via ::after), Auto-fetch toggle. Header carries global
"Fetch all on codbash start" toggle.
- Polling 2s only while a visible repo is fetching; time-ticker 30s
re-renders relative timestamps. Both skip the swap if focus is
inside the slot (no focus theft).
- Optimistic toggle with rollback + 3s-deduplicated toasts. Input
disabled during inflight POST (closes the toggle race risk).
- new-chat hook shows a spinner on the launch button + aria-busy.
Re-entrancy guard via dataset.rrLaunchInflight.
- A11y: native checkbox (no role="switch"), role="group" wrapper,
role="status" + aria-live="polite" on the outer badge slot so
innerHTML swaps don't tear down the live region, aria-describedby
-> visually-hidden span with full error text for SR/keyboard.
prefers-reduced-motion disables the spinner animation.
Tests: 34 new tests across atomic.js, repo-refresh.js, the routes,
and explicit cases for credential redaction, known-roots gating,
and provider injection.
Deferred (documented LOW from review pass 2, separate follow-ups):
- Retrofit pre-existing readBody to enforce body size cap across
16 callers (out of scope for this feature; chore PR).
- Periodic scheduler (5/10/15/30/60 min intervals).
- Page-refresh bulk trigger from the browser.
- "Behind by N commits" indicator.
- Connection-lost banner when poll fails.
- toast queue/stack (current dedupe is sufficient for v1).
Collaborator
Author
Merge sequence — DO NOT MERGE BEFORE #212Blocked on #212 ( WhyThis PR's
The orphan-GC logic in Conflict status
Plan after #212 merges
|
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a background `git fetch --all --prune` worker for connected repositories so a new chat session starts on fresh `origin/` refs. Three triggers (manual button, new-chat click, service start), per-repo single-flight + semaphore max 4, 60s timeout with SIGTERM→SIGKILL grace, atomic settings persistence.
The working tree is never touched — the user decides when to merge.
Artefacts
What ships
Backend (`src/repo-refresh.js`, `src/repo-refresh-routes.js`, `src/atomic.js`)
Frontend (`src/frontend/app.js`, `src/frontend/styles.css`)
Tests
34 new tests:
All 34 pass. Pre-existing `test/wsl-windows.test.js` failure on macOS is unrelated (#212).
Reviews
Two parallel review passes (code-reviewer, security-reviewer, UX/UI-reviewer with `vercel-web-design-guidelines` + `ux-designer` skills loaded). All CRITICAL/HIGH/MEDIUM findings resolved in-PR. Selected LOWs deferred with documented reason (see commit body).
Deferred to follow-ups
Test plan