Skip to content

`crimes@0.4.0` — Agent context quality and signal-to-noise

Draft release notes for the GitHub Release tagged v0.4.0. 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.4.0 raises the trust ratio of every detector that already shipped, without adding a single new detector. Real-repo trials of 0.3.0 with Claude Code and Codex CLI surfaced two coupled gaps:

  1. crimes context didn’t tell agents what else to read before editing the target file. Now it does — see related_files.
  2. Several existing detectors fired too often on shapes they shouldn’t (React pages, route handlers, test callbacks, GitHub-relative README links, shallow git clones, nested-package roots). Now they don’t.

The wedge is unchanged: local, open-source, agent-native. Deterministic, no LLM, no API key, no network access.

All additions are additive and backwards-compatible — no schema_version bump, no required field changes, no CLI behaviour regressions.

crimes context — neighbourhood and root detection

Section titled “crimes context — neighbourhood and root detection”
  • ContextReport.related_files (new) — up to 10 files an agent should read before editing the target, ranked by a deterministic blend of:
    • IA finding passthrough (related to Route Metadata Drift)
    • Shared IA path tokens (shares domain token "billing")
    • Domain-prefix filename matches (matches domain "admin")
    • Same-directory siblings (same directory)
  • Monorepo root auto-detectioncrimes context examples/messy-ts-app/src/foo.ts from the monorepo root now produces the same findings as crimes context src/foo.ts from inside examples/messy-ts-app/. Walks up to the nearest enclosing package.json. Explicit --root still wins.
  • Top-level agent_guidance first — serialised JSON places agent_guidance ahead of findings so agents read the actionable summary first.
  • Neighbourhood guidance line — clean files with related files no longer return an empty agent_guidance; they get one line pointing at the neighbourhood.
  • Empty-field self-explanationagent_guidance_reason, related_files_reason, likely_tests_reason distinguish “we searched and found nothing” from “we didn’t search”.
  • _test.ts / _spec.ts likely-test discovery — Go-style suffix conventions join the existing .test.ts / .spec.ts rules.

ParsedFunction now carries a shape: FunctionShape (domain | test_callback | react_component | page_export | route_handler | unknown) computed during AST parsing. The detector applies per-shape thresholds:

ShapeThresholdSeverity at thresholdSeverity at 2×
domain (config)60mediumhigh
route_handler100mediumhigh
react_component200mediumhigh
page_export200mediumhigh
test_callback200lowmedium
unknown80mediumhigh

Evidence now names the shape (e.g. "3.4× the domain function threshold (60 lines)") so a reader can verify which budget applied. The bundled fixture’s generateInvoice God Function still flags at high severity.

docs_code_drift — GitHub-relative allowlist

Section titled “docs_code_drift — GitHub-relative allowlist”

../../issues, ../../pull/N, ../../wiki/Home, ../../blob/main/..., and the rest of GitHub’s server-rewritten path set are no longer flagged as broken local links. Real ../../docs/foo.md paths still resolve normally.

crimes scan --changed --format json now emits a top-level array listing every file the resolver returned, sorted and deduplicated, including files with zero findings (touched markdown, lockfiles, config edits). Plain crimes scan omits the field.

{
"summary": { "total": 0, "high": 0, "medium": 0, "low": 0 },
"findings": [],
"changed_files": ["NOTES.md", "src/billing.ts", "src/billing.test.ts"]
}

HotspotsReport.history_limited (and history_limited_reason) is set when git rev-parse --is-shallow-repository returns true. Common on CI runners with fetch-depth: 1. The human reporter prints the notice on the same line as the existing not-a-git-repo warning. Agents should downweight rankings when the flag is set.

Live trials of 0.3.0 against real repos showed the next painful experience wasn’t “I can’t suppress this finding” — it was “I don’t trust this finding”. Adding more detectors on top of a noisy floor makes that worse, not better. 0.4.0 closes that gap so future detector work (or the IA detectors still on the roadmap) lands on a trusted base.

Three alternative themes were considered and deferred:

  • More IA detectors (orphaned_destination, parallel_destination, permission_ia_drift, action_label_drift) — pre-empted by the “no more detectors before fixing noise” feedback.
  • Per-finding scores.churn / test_gap / blast_radius — touches every detector and the scoring contract; bigger than one minor release.
  • crimes init + crimes ignore + .crimes/suppressions.json — moved to 0.5.0. Fixing detector noise at source (this release) removes most of the underlying demand.

Full rationale and scope in .planning/archive/0.4.0-agent-context-quality.md.

  • schema_version stays at "0.1.0" — no breaking changes.
  • New optional fields on ScanReport, ContextReport, and HotspotsReport are all documented in docs/json-schema.md.
  • The canonical JSON key order for ContextReport changed (agent_guidance now precedes findings). Object-key order is not part of the schema contract; consumers that read by key name (the recommended pattern in docs/json-schema.md) are unaffected.
  • CLI surface unchanged. Every command, flag, and exit-code behaviour from 0.2.0 / 0.3.0 still works.
  • .crimes/baseline.json from 0.3.0 still validates. The fingerprint scheme is unchanged.
  • Baseline drift expected on adoption. Repos that committed a 0.3.0 baseline with many large_function findings on React components / route handlers / test callbacks may see those entries classified as fixed after upgrading — the detector no longer fires on those shapes. This is the intended outcome; refresh the baseline with crimes baseline save after upgrading.
  • crimes init + crimes.config.json plumbing.
  • crimes ignore <id> + .crimes/suppressions.json.
  • crimes explain <id> long-form per-finding rationale.
  • Per-finding scores.churn / scores.test_gap / scores.blast_radius (M2 work).
  • crimes diff --fail-on new-high (final M4 CI-gate flag).
  • More IA detectors (orphaned_destination, parallel_destination, permission_ia_drift, action_label_drift, command-drift variant of docs_code_drift).
  • Importer / importee detection inside related_files.
  • CLI breadcrumb (stderr line) when the auto-detected package root differs from process.cwd().
  • pnpm-workspace.yaml / turbo.json as additional monorepo markers.
  • crimes ask / LLM-assisted modes (v1+).
  • Homebrew tap and standalone macOS / Linux / Windows binaries.
Terminal window
npm install -g crimes@0.4.0
# or
npx crimes@0.4.0 scan .

Then, in CI:

- uses: actions/checkout@v4
with:
fetch-depth: 0 # clears HotspotsReport.history_limited
- run: npx crimes scan --changed --fail-on high

Local verification (run from a clone of the repo):

Terminal window
pnpm install
pnpm build
pnpm typecheck
pnpm test # 410 tests across 4 packages
pnpm scan:example # demonstrates all five IA finding types + shape-aware large_function
pnpm --filter crimes smoke # the publish-tarball smoke

This release exists because Claude Code and Codex CLI users ran crimes@0.3.0 against real repositories and reported back honestly about where it helped and where it hurt. That feedback drove the scope — every must-ship item in .planning/archive/0.4.0-agent-context-quality.md maps to a specific real-repo observation.