Skip to content

`crimes@0.8.1` — Calibration Patch

Draft release notes for the GitHub Release tagged v0.8.1. The body below is what should go in the Releases page when you cut the tag — that triggers .github/workflows/release.yml and publishes to npm via Trusted Publishing.

crimes@0.8.1 is a patch on top of 0.8.0 that tunes the new 0.8.0 detectors against dogfood evidence — no new detectors, no schema change, no new commands. Three changes:

  • boolean_naming_drift built-in allowlist expanded. Eight idiomatic state names that 0.8.0 over-flagged on real codebases (loaded, found, settled, overflow, typeonly, interpolated, limited, existed) are now in the React-state allowlist. Project-specific names still go through detectors.options.boolean_naming_drift.allowedNames — this is pure default-tuning, not a behaviour change for configured users.
  • Self-scan signal cleanup. The crimes monorepo’s own crimes.config.json now excludes evals/fixtures/** and examples/messy-ts-app/** from the asset pass, so the dogfood scan no longer surfaces the intentional-bad demo assets at the top of the report. No effect on downstream users’ configs.
  • scan-assets.ts refactored. The 80-line runAssetDetectorsForRoot body broken into four named helpers (discoverAssetFiles, groupDetectorsByExtension, runDetectorsForAssetFile, buildAssetContext). Each block is now a single responsibility and each is independently testable. Behaviour byte-identical to 0.8.0.

Schema: schema_version stays at "0.1.0". The published finding shape is unchanged.

0.8.0 ships boolean_naming_drift with a 26-name React-state allowlist (loading, ready, active, …). The dogfood pass surfaced eight more names where the unprefixed form is the natural identifier:

NameWhere it shows up
loadedPost-async-load flag — symmetric to the existing loading.
foundSearch / lookup result presence.
settledPromise / async-state idiom.
overflowUI dimension flag (text / box overflow).
typeonlyTypeScript import type discrimination.
interpolatedTemplate-string / fold-state flag.
limitedTruncation / cap signal (e.g. history_limited).
existedPre-condition flag in delete / cleanup flows.

All eight are now exempt by default — adding them to a project’s detectors.options.boolean_naming_drift.allowedNames is no longer required. Users who had them in their config can leave them there; the lookup is set-membership so duplicates are harmless.

The 0.8.0 release intentionally shipped the messy-ts-app demo with violating asset files (hero-banner.png, check-icon.png, partner-logo.svg) so the asset detectors had fixture coverage. But when we ran crimes scan on the crimes monorepo itself, the demo partner-logo.svg was the #1 high-severity finding — a fixture-driven artefact dominating production-code signal.

The fix is one config change in our own crimes.config.jsonassets.exclude now adds evals/fixtures/** and examples/messy-ts-app/** on top of the defaults. The default assets.exclude already excludes **/fixtures/** (added in phase 5b) which catches most real-world fixture trees; this monorepo’s two paths needed explicit listing because they live alongside production code rather than under a canonical fixtures/ folder.

No effect on downstream users — their crimes.config.json is unchanged, and the defaults haven’t moved.

The 0.8.0 runAssetDetectorsForRoot was a single 80-line function mixing four responsibilities: discover asset files, group detectors by extension, build a per-file detector context, and dispatch the run loop. Split into four named helpers:

  • discoverAssetFiles(root, config) — owns the assets.include / assets.exclude discipline, including the “explicitly cleared include = skip the pass entirely” semantics.
  • groupDetectorsByExtension(detectors) — builds the Map<extension, AssetDetector[]> used by the dispatch loop.
  • runDetectorsForAssetFile({ root, absolutePath, config, byExtension }) — per-file orchestration: lookup applicable detectors, build context, run each, swallow per-detector exceptions.
  • buildAssetContext({ root, absolutePath, extension, config }) — pre-fetches byteSize via fs.stat, sets up the lazy per-file-cached read(), returns the AssetDetectorContext or undefined for unreadable files.

Same behaviour, same per-file caching, same exception isolation — the public runAssetDetectorsForRoot signature is unchanged. The refactor was motivated by making each piece individually substitutable in tests and future asset pipelines (e.g. parallel asset scan, batch I/O via worker threads).

Same harness, same 38 scenarios per agent, against the patched detector set:

AgentStructural pass rateΔ vs 0.7.15
claude0.82-3pp
codex0.76+2pp

Per scenario kind:

Kindclaudecodexclaude Δcodex Δ
bugfix0.750.63+18pp+13pp
context0.931.00-6pp+0pp
plan0.710.65-23pp+6pp
refactor0.930.80+4pp+2pp
review0.710.74-10pp-2pp

The per-kind shifts are run-to-run variance — every kind samples small enough (4-11 scenarios) that one scenario flipping is ±10pp or more. The allowlist change doesn’t touch the fixtures (the eight new exempt names aren’t in the messy-ts-app fixture — the fixture’s drift cases like paid / expired / stale continue to fire). Re-pinning the baseline is the policy and keeps the per-version trail clean.

The first run of this baseline hit nine transient claude exited 1 failures (no stderr — looks like subscription-side throttling). We re-ran --agent claude to fill them; the codex side from the first pass was clean. The summary above is the merged result.

Result transcripts and rubric scores at evals/results/0.8.1/.

  • No new detectors. Detector count unchanged at 47.
  • No new commands. CLI surface byte-equivalent to 0.8.0.
  • No schema bump.
  • No config schema changes. The assets.exclude extension in the bundled crimes.config.json is monorepo-specific, not a default-value change.
  • No package dependency changes.
Terminal window
npm install -g crimes@0.8.1
crimes --version # crimes@0.8.1

For users on crimes@0.8.0:

  • If you previously added loaded / found / settled / overflow / typeonly / interpolated / limited / existed to your detectors.options.boolean_naming_drift.allowedNames — you can leave them; the default allowlist now covers them too (set-membership lookup, duplicates are harmless).
  • If your CI gates on crimes scan --fail-on high or crimes baseline check --fail-on, no findings should regress: the changes are detector-quietening (allowlist additions) plus a structural refactor.