Imported from tylerjrbuell/reactive-agents-ts (
.agents/skills/codebase-health-sweep/SKILL.md). Install upstream withnpx skills add tylerjrbuell/reactive-agents-ts --skill codebase-health-sweep. Copyright stays with the author.
Codebase Health Sweep
Structured bug-finding, prioritization, and safe-fix loop for the reactive-agents-ts monorepo. Three phases: SCAN (parallel agent sweep) → TRIAGE (prioritize by severity + risk) → FIX (execute bounded safe fixes, file the rest).
Guiding principle: Fix what's safe in one pass. File everything else. Never guess at correctness — verify with the build and test suite.
When to Use
- Pre-release: before cutting a version tag
- Post-merge: after a large decomposition or refactor lands
- Periodic maintenance: scheduled sweep (weekly/monthly cadence)
- Build is RED or test count regressed unexpectedly
as any,@ts-ignore, or// TODOdensity has grown
Don't use for:
- A specific known bug → use
kernel-debuginstead - Architectural drift only → use
architecture-audit - Effect-TS abstraction opportunities only → use
effect-abstraction-audit
Prerequisites — Orient First
Before launching agents, spend 5 minutes on current state. Don't skip.
# Build baseline
bun run build 2>&1 | tail -30
# Test baseline
bun test 2>&1 | tail -40
# TypeScript errors (authoritative for DTS issues)
bunx turbo run build --filter=... 2>&1 | grep -E "error TS|Type error" | head -30
# Recent changes (last 10 commits)
git log --oneline -10
Query the wiki for prior sweep results to avoid re-filing known issues:
claude-obsidian:wiki-query "health sweep bugs issues YYYY"
claude-obsidian:wiki-query "running issues log"
Read:
wiki/Hot.md— current priorities, in-flight workwiki/Issues/Running Issues Log.md— known open issues (don't re-file these)
Record the baseline before proceeding: build status (GREEN/RED), test count, failure count. You need this to measure "did we improve?"
Phase 1 — SCAN (parallel, ~10 minutes)
Launch four agents concurrently via the Agent tool. Each gets the baseline counts and is told NOT to fix anything — scan only.
Agent A: Type Safety & Compiler Errors
Scan every package for:
- TypeScript errors —
as any,@ts-ignore,// @ts-nocheck, unsafe casts, missing generics onEffect<unknown>/Layer<unknown> - Dead exports — public API exports with zero callers inside the monorepo (
grep -r "from '.*package-name'"across all packages) - Implicit any — function parameters missing types, untyped return values in public APIs
- Type drift — interfaces that claim
Abut returnBat runtime (look foras unknown as X)
Scope: all packages/*/src/**/*.ts and apps/*/src/**/*.ts. Skip *.test.ts for type-safety scan (test files have intentional loose typing).
Output per finding: file:line | severity(P0/P1/P2) | one-line description | fix-direction
Agent B: Bug Patterns & Runtime Risks
Scan for:
- Unhandled promise rejections —
Promisecalls not wrapped inEffect.tryPromise, bare.catch()that swallows errors - Null/undefined deref risks — optional chaining missing where shape is nullable, array access without bounds check in hot paths
- Effect error channel leaks —
Effect.runPromisewithout.catchat call sites;pipe(Effect.catch(...))with empty handler - Race conditions — shared mutable state mutated across concurrent Effect fibers without
Ref - Incorrect termination paths — any site that calls
process.exitorthrowoutside of top-level runners (kernel loop, CLI entrypoints) - Test–production divergence —
// for testing onlycomments in non-test files; conditional behavior onprocess.env.NODE_ENV
Output per finding: file:line | severity | description | fix-direction
Agent C: Inefficiencies & Dead Weight
Scan for:
- Dead code — functions/classes/types exported but unreferenced;
// disabled,// TODO: remove,// legacycomments attached to active code - Redundant rebuilds —
packages/*/distchecked into git; build artifacts that should be.gitignored - Duplicate logic — near-identical blocks (>10 lines) appearing in 2+ files; provider-specific parsing that could share a helper
- Over-large files — files >500 LOC in packages that have a decomposition pattern in play; files >1000 LOC anywhere
- Expensive operations in hot paths —
JSON.parse/stringifyinside tight loops; synchronous file reads in kernel loop phases;Array.fromon already-array values - Stale dependencies —
package.jsonentries with^ranges that resolve to a version known to break (check againstMEMORY.mdfeedback_bun_version_pin.md)
Output per finding: file:line | severity | description | effort(S/M/L)
Agent D: Test Coverage & Quality Gaps
Scan for:
- Test failures — run
bun testin each package; surface net-new failures vs baseline - Skipped/xfailed tests —
.skip,.todo,it.skip,describe.skipwithout a tracking comment - Missing coverage for public exports — exported symbols in
packages/*/src/index.tswith no corresponding test file - Flaky test signatures — tests with
setTimeout,sleep, fixed delays, orDate.now()comparisons without tolerance - Mock–production divergence — mocks that don't match current interface signatures (detect via
as unknown ascasts in test setup)
Output per finding: package | test-name or file:line | severity | description
Phase 1.5 — VERIFY (MANDATORY before TRIAGE)
Why this exists: the 2026-05-21 audit-of-audit found 3/31 prior sweep items shipped with bad framing (HS-18 orthogonal-not-superseded, HS-22 65→9 emit sites, HS-31 74→55 casts). Pattern: agents grepped without semantic verification, then committed bad counts to the register.
Rule: every finding from Phase 1 must have a verified-by: line BEFORE it can be triaged. No exceptions.
Verification protocol per finding
For each finding, the sweep-runner re-runs the exact claim with explicit evidence:
finding: "Provider retry loops overwrite lastError across 5 providers"
verified-by:
- grep -n "lastError = " packages/llm-provider/src/providers/{anthropic,openai,gemini,local,litellm}.ts
- 5 matches, 1 per file, all inside retry loops (lines: anthropic 346, openai 486, gemini 575, local 691, litellm 479)
- Each loop has no error accumulator — only `lastError = e` reassignment
Acceptable evidence shapes:
grep -c <pattern> <file>→ exact countwc -l <file>→ exact LOCgrep -n <pattern> <file>→ file:line list- File read of named lines (paste 3-line context if claiming a semantic concern)
- For runtime claims: trace/test that demonstrates the behavior
Unacceptable:
- "grep showed about 60 matches" (no exact number, no command)
- "looks like duplication" (no diff'd block, no LOC)
- "should be unused" (no zero-callers proof)
- Counting
grep -rn <pattern> | wc -lfor "occurrences" — that counts MATCH-LINES, which is ≥ occurrences when multiple matches share a line. Usegrep -ro <pattern> | wc -lfor occurrences.
Inflation guard for common patterns
| Claim shape | Common inflation | Correct check |
|---|---|---|
| "N duplicated lines" | grep counts ALL matches across files | Diff the blocks; count semantically equivalent ones |
"N as any casts" |
grep -c counts lines, not casts |
grep -ro 'as any' | wc -l |
| "@deprecated annotations" | Counts include re-export sites | Check declaration site only |
| "File >1500 LOC" | Includes blank lines + comments | wc -l is fine, but note effective code LOC |
| "Zero tests" | Misses tests/ vs test/ vs tests/ directory variations | Check all 3 conventions |
Verified-by carries forward to GH issue
When migrating findings to a GitHub issue (template audit-finding.yml), the verified-by text becomes a required body field. The template will reject submission without it.
Phase 2 — TRIAGE
Wait for all four agents. Merge findings into a single prioritized register.
Severity Levels
| Level | Criteria | Action |
|---|---|---|
| P0 | Build broken, test suite regressed, data corruption risk, process crash | Fix this sweep |
| P1 | Type error leaks to public API, unhandled runtime error path, test skip with no owner | Fix this sweep if bounded |
| P2 | Inefficiency, dead code, coverage gap, style drift | File for planning |
Safe-Fix Criteria (fix now)
A finding is safe to fix THIS sweep if ALL of:
- Change is ≤ 25 lines
- Touches ≤ 2 files
- No behavioral change (type annotation fixes, dead export removal, import cleanup)
- OR the fix is additive-only (add a missing
.catch, add a type guard) - Has a test covering the affected path (or fix IS adding the test)
If ANY criterion fails → file it, don't fix it.
Register Format
Append findings to wiki/Issues/Running Issues Log.md under a dated section:
## Health Sweep — YYYY-MM-DD
| ID | Agent | File | Severity | Description | Status | Fix-Direction |
|----|-------|------|----------|-------------|--------|---------------|
| HS-01 | A | packages/reasoning/src/kernel/loop/runner.ts:423 | P1 | `as any` cast on KernelState.meta | FIX-THIS-SWEEP | Add `Record<string, unknown>` type |
| HS-02 | C | packages/runtime/src/execution-engine/... | P2 | 847 LOC, decomposition candidate | FILE | Plan as W26 decomp |
De-duplicate against existing open issues. If a finding already has an entry, update its Status rather than adding a duplicate row.
Phase 3 — FIX
Work through the safe-fix list in P0 → P1 order. One fix at a time, verify after each.
Fix Loop
1. Pick next P0/P1 safe fix from register
2. Edit the file(s)
3. Build affected package: cd packages/<name> && bun run build
4. Run affected tests: bun test packages/<name>
5. Net new failures? → revert fix, mark HS-XX as NEEDS-PLANNING
6. Tests pass? → mark HS-XX as FIXED, commit
7. Repeat
Commit Convention
fix(<package>): <one-line description>
Health sweep YYYY-MM-DD: HS-XX
<what was wrong, one sentence>
<what changed, one sentence>
Tests: N ran, M fail (= baseline M, zero new regressions)
No Co-Authored-By lines. See user memory.
What NOT to Do
- Don't batch multiple unrelated fixes in one commit — one finding per commit
- Don't fix P2 items inline with P0/P1 fixes — separate commits or defer
- Don't remove code you're unsure is dead — grep the full monorepo first (
grep -r "functionName" packages/) - Don't break a passing test to make the fix work — fix the fix
Output
When sweep is complete, report:
- Baseline vs final: build status, test count before/after
- Fixed this sweep: list of HS-IDs with one-line description
- Filed for planning: count of P1/P2 items added to Issues Log
- Top 3 P2 opportunities worth prioritizing next sprint
Keep the summary under 15 lines. Full detail lives in the Issues Log.
Wiki Integration
After each sweep:
claude-obsidian:save → wiki/Research/Debriefs/YYYY-MM-DD-health-sweep-debrief.md
Include: baseline, findings summary, what was fixed, what was filed, and any surprising patterns (new failure mode? new debt area?). Link to the Issues Log section via [[wiki/Issues/Running Issues Log#Health Sweep YYYY-MM-DD]].
Run lint after if new pages were added:
claude-obsidian:wiki-lint
Quick Reference
# Build all
bun run build
# Test all (with output)
bun test 2>&1 | tail -50
# Test single package
cd packages/<name> && bun test
# Find dead exports (example)
grep -r "export.*functionName" packages/ apps/
# TypeScript errors only
bunx turbo run build 2>&1 | grep "error TS"
# Count TODOs/FIXMEs
grep -r "TODO\|FIXME\|HACK\|as any\|@ts-ignore" packages/*/src --include="*.ts" | wc -l
Anti-Patterns
| Anti-pattern | Why bad |
|---|---|
| Fix while scanning | Pollutes the baseline; you lose the before/after signal |
| Fix P2 items in the P0/P1 pass | Scope creep; P2 needs a plan |
| File a finding already in the Issues Log | Creates duplicate tracking debt |
| Commit multiple unrelated fixes together | Makes bisect impossible |
| Remove export without grepping monorepo | "Unused" in one package may be used in another |
| Skip the build verify after each fix | Type-correct ≠ build-correct in a multi-package monorepo |