`crimes@0.11.0` — Triage as the front door (Release B)
Draft release notes for the GitHub Release tagged
v0.11.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.11.0 is a minor release that adds the crimes triage
command as the recommended front door for handling existing findings —
crimes baseline save stays in place as the escape hatch. Silenced
triage entries and baseline entries now resurface automatically in
crimes scan when their file is in the branch diff, so a one-time
decision doesn’t become permanent amnesia. crimes init --agents ships
a Claude Code PreToolUse Edit hook so the pre-edit briefing happens
without the agent having to remember to call crimes context. The
human renderer’s secondary scores read as prose (blast top-quartile (11 importers) instead of 0.72); the JSON contract is unchanged on
those numerics.
The schema bumps "0.1.0" → "0.2.0" to add two new required fields
on every Finding: effort and fix_shape. Existing field shapes,
names, and semantics are unchanged.
Changes requiring agent attention:
schema_versionis now"0.2.0". Consumers that hard-checked=== "0.1.0"must accept the new string.Finding.effortandFinding.fix_shapeare required. Both are detector-supplied with defaults; consumers that ignored unknown fields under0.1.0see them appear for free..crimes/baseline.jsonand.crimes/suppressions.jsonfiles written by oldercrimesversions still load — the loader tolerates the"0.1.0"schema_versionon those files, which keeps teams from having to rewrite committed state on the same PR they upgrade.
Design rationale:
docs/superpowers/specs/2026-05-20-release-b-triage-design.md.
Things requiring agent attention
Section titled “Things requiring agent attention”schema_version bumps "0.1.0" → "0.2.0"
Section titled “schema_version bumps "0.1.0" → "0.2.0"”Every report (ScanReport, ContextReport, DiffReport,
BaselineCheckReport, VerdictReport, ExplainReport,
HotspotsReport, on-disk Baseline + Suppressions, plus the new
on-disk Triage) now carries schema_version: "0.2.0". The
top-of-file docs/json-schema.md migration note
documents exactly what changed.
Agent code that pinned to the old string:
if (report.schema_version !== "0.1.0") throw new Error("unknown schema");…must switch to accepting both:
if (report.schema_version !== "0.1.0" && report.schema_version !== "0.2.0") { throw new Error("unknown schema");}No existing field changed shape, name, or semantics. The bump exists specifically to add the two new required fields below.
New required Finding fields: effort + fix_shape
Section titled “New required Finding fields: effort + fix_shape”interface Finding { // ...existing fields... /** Estimated effort to address. */ effort: "quick" | "small" | "medium" | "large"; /** One-line description of the shape of the fix (≤120 chars). */ fix_shape: string;}The effort ladder:
quick— ≤1-line change.small— under one hour of work.medium— fits within one PR.large— needs design.
fix_shape is a one-line description of the shape of the fix
(e.g. "extract orchestration; move pure helpers to a sibling module"),
not a complete patch. ≤120 chars, single line, non-empty. Detector
defaults live in
packages/core/src/detector-defaults.ts.
A detector that omits either field falls back to the per-type default,
then to medium + "refactor to remove this signal; add a test that pins the fix".
Loader tolerance for older state files
Section titled “Loader tolerance for older state files”.crimes/baseline.json and .crimes/suppressions.json committed under
crimes@0.10.x carry schema_version: "0.1.0". The 0.11.0 loaders
accept either "0.1.0" or "0.2.0" on those files, so a team
upgrading to 0.11.0 doesn’t have to rewrite committed state in the
same PR. The first crimes ignore / crimes baseline save after the
upgrade rewrites the file with "0.2.0".
.crimes/triage.json is new in this release — there’s nothing to
migrate.
What shipped
Section titled “What shipped”crimes triage command
Section titled “crimes triage command”Per-finding interactive walk over current findings, top-of-rank first. Each prompt asks for a disposition, reason, and owner, then stamps the date from the system clock. Five dispositions:
| Disposition | Default crimes scan | Resurfaces on touched files? |
|---|---|---|
fix-now | shown (▶ prefix) | n/a |
fix-this-PR | shown (▶ prefix) | n/a |
needs-design | hidden | yes — “still intentional?” |
wont-fix | hidden | yes — “still intentional?” |
scaffolding | hidden | yes — “still intentional?” |
Each disposition writes to .crimes/triage.json immediately
(incremental persistence) so a SIGINT or terminal crash mid-walk does
not lose progress. The file is intended to be committed and is
hand-reviewable in git diff.
Sample interactive flow:
$ crimes triageTriaging 47 findings against .crimes/triage.json.Keys: f fix-now · p fix-this-PR · d needs-design · w wont-fix · s scaffolding · k skip · q quit
[1/47] 🚨 src/billing/invoice.ts God Function · generateInvoice() — 214 lines, 6 awaits, 3 tables Risk: churn high · test gap top-quartile · blast 0.72 Fix shape: extract orchestration; move pure helpers to a sibling module Effort: medium Disposition? w Reason (one line): legacy billing, full rewrite Q3 — see ADR-014 Owner [@amayfield]: ✓ wont-fix · saved.
[2/47] …Flags:
| Flag | What it does |
|---|---|
--apply <file> | Non-interactive — read dispositions from a JSON file (same shape as on disk). |
--list | Show the current triage entries and exit; no scan, no prompts. |
--clear <fingerprint> | Remove a single entry by fingerprint. |
--retriage <target> | Re-open the disposition prompt for matching entries (fingerprint, file path, or glob). |
--owner <handle> | Default owner for new dispositions written this run. |
--all | Include non-domain tier findings in the interactive walk. |
--format human|json | Output format for the summary line on exit. |
crimes triage refuses to start the interactive walk in CI or a
non-TTY (exits 2 with a hint to use --apply). --apply merges by
fingerprint — applied entries overwrite existing fingerprints;
unmentioned fingerprints stay untouched.
.crimes/triage.json on-disk shape:
docs/json-schema.md.
Source: packages/core/src/triage.ts,
packages/cli/src/commands/triage.ts.
Triage + baseline resurfacing
Section titled “Triage + baseline resurfacing”crimes scan now re-runs the relevant detector on files that match
.crimes/triage.json or .crimes/baseline.json entries when those
files appear in the branch diff against
config.triage.resurfaceBase. Findings that still match the stored
fingerprint surface with annotations:
interface Finding { /** True when this finding matches an entry in .crimes/triage.json. */ previously_triaged?: true; previous_triage?: { disposition: "fix-now" | "fix-this-PR" | "needs-design" | "wont-fix" | "scaffolding"; reason: string; owner: string; date: string; // YYYY-MM-DD };
/** True when this finding matches an entry in .crimes/baseline.json. */ previously_baselined?: true; previous_baseline?: { date?: string; reason?: string; };}The human renderer shows a “You’re editing files you previously
triaged — was this still intentional?” block before the main “Top
files by risk” header, with one row per resurfaced finding and a
crimes triage --retriage <file> hint. Baseline resurfacings render
in a sibling block.
config.triage.resurfaceBase defaults to "main". Set it to ""
(empty string) in crimes.config.json to disable resurfacing
entirely:
{ "triage": { "resurfaceBase": "" }}Resurfacing is also skipped silently when the directory isn’t a git
repo, when HEAD resolves to the same ref as <resurfaceBase>, or
when the re-detect finds zero matching findings for the stored
fingerprint (i.e. the user already fixed it — we don’t nag).
Interaction with scopeTiers.nonDomain: resurfacing crosses tiers.
A triaged finding in a non-domain file (under scripts/**,
**/__tests__/**, etc.) still resurfaces when that file is touched
on the branch — the non-domain footer is a display tier, not a
“don’t care” tier. The crimes triage interactive walk visits
domain-tier findings only by default; crimes triage --all includes
non-domain.
New crimes scan flags:
| Flag | Default | Effect |
|---|---|---|
--show-triaged | off | Include silenced dispositions in the output (annotated with hidden_triage). |
--gate-needs-design | off | When --fail-on is set, count needs-design findings toward the gate. |
--gate-resurfaced | off | When --fail-on is set, count resurfaced findings on touched files toward the gate. |
Default gate semantics: wont-fix, scaffolding, and needs-design
are excluded from --fail-on because the user already triaged them.
Resurfaced findings are surfaced as a reminder, not a block. Opt in
with the flags above when your team wants stricter gating.
Source: packages/core/src/resurface.ts,
packages/core/src/triage-filter.ts,
packages/core/src/scan.ts.
effort + fix_shape schema fields
Section titled “effort + fix_shape schema fields”Bundled into a single schema_version bump ("0.1.0" → "0.2.0")
so consumers cross the boundary once. Both fields are required on
output; both come from a detector-defaults map keyed on detector type
with a generic medium + “refactor to remove this signal; add a test
that pins the fix” fallback.
crimes scan --all and crimes explain <id> show both fields in
human output. crimes context per-finding block shows fix_shape
under the finding header. crimes triage shows both fields next to
the finding when prompting for a disposition — the brief explicitly
calls this out as the load-bearing UX, since the user is deciding
how much work this is before they pick a disposition.
Source: packages/core/src/finding.ts,
packages/core/src/detector-defaults.ts,
packages/core/src/detector.ts.
PreToolUse hook in init --agents
Section titled “PreToolUse hook in init --agents”crimes init --agents now writes hook configuration alongside the
existing SKILL.md files:
| Path | Behaviour |
|---|---|
.claude/skills/crimes/SKILL.md | Unchanged (since 0.9.0). |
.agents/skills/crimes/SKILL.md | Unchanged (since 0.9.0). |
.claude/settings.local.json | New. Merge-write of a PreToolUse Edit hook. |
.agents/settings.local.json | New (placeholder). Codex doesn’t honour PreToolUse yet. |
The Claude hook merged into .claude/settings.local.json:
{ "hooks": { "PreToolUse": [ { "matcher": "Edit|Write|NotebookEdit", "hooks": [ { "type": "command", "command": "npx -y crimes context \"$CLAUDE_TOOL_INPUT_file_path\" --format json 2>/dev/null || true", "timeout": 8000 } ] } ] }}Merge semantics:
- If
.claude/settings.local.jsondoesn’t exist, create with just the hook. - If it exists and already contains a crimes hook (detected by the
crimes contextsubstring incommand), skip (idempotent). - If it exists with other hooks, parse, append, write back with 2-space indent + trailing newline.
- If it exists with malformed JSON, exit
2with a clear message.--forceoverwrites only the crimes hook entry.
--no-hooks opts out of writing either settings file (SKILL.md
files still ship). crimes init without --agents is unaffected.
.agents/settings.local.json mirrors the Claude shape with
$CODEX_TOOL_INPUT_file_path. Codex doesn’t honour PreToolUse hooks
as of 0.11.0; the file is a forward-looking stub with a top-of-file
_note (or sibling README) documenting that it’s safe to delete.
Source: packages/cli/src/commands/init.ts,
packages/cli/src/hook-templates.ts.
Human-readable secondary scores
Section titled “Human-readable secondary scores”The scan per-file Risk: line and the context per-finding score block
now print interpretive prose instead of bare decimals:
Before (scan default view, file header):
Risk: churn high · test gap top-quartile · blast 0.72After:
Risk: churn high (24 commits in 90d) · test gap top-quartile · blast top-quartile (11 importers)Before (crimes context per-finding):
scores: severity 0.86 · confidence 0.88 · blast 0.72 · churn 0.64 · test gap 1.00 · agent risk 0.91After:
scores: severity 0.86 · confidence 0.88 · agent risk 0.91 blast top-quartile · imported by 11 files (top 5% of fan-in) churn 24 commits over 90d · last touched 2 days ago test gap top-quartile · no test file imports this moduleJSON numerics in Finding.scores are unchanged —
blast_radius, churn, test_gap, recency, and agent_risk
still ship as 0–1 floats rounded to two decimals. This is a
renderer-only change. Fallback for missing inputs (git
unavailable, no clues.churn data) shows the raw quartile label
only; never a bare decimal.
Source:
packages/reporter/src/human/score-format.ts,
packages/reporter/src/human/scan.ts,
packages/reporter/src/human/context.ts.
Unified human glyph vocabulary
Section titled “Unified human glyph vocabulary”The human renderers for crimes diff, crimes baseline check,
crimes verdict, and the per-finding Charge: / Also touches: lines
now share one glyph vocabulary:
| Glyph | Meaning |
|---|---|
⊕ | new (also doubles as the terminal logo) |
⊖ | fixed |
≡ | unchanged |
§ | charge marker on a finding header |
⧉ | related-files block |
▲ | verdict: worse |
▼ | verdict: cleaner |
◆ | verdict: mixed |
JSON output is untouched — these marks are presentation-only. Every
glyph is suppressed in --no-color mode, so piped output, CI logs,
and accessibility-conscious terminals stay plain ASCII.
Source:
packages/reporter/src/human/shared.ts.
Agent-skill guidance: JSON for decisions, human for readbacks
Section titled “Agent-skill guidance: JSON for decisions, human for readbacks”Both shipped SKILL.md files (.claude/skills/crimes/SKILL.md and
.agents/skills/crimes/SKILL.md), the crimes init skill template,
and docs/agent-usage.md gained a short rule:
Use
--format jsonwhen you need to plan, gate, compare, or make decisions. When the user wants to see the results — or you are summarising findings back in chat — rerun without--format jsonand paste the relevant human-readable readout instead of paraphrasing the JSON in your own voice.
The human report already renders severity glyphs, file groupings, evidence, feedback hints, suppressions, and gate status. The skill guidance now says so explicitly so agents stop re-rendering the findings prose-style in chat.
Source:
.claude/skills/crimes/SKILL.md,
.agents/skills/crimes/SKILL.md,
docs/agent-usage.md,
packages/cli/src/commands/init.ts.
What’s not in 0.11.0
Section titled “What’s not in 0.11.0”- No new detectors. Detector count unchanged at 48. Detector taxonomy stays frozen — Release B is workflow + schema fields + renderer, not new signal.
- No
crimes baselinedeprecation.baseline savestays as the escape hatch; the brief is explicit that triage is the front door, not the replacement. - No
--quick-wins/--budget <minutes>filter flags.effortis added to make these trivial in a follow-up, but they’re explicitly out of scope for this release. - No
crimes ask/ LLM-assisted modes. Still deferred tov1+per PRD §26.
Upgrading
Section titled “Upgrading”npm install -g crimes@0.11.0crimes --version # 0.11.0crimes triage # new — the recommended front doorcrimes scan . --format json | jq '.findings[0] | {effort, fix_shape}'crimes init --agents # writes PreToolUse hook by defaultcrimes init --agents --no-hooks # opts out of the hookJSON consumer migration:
// Before:if (report.schema_version !== "0.1.0") throw new Error("unknown schema");
// After:if (report.schema_version !== "0.1.0" && report.schema_version !== "0.2.0") { throw new Error("unknown schema");}Finding.effort and Finding.fix_shape are required from 0.2.0;
existing consumers that ignored unknown fields see them appear for
free. No existing field changed shape, name, or semantics. Triage and
resurface annotations (triaged, hidden_triage, previously_triaged,
previous_triage, previously_baselined, previous_baseline) are
optional; check
docs/json-schema.md
for the full shape.
Existing .crimes/baseline.json and .crimes/suppressions.json
files carrying schema_version: "0.1.0" continue to load — the
loader tolerates either string. The first crimes ignore /
crimes baseline save after the upgrade rewrites the file with
"0.2.0".
Notable links
Section titled “Notable links”docs/releases/v0.10.0.md— the previous release (Release A, front-door redesign).docs/superpowers/specs/2026-05-20-release-b-triage-design.md— design rationale and decision log.docs/agent-usage.md— pre-edit / post-edit workflow with the new triage step and PreToolUse hook contract.docs/json-schema.md— schema bump migration note + newFindingfields +.crimes/triage.jsonreference.docs/configuration.md— newtriage.resurfaceBaseconfig key.packages/core/src/triage.ts,packages/core/src/triage-filter.ts,packages/core/src/resurface.ts— triage data layer, scan-time filter, resurface re-detect.packages/core/src/detector-defaults.ts— per-detectoreffort+fix_shapedefaults.packages/cli/src/commands/triage.ts— interactive walk +--apply/--list/--clear/--retriage.packages/cli/src/commands/init.ts,packages/cli/src/hook-templates.ts— PreToolUse hook merge logic.packages/reporter/src/human/score-format.ts— interpretive prose for secondary scores.