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

6.3 KiB

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.

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

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