Skip to content

Commit 0aa9948

Browse files
committed
feat(index): Sonar cognitive complexity column and recipes
Add symbols.cognitive_complexity in the same oxc walk as cyclomatic, including class methods. Ship high-cognitive-complexity (min_score 15) and extend high-complexity-untested SELECT. Schema v38; plan deleted.
1 parent cf7df2f commit 0aa9948

21 files changed

Lines changed: 260 additions & 158 deletions

.changeset/cognitive-complexity.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@stainless-code/codemap": patch
3+
---
4+
5+
Add SonarSource cognitive complexity on `symbols` (same function-shaped coverage as cyclomatic, including class methods). New recipe `high-cognitive-complexity`; `high-complexity-untested` rows include `cognitive_complexity`.

docs/architecture.md

Lines changed: 27 additions & 26 deletions
Large diffs are not rendered by default.

docs/glossary.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,11 @@ React components (PascalCase + JSX return or hook usage). PascalCase functions t
137137

138138
### `symbols.complexity` / cyclomatic complexity / McCabe
139139

140-
Per-function decision-point count (REAL column on `symbols`). Computed by the parser walker (`src/parser.ts`) per the McCabe formula: `1 + (decision points)`. Counted nodes: `if`, `while`, `do…while`, `for`, `for…in`, `for…of`, `case X:` (not `default:` — that's the fall-through arm, not a decision), `&&`, `||`, `??`, `?:`, `catch`. Function-shaped symbols only — non-functions (interfaces, types, enums, plain consts) and class methods get `complexity = NULL` (v1 limitation; class methods tracked under `high-complexity-untested.md`). Joins to `coverage` via `(file_path, name, line_start)` natural key for the bundled `high-complexity-untested` recipe (complexity ≥ 10 ⨯ coverage < 50%).
140+
Per-function decision-point count (REAL column on `symbols`). Computed by the parser walker per the McCabe formula: `1 + (decision points)`. Counted nodes: `if`, `while`, `do…while`, `for`, `for…in`, `for…of`, `case X:` (not `default:`), `&&`, `||`, `??`, `?:`, `catch`. Function-shaped symbols only (top-level `function`, named arrow/const, class methods); non-functions get `complexity = NULL`. Joins to `coverage` for `high-complexity-untested` (cyclomatic ≥ 10 ⨯ coverage < 50%). See also [`symbols.cognitive_complexity`](#symbolscognitive_complexity--cognitive-complexity).
141+
142+
### `symbols.cognitive_complexity` / cognitive complexity
143+
144+
SonarSource cognitive complexity (INTEGER on `symbols`) for the same function-shaped symbols as cyclomatic `complexity`. Penalizes nested control flow; same oxc walk as McCabe (`src/extractors/complexity.ts`). Recipes: `high-cognitive-complexity` (`min_score` default 15); `high-complexity-untested` includes the column while filtering on cyclomatic `complexity`.
141145

142146
### `source_fts` (FTS5 virtual table) / `--with-fts` / opt-in full-text
143147

docs/plans/ast-hash-duplication.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,5 +147,5 @@ Register golden scenario per [`docs/golden-queries.md`](../golden-queries.md); g
147147
## Dependencies
148148

149149
- Shipped: `symbols` extraction, `hashContent`, recipe loader
150-
- Independent of [churn-complexity-hotspots](./churn-complexity-hotspots.md), [cognitive-complexity](./cognitive-complexity.md)
150+
- Independent of [churn-complexity-hotspots](./churn-complexity-hotspots.md), [`symbols.cognitive_complexity`](../glossary.md#symbolscognitive_complexity--cognitive-complexity)
151151
- Supersedes motivation for suffix-array semantic dupes (stay deferred)

docs/plans/cognitive-complexity.md

Lines changed: 0 additions & 102 deletions
This file was deleted.

docs/plans/graph-estimated-crap.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,5 @@ bun test scripts/query-golden-coverage-matrix.test.mjs # after golden scenario
125125
## Dependencies
126126

127127
- Shipped: `symbols.complexity`, `coverage`, `dependencies`, `calls`, `references`, `test_suites`, `affected-tests` glob conventions
128-
- Synergy: [cognitive-complexity](./cognitive-complexity.md) (optional second axis in same recipe later); [coverage-deletion-confidence](./coverage-deletion-confidence.md) (opposite signal — dead + zero coverage)
128+
- Synergy: [`symbols.cognitive_complexity`](../glossary.md#symbolscognitive_complexity--cognitive-complexity) (optional second axis in same recipe later); [coverage-deletion-confidence](./coverage-deletion-confidence.md) (opposite signal — dead + zero coverage)
129129
- Weaker until: [c9-plugin-layer](./c9-plugin-layer.md) (framework test files may be misclassified)

docs/roadmap.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ Predicate-as-API only — enrich row shape and audit deltas; no standalone pass/
111111
- [ ] **`history` table** (deferred — revisit-triggered) — temporal queries: "when did symbol X get `@deprecated`?", "coverage trend over last 50 commits", "files that became dead this week". `audit --base <ref>` covers the most-common temporal question (PR-scoped diff) without schema growth, so the table earns its place only when bigger questions emerge. Two shapes (per-commit snapshots ~N × DB size; append-only event log heavier CTE walks); both pay an N-reindexes backfill cost (~30s per reindex). **Revisit triggers:** two consumers ship `jq`-based "audit-runs-over-time" workflows, OR `query_baselines` evolution becomes a recurring agent need.
112112
- [ ] **`codemap audit` verdict + thresholds** (v1.x) — `verdict: "pass" | "warn" | "fail"` driven by an `audit.deltas[<key>].{added_max, action}` field on the config object (`.codemap/config.{ts,js,json}`). Triggers: two consumers ship `jq`-based threshold scripts with similar shapes, OR one consumer asks with a concrete config sketch. Until then, raw deltas + consumer-side `jq` is the CI exit-code idiom. **Likely accelerant:** the Marketplace Action (next item) shipping is the most plausible path to firing the trigger — once `- uses: stainless-code/codemap@v1` is the dominant CI path, real `jq` threshold scripts will surface.
113113
- [ ] **GitHub Marketplace Action — publish + listing finish** — core Action implementation is in-tree: root `action.yml`, `query --ci`, `audit --format sarif` / `--ci`, package-manager detection, dogfood smoke, and opt-in `pr-comment` summary renderer have shipped. Remaining work is the release/listing slice: `MARKETPLACE.md`, `v1.0.0` / floating `v1` tags, Marketplace setup, sacrificial-repo smoke, and making `action-smoke` blocking once the Action tag exists. Action version stream is independent of CLI version (`package.json` currently drives CLI/npm version; Action publishes at its own `v1.0.0`). Plan: [`plans/github-marketplace-action.md`](./plans/github-marketplace-action.md). Effort: S.
114-
- [ ] **Cognitive complexity column**`symbols.cognitive_complexity` (SonarSource rules) alongside cyclomatic `complexity` in the same oxc walk; recipe `high-cognitive-complexity`. Improves refactor-priority JOINs with coverage/churn recipes. Plan: [`plans/cognitive-complexity.md`](./plans/cognitive-complexity.md). Effort: M.
115114
- [ ] **Churn × complexity hotspots**`file_churn` table (git `log --numstat` over indexed paths, recency-weighted commits, optional trend) + bundled recipe **`churn-complexity-hotspots`** JOINing `symbols.complexity` for ranked refactor targets. Distinct from outcome alias `hotspots``fan-in`. Score is a recipe column, not a verdict ([Moat A](./roadmap.md#moats-load-bearing)). Plan: [`plans/churn-complexity-hotspots.md`](./plans/churn-complexity-hotspots.md). Effort: L–M.
116115
- [ ] **AST-hash duplication**`symbols.body_hash` column (normalized AST hash via oxc, computed at parse time — Rust-native, fast) + bundled `duplicates` recipe joining on `body_hash` (`GROUP BY body_hash HAVING COUNT(*) > 1`). **Different shape from token-level suffix-array dupes** (catches structurally-identical functions, not copy-paste with renamed variables). Substrate addition — consumer writes the JOIN that decides "this is a problem"; no severity, no suppression-by-default. Plan: [`plans/ast-hash-duplication.md`](./plans/ast-hash-duplication.md). Effort: M.
117116
- [ ] **Falsifiable benchmark CI on named external fixtures** — structural-cost A/B (indexed queries vs `find` + `grep` + `Read`-loop discovery) on zod, fastify, vue-core, next.js. Numbers land in [`docs/benchmark.md`](./benchmark.md); headline figures surface in `MARKETPLACE.md` only after external runs land. Harness: [benchmark § Agent eval harness](./benchmark.md#agent-eval-harness) + external fixture extension; pair with **Agent eval: quality × tokens × wall** for scored completion metrics. **Partial:** manual [`.github/workflows/agent-eval-external.yml`](../.github/workflows/agent-eval-external.yml) for in-repo fixture paths (not zod/fastify/nightly). Effort: M. **Self-index regression guardrail shipped** (#96): `bun run check:perf-baseline` + weekly scheduled workflow (demoted from PR hard gate — GHA runner variance).
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[
2+
{
3+
"name": "labyrinth",
4+
"kind": "function",
5+
"file_path": "src/lib/complexity-fixture.ts",
6+
"line_start": 22,
7+
"line_end": 83,
8+
"cognitive_complexity": 40,
9+
"complexity": 19,
10+
"nesting_depth": 5
11+
}
12+
]

fixtures/golden/minimal/high-complexity-untested.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"line_start": 22,
77
"line_end": 83,
88
"complexity": 19,
9+
"cognitive_complexity": 40,
910
"coverage_pct": 0
1011
}
1112
]

fixtures/golden/scenarios.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,11 @@
452452
"prompt": "Functions with nesting_depth >= 4, ranked by depth/complexity.",
453453
"recipe": "deeply-nested-functions"
454454
},
455+
{
456+
"id": "high-cognitive-complexity",
457+
"prompt": "Functions with cognitive complexity >= default threshold (15).",
458+
"recipe": "high-cognitive-complexity"
459+
},
455460
{
456461
"id": "circular-imports",
457462
"prompt": "Files in import cycles (SCCs of size >= 2) via Tarjan.",

0 commit comments

Comments
 (0)