Enforce dual complexity ceilings (cyclomatic + cognitive ≤16) and decompose pm next renderer

Merged
UN
4mo ago
+424-82

Summary

Closes the gate gap behind GH-518 (CodeFactor "Complex Method" in src/cli/commands/next.ts, tracked as pm-2gvp) and fixes the finding itself.

Why the old gate missed it

The existing complexity: max 17 rule was calibrated to an assumption that CodeFactor flags at cyclomatic ≥ 18. GH-518 disproved that: the flagged renderNextMarkdown scores cyclomatic 14 but cognitive complexity 22 (deeply nested sections), while runNext in the same file (cyclomatic 16, cognitive 5 — flat early returns) was never flagged. CodeFactor's detector is nesting-weighted, so a purely cyclomatic gate is structurally blind to this finding class.

Changes

  • eslint.config.mjs: twin ceilings — complexity: max 16 and sonarjs/cognitive-complexity: max 16 (policy: no function over 16 on either metric). Calibration evidence documented in the config comment.
  • src/cli/commands/next.ts: renderNextMarkdown decomposed into renderNextSummaryLines / renderNextRecommendedRows / renderNextSection / formatNextBlockedLine; output byte-identical (all rendering specs unchanged and green); cognitive 22 → ≤3 per function.
  • eslint-suppressions.json: 76 pre-existing violations grandfathered (62 cognitive >16, 14 at cyclomatic 17); the shrink-only ratchet still fails lint on any new violation or stale suppression. Burn-down tracked as pm-j8vq (child of pm-92if) with the worst offenders enumerated.
  • static-quality-gate.mjs: MAX_ESLINT_SUPPRESSIONS re-pinned 104 → 180 with the rule-addition exception documented (budget remains lower-only otherwise).
  • package.json: lint:complexity:baseline now covers both rules; adds eslint-plugin-sonarjs (dev).
  • CONTRIBUTING.md: documents the twin ceilings and the GH-518 calibration evidence.

Verification

  • pnpm lint (ESLint + jscpd + static quality gate + token budget): green
  • Full coverage gate: 100% statements/branches/functions/lines (34,726/34,726 statements)
  • next/cli rendering suites: 393 tests green

🤖 Generated with Claude Code

https://claude.ai/code/session_01VGcEVUn53YH2gwpLpUdvEJ

Summary by Sourcery

Enforce dual cyclomatic and cognitive complexity ceilings in ESLint and refactor the pm next CLI renderer to comply while keeping output unchanged.

Enhancements:

  • Refactor the pm next markdown renderer by extracting summary, section, recommendation, and blocked-row helpers to reduce per-function complexity while preserving existing output.
  • Tighten the maintainability gate by adding a sonarjs-based cognitive-complexity rule alongside a lower cyclomatic-complexity limit and wiring the plugin into the shared ESLint config.
  • Document the new dual complexity ceilings and their CodeFactor calibration details in CONTRIBUTING.

Build:

  • Update the static quality gate’s ESLint suppression budget to account for newly grandfathered complexity violations.
  • Extend the complexity baseline lint script to cover both cyclomatic and cognitive complexity rules and add eslint-plugin-sonarjs as a dev dependency.

Summary by cubic

Enforce dual complexity ceilings (cyclomatic and cognitive ≤16) and refactor the pm next markdown renderer into small helpers to close the CodeFactor “Complex Method” gap. Addresses GH-518, tracked as pm-2gvp; behavior and output are unchanged.

  • Refactors

    • ESLint: add complexity max 16 and sonarjs/cognitive-complexity max 16; extend lint:complexity:baseline to cover both.
    • Decompose renderNextMarkdown into renderNextSummaryLines / renderNextRecommendedRows / renderNextSection / formatNextBlockedLine; add an inline note explaining the decision_needed ?? [] legacy-payload guard; output remains byte-identical.
    • Grandfather existing violations in eslint-suppressions.json; re-pin static gate MAX_ESLINT_SUPPRESSIONS from 104 to 180 with rule-addition note; document policy in CONTRIBUTING.md.
  • Dependencies

    • Add eslint-plugin-sonarjs for cognitive complexity rules.