Skip to content

fix: cache the embedding index on disk so a cold process start costs bytes, not megabytes (GIT-98) - #31

Merged
1 commit merged into
mainfrom
bugfix/GIT-98-startup-egress
Sep 21, 2026
Merged

1 commit merged into
mainfrom
bugfix/GIT-98-startup-egress

Conversation

@nTEG-dev

Copy link
Copy Markdown
Member

Linear: https://linear.app/nteg-labs/issue/GIT-98
Read the Linear comments first; they contain corrections and known gaps.

⚠️ UNVERIFIED against a real Supabase. Do not merge until the checks below pass.

What

Caches the embedding index on disk so a cold process start transfers a few bytes instead of megabytes.

  • getLearningsFingerprint() makes one request for one row (Prefer: count=exact) and gets the row count plus the newest updated_at, about 60 bytes.
  • vector-disk-cache stores the rows under that fingerprint, keyed by a hash of (store URL, table). Any mismatch, corruption or unknown fingerprint counts as a miss and falls back to the full download. Writes are atomic (temp file + rename) with mode 0600.
  • Only one process downloads on a miss: an O_EXCL lock picks a leader, and the others wait (async, bounded) and then read its cache.
  • cache-flush skips the disk cache and rewrites it.
  • Opt out with GITMEM_VECTOR_DISK_CACHE=0.

Why

In local search mode, every server process downloaded every learning with its 1536-dim embedding at startup and threw it away at exit. Short-lived processes (sub-agents, fan-outs, CI) paid that cost every time, and one customer used up a Supabase egress allowance this way.

Provenance

Written and unit-tested by Brain (Claude chat) in a container that could neither push nor reach a real Supabase. Applied here with git am onto origin/main @ d47a625, with commit authorship left unchanged. File blob hashes after apply match the expected values:

File Blob
src/services/startup.ts 6a127ed ✅
src/services/supabase-client.ts 1e52576 ✅
src/services/vector-disk-cache.ts 91853b9 ✅
tests/unit/services/vector-disk-cache.test.ts b87d965 ✅

Verified locally (unit level only)

  • npx tsc --noEmit -p . passes with no errors.
  • npm run test:unit (run with HOME set to a temp dir, see GIT-92): 75 files / 1228 tests passed.
  • Stack check: applied on top of GIT-97 (the sibling PR), 76 files / 1234 tests passed and tsc clean.

Required before merge (NOT yet run)

  • On a real Pro store, log the bytes received on process start 1 (cold) and on process start 2.

Known caveat: refreshBehavioralScores() bumps updated_at on every session_start. That changes the fingerprint, so expect roughly one full download per session, not a true warm start. Per-row delta sync is the follow-up.

Checklist

  • npm run test:unit passes
  • Real Pro store byte measurement (see above)
  • New tests added (tests/unit/services/vector-disk-cache.test.ts)
  • No breaking changes: needs confirmation on a real store
  • Docs updated: GITMEM_VECTOR_DISK_CACHE opt-out is new and user-facing

No version bump and no publish in this PR.

🤖 Generated with Claude Code

…bytes, not megabytes (GIT-98)

In local search mode every server process downloaded every learning with its
1536-dim embedding at startup and discarded it at exit. Short-lived processes
(sub-agents, fan-outs, CI) paid full price each time; a customer exhausted a
Supabase egress allowance this way.

- getLearningsFingerprint(): one request, one row (Prefer: count=exact) gives
  row count + newest updated_at for ~60 bytes.
- vector-disk-cache: rows persisted under that fingerprint, keyed by a hash of
  (store URL, table). Any mismatch, corruption or unknown fingerprint is a miss
  and falls back to the download. Atomic temp+rename writes, mode 0600.
- Misses are single-flighted across processes with an O_EXCL lock: one leader
  downloads, followers wait (async, bounded) and read its cache.
- cache-flush bypasses the disk cache and rewrites it.
- Opt out with GITMEM_VECTOR_DISK_CACHE=0.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants