Files
simplify-code/references/DETECTION.md
T
Giancarmine SalucciandClaude Opus 4.8 81b33fff26 refactor: report-only skill — actionable findings, no apply/modes
The skill no longer modifies code. It emits exactly two artifacts under simplify/:
findings.json (machine-actionable ledger) + a ranked report. Every finding now REQUIRES
title/action/verify (locked in ledger.schema.json) so each is independently executable by
the calling agent. Removed: apply/report modes, Track A autofix execution, baseline gate,
verify-each-change loop, the include-tests switch (tests excluded by default). Reframed
SKILL.md, README, PATTERNS (deterministic/judgment), DETECTION, OPTIMIZATION (judge-only
model tiering), PORTABILITY, SVELTE, CONSOLIDATION accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 02:53:19 +02:00

102 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Detection, ranking & the ledger
How candidates are found mechanically, scored, classified, and recorded — so the model
works from a small file on disk instead of the whole codebase.
## The scan command
```
AST_GREP scan -c <skill-dir>/sgconfig.yml --json=compact <paths>
```
- `<paths>` is the git-visible candidate set (SKILL.md Step 1), with **test files excluded
by default** (overridable via the include-tests switch; recorded in `config.include_tests`).
- `--json=compact` emits one JSON array of matches. Use `--json=stream` for very large
repos (newline-delimited; process incrementally).
### JSON fields you can rely on
Each match object includes (ast-grep 0.44):
- `ruleId` — the rule that matched (join key to `rules/manifest.json`).
- `file` — path.
- `range.start.line` / `range.end.line`**0-indexed**; add 1 for human/editor lines.
- `range.start.column` / `range.end.column`, plus byte offsets.
- `lines` — the matched source text.
- `metaVariables` — captured `$VARS` (single + `$$$` multi).
- `severity`, `message`, `note`.
> `metadata` set in a rule is **not** emitted in JSON. That is why classification lives
> in `rules/manifest.json`, keyed by `ruleId` — read it once during assembly.
## Classification (`rules/manifest.json`)
For every `ruleId`, the manifest gives:
- `pattern` — catalog key (`references/PATTERNS.md`),
- `track``ast-grep` (a deterministic autofix command exists) or `llm` (needs a judgment
edit); the assembly maps this to the finding's `fix_kind`. The skill never applies it,
- `difficulty``mechanical | semantic | hard`,
- `weight` — contribution to the ranking score,
- optional `requires` — a capability gate (e.g. `svelte-grammar`).
Every manifest `pattern` resolves to a heading in `PATTERNS.md`. The reverse isn't
required: a few catalog patterns (`extract-method`, `remove-dead-code`) have no detector
rule — they come from metrics/enrichment, so they carry no manifest entry.
If a `ruleId` is missing from the manifest, default to `track: llm`,
`difficulty: semantic`, `weight: 5`, and note it.
## Metrics that aren't single nodes
ast-grep matches nodes, not measurements. Derive these during assembly:
- **Function length** — from a finding's `range` (end start lines), or from enrichment.
- **Param count** — the `*-long-param-list` rules fire at ≥5 via `nthChild`; exact count
comes from the matched signature text or enrichment.
- **Nesting depth** — the `*-deep-nesting` rules fire at ≥3 nested conditionals.
- **Cyclomatic complexity** — only via enrichment tools (below); otherwise approximate
from nesting + branch counts.
## Optional enrichment tools (only if already installed)
Never install these; use them when present and fold output into `signal`/extra findings.
| Language | Metrics | Dead code | Duplication |
|----------|---------|-----------|-------------|
| any | `scc` (size), `lizard` (CC, multi-lang) | — | `jscpd` |
| JS/TS | `eslint` (+`sonarjs` cognitive complexity) | `knip` | `jscpd` |
| Python | `radon` (CC/MI) | `vulture` | `jscpd` |
| Go | `gocyclo` | `staticcheck` | — |
| Rust | `rust-code-analysis`, `cargo clippy` | `cargo-machete` (deps) | — |
| Java | `PMD` (CC + dead + CPD) | PMD | PMD CPD |
| Svelte | `svelte-check`, `eslint-plugin-svelte`, `svelte/compiler` warnings | eslint | — |
## Ranking (biggest-win-first)
Per finding, base score from the manifest `weight`, scaled by available metrics:
```
severity = manifest.weight
+ max(0, cyclomatic - 10) * (lines / 20)
+ unused_params * 10
+ dup_blocks * 50
```
Clamp to 0100. Process highest first, capped at a small top-N per run (default 10) plus
any user/token budget. Tune the weights against `fixtures/`.
## The ledger (`simplify/findings.json`)
Full schema + example: `../assets/ledger.schema.json`. Two blocks: `config` (Step 4) and
`findings`. Key rule: **status is the source of truth** — on rerun, load the ledger and
process only `status: pending`; never blindly re-scan-and-overwrite.
## Authoring new rules
- One YAML doc per rule (separate docs in a file with `---`). Required: `id`, `language`,
`rule`. Add `fix` only when the rewrite is behavior-preserving for *all* operand types
(→ `track: ast-grep`); otherwise leave it out (→ `track: llm`).
- **Quote any `pattern` containing `:` or `?`** (e.g. ternaries) or YAML mis-parses it.
- Prefer relational rules (`inside`/`has` + `stopBy: end`, `nthChild`) for structural
smells; prefer `pattern` for concrete rewrites.
- Add a matching entry to `rules/manifest.json` and a fixture under `fixtures/`.
- Validate: `AST_GREP scan -c sgconfig.yml --json=compact fixtures/<lang>/...`.