Fixroot
Trace a bug to its structural origin and eliminate it — not patch it
---
description: Trace a bug to its structural origin and eliminate it — not patch it
allowed-tools: Read, Bash(grep:*), Bash(find:*), Bash(git:*), Bash(npm test:*), Bash(npm run test:*), Bash(npx vitest:*), Edit, Write
---
# Fix Root Command
Investigate a bug by tracing it backwards to its origin, identify the broken contract or invariant, and propose a structural fix — before touching a single line of code.
---
## Usage
/fixroot
**Examples:**
/fixroot TypeError: Cannot read properties of undefined reading 'id' /fixroot cart items disappear on page refresh /fixroot null check in three different places for the same value /fixroot API response sometimes returns [] instead of the real data
---
## Core Philosophy
Most fixes add code. The best root-cause fix deletes it.
Surface patches handle bad state. Root fixes eliminate the conditions that produce it. The symptom location is almost never the bug location — it's the first place downstream code noticed something was wrong. The bug lives upstream.
**The north star**: if the fix is correct, the downstream guards, null checks, and fallbacks that were defending against the bug become unreachable — and can be deleted.
---
## What This Command Does NOT Do
- Add a null check where null shouldn't exist
- Wrap errors in try/catch instead of preventing them
- Add a fallback `|| []` to mask a contract violation
- Move a guard to a "better" location (still a patch)
- Touch any code before the investigation is complete and signed off
---
## Workflow
### Phase 1 — LOCATE: Find the symptom
Identify where the bug manifests. Note the surface-patch pattern already present (if any):
| Pattern | What it signals |
|---------|----------------|
| `?.` optional chain | Caller doesn't trust callee to return defined value |
| `\|\| []` / `\|\| {}` fallback | Bad data reaching a consumer that expects clean data |
| `try/catch` returning null/empty | Error being swallowed instead of prevented |
| `setTimeout` / `useEffect` timing hack | Async ordering assumption that isn't guaranteed |
| `as SomeType` TypeScript cast | Type system being bypassed because actual type is wrong |
| Same null-check in 2+ places | **Rule of Two** — contract is broken at the source |
### Phase 2 — TRACE: Follow the bad value upstream
Walk backwards through the call stack and data flow. Ask at each step:
> "Where does this value come from? Could it be wrong there?"
Keep going until you reach either:
- The system boundary (user input, external API, database read) — that's the origin
- A component that violates a contract it promised to fulfill — that's the bug
### Phase 3 — NAME: Identify the broken contract or invariant
State explicitly:
- **What should always be true** at the bug site that isn't
- **Which two layers** have a mismatched assumption (caller expects X, callee produces Y)
- **Why the type/schema/model permits a state** that should be impossible
### Phase 4 — PROPOSE: Deletion-first fix
Present the structural fix. The proposal must answer:
1. Where exactly does the fix live? (origin, not symptom site)
2. What code gets deleted as a result?
3. What minimal change makes the bad state structurally impossible?
Show a before/after. Net lines should go down or stay flat — never up.
### Phase 5 — GATE: Await sign-off
**Stop here.** Present the full investigation report and ask:
> "Does this match what you expected? Should I implement it?"
Do not proceed until confirmed.
### Phase 6 — IMPLEMENT: Fix at the origin
- Change the source, not the consumers
- Delete downstream guards that are now unreachable
- Run tests
### Phase 7 — VERIFY: Confirm structural improvement
Report:
- Tests passing
- Net lines deleted
- Which downstream guards were removed
---
## Investigation Report Format
Symptom
[Where and how it manifests. File:line if known.]
Trace
[The path backwards. Origin → ... → symptom site.]
Broken Contract
[Caller assumed X. Callee produces Y. The mismatch.]
Invariant That Should Hold
[What should always be true here. Why it currently isn't.]
Proposed Fix
[What changes, where, and why it's structural not cosmetic.]
Origin file: path/to/file.ts:line Fix type: [delete bad state / enforce type / fix contract / restructure data flow]
Before: [relevant code]
After: [structural fix]
Code deleted downstream (now unreachable):
- path/to/file.ts:line — [the guard/fallback/check that becomes dead]
- ...
Net change: -N lines
Proceed? (y / adjust / explain more)
---
## Example Session
User: /fixroot user.settings is undefined three places in the codebase
Agent:
Symptom
user.settings is accessed with optional chaining (user.settings?.theme,
user.settings?.notifications, user.settings?.timezone) in three files:
- src/components/Header.tsx:42
- src/pages/Dashboard.tsx:88
- src/hooks/usePreferences.ts:15
The Rule of Two applies — same guard in 3 places means the contract is broken upstream, not in each consumer.
Trace
Following user object upstream:
usePreferences.ts:15 → receives user from useAuth()
useAuth.ts:34 → returns user from Supabase session
supabase/auth.ts:21 → maps raw session to User type
Found: User type definition
type User = { id: string; email: string; settings?: UserSettings }
^
settings is optional. But the Supabase session always includes settings
(confirmed in DB schema — settings column is NOT NULL with default).
The type is lying about what the data actually guarantees.
Broken Contract
Caller (every component) assumes settings may be undefined.
DB schema guarantees it's always present.
Type definition is wrong — it declared optional what is actually required.
Invariant That Should Hold
Every authenticated user object should have a fully populated settings
object. The DB enforces this. The application type should too.
Proposed Fix
**Ori ```
Maintain Fixroot?
Let people know it's listed here — add the badge (live metrics, light/dark aware) or a plain link to your README or docs.
[Fixroot on getagentictools](https://getagentictools.com/loops/superfastsat1600-fix-root-command?ref=badge) npx agentictools info loops/superfastsat1600-fix-root-command The second line is the CLI lookup for this page — handy in READMEs and docs.