Internals & gotchas
Why it feels instant
A board rebuild costs ~3s: paginated PR comments, the viewed-state GraphQL query, git ls-remote, and the coherence rules. A pin costs 4ms. So mutations never reload the board — the UI updates locally and reverts if the request fails — and those four lookups are memoised per session (server/cache.ts), invalidated by the writes that actually change them. ?fresh=1 forces a real refetch.
board (cold) 3.12s pin 0.010s
board (warm) 0.002s delete 0.006sThings that will bite you if changed carelessly
- Anchors. A review comment must land on a line that exists on the new side of the current diff. GitHub's review POST is atomic — one bad anchor kills the whole batch.
validateAnchorsruns first; unanchorable findings go out as one plain PR comment instead. - Force-push. Findings store the head SHA they were written against. On mismatch the UI blocks submit until you re-triage; verdicts are cleared because they described other code.
- Pending drafts. Submitting creates a review with no
event, which leaves it PENDING — that is the draft. GitHub allows one per user and answers 422 for a second, so findings are added to the existing draft instead. - Sampling honesty. Above 40 files the judge samples. The coverage bar reports read vs judged per directory, so "we looked at everything" is never implied.
Persistence
Two stores, on purpose:
- Postgres (
server/db.ts) holds sessions, files and findings. It's a cache of GitHub plus your findings — safe to reset. On Railway it's the managed Postgres; locally,docker compose up postgres. Tests never touch a real server: they run against pglite, real Postgres compiled to WASM, in-process. events.jsonl(server/events.ts) is the append-only verdict log. It is deliberately not in the DB: it records judgements with no upstream to refetch from, so it lives as its own file (per-line schema version) on the/datavolume. Back it up.
Auth
server/auth.ts is Better Auth with the organization plugin. The API is gated by default — a single onBeforeHandle in server/index.ts requires a GitHub session (or the shared NAVIDIFF_TOKEN) for every route except the SPA shell, /api/auth/*, and the diagnostics endpoints. See Deploying for the access model.