mirror of
https://github.com/affaan-m/ECC.git
synced 2026-09-29 04:55:12 +02:00
Merge reviewed contributor PR #3099 into backlog batch
Source-PR: https://github.com/affaan-m/ECC/pull/3099
Source-Head: d12c5a584d
Local integration checkpoint; aggregate review and hosted acceptance pending.
This commit is contained in:
@@ -37,7 +37,7 @@ Phase 3: Delegate + Verify
|
||||
├─ Spawns claude -p subprocess with violations JSON
|
||||
├─ Routes to model tier based on violation complexity:
|
||||
│ ├─ Haiku: formatting, imports, style (E/W/F codes) — 120s timeout
|
||||
│ ├─ Sonnet: complexity, refactoring (C901, PLR codes) — 300s timeout
|
||||
│ ├─ Sonnet: complexity, refactoring (C901, PLR codes, oxlint complexity) — 300s timeout
|
||||
│ └─ Opus: type system, deep reasoning (unresolved-attribute) — 600s timeout
|
||||
├─ Re-runs Phase 1+2 to verify fixes
|
||||
└─ Exit 0 if clean, Exit 2 if violations remain (reported to main agent)
|
||||
@@ -102,7 +102,7 @@ To use Plankton hooks in your own project:
|
||||
| Language | Required | Optional |
|
||||
|----------|----------|----------|
|
||||
| Python | `ruff`, `uv` | `ty` (types), `vulture` (dead code), `bandit` (security) |
|
||||
| TypeScript/JS | `biome` | `oxlint`, `semgrep`, `knip` (dead exports) |
|
||||
| TypeScript/JS | `biome`; `oxlint` (>= 1.37.0) when using `complexity` | `semgrep`, `knip` (dead exports) |
|
||||
| Shell | `shellcheck`, `shfmt` | — |
|
||||
| YAML | `yamllint` | — |
|
||||
| Markdown | `markdownlint-cli2` | — |
|
||||
@@ -235,3 +235,94 @@ Track:
|
||||
- average remediation time
|
||||
- repeat violations by category
|
||||
- merge blocks due to gate failures
|
||||
|
||||
---
|
||||
|
||||
## Gabe's addition: oxlint complexity + ratchet ceiling (JS/TS)
|
||||
|
||||
Closes the JS/TS gap in the model-routing table above. Python complexity (ruff C901,
|
||||
PLR) already routes to Sonnet. JS/TS had no equivalent rule turned on by default.
|
||||
|
||||
### Turn on oxlint's `complexity` rule when using it
|
||||
|
||||
oxlint's `complexity` rule (source: eslint's `complexity` rule, ported) lives in the
|
||||
"restriction" category, which oxlint does not enable by default. It must be turned on
|
||||
by hand. Verified against [Oxlint's complexity rule](https://oxc.rs/docs/guide/usage/linter/rules/eslint/complexity)
|
||||
(2026-08-27): default option is `max: 20`. The rule is available in oxlint >= 1.37.0.
|
||||
|
||||
The skill's Language-Specific Dependencies table keeps `oxlint` optional for TypeScript/JS
|
||||
generally. When adopting the `complexity` rule, use oxlint >= 1.37.0 and treat it as a
|
||||
required dependency. Add the rule to the supported oxlint configuration the project already
|
||||
uses (`.oxlintrc.json`, `.oxlintrc.jsonc`, `oxlint.config.ts`, or `oxlint.config.mts`). If
|
||||
none exists, create one; do not create a second configuration file in the same directory.
|
||||
|
||||
Measure the codebase's current worst complexity score before choosing the initial enforced
|
||||
ceiling. The template below is intentionally incomplete: replace `<MEASURED_CEILING>` with
|
||||
that score, optionally plus a small amount of headroom, before committing the `"error"` gate.
|
||||
Oxlint's default of 20 is a long-term target, not a safe universal starting ceiling.
|
||||
|
||||
```json
|
||||
{
|
||||
"rules": {
|
||||
"complexity": ["error", { "max": "<MEASURED_CEILING>" }]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Replace the placeholder before running oxlint; it is not a valid numeric threshold until the
|
||||
repository has been measured. See the ratchet section below for how the ceiling gets set on
|
||||
a real codebase.
|
||||
|
||||
### Model-routing row
|
||||
|
||||
Add oxlint `complexity` violations to the same row as the existing Python entry in the
|
||||
Phase 3 subprocess table:
|
||||
|
||||
```
|
||||
├─ Sonnet: complexity, refactoring (C901, PLR codes, oxlint complexity), 300s timeout
|
||||
```
|
||||
|
||||
Same tier as Python's C901/PLR. A complexity violation is a refactoring job either way,
|
||||
language does not change the model tier.
|
||||
|
||||
### The ratchet-ceiling technique
|
||||
|
||||
Source: [Hunk PR #861](https://github.com/modem-dev/hunk/pull/861) (merged 2026-08-26, verified against
|
||||
the PR's own diff and description via the GitHub API, not paraphrased from memory). Hunk
|
||||
turned on oxlint's `complexity` rule with `"error", { "max": 80 }` in `.oxlintrc.json`.
|
||||
Their own worst score at the time was 78 (`App`), next was 76
|
||||
(`validateFileViewLayout`). From the PR body: "This is intentionally an initial
|
||||
regression ceiling rather than the long-term target... so 80 adds enforcement without
|
||||
grandfathering or suppressions. The ceiling can be ratcheted downward as existing
|
||||
hotspots are simplified." And: "A global ceiling does not prevent a function below 80
|
||||
from growing toward it."
|
||||
|
||||
The technique, as actually run in that PR:
|
||||
|
||||
1. Measure the current worst complexity score in the codebase (oxlint reports it when
|
||||
the rule fires).
|
||||
2. Set the ENFORCED ceiling to a value at least as high as that worst score, not to
|
||||
oxlint's own default of 20. Add a small amount of headroom if needed so the initial
|
||||
enable does not fail CI on a score you have not fixed yet (hunk used 80 against a
|
||||
worst of 78).
|
||||
3. Commit that as `"error"`, wired into the existing lint CI step. This is a real gate
|
||||
from the first commit, not a suggestion.
|
||||
4. Never grandfather. The ceiling is global. A function sitting at 40 today is not
|
||||
exempt, it still cannot cross the ceiling later. That is the whole point: catch
|
||||
growth, not just today's worst offenders.
|
||||
5. Never blanket-suppress. No per-file or per-function disable comments to make a
|
||||
violation go away. Fix it or leave it under the ceiling.
|
||||
6. Each time a flagged hotspot is refactored below the ceiling, re-measure the global
|
||||
maximum across the whole codebase. Lower the ceiling by hand only to a value that
|
||||
remains at least as high as every remaining function's score (plus any deliberate
|
||||
headroom). This is a manual step done as its own commit, not automated. It is how the
|
||||
ceiling moves toward the linter's real default of 20 over time instead of sitting at
|
||||
the codebase's worst score forever.
|
||||
|
||||
One caveat, stated plainly: the PR itself went straight from "rule off" to `"error"`
|
||||
enforcement in one commit. It did not stage through a report-only or warn-only phase
|
||||
first. Starting with the rule set to `"warn"` for one CI run before flipping it to
|
||||
`"error"` is a reasonable staging step if a team has never measured its own worst score
|
||||
and does not want a surprise CI failure, but that staging step is Gabe's own prudent
|
||||
practice, not something verified in hunk's PR. Say so if you use it, do not attribute it
|
||||
to the source.
|
||||
|
||||
Reference in New Issue
Block a user