mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-26 23:51:14 +02:00
3.5 KiB
3.5 KiB
Phase 1 — Git storage core
Build first; every later phase uses this. Umbrella:
00-umbrella-plan.md. No agent wiring here — this phase is a standalone, testable git service + per-workspace lock.
Objective
A GitRepoService that owns one Git repository per workspace on disk and exposes the small set of primitives the rest of the pivot needs, with single-writer safety. Engine = dulwich (pure Python; no system git dependency in the container; real wire protocol so future "bring your own remote" is free).
Locked model
- One repo per workspace, persistent working tree, at
{FILE_STORAGE_LOCAL_PATH parent}/.kb_git/{workspace_id}(see00c-shared-contract.mdC1 for the exact layout + filename rules — reusepath_resolver'ssafe_filename/safe_folder_segment, keep.xml). - Markdown/text only in git; binaries stay in the blob store (Phase-agnostic; see umbrella).
- Single-writer per repo via a Redis lock keyed
kb_git:lock:{workspace_id}— mandatory from v1, not an in-processasyncio.Lock. The backend runs as multiple OS processes (uvicorn workers + Celery workers), so an in-process lock gives false safety; Redis is already deployed.ponytail:v1 ceiling = one Redis lock held per commit; upgrade path = per-workspace write queue. Full rationale:00c-shared-contract.mdC3. - dulwich for the hot path, shell out to
git gc/repack only for periodic maintenance (not in v1).
Work items
- Add
dulwichtosurfsense_backenddeps (pyproject/requirements) — latest release. - New package
app/kb_git/:paths.py—repo_path(workspace_id); ensure-dir helper.service.py—GitRepoService(workspace_id)with async-friendly wrappers (run dulwich in a threadpool viaasyncio.to_thread) over:ensure_repo()— init if absent.read_file(path),write_file(path, content),list_tree(prefix),move(src, dst),remove(path).commit(message, author) -> sha,log(path=None),read_at(sha, path),revert(sha).blob_sha(path)(content-addressed id, consumed by Phase 4).
lock.py—workspace_lock(workspace_id)async context manager over the Redis lock (C3).
- Config flag
KB_GIT_ENABLED(off by default) + repo root setting.
Tests
ensure_repo()is idempotent; second call no-ops.- write →
commit→logshows the commit;read_filereturns content;read_at(prev)returns the pre-commit content. move/removereflected inlist_tree;revert(sha)restores prior state.blob_shais stable for identical content and differs after an edit.- Two concurrent
commits on the same workspace serialize under the Redis lock (no corruption); different workspaces do not block each other. Cover cross-process contention, not just async tasks.
Out of scope
- Agent/backend wiring → Phase 2. Commit-on-turn → Phase 3. Indexing → Phase 4.
- Remotes (push/pull), Git-LFS,
gc/repack scheduling — deferred (umbrella).
Resolved (see 00c-shared-contract.md)
- Lock: Redis lock, from v1 (C3) — deploy topology is multi-process, so in-process locks are out.
- Repo model: persistent working tree per workspace (C1).
- Repo root:
{blob store parent}/.kb_git/{workspace_id}(C1); backup/retention folds into existing blob-store backup.
Open questions
gc/repack scheduling threshold (deferred to a later ops pass, not v1).