Repository navigation
feat(codegraph-adapter): one in-process read-only read session (#574 S1) - #589
Conversation
9792563 to
4cc2537
Compare
Codex Review Summary: ❌ FailedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
repo-harness acceptance claim |
Ancienttwo
left a comment
There was a problem hiding this comment.
repo-harness acceptance review
Round: 1
Subject: 35795ac
Tier: high
Verdict: FAIL
Reviewer: codex (gpt-6.1-terra)
Model relation: different-family
Cross-family review (Codex CLI, read-only) of S1 at 35795ac against 0983ae9. The read session uses the in-process read-only handle, the lockfile diff changes only the agreed bin entry, and the package boundary audit passes. Two P1 findings and one P2.
NITs
None.
Findings outside the diff
- P2 F3 at PR #589 body:53: Changed and removed assertions are not justified item by item; the missing-platform-bundle/no-download assertion was removed and its replacement tests a different behavior. -> Document each changed or removed assertion with its retained or replacement coverage, including the missing-provider behavior.
|
repo-harness acceptance record
Model relation: different-family Findings:
|
|
repo-harness acceptance claim |
Ancienttwo
left a comment
There was a problem hiding this comment.
repo-harness acceptance review
Round: 1
Subject: 35795ac
Tier: high
Verdict: FAIL
Reviewer: pi (claude-opus-5-5)
Model relation: same-family
Fresh same-family review (Pi, Opus 5.5) of S1 at 35795ac. The reviewer returned PASS with one P2. A P2 blocks under the fix-loop rule, so the orchestrator records the round as FAIL. One read path, failure codes, handle lifetime, 1a/1b/darwin module and the release bundle all verified, including a fresh frozen install and a bundle build.
NITs
- F2: index.ts:1686 the gate reads the compatibility tier, which never adds pending-source-changes, so the issues.every(pending-source-changes) branch is dead; remove it.
- F3: read-session.ts:204-207 a missing codegraph.db maps to not-initialized against the design table's index-missing, undeclared in the PR body; a dangling .codegraph symlink returns index-missing because existsSync runs before lstat.
- F4: read-session.ts:208 the build lock is checked once before the open; a sync that starts afterwards can tear compatibility() and getFiles(). Re-check after the reads and cover a live writer in the Node integration script.
- F5: index.ts:1925 loadCapabilityCodeGraphProjectionInputs runs Bun.gc(true) on win32 even when no selector index was opened; guard it.
- F6: cli/bin/bun-darwin-sqlite.ts:22,41 the CI stderr diagnostic line from the probe is still present; the exists seam and 1a have no test.
- F7: projection-codegraph-status.test.ts removed cases (packaged shim with no platform bundle, CODEGRAPH_NO_DOWNLOAD, worktree-mismatch) are not listed in the PR body.
- F8: PR body test budget counts are slightly off: product +575/-136 and tests +556/-289 by numstat.
- F9: The SDK's validateDirectory rewrites .codegraph/.gitignore on a readOnly openSync when it is missing or an old default, outside ADR-0058 Decision 4; provider behavior to record on #574 or amend in S6.
|
repo-harness acceptance record
Model relation: same-family Findings:
|
|
repo-harness acceptance claim |
Ancienttwo
left a comment
There was a problem hiding this comment.
repo-harness acceptance review
Round: 2
Subject: f2a38df
Tier: high
Verdict: FAIL
Reviewer: codex (gpt-6.1-terra)
Model relation: different-family
Round-2 delta review. All three round-1 findings are resolved: a missing database maps to index-missing, the G4 lock and revision checks are in, and the PR body justifies the changed assertions. The launcher change keeps its assertions. Two failure-classification defects remain on the new paths.
NITs
None.
|
repo-harness acceptance record
Model relation: different-family Findings:
|
|
repo-harness acceptance claim |
Ancienttwo
left a comment
There was a problem hiding this comment.
repo-harness acceptance review
Round: 2
Subject: f2a38df
Tier: high
Verdict: PASS
Reviewer: pi (claude-opus-5-5)
Model relation: same-family
Round-2 review by a fresh same-family context (Pi, Opus 5.5). Round-1 F1 to F3 are resolved. Re-verified: frozen install, tsc clean, 165 pass across the adapter, not-ready, doctor, debt and protocol suites, 17 pass for flow-selector-check plus ref-impact, and test:codegraph-selector-node under Node 24. P3 notes only.
NITs
- R1: projection-codegraph-status.test.ts:181: with readFails the fake's getIndexRevision throws at open, so the 'read fails after the open' test exercises the open path; no test reaches the inspect-level catch after a good open.
- R2: index.ts:1662: when moving is true and the bundle version is not the pin, the refusal carries install-compatible-codegraph with retryable true; retrying cannot fix a version mismatch.
- R3: read-session.ts:212-219: a 0-byte or schema-less codegraph.db now gives index-open-failed where it gave not-initialized; the PR body does not mention it.
- R4: bun-darwin-sqlite.ts:22 and main.ts:3454: selectDarwinSqliteLibrary and the Bun-only execArgv forwarding have no unit test.
- R5: No live-writer or live-WAL case; the SDK open can rewrite .codegraph/.gitignore outside ADR-0058 Decision 4. Record both for S6.
|
repo-harness acceptance record
Model relation: same-family Findings:
|
Draft for the CI probe: readiness reads the index through openCodeGraphReadSession; Bun on darwin loads a read-only-capable SQLite through packages/surfaces/cli/bin/bun-darwin-sqlite.ts (test preload and dev bin/archctx). Refs #574
…ntime fixes (#574 S1) - read-session tests: one per branch, a real index without a holder, the git spy, and the provider bump guard (ADR-0058 Decision 7). - freshness counts an indexed file gone from disk as removed; a deleted tracked file is not a new source. - close() runs Bun.gc(true) under Bun on Windows only (cached statements hold the database file until GC). - main.ts forwards process.execArgv to the daemon only under Bun. - codegraph-selector-node-integration exercises the session under Node. Refs #574
…e readiness reader (#574 S1) - A .codegraph without codegraph.db is index-missing; lstat runs first, so a dangling .codegraph link is index-open-failed. - Design G4: after the reads, the session re-checks the writer lock and the revision taken at open; a moved index is not-ready and retryable. - withExistingCodeGraphSnapshot reads readiness through the session; the CLI probe and CodeGraphSnapshotOptions are gone. - Dead pending branch in the gate removed; selector GC guarded; probe stderr line removed. - Protocol fixture cleanup retries a transient Windows hold after GC. Refs #574
… 1.4.4 (#574 S1) Bun 1.4.4 releases the database files on close(): measured with lsof, 0 files stay open after close() (Bun 1.4.3 kept 5-8 until GC), and the protocol fixtures hold no file under root after stop() without any GC. The read session, selector index and symbol reader close as on main; the protocol fixture keeps only rmSync retries for a transient Windows hold outside the process. Refs #574
f2a38df to
9db4488
Compare
|
repo-harness acceptance claim |
Ancienttwo
left a comment
There was a problem hiding this comment.
repo-harness acceptance review
Round: 3
Subject: 9db4488
Tier: high
Verdict: FAIL
Reviewer: codex (gpt-6.1-terra)
Model relation: different-family
Round-3 delta review against the clean merge of f2a38df with 60f2968. R2-1 is resolved (the snapshot keeps index-open-failed). R2-2 is resolved for lstat(.codegraph), but the following database existence check still collapses access errors into index-missing. The changed handle paths close explicitly, including when the initial revision read fails; Windows CI on this head decides the GC removal.
NITs
None.
|
repo-harness acceptance record
Model relation: different-family Findings:
|
|
repo-harness acceptance claim |
Ancienttwo
left a comment
There was a problem hiding this comment.
repo-harness acceptance review
Round: 3
Subject: 9db4488
Tier: high
Verdict: PASS
Reviewer: pi (claude-opus-5-5)
Model relation: same-family
Round-3 review (Pi, Opus 5.5) with Bun 1.4.4 on a git archive copy of 9db4488: tsc clean; 184 pass across the adapter, not-ready, doctor, debt, protocol, flow-selector-check and ref-impact suites; test:codegraph-selector-node verified under Node 24. Round-2 notes resolved, no handle references collectReleasedCodeGraphHandles, the darwin module is still needed and fails closed without it, and the PR changes no byte of bun.lock. P3 notes only.
NITs
- F1: The PR body still says lstat(.codegraph) failure gives index-missing; the code maps only ENOENT/ENOTDIR there, and no test covers the non-ENOENT branch.
- F2: Carried over: selectDarwinSqliteLibrary and 1a have no unit test, and there is no live-writer case.
- F3: index.ts:1263-1264 the comment saying Node owns all SDK/SQLite handles is stale now that snapshot readiness opens in-process under Bun, and the snapshot's rmSync(temporaryRoot) has no retries; Windows CI on this head decides it.
- F4: Dropping 2a and the 1b lock edit departs from the recorded #574 decisions; the reasons are only in the PR body.
|
repo-harness acceptance record
Model relation: same-family Findings:
|
|
repo-harness acceptance claim |
Ancienttwo
left a comment
There was a problem hiding this comment.
repo-harness acceptance review
Round: 4
Subject: 8649579
Tier: high
Verdict: PASS
Reviewer: codex (gpt-6.1-terra)
Model relation: different-family
Round-4 delta review (9db4488..8649579). R3-1 is resolved: the database check distinguishes absence (ENOENT/ENOTDIR) from access and I/O failures, which map to index-open-failed. The new permission test would fail on 9db4488 for a non-root POSIX process and skips on win32 and under root. No new defect.
NITs
None.
|
repo-harness acceptance record
Model relation: different-family Findings:
|
|
repo-harness acceptance claim |
Ancienttwo
left a comment
There was a problem hiding this comment.
repo-harness acceptance review
Round: 4
Subject: 8649579
Tier: high
Verdict: PASS
Reviewer: pi (claude-opus-5-5)
Model relation: same-family
Round-4 review (Pi, Opus 5.5) with Bun 1.4.4 on a git archive copy of 8649579: tsc clean; 137 pass across codegraph-adapter, code-facts-not-ready, doctor-code-facts and flow-selector-check. Round-3 notes resolved. P3 notes only.
NITs
- G1: index.ts:1232 the private snapshot still pre-checks existsSync(.codegraph/codegraph.db), so an unreadable .codegraph refuses there as index-missing; it fails closed and S4 deletes this path.
- G2: Carried over: darwin module and 1a lack unit tests, no live-writer case, and the #574 record should note the 2a/1b supersession and the S6 follow-ups.
|
repo-harness acceptance record
Model relation: same-family Findings:
|
|
Joint decision (orchestrator + advisor), 2026-11-14: merge #589 (#574 S1) at 8649579 onto main 60f2968.
Seat: orchestrator t7 |
…1b) (#596) Refs #574 · Tier Medium (one GPT review) · slice S1b (joint decision "parallelizing the remaining issues", item 3) Move-only split of `packages/local-runtime/codegraph-adapter/src/index.ts` (2226 lines) by responsibility. Base: `main` 449b61b (S1, #589). ## Goal Give S2+S3, S4 and S5 their own modules so they can run in parallel, one writer per file. Behavior does not change. ## Change: what moved where Every top-level declaration is copied verbatim, in its original relative order. The only edits are moving declarations, adding imports and exports between the new modules, adding `export ` to 17 moved declarations that another module now imports, and dropping imports that became unused. No logic edit, rename, signature change or reorder inside a body. | Module | Contents | |---|---| | `cli-provider.ts` (397) | telemetry env (`CODEGRAPH_TELEMETRY_*`, `disableCodeGraphTelemetryByDefault`, `MutableEnv`), `DEFAULT_CODEGRAPH_BINARY`, `requireFromAdapter`, CLI output/timeout limits, `CodeGraphProvider`, `CodeGraphCliProvider`, `CodeGraphCliInvocation`, `packagedCodeGraphCliInvocation`, `codeGraphCliInvocation`, `resolveExecutable`, `isExecutablePath`, `resolvePackagedCodeGraphShim`, `packagedCodeGraphRuntimeDigest`, `isNodeRuntimeScript`, `readFilePrefix`, `runCodeGraphCli`, the output parsers (`CodeGraphCliNode`, `syntheticSymbolId`, `normalizeCliNode`, `scopedTaskQuery`, `parseNodeTrail`, `trailEntries`, `uniqueSymbols`, `uniqueEdges`, `parseExploreSymbols`), `digestFile` | | `repository-code-facts.ts` (224) | `RepositoryCodeFactsPendingChangesV1`, `RepositoryCodeFactsV1`, `REPOSITORY_CODE_FACTS_SCHEMA` … `assertRepositoryCodeFactsReady`, `codeGraphIndexAvailable` | | `projection-handshake.ts` (465) | the projection section comment, `CodeGraphProjectionStatusV1`, `CodeGraphProjectionHandshakeV1`, `PreparedProjectionCodeFacts`, `PreparedArchitectureDocumentationProjectionSnapshot`, `prepareArchitectureDocumentationProjectionSnapshot` … `runProjectionCodeGraph` (code facts, status issues, status digest) | | `snapshot.ts` (298) | `codeGraphSnapshotRuntime`, `codeGraphSourceScanner`, `CODEGRAPH_SNAPSHOT_*`, `codeGraphSnapshotQueries`, `withExistingCodeGraphSnapshot`, `assertSnapshotGitReadSafety`, `pinSnapshotHooks`, `mirrorCodeGraphInputs` | | `import-population.ts` (281) | `CODEGRAPH_IMPORT_NODE_READ_LIMIT`, `CODEGRAPH_IMPORT_NODE_OUTPUT_BYTES`, `SOURCE_EXTENSIONS`, `importQuery`, `importEdgesFromQueryNodes`, `resolvedImportPairs`, `resolveImportTarget`, `importTargetCandidates`, `fileSymbolId`, `normalizeChangedPaths`, `uniqueStrings`, `ProjectionImportPopulationV1`, `RepositoryImportPair(s)V1`, `repositoryImportPairs`, `importPairsOf`, `statusImportNodeCount`, `codeGraphImportPopulation`, `capabilityImportGraphs` | | `selector-index.ts` (372) | `codeGraphSdk`, `CapabilityCodeGraphProjectionInputs`, `CodeGraphSelectorIndex`, `CODEGRAPH_NO_DOWNLOAD_ENV`, the symbol-reader types, `NON_DECLARATION_KINDS`, `openCodeGraphSymbolReader`, `indexedDeclaration`, `memoizedSourceDeclarations`, `loadCapabilityCodeGraphProjectionInputs`, `RUNTIME_BUILTIN_CALL_NAMES`, `exactSelectorEvidence`, `indexedSymbolsAt`, `edgeSites`, `isFalseBuiltinCall`, `normalizedIndexPath` | | `index.ts` (309) | `CodeGraphAdapter`, `LandscapeContextInput`, `MultiRepoCodeGraphAdapter`, `assertNoCodeGraphInternalPathAccess`, `architectureDeltaTask`, and explicit re-exports of the same 64 public names | **Boundary adjustments (one shared symbol each, placed with its main user):** - `digestFile` → `cli-provider.ts`. `packagedCodeGraphRuntimeDigest` calls it 4 times and `prepareProjectionCodeFacts` once. - `normalizeChangedPaths` and `uniqueStrings` → `import-population.ts` (`importQuery` uses both), not `index.ts`. That way no module imports the `index.ts` barrel, which re-exports all of them. `CodeGraphAdapter` and `architectureDeltaTask` import them from there. - `CodeGraphProvider` → `cli-provider.ts`, next to its implementation, for the same reason. - Small helpers used only by one module go with it: `codeGraphSdk` → selector-index, `codeGraphSourceScanner` → snapshot, `CODEGRAPH_NO_DOWNLOAD_ENV` → selector-index (only `loadCapabilityCodeGraphProjectionInputs` reads it), and `SOURCE_EXTENSIONS` and `fileSymbolId` → import-population. `read-session.ts`, `snapshot-query.ts`, `child-env.ts` and `ref-impact-facts.ts` are not touched. No test file is changed. Every test imports `../src/index` or the package entry. Test budget: **0 test lines**. ## Evidence ### Move-only proof The script below is run from the repository root as `node move-only-proof.mjs origin/main`. It is not committed, because the write scope is `src/` only. It checks two things: 1. **Diff check.** Every line removed from `index.ts` appears verbatim in a changed `src/` file. A moved declaration may gain a leading `export `. No other line is added. Import statements, `export { } from` statements and blank lines are ignored. It also asserts that `index.ts` is the only modified file and every other changed file is new. 2. **Block check.** Each of the 118 top-level blocks of the old `index.ts` (one declaration with its leading comment) appears as one contiguous run in exactly one file. So no body was edited or reordered. A tamper test changed `timeout: 5_000` to `6_000` in one moved function, and the script reports `FAIL` on both checks. ``` base origin/main (449b61b) changed files: A cli-provider.ts, A import-population.ts, M index.ts, A projection-handshake.ts, A repository-code-facts.ts, A selector-index.ts, A snapshot.ts diff: 1981 lines removed from index.ts, 2101 lines added under src/ ignored: 40 removed import/export lines, 158 added import/export lines, 232 blank lines moved verbatim: 1826 of 1826 removed code lines 'export ' added to 17 moved declarations: cli-provider.ts:16 export const DEFAULT_CODEGRAPH_BINARY = "codegraph"; cli-provider.ts:20 export const CODEGRAPH_OUTPUT_MAX_BYTES = 32 * 1024 * 1024; cli-provider.ts:21 export const CODEGRAPH_QUERY_TIMEOUT_MS = 30_000; cli-provider.ts:190 export function isExecutablePath(path: string): boolean { cli-provider.ts:221 export function packagedCodeGraphRuntimeDigest(): string { cli-provider.ts:280 export interface CodeGraphCliNode { cli-provider.ts:358 export function digestFile(path: string): string { cli-provider.ts:366 export function runCodeGraphCli(binary: string, workspaceRoot: string, a import-population.ts:10 export function importQuery(changedPaths: string[]): string | undefined import-population.ts:16 export function importEdgesFromQueryNodes(gitVisibleFiles: ReadonlySet<s import-population.ts:44 export function resolveImportTarget(gitVisibleFiles: ReadonlySet<string> import-population.ts:63 export function normalizeChangedPaths(paths: string[]): string[] { import-population.ts:70 export function uniqueStrings(items: string[]): string[] { import-population.ts:199 export function statusImportNodeCount(status: CodeGraphProjectionStatusV import-population.ts:242 export function capabilityImportGraphs( projection-handshake.ts:321 export function readProjectionCodeGraphStatus( selector-index.ts:370 export function normalizedIndexPath(path: string): string { removed lines not found in any new file: 0 added lines that are not moved lines: 0 blocks: 118 top-level blocks of the old index.ts, each found contiguous in exactly one file: true cli-provider.ts: 30 import-population.ts: 19 index.ts: 5 projection-handshake.ts: 21 repository-code-facts.ts: 14 selector-index.ts: 20 snapshot.ts: 9 MOVE-ONLY: PASS ``` <details><summary>move-only-proof.mjs</summary> ```js #!/usr/bin/env node // Move-only proof for the #574 S1b split of codegraph-adapter/src/index.ts. // Usage: node move-only-proof.mjs <base-ref> (run from the repository root) // 1. Diff check: every line removed from index.ts is added verbatim in src/ (a moved declaration // may gain a leading `export `), and no other line is added. Import statements, `export { } from` // re-export statements and blank lines are ignored. // 2. Block check: every top-level block of the old index.ts (a declaration with its leading comment) // appears as one contiguous run in exactly one file under src/, so no body was edited or reordered. import { execFileSync } from "node:child_process"; import { readFileSync } from "node:fs"; const base = process.argv[2] ?? "origin/main"; const dir = "packages/local-runtime/codegraph-adapter/src"; const git = (...args) => execFileSync("git", args, { encoding: "utf8", maxBuffer: 64 << 20 }); const oldIndex = git("show", `${base}:${dir}/index.ts`).split("\n"); const changed = git("diff", "--name-status", "--no-renames", base, "--", dir).trim().split("\n").map((line) => line.split("\t")); // Only index.ts may change; every other changed file must be new. read-session.ts, snapshot-query.ts, // child-env.ts and ref-impact-facts.ts stay untouched. for (const [status, path] of changed) { if (!(status === "M" && path === `${dir}/index.ts`) && status !== "A") throw new Error(`unexpected change: ${status} ${path}`); } const files = Object.fromEntries(changed.map(([, path]) => [path, readFileSync(path, "utf8").split("\n")])); /** Line numbers (1-based) that belong to an import statement or an `export { ... } from` statement. */ function importExportLines(lines) { const marked = new Set(); for (let i = 0; i < lines.length; i++) { if (!/^(import\b|export \{|export type \{|export \* from)/.test(lines[i])) continue; let j = i; while (!/;\s*$/.test(lines[j])) j++; if (lines[i].startsWith("export") && !/\bfrom\s+"[^"]+";\s*$/.test(lines[j])) continue; for (let k = i; k <= j; k++) marked.add(k + 1); i = j; } return marked; } // --- 1. Diff check ------------------------------------------------------------------------- const diff = git("diff", "--no-color", "--no-renames", "-U0", base, "--", dir); const removed = []; const added = []; let file, oldLine, newLine; for (const line of diff.split("\n")) { if (line.startsWith("+++ ")) { file = line.slice(4).replace(/^b\//, ""); continue; } if (line.startsWith("--- ")) continue; const hunk = line.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/); if (hunk) { oldLine = Number(hunk[1]); newLine = Number(hunk[2]); continue; } if (line.startsWith("-")) removed.push({ file, n: oldLine++, text: line.slice(1) }); else if (line.startsWith("+")) added.push({ file, n: newLine++, text: line.slice(1) }); } const oldMarks = importExportLines(oldIndex); const newMarks = Object.fromEntries(Object.entries(files).map(([f, lines]) => [f, importExportLines(lines)])); const notes = { removedImportExport: 0, addedImportExport: 0, blank: 0, exportAdded: [] }; const pool = new Map(); for (const r of removed) { if (r.file !== "/dev/null" && !r.file.endsWith("/index.ts")) throw new Error(`unexpected removal in ${r.file}`); if (!r.text.trim()) { notes.blank++; continue; } if (oldMarks.has(r.n)) { notes.removedImportExport++; continue; } pool.set(r.text, (pool.get(r.text) ?? 0) + 1); } const removedCount = [...pool.values()].reduce((a, b) => a + b, 0); const take = (text) => { const count = pool.get(text) ?? 0; if (count === 0) return false; count === 1 ? pool.delete(text) : pool.set(text, count - 1); return true; }; const unexplained = []; for (const a of added) { if (!a.text.trim()) { notes.blank++; continue; } if (newMarks[a.file]?.has(a.n)) { notes.addedImportExport++; continue; } if (take(a.text)) continue; if (a.text.startsWith("export ") && take(a.text.slice("export ".length))) { notes.exportAdded.push(`${a.file.split("/").pop()}:${a.n} ${a.text.slice(0, 72)}`); continue; } unexplained.push(`${a.file}:${a.n}: ${a.text}`); } const missing = [...pool.entries()].map(([text, count]) => `${count}x ${text}`); // --- 2. Block check ------------------------------------------------------------------------ const body = oldIndex.map((text, i) => ({ text, n: i + 1 })).filter(({ n }) => !oldMarks.has(n)); const blocks = []; for (let i = 0; i < body.length; i++) { const { text } = body[i]; // A block starts at column 0 after a blank line, or at a declaration or comment opener that does // not continue a leading comment, so adjacent one-line declarations are separate blocks. const previous = i === 0 ? "" : body[i - 1].text; const startsBlock = text.trim() && /^[^\s})\]]/.test(text) && (!previous.trim() || (/^(export |const |let |function |async function |class |type |interface |\/\*\*|\/\/)/.test(text) && !/^(\/\*\*|\s\*|\/\/)/.test(previous))); if (startsBlock) blocks.push([]); if (blocks.length && (text.trim() || blocks.at(-1).length)) blocks.at(-1).push(body[i].text); } for (const block of blocks) while (block.length && !block.at(-1).trim()) block.pop(); const sameLine = (actual, expected) => actual === expected || actual === `export ${expected}`; const blockFailures = []; const placement = {}; for (const block of blocks) { const hits = []; for (const [f, lines] of Object.entries(files)) { for (let i = 0; i + block.length <= lines.length; i++) { if (block.every((expected, k) => sameLine(lines[i + k], expected))) hits.push(f); } } if (hits.length !== 1) blockFailures.push(`${hits.length} placements: ${block[0]}`); else placement[hits[0].split("/").pop()] = (placement[hits[0].split("/").pop()] ?? 0) + 1; } console.log(`base ${base} (${git("rev-parse", "--short", base).trim()})`); console.log(`changed files: ${changed.map(([status, path]) => `${status} ${path.split("/").pop()}`).join(", ")}`); console.log(`diff: ${removed.length} lines removed from index.ts, ${added.length} lines added under src/`); console.log(` ignored: ${notes.removedImportExport} removed import/export lines, ${notes.addedImportExport} added import/export lines, ${notes.blank} blank lines`); console.log(` moved verbatim: ${removedCount - missing.length} of ${removedCount} removed code lines`); console.log(` 'export ' added to ${notes.exportAdded.length} moved declarations:`); for (const note of notes.exportAdded) console.log(` ${note}`); console.log(` removed lines not found in any new file: ${missing.length}`); for (const m of missing) console.log(` ${m}`); console.log(` added lines that are not moved lines: ${unexplained.length}`); for (const u of unexplained) console.log(` ${u}`); console.log(`blocks: ${blocks.length} top-level blocks of the old index.ts, each found contiguous in exactly one file: ${blockFailures.length === 0}`); for (const [f, count] of Object.entries(placement).sort()) console.log(` ${f}: ${count}`); for (const failure of blockFailures) console.log(` FAIL ${failure}`); const ok = missing.length === 0 && unexplained.length === 0 && blockFailures.length === 0; console.log(ok ? "MOVE-ONLY: PASS" : "MOVE-ONLY: FAIL"); process.exit(ok ? 0 : 1); ``` </details> `git diff --color-moved=zebra --stat origin/main`: ``` .../codegraph-adapter/src/cli-provider.ts | 397 ++++ .../codegraph-adapter/src/import-population.ts | 281 +++ .../local-runtime/codegraph-adapter/src/index.ts | 2045 +------------------- .../codegraph-adapter/src/projection-handshake.ts | 465 +++++ .../codegraph-adapter/src/repository-code-facts.ts | 224 +++ .../codegraph-adapter/src/selector-index.ts | 372 ++++ .../codegraph-adapter/src/snapshot.ts | 298 +++ 7 files changed, 2101 insertions(+), 1981 deletions(-) ``` With `--color-moved=zebra`, 1960 added and 1947 removed lines are coloured as moved. The lines that are not are imports and re-exports, blank lines, the 17 `export ` prefixes, and the 3-line `uniqueStrings` body, which is below git's moved-block minimum. ### Public export list is unchanged The TS checker's `getExportsOfModule(index.ts)` gives the same 64 names before (449b61b) and after: `diff exports-before.txt exports-after.txt` is empty. The runtime `Object.keys(await import(index.ts))` also matches: 36 value exports. <details><summary>the 64 exports (name, kind)</summary> ``` CODEGRAPH_IMPORT_NODE_READ_LIMIT value CODEGRAPH_PROVIDER_INTERNAL_EXPORTS value CODEGRAPH_SNAPSHOT_MAX_BYTES value CODEGRAPH_SNAPSHOT_MAX_ENTRIES value CODEGRAPH_TELEMETRY_DISABLED_VALUE value CODEGRAPH_TELEMETRY_ENV value CapabilityCodeGraphProjectionInputs type CodeGraphAdapter value CodeGraphCallee type CodeGraphCliInvocation type CodeGraphCliProvider value CodeGraphIndexCompatibilityV1 type CodeGraphIndexFreshnessV1 type CodeGraphIndexRevisionV1 type CodeGraphIndexedDeclaration type CodeGraphProjectionHandshakeV1 type CodeGraphProjectionStatusV1 type CodeGraphProvider type CodeGraphProviderUnavailableError value CodeGraphReadProvider type CodeGraphReadSession type CodeGraphReadSessionOpenV1 type CodeGraphSelectorIndex type CodeGraphSymbolDeclaration type CodeGraphSymbolReader type CodeGraphSymbolReaderResult type LandscapeContextInput type MultiRepoCodeGraphAdapter value PreparedArchitectureDocumentationProjectionSnapshot type PreparedProjectionCodeFacts type ProjectionCodeFactsUnavailableError value ProjectionCodeGraphStatusIssueCode type ProjectionCodeGraphStatusIssueV1 type ProjectionImportPopulationV1 type REQUIRED_CODEGRAPH_PACKAGE value REQUIRED_CODEGRAPH_VERSION value RUNTIME_BUILTIN_CALL_NAMES value RepositoryCodeFactsInspectionOptions type RepositoryCodeFactsPendingChangesV1 type RepositoryCodeFactsV1 type RepositoryImportPairV1 type RepositoryImportPairsV1 type assertNoCodeGraphInternalPathAccess value assertProjectionCodeFactsAvailable value assertRepositoryCodeFactsReady value codeGraphCliInvocation value codeGraphIndexAvailable value codeGraphSnapshotQueries value codeGraphSnapshotRuntime value disableCodeGraphTelemetryByDefault value inspectRepositoryCodeFacts value loadCapabilityCodeGraphProjectionInputs value memoizedSourceDeclarations value openCodeGraphReadSession value openCodeGraphSymbolReader value packagedCodeGraphCliInvocation value pinnedCodeGraphProvider value prepareArchitectureDocumentationProjectionSnapshot value prepareProjectionCodeFacts value projectionCodeFactsRequired value projectionCodeGraphStatusIssues value repositoryImportPairs value withCodeGraphReadSession value withExistingCodeGraphSnapshot value ``` </details> ### Checks (Bun 1.4.4, macOS arm64) - `bun run typecheck`: pass. `tsc --noEmit --noUnusedLocals` reports nothing in `codegraph-adapter/src`, so no import is left unused. - `bun test packages/local-runtime/codegraph-adapter`: 113 pass, 0 fail (8 files). - `bun test packages/local-runtime/runtime-daemon`: 855 pass, 1 skip, 0 fail (47 files). - `bun test packages/surfaces/cli`: 312 pass, 0 fail (29 files). - `bun test` on `core/application/test/control-loop.test.ts`, `core/context-compiler/test/context-compiler.test.ts`, `tests/projection-prior-committed-applies.test.ts` and `tests/ownership-change-acceptance-recovery.test.ts` (the other direct importers): 25 pass, 0 fail. - `bun run test:codegraph-selector-node`: verified. It bundles `src/index.ts` for Node and checks the selectors, the snapshot and the read session. - `node scripts/package-boundary-audit.mjs` and `node scripts/production-mock-reachability-audit.mjs`: pass. ## Risk - **Import cycles between the new modules.** `cli-provider` ↔ `import-population`, `repository-code-facts` ↔ `projection-handshake`, and `import-population` → `projection-handshake` → `selector-index` → `import-population` are cycles. They follow from the agreed boundaries; in the single file these were all forward references. They are safe because no module reads an imported binding at evaluation time. Every top-level initializer is a literal, a `node:`/`@archcontext/contracts` value or `createRequire(import.meta.url)`. Cross-module references sit only in function bodies, class members and default parameters. The Bun runtime (all suites above) and the Bun-built Node bundle (`test:codegraph-selector-node`) both load them. - `requireFromAdapter = createRequire(import.meta.url)` now lives in `cli-provider.ts`. That file is in the same directory, so package resolution is unchanged. - 17 formerly private helpers are now exported from their module, so the new modules can use them. `index.ts` does not re-export them, so the package's public surface is unchanged. ## Rollback Revert this single commit. No data, schema, contract or test changes.
## Summary Refs #574 **Tier: High** (GPT plus Opus review). S5 of ADR-0058: SDK-backed provider reads. Base: `main` 6efd32a (S1 #589, S1b #596). ## Goal `CodeGraphCliProvider` uses the CodeGraph CLI only to build the index (`sync` under `codeGraphChildEnv`). `buildContext`, `findSymbols` and `getImpactRadius` read through one S1 read session per read (`openCodeGraphReadSession`, read-only, closed after settling the read). No read starts a CodeGraph process, and no read opens the index writable. ## Changes (`codegraph-adapter/src/cli-provider.ts` only) - **Split.** A private `CodeGraphCliIndexBuilder` keeps `sync`. The provider composes it with SDK reads. `runCodeGraphCli` delegates to one shared `runCodeGraphInvocation` and keeps its signature (S2 still uses it). - **Deleted:** every CLI read (`explore`, `query`, `node`, `impact`), `parseExploreSymbols`, `parseNodeTrail`, `trailEntries`, `normalizeCliNode`, `syntheticSymbolId` and the `findSymbols` fallback inside `buildContext`. There is no CLI path, no snapshot path and no mode branch. - **`buildContext` (C6).** The file set comes from `findRelevantContext(query)`: entry-point files first, then node order, capped at `maxSymbols`. The query nodes come from `searchNodes`. Symbols are the non-import query nodes, followed by the index's file nodes for the relevant files (`getNodesInFiles`). Import edges are unchanged: the same scoped `import <paths>` search with `kinds: ["import"]`, resolved by `importEdgesFromQueryNodes`. The digest and evidence bind the relevant-file list where they used to bind the `explore` markdown. The evidence summary is now `Observed by CodeGraph for task: …`. - **`findSymbols`.** `searchNodes(query, { limit, kinds })` in a total order: hand-written files before generated ones (`generatedFilePredicate`, as the CLI ranks them), then score, then file path, start line and id. Equal scores therefore never reorder. - **Impact.** `symbolId` resolves to the node with that id. Otherwise it resolves to every node whose exact qualified name or exact name matches. An unknown symbol gives an empty result, and an ambiguous name gives the union over its definitions. `affectedPaths` lists the unique, sorted files of `getImpactRadius(id, depth)` for each definition. `callers` and `callees` are the direct `calls`/`instantiates` edges of the definitions, with CodeGraph node ids on both ends. - **Failures.** Each read opens one session, and nothing is retried. Provider-load exceptions map to AC_CODE_FACTS_UNAVAILABLE / code-facts-unavailable, matching readiness. A close failure preserves an existing mapped read error; after a successful read it becomes index-open-failed. A throw while the index moved is retryable. - `index-missing` → `AC_CODE_FACTS_UNAVAILABLE`/`index-missing` - open failure → `AC_CODE_FACTS_UNAVAILABLE`/`index-open-failed` - live build lock → `AC_CODE_FACTS_NOT_READY`/`code-facts-not-ready` (`index-incomplete`, retryable) - Any throw inside an SDK call, including a fault-injected write that the read-only handle refuses, is a read failure: `index-open-failed`, with no provider text in it. It is never retried, and never with a writable open. - An index that moved during the read (`unchangedSinceOpen()` is false, design G4) is refused like a build in progress. `index.ts`, `read-session.ts`, `real-index.ts`, `read-provider-fixture.ts` and `failure.ts` are untouched. The class keeps the name `CodeGraphCliProvider` and its constructor `(root, binary, readProvider?)`. A rename would touch `index.ts` (S4) and `runtime-daemon`, so it is left for S6 if wanted. ## C6 before/after (same fixture as the new real-index tests) `buildContext("change the cart total pricing", { maxSymbols: 8, changedPaths: ["src/checkout/cart.ts"] })` on the original five-file fixture (the final real-index fixture also includes the type-reference pair) (`leaf.ts` ← `tax.ts` ← `cart.ts`, plus the two `render` definitions): | | before (main, CLI `explore` + `query`) | after (SDK) | |---|---|---| | symbols | `function cartTotal`, `file cart.ts`, `file leaf.ts`, `file tax.ts` | same four, same ids | | edges | `cart.ts → tax.ts`, `tax.ts → leaf.ts` | same | | evidence summary | `Observed by codegraph explore for task: …` | `Observed by CodeGraph for task: …` | | digest | `sha256:3c427fa5…f11582` | `sha256:594ad299…6eaef` | The symbol set matches on this fixture, but the digest changes once because it now binds the structured file set instead of the markdown. When a query has few search hits, the relevant context adds files: `buildContext("basePrice", { maxSymbols: 4 })` gives `basePrice`, `leaf.ts`, `tax.ts`, `cart.ts`, which the new test pins. Impact, same fixture, `getImpactRadius("render", 1)`: - before: one caller with a synthetic id (`codegraph.6c225064b482 → "render"`). The CLI trail picked one definition. `affectedPaths` was `[page.ts, page.ts, widget.ts, widget.ts]`. - after: two callers, `showPage → render()` and `showWidget → Widget::render`, with node ids. `affectedPaths` is `[src/views/page.ts, src/views/widget.ts]`. ## Release note (0.15.0) - `buildTaskContext` reads CodeGraph in-process: files come from `findRelevantContext` and query nodes from `searchNodes`, with no `codegraph explore`, so the code-context digest, the explorer's observed views and practice matches change once. Impact callers and callees are typed call edges with CodeGraph node ids, and an ambiguous name gives the union over its definitions. ## Tests (one case per branch) `codegraph-adapter.test.ts`, a new real-index describe (indexed once by `indexCodeGraphFixture`): - `buildContext` returns the query node, then the relevant files in relevance order, with the exact `[kind, name, path, range]` list and the import edge. A second call returns a deep-equal result. - `findSymbols` with `kinds: ["method"]` gives only `Widget.render`, and with `["function"]` only `render()`. - impact at depth 1 gives `leaf.ts`, `tax.ts`, and at depth 2 adds `cart.ts`. The caller is the `taxedPrice → basePrice` call edge. A node id resolves like its name, and callees are pinned too. - an unknown symbol gives an empty impact. - an ambiguous `render` gives the union of both callers and both files. The qualified `Widget::render` narrows it to the method. - **read-only refusal:** - The wrapped real handle makes a real write through the read-only database inside `findRelevantContext`, and SQLite refuses it (`attempt to write a readonly database`, asserted). - The result is a `FailureError` `AC_CODE_FACTS_UNAVAILABLE`/`index-open-failed` with no provider text. - A spy on the SDK's `CodeGraph.open`/`openSync` records exactly `[["openSync", { readOnly: true }]]`, so there is no retry and no writable open. The handle closed once. Session refusals, on the fake provider: no index, open fails, build lock and revision moves. Each case asserts its code, reason, retryable flag and the handles opened and closed. ### Changed assertions, each justified - `extracts import edges … scoped by changed paths`: the reads moved from the fake CLI to an injected provider. The argv checks (`-k import`, last query has the changed path and `import`, not the task, not the escapes) became the same checks on the last `searchNodes` call (`kinds: ["import"]`, same query text checks). The edge and delta assertions are unchanged. - Removed `extracts callers and callees from the CodeGraph node trail` and `resolves callers and callees to empty arrays when the CLI node trail has no recognizable calls`. They pinned the deleted text-trail parser (duplicate collapse, `+28 more`, synthetic ids). Their behaviors are now covered on a real index by the impact, unknown and ambiguous cases above. - `refactor-baseline.test.ts`: the `normalized-import-edges` freeze now reads from an injected provider answering the same import dump, with scores in dump order so the search keeps the CLI's order. The frozen fixture and its digest are unchanged. The fake CLI loses the non-import `query` and `explore` branches, which nothing calls now. ### Test budget (`git diff --numstat origin/main`) ``` 187 added product lines; 248 added test lines; ratio 1.33:1; 0 added test files. Measured against 6efd32a...HEAD; product roots: packages/**/src + scripts; test roots: packages/**/test. ``` ## Previous verification on f14b6a8 (Bun 1.4.4, macOS arm64, Node v24.22.1) - `bun run typecheck`: clean - `bun test packages/local-runtime/codegraph-adapter`: 118 pass, 0 fail - `bun test --timeout 60000 packages/surfaces/cli/test/code-facts-not-ready.test.ts packages/local-runtime/runtime-daemon`: 871 pass, 0 fail - `bun test packages/core/context-compiler packages/core/application`: 31 pass - `bun run test:codegraph-selector-node`: verified - `node scripts/package-boundary-audit.mjs` and `node scripts/production-mock-reachability-audit.mjs`: pass ## Current handoff and verification **Draft, not accepted for merge.** The owner requested that the uncommitted round-2 work be saved to this PR for another machine. Current content is preserved unchanged from the S5 writer; commit `d59c8f83cead0f1dcf311d281b2b2ba1745e426b`. See [handoff](#574 (comment)) and [approved design amendment](#574 (comment)). On this content, Codex reran `bun run typecheck && bun test --timeout 60000 packages/local-runtime/codegraph-adapter` using Bun 1.4.4: **120 pass, 0 fail, exit 0**. `git diff --check` passed. The worker also reported 902 pass/0 fail across 903 affected tests and a verified Node integration before stopping for provider credit exhaustion; those are prior pane evidence, not new-head CI receipts. The old f14b6a8 CI passed, but the GPT round-1 acceptance was FAIL and the Opus round-1 record was PASS with P3 findings. Neither old review nor old CI establishes acceptance of d59c8f8. Updated-head round-2 review and required CI remain pending. Review the changed assertions explicitly for tests bent to fit implementation; no new PASS is claimed here. Changed assertions in the current addition: - Extend the existing session-refusal table with read-throws-while-index-moves and read-plus-close failure; retain code/reason/retryability and handle-count assertions. - Add a provider-load mapping test that requires the readiness failure envelope and no provider text. - Add a real-index type-reference case: affectedPaths includes the reference, callers/callees remain empty. - Replace ambiguous-name length/containment assertions with an exact ordered callers array; add the unfiltered findSymbols tie-break assertion. These strengthen order coverage. - Correct the fault-injection comment: pinned CodeGraph 1.6.2 findRelevantContext makes no heal write; the test deliberately injects the refused write. - The four S2 import-graph baseline cases remain untouched by this addition. Impact port removal stays in S6. ## Risk - C6 changes the context output once (above). It is not on a timed path. - Callers and callees no longer count type references as calls, because the old text trail mixed them in. `affectedPaths` still covers them through the impact radius. - `buildContext` awaits `findRelevantContext` while the session is open, and the handle is closed after the awaited read settles. ## Rollback Revert this PR. There is no data, schema or ledger change.
Refs #574
S1 of ADR-0058: one in-process, read-only CodeGraph read session, with the repository readiness check rebuilt on it. Base:
main60f2968 (0.14.1, #593 and Bun 1.4.4 from #595).Goal
inspectRepositoryCodeFacts(doctor,completedebt, the private snapshot) andassertRepositoryCodeFactsReady(the checkpoint, debt, projection and CLI-provider gate) read the index only throughCodeGraph.openSync(root, { readOnly: true }). They start no CodeGraph process and run nogit statusorgit diff.Changes
Read session (
codegraph-adapter/src/read-session.ts, new)openCodeGraphReadSession/withCodeGraphReadSessionopen one handle per operation and close it infinally.version(),compatibility(),freshness()(memoized per session),revision(),unchangedSinceOpen(),graphandclose().lstat(.codegraph)fails withENOENTorENOTDIR:index-missing. Any otherlstaterror:index-open-failed..codegraphis a link, dangling or not:index-open-failed.stat(codegraph.db)fails withENOENTorENOTDIR:index-missing. Any otherstaterror:index-open-failed. This is the same rule as for.codegraph.build-in-progress, and the index is never opened.index-open-failed.open.unchangedSinceOpen()re-checks the writer lock and compares the revision taken at open with the revision after the reads. When anything moved, readiness answers not-ready (index-incomplete, every index fieldnull), and the gate refuses withcode-facts-not-readyandretryable: true. It never passes.freshness()is CodeGraph's own change test: the provider's enumeration, plus its change hash (readBoundedSourceSync,oversizeStamp,hashContent).pinnedCodeGraphProvider()takes its version from the manifest of the loaded platform bundle, soREQUIRED_CODEGRAPH_VERSIONstays the only pin.CODEGRAPH_PROVIDER_INTERNAL_EXPORTSis the Decision 7 table of provider-internal modules and exports.Readiness (
codegraph-adapter/src/index.ts)inspectRepositoryCodeFactsreads both tiers, compatibility and freshness.assertRepositoryCodeFactsReadyreads compatibility only and reads no source file.withExistingCodeGraphSnapshotcalls the sameinspectRepositoryCodeFacts(snapshotRoot). The CLI probe andCodeGraphSnapshotOptionsare deleted. S4 removes the snapshot itself.RepositoryCodeFactsV1keeps its shape. C2:unavailable.reasonCodegains"index-open-failed".binaryremoved from the read options.RepositoryCodeFactsInspectionOptionsis now{ provider? }.CodeGraphCliProvidertakes an optional third argument,readProvider; itsbinarynow only builds the index (sync).prepareArchitectureDocumentationProjectionSnapshotacceptsreadProviderfor its gate.runtime-daemon/src/flow-selector-check.tsno longer passesbinaryto the snapshot.A
.codegraphwithoutcodegraph.dbisindex-missing(round-2 item 1). The design table maps an absent database toAC_CODE_FACTS_UNAVAILABLE/index-missing, and the surviving snapshot path (index.ts:1225on main) already refused it that way. Round 1 had returnednot-initialized(AC_CODE_FACTS_NOT_READY); that state is gone. Doctor reportsindex-missingwithindexPresent: false, and the checkpoint gate refuses withindex-missing. A 0-byte or schema-lesscodegraph.dbnow givesindex-open-failedinstead ofnot-initialized.Contracts
failure.tspolicy table gainsindex-open-failed(AC_CODE_FACTS_UNAVAILABLE,inspect-diagnostics,retryable: false).Agreed scope widening
runtime-daemon/src/architecture-debt-code-facts.ts: the two inspect calls dropbinaryand take the injectedprovider.surfaces/cli/src/main.tspassesprocess.execArgvto the daemon it starts, only whenprocess.versions.bunis set. Under Node nothing new is passed.bun.lockhand-edit is dropped. Main's lock was regenerated by Bun 1.4.4 (chore: pin Bun 1.4.4 (#594) #595) and already points the workspacearchctxbin at./cli/bin/archctx. This PR changes no byte ofbun.lock.bun install --frozen-lockfileunder 1.4.4 is accepted, andreadlink node_modules/.bin/archctx→../@archcontext/surfaces/cli/bin/archctx.packages/surfaces/cli/bin/bun-darwin-sqlite.tsruns only under Bun on darwin. It callsbun:sqliteDatabase.setCustomSQLitewith a library from a closed list (/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib,/usr/local/opt/sqlite/lib/libsqlite3.dylib), and throws with abrew install sqlitehint when neither exists.bunfig.tomltest preload imports it. Devbin/archctximports it and preloads it into the CLI process it starts.setCustomSQLite, nobun-darwin-sqliteand nolibsqlite3.dylib.Refactors
index.tsintoread-session.ts.REQUIRED_CODEGRAPH_PACKAGEandREQUIRED_CODEGRAPH_VERSIONare re-exported unchanged.codeGraphSnapshotRuntimeand the snapshot's source scanner use the shared bundle lookup.repositoryCodeFactsFromStatusis the one evaluator of an index state.pending-source-changesbranch is removed, because the compatibility tier never measures pending changes.Paths not yet moved, each with the slice that moves it. These are sequencing, not fallbacks.
prepareProjectionCodeFacts) and the import population still use the CLIstatusandquery. S2+S3 move them.Round 4
stat, with the same rule as the.codegraphlstat: onlyENOENTandENOTDIRmeanindex-missing, and any other error isindex-open-failed..codegraph(chmod 000, socodegraph.dbcannot be stat'ed) and an unsearchable project directory (chmod 000, so.codegraphcannot be lstat'ed) are bothindex-open-failed, and neither opens the index.lstatrule exactly.rmSyncis unchanged.Bun 1.4.4 (rebased onto 60f2968)
Both checks were run locally with Bun 1.4.4 on macOS.
codegraph syncremoved the sidecar files, Bun 1.4.4 with the system SQLite (3.51.0) still fails the read-only open with "unable to open database file". The darwin module stays unchanged.lsof: afterclose(), Bun 1.4.4 holds 0 of the 5 database files it had open. Bun 1.4.3 held 6 of 6 until a GC after the handle was released.daemon.stop()without any GC, against 8 under 1.4.3, and had no child process.collectReleasedCodeGraphHandlesand its three call sites are deleted, and so is the test-sideBun.gc(true).maxRetries: 5, retryDelay: 100on win32, for a transient hold outside the process.EBUSYreturns there, the drop commit (9db4488) is the one to revert.Round 3
unavailable/index-open-failed,withExistingCodeGraphSnapshotthrowsindex-open-failed; every other unavailable answer stayscode-facts-unavailable. Test: a non-SQLitecodegraph.dbthrough the snapshot givesAC_CODE_FACTS_UNAVAILABLE/index-open-failed.lstat(.codegraph)failing withENOENTorENOTDIRisindex-missing. Any other failure (permission, I/O) isindex-open-failed, because it does not prove the index is absent.readFailsAfterOpen, which throws only from the build info thatcompatibility()reads after the open. That test assertsindex-open-failedwith open = close = 1. The formerreadFailstest is renamed to say it covers a read during the open.retryable: trueonly when the loaded provider has the pinned version. With a version mismatch it refusesretryable: falsewithinstall-compatible-codegraph, asserted in the G4 test.Windows EBUSY (round-2 item 5, superseded by Bun 1.4.4)
Under Bun 1.4.3, an instrumented local run (not committed) of all 23 protocol tests showed:
Bun.gc(true), and 0 after it;The remaining Windows hold was therefore outside the test process; the rmSync retries cover it. Under 1.4.4 no GC is needed (above), and the retries stay.
Changed or removed assertions, and their coverage
codegraph-adapter/test/projection-codegraph-status.test.ts(inspect section, rewritten against an injected provider). TheprojectionCodeGraphStatusIssuesdescribe is kept unchanged, because S2+S3 still evaluate CLIstatus -jwith it.Proxywhoseversiongetter throwsCodeGraphProviderUnavailableError. That is the errorpinnedCodeGraphProvider()throws when the SDK, the platform bundle, its manifest or a Decision 7 export is missing. The real loader cannot be made to fail in a running process. The Decision 7 bump guard (read-session.test.ts) asserts that every listed module path and export exists in the pinned bundle.read-session.test.tsreads a real index with no holder process, and its git spy proves nogit statusorgit diffruns.test.each:test.each(bundle version, build version, unknown build version, partial, pending refs, stale extraction, other project root), plus the pending-changes test.not-initialized→index-missing(item 1).worktree-mismatchis dropped for the session.openSync(root)never walks up to another worktree's index (ADR-0058 table), so the session setsworktreeMismatch: null. The evaluator'sworktree-mismatchbranch stays covered by the keptprojectionCodeGraphStatusIssuestests.test.eachplus the pending-changes test.index-open-failed.code-facts-not-ready.test.tsasserts an empty process log for every readiness path. The remaining CLI read, the importqueryuntil S2, still passesCODEGRAPH_NO_DOWNLOAD=1, andarchitecture-debt-code-facts.test.tsstill asserts it (query=1).npm-sdk.js) never downloads ("we never trigger a network download from inside require()"); it only reuses an installed or cached bundle. A missing bundle surfaces asCodeGraphProviderUnavailableError→codegraph-unavailable, which the Proxy test covers. The shim still builds the index (init,sync), and that is unchanged.codegraph-adapter/test/build-lock.test.ts. Live, stale and malformed lock, ported from the CLI call log to provider open counts: a live lock never opens the index, a stale or malformed one opens and closes it once.codegraph-adapter/test/codegraph-adapter.test.ts. The two shim tests injectreadProvider; theirsyncandqueryassertions are unchanged.surfaces/cli/test/code-facts-not-ready.test.ts--version\nstatus\nto empty: no readiness process..codegraphwithout a database isindex-missing" (item 1).readProvider.initSync) still yields version-mismatch plus index-incomplete.surfaces/cli/test/doctor-code-facts.test.ts. An empty.codegraphis nowindex-missingwithindexPresent: false(item 1). Doctorokis unchanged.runtime-daemon/test/architecture-debt-code-facts.test.ts. Readiness comes from the injected provider, which opens and closes twice around the reads. The process log keeps onlyquery=1(S2), and a missing or stale index starts nothing.runtime-daemon/test/flow-selector-check.test.ts,surfaces/cli/test/ref-impact.test.ts. Their subprocess runs the dev launcherbin/archctxinstead ofsrc/main.ts. The snapshot's readiness now reads in-process (item 3), so on darwin the CLI process needs the same SQLite library as the test process. The assertions are unchanged.surfaces/cli/test/projection-protocol-lifecycle.test.ts. Cleanup uses rmSync retries on win32; no assertion changed.New tests
read-session.test.ts:.git/indexmtime and the database sha256 unchanged; the git spy;scripts/codegraph-selector-node-integration.mjs(verify:pre, all three OSes) runs the read session under Node with no holder: compatibility and freshness, pending counts, the compatibility-only gate, the git spy, and.git/indexand the database unchanged. A live-writer case did not fit this script cheaply and is not added; the injected revision test covers G4.Test budget (
git diff --numstat origin/main, head 8649579)src,bin,main.ts, contracts): +556 / −144.bunfig.toml+1 / −1.bun.lockunchanged.Verification (Bun 1.4.4)
bun install --frozen-lockfile: accepted.bun run typecheck: exit 0.bun test --timeout 120000over codegraph-adapter, code-facts-not-ready, doctor-code-facts, architecture-debt-code-facts, flow-selector-check, ref-impact, complete-architecture-debt, projection-protocol-lifecycle, docs-anchors, local-product-e2e and the contracts tests: 699 pass, 0 fail.bun run typecheckexit 0. codegraph-adapter, code-facts-not-ready, doctor-code-facts, architecture-debt-code-facts, flow-selector-check, ref-impact and complete-architecture-debt: 162 pass, 0 fail.bun run test:codegraph-selector-node: verified.node scripts/packaged-cli-smoke.mjs: OK.node scripts/package-boundary-audit.mjs: passed.Risk
.codegraphwithout a database moves from not-ready toindex-missing, the same refusal as no index at all.Rollback
Revert this PR. There is no data migration; the WAL sidecar files are managed by SQLite.