Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
40 changes: 38 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,50 @@ Hotfix on top of 2.2.3 for two bugs surfaced by a full first-time-user smoke tes

### Added
- **Codex platform install support** (PR #177): `code-review-graph install --platform codex` appends a `mcp_servers.code-review-graph` section to `~/.codex/config.toml` without overwriting existing Codex settings.
- **Luau language support** (PR #165, closes #153): Roblox Luau (`.luau`) parsing functions, classes, local functions, requires, tests.
- **Luau language support** (PR #165, closes #153): Roblox Luau (`.luau`) parsing -- functions, classes, local functions, requires, tests.
- **REFERENCES edge type** (PR #217): New edge kind for symbol references that aren't direct calls (map/dispatch lookups, string-keyed handlers), including Python and TypeScript patterns.
- **`recurse_submodules` build option** (PR #215): Build/update can now optionally recurse into git submodules.
- **`.gitignore` default for `.code-review-graph/`** (PR #185): Fresh installs automatically add the SQLite DB directory to `.gitignore` so the database isn't accidentally committed.
- **Clearer gitignore docs** (PR #171, closes #157): Documentation now spells out that `code-review-graph` already respects `.gitignore` via `git ls-files`.
- **Parser refactoring**: Extracted 16 per-language handler modules into `code_review_graph/lang/` package using a strategy pattern, replacing monolithic conditionals in `parser.py`
- **Jedi-based call resolution**: New `jedi_resolver.py` module resolves Python method calls at build time via Jedi static analysis, with pre-scan filtering by project function names (36s to 3s on large repos)
- **PreToolUse search enrichment**: New `enrich.py` module and `code-review-graph enrich` CLI command inject graph context (callers, callees, flows, community, tests) into agent search results passively
- **Typed variable call enrichment**: Track constructor-based type inference and instance method calls for Python, JS/TS, and Kotlin/Java
- **Star import resolution**: Resolve `from module import *` by scanning target module's exported names
- **Namespace imports**: Track `import * as X from 'module'` and CommonJS `require()` patterns
- **Angular template parsing**: Extract call targets from Angular component templates
- **JSX handler tracking**: Detect function/class references passed as JSX event handler props
- **Framework decorator recognition**: Identify entry points decorated with `@app.route`, `@router.get`, `@cli.command`, etc., reducing dead code false positives
- **Module-level import tracking**: Track module-qualified call resolution (`module.function()`)
- **Thread safety**: Double-check locking on parser caches (`_type_sets`, `_get_parser`, `_resolve_module_to_file`, `_get_exported_names`)
- **Batch file storage**: `store_file_batch()` groups file insertions into 50-file transactions for faster builds
- **Bulk node loading**: `get_all_nodes()` replaces per-file SQL queries for community detection
- **Adjacency-indexed cohesion**: Community cohesion computed in O(community-edges) instead of O(all-edges), yielding 21x speedup (48.6s to 2.3s on 41k-node repos)
- **Phase timing instrumentation**: `time.perf_counter()` timing at INFO level for all build phases
- **Batch risk_index**: 2 GROUP BY queries replace per-node COUNT loops in risk scoring
- **Weighted flow risk scoring**: Risk scores weighted by flow criticality instead of flat edge counts
- **Transitive TESTED_BY lookup**: `tests_for` and risk scoring follow transitive test relationships
- **DB schema v8**: Composite edge index for upsert performance (v7 reserved by upstream PR #127)
- **`--quiet` and `--json` CLI flags**: Machine-readable output for `build`, `update`, `status`
- **829+ tests** across 26 test files (up from 615), including `test_pain_points.py` (1,587 lines TDD suite), `test_hardened.py` (467 lines), `test_enrich.py` (237 lines)
- **14 new test fixtures**: Kotlin, Java, TypeScript, JSX, Python resolution scenarios

### Changed
- Community detection is now bounded — large repos complete in reasonable time instead of hanging indefinitely.
- Community detection is now bounded -- large repos complete in reasonable time instead of hanging indefinitely.
- New `[enrichment]` optional dependency group for Jedi-based Python call resolution
- Leiden community detection scales resolution parameter with graph size
- Adaptive directory-based fallback for community detection when Leiden produces poor clusters
- Search query deduplication and test function deprioritization

### Fixed
- **Dead code false positives**: Decorators, CDK construct methods, abstract overrides, and overriding methods with called parents no longer flagged as dead
- **E2e test exclusion**: Playwright/Cypress e2e test directories excluded from dead code detection
- **Unique-name plausible caller optimization**: Faster dead code analysis via pre-filtered candidate sets
- **Store cache liveness check**: Cached SQLite connections verified as alive before reuse

### Performance
- **Community detection**: 48.6s to 2.3s (21x) on Gadgetbridge (41k nodes, 280k edges)
- **Jedi enrichment**: 36s to 3s (12x) via pre-scan filtering by project function names

## [2.2.2] - 2026-04-08

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ When using code-review-graph MCP tools, follow these rules:

```bash
# Development
uv run pytest tests/ --tb=short -q # Run tests (572 tests)
uv run pytest tests/ --tb=short -q # Run tests (609 tests)
uv run ruff check code_review_graph/ # Lint
uv run mypy code_review_graph/ --ignore-missing-imports --no-strict-optional

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ code-review-graph watch # Auto-update on file changes
code-review-graph visualize # Generate interactive HTML graph
code-review-graph wiki # Generate markdown wiki from communities
code-review-graph detect-changes # Risk-scored change impact analysis
code-review-graph enrich # Enrich search results with graph context
code-review-graph register <path> # Register repo in multi-repo registry
code-review-graph unregister <id> # Remove repo from registry
code-review-graph repos # List registered repositories
Expand Down Expand Up @@ -296,6 +297,7 @@ Optional dependency groups:
pip install code-review-graph[embeddings] # Local vector embeddings (sentence-transformers)
pip install code-review-graph[google-embeddings] # Google Gemini embeddings
pip install code-review-graph[communities] # Community detection (igraph)
pip install code-review-graph[enrichment] # Jedi-based Python call resolution
pip install code-review-graph[eval] # Evaluation benchmarks (matplotlib)
pip install code-review-graph[wiki] # Wiki generation with LLM summaries (ollama)
pip install code-review-graph[all] # All optional dependencies
Expand All @@ -319,7 +321,7 @@ pytest
<summary><strong>Adding a new language</strong></summary>
<br>

Edit `code_review_graph/parser.py` and add your extension to `EXTENSION_TO_LANGUAGE` along with node type mappings in `_CLASS_TYPES`, `_FUNCTION_TYPES`, `_IMPORT_TYPES`, and `_CALL_TYPES`. Include a test fixture and open a PR.
Edit the appropriate language handler in `code_review_graph/lang/` (e.g., `_python.py`, `_kotlin.py`) or create a new one following `_base.py`. Add your extension to `EXTENSION_TO_LANGUAGE` in `parser.py`, include a test fixture, and open a PR.

</details>

Expand Down
12 changes: 2 additions & 10 deletions code-review-graph-vscode/src/backend/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,22 +53,14 @@ export class CliWrapper {
/**
* Build (or fully rebuild) the graph database for a workspace.
*/
async buildGraph(
workspaceRoot: string,
options?: { fullRebuild?: boolean },
): Promise<CliResult> {
const args = ['build'];
if (options?.fullRebuild) {
args.push('--full');
}

async buildGraph(workspaceRoot: string): Promise<CliResult> {
return vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
title: 'Code Review Graph: Building graph\u2026',
cancellable: false,
},
() => this.exec(args, workspaceRoot),
() => this.exec(['build'], workspaceRoot),
);
}

Expand Down
2 changes: 1 addition & 1 deletion code-review-graph-vscode/src/backend/sqlite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ export class SqliteReader {
if (row) {
const version = parseInt(row.value, 10);
// Must match LATEST_VERSION in code_review_graph/migrations.py
const SUPPORTED_SCHEMA_VERSION = 6;
const SUPPORTED_SCHEMA_VERSION = 8;
if (!isNaN(version) && version > SUPPORTED_SCHEMA_VERSION) {
return `Database was created with a newer version (schema v${version}). Update the extension.`;
}
Expand Down
20 changes: 12 additions & 8 deletions code_review_graph/changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,19 @@ def compute_risk_score(store: GraphStore, node: GraphNode) -> float:
Scoring factors:
- Flow participation: 0.05 per flow membership, capped at 0.25
- Community crossing: 0.05 per caller from a different community, capped at 0.15
- Test coverage: 0.30 if no TESTED_BY edges, 0.05 if tested
- Test coverage: 0.30 (untested) scaling down to 0.05 (5+ TESTED_BY edges)
- Security sensitivity: 0.20 if name matches security keywords
- Caller count: callers / 20, capped at 0.10
"""
score = 0.0

# --- Flow participation (cap 0.25) ---
flow_count = store.count_flow_memberships(node.id)
score += min(flow_count * 0.05, 0.25)
# --- Flow participation (cap 0.25), weighted by criticality ---
flow_criticalities = store.get_flow_criticalities_for_node(node.id)
if flow_criticalities:
score += min(sum(flow_criticalities), 0.25)
else:
flow_count = store.count_flow_memberships(node.id)
score += min(flow_count * 0.05, 0.25)

# --- Community crossing (cap 0.15) ---
callers = store.get_edges_by_target(node.qualified_name)
Expand All @@ -179,10 +183,10 @@ def compute_risk_score(store: GraphStore, node: GraphNode) -> float:
cross_community += 1
score += min(cross_community * 0.05, 0.15)

# --- Test coverage ---
tested_edges = store.get_edges_by_target(node.qualified_name)
has_test = any(e.kind == "TESTED_BY" for e in tested_edges)
score += 0.05 if has_test else 0.30
# --- Test coverage (direct + transitive) ---
transitive_tests = store.get_transitive_tests(node.qualified_name)
test_count = len(transitive_tests)
score += 0.30 - (min(test_count / 5.0, 1.0) * 0.25)

# --- Security sensitivity ---
name_lower = node.name.lower()
Expand Down
Loading
Loading