Repository navigation
fix(directory): a schema-less codegraph.db does not make a project initialized (#1895) - #1913
danusha2345 wants to merge 3 commits into
Conversation
|
Pushed 74ba791: the schema probe now fails open. Once the file passes the size gate and the |
|
Follow-up from a review pass (738b35a): a |
…db-not-initialized @ 738b35a) # Conflicts: # CHANGELOG.md
colbymchenry#1842's quiet-sync test skips the relaunch (CODEGRAPH_WASM_RELAUNCHED=1) that adds --disable-warning=ExperimentalWarning. With colbymchenry#1913 merged the directory probe loads node:sqlite on that path, so the SQLite warning reached stderr and broke the one-line assertion. Pass the same flags.
fca9364 to
66d50ad
Compare
…itialized (colbymchenry#1895) isInitialized() accepted any existing .codegraph/codegraph.db, so an empty or table-less file in an ancestor directory (an interrupted init, a stray touch, a never-populated ~/.codegraph/) captured the upward walk of findNearestCodeGraphRoot / resolveProjectPath / resolveServerRoot for every project beneath it: the real index of a sub-project became unreachable, the sub-project down-scan never ran, and commands either operated on the broken file or failed opening it instead of showing the not-initialized guidance. An initialized project is now one whose db carries the schema: the probe is gated by file size (>= the 100-byte SQLite header), then the header magic, then a read-only node:sqlite open with a single sqlite_master lookup for the `nodes` table, memoized per path + mtime + size so hot callers (the prompt hook, MCP root resolution on every call) never reopen an unchanged db. The handle is closed immediately so the probe never holds the db on Windows; a locked/busy db counts as live. createDirectory() uses the same predicate, so `codegraph init` in the directory with the broken file repairs it in place (the schema is applied with CREATE ... IF NOT EXISTS) instead of refusing with "Already initialized"; the CLI says what it found. Nothing is deleted automatically. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nitialized The schema probe failed CLOSED: a real WAL codegraph.db in a directory the process cannot write (a read-only checkout, a mount, another user's tree) made the read-only node:sqlite open throw — SQLite must create `-shm` to open a WAL database — and isInitialized() reported the project as not initialized, where main's file-exists check had been right. The probe now fails OPEN. Once the size gate and the `SQLite format 3\0` header have passed, the file is a SQLite database; only a SUCCESSFUL sqlite_master query proving the `nodes` table absent may return false. Any open/prepare error (locked, busy, cantopen, readonly, disk I/O) returns true, the pre-existing behaviour for a database we cannot inspect. No error strings are matched. The per path + mtime + size memo is unchanged, and schema-less/empty files still return false, so `codegraph init` still repairs them and never touches a real db. Test: a WAL db copied into a `.codegraph/` made read-only with chmod stays initialized — skipped on win32 and when running as root (root ignores mode bits). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ing a rebuild Review found that a `.codegraph/codegraph.db` holding bytes that are not SQLite at all counted as "schema-less", so `init` printed "rebuilding it" and then failed with SQLite's "file is not a database". Only an empty file or a SQLite database without the codegraph tables can be rebuilt in place; `hasSchemalessDb` now means exactly that, and the new `hasForeignDbFile` names the other case. `init` stops on it with exit 1, says which file it is and to move or delete it, and never touches it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
66d50ad to
9a91c52
Compare
colbymchenry#1842's quiet-sync test skips the relaunch (CODEGRAPH_WASM_RELAUNCHED=1) that adds --disable-warning=ExperimentalWarning. With colbymchenry#1913 merged the directory probe loads node:sqlite on that path, so the SQLite warning reached stderr and broke the one-line assertion. Pass the same flags.
Tree = upstream main + the open PRs that still merge cleanly: colbymchenry#1925, colbymchenry#1957, colbymchenry#1962, colbymchenry#1955, colbymchenry#1965 and the current heads of colbymchenry#1842 and colbymchenry#1913. Upstream has already carried colbymchenry#1969-colbymchenry#1971, colbymchenry#1082 and colbymchenry#1791. Dropped until they are rebased onto upstream's resolver rewrite: colbymchenry#1704, colbymchenry#1921, colbymchenry#1926 and colbymchenry#1935.
|
Thanks @danusha2345 — this landed in #2083 with all your commits and authorship kept, rebased onto current main. I added one commit on top: the header check opened and closed its own descriptor on |
Symptom
isInitialized()treated any existing.codegraph/codegraph.dbas an initialized project. An empty or zero-table file in an ancestor directory — what an interruptedcodegraph init, a straytouch, or a never-populated~/.codegraph/leaves behind — therefore captured the upward walk offindNearestCodeGraphRoot/resolveProjectPath/resolveServerRootfor every project beneath it. The real index of a sub-project became unreachable, the #1606 sub-project down-scan never ran (resolveServerRootshort-circuits on the up-walk hit), and commands either operated on the broken file or failed opening it instead of showing the not-initialized guidance. Reproduction in #1895: an 8 KB zero-table db two directories abovews/made the fully indexedws/subinvisible tocodegraph serve --mcp.The new predicate
An initialized project is one whose db actually carries the codegraph schema.
isInitialized()now probes, cheapest first:codegraph.dbis a regular file of at least 100 bytes (the SQLite header size) — an empty file fails here;SQLite format 3\0magic — one small read;node:sqliteopen with a singlesqlite_masterlookup for thenodestable.Step 3 is memoized per path + mtime + size, so hot callers (the prompt hook, MCP root resolution on every tool call) never reopen an unchanged db; a real index costs one
statafter the first call. The handle is closed immediately so the probe never holds the db (Windows file locking); a db another process holds locked counts as live, not broken.Every caller goes through the one predicate — CLI
resolveProjectPath,findNearestCodeGraphRoot,findIndexedSubprojectRoots,resolveServerRoot(MCP session/engine/roots),planFrontload(prompt hook),ToolHandler'sprojectPathresolution — so the behavior is consistent everywhere. With the ancestor no longer captured,resolveServerRootfalls through to the down-scan and adopts the indexed sub-project as before.createDirectory()uses the same predicate, socodegraph initin the directory holding the broken file repairs it in place (the schema is applied withCREATE ... IF NOT EXISTS) instead of refusing with "Already initialized"; the CLI warns about what it found. Nothing is deleted automatically.Verification
New
__tests__/schemaless-db-not-initialized.test.ts(10 tests):isInitialized/findNearestCodeGraphRoot/resolveServerRoot/planFrontloadreject an empty, a zero-table and a garbagecodegraph.db;codegraph statusandcodegraph explorerun from the child resolve the child;isError;codegraph initin the directory with the broken file succeeds and the db ends up with thenodestable.__tests__/frontload-hook.test.tsfaked an indexed project with an emptycodegraph.db; its helper now creates a real one. Also run:cli-index-explicit-path,mcp-unindexed,mcp-roots,frontload-hook,index-command,security— all green;tsc --noEmitexit 0.Fixes #1895
🤖 Generated with Claude Code