Files
msd-core/gsd-core/references/common-bug-patterns.md
Tom Boucher 6baa2a8182 feat(#1961): add bug-taxonomy classification + strategy routing to gsd-debugger (#2407)
* test(#1961): add failing-first bug-taxonomy routing contract tests

Epic #1957 Phase 2B. Source-text-is-the-product contract tests (3 taxonomy
classes, explicit class->technique routing table, Bohrbug->repro+SBFL+bisect,
Heisenbug->record-replay/stability+SKIP-SBFL, Concurrency->atomicity/order/
deadlock checklist, bug_class in DEBUG Current Focus, supersede-not-append)
plus a routing-table specification object pinning the documented decisions
(SBFL forbidden on Heisenbug is the load-bearing 1B/2B seam).

Failing-first: reference, Phase 1.75, and routing-table reframe do not yet exist.

* feat(#1961): add bug-taxonomy classification + strategy routing to gsd-debugger

Epic #1957 Phase 2B (reliability-critical). Adds Phase 1.75: classify the
failure as Bohrbug / Heisenbug-Mandelbug / Concurrency, then route the
investigation technique via an explicit class->technique table (Kernighan: no
opaque heuristic). Bohrbug -> reproduction + SBFL (Phase 1.25) + git bisect;
Heisenbug/Mandelbug -> record-replay (rr) + stability-stress + statistical
sampling, with SBFL explicitly SKIPPED (a flaky spectrum poisons the Ochiai
ranking — the load-bearing 1B/2B seam); Concurrency -> the
atomicity/order/deadlock checklist first.

Reframes (supersedes, not appends — Zawinski) the flat 'Technique Selection by
situation' table into a class-routed table; the 11 techniques remain as routed
targets. bug_class recorded in Current Focus (DEBUG template); common-bug-
patterns catalog cross-referenced to the taxonomy.

Full rules extracted to gsd-core/references/debugger-bug-taxonomy.md. INVENTORY
+ manifest + agent-size baseline + install-parity goldens + AGENTS.md updated.

* fix(#1961): address orthogonal review (phase-name drift, General lane, revoke framing, row-scoped tests, bounding)

- HIGH: reference said 'Phase 1B' (epic shorthand); corrected to the deployed
  'Phase 1.25' (matches the agent + SBFL reference).
- HIGH: 6 of 11 techniques (Rubber duck, Delta, Working backwards,
  Differential, Comment-out, Follow-the-indirection) were orphaned by the
  situation-table reframe. Added a 'General (any class, situation-cued)'
  lane to BOTH the reference routing table and the agent's Technique
  Selection table that re-homes them — supersede-not-append now holds.
- MEDIUM: the SBFL-skip is structurally retroactive (Phase 1.25 runs before
  Phase 1.75 classification), so reframed the table column from 'Do NOT use'
  to 'Revoke if already run' + an explicit 'retroactive revocation, not
  proactive skip' note stating the ordering honestly.
- MEDIUM: contract tests are now row-scoped (parse the table by class, assert
  per-row) instead of presence-only; added a guard that the previously-
  orphaned techniques now have a General-lane route.
- LOW: pinned the canonical bug_class value form (lowercase-kebab:
  bohrbug|heisenbug-mandelbug|concurrency; prose may use title-case).
- NIT: added a 'Bound the Heisenbug-chase runs' note (rr/stability/sampling
  timeouts) per the unbounded-subprocess gauntlet.

* chore(#1961): backfill changeset pr number (PR #2407)
2026-07-18 14:42:29 -04:00

128 lines
6.3 KiB
Markdown

# Common Bug Patterns
Checklist of frequent bug patterns to scan before forming hypotheses. Ordered by frequency. Check these FIRST — they cover ~80% of bugs across all technology stacks.
<patterns>
## Null / Undefined Access
- **Null property access** — accessing property on `null` or `undefined`, missing null check or optional chaining
- **Missing return value** — function returns `undefined` instead of expected value, missing `return` statement or wrong branch
- **Destructuring null** — array/object destructuring on `null`/`undefined`, API returned error shape instead of data
- **Undefaulted optional** — optional parameter used without default, caller omitted argument
## Off-by-One / Boundary
- **Wrong loop bound** — loop starts at 1 instead of 0, or ends at `length` instead of `length - 1`
- **Fence-post error** — "N items need N-1 separators" miscounted
- **Inclusive vs exclusive** — range boundary `<` vs `<=`, slice/substring end index
- **Empty collection** — `.length === 0` falls through to logic assuming items exist
## Async / Timing
- **Missing await** — async function called without `await`, gets Promise object instead of resolved value
- **Race condition** — two async operations read/write same state without coordination
- **Stale closure** — callback captures old variable value, not current one
- **Initialization order** — event handler fires before setup complete
- **Leaked timer** — timeout/interval not cleaned up, fires after component/context destroyed
## State Management
- **Shared mutation** — object/array modified in place affects other consumers
- **Stale render** — state updated but UI not re-rendered, missing reactive trigger or wrong reference
- **Stale handler state** — closure captures state at bind time, not current value
- **Dual source of truth** — same data stored in two places, one gets out of sync
- **Invalid transition** — state machine allows transition missing guard condition
## Import / Module
- **Circular dependency** — module A imports B, B imports A, one gets `undefined`
- **Export mismatch** — default vs named export, `import X` vs `import { X }`
- **Wrong extension** — `.js` vs `.cjs` vs `.mjs`, `.ts` vs `.tsx`
- **Path case sensitivity** — works on Windows/macOS, fails on Linux
- **Missing extension** — ESM requires explicit file extensions in imports
## Type / Coercion
- **String vs number compare** — `"5" > "10"` is `true` (lexicographic), `5 > 10` is `false`
- **Implicit coercion** — `==` instead of `===`, truthy/falsy surprises (`0`, `""`, `[]`)
- **Numeric precision** — `0.1 + 0.2 !== 0.3`, large integers lose precision
- **Falsy valid value** — value is `0` or `""` which is valid but falsy
## Environment / Config
- **Missing env var** — environment variable missing or wrong value in dev vs prod vs CI
- **Hardcoded path** — works on one machine, fails on another
- **Port conflict** — port already in use, previous process still running
- **Permission denied** — different user/group in deployment
- **Missing dependency** — not in package.json or not installed
## Data Shape / API Contract
- **Changed response shape** — backend updated, frontend expects old format
- **Wrong container type** — array where object expected or vice versa, `data` vs `data.results` vs `data[0]`
- **Missing required field** — required field omitted in payload, backend returns validation error
- **Date format mismatch** — ISO string vs timestamp vs locale string
- **Encoding mismatch** — UTF-8 vs Latin-1, URL encoding, HTML entities
## Regex / String
- **Sticky lastIndex** — regex `g` flag with `.test()` then `.exec()`, `lastIndex` not reset between calls
- **Missing escape** — `.` matches any char, `$` is special, backslash needs doubling
- **Greedy overmatch** — `.*` eats through delimiters, need `.*?`
- **Wrong quote type** — string interpolation needs backticks for template literals
## Error Handling
- **Swallowed error** — empty `catch {}` or logs but doesn't rethrow/handle
- **Wrong error type** — catches base `Error` when specific type needed
- **Error in handler** — cleanup code throws, masking original error
- **Unhandled rejection** — missing `.catch()` or try/catch around `await`
## Scope / Closure
- **Variable shadowing** — inner scope declares same name, hides outer variable
- **Loop variable capture** — all closures share same `var i`, use `let` or bind
- **Lost this binding** — callback loses context, need `.bind()` or arrow function
- **Scope confusion** — `var` hoisted to function, `let`/`const` block-scoped
</patterns>
<usage>
## How to Use This Checklist
1. **Before forming any hypothesis**, scan the relevant categories based on the symptom
2. **Match symptom to pattern** — if the bug involves "undefined is not an object", check Null/Undefined first
3. **Each checked pattern is a hypothesis candidate** — verify or eliminate with evidence
4. **If no pattern matches**, proceed to open-ended investigation
### Pattern categories → bug taxonomy (Phase 1.75)
The categories here feed bug-class classification (see `debugger-bug-taxonomy.md`):
| Pattern category | Typical bug_class |
|---|---|
| Null / Undefined, Off-by-One, State, Import, Type, Regex, Error Handling, Scope | Bohrbug (deterministic) |
| Async / Timing (intermittent, leaked timer, init order) | Heisenbug / Concurrency |
| Environment / Config (works-here-not-there) | Heisenbug / Mandelbug (or config-as-root-cause) |
| Data Shape / API Contract | Bohrbug (or Mandelbug if volume-dependent) |
The taxonomy routes the investigation technique (SBFL + bisect for Bohrbugs; record-replay/stability for Heisenbugs; atomicity/order/deadlock checklist for Concurrency).
### Symptom-to-Category Quick Map
| Symptom | Check First |
|---------|------------|
| "Cannot read property of undefined/null" | Null/Undefined Access |
| "X is not a function" | Import/Module, Type/Coercion |
| Works sometimes, fails sometimes | Async/Timing, State Management |
| Works locally, fails in CI/prod | Environment/Config |
| Wrong data displayed | Data Shape, State Management |
| Off by one item / missing last item | Off-by-One/Boundary |
| "Unexpected token" / parse error | Data Shape, Type/Coercion |
| Memory leak / growing resource usage | Async/Timing (cleanup), Scope/Closure |
| Infinite loop / max call stack | State Management, Async/Timing |
</usage>