@nurix/etna 0.18.4 → 0.18.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/harness-index.json +1 -1
- package/data/harnesses/code/harness.json +2 -2
- package/data/harnesses/hr/CLAUDE.md +7 -0
- package/data/harnesses/hr/harness.json +2 -2
- package/data/harnesses/hr/rules/code-eval-batcher.md +71 -0
- package/data/harnesses/hr/skills/evaluator/SKILL.md +208 -0
- package/data/harnesses/hr/skills/evaluator/criteria/code-quality.md +350 -0
- package/data/harnesses/hr/skills/evaluator/criteria/deployment-readiness.md +414 -0
- package/data/harnesses/hr/skills/evaluator/criteria/maintainability.md +445 -0
- package/data/harnesses/hr/skills/evaluator/criteria/mechanical-ambition.md +300 -0
- package/data/harnesses/hr/skills/evaluator/criteria/problem-substantiveness.md +88 -0
- package/data/harnesses/hr/skills/evaluator/criteria/testing.md +399 -0
- package/data/harnesses/hr/skills/evaluator/criteria/ux-mechanics.md +283 -0
- package/data/harnesses/hr/skills/evaluator/criteria/value-articulation.md +76 -0
- package/data/harnesses/hr/skills/evaluator/preflight.md +74 -0
- package/data/harnesses/hr/skills/evaluator/preflight.sh +100 -0
- package/data/harnesses/hr/skills/evaluator/validate_scorecard.py +98 -0
- package/package.json +1 -1
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
# Code Quality — Extraction Plan
|
|
2
|
+
|
|
3
|
+
- **Sub-criterion:** 1.1 (AI-only)
|
|
4
|
+
- **Weight:** 12.5 pts (4 Code AI × 12.5 = 50 pts half)
|
|
5
|
+
- **Contenders:** LLM interpretive read (primary, load-bearing) + lint (`ruff` / `biome` / `golangci-lint`) + `lizard` complexity (**per-language CCN**: Python 10 / JS-TS 10 / Java 12 / Go 15) + `semgrep` quality packs + CodeRabbit scrape (**calibrating modifier**).
|
|
6
|
+
- **Source quote:** _"Readable, modular, well-structured. Good naming, clean APIs, no code smells."_
|
|
7
|
+
|
|
8
|
+
Bands are **absolute** — the _Banding_ table in this recipe declares per-signal numeric thresholds. `lizard_god_per_kloc` is computed against **per-language CCN thresholds**: Python / JS / TS functions are "god" at CCN > 10; Java at CCN > 12; Go at CCN > 15 — a per-language lookup keyed on file extension. `_NOT_RUN: true` is supported for tool-missing cases but not for language-not-present (emit `null` aggregates instead). 1.1 has no gates of its own; secret/vuln signals are owned by `maintainability.md`.
|
|
9
|
+
|
|
10
|
+
> **Why CR is a modifier, not primary.** Null-data semantics and the plan-upgrade escape hatch are spelled out once in [`SKILL.md → CodeRabbit — calibrating modifier with null-data semantics`](../SKILL.md#coderabbit--calibrating-modifier-with-null-data-semantics). Summary: CR joins lint / complexity / file-size / semgrep as a **calibrating modifier**; contributes when a project has non-empty CR data, no-op (`null`) when empty.
|
|
11
|
+
|
|
12
|
+
> **Source-quote is semantic.** "Readable", "good naming", "clean APIs", "no code smells" are all reader-experience judgments. No single mechanical tool captures them — so the LLM read is the load-bearing signal, with lint / complexity / file-size / semgrep / CR acting as calibrating modifiers.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Preflight
|
|
17
|
+
|
|
18
|
+
Before running this recipe, execute [`../../preflight.md`](../../preflight.md). The recipe hard-requires:
|
|
19
|
+
|
|
20
|
+
- `ruff` (Python repos)
|
|
21
|
+
- `biome` (TS/JS repos) — preferred over eslint for speed; eslint is a per-project fallback if biome rejects the repo's config shape
|
|
22
|
+
- `golangci-lint` (Go repos)
|
|
23
|
+
- `lizard` (polyglot complexity)
|
|
24
|
+
- `semgrep` (polyglot static analysis)
|
|
25
|
+
- `gh`, `jq` (CR scrape + utilities)
|
|
26
|
+
|
|
27
|
+
If any of the above is missing **and preflight cannot install it**, the recipe must emit `{signal}_NOT_RUN: true` + `reason` and flag the shortfall loudly in `§6.1` of the analysis file. No silent grep-estimation substitutes.
|
|
28
|
+
|
|
29
|
+
Tools are called only for languages present in the repo (see each signal below).
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Extraction recipe
|
|
34
|
+
|
|
35
|
+
### Signal 1 — LLM interpretive read (**primary**, load-bearing)
|
|
36
|
+
|
|
37
|
+
Sample **6 source files** that give the reader a representative slice:
|
|
38
|
+
|
|
39
|
+
1. **Largest source file** by LoC (one per primary language)
|
|
40
|
+
2. **Highest-churn file** from `git log --numstat --all --no-merges | awk ...` (top by lines touched)
|
|
41
|
+
3. **Entrypoint / handler**: matches `*/main.{py,ts,js,go}`, `*/app.{py,ts}`, `*/handlers/*`, `*/routes/*`, `*/api/*`
|
|
42
|
+
4. **3 random** across distinct directories, excluding `tests/`, `__tests__/`, generated, vendored, lockfiles. Prefer files > 50 LoC.
|
|
43
|
+
|
|
44
|
+
Reviewer prompt (apply identically across all six files):
|
|
45
|
+
|
|
46
|
+
> Score 0–5 on each axis:
|
|
47
|
+
>
|
|
48
|
+
> - **Naming** — intent-revealing identifiers, consistent vocabulary, no cryptic abbreviations.
|
|
49
|
+
> - **API shape** — function signatures clear, arguments typed / labeled, side-effects explicit, return types obvious.
|
|
50
|
+
> - **Abstraction** — one responsibility per module, boundaries intentional, no god-objects.
|
|
51
|
+
> - **Modularity** — low coupling, reasonable import graph, shared concerns extracted.
|
|
52
|
+
> - **Code smells** — no duplicated blocks, no magic numbers, nesting ≤ 3, no dead branches.
|
|
53
|
+
|
|
54
|
+
Emit **per file** `{path, naming, api_shape, abstraction, modularity, smells, one_sentence}`.
|
|
55
|
+
|
|
56
|
+
Emit **aggregate**:
|
|
57
|
+
|
|
58
|
+
- `llm_quality_score` = mean of per-file axis means, clamped to 0–5, to one decimal.
|
|
59
|
+
- `llm_notes` — 2–3 sentences summarising the strongest signal that drove the score (good or bad).
|
|
60
|
+
- `llm_files_sampled` — list of 6 paths.
|
|
61
|
+
|
|
62
|
+
### Signal 2 — Lint cleanliness
|
|
63
|
+
|
|
64
|
+
Run the lint tool for every language present in the repo. Skip the tool for languages absent.
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Detect primary languages (reuse the detection from 1.2 / 2.4 recipes)
|
|
68
|
+
LANGS=$(find <repo> -type f \
|
|
69
|
+
\( -name '*.py' -o -name '*.ts' -o -name '*.tsx' \
|
|
70
|
+
-o -name '*.js' -o -name '*.jsx' -o -name '*.go' \) \
|
|
71
|
+
-not -path '*/node_modules/*' -not -path '*/.venv/*' \
|
|
72
|
+
| sed 's/.*\.//' | sort -u)
|
|
73
|
+
|
|
74
|
+
# Python
|
|
75
|
+
if echo "$LANGS" | grep -q '^py$'; then
|
|
76
|
+
ruff check <repo> --output-format json > /tmp/ruff.json || true
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# TS / JS — prefer biome. Use `biome lint` (not `biome ci` or `biome check`):
|
|
80
|
+
# `ci` / `check` bundle lint + format + organizeImports as diagnostics, which
|
|
81
|
+
# inflates `lint_findings_per_kloc` with stylistic `category: "format"` noise
|
|
82
|
+
# that the scorer doesn't want (one observed project showed 11.4/kLoC with
|
|
83
|
+
# format, 5.1/kLoC without — crossing the band-4 threshold of 10). `biome lint`
|
|
84
|
+
# is pure lint and matches the intent of the signal.
|
|
85
|
+
if echo "$LANGS" | grep -qE '^(ts|tsx|js|jsx)$'; then
|
|
86
|
+
cd <repo>
|
|
87
|
+
biome lint . --reporter=json > /tmp/biome.json 2>/dev/null \
|
|
88
|
+
|| npx --yes eslint . --format json > /tmp/eslint.json 2>/dev/null \
|
|
89
|
+
|| true
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# Go
|
|
93
|
+
if echo "$LANGS" | grep -q '^go$'; then
|
|
94
|
+
cd <repo> && golangci-lint run --out-format json ./... > /tmp/golangci.json 2>/dev/null || true
|
|
95
|
+
fi
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Parse (same shape across tools):
|
|
99
|
+
|
|
100
|
+
- `lint_findings_total` — sum across tools
|
|
101
|
+
- `lint_findings_per_kloc` — `total ÷ (scc_code_loc ÷ 1000)`; reuse `scc_code_loc` from 2.4 recipe
|
|
102
|
+
- `lint_by_severity` — `{error: N, warning: N, info: N}` mapped from each tool's severity field
|
|
103
|
+
- `lint_by_tool` — `{ruff: N, biome: N, eslint: N, golangci: N}` for transparency
|
|
104
|
+
- `lint_has_config_in_repo` — bool: does the repo itself ship a `ruff.toml` / `biome.json` / `.eslintrc*` / `.golangci.yml`? (rewards repos that committed to a standard).
|
|
105
|
+
|
|
106
|
+
### Signal 3 — Complexity density (`lizard`; shared with 1.4)
|
|
107
|
+
|
|
108
|
+
`lizard` is run once by 1.4 Maintainability — **read its output**, do not re-run. 1.4's Signal 2 applies per-language CCN thresholds (Python 10 / JS-TS 10 / Java 12 / Go 15 — see [maintainability.md](maintainability.md#signal-2--polyglot-complexity-lizard-per-language-ccn)); 1.1 reuses those aggregates verbatim.
|
|
109
|
+
|
|
110
|
+
Reuse:
|
|
111
|
+
|
|
112
|
+
- `lizard_mean_ccn` — mean cyclomatic complexity across functions
|
|
113
|
+
- `lizard_god_functions` — count with CCN > per-language threshold (Py/JS/TS 10, Java 12, Go 15, other 15)
|
|
114
|
+
- `lizard_god_by_language` — map `{py, js, ts, go, java, other}` → god count per language
|
|
115
|
+
- `lizard_god_per_kloc` — `god_functions ÷ (scc_code_loc ÷ 1000)`
|
|
116
|
+
- `lizard_god_ratio` — `god_functions ÷ total_functions`
|
|
117
|
+
- `lizard_thresholds_applied` — `{py: 10, js: 10, ts: 10, java: 12, go: 15, default: 15}` (audit constant)
|
|
118
|
+
|
|
119
|
+
If 1.4 has not been run yet for this repo, run lizard now (same command, same CSV location) and share the output.
|
|
120
|
+
|
|
121
|
+
### Signal 4 — File-size distribution (filesystem)
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
find <repo> -type f \
|
|
125
|
+
\( -name '*.py' -o -name '*.ts' -o -name '*.tsx' \
|
|
126
|
+
-o -name '*.js' -o -name '*.jsx' -o -name '*.go' \
|
|
127
|
+
-o -name '*.java' -o -name '*.kt' -o -name '*.rs' \) \
|
|
128
|
+
-not -path '*/node_modules/*' -not -path '*/.venv/*' \
|
|
129
|
+
-not -path '*/dist/*' -not -path '*/build/*' \
|
|
130
|
+
-exec wc -l {} + 2>/dev/null | sort -rn | head -20
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Capture:
|
|
134
|
+
|
|
135
|
+
- `files_over_500_loc` — count of source files ≥ 500 LoC
|
|
136
|
+
- `largest_file_loc` — largest source file
|
|
137
|
+
- `mean_source_file_loc` — mean over all source files
|
|
138
|
+
|
|
139
|
+
Shared with 1.4; avoid duplicate computation.
|
|
140
|
+
|
|
141
|
+
### Signal 5 — Semgrep quality rule-set
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# NOTE: `--config p/code-quality` is deprecated on Semgrep Registry and
|
|
145
|
+
# returns HTTP 404 with retry-storm. Stick to the per-language packs that
|
|
146
|
+
# resolve cleanly. If you need an umbrella, use `p/default` (lower signal).
|
|
147
|
+
timeout 180 semgrep --config p/python --config p/typescript \
|
|
148
|
+
--config p/javascript --config p/go \
|
|
149
|
+
--json --timeout 30 --disable-version-check --metrics=off \
|
|
150
|
+
<repo> > /tmp/semgrep-quality.json
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Filter out findings that belong to 1.2 (resilience) or 1.4 (SECURITY). The `p/security-audit` and resilience rules carry distinct `check_id` prefixes — drop findings whose `check_id` matches `(bare-except|broad-except|swallow|missing-(validation|input-validation)|injection|deserial|unsafe|hardcoded-password|hardcoded-api-key|ssrf|sqli|xxe|eval-)`.
|
|
154
|
+
|
|
155
|
+
Capture:
|
|
156
|
+
|
|
157
|
+
- `semgrep_quality_findings` — post-filter count
|
|
158
|
+
- `semgrep_quality_per_kloc` — `findings ÷ (scc_code_loc ÷ 1000)`
|
|
159
|
+
- `semgrep_quality_by_severity` — `{ERROR, WARNING, INFO}`
|
|
160
|
+
|
|
161
|
+
### Signal 6 — CodeRabbit scrape (**calibrating modifier**)
|
|
162
|
+
|
|
163
|
+
Scrape CR comments from both endpoints CR posts to — inline review comments and issue-style walkthrough comments. Both are needed: walkthrough-only repos look empty on the pulls endpoint alone. CR's label text is authoritative; emoji is a fallback (CR has rotated emoji twice recently).
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# Cap each endpoint at 90s so a CR-heavy repo can't stall the run. The
|
|
167
|
+
# `--paginate-limit` flag was removed in gh 2.89+, so we rely on `--paginate`
|
|
168
|
+
# (fetches all pages) wrapped in `timeout` for the actual cap. Typical comment
|
|
169
|
+
# counts fit easily inside the 90s window.
|
|
170
|
+
(
|
|
171
|
+
timeout 90 gh api "repos/<org>/<repo>/pulls/comments?per_page=100" \
|
|
172
|
+
--paginate \
|
|
173
|
+
--jq '[.[] | select(.user.login == "coderabbitai[bot]")]'
|
|
174
|
+
timeout 90 gh api "repos/<org>/<repo>/issues/comments?per_page=100" \
|
|
175
|
+
--paginate \
|
|
176
|
+
--jq '[.[] | select(.user.login == "coderabbitai[bot]")]'
|
|
177
|
+
) > /tmp/cr.json
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Parse each CR comment; prefer label text, fall back to emoji:
|
|
181
|
+
|
|
182
|
+
| Label text (authoritative) | Emoji (may drift) | Category |
|
|
183
|
+
| -------------------------- | ----------------- | ------------- |
|
|
184
|
+
| `Refactor suggestion` | `🛠️` | `refactor` |
|
|
185
|
+
| `Potential issue` / `Bug` | `🐛` | `bug` |
|
|
186
|
+
| `Warning` | `⚠️` | `warning` |
|
|
187
|
+
| `Nitpick` | `📝` / `🧹` | `nitpick` |
|
|
188
|
+
| `Security` | `🔒` | `security` |
|
|
189
|
+
| `Performance` | `⚡` | `performance` |
|
|
190
|
+
|
|
191
|
+
**Walkthrough detection (positive-match, not silence-inferred).** CR posts one issue-level walkthrough per PR it reviews, regardless of plan tier. Detect walkthroughs positively via any of these markers in the body (robust across CR template drift):
|
|
192
|
+
|
|
193
|
+
- HTML comment: `<!-- walkthrough_start -->` (current stable anchor)
|
|
194
|
+
- Section heading: `## Walkthrough`
|
|
195
|
+
- Collapsible summary: `<summary>📝 Walkthrough</summary>`
|
|
196
|
+
|
|
197
|
+
Any one match ⇒ count the comment as a walkthrough and record `cr_walkthrough_count`. This is separate from `cr_pr_count` (unique PRs with ≥1 CR comment of any kind).
|
|
198
|
+
|
|
199
|
+
**Actionable-count capture.** When CR has actionable inline feedback, the walkthrough summary carries a line of the form `Actionable comments posted: N` (optionally bold-wrapped). Match with a tolerant regex:
|
|
200
|
+
|
|
201
|
+
```regex
|
|
202
|
+
\*{0,2}Actionable comments posted:\s*(\d+)\*{0,2}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Sum captured `N` across walkthroughs → `cr_actionable_total`. If no walkthrough contains this string **and** at least one walkthrough contains a Free-plan disclaimer (`Summarized by CodeRabbit Free` / `is on the Free plan`), set `cr_free_plan: true` — this distinguishes "zero actionables because Free-plan walkthrough-only" from "zero actionables because the regex is blind to a new template." Unmatched inline comments bucket to `uncategorized`; surface the count so loss is visible.
|
|
206
|
+
|
|
207
|
+
Capture:
|
|
208
|
+
|
|
209
|
+
- `cr_pr_count` — unique PRs with ≥1 CR comment
|
|
210
|
+
- `cr_walkthrough_count` — CR walkthrough comments matched via positive marker
|
|
211
|
+
- `cr_free_plan` — bool: body contains Free-plan disclaimer; explains zero-actionable outcomes
|
|
212
|
+
- `cr_actionable_total` — Σ captured `N` across walkthroughs (regex above)
|
|
213
|
+
- `cr_actionable_per_kloc` — `cr_actionable_total ÷ (scc_code_loc ÷ 1000)` (lower-is-better; `null` when `cr_pr_count == 0`)
|
|
214
|
+
- `cr_by_category` — map of category → count (`uncategorized` bucket when neither label nor emoji matches)
|
|
215
|
+
- `cr_severe_count` — `by_category.bug + by_category.security + by_category.warning`
|
|
216
|
+
- `cr_severe_per_kloc` — `cr_severe_count ÷ (scc_code_loc ÷ 1000)` (lower-is-better; `null` when `cr_pr_count == 0`)
|
|
217
|
+
|
|
218
|
+
**Banding rule:** CR is one of several calibrating modifiers (see _Banding_ below). When `cr_pr_count > 0`, `cr_actionable_per_kloc` and `cr_severe_per_kloc` join lint/lizard/semgrep as density signals that cap the band. When `cr_pr_count == 0` or all walkthroughs are actionable-free (the Free-plan default for many projects), both fields are `null` and the modifier is a no-op — the LLM + remaining density signals decide the band. Scorer cites the top 3 CR comments in the evidence string when `cr_actionable_total > 0`.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Raw dumps (flat — files under `raw/` named `code-quality-*`)
|
|
223
|
+
|
|
224
|
+
Each signal writes its raw output verbatim to the flat `raw/` directory at the workspace root. Absent files = "not extracted" (tool missing / language not present). Aggregates land in `scorecard.yaml` under `data.`.
|
|
225
|
+
|
|
226
|
+
| File | Source signal | Shape / note |
|
|
227
|
+
| --- | --- | --- |
|
|
228
|
+
| `raw/code-quality-llm-sample.jsonl` | Signal 1 | 6 lines, one per sampled file: `{path, naming, api_shape, abstraction, modularity, smells, one_sentence}` |
|
|
229
|
+
| `raw/code-quality-lint-ruff.json` | Signal 2 | Raw `ruff check --output-format json` (Python only) |
|
|
230
|
+
| `raw/code-quality-lint-biome.json` | Signal 2 | Raw `biome ci --reporter json` (TS/JS preferred) |
|
|
231
|
+
| `raw/code-quality-lint-eslint.json` | Signal 2 | Raw `eslint --format json` (TS/JS fallback when biome rejects config) |
|
|
232
|
+
| `raw/code-quality-lint-golangci.json` | Signal 2 | Raw `golangci-lint run --out-format json ./...` (Go only) |
|
|
233
|
+
| `raw/code-quality-semgrep-quality.json` | Signal 5 | Post-filter semgrep output (resilience + security rules stripped) |
|
|
234
|
+
| `raw/code-quality-cr-comments.jsonl` | Signal 6 | One line per CR comment: `{pr, file, line, category, label, body_excerpt}`. Empty file when `cr_pr_count == 0` — still emit it (absence of file would be ambiguous with "scrape not run"). |
|
|
235
|
+
|
|
236
|
+
`raw/maintainability-lizard.csv` and `raw/maintainability-file-sizes.txt` are shared with 1.4 — read, don't duplicate.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Banding → 0–5
|
|
241
|
+
|
|
242
|
+
**Primary axis:** `llm_quality_score` (higher-is-better).
|
|
243
|
+
|
|
244
|
+
**Calibrating modifiers** (lower-is-better for all):
|
|
245
|
+
|
|
246
|
+
- `lint_findings_per_kloc`
|
|
247
|
+
- `lizard_god_per_kloc`
|
|
248
|
+
- `files_over_500_loc`
|
|
249
|
+
- `semgrep_quality_per_kloc`
|
|
250
|
+
- `cr_actionable_per_kloc` _(participates only when `cr_pr_count > 0`; null = no-op)_
|
|
251
|
+
- `cr_severe_per_kloc` _(participates only when `cr_pr_count > 0`; null = no-op)_
|
|
252
|
+
|
|
253
|
+
Absolute, per-signal thresholds. CR caps apply only when `cr_pr_count > 0` (null = no-op).
|
|
254
|
+
|
|
255
|
+
| Band | `llm_quality_score` | Density caps |
|
|
256
|
+
| --- | --- | --- |
|
|
257
|
+
| **5** | ≥ 4.5 | AND `lint_findings_per_kloc` ≤ 5 AND `lizard_god_per_kloc` ≤ 2 AND `files_over_500_loc` ≤ 1 AND (when non-null) `cr_severe_per_kloc` ≤ 0.5 |
|
|
258
|
+
| **4** | 3.5 – 4.5 | AND `lint_findings_per_kloc` ≤ 10 AND `lizard_god_per_kloc` ≤ 5 AND (when non-null) `cr_actionable_per_kloc` ≤ 5 |
|
|
259
|
+
| **3** | 2.5 – 3.5 | — |
|
|
260
|
+
| **2** | 1.5 – 2.5 | OR `lint_findings_per_kloc` > 20 OR `lizard_god_per_kloc` > 10 OR (when non-null) `cr_severe_per_kloc` ≥ 3 |
|
|
261
|
+
| **1** | < 1.5 | OR `files_over_500_loc` > 5 OR (when non-null) `cr_severe_per_kloc` ≥ 5 |
|
|
262
|
+
| **0** | no source code (empty repo) | — |
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Output (what the scorer emits)
|
|
267
|
+
|
|
268
|
+
```yaml
|
|
269
|
+
sub_criterion: code_quality
|
|
270
|
+
score: 4
|
|
271
|
+
evidence: "LLM sample (6 files) scored 4.2 — naming consistent, API signatures typed, one god-function in src/orchestrator.py flagged by lizard (CCN 18). ruff 3.2 findings/kLoC; semgrep 1.1 quality findings/kLoC; CodeRabbit 12 PRs, 4 actionables (refactor×3, nitpick×1) — 0.3/kLoC, no severe. No code smells beyond the one god-function."
|
|
272
|
+
data:
|
|
273
|
+
llm_quality_score: 4.2
|
|
274
|
+
llm_files_sampled:
|
|
275
|
+
[
|
|
276
|
+
src/orchestrator.py,
|
|
277
|
+
src/api/routes.py,
|
|
278
|
+
src/db.py,
|
|
279
|
+
src/worker.py,
|
|
280
|
+
internal/retry.py,
|
|
281
|
+
scripts/seed.py,
|
|
282
|
+
]
|
|
283
|
+
llm_notes: "..."
|
|
284
|
+
lint_findings_total: 42
|
|
285
|
+
lint_findings_per_kloc: 3.2
|
|
286
|
+
lint_by_severity: { error: 0, warning: 12, info: 30 }
|
|
287
|
+
lint_by_tool: { ruff: 42, biome: 0, eslint: 0, golangci: 0 }
|
|
288
|
+
lint_has_config_in_repo: true
|
|
289
|
+
lizard_mean_ccn: 4.1
|
|
290
|
+
lizard_god_per_kloc: 1.8
|
|
291
|
+
files_over_500_loc: 1
|
|
292
|
+
largest_file_loc: 580
|
|
293
|
+
mean_source_file_loc: 112
|
|
294
|
+
semgrep_quality_findings: 14
|
|
295
|
+
semgrep_quality_per_kloc: 1.1
|
|
296
|
+
semgrep_quality_by_severity: { ERROR: 0, WARNING: 4, INFO: 10 }
|
|
297
|
+
cr_pr_count: 12
|
|
298
|
+
cr_actionable_total: 4
|
|
299
|
+
cr_actionable_per_kloc: 0.3
|
|
300
|
+
cr_severe_count: 0
|
|
301
|
+
cr_severe_per_kloc: 0.0
|
|
302
|
+
cr_by_category:
|
|
303
|
+
{
|
|
304
|
+
refactor: 3,
|
|
305
|
+
nitpick: 1,
|
|
306
|
+
warning: 0,
|
|
307
|
+
bug: 0,
|
|
308
|
+
security: 0,
|
|
309
|
+
performance: 0,
|
|
310
|
+
uncategorized: 0,
|
|
311
|
+
}
|
|
312
|
+
signals_used: [llm, ruff, lizard, filesystem, semgrep, coderabbit]
|
|
313
|
+
scoring_method: llm_primary_with_mechanical_modifiers
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
When `cr_pr_count == 0` or all walkthroughs are actionable-free (the Free-plan default for most projects), emit:
|
|
317
|
+
|
|
318
|
+
```yaml
|
|
319
|
+
cr_pr_count: 12
|
|
320
|
+
cr_actionable_total: 0
|
|
321
|
+
cr_actionable_per_kloc: null
|
|
322
|
+
cr_severe_count: 0
|
|
323
|
+
cr_severe_per_kloc: null
|
|
324
|
+
cr_by_category: {}
|
|
325
|
+
signals_used: [llm, ruff, lizard, filesystem, semgrep, coderabbit_empty]
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
`null` values signal the scorer to skip the CR modifier for this project — banding leans on the remaining density signals.
|
|
329
|
+
|
|
330
|
+
If any required tool failed preflight:
|
|
331
|
+
|
|
332
|
+
```yaml
|
|
333
|
+
data:
|
|
334
|
+
lint_findings_total: null
|
|
335
|
+
lint_NOT_RUN: true
|
|
336
|
+
lint_reason: "preflight could not install ruff — host lacks compatible Python"
|
|
337
|
+
scoring_method: llm_primary_with_mechanical_modifiers_degraded
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Caveats
|
|
343
|
+
|
|
344
|
+
- **LLM sample size = 6 files.** Evidence must name every file sampled; the reader should be able to audit each axis score.
|
|
345
|
+
- **Lint tool coverage varies.** A TS repo without a `biome.json` or `.eslintrc` will emit different default rules than one with either — `lint_has_config_in_repo` surfaces the difference.
|
|
346
|
+
- **Mixed-language repos.** Sum findings across tools; normalise by `scc_code_loc` (not per-language LoC) to avoid double-normalising.
|
|
347
|
+
- **`lizard` CCN thresholds are language-idiomatic** — handled by per-language lookup (Py/JS/TS 10, Java 12, Go 15, other 15). See [maintainability.md → Signal 2](maintainability.md#signal-2--polyglot-complexity-lizard-per-language-ccn).
|
|
348
|
+
- **CodeRabbit Free plan** sparsity → `cr_*_per_kloc: null`, modifier no-op. Full rationale + plan-upgrade escape hatch in [`SKILL.md → CodeRabbit — calibrating modifier with null-data semantics`](../SKILL.md#coderabbit--calibrating-modifier-with-null-data-semantics).
|
|
349
|
+
- **`AI_AUTHORED_SUSPECTED` signal** (>50% added lines in commits > 500 LoC) remains useful — LLM reviewer should weigh LLM-generated code patterns in `llm_notes` (repetitive scaffolding, over-engineered edge-case branches, inconsistent naming within a single file, etc.).
|
|
350
|
+
- **No silent fallback.** If a tool is absent despite preflight, the signal is emitted as `_NOT_RUN` with `reason`. Grep-based substitutes break the absolute band thresholds and are banned.
|