Project health /heal:sweep

complete

Run 2026-07-12T10-39-53Z-demo · mode report-only · started 2026-07-12 10:39:53 UTC · finished 2026-07-12 11:58:35 UTC · wall clock 78m 42s

Sample dashboard. Rendered by the suite's real report generator, but the audited project (acme/storefront) and its findings are fictional demo content — the run's duration and token statistics are genuine numbers from a production sweep.

A roster, not the catalog. This run covers the 14 axes the project turned on — one .heal/axes/<axis>.md facts file each. The suite ships 18; the four this project left off (content-ip, i18n, privacy, web-egress) would join it with /heal:init --add <axis>. A sweep runs what a repo enabled, never the whole catalog — an axis with nothing to audit would only produce noise.

Findings burden
18.9
severity-weighted, baseline-excluded — the convergence signal
▼ 5.7 since the last run
27
Total findings
1 high · 12 med · 14 low
87
Health score (advisory)
mean of 3 scored axes · not run-to-run comparable
5
Total preventions
3 guardrails · 3 promotions
60%
Prevention score
these guardrails would prevent 3/5 finding-classes
11/14
Healers with findings
3 clean · 0 partial
59.1M
Tokens (run total)
78m 42s across 14 healers
~$49
Cost (notional)
API-equivalent — a Pro/Max subscription isn't billed per token

Findings by severity

1
12
14
1 high 12 medium 14 low

Dashboard

HealerStatusFindingsPreventionsHML ScoreFixedDurationTokens~$Backlog heading
self clean 0 0 0 0 0 4m 37s 4.1M $6.64
arch clean 0 0 0 0 0 4m 1s 1.4M $2.08
api findings 1 0 0 1 0 6m 54s 2.5M $2.66 ## API contract health
db clean 0 0 0 0 0 4m 8s 1.3M $1.86
ci findings 2 0 0 1 1 9m 38s 5.8M $3.21 ## CI/CD health
packages findings 6 0 0 2 4 9m 40s 4.9M $2.78 ## Dependency health
ai findings 1 0 0 0 1 4m 1s 2.2M $2.90 ## AI/LLM health
security findings 1 +1 0 0 1 1 3m 43s 3.2M $3.12 ## Security health
tests findings 1 1 0 0 1 4m 17s 1.0M $1.33 ## Test-suite health
quality findings 3 1 0 1 2 88 4m 18s 1.3M $1.66 ## Quality debt
perf findings 2 0 0 0 2 90 3m 29s 1.7M $2.11 ## Performance health
docs findings 3 1 0 2 1 5m 34s 4.6M $2.49 ## Docs drift
a11y findings 3 1 1 2 0 7m 4s 12.1M $5.04 ## Accessibility health
design findings 3 1 0 2 1 82 7m 18s 5.3M $2.51 ## UI consistency

🧭 Next steps

The same close /heal:list and the sweep's final section print — one renderer (heal-findings.mjs renderNextSteps), so this dashboard, the chat close and /heal:list can't disagree about what to run next. Any /heal:fix line below is copy-paste ready — the id: bullets under it are its coverage manifest, not input.

Strategy — batch, don't parallelize. Several ids in ONE /heal:fix call = one branch/PR + one state tick; parallel per-id invocations conflict on shared files and .heal/backlog.md. One PR per axis (same-axis findings share files). Skip an axis that already has an open heal PR (gh pr list --state open); merge serially, the .heal state PR last. Full law: ${CLAUDE_PLUGIN_ROOT}/protocol/fix-core.md.

Ids or a bare axis word? Paste the ids below — they fix exactly the vetted set you just read, and naming an id also attempts non-decision judgment items. /heal:fix <axis> is the advanced bulk-drain: it scopes itself to whatever is currently [ ] under that backlog heading (classifier-curated, so it can have drifted from this run) and defers judgment items under the default --mechanical.

1 · Fix commands

Type only the /heal:fix line — the id: bullets under it are its coverage manifest, not input. Safe to run as-is: /heal:fix defers any decision-shaped finding (dep majors, contract changes, security posture) with a question instead of guessing.

/heal:fix a11y
  id: a11y:placeholder-labels:apps/storefront/src/checkout/checkout-form.tsx — checkout inputs carry placeholders instead of labels
  id: a11y:modal-focus-trap:apps/storefront/src/components/modal.tsx — modal does not trap focus or restore it on close
  id: a11y:unnamed-icon-buttons:apps/storefront/src/components/product-card.tsx — icon-only buttons ship with no accessible name

/heal:fix packages
  id: packages:framework-minor-lag:apps/storefront/package.json — framework minors behind the declared pin range
  id: packages:transitive-advisory:pnpm-lock.yaml — transitive advisory awaiting an upstream release
  id: packages:outdated-data-fetching:apps/storefront/package.json — data-fetching library a minor behind
  id: packages:outdated-icons:apps/storefront/package.json — icon set a patch behind
  id: packages:outdated-lint:package.json — lint toolchain a patch behind
  id: packages:outdated-test-runner:package.json — test runner a minor behind

/heal:fix docs
  id: docs:removed-script:README.md — README documents a package script that no longer exists
  id: docs:phantom-env-var:README.md — README documents an env var nothing reads
  id: docs:stale-arch-diagram:docs/architecture.md — architecture diagram predates the shared pricing engine

/heal:fix ci
  id: ci:deprecated-action-major:github/workflows/test.yml — artifact-upload action pinned to a major with an announced EOL date
  id: ci:missing-concurrency:github/workflows/test.yml — no concurrency group — rapid consecutive pushes run duplicate jobs to completion

/heal:fix security:unthrottled-password-reset:apps/server/src/routes/auth.ts
  id: security:unthrottled-password-reset:apps/server/src/routes/auth.ts — reset endpoint accepts unlimited requests, enabling enumeration and mail-bomb abuse

/heal:fix ai:legacy-model-alias:apps/server/src/services/description-service.ts
  id: ai:legacy-model-alias:apps/server/src/services/description-service.ts — product-description generator pins a legacy model alias with a newer snapshot at identical pricing

/heal:fix tests:presence-only-assertion:apps/server/src/services/order-service.test.ts
  id: tests:presence-only-assertion:apps/server/src/services/order-service.test.ts — checkout-total test asserts the result is defined but never the amount
2 · Targeted fix prompts

Paste each whole paragraph into a fresh session — /heal:fix won't auto-apply these axes (quality, perf, design, api). Each paragraph now declares its own execution contract (isolated worktree → PR off the integration branch → resolve + rotate → teardown), so a pasted prompt behaves like /heal:fix. Each ends with the resolve + rotate commands that close the finding; skip them and the code gets fixed but the finding stays open in /heal:list.

Fix (or, if it turns out not to be a real issue, dismiss) the heal:api finding api:response-shape-drift:apps/server/src/routes/orders.ts: DELETE /orders/:id returns { deleted: true } but the client wrapper types the response as void (apps/server/src/routes/orders.ts:142). Run it the way /heal:fix does (${CLAUDE_PLUGIN_ROOT}/protocol/fix-core.md): do the fix in an isolated git worktree cut off origin/main — never the shared checkout — ship it as a focused PR off main, then remove the worktree once the PR is open. The resolve + rotate below run from the main checkout, not the worktree. Once adjudicated — the fix landed and lint/typecheck/test pass, or investigation showed it was wrong — record it from the main checkout (.heal/runs/ is gitignored, so never from the worktree): node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-findings.mjs" resolve api:response-shape-drift:apps/server/src/routes/orders.ts --verdict <fixed|false-positive|duplicate|wontfix> --note "<what happened, e.g. fixed in <sha>>", then node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-run.mjs" rotate --run 2026-07-12T10-39-53Z-demo --only api:response-shape-drift:apps/server/src/routes/orders.ts to rotate the bullet into .heal/archive/--only scopes the move to your batch, since the overlay is shared by every session (ship that .heal edit per fix-core's tick split).

Fix (or, if it turns out not to be a real issue, dismiss) the heal:design finding design:hand-rolled-spinner:apps/storefront/src/components/order-status.tsx: hand-rolled spinner markup instead of the shared primitive (apps/storefront/src/components/order-status.tsx:18). Run it the way /heal:fix does (${CLAUDE_PLUGIN_ROOT}/protocol/fix-core.md): do the fix in an isolated git worktree cut off origin/main — never the shared checkout — ship it as a focused PR off main, then remove the worktree once the PR is open. The resolve + rotate below run from the main checkout, not the worktree. Once adjudicated — the fix landed and lint/typecheck/test pass, or investigation showed it was wrong — record it from the main checkout (.heal/runs/ is gitignored, so never from the worktree): node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-findings.mjs" resolve design:hand-rolled-spinner:apps/storefront/src/components/order-status.tsx --verdict <fixed|false-positive|duplicate|wontfix> --note "<what happened, e.g. fixed in <sha>>", then node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-run.mjs" rotate --run 2026-07-12T10-39-53Z-demo --only design:hand-rolled-spinner:apps/storefront/src/components/order-status.tsx to rotate the bullet into .heal/archive/--only scopes the move to your batch, since the overlay is shared by every session (ship that .heal edit per fix-core's tick split).

Fix (or, if it turns out not to be a real issue, dismiss) the heal:design finding design:raw-select:apps/storefront/src/checkout/address-form.tsx: raw <select> bypassing the shared field primitive (apps/storefront/src/checkout/address-form.tsx:71). Run it the way /heal:fix does (${CLAUDE_PLUGIN_ROOT}/protocol/fix-core.md): do the fix in an isolated git worktree cut off origin/main — never the shared checkout — ship it as a focused PR off main, then remove the worktree once the PR is open. The resolve + rotate below run from the main checkout, not the worktree. Once adjudicated — the fix landed and lint/typecheck/test pass, or investigation showed it was wrong — record it from the main checkout (.heal/runs/ is gitignored, so never from the worktree): node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-findings.mjs" resolve design:raw-select:apps/storefront/src/checkout/address-form.tsx --verdict <fixed|false-positive|duplicate|wontfix> --note "<what happened, e.g. fixed in <sha>>", then node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-run.mjs" rotate --run 2026-07-12T10-39-53Z-demo --only design:raw-select:apps/storefront/src/checkout/address-form.tsx to rotate the bullet into .heal/archive/--only scopes the move to your batch, since the overlay is shared by every session (ship that .heal edit per fix-core's tick split).

Fix (or, if it turns out not to be a real issue, dismiss) the heal:quality finding quality:god-module:apps/server/src/services/order-service.ts: order-service has regrown past the size-watch threshold (apps/server/src/services/order-service.ts). Run it the way /heal:fix does (${CLAUDE_PLUGIN_ROOT}/protocol/fix-core.md): do the fix in an isolated git worktree cut off origin/main — never the shared checkout — ship it as a focused PR off main, then remove the worktree once the PR is open. The resolve + rotate below run from the main checkout, not the worktree. Once adjudicated — the fix landed and lint/typecheck/test pass, or investigation showed it was wrong — record it from the main checkout (.heal/runs/ is gitignored, so never from the worktree): node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-findings.mjs" resolve quality:god-module:apps/server/src/services/order-service.ts --verdict <fixed|false-positive|duplicate|wontfix> --note "<what happened, e.g. fixed in <sha>>", then node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-run.mjs" rotate --run 2026-07-12T10-39-53Z-demo --only quality:god-module:apps/server/src/services/order-service.ts to rotate the bullet into .heal/archive/--only scopes the move to your batch, since the overlay is shared by every session (ship that .heal edit per fix-core's tick split).

Fix (or, if it turns out not to be a real issue, dismiss) the heal:design finding design:token-drift:apps/storefront/src/styles/tokens.css: two off-scale spacing values outside the token set (apps/storefront/src/styles/tokens.css:44). Run it the way /heal:fix does (${CLAUDE_PLUGIN_ROOT}/protocol/fix-core.md): do the fix in an isolated git worktree cut off origin/main — never the shared checkout — ship it as a focused PR off main, then remove the worktree once the PR is open. The resolve + rotate below run from the main checkout, not the worktree. Once adjudicated — the fix landed and lint/typecheck/test pass, or investigation showed it was wrong — record it from the main checkout (.heal/runs/ is gitignored, so never from the worktree): node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-findings.mjs" resolve design:token-drift:apps/storefront/src/styles/tokens.css --verdict <fixed|false-positive|duplicate|wontfix> --note "<what happened, e.g. fixed in <sha>>", then node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-run.mjs" rotate --run 2026-07-12T10-39-53Z-demo --only design:token-drift:apps/storefront/src/styles/tokens.css to rotate the bullet into .heal/archive/--only scopes the move to your batch, since the overlay is shared by every session (ship that .heal edit per fix-core's tick split).

Fix (or, if it turns out not to be a real issue, dismiss) the heal:perf finding perf:hero-asset:apps/storefront/src/pages/home.tsx: uncompressed hero image on the first-visit path (apps/storefront/src/pages/home.tsx:21). Run it the way /heal:fix does (${CLAUDE_PLUGIN_ROOT}/protocol/fix-core.md): do the fix in an isolated git worktree cut off origin/main — never the shared checkout — ship it as a focused PR off main, then remove the worktree once the PR is open. The resolve + rotate below run from the main checkout, not the worktree. Once adjudicated — the fix landed and lint/typecheck/test pass, or investigation showed it was wrong — record it from the main checkout (.heal/runs/ is gitignored, so never from the worktree): node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-findings.mjs" resolve perf:hero-asset:apps/storefront/src/pages/home.tsx --verdict <fixed|false-positive|duplicate|wontfix> --note "<what happened, e.g. fixed in <sha>>", then node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-run.mjs" rotate --run 2026-07-12T10-39-53Z-demo --only perf:hero-asset:apps/storefront/src/pages/home.tsx to rotate the bullet into .heal/archive/--only scopes the move to your batch, since the overlay is shared by every session (ship that .heal edit per fix-core's tick split).

Fix (or, if it turns out not to be a real issue, dismiss) the heal:perf finding perf:unmemoized-list:apps/storefront/src/components/product-grid.tsx: product grid re-renders every row on unrelated state changes (apps/storefront/src/components/product-grid.tsx:48). Run it the way /heal:fix does (${CLAUDE_PLUGIN_ROOT}/protocol/fix-core.md): do the fix in an isolated git worktree cut off origin/main — never the shared checkout — ship it as a focused PR off main, then remove the worktree once the PR is open. The resolve + rotate below run from the main checkout, not the worktree. Once adjudicated — the fix landed and lint/typecheck/test pass, or investigation showed it was wrong — record it from the main checkout (.heal/runs/ is gitignored, so never from the worktree): node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-findings.mjs" resolve perf:unmemoized-list:apps/storefront/src/components/product-grid.tsx --verdict <fixed|false-positive|duplicate|wontfix> --note "<what happened, e.g. fixed in <sha>>", then node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-run.mjs" rotate --run 2026-07-12T10-39-53Z-demo --only perf:unmemoized-list:apps/storefront/src/components/product-grid.tsx to rotate the bullet into .heal/archive/--only scopes the move to your batch, since the overlay is shared by every session (ship that .heal edit per fix-core's tick split).

Fix (or, if it turns out not to be a real issue, dismiss) the heal:quality finding quality:any-casts:apps/storefront/src/lib/cart-mapper.ts: any casts across the cart mapper drop type safety at the boundary (apps/storefront/src/lib/cart-mapper.ts:33). Run it the way /heal:fix does (${CLAUDE_PLUGIN_ROOT}/protocol/fix-core.md): do the fix in an isolated git worktree cut off origin/main — never the shared checkout — ship it as a focused PR off main, then remove the worktree once the PR is open. The resolve + rotate below run from the main checkout, not the worktree. Once adjudicated — the fix landed and lint/typecheck/test pass, or investigation showed it was wrong — record it from the main checkout (.heal/runs/ is gitignored, so never from the worktree): node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-findings.mjs" resolve quality:any-casts:apps/storefront/src/lib/cart-mapper.ts --verdict <fixed|false-positive|duplicate|wontfix> --note "<what happened, e.g. fixed in <sha>>", then node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-run.mjs" rotate --run 2026-07-12T10-39-53Z-demo --only quality:any-casts:apps/storefront/src/lib/cart-mapper.ts to rotate the bullet into .heal/archive/--only scopes the move to your batch, since the overlay is shared by every session (ship that .heal edit per fix-core's tick split).

Fix (or, if it turns out not to be a real issue, dismiss) the heal:quality finding quality:duplicate-helper:apps/storefront/src/lib/format-price.ts: price formatting helper duplicated from the shared package (apps/storefront/src/lib/format-price.ts:14). Run it the way /heal:fix does (${CLAUDE_PLUGIN_ROOT}/protocol/fix-core.md): do the fix in an isolated git worktree cut off origin/main — never the shared checkout — ship it as a focused PR off main, then remove the worktree once the PR is open. The resolve + rotate below run from the main checkout, not the worktree. Once adjudicated — the fix landed and lint/typecheck/test pass, or investigation showed it was wrong — record it from the main checkout (.heal/runs/ is gitignored, so never from the worktree): node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-findings.mjs" resolve quality:duplicate-helper:apps/storefront/src/lib/format-price.ts --verdict <fixed|false-positive|duplicate|wontfix> --note "<what happened, e.g. fixed in <sha>>", then node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-run.mjs" rotate --run 2026-07-12T10-39-53Z-demo --only quality:duplicate-helper:apps/storefront/src/lib/format-price.ts to rotate the bullet into .heal/archive/--only scopes the move to your batch, since the overlay is shared by every session (ship that .heal edit per fix-core's tick split).

3 · Preventions

/heal:prevent --apply — 3 guardrail proposal(s) awaiting wiring: P4 raw-primitive-bypass (proposed; design) · P5 unnamed-interactive (proposed; a11y) · P7 readme-vs-package-scripts (drafted; docs). Triage first: node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-preventions.mjs" next; the rendered backlog lives under ## Prevention backlog in .heal/backlog.md.

⚪ Accepted (1) — deliberate standing tradeoffs recorded in .heal/baseline.json (node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-run.mjs" baseline --add <id> --reason "…"), not silence: still audited and reported every run, but excluded from burden and never /heal:fix targets. When the codebase has moved on, re-open one with node "${CLAUDE_PLUGIN_ROOT}/scripts/heal-run.mjs" baseline --remove <id> and it counts as open work again.

Trend vs previous run

Joined by finding id against run 2026-07-05T09-01-27Z-dm03 · burden 24.6 → 18.9 (severity-weighted, baseline-excluded).

HealerNewFixedPersistingAccepted
self0100
arch0000
api0110
db0100
ci1110
packages0260
ai1100
security0111
tests0110
quality1120
perf1110
docs2110
a11y1120
design0130
total714191

New findings

  • ci:missing-concurrency:github/workflows/test.yml
  • ai:legacy-model-alias:apps/server/src/services/description-service.ts
  • quality:any-casts:apps/storefront/src/lib/cart-mapper.ts
  • perf:hero-asset:apps/storefront/src/pages/home.tsx
  • docs:removed-script:README.md
  • docs:phantom-env-var:README.md
  • a11y:placeholder-labels:apps/storefront/src/checkout/checkout-form.tsx

Fixed since the previous run

  • self:stale-cited-path:.heal/axes/perf.md
  • api:orphan-route:apps/server/src/routes/legacy-webhooks.ts
  • db:unindexed-fk:apps/server/src/db/schema.ts
  • ci:unpinned-action-sha:github/workflows/deploy.yml
  • packages:deprecated-dep:apps/server/package.json
  • packages:stale-types:package.json
  • ai:removed-sampling-param:apps/server/src/services/description-service.ts
  • security:missing-security-headers:apps/server/src/app.ts
  • tests:flaky-retry-poll:apps/server/src/services/inventory.test.ts
  • quality:dead-exports:packages/shared/src/index.ts
  • perf:n-plus-one-order-list:apps/server/src/services/order-service.ts
  • docs:wrong-build-command:README.md
  • a11y:low-contrast-muted:apps/storefront/src/styles/tokens.css
  • design:inconsistent-empty-state:apps/storefront/src/pages

Accepted (baseline)

  • security:cookie-samesite:apps/server/src/lib/session.ts

Run history

Last 4 of 4 completed heal/sweep runs. Machine-local (logs/ is gitignored) — burden is severity-weighted and baseline-excluded.

RunFinishedHealersFindingsBurdenScoreTokens
2026-06-21T09-02-11Z-dm01 2026-06-21 10:31:40 UTC 14 41 39.8
79 61.2M
2026-06-28T09-05-42Z-dm02 2026-06-28 10:19:05 UTC 14 38 31.5
81 55.9M
2026-07-05T09-01-27Z-dm03 2026-07-05 10:21:44 UTC 14 34 24.6
82 52.5M
2026-07-12T10-39-53Z-demo 2026-07-12 11:58:35 UTC 14 27 18.9
87 59.1M

Smallest safe first steps — combined

The first action each healer recommends, gathered for a single triage pass.

self clean

Nothing to do — the suite still describes the repo as it is. Keep the discipline: any rename that touches a path a healer cites should run /heal:self in the same PR.

arch clean

Nothing to do. Watch the seam that historically drifts first: any new route that returns computed totals should call the shared engine, never re-derive.

api findings

Fix the delete wrapper's return type first — it is the only remaining mismatch, and the change is 3 lines plus the contract test.

db clean

Nothing to do — the history applies cleanly, exactly once, in order. Keep indexing new hot-path FKs at declaration time as 0031 did.

ci findings

Apply the artifact-action bump — it is mechanical, and the EOL clock is running.

packages findings

Take the four low-severity bumps as one --fix batch (lint, test runner, data-fetching, icons) — green CI on one PR retires two thirds of this axis's count.

ai findings

Take the alias bump with the fixture regeneration in one commit — cheapest finding on the board this sweep.

security findings

Land the reset-endpoint limiter with its 429 test — the only open medium on this axis, and the fix is a copy of an existing pattern.

tests findings

Repair the checkout-total assertion — a money-path test that can't fail is worse than no test, because it reads as coverage.

quality findings

Delete the duplicated price helper (5-minute PR), then schedule the order-service split — it is the root of both the complexity and half the review friction on this axis.

perf findings

Compress the hero image — one asset swap, measurable on every first visit.

docs findings

Apply the two mechanical fixes under --fix, then decide the phantom env var — it is the only judgment item on this axis.

a11y findings

Fix the checkout labels today — one high on the money path outweighs everything else in this report combined.

design findings

Swap the three spinners for the shared primitive — smallest diff, and it retires the axis's most visible inconsistency.

Preventions — combined

Prevention registry — coverage map

The durable P-item registry (.heal/preventions.json): guardrail status across every run, not just this one. Axes sorted least-covered first — that's where the next guardrail buys the most.

3wired
1drafted
2proposed
2held
AxisWired / active P-itemsCoverage
design0/1open
a11y0/1open
quality0/1open
docs0/1open
tests1/2open
security1/1covered
db1/1covered
5proposed
3with a guardrail
3promotions
60%would-solve score

A guardrail proposal for every finding — promotions and recurring classes first, first-sight below. Suggest-only: run /heal:prevent --apply to write the low-risk artifacts.

heal:prevent — guardrail proposals (run 2026-07-12T10-39-53Z-demo)

  • Run read: .heal/runs/2026-07-12T10-39-53Z-demo — a whole-repo report-only /heal:sweep (14 healers, main @ 9f3d7c1). Suggest-only (no --apply): every artifact below is drafted, nothing written except the backlog convergence.
  • Recurrence summary: 27 findings across 14 healers; after folding into classes and excluding the baseline-accepted id: 5 guardrail proposals — 3 recurring (occ > 1, seen in 2026-07-05T09-01-27Z-dm03 too), 2 first-sight. Burden trend across the two runs: 24.6 → 18.9.
  • Held guardrails: the security-headers middleware, the FK-index rule, and the fake-timers test convention — all added after previous findings, none of their classes recurred this run.

Prevention backlog

Finding (axis)OccurrencesWould-prevent layerChannelNew / PromoteGeneralises?
Raw select and hand-rolled spinners bypassing shared primitives (design)3lint no-restricted-syntax on raw select + spinner markup in app code (design)RulePromoteyes
Unnamed icon buttons and placeholder-only labels (a11y)2jsx-a11y lint preset + aria-label as a required prop type (a11y)Rule/CodePromoteyes
God module regrowth past the size-watch threshold (quality)3stays detect — module splits are judgment; size-watch already ranks it first (quality)(detect)Promoteno
README documents removed scripts and phantom env vars (docs)1script comparing package.json scripts + env reads to README, wired pre-commit (docs)Code/ScriptNewyes
Presence-only assertions on money-path tests (tests)2stays detect — asserting the value is a per-test judgment call (tests)(detect)Newno

Notes

  • The two Promote rule rows recur despite an existing convention doc — escalated from

prose to lint enforcement, the cheapest layer that makes the class impossible.

  • The docs script proposal ships as an inert draft under scripts/proposals/;

wiring it into pre-commit stays a human call.

  • The god-module row is a deliberate detect-only promote: emphasis escalates in the

report, but a split is never proposed as a mechanical artifact.

Findings detail

Each healer's full report, in run order. Report-only — nothing auto-applied.

self clean 0/0/0

heal:self — Meta-audit of the heal/* commands

1. Scope

Whole-repo sweep of acme/storefront (demo target), compared against the previous sweep (2026-07-05T09-01-27Z-dm03). Report-only — nothing was changed; low-risk items are marked auto-fixable under --fix.

2. What was checked

CheckStatus
Cited paths/commands in all 14 healer specs existOK — 0 dangling references
Asserted facts match the project's CLAUDE.mdOK
Coverage: repo surfaces vs healer rosterOK — no orphan surface
Suite doc drift (README / overview page / run order)OK
Structural gate (check-heal-suite.mjs)OK (exit 0)

3. Findings

None. The previous sweep's finding (a healer citing a renamed script) was fixed and did not recur — the guardrail added for it (path check in the structural gate) is holding.

4. Smallest safe first step

Nothing to do — the suite still describes the repo as it is. Keep the discipline: any rename that touches a path a healer cites should run /heal:self in the same PR.

5. Fix summary

Report-only run; nothing changed.

TODO delta

none

4m 37sDuration
52Turns
104Input
22.2kOutput
3.4MCache read
702.5kCache write
4.1MTotal tokens
arch clean 0/0/0

heal:arch — Architecture drift vs the committed invariants

1. Scope

Whole-repo sweep of acme/storefront (demo target), compared against the previous sweep (2026-07-05T09-01-27Z-dm03). Report-only — nothing was changed; low-risk items are marked auto-fixable under --fix.

2. What was checked

InvariantStatus
HTTP boundary — no apps/serverapps/storefront importsOK
packages/shared purity (no I/O, no framework imports)OK
Single source of pricing math (no hand-rolled totals)OK
Server/client DTO parity on shared shapesOK
Deterministic arch gateOK (exit 0)

3. Findings

None. The pricing engine remains the only place money math happens — both the checkout route and the storefront preview import it from packages/shared, and no arithmetic on raw price fields appears outside it.

4. Smallest safe first step

Nothing to do. Watch the seam that historically drifts first: any new route that returns computed totals should call the shared engine, never re-derive.

5. Fix summary

Report-only run; nothing changed.

TODO delta

none

4m 1sDuration
30Turns
52Input
5.9kOutput
1.1MCache read
218.7kCache write
1.4MTotal tokens
api findings 0/1/0

heal:api — API contracts across the HTTP boundary

1. Scope

Whole-repo sweep of acme/storefront (demo target), compared against the previous sweep (2026-07-05T09-01-27Z-dm03). Report-only — nothing was changed; low-risk items are marked auto-fixable under --fix.

2. What was checked

42 routes ↔ 42 client fetch wrappers, request schemas on every mutating route, the shared error-code union, and cache invalidation after each mutation.

3. Findings

M — response-shape drift · apps/server/src/routes/orders.ts:142 *(persisting — 2nd sweep)*

DELETE /orders/:id returns { deleted: true } but the client wrapper types the response as void, so the flag is silently discarded and a future consumer would type-check against the wrong shape.

  • Suggested fix: align the wrapper's return type with the server (or drop the body

server-side) — one commit touching both sides of the boundary, which is why this is never auto-applied.

  • Suggested test: a contract test asserting the parsed response of the delete wrapper

matches the route's serialized output for a fixture order.

The orphan legacy-webhooks route flagged last sweep was removed — confirmed gone.

4. Smallest safe first step

Fix the delete wrapper's return type first — it is the only remaining mismatch, and the change is 3 lines plus the contract test.

5. Fix summary

Report-only run; nothing changed.

TODO delta

API contract health — 1 open item(s) converged.

6m 54sDuration
51Turns
98Input
13.2kOutput
2.3MCache read
189.9kCache write
2.5MTotal tokens
db clean 0/0/0

heal:db — Database schema & migration health

1. Scope

Whole-repo sweep of acme/storefront (demo target), compared against the previous sweep (2026-07-05T09-01-27Z-dm03). Report-only — nothing was changed; low-risk items are marked auto-fixable under --fix.

2. What was checked

InvariantStatus
Journal ordering strictly monotonicOK (31 entries)
Schema ↔ migration drift (drizzle-kit check)OK
FKs indexed on hot pathsOK — order_items.order_id index added since last sweep
Backfill-safe data migrations (add-nullable → backfill → NOT NULL)OK
Multi-step writes transactionalOK
Destructive DDL intentional & commentedOK

3. Findings

None. Last sweep's unindexed foreign key on order_items.order_id was fixed in migration 0031 — the checkout hot path now walks an index.

4. Smallest safe first step

Nothing to do — the history applies cleanly, exactly once, in order. Keep indexing new hot-path FKs at declaration time as 0031 did.

5. Fix summary

Report-only run; nothing changed.

TODO delta

none

4m 8sDuration
37Turns
70Input
9.3kOutput
1.2MCache read
166.6kCache write
1.3MTotal tokens
ci findings 0/1/1

heal:ci — CI/CD pipeline health

1. Scope

Whole-repo sweep of acme/storefront (demo target), compared against the previous sweep (2026-07-05T09-01-27Z-dm03). Report-only — nothing was changed; low-risk items are marked auto-fixable under --fix.

2. What was checked

Runtime currency (runner images, action majors), hermeticity, least-privilege (permissions: per job, no pull_request_target, no untrusted-code execution), and log quietness (zero tolerated deprecation warnings).

3. Findings

M — deprecated action major · .github/workflows/test.yml:58 *(persisting — 2nd sweep)*

The artifact-upload action is pinned to a major with an announced EOL date.

  • Suggested fix: bump the major (one line, no config change) — auto-fixable under

--fix. Flagged last sweep too; it should not survive a third.

  • Suggested test: none needed — the workflow run itself verifies.
L — no concurrency group · .github/workflows/test.yml:3 *(new)*

Rapid consecutive pushes run duplicate jobs to completion.

  • Suggested fix: a concurrency: block keyed on the ref with

cancel-in-progress: true.

The unpinned third-party action flagged last sweep is now pinned to a full commit SHA — fixed and verified.

4. Smallest safe first step

Apply the artifact-action bump — it is mechanical, and the EOL clock is running.

5. Fix summary

Report-only run; nothing changed.

TODO delta

CI/CD health — 2 open item(s) converged.

9m 38sDuration
91Turns
3.9kInput
29.9kOutput
5.4MCache read
296.9kCache write
5.8MTotal tokens
packages findings 0/2/4

heal:packages — Dependency currency & trust

1. Scope

Whole-repo sweep of acme/storefront (demo target), compared against the previous sweep (2026-07-05T09-01-27Z-dm03). Report-only — nothing was changed; low-risk items are marked auto-fixable under --fix.

2. What was checked

Currency across all three workspaces (respecting the declared pins), plus the trust pass: deprecated/archived deps, typosquat-adjacent names, new install scripts, lockfile integrity.

3. Findings

Six packages behind, all within range — 2 medium (the framework minors and a transitive advisory awaiting an upstream release) and 4 routine patch/minor lags. All are safe in-range bumps under --fix; the changelogs were read, none touches a breaking surface.

Since last sweep: the deprecated date library was replaced and the stale type-stub package removed — both confirmed gone from the lockfile.

Trust pass: clean. No deprecated or archived direct deps remain, no new install scripts, lockfile integrity verified.

4. Smallest safe first step

Take the four low-severity bumps as one --fix batch (lint, test runner, data-fetching, icons) — green CI on one PR retires two thirds of this axis's count.

5. Fix summary

Report-only run; nothing changed.

TODO delta

Dependency health — 6 open item(s) converged.

9m 40sDuration
88Turns
176Input
41.9kOutput
4.6MCache read
204.7kCache write
4.9MTotal tokens
ai findings 0/0/1

heal:ai — AI provider & model health

1. Scope

Whole-repo sweep of acme/storefront (demo target), compared against the previous sweep (2026-07-05T09-01-27Z-dm03). Report-only — nothing was changed; low-risk items are marked auto-fixable under --fix.

2. What was checked

The configured LLM provider registry: model ids active and non-deprecated, call-site parameters valid against current API docs, retry/timeout settings sane, and newly released models worth adopting.

3. Findings

L — legacy model alias · description-service.ts:27 *(new)*

The product-description generator pins a model alias that still resolves but has a newer snapshot at identical pricing with better instruction following.

  • Suggested fix: one-line alias bump — auto-fixable under --fix.
  • Suggested test: the existing golden-output test covers the swap (regenerate the

fixture once, review the diff).

The removed sampling parameter flagged last sweep was cleaned up — the call site now passes only supported options.

4. Smallest safe first step

Take the alias bump with the fixture regeneration in one commit — cheapest finding on the board this sweep.

5. Fix summary

Report-only run; nothing changed.

TODO delta

AI/LLM health — 1 open item(s) converged.

4m 1sDuration
40Turns
4.5kInput
15.8kOutput
1.9MCache read
242.1kCache write
2.2MTotal tokens
security findings 0/1/1

heal:security — Application security

1. Scope

Whole-repo sweep of acme/storefront (demo target), compared against the previous sweep (2026-07-05T09-01-27Z-dm03). Report-only — nothing was changed; low-risk items are marked auto-fixable under --fix.

2. What was checked

Secret hygiene, auth/session handling, access control on every data route (IDOR sweep), mass assignment, injection, XSS, CSRF, security response headers, and dependency advisories (pnpm audit).

3. Findings

M — unthrottled password reset · routes/auth.ts:96 *(persisting — 2nd sweep)*

The reset endpoint accepts unlimited requests, enabling enumeration and mail-bomb abuse.

  • Suggested fix: reuse the login route's limiter (same window/threshold) —

mechanical, auto-fixable under --fix. Persisting a second sweep; prioritize.

  • Suggested test: an integration test asserting the N+1-th request within the window

returns 429 and no mail job is enqueued.

L — SameSite=Lax · lib/session.ts:18 *(accepted baseline)*

Standing tradeoff (cross-site navigation UX; CSRF covered by the token check). Re-reported for visibility; excluded from burden and NEW counts.

Fixed since last sweep: the security response headers now ship on every route — the middleware added after the previous finding is verified present and correctly ordered.

4. Smallest safe first step

Land the reset-endpoint limiter with its 429 test — the only open medium on this axis, and the fix is a copy of an existing pattern.

5. Fix summary

Report-only run; nothing changed.

TODO delta

Security health — 1 open item(s) converged.

3m 43sDuration
69Turns
131Input
7.1kOutput
2.9MCache read
237.2kCache write
3.2MTotal tokens
tests findings 0/0/1

heal:tests — Test-suite value

1. Scope

Whole-repo sweep of acme/storefront (demo target), compared against the previous sweep (2026-07-05T09-01-27Z-dm03). Report-only — nothing was changed; low-risk items are marked auto-fixable under --fix.

2. What was checked

Assertion quality (no-assertion and presence-only tests), implementation coupling, redundancy, flakiness across recent runs, and coverage on the pricing engine's edge cases (rounding, currency, discount stacking).

3. Findings

L — presence-only assertion · order-service.test.ts:211 *(persisting — 2nd sweep)*

The checkout-total test asserts the result is defined but never the amount — it would pass for any wrong total on the money path.

  • Suggested fix: assert the exact expected sum for the fixture cart (subtotal,

discount, tax lines individually).

  • Suggested test: this *is* the test — repair, don't delete: the scenario is the

right one, only the assertion is hollow.

Fixed since last sweep: the flaky inventory poll test now uses fake timers — zero flakes in the observed window.

4. Smallest safe first step

Repair the checkout-total assertion — a money-path test that can't fail is worse than no test, because it reads as coverage.

5. Fix summary

Report-only run; nothing changed.

TODO delta

Test-suite health — 1 open item(s) converged.

Preventions for this axis 1

  • detect Presence-only assertions on money-path tests — stays detect — asserting the value is a per-test judgment call (tests) (detect) ×2
4m 17sDuration
36Turns
72Input
9.4kOutput
905.3kCache read
102.7kCache write
1.0MTotal tokens
quality findings 0/1/2 score 88

heal:quality — Code quality

1. Scope

Whole-repo sweep of acme/storefront (demo target), compared against the previous sweep (2026-07-05T09-01-27Z-dm03). Report-only — nothing was changed; low-risk items are marked auto-fixable under --fix.

Score 88/100 (structure 90 · duplication 85 · complexity 92 · type-safety 85) — up from 84 last sweep after the dead-export cleanup landed.

2. What was checked

The deterministic size-watch + dead-export triage ranked the read; SOLID seams, duplication (jscpd-seeded), complexity hotspots, and type-safety erosion (any/assertion counts) audited on the ranked set.

3. Findings

M — god module · order-service.ts *(persisting — 3rd sweep, escalated)*

700+ lines, 24 exports, three responsibilities (checkout, fulfilment, refunds). The size-watch has ranked it #1 three sweeps running.

  • Suggested fix: split along the checkout/fulfilment seam first (the refund logic is

small enough to stay); judgment call, never auto-applied.

  • Suggested test: none new — the existing service tests partition cleanly with the

split.

L — duplicated helper · format-price.ts *(persisting)* — the client re-implements

formatPrice that packages/shared exports; delete and re-import.

L — type erosion · cart-mapper.ts:44 *(new)* — three as any casts where the

inferred query type compiles; narrowing them keeps the DTO chain honest.

4. Smallest safe first step

Delete the duplicated price helper (5-minute PR), then schedule the order-service split — it is the root of both the complexity and half the review friction on this axis.

5. Fix summary

Report-only run; nothing changed.

TODO delta

Quality debt — 3 open item(s) converged.

Preventions for this axis 1

  • detect God module regrowth past the size-watch threshold — stays detect — module splits are judgment; size-watch already ranks it first (quality) (detect) ×3
4m 18sDuration
38Turns
74Input
11.3kOutput
1.2MCache read
127.1kCache write
1.3MTotal tokens
perf findings 0/0/2 score 90

heal:perf — Performance health

1. Scope

Whole-repo sweep of acme/storefront (demo target), compared against the previous sweep (2026-07-05T09-01-27Z-dm03). Report-only — nothing was changed; low-risk items are marked auto-fixable under --fix.

Score 90/100 (server 95 · bundle 92 · render 85) — up from 87 after the order-list N+1 fix landed.

2. What was checked

Server query shapes (N+1, over-fetch) across all list endpoints, the client bundle against its size budget, and render cost (avoidable re-renders, unbounded lists, asset loading).

3. Findings

L — unvirtualized admin table · product-table.tsx:31 *(persisting)*

Fine at demo scale; degrades past ~1k rows. Virtualize or paginate when the catalog grows — tracked, not urgent.

L — heavyweight hero asset · home.tsx:12 *(new)*

Full-resolution image, no responsive sizes, no modern format — the largest single contributor to first-paint weight on the landing page.

  • Suggested fix: responsive srcset + a compressed modern format; no layout change.

Fixed since last sweep: the order-list endpoint no longer issues one query per row — verified batched, p95 improved accordingly.

4. Smallest safe first step

Compress the hero image — one asset swap, measurable on every first visit.

5. Fix summary

Report-only run; nothing changed.

TODO delta

Performance health — 2 open item(s) converged.

3m 29sDuration
46Turns
79Input
12.6kOutput
1.5MCache read
169.3kCache write
1.7MTotal tokens
docs findings 0/2/1

heal:docs — Documentation drift

1. Scope

Whole-repo sweep of acme/storefront (demo target), compared against the previous sweep (2026-07-05T09-01-27Z-dm03). Report-only — nothing was changed; low-risk items are marked auto-fixable under --fix.

2. What was checked

Every documented command against package.json scripts, every documented env var against .env.example and actual reads in code, every cited path, and the setup walkthrough end-to-end.

3. Findings

M — removed script still documented · README.md:74 *(new)*

Copy-pasting the setup section fails at this step — the db helper script was removed two sprints ago.

  • Suggested fix: delete the step (mechanical under --fix); the replacement command

is already documented two lines below.

M — phantom env var · README.md:102 *(new)*

Documented and templated but read nowhere — either wire it or drop it. Judgment call: reported, not auto-fixed.

L — stale path · CONTRIBUTING.md:33 *(persisting)* — moved one directory up in the

last refactor; mechanical fix.

Fixed since last sweep: the wrong build command in the README was corrected.

4. Smallest safe first step

Apply the two mechanical fixes under --fix, then decide the phantom env var — it is the only judgment item on this axis.

5. Fix summary

Report-only run; nothing changed.

TODO delta

Docs drift — 3 open item(s) converged.

Preventions for this axis 1

  • new README documents removed scripts and phantom env vars — script comparing package.json scripts + env reads to README, wired pre-commit (docs) Code/Script ×1
5m 34sDuration
82Turns
164Input
21.9kOutput
4.4MCache read
224.7kCache write
4.6MTotal tokens
a11y findings 1/2/0

heal:a11y — Web accessibility

1. Scope

Whole-repo sweep of acme/storefront (demo target), compared against the previous sweep (2026-07-05T09-01-27Z-dm03). Report-only — nothing was changed; low-risk items are marked auto-fixable under --fix.

2. What was checked

Semantic HTML, accessible names/ARIA, keyboard operability, focus management, and contrast across every storefront route plus the admin pages.

3. Findings

H — placeholder-only labels · checkout-form.tsx:58 *(new — regression)*

The checkout redesign shipped inputs with placeholder text as the only label: screen readers announce empty fields, and the label vanishes the moment the user types. This is the highest-traffic form in the app.

  • Suggested fix: visible <label htmlFor> per field — mechanical, auto-fixable

under --fix, no visual redesign required (the design already reserves the space).

  • Suggested test: a render test asserting every checkout input has an accessible

name.

M — focus not trapped in modal · modal.tsx:19 *(persisting — 2nd sweep)*

Tab walks out of the dialog into the obscured page. The component library in use ships a focus-trap — wire it.

M — unnamed icon buttons · icon-button.tsx:9 *(persisting — 2nd sweep)*
  • Suggested fix: make aria-label a required prop so the gap cannot recur —

also proposed as a guardrail (see prevention backlog: recurring, promoted).

Fixed since last sweep: the muted-text contrast failure — the token was darkened and re-verified at 4.6:1.

4. Smallest safe first step

Fix the checkout labels today — one high on the money path outweighs everything else in this report combined.

5. Fix summary

Report-only run; nothing changed.

TODO delta

Accessibility health — 3 open item(s) converged.

Preventions for this axis 1

  • promote Unnamed icon buttons and placeholder-only labels — jsx-a11y lint preset + aria-label as a required prop type (a11y) Rule/Code ×2
7m 4sDuration
117Turns
234Input
23.2kOutput
11.7MCache read
312.3kCache write
12.1MTotal tokens
design findings 0/2/1 score 82

heal:design — UI consistency & design-system adherence

1. Scope

Whole-repo sweep of acme/storefront (demo target), compared against the previous sweep (2026-07-05T09-01-27Z-dm03). Report-only — nothing was changed; low-risk items are marked auto-fixable under --fix.

Score 82/100 (reuse 75 · tokens 90 · responsive 85 · gallery 80) — up from 76 after the empty-state unification landed.

2. What was checked

The jscpd duplicate-cluster precompute seeded the read: duplicated markup, raw HTML where a primitive exists, styling-token drift, responsive integrity at the mobile floor, and component-gallery completeness.

3. Findings

M — hand-rolled loading states · src/pages *(persisting — 3rd sweep, escalated)*

Three pages re-implement a spinner the design system exports; visually inconsistent and each a divergence point. Recurs despite the convention doc — promoted to a lint proposal (see prevention backlog).

M — raw <select> · filters.tsx:66 *(persisting — 3rd sweep, escalated)*

Bypasses the shared Select primitive; renders unstyled on two browsers. Same promotion.

L — off-scale spacing · summary.tsx:23 *(persisting)* — one arbitrary pixel value

outside the spacing tokens; mechanical token swap.

Fixed since last sweep: the four inconsistent empty-state layouts were unified onto the shared EmptyState component.

4. Smallest safe first step

Swap the three spinners for the shared primitive — smallest diff, and it retires the axis's most visible inconsistency.

5. Fix summary

Report-only run; nothing changed.

TODO delta

UI consistency — 3 open item(s) converged.

Preventions for this axis 1

  • promote Raw select and hand-rolled spinners bypassing shared primitives — lint no-restricted-syntax on raw select + spinner markup in app code (design) Rule ×3
7m 18sDuration
92Turns
184Input
26.6kOutput
5.2MCache read
149.4kCache write
5.3MTotal tokens

Token breakdown

Input
10.0k · $0.05
Output
353.8k · $8.84
Cache read
55.0M · $28
Cache write
3.7M · $23

967 turns · per-tier $ above priced at the run's dominant model (claude-opus-4-8) to show the tier mix — cache-read dominates the token count but is billed at a steep discount vs fresh input. The exact per-model total (~$49) is on the Cost card, which prices each healer at its own model; a Pro/Max subscription isn't billed per token.