diff --git a/docs/plans/2026-05-12-graph-first-language-expansion.md b/docs/plans/2026-05-12-graph-first-language-expansion.md index a28a1b32..db487d5d 100644 --- a/docs/plans/2026-05-12-graph-first-language-expansion.md +++ b/docs/plans/2026-05-12-graph-first-language-expansion.md @@ -5,7 +5,7 @@ ## Context -The repo has an established language-support workflow: define support surface, add a language definition, wire native grammars and reduced-mode behavior, add fixtures/tests, then update parity/scenario docs and the agent skill surface. Follow [docs/adding-language-support.md](../../adding-language-support.md) exactly. +The repo has an established language-support workflow: define support surface, add a language definition, wire native grammars and reduced-mode behavior, add fixtures/tests, then update parity/scenario docs and the agent skill surface. Follow [docs/adding-language-support.md](../adding-language-support.md) exactly. This plan intentionally targets graph-first support. Each language should provide parsing, chunking, top-level symbol extraction, and static dependency edges. It should not claim full cross-file semantic navigation until shared `goto`, `references`, and native semantic parity tests prove that behavior. @@ -578,9 +578,9 @@ git commit -m "docs: finalize graph-first language support" ## References -- [Language support checklist](../../adding-language-support.md) -- [Language parity matrix](../../language-parity.md) -- [Scenario catalog](../../scenario-catalog.md) +- [Language support checklist](../adding-language-support.md) +- [Language parity matrix](../language-parity.md) +- [Scenario catalog](../scenario-catalog.md) - [`tree-sitter-scala` on npm](https://www.npmjs.com/package/tree-sitter-scala) - [`tree-sitter-lua` on npm](https://www.npmjs.com/package/tree-sitter-lua) - [`tree-sitter-powershell` on npm](https://www.npmjs.com/package/tree-sitter-powershell) diff --git a/docs/plans/2026-07-03-01-project-lifecycle-commands.md b/docs/plans/2026-07-03-01-project-lifecycle-commands.md deleted file mode 100644 index 175ffc0a..00000000 --- a/docs/plans/2026-07-03-01-project-lifecycle-commands.md +++ /dev/null @@ -1,105 +0,0 @@ -# Project lifecycle commands - -## Goal - -Add a small project lifecycle surface so users can initialize, refresh, inspect, and remove Codegraph state for a repository without learning cache internals. - -Commands: - -- `codegraph init [path]` -- `codegraph status [path]` -- `codegraph sync [path]` -- `codegraph uninit [path]` - -## Design - -Use `--root` semantics as the source of truth. Positional `[path]` resolves to the project root unless `--root` is passed; if both are passed, `[path]` is an include root inside `--root` only where existing command semantics already allow it. - -Create a project-local `.codegraph/manifest.json` with only metadata: - -```json -{ - "schemaVersion": 1, - "root": ".", - "createdAt": "2026-07-03T00:00:00.000Z", - "lastSyncAt": "2026-07-03T00:00:00.000Z", - "configHash": "...", - "buildOptionsHash": "...", - "fileCount": 123, - "analysis": { "label": "native semantic" } -} -``` - -Do not duplicate the full graph in `.codegraph/` in this PR. Reuse the existing disk cache and session/index builders. The manifest is a user-facing lifecycle marker and status source, not a second graph database. - -## Command behavior - -### init - -- Ensure root is safe and inside the selected project boundary. -- Create `.codegraph/` if missing. -- Run the same index build path used by `index --cache disk`. -- Write `manifest.json` atomically through a temp file plus rename. -- Exit successfully if already initialized and current; print a short status. -- Support `--force` to rebuild and overwrite manifest. - -### status - -- Read `.codegraph/manifest.json`. -- Recompute current config/build option hash. -- Re-stat discovered files using current discovery settings. -- Print: - - initialized or not initialized - - last sync time - - file count then/current - - config changed yes/no - - native/reduced analysis label - - suggested next command -- Add `--json` with stable `schemaVersion: 1`. - -### sync - -- Require initialized project unless `--init` is passed. -- Reuse incremental index/cache build. -- Update manifest atomically. -- Print changed/removed/added counts when available; otherwise print file count and elapsed time. - -### uninit - -- Refuse to delete non-Codegraph directories. -- Remove `.codegraph/manifest.json` and known empty `.codegraph/` scaffolding. -- Preserve unrelated files in `.codegraph/` unless `--force` is passed and every entry is recognized. - -## Files likely touched - -- `src/cli/help.ts` -- `src/cli/options.ts` -- `src/cli.ts` command dispatch -- new `src/cli/lifecycle.ts` -- new `src/lifecycle/manifest.ts` -- `docs/cli.md` -- `README.md` -- `codegraph-skill/codegraph/SKILL.md` -- tests under `tests/cli-regressions.test.ts` or new `tests/lifecycle.test.ts` - -## Tests - -- `init` creates `.codegraph/manifest.json` and warms cache. -- `init` is idempotent. -- `init --force` rebuilds and updates `lastSyncAt`. -- `status --json` reports initialized project. -- `status` reports not initialized without throwing. -- `status` detects config hash changes. -- `sync` updates manifest after a file edit. -- `uninit` removes only recognized files. -- `uninit` refuses to delete unknown files without `--force`. - -## Acceptance - -- Users can run `init`, edit files, run `status`, then run `sync` and see state update. -- Existing commands continue working without `.codegraph/`. -- No existing cache format is made dependent on the lifecycle manifest. - -## Review pass - -Checked scope: this plan avoids introducing a second persistent graph database. It keeps the addition idiomatic for this project by reusing `--root`, disk cache, existing index builders, atomic file writes, and documented config hashing behavior. diff --git a/docs/plans/2026-07-03-02-mcp-freshness-on-query.md b/docs/plans/2026-07-03-02-mcp-freshness-on-query.md deleted file mode 100644 index daba2534..00000000 --- a/docs/plans/2026-07-03-02-mcp-freshness-on-query.md +++ /dev/null @@ -1,109 +0,0 @@ -# MCP freshness-on-query - -## Goal - -Prevent long-running MCP sessions from returning silently stale answers after files change. Prefer cheap request-time freshness checks over a mandatory background watcher. - -## Design - -Add a freshness gate inside the MCP/session request path. Before serving tools that depend on indexed state, compare the current project signature against the session snapshot signature. - -If no relevant changes exist, answer from the warm snapshot. If changes are small, rebuild incrementally before answering. If changes are large or still pending, return an explicit staleness banner instead of an unqualified answer. - -## Policy - -Add a session option: - -```ts -type FreshnessPolicy = "manual" | "check" | "auto"; -``` - -Defaults: - -- CLI one-shot commands: unchanged. -- MCP stdio/HTTP: `auto`. -- Library `createAgentSession`: default `check`, overridable. - -Behavior: - -- `manual`: current behavior; user calls `refresh_index`. -- `check`: detect changes and include stale metadata, but do not rebuild automatically. -- `auto`: detect changes and rebuild incrementally when below thresholds. - -Thresholds should be conservative and configurable through session options, not global environment variables first: - -```ts -{ - maxAutoRefreshFiles: 50, - maxAutoRefreshBytes: 2_000_000, - staleBanner: true -} -``` - -## Implementation - -Add a lightweight session manifest: - -```ts -type SessionFileSignature = { - path: string; - size: number; - mtimeMs: number; -}; -``` - -Use current discovery options and include roots. Compare discovered file signatures to the signature stored on the active session snapshot. - -Freshness result: - -```ts -type FreshnessResult = - | { state: "fresh" } - | { state: "refreshed"; changedFiles: string[] } - | { state: "stale"; changedFiles: string[]; reason: string } - | { state: "uninitialized" }; -``` - -Thread this into MCP tool responses as metadata and, for text responses, a short prefix: - -```text -Freshness: refreshed 3 changed files before answering. -``` - -or - -```text -Freshness warning: 82 files changed since this session snapshot. Run refresh_index or narrow the query. Files include src/a.ts, src/b.ts, ... -``` - -For `get_file`, always read live file bytes from disk and include freshness metadata separately. File reads must not depend on stale index state. - -## Files likely touched - -- `src/agent/session.ts` -- `src/mcp/server.ts` -- `src/mcp/tools.ts` -- `src/cli/help.ts` for `refresh_index` docs if needed -- `docs/mcp.md` -- `docs/library-api.md` -- tests under `tests/mcp-server.test.ts` or new `tests/mcp-freshness.test.ts` - -## Tests - -- Start a session, search for a symbol, edit file, search again, and observe refreshed result. -- Delete a file and verify subsequent search/packet output no longer returns it after auto refresh. -- Large edit burst returns stale warning and does not rebuild automatically. -- `manual` policy preserves current behavior. -- `get_file` returns live bytes even when index is stale. -- Config/include-glob changes produce stale reason requiring explicit refresh or rebuild. - -## Acceptance - -- MCP users do not need to call `refresh_index` for normal small edits. -- No silent stale answers when changed files are detected. -- One-shot CLI behavior remains deterministic and unchanged. -- Freshness metadata is visible in JSON and human-readable responses. - -## Review pass - -Checked scope: this plan intentionally avoids an always-on watcher and daemon. It uses existing session/index/cache mechanics and adds a bounded freshness gate at the point where stale answers matter. diff --git a/docs/plans/2026-07-03-03-shared-server-lifecycle.md b/docs/plans/2026-07-03-03-shared-server-lifecycle.md index 3988fc7f..1a73f46d 100644 --- a/docs/plans/2026-07-03-03-shared-server-lifecycle.md +++ b/docs/plans/2026-07-03-03-shared-server-lifecycle.md @@ -18,7 +18,7 @@ codegraph server stop --root . Keep `codegraph mcp serve` as the low-level primitive. `server start` is a convenience wrapper around it. -Shared servers use the immutable Windows native cache described in [`2026-07-12-windows-native-runtime-cache-updates.md`](./2026-07-12-windows-native-runtime-cache-updates.md). Their registry and health output must preserve the version captured at server start, surface installed-version drift, and require an explicit restart rather than replacing mapped runtime files or terminating clients automatically. +Shared servers must use the current immutable Windows native cache. Their registry and health output must preserve the version captured at server start, surface installed-version drift, and require an explicit restart rather than replacing mapped runtime files or terminating clients automatically. ## Server registry diff --git a/docs/plans/2026-07-03-04-self-contained-distribution.md b/docs/plans/2026-07-03-04-self-contained-distribution.md deleted file mode 100644 index f1e7390c..00000000 --- a/docs/plans/2026-07-03-04-self-contained-distribution.md +++ /dev/null @@ -1,105 +0,0 @@ -# Self-contained distribution - -## Goal - -Reduce install friction for CLI users who do not already have the required Node.js version or registry configuration, while preserving the current npm/library package model. - -## Design - -Add an optional release artifact channel that bundles a Node runtime with the built CLI. Do not replace the existing `@lzehrung/codegraph` package in this PR. - -Release artifacts: - -```text -codegraph-linux-x64.tar.gz -codegraph-linux-arm64.tar.gz -codegraph-darwin-x64.tar.gz -codegraph-darwin-arm64.tar.gz -codegraph-win32-x64.zip -codegraph-win32-arm64.zip -``` - -Bundle layout: - -```text -codegraph-/ - node | node.exe - lib/ - dist/ - package.json - node_modules/ - bin/ - codegraph | codegraph.cmd -``` - -## Build approach - -Use a script under `scripts/`: - -```bash -node scripts/build-bundle.mjs linux-x64 -``` - -The script should: - -- require a built `dist/` -- install production dependencies into a staging dir with `npm ci --omit=dev` -- copy package metadata and dist files -- download an official Node release for the target -- create launcher scripts -- produce archive and checksum - -Do not cross-compile native Rust artifacts in this script. Use the already published/packaged native addon when available, or document reduced-mode fallback for unsupported targets. - -## Installer scripts - -Add minimal installers after bundle generation works: - -- `install.sh` -- `install.ps1` - -Behavior: - -- detect OS/arch -- download matching release artifact -- verify checksum when available -- install under user-local directory -- add or print PATH instructions -- never require sudo by default - -## Non-goals - -- No auto-update in this PR. -- No package manager taps/casks/scoop manifests. -- No code signing or notarization. -- No change to source checkout development flow. - -## Files likely touched - -- new `scripts/build-bundle.mjs` -- new `install.sh` -- new `install.ps1` -- `.github/workflows/release.yml` if release automation exists or is added later -- `docs/installation.md` -- `README.md` -- `PUBLISHING.md` -- `codegraph-skill/codegraph/SKILL.md` only if install guidance changes -- tests under `tests/release-script.test.ts` or new installer tests - -## Tests - -- bundle script rejects missing `dist/` with clear message. -- bundle contains launcher, Node binary, dist, package metadata, and production deps. -- launcher invokes `dist/cli.js version` successfully on current platform. -- installer target detection maps known OS/arch pairs correctly. -- installer refuses unsupported target clearly. - -## Acceptance - -- A release artifact can run `codegraph version` without relying on system Node. -- Existing npm install path still works. -- Documentation clearly distinguishes source, npm, and bundled install paths. - -## Review pass - -Checked scope: this plan makes bundled distribution an additive channel. It avoids destabilizing the current package exports, native addon model, and contributor workflow. diff --git a/docs/plans/2026-07-03-05-agent-installer-workflow.md b/docs/plans/2026-07-03-05-agent-installer-workflow.md deleted file mode 100644 index e636e78c..00000000 --- a/docs/plans/2026-07-03-05-agent-installer-workflow.md +++ /dev/null @@ -1,104 +0,0 @@ -# Agent installer workflow - -## Goal - -Provide a top-level installer that configures Codegraph for common agent clients without requiring users to manually assemble MCP config or skill install commands. - -Commands: - -```bash -codegraph install -codegraph uninstall -codegraph install --target codex,claude --yes -codegraph install --print-config codex -``` - -## Design - -Keep existing `skill install` as the lower-level primitive. Add `install` as an orchestration layer that can configure MCP and skills for supported clients. - -Supported initial targets should match clients already documented or implemented locally: - -- `claude` -- `codex` -- `cursor` -- `gemini` -- `opencode` -- `agents` - -Do not add targets whose config format is not verified in this PR. - -## Target interface - -Create a registry: - -```ts -type InstallTarget = { - id: string; - label: string; - detect(): Promise; - printConfig(options: InstallOptions): string; - install(options: InstallOptions): Promise; - uninstall(options: UninstallOptions): Promise; -}; -``` - -Each target owns its file paths, marker comments, merge strategy, and validation. - -## Safety - -- Use marker-fenced blocks for instruction files. -- Preserve user config formatting where practical. -- Never overwrite an unknown config object wholesale. -- Make every install idempotent. -- Make every uninstall idempotent. -- Support `--dry-run` and `--print-config`. -- Require `--yes` for non-interactive writes. - -## CLI behavior - -`codegraph install`: - -- Detect available targets. -- In interactive mode, prompt for targets and location. -- In `--yes` mode, configure detected supported targets with safe defaults. -- Print exact files changed. - -`codegraph uninstall`: - -- Remove only marker-owned blocks or known MCP entries matching Codegraph. -- Leave project indexes, caches, and artifacts untouched unless a separate flag is introduced later. - -## Files likely touched - -- `src/cli/help.ts` -- `src/cli/options.ts` -- `src/cli.ts` -- new `src/installer/registry.ts` -- new `src/installer/targets/*.ts` -- existing skill installer integration -- `docs/installation.md` -- `docs/mcp.md` -- `docs/agent-workflows.md` -- `README.md` -- `codegraph-skill/codegraph/SKILL.md` -- tests under new `tests/installer.test.ts` - -## Tests - -- target detection handles present and missing config dirs. -- `--print-config codex` prints valid TOML snippet. -- install is idempotent for each target. -- uninstall removes only Codegraph-owned entries. -- malformed existing config fails with actionable error. -- `--dry-run` reports changes without writing. - -## Acceptance - -- A user can run one command to configure supported agents. -- Existing manual MCP setup remains documented. -- Existing `skill install` still works. - -## Review pass - -Checked scope: this plan avoids speculative agent targets and preserves the current skill installer. It adds ergonomic orchestration without weakening config safety. diff --git a/docs/plans/2026-07-03-06-explore-facade.md b/docs/plans/2026-07-03-06-explore-facade.md deleted file mode 100644 index 5c70de54..00000000 --- a/docs/plans/2026-07-03-06-explore-facade.md +++ /dev/null @@ -1,127 +0,0 @@ -# Explore facade - -## Goal - -Add one high-level query surface that returns enough structured context for an agent or human to start work without manually sequencing `orient`, `search`, `packet_get`, `refs`, `deps`, and `path`. - -Commands/tools: - -```bash -codegraph explore "how does auth reach db?" --root . -codegraph explore "src/auth.ts" --json -``` - -MCP tool: - -```text -explore -``` - -## Design - -Implement `explore` as a facade over existing primitives, not as a second search engine. - -Pipeline: - -1. Parse query. -2. Detect explicit file paths or handles. -3. Run `searchCodegraph()` for semantic anchors. -4. Fetch bounded packets for top anchors. -5. Add graph paths when query has two recognizable anchors or phrases like "reach", "flow", "call", "through". -6. Add reverse dependencies/blast-radius for primary anchors. -7. Add candidate tests when git context is available or a changed file is referenced. -8. Return omissions and follow-up commands. - -## Output shape - -JSON: - -```ts -type ExploreResponse = { - schemaVersion: 1; - query: string; - analysis: AnalysisSummary; - summary: string[]; - anchors: SearchResult[]; - packets: PacketSummary[]; - paths: DependencyPathSummary[]; - blastRadius: BlastRadiusSummary[]; - candidateTests: string[]; - followUps: string[]; - limits: Record; - omittedCounts: Record; -}; -``` - -Pretty output should be concise: - -```text -Summary -Anchors -Relevant source -Paths -Blast radius -Candidate tests -Follow-ups -Limits -``` - -## Limits - -Defaults must be bounded: - -- max anchors: 5 -- max packets: 3 -- max source lines per packet: existing packet defaults -- max paths: 3 -- max reverse dependencies: 20 - -Expose flags: - -```bash ---limit ---max-packets ---max-paths ---no-source ---json ---pretty -``` - -## MCP behavior - -Keep existing MCP primitives. Add `explore` as the recommended first tool for broad questions. Do not hide existing tools. - -## Files likely touched - -- `src/agent/search.ts` -- `src/agent/packet.ts` -- new `src/agent/explore.ts` -- `src/mcp/tools.ts` -- `src/mcp/server.ts` -- `src/cli/help.ts` -- `src/cli/options.ts` -- new `src/cli/explore.ts` -- `docs/cli.md` -- `docs/mcp.md` -- `docs/agent-workflows.md` -- `codegraph-skill/codegraph/SKILL.md` -- tests under new `tests/agent-explore.test.ts` - -## Tests - -- file-path query returns that file packet and reverse dependencies. -- symbol query returns matching anchor and packet. -- flow query between two files includes dependency path when one exists. -- no-result query returns useful next steps, not an empty crash. -- JSON schema is stable and bounded. -- MCP tool validates flat schema inputs. - -## Acceptance - -- `explore` answers broad repo questions in one call using existing packet/search semantics. -- Existing search/explain/packet behavior remains unchanged. -- Output is bounded, provenance-aware, and includes follow-ups. - -## Review pass - -Checked scope: this plan keeps `explore` as an orchestration layer. It improves agent ergonomics without duplicating search, packet, or graph traversal logic. diff --git a/docs/plans/2026-07-03-07-read-parity-file-view.md b/docs/plans/2026-07-03-07-read-parity-file-view.md deleted file mode 100644 index ff6ef78f..00000000 --- a/docs/plans/2026-07-03-07-read-parity-file-view.md +++ /dev/null @@ -1,125 +0,0 @@ -# Read-parity file view - -## Goal - -Make Codegraph's live file-read surface a practical replacement for raw file reads in agent workflows without making indexed graph access implicit. - -Implemented surfaces: - -- MCP `get_file` -- CLI `codegraph file ` -- exact file-path `explore` responses through `fileView` -- root and agent-subpath library exports - -## Design - -The shared reader returns current disk bytes with predictable line and byte pagination. Graph context and raw sensitive values are separate explicit choices. - -```ts -type AgentFileViewRequest = { - root: string; - file: string; - offset?: number; - limit?: number; - maxBytes?: number; - includeGraphContext?: boolean; - allowSensitive?: boolean; - buildOptions?: BuildOptions; -}; -``` - -Defaults and caps: - -- `offset`: 1-based, default `1` -- `limit`: default `2000` lines, cap `10000` -- `maxBytes`: default `80000`, cap `500000`, applied to unnumbered raw page text including line separators -- input size: hard 16 MiB for raw reads and structural text-config summaries, separate from `maxBytes`; larger inputs reject before unbounded I/O -- `includeGraphContext`: `false` -- `allowSensitive`: `false` - -The graph default is intentionally false. Ordinary reads should not pay for an index build, disclose dependency neighborhoods, or risk conflating stale indexed context with live file content; callers opt in when that extra context is useful. - -## Output - -```ts -type AgentFileViewResponse = { - schemaVersion: 1; - file: string; - offset: number; - limit: number; - totalLines: number; - content: string; - lineFormat: "number-tab-line"; - text: string; - truncated: boolean; - freshness: AgentFreshnessResult; - graphContext?: { - usedBy: string[]; - imports: string[]; - symbols: Array<{ name: string; kind: string; line: number }>; - }; - sensitive?: { - kind: "environment" | "authentication-config" | "credential-config" | "key-material"; - redacted: boolean; - allowSensitiveRequired: true; - }; - page?: { nextOffset?: number }; -}; -``` - -For accepted raw reads, `totalLines` is counted across the complete live file even when only one page is returned. The 16 MiB input limit bounds that counting and the complete-stream binary/UTF-8 validation used for raw reads and structural text-config summaries. `page.nextOffset` is present when another line remains, and a file-ending newline contributes a final empty line. - -`content` is exact unpadded decimal line number, one tab, then source line; `text` is the same selected source without prefixes. For raw pages, the byte budget applies to `text`, so numbered `content` can be larger and a byte boundary can return fewer than `limit` lines. - -```text -File: src/auth.ts -Lines 41-42 of 126 -41 export function authenticate(request) { -42 return verify(request); -Next page: codegraph file src/auth.ts --offset 43 --limit 2 -``` - -At an offset beyond EOF, JSON `content` and `text` are empty, while pretty output says `Lines: none at offset of `. - -`graphContext` appears only when requested and available in the index. It contains at most 100 sorted direct importers, imports, and symbols; `freshness` describes that indexed context separately from the always-live file page. - -An `explore` query consisting only of an indexed project-relative path, or a uniquely matching basename, adds the same response under `fileView`. Disabling source with `includeSource: false` or `--no-source` suppresses it; CLI and library callers pass graph/sensitive options through only when explicit. - -## Safety - -- Constrain paths to `root` or `--root` after final realpath resolution. -- Reject raw reads and structural text-config summaries over the separate 16 MiB hard input-size limit. Within it, validate the complete stream and reject known binary extensions, NUL bytes, and malformed or incomplete UTF-8 before returning a bounded page or extracting bounded keys. -- For default key-material reads, return metadata that may report file size without reading raw secret bytes. Explicit `allowSensitive: true` or `--allow-sensitive` raw access remains subject to the input-size, binary, NUL, and UTF-8 guards, so `.p12` and `.pfx` bundles summarize by default and reject raw access. -- Read live bytes without requiring fresh index state; check freshness only for requested graph context. - -## Implementation surface - -- `src/agent/fileView.ts` -- `src/agent/explore.ts` -- `src/cli/file.ts` and CLI routing/help/options -- `src/mcp/server.ts` and `src/mcp/tools.ts` -- root and agent facade exports -- canonical CLI, workflow, MCP, library, and skill documentation - -## Tests - -- Exact `1\ttext` number-tab-line format and unnumbered `text`. -- 1-based offset, line limit, byte limit, exact whole-file `totalLines`, and `nextOffset` pagination beyond the former prefix. -- 16 MiB input rejection independently of the 500000-byte output-page cap. -- Beyond-EOF offsets return empty JSON content/text and explicit pretty no-lines output. -- Stable trailing empty line for a file-ending newline. -- Root confinement, binary rejection, text-config structural summaries, key-material metadata summaries, and guarded explicit raw-sensitive access. -- Live disk changes remain visible independently of stale index state. -- Graph context is absent by default and bounded when explicitly requested. -- Exact file-path explore responses include `fileView`; broad queries and no-source mode do not. - -## Acceptance - -- Agents can use CLI `file`, MCP `get_file`, library helpers, or exact-path `explore` where they would normally read source. -- Every surface returns the same bounded line-page contract and clear continuation offset. -- Graph context remains explicit opt-in, and live-byte correctness remains separate from index freshness. -- Binary and sensitive formats remain safe by default. - -## Review pass - -The implementation adds one shared file-view contract rather than another packet format. It preserves root confinement, keeps live bytes independent of the index, and intentionally defaults graph context off for safety and predictable read cost. diff --git a/docs/plans/2026-07-03-13-upgrade-command.md b/docs/plans/2026-07-03-13-upgrade-command.md deleted file mode 100644 index c2373c62..00000000 --- a/docs/plans/2026-07-03-13-upgrade-command.md +++ /dev/null @@ -1,97 +0,0 @@ -# Upgrade command - -## Goal - -Add a user-facing command that checks for newer releases and prints safe upgrade instructions for the user's install channel. - -Command: - -```bash -codegraph upgrade --check -codegraph upgrade -codegraph upgrade 1.9.0 -``` - -## Design - -Start with a conservative implementation that detects install channel and prints exact commands. Do not self-modify global installs in the first PR unless the channel is a self-contained bundle installed by our own installer. - -This command depends on the immutable Windows native runtime cache and runtime-version diagnostics in [`2026-07-12-windows-native-runtime-cache-updates.md`](./2026-07-12-windows-native-runtime-cache-updates.md). For npm installs it may report readiness and invoke npm only with explicit consent; it must not kill MCP hosts or replace npm as install authority. - -Install channels: - -- source checkout -- npm package -- release tarball/bundle -- unknown - -## Behavior - -### --check - -- Read current version from package metadata. -- Query GitHub releases or package metadata with a short timeout. -- Print current/latest and whether update is available. -- In CI or offline, fail softly with actionable message unless `--json` requested. - -### upgrade - -For source checkout: - -- Print: pull/build instructions. -- Do not run git commands. - -For npm install: - -- Print exact npm command using the documented scoped package and registry. -- Do not run package manager by default. -- Support `--apply` later if desired, but not in this PR. - -For self-contained bundle: - -- If the installer records install metadata, download and replace atomically. -- Otherwise print installer command. - -## JSON output - -```ts -type UpgradeReport = { - schemaVersion: 1; - currentVersion: string; - latestVersion?: string; - updateAvailable: boolean; - channel: "source" | "npm" | "bundle" | "unknown"; - command?: string; -}; -``` - -## Files likely touched - -- `src/cli/help.ts` -- `src/cli/options.ts` -- `src/cli.ts` -- new `src/cli/upgrade.ts` -- `src/cli/packageInfo.ts` -- `docs/installation.md` -- `docs/cli.md` -- `PUBLISHING.md` -- tests under new `tests/upgrade-command.test.ts` - -## Tests - -- source checkout channel detected from package root. -- npm channel detected when installed package metadata indicates npm path. -- unknown channel prints safe fallback. -- `--check --json` returns stable schema. -- network failure returns clear non-crashing result. -- version argument validates semver. - -## Acceptance - -- Users can discover whether they are outdated. -- The command never corrupts source checkouts or package-manager installs. -- Any auto-apply path is limited to install formats this project owns end to end. - -## Review pass - -Checked scope: this plan prioritizes safety over magic. It improves update discoverability without letting the CLI mutate arbitrary package-manager state. diff --git a/docs/plans/2026-07-03-15-public-docs-benchmarks.md b/docs/plans/2026-07-03-15-public-docs-benchmarks.md deleted file mode 100644 index ee546d16..00000000 --- a/docs/plans/2026-07-03-15-public-docs-benchmarks.md +++ /dev/null @@ -1,88 +0,0 @@ -# Public docs and benchmark evidence - -## Goal - -Add a reproducible benchmark and documentation surface that explains where Codegraph saves time and where it does not, without marketing claims that cannot be re-run. - -## Design - -Create a benchmark harness that runs fixed repo-understanding tasks with and without Codegraph assistance and records tool-call counts, file reads, wall time, and answer completeness checks. - -Do not publish broad performance claims until the harness is reproducible in CI or documented for local reruns. - -## Benchmark structure - -Directory: - -```text -docs/benchmarks/ - README.md - scenarios.json - results.example.json -scripts/benchmarks/ - run-scenario.mjs - summarize-results.mjs -``` - -Scenario schema: - -```json -{ - "id": "repo-orientation-small-ts", - "repo": "local-fixture-or-url", - "task": "Explain how the request reaches the handler", - "expectedAnchors": ["src/server.ts", "src/routes.ts"], - "metrics": ["toolCalls", "fileReads", "wallTimeMs"] -} -``` - -## Documentation - -Add a concise docs page: - -- what was measured -- how to reproduce -- hardware/runtime assumptions -- why file reads and tool calls matter -- where results are expected to vary -- current limitations - -Prefer a modest table over inflated claims. - -## Initial scenarios - -Use local fixtures first so CI can run them: - -- small TypeScript service fixture -- Python import/reference fixture -- SQL migration plus application review fixture -- mixed docs/source graph fixture - -External repo benchmarks can be optional and manually run. - -## Files likely touched - -- new `docs/benchmarks/README.md` -- new `docs/benchmarks/scenarios.json` -- new `scripts/benchmarks/run-scenario.mjs` -- new `scripts/benchmarks/summarize-results.mjs` -- `README.md` docs index link -- `docs/how-it-works.md` or `docs/agent-workflows.md` link -- tests for scenario schema parsing - -## Tests - -- scenario JSON schema validates. -- summarizer handles multiple runs and reports medians. -- benchmark scripts can run against local fixtures without network. -- README table is generated or checked from results fixture. - -## Acceptance - -- A maintainer can run one documented command to reproduce local benchmark examples. -- Public docs explain methodology and limitations. -- No benchmark claim is disconnected from a checked-in scenario or result file. - -## Review pass - -Checked scope: this plan prioritizes reproducibility and modest claims. It creates evidence infrastructure before adding broad public performance messaging. diff --git a/docs/plans/2026-07-03-plan-priority-index.md b/docs/plans/2026-07-03-plan-priority-index.md index a622eb00..1cc6a932 100644 --- a/docs/plans/2026-07-03-plan-priority-index.md +++ b/docs/plans/2026-07-03-plan-priority-index.md @@ -1,93 +1,61 @@ -# Plan priority and dependencies +# Plan priority and status -The 16 numbered plan files follow the original finding order. They are not strict implementation priority order. +This is the live index for plans that still need a decision or implementation. Completed and superseded plans are removed because Git history and merged pull requests preserve their implementation record. -## Recommended implementation order +## Next -### P0: highest immediate user value +These plans describe current, executable work: -1. `2026-07-03-02-mcp-freshness-on-query.md` - - Prevents stale MCP answers after edits. - - Can ship without lifecycle commands or shared server work. -2. `2026-07-03-06-explore-facade.md` - - Gives agents one high-level entry point while preserving existing primitives. - - Can ship on current search/packet/path/session APIs. -3. `2026-07-03-05-agent-installer-workflow.md` - - Reduces setup friction using existing MCP and skill surfaces. - - Does not require bundled distribution. +1. `2026-07-28-explore-first-query-ranking-tuning.md` + - Improve natural-language ranking, explore composition, and candidate-test selection. +2. `2026-07-25-performance-program-index.md` + - Keep the shared performance baseline and coordinate the remaining Git, native-startup, and hydration work. +3. `2026-07-25-git-subprocess-elimination.md` + - Continue the measured work after implemented priorities 0 and 1. +4. `2026-07-25-native-runtime-startup.md` + - Reduce native fingerprint and startup costs after validating installed-user impact. +5. `2026-07-25-warm-index-hydrate-costs.md` + - Continue only the hydration cuts that preserve sidecar tamper and semantic validation. -### P1: useful foundations and narrow CLI wins +## Planned -4. `2026-07-03-01-project-lifecycle-commands.md` - - Adds `init`, `status`, `sync`, and `uninit` mental model. - - Useful foundation for freshness and installer messaging, but not a hard dependency. -5. `2026-07-03-12-affected-tests-command.md` - - Small, high-value CLI on existing impact/candidate-test logic. -6. `2026-07-03-16-config-extension-mapping.md` - - Low-risk config improvement. - - Should land before broad language expansion to avoid touching language detection twice. -7. `2026-07-03-07-read-parity-file-view.md` - - Improves agent file-read behavior. - - Strengthens `explore`, but `explore` does not need to wait for it. +These plans are unimplemented and remain distinct product outcomes: -### P2: long-running server and graph-intelligence expansion +- `2026-05-14-packaged-graph-viewer.md` +- `2026-05-27-agent-test-plan-generation.md` +- `2026-07-03-03-shared-server-lifecycle.md` +- `2026-07-03-08-framework-route-nodes.md` +- `2026-07-03-09-mobile-bridge-edges.md` +- `2026-07-03-10-dispatch-synthesizers.md` +- `2026-07-03-12-affected-tests-command.md` +- `2026-07-03-14-privacy-preserving-diagnostics.md` +- `2026-07-03-16-config-extension-mapping.md` -8. `2026-07-03-03-shared-server-lifecycle.md` - - Builds on existing HTTP MCP server. - - Benefits from freshness-on-query, but can be implemented independently. -9. `2026-07-03-10-dispatch-synthesizers.md` - - Best first step for synthetic/provenance-tagged graph edges. - - Provides a reusable pattern for later bridge edges. -10. `2026-07-03-08-framework-route-nodes.md` - - Can ship independently, but should reuse any synthesized-edge provenance conventions if plan 10 lands first. -11. `2026-07-03-11-source-language-expansion.md` - - Should follow extension mapping if both are planned. - - One language per PR. -12. `2026-07-03-09-mobile-bridge-edges.md` - - Should follow the synthesized-edge framework from plan 10 if possible. - - Highest risk of heuristic debt; keep narrow. +The affected-tests plan has an old open implementation in PR #146. Reconcile that branch against current `main` before treating it as executable work. -### P3: adoption and release polish +## Needs reconciliation -13. `2026-07-03-15-public-docs-benchmarks.md` - - More credible after freshness/explore improvements land. - - Can start with local fixtures anytime. -14. `2026-07-03-04-self-contained-distribution.md` - - Important for broad adoption, but larger operational surface. - - Better after installer semantics stabilize. -15. `2026-07-03-13-upgrade-command.md` - - Most valuable after bundled/self-contained distribution exists. - - Can start as safe `--check` and printed instructions earlier. -16. `2026-07-03-14-privacy-preserving-diagnostics.md` - - Useful support feature, but not core product behavior. - - Independent. +These plans mix completed work, overlapping scope, or measurement-gated follow-ups. Do not implement or delete them until the remaining outcome is restated: -## Dependency notes +- `2026-05-12-graph-first-language-expansion.md` + - Consolidate with `2026-07-03-11-source-language-expansion.md` into one language-expansion owner. +- `2026-07-03-11-source-language-expansion.md` + - Preserve the narrow vertical-slice guidance when consolidating language work. +- `2026-06-06-performance-and-cache-opportunities.md` + - Most ranked work shipped; move only still-validated gaps into the performance program. +- `2026-07-03-core-command-performance-cache-sync.md` + - Request-time freshness replaced parts of the interval/cache-sync design. +- `2026-07-21-warm-run-discovery-avoidance.md` + - Priorities 1-4 and audit ranks 1-3 shipped; decide whether the remaining stretch items still justify ownership. -Hard dependencies are minimal. Most plans are intentionally single-PR vertical slices. +## Deferred or rejected -```text -16 config extension mapping -> 11 source language expansion -10 dispatch synthesizers -> 09 mobile bridge edges -02 MCP freshness -> 03 shared server lifecycle (recommended, not required) -07 read-parity file view -> 06 explore facade (helpful, not required) -04 self-contained distribution -> 13 upgrade command (for self-update, not for --check) -01 project lifecycle -> 05 agent installer workflow (helpful messaging, not required) -``` +- A check-only `upgrade` command is rejected because the name implies an update that it would not perform. +- Any future `upgrade` must execute channel-specific updates, confirm unless `--yes`, stream and propagate subprocess results, verify the resulting version, and safely handle permissions, dirty or detached source trees, and Windows runtime locking. +- More language count, graph UI work, and new CLI commands remain lower priority until current semantic quality and primary workflows justify them. -## Suggested first three PRs +## Status rules -1. MCP freshness-on-query. -2. Explore facade. -3. Agent installer workflow. - -Reason: these three improve day-to-day agent usefulness without large parser, release, or heuristic-risk changes. - -## Suggested graph-intelligence sequence - -1. Dispatch synthesizer framework with one concrete pattern. -2. Framework route nodes for one or two frameworks. -3. Mobile bridge edges for one bridge family. -4. Source language expansion one language at a time. - -Reason: provenance and confidence metadata should be standardized before adding multiple heuristic graph sources. +- Every new plan must appear here as `next`, `planned`, `blocked`, or `needs reconciliation`. +- A plan leaves this index when its outcome is merged, explicitly rejected, or superseded. +- Completed and superseded plan files should be removed instead of retained as a second roadmap. diff --git a/docs/plans/2026-07-12-ide-style-agent-navigation-refactor.md b/docs/plans/2026-07-12-ide-style-agent-navigation-refactor.md deleted file mode 100644 index a6b4fb8d..00000000 --- a/docs/plans/2026-07-12-ide-style-agent-navigation-refactor.md +++ /dev/null @@ -1,1089 +0,0 @@ -# IDE-style agent navigation and refactor plan - -Date: 2026-07-12 - -## Goal - -Make Codegraph expose the semantic operations an agent expects from an IDE or language server while preserving Codegraph's differentiators: deterministic bounded output, stable handles, cross-language operation, root confinement, freshness metadata, explicit provenance, omission counts, and review-grade evidence. - -The target workflow is: - -```text -find a symbol - -> inspect definition - -> inspect references/callers/callees/type relationships - -> preview a rename or refactor - -> review exact edits, ambiguities, omissions, and tests - -> let the agent apply edits with its existing editing tool -``` - -This plan must be implementable without context from the comparison discussion that produced it. Treat the repository state at implementation time as authoritative and preserve existing public contracts unless this plan explicitly changes them. - -## Product decision - -Codegraph should become an IDE-like semantic evidence service for agents, not a competing editor. - -- Add workspace-symbol lookup, call hierarchy, type hierarchy, implementation lookup, and rename preview. -- Keep `goto`, `refs`, `search`, `get_symbol`, `deps`, `rdeps`, `path`, `explore`, and file views. -- Return semantic edit plans; do not apply source edits in the initial implementation. -- Never convert ambiguous or heuristic sites into automatic rename edits. -- Make unsupported or degraded behavior explicit per language and per result. -- Preserve live-file versus indexed-context freshness semantics. - -## Current foundations - -Reuse these existing surfaces instead of creating parallel infrastructure: - -- `src/indexer/navigation.ts` - - `goToDefinition()` - - `findReferences()` -- `src/indexer/symbols.ts` - - `listSymbols()` - - `resolveSymbolId()` - - `goToDefinitionById()` - - `findReferencesById()` -- `src/indexer/types.ts` - - `ProjectIndex` - - `SymbolDef` - - `SymbolHandle` - - `Reference` - - `GoToResult` - - `FindReferencesResult` -- `src/agent/handles.ts` - - portable `symbol:`, `file:`, `chunk:`, `graph:`, and `sql:` handles -- `src/agent/symbolLookup.ts` - - snapshot symbol lookup maps -- `src/agent/search.ts` - - deterministic ranking, portable handles, provenance, bounded evidence, and follow-ups -- `src/agent/explain.ts` - - portable-handle resolution and bounded references/snippets -- `src/agent/session.ts` - - warm snapshot reuse and MCP freshness integration -- `src/mcp/server.ts` - - typed handlers and `withFreshness()` wrapping -- `src/mcp/tools.ts` - - flat MCP schemas -- `src/agent-tools.ts` - - public tool wrappers and path normalization -- `src/graphs/symbol-graph.ts` and `src/graphs/symbol-graph-detailed.ts` - - existing symbol nodes and semantic edges -- `src/impact/*` and `src/review.ts` - - changed-symbol handles, candidate tests, risk, and review evidence - -Existing tests to extend rather than replace: - -- `tests/goto.test.ts` -- `tests/references.test.ts` -- `tests/handles.test.ts` -- `tests/agent-search.test.ts` -- `tests/agent-explain.test.ts` -- `tests/agent-tools.test.ts` -- `tests/mcp-server.test.ts` -- `tests/native-semantic-parity.test.ts` -- nearest `tests/languages/*.test.ts` suites for language-specific behavior - -## Scope - -### Required features - -1. Workspace-symbol lookup. -2. Incoming and outgoing call hierarchy. -3. Supertypes and subtypes. -4. Interface/trait/abstract-member implementation lookup. -5. Rename preview with exact edits and explicit unsafe sites. -6. Refactor evidence packet that composes the operations above. -7. CLI, MCP, library, and agent-tool surfaces for each operation. -8. Stable schemas, bounded output, freshness, analysis metadata, and omission counts. -9. Documentation and cross-language parity updates. -10. Focused performance and correctness benchmarks. - -### Non-goals - -- Applying edits to the worktree. -- Formatting files. -- General extract-method or move-class automation. -- Compiler-complete type checking. -- Renaming strings, comments, filenames, SQL text, or generated code by default. -- Pretending every supported language has identical semantic depth. -- Adding an LSP server protocol endpoint. -- Replacing existing MCP primitives with one new facade. -- Broad framework dispatch synthesis unrelated to a required feature. - -## Cross-cutting response contract - -All new agent-facing responses must share one envelope. Reuse existing freshness and analysis types where possible; do not create duplicate definitions with subtly different fields. - -```ts -type SemanticResponseEnvelope = { - schemaVersion: 1; - root: string; - analysis: AnalysisSummary; - freshness: CodegraphMcpFreshness; - limits: Record; - omittedCounts: Record; -}; -``` - -MCP may continue wrapping tool payloads with its existing freshness helper. CLI/library results that do not use the MCP wrapper must still expose equivalent analysis, limits, and omissions. Do not add a generation identifier until the shared-server/snapshot-generation work defines a canonical generation contract. - -### Common location - -Use one location type for declaration, reference, callsite, hierarchy, and edit ranges: - -```ts -type SemanticLocation = { - file: string; - range: Range; - context?: string; -}; -``` - -Requirements: - -- Files are project-relative in public CLI, MCP, and agent-tool output. -- Lines remain 1-based and columns remain consistent with existing navigation contracts. -- Ranges are exact source ranges, not whole lines, when the parser provides them. -- `context` is bounded and optional. -- Results sort deterministically by normalized file, start line, start column, then stable handle. - -### Common provenance - -```ts -type SemanticProvenance = { - capability: "semantic" | "graph" | "heuristic"; - backend: "native" | "reduced" | "mixed" | "unknown"; - confidence: "high" | "medium" | "low"; - reason?: string; -}; -``` - -Reuse the repository's existing analysis/provenance vocabulary if field names differ. The invariant is more important than this exact alias: every nontrivial semantic relationship must state how it was obtained and how safe it is to use. - -## Feature 1: workspace-symbol lookup - -### User contract - -CLI: - -```bash -codegraph symbols "CodeReviewSession" --root . --json -codegraph symbols "review report" --kind class,function --exported --limit 50 -``` - -MCP: - -```text -workspace_symbols(query, kinds?, exportedOnly?, includeImports?, fileGlob?, limit?) -``` - -Library: - -```ts -workspaceSymbols(indexOrSession, request); -``` - -Agent tool: - -```ts -tool_workspaceSymbols(root, request, runtimeOptions?) -``` - -Do not overload `search`. Workspace-symbol lookup has symbol identity semantics and predictable filters; `search` remains hybrid relevance search across paths, text, SQL, and graph evidence. - -### Request - -```ts -type WorkspaceSymbolsRequest = { - query: string; - kinds?: SymbolKind[]; - exportedOnly?: boolean; - includeImports?: boolean; - fileGlob?: string; - limit?: number; -}; -``` - -Defaults: - -- `limit`: 50. -- Maximum `limit`: 500. -- `exportedOnly`: false. -- `includeImports`: false. -- Empty or whitespace-only query is rejected unless a narrow `fileGlob` or `kinds` filter is supplied. Do not allow an unbounded whole-index dump. - -### Response - -```ts -type WorkspaceSymbolItem = { - handle: string; - name: string; - localName: string; - qualifiedName?: string; - kind: SymbolKind; - location: SemanticLocation; - exported: boolean; - provenance: SemanticProvenance; -}; - -type WorkspaceSymbolsResponse = SemanticResponseEnvelope & { - query: string; - symbols: WorkspaceSymbolItem[]; -}; -``` - -### Matching and ranking - -Order by: - -1. Exact qualified-name match. -2. Exact local/exported-name match. -3. Case-insensitive exact match. -4. Prefix match. -5. Tokenized camelCase/PascalCase/snake_case match. -6. Remaining deterministic substring match. - -Then prefer: - -- exported over non-exported when scores tie; -- definitions over import aliases unless `includeImports` is true and the alias is the exact query; -- source over tests over docs only as a tie-breaker, not as hidden filtering; -- normalized file/range/handle ordering for final stability. - -Do not use vector search. Do not let ordinary prose tokens outrank an exact symbol identity. - -### Implementation shape - -- Add `src/indexer/workspace-symbols.ts` for index-level filtering/ranking. -- Add `src/agent/workspaceSymbols.ts` for session loading, portable handles, public normalization, limits, analysis, and omissions. -- Reuse or extend `buildSymbolLookup()`; do not rebuild independent symbol maps on every query. -- If the lookup is expensive, cache it on `AgentProjectSnapshot` or session state and invalidate with the snapshot. -- Exclude graph-only nodes that cannot resolve to a real `SymbolDef` from symbol handles. -- Namespace/star aliases without a real `SymbolDef` remain excluded and contribute explicit import omissions rather than receiving incompatible handles. - -### Acceptance tests - -- Exact name outranks prose/token matches. -- Qualified name disambiguates same-named symbols. -- Shadowed locals remain separate. -- Imports are excluded by default and included when requested. -- Exported filtering is correct. -- Kind and file-glob filters compose. -- Results are project-relative and deterministic. -- Limit is applied after ranking and omission count records the remainder. -- Reduced mode reports capability honestly. -- SQL objects remain under SQL/search surfaces unless intentionally represented as `SymbolDef`; do not silently mix incompatible handle types. - -## Feature 2: call hierarchy - -### User contract - -CLI: - -```bash -codegraph callers --depth 1 --limit 100 --json -codegraph callees --depth 2 --limit 100 -``` - -MCP: - -```text -callers(handle, depth?, limit?, includeHeuristic?) -callees(handle, depth?, limit?, includeHeuristic?) -``` - -Library: - -```ts -findCallers(indexOrSession, request); -findCallees(indexOrSession, request); -``` - -### Relationship model - -```ts -type CallHierarchyEntry = { - symbol: WorkspaceSymbolItem; - callsites: SemanticLocation[]; - depth: number; - provenance: SemanticProvenance; -}; - -type CallHierarchyResponse = SemanticResponseEnvelope & { - target: WorkspaceSymbolItem; - direction: "incoming" | "outgoing"; - entries: CallHierarchyEntry[]; -}; -``` - -One entry represents one caller/callee symbol. Multiple callsites between the same pair are grouped and bounded. Do not emit one duplicated symbol entry per callsite. - -### Semantics - -- `callers`: symbols containing semantic callsites that resolve to the target. -- `callees`: semantic targets called from within the target symbol's source range. -- Depth 1 is direct only. -- Maximum depth is 5. -- Cycle traversal uses visited symbol handles and still reports direct cycle edges once. -- Callsites must be real ranges from source evidence. -- A file-level dependency edge is not a call edge. -- Imports, inheritance, route metadata, and document links are not call edges. -- Heuristic/synthesized calls are excluded by default unless existing graph provenance already marks them and `includeHeuristic` is true. -- If a language cannot attribute a callsite to a containing caller symbol, report a bounded file-level unresolved site separately rather than inventing a caller symbol. - -### Required index work - -Inspect current native and JS extraction contracts before editing. Prefer adding callsite ownership to existing reference/call records over reparsing source for every query. - -Add a query-ready structure if the current graph cannot answer both directions efficiently: - -```ts -type CallHierarchyIndex = { - outgoingByCaller: Map; - incomingByCallee: Map; -}; - -type CallEdge = { - caller: SymbolHandle; - callee: SymbolHandle; - callsite: SemanticLocation; - provenance: SemanticProvenance; -}; -``` - -Requirements: - -- Construct once per project snapshot, lazily if not needed by normal indexing. -- Cache on the snapshot/session, not in an unbounded global map. -- Invalidate with the snapshot. -- Bound fan-out and report omitted symbols and callsites separately. -- Avoid copying whole `SymbolDef` objects into every edge; retain stable handles and resolve on output. - -### Files likely touched - -- `src/indexer/types.ts` -- `src/indexer/navigation.ts` or new `src/indexer/call-hierarchy.ts` -- native contracts under `src/native/` and `packages/codegraph-native` only if existing extraction lacks callsite target/owner data -- `src/agent/callHierarchy.ts` -- `src/agent/session.ts` -- `src/agent-tools.ts` -- `src/mcp/server.ts` -- `src/mcp/tools.ts` -- CLI command/help/options modules - -### Acceptance tests - -- Direct caller and callee with exact callsite range. -- Multiple callsites group under one relationship. -- Nested functions attribute calls to the nearest containing symbol. -- Recursive call reports once without infinite traversal. -- Depth traversal is deterministic and bounded. -- Same-named methods on different receiver types do not cross-link. -- Unresolved dynamic calls are omitted or explicitly listed as unresolved; never guessed. -- Native and supported reduced behavior match documented capability. -- Large fan-out reports symbol and callsite omissions. -- Existing `refs` semantics remain unchanged. - -## Feature 3: type hierarchy - -### User contract - -CLI: - -```bash -codegraph supertypes --json -codegraph subtypes --depth 2 --limit 100 -``` - -MCP: - -```text -supertypes(handle, depth?, limit?) -subtypes(handle, depth?, limit?) -``` - -### Response - -```ts -type TypeHierarchyRelation = { - type: WorkspaceSymbolItem; - relation: "extends" | "implements" | "trait" | "mixin" | "unknown"; - declarationSite?: SemanticLocation; - depth: number; - provenance: SemanticProvenance; -}; - -type TypeHierarchyResponse = SemanticResponseEnvelope & { - target: WorkspaceSymbolItem; - direction: "super" | "sub"; - relations: TypeHierarchyRelation[]; -}; -``` - -### Semantics - -- Only type-like symbols are valid targets. -- Reuse existing detailed symbol-graph inheritance edges where they resolve to actual definitions. -- Distinguish `extends` from `implements` when extraction data supports it. -- Do not collapse a class and same-named interface in another namespace/package. -- Preserve language scoping and import resolution. -- Maximum depth is 10; default 1. -- Cycles are bounded and reported once. -- Ambiguous bases are not linked. - -### Implementation shape - -- Add `src/indexer/type-hierarchy.ts` or a graph query module operating on existing detailed symbol graph. -- Build forward and reverse adjacency once per snapshot. -- If current edges lose relation kind or declaration site, extend edge metadata at extraction time rather than guessing during query. -- Keep this feature independent of framework route or dynamic-dispatch synthesizers. - -### Acceptance tests - -Cover at least the existing supported forms used by current semantic parity tests: - -- TypeScript/JavaScript class extension and interface implementation where supported. -- Java/C#/Kotlin class/interface relationships. -- Rust trait implementation where current extraction can prove it. -- Swift protocol conformance where current extraction can prove it. -- C++ inheritance. -- Python class inheritance. -- Ambiguous same-name bases remain unresolved. -- Transitive hierarchy, cycles, limits, and omission counts. - -When the feature changes a claimed cross-language capability, update `tests/native-semantic-parity.test.ts`, the nearest `tests/languages/*.test.ts`, `docs/language-parity.md`, and `docs/scenario-catalog.md` in the same change. - -## Feature 4: implementation lookup - -### User contract - -CLI: - -```bash -codegraph implementations --limit 100 --json -``` - -MCP: - -```text -implementations(handle, limit?) -``` - -### Target types - -Accept: - -- interface or trait type; -- interface/trait/abstract member; -- abstract/virtual method where override relationships are statically established. - -Reject ordinary concrete symbols with a clear target-kind error rather than returning an empty success that looks authoritative. - -### Response - -```ts -type ImplementationEntry = { - symbol: WorkspaceSymbolItem; - relationSite?: SemanticLocation; - provenance: SemanticProvenance; -}; - -type ImplementationsResponse = SemanticResponseEnvelope & { - target: WorkspaceSymbolItem; - implementations: ImplementationEntry[]; -}; -``` - -### Resolution rules - -- Type implementations derive from proven `implements`, trait, protocol, or equivalent hierarchy edges. -- Member implementations require both a proven type relationship and compatible member identity under the language's existing resolver. -- Do not use name-only global matching. -- Reuse existing receiver-aware/member-resolution logic where possible. -- Overloads remain distinct when the index preserves signature/arity data. -- If the index lacks enough information to distinguish overloads, return ambiguity metadata rather than all same-named methods as high-confidence results. - -### Acceptance tests - -- Interface type to implementing types. -- Interface method to concrete method. -- Abstract base method to overrides. -- Same method name on unrelated types excluded. -- Inherited implementation included only once. -- Ambiguous overloads downgraded or excluded with reason. -- Unsupported language returns explicit capability limitation. - -## Feature 5: rename preview - -### User contract - -CLI: - -```bash -codegraph rename-preview --json -codegraph rename-preview --include-comments -``` - -MCP: - -```text -rename_preview(handle, newName, includeComments?, includeStrings?, includeFilenames?, maxEdits?) -``` - -Library: - -```ts -previewRename(indexOrSession, request); -``` - -No `rename --apply` command is part of this plan. - -### Request - -```ts -type RenamePreviewRequest = { - handle: string; - newName: string; - includeComments?: boolean; - includeStrings?: boolean; - includeFilenames?: boolean; - maxEdits?: number; -}; -``` - -Defaults: - -- comments: false; -- strings: false; -- filenames: false; -- maximum edits: 5,000; -- reject empty names, path separators, NUL, and names that are syntactically invalid for the target language when a validator exists; -- if no language validator exists, validate conservatively as an identifier and report the limitation. - -### Response - -```ts -type RenameEdit = { - file: string; - range: Range; - oldText: string; - newText: string; - kind: "definition" | "reference" | "import" | "export" | "comment" | "string" | "filename"; - provenance: SemanticProvenance; -}; - -type RenameUnsafeSite = { - location: SemanticLocation; - text: string; - reason: - | "ambiguous_target" - | "unresolved_reference" - | "dynamic_access" - | "unsupported_syntax" - | "parse_degraded" - | "generated_file" - | "sensitive_file" - | "outside_root" - | "limit_exceeded"; - provenance: SemanticProvenance; -}; - -type RenameConflict = { - file: string; - location?: SemanticLocation; - reason: "name_collision" | "shadowing" | "duplicate_export" | "invalid_identifier" | "case_only_filesystem_risk"; - existingHandle?: string; - message: string; -}; - -type RenamePreviewResponse = SemanticResponseEnvelope & { - target: WorkspaceSymbolItem; - newName: string; - safe: boolean; - edits: RenameEdit[]; - unsafeSites: RenameUnsafeSite[]; - conflicts: RenameConflict[]; - candidateTests: Array<{ file: string; confidence: "high" | "medium" | "low"; reason: string }>; -}; -``` - -`safe` is true only when: - -- the definition resolves uniquely; -- all semantic references selected for editing resolve to that definition; -- no high-confidence collision exists; -- no parse-degraded file that could contain references is known; -- no edit or candidate scan was truncated; -- freshness is `fresh` or `refreshed`; -- every edit range still contains `oldText` in the live file bytes at response time. - -A response with `safe: false` is still useful as evidence. It must never be presented as safe merely because ambiguous sites were omitted. - -### Edit construction - -1. Resolve the portable symbol handle to exactly one `SymbolDef`. -2. Run semantic references using the same snapshot. -3. Include the declaration range. -4. Classify import/export sites using existing `Reference.via` data or equivalent provenance. -5. Read live bytes for every candidate file after root confinement. -6. Verify each range still contains the expected old text. -7. Reject overlapping edits unless they are byte-identical duplicates; deduplicate those. -8. Sort edits by file and ascending range for output. Consumers applying edits should apply in reverse-offset order per file, but Codegraph does not apply them. -9. Compute collisions in the target scope before declaring `safe`. -10. Add candidate tests through existing impact/review candidate-test logic; do not introduce separate test-pattern heuristics. - -### Comments and strings - -Comments and strings are opt-in, always lower confidence, and never required for `safe: true` unless the caller opted in. - -- Search only files already in the project snapshot and within root. -- Return occurrences as candidate edits with heuristic provenance. -- Do not rename partial words by default. -- Do not interpret serialized data, reflection names, routes, SQL strings, or framework metadata as semantic references unless an existing extractor proves the relationship. -- Sensitive files follow existing file-view redaction and access rules. - -### Filename suggestions - -`includeFilenames` returns suggestions, not normal source edits. Do not move files in this plan. - -If a filename convention exactly matches a uniquely resolved public type, return a `filename` suggestion with case-sensitivity risk metadata. Never infer broad folder renames. - -### Collision checks - -Minimum required checks: - -- Same-scope local already uses `newName`. -- Same module already exports `newName`. -- Import alias would collide in a consumer. -- Member rename collides on the same type. -- Case-only rename risk on case-insensitive filesystems. -- New name is invalid for the target language. - -If a language-specific scope rule cannot be proven, mark the limitation and do not declare the preview safe. - -### Acceptance tests - -- Rename exported function across definition, imports, aliases, and calls. -- Rename local symbol without touching shadowed same-name locals. -- Rename method only for the resolved receiver type. -- Rename interface method and implementations only when implementation relationships are proven. -- Same-name unrelated methods remain unchanged. -- Collision produces `safe: false` and no misleading safe plan. -- Stale live range produces unsafe site instead of an edit. -- Changed file during preview cannot yield `safe: true`. -- Ambiguous and unresolved candidates are surfaced. -- Comments/strings excluded by default and bounded when included. -- Overlapping edits deduplicate or fail clearly. -- Project-relative output and root confinement. -- Deleted files and malformed UTF-8 handled without crashing. -- Large rename truncation forces `safe: false` and reports omissions. -- Native/reduced mode differences are explicit. - -## Feature 6: refactor evidence packet - -### Purpose - -Agents should not manually stitch workspace-symbol, references, hierarchy, call hierarchy, and rename results together for every refactor. - -Add a read-only composition surface: - -CLI: - -```bash -codegraph refactor-plan --rename --json -``` - -MCP: - -```text -refactor_plan(handle, renameTo?, maxReferences?, maxCallers?, maxHierarchy?, includeSource?) -``` - -### Response - -```ts -type RefactorPlanResponse = SemanticResponseEnvelope & { - target: WorkspaceSymbolItem; - definition: SemanticLocation; - references: SemanticLocation[]; - callers: CallHierarchyEntry[]; - callees: CallHierarchyEntry[]; - supertypes: TypeHierarchyRelation[]; - subtypes: TypeHierarchyRelation[]; - implementations: ImplementationEntry[]; - rename?: RenamePreviewResponse; - candidateTests: Array<{ file: string; confidence: "high" | "medium" | "low"; reason: string }>; - followUps: string[]; -}; -``` - -### Composition rules - -- Use one loaded snapshot and one freshness decision for the whole packet. -- Do not call public wrappers that each rebuild or reload the project. -- Deduplicate shared symbol/location data. -- Give each section an independent limit and omission count. -- Default source inclusion to off; stable handles and exact ranges are sufficient for most agents. -- Follow-ups must be copyable CLI commands using portable handles. -- If `renameTo` is absent, return analysis only. -- A rename's `safe` decision remains authoritative; the wrapper must not reinterpret it. - -## CLI design - -Add focused command modules following current conventions: - -- `src/cli/symbols.ts` -- `src/cli/callHierarchy.ts` -- `src/cli/typeHierarchy.ts` -- `src/cli/implementations.ts` -- `src/cli/renamePreview.ts` -- `src/cli/refactorPlan.ts` - -Update: - -- `src/cli.ts` dispatch -- `src/cli/help.ts` -- `src/cli/options.ts` -- `tests/cli-command-modules.test.ts` -- `tests/cli-options-validation.test.ts` -- focused CLI integration tests for JSON and pretty contracts - -Requirements: - -- JSON is stable and machine-first. -- Pretty output is concise and includes the target, result counts, unsafe/conflict summary, omissions, and copyable follow-ups. -- All numeric options use existing integer/range validation. -- All paths follow `--root` normalization and confinement. -- Commands reuse shared index flags: cache, native, workers, discovery globs, and gitignore behavior where applicable. - -## MCP design - -Add tools: - -- `workspace_symbols` -- `callers` -- `callees` -- `supertypes` -- `subtypes` -- `implementations` -- `rename_preview` -- `refactor_plan` - -Requirements: - -- Keep schemas flat JSON objects. -- Tool descriptions state when to use the new tool instead of `search`, `refs`, or file-level `deps`. -- Every index-backed handler uses the existing freshness gate. -- No per-request root override. -- Read-only default remains unchanged. -- Rename preview does not require MCP write access because it writes nothing. -- Limits are server-bounded even if the client requests more. -- Add tools to the operating pattern in `docs/mcp.md` and the bundled skill. - -## Library and package exports - -Expose stable entry points through existing public modules rather than adding a new package subpath unless the current public API organization requires it. - -Likely exports: - -```ts -export { - workspaceSymbols, - findCallers, - findCallees, - findSupertypes, - findSubtypes, - findImplementations, - previewRename, - buildRefactorPlan, -}; -``` - -Also export request/response types. - -Update: - -- `src/index.ts` -- `src/agent.ts` or `@lzehrung/codegraph/agent` exports -- `src/indexer.ts` for index-level operations if consistent with existing boundaries -- `docs/library-api.md` -- package export tests - -Do not expose internal caches or mutable adjacency maps. - -## Stable-handle behavior - -Current portable agent handles encode project-relative file, symbol name, line, and column. Preserve that format for this plan unless a separate migration is approved. - -Required behavior: - -- Every new result uses portable handles, never absolute-path symbol IDs. -- A handle resolves within the current root only. -- A handle that no longer resolves after refresh returns a specific stale/missing-handle result and recommends workspace-symbol lookup; do not silently bind to a same-named symbol elsewhere. -- Rename preview validates the live declaration text even when a handle resolves in the snapshot. -- Review/impact handles should flow into refactor operations when they resolve to symbol handles; add conversion only where identity is proven. - -Do not add compatibility aliases or a second symbol-handle format in this feature series. - -## Freshness and concurrency - -Every operation except raw file reads depends on indexed state. - -- Use the current MCP/session freshness gate. -- A composed refactor plan uses one snapshot for all sections. -- If auto-refresh succeeds, report `refreshed`. -- If freshness is stale, navigation may return bounded evidence under the existing policy, but rename preview must set `safe: false`. -- Verify rename edit ranges against live bytes after semantic analysis. -- If files change during range verification, return unsafe sites and do not claim safety. -- Do not hold global locks while reading source or formatting responses. -- Cache query-ready structures by snapshot/session and release them when the session invalidates. - -## Performance requirements - -The feature is incomplete if it works only by rebuilding detailed graphs or scanning every file for every query. - -### Targets to establish before optimization - -Add deterministic benchmark fixtures and record environment metadata. Measure at minimum: - -- workspace-symbol exact lookup; -- direct callers and callees; -- depth-3 call hierarchy; -- direct subtypes and implementations; -- rename preview with 10, 100, and 1,000 references; -- repeated warm calls through one session; -- peak RSS for hierarchy-index construction. - -Do not publish universal latency claims from tiny fixtures. Use these benchmarks to catch regressions and compare implementation strategies. - -### Complexity expectations - -- Workspace exact-name lookup: expected near `O(matches)` after cached lookup construction. -- Direct callers/callees: expected `O(edges for target)` after adjacency construction. -- Type hierarchy: expected `O(reachable hierarchy edges)` within depth/limit. -- Rename preview: expected `O(candidate references + edited file bytes)`, not all project files unless opt-in comments/strings require text scanning. -- Repeated warm queries must not rebuild lookup/adjacency structures. - -### Memory constraints - -- Store handles/ranges in adjacency, not duplicate source text or full symbol objects. -- Bound callsites retained per relationship if extraction can generate extreme duplicates; preserve total/omitted counts. -- Avoid global caches keyed by arbitrary roots. -- Session disposal must release derived indexes. - -## Stability and safety requirements - -- Root confinement on every file read and returned path. -- No writes from any feature in this plan. -- No unsafe rename plan from stale, truncated, degraded, or ambiguous evidence. -- Parser failures become diagnostics/unsafe sites, not crashes. -- Unsupported languages return explicit limitations. -- Deterministic sorting for every collection. -- Numeric and payload limits enforced in CLI, MCP, and library wrappers. -- No source contents from sensitive files unless existing policy allows them. -- No `any`, `as unknown as`, nested ternaries, or nonstandard quote characters. - -## Implementation sequence - -Each phase must leave the repository compiling and the existing CLI/MCP behavior intact. Prefer one PR per phase. - -### Phase 1: shared semantic contracts and workspace symbols - -1. Define common response, location, provenance, limit, and omission helpers by reusing existing types. -2. Implement cached workspace-symbol lookup and deterministic ranking. -3. Add portable agent response normalization. -4. Add library and agent-tool exports. -5. Add CLI `symbols` and MCP `workspace_symbols`. -6. Add focused tests and docs. - -Exit criteria: - -- An agent can resolve an exact/qualified symbol without hybrid search. -- Every result has a portable handle and exact declaration range. -- Existing search ordering and schemas are unchanged. - -### Phase 2: type hierarchy and implementations - -1. Inventory existing inheritance/implementation edge metadata across supported languages. -2. Add forward/reverse type adjacency per snapshot. -3. Implement supertype/subtype queries. -4. Implement type and member implementation lookup. -5. Add CLI/MCP/library surfaces. -6. Add parity fixtures and documentation with explicit supported/unsupported forms. - -Exit criteria: - -- Type relationships do not use global name-only matching. -- Ambiguity and unsupported forms are visible. -- Cross-language capability claims have matching tests/docs. - -### Phase 3: call hierarchy - -1. Inventory call-edge ownership and callsite range data. -2. Extend extraction contracts only where required. -3. Build lazy incoming/outgoing call adjacency. -4. Implement direct and bounded transitive queries. -5. Add CLI/MCP/library surfaces. -6. Add call hierarchy fixtures, cycle tests, and performance baselines. - -Exit criteria: - -- Direct callers/callees return containing symbols and exact callsites. -- Same-named unrelated methods do not cross-link. -- Warm repeat queries reuse derived indexes. - -### Phase 4: rename preview - -1. Implement target resolution and language-aware new-name validation. -2. Construct definition/reference/import/export edits from semantic evidence. -3. Add live-range verification, deduplication, overlap detection, and collision checks. -4. Add unsafe-site and conflict reporting. -5. Integrate candidate-test logic. -6. Add opt-in comment/string candidates and filename suggestions only after semantic rename is correct. -7. Add CLI/MCP/library surfaces and exhaustive safety tests. - -Exit criteria: - -- No source writes. -- `safe: true` requires fresh, complete, nontruncated, nonambiguous, live-verified evidence. -- A plausible stale/ambiguous/collision bug is caught by a deterministic test. - -### Phase 5: refactor evidence packet and workflow integration - -1. Compose all semantic operations on one snapshot. -2. Add review/impact handle handoff where identity is already available. -3. Add bounded follow-ups and optional rename preview. -4. Add CLI/MCP/library surface. -5. Update agent workflows and bundled skill. -6. Add end-to-end scenario tests from search/review handle through refactor plan. - -Exit criteria: - -- An agent can move from a review/search handle to a complete refactor evidence packet without re-searching by prose. -- Limits and omissions remain section-specific and honest. - -## Test matrix - -### Core behavior - -- Exact symbol identity and disambiguation. -- Shadowing and same-name unrelated symbols. -- Import aliases and re-exports. -- Definitions and references. -- Callsite ownership. -- Type hierarchy and implementations. -- Rename collisions, unsafe sites, and live-range drift. -- Deterministic ordering and bounds. -- Fresh, refreshed, and stale states. -- Native, reduced, and mixed analysis metadata. - -### Boundary behavior - -- Empty project. -- Target file removed after indexing. -- Target moved after indexing. -- Very large reference set. -- Reference fan-out over limits. -- Recursive and mutually recursive calls. -- Inheritance cycles or malformed code. -- Same-named symbols across packages/namespaces. -- Case-only rename on Windows/macOS-like filesystems. -- Non-ASCII identifiers where the language supports them. -- Malformed UTF-8 and binary files. -- Root symlinks and outside-root paths. -- Sensitive files. - -### Cross-language behavior - -For every language included in a claimed capability: - -- nearest `tests/languages/.test.ts` scenario; -- shared `tests/goto.test.ts` and `tests/references.test.ts` when relevant; -- `tests/native-semantic-parity.test.ts` when native runtime semantics are involved; -- explicit unsupported/limited fixture when parity is intentionally unavailable. - -Do not claim universal implementations or rename safety based on one language. - -### MCP behavior - -- Tool listing and schemas. -- Flat argument validation. -- Handle mode and target validation. -- Freshness wrapping. -- Limit enforcement. -- Root confinement. -- One session reused across chained operations. -- Rename preview remains available in read-only mode and performs no writes. - -### CLI behavior - -- Help text and examples. -- JSON schema snapshots/assertions. -- Pretty output summaries. -- Numeric validation. -- Quoted handles and paths with shell metacharacters. -- Shared index flags and `--root` behavior. - -## Documentation updates - -Every phase that changes public CLI, MCP, library, agent workflow, or cross-language claims must update the relevant canonical documents in the same change: - -- `README.md`: concise discovery table and docs links only. -- `docs/cli.md`: commands, flags, schemas, examples, and limits. -- `docs/mcp.md`: tools, argument rules, freshness, and operating pattern. -- `docs/library-api.md`: functions and public types. -- `docs/agent-workflows.md`: search-to-refactor workflow. -- `docs/how-it-works.md`: derived indexes, caching, freshness, and limitations. -- `docs/language-parity.md`: exact hierarchy/call/rename support by language. -- `docs/scenario-catalog.md`: fixture coverage. -- `codegraph-skill/codegraph/SKILL.md`: tool selection and copyable commands. - -Keep paragraphs concise and use tables for parity details. Do not inflate the README into the canonical reference. - -## Verification commands - -During implementation, use the narrowest relevant tests: - -```bash -npx vitest run tests/handles.test.ts tests/agent-search.test.ts tests/agent-explain.test.ts -npx vitest run tests/goto.test.ts tests/references.test.ts -npx vitest run tests/agent-tools.test.ts tests/mcp-server.test.ts -npx vitest run tests/cli-command-modules.test.ts tests/cli-options-validation.test.ts -``` - -Add and run new focused suites such as: - -```bash -npx vitest run tests/workspace-symbols.test.ts -npx vitest run tests/call-hierarchy.test.ts -npx vitest run tests/type-hierarchy.test.ts -npx vitest run tests/implementations.test.ts -npx vitest run tests/rename-preview.test.ts -npx vitest run tests/refactor-plan.test.ts -``` - -When native extraction contracts or cross-language semantic behavior change: - -```bash -npm run test:native -``` - -Before concluding each major PR: - -```bash -npm run check -``` - -## Definition of done - -The plan is complete only when all of the following are true: - -- Workspace symbol, callers, callees, supertypes, subtypes, implementations, rename preview, and refactor plan exist across CLI, MCP, and supported library/agent-tool surfaces. -- All responses are bounded, deterministic, root-confined, project-relative, provenance-aware, and omission-aware. -- Every semantic operation participates in current freshness behavior. -- Rename preview writes nothing and cannot report `safe: true` from stale, ambiguous, degraded, truncated, conflicting, or live-range-invalid evidence. -- Warm repeated queries reuse derived lookup/adjacency structures. -- Existing navigation/search/explain schemas remain compatible unless an explicit schema version change is documented and tested. -- Claimed language support is documented and covered by language and shared parity tests. -- Agent workflow documentation demonstrates search/review handle to refactor-plan continuity. -- Focused tests and `npm run check` pass. -- No placeholders, stubs, compatibility shims, or unimplemented command surfaces remain. diff --git a/docs/plans/2026-07-12-windows-native-runtime-cache-updates.md b/docs/plans/2026-07-12-windows-native-runtime-cache-updates.md deleted file mode 100644 index c3462355..00000000 --- a/docs/plans/2026-07-12-windows-native-runtime-cache-updates.md +++ /dev/null @@ -1,1005 +0,0 @@ -# Windows native runtime cache and update resilience - -Date: 2026-07-12 -Status: Implemented - -## Goal - -Allow `npm install -g @lzehrung/codegraph@latest` to replace a global Codegraph installation on Windows even while long-running Codegraph MCP servers are using the native runtime. - -The primary design change is to stop loading the Windows `.node` addon from npm-owned package paths. Codegraph will copy the resolved native addon into an immutable, content-addressed per-user cache and load that cached copy instead. - -The implementation must also make the running-versus-installed version visible, provide safe update diagnostics, and document the one-time transition requirement for users upgrading from a direct-loading release. - -## Why this plan exists - -A global update from `@lzehrung/codegraph@1.8.92` to `1.8.93` failed on Windows with: - -```text -EBUSY: resource busy or locked, copyfile -C:\Users\...\node_modules\@lzehrung\codegraph\node_modules\ - @lzehrung\codegraph-native-win32-x64-msvc\index.win32-x64-msvc.node --> -C:\Users\...\node_modules\@lzehrung\.codegraph-\node_modules\ - @lzehrung\codegraph-native-win32-x64-msvc\index.win32-x64-msvc.node -``` - -Runtime inspection found four global Codegraph stdio MCP server processes. Two had the exact npm-owned `index.win32-x64-msvc.node` mapped. They were children of long-running agent sessions. The other two could load the addon lazily during the update window. - -The npm log established that: - -- dependency resolution selected the new release successfully; -- failure occurred during `reify:retireShallow`, before extraction or package lifecycle scripts; -- npm first attempted to rename the old global package; -- npm fell back to recursively copying the package to a temporary sibling; -- the copy failed on the native `.node` file; -- a failed retirement directory remained afterward; -- the active install could be left partially damaged by an interrupted retry. - -Stopping only the exact Codegraph MCP child processes, preserving the stale retirement directory outside the install scope, and retrying the install completed successfully. - -The evidence proves that running MCP processes mapped the npm-owned native addon. It does not isolate whether the active source mapping, the stale retirement destination, or a transient filesystem holder caused the exact `CopyFileW` failure. The design therefore removes Codegraph's persistent handle from npm-owned files and adds diagnostics for stale retirement state. - -## Current behavior - -The native runtime is lazy but process-lifetime once loaded. - -```text -MCP request that needs indexing - -> resolveNativeBindingState() - -> loadBinding() - -> loadNativeBinding() - -> require("@lzehrung/codegraph-native") - -> require("@lzehrung/codegraph-native-win32-x64-msvc") - -> process.dlopen(index.win32-x64-msvc.node) -``` - -Relevant code: - -- `src/native/runtime.ts` - - caches `NativeBindingState` for the process lifetime; -- `src/native/bindingLoader.ts` - - prefers a local workspace binary; - - otherwise loads `@lzehrung/codegraph-native`; -- `packages/codegraph-native/index.js` - - resolves and eagerly requires the platform package; -- `src/worker/nativeExtractWorker.ts` - - uses the same binding loader inside native extraction workers; -- `src/mcp/server.ts` - - keeps stdio servers alive for the client-owned connection; - - currently advertises hardcoded MCP server version `1.0.0`. - -An MCP process does not load the native addon merely because `mcp serve` starts. It loads the addon on warmup or on the first operation that needs native indexing. Once loaded, Node and N-API keep it mapped until the process exits. - -## Product decisions - -### 1. Load an immutable cached copy on Windows - -For installed packages on Windows, resolve the platform-specific `.node` entry without loading it, copy it to a content-addressed cache, verify the copied bytes, and require the cached path. - -Do not cache a local source-checkout build. Developers rebuilding `packages/codegraph-native` must continue loading the current workspace binary directly. - -### 2. Keep package-manager ownership separate from runtime ownership - -npm owns files under its global package root. Long-running Codegraph processes must not map native files from that root. - -Codegraph owns files under its per-user native cache. Old processes may keep old cache entries mapped without blocking npm or a new Codegraph version. - -### 3. Preserve exact bytes and immutable identities - -A cache entry is identified by: - -- cache schema version; -- native package name; -- native package version; -- platform target suffix; -- SHA-256 of the native binary. - -A final cache entry is never overwritten in place. - -### 4. Do not auto-kill hosts or arbitrary Node processes - -Codegraph must not terminate OMP, Codex, Cursor, other IDEs, or every `node.exe` process. Agent clients own stdio MCP lifecycles and may have unsaved state. - -Diagnostics may identify exact Codegraph child processes. Any stop operation must remain explicit and separately authorized. - -### 5. Do not build a detached self-updater - -A detached updater races with clients that respawn stdio MCP servers. It can wait indefinitely, lose errors, or collide with a newly loaded runtime. - -A future `codegraph upgrade` may check readiness, print or invoke the correct package-manager command with explicit consent, and verify the result. It must not replace npm as the install authority for npm-managed installs. - -### 6. Keep restart visibility even after lock prevention - -The cache allows npm to update successfully while an old MCP server remains alive. That process still runs the old Codegraph version until the client reconnects. - -Codegraph must expose the running version and native cache identity, detect installed-version drift, and tell the user which sessions need restart. Automatic termination is deferred until every supported client has proven reconnect behavior. - -## Scope - -### In scope - -- Windows content-addressed native addon cache; -- installed-package native resolution without eager loading; -- race-safe atomic cache population; -- cache integrity verification; -- cache-origin metadata in native runtime state; -- doctor diagnostics for native source, loaded path, cache identity, and stale npm retirement paths; -- actual Codegraph package version in MCP server information; -- installed-versus-running version drift detection; -- Windows update and restart documentation; -- bounded, non-destructive stale-cache cleanup guidance; -- deterministic tests for success, races, corruption, fallback, and non-Windows compatibility; -- a Windows integration test proving a process loads the cached DLL rather than the npm-owned source. - -### Out of scope - -- silently killing agent or IDE processes; -- broad process termination by executable name; -- replacing npm for npm-managed installs; -- mandatory background daemon management; -- automatic self-update during MCP requests; -- full immutable JavaScript runtime copies in the first implementation; -- changing native parser semantics or supported languages; -- changing local source-checkout native build behavior; -- deleting locked cache entries; -- treating a cache failure as proof that native support is unavailable. - -## Target architecture - -```text -Global npm package - @lzehrung/codegraph - node_modules/ - @lzehrung/codegraph-native-win32-x64-msvc/ - index.win32-x64-msvc.node - | - | resolve, realpath, stream-hash, copy, verify - v -Per-user immutable runtime cache - %LOCALAPPDATA%\codegraph\native-cache\v1\ - win32-x64-msvc\ - 1.8.72-\ - index.win32-x64-msvc.node - manifest.json - | - | require(cached path) - v -Long-running MCP process -``` - -After this change, a running MCP process maps only the cached binary. npm may rename, copy, remove, or replace its own package directory without Codegraph holding that native source file open. - -## Cache layout and manifest - -Default root on Windows: - -```text -%LOCALAPPDATA%\codegraph\native-cache\v1 -``` - -Entry layout: - -```text -v1\ - win32-x64-msvc\ - 1.8.72-0123456789abcdef...\ - index.win32-x64-msvc.node - manifest.json -``` - -Manifest schema: - -```ts -type NativeCacheManifestV1 = { - schemaVersion: 1; - packageName: string; - packageVersion: string; - target: string; - sourceFileName: string; - sourceSize: number; - sourceSha256: string; - cachedAt: string; -}; -``` - -The manifest is diagnostic metadata, not the integrity authority. Codegraph must hash the cached binary before loading it. A matching manifest without matching bytes is invalid. - -Do not add a public environment variable for the cache root in the first implementation. Tests may inject an internal cache root through loader dependencies. Production uses `%LOCALAPPDATA%` on Windows and falls back to direct package loading with a diagnostic when no safe per-user cache root is available. - -## Native binding location contract - -Extend the loader result so callers and doctor output can report what was loaded. - -```ts -type NativeBindingOrigin = { - mode: "workspace" | "package" | "cache"; - packageName: string; - packageVersion?: string; - target?: string; - sourcePath?: string; - loadedPath?: string; - cacheKey?: string; - sha256?: string; - cacheError?: string; -}; - -type NativeBindingLoadResult = - | { - binding: T; - error?: undefined; - origin: NativeBindingOrigin; - } - | { - binding: null; - error?: unknown; - origin?: NativeBindingOrigin; - }; -``` - -Extend the loaded branch of `NativeBindingState`: - -```ts -type NativeBindingState = - | { - loaded: true; - binding: NativeBinding; - supportedLanguageIds: Set; - origin: NativeBindingOrigin; - } - | { - loaded: false; - error?: unknown; - origin?: NativeBindingOrigin; - }; -``` - -All paths returned publicly must use the repository's normalized display form. Internal filesystem calls retain native absolute paths. - -## Installed native package resolution - -The current fallback requires the umbrella package, which immediately loads the platform addon. The cache must resolve the platform binary before any `require()` or `dlopen` occurs. - -For `packageName = "@lzehrung/codegraph-native"` and target `win32-x64-msvc`: - -```text -platformPackageName = "@lzehrung/codegraph-native-win32-x64-msvc" -platformEntry = require.resolve(platformPackageName) -``` - -Validate that: - -- resolution succeeds; -- the entry basename is exactly `index.win32-x64-msvc.node`; -- the resolved path exists and is a regular file; -- `realpath` succeeds; -- the adjacent platform package metadata names the expected package; -- the platform package version is present; -- the binary size is positive. - -Package-manager layouts may use symlinks or junctions. Resolve the source to its real path rather than rejecting valid npm/pnpm layouts solely because an ancestor is linked. Cache destination paths require stricter reparse-point checks. - -If direct platform resolution fails, retain the current umbrella-package fallback and record a cache-origin diagnostic. Do not convert an optional native package resolution failure into an unconditional CLI startup failure when runtime mode is `auto`. - -## Hashing without avoidable allocation - -The Windows x64 binary is roughly 30 MB. Do not use `readFileSync()` and allocate the entire binary only to compute a hash. - -Use a fixed-size reusable buffer with `openSync`, `readSync`, and `createHash("sha256")`: - -```text -open source -allocate one bounded buffer -read chunk -hash.update(chunk view) -repeat -close in finally -``` - -A 1 MiB buffer is sufficient. The hash helper returns SHA-256 and total bytes read. Reject a size mismatch against the pre-hash stat to catch replacement during hashing. - -## Secure cache-root preparation - -Before writing: - -1. Resolve `%LOCALAPPDATA%`. -2. Construct the fixed `codegraph/native-cache/v1` child path. -3. Create missing directories one component at a time. -4. For every existing cache component, use `lstat` and reject symbolic links or reparse-point-like substitutions supported by Node's APIs. -5. Resolve the real cache root. -6. Confirm all candidate paths remain inside that real root. -7. Never accept a caller-provided production path from MCP/CLI input. - -If safe cache preparation fails, return a diagnostic and use the direct package fallback. Never load an unverified file from an unsafe cache root. - -## Atomic cache population - -For the expected final binary path: - -```text -\index.win32-x64-msvc.node -``` - -use this algorithm: - -1. If the final file exists: - - require a regular file; - - stream-hash it; - - use it only if size and SHA-256 match the source. -2. If it is missing: - - copy the source to a unique sibling temporary file; - - use exclusive creation; - - close the temporary file before verification; - - stream-hash the temporary file; - - reject and remove it if size or hash differs; - - rename it atomically to the final immutable name. -3. If another process wins the final rename race: - - remove this process's temporary file; - - verify the winner's final file; - - use the winner only when its bytes match. -4. Write `manifest.json` through the same temporary-file and atomic-rename pattern. -5. Require the final cached `.node` path. - -Never overwrite the final cached binary. A mapped cache file is immutable for its entire lifetime. - -Temporary file names must include PID and random entropy: - -```text -index.win32-x64-msvc.node...tmp -``` - -A failed startup may leave temporary files. Doctor cleanup may remove old `.tmp` files that are regular files, remain within the cache root, and exceed a conservative age threshold. - -## Corrupt cache handling - -If the final file exists but has the wrong hash: - -1. Do not require it. -2. Attempt to rename it within the same entry to a bounded recovery name. -3. Populate a correct immutable final file. -4. If rename or repair fails, fall back to direct package loading and report `cacheError`. - -Never overwrite or delete a file that may be mapped. Never treat a manifest match as sufficient when the binary hash differs. - -## Fallback behavior - -The loader must preserve current availability behavior. - -```text -workspace binary exists - -> load workspace binary directly - -installed Windows platform binary resolves - -> prepare and verify cache - -> load cached binary - -cache preparation fails - -> record cacheError - -> load installed umbrella package directly - -installed native package fails - -> preserve current unavailable/reduced-mode behavior -``` - -When runtime mode is `on`, existing required-native failure behavior remains authoritative. When runtime mode is `auto`, cache failure alone does not disable native parsing if direct loading still succeeds. - -## Worker behavior - -`src/worker/nativeExtractWorker.ts` calls the same `loadNativeBinding()` helper. It must therefore resolve the same cache key and cached path as the main process. - -Required invariant: - -```text -main process loadedPath == worker loadedPath -``` - -Concurrent main-thread and worker initialization must be safe through the atomic population algorithm. No separate worker cache implementation is allowed. - -## Runtime and doctor diagnostics - -Extend `DoctorReport.native`: - -```ts -type DoctorNativeReport = { - available: boolean; - loadError?: string; - supportedLanguageIds: string[]; - origin?: { - mode: "workspace" | "package" | "cache"; - packageName: string; - packageVersion?: string; - target?: string; - sourcePath?: string; - loadedPath?: string; - cacheKey?: string; - sha256?: string; - updateSafeForCurrentProcess: boolean; - cacheError?: string; - }; - update?: { - staleRetirementPaths: string[]; - restartRequired: boolean; - runningVersion?: string; - installedVersion?: string; - reason?: string; - }; -}; -``` - -`updateSafeForCurrentProcess` means only that this process did not map an npm-owned native binary. It must not claim that no other process on the machine holds the source file. - -Cross-process PID attribution through Windows Restart Manager is useful but not required for the first cache PR. Do not scrape or kill arbitrary processes merely to produce doctor output. A later diagnostic may use Restart Manager or a narrowly scoped platform helper after a separate security review. - -Detect stale npm retirement siblings conservatively: - -- only direct children of the resolved package scope directory; -- only names matching npm's `.codegraph-*` retirement form; -- report paths but do not delete them during normal doctor execution; -- offer a separate explicit cleanup action only after path, ownership, and liveness checks. - -## MCP running-version identity - -Replace the hardcoded MCP server version in `src/mcp/server.ts`: - -```ts -{ - name: "codegraph", - version: getCodegraphVersion(), -} -``` - -Capture runtime identity once when the server starts: - -```ts -type CodegraphRuntimeIdentity = { - startedAt: string; - runningVersion: string; - packageRoot: string; - nativeOrigin?: NativeBindingOrigin; -}; -``` - -Do not recompute `runningVersion` from package metadata after an update. It identifies the code already loaded into the process. - -## Installed-version drift - -A cached native addon removes npm's DLL lock, but an old MCP process can remain alive after npm installs a new version. - -Add a throttled installed-version check: - -- read no more than once every 30 seconds; -- read the package metadata path captured at startup; -- compare the current disk version with `runningVersion`; -- tolerate temporary absence or malformed JSON during package replacement; -- warn once per observed installed version; -- never crash an in-flight request; -- do not automatically exit in the first implementation. - -The status should distinguish: - -```text -same version - -> restartRequired: false - -new version on disk - -> restartRequired: true - -> reason: "installed Codegraph 1.8.94 differs from running MCP 1.8.93" - -package path temporarily unavailable - -> restartRequired: true - -> reason: "Codegraph installation changed while this MCP server was running" -``` - -Expose drift through doctor and existing server logs first. If a stable MCP status tool is added, update `docs/mcp.md`, `docs/cli.md`, the bundled skill, and protocol tests in the same change. - -## Update workflow - -This plan complements `docs/plans/2026-07-03-13-upgrade-command.md`. - -For npm-managed installs, a future `codegraph upgrade` should: - -1. report current, latest, and running MCP versions; -2. report whether the current process loaded from cache or the package path; -3. report stale npm retirement siblings; -4. print the exact scoped-registry package-manager command; -5. require explicit consent before `--apply` is ever added; -6. execute npm as the install authority; -7. verify the installed version afterward; -8. tell the user which clients still run the old version. - -The command must not kill host applications or silently rewrite MCP configuration. - -A package `preinstall`, `install`, or `postinstall` script cannot solve the raw npm update failure. npm retires the old package before incoming lifecycle scripts run. Do not add lifecycle scripts that claim to preflight this condition. - -## Transition behavior - -The first release containing this cache still has a bootstrap problem: older running MCP servers load the DLL directly from the npm package. - -Users must perform one final stop-update-restart sequence to install the cache-enabled release: - -1. close or disconnect Codegraph MCP clients; -2. confirm no Codegraph native DLL is mapped; -3. install the cache-enabled release; -4. restart clients. - -After all running clients use the cache-enabled loader, later npm updates should no longer be blocked by Codegraph mapping the npm-owned native file. - -Release notes and installation docs must state this one-time transition explicitly. - -## Implementation phases - -### Phase 1: cache primitives - -Add a focused internal module, for example: - -```text -src/native/runtimeCache.ts -``` - -Responsibilities: - -- fixed production cache-root resolution; -- source and cached file streaming hashes; -- safe cache-root preparation; -- cache-key construction; -- immutable entry verification; -- atomic race-safe population; -- manifest creation; -- bounded corruption recovery; -- structured result without loading the addon. - -Suggested contract: - -```ts -type PrepareNativeRuntimeCacheRequest = { - sourcePath: string; - packageName: string; - packageVersion: string; - target: string; - cacheRoot?: string; // Internal test dependency only. -}; - -type PrepareNativeRuntimeCacheResult = - | { - status: "cached" | "reused"; - sourcePath: string; - loadedPath: string; - cacheKey: string; - sha256: string; - } - | { - status: "unavailable"; - sourcePath: string; - error: Error; - }; -``` - -The module does not call `require()` and does not know about `NativeBinding`. - -### Phase 2: binding-loader integration - -Update `src/native/bindingLoader.ts`: - -- preserve workspace binary precedence; -- derive the platform package name; -- resolve the platform entry without loading it; -- read validated platform package metadata; -- call the Windows cache module; -- require the returned cached path; -- fall back to the current umbrella package path on cache failure; -- return structured origin metadata. - -Update both: - -- `src/native/runtime.ts`; -- `src/worker/nativeExtractWorker.ts`. - -Do not implement a second cache path in `packages/codegraph-native/index.js` in this phase. Codegraph's loader must intercept before the umbrella package eagerly loads the platform addon. If the native wrapper later becomes a public standalone runtime, move or share the cache implementation in a separate compatibility change. - -### Phase 3: diagnostics and identity - -Update: - -- `src/native/contracts.ts`; -- `src/cli/doctor.ts`; -- `src/cli/packageInfo.ts` as needed; -- `src/mcp/server.ts`; -- CLI JSON/pretty doctor formatting; -- MCP server version metadata. - -Keep diagnostics bounded and avoid cross-process enumeration in the initial implementation. - -### Phase 4: drift warning - -Add the throttled installed-version comparison and one-time warning. It must not fail tool calls or terminate the server. - -If future client testing proves graceful reconnect behavior, a separate change may add an opt-in server exit after an update. - -### Phase 5: cleanup and documentation - -Only after cache loading and diagnostics work end to end: - -- document Windows update behavior; -- document the one-time transition; -- document cache layout and cleanup; -- update CLI/MCP contracts; -- update the bundled skill; -- add release guidance; -- remove any temporary test-only scaffolding. - -## TDD implementation sequence - -Follow vertical RED-GREEN slices. Do not write all tests before implementation. - -### Slice 1: installed Windows binding loads from cache - -RED: - -- resolve a synthetic installed Windows platform package; -- call the public binding loader with injected resolver/require boundary; -- assert `requireFn` receives a cache path outside the package root; -- assert bytes match the source. - -GREEN: - -- implement minimum cache root, hash, copy, verify, and load path. - -### Slice 2: same bytes reuse one immutable entry - -RED: - -- load the same package twice; -- assert the same cache path is used; -- assert the final binary is not rewritten. - -GREEN: - -- verify and reuse an existing content-addressed entry. - -### Slice 3: concurrent population is race-safe - -RED: - -- launch multiple child processes or isolated workers against one empty cache; -- assert every process receives the same verified final path; -- assert no partial final binary is observed. - -GREEN: - -- add exclusive temporary files, atomic rename, and winner verification. - -### Slice 4: corrupt cache is never loaded - -RED: - -- place wrong bytes at the expected final path; -- assert `requireFn` never receives those bytes/path; -- assert repair or safe fallback is reported. - -GREEN: - -- add hash verification and bounded repair. - -### Slice 5: unsafe or unavailable cache falls back - -RED: - -- simulate missing `%LOCALAPPDATA%`, permission failure, and unsafe cache path; -- assert direct package loading remains available; -- assert structured `cacheError` is retained. - -GREEN: - -- add safe fallback without changing native-required semantics. - -### Slice 6: non-Windows behavior remains unchanged - -RED/GREEN contract: - -- Linux and macOS installed-package calls continue requiring the umbrella package; -- local workspace binaries continue loading directly on all platforms; -- no cache directory is created outside Windows installed-package mode. - -### Slice 7: main and worker paths agree - -RED: - -- load through runtime and native worker boundaries; -- assert both report the same cache identity and loaded path. - -GREEN: - -- route both through the shared loader/cache module. - -### Slice 8: doctor reports origin and update readiness - -RED: - -- assert JSON doctor output distinguishes workspace, package, and cache origins; -- assert cache error and stale retirement paths are bounded and normalized. - -GREEN: - -- extend doctor report and formatting. - -### Slice 9: MCP advertises actual running version - -RED: - -- initialize the MCP protocol server; -- assert server info uses the package version rather than hardcoded `1.0.0`. - -GREEN: - -- inject captured package identity into protocol server creation. - -### Slice 10: installed-version drift is non-fatal - -RED: - -- start with version A; -- change readable package metadata to version B; -- assert restart-required status/warning; -- assert tool calls still complete; -- assert repeated checks are throttled and warnings deduplicated. - -GREEN: - -- add runtime identity and throttled comparison. - -## Test matrix - -### Focused unit and behavior suites - -Update or add coverage near: - -- `tests/native-binding-loader.test.ts`; -- `tests/native-runtime-mode.test.ts`; -- `tests/native-worker-path.test.ts`; -- `tests/cli-command-modules.test.ts`; -- `tests/cli-regressions.test.ts`; -- `tests/mcp-server.test.ts`. - -Required cases: - -- Windows x64 and arm64 package-name derivation; -- workspace binary bypasses cache; -- installed Windows binary uses cache; -- installed non-Windows binary keeps current behavior; -- cache key changes when package version or bytes change; -- identical bytes reuse the same entry; -- source replacement during hash is rejected; -- concurrent writers converge; -- partial temporary files are ignored; -- corrupt final bytes are never loaded; -- symlink/reparse cache destination is rejected; -- permission failure falls back with diagnostic; -- native `on` and `auto` semantics remain correct; -- main and worker loaded paths match; -- doctor output is deterministic and bounded; -- MCP server version is current; -- drift warning is throttled and non-fatal. - -### Windows integration test - -Add a Windows-only native-required integration test that uses a real built/published native binary: - -1. create a temporary installed-package-like tree; -2. launch a child process that loads native through Codegraph's binding loader; -3. trigger a native operation; -4. have the child report `sourcePath`, `loadedPath`, version, and hash; -5. assert `loadedPath` is under the cache root and not under the package tree; -6. while the child remains alive, rename or remove the synthetic package source tree; -7. assert the operation succeeds; -8. assert the child can complete another already-loaded native call; -9. stop the child cleanly; -10. clean the synthetic cache entry. - -Do not run this test against the developer's real global npm root. Do not stop unrelated processes. - -### Regression for the reported failure class - -The acceptance regression is not a source-text assertion. It must prove the observable invariant: - -```text -A live Codegraph process using native parsing does not map the native addon from the npm-owned package path. -``` - -On Windows CI, inspect the child process module list or use loader-reported origin plus a successful rename of the synthetic source package while the child remains alive. - -## Documentation updates - -Update in the same implementation change: - -- `README.md` - - concise Windows updating note and link to installation docs; -- `docs/installation.md` - - updating section; - - one-time transition requirement; - - safe stop/retry/restart procedure for older releases; - - native cache location and cleanup behavior; -- `docs/mcp.md` - - running-version identity; - - restart-required behavior; - - cached native runtime lifecycle; -- `docs/cli.md` - - doctor JSON/pretty output changes; - - any new diagnostic flags; -- `docs/how-it-works.md` - - package-owned source versus cached loaded binary; -- `codegraph-skill/codegraph/SKILL.md` - - update troubleshooting and any doctor/upgrade surface changes; -- `PUBLISHING.md` - - release validation for Windows cache-enabled packages; -- `docs/plans/2026-07-03-13-upgrade-command.md` - - cross-reference this runtime-cache prerequisite; -- `docs/plans/2026-07-03-03-shared-server-lifecycle.md` - - clarify that shared servers also load immutable cache entries and report runtime identity. - -Do not change `docs/language-parity.md` unless native availability claims change. This plan changes loading and lifecycle, not language semantics. - -## Release and rollout - -### Cache-enabled release validation - -Before publishing: - -- package all Windows target artifacts; -- verify platform package resolution for x64 and arm64; -- run native-required Windows tests; -- run the synthetic live-process source-replacement integration test; -- inspect published tarball contents; -- verify doctor reports cache origin from an installed package; -- verify reduced mode still works when native packages are absent; -- verify a global update succeeds while a cache-enabled MCP process remains alive; -- verify the old MCP process reports restart required after the update; -- verify a new MCP process reports the new version and cache key. - -### Transition release note - -State explicitly: - -```text -Windows users must close existing Codegraph MCP clients once before installing this release. Older Codegraph processes load the native addon directly from npm's package directory. After this cache-enabled release is installed and clients are restarted, later global updates no longer require stopping Codegraph solely to release the npm-owned native DLL. -``` - -Do not claim updates are universally lock-free. Antivirus, backup software, stale npm retirement paths, and other filesystem holders may still produce `EBUSY`. The exact product claim is that Codegraph no longer keeps the npm-owned native addon mapped. - -## Failure and rollback strategy - -- Cache failure falls back to the current direct package loader. -- Native `auto` may still degrade if both cache and direct load fail. -- Native `on` retains explicit failure behavior. -- Removing cache integration restores current loading without changing cache data. -- Old cache entries are inert files and may be removed after no process maps them. -- Cache schema changes use a new top-level schema directory (`v2`, not in-place mutation). -- Do not migrate or rewrite mapped `v1` entries. - -## Primary evidence and references - -- Microsoft `DeleteFileW` mapped-file behavior: -- Microsoft dynamic-link library update guidance: -- Node.js 24.15 native binding lifecycle: -- npm filesystem move fallback used during package retirement: -- Existing upgrade command plan: [`2026-07-03-13-upgrade-command.md`](./2026-07-03-13-upgrade-command.md) -- Existing shared server lifecycle plan: [`2026-07-03-03-shared-server-lifecycle.md`](./2026-07-03-03-shared-server-lifecycle.md) - -The incident establishes that Codegraph processes mapped the npm-owned binary and that the update succeeded after those processes stopped and the stale retirement directory moved aside. It does not claim that a mapped source DLL was the only possible `CopyFileW` holder. Tests and release language must preserve that distinction. - -## Rejected alternatives - -### Stop MCP servers automatically - -Rejected because hosts own stdio children, may respawn them immediately, and may contain unsaved user state. - -### Kill all Node processes - -Rejected as unsafe and unrelated to Codegraph ownership. - -### Incoming npm lifecycle preflight - -Rejected because npm retires the old package before incoming lifecycle scripts execute. - -### Detached updater - -Rejected because it races with host respawn and duplicates package-manager responsibility. - -### One mutable cache filename - -Rejected because the next update recreates the same mapped-file conflict. - -### Cache under `%TEMP%` - -Rejected because shared or weakly controlled temporary locations increase DLL planting and cleanup risk. - -### Copy the complete runtime immediately - -Deferred. A full immutable runtime would also eliminate mixed-version lazy import risk, but requires a self-contained bundled MCP artifact and materially more lifecycle machinery. Implement the native cache and drift visibility first. - -### Load with native mode disabled during updates - -Insufficient. Existing servers may already have loaded the addon, and raw npm updates cannot reconfigure them before package retirement. - -## Relationship to a future immutable runtime - -If zero-restart or long-lived mixed-version operation becomes a product requirement, evolve from native-only cache to: - -```text -%LOCALAPPDATA%\codegraph\runtimes\ - -\ - dist\ - native\ - runtime-manifest.json -``` - -A stable launcher would start MCP from one immutable runtime. Existing sessions finish on the old runtime, new sessions use the new runtime, and npm replaces only the launcher/package metadata. - -This is not required to fix the current native DLL lock. Keep it as a separately reviewed follow-up after the native cache has production evidence. - -## Acceptance criteria - -The plan is complete only when all of the following are true: - -- Installed Windows Codegraph resolves the platform `.node` entry without loading it from the package path. -- The native binary is copied to a per-user, content-addressed, immutable cache. -- The cached bytes are SHA-256 verified before loading. -- Cache population is safe across concurrent MCP processes. -- No final cached binary is overwritten in place. -- Workspace source-checkout binaries keep current direct-load behavior. -- Non-Windows installed-package behavior remains compatible. -- Main and worker native loading use the same cache implementation and cache key. -- Native runtime state records source and loaded origins. -- Doctor reports the cache origin, fallback errors, and restart/version drift honestly. -- MCP server info advertises the actual running Codegraph version. -- Installed-version drift does not crash or silently terminate tool calls. -- A Windows integration test proves a live native process does not map the npm-owned source binary. -- A synthetic package source can be renamed while the cache-loaded process remains alive. -- The first cache-enabled release documents the one-time stop-update-restart transition. -- CLI/MCP output changes are documented in `docs/cli.md`, `docs/mcp.md`, and the bundled skill. -- Published Windows x64 and arm64 package layouts are validated. -- Focused native, worker, CLI doctor, and MCP tests pass. -- `npm run test:native`, `npm run test:integration`, and `npm run check` pass in the intended CI environments. -- No automatic host termination, detached updater, unsafe temp loading, or unbounded cache cleanup is introduced. - -## Suggested implementation ownership - -One agent should own cache primitives and binding-loader integration because their contracts are tightly coupled. A second independent agent may own doctor/MCP identity after the loader result shape is fixed. A third agent may own documentation and Windows integration coverage after behavior is working. - -Do not parallelize edits to `src/native/bindingLoader.ts`, `src/native/runtime.ts`, and `src/native/contracts.ts` across multiple agents. Establish the loader and origin contract first, then fan out diagnostics and documentation. - -## Verification commands - -During vertical implementation, use the narrowest relevant suites: - -```bash -npx vitest run tests/native-binding-loader.test.ts -npx vitest run tests/native-runtime-mode.test.ts tests/native-worker-path.test.ts -npx vitest run tests/cli-command-modules.test.ts -t "doctor" -npx vitest run tests/cli-regressions.test.ts -t "doctor" -npx vitest run tests/mcp-server.test.ts -``` - -When the loader and Windows integration are complete: - -```bash -npm run test:native -npm run test:integration -``` - -Before concluding the implementation: - -```bash -npm run check -``` - -## Implementer handoff - -Start with `src/native/bindingLoader.ts`, `src/native/runtime.ts`, `src/worker/nativeExtractWorker.ts`, `src/native/contracts.ts`, and `tests/native-binding-loader.test.ts`. - -The first proof must be RED-GREEN behavior showing that an installed Windows platform binary is required from a cache path outside the synthetic npm package root. Do not begin with doctor output, docs, cleanup, process detection, or an upgrade command. Those depend on a trustworthy loader-origin contract. - -The core invariant is: - -```text -Long-running Codegraph processes may map Codegraph-owned immutable cache files, but must not map npm-owned native addon files. -``` diff --git a/docs/plans/2026-07-21-bounded-impact-review-and-candidate-tests.md b/docs/plans/2026-07-21-bounded-impact-review-and-candidate-tests.md deleted file mode 100644 index 855dd542..00000000 --- a/docs/plans/2026-07-21-bounded-impact-review-and-candidate-tests.md +++ /dev/null @@ -1,283 +0,0 @@ -# Bounded Impact/Review Fixes and Candidate-Test Accuracy Plan - -**Status: Implemented.** Evidence and source references below were verified against `main` at `3c09f572` (`v1.8.99`) and the current `../gunship` branch on 2026-07-21. - -## Scope decision - -Original feedback ("build a review packet with two reviewer lanes, execution paths, and a -finding ledger") was intended for `../review-until-clean-odw`, not this repository. That tool -already owns orientation, reviewer grouping, and correction-loop state: - -- `review-until-clean-odw`'s Orient phase builds its own shared context (diff summary, - callers/callees, doc excerpts, risk hunks) as a markdown blob inlined into its own prompts. - Its only codegraph touchpoint is one CLI call: `codegraph impact --provider git --base ---head --pretty` (or `refs`/`callers`/`callees`), explicitly documented as an - "OPTIONAL accelerant, never a requirement" (`../review-until-clean-odw/workflows/review-and-correct.js:265-271`, - `README.md:19-21`). It never asks codegraph for a structured packet, lanes, or ledger schema. -- `../code-review-agent` tried the opposite: `OpencodeReviewPacket` - (`agent/src/review/opencode/types.ts`) fuses graph evidence with workflow policy — `mode`, - `checklist`, `outputContract`, `candidateGenerationPolicy`, `scope`, `packetFingerprint` — into - one object threaded through `backend.ts` (1500+ lines), `reviewPacket.ts`, `validation.ts`, - `contextLayerDiagnostics.ts`, `exploratorySession.ts`, and `pipeline.ts` (2900+ lines). That - coupling between structural evidence and orchestration policy is the fragmentation this - plan avoids repeating. - -Codegraph's job stays narrow: correct, deterministic, bounded structural evidence behind -`impact`, `review`, `refs`, `callers`, `callees`. Reviewer grouping, risk-hunk narrative, -context packets, and finding ledgers are consumer-owned (ODW today, any other orchestrator -tomorrow). This plan only fixes defects that block that narrow contract: wrong data, unbounded -compute, and weak candidate-test evidence in valid C/C++ layouts. - -## Goals - -- `codegraph impact --provider git --pretty` and `codegraph review --summary` return correct, - complete-or-honestly-partial results within a bounded time on realistic diffs, so an - orchestrator's single accelerant call is actually fast enough to be worth making. -- Existing tracked, non-indexed files that changed are never misreported as deleted. -- MCP `impact` returns impact data, not a review report. -- Candidate-test ranking reflects proven graph edges when they exist, including for C/C++ - projects with repo-local include roots. - -## Non-goals - -- No new packet/context schema, reviewer-lane taxonomy, execution-path grouping, or finding - ledger. Those are workflow concerns owned outside this repository. -- No change to how `review`/`impact` results are consumed, grouped, or narrated by callers. -- No general workflow engine or reviewer scheduler. - -## Verified baseline defects - -### F1: Minimal review is not computationally minimal - -On `../gunship`, this command reviewed 40 changed files and 1,043 changed symbols: - -```bash -node ../codegraph/dist/cli.js review --root . --base main --head HEAD \ - --summary --review-depth minimal --duplicates off --report -``` - -Two consecutive runs took 52.69s and 53.57s. The reports attributed 16.24s and 16.70s to -indexing, leaving about 36.45s and 36.87s in unreported work. - -`buildReviewReport()` always calls `collectReviewDuplicateTasks()` (`src/review.ts:349-356`). -`--duplicates off` is parsed only after the report has been built (`src/cli/review.ts:326-339`), -so it suppresses the second human-summary duplicate pass but not the expensive duplicate task -pass underneath it. - -### F2: Impact bounds are per symbol, not per request - -This Gunship command took 291.27s even with `--max-refs 200` and `--depth 2`: - -```bash -node ../codegraph/dist/cli.js impact --root . --base main --head HEAD \ - --pretty --duplicates off --compact --max-refs 200 --depth 2 -``` - -`analyzeDirectReferences()` schedules one lookup for every changed symbol -(`src/impact/direct.ts:36-47`). `maxRefs` is applied independently inside every lookup -(`src/impact/direct.ts:61-75`), so 1,043 changed symbols can still trigger 1,043 reference -queries and retain up to 208,600 references before any request-wide cap applies. - -This is the exact command shape `review-until-clean-odw`'s Orient phase runs as its accelerant -call. An unbounded 291s run defeats the purpose of the call being an accelerant at all. - -### F3: MCP `impact` does not run impact analysis - -The MCP `impact` handler calls `buildReviewReport(..., reviewDepth: "minimal")` -(`src/mcp/server.ts:759-768`). Its tool description says it builds compact impact context -(`src/mcp/tools.ts:293-295`), but it returns a `ReviewReport`, not an `ImpactReport`. Any MCP -caller selecting or reasoning about impact limits gets review data instead. - -### F4: Changed non-indexed files are falsely reported as deleted - -Gunship's modified PowerShell scripts and `justfile` exist on disk and are `M` in -`git diff --name-status main...HEAD`. The review summary reported them as `deleted`. - -When a changed file has no indexed module, `summarizeChangedFiles()` maps it to `deleted` -unless it is a missing explicit input (`src/review/summaries.ts:402-415`). Status must come -from the diff kind and disk existence, not from whether the language produced a `ModuleIndex`. - -### F5: Candidate tests degrade to pattern-only hints in valid C++ layouts - -Gunship review returned 10 low-confidence pattern matches and no high- or medium-confidence -candidates. `deps Source/Gunship/Private/Damage/Tests/DamageModelTests.cpp` returned an empty -list even though the file includes `"Damage/Simulation/GunshipDamageModel.h"`. - -Codegraph already supports `graph.resolutionHints` and CLI `--resolution-hint`, but -`codegraph.config.json` persists only discovery settings (`src/config.ts:10-27`). MCP and -library sessions therefore cannot inherit repo-local C/C++ include roots without host-specific -startup configuration on every call. - -## Priority 0: Repair correctness and MCP semantics - -- [x] In `src/review/summaries.ts`, classify `deleted` only when the diff says deleted or the - file is absent for a non-explicit diff entry. Existing non-indexed files become `updated` - with `symbols: []`. -- [x] Add a regression with modified tracked PowerShell, Markdown, and extensionless files. - Assert that language support does not control change status. -- [x] Change MCP `impact` to load one session snapshot and call `analyzeImpactFromDiff()` with - `provider: "git"`, `compact: true`, and bounded MCP defaults. -- [x] Keep MCP `review` returning `ReviewReport`; no schema change there. -- [x] Add MCP tests that distinguish impact and review by schema: impact has - `format: "compact"` and `impacted`; review has `riskSummary` and `reviewTasks`. - -Likely files: `src/review/summaries.ts`, `src/mcp/server.ts`, `src/mcp/tools.ts`, -`tests/review.test.ts`, `tests/mcp-server.test.ts`, `tests/git-diff-semantics.test.ts`. - -Acceptance: - -- Gunship's changed Build scripts and `justfile` report `updated`. -- MCP `impact` returns actual impacted items and never returns a `ReviewReport` under the - impact tool name. - -## Priority 1: Add hard work, time, and byte budgets - -Count limits must constrain computation before work is scheduled, not just truncate output -after the fact. - -### Impact work budgets - -Add these optional `ImpactOptions` fields alongside existing per-symbol `maxRefs`: - -- `maxChangedSymbols`: maximum changed symbols that receive precise reference analysis. -- `maxReferenceLookups`: maximum calls to `findReferences()` and call-compatibility analysis. -- `maxTotalReferences`: request-wide retained reference budget. -- `timeBudgetMs`: soft post-index analysis deadline. - -Rank changed symbols deterministically before applying limits: - -1. signature changed; -2. exported/public contract; -3. proven incoming call or reference edge; -4. callable/type declaration before local variable; -5. higher file fan-in; -6. stable file, range, and handle ordering. - -Process bounded batches of at most eight symbols. Check the deadline before launching each -batch, finish already-started work, and return a valid partial report with exact skipped -counts (`changedSymbolsTotal/Analyzed/Omitted`, `referenceLookupsStarted/Omitted`, -`referencesRetained/Omitted`, `deadlineExceeded`). - -Do not make timing the only bound. The hard symbol and lookup limits guarantee finite work -even on a fast machine or a client with no timeout. - -### Review stage budgets - -Add review options for `duplicateTasks`, `maxChangedFiles`, `maxChangedSymbols`, and -`maxGraphDeltaEdges`. Parse `--duplicates off` before building the report so it prevents -`prepareDuplicateAnalysis()` entirely, not just the human-summary pass. `changed|impacted|all` -remains explicit opt-in work. - -### MCP/CLI pretty and JSON output budgets - -Apply a request-wide `maxOutputBytes` to MCP `impact` and `review` responses (defaults tuned to -MCP context budgets), dropping whole lowest-ranked entries and recording exact omitted counts -rather than truncating serialized JSON mid-field. This is defensive sizing for the existing -count-bounded surfaces (`--max-refs`, `--limit`), not a new packet format. - -Likely files: `src/impact/types.ts`, `src/impact/collect.ts`, `src/impact/analyzer.ts`, -`src/impact/direct.ts`, `src/impact/callCompatibility.ts`, `src/review.ts`, -`src/review/types.ts`, `src/cli/review.ts`, `src/mcp/server.ts`, `src/presentation/bounds.ts`, -`tests/impact-analyzer.test.ts`, `tests/review.test.ts`, `tests/mcp-server.test.ts`. - -Acceptance: - -- A synthetic 1,000-symbol diff starts no more than the configured reference lookup count. -- The same input and limits produce the same selected symbols and omissions across repeated - runs. -- Deadline exhaustion returns partial evidence with exact omitted counts rather than throwing - or returning an empty success. -- `--duplicates off` records zero duplicate-analysis time and creates no duplicate tasks. -- `codegraph impact --provider git --base main --head HEAD --pretty` completes in well under - 30s cold and 10s warm on the Gunship baseline (down from 291.27s), and - `codegraph review --summary --review-depth minimal --duplicates off` completes in well under - 15s cold and 5s warm (down from ~53s). - -## Priority 2: Fix candidate-test ranking and persist C/C++ resolution hints - -- [x] Add `changedTest` and `symbolReference` candidate reasons. -- [x] Always include changed test files as high-confidence focused-test inputs. -- [x] Rank a test high when a proven symbol or file edge reaches a changed contract, and medium - when a bounded co-location/name heuristic matches. -- [x] Keep pattern-only matches low confidence and expose why higher-confidence linkage was - unavailable. -- [x] Extend `codegraph.config.json` with `graph.resolutionHints: string[]`; merge config - values with explicit build options and CLI/MCP overrides using the existing - `normalizeResolutionHints()` path. -- [x] Include normalized resolution hints in manifest and snapshot identity exactly as current - graph options do. -- [x] Add a Gunship-shaped C++ fixture where `Private/Damage/Tests/DamageModelTests.cpp` - includes `"Damage/Simulation/GunshipDamageModel.h"` and a configured - `Source/Gunship/Private` hint resolves it. - -Likely files: `src/review/candidates.ts`, `src/impact/context.ts`, `src/impact/testPatterns.ts`, -`src/config.ts`, `src/agent/session.ts`, `src/cli.ts`, `src/indexer/build-cache/options.ts`, -`tests/review.test.ts`, `tests/config.test.ts`, `tests/languages/cpp.test.ts`, -`tests/goto.test.ts`, `tests/references.test.ts`, `tests/native-semantic-parity.test.ts`. - -Acceptance: - -- Gunship's changed `DamageModelTests.cpp` is high confidence because it changed. -- With `graph.resolutionHints`, the test-to-model edge resolves and an unchanged test can be - selected from a changed model. -- Invalid or escaping hints fail root confinement or remain unresolved; they never create - off-root graph edges. - -## Priority 3: Documentation - -Update docs to describe `impact --provider git --pretty` and `review --summary` as the -intended fast, bounded entry points for external orchestrators (ODW-style tools) to call as -an accelerant — not as a definition of any review workflow, packet, or protocol codegraph owns. - -Required updates per repository policy: - -- `docs/cli.md`: new budget flags, corrected MCP `impact` behavior. -- `docs/library-api.md`: new `ImpactOptions`/review options fields. -- `docs/mcp.md`: real impact semantics. -- `docs/how-it-works.md`: bounds and partial-result behavior. -- `codegraph-skill/codegraph/SKILL.md`: note that `impact`/`review` are safe to call as a - bounded accelerant from another agent's own review workflow. -- `docs/language-parity.md` and `docs/scenario-catalog.md`: configured C/C++ resolution hints. - -## Implementation order - -1. Priority 0: correctness and MCP semantic repairs. -2. Priority 1: hard computation and payload bounds. -3. Priority 2: candidate-test and C++ resolution improvements. -4. Priority 3: documentation, after behavior is proven. - -## Validation checklist - -### Focused automated checks - -- [x] `npx vitest run tests/review.test.ts tests/impact-analyzer.test.ts tests/mcp-server.test.ts` -- [x] `npx vitest run tests/config.test.ts tests/git-diff-semantics.test.ts` -- [x] `npx vitest run tests/languages/cpp.test.ts tests/goto.test.ts tests/references.test.ts tests/native-semantic-parity.test.ts` -- [x] CLI regression coverage for every new flag and incompatible combination. - -### Gunship acceptance scenario - -Run from the current Gunship branch with the built CLI from this checkout: - -- [x] Cold and warm `impact --provider git --base main --head HEAD --pretty` complete well - under 30s/10s (from 291.27s). -- [x] Cold and warm `review --summary --review-depth minimal --duplicates off` complete well - under 15s/5s (from ~53s). -- [x] Build scripts and `justfile` are `updated`, not `deleted`. -- [x] MCP `impact` returns real impacted items, distinct from MCP `review` output. -- [x] Configured `Source/Gunship/Private` resolution links `DamageModelTests.cpp` to - `GunshipDamageModel.h`, raising it out of pattern-only confidence. -- [x] A bounded impact request completes inside the configured budget and reports skipped - changed symbols instead of timing out. - -### Repository qualification - -- [x] `node ./dist/cli.js doctor` -- [x] `npm run check` - -## Success criteria - -`codegraph impact --provider git --pretty` and `codegraph review --summary` are fast, correct, -and bounded enough that an external review orchestrator (`review-until-clean-odw` or similar) -can call them as a one-line accelerant exactly as already designed, without codegraph taking on -any reviewer-grouping, packet, or ledger responsibility that belongs to that orchestrator. diff --git a/docs/plans/2026-07-23-cold-start-explore-latency.md b/docs/plans/2026-07-23-cold-start-explore-latency.md deleted file mode 100644 index 2421cbdc..00000000 --- a/docs/plans/2026-07-23-cold-start-explore-latency.md +++ /dev/null @@ -1,60 +0,0 @@ -# Cold-start and explore latency opportunities - -Status: Slice 1 implemented and merged (basic symbol graph for hybrid search/explore, PR #165). -Remaining ranks are superseded by the measured plans listed below. -Date: 2026-07-23 - -**Superseded by** [2026-07-25-performance-program-index.md](2026-07-25-performance-program-index.md) -and its four implementation plans. This note's ranked follow-ups were written from partial data; -the replacement plans carry measured per-layer costs and cite exact source locations. - -## Context - -PR #165 bounded `impact`/`review` only. Measured `orient`/`explore`/`search`/`refs` on sibling repos showed: - -- Cold CLI: 4-11s on ~150-650 file repos -- Warm `orient`: ~0.5s when the index settles -- Warm `search`/`explore`: still 1-3.5s -- Sheetflare looked "warm-broken" with perpetual `Updated project index: 14 files` - -Two freshness bugs fixed alongside this note: - -1. Hard-ignore `.codegraph/` + `.codegraph-cache/` in discovery; init also gitignores `.codegraph-cache/` -2. Stop force-reparsing git dirty `lastCommit...WORKTREE` files when signatures already match - -Sheetflare warm `orient` now settles (~0.49s/run, no perpetual update). - -## Where the remaining time goes - -CLI one-shot path (`createAgentSession` -> `loadProject`): - -1. Node process floor (~0.35-0.40s) -2. Native `.node` load (Windows also hashes/copies via runtime cache) -3. Incremental index open: manifest + git reconcile + snapshot JSON hydrate -4. For default hybrid `search` / `explore`: eager detailed symbol-graph load/build - -`orient` already uses `symbolGraph: "skip"`. `refs` skips the agent session and goes straight to incremental index + `findReferences`. - -Long-lived MCP already amortizes (1)/(2)/(4) across tools after warmup. - -## Ranked follow-ups - -| Rank | Opportunity | Effort | Expected win | Notes | -| ---- | -------------------------------------------------------------------------------------------- | ------------------------------ | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------ | -| 1 | Defer detailed symbol graph for hybrid `search` / `explore` until a result actually needs it | Medium | High on warm CLI explore/search (often 0.5-2s) | Today `searchNeedsSymbolGraph` only skips for `path`/`text`/`sql`; explore always starts hybrid search | -| 2 | Shrink / speed project-index-snapshot hydrate (binary or chunked sidecar) | Medium-High | Medium warm open | Large JSON snapshot parse shows up even when nothing changed | -| 3 | Prefer MCP / shared daemon for agent loops instead of one-shot CLI | Low (workflow) / High (daemon) | High for repeated ops | MCP session already solves process+native+in-memory reuse | -| 4 | Avoid git reconcile work when signatures prove clean without candidate fan-out | Low-Medium | Low-Medium | Freshness fix removed perpetual reparses; further skip of dependent work possible | -| 5 | Non-git discovery fast path for clean trees | Medium | Low-Medium cold | Full `listProjectFiles` still expensive when git fast path unavailable | - -## Non-goals / already covered - -- Impact/review budgets (#165) -- Warm scoped inspect / no-change review / repeated symbol search (#164) -- Claiming one-shot CLI can match warm MCP without a persistent process - -## Suggested next implementation slice - -1. ~~Make hybrid `search` load the detailed symbol graph lazily (and teach `explore` not to force it up front).~~ **Done:** `loadProject({ symbolGraph: "basic" })` builds an in-memory `buildSymbolGraph` from the loaded index; hybrid/symbol/graph search uses it and no longer loads the detailed sidecar. Explore already followed search with `skip`. -2. Add a microbench: warm `search`/`explore` before/after on sheetflare + code-review-agent. -3. Only then consider snapshot format changes. diff --git a/docs/plans/2026-07-25-performance-program-index.md b/docs/plans/2026-07-25-performance-program-index.md index 0414a7b9..cef20ee9 100644 --- a/docs/plans/2026-07-25-performance-program-index.md +++ b/docs/plans/2026-07-25-performance-program-index.md @@ -26,14 +26,14 @@ Warm, this repository: Where a warm command spends time: -| Layer | Measured cost | Plan | -| --------------------------------------------- | ------------------------- | ---------------------------------------------------------------- | -| Process start + ESM module graph | 262 ms | [startup](2026-07-25-startup-cost-bundling-and-lazy-dispatch.md) | -| Git subprocesses (7 spawns, serialized) | ~200 ms | [git](2026-07-25-git-subprocess-elimination.md) | -| Bloom filter rebuild over 668 unchanged files | 404 ms | [hydrate](2026-07-25-warm-index-hydrate-costs.md) | -| Detailed symbol-graph sidecar validation | 215-240 ms | [hydrate](2026-07-25-warm-index-hydrate-costs.md) | -| Native fingerprint (forces addon load) | 35 ms warm / ~300 ms cold | [native](2026-07-25-native-runtime-startup.md) | -| Project snapshot read + parse (8.97 MB) | 42 ms, sometimes twice | [hydrate](2026-07-25-warm-index-hydrate-costs.md) | +| Layer | Measured cost | Plan | +| --------------------------------------------- | ------------------------- | ------------------------------------------------- | +| Process start + ESM module graph | 262 ms | Implemented | +| Git subprocesses (7 spawns, serialized) | ~200 ms | [git](2026-07-25-git-subprocess-elimination.md) | +| Bloom filter rebuild over 668 unchanged files | 404 ms | [hydrate](2026-07-25-warm-index-hydrate-costs.md) | +| Detailed symbol-graph sidecar validation | 215-240 ms | [hydrate](2026-07-25-warm-index-hydrate-costs.md) | +| Native fingerprint (forces addon load) | 35 ms warm / ~300 ms cold | [native](2026-07-25-native-runtime-startup.md) | +| Project snapshot read + parse (8.97 MB) | 42 ms, sometimes twice | [hydrate](2026-07-25-warm-index-hydrate-costs.md) | Cold start, measured once on first touch after a build: @@ -44,16 +44,16 @@ Cold start, measured once on first touch after a build: The cold penalty is per-file overhead multiplied across the module graph (open, stat, resolve, read, and on Windows antivirus inspection). It is the single largest cold-start term. -## The four plans +## Remaining plans and completed startup slice -1. [Startup cost: bundling, lazy dispatch, compile cache](2026-07-25-startup-cost-bundling-and-lazy-dispatch.md) - - Removes a measured 185-210 ms from every invocation and is the primary cold-start fix. -2. [Warm index hydrate costs](2026-07-25-warm-index-hydrate-costs.md) +The startup work shipped lazy dispatch, a bundled CLI entry, the V8 compile cache, and eager-import trimming. Further startup cuts must be re-measured before adding work. + +1. [Warm index hydrate costs](2026-07-25-warm-index-hydrate-costs.md) - Removes recomputation of data that is already persisted. Largest warm win. -3. [Git subprocess elimination](2026-07-25-git-subprocess-elimination.md) +2. [Git subprocess elimination](2026-07-25-git-subprocess-elimination.md) - Takes warm read-only queries from 6-7 git spawns toward zero, and fixes a latent `ENAMETOOLONG` correctness bug. -4. [Native runtime and worker startup](2026-07-25-native-runtime-startup.md) +3. [Native runtime and worker startup](2026-07-25-native-runtime-startup.md) - Stops loading and hashing a 29 MB addon for commands that never parse a file. ## Recommended implementation order @@ -74,7 +74,7 @@ Order is chosen so that each step is independently shippable and independently m ## Program-level acceptance -Measured on this repository, warm, after all four plans: +Measured on this repository, warm, after the program: - [ ] `codegraph --version` at or under 100 ms (from 299 ms). - [ ] Warm `orient --budget small` at or under 450 ms (from 1256 ms). @@ -94,6 +94,4 @@ Measured on this repository, warm, after all four plans: ## Supersedes -`2026-07-23-cold-start-explore-latency.md` proposed ranked follow-ups from partial data. Its -Slice 1 shipped (basic symbol graph for hybrid search and explore). Its remaining ranks are -replaced by the measured findings in these four plans. +An earlier cold-start exploration proposed ranked follow-ups from partial data. Its basic symbol-graph slice shipped; its remaining ranks were replaced by the measured findings in this program. diff --git a/docs/plans/2026-07-25-startup-cost-bundling-and-lazy-dispatch.md b/docs/plans/2026-07-25-startup-cost-bundling-and-lazy-dispatch.md deleted file mode 100644 index f26896b4..00000000 --- a/docs/plans/2026-07-25-startup-cost-bundling-and-lazy-dispatch.md +++ /dev/null @@ -1,308 +0,0 @@ -# Startup cost: bundling, lazy dispatch, and compile cache - -Status: Priority 0-3 implemented on branch. Measurements verified on `main` at `3024ed2b` (`v1.8.100`), Node v24.15.0, -Windows 11, on 2026-07-25. See -[performance program index](2026-07-25-performance-program-index.md) for the shared baseline. - -## Goal - -Remove the fixed per-invocation cost that every command pays before doing any work, and remove -the per-file cold-start penalty that dominates a first run. - -## Non-goals - -- No change to command behavior, output, or flag surface. -- No removal of the unbundled `dist/` layout used by tests and library consumers. -- No daemon. This plan makes one-shot invocations cheaper; it does not replace MCP. - -## Verified baseline defects - -### F1: The full command surface loads on every invocation - -`src/cli.ts:30-59` statically imports roughly 30 command handlers. Only four commands are -dynamically imported today (`cli/sql.js`, `cli/artifact.js`, `cli/mcp.js`, `cli/graph.js`, -reached via `import()` in `src/cli.ts`). - -Walking the static ESM graph from `dist/cli.js` measured: - -- 283 of 335 `.js` files load eagerly, totalling 1.84 MB. -- Only 52 files (257 KB) are outside the eager graph. - -Per-command closures, if each handler were lazily imported: - -| Command | Modules needed | Bytes | Share of today's eager graph | -| --------- | -------------- | ------- | ---------------------------- | -| `install` | 4 | 32 KB | 2% | -| `doctor` | 12 | 46 KB | 2% | -| `orient` | 165 | 920 KB | 49% | -| `search` | 161 | 962 KB | 51% | -| `review` | 190 | 1180 KB | 63% | -| `impact` | 204 | 1277 KB | 68% | -| `explore` | 210 | 1358 KB | 72% | - -37 modules (317 KB) are reachable only from `duplicates`, `review`, `impact`, `renamePreview`, -`install`, and `drift`. Every agent query loads them and never calls them. - -### F2: Fixed overhead is 262 ms before any work happens - -Measured medians over 5 runs each: - -- `node -e 0`: 36 ms -- `node dist/cli.js --version`: 299 ms -- `node dist/cli.js --help`: 293 ms -- `node dist/cli.js doctor`: 299 ms - -Printing a version string costs 263 ms more than starting Node. `--help` and `doctor` cost the -same, which confirms the cost is module loading, not command work. - -### F3: Cold start is dominated by per-file module loading - -- First `import dist/cli.js` after a build: 6119 ms -- Same import warm: 301 ms - -The gap is per-file overhead across 283 modules. A CPU profile of a warm `orient` on the tiny -fixture attributes roughly 80 ms of self time to ESM loader machinery alone -(`internalModuleStat`, `compileSourceTextModule`, `resolveSync`, `module_map`, -`package_json_reader`). - -### F4: Node's compile cache is available and unused - -Node v24 exposes `module.enableCompileCache()`. There is no reference to it, nor to -`NODE_COMPILE_CACHE`, anywhere in `src/`, `scripts/`, or `package.json`. - -## Measured proof of the fix - -A throwaway esbuild bundle of `dist/cli.js` (single file, 3.54 MB, `node:*` and the native -addon left external) produced byte-identical `orient --json` output and these medians: - -| Entry | `--version` | `orient` (tiny fixture) | `search` (tiny fixture) | -| ------------------------------------ | ----------- | ----------------------- | ----------------------- | -| `dist/cli.js` | 299 ms | 471 ms | 455 ms | -| bundled | 111 ms | 287 ms | 266 ms | -| bundled + `NODE_COMPILE_CACHE` | 89 ms | not measured | not measured | -| `dist/cli.js` + `NODE_COMPILE_CACHE` | 283 ms | not measured | not measured | - -Reading of those numbers: - -- Bundling removes about 185 ms per invocation. Fixed overhead drops from 263 ms to 75 ms. -- Compile cache adds a further 22 ms only once bundled, because unbundled cost is resolution - and file I/O rather than compilation. -- Together, fixed overhead drops from 263 ms to 51 ms. - -An esbuild build with `splitting: true` produced 20 chunks totalling 3.38 MB, confirming that -code splitting is viable and that lazy dispatch survives bundling. - -## Priority 0: Lazy command dispatch - -Convert static handler imports in `src/cli.ts` to dynamic `import()` resolved at dispatch, -matching the pattern already used for `sql`, `artifact`, `mcp`, and `graph`. - -- [x] Replace the static `handle*Command` imports in `src/cli.ts` with `import()` calls - inside each command branch. -- [x] Keep `src/cli/help.js` and `src/cli/options.js` eager. Argument validation and help text - run before dispatch. -- [x] Keep the `runCli` export signature and `isDirectCliExecution` behavior unchanged. -- [x] Confirm no handler is imported for its side effects. If any is, make the side effect - explicit rather than relying on import order. - -Also required to hit the <30-module acceptance gate (not obvious from the original sketch): - -- [x] Make `buildDoctorReport`, `collectGraph`, and `CodegraphLifecycleUserError` dynamic. - The lifecycle error class was a silent eager pull of ~120 modules via - `lifecycle/manifest.js`. -- [x] Use `import type` for `GraphBuildOptions` / `NativeRuntimeMode`. With - `verbatimModuleSyntax`, `import { type X }` was emitting empty `import {}` statements - that still loaded those modules. - -Likely files: `src/cli.ts`, `tests/cli-startup-eager-modules.test.ts`. - -Acceptance: - -- [x] `codegraph --version`, `--help`, and `doctor` load fewer than 30 project modules, - asserted by a test that counts resolved module URLs. - Measured after change: `--version`/`--help` 18 modules, `doctor` 27 (was 283 for - `--version`). -- [x] Focused CLI startup tests green; existing handler surface unchanged (dispatch only). - -### Priority 0 measured result - -Warm medians over 5 runs on this repository (Node v24.15.0, Windows): - -| Entry | Before (plan baseline) | After Priority 0 | -| ----- | ---------------------- | ---------------- | -| `--version` | 299 ms | 139 ms | -| `--help` | 293 ms | 140 ms | -| `doctor` | 299 ms | 150 ms | - -Lazy dispatch alone removes ~160 ms from fixed overhead on the unbundled path by collapsing -the eager graph from 283 modules to 18 for `--version`. Bundling (Priority 1) remains the -main cold-start lever and should compose with these split points. - -### Note on interaction with bundling - -Lazy dispatch alone helps the unbundled path. Bundling alone helps cold start. They compose only -if the bundle uses `splitting: true` so dynamic imports become separate chunks. Land lazy -dispatch first so the bundle has real split points to work with. - -## Priority 1: Ship a bundled CLI entry -- IMPLEMENTED (`a7115e40`) - -Add a bundling step to the build that emits a split ESM bundle, and point the `codegraph` bin at -it. Keep the unbundled `dist/` output intact for tests and for library consumers importing -`@lzehrung/codegraph`. - -- [x] Added `scripts/bundle-cli.mjs` / `bundle-cli-lib.mjs` using esbuild 0.27.2 (now an - explicit `devDependency`; previously only transitive via vitest) producing `dist/bin/` - from `dist/cli.js` with `bundle: true`, `platform: "node"`, `format: "esm"`, - `splitting: true`, and `node:*` plus `@lzehrung/codegraph-native` external. -- [x] Emit a `createRequire` banner on every chunk. Required not just for - `src/sqlite-driver.ts`, but because some transitive CJS deps still emit bare - `require("os")`-style calls that esbuild otherwise rewrites into a throwing helper. -- [x] Repointed `package.json` `bin.codegraph` to `dist/bin/cli.js`. -- [x] Kept `dist/index.js` and the rest of `dist/` unbundled and exported as today. Tests still - import unbundled modules; `ensure-dist-for-tests` now also requires the bundled entry so a - partial build fails closed. -- [x] `package.json` `files` already ships `dist/`, which includes `dist/bin/`. -- [x] Bundle step verifies `--version` parity and `orient --json` parity on a fresh tiny - fixture before the build succeeds. Regression coverage in - `tests/cli-bundle-entry.test.ts`. - -Likely files: `package.json`, `scripts/bundle-cli.mjs`, `scripts/bundle-cli-lib.mjs`, -`scripts/ensure-dist-for-tests-lib.mjs`, `docs/installation.md`, -`tests/cli-bundle-entry.test.ts`. - -Acceptance: - -- [x] Bundled `--version` at or under 120 ms on the reference machine. - Measured warm median over 5 runs: **73 ms** (unbundled after Priority 0: 147 ms; - original baseline: 299 ms). -- [x] Bundled `orient --json` output is byte-identical to unbundled for a tiny fixture (build - smoke + test) and for this repository (manual A/B). -- [x] Focused bundle/startup tests green; `test:fast` still exercises unbundled `dist/` - imports. - -### Priority 1 measured result - -Warm medians over 5 runs on this repository (Node v24.15.0, Windows): - -| Entry | After Priority 0 (unbundled) | After Priority 1 (bundled bin) | -| ----- | ---------------------------- | ------------------------------ | -| `--version` | 144 ms | 73 ms | -| `--help` | 146 ms | 72 ms | -| `doctor` | 151 ms | 83 ms | - -Cold-start still benefits further from Priority 2 (compile cache) because bundling removes -per-file resolution cost but not V8 parse/compile of the remaining entry chunk. - -## Priority 2: Enable the V8 compile cache -- IMPLEMENTED (`69d3ec77`) - -- [x] Added `src/cliBootstrap.ts` that calls `enableCliCompileCache()` before dynamically importing - the heavy `cli.js` graph, so compile cache is armed before V8 parses the main entry chunk. -- [x] Cache directory is under the per-user codegraph state root (`%LOCALAPPDATA%/codegraph/compile-cache` - on Windows; `$XDG_CACHE_HOME/codegraph/compile-cache` or `~/.cache/codegraph/compile-cache` - elsewhere), never under project `.codegraph/` / `.codegraph-cache/`. -- [x] Failures are non-fatal. `NODE_COMPILE_CACHE` still overrides the directory; `NODE_DISABLE_COMPILE_CACHE=1` - disables as usual. -- [x] Bundler entry switched to `dist/cliBootstrap.js` with `entryNames: "cli"` so the published - `dist/bin/cli.js` remains the bin path. -- [x] Regression coverage in `tests/cli-compile-cache.test.ts` (path resolution, enable smoke, - bootstrap ordering, delete-cache behavior-only). - -Likely files: `src/cliBootstrap.ts`, `src/cli/compileCache.ts`, `src/cli.ts`, `scripts/bundle-cli-lib.mjs`, -`tests/cli-compile-cache.test.ts`, `docs/installation.md`. - -Acceptance: - -- [x] Second and subsequent invocations measurably faster than the first, with the delta - recorded below. -- [x] Deleting the cache directory changes timing only, never behavior. - -### Priority 2 measured result - -Warm medians over 5 runs on this repository (Node v24.15.0, Windows), bundled bin: - -| Entry | Priority 1 (cache disabled) | Priority 2 (compile cache) | -| ----- | --------------------------- | -------------------------- | -| `--version` | 75 ms | **64 ms** | -| `--help` | 74 ms | **64 ms** | -| `doctor` | 80 ms | **72 ms** | - -First touch into an empty cache directory for `--version`: 88 ms; immediate second touch: 70 ms. -Compile cache is a smaller warm win once Priority 1 has already collapsed the module graph; it still -cuts first-touch parse/compile after a clean cache and shaves ~8-11 ms from warm lightweight commands. - -## Priority 3: Trim what stays eager -- IMPLEMENTED (`1a527e87`) - -Even after Priority 0–2, the unbundled `dist/cli.js` entry still pulled discovery/config/git -helpers into the lightweight `--version` / `--help` / `doctor` graph via static imports from -`src/cli.ts` and `src/cli/context.ts`. - -- [x] Re-ran the static module-load walk for `--version`, `--help`, and `doctor`. After this - cut, each stays under the existing `<30` dist-module gate and no longer loads - `projectFiles.js`, `config.js`, `git.js`, or `duplicates.js`. -- [x] Confirmed `os.cpus()` already lives inside `resolveThreadCount()` in - `src/worker/nativeWorkerPool.ts` (not at module scope); locked that with a regression. -- [x] Moved CLI discovery-glob helpers out of eager `cli/context.ts` into - `cli/discoveryGlobs.ts`, extracted path match helpers into lightweight - `util/discoveryPath.ts`, and lazy-loaded `config`, `projectFiles`, `git`, - `includeRoots`, and discovery globs from `cli.ts` only after the lightweight early - returns (and after `doctor` returns). -- [x] `duplicates.js` is not in the eager set for `--version` / `--help` / `doctor`. - -Likely files: `src/cli.ts`, `src/cli/context.ts`, `src/cli/discoveryGlobs.ts`, -`src/util/discoveryPath.ts`, `src/util/projectFiles.ts`, -`tests/cli-startup-eager-modules.test.ts`. - -Acceptance: - -- [x] Eager dist-module count for `--version` / `--help` / `doctor` stays under 30. -- [x] Those lightweight entrypoints do not load `duplicates.js` or `projectFiles.js`. -- [x] Measured warm medians recorded below. - -### Priority 3 measured result - -Warm medians over 5 runs on this repository (Node v24.15.0, Windows), bundled bin after Priority 3: - -| Entry | Priority 2 | Priority 3 | -| ----- | ---------- | ---------- | -| `--version` | 64 ms | **41 ms** | -| `--help` | 64 ms | **42 ms** | -| `doctor` | 72 ms | **48 ms** | - -Unbundled `dist/cli.js` eager dist-module counts after Priority 3: `--version`/`--help` **8**, `doctor` **19** (still under the `<30` gate; none load `duplicates.js`, `projectFiles.js`, `config.js`, or `git.js`). - -## Validation checklist - -### Focused automated checks - -- [ ] `npx vitest run tests/cli-regressions.test.ts` -- [ ] `npx vitest run tests/agent-search.test.ts tests/agent-session.test.ts` -- [x] New test asserting the eager module count for `--version` stays under a threshold, so this - regression cannot silently return. - -### Measurement protocol - -Record medians of 5 runs, warm, on this repository, before and after each priority: - -- [x] `--version` -- [ ] `orient --root . --budget small --json` -- [ ] `search --root . --limit 3` -- [ ] First-touch cold `import` of the CLI entry, measured after a clean rebuild. - -### Repository qualification - -- [ ] `node ./dist/cli.js doctor` -- [ ] `npm run check` - -## Documentation - -Per repository policy, update in the same change if behavior or guidance moves: - -- [ ] `docs/installation.md` if the shipped entry point changes. -- [ ] `PUBLISHING.md` if the release workflow gains a bundle step. -- [ ] `docs/how-it-works.md` with a short note on startup cost and why MCP remains preferred for - repeated operations. - -## Success criteria - -A one-shot CLI invocation costs close to Node's own floor plus the command's real work. Cold -first use no longer pays a multi-second module-loading penalty. diff --git a/docs/plans/2026-07-27-one-command-product-funnel.md b/docs/plans/2026-07-27-one-command-product-funnel.md deleted file mode 100644 index 45ff67c7..00000000 --- a/docs/plans/2026-07-27-one-command-product-funnel.md +++ /dev/null @@ -1,685 +0,0 @@ -# One-command product funnel - -Status: Implemented - -Parent review: [Project improvement review](./2026-07-27-project-improvement-review.md) - -Related plans: - -- [Self-contained distribution](./2026-07-03-04-self-contained-distribution.md) -- [Agent installer workflow](./2026-07-03-05-agent-installer-workflow.md) -- [Upgrade command](./2026-07-03-13-upgrade-command.md) - -## Vision - -A new user should move from no Codegraph installation to one useful, evidence-backed repository answer in under five minutes. The path should be obvious without reading the full CLI reference, safe without hidden writes, and consistent across package, standalone, source-checkout, and agent-client installation channels. - -The funnel is: - -```text -Install one trusted artifact - -> configure one detected agent client - -> verify runtime health - -> ask one real repository question - -> receive bounded evidence and one copyable follow-up -``` - -This program is not a marketing-only rewrite. It changes the default CLI entrance, turns the existing installer into a guided transaction, ships self-contained release artifacts, and validates the entire path on clean environments. - -## Why this is needed - -The product already has the hard capabilities: - -- `explore` provides a broad first answer with bounded packets and follow-ups. -- `orient` provides a deterministic first-turn map. -- `install` detects Codex, Claude, Cursor, Gemini, OpenCode, OMP, Kilo Code, and generic agent skill directories. -- installer writes are owned and reversible. -- `doctor` reports runtime/package/native state. -- MCP exposes the same agent-facing search and navigation contracts. - -At plan creation, the entrance was fragmented: - -- bare `codegraph` fell through to the graph command rather than teaching the product path, -- the full help was command-oriented rather than task-oriented, -- unknown commands failed without a useful correction, -- `codegraph install` required users to infer `--detect`, `--dry-run`, and `--yes`, -- GitHub Packages installation required scoped registry configuration, -- npm release tarballs did not include Node or the native addon, -- no clean-machine test proved install-to-first-query duration and output. - -The result was a capable tool with a high activation cost. - -## Product principles - -1. **Task first.** Lead with questions users have, not an alphabetical command inventory. -2. **No silent writes.** Interactive confirmation or explicit `--yes` is always required. -3. **One recommendation.** Every state offers one primary next action, then alternatives. -4. **Copyable commands.** Paths are quoted correctly for the active shell where possible. -5. **Structured output stays stable.** Human guidance never leaks into JSON or protocol responses. -6. **No hidden network behavior.** Installed commands do not download or phone home unless the user invokes an explicit installer/update path. -7. **Native state is visible.** Reduced mode is useful but never presented as equivalent semantic coverage. -8. **Fast entrance.** No-argument help and typo handling stay on the lightweight CLI path. - -## Primary personas - -### Agent user - -Has Codex, Claude, Cursor, Gemini, or OpenCode and wants Codegraph available as MCP tools and a bundled skill. - -Success: one configuration command, client restart if required, then an agent can call `explore` or `search`. - -### Terminal user - -Wants to inspect a repository without configuring an agent. - -Success: install, `doctor`, then `codegraph explore "..." --root .`. - -### Contributor - -Uses a source checkout and needs commands that distinguish contributor workflow from published/global guidance. - -Success: `npm install`, `npm run build`, then `node ./dist/cli.js ...` with the same onboarding content. - -## Target journey - -### Published package path - -```bash -npm login --scope=@lzehrung --auth-type=legacy --registry=https://npm.pkg.github.com -npm config set "@lzehrung:registry" "https://npm.pkg.github.com" -npm install -g @lzehrung/codegraph -codegraph install -``` - -`codegraph install` detects local clients, shows exact proposed changes, asks once on an interactive terminal, applies only after confirmation, runs a bounded doctor check, and prints the client-specific first query. - -### Standalone path - -PowerShell: - -```powershell -irm https://github.com/lzehrung/codegraph/releases/latest/download/install.ps1 | iex -``` - -POSIX shell: - -```bash -curl -fsSL https://github.com/lzehrung/codegraph/releases/latest/download/install.sh | sh -``` - -The bootstrap script downloads a platform archive and `SHA256SUMS`, verifies the selected archive before extraction, installs under a user-owned directory, and reports the launcher directory for `PATH`. The preview channel is checksummed but not signed; documentation must also show an inspect-then-run alternative for users who do not pipe remote scripts to a shell. - -After installation, the script invokes or recommends the same `codegraph install` flow. The standalone archive includes Node, the CLI, production dependencies, bundled skill, and matching native addon; it does not depend on npm registry configuration at runtime. - -### Source checkout path - -```bash -npm install -npm run build -node ./dist/cli.js install -``` - -Contributor docs keep using `node ./dist/cli.js`. Bare `codegraph` remains reserved for published/global guidance. - -## Default CLI entrance - -### Bare invocation - -Change `codegraph` with no arguments to print concise task-oriented help and exit 0. It must not build an index, scan the project, read config, or load command modules. - -Proposed output contract: - -```text -codegraph - Ask structural questions about a repository - -Start here: - Understand a repository codegraph explore "how does auth reach the database?" --root . - Review local changes codegraph review --base HEAD --head WORKTREE --summary - Find a symbol or file codegraph search "SessionManager" --json - Configure an agent codegraph install - Check runtime health codegraph doctor - -Run codegraph --help for all commands. -``` - -Keep this output under 15 lines and 1 KiB. Measure startup against `codegraph --version`; p50 may regress by no more than 10%. - -`codegraph --help` retains the complete reference but moves the same five-task block above command details. - -### Unknown command - -For an unknown command: - -1. return the current usage-error exit code, -2. print `Unknown command "...".`, -3. print up to three close command names, -4. print one task-oriented route when the token resembles a user intent, -5. never start project discovery. - -Example: - -```text -Unknown command "serach". -Did you mean: search? -Try: codegraph search "" --json -``` - -Use a dependency-free edit-distance helper in the lightweight CLI graph. Normalize ASCII case, compare exact command names and intentional aliases, cap suggestions by both distance and count, and sort deterministically by distance then command name. - -Do not guess and execute. Suggestions are text only. - -### Single command catalog - -Add `src/cli/commandCatalog.ts` as lightweight metadata: - -```ts -type CliCommandMetadata = { - name: string; - summary: string; - family: "start" | "search" | "navigate" | "review" | "graph" | "manage"; - aliases?: readonly string[]; -}; -``` - -Derive known-command checks and help command lists from the catalog. Dispatch remains explicit so importing the catalog does not eagerly import handlers. - -Add a test that every dispatchable command is present exactly once and every catalog command reaches a handler or documented alias. This removes help/dispatch drift without a broad command-framework rewrite. - -## Guided installer transaction - -The current registry implementation already detects targets, previews actions, writes exact owned blocks/files, and requires `--yes`. Preserve those safety properties while improving the interactive path. - -### State machine - -```text -DETECT - -> no targets: explain and offer explicit --target choices - -> targets found: PREVIEW -PREVIEW - -> non-interactive without --yes: print copyable --dry-run/--yes commands, exit usage error - -> interactive: show target + path + create/update/unchanged actions, PROMPT -PROMPT - -> no/EOF: exit 0 with no changes - -> yes: APPLY -APPLY - -> verify owned files/config - -> run bounded health checks - -> print client-specific restart and first-query instructions -``` - -### Interactive behavior - -When stdin and stderr are TTYs and neither `--yes` nor `--dry-run` is supplied: - -1. detect targets, -2. compute the same dry-run result used by `--dry-run`, -3. show a concise table grouped by target, -4. ask `Configure Codegraph for N detected target(s)? [y/N]`, -5. accept only `y` or `yes` case-insensitively, -6. treat blank, EOF, interrupt, and all other input as no, -7. recompute and apply using existing atomic writes, -8. verify exact owned markers/payloads. - -Use `node:readline/promises`; add no prompt dependency. - -Explicit `--yes` remains the noninteractive automation path. Explicit target lists preserve their current meaning. `--dry-run`, `--detect`, and `--print-config` remain noninteractive. - -### No detected targets - -Do not return an empty successful install that appears to have configured something. Print: - -- supported clients, -- what paths were checked, -- `codegraph install --target --dry-run`, -- `codegraph install --target --yes`. - -Structured output includes `detected: []`, `installed: false`, and a stable `reason: "no-targets-detected"` without prose-only ambiguity. - -### Verification - -After writes, verify only owned state: - -- expected MCP entry or TOML marker block exists once, -- bundled skill payload hash matches the installed payload, -- pointer/marker files resolve inside the target directory, -- no unrelated config keys changed, -- `codegraph doctor` can resolve package and native status. - -Do not launch or modify the agent application. Print restart/reload guidance by target. - -### Agent-specific completion messages - -Keep completion output short and exact: - -- Codex/Claude/Cursor/Gemini/OpenCode: restart or reload the MCP client, then ask it to use Codegraph to map the repository. -- generic `agents`: state that only the skill was installed and provide the manual MCP command/config path if applicable. -- terminal-only users: provide one `explore` command. - -Never claim the agent has connected until an MCP handshake was observed. - -## First-query experience - -### Recommended first command - -Use `explore`, not `orient`, as the primary human onboarding command because it answers a concrete question. Keep `orient` as the fallback when the user does not yet know what to ask. -Ranking and composition corrections for that first answer are specified in the [Explore first-query ranking tuning plan](./2026-07-28-explore-first-query-ranking-tuning.md). - -Primary: - -```bash -codegraph explore "Where should I start in this repository?" --root . -``` - -Alternative: - -```bash -codegraph orient --root . --budget small -``` - -The first successful human-readable response should end with one clearly labeled recommended next command. Existing response schemas and follow-up bounds remain unchanged. - -### Progress and latency - -Cold first queries may index. Preserve automatic interactive progress and `--no-progress`. The onboarding docs must explain that the first query prepares the index and later queries reuse it. - -Coordinate with the persistent-query-substrate plan, but do not block the basic funnel on that performance work. The funnel smoke records cold and warm durations so the performance program can improve them without changing onboarding contracts. - -### Error recovery - -For common failures, print one source fix and one diagnostic: - -- no files discovered: show the effective root and relevant include/ignore guidance, -- native unavailable: state reduced mode and `codegraph doctor`, -- registry package mismatch: show installed and running versions, -- permission denied during install: show the exact user-owned path and avoid recommending administrator mode by default, -- agent target not detected: show explicit `--target` examples, -- stale MCP process: explain client restart and actual running version. - -Human guidance belongs on stderr for failures. JSON remains valid and prose-free. - -## Self-contained distribution - -This program absorbs the user journey from the existing self-contained-distribution plan. Reuse that plan's artifact layout unless implementation evidence requires a change. - -### Artifacts - -```text -codegraph-linux-x64.tar.gz -codegraph-linux-arm64.tar.gz -codegraph-darwin-x64.tar.gz -codegraph-darwin-arm64.tar.gz -codegraph-win32-x64.zip -codegraph-win32-arm64.zip -SHA256SUMS -SHA256SUMS.sig or provenance attestation -install.sh -install.ps1 -``` - -Each archive contains: - -```text -codegraph-/ - node or node.exe - bin/codegraph or bin/codegraph.cmd - dist/ - package.json - node_modules/production dependencies - node_modules/@lzehrung/codegraph-native/ - node_modules/@lzehrung// - codegraph-skill/ - LICENSE - THIRD_PARTY_NOTICES - manifest.json -``` - -The launcher resolves all paths relative to itself and uses the bundled Node runtime. It must not depend on the current directory, global npm, shell initialization, or registry access. - -### Installer bootstrap safety - -The release bootstrap scripts: - -1. detect supported OS and architecture, -2. resolve an explicit release version, defaulting to latest only when requested, -3. download archive and checksum/provenance files over HTTPS, -4. verify SHA-256 before extraction, -5. reject archive traversal, absolute paths, device files, and unsafe symlinks, -6. extract into a versioned temporary directory, -7. run bundled `codegraph version` and `doctor`, -8. atomically move into a versioned user-owned install root, -9. update a small `current` pointer or launcher atomically, -10. preserve the previous version for rollback, -11. offer agent configuration through the bundled CLI. - -Default install roots: - -- Windows: `%LOCALAPPDATA%\Programs\codegraph\` -- macOS/Linux: `${XDG_DATA_HOME:-~/.local/share}/codegraph/` -- launchers: user-local bin directory, with explicit PATH instructions when absent - -Never overwrite a running native addon in place. Versioned roots and the existing immutable native cache make upgrades safe for long-lived MCP processes. - -### Signing and provenance - -Checksums alone prove integrity only after the checksum file is trusted. Use GitHub artifact attestations or a documented signing mechanism for release assets. The installer records release URL, version, archive hash, and verification method in an install manifest. - -If signing is not ready in the first PR, label the channel preview and require checksum verification; do not describe it as signed. - -### Unsupported platforms - -Detect unsupported OS/architecture before download and link to package/source installation. Do not silently install reduced mode while claiming the standalone target is native-capable. - -Runtime certification comes from the release-semantic-certification program. A standalone artifact cannot be promoted from preview until its matching host smoke passes. - -## Upgrade behavior - -Do not add an `upgrade` command that only checks for updates and prints instructions. That behavior was rejected as misleading. - -A real standalone/package upgrade must: - -- detect the active installation channel, -- show current and target versions, -- confirm unless `--yes`, -- execute the channel-specific update, -- stream subprocess output, -- propagate exit status, -- verify the resulting installed version, -- handle permissions without defaulting to elevation, -- refuse dirty or detached source-tree updates, -- avoid Windows native-addon locking through versioned immutable installs. - -The funnel may initially print channel-specific manual update commands. Name them "update instructions", not `upgrade`, until the full contract above exists. - -## Landing-page and documentation hierarchy - -Keep `README.md` as the landing page and index, not the canonical reference for every workflow. - -### README top section - -Within the first screen: - -1. one sentence: what Codegraph does, -2. one verified install path, -3. one `codegraph install` command, -4. one first repository question, -5. one small real output excerpt or screenshot generated from a checked fixture, -6. links to installation, agent workflows, benchmarks, and CLI reference. - -Do not lead with every command, package role, or architecture detail. - -### Canonical docs - -- `docs/installation.md`: all channels, verification, PATH, updates, uninstall -- `docs/agent-workflows.md`: agent setup and first prompts -- `docs/cli.md`: no-arg, suggestions, installer flags, exit codes -- `docs/mcp.md`: client configuration and handshake troubleshooting -- `docs/how-it-works.md`: indexing/caching behavior after first query -- `PUBLISHING.md`: standalone archive assembly and provenance -- `codegraph-skill/codegraph/SKILL.md`: exact CLI commands and agent guidance - -Update README table of contents whenever headings change. - -## Funnel smoke harness - -Add `scripts/onboarding/`: - -- `funnel-contract-lib.mjs`: result schema and step assertions -- `run-funnel-smoke.mjs`: clean-home scenario runner -- `standalone-install-lib.mjs`: shared archive/install validation where safe -- tests for output parsing, timeout, cleanup, and secret redaction - -Run one channel at a time: - -```bash -node scripts/onboarding/run-funnel-smoke.mjs --channel source --root . --output funnel-source.json -node scripts/onboarding/run-funnel-smoke.mjs --channel package --artifact --output funnel-package.json -node scripts/onboarding/run-funnel-smoke.mjs --channel standalone --artifact --target --output funnel-standalone.json -``` - -Result schema: - -```ts -type FunnelSmokeResultV1 = { - schemaVersion: 1; - scenario: "clean-home-source" | "exact-package-candidate" | "extracted-standalone"; - channel: "source" | "package" | "standalone"; - target: "win32-x64" | "win32-arm64" | "darwin-x64" | "darwin-arm64" | "linux-x64" | "linux-arm64"; - status: "pass" | "fail"; - version: string | null; - timings: { - totalMs: number; - steps: Array<{ name: string; durationMs: number }>; - }; - checks: Array<{ - name: string; - status: "pass" | "fail" | "skipped"; - durationMs: number; - exitCode?: number | null; - }>; - diagnostics: Array<{ - code: string; - message: string; - step?: string; - command?: string; - exitCode?: number | null; - stdout?: string; - stderr?: string; - }>; -}; -``` - -The runner uses temporary `HOME`, `USERPROFILE`, XDG, application-data, and npm-cache roots plus a temporary repository fixture. It must fail if the installed binary resolves modules from the developer checkout. - -### Scenarios - -1. Package install from exact candidate tarballs. -2. Standalone archive install through the bootstrap logic without piping from the network. -3. Source checkout contributor flow. -4. No detected agent target. -5. One detected target with decline. -6. One detected target with confirmation. -7. Noninteractive install without `--yes`. -8. Explicit `--yes` install. -9. Native available and native unavailable doctor states. -10. First `explore` query and one follow-up command. -11. MCP initialize/list-tools/search after configuration. -12. Uninstall removes owned state and preserves unrelated config. - -Use fake home directories containing realistic minimal client config, not actual developer settings. - -## Acceptance metrics - -### Correctness - -- bare invocation exits 0, prints task help, and does no discovery, -- typo suggestions are deterministic and never execute, -- interactive decline writes nothing, -- EOF/interrupt writes nothing, -- confirmed installer changes only owned state, -- packed and standalone binaries pass `version`, `doctor`, first query, and MCP handshake, -- uninstall preserves unrelated config byte-for-byte where formatting permits. - -### Time - -On clean supported CI runners, excluding artifact download bandwidth but including extraction/configuration/indexing: - -- package or standalone install to successful `doctor`: <= 2 minutes, -- install to first successful bounded query: <= 5 minutes, -- no-arg help p50: within 10% of `--version`, -- installer detection and preview: <= 2 seconds on synthetic homes, -- warm repeat query recorded separately from cold first query. - -Use timeouts to catch hangs, not as the only performance evidence. - -### Usability - -A fresh-user test script provides only: - -- the install command, -- a sample repository path, -- the goal "find where authentication reaches storage". - -Success requires reaching a source-backed answer without opening the full CLI reference. Record wrong turns and revise guidance; do not add telemetry to user machines. - -## Implementation sequence - -### PR 1: lightweight entrance - -- add command catalog, -- make bare invocation task-oriented, -- add typo suggestions, -- move task routes to the top of full help, -- add startup and dispatch parity tests. - -Exit: no-arg and unknown-command paths load no heavy project modules. - -### PR 2: guided installer - -- add TTY detection, preview, prompt, apply, verify state machine, -- handle no-target state explicitly, -- add client-specific completion guidance, -- preserve `--yes`, `--dry-run`, `--detect`, and `--print-config`. - -Exit: clean-home interactive and automation scenarios pass on Windows, macOS, and Linux. - -### PR 3: first-query and docs funnel - -- update README landing section and canonical docs, -- add clear cold-index progress and one recommended follow-up, -- add package/source funnel smoke harness. - -Exit: package candidate reaches first query and MCP handshake in the clean-home harness. - -### PR 4: standalone archives - -- assemble Node, CLI, production deps, native package, skill, notices, and manifest, -- add relative launchers, -- attach preview artifacts to releases, -- validate every host-executable target. - -Exit: archives run on clean hosts without npm or system Node. - -### PR 5: verified bootstrap and promotion - -- add install scripts, checksum/provenance validation, atomic versioned install, rollback metadata, -- run standalone funnel matrix, -- promote channel from preview after release certification passes. - -Exit: documented one-command path installs verified bytes and reaches first answer. - -### PR 6: real upgrade, only if separately approved - -- implement channel-aware update execution to the full contract, -- add dirty/detached source and Windows locking tests, -- never ship a check-only command named `upgrade`. - -Exit: resulting version is verified after a real update and failures propagate. - -## Required tests - -### CLI entrance - -- no args, `--help`, `help`, version flags -- known command catalog/dispatch completeness -- close typo, distant typo, multiple deterministic suggestions -- no config/discovery imports on lightweight paths -- stdout/stderr and exit-code contracts -- JSON modes remain prose-free - -### Installer - -- TTY yes/no/blank/EOF/interrupt -- non-TTY with and without `--yes` -- no targets and explicit targets -- config create/update/unchanged/idempotent -- invalid config and permission errors -- unrelated config preservation -- verification failure after write with actionable recovery -- concurrent installer attempts and atomic output -- Windows and POSIX path quoting - -### Standalone - -- archive manifest and checksum -- traversal, absolute path, symlink, and checksum rejection -- launcher from directories containing spaces -- no system Node/npm available -- native package selected for target -- reduced-mode behavior only where intentional -- versioned atomic replacement and rollback -- long-lived Windows MCP process during update -- uninstall owns only its install root and launcher - -### Funnel - -- exact candidate package install -- first query has source evidence and bounded follow-up -- MCP handshake from installed binary -- no checkout module leakage -- clean HOME and cleanup after failure -- duration fields and failure artifacts - -Run targeted suites during implementation, then `npm run check`. Drive the actual CLI and packed binaries; source-text assertions alone do not prove the funnel. - -## Documentation compatibility - -This work changes public CLI and installation behavior. The implementation PRs must update, as applicable: - -- `README.md` -- `docs/installation.md` -- `docs/cli.md` -- `docs/agent-workflows.md` -- `docs/how-it-works.md` -- `docs/mcp.md` -- `PUBLISHING.md` -- `codegraph-skill/codegraph/SKILL.md` - -When standalone distribution lands, update package role and target guidance. When no-arg or installer output changes, update help snapshots and command examples in the bundled skill in the same PR. - -## Risks and mitigations - -### Remote bootstrap trust - -Piping scripts to a shell is high trust. Publish inspect-first alternatives, verify signed/checksummed artifacts, keep scripts short, and test every destructive path. - -### Installer modifies user configuration - -Preview exact owned changes, default prompts to no, preserve `--dry-run`, use atomic writes, and verify unrelated config remains. - -### Help path regresses startup - -Keep catalog metadata dependency-free and enforce eager-module denylist/startup tests. - -### Standalone artifact size - -Bundled Node and dependencies are larger than npm installation. Publish sizes, keep npm as a supported channel, and avoid bundling development files. - -### Multiple installation channels confuse upgrades - -Write channel and version into the install manifest. Doctor reports running, installed, native, and channel identities. - -### Reduced mode appears successful - -Doctor and first-query diagnostics state the backend. Standalone supported targets require native runtime certification. - -### Scope grows into a command framework rewrite - -Do not replace explicit dispatch. Add only the lightweight catalog needed for help and suggestions. - -## Definition of done - -- [x] Bare `codegraph` prints concise task-oriented help and exits 0. -- [x] Unknown commands offer bounded deterministic suggestions without discovery. -- [x] Command metadata and dispatch cannot drift silently. -- [x] Interactive `codegraph install` previews, confirms, applies, and verifies owned changes. -- [x] Noninteractive writes still require `--yes`. -- [x] No-target and reduced-native states are explicit. -- [x] README leads with install, configure, and first query. -- [x] Package/source funnel smoke reaches a bounded answer and MCP handshake. -- [x] Standalone archives include Node, native runtime, CLI, skill, notices, and manifest. -- [x] Bootstrap scripts verify artifact integrity and install atomically under a user-owned versioned root. -- [x] Clean-host package and standalone journeys complete within acceptance budgets. -- [x] Uninstall preserves unrelated user state. -- [x] No misleading check-only `upgrade` command ships. -- [x] Canonical docs and bundled skill match the CLI. -- [x] `npm run check` passes. diff --git a/docs/plans/2026-07-27-persistent-query-substrate.md b/docs/plans/2026-07-27-persistent-query-substrate.md deleted file mode 100644 index f1ad7220..00000000 --- a/docs/plans/2026-07-27-persistent-query-substrate.md +++ /dev/null @@ -1,559 +0,0 @@ -# Persistent query substrate - -Status: Implemented - -Parent review: [Project improvement review](./2026-07-27-project-improvement-review.md) - -## Vision - -A warm Codegraph session should search a persistent index, not reread source files. The first query creates it beside the project snapshot. Later CLI and MCP calls reuse it, score candidates, and preserve deterministic results. - -This extends warm project-index work. Without it, warm project loading still leaves `search`, `explore`, and agent packets scanning source or rebuilding normalized content in each process. - -## User outcome - -After implementation: - -- the first search after an index-changing edit updates only changed files, -- a no-change CLI search opens the persistent sidecar and performs no repository-wide source reads, -- repeated MCP searches reuse prepared statements and in-memory result caches, -- CLI and MCP return the same ordered results for the same request and snapshot, -- cache corruption, unsupported schemas, writer contention, and path attacks fall back to a correct rebuild or in-memory search, never a wrong answer. - -## Current measured baseline - -Snapshot: `main` at `44de8b47` (`v1.8.103`), Windows 11, Node.js 24.15.0, 2026-07-27. - -Five-run warm CLI samples on this repository: - -| Mode | Median | Range | Approximate output | -| ------ | -----: | ---------: | -----------------: | -| hybrid | 2.98s | 2.97-3.03s | 11.5 KiB | -| symbol | 1.03s | 1.02-1.04s | 11.5 KiB | -| path | 0.42s | 0.41-0.47s | 3.7 KiB | -| text | 2.26s | 2.16-2.33s | 4.6 KiB | -| graph | 0.86s | 0.86-0.87s | 11.5 KiB | - -Three live MCP `search` calls took approximately 20.5s cold, then 1.14-1.18s warm, with about 26 KiB serialized output per call. - -Runtime tracing showed: - -- `orient` and `inspect` issue no git subprocesses on the measured warm path, -- hybrid and text search costs remain proportional to repository files, -- `src/agent/search.ts` reads snapshot files sequentially and prepares normalized file/chunk content in an in-memory `SearchCache`, -- a fresh CLI process cannot reuse that prepared content, -- MCP can reuse the in-memory cache only while its `AgentSession` remains alive. - -Re-run this matrix before implementation. Store raw JSON timings under `Saved/` or an ignored temporary path; do not commit machine-specific timings as product claims. - -## Existing architecture to preserve - -Build on these existing boundaries; do not create a second indexing stack: - -- `src/indexer/build-index.ts` creates `ProjectIndex.manifestEntries` from the same signatures used by the graph cache. -- `ProjectIndex.projectSnapshotIdentity` identifies an exact reusable project snapshot. -- `src/indexer/build-cache/project-snapshot.ts` already persists the graph/index snapshot and a versioned detailed-symbol-graph sidecar. -- `src/agent/session.ts` owns freshness checks, project reloads, and session-level caches. -- `src/agent/search.ts` owns query parsing, current matching, ranking, omissions, and response formatting. -- CLI `search` calls `searchCodegraph`, while MCP calls the same search contract through a persistent `AgentSession`. - -Do not change search into a vector service. Do not create a daemon. Do not make search correctness depend on network access. - -## Scope - -### Included - -- persistent normalized file and chunk content, -- substring-safe candidate retrieval, -- incremental per-file updates using current manifest identities, -- shared CLI/MCP query execution, -- sidecar schema/version handling, -- cache correctness and concurrency tests, -- query-stage telemetry in existing reports, -- performance and parity gates. - -### Excluded - -- embeddings or approximate nearest-neighbor search, -- natural-language model calls, -- cross-project global indexes, -- replacing symbol, graph, SQL, or navigation indexes, -- changing public search ranking as an optimization shortcut, -- background services or file watchers, -- storing results outside the configured `--root` cache boundary. - -## Design decision: SQLite FTS5 trigram sidecar - -When disk cache is enabled, add one derived sidecar under the existing cache root. For the default project-local cache, its path is `.codegraph-cache/index-v1/search-v1.sqlite`. - -SQLite fits the existing artifacts, Node 22.16+ provides `node:sqlite`, transactions support safe incremental updates, and FTS5 trigram finds normalized substrings of at least three characters. - -Probe `ENABLE_FTS5` and trigram support on the minimum Node version. If unavailable on a supported runtime, ship an explicit trigram-postings table in this schema. Do not claim the persistent index is active while falling back to a full source scan. - -Queries shorter than three normalized characters use `instr(normalized_text, ?)` over sidecar rows. They may scan the sidecar, but they must not reread repository files. - -## Sidecar schema - -Use `PRAGMA user_version = 1` and a metadata table. All paths are normalized project-relative paths; never store absolute source paths in searchable rows. - -```sql -CREATE TABLE metadata ( - key TEXT PRIMARY KEY, - value TEXT NOT NULL -) STRICT; - -CREATE TABLE files ( - file_id INTEGER PRIMARY KEY, - path TEXT NOT NULL UNIQUE, - source_identity TEXT NOT NULL, - surface TEXT NOT NULL, - language TEXT, - normalized_text TEXT NOT NULL, - byte_length INTEGER NOT NULL, - line_count INTEGER NOT NULL -) STRICT; - -CREATE TABLE chunks ( - chunk_id INTEGER PRIMARY KEY, - file_id INTEGER NOT NULL REFERENCES files(file_id) ON DELETE CASCADE, - ordinal INTEGER NOT NULL, - kind TEXT NOT NULL, - name TEXT, - start_line INTEGER NOT NULL, - end_line INTEGER NOT NULL, - text TEXT NOT NULL, - normalized_text TEXT NOT NULL, - UNIQUE(file_id, ordinal) -) STRICT; - -CREATE VIRTUAL TABLE file_search USING fts5( - normalized_text, - content='files', - content_rowid='file_id', - tokenize='trigram' -); - -CREATE VIRTUAL TABLE chunk_search USING fts5( - normalized_text, - content='chunks', - content_rowid='chunk_id', - tokenize='trigram' -); -``` - -Metadata keys: - -- `schemaVersion` -- `projectSnapshotIdentity` -- `normalizerVersion` -- `chunkerVersion` -- `projectRootIdentity` -- `createdByCodegraphVersion` -- `updatedAt` - -`normalizerVersion` and `chunkerVersion` are integer constants owned by the query-index module. Change them whenever matching normalization or chunk boundaries change. - -The sidecar is derived but persistent. New schemas need an explicit migration, or a validated rebuild path and prior-schema regression fixture. Never write an unknown future schema. - -## Source identity and incremental invalidation - -Use `ProjectIndex.manifestEntries` as the file-content provenance. For each file, compute a stable source identity from the available `sig` and `gitSig` fields with an unambiguous versioned encoding. - -```text -sourceIdentity = sha256("search-source-v1\0" + normalizedPath + "\0" + sig + "\0" + (gitSig ?? "")) -``` - -One warm-path gap remains: `tryLoadProjectIndexSnapshot` receives only `filesSignature`, so loaded `ProjectIndex` objects lack `manifestEntries`. Pass it the validated manifest-entry map used for `filesSignature`, then derive or verify the signature and attach sanitized entries to the loaded index. Keep the manifest as the source of truth; do not duplicate entries in `project-index-snapshot.json` or bump that schema. - -Test that a fresh-process snapshot hit exposes the same path, `sig`, and `gitSig` entries as a live incremental build. Use its manifest entries for identity; do not read file metadata again before deciding whether files changed. - -Update algorithm: - -1. Open and validate the sidecar metadata and schema. -2. If `projectSnapshotIdentity`, normalizer version, and chunker version all match, return a read-ready store without reading source files. -3. Otherwise, compare current `ProjectIndex.manifestEntries` with `files.source_identity`. -4. Delete rows for paths absent from the current index. -5. Read, normalize, and rechunk only added or identity-changed paths. -6. Replace each changed file and its chunks within one transaction. -7. Update FTS rows and metadata only after all changed rows succeed. -8. Commit, then reopen or refresh read statements. - -A graph-only snapshot change can change `projectSnapshotIdentity` without changing source identities. Update metadata without rebuilding text rows. - -Current transient include-root files use their manifest identity. Delete retired transients from the sidecar when they leave `ProjectIndex.byFile`. - -If `manifestEntries` is unavailable because cache is off or an older caller built `ProjectIndex` manually, use in-memory search and report `sidecarState: "unavailable"`. Do not invent weaker identities. - -## Atomicity and concurrency - -Multiple CLI processes and an MCP server may share one cache root. - -Use these rules: - -- SQLite WAL mode for readers during updates. -- Foreign keys on. -- A bounded busy timeout no longer than the existing command startup tolerance. -- `BEGIN IMMEDIATE` only around changed-row writes, not source reads or chunk preparation. -- Prepare all changed-file content before acquiring the write transaction. -- Recheck sidecar metadata after acquiring the writer lock; another process may already have completed the update. -- Commit all changed rows and metadata together. -- On `SQLITE_BUSY`, continue with a correct process-local prepared cache and report `sidecarState: "writer-busy"`; never block a user query indefinitely. -- On corruption, rename the sidecar to a bounded diagnostic name when safe, rebuild to a temporary file, fsync, and atomically replace it. -- On Windows, close all database handles before rename or replacement. - -Keep at most one retained corrupt copy per cache identity. Cleanup must never follow symlinks or delete outside the fixed cache root. - -## Query execution - -### Candidate retrieval - -Retain the current search-term parser and current scoring functions. - -For each normalized term: - -- length >= 3: query FTS5 trigram rows, -- length < 3: query `instr(normalized_text, ?)` with the same candidate cap, -- union candidates for the current `textCouldMatchNormalized` any-term semantics, -- verify each candidate with the current exact matcher before scoring. - -FTS rank is never the public score. It selects candidates only; the existing deterministic matcher and ranker remain authoritative. - -Fetch only rows needed for bounded scoring and formatting. Reject at file level before materializing every chunk where possible. - -### Search modes - -- `path`: continue using path candidates from the project index; the sidecar is unnecessary. -- `symbol`: continue using symbol lookup and current symbol scoring; sidecar may supply snippets only for final rows. -- `graph`: continue using graph neighborhoods; sidecar may supply text evidence only for selected files. -- `text`: use sidecar file/chunk candidates directly. -- `hybrid`: merge symbol, graph, path, and sidecar candidates before the existing final ranking and bounds. -- `sql`: preserve current SQL-object search; do not duplicate SQL facts into this sidecar in v1. - -This mode split prevents the persistent text substrate from slowing already-fast path and graph searches. - -### Snippets and source reads - -Persist chunk text for matching and bounded snippets. For a whole-file match outside a chunk, use sidecar content or read only the final matched file after checking its manifest identity. - -Store enough raw chunk text and line offsets to format current results without source reads. Preserve current binary, size, and sensitive-file exclusions. - -### Result cache - -Keep the existing session-level result cache above the sidecar. Its key must include: - -- project snapshot identity, -- normalized query, -- mode, -- result limit, -- depth/from options, -- include-snippets flag, -- versions of the normalizer and ranking contract. - -A sidecar update or refresh invalidates only results for the old snapshot. This top-level cache mainly benefits MCP, not one-shot CLI processes. - -## Module boundaries - -Add: - -```text -src/agent/query-index/ - schema.ts - paths.ts - sourceIdentity.ts - store.ts - update.ts - candidates.ts - worker.ts -``` - -Responsibilities: - -- `schema.ts`: version constants, SQL, payload validation, migrations. -- `paths.ts`: cache-root confinement and sidecar paths. -- `sourceIdentity.ts`: manifest-entry identity only. -- `store.ts`: database lifecycle and prepared statements. -- `update.ts`: diff, changed-file preparation, transactions, corruption recovery. -- `candidates.ts`: safe FTS query construction and bounded candidate retrieval. -- `worker.ts`: off-main-thread normalization/chunking and optional initial database build. - -Keep public request/response types in `src/agent/search.ts` or their existing shared modules. The query-index directory is internal and must not become a second public API in the first PR. - -## Worker strategy - -Initial population can normalize hundreds of files and make many synchronous SQLite writes. Keep it off the MCP event loop. - -- Use one worker for initial build or updates above a small measured changed-file threshold. -- Use the current process for no-change opens and tiny updates. -- Pass project-relative paths and bounded build options to the worker, not a serialized `ProjectIndex`. -- The parent validates paths against the project root before dispatch. -- The worker returns counts, timings, and final identity; it does not return source content. -- Bundle the worker into the published CLI and add a package test proving the bundled worker resolves. - -Choose the threshold from benchmark evidence. Start with a constant, not an adaptive policy. - -## AgentSession integration - -Extend the session's loaded snapshot with an internal query-store handle. Lifecycle: - -1. `loadProject` builds or loads the `ProjectIndex` as today. -2. First text/hybrid search calls `ensureQueryIndex(snapshot)` lazily. -3. No-change sessions open and validate the sidecar. -4. Changed sessions update it from current manifest provenance. -5. `checkFreshness` keeps current semantics; a stale snapshot never queries newer sidecar rows. -6. `refresh` closes statements tied to the old identity, loads the new snapshot, and lazily reopens or updates. -7. `clear` closes database handles and clears memory results. - -Do not eagerly open the sidecar for commands that never search. - -One-shot CLI search must use the same `AgentSession` path, not a separate `searchCodegraph` preparation path. If the exported convenience function needs a wrapper, it creates, uses, and disposes a session. - -## Freshness invariant - -A response may combine only artifacts that prove the same current project state. - -Before returning a sidecar-backed response, verify: - -- session snapshot identity is unchanged, -- sidecar metadata identity equals the loaded snapshot identity, -- each changed row transaction committed, -- freshness state still permits a response under current MCP rules. - -If a file changes during preparation, the existing freshness check must mark or refresh the snapshot. Never mix old symbol results with new text rows. - -## Observability - -Extend internal analysis/report data with: - -```ts -type QueryIndexDiagnostics = { - sidecarState: "hit" | "created" | "updated" | "unavailable" | "writer-busy" | "rebuilt-corrupt"; - filesRead: number; - filesAdded: number; - filesUpdated: number; - filesDeleted: number; - fileCandidates: number; - chunkCandidates: number; - openMs: number; - updateMs: number; - candidateMs: number; - scoringMs: number; -}; -``` - -Expose this only through existing verbose/report diagnostics unless a reviewed public schema changes. Ordinary output stays unchanged. - -Add a test-only file-read counter at the query-index boundary. Exact warm-hit tests must prove zero source reads; timing alone is not proof. - -## Security and privacy - -The sidecar contains normalized source and chunk text. Treat it as sensitive local derived data. - -- Keep it under the existing project cache boundary. -- Respect `--root`, include roots, ignore globs, gitignore, sensitive-file filtering, and max-file-size limits. -- Normalize and realpath-check source paths before reads. -- Reject sidecar rows with absolute paths, traversal, NULs, or paths outside the loaded project. -- Never include environment variables, registry tokens, or source text in corruption filenames or logs. -- Use parameterized SQL exclusively. -- Escape FTS syntax in one tested helper; never concatenate user text into SQL. -- Keep `uninit` unchanged unless the product explicitly decides to remove cache state. - -Document that local cache files can contain indexed source content. - -## Schema evolution - -Version 1 has no deployed predecessor, but implement the framework now: - -1. read `PRAGMA user_version`, -2. reject future versions, -3. migrate known older versions transactionally, -4. rebuild only when a documented migration is impossible for derived data, -5. test an old-schema fixture for every v2+ change. - -A normalizer or chunker mismatch is not a schema migration. It invalidates row content and triggers a bounded rebuild without changing the database contract. - -## Performance targets - -Measure cold, first-sidecar, warm-sidecar, one-file-change, deletion, and repeated-MCP cases. Record five CLI and ten in-process MCP samples after discarding one warmup. - -Reviewed targets for this repository and workstation class: - -- warm hybrid CLI p50 <= 1.2s and at least 2.5x faster than the recorded pre-implementation baseline, -- warm text CLI p50 <= 0.9s and at least 2.5x faster, -- warm symbol/path/graph modes regress by no more than 10% against the same-revision sidecar-disabled baseline, -- repeated warmed MCP search p50 <= 300ms and p95 <= 600ms, -- exact warm sidecar hit reads zero repository source files, -- one-file incremental update reads only that file plus files the current chunking contract requires, -- first-sidecar search regresses no more than 20% versus the current cold search, -- bounded response schemas, ranking, and omission counts remain equivalent. - -Fresh-process startup and snapshot validation raised the hybrid target from 1.0s to 1.2s. Compact cross-word recovery within files admitted by the existing full-file prefilter raised the text target from 0.8s to 0.9s. Both retain the 2.5x goal without changing semantics or parity. - -On Windows 11 with Node.js 24.15.0, five fresh CLI and ten warmed MCP samples on 2026-07-28 measured hybrid p50 at 1.189s (baseline 2.98s), text p50 at 0.863s (baseline 2.26s), and warmed MCP p50/p95 at 166/174ms. - -The same-revision sidecar-disabled p50s were 0.961s symbol, 0.334s path, and 0.967s graph. With the sidecar, they were 0.964s, 0.337s, and 0.974s, within the regression target. - -Absolute timings are environment-specific. CI should gate relative regressions and structural counters; local benchmarks may report absolute values with environment metadata. - -## Correctness tests - -### Candidate parity - -Run the old full-scan matcher and new sidecar candidate path against the same snapshots and compare final responses for: - -- hybrid, text, symbol, path, and graph modes, -- one-, two-, and three-character queries, -- punctuation and quoted text, -- Unicode normalization supported by current search, -- mixed code and documentation matches, -- no matches, -- maximum result limits and pagination/from behavior, -- snippets enabled and disabled. - -Keep the old matcher as a rollout test oracle. Remove it from production after parity is proved. - -### Invalidation - -Cover: - -- no-change exact identity, -- one added file, -- one modified tracked file, -- one untracked file, -- deleted tracked file, -- retired transient include-root file, -- changed config/include roots, -- changed normalizer/chunker version, -- graph identity change with unchanged source rows, -- cache off/memory mode, -- stale MCP snapshot and explicit refresh. - -### Storage and migration - -Cover: - -- initial create, -- reopen in a fresh process, -- malformed metadata, -- unknown future user version, -- truncated/corrupt database, -- FTS table corruption, -- writer contention, -- Windows close-before-replace behavior, -- symlink/cache path escape, -- bounded corrupt-copy retention. - -### Packaging - -Cover: - -- worker bundled in `dist/bin`, -- package metadata includes required runtime files, -- packed CLI search uses the sidecar, -- source checkout and published bin resolve the same cache path, -- minimum supported Node provides the required SQLite feature. - -## Rollout sequence - -### PR 1: measurement and schema probe - -- add benchmark harness and file-read instrumentation, -- probe SQLite FTS5 trigram on supported CI runtimes, -- lock current search parity fixtures, -- make no behavior change. - -Exit: repeatable baselines and a documented storage choice. - -### PR 2: sidecar create and exact-hit read - -- add schema/store/update modules, -- populate lazily on first text/hybrid search, -- reopen on a fresh CLI process, -- keep the full-scan matcher only as a guarded test comparison path. - -Exit: exact warm hit performs zero source reads and parity suite passes. - -### PR 3: incremental update and concurrency - -- diff by `manifestEntries`, -- update added/changed/deleted/transient files, -- add worker threshold, WAL, busy fallback, and corruption recovery. - -Exit: one-file changes stay bounded and concurrent CLI/MCP tests remain correct. - -### PR 4: unify CLI and MCP path - -- route one-shot CLI through the session/query-store lifecycle, -- retain MCP prepared statements and result cache, -- add diagnostics and bundle checks. - -Exit: identical request/snapshot yields identical CLI and MCP ordering and metadata. - -### PR 5: documentation and performance gate - -- publish methodology and measured results, -- add relative performance checks, -- document local source cache sensitivity and cleanup. - -Exit: targets are met on the baseline repository and no existing mode regresses beyond thresholds. - -## Documentation updates - -When implementation lands, update: - -- `docs/how-it-works.md`: search sidecar, identities, invalidation, concurrency -- `docs/cli.md`: cache behavior only if user-visible flags/output change -- `docs/agent-workflows.md`: persistent MCP session behavior and freshness -- `docs/mcp.md`: warmup, refresh, and sidecar diagnostics -- `docs/installation.md`: local cache may contain source-derived text -- `README.md`: one concise performance statement and canonical link -- `codegraph-skill/codegraph/SKILL.md`: any changed CLI/tool contract - -Add no cache flag unless existing `--cache off|memory|disk` cannot express the behavior. Defaults follow the command's current effective cache mode. - -## Risks and mitigations - -### FTS candidate false negatives - -A candidate filter that misses a match is a correctness bug. Use trigram substring candidates, the existing exact verifier, short-query fallback, and parity fixtures before removing production comparison code. - -### Sidecar size - -Raw chunks can duplicate source. Measure indexed bytes per source byte, retain current oversized-file exclusions, and cap the representative corpus at 2.5x indexed text before SQLite overhead. - -### Initial-query regression - -The first sidecar build adds work. Prepare once, batch writes, use a worker for large builds, and cap cold regression at 20%. - -### SQLite synchronous stalls - -`node:sqlite` is synchronous. Keep heavy create/update work off the MCP event loop and bound candidates before materializing rows. - -### Cache identity mismatch - -Mixing graph and text snapshots can produce plausible wrong results. Verify identity before response assembly and invalidate handles on refresh. - -### Windows locking - -Open handles can block replacement. Centralize ownership, close before rename, and test on Windows CI. - -### Public API growth - -`ProjectIndex` already carries manifest provenance. Keep query-store handles session-internal and do not export storage details in v1. - -## Definition of done - -The checked items below remain the implementation audit ledger. - -- [x] Disk cache creates the project-local `.codegraph-cache/index-v1/search-v1.sqlite` sidecar; cache-off creates none. -- [x] Source identities use current `ProjectIndex.manifestEntries`. -- [x] Exact warm CLI search opens the sidecar without repository-wide reads. -- [x] Updates touch only added, changed, deleted, or retired paths. -- [x] FTS selects candidates; existing matching and ranking decide results. -- [x] CLI and MCP use one query path. -- [x] Freshness prevents mixed-snapshot responses. -- [x] Corruption, future schemas, contention, and path escapes fail safely. -- [x] Parity tests cover every mode and short queries. -- [x] Packed-binary tests cover worker and sidecar behavior. -- [x] Hybrid, text, and MCP targets pass without regressions elsewhere. -- [x] Cache privacy and behavior are documented. -- [x] `npm run check` passes. diff --git a/docs/plans/2026-07-27-project-improvement-review.md b/docs/plans/2026-07-27-project-improvement-review.md deleted file mode 100644 index 63e9e92f..00000000 --- a/docs/plans/2026-07-27-project-improvement-review.md +++ /dev/null @@ -1,337 +0,0 @@ -# Project improvement review - -Date: 2026-07-27 -Reviewed revision: `8bad0a98` (`v1.8.103`) -Primary environment: Windows 11, Node.js 24.15.0 - -## Executive recommendation - -Do three cross-cutting programs, not a long list of isolated fixes. - -| Rank | Program | Severity | ROI | Primary result | -| ---: | --------------------------------------------------------------------------------------------------- | -------- | --------- | ----------------------------------------------------------------------------------------------------------------- | -| 1 | [Release and semantic certification matrix](./2026-07-27-release-semantic-certification-program.md) | S0 | Very high | Every published package is secure, loadable on its target, and backed by measured semantic quality. | -| 2 | [Persistent query substrate](./2026-07-27-persistent-query-substrate.md) | S1 | Very high | Search and agent queries stop rescanning the repository and return compact results within an interactive budget. | -| 3 | [One-command product funnel](./2026-07-27-one-command-product-funnel.md) | S1 | Very high | A new user can install, understand, configure, and prove value without registry knowledge or command archaeology. | - -These programs cover performance, stability, correctness, usability, and marketability with shared infrastructure. They also absorb most unfinished roadmap work that still has a strong user outcome. - -## Severity and ROI rubric - -- **S0 - release blocker:** known security exposure, unverified shipped binaries, or a defect that can invalidate trust in a release. -- **S1 - material product problem:** common workflows are slow, fail, mislead, or impose enough friction to lose users. -- **S2 - maintainability drag:** raises future defect cost but does not currently block the primary workflow. -- **Very high ROI:** one shared change removes several high-value problems or creates reusable evidence. -- **High ROI:** a focused change materially improves one core workflow. -- **Low ROI:** measurable work with little effect on adoption, answer quality, or operational risk. - -## What should be preserved - -The project already has a stronger technical core than its adoption signals suggest. - -- `inspect ./src` found 342 TypeScript files, zero unresolved project imports, and zero dependency cycles. -- Startup work succeeded: warm medians were 45 ms for `--version`, 47 ms for `--help`, 59 ms for `doctor`, and 506 ms for `orient --budget small --json`. -- Current JavaScript coverage is 90.66% of lines, 94.35% of functions, and 78.66% of branches. Native coverage documentation reports 96.49% of Rust lines. -- The README states limitations instead of presenting Tree-sitter heuristics as compiler truth (`README.md:286-297`). -- Agent responses expose provenance, limits, omission counts, and copyable follow-ups. That is a credible differentiator worth keeping. -- The codebase has no dependency cycle that justifies a broad architecture rewrite. High fan-in type and path modules are expected foundations, not evidence of a defect. - -## Evidence summary - -| Observation | Direct evidence | Consequence | -| ------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -| Production dependencies have one high, two moderate, and one low advisory. | `npm audit --omit=dev --json` on 2026-07-27. | Release trust is currently below the standard implied by a local security-sensitive MCP tool. | -| Primary CI runs only on Ubuntu and Node 22.16. | `.github/workflows/on-demand-ci.yml:10-38` | Windows and macOS behavior can regress between releases. | -| Release jobs build eight native targets but do not load or parse with the resulting artifacts. | `.github/workflows/release.yml:51-136` | A successful build can still publish an unloadable or incorrectly staged binary. | -| Warm hybrid CLI search took 2.46-2.98 seconds on this repository. | Five-run and three-run local samples. | The default search path remains outside an interactive agent budget. | -| A first MCP hybrid search took 20.5 seconds; repeated calls took 1.14-1.18 seconds. | Three calls through the live MCP device. | Cold or invalidated sessions feel stalled, and even warm queries remain expensive. | -| Three MCP search results serialized about 26 KiB. | Live MCP search response. | The tool spends agent context on repeated evidence, neighbors, and follow-ups. | -| Hybrid search scans every snapshot file sequentially for text. | `src/agent/search.ts:597-640` | Query cost is proportional to repository size even when few files can match. | -| Search caches are keyed only by an in-memory snapshot object. | `src/agent/search.ts:683-725` | Fresh CLI processes cannot reuse normalized text or chunks. | -| Running `codegraph` with no arguments emitted a 93,635-byte Mermaid graph. | Direct CLI smoke. `src/cli.ts:141-143` defaults to `graph`. | The first-run experience performs expensive work and hides the intended `orient`/`explore` entry points. | -| A mistyped command returned only `Unknown command: serach`. | Direct CLI smoke. | Users get no correction or next action. | -| Top-level help exposes 42 commands and 46 usage lines. | Parsed `--help` output. | Powerful primitives dominate the front door instead of the five workflows most users need. | -| Published installation requires Node 22.16 and either GitHub Packages configuration or a root tarball without native semantics. | `README.md:51-82`, `docs/installation.md:40-72` | The easiest install path is not the full product. | -| Checked documentation benchmarks explicitly do not measure answer quality, relevance, correctness, tokens, or general speed. | `docs/benchmarks/README.md:5-11`, `:73-80` | The project cannot yet turn its strongest design claims into comparative proof. | -| Source samples cover many languages but remain small. | 303 source fixture files totaling 49,664 bytes; language samples total 5,499 bytes. | Unit breadth is strong, but ecosystem-scale resolution behavior is not certified. | -| Roadmap status is stale and fragmented. | 31 plan files; many have no status, and the performance index still says `Planned` while recording shipped priorities. | Maintainers and users cannot distinguish shipped, superseded, and next work reliably. | -| The only open PR is a dirty draft based on an old feature branch. | PR [#146](https://github.com/lzehrung/codegraph/pull/146) | The public backlog does not represent an executable current roadmap. | - -## 1. Release and semantic certification matrix - -Implementation plan: [Release and semantic certification program](./2026-07-27-release-semantic-certification-program.md) - -**Severity: S0** -**ROI: Very high** -**Dimensions: stability, correctness, security, marketability** - -### Problem - -The current release process proves that code builds on Ubuntu and that native artifacts can be produced. It does not prove that every published artifact loads on its host, that the installed package selects it correctly, or that the advertised semantic operations remain accurate on representative repositories. - -The production dependency audit reported: - -- `fast-uri`: high-severity host-confusion advisories [GHSA-v2hh-gcrm-f6hx](https://github.com/advisories/GHSA-v2hh-gcrm-f6hx) and [GHSA-4c8g-83qw-93j6](https://github.com/advisories/GHSA-4c8g-83qw-93j6). -- `@modelcontextprotocol/sdk` through `@hono/node-server`: moderate Windows encoded-backslash traversal advisory [GHSA-frvp-7c67-39w9](https://github.com/advisories/GHSA-frvp-7c67-39w9). -- `body-parser`: low-severity limit-enforcement denial of service advisory [GHSA-v422-hmwv-36x6](https://github.com/advisories/GHSA-v422-hmwv-36x6). - -Actual exploitability through Codegraph's custom Node HTTP path is not established. The safe action is to update, verify reachability, and gate future releases rather than infer that transitive code is unreachable. - -### One cross-cutting change - -Create one manifest-driven certification runner used by pull requests, release jobs, scheduled quality runs, and the public benchmark report. - -1. **Dependency policy** - - Update `@modelcontextprotocol/sdk` and the lockfile to patched transitive versions. - - Fail release qualification on unreviewed production advisories. - - Allow exceptions only with package, advisory, reachability evidence, owner, and expiry recorded in one machine-readable allowlist. - -2. **Install and runtime matrix** - - Pack the exact root, native meta, and target packages that will be published. - - Install them into clean temporary projects on Windows, Linux glibc, Linux musl, and macOS runners where executable hosts exist. - - Require `doctor`, `--version`, `orient`, one native parse, and one MCP request to succeed from the packed artifacts. - - Verify that `doctor` reports the expected package version, native target, loaded path, and reduced-mode reason where native is intentionally unavailable. - - Make publishing depend on these tests, not only on artifact creation. - -3. **Semantic corpus** - - Define pinned repository revisions and task manifests for the most important ecosystems: TypeScript, Python, Go, Rust, Java/Kotlin, C/C++, C#, PHP, SQL, and mixed monorepos. - - Keep ordinary PR tests network-free. Run the pinned external corpus in scheduled and release qualification jobs, with a small committed regression subset for every discovered defect. - - Compare go-to-definition, references, hierarchy, call edges, imports, and candidate tests against compiler/LSP results where an authoritative provider exists. - - Track precision, recall, unsupported cases, parse degradation, fallback use, latency, peak memory, and output bytes by language and operation. - -4. **Hermetic tests** - - Give every test a private project copy and cache root. - - Prohibit writes to shared source fixtures. - - Add a post-suite assertion that fixture trees and tracked files did not change. - -During the preceding full check, `workspace-detection.test.ts` transiently failed while copying a `.codegraph-cache` temporary manifest from a shared sample tree. The isolated rerun and a clean full rerun passed, which makes fixture isolation the correct fix rather than a retry policy. - -### Acceptance gate - -- `npm audit --omit=dev` has no unreviewed advisory. -- Every executable release target installs from packed artifacts, loads the intended native binary, and completes the same smoke scenario. -- No release artifact is published before its target smoke passes. -- The corpus publishes versioned quality and latency results with exact repository revisions. -- Every supported semantic capability has a stated measured denominator, not only a representative happy-path fixture. -- Repeated full-suite runs leave shared fixtures unchanged and do not depend on test ordering. - -### Why this is one program - -The same matrix produces release confidence, semantic regression protection, and credible public proof. Separate security, cross-platform, accuracy, and benchmark projects would duplicate package staging, fixture provisioning, result schemas, and reporting. - -## 2. Persistent query substrate - -Implementation plan: [Persistent query substrate](./2026-07-27-persistent-query-substrate.md) - -**Severity: S1** -**ROI: Very high** -**Dimensions: performance, stability, correctness, usability** - -### Problem - -Startup is no longer the dominant cost. The query layer still rebuilds searchable state and scores repository-wide candidates on each fresh process. - -Measured on the reviewed repository: - -| Operation | Result | -| ----------------------------------------------- | -----------: | -| `--version` warm median | 45 ms | -| `doctor` warm median | 59 ms | -| `orient --budget small --json` warm median | 506 ms | -| CLI hybrid search, `--limit 3` | 2.46-2.98 s | -| CLI symbol search | 1.03 s | -| CLI path search | 428 ms | -| CLI text search | 2.26 s | -| First live MCP hybrid search after invalidation | 20.5 s | -| Repeated live MCP hybrid search | 1.14-1.18 s | -| MCP payload for three results | about 26 KiB | - -`addTextResults` awaits normalized text for every file, one file at a time (`src/agent/search.ts:597-640`). The `WeakMap` cache helps repeated queries against the same in-memory snapshot, but it cannot help a new CLI process and it still leaves repository-wide matching work on every query (`src/agent/search.ts:683-725`). - -### One cross-cutting change - -Add one versioned, immutable query sidecar keyed by the existing project snapshot identity. - -1. **Persist query-ready data once** - - Store normalized file text, semantic chunks, token postings, path tokens, symbol lookup keys, and file/symbol adjacency. - - Use a dedicated `search-v1` sidecar so search evolution does not inflate or destabilize the core project snapshot. - - Write atomically and record the exact runtime, discovery, include-root, graph-option, and source identities that already govern cache validity. - -2. **Update incrementally** - - Rebuild rows only for added, changed, retired, or explicitly supplied transient files. - - Treat deletions as invalidation events before removing their old dependency evidence. - - Keep `--root`, include roots, CLI globs, config globs, native identity, and reduced-mode identity in the cache key. - -3. **Retrieve candidates before scoring** - - Use postings to select a bounded candidate set. - - Preserve the current deterministic ranking and provenance logic over that set. - - Do not delegate ranking to an opaque database relevance score. - -4. **Share it across surfaces** - - CLI, library sessions, MCP, `search`, `explore`, packets, and source-snippet retrieval must consume the same query snapshot. - - Cache file and symbol neighbor indexes once per query snapshot instead of rebuilding them per request. - -5. **Make response detail explicit** - - Add compact, standard, and full detail levels to search/explore response construction. - - Keep handles, provenance, limits, and omission counts in compact mode. - - Avoid repeating neighbors, follow-ups, and snippets when the caller requested only a few ranked anchors. - -6. **Profile remaining cache work after this lands** - - Finish native fingerprint avoidance, worker reuse, and cache pruning only where the new profile still shows material cost. - - Retire or update the July 25 performance plans instead of maintaining parallel descriptions of the same budget. - -### Acceptance gate - -Use the existing performance-program hardware and command definitions, then record p50 and p95: - -- Warm CLI hybrid search on this repository is at or below the existing 700 ms target. -- Warm MCP search is below 200 ms for a three-result query. -- A valid persisted snapshot does not trigger a full repository text read. -- Cold or invalidated MCP work reports progress before the client timeout and completes without a 30-second request failure. -- Compact three-result search output is below 8 KiB while retaining handles, provenance, limits, and omissions. -- Golden relevance results do not regress on the certification corpus. -- Corrupt, stale, partial, or incompatible sidecars fail closed to a rebuild with a diagnostic, never to stale answers. - -### Why this is one program - -A daemon, another startup pass, and isolated micro-caches would attack symptoms. One query snapshot removes repeated I/O, repeated normalization, repeated graph-index construction, response bloat, and CLI/MCP behavioral drift together. - -## 3. One-command product funnel - -Implementation plan: [One-command product funnel](./2026-07-27-one-command-product-funnel.md) - -**Severity: S1** -**ROI: Very high** -**Dimensions: usability, marketability, supportability** - -### Problem - -The product's strongest workflow is hidden behind installation and command-discovery friction. - -- Bare `codegraph` builds and prints a full graph instead of explaining the product. -- Help exposes 42 commands even though README onboarding centers on `explore`, `orient`, `review`, `impact`, and `install`. -- Unknown commands provide no suggestion. -- The full native product requires GitHub Packages configuration. The registry-free release tarball installs only the reduced semantic mode unless the registry is configured separately. -- Node 22.16 is a hard prerequisite. -- The public repository has 2 stars and no open issues. Stars are not a quality metric, but they are a direct discoverability signal. - -The category is crowded and sets a much lower-friction expectation: - -- [oraios/serena](https://github.com/oraios/serena): 27,019 stars; positions itself as semantic retrieval and editing for agents. -- [DeusData/codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp): 35,853 stars; leads with a persistent graph, a single static binary, and zero dependencies. -- [abhigyanpatwari/GitNexus](https://github.com/abhigyanpatwari/GitNexus): 44,691 stars; leads with a browser-based interactive graph and immediate visual demo. - -[INFERENCE] Codegraph should not compete on the largest language count, a generic graph screenshot, or compiler-grade editing. Its defensible story is deterministic local evidence for agents: bounded source, explicit omissions, conservative semantics, and diff-aware impact/review. - -### One cross-cutting change - -Design one acquisition-to-proof path and make every public surface reinforce it. - -1. **Frictionless distribution** - - Ship signed per-platform GitHub Release archives containing the CLI runtime and matching native addon. - - Keep the package/library channel for TypeScript consumers, but do not require registry configuration for the primary CLI trial. - - Add platform-native install wrappers only after the same standalone artifact passes the certification matrix. - - Implement a real `upgrade` only when the channel can update itself safely: confirmation unless `--yes`, streamed subprocess output, exit propagation, resulting-version verification, dirty/detached source-tree refusal, permission handling, and Windows mapped-runtime safety. - -2. **Opinionated first run** - - Bare `codegraph` prints a concise explanation plus `doctor`, `orient`, `explore`, `review`, and `install` next actions. It must not scan the repository. - - Unknown commands suggest the nearest valid command and show its short usage. - - `codegraph install --detect --yes` becomes the single agent-configuration path; keep dry-run and ownership-safe uninstall. - - Group low-level commands under an advanced help section without removing scriptable primitives. - -3. **One product claim with proof** - - Lead with: "Give an agent bounded, local evidence for where code lives and what a change can break." - - Publish certification-corpus results for answer quality, latency, output bytes, and unsupported cases. - - Add one short end-to-end demonstration: install, ask an architecture question, inspect cited source, review a diff, and select tests. - - Compare workflows, not raw feature counts. Show where Codegraph is more conservative and where a compiler/LSP remains authoritative. - -4. **One live roadmap** - - Replace status inference across 31 plan files with one index containing `shipped`, `next`, `blocked`, and `superseded` states. - - Keep historical plans, but require each to point back to the current index and name the release or PR that changed its state. - - Resolve PR #146 explicitly against current `main`: re-scope/reimplement the user outcome or mark it superseded. Do not preserve its 38-file stale stack merely because work already exists. - - Stop adding commands until the front door and certification data show a missing workflow rather than a missing primitive. - -### Acceptance gate - -- A fresh supported machine reaches native `doctor: available` and a useful `orient` result from one documented install command. -- Bare `codegraph` completes without repository discovery and emits less than 3 KiB. -- A one-edit typo such as `serach` suggests `search`. -- Top-level help presents no more than five primary workflows before advanced commands. -- The README trial path is install, verify, ask; registry and native-package internals move to installation reference material. -- Published benchmark pages include quality, latency, output-size, corpus revision, and known-limit data generated by the certification runner. -- Every active roadmap item has one status and one owning issue or PR. - -### Why this is one program - -Distribution, first-run behavior, setup, proof, and roadmap focus are one funnel. Improving only README wording cannot overcome an authenticated install, a 93 KiB no-argument response, or absent quality evidence. - -## Severity and ROI-ranked findings within the programs - -| Order | Finding | Severity | ROI if addressed through the program | -| ----: | ---------------------------------------------------------------------------- | -------- | ------------------------------------ | -| 1 | Unreviewed production dependency advisories | S0 | Very high | -| 2 | Native release artifacts are built but not executed before publish | S0 | Very high | -| 3 | CI lacks Windows and macOS product-path coverage | S0 | Very high | -| 4 | Hybrid search remains 2.46-2.98 seconds and scales with repository files | S1 | Very high | -| 5 | Cold MCP query work can exceed the 30-second request budget | S1 | Very high | -| 6 | Full MCP search responses spend about 26 KiB on three results | S1 | High | -| 7 | Full native install requires registry-specific knowledge | S1 | Very high | -| 8 | Bare CLI invocation emits a repository graph | S1 | Very high | -| 9 | Public benchmarks do not measure semantic quality or token/output efficiency | S1 | Very high | -| 10 | Broad language claims rely mostly on small fixtures | S1 | Very high | -| 11 | Forty-two CLI commands dilute the primary workflows | S2 | High | -| 12 | Stale plan state and one dirty draft PR obscure priorities | S2 | High | -| 13 | Root compatibility surface and internal exports are broad | S2 | Low until adoption grows | - -## Recommended sequence - -1. Land the production dependency updates and release-audit gate first. -2. Add packed-package smoke tests to existing target runners before changing distribution. -3. Define the certification result schema and a small multi-language corpus. -4. Build the persistent query sidecar against that corpus so speed work cannot silently trade away relevance or freshness. -5. Switch CLI/MCP search and explore to the shared query snapshot and compact response levels. -6. Change the no-argument/unknown-command front door. -7. Publish standalone artifacts only after target certification passes. -8. Publish the quality/latency scorecard and rewrite positioning from those results. -9. Reconcile roadmap status and decide PR #146 against the now-measured priorities. - -## Explicitly defer - -These are lower ROI than the three programs above: - -- More language count before existing semantic claims have corpus-level precision and recall. -- A broad internal module refactor; current source inspection reports zero dependency cycles. -- More startup-only work; lightweight commands are already 45-59 ms warm. -- A new graph UI before install and first-query friction are solved. -- More CLI commands, including `affected`, until the existing impact/review workflow is measured against real user tasks. -- A breaking cleanup of the root TypeScript export surface. Document and freeze it first; narrow only with real consumer evidence. -- Telemetry by default. Reproducible local diagnostics and opt-in benchmark artifacts fit the privacy position better. - -## Method and limitations - -The review used current source, documentation, GitHub state, runtime experiments, coverage, dependency audit, and Codegraph's own structural reports. - -Commands and experiments included: - -- `doctor`, `orient`, and `inspect ./src` -- repeated CLI timing for version, help, doctor, orient, inspect, and each search mode -- live MCP cold and warm search timing -- CLI error-path and no-argument output sampling -- `npm run test:coverage` -- `npm audit --omit=dev --json`, full `npm audit --json`, and `npm outdated --json` -- `npm pack --dry-run --json` -- fixture, command, plan-status, CI, release, and public API inventories -- current GitHub repository, PR, issue, and competitor repository metadata - -Limits: - -- Performance numbers are local Windows samples, not universal claims. -- The review did not run a controlled agent A/B study; that is a deliverable of the certification matrix. -- Competitor stars and repository descriptions measure visibility and positioning, not technical correctness. -- Codegraph's self-inspection was paired with source reads and runtime evidence, but it is not an independent oracle for Codegraph's semantic accuracy. - -## Bottom line - -The project does not need more breadth first. It needs release-grade trust, a genuinely persistent query path, and a first-run story that exposes the value already present. - -Those three programs turn the existing engineering strengths into a product that is faster to trust, faster to try, and easier to differentiate. diff --git a/docs/plans/2026-07-27-release-semantic-certification-program.md b/docs/plans/2026-07-27-release-semantic-certification-program.md deleted file mode 100644 index da1a08b8..00000000 --- a/docs/plans/2026-07-27-release-semantic-certification-program.md +++ /dev/null @@ -1,536 +0,0 @@ -# Release and semantic certification program - -Status: Implemented - -Parent review: [Project improvement review](./2026-07-27-project-improvement-review.md) - -## Vision - -A Codegraph release should be a proved artifact, not a successful publish command. Every shipped package must be secure, installable, loadable on its claimed target, and backed by measured semantic behavior on a pinned multilingual corpus. - -This program combines release qualification, native-package smoke testing, semantic accuracy measurement, fixture hermeticity, and public benchmark publication. It creates one evidence chain from a source revision to the exact package tarballs and the claims made about them. - -## Why this is one program - -The current repository already has strong pieces: - -- `.github/workflows/release.yml` builds every native target and refuses incomplete target artifacts. -- `scripts/check-native-artifacts.mjs` validates package structure before publication. -- `tests/native-semantic-parity.test.ts` checks selected native semantic behavior. -- `docs/benchmarks/` has versioned scenario and result contracts. -- `npm run check` covers formatting, lint, build, and the permanent test suite. - -The missing property is composition. Structural artifact checks do not prove that packed packages install and execute, fixture tests do not quantify semantic accuracy, the public benchmark measures evidence retrieval rather than correctness, and the production dependency audit currently reports four known advisories. - -A single certification envelope makes those distinctions explicit instead of allowing a green release job to imply more than it proved. - -## Current measured baseline - -Snapshot: `main` at `44de8b47` (`@lzehrung/codegraph` 1.8.103), Windows 11, Node.js 24.15.0, 2026-07-27. - -- Repository check: 214 test files passed, 2,443 tests passed, 2 skipped. -- TypeScript coverage summary: 90.66% lines, 94.35% functions, 78.66% branches. -- Native coverage summary: 84.19% lines, 65.67% functions. -- Production audit: 4 advisories: 1 low, 2 moderate, 1 high. -- Direct affected dependency: `@modelcontextprotocol/sdk` 1.29.0 through `@hono/node-server`. -- Transitive affected dependencies: `body-parser`, `fast-uri`, and `@hono/node-server`. -- `npm outdated --json` reports a current `@modelcontextprotocol/sdk` release beyond the installed range. -- Release validation proves that target package files exist, but it does not install and execute the exact packed package set on each target host. -- `tests/samples/monorepo/.codegraph-cache/` can be left behind by failed or interrupted tests, which proves shared source fixtures are not fully hermetic. - -Re-measure these values before implementation. They are planning baselines, not permanent thresholds. - -## Outcomes - -After this program: - -1. Production dependency advisories are either zero or represented by an explicit, expiring, reviewed exception. -2. Release jobs build the exact tarballs once, certify those bytes, and publish those same bytes. -3. Every claimed executable native target has a host-runtime smoke result. A target without a real host is labeled unexecuted and cannot silently count as certified. -4. Definitions, references, graph edges, and candidate-test selection have published precision, recall, support, and latency results on pinned repositories. -5. Test suites cannot mutate checked-in fixtures or leave durable cache state under source fixtures. -6. Public claims link to a machine-readable result containing revision, package versions, target matrix, corpus revision, environment, and failure details. - -## Non-goals - -- Do not claim that a finite corpus proves universal correctness. -- Do not compare Codegraph to competitors in the first implementation. -- Do not make scheduled external-repository network availability a release blocker. -- Do not add telemetry to end-user commands. -- Do not replace the existing unit and integration suites with corpus tests. -- Do not publish a target as runtime-certified when CI only inspected its archive. -- Do not weaken native artifact checks or reduced-mode fallback behavior. - -## Certification model - -Use one versioned result envelope with independent sections. A release is qualified only when every required section has `status: "pass"`. - -```ts -type CertificationReportV1 = { - schemaVersion: 1; - generatedAt: string; - source: { - repository: string; - revision: string; - dirty: false; - }; - versions: { - root: string; - native: string; - node: string; - rust?: string; - }; - security: SecurityCertification; - packages: PackageCertification[]; - semantics: SemanticCertification; - hermeticity: HermeticityCertification; - summary: { - status: "pass" | "fail" | "incomplete"; - failures: CertificationFailure[]; - }; -}; -``` - -Every failure needs a stable code, a human message, and enough structured context to reproduce it. Do not represent an unexecuted check as a pass. - -## Workstream A: production dependency security - -### A1. Update and verify the MCP dependency chain - -Start with the smallest supported update that clears the current advisories: - -1. Update `@modelcontextprotocol/sdk` and regenerate `package-lock.json` with `npm install`. -2. Run the MCP stdio and Streamable HTTP suites, installer tests, package metadata tests, and `npm run check`. -3. Run `npm audit --omit=dev --json` against the resulting lockfile. -4. Confirm that HTTP host validation, loopback binding, request-size limits, and JSON body parsing behavior are unchanged. -5. If a major SDK update is required, use current SDK documentation and split API migration from the audit gate. - -Do not use `npm audit fix --force`. The dependency update must be deliberate and reviewable. - -### A2. Add a deterministic production-audit gate - -Add: - -- `scripts/check-production-audit.mjs` -- `scripts/security/production-audit-allowlist.json` -- unit tests for parser, severity policy, malformed audit output, and expired exceptions -- `npm run security:production` - -The script runs `npm audit --omit=dev --json`, normalizes npm's nonzero exit behavior, and fails when any advisory is not covered by an active exception. The checked-in exception schema is: - -```json -{ - "schemaVersion": 1, - "exceptions": [ - { - "advisory": "GHSA-...", - "package": "package-name", - "reason": "Why exposure is not reachable", - "owner": "GitHub handle", - "expires": "YYYY-MM-DD", - "trackingIssue": "https://github.com/..." - } - ] -} -``` - -Exceptions must match advisory and package, must not be expired, and must never use wildcard IDs. The report records both accepted exceptions and rejected vulnerabilities so a green gate is still auditable. - -Run this gate in on-demand CI and before release packaging. Keep development-only audit output separate; production release qualification must not be obscured by unrelated tooling advisories. - -### A3. Security acceptance - -- `npm audit --omit=dev --json` reports zero unexcepted advisories. -- No exception lacks owner, reason, issue, or expiry. -- A fixture containing an expired exception fails the script. -- A malformed npm audit response fails closed. -- MCP stdio and HTTP smoke tests pass against the updated dependency graph. - -## Workstream B: certify exact package bytes - -### B1. Build release candidates once - -Refactor the release workflow into this DAG: - -```text -plan release - -> build native target directories - -> assemble exact package versions - -> npm pack every target, native meta-package, and root package - -> upload immutable release-candidate artifact - -> certify package matrix and semantics - -> publish the already-certified tarballs - -> create GitHub release from the same tarballs and report -``` - -The assembly job owns all temporary version edits. Downstream jobs consume tarballs and checksums, never rerun `npm pack` from independently modified directories. - -Add a release-candidate manifest: - -```ts -type ReleaseCandidateManifestV1 = { - schemaVersion: 1; - sourceRevision: string; - rootVersion: string; - nativeVersion: string; - files: Array<{ - package: string; - target?: string; - file: string; - sha256: string; - size: number; - }>; -}; -``` - -Validate every downloaded file against this manifest before smoke testing or publishing. - -### B2. Package smoke runner - -Add reusable implementation under `scripts/certification/`: - -- `package-contract-lib.mjs`: manifest validation, package selection, and result types -- `package-smoke-lib.mjs`: temporary installation and subprocess helpers -- `run-package-smoke.mjs`: CLI entry -- focused tests using tiny generated tarballs and mocked subprocess results - -Each target smoke creates a fresh temporary directory outside the checkout and installs, in one npm invocation: - -1. the target-specific native tarball, -2. the `@lzehrung/codegraph-native` meta-package tarball, -3. the root `@lzehrung/codegraph` tarball. - -The explicit local tarballs must satisfy the scoped package names. Public production dependencies may come from npm, but the tested Codegraph packages must not come from a registry. - -The runner verifies: - -- installed package names and versions match the candidate manifest, -- installed Codegraph package files hash to the packed tarball contents, -- `import("@lzehrung/codegraph")` succeeds, -- `import("@lzehrung/codegraph-native")` selects the expected target package, -- `codegraph version` reports the candidate root version, -- `codegraph doctor` reports the expected native availability and target, -- `codegraph index` or `search` with `--native on` parses a tiny language fixture and returns a known symbol, -- one stdio MCP initialize/list-tools/search exchange succeeds from the packed binary, -- reduced mode still starts when the native package is intentionally omitted in a separate root-package smoke. - -Capture stdout, stderr, exit code, duration, selected native path, and package identities. Bound captured output and redact tokens and registry credentials. - -### B3. Runtime target matrix - -Classify every native target as one of: - -- `runtime`: executed on a matching OS and architecture, -- `emulated`: executed under an explicitly named emulator with limitations, -- `structural`: archive/package validated but not loaded, -- `unsupported`: intentionally not published. - -Initial runtime jobs should use matching GitHub-hosted or repository-managed runners for macOS x64/arm64, Linux glibc x64/arm64, Linux musl x64/arm64, and Windows x64. Linux musl jobs run inside matching Alpine containers only when architecture matches. - -Windows arm64 currently lacks a matching GitHub-hosted runner in this workflow. Choose one before calling it certified: - -1. add a maintained Windows arm64 self-hosted runner, -2. prove the addon under a suitable emulator and label the result `emulated`, or -3. keep publication behind a reviewed `structural-only` exception with owner and expiry. - -The workflow must not map cross-compilation success to runtime success. - -### B4. Release workflow enforcement - -The publish job requires: - -- security report pass, -- all required package smoke rows pass, -- candidate manifest checksum pass, -- source revision still equals the planned revision, -- no missing target artifacts, -- package versions match the release plan, -- semantic release-gate suite pass. - -Use GitHub artifact IDs and SHA-256 checksums in the final report. Publishing must stop before the first registry write if any required row is incomplete. - -### B5. Package acceptance - -- A deliberately wrong native target fails with a stable target-mismatch code. -- A modified tarball fails before installation. -- A package that imports but cannot parse fails runtime certification. -- The MCP smoke uses the packed bin, not `src/` or the checkout's `dist/`. -- The GitHub release attaches the exact checksums that passed certification. - -## Workstream C: semantic quality corpus - -### C1. Two-tier corpus - -Use two tiers with the same schema: - -1. `release`: small checked-in fixtures, deterministic, no network, blocks every release. -2. `representative`: pinned public repositories, scheduled and manually runnable, publishes quality trends after stabilization. - -External repository availability must not block a release. The scheduled job clones immutable revisions into a cache, verifies the resolved commit, and records clone provenance. A later PR may promote a mirrored, license-compatible snapshot into the release tier after repository size and licensing review. - -### C2. Corpus manifest - -Add `docs/benchmarks/semantic-corpus.json` with this contract: - -```ts -type SemanticCorpusV1 = { - schemaVersion: 1; - corpusRevision: string; - repositories: Array<{ - id: string; - url: string; - revision: string; - license: string; - includeRoots?: string[]; - config?: string; - }>; - cases: Array<{ - id: string; - tier: "release" | "representative"; - repository: string; - language: string; - operation: "definition" | "references" | "dependency" | "candidate-tests"; - request: Record; - expected: { - required: SemanticLocation[]; - allowed?: SemanticLocation[]; - forbidden?: SemanticLocation[]; - unsupported?: string; - }; - rationale: string; - }>; -}; -``` - -The manifest is data only. Do not allow arbitrary shell commands, environment expansion, absolute paths, or repository-relative traversal. - -Select repositories using explicit criteria: - -- permissive license and stable public history, -- realistic package/module structure, -- at least two languages from Codegraph's claimed native set, -- manageable checkout and index size, -- constructs that exercise imports, re-exports, inheritance, generated declarations, tests, and framework conventions, -- no secrets or large binary assets required for the selected include roots. - -Start with 3-5 repositories and 15-25 reviewed cases per operation. Expand only when a new case covers a known semantic class rather than inflating counts. - -### C3. Goldens and oracle process - -Goldens are reviewed repository facts, not snapshots of Codegraph output. - -For each case: - -1. derive candidates with the language's compiler, LSP, or repository search, -2. review the source at the pinned revision, -3. record required, allowed, and forbidden locations, -4. explain ambiguity in `rationale`, -5. require a second reviewer for golden changes. - -Store file paths and ranges relative to the pinned repository root. Use symbol handles only as requests, never as the golden identity, because handles are implementation output. - -### C4. Scoring - -Use exact, documented denominators: - -- A required location returned is a true positive. -- A required location omitted is a false negative. -- A returned location listed as forbidden is a false positive. -- Allowed locations are neutral. -- Unexpected returned locations require triage; once classified, update the golden in the same review. -- Unsupported cases count against support rate and are excluded from precision/recall only when the public table displays the support denominator beside those metrics. - -Report per operation, language, repository, runtime mode, and total: - -```text -support = supported cases / all cases -precision = true positives / (true positives + false positives) -recall = true positives / (true positives + false negatives) -F1 = harmonic mean of precision and recall -latency = p50, p95, and max per operation -``` - -For dependency edges, compare normalized project-relative `(from, to, kind)` tuples. For candidate tests, score required and forbidden test files and report mean reciprocal rank for the first required test. - -Never collapse reduced and native modes into one quality number. - -### C5. Runner and result files - -Add: - -- `scripts/benchmarks/run-semantic-corpus.mjs` -- `scripts/benchmarks/semantic-corpus-lib.mjs` -- `scripts/benchmarks/summarize-semantic-corpus.mjs` -- tests for schema validation, path confinement, scoring, unsupported cases, duplicate results, and deterministic ordering -- `docs/benchmarks/semantic-results.example.json` - -The runner invokes published/library contracts where practical: - -- `goto`/definition API for definitions, -- `refs`/references API for references, -- normalized project graph for dependencies, -- impact/review candidate tests for test selection. - -Run release-tier cases against the exact packed root and native packages. Representative scheduled runs may use the built checkout, but their report must identify that package mode distinctly. - -### C6. Initial quality gates - -Do not invent thresholds before the first reviewed baseline. Land in this order: - -1. schema and runner with informational output, -2. manually review all goldens, -3. publish three consecutive stable runs, -4. set release gates to prevent regression from the accepted baseline, -5. set absolute minimums only after known unsupported classes are documented. - -The first blocking policy should be: - -- release-tier support, precision, and recall cannot decrease, -- no previously passing case may regress without an approved golden or limitation update, -- runtime mode cannot silently change from native to reduced, -- p95 latency may not regress more than 20% without an attached benchmark explanation. - -## Workstream D: fixture hermeticity - -### D1. Isolate mutable state - -No test may run a cache-writing command directly against a checked-in fixture directory. Introduce one helper that: - -1. creates a unique temporary directory, -2. copies the required fixture subset, -3. runs the test against the copy, -4. removes it in `finally`, -5. reports the retained temporary path only when an explicit debug environment variable is set. - -Migrate workspace, lifecycle, cache, benchmark, and installer tests that can write `.codegraph`, `.codegraph-cache`, package locks, config files, or generated artifacts. - -Tests that only read immutable samples may continue reading them in place. Make that distinction explicit in helper names. - -### D2. Add a post-suite cleanliness gate - -Add `scripts/check-fixture-cleanliness.mjs` and run it after tests in CI and `npm run check`. It fails on: - -- `.codegraph/` or `.codegraph-cache/` below checked-in fixture roots, -- generated package locks not committed as fixture input, -- benchmark outputs outside approved result paths, -- modified tracked fixture files, -- known temporary naming patterns. - -The script must be cross-platform and use Node APIs, not shell-specific `find` or `git clean`. CI may additionally use `git diff --exit-code` as defense in depth. - -### D3. Hermeticity acceptance - -- A test that intentionally writes a cache into `tests/samples/` is caught. -- An interrupted test leaves state only under the OS temporary root. -- Two concurrent test workers receive different fixture copies. -- Running the full suite twice leaves an identical checkout. -- The certification report records the cleanliness result. - -## Public report and documentation - -Extend `docs/benchmarks/README.md` with a semantic certification section that states exactly what is and is not measured. Publish a machine-readable report as a workflow artifact first; commit curated results only through reviewed pull requests. - -Update these canonical surfaces when implementation changes behavior: - -- `README.md`: concise trust claim and links only -- `docs/installation.md`: certified package/target meaning -- `docs/language-parity.md`: measured support and intentional limitations -- `docs/scenario-catalog.md`: fixture and corpus coverage -- `docs/benchmarks/README.md`: methodology and result schema -- `PUBLISHING.md`: certification gate and release-candidate byte flow -- `codegraph-skill/codegraph/SKILL.md`: only if CLI commands or flags change - -Do not publish a single "accuracy" percentage without operation, runtime mode, support denominator, corpus revision, and date. - -## Implementation sequence and pull requests - -### PR 1: security and hermetic baseline - -- update the MCP SDK dependency chain, -- add the production audit gate, -- add fixture-copy helper and cleanliness gate, -- migrate known mutating tests, -- keep release behavior otherwise unchanged. - -Exit: zero unexcepted production advisories and two clean consecutive full-suite runs. - -### PR 2: immutable release candidates - -- assemble and checksum exact tarballs once, -- add package smoke runner, -- run existing host-executable target rows, -- publish only certified tarballs. - -Exit: Windows x64, Linux glibc/musl, and macOS rows execute packed CLI, native parse, and MCP smoke; unexecuted targets are explicit. - -### PR 3: semantic corpus contract - -- add manifest, validators, runner, and release fixture tier, -- add informational report to CI, -- document scoring. - -Exit: deterministic report on repeated runs and manually reviewed goldens. - -### PR 4: representative corpus and gates - -- add pinned public repositories, -- run scheduled matrix, -- publish first baseline, -- add non-regression thresholds after three stable runs. - -Exit: public report can be regenerated from documented commands and pinned inputs. - -## Required tests - -- production audit JSON parsing and exception expiry -- release candidate checksum and package identity validation -- target mismatch and native load failure -- packed CLI version, doctor, parse, and MCP round trip -- corpus schema rejection for traversal and arbitrary commands -- deterministic semantic scoring and ordering -- duplicate/allowed/forbidden location handling -- native versus reduced result separation -- fixture copy isolation and concurrent cleanup -- release workflow contract tests for required jobs and artifact handoff - -Use the narrowest relevant suites during implementation, then run `npm run check` before each PR concludes. For workflow-only changes, also execute the reusable scripts locally with synthetic artifacts and validate the workflow YAML paths. - -## Risks and mitigations - -### External repository drift - -Pinned commits can disappear or repositories can become unavailable. Record resolved commits, cache clones, and keep external runs scheduled rather than release-blocking. - -### Golden bias - -Codegraph-generated output can accidentally become the oracle. Require source rationale and second review for golden changes. - -### Cross-target blind spots - -Cross-compilation looks green without executing. Preserve `runtime`, `emulated`, and `structural` as separate states and block unsupported claims. - -### Release duration - -Native builds already dominate the workflow. Run package smokes in parallel after one assembly job and keep the release semantic tier intentionally small. - -### Security exception permanence - -Allowlist entries tend to live forever. Require expiry and fail closed on expired rows. - -### Flaky latency gates - -Hosted runners vary. Use repeated samples, compare to a same-job baseline where possible, and gate large regressions rather than millisecond noise. - -## Definition of done - -- [x] No unexcepted production advisory remains. -- [x] Exact candidate tarballs are assembled once and checksummed. -- [x] Published bytes equal certified bytes. -- [x] Every published native target has an explicit runtime/emulated/structural state. -- [x] Required runtime rows pass library, CLI, native parse, and MCP smoke tests. -- [x] Release and representative semantic corpus tiers exist with reviewed goldens. -- [x] Precision, recall, support, and latency are reported by operation and runtime mode. -- [x] Checked-in fixtures remain unchanged after repeated and concurrent suites. -- [x] Release workflow blocks incomplete certification. -- [x] Public docs state the scope and limits of every claim. -- [x] `npm run check` passes. diff --git a/docs/plans/2026-07-28-explore-first-query-ranking-tuning.md b/docs/plans/2026-07-28-explore-first-query-ranking-tuning.md index 45712936..d74b1cc2 100644 --- a/docs/plans/2026-07-28-explore-first-query-ranking-tuning.md +++ b/docs/plans/2026-07-28-explore-first-query-ranking-tuning.md @@ -2,7 +2,7 @@ Status: Proposed -Parent plan: [One-command product funnel](./2026-07-27-one-command-product-funnel.md) +Origin: follow-up to the completed one-command product funnel. ## Vision diff --git a/package-lock.json b/package-lock.json index 4ddd5b92..a0f6a85a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -253,7 +253,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" }, @@ -302,11 +301,35 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" } }, + "node_modules/@emnapi/core": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", + "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", @@ -314,6 +337,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -2824,7 +2848,6 @@ "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", @@ -3382,7 +3405,6 @@ "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -3439,7 +3461,6 @@ "integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.53.1", "@typescript-eslint/types": "8.53.1", @@ -3657,7 +3678,6 @@ "integrity": "sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@bcoe/v8-coverage": "^1.0.2", "@vitest/utils": "4.1.8", @@ -3787,7 +3807,6 @@ "integrity": "sha512-RUS2ZU2TsduVrI+9c12uTNaKrNUTsm6yFt3fueEUB9iKvyC2UP83F+sqIz00HQIah4UOL1TMoDAki8K0NjGvsA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/utils": "4.1.8", "fflate": "^0.8.2", @@ -3838,7 +3857,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4564,7 +4582,6 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4811,7 +4828,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -5307,7 +5323,6 @@ "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.30.tgz", "integrity": "sha512-emn+JoJjrN9YTpRDS5it/UI2SO9BAE37T6I3d963RxcZ81G9A4pr2SZTEiiaiKbzx+NKRg5BZ89fCL7gCJCUog==", "license": "MIT", - "peer": true, "engines": { "node": ">=16.9.0" } @@ -7216,7 +7231,6 @@ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" @@ -7291,7 +7305,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7401,7 +7414,6 @@ "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", @@ -7480,7 +7492,6 @@ "integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "4.1.8", "@vitest/mocker": "4.1.8", @@ -7710,7 +7721,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" }