Repository navigation
refactor(codegraph-adapter): split index.ts by responsibility (#574 S1b) - #596
Conversation
Move-only split of codegraph-adapter/src/index.ts into cli-provider, repository-code-facts, projection-handshake, snapshot, import-population and selector-index. index.ts keeps CodeGraphAdapter and MultiRepoCodeGraphAdapter and re-exports the same public names. No behavior change. Refs #574
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: d5ac750
Tier: medium
Verdict: PASS
Reviewer: codex (gpt-6.1-terra)
Model relation: different-family
Independent move-only check against 449b61b. A normalized diff comparison found 1,826 removed and 1,826 added code lines with no unmatched line, and the zebra diff identifies the moved blocks. The same 64 public export names and type/value classifications appear before and after, and the package export is unchanged. The new modules have import cycles, but no cyclic binding is read during module initialization. Only the seven codegraph-adapter/src files changed; git diff --check passes.
NITs
None.
|
repo-harness acceptance record
Model relation: different-family Findings:
|
|
Joint decision (orchestrator + advisor), 2026-11-14: merge #596 (#574 S1b) at d5ac750 onto main 449b61b. All 35 checks pass. GPT PASS: an independent normalized check found 1826 removed and 1826 added code lines with none unmatched, and the 64 export names are identical (596#issuecomment-6862759587). The chatgpt-codex-connector summary failed on the Codex usage limit and left no inline finding. Seat: orchestrator t7 |
## 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 · 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:main449b61b (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
exportto 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.cli-provider.ts(397)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),digestFilerepository-code-facts.ts(224)RepositoryCodeFactsPendingChangesV1,RepositoryCodeFactsV1,REPOSITORY_CODE_FACTS_SCHEMA…assertRepositoryCodeFactsReady,codeGraphIndexAvailableprojection-handshake.ts(465)CodeGraphProjectionStatusV1,CodeGraphProjectionHandshakeV1,PreparedProjectionCodeFacts,PreparedArchitectureDocumentationProjectionSnapshot,prepareArchitectureDocumentationProjectionSnapshot…runProjectionCodeGraph(code facts, status issues, status digest)snapshot.ts(298)codeGraphSnapshotRuntime,codeGraphSourceScanner,CODEGRAPH_SNAPSHOT_*,codeGraphSnapshotQueries,withExistingCodeGraphSnapshot,assertSnapshotGitReadSafety,pinSnapshotHooks,mirrorCodeGraphInputsimport-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,capabilityImportGraphsselector-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,normalizedIndexPathindex.ts(309)CodeGraphAdapter,LandscapeContextInput,MultiRepoCodeGraphAdapter,assertNoCodeGraphInternalPathAccess,architectureDeltaTask, and explicit re-exports of the same 64 public namesBoundary adjustments (one shared symbol each, placed with its main user):
digestFile→cli-provider.ts.packagedCodeGraphRuntimeDigestcalls it 4 times andprepareProjectionCodeFactsonce.normalizeChangedPathsanduniqueStrings→import-population.ts(importQueryuses both), notindex.ts. That way no module imports theindex.tsbarrel, which re-exports all of them.CodeGraphAdapterandarchitectureDeltaTaskimport them from there.CodeGraphProvider→cli-provider.ts, next to its implementation, for the same reason.codeGraphSdk→ selector-index,codeGraphSourceScanner→ snapshot,CODEGRAPH_NO_DOWNLOAD_ENV→ selector-index (onlyloadCapabilityCodeGraphProjectionInputsreads it), andSOURCE_EXTENSIONSandfileSymbolId→ import-population.read-session.ts,snapshot-query.ts,child-env.tsandref-impact-facts.tsare not touched. No test file is changed. Every test imports../src/indexor 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 issrc/only. It checks two things:index.tsappears verbatim in a changedsrc/file. A moved declaration may gain a leadingexport. No other line is added. Import statements,export { } fromstatements and blank lines are ignored. It also asserts thatindex.tsis the only modified file and every other changed file is new.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_000to6_000in one moved function, and the script reportsFAILon both checks.move-only-proof.mjs
git diff --color-moved=zebra --stat origin/main: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 17exportprefixes, and the 3-lineuniqueStringsbody, 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.txtis empty. The runtimeObject.keys(await import(index.ts))also matches: 36 value exports.the 64 exports (name, kind)
Checks (Bun 1.4.4, macOS arm64)
bun run typecheck: pass.tsc --noEmit --noUnusedLocalsreports nothing incodegraph-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 testoncore/application/test/control-loop.test.ts,core/context-compiler/test/context-compiler.test.ts,tests/projection-prior-committed-applies.test.tsandtests/ownership-change-acceptance-recovery.test.ts(the other direct importers): 25 pass, 0 fail.bun run test:codegraph-selector-node: verified. It bundlessrc/index.tsfor Node and checks the selectors, the snapshot and the read session.node scripts/package-boundary-audit.mjsandnode scripts/production-mock-reachability-audit.mjs: pass.Risk
cli-provider↔import-population,repository-code-facts↔projection-handshake, andimport-population→projection-handshake→selector-index→import-populationare 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, anode:/@archcontext/contractsvalue orcreateRequire(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 incli-provider.ts. That file is in the same directory, so package resolution is unchanged.index.tsdoes not re-export them, so the package's public surface is unchanged.Rollback
Revert this single commit. No data, schema, contract or test changes.