Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .agents/skills/harden-pr/LEDGER.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ By-design or false-positive findings — do not re-raise.
- **[category]** `file:line` — label: reason
```

- **[correctness]** `src/application/impact-engine.ts:147` — explicit inPath on single-definition symbol enables first-hop scopeFiles: by-design — matches show `--in` disambiguation (plan P2.1).
- **[correctness]** `src/application/impact-engine.ts:162` — per-file walk LIMIT before global dedup: by-design v1 — plan architecture per-defining-file walks; global limit still applies at slice.

<!-- Example:
- **[security]** `src/cli/proxy.ts:42` — https_proxy env: by-design — standard CLI proxy convention.
-->
Expand Down
5 changes: 5 additions & 0 deletions .changeset/impact-inpath-homonyms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stainless-code/codemap": patch
---

Scope `codemap impact` and MCP/HTTP `impact` homonym symbols: `--in` / `in` disambiguates by defining file (same prefix/exact rules as `codemap show --in`); unscoped homonyms union per-defining-file call graphs instead of merging by name only. Mismatched scope returns empty `matches` with `skipped_scope`.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,12 +204,14 @@ codemap snippet foo --json # {matches: [{..

# Impact analysis — symbol/file blast-radius walker (callers, callees, dependents, dependencies)
codemap impact handleQuery # both directions, depth 3, all compatible graphs
codemap impact dup --in src/a.ts --via calls # homonym symbol scoped to one defining file
codemap impact src/db.ts --direction up # what depends on db.ts (file-level, deps + imports)
codemap impact handleAudit --depth 1 --via calls # direct callers via the calls table only
codemap impact runWatchLoop --json --summary | jq '.summary.nodes' # CI-gate fan-in score
# Replaces hand-composed `WITH RECURSIVE` queries. Cycle-detected, depth-bounded
# (default 3, --depth 0 = unbounded), limit-capped (default 500). Result envelope:
# {target, matches: [{depth, edge, kind, name?, file_path}], summary: {nodes, terminated_by}}.
# {target, matches: [...], summary: {nodes, terminated_by}, skipped_scope?}.
# Homonym symbols: unscoped unions per-defining-file graphs; --in scopes one file.

# Affected tests — reverse dependency walk from changed sources → test files to run
codemap affected --json # working-tree changes vs HEAD (git status + diff)
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ A local SQLite database (`.codemap/index.db`) indexes the project tree and store

**`src/cli/cmd-context.ts`** (argv + render) → **`handleContext`** in **`tool-handlers.ts`** → **`src/application/context-engine.ts`** (engine — **`buildContextEnvelope`**, **`classifyIntent`**, **`composeStartHere`**, **`resolveContextBudget`**, `ContextEnvelope` type). `buildContextEnvelope` composes the JSON envelope from existing recipes (legacy **`hubs`** at the bundled `fan-in` recipe default limit; budget-capped **`start_here.hub_leaders`** via **`resolveContextBudget(file_count)`**), intent-scoped `sample_markers`, `QUERY_RECIPES` catalog, **`start_here`** (inline `index-summary`, intent-ranked `query_recipe` cards, hub leaders with exported-symbol signatures — optional one-line **`include_snippets`** via CLI `--include-snippets` or MCP/HTTP `include_snippets`, path-contained disk reads with `stale`/`missing` flags), and **`index_freshness`** via **`src/application/index-freshness.ts`**. Debug `--for` / MCP `intent` biases markers toward FIXME/TODO kinds; whitespace-only intent is treated as no intent on all transports. **`classifyIntent`** maps free text to `refactor | debug | test | feature | explore | other`; **`start_here.classified_as`** is `"default"` when no intent is supplied. Hub-leader **`include_snippets`** one-liners share the adaptive **`signature_max_chars`** cap. **`--compact`** drops `hubs`, `sample_markers`, and `start_here` and emits minified JSON (non-compact pretty-prints with 2-space indent). Whitespace-only `--for` values are rejected at CLI parse time. **`include_snippets`** is a no-op when **`compact: true`**. Product-shape constraint: [No split-brain incremental index](./roadmap.md#floors-v1-product-shape).

**Impact wiring:** **`src/cli/cmd-impact.ts`** (argv — `<target>` + `--direction up|down|both` + `--depth N` + `--via dependencies|calls|imports|all` + `--limit N` + `--summary` + `--json`; bootstrap absorbs `--root`/`--config`) + **`src/application/impact-engine.ts`** (engine — `findImpact({db, target, direction?, via?, depth?, limit?})`). Pure transport-agnostic walker over the calls + dependencies + imports graphs; **`--via calls`** walks only parse-resolved `calls` rows (`CALLS_AST_ONLY_SQL` — excludes callback-synthesis heuristics unless consumers query `calls-including-heuristic`); CLI / MCP / HTTP all dispatch the same engine function via `tool-handlers.ts`'s `handleImpact`. Target auto-resolves: contains `/` or matches `files.path` → file target; otherwise symbol (case-sensitive). Walks compatible backends per resolved kind: **symbol** → `calls` (callers / callees by `caller_name` / `callee_name`); **file** → `dependencies` (`from_path` / `to_path`) + `imports` (`file_path` / `resolved_path`, `IS NOT NULL` filter). `--via <b>` overrides; mismatched explicit choices land in `skipped_backends` (no error — agents see why their backend selection yielded fewer rows than expected). One `WITH RECURSIVE` per (direction, backend) combo with cycle detection via path-string `instr` check (SQLite has no native cycle predicate); JS-side merge + dedup by `(direction, kind, name?, file_path)` keeping the shallowest depth. `--depth 0` uses an unbounded sentinel (`UNBOUNDED_DEPTH_SENTINEL = 1_000_000`); cycle detection + `LIMIT` keep cyclic graphs cheap regardless. Termination reason classification: `limit` (truncated) > `depth` (any node sat at the cap) > `exhausted`. Result envelope: `{target, direction, via, depth_limit, matches: [{depth, direction, edge, kind, name?, file_path}], summary: {nodes, max_depth_reached, by_kind, terminated_by}, skipped_backends?}`. `--summary` blanks `matches` (transport bandwidth saver) but preserves `summary.nodes` so CI gates (`jq '.summary.nodes'`) still see the count. SARIF / annotations not supported (graph traversal, not findings — the parser accepts the flag combos but the engine only emits JSON).
**Impact wiring:** **`src/cli/cmd-impact.ts`** (argv — `<target>` + `--in <path>` + `--direction up|down|both` + `--depth N` + `--via dependencies|calls|imports|all` + `--limit N` + `--summary` + `--json`; bootstrap absorbs `--root`/`--config`) + **`src/application/impact-engine.ts`** (engine — `findImpact({db, target, direction?, via?, depth?, limit?, inPath?})`). Pure transport-agnostic walker over the calls + dependencies + imports graphs; **`--via calls`** walks only parse-resolved `calls` rows (`CALLS_AST_ONLY_SQL` — excludes callback-synthesis heuristics unless consumers query `calls-including-heuristic`); CLI / MCP / HTTP all dispatch the same engine function via `tool-handlers.ts`'s `handleImpact` (MCP/HTTP `in` arg). Target auto-resolves: contains `/` or matches `files.path` → file target; otherwise symbol (case-sensitive). **Homonym symbols** (`matched_in.length > 1`): unscoped walks union per-defining-file call graphs (first hop scoped to each definition's call sites); `--in` / MCP `in` filters `matched_in` via show-engine prefix/exact rules — no match → empty `matches` + `skipped_scope`. Walks compatible backends per resolved kind: **symbol** → `calls` (callers / callees by `caller_name` / `callee_name`); **file** → `dependencies` (`from_path` / `to_path`) + `imports` (`file_path` / `resolved_path`, `IS NOT NULL` filter). `--via <b>` overrides; mismatched explicit choices land in `skipped_backends` (no error — agents see why their backend selection yielded fewer rows than expected). One `WITH RECURSIVE` per (direction, backend) combo with cycle detection via path-string `instr` check (SQLite has no native cycle predicate); JS-side merge + dedup by `(direction, kind, name?, file_path)` keeping the shallowest depth. `--depth 0` uses an unbounded sentinel (`UNBOUNDED_DEPTH_SENTINEL = 1_000_000`); cycle detection + `LIMIT` keep cyclic graphs cheap regardless. Termination reason classification: `limit` (truncated) > `depth` (any node sat at the cap) > `exhausted`. Result envelope: `{target, direction, via, depth_limit, matches: [{depth, direction, edge, kind, name?, file_path}], summary: {nodes, max_depth_reached, by_kind, terminated_by}, skipped_backends?, skipped_scope?}`. `--summary` blanks `matches` (transport bandwidth saver) but preserves `summary.nodes` so CI gates (`jq '.summary.nodes'`) still see the count. SARIF / annotations not supported (graph traversal, not findings — the parser accepts the flag combos but the engine only emits JSON).

**Affected wiring:** **`src/cli/cmd-affected.ts`** (argv — positional paths / `--stdin` / `--changed-since <ref>` / `--params test_glob|max_depth` + `--json`; bootstrap absorbs `--root`/`--config`) + **`src/application/affected-engine.ts`** (engine — `resolveAffectedChangedPaths` + `executeAffectedTests`; pure recipe composer over bundled `affected-tests` SQL). CLI / MCP / HTTP dispatch the same engine via `tool-handlers.ts`'s `handleAffected` (MCP/HTTP) and `runAffectedCmd` (CLI). Path precedence: explicit paths (CLI positional / MCP `paths` array) → CLI `--stdin` → git vs `changed_since` / `HEAD` (`paths: []` on MCP/HTTP skips git). Result envelope: JSON array of `{test_path, impact_depth, actions?}` — file paths only; CI composes the runner command. **`tryRecordRecipeRun("affected-tests")`** lives at the orchestration layer (`handleAffected` + `runAffectedCmd`), not in the engine — same boundary discipline as `query_recipe` (see [§ `recipe_recency`](#recipe_recency--per-recipe-last-run--run-count-user-data-strict-without-rowid)). Recency records only when at least one changed path was resolved and the recipe SQL ran (empty path sets return `[]` without a recency write).

Expand Down
2 changes: 1 addition & 1 deletion docs/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ Index mode that diffs against `last_indexed_commit` (git) and only re-indexes ch

### `codemap impact` / impact tool

Symbol or file blast-radius walker. CLI: `codemap impact <target> [--direction up|down|both] [--depth N] [--via dependencies|calls|imports|all] [--limit N] [--summary] [--json]`. MCP: `impact` tool. HTTP: `POST /tool/impact`. Replaces hand-composed `WITH RECURSIVE` queries that agents struggle to write reliably. Walks compatible graphs based on resolved target kind: **symbol** targets walk `calls` (callers / callees by name); **file** targets walk `dependencies` + `imports` (`resolved_path` only). Mismatched explicit `--via` choices land in `skipped_backends` instead of failing. Cycle-detected via path-string `instr` check inside the recursive CTE; bounded by `--depth` (default 3, 0 = unbounded but still cycle-detected and limit-capped) and `--limit` (default 500). Result envelope: `{target, direction, via, depth_limit, matches: [{depth, direction, edge, kind, name?, file_path}], summary: {nodes, max_depth_reached, by_kind, terminated_by: 'depth'|'limit'|'exhausted'}}`. `--summary` trims `matches` for cheap CI gate consumption (`jq '.summary.nodes'`) but preserves the count. Pure transport-agnostic engine in `application/impact-engine.ts`; CLI / MCP / HTTP all dispatch the same `findImpact` function. `sarif` / `annotations` formats not supported (impact rows are graph traversals, not findings).
Symbol or file blast-radius walker. CLI: `codemap impact <target> [--in <path>] [--direction up|down|both] [--depth N] [--via dependencies|calls|imports|all] [--limit N] [--summary] [--json]`. MCP: `impact` tool (`in` arg). HTTP: `POST /tool/impact`. Replaces hand-composed `WITH RECURSIVE` queries that agents struggle to write reliably. Walks compatible graphs based on resolved target kind: **symbol** targets walk `calls` (callers / callees by name); **file** targets walk `dependencies` + `imports` (`resolved_path` only). **Homonym symbols:** unscoped walks union per-defining-file call graphs; `--in` / MCP `in` disambiguates via show-engine prefix/exact rules — mismatch → empty `matches` + `skipped_scope`. Mismatched explicit `--via` choices land in `skipped_backends` instead of failing. Cycle-detected via path-string `instr` check inside the recursive CTE; bounded by `--depth` (default 3, 0 = unbounded but still cycle-detected and limit-capped) and `--limit` (default 500). Result envelope: `{target, direction, via, depth_limit, matches: [{depth, direction, edge, kind, name?, file_path}], summary: {nodes, max_depth_reached, by_kind, terminated_by: 'depth'|'limit'|'exhausted'}, skipped_scope?}`. `--summary` trims `matches` for cheap CI gate consumption (`jq '.summary.nodes'`) but preserves the count. Pure transport-agnostic engine in `application/impact-engine.ts`; CLI / MCP / HTTP all dispatch the same `findImpact` function. `sarif` / `annotations` formats not supported (impact rows are graph traversals, not findings).
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

### `import_specifiers` (table)

Expand Down
81 changes: 0 additions & 81 deletions docs/plans/impact-inpath-homonyms.md

This file was deleted.

Loading
Loading