slopbrick 0.11.0
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/LICENSE +21 -0
- package/README.md +902 -0
- package/bin/slopbrick.js +6 -0
- package/dist/engine/worker.cjs +7392 -0
- package/dist/engine/worker.js +7366 -0
- package/dist/index.cjs +21516 -0
- package/dist/index.d.cts +1289 -0
- package/dist/index.d.ts +1289 -0
- package/dist/index.js +21508 -0
- package/package.json +97 -0
package/README.md
ADDED
|
@@ -0,0 +1,902 @@
|
|
|
1
|
+
# slopbrick
|
|
2
|
+
|
|
3
|
+
> **The AI-coding-agent drift detector.** Stops the `zustand + redux in the same project`, the three-modal-systems, the off-scale `p-[13px]`, the hardcoded `sk-...` keys, and the `expect(x).toBeDefined()` tests that no one wrote and no one will run. Run `npx slopbrick` and get a single Repository Coherence score (0–100) with per-rule precision/recall so you know which findings to fix and which to ignore. Add the MCP server and your AI agent reads your existing patterns before it writes new ones.
|
|
4
|
+
|
|
5
|
+
**The problem.** AI coding assistants write logic well, but they drift. Every project ends up with three button variants, a hardcoded API key, inline styles next to Tailwind utilities, and a test file full of `expect(x).toBeDefined()`. The drift isn't the agent's fault — it's that the agent doesn't know your conventions. Existing linters catch syntax; nothing catches "you just invented a fourth modal system when this repo already has three."
|
|
6
|
+
|
|
7
|
+
**What this does.** `slopbrick` extracts the canonical patterns from your codebase (state lib, form lib, modal system, API client, data-fetching), enforces a declared Constitution at PR time, and exposes the pattern inventory to AI agents via MCP (`slop_suggest`) so they reuse what's there instead of inventing new patterns. The headline Repository Coherence score (0–100) is a proof that the Constitution is being followed — but the actual moat is the Constitution + Pattern Inventory itself, not the number.
|
|
8
|
+
|
|
9
|
+
## What an AI agent gets from `slop_suggest` (the primary entry point)
|
|
10
|
+
|
|
11
|
+
The MCP tool `slop_suggest` returns, in one call:
|
|
12
|
+
|
|
13
|
+
- **Existing patterns** — the canonical modal, button, API client, state library, data-fetching library the project already uses.
|
|
14
|
+
- **Do-not-create list** — explicit `constitution.forbidden` packages + canonical patterns not to duplicate.
|
|
15
|
+
- **Top issues by rule** — what to fix first in the changed files.
|
|
16
|
+
- **Hot files by issue count** — where the slop is concentrated.
|
|
17
|
+
- **Composite Repository Health** — the headline 0-100 score.
|
|
18
|
+
|
|
19
|
+
> **Call this BEFORE writing new code so the agent reuses existing patterns instead of duplicating them.**
|
|
20
|
+
|
|
21
|
+
`src/mcp/tools.ts:69-81`. The MCP server is one command: `slopbrick mcp`.
|
|
22
|
+
|
|
23
|
+
## Headline 5-bucket score (compressed from 13 subscores)
|
|
24
|
+
|
|
25
|
+
The full diagnostic surface is 13 subscores. The user-facing surface is 5 buckets:
|
|
26
|
+
|
|
27
|
+
| Bucket | What it measures | One-line question | v4.1 calibration |
|
|
28
|
+
|--------|------------------|-------------------|------------------|
|
|
29
|
+
| **Architecture Consistency** | Cross-file pattern duplication + token usage + component reuse | "Are components and patterns consistent?" | 8 USEFUL rules, top signal: Pattern Fragmentation |
|
|
30
|
+
| **AI Slop Signal** | Ghost defensive code, debug logs, unused state, missing auth, hardcoded secrets | "Does this look like AI wrote it?" | 18 USEFUL rules with measured P/R/FPR |
|
|
31
|
+
| **Security** | Hardcoded secrets, dangerous CORS, unsafe HTML, SQL concat, missing auth | "Are there security holes?" | 4 USEFUL, 3 INVERTED |
|
|
32
|
+
| **Delivery Quality** | Test quality, business-logic coherence, docs freshness | "Can the team ship safely?" | 4 PASS, 3 INVERTED |
|
|
33
|
+
| **Codebase Health** | DB schema, design-token drift, product consistency | "Will this codebase hold up at scale?" | 5 INVERTED/DORMANT — calibration pending |
|
|
34
|
+
|
|
35
|
+
`Repository Health` (the composite) = `0.25 × Architecture + 0.30 × AI Slop + 0.25 × Security + 0.10 × Delivery + 0.10 × Codebase`.
|
|
36
|
+
|
|
37
|
+
The 13-subscore diagnostic surface remains available behind `slopbrick scan --format json` and `--format detailed` for the calibration / power-user audience.
|
|
38
|
+
|
|
39
|
+
## Why this works (calibration evidence, v4.1)
|
|
40
|
+
|
|
41
|
+
The rules are calibrated against a balanced 1:1 corpus:
|
|
42
|
+
|
|
43
|
+
- **95,467 human-written frontend files** — 39 production repos (mui 16k, supabase 6.8k, antd 5.5k, storybook 3.5k, react-spectrum 3.3k, refine 6.3k, appsmith 5.5k, heroui 2.1k, …) + 54,980 from `ai-slop-baseline`.
|
|
44
|
+
- **76,981 AI-generated frontend files** — 50 existing repos + **100 NEW shallow-cloned vibe-coded repos** (Claude Code, Cursor, Lovable, Bolt, gpt-pilot, v0, BloopAI, tldraw) in `corpus-expansion/positive/vibe-coded/`.
|
|
45
|
+
|
|
46
|
+
The form engineers actually trust, per-rule (full table in [`docs/research/v4-per-rule-pr-fpr.md`](./docs/research/v4-per-rule-pr-fpr.md)):
|
|
47
|
+
|
|
48
|
+
> `security/missing-auth-check` fires on **0.63% of AI files** and **0.04% of human files**. When it fires, **92% of the time the file is AI**.
|
|
49
|
+
|
|
50
|
+
Top 5 AI signals (v4.1 P/R/FPR):
|
|
51
|
+
|
|
52
|
+
| Rule | Precision | Lift | Verdict |
|
|
53
|
+
|------|----------:|-----:|---------|
|
|
54
|
+
| `logic/ghost-defensive` (dead `if (x) return` guards) | 94.74% | 22.5× | USEFUL |
|
|
55
|
+
| `security/missing-auth-check` (auth bypass) | 92.47% | 15.3× | USEFUL |
|
|
56
|
+
| `logic/math-console-log-storm` (debug logs everywhere) | 89.84% | 11.0× | USEFUL |
|
|
57
|
+
| `logic/zombie-state` (unused `useState`) | 83.33% | 6.2× | USEFUL |
|
|
58
|
+
| `test/duplicate-setup` (`beforeEach` copy-paste) | 70.97% | 3.1× | USEFUL |
|
|
59
|
+
|
|
60
|
+
The 18 USEFUL rules have **per-file P/R/FPR thresholds** in `tests/integration/calibration-expanded.test.ts` that fail CI when the signal drifts. See [`docs/research/calibration-report-2026.md`](./docs/research/calibration-report-2026.md) for the full calibration trajectory (v1 ratio → v3 ratio → v4 ratio → **v4.1 P/R/FPR**).
|
|
61
|
+
|
|
62
|
+
## For humans
|
|
63
|
+
|
|
64
|
+
- `slopbrick scan` in CI gates PRs on `slopIndex` and the Constitution.
|
|
65
|
+
- `slopbrick pr` scores the PR itself, weighted by severity.
|
|
66
|
+
- `slopbrick architecture` is the headline consistency number.
|
|
67
|
+
- `slopbrick security` is the categorical security gate.
|
|
68
|
+
- `slopbrick test` / `business-logic` / `patterns` / `db` are specialised subcommands.
|
|
69
|
+
- `slopbrick drift` exits 1 on any Constitution violation.
|
|
70
|
+
- `slopbrick --format json` exposes the 13-subscore diagnostic surface + per-rule P/R/FPR for the calibration audience.
|
|
71
|
+
|
|
72
|
+
## For AI agents
|
|
73
|
+
|
|
74
|
+
Install the MCP server (`@slopbrick/mcp`), then call `slop_suggest` before writing new code. The agent never has to guess what's already in the codebase.
|
|
75
|
+
|
|
76
|
+
## What it does not do
|
|
77
|
+
|
|
78
|
+
It does **not** detect whether a human or AI wrote the code. It surfaces patterns that AI generates disproportionately (4 modal systems, exposed `NEXT_PUBLIC_OPENAI_API_KEY`, `if (NODE_ENV === 'development') return true`), and enforces the constitution the project has declared.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 13-score diagnostic surface (for the calibration audience)
|
|
83
|
+
|
|
84
|
+
The headline 5-bucket score is a compression of 13 subscores. The full surface is what `slopbrick scan --format json` returns, and what the [`tests/integration/calibration-expanded.test.ts`](./tests/integration/calibration-expanded.test.ts) test guards against drift.
|
|
85
|
+
|
|
86
|
+
**Tier 1 — Deterministic (Constitution enforcement):**
|
|
87
|
+
|
|
88
|
+
| Score | Shape | Use it for |
|
|
89
|
+
|-------|-------|------------|
|
|
90
|
+
| **Slop Index** | 0–100 | Frontend lint quality (composite) |
|
|
91
|
+
| **Architecture Consistency** | 0–100 | Cross-file pattern duplication |
|
|
92
|
+
| **AI Security Risk** | `low` / `medium` / `high` / `critical` | AI-induced security failures |
|
|
93
|
+
| **Constitution drift** | pass / fail | Imports that violate the declared stack |
|
|
94
|
+
| **Design-token drift** | inline violations | Spacing/radius off declared scales |
|
|
95
|
+
| **Pattern Fragmentation** | 0–100 | How many competing patterns per category (modal / auth / state / api / …) — *the input to `slop_suggest`'s `doNotCreate` list* |
|
|
96
|
+
| **PR Slop Score** | integer | One number per PR, weighted by severity |
|
|
97
|
+
|
|
98
|
+
**Tier 2 — Heuristic (specialised subcommands):**
|
|
99
|
+
|
|
100
|
+
| Score | Shape | Use it for |
|
|
101
|
+
|-------|-------|------------|
|
|
102
|
+
| **Test Quality** | 0–100 | AI test smells |
|
|
103
|
+
| **Business Logic Coherence** | 0–100 | Pricing precision, validation completeness |
|
|
104
|
+
| **Documentation Freshness** | 0–100 | Stale READMEs, drift between docs and code |
|
|
105
|
+
| **Database Health** | 0–100 | Missing indexes, N+1, soft-delete inconsistencies |
|
|
106
|
+
|
|
107
|
+
**Tier 3 — Derived (dashboards):**
|
|
108
|
+
|
|
109
|
+
| Score | Shape | Use it for |
|
|
110
|
+
|-------|-------|------------|
|
|
111
|
+
| **Repository Health** | 0–100 | Weighted average of the 5 buckets |
|
|
112
|
+
| **AI Maintenance Cost** | $/month | $ cost of fixing the issues, given velocity |
|
|
113
|
+
| **AI Debt band** | A / B / C / D / F | Letter grade from the above two |
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## What's new in 0.7.0
|
|
118
|
+
|
|
119
|
+
> **Repository Constitution Engine for AI Coding Agents.** The moat is the Constitution. Everything else is a score that proves it's being followed.
|
|
120
|
+
|
|
121
|
+
0.7.0 is the **Constitution-first release** — three new specialised subcommands (`test`, `business-logic`, `patterns`), a one-number-per-PR score (`pr`), the `forbidden` deny-list for explicit "never use this", and the rename from `conventions` to `constitution` everywhere.
|
|
122
|
+
|
|
123
|
+
The 0.6.x series built the foundation. The 0.7.x series turns the Constitution from a config field into a working contract — AI agents that read it via MCP (`slop_suggest`) and humans that enforce it via PR gates.
|
|
124
|
+
|
|
125
|
+
**What landed in 0.7.0:**
|
|
126
|
+
|
|
127
|
+
- `slopbrick pr` — one weighted number per PR, gates on `--threshold`
|
|
128
|
+
- `slopbrick test` — Test Quality score (0–100) + 4 `test/*` rules
|
|
129
|
+
- `slopbrick business-logic` — Business Logic Coherence score (0–100) + 8 rules
|
|
130
|
+
- `slopbrick patterns` — Pattern Fragmentation score (0–100), 8 categories
|
|
131
|
+
- `constitution.forbidden` — explicit deny-list (e.g. `['moment', 'lodash']`)
|
|
132
|
+
- `conventions` → `constitution` rename across config, types, MCP tool names, report columns
|
|
133
|
+
|
|
134
|
+
### 0.6.0 – 0.6.4 recap (the foundation)
|
|
135
|
+
|
|
136
|
+
0.6.0 was the engine re-architecture. The 0.6.1 – 0.6.4 patch series shifts the framing from "AI slop detector" to **repository coherence engine** — the same scanner, now with three new scores, MCP tools so AI agents check before they PR, and eight security rules for AI-induced failures.
|
|
137
|
+
|
|
138
|
+
Five orthogonal scores, all in `slopbrick scan`:
|
|
139
|
+
|
|
140
|
+
| Score | Shape | Use it for |
|
|
141
|
+
|-------|-------|------------|
|
|
142
|
+
| **Slop Index** | 0–100 | Frontend lint quality |
|
|
143
|
+
| **Architecture Consistency** | 0–100 | Cross-file pattern duplication *(0.6.3)* |
|
|
144
|
+
| **AI Security Risk** | `low` / `medium` / `high` / `critical` | AI-induced security failures *(0.6.4)* |
|
|
145
|
+
| **Constitution drift** | pass / fail | Imports that violate declared stack *(0.6.2)* |
|
|
146
|
+
| **Design-token drift** | inline violations | Spacing/radius off declared scales *(0.6.3)* |
|
|
147
|
+
|
|
148
|
+
The slop detector is still here — but the bigger lever is *coherence*: one modal system, one state library, one fetch lib, a declared constitution that the AI agent checks before PR.
|
|
149
|
+
|
|
150
|
+
### What landed in 0.6.1 – 0.6.4
|
|
151
|
+
|
|
152
|
+
**0.6.4 — AI Security Risk (new score) + 8 Tier-1/Tier-2 security rules**
|
|
153
|
+
|
|
154
|
+
NOT a security scanner — Semgrep / GHAS / CodeQL / Gitleaks own that. This is a **categorical** score (`low | medium | high | critical`) for security failures that AI generates disproportionately. Independent of `slopIndex` — security failures do not get diluted into "good slop score" territory.
|
|
155
|
+
|
|
156
|
+
- `security/hardcoded-secret` — provider prefixes (`sk-`, `sk-ant-`, `AKIA`, `ghp_`, `sk_live_`, `AIza`, `xox[abprs]-`) + sensitive-name literals.
|
|
157
|
+
- `security/exposed-env-var` — `NEXT_PUBLIC_*` / `VITE_*` / etc. with secret names — inlined into every browser build.
|
|
158
|
+
- `security/dangerous-cors` — wildcard `Access-Control-Allow-Origin: *` + `cors({ origin: '*' })` + reflective `cors({ origin: true })`.
|
|
159
|
+
- `security/missing-auth-check` — Next.js `route.ts` / `pages/api` / Express handlers with no auth primitive.
|
|
160
|
+
- `security/unsafe-html-render` — `dangerouslySetInnerHTML` fed a non-literal value.
|
|
161
|
+
- `security/fail-open-auth` — `if (NODE_ENV === 'development') return true/next()`.
|
|
162
|
+
- `security/sql-construction` — template-literal / concat SQL queries (use parameterized queries).
|
|
163
|
+
- `security/public-admin-route` — routes under `/admin`, `/internal`, `/debug`, `/staff`, `/manage`, `/private`, etc. without an additional role check.
|
|
164
|
+
|
|
165
|
+
New `slopbrick security [--format pretty|json] [--strict]` subcommand. `--strict` exits 1 on high/critical (CI gate).
|
|
166
|
+
|
|
167
|
+
**0.6.3 — Architecture Consistency Score (the headline metric) + design-token enforcement**
|
|
168
|
+
|
|
169
|
+
One 0–100 number that reflects how consistent a repository's patterns are. Subtracts from 100 for each pattern-duplication finding: `-12` per extra modal system, `-8` per extra button variant, `-10` per extra API client module, `-15` per extra state library (highest), `-10` per extra data-fetching library, `-1` per 5 off-scale spacing values, `-1` per 5 off-scale radius values. A project with 1 modal, 1 button, 1 api client, 1 state lib, 1 fetch lib lands at 100. A project with 3 modal systems + 4 button variants + 2 state libs lands at 37.
|
|
170
|
+
|
|
171
|
+
Two new rules turn design tokens from docs into enforceable contracts:
|
|
172
|
+
- `visual/spacing-scale-violation` — flags `p-[13px]`, `gap-[1.75rem]` etc. off the declared `spacingScale`.
|
|
173
|
+
- `visual/radius-scale-violation` — flags `rounded-[7px]`, `rounded-tl-[2rem]` etc. off the declared `radiusScale`.
|
|
174
|
+
|
|
175
|
+
Both emit auto-fix candidates so `slopbrick scan --fix` rewrites `p-[13px]` → `p-1`.
|
|
176
|
+
|
|
177
|
+
**0.6.2 — Repository governance for AI coding agents**
|
|
178
|
+
|
|
179
|
+
The single feature most projects asked for. New top-level `constitution` field in `slopbrick.config.mjs`:
|
|
180
|
+
|
|
181
|
+
```js
|
|
182
|
+
export default {
|
|
183
|
+
constitution: {
|
|
184
|
+
stateManagement: ['zustand'],
|
|
185
|
+
dataFetching: ['react-query'],
|
|
186
|
+
uiLibrary: ['shadcn', 'radix'],
|
|
187
|
+
forms: ['react-hook-form', 'zod'],
|
|
188
|
+
styling: ['tailwind'],
|
|
189
|
+
routing: ['next'],
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Auto-detected from `package.json` when unset; user declarations always win.
|
|
195
|
+
|
|
196
|
+
- `slopbrick drift` — CLI command, exits 1 on any violation (CI-friendly).
|
|
197
|
+
- `slop_suggest` MCP tool — project-wide inventory of existing patterns; AI agents call before writing new code.
|
|
198
|
+
- `slop_check_constitution` MCP tool — per-file constitution diff.
|
|
199
|
+
- `slop_architecture_score` MCP tool — Architecture Consistency Score via MCP.
|
|
200
|
+
|
|
201
|
+
**0.6.1 — bug fixes + small refinements**
|
|
202
|
+
|
|
203
|
+
- `slopbrick trend --format markdown` now actually emits markdown (the local flag was being shadowed by the global scan `--format`; renamed to `--render`).
|
|
204
|
+
- Calibration test surfaces stderr/stdout on chunk failures instead of swallowing them.
|
|
205
|
+
- v1.x working-tree labels stripped.
|
|
206
|
+
|
|
207
|
+
### CLI surface summary (post-0.8.0)
|
|
208
|
+
|
|
209
|
+
| Command | Purpose |
|
|
210
|
+
|---------|---------|
|
|
211
|
+
| `slopbrick scan` | Main scan — runs all rules + computes all 8 scores |
|
|
212
|
+
| `slopbrick architecture` | Architecture Consistency Score only |
|
|
213
|
+
| `slopbrick security` | AI Security Risk only |
|
|
214
|
+
| `slopbrick drift` | Constitution-violation scanner |
|
|
215
|
+
| `slopbrick pr` | PR slop score (single weighted number per PR) |
|
|
216
|
+
| `slopbrick test` | Test Quality score (4 `test/*` rules) |
|
|
217
|
+
| `slopbrick business-logic` | Business Logic Coherence score (8 rules) |
|
|
218
|
+
| `slopbrick patterns` | Pattern Fragmentation score (input to `slop_suggest`) |
|
|
219
|
+
| `slopbrick maintenance-cost` | AI Maintenance Cost (categorical low/medium/high/critical + $/month) *(0.8.0)* |
|
|
220
|
+
| `slopbrick docs` | Documentation Freshness (4 `docs/*` rules) *(0.8.0)* |
|
|
221
|
+
| `slopbrick db` | Database Health (6 `db/*` rules, Postgres-only) *(0.8.0)* |
|
|
222
|
+
| `slopbrick mcp` | MCP server (`slop_scan_file`, `slop_explain_rule`, `slop_list_rules`, `slop_suggest`, `slop_check_constitution`, `slop_architecture_score`) |
|
|
223
|
+
| `slopbrick trend` | Slop Index trend over time |
|
|
224
|
+
| `slopbrick flywheel` | Aggregated scan telemetry |
|
|
225
|
+
| `slopbrick init` | Interactive setup wizard |
|
|
226
|
+
|
|
227
|
+
### What 0.6.x did *not* change
|
|
228
|
+
|
|
229
|
+
- **No new competitor overlap.** We did not add a general security scanner, dependency vulnerability scanner, formatter, type checker, or coverage tool.
|
|
230
|
+
- **No breaking CLI changes.** Existing scan commands, JSON / SARIF / HTML output formats, and public-API exports are unchanged.
|
|
231
|
+
|
|
232
|
+
The full release history is in [CHANGELOG.md](./CHANGELOG.md).
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Why this matters (research-backed)
|
|
237
|
+
|
|
238
|
+
The 0.7.0 release sits on top of an industry that's converging fast on AI-generated-code debt. The numbers below are from 2024–2026 studies and explain why the Constitution, not the Slop Index, is the moat.
|
|
239
|
+
|
|
240
|
+
- **AI slows experienced developers.** METR's July 2025 RCT (16 experienced open-source devs, 246 tasks on repos averaging 22k stars / 1M LoC) found AI tools produced a **19% slowdown** — developers had expected a 24% speedup. ([METR, 2025](https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/))
|
|
241
|
+
- **AI-generated code carries 1.7× more issues per PR** (10.83 vs 6.45) and a higher share of critical/major issues. ([CodeRabbit, 2025](https://coderabbit.ai/blog/state-of-ai-vs-human-code-generation-report))
|
|
242
|
+
- **Refactoring is collapsing.** GitClear's 211M-line analysis of Google/Microsoft/Meta repos shows "refactored" lines fell from 25% → <10% and "copy-pasted" lines rose from 8.3% → 12.3% between 2021–2024. ([GitClear, 2025](https://www.gitclear.com/ai_assistant_code_quality_2025_research))
|
|
243
|
+
- **PR size is up 51%, bugs/PR up 28%, incidents/PR up 3×, code churn up 10×** across 22k developers in 2026. ([Faros AI, 2026](https://www.faros.ai/research/ai-acceleration-whiplash))
|
|
244
|
+
- **Trust in AI accuracy dropped from 40% → 29%** in one year; 66% of devs spend *more* time debugging AI output. ([Stack Overflow 2025 Developer Survey](https://survey.stackoverflow.co/2025/ai))
|
|
245
|
+
- **Code-surface ↔ doc-surface staleness is an open hole.** No shipped tool (Docusaurus, Mintlify, GitBook, mkdocstrings, TypeDoc) cross-references `package.json` ↔ README or exported names ↔ markdown inline code. The 2026 state-of-the-art is F1 = 96.73% on a single analog task (description-code inconsistency, [arXiv 2606.04769](https://arxiv.org/html/2606.04769v1)). That's what `slopbrick docs` ships against in 0.8.0.
|
|
246
|
+
- **Schema-quality static analysis is an open hole for Drizzle.** The official `eslint-plugin-drizzle` has exactly 2 rules. Prisma has 8+ Prisma-Lens rules but they target per-file linting, not schema-wide drift. Squawk owns migration safety; nobody owns schema quality. That's the wedge for `slopbrick db` in 0.8.0.
|
|
247
|
+
- **The canonical AI-coding-agent failure is the AWS Kiro outage (Dec 2025).** An agentic coding tool autonomously deleted a production environment; 13-hour outage in a China region. ([Docker blog, 2026](https://www.docker.com/blog/coding-agent-horror-stories-the-13-hour-aws-outage/)) The post-mortem: "predictable given unchecked AI permissions." The preventive: a Constitution the agent checks before it acts, with a `$306,000/yr/MLoC` baseline for what the debt costs when it isn't checked. ([Sonar, 2025](https://www.sonarsource.com/blog/new-research-from-sonar-on-cost-of-technical-debt/))
|
|
248
|
+
|
|
249
|
+
The full research notes for each 0.8.0 phase are in [`docs/research/`](./docs/research/).
|
|
250
|
+
|
|
251
|
+
**Mathematical foundations** — the peer-reviewed methods behind every threshold:
|
|
252
|
+
[`docs/research/math-foundations-for-slopbrick.md`](./docs/research/math-foundations-for-slopbrick.md) maps 8 published results (Halstead 1977, Hindle 2012, Rissanen 1978, Kullback-Leibler 1951, Blondel 2008, Fiedler 1973, McCabe 1976, Adams-MacKay 2007) to the slopbrick rules and composite scores that cite them. v0.9.3+ ships the highest-leverage ones (Halstead, Code Naturalness, MDL composite) to replace heuristic thresholds with closed-form citations.
|
|
253
|
+
|
|
254
|
+
**v0.10 implementation plan** — the credibility-milestone roadmap with dependency graph, effort estimates per phase, and readiness checklist: [`docs/research/v0.10-implementation-plan.md`](./docs/research/v0.10-implementation-plan.md). Phases 1–5 (~4 working days) ship v0.10; Phases 6–11 land the far-horizon graph-theoretic, Repository Memory, `--diff`, `find_similar_function`, BRICK, and SARIF work.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Roadmap
|
|
259
|
+
|
|
260
|
+
| Version | Themes | Status |
|
|
261
|
+
|---------|--------|--------|
|
|
262
|
+
| 0.5.x | Engine re-architecture, Slop Index, framework support | Shipped |
|
|
263
|
+
| 0.6.x | Constitution, Architecture Consistency, AI Security Risk, design-token enforcement | Shipped |
|
|
264
|
+
| 0.7.0 | Constitution rename + `forbidden` deny-list, `pr` subcommand, Test / Business-Logic / Patterns subcommands | Shipped 2026-06-25 |
|
|
265
|
+
| 0.8.0 | `docs` (Doc Drift), `db` (Database Health, Postgres-static), `maintenance-cost` ($/month categorical) | Shipped 2026-07-15 |
|
|
266
|
+
| **0.9.0** | **Repository Coherence Scanner reframe, default-off INVERTED + NOISY rules, expanded `slop_suggest`, new `slop_governance` MCP tool** | **Shipped 2026-08-15** |
|
|
267
|
+
| **0.10** | **Credibility milestone: per-rule P/R/FPR + peer-reviewed thresholds (Halstead, McCabe, Hindle, Rissanen, Kullback-Leibler); MDL composite replaces heuristic weights** | **In flight — see [`docs/research/v0.10-implementation-plan.md`](./docs/research/v0.10-implementation-plan.md)** |
|
|
268
|
+
| 1.0 | Stability commitment — 6+ months post-v0.10 empirical feedback; freezes the surface, no new scores | Far horizon |
|
|
269
|
+
|
|
270
|
+
Per-version research notes:
|
|
271
|
+
- [Phase 6 — Doc Drift](./docs/research/phase-6-doc-drift-internet-2026.md)
|
|
272
|
+
- [Phase 8 — DB Health](./docs/research/phase-8-db-health-internet-2026.md)
|
|
273
|
+
- [Memo #4 — AI Maintenance Cost](./docs/research/phase-memo4-ai-cost-internet-2026.md)
|
|
274
|
+
- [Math foundations — peer-reviewed methods for v0.9.3+ rules](./docs/research/math-foundations-for-slopbrick.md)
|
|
275
|
+
- [v0.10 implementation plan — credibility milestone roadmap](./docs/research/v0.10-implementation-plan.md)
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Installation
|
|
280
|
+
|
|
281
|
+
Run once without installing:
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
npx slopbrick
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Add to a project as a dev dependency:
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
pnpm add -D slopbrick
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Quick start
|
|
296
|
+
|
|
297
|
+
Initialize a config in the project root:
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
npx slopbrick init
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Scan the current workspace:
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
npx slopbrick scan
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Or scan specific paths:
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
npx slopbrick scan src app
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
On first run, `slopbrick` auto-detects your framework, styling solution, UI libraries (Tailwind, Tamagui, shadcn/ui, MUI, etc.), and workspace packages. Framework presets automatically disable or downgrade rules that are idiomatic for React Native, Expo, or Tamagui.
|
|
316
|
+
|
|
317
|
+
### Don't want to write a config from scratch?
|
|
318
|
+
|
|
319
|
+
Four ready-to-use starter configs live in [`examples/`](./examples):
|
|
320
|
+
|
|
321
|
+
- [`examples/basic/`](./examples/basic) — sensible defaults for most projects
|
|
322
|
+
- [`examples/strict/`](./examples/strict) — CI gating with `noIncrease` baseline
|
|
323
|
+
- [`examples/monorepo/`](./examples/monorepo) — pnpm/turbo workspaces
|
|
324
|
+
- [`examples/ci/`](./examples/ci) — JSON + SARIF output for code-scanning upload
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
cp examples/strict/slopbrick.config.mjs ./slopbrick.config.mjs
|
|
328
|
+
npx slopbrick validate-config # check it before running a scan
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
See [`examples/README.md`](./examples/README.md) for the full walkthrough.
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Configuration
|
|
336
|
+
|
|
337
|
+
Config lives at `slopbrick.config.mjs` in the project root. It is an ES module that exports a default object.
|
|
338
|
+
|
|
339
|
+
```js
|
|
340
|
+
export default {
|
|
341
|
+
include: ['src/**/*', 'app/**/*', 'pages/**/*', 'components/**/*'],
|
|
342
|
+
exclude: [
|
|
343
|
+
'**/node_modules/**',
|
|
344
|
+
'**/*.test.{ts,tsx,js,jsx}',
|
|
345
|
+
'**/*.stories.{ts,tsx}',
|
|
346
|
+
'**/.next/**',
|
|
347
|
+
'**/dist/**',
|
|
348
|
+
'**/build/**',
|
|
349
|
+
'**/coverage/**',
|
|
350
|
+
],
|
|
351
|
+
|
|
352
|
+
// Per-category weight multiplier
|
|
353
|
+
categoryWeights: {
|
|
354
|
+
visual: 1.2,
|
|
355
|
+
logic: 1.0,
|
|
356
|
+
perf: 0.8,
|
|
357
|
+
typo: 0.5,
|
|
358
|
+
wcag: 1.0,
|
|
359
|
+
layout: 1.0,
|
|
360
|
+
component: 1.0,
|
|
361
|
+
arch: 1.0,
|
|
362
|
+
security: 1.0,
|
|
363
|
+
},
|
|
364
|
+
|
|
365
|
+
// CI threshold (Phase 2 §10: composite Slop Index only)
|
|
366
|
+
thresholds: {
|
|
367
|
+
meanSlop: 30,
|
|
368
|
+
},
|
|
369
|
+
|
|
370
|
+
// Rule severity overrides.
|
|
371
|
+
// 'auto' keeps the rule's natural severity; 'off' disables it.
|
|
372
|
+
rules: {
|
|
373
|
+
'visual/inline-style': 'auto',
|
|
374
|
+
'visual/hardcoded-color': 'low',
|
|
375
|
+
'logic/style-sheet-avoidance': 'medium',
|
|
376
|
+
},
|
|
377
|
+
|
|
378
|
+
// Boost or reduce scores for specific frameworks
|
|
379
|
+
frameworkMultipliers: {
|
|
380
|
+
astro: 0.8,
|
|
381
|
+
},
|
|
382
|
+
|
|
383
|
+
// Phase 2 §10: brick.config.json import paths. Defaults to common
|
|
384
|
+
// shadcn-style paths. Imports from `@/components/*` not matching
|
|
385
|
+
// these prefixes are flagged by `context/import-path-mismatch`.
|
|
386
|
+
allowedImports: [
|
|
387
|
+
'@/components/ui/',
|
|
388
|
+
'@/components/',
|
|
389
|
+
'@/lib/',
|
|
390
|
+
'@/hooks/',
|
|
391
|
+
],
|
|
392
|
+
};
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Key options
|
|
396
|
+
|
|
397
|
+
| Option | What it does |
|
|
398
|
+
|--------|--------------|
|
|
399
|
+
| `include` | File patterns to scan (default: all source files) |
|
|
400
|
+
| `exclude` | File patterns to skip |
|
|
401
|
+
| `categoryWeights` | Make certain issue types count more or less |
|
|
402
|
+
| `thresholds` | CI gates — see "Thresholds" below |
|
|
403
|
+
| `rules` | Turn specific rules off or change their severity |
|
|
404
|
+
| `frameworkMultipliers` | Boost/reduce scores for specific frameworks |
|
|
405
|
+
| `arbitraryValueAllowlist` | Tailwind values that are OK to use |
|
|
406
|
+
| `allowedImports` | brick.config.json import paths (Phase 2 §10) |
|
|
407
|
+
| `wcag` | Accessibility-specific settings |
|
|
408
|
+
|
|
409
|
+
---
|
|
410
|
+
|
|
411
|
+
## Composite Slop Index (Phase 2 §10)
|
|
412
|
+
|
|
413
|
+
slopbrick produces a single composite score that prioritizes structural integrity over minor visual escapes:
|
|
414
|
+
|
|
415
|
+
```
|
|
416
|
+
S = (0.40 × S_boundary) + (0.35 × S_context) + (0.25 × S_visual)
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Each subscore is `min(100, severityPoints / componentCount)`, where `severityPoints` is the sum of severity weights for issues in that bucket.
|
|
420
|
+
|
|
421
|
+
**Bucket weights:**
|
|
422
|
+
|
|
423
|
+
| Bucket | Weight | What it measures |
|
|
424
|
+
|--------|-------:|------------------|
|
|
425
|
+
| **Boundary** | 40% | Structural integrity: file-size limits, multiple components per file, direct API calls in UI |
|
|
426
|
+
| **Context** | 35% | Prop correctness, imports, state management |
|
|
427
|
+
| **Visual** | 25% | CSS, layout, typography, accessibility |
|
|
428
|
+
|
|
429
|
+
**Rule → Subscore mapping:**
|
|
430
|
+
|
|
431
|
+
- **Boundary (40%)**: `logic/boundary-violation`, `component/giant-component`, `component/multiple-components-per-file`
|
|
432
|
+
- **Context (35%)**: `component/shadcn-prop-mismatch`, `arch/astro-island-leak`, `context/import-path-mismatch`, most `logic/*`
|
|
433
|
+
- **Visual (25%)**: all `visual/*`, `layout/*`, `typo/*`, `wcag/*`, `perf/*`
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
## CLI reference
|
|
438
|
+
|
|
439
|
+
```text
|
|
440
|
+
Usage: slopbrick [options] [command]
|
|
441
|
+
|
|
442
|
+
Options:
|
|
443
|
+
-V, --version output the version number
|
|
444
|
+
--framework <name> framework multiplier to apply
|
|
445
|
+
--include <glob> include pattern (repeatable)
|
|
446
|
+
--exclude <glob> exclude pattern (repeatable)
|
|
447
|
+
--ai-only only report AI-specific issues
|
|
448
|
+
--human-only only report human-facing issues
|
|
449
|
+
--ignore-wcag22 ignore WCAG 2.2 related issues
|
|
450
|
+
--format <pretty|json|sarif|html> output format (default: "pretty")
|
|
451
|
+
--threads <n> number of worker threads
|
|
452
|
+
--since <ref> only scan files changed since git ref
|
|
453
|
+
--workspace <path> workspace/project path
|
|
454
|
+
--tighten tighten baseline allowances
|
|
455
|
+
--fix apply auto-fixes
|
|
456
|
+
--dry-run preview fixes without writing any files
|
|
457
|
+
--diff print unified diff of proposed auto-fixes
|
|
458
|
+
--doctor run diagnostics
|
|
459
|
+
--watch watch files and re-run
|
|
460
|
+
--suggest print remediation advice
|
|
461
|
+
--heatmap print migration ROI heatmap
|
|
462
|
+
--quiet suppress non-error output
|
|
463
|
+
--strict exit 2 if any high-severity issue remains
|
|
464
|
+
--no-increase exit 2 if slop index increased since last run
|
|
465
|
+
--auto-disable-noisy-rules downgrade rules whose measured precision < 0.5
|
|
466
|
+
or recall < 0.1 by one severity step
|
|
467
|
+
--baseline save a baseline after this scan
|
|
468
|
+
--trend [n] print a sparkline of the last n runs
|
|
469
|
+
--json [path] write JSON report to path or stdout
|
|
470
|
+
--html [path] write HTML report to path or stdout
|
|
471
|
+
--staged scan only staged files
|
|
472
|
+
--changed scan working-tree changes
|
|
473
|
+
--incremental skip unchanged files via content-hash cache
|
|
474
|
+
--cache-path <path> path to the incremental cache (default: .slop-audit-cache.json)
|
|
475
|
+
--tokens <path> merge tokens.json layout values into the
|
|
476
|
+
arbitrary-value allowlist
|
|
477
|
+
--cache cache parsed AST results locally
|
|
478
|
+
--rule <ruleId> run a single rule by id, skip all others
|
|
479
|
+
-h, --help display help for command
|
|
480
|
+
|
|
481
|
+
Commands:
|
|
482
|
+
init [options] create a slopbrick config file
|
|
483
|
+
install install the git pre-commit hook
|
|
484
|
+
uninstall uninstall the git pre-commit hook
|
|
485
|
+
badge print a shields.io slop-index badge
|
|
486
|
+
suggest print remediation advice
|
|
487
|
+
flywheel [options] summarize aggregated scan telemetry
|
|
488
|
+
scan [paths...] scan files for slop
|
|
489
|
+
explain <ruleId> print rationale, pattern, and remediation for a single rule
|
|
490
|
+
tokens <path> ingest a W3C DTCG tokens.json and summarize it by category
|
|
491
|
+
report <path> re-render a saved JSON report (from --json path.json)
|
|
492
|
+
doctor check your setup, config, and environment for common problems
|
|
493
|
+
rules [--category <name>] [--ai-only] [--json]
|
|
494
|
+
list all built-in rules with descriptions
|
|
495
|
+
mcp run an MCP server (for AI agents)
|
|
496
|
+
help [command] display help for command
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### pr — score a pull request
|
|
500
|
+
|
|
501
|
+
`slopbrick pr` runs the engine over the files changed between two
|
|
502
|
+
git refs and returns a single weighted slop score. The score is
|
|
503
|
+
`sum(SEVERITY_WEIGHTS[issue.severity]) + constitution_violations`
|
|
504
|
+
per file, summed across the diff. With the default threshold of
|
|
505
|
+
`20` (configurable via `prScoreThreshold` or `--threshold`), a PR
|
|
506
|
+
can introduce roughly 4 medium-severity issues before it fails.
|
|
507
|
+
|
|
508
|
+
```bash
|
|
509
|
+
slopbrick pr [--base <ref>] [--head <ref>]
|
|
510
|
+
[--format text|json|markdown]
|
|
511
|
+
[--threshold <n>] [--max-files <n>]
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
Defaults: `--base main` (falls back to `master` then the first
|
|
515
|
+
commit), `--head HEAD`, `--format text`, `--threshold 20`,
|
|
516
|
+
`--max-files 500`. The diff is computed with three-dot syntax
|
|
517
|
+
(`git diff --name-only base...head`), which matches GitHub's PR
|
|
518
|
+
view (merge-base comparison).
|
|
519
|
+
|
|
520
|
+
```text
|
|
521
|
+
$ slopbrick pr
|
|
522
|
+
PR score: 4 (threshold: 20) — PASS
|
|
523
|
+
Base: main Head: HEAD
|
|
524
|
+
Files changed: 1
|
|
525
|
+
|
|
526
|
+
src/store.ts issues=1 constitution=1 score=4
|
|
527
|
+
[medium ] security/public-admin-route — line 1
|
|
528
|
+
[forbidden] Constitution violation: … imports 'redux' (canonical: 'redux').
|
|
529
|
+
|
|
530
|
+
────────────────────────────────────────────
|
|
531
|
+
PR score: 4 / 20 threshold — PASS
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
Use it as a CI gate:
|
|
535
|
+
|
|
536
|
+
```yaml
|
|
537
|
+
- run: npx slopbrick pr --threshold 10
|
|
538
|
+
# exits 1 when PR score > 10
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
### Exit codes
|
|
542
|
+
|
|
543
|
+
| Code | Meaning |
|
|
544
|
+
|------|---------|
|
|
545
|
+
| `0` | Composite Slop Index under `meanSlop` threshold |
|
|
546
|
+
| `1` | Composite Slop Index exceeds threshold |
|
|
547
|
+
| `2` | High-severity issues with `--strict`, score regression with `--no-increase`, or hook install failure |
|
|
548
|
+
| `3` | Unexpected scan error (parser crash, worker failure after retries) |
|
|
549
|
+
|
|
550
|
+
---
|
|
551
|
+
|
|
552
|
+
## Example terminal output
|
|
553
|
+
|
|
554
|
+
```text
|
|
555
|
+
$ npx slopbrick scan
|
|
556
|
+
Scanned 312 files, 501 components, 1423 issues (high: 48, medium: 321, low: 1054)
|
|
557
|
+
|
|
558
|
+
Slop Index: 14.2 / 100 [PASS]
|
|
559
|
+
(Phase 2 §10 composite: 0.40 × Boundary + 0.35 × Context + 0.25 × Visual)
|
|
560
|
+
├─ Boundary Slop: 12.5 (Weighted: 5.0)
|
|
561
|
+
├─ Context Slop: 4.0 (Weighted: 1.4)
|
|
562
|
+
└─ Visual Slop: 31.0 (Weighted: 7.8)
|
|
563
|
+
|
|
564
|
+
Top offending components
|
|
565
|
+
72.3 src/app/(tabs)/keepsakes.tsx
|
|
566
|
+
65.1 src/app/(tabs)/search.tsx
|
|
567
|
+
58.0 src/app/child/[id]/edit.tsx
|
|
568
|
+
...
|
|
569
|
+
|
|
570
|
+
Thresholds
|
|
571
|
+
|
|
572
|
+
Composite Slop Index 14.2 ≤ 30 pass
|
|
573
|
+
|
|
574
|
+
All thresholds passed.
|
|
575
|
+
|
|
576
|
+
Issues (1423)
|
|
577
|
+
[HIGH ] logic/boundary-violation · src/app/(tabs)/keepsakes.tsx:91:22
|
|
578
|
+
Data layer mixed with UI component
|
|
579
|
+
→ Move fetch/state into a server action or hook.
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
## How scoring works
|
|
585
|
+
|
|
586
|
+
### Severity weights
|
|
587
|
+
|
|
588
|
+
| Severity | Weight |
|
|
589
|
+
|----------|-------:|
|
|
590
|
+
| high | 5 |
|
|
591
|
+
| medium | 3 |
|
|
592
|
+
| low | 1 |
|
|
593
|
+
|
|
594
|
+
(The `critical` tier was removed during the scoring-model refactor to prevent scoring inflation.)
|
|
595
|
+
|
|
596
|
+
### Per-file scoring
|
|
597
|
+
|
|
598
|
+
For each file, the engine:
|
|
599
|
+
1. Parses the source (SWC for TS/JS, regex for HTML, dedicated parsers for Vue/Svelte/Astro).
|
|
600
|
+
2. Walks the AST to extract facts: imports, JSX elements, class names, inline styles, hooks, state bindings, etc.
|
|
601
|
+
3. Runs each of the 42 registered rules against the facts.
|
|
602
|
+
4. Each rule returns 0+ issues with severity, line, column, and optional fix suggestions.
|
|
603
|
+
|
|
604
|
+
### Project scoring (Phase 2 §10)
|
|
605
|
+
|
|
606
|
+
1. **Bucket** every issue into one of three subscores (boundary, context, visual) using the rule-to-bucket map.
|
|
607
|
+
2. **Sum** severity weights per bucket: `bucketPoints[b] = Σ SEVERITY_WEIGHTS[issue.severity]`.
|
|
608
|
+
3. **Normalize**: `subscore[b] = min(100, bucketPoints[b] / componentCount × 100)`.
|
|
609
|
+
4. **Composite**: `slopIndex = 0.40 × boundary + 0.35 × context + 0.25 × visual`.
|
|
610
|
+
5. **Health**: `assemblyHealth = max(0, 100 - slopIndex)`.
|
|
611
|
+
|
|
612
|
+
### Threshold
|
|
613
|
+
|
|
614
|
+
A single threshold (`meanSlop`) gates the exit code: `slopIndex > meanSlop` → exit 1.
|
|
615
|
+
|
|
616
|
+
---
|
|
617
|
+
|
|
618
|
+
## Architecture
|
|
619
|
+
|
|
620
|
+
### High-level pipeline
|
|
621
|
+
|
|
622
|
+
```
|
|
623
|
+
┌────────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
624
|
+
│ CLI bin/ │───▶│ discover │───▶│ parser │───▶│ visitor │───▶│ rules │
|
|
625
|
+
│ slopbrick │ │ discover │ │ engine/ │ │ engine/ │ │ rules/ │
|
|
626
|
+
│ .js │ │ .ts │ │ parser.ts│ │ visitor.ts│ │ *.ts │
|
|
627
|
+
└────────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
|
|
628
|
+
│
|
|
629
|
+
▼
|
|
630
|
+
┌────────────┐ ┌──────────────┐ ┌────────────────┐ ┌─────────────────┐
|
|
631
|
+
│ Output │◀───│ aggregate │◀───│ report │◀───│ ProjectReport │
|
|
632
|
+
│ report/ │ │ metrics │ │ ProjectReport│ │ (per-issue) │
|
|
633
|
+
│ *.ts │ │ metrics.ts │ │ │ │ │
|
|
634
|
+
└────────────┘ └──────────────┘ └────────────────┘ └─────────────────┘
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
### End-to-end flow (`slopbrick scan`)
|
|
638
|
+
|
|
639
|
+
1. **CLI entry** (`bin/slopbrick.js`)
|
|
640
|
+
|
|
641
|
+
Loads `dist/index.js` (the bundled CLI), parses command-line flags with Commander, and resolves `cwd` from `--workspace` or `process.cwd()`.
|
|
642
|
+
|
|
643
|
+
2. **Config loading** (`src/config.ts`)
|
|
644
|
+
|
|
645
|
+
Walks up from cwd looking for `slopbrick.config.{js,mjs,cjs,ts}`, merges the user config with `DEFAULT_CONFIG` (which sets `thresholds`, `rules`, `allowedImports`), and validates the merged result against the schema.
|
|
646
|
+
|
|
647
|
+
3. **File discovery** (`src/discover.ts`)
|
|
648
|
+
|
|
649
|
+
Uses `globby` to expand `include` patterns and `minimatch` to apply `exclude`. For files without an extension, it reads the first 512 bytes and sniffs the content type (TSX/TS/JSX/JS/Vue/Svelte/Astro/HTML). It de-duplicates by basename when both extension-less and proper-extension versions exist, then filters by `SOURCE_EXTENSIONS` (`.ts`, `.tsx`, `.js`, `.jsx`, `.vue`, `.svelte`, `.astro`, `.html`).
|
|
650
|
+
|
|
651
|
+
4. **Per-file scanning** (`src/engine/worker.ts`, `src/engine/parser.ts`, `src/engine/visitor.ts`)
|
|
652
|
+
|
|
653
|
+
Runs in worker threads (configurable via `--threads`).
|
|
654
|
+
|
|
655
|
+
The **parser** dispatches based on file extension: SWC for TS/JS, dedicated handlers for Vue/Svelte/Astro, regex for HTML. Extension-less files try TSX → TS → JSX → JS in order.
|
|
656
|
+
|
|
657
|
+
The **visitor** walks the AST and extracts a `ScanFacts` summary, including:
|
|
658
|
+
- `imports[]` — `{source, importedNames, line, column}`
|
|
659
|
+
- `interactiveElements[]` — JSX `<button>`, `<a>`, `<input>`, etc. with attributes
|
|
660
|
+
- `staticClassNames[]` — className string literals
|
|
661
|
+
- `styleProps[]` — inline `style={{...}}` props
|
|
662
|
+
- `componentSizes[]` — per-component line count + JSX branch count
|
|
663
|
+
- `propBindings[]`, `stateBindings[]`, `hooks[]`, `logicalExpressions[]`, etc.
|
|
664
|
+
|
|
665
|
+
**Rule execution** iterates the registered rules (built-ins + user overrides) and calls each rule's `analyze(facts, context)` method, collecting `Issue[]`.
|
|
666
|
+
|
|
667
|
+
5. **Aggregation** (`src/engine/metrics.ts`)
|
|
668
|
+
|
|
669
|
+
Sums severity points per subscore bucket (boundary / context / visual), normalizes each bucket by component count capped at 100, and computes `slopIndex = 0.40 × boundary + 0.35 × context + 0.25 × visual`. Returns the structured `ProjectReport` with all subscores, severity counts, and per-file scores.
|
|
670
|
+
|
|
671
|
+
6. **Threshold check** (`src/cli/threshold.ts`)
|
|
672
|
+
|
|
673
|
+
Calls `thresholdExceeded(report, config)`, which compares `report.slopIndex` against `config.thresholds.meanSlop`. Returns true → exit code 1. Also checks per-category thresholds (`categoryThresholds`) if configured.
|
|
674
|
+
|
|
675
|
+
7. **Output rendering** (`src/report/`)
|
|
676
|
+
|
|
677
|
+
One module per format:
|
|
678
|
+
- `pretty.ts` — human-readable terminal output with the composite breakdown tree
|
|
679
|
+
- `json.ts` — serialized `ProjectReport` (full data)
|
|
680
|
+
- `sarif.ts` — SARIF 2.1.0 for IDE/editor integration
|
|
681
|
+
- `html.ts` — self-contained HTML report with score cards
|
|
682
|
+
- `markdown.ts` — Markdown report for PR comments
|
|
683
|
+
- `heatmap.ts` — migration ROI heatmap (top files by score)
|
|
684
|
+
- `unified-diff.ts` — unified diff of the report
|
|
685
|
+
- `advice.ts` — remediation suggestions
|
|
686
|
+
- `flywheel.ts` — telemetry summary
|
|
687
|
+
|
|
688
|
+
### File layout
|
|
689
|
+
|
|
690
|
+
```
|
|
691
|
+
src/
|
|
692
|
+
├── index.ts # Public facade (re-exports from ./cli/)
|
|
693
|
+
├── config.ts # Public config facade (re-exports from ./config/)
|
|
694
|
+
├── config/ # config/{defaults,presets,detect,load,init}
|
|
695
|
+
├── cli/ # CLI surface (Commander wiring + scan + init engines)
|
|
696
|
+
│ ├── program.ts # runCli — Commander setup, per-command .action() callbacks
|
|
697
|
+
│ ├── scan.ts # runScan, scanProject, watchProject, renderOutput
|
|
698
|
+
│ ├── init.ts # runInitWizard, runDoctor, init prompts
|
|
699
|
+
│ ├── options.ts # CLI option parsers (parseThreads, collectGlob, …)
|
|
700
|
+
│ ├── render.ts # colorForSlop, formatBadge, formatSparkline, …
|
|
701
|
+
│ └── threshold.ts # thresholdExceeded, stagedGating, filterIssues, …
|
|
702
|
+
├── engine/
|
|
703
|
+
│ ├── parser.ts # SWC/Vue/Svelte/Astro/HTML dispatch + extension-less fallback
|
|
704
|
+
│ ├── visitor.ts # AST walker → ScanFacts extraction (1313 lines — largest file)
|
|
705
|
+
│ ├── worker.ts # Per-file scan worker thread
|
|
706
|
+
│ ├── metrics.ts # Composite Slop Index aggregation
|
|
707
|
+
│ ├── logger.ts # Test-aware logging
|
|
708
|
+
│ ├── pool.ts # WorkerPool with work-stealing + retry
|
|
709
|
+
│ ├── executor.ts # Inline scan path for small file counts
|
|
710
|
+
│ ├── cache.ts # .slop-audit-cache.json + baseline.json
|
|
711
|
+
│ ├── memory.ts # run-history.json (--trend, --no-increase)
|
|
712
|
+
│ ├── telemetry.ts # Flywheel payloads
|
|
713
|
+
│ └── trend.ts # --trend sparkline builder
|
|
714
|
+
├── rules/ # Rule modules (42 built-in rules across 9 categories)
|
|
715
|
+
│ ├── arch/ # 1 rule — astro-island-leak
|
|
716
|
+
│ ├── component/ # 3 rules — giant-component, multiple-components-per-file,
|
|
717
|
+
│ │ shadcn-prop-mismatch
|
|
718
|
+
│ ├── context/ # 1 rule — import-path-mismatch
|
|
719
|
+
│ ├── layout/ # 4 rules — gap-monopoly, math-element-uniformity,
|
|
720
|
+
│ │ math-grid-uniformity, spacing-grid
|
|
721
|
+
│ ├── logic/ # 11 rules — boundary-violation, ghost-defensive,
|
|
722
|
+
│ │ key-prop-missing, math-any-density,
|
|
723
|
+
│ │ math-console-log-storm, math-gini-class-usage,
|
|
724
|
+
│ │ math-variable-name-entropy, optimistic-no-rollback,
|
|
725
|
+
│ │ qwik-hook-leak, reactive-hook-soup, zombie-state
|
|
726
|
+
│ ├── perf/ # 2 rules — cls-image, css-bloat
|
|
727
|
+
│ ├── typo/ # 5 rules — calc-fontsize, calc-raw-px, clamp-offscale,
|
|
728
|
+
│ │ math-button-label-uniformity, math-cta-vocabulary
|
|
729
|
+
│ ├── visual/ # 11 rules — arbitrary-escape, clamp-soup, generic-centering,
|
|
730
|
+
│ │ inline-style-dominance, math-color-cluster,
|
|
731
|
+
│ │ math-default-font, math-font-entropy,
|
|
732
|
+
│ │ math-gradient-hue-rotation, math-rounded-entropy,
|
|
733
|
+
│ │ math-spacing-entropy
|
|
734
|
+
│ ├── wcag/ # 4 rules — dragging-movements, focus-appearance,
|
|
735
|
+
│ │ focus-obscured, target-size
|
|
736
|
+
│ ├── builtins.ts # Auto-generated registry (pnpm generate:rules)
|
|
737
|
+
│ ├── rule.ts # createRule + RuleDefinition types
|
|
738
|
+
│ ├── registry.ts # RuleRegistry (loadBuiltins, loadProjects)
|
|
739
|
+
│ ├── registry-loader.ts # shadcn/ui registry snapshot cache
|
|
740
|
+
│ ├── project.ts # Project-level rules (runProjectRules)
|
|
741
|
+
│ ├── signal-strength.ts # --show-signal-strength lookup
|
|
742
|
+
│ └── signal-strength.json # Per-rule precision/recall measurements
|
|
743
|
+
├── report/ # Output formatters (pretty, json, sarif, html, …)
|
|
744
|
+
│ ├── pretty.ts, json.ts, sarif.ts, html.ts, markdown.ts
|
|
745
|
+
│ ├── advice.ts # --suggest output
|
|
746
|
+
│ ├── unified-diff.ts # --diff output
|
|
747
|
+
│ ├── heatmap.ts # --heatmap output
|
|
748
|
+
│ ├── flywheel.ts # flywheel summary
|
|
749
|
+
│ └── html/ # html/{utils,sections,static}.ts
|
|
750
|
+
├── fix/ # Auto-fix codemods
|
|
751
|
+
│ ├── index.ts # applyFixes orchestrator
|
|
752
|
+
│ ├── visual-codemod.ts # Round-20 visual codemods entry point
|
|
753
|
+
│ └── visual-codemods/ # tailwind.ts, jsx.ts, source.ts
|
|
754
|
+
├── snippet.ts # AI agent rule snippet generators (facade)
|
|
755
|
+
├── snippet/ # snippet/{data,render,generators,targets}
|
|
756
|
+
├── flywheel.ts # Flywheel state machine
|
|
757
|
+
├── mcp/ # MCP server (src/mcp/server.ts + tools)
|
|
758
|
+
├── research/ # research/generate, analyze, candidates, calibrate
|
|
759
|
+
├── config-validation.ts # Static config schema validator
|
|
760
|
+
├── discover.ts # File discovery + extension sniffing
|
|
761
|
+
├── git.ts # --staged / --changed / --since git integration
|
|
762
|
+
├── installer.ts # install/uninstall git pre-commit hook
|
|
763
|
+
├── explain.ts # `slopbrick explain <ruleId>` output
|
|
764
|
+
├── tokens.ts # W3C DTCG tokens.json parser
|
|
765
|
+
├── types.ts # All public types (ProjectReport, ScanFacts, Issue, …)
|
|
766
|
+
└── bin/ # bin/slopbrick.js entry point
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
---
|
|
770
|
+
|
|
771
|
+
## MCP server (for AI agents)
|
|
772
|
+
|
|
773
|
+
slopbrick ships a [Model Context Protocol](https://modelcontextprotocol.io/) server so AI coding agents can call it directly:
|
|
774
|
+
|
|
775
|
+
```bash
|
|
776
|
+
slopbrick mcp # JSON-RPC 2.0 over stdio
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
Exposes three tools:
|
|
780
|
+
|
|
781
|
+
| Tool | Args | Returns |
|
|
782
|
+
|------|------|---------|
|
|
783
|
+
| `slop_scan_file` | `{path, framework?}` | issues + Slop Index for one file |
|
|
784
|
+
| `slop_explain_rule` | `{ruleId}` | rule metadata + rationale + file location |
|
|
785
|
+
| `slop_list_rules` | `{category?}` | all rules with category / severity / aiSpecific |
|
|
786
|
+
|
|
787
|
+
Add to your MCP client config:
|
|
788
|
+
|
|
789
|
+
```json
|
|
790
|
+
{
|
|
791
|
+
"mcpServers": {
|
|
792
|
+
"slopbrick": {
|
|
793
|
+
"command": "npx",
|
|
794
|
+
"args": ["slopbrick", "mcp"],
|
|
795
|
+
"cwd": "/path/to/your/project"
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
### AI agent rule snippets
|
|
802
|
+
|
|
803
|
+
Generate directive snippets that teach your AI agent the slop rules BEFORE it writes code:
|
|
804
|
+
|
|
805
|
+
```bash
|
|
806
|
+
slopbrick init --matrix # print the matrix table
|
|
807
|
+
slopbrick init --yes --agents-md # Codex / opencode / Pi / Cline / Gemini
|
|
808
|
+
slopbrick init --yes --claude-md # Claude Code
|
|
809
|
+
slopbrick init --yes --all # all targets at once
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
| Flag | File | Agent |
|
|
813
|
+
|------|------|-------|
|
|
814
|
+
| `--cursor` | `.cursor/rules/slopbrick.mdc` | Cursor (new format) |
|
|
815
|
+
| `--cursorrules` | `.cursorrules` | Cursor (legacy format, deprecated) |
|
|
816
|
+
| `--agents-md` | `AGENTS.md` | OpenAI Codex / opencode / Pi / Cline / Continue / Gemini |
|
|
817
|
+
| `--claude-md` | `CLAUDE.md` | Claude Code (takes precedence over AGENTS.md) |
|
|
818
|
+
| `--aider` | `CONVENTIONS.md` | Aider |
|
|
819
|
+
| `--windsurf` | `.windsurfrules` | Windsurf (Cascade) |
|
|
820
|
+
| `--cline` | `.clinerules/AGENTS.md` | Cline (folder-based) |
|
|
821
|
+
| `--gemini` | `.gemini/GEMINI.md` | Gemini CLI |
|
|
822
|
+
| `--copilot` | `.github/copilot-instructions.md` | GitHub Copilot |
|
|
823
|
+
|
|
824
|
+
Content is generated live from the rule registry — always matches what slopbrick actually checks.
|
|
825
|
+
|
|
826
|
+
---
|
|
827
|
+
|
|
828
|
+
## Adding new rules
|
|
829
|
+
|
|
830
|
+
Rule modules live in `src/rules/<category>/<rule>.ts`. Each module must export a const ending in `Rule` and a matching default export:
|
|
831
|
+
|
|
832
|
+
```ts
|
|
833
|
+
import { createRule } from '../rule';
|
|
834
|
+
import type { Issue, Rule, RuleContext, ScanFacts } from '../../types';
|
|
835
|
+
|
|
836
|
+
export const myRule = createRule<RuleContext>({
|
|
837
|
+
id: 'category/my-rule',
|
|
838
|
+
category: 'visual',
|
|
839
|
+
severity: 'medium',
|
|
840
|
+
aiSpecific: true,
|
|
841
|
+
description: 'Short one-line description used in `slopbrick rules` output.',
|
|
842
|
+
create(context) {
|
|
843
|
+
return context;
|
|
844
|
+
},
|
|
845
|
+
analyze(_context, facts: ScanFacts): Issue[] {
|
|
846
|
+
const issues: Issue[] = [];
|
|
847
|
+
// ... analyze facts ...
|
|
848
|
+
return issues;
|
|
849
|
+
},
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
export default myRule satisfies Rule<RuleContext>;
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
Run `pnpm generate:rules` to regenerate `src/rules/builtins.ts`. This runs automatically before `pnpm build` and `pnpm test`.
|
|
856
|
+
|
|
857
|
+
---
|
|
858
|
+
|
|
859
|
+
## Calibration against held-out human code
|
|
860
|
+
|
|
861
|
+
The tool ships with an automated calibration test (`tests/integration/calibration.test.ts`) that scans both corpora and asserts every AI-signal rule has a recall/FP ratio ≥ its threshold. It runs as part of `pnpm test` and exits non-zero on regression.
|
|
862
|
+
|
|
863
|
+
The corpora live at `/Users/cheng/ai-slop-baseline/extracted/`:
|
|
864
|
+
- `positive/` — 6,142 AI-generated samples (vibe-coded React apps).
|
|
865
|
+
- `negative/` — 54,980 human-written samples (shadcn/ui, calcom, dub, mantine, excalidraw, lobehub, etc.).
|
|
866
|
+
|
|
867
|
+
A rule with a recall/FP ratio above 1.0× is a useful AI tell. A ratio below 1.0× is an anti-signal — tighten it, scope-restrict it, or drop it.
|
|
868
|
+
|
|
869
|
+
---
|
|
870
|
+
|
|
871
|
+
## Glossary
|
|
872
|
+
|
|
873
|
+
- **Slop Index** — 0–100 composite score per Phase 2 §10. Lower is better. Weighted average of boundary (40%), context (35%), and visual (25%) subscores.
|
|
874
|
+
- **Assembly Health** — Inverse of Slop Index. Higher is better.
|
|
875
|
+
- **Composite Slop Index** — Phase 2 §10's weighted three-bucket formula.
|
|
876
|
+
- **AI-specific rule** — Rule that catches patterns AI defaults to but humans rarely do (e.g. `bg-violet-500`, "Get started today", badge-above-h1 layout).
|
|
877
|
+
- **General rule** — Catches real bugs or code-quality issues regardless of author.
|
|
878
|
+
- **brick.config.json** — Project config (in `slopbrick.config.mjs`) listing allowed import paths for `context/import-path-mismatch`.
|
|
879
|
+
- **RSC / Server component** — React Server Component. Runs on the server, can't use `useState`/`useEffect`. The fix is `'use client'`.
|
|
880
|
+
- **Memoization** — React skips re-renders if inputs haven't changed. Inline handlers break memoization because they're new functions on every render.
|
|
881
|
+
- **Astro island** — Interactive component inside an otherwise static Astro page. Without `client:*` directive, clicks won't fire.
|
|
882
|
+
- **DTCG tokens** — W3C Design Token Community Group JSON format. `slopbrick tokens <path>` reads these.
|
|
883
|
+
- **MCP** — Model Context Protocol. JSON-RPC 2.0 over stdio for AI agent integration.
|
|
884
|
+
|
|
885
|
+
---
|
|
886
|
+
|
|
887
|
+
## Development
|
|
888
|
+
|
|
889
|
+
```bash
|
|
890
|
+
pnpm install
|
|
891
|
+
pnpm typecheck
|
|
892
|
+
pnpm test
|
|
893
|
+
pnpm build
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
Adding a rule? Update `tests/integration/calibration.test.ts` to add a calibration entry — the corpus test will verify your rule discriminates AI from human code.
|
|
897
|
+
|
|
898
|
+
---
|
|
899
|
+
|
|
900
|
+
## License
|
|
901
|
+
|
|
902
|
+
[MIT](./LICENSE)
|