diff --git a/.github/workflows/daemon-lifecycle.yml b/.github/workflows/daemon-lifecycle.yml index a6e82d974e..1394dc48df 100644 --- a/.github/workflows/daemon-lifecycle.yml +++ b/.github/workflows/daemon-lifecycle.yml @@ -58,4 +58,4 @@ jobs: - run: npm ci --ignore-scripts - run: npm run build:kernel - run: npm run build - - run: npx vitest run __tests__/runtime-control.test.ts __tests__/daemon-install-check.test.ts __tests__/daemon-older-version.test.ts __tests__/cli-version.test.ts __tests__/daemon-registry.test.ts __tests__/daemon-pid-reuse.test.ts __tests__/daemon-lock-refresh.test.ts __tests__/mcp-daemon.test.ts __tests__/daemon-client-liveness.test.ts __tests__/daemon-bind-failure.test.ts __tests__/daemon-socket-fallback.test.ts __tests__/daemon-rendezvous-case.test.ts __tests__/writer-lock.test.ts __tests__/writer-handover-ordering.test.ts __tests__/mcp-writer-lock.test.ts __tests__/mcp-projectpath-lifecycle.test.ts __tests__/query-pool.test.ts __tests__/status-index-health.test.ts --maxWorkers=2 --minWorkers=1 + - run: npx vitest run __tests__/runtime-control.test.ts __tests__/daemon-install-check.test.ts __tests__/daemon-older-version.test.ts __tests__/cli-version.test.ts __tests__/daemon-registry.test.ts __tests__/daemon-pid-reuse.test.ts __tests__/daemon-lock-refresh.test.ts __tests__/mcp-daemon.test.ts __tests__/daemon-client-liveness.test.ts __tests__/daemon-bind-failure.test.ts __tests__/daemon-socket-fallback.test.ts __tests__/daemon-rendezvous-case.test.ts __tests__/writer-lock.test.ts __tests__/writer-handover-ordering.test.ts __tests__/mcp-writer-lock.test.ts __tests__/mcp-projectpath-lifecycle.test.ts __tests__/query-pool.test.ts __tests__/status-index-health.test.ts __tests__/vbnet-project-ties.test.ts __tests__/vbnet-receiver-types.test.ts __tests__/upstream-native-scope-regressions.test.ts --maxWorkers=2 --minWorkers=1 diff --git a/CHANGELOG.md b/CHANGELOG.md index eae1a5980c..5226c01f71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,10 +32,6 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### New Features -- Ordinary watcher startup can initialize a missing project index under daemon ownership and waits for fresh file indexing and queued synthesized edges before reporting readiness. - -- Installers can start or reuse a checkout's active watcher without replacing existing writers, and MCP launchers can preserve existing daemons across reconnects with `--preserve-existing`. - - Installers can verify daemon readiness and safely hand writer ownership across build promotion and rollback through a supported runtime-control API. - `codegraph_explore` now finds quoted prose in script strings and template text through a capped source scan, ignoring case and punctuation without requiring a re-index. @@ -93,8 +89,21 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - **Markdown is indexed, and a documentation question gets the section, not the graph.** Every `.md` file's headings, sections, tables and links are nodes (the extractor from #361), and a doc-shaped `codegraph_explore` query that names a markdown file now renders that file's best sections first and whole — the top three by idf-weighted line hits, a heading the query covers word for word counted as named, 8k characters per file — with the blast-radius, relationships and "additional files" blocks held back unless a code file rendered too. Measured on a 109-file docs corpus under headless Claude Code, 36 cells over three rounds: the right file and section in every call, median 1 tool call against 4 for Grep-then-Read, 36 of 36 correct. Code answers keep their shape: markdown nodes leave a subgraph the doc tier did not seed, a markdown body is never mistaken for a generated-file header, and the explore budget tiers count code files only, so a README-heavy repo does not cross a breakpoint. The server instructions say markdown is indexed, which the branch's own text still denied. (#361, #1439) +- Installers can start or reuse a checkout's active watcher without replacing existing writers, and MCP launchers can preserve existing daemons across reconnects with `--preserve-existing`. + +- Ordinary watcher startup can initialize a missing project index under daemon ownership and waits for fresh file indexing and queued synthesized edges before reporting readiness. + ### Fixes +- Incremental Go sync invalidates nearest-module caches after a module is renamed, removed or added. +- JavaScript loop-local object paths stay local, and scoped destructured member calls resolve across source lines while preserving their declaration-site source binding in scripts and single-file components. +- Dart fields in the visible class hierarchy shadow inherited and extension getters; an unrelated same-named class cannot suppress a getter call. +- C# target-typed initializers retain relative and global qualified declared types in both native and generic extraction. + +- Vue template attributes decode HTML entities before finding calls, so quoted strings do not create false callers and helper calls retain their source positions. + +- Go factory chains retain qualified return types and resolve them through the factory declaration's imports. A local or parameter that shadows a package factory no longer borrows that factory's return type. + - Writer handovers, stale-lock cleanup, readiness updates and releases now share an OS file lock, preventing a competing launcher from overwriting a newer writer. The OS releases coordination locks on process exit. Contended retirement retries release while the owner stays alive; legacy daemons without coordinated handover stay running until their old sessions are restarted. - Python member calls no longer keep a receiver's original type after it is reassigned on a later line. @@ -308,6 +317,46 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - A file is no longer saved with no symbols when its language parser can't be loaded, which is what happened to every file a background server re-indexed after an upgrade removed its install: the file keeps what it had and is indexed again once the parser loads, and files an earlier version emptied this way are re-indexed by the next sync. A background server also exits on its own once its install is upgraded or removed, so the next session starts one from the current install. Thanks @lipchey for the report. (#2335) - `codegraph status` no longer says the index is up to date while indexed files are missing their symbols: it now names files the parser couldn't read and files stored without their symbols (which `codegraph sync` repairs), `status --json` counts both, and `codegraph files --json` lists each file's recorded errors. Thanks @lipchey for the report. (#2336) + +- In C#, a field or property initializer like `private readonly ILogger _log = LogManager.GetLogger(typeof(X));` or `public List Items { get; } = new();` now links what it calls, creates and reads, including inside a lambda, and a target-typed `new()` there counts as creating the declared type. Before, initializers were skipped, so a method or class used only from one — like a converter created in a static list — looked unused, and a method passed as a value there was credited to the whole class instead of the field or property. + +- In C# and VB.NET, a constant no longer stands in for a type with the same name: `new Station { … }` links to class `Station` even when another class declares `private const string Station`, `new Version(…)` no longer links to a `const string Version`, and a field like `static readonly Meter Meter` no longer points at itself. Thanks @EvanYu1980 for the report and @drakeo338. (#2337) + +- In VB.NET, a call on a variable, parameter, field or property now reaches a method of the type it is declared with — one that type inherits, or an extension method written for it — and nothing when that type comes from outside your project, instead of any project method that merely shares the name. + +- In VB.NET, a call to one of .NET's own methods such as `Add`, `Contains`, `Clear` or `Dispose` on a value whose type isn't known is no longer linked to a project method of that name, unless the value is named after that method's class. + +- VB.NET resolves type names through surrounding namespaces, file and project `Imports` (including aliases), and the caller's project. Same-named types in different namespaces or projects no longer capture every call. When scope and imports do not distinguish the candidates, the call stays unlinked. + +- In VB.NET, a call with no receiver, or on `Me`, now reaches only a member of the class it is written in, of a class it inherits, or of a Module, and no longer a nearby class's member of the same name. + +- VB.NET designer code such as `New System.Drawing.Point(…)` or `Me.Size = New System.Drawing.Size(…)` is no longer linked to a project type or member that only shares the name, and a class nested inside another is matched by its bare name only from inside that class or one that inherits it. + +- Python global-write inference validates the local binding before scanning other files. The native resolver retains its bounded source and name caches. + +- In VB.NET, reading or setting a `Shared` field or property through its class or module name, like `AppSession.SessionId`, `AppSession.CurrentUser = "demo"` or `AppSession.Items(0)`, now counts as a use of that member and of the class, and so do reading an `Enum` value like `Mode.Fast` and calling a `Shared` function without parentheses. Before, only calls written with parentheses were linked, so `codegraph callers` on such a field, property or class came back empty and impact missed most of the code that depends on it. A local, parameter or field that only shares a class's name is not mistaken for the class. Thanks @serkanince for the report and @ChrisPrapas. (#2305) + +- In Vue and Nuxt components, calls written in the template (`{{ formatDate(post) }}`, `:to="getAccountRoute(account)"`, `@click="save(item)"`) and calls in a destructuring declaration at the top of ` +`; + const code = ending === 'CRLF' ? lf.replace(/\n/g, '\r\n') : lf; + const result = extractFromSource('Card.vue', code); + const component = result.nodes.find((n) => n.kind === 'component')!; + const calls = result.unresolvedReferences + .filter((r) => r.referenceKind === 'calls') + .map((r) => `${r.fromNodeId === component.id ? 'component' : r.fromNodeId}:${r.referenceName}@${r.line}`) + .sort(); + expect(calls).toEqual([ + 'component:label@3', + 'component:useBar@2', + 'component:useBar@2', + 'component:useFoo@7', + 'component:useFoo@8', + ]); + }); + it('should extract calls from Vue Options API object methods', () => { const code = `