`crimes@0.10.0` — Front-door redesign (Release A)
Draft release notes for the GitHub Release tagged
v0.10.0. The body below is what should go in the Releases page when you cut the tag — that triggers.github/workflows/release.ymland publishes to npm via Trusted Publishing.
crimes@0.10.0 is a minor release that redesigns the crimes scan
default output and the first-run experience so the first screen tells
you what to fix rather than listing everything at once. crimes context
now leads in every entry-point — banner, --help, README, agent docs.
Two changes require agent attention:
- Default
crimes scanis now file-grouped, not severity-grouped. Use--flatto get the old layout back. scores.test_gapis now a repo-relative quartile (0 / 0.25 / 0.5 / 0.75 / 1.0), not the prior fixed{0, 0.5, 1.0}mapping. Agents that comparedtest_gap === 1must switch to>= 0.75.
Both are additive with respect to schema_version — it stays at
"0.1.0". The new fields (Finding.tier, Finding.scores.recency,
ContextReport.clues) are optional and absent on output from earlier
versions.
Design rationale:
docs/superpowers/specs/2026-05-20-release-a-front-door-design.md.
What shipped
Section titled “What shipped”File-grouped crimes scan layout
Section titled “File-grouped crimes scan layout”The default crimes scan human output now groups findings by file and
sorts files by aggregate risk — a blend of churn, test-gap quartile,
blast radius, and recency. The prior layout (severity-grouped flat list)
is available via --flat.
Default view (human output):
CRIME SCENE REPORTrepo: acme-app · 173 findings across 47 files
Top files by risk
🚨 src/billing/invoice.ts 4 findings · 2 high 1. God Function · generateInvoice() 214 lines, 6 awaits, 3 tables 2. Duplicated Policy Logic · isOwner role check in 4 sites 3. Temporal Recklessness UTC + local mixed 4. Direct Date.now() in domain ln 187 Risk: churn high · test gap top-quartile · blast 0.72 id=crime_01982 … crime_01985
⚠️ src/api/users.ts 3 findings · 1 high ...
Showing top 5 files of 47. Run with --all to see every finding.The JSON wire format is unchanged: findings[] is still a flat array
on ScanReport, sorted by rank score. The human reporter folds them
into the per-file view.
New crimes scan flags:
| Flag | What it does |
|---|---|
--top <n> | Show top N files (default 5). Overrides scan.topFiles config. |
--flat | Revert to severity-grouped flat output. |
--no-recency | Disable recency weighting in file ranking. |
New config keys:
| Key | Type | Default | What it does |
|---|---|---|---|
scan.topFiles | number | 5 | Default number of files shown in file-grouped output. Override per-invocation with --top N. |
scopeTiers.nonDomain | string[] | [7-pattern list, see below] | Glob patterns whose findings appear in a separate “Also flagged elsewhere” footer. Empty array opts out. |
The default scopeTiers.nonDomain list (applied at scan time when the
key is absent from crimes.config.json):
[ "scripts/**", "examples/**", "fixtures/**", "public/**", "**/__tests__/**", "**/*.test.{ts,tsx,js,jsx}", "**/*.spec.{ts,tsx,js,jsx}"]Repo-relative test_gap quartile
Section titled “Repo-relative test_gap quartile”Finding.scores.test_gap was previously one of three fixed values
(0 = covered, 0.5 = weakly covered, 1.0 = uncovered). From
0.10.0 it is a quartile rank computed against the distribution
of test-file proximity across all source files in the repo:
| Quartile | test_gap value | Meaning |
|---|---|---|
| Bottom | 0.00 | Well-tested relative to this repo |
| Q1 | 0.25 | Modestly covered |
| Q2 (median) | 0.50 | Average for this repo |
| Q3 | 0.75 | Weakly covered relative to this repo |
| Top | 1.00 | Among the least-tested files in the repo |
The human reporter renders one of four labels — top-quartile,
~median, bottom-quartile, unknown — instead of the raw float.
The JSON clues.test_gap.label enum drops the tilde
(top-quartile | median | bottom-quartile | unknown).
Migration note for agents. If your agent code compares
scores.test_gap === 1 (or === 1.0) to detect “no tests”, change
the comparison to scores.test_gap >= 0.75. The 1.0 value now means
“top-quartile test gap in this specific repo”, not “unconditionally
uncovered”.
Recency-weighted ranking
Section titled “Recency-weighted ranking”Finding.scores.recency is a new optional 0–1 float:
1.0— file last committed in the past 7 days- Linear decay from
1.0to0.0between 7 and 14 days 0.0— file untouched for 14 days or more, orlatestChangeabsent (non-git directory, shallow clone window excludes the file, etc.)
The rank score that drives file ordering in the default scan layout
multiplies recency in (rank_score = agent_risk × (1 + recency × 0.5)),
so files that are both risky and recently touched surface first. Use
--no-recency to collapse the multiplier to 1 and rank by
agent_risk alone — recency still ships on FindingScores for JSON
consumers, the CLI just doesn’t apply it to ordering.
Finding.tier and scope-tier classifier
Section titled “Finding.tier and scope-tier classifier”Each finding is now tagged with tier: "domain" | "nonDomain",
computed from the finding’s file path matched against
config.scopeTiers.nonDomain (defaults documented above). Domain
findings are the ones the default scan layout shows in its top-N file
list; non-domain findings are grouped under an “Also flagged
elsewhere” footer with per-prefix counts (scripts/ 6 findings examples/ 3 findings tests/ 12 findings).
tier is optional and absent on output from earlier versions; treat
absence as "domain" for backwards-compatible reads. Set
scopeTiers.nonDomain: [] in crimes.config.json to disable tiering
entirely (every finding becomes "domain").
clues block on crimes context --json
Section titled “clues block on crimes context --json”Frozen contract for downstream consumers (e.g. PreToolUse hooks).
ContextReport gains an optional clues object with three substantive
sub-blocks plus a reserved key:
{ "clues": { "churn": { "commits_90d": 14, "last_commit_at": "2026-05-18T12:30:00Z", "unique_authors_90d": 3 }, "suppressions": [ { "fingerprint": "large_function::src/billing.ts::renderInvoice", "detector": "large_function", "reason": "Legacy billing module, rewrite planned", "pinned_version": "0.9", "matches_current_finding": false } ], "test_gap": { "raw": 1, "percentile": 0.85, "label": "top-quartile" }, "related_signals": [] }}Omission rules:
clues.churnis omitted when git is unavailable.clues.suppressionsis omitted when no entries scope to this file.clues.test_gap.percentileis omitted in repos with fewer than 4 source files; in that caselabelis"unknown"andrawcarries the raw0 / 0.5 / 1value.clues.test_gap.labelenum is exactly"top-quartile" | "median" | "bottom-quartile" | "unknown"(the human reporter prints~medianwith a tilde; JSON does not).clues.related_signalsis always present, always[]in0.10.0— reserved for future use.cluesitself is omitted when every substantive sub-block would be empty (rare in practice sincetest_gapis almost always populated).
The human crimes context output renders a “Clues” block between
“Likely tests” and “Findings” with one line per sub-block.
Two-prompt auto-init with agent detection
Section titled “Two-prompt auto-init with agent detection”Running any crimes subcommand (other than init, feedback,
ignore, unignore, baseline) on a repo with no
crimes.config.json triggers a short interactive setup instead of
silently proceeding. Up to two prompts:
- Config: “No crimes.config.json found. Generate one for this
repo? [Y/n]” — accept and
crimeswrites a config tuned to the repo (TS-onlyincludeif no JS files exist,**/.next/**exclude for Next.js projects,**/dist/**for Vite, andscopeTiers.nonDomainpopulated from directories that actually exist plus the standard test globs). - Agent skill (only when an agent is detected): “Write
.claude/skills/crimes/SKILL.mdso Claude Code discovers crimes for future sessions? [Y/n]” — or the equivalent Codex prompt for.agents/.
Agent detection priority (first match wins):
process.env.CLAUDECODEorCLAUDE_CODE→claudeprocess.env.OPENAI_CODEXorCODEX_AGENT→codex.claude/directory exists →claude.agents/directory exists →codex- otherwise → no skill prompt
Skip conditions (the whole prompt block is suppressed):
process.env.CIset- stdout is not a TTY (piped invocations,
runCli-style tests) --no-initglobal flag.crimes/.skip-initmarker file exists (written when the user declines a prompt without accepting either)
--init global flag re-enters the prompt block even when
crimes.config.json already exists.
New flags:
| Flag | Where | What it does |
|---|---|---|
--init (global) | Any cmd | Force the auto-init prompt block even if a config already exists. |
--no-init (global) | Any cmd | Suppress the auto-init prompt block entirely. |
--no-detect | init | On explicit crimes init, skip repo-shape detection; write static config. |
context leads in banner, --help, and docs
Section titled “context leads in banner, --help, and docs”The welcome banner (printed on bare crimes) and the --help output
now lead with crimes context <file> as the first suggested command.
The README Quick start and docs/agent-usage.md are restructured to
the same order: context first, then scan gates, then verdict.
What’s not in 0.10.0
Section titled “What’s not in 0.10.0”- No
schema_versionbump. TheFindingwire format is backwards-compatible:tier,scores.recency, andContextReport.cluesare additive optional fields.schema_versionstays"0.1.0". - No new detectors. Detector count unchanged at 48. Detector taxonomy is frozen for Release A per the design spec.
- No
crimes ask/ LLM-assisted modes. Still deferred tov1+.
Upgrading
Section titled “Upgrading”npm install -g crimes@0.10.0crimes --version # 0.10.0crimes context src/billing.ts --format json # new clues blockcrimes scan . # new file-grouped layoutcrimes scan . --flat # old severity-grouped layoutIf your agent code reads scores.test_gap:
Change any === 1 or === 1.0 comparison to >= 0.75. Values
shifted from the fixed three-point scale to repo-relative quartiles.
The 1.0 value is preserved (top-quartile files still score 1.0) so
the absolute ceiling is unchanged, but everything between 0 and 1
may move.
If your agent code reads crimes scan --format json and parses
findings by severity group:
The JSON wire format is unchanged — findings[] is still a flat array
on ScanReport. Only the human renderer changed to file-grouped. If
your code consumes --format json, no changes are needed.
Auto-init on first run:
If you run crimes in a fresh repo (no crimes.config.json) in a CI
environment, the auto-init step detects CI is set and skips
silently — no prompt, no files written. Use --no-init to suppress
the prompt unconditionally (CI or local), or --init to force the
prompt block to re-enter when a config already exists.
Notable links
Section titled “Notable links”docs/releases/v0.9.2.md— the previous release.docs/superpowers/specs/2026-05-20-release-a-front-door-design.md— design rationale and decision log.docs/agent-usage.md— full pre-edit/post-edit workflow with the new context-first structure.docs/configuration.md— newscopeTiers.nonDomainandscan.topFilesconfig keys.packages/reporter/src/human/scan.ts— file-grouped renderer.packages/core/src/scoring/quartile.ts— quartile-rank utility fortest_gap.packages/core/src/scoring/build.ts— recency index and scoring context assembly.