Skip to content

refactor(checks)!: cut 20 linter-level checks from the catalog (CD-357) - #223

Open
TAJD wants to merge 8 commits into
mainfrom
fix/cd-357-cut-linter-checks
Open

refactor(checks)!: cut 20 linter-level checks from the catalog (CD-357)#223
TAJD wants to merge 8 commits into
mainfrom
fix/cd-357-cut-linter-checks

Conversation

@TAJD

@TAJD TAJD commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Workstream A1 of the policy-engine repositioning. See ROADMAP.md; detailed tracking is private.

What this does

Removes 20 checks that judged code a line at a time — the ones a linter already does better, and that made cofferdam read as "another linter with fewer rules". The catalogue goes from 47 published pages to 27.

Category Removed
Warning.* TripleEquals, NoConsoleLog, NoDebugger, NoEval, UnusedImport, UnusedNullCheck
Readability.* MaxLineLength, MaxFunctionLength
Refactor.* CyclomaticComplexity, CognitiveComplexity, DuplicateBlock, MutatedParameter, PreferArrayMethodOverLoop, PreferConstOverLet, PreferNullishCoalescing, PreferOptionalChain, UnusedVariable
Design.* MaxParameters
Consistency.* QuoteStyle, SpellingDialect

Result: 24 findings-emitting built-ins, plus 5 context providers in their own registry — the ~25 target met on the cut list alone, with no residue trimming.

Refactor.LongAndComplex covers the intersection the two standalone complexity checks aimed at. Refactor.NearDuplicateBlock absorbed Refactor.DuplicateBlock and reports both verbatim and literal-drift clones.

Breaking

Config keys, suppression directives and baseline entries naming a removed id stop matching. [checks."X"] and [budgets] warn; [[overrides]] and suppression comments do not — those are silent, and are called out in the CHANGELOG as manual migration steps.

Duplicate detection is not reduced, but its severity changed: verbatim clones reported at medium and gated a default --fail-on medium build; they now report at low and no longer fail a build by default. Restore with [checks."Refactor.NearDuplicateBlock"] severity = "medium".

cofferdam advise --analyze is removed outright. All five of its budget entries were hardcoded to checks on the cut list, and reporting un-gated complexity numbers is explicitly not what cofferdam is for. The [budgets] CI ratchet is a separate feature and is untouched.

Found by review, fixed here

An adversarial review of the diff caught a real regression that the green test suite did not. partition_claimed_groups runs one overlap-claim pass across all candidates; after the cut, exact-clone groups still claimed territory but were no longer emitted — so an exact clone silently suppressed any overlapping near-duplicate and then vanished itself. Adding a verbatim copy of a file made cofferdam report strictly less. Both halves now report; two tests that pinned the buggy behaviour are inverted and a direct regression test added.

The same review found the CHANGELOG's removal list wrong in both directions — eight ids named that never existed here (Credo rule names, copied from the ticket rather than read from all_builtins()) and eight real removals unmentioned. The list above is derived from the diff.

Notable

doctor's formatter-coexistence check warned that cofferdam double-reports against Biome/ESLint and named four built-ins to disable — all four are deleted here. It now passes, and names the one genuine remaining overlap (Refactor.LongAndComplex vs ESLint complexity) rather than claiming none exists.

gen-docs no longer publishes a category heading with nothing under it: Readability is now empty.

Two engine capabilities are left with no first-party user — pass-2 consistency mode and Check::languages() — both still reachable by plugins, tracked separately.

Verification

cargo build --workspace, cargo test --workspace (1335 passed), cargo clippy --workspace --all-targets -- -D warnings, cargo fmt --all --check, cofferdam gen-docs --check — all clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_012eUbK1Te7zdsrAXYeTSc1j

TAJD and others added 6 commits August 13, 2026 00:51
…advise --analyze (CD-357 pass 1/4)

Cofferdam is a policy engine, not a linter. A check ships only if it reads
the spec, needs the corpus/graph, or needs the pass-2 consistency mode.
These eight fail that test and belong to Biome/oxlint:

  Warning.TripleEquals, Warning.NoConsoleLog, Warning.NoDebugger,
  Warning.NoEval, Warning.UnusedImport, Warning.UnusedNullCheck,
  Readability.MaxLineLength, Readability.MaxFunctionLength

Both categories empty out entirely, so warning.rs and readability.rs are
deleted outright. The Category::Warning and Category::Readability enum
variants stay — they are public API, and how the five-category model is
restated is a positioning question for the README rewrite, not a deletion
question. Warning.ParseError is engine-internal, not a registered check,
and is unaffected.

Also removes `advise --analyze`. Its AnalyzeEnvelope was a hardcoded list
of exactly five budget entries, all five of them checks being cut, so it
is not generic over the catalogue and post-cut reports nothing. Keeping it
would ship numbers that gate nothing, which is the metrics product we are
explicitly not building. The [budgets] CI ratchet is a different feature
and is untouched. Removing the whole subcommand also avoids a non-additive
change to the advise JSON schema.

Refs CD-357.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012eUbK1Te7zdsrAXYeTSc1j
…ss 2/4)

Removes Refactor.UnusedVariable, PreferConstOverLet, PreferOptionalChain,
PreferNullishCoalescing, PreferArrayMethodOverLoop, MutatedParameter,
CyclomaticComplexity, CognitiveComplexity and DuplicateBlock. All are style
or smell rules Biome and oxlint already own; none reads the spec, needs the
corpus, or needs pass-2 mode.

duplicate_block.rs is trimmed rather than deleted: Refactor.NearDuplicateBlock
survives and lived in the same file, so it absorbs the corpus-collection logic
and becomes the sole writer. Exact-clone regression coverage is preserved via a
cfg(test) helper.

Also removes max_cyclomatic_complexity_in_file and max_cognitive_complexity_in_file,
dead since pass 1 removed advise --analyze.

The three suppression fixtures previously pointed at PreferConstOverLet, which
this commit deletes. Because a suppression directive naming an unregistered
check id is silently ignored (CD-395), that would have left the fixtures
parsing, the tests passing, and Consistency.BroadSuppression /
Consistency.UnusedSuppression exercised by nothing at all. They are re-pointed
at Design.ReadonlyArrayParam, which survives, and a new integration test
asserts a non-empty finding set so the next removal fails loudly instead of
silently vacating the coverage.

Refs CD-357, CD-395.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012eUbK1Te7zdsrAXYeTSc1j
… 3/4)

Removes Design.MaxParameters, Consistency.QuoteStyle and
Consistency.SpellingDialect, completing the 20-check cut. consistency.rs is
trimmed (3082 -> 1293 lines), not deleted: BroadSuppression,
UnusedSuppression and ErrorHandlingIdiom survive in it.

all_builtins() now registers 24 findings-emitting TypeScript checks, plus 5
context providers in their own registry. With the Rust and HTML adapter
checks evicted by A3, that is the ~25-check target met without needing to
trim the residue.

Two structural consequences, both deliberate and both flagged upstream:

QuoteStyle was the last builtin with consistency: true, so no builtin now
uses pass-2 mode. Cache tests that depended on pass-2 dispatch to register
hits are kept honest with a synthetic Test.NoopConsistencyCheck rather than
being weakened.

SpellingDialect was the only builtin overriding Check::languages() to reach
markdown, so the test asserting builtin markdown reach is deleted — no
repoint target exists.

Design.MaxParameters was load-bearing well beyond its own check: the release
smoke fixture, the starter cofferdam.toml, config doc examples and several
integration tests all used it as a convenient trigger. Each is re-pointed at
a surviving check, chosen per call site — Design.ReadonlyArrayParam where any
finding will do, Refactor.LongAndComplex where a numeric option was needed.

Refs CD-357.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012eUbK1Te7zdsrAXYeTSc1j
…357 pass 4/4)

Passes 1-3 removed the checks themselves; this clears what the compiler and
test suite could not see. Doc prose, CLI help text, config examples, the
starter TOML, generated checks.json, the repo's own dogfooding baseline, CI
smoke scripts and the type-host fixtures all named checks a user can no
longer run.

Also drops the unicode-width direct dependency, which existed solely so
Readability.MaxLineLength could measure display columns rather than UTF-8
bytes. Nothing else in the tree used it.

Doc comments in cofferdam-core are updated rather than deleted where they
explained a mechanism by example: Check::languages() and Language::Markdown
both used Consistency.SpellingDialect as their illustration, and now record
that no built-in declares a non-TypeScript language while plugins still can.
That gap is tracked as CD-396.

Verified: 1333 tests pass, clippy clean, fmt clean, gen-docs --check clean.

Refs CD-357.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012eUbK1Te7zdsrAXYeTSc1j
…s (CD-357)

Completes the pass-4 sweep. The agent-facing recipe table in CLAUDE.md was
the worst of the residue: four of its six rows pointed at files deleted in
passes 1-3, so any agent following it would have opened nothing.

The "text-line scan" row is deleted rather than repointed. No surviving
check reads only file.lines() — that was the whole point of CD-357 — so the
table now says so explicitly, and treats "needs neither graph nor AST" as a
signal the rule belongs in a linter instead.

Removes .claude/plans/post-v1-roadmap.md. It proposed adding NoConsoleLog,
QuoteStyle and a TripleEquals autofix, which is the exact inverse of the
direction this branch takes, and an in-repo plan doc that contradicts the
tracker is worse than no plan doc.

Suppression unit tests in consistency.rs and context/findings.rs still name
removed check IDs. Left alone deliberately: they pass the ID list in
explicitly and exercise the directive parser, so the strings are fixtures,
not catalog references.

Refs CD-357, CD-396.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012eUbK1Te7zdsrAXYeTSc1j
Resolves nine conflicts between the check cut and #197's docs restructure.
Main's prose wins wherever #197 deliberately rewrote a section (README,
docs/index.md, MAINTAINERS roadmap pointer); this branch wins wherever the
only difference was a check ID that no longer exists.

docs/type-aware-checks.md keeps main's richer configuration example --
including the note that disabling the last type-aware check does not stop
the type host spawning -- repointed from Warning.UnusedNullCheck to
Design.UnionExhaustivenessGap.

The merge also exposed a defect four sweep passes missed. doctor's
formatter-coexistence check warned that cofferdam double-reports against
biome/eslint and told users to disable four named built-ins -- all four of
which this branch deletes. The honest answer is now the opposite of a
warning: the catalogue is disjoint from a linter's by construction, so the
check reports Pass and says so. Kept rather than deleted because users
arriving from a linter expect an overlap and the useful answer is that there
isn't one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012eUbK1Te7zdsrAXYeTSc1j
@github-actions

Copy link
Copy Markdown

Criterion benchmark comparison (PR head vs base branch)

�[1m�[92m   Compiling�[0m cofferdam-core v0.4.2 (/home/runner/work/cofferdam/cofferdam/crates/cofferdam-core)
�[1m�[92m   Compiling�[0m cofferdam-html v0.4.2 (/home/runner/work/cofferdam/cofferdam/crates/cofferdam-html)
�[1m�[92m   Compiling�[0m cofferdam-graph v0.4.2 (/home/runner/work/cofferdam/cofferdam/crates/cofferdam-graph)
�[1m�[92m   Compiling�[0m cofferdam-rust v0.4.2 (/home/runner/work/cofferdam/cofferdam/crates/cofferdam-rust)
�[1m�[92m   Compiling�[0m cofferdam-checks v0.4.2 (/home/runner/work/cofferdam/cofferdam/crates/cofferdam-checks)
�[1m�[92m   Compiling�[0m cofferdam-engine v0.4.2 (/home/runner/work/cofferdam/cofferdam/crates/cofferdam-engine)
�[1m�[92m    Finished�[0m `bench` profile [optimized] target(s) in 48.13s
�[1m�[92m     Running�[0m benches/engine_bench.rs (target/release/deps/engine_bench-e2309ad71dcae46d)
Gnuplot not found, using plotters backend
Benchmarking full_run_no_cache
Benchmarking full_run_no_cache: Warming up for 3.0000 s
Benchmarking full_run_no_cache: Collecting 20 samples in estimated 5.4425 s (420 iterations)
Benchmarking full_run_no_cache: Analyzing
full_run_no_cache       time:   [12.320 ms 12.560 ms 12.945 ms]
                        change: [-27.553% -25.370% -23.089%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 2 outliers among 20 measurements (10.00%)
  1 (5.00%) high mild
  1 (5.00%) high severe

Benchmarking single_file_edit_incremental
Benchmarking single_file_edit_incremental: Warming up for 3.0000 s

Warning: Unable to complete 20 samples in 5.0s. You may wish to increase target time to 7.2s, enable flat sampling, or reduce sample count to 10.
Benchmarking single_file_edit_incremental: Collecting 20 samples in estimated 7.1618 s (210 iterations)
Benchmarking single_file_edit_incremental: Analyzing
single_file_edit_incremental
                        time:   [5.8748 ms 5.9599 ms 6.0302 ms]
                        change: [-19.108% -17.584% -16.060%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 1 outliers among 20 measurements (5.00%)
  1 (5.00%) high mild


The page still described the old two-check split: it linked to the deleted
Refactor.DuplicateBlock page (failing the VitePress dead-link gate) and, worse,
told users to configure min_statements and friends under
[checks."Refactor.DuplicateBlock"]. Pass 2 moved DUP_BLOCK_OPTIONS onto
NearDuplicateBlock, so that advice pointed at a key that does nothing.

gen-docs --check passes on both the old and new text — it verifies the
generated page matches its source, not that the links resolve — so CI's docs
build was the only thing that could catch this.

Refs CD-357.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012eUbK1Te7zdsrAXYeTSc1j
@github-actions

Copy link
Copy Markdown

Criterion benchmark comparison (PR head vs base branch)

�[1m�[92m   Compiling�[0m cofferdam-core v0.4.2 (/home/runner/work/cofferdam/cofferdam/crates/cofferdam-core)
�[1m�[92m   Compiling�[0m cofferdam-html v0.4.2 (/home/runner/work/cofferdam/cofferdam/crates/cofferdam-html)
�[1m�[92m   Compiling�[0m cofferdam-rust v0.4.2 (/home/runner/work/cofferdam/cofferdam/crates/cofferdam-rust)
�[1m�[92m   Compiling�[0m cofferdam-graph v0.4.2 (/home/runner/work/cofferdam/cofferdam/crates/cofferdam-graph)
�[1m�[92m   Compiling�[0m cofferdam-engine v0.4.2 (/home/runner/work/cofferdam/cofferdam/crates/cofferdam-engine)
�[1m�[92m   Compiling�[0m cofferdam-checks v0.4.2 (/home/runner/work/cofferdam/cofferdam/crates/cofferdam-checks)
�[1m�[92m    Finished�[0m `bench` profile [optimized] target(s) in 42.93s
�[1m�[92m     Running�[0m benches/engine_bench.rs (target/release/deps/engine_bench-e2309ad71dcae46d)
Gnuplot not found, using plotters backend
Benchmarking full_run_no_cache
Benchmarking full_run_no_cache: Warming up for 3.0000 s
Benchmarking full_run_no_cache: Collecting 20 samples in estimated 5.0914 s (420 iterations)
Benchmarking full_run_no_cache: Analyzing
full_run_no_cache       time:   [11.473 ms 11.575 ms 11.689 ms]
                        change: [-31.919% -29.055% -27.027%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 1 outliers among 20 measurements (5.00%)
  1 (5.00%) high mild

Benchmarking single_file_edit_incremental
Benchmarking single_file_edit_incremental: Warming up for 3.0000 s

Warning: Unable to complete 20 samples in 5.0s. You may wish to increase target time to 7.0s, enable flat sampling, or reduce sample count to 10.
Benchmarking single_file_edit_incremental: Collecting 20 samples in estimated 7.0063 s (210 iterations)
Benchmarking single_file_edit_incremental: Analyzing
single_file_edit_incremental
                        time:   [5.5569 ms 5.6176 ms 5.6675 ms]
                        change: [-17.909% -16.238% -14.733%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 1 outliers among 20 measurements (5.00%)
  1 (5.00%) low mild


…NGELOG

Fixes found by an adversarial review of the A1 diff.

The regression: partition_claimed_groups runs one overlap-claim pass over
every candidate group, and exact-clone groups still claimed territory after
pass 2 stopped emitting them. An exact group therefore suppressed any
overlapping literal-drift group and then vanished itself, so adding a
verbatim copy of a file made cofferdam report strictly less than before it
was added. Both halves now report under Refactor.NearDuplicateBlock,
distinguished by message wording. This also un-inerts include_tokens, whose
fingerprints always have exact_hash == hash and so could only ever land in
the discarded bucket.

Two tests pinned the buggy behaviour and are inverted; a new test covers the
suppression case directly, since the existing overlap test concatenates its
fixtures so their spans never collide. finalize_all_groups_for_test now
delegates to the real finalize instead of re-implementing it.

The CHANGELOG's removal list was wrong in both directions. Eight ids it named
never existed in this repo -- they are Credo rule names, copied from the
ticket rather than read out of all_builtins() -- and eight ids that were
actually removed went unmentioned, so a user upgrading was told to delete
config for rules they never had while not being told Warning.NoEval and
Refactor.UnusedVariable had stopped firing. The list is now derived from the
diff. It also documents the severity consequence (exact clones dropped from
medium to low, so duplication no longer fails a default build), the option
relocation, and the three silent migration hazards.

Restores duplicate_block_suppressed_from_either_occurrence, which covered
engine behaviour rather than the removed check, repointed and strengthened to
assert the finding fires before asserting a comment suppresses it.

doctor's coexistence message overstated its case: Refactor.LongAndComplex
does overlap ESLint's complexity rules. It now names that one rather than
claiming no overlap exists.

gen-docs no longer publishes a category heading with nothing under it --
Readability is empty as of CD-357 and rendered as a bare heading.

Refs CD-357, CD-396.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012eUbK1Te7zdsrAXYeTSc1j
@github-actions

Copy link
Copy Markdown

Criterion benchmark comparison (PR head vs base branch)

�[1m�[92m   Compiling�[0m cofferdam-core v0.4.2 (/home/runner/work/cofferdam/cofferdam/crates/cofferdam-core)
�[1m�[92m   Compiling�[0m cofferdam-rust v0.4.2 (/home/runner/work/cofferdam/cofferdam/crates/cofferdam-rust)
�[1m�[92m   Compiling�[0m cofferdam-html v0.4.2 (/home/runner/work/cofferdam/cofferdam/crates/cofferdam-html)
�[1m�[92m   Compiling�[0m cofferdam-graph v0.4.2 (/home/runner/work/cofferdam/cofferdam/crates/cofferdam-graph)
�[1m�[92m   Compiling�[0m cofferdam-checks v0.4.2 (/home/runner/work/cofferdam/cofferdam/crates/cofferdam-checks)
�[1m�[92m   Compiling�[0m cofferdam-engine v0.4.2 (/home/runner/work/cofferdam/cofferdam/crates/cofferdam-engine)
�[1m�[92m    Finished�[0m `bench` profile [optimized] target(s) in 43.34s
�[1m�[92m     Running�[0m benches/engine_bench.rs (target/release/deps/engine_bench-e2309ad71dcae46d)
Gnuplot not found, using plotters backend
Benchmarking full_run_no_cache
Benchmarking full_run_no_cache: Warming up for 3.0000 s
Benchmarking full_run_no_cache: Collecting 20 samples in estimated 5.0231 s (420 iterations)
Benchmarking full_run_no_cache: Analyzing
full_run_no_cache       time:   [11.379 ms 11.407 ms 11.437 ms]
                        change: [-29.240% -28.728% -28.144%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 1 outliers among 20 measurements (5.00%)
  1 (5.00%) high severe

Benchmarking single_file_edit_incremental
Benchmarking single_file_edit_incremental: Warming up for 3.0000 s

Warning: Unable to complete 20 samples in 5.0s. You may wish to increase target time to 6.7s, enable flat sampling, or reduce sample count to 10.
Benchmarking single_file_edit_incremental: Collecting 20 samples in estimated 6.7020 s (210 iterations)
Benchmarking single_file_edit_incremental: Analyzing
single_file_edit_incremental
                        time:   [5.2478 ms 5.2957 ms 5.3446 ms]
                        change: [-16.630% -15.332% -14.041%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 1 outliers among 20 measurements (5.00%)
  1 (5.00%) high mild


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant