Status: open · Priority: P2 · Effort: M (~2 weeks)
Motivator:
symbols.complexitystores cyclomatic (McCabe) counts only. Agents and recipes (high-complexity-untested, future churn-hotspots JOINs) lack cognitive complexity — a separate signal that penalizes nesting and non-linear control flow more than flat branch chains. Surfacing both axes improves refactor-priority ranking without new verdict verbs.Roadmap: § Core substrate & platform
Extend createComplexityTracker in one oxc visitor pass (R.1 single-pass — no second AST walk). Ship column + migration + one parse fixture before the bundled recipe. Follow substrate-extraction.md tier patterns for schema bump.
| File | What to read |
|---|---|
src/extractors/complexity.ts |
createComplexityTracker, complexityExtractor, popTop |
src/parser.ts |
complexity: createComplexityTracker(symbols) wiring |
src/db.ts |
symbols table — add cognitive_complexity |
templates/recipes/high-complexity-untested.sql |
Optional SELECT extension in v2 |
| Parser / golden fixtures | Nested-if fixture asserting cognitive > cyclomatic |
oxc visitor (existing complexity walk)
→ cognitive counter + nesting stack (parallel to cyclomatic)
→ symbol row: complexity + cognitive_complexity + nesting_depth
recipe high-cognitive-complexity
→ SQL filter on cognitive_complexity >= :min_score
- Tracker increments on nested
iffixture (cognitive > cyclomatic). 2.SCHEMA_VERSIONbump + migration. 3. Reindex self or fixture. Recipe in slice 2.
Replacing cyclomatic complexity; Sonar-exact parity certification (match spec behavior on fixtures, not full corpus diff).
| # | Decision | Source |
|---|---|---|
| C.1 | New column symbols.cognitive_complexity INTEGER — nullable; populated for function-scoped symbols same as cyclomatic. |
Moat B |
| C.2 | Same extractor walk — extend complexityExtractor / createComplexityTracker in one oxc visitor pass; no second AST traversal. |
substrate-extraction R.1 |
| C.3 | SonarSource cognitive rules — increment on breaks in linear flow; nesting increment when entering if/loop/catch/switch; boolean sequence handling per published spec. |
SonarSource cognitive complexity spec |
| C.4 | Moat-A exposure — parametrised recipe high-cognitive-complexity (threshold param); optionally extend high-complexity-untested with cognitive_complexity column in SELECT. |
Moat A |
| C.5 | Keep cyclomatic — do not replace complexity; recipes choose axis via SQL. |
Backwards compat |
- Extend
ComplexityTrackerwith cognitive counter + nesting penalty stack (parallel to cyclomatic stack). - Wire increments on
IfStatement, loops,catch,switch,&&/||sequences per spec (unit-test small fixtures). popTop()writescognitive_complexityonto symbol row alongsidecomplexity/nesting_depth.SCHEMA_VERSIONbump + migration indb.ts.- Bundled recipe
high-cognitive-complexity(params: min_score, default 15). - Golden parse fixture: nested
ifchain — cognitive > cyclomatic. - Docs —
architecture.mdsymbolscolumns;glossary.mddisambiguate cyclomatic vs cognitive.
bun test src/parser.test.ts # nested-if fixture
bun src/index.ts --files <fixture-path>
bun src/index.ts query --recipe high-cognitive-complexity --json
bun run typecheck # SymbolRow + db migration- Nested control-flow fixture:
cognitive_complexity>complexity - Flat
ifchain: cognitive ≈ cyclomatic (within spec tolerance) - Incremental reindex updates column for changed files
- No new CLI verdict verb
| # | Question |
|---|---|
| Q1 | Populate cognitive for arrow fns / methods only, or all function kinds? |
| Q2 | Default min_score for high-cognitive-complexity recipe (15 vs 20)? |
| Q3 | Extend high-complexity-untested SELECT in v1 or defer to v2? |
- Shipped:
src/extractors/complexity.ts,symbols.complexity,high-complexity-untestedrecipe - Synergy: churn-complexity-hotspots may JOIN cognitive column in v2 recipe param