Repository navigation
Conversation
v0.56 changes graph meaning (framework resolvers, synthesis, VB.NET, grammar bumps), so the extraction version moves once, first, to 23. A v0.55 index (schema 10, extraction version 22) is Outdated and upgrades through the existing full migration; v0.55 reports a version-23 index as Future and writes nothing. - upgrade_from_v055 covers the paths upgrade_from_v054 covers (CLI sync, catch-up, the cold MCP session's daemon upgrade, an older uninit stays Uninitialized, a failed upgrade still refuses, interrupted builds, tombstones and missing databases are skipped) over a fixture the official v0.55.2 linux-musl binary wrote; checksum, attestation and commands are in its README. The shared helpers move to tests/upgrade_support unchanged. - upgrade_from_schema_9 downgrades a fresh init of the mini corpus to schema 9 instead of planting rows from 3878d52, so its comparison with a fresh init checks only the migration. The release fixture keeps a third test that checks the in-place migration alone.
A database whose schema_versions records a version above this binary's was written by a newer codegraph (OD-19). Reads, status, sync, MCP, the daemon, init and index --force now refuse it before they change a byte, and the error names both versions. ensure_schema_and_migrations returns MigrationError::NewerSchema before it creates a table, migrates or analyzes. The newer version may sit only in an un-checkpointed write-ahead log. The writers that tolerate a live log (an incremental sync, the daemon's recovery of a dead owner's log) and every rebuild entry (before an explicit init removes a copied project's state slots, before a lockless root gets its lock, before building is published) decide from schema_probe: the main file plus the log's committed frames, applied in memory the way SQLite's recovery does, so no -wal or -shm is created or folded. A file that is not SQLite is still replaced; a database whose version cannot be read is refused, and the error says nothing changed. uninit --force skips the check and stays the way to discard such an index; the refusal offers it only for the states uninit accepts. status reports the index as current with initialized false and both versions.
test_engine() returned an engine over /tmp/cg-mcp-engine-{pid}-{seq} and
never removed the directory, so a reused pid opened a stale index and
engine tests failed locally. TestEngine derefs to the engine, removes a
stale directory before it creates one, and on drop closes the engine
before removing its directory.
The member-path std gate deferred on 2026-10-03 needs no code: the v0.55 port of upstream #2169 already keeps a receiver whose declared type the project does not declare (PathBuf, &Path, std::path::PathBuf) from binding a project method by name. The probe pins it: index_root.join, base.join, p.join and q.join stay unresolved and reach no LineIndex::join, while env.set() still reaches EnvGuard::set.
Takes dependabot #334 except tree-sitter-swift: tree-sitter 0.27.1, tree-sitter-language 0.1.9, erlang 0.21.0, php 0.25.1, scala =0.26.3, cfml 0.26.43, and the windows-sys reselection. Every golden, the four precision corpora and this repository index identically before and after. tree-sitter-swift stays at =0.7.3 (OD-B4). 0.7.4 fixes the scanner line behind upstream #2683 (`1UL << 32`, which MSVC folds to 0, so the Windows build reads `try! Foo()` as a call of `! Foo`), but it also regenerates the grammar: a call after an infix or prefix operator (`a + b.c()`, `!a.b()`) comes out as another tree, which the walker does not read as a member call. Upgrading it lost an exact-match edge in the swift golden and turned two Alamofire callees into whole expressions; the upgrade waits for that walker change. The #2683 probe pins calls after try!, try? and try. It is ignored on Windows until the upgrade; with the scanner line changed to what MSVC compiles it to, it fails on Linux the way upstream observed on Windows. No real Windows host was used. The grammar manifest records the held version, and languages.md the Windows limitation.
A Current index beside the uninit tombstone (an init that published Current but could not remove it) refused uninit --force with CurrentTombstoned. With a schema a newer codegraph wrote, init refuses that index and names uninit --force, so the refusal pointed at a command that then failed. Uninit's corroboration now accepts the tombstone: it discards the index like any other Current one and keeps the tombstone, so only init rebuilds it. Covered with and without a newer schema, in the store and through the CLI.
A Current index beside the uninit tombstone was reported as tombstoned before its schema was read, so status named `codegraph init` as the recovery even when a newer codegraph wrote the database, and init then refused it. When the tombstone is present, the corroboration now reads the schema version first (the write-ahead-log-aware probe, which touches no file); a positively newer version is FutureSchema, which offers uninit --force. Any other outcome is still CurrentTombstoned, so the same-version contract and its init recovery are unchanged.
A refusal of a newer codegraph's index offered `uninit --force` for every state uninit accepts, but on a Current namespace uninit first corroborates the database's extraction stamp. With the stamp missing, malformed or not this binary's, the offered command then failed. The offer now runs the same read-only corroboration uninit runs before its first change, for the tombstone precedence in the read gate and for the rebuild prologue. A newer schema with a bad stamp, tombstoned or not, is refused without the offer.
status's human output for an index a newer codegraph wrote always named `uninit --force`, even when the refusal it reports does not offer it because uninit would refuse the index (a missing or foreign extraction stamp). It now follows the refusal: upgrade, plus the uninit recovery only where uninit would discard the index.
Moving the shared helpers to tests/upgrade_support left upgrade_from_v054 and upgrade_from_v055 using ExtractionStatus and Store only in their unix-only tests, so the Windows Clippy job failed on unused imports. The import is now cfg(unix).
Codecov Report❌ Patch coverage is ❌ Your patch check has failed because the patch coverage (94.10%) is below the target coverage (95.00%). You can increase the patch coverage or adjust the target coverage. @@ Coverage Diff @@
## main #338 +/- ##
==========================================
- Coverage 95.59% 95.56% -0.03%
==========================================
Files 280 286 +6
Lines 166519 171840 +5321
==========================================
+ Hits 159181 164219 +5038
- Misses 7338 7621 +283
... and 3 files with indirect coverage changes 🚀 New features to boost your workflow:
|
The groundwork the routers of the next commits draw their edges with (F1, upstream #2129 and #2395). No registered resolver uses the new pieces yet, so every golden and the express, typescript-large and self-index graphs are unchanged. - EdgeKind::Navigates, stored as text like every edge kind. Callers, callees, the named-symbol flow and a flow's boundary follow it, as upstream's traversal, FLOW_EDGE_KINDS and CONTINUATION_KINDS do. - A ResolvedRef can carry an EdgeShape (upstream edgeKind, metadata and alsoTargets): the edges' kind, the metadata on the target's edge, and more targets, each its own edge under the same kind and confidence. ResolvedRef::new builds the plain resolution every resolver returned before. - frameworks/package_deps reads the dependencies a project declares in its root package.json and up to 24 nested ones: one or two directories down, then deeper above JavaScript-family code, never inside node_modules. React and NestJS detect through it, so an app whose manifest is in frontend/, apps/web/ or src/Web/ClientApp/ is detected without its fallback evidence. Upstream package-deps.ts is unchanged between b635dd46 and e8ddc4e2. - A framework can name its app dependencies (appDependencies). When a manifest declares one, its extractor runs only on files whose nearest package.json naming such a framework names it. The frameworks row a sync compares adds a digest of where each gated framework runs, so a manifest edit that moves an app re-extracts every file, as a framework that came or went does. The JS control inputs digest every manifest detection may read, so a sync notices one appearing beside no script. - The trait gains cross_file_nodes and navigation (upstream #2400 and #2425), both None by default; nothing calls them until a router implements them. The sync matrix gains two cases: React declared in an app's manifest, and in a manifest one directory down above no script. The gating sync case waits for the first gated framework (W7-07).
An edge a framework resolver made now names that resolver in
metadata.framework (upstream #2159), as a synthesized edge names its
pass in synthesizedBy: a Tauri invoke('greet') says `tauri`, a Godot
autoload call `godot`. The orchestrator adds it to every result a
framework's resolve returns, before the confidence race, so a lower
confidence framework result that wins later carries it too. When a
resolution reaches several targets, only the edge to the first carries
it, as upstream's does; the others keep the metadata the resolver gave
each.
Goldens: godot's four autoload calls and routers' one Vue-resolved call
gain the key; no edge, node or reference comes or goes. The same holds
for this repository (2 edges) and typescript-large (590 edges, all
`react`).
Framework detection reads the manifests one or two directories down from every indexed file, whatever its language (upstream #2395), and the JS control-input row digests every such candidate. The watcher re-read that row only when a JS-family file came or went, so a file of another language arriving in a new directory left the row behind a fresh index: 39 cases of the sync matrix (CFML, C, C++, C#, Dart, Go, Java, Kotlin, Liquid, Lua, Objective-C, PHP, Python, Rust and Solidity edits) failed under the watch driver. The graph itself converged, since a sync with changes re-detects the frameworks anyway. The control-input check now counts any file that comes or goes as touching the JS kind. typescript_react_dependency_beside_an_added_script covers it directly: React declared in frontend/package.json is detected once frontend/build.py is indexed, and the sync records that manifest as a fresh index does.
…ts; typed styled components
React resolution now follows upstream b635dd46 (#2120's React half,
#2240, #2429, #841, #2475's static half and #2420's wrapper half):
- A component, hook or context the file imports is the import's: a
UI kit's `<Button>` or @tanstack/react-query's `useQuery` no longer
binds a project namesake, and a project module's is import
resolution's to find (#2120, #2475).
- A hook nested inside another function is never a target, and the
file's own hook comes first (#2120). In a JS/TS module another file's
hook or context is reached only through an import, never by bare name
(#2240), and `f?.()` is a bare call, so an optional call no longer
binds a class member or a property.
- A PascalCase name a .tsx/.jsx file declares as a module-level value
is that declaration wherever the file uses it: the function the value
wraps (`observer(function X() {…})`, `forwardRef(Inner) as …`), else
the declaration, never another app's same-named component (#2420).
The lazy loader half needs W7-09's reader.
- A PascalCase const declared through forwardRef/memo (bare or `React.`)
or a styled-components/emotion tag (`styled.x`…``, `styled(Base)`…``,
typed `styled.x<P>`…``, #2429) is a `component` node, and an inline
render function's calls are the component's (#841). React's extract no
longer mints regex component/hook duplicates of tree-sitter nodes; it
emits routes only, now from .tsx/.jsx files too.
js_scan ports the bracket and string scanner upstream reads declarations
with, iteratively, so deeply nested source cannot exhaust the stack.
Goldens: routers gains the four .tsx page and data-router routes and one
route -> Layout edge; synthesis loses the regex `useCartStore` hook node
(log row in docs/equivalence.md). Precision: express-medium identical;
this repository +4 routes, -8 regex hook duplicates, and a Svelte
`onclose?.()` is now unresolved, as upstream; typescript-large and
LibreChat sampled 20 + 20 rows each, no wrong edge introduced.
LibreChat loses 919 reference sites in the interim: 857 behind the
`~/` alias its client/tsconfig.json declares (Rust reads only the root
tsconfig; upstream #2165's nearest-config aliases come with W7-09) and
62 rendering a module whose default export is `memo(X)` (#2563's
static half, W7-09). Upstream landed #2120 before #2165 the same way.
The sync matrix gains tsx_route_element_body_edit and
tsx_wrapper_value_body_edit; the react_dependency cases observe a data
router's route instead of a regex hook node. Upstream's later react.ts
rows #2737 (PORT) and #2738 (PARTIAL) are left for the next round.
…sioning
In a TS/JS module the provider convention (`*Service`, `*Controller`, …)
now takes only the reference's own class or the convention file beside
it (users.controller.ts -> users.service.ts); anything farther is import
resolution's or name matching's (upstream #2246). Nest's own specs'
`TransientService` no longer reaches an integration app's, and a
decorator call `@Controller('users')` no longer instantiates a PHP class
named `Controller`.
A NestJS route is named by the path a request takes to it (upstream
#2117). post_extract reads each `NestFactory.create` bootstrap and
renames the routes of its app's controllers in NestJS's order,
`/{prefix}/{v}{version}/{RouterModule prefix}/{path}`:
- `exclude` paths (plain, `{ path, method }`, `{/*wildcard}`, `(.*)`,
`*`) skip the prefix and keep their version;
- a route's version is its own `@Version` (found past decorators with
object arguments), else its class's `@Version` or `@Controller({
version })`, else `defaultVersion`; `VERSION_NEUTRAL` has none, and
header or media-type versioning adds no segment;
- a computed prefix is not guessed; each app takes the bootstrap with
the longest root above it, a seed script that creates the app without
serving it does not displace the one that configures it, and an app
that sets nothing keeps its routes as written;
- only a route whose decorator sits in a `@Controller` is renamed.
A route keeps its id and qualified name. One an earlier pass renamed
that nothing renames any more (a removed global prefix, version or
RouterModule entry) gets the name its file gives it back, so a sync
converges with a fresh index; before, a removed RouterModule entry left
the old name, as colby's sync does. The decorator regexes are compiled
once, since the pass now scans controller files on every sync.
Goldens: servers' NestJS route becomes `GET /api/v1/users/:id` (log row
in docs/equivalence.md). Precision: express-medium and typescript-large
identical; this repository renames that route and drops the PHP
`Controller` guess; nestjs/nest's samples and specs move 43 provider
references from another app's class to the spec's own and resolve 14
more. Its 302 routes without a bootstrap of their own take the `/api` of
the one root-level e2e spec that sets one, by upstream's bootstrapFor.
A sync of typescript-large declaring @nestjs/core takes 2.8% longer
(median of three).
The sync matrix gains four NestJS cases and checks node names. The
Express and cross-tier halves of upstream's test come with W7-19a and
W7-20.
…component Nuxt's file routes move from the Vue resolver to their own `nuxt` resolver (upstream #2130), detected by a `nuxt`, `nuxt3` or `@nuxt/kit` dependency or a `nuxt.config.*` file, and gated to the apps whose nearest manifest declares one (#2129). A plain Vue app keeps its views in a `pages/` folder just as often, and those are components a router config names, not addresses: halo's console no longer makes a route of each of them. Every path gets a leading `/` before the `pages/`, `server/api/` and `middleware/` searches, so a Nuxt app at the repository root finds its endpoints and middleware as a nested one does; a top-level `pages/index.vue` is `/`, and a `server/api/` file takes a page's param syntax (`[id]` -> `:id`, `[...slug]` -> `*slug`; `index` is the folder's path, `/api` at the top). A page route `calls` the component its own file is (#2267, new `frameworks/page_component.rs`): the route says so by the file's basename, and the resolver lands it on that file's component only, never on a same-named one elsewhere (every `index.vue` is a component named `index`). The viewer's routes list counts a component as a route's handler, so a Nuxt page is listed with the component that serves it. Goldens: routers' Nuxt index route becomes `/` (its id moves) and its two pages gain a `framework` edge each (log row in docs/equivalence.md). Precision: express-medium and typescript-large identical; elk's 59 pages each reach their own component, its root route is `/` and its four `server/api/` routes appear; halo's console loses 12 routes that were components. The sync matrix gains three Nuxt cases: a `nuxt.config.ts` appearing, an app's manifest coming to declare Nuxt, and a page's template edit. The Astro half of #2267 comes with W7-19a, and #2130's Vue Router admin-template half with W7-11. Upstream's later change to vue.ts (#2633, 986de95c: `@/`/`~/` imports left to the import resolver) is left for the next round.
Explore opens with the call path among the symbols the query names (upstream's Flow section, #365): when two or more named callables are connected by a chain of three or more, one numbered line per symbol and one `↓` line per hop come before the header. The search is named_symbol_flow's, the one the viewer's Flow rides, so of two equally long routes the Flow takes the one through the symbols the query names (#2453's explore half). A hop names the conditions its call runs under (#1655). The new codegraph-graph branch_guards module reads them from the caller's current source with the extractor's grammars: the if/else and ternary arms, switch cases, `&&` / `||` short circuits and catch blocks around the call site up to the enclosing function, and the early exits before it, negated, for JavaScript, TypeScript, Swift, Python, Java, Kotlin, C#, Go, C, C++ and Objective-C callers: `↓ calls (when !busy && ready)`. A caller file that changed since it was indexed, or one above 256 KiB, gets no condition rather than one read at a moved line. Upstream's rules read fwcd's Kotlin grammar and an older Go grammar; this port reads kotlin-ng's tree (arms without control_structure_body, return_expression, a bare break or continue as an identifier) and tree-sitter-go 0.25's statement_list for the same labels, and passes every guard label of upstream's branch-guards tests. codegraph-graph now depends on codegraph-extract for the grammars. A synthesized hop reads as the dynamic dispatch it is, in the Flow and in the codegraph_node trail: `dynamic: interface → impl @src/pdf-exporter.ts:5` (new synthesized_hop module, upstream's synthesized-hop.ts at cd97bb37, #2428). The index has no synthesized edges until the synthesis stage (W7-14), so only the engine test shows the tag for now. Not in this commit: the dynamic-dispatch links among the named symbols (W7-26), boundary and polymorphic-dispatch announcements for a flow that does not connect (the port keeps its boundary scan over the explore seeds), and the spine's weight in file ranking and body windows (W7-25). Upstream's interface cases of #2453 need interface-impl edges and come with W7-14. Upstream rows left for the next round: #2750 (f5dfa21f, PORT, named-symbol-flow.ts), #2769's branch-guards hunk (6bd217a2, PARTIAL, the Steps status reader this commit does not port), and the explore rows #2783 (1890b10c, PARTIAL), #2789 (214ef50e, PORT) and #2557 (5da4a41d, PARTIAL). Goldens and the MCP fixtures are unchanged: the mini fixture's query `Counter add runDemo` connects no chain of three, because runDemo calls add directly and the walk reaches it first.
A `NestFactory.create` in a test file (an e2e spec) with no `src/`
segment above it now belongs to the directory above its test folder
(`integration/security/` for `integration/security/e2e/csrf.spec.ts`),
or to the directory holding it. Within one app root, the bootstraps of
the app's entry points decide over a test's: an e2e spec's
`setGlobalPrefix('api')` does not rename the routes `src/main.ts`
serves.
W7-02 ported upstream b635dd46's appRootOf and bootstrapFor as they
are. They put every such spec at the repository root and prefer the
bootstrap that configures the app, so on nestjs/nest one integration
app's spec gave its `/api` to 306 routes of ten other integration apps
and packages/core's specs. With this change only
integration/security's own routes and sample/24's keep `/api`.
express-medium and typescript-large are identical, and no golden
changes. This is a deliberate difference from upstream (KEEP-RUST,
recorded in KNOWN_DIFFS.md). It answers the W7a sub-wave gate's first
round.
W7-04 (9ef9f69) names the resolver on the edges a framework resolution draws (`metadata.framework`, upstream #2159) without adding a sync case of its own. The plan requires one for every commit that changes crates/codegraph-resolve/src. The matrix harness gains `edge_metadata_after`, which requires an edge of the fresh index to hold a value under a metadata key. Each sync is already compared with the fresh index by every metadata key. Two cases use it: - tauri_command_body_edit_keeps_the_invoke_edges_resolver_name: an edit to a Tauri command's body re-resolves the TypeScript `invoke('greet')` at its site, and the edge keeps `framework: "tauri"`. - tsx_framework_edge_keeps_its_resolvers_name: a `.tsx` file is re-extracted, and the React-resolved edge to the function a component value wraps keeps `framework: "react"`. Both cases fail if W7-04's naming is removed (the fresh index lacks the key). They also fail if the incremental resolution drops the name (the sync differs from the fresh index). Only test files change.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The v0.56.0 follow-up program: the work split out of v0.55 (OD-18) plus the
items that came after it. This PR stays a draft while the waves land; each
wave is pushed when its local gate passes.
Planned scope:
resolvers)
codegraph context, upstream #1617, and the v1.5.0 performance strategiesW0 (pushed)
graph meaning, so users rebuild once.
upgrade_from_v055covers thev0.55 → v0.56 path (a v0.55 index is
Outdated{22}and upgrades to agraph equal to a fresh
init).StoreError::FutureSchemafrom every opening path (sync, daemon catch-up,MCP sessions,
rebuild), probed WAL-aware before any writable open. Therecovery text offers
uninitonly whereuninitwould succeed.uninit --forcecan still discard such an index.uninit --forcebeside a leftover tombstone: a current index next tothe uninit tombstone is now discarded (the tombstone stays, so only
initrebuilds it), and a newer schema is reported before the tombstone.
std-typed receivers (
PathBuf,&Path) stay unresolved.tree-sitter-swift,which stays at 0.7.3 (0.7.4 regenerates the grammar; documented in
docs/grammar-manifest.md).Verification (W0, HEAD 5cb5f32)
make check: 239 suites, 6135 passed, 0 failed, 1 ignored.make pre-ci(pre-push hook): passed, including the viewer bundle bytecheck and the release-archive smoke.
scripts/regen-goldens.sh --check); graphdiffs over the precision corpora identical before and after the dependency
updates.
within budget.
Not in this PR yet
W7-W10. This description is updated as each wave is pushed.