Systematic Debugging
Find root cause before attempting fixes. Random fixes waste time and create new bugs.
The Iron Law
No fixes without root cause investigation first. If you haven't completed Phase 1, do not propose fixes.
Phase 1: Root Cause Investigation
BEFORE attempting ANY fix:
Step 1: Read Error Messages Carefully
- Read stack traces completely — note file paths, line numbers, error codes
- Check the terminal output from
pnpm lint:tsc,pnpm test, orpnpm build - In this repo, common error sources: TypeScript strict mode violations, ESLint boundary violations, proto type mismatches
Step 2: Reproduce Consistently
- For test failures:
cd apps/<app> && TZ=America/Los_Angeles pnpm vitest run path/to/file.test.tsx(same shape from apackages/*dir) - For type errors:
pnpm lint:tsc - For lint errors:
pnpm lint - For build failures:
pnpm clean && pnpm build - If not reproducible, gather more data — do not guess
Step 3: Check Recent Changes
git difffor unstaged changesgit log --oneline -10for recent commitsgit diff HEAD~3to see what changed recently- Check if proto types changed (
@bufteam/*packages). If the pinned protos look stale, flag it and propose a user-confirmed bump — do not runbuf-bumpyourself. It is a gated, side-effecting skill that rebuilds packages and patches code; let the user invoke it.
Step 3.5: Suspect Silent Failures
- A passing test or clean type-check is not proof the bug is gone — confirm the code path actually executed. Ask: would this failure mode pass silently?
- Watch for swallowed errors (empty
catch, defaulting tonull/[]), a mock that hides a real call, a feature flag short-circuiting the path, or an effect that never ran. - When you fix a silent failure, add a canary: a positive assertion that fails loudly if the workaround breaks or the bad state returns — not a guard that quietly hides it.
Step 4: Trace the Data Flow
- For RPC issues: trace from server action → Connect RPC client → proto types → form state
- For component issues: trace from data source →
rpcToForm*converter → form state → component props - For test failures: check if mocks from
@hadrian-mtv/vitest-utilsmatch current interfaces - See
references/root-cause-tracing.mdfor the full backward tracing technique
Phase 2: Pattern Analysis
- Find working examples — locate similar working code in the same app or domain
- Compare — what's different between working and broken code?
- Check shared packages — is the issue in a shared package rather than the app? List them
with
ls packages/— do not rely on a memorized list; they drift - Check domain boundaries — does the import violate
eslint-plugin-boundariesrules?
Phase 3: Hypothesis and Testing
- State your hypothesis clearly — "I think X is the root cause because Y"
- Make the smallest possible change to test it — one variable at a time
- Verify — run the specific failing test or lint check
- If it didn't work — form a NEW hypothesis. Do not add more fixes on top
After 3+ Failed Fixes: Stop and Reassess
- Each fix revealing new problems in different places = architectural issue
- Ask the user before attempting more fixes
- Consider whether the pattern is fundamentally wrong vs. a surface bug
Phase 4: Implementation
- Run the failing test to confirm it fails:
cd apps/<app> && TZ=America/Los_Angeles pnpm vitest run -t "test name" - Implement a single fix addressing root cause — one change at a time
- Verify the fix:
TZ=America/Los_Angeles pnpm vitest run path/to/file.test.tsx— specific test passespnpm lint:tsc— no type errors introducedpnpm lint— no lint violations
- If fix doesn't work — return to Phase 1 with new information. Do not retry blindly.
Common Issues in This Repo
| Symptom | Likely Cause | Fix |
|---|---|---|
Type 'X' is not assignable to type 'Y' in proto types |
Proto dependency out of date | Flag for a user-confirmed pnpm buf-bump:<service> (don't auto-run) |
Cannot find module '@hadrian-mtv/...' |
Package not built | pnpm build from root |
| ESLint boundary violation | Cross-domain import | Move shared logic to app/_lib/ or a shared package |
| Hydration mismatch | Server/client rendering difference | Check for typeof window, date formatting, browser-only APIs |
console.* lint error |
Wrong logger | Use @hadrian-mtv/flow-logger instead |
next/link lint error |
Wrong navigation component | Use FlowLink/FlowLinkButton from @hadrian-mtv/flow-navigation |
| Test fails with timezone error | Missing TZ env var | Prefix with TZ=America/Los_Angeles |
| Stale build artifacts | Turbo cache | pnpm clean && pnpm build |
Red Flags — Stop and Restart Investigation
If you catch yourself:
- Proposing fixes before tracing the data flow
- Adding multiple changes at once
- Saying "let's try this" without a hypothesis
- Skipping test verification after a fix
- On your 3rd+ fix attempt without going back to Phase 1