Bug Hunting Workflow
Intent
This skill is for finding real bugs in the implementation, not guessing. It forces a discovery phase (build a mental model, reproduce, narrow scope) before making changes.
You must prioritize:
- correctness and durability
- clear reproduction
- minimal fix + regression test
Non-negotiable rules
- No fix without a reproduction (a failing test, a minimal repro program, or a deterministic HTTP/CLI script).
- No durability regressions: do not weaken WAL/manifest/atomic rename/fsync ordering.
- No “drive-by refactors” while bug hunting; keep patches tight and reviewable.
- After changes, you must run the full quality suite from
code-quality.
Discovery phase (required)
0) Establish the boundaries
Before opening files, answer (in notes) these questions:
- Which surface is affected? CLI, HTTP, embedded API, wasm, or ffi?
- Is it a correctness issue (wrong results), durability issue (lost/duplicated data), or stability issue (panic/hang)?
- Does it require a specific feature flag (
vectors, zstd, gpu, wasm/IndexedDB, ffi)?
- Is the failure deterministic or intermittent?
1) Baseline the repo (don’t trust your local state)
Run in workspace root:
cargo fmt --all
cargo clippy --all --all-features --all-targets -- -D warnings
cargo test --all --all-features
If anything fails here, you may be chasing a known failure; fix/resolve that first.
2) Map the architecture you’re about to debug
Create a quick map in notes (1–2 minutes):
searchlite-core: schema, indexing, WAL/manifest, segments, query execution, filters/aggs, vectors
searchlite-cli: user-facing commands and JSON output formatting
searchlite-http: endpoints (/init, /add, /bulk, /delete, /commit, /refresh, /search, /inspect, /stats, /compact), limits/timeouts/concurrency
searchlite-wasm: bindings + IndexedDB storage semantics (no fsync)
searchlite-ffi: C ABI / safety edges
This map guides where you look first.
3) Reproduce the bug with the smallest possible corpus
Produce a “minimal reproducible case” artifact set:
- a schema JSON (small)
- a small NDJSON/doc list (5–50 docs)
- one request (HTTP JSON or CLI command) that fails
For HTTP:
- include the server flags/env (bind addr, index path, max-body-bytes, max-concurrency, request-timeout, refresh-on-commit)
For CLI:
- include exact commands and any flags
Goal: a script a teammate can run in <60 seconds.
4) Turn on observability (don’t add prints yet)
Use existing debugging features first:
- For search correctness: add
explain: true to the request.
- For performance/odd slowdowns while reproducing: add
profile: true.
- For server behavior: run with
RUST_LOG=debug (or more targeted module filters) and capture the relevant lines.
If your issue is “writes not visible,” verify lifecycle:
- you must
commit
- you may need
/refresh in HTTP depending on config
5) Narrow the fault line by bisecting the pipeline
Pick one axis at a time:
Correctness axis
- Does the doc get ingested successfully?
- Does it appear in
/stats documents?
- Does
/inspect show a new segment after commit?
- Does the query match in bm25 mode vs wand/bmw mode?
Durability axis
- Is WAL appended correctly?
- Is manifest updated atomically?
- Is WAL truncated only after manifest persistence?
- After restart, does replay do the expected thing?
HTTP axis
- Is the request being rejected due to max body / timeouts / concurrency?
- Is NDJSON streaming partial and rolling back?
- Are errors correctly surfaced as
{"error":{"type","reason"}}?
6) Perform a targeted static sweep of the suspected modules
Once you know the rough area, do a deliberate scan for bug smells:
Search patterns to grep/ripgrep for in the relevant crate:
- panics:
unwrap(), expect(, panic!, unreachable!, todo!
- suspicious error handling:
map_err(|_|, ok()? in non-optional contexts, silent let _ =
- integer edge cases: casts (
as usize), underflow (- 1), unchecked indexing
- concurrency hazards:
Mutex/RwLock lock ordering, .await while holding locks, channels without bounds
- IO hazards: missing
fsync, non-atomic file replace, partial writes not handled, directory fsync omissions
- schema mismatch hazards: stringly-typed field lookups, analyzer lookup defaults, missing nested path validation
- vector hazards: dimension mismatch, normalization assumptions, unchecked candidate sizing,
NaN propagation
You are not “fixing” in this step—just identifying suspects.
7) Dynamic bug probes by subsystem (choose what fits)
A) WAL / manifest / commit / rollback
If you suspect commit/durability issues:
- Reproduce a crash-window scenario:
- ingest docs
- start commit
- force termination mid-way (or simulate via test hooks if present)
- restart and observe replay
- Validate the invariants:
- manifest atomic update (rename) and directory sync
- WAL truncation happens after manifest sync
- rollback cleans new segment artifacts
If you can’t safely crash in tests, create a unit test around writer/commit phases and validate state.
B) Segment lifecycle / compaction / cleanup
If you see duplicates, ghosts, or bloat:
- Inspect segments via
inspect and compare to stats
- Run compaction and verify:
- live docs preserved
- deleted/tombstoned dropped
- old segment files removed (or expected to remain until cleanup)
C) Query execution modes
If you suspect scoring/pruning bugs:
- Run the same query with
execution=bm25, execution=wand, execution=bmw
- Results should match for deterministic corpora (unless the contract explicitly says otherwise)
- If they diverge, you likely have a pruning-bound bug or block-max metadata mismatch
D) Filters/aggs/nested correctness
If filters/aggs are wrong:
- Verify required fields are
fast: true
- For nested, verify the filter uses a
Nested block and that clauses bind to the same object instance
- Add a repro doc with two nested objects that would catch “cross-object” leakage
E) Vectors (feature vectors)
If vector behavior is wrong:
- Check dimension mismatch handling
- Check cosine normalization expectations
- Confirm ANN parameters (
candidate_size, ef_search) don’t overflow or accept nonsensical values
- Verify hybrid search doesn’t double-count or drop vector scores
Fix phase (required)
1) Write the failing regression test first
- Prefer a unit test in the most local crate if possible.
- If the bug is surface-level behavior (HTTP contract, CLI output), add an integration test.
- Make it deterministic and small.
- Assert on structure, not strings (parse JSON output).
2) Implement the minimal fix
- Tight patch, minimal diff.
- Keep durability semantics intact.
- Avoid introducing allocations/locks in hot paths unless justified.
3) Prove it
Run:
cargo test --all --all-features
- Any relevant examples (e.g. pruning example if you touched pruning logic)
- If the bug touched performance-sensitive code, run the relevant bench before/after.
4) Add a “root cause” note
In the PR description or internal notes, include:
- symptom
- root cause
- why the fix is correct
- what test prevents regressions
Output format (what Codex should produce)
When using this skill, produce:
- Repro artifact (schema + docs + request/command)
- Fault localization (files + functions involved)
- Root cause analysis
- Fix plan (minimal changes)
- Regression test (where it lives and what it asserts)
- Validation commands run (from code-quality)
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: bug-hunting3description: Structured workflow for discovering, reproducing, and fixing bugs in Searchlite by inspecting real code paths (CLI/HTTP/core), validating durability invariants, and producing regression tests; use when investigating incorrect results, crashes, data loss risks, or “it sometimes fails” reports. Use when this capability is needed.4---56# Bug Hunting Workflow78## Intent910This skill is for **finding real bugs in the implementation**, not guessing. It forces a discovery phase (build a mental model, reproduce, narrow scope) before making changes.1112You must prioritize:13141. correctness and durability152. clear reproduction163. minimal fix + regression test1718---1920## Non-negotiable rules2122- **No fix without a reproduction** (a failing test, a minimal repro program, or a deterministic HTTP/CLI script).23- **No durability regressions**: do not weaken WAL/manifest/atomic rename/fsync ordering.24- **No “drive-by refactors”** while bug hunting; keep patches tight and reviewable.25- After changes, you must run the full quality suite from `code-quality`.2627---2829## Discovery phase (required)3031### 0) Establish the boundaries3233Before opening files, answer (in notes) these questions:3435- Which surface is affected? **CLI**, **HTTP**, **embedded API**, **wasm**, or **ffi**?36- Is it a **correctness** issue (wrong results), **durability** issue (lost/duplicated data), or **stability** issue (panic/hang)?37- Does it require a specific feature flag (`vectors`, `zstd`, `gpu`, wasm/IndexedDB, ffi)?38- Is the failure deterministic or intermittent?3940### 1) Baseline the repo (don’t trust your local state)4142Run in workspace root:4344- `cargo fmt --all`45- `cargo clippy --all --all-features --all-targets -- -D warnings`46- `cargo test --all --all-features`4748If anything fails here, you may be chasing a known failure; fix/resolve that first.4950### 2) Map the architecture you’re about to debug5152Create a quick map in notes (1–2 minutes):5354- `searchlite-core`: schema, indexing, WAL/manifest, segments, query execution, filters/aggs, vectors55- `searchlite-cli`: user-facing commands and JSON output formatting56- `searchlite-http`: endpoints (/init, /add, /bulk, /delete, /commit, /refresh, /search, /inspect, /stats, /compact), limits/timeouts/concurrency57- `searchlite-wasm`: bindings + IndexedDB storage semantics (no fsync)58- `searchlite-ffi`: C ABI / safety edges5960This map guides where you look first.6162### 3) Reproduce the bug with the smallest possible corpus6364Produce a “minimal reproducible case” artifact set:6566- a schema JSON (small)67- a small NDJSON/doc list (5–50 docs)68- one request (HTTP JSON or CLI command) that fails6970For HTTP:7172- include the server flags/env (bind addr, index path, max-body-bytes, max-concurrency, request-timeout, refresh-on-commit)7374For CLI:7576- include exact commands and any flags7778**Goal:** a script a teammate can run in <60 seconds.7980### 4) Turn on observability (don’t add prints yet)8182Use existing debugging features first:8384- For search correctness: add `explain: true` to the request.85- For performance/odd slowdowns while reproducing: add `profile: true`.86- For server behavior: run with `RUST_LOG=debug` (or more targeted module filters) and capture the relevant lines.8788If your issue is “writes not visible,” verify lifecycle:8990- you must `commit`91- you may need `/refresh` in HTTP depending on config9293### 5) Narrow the fault line by bisecting the pipeline9495Pick one axis at a time:9697**Correctness axis**9899- Does the doc get ingested successfully?100- Does it appear in `/stats` documents?101- Does `/inspect` show a new segment after commit?102- Does the query match in bm25 mode vs wand/bmw mode?103104**Durability axis**105106- Is WAL appended correctly?107- Is manifest updated atomically?108- Is WAL truncated only after manifest persistence?109- After restart, does replay do the expected thing?110111**HTTP axis**112113- Is the request being rejected due to max body / timeouts / concurrency?114- Is NDJSON streaming partial and rolling back?115- Are errors correctly surfaced as `{"error":{"type","reason"}}`?116117### 6) Perform a targeted static sweep of the suspected modules118119Once you know the rough area, do a deliberate scan for bug smells:120121Search patterns to grep/ripgrep for in the relevant crate:122123- panics: `unwrap()`, `expect(`, `panic!`, `unreachable!`, `todo!`124- suspicious error handling: `map_err(|_|`, `ok()?` in non-optional contexts, silent `let _ =`125- integer edge cases: casts (`as usize`), underflow (`- 1`), unchecked indexing126- concurrency hazards: `Mutex`/`RwLock` lock ordering, `.await` while holding locks, channels without bounds127- IO hazards: missing `fsync`, non-atomic file replace, partial writes not handled, directory fsync omissions128- schema mismatch hazards: stringly-typed field lookups, analyzer lookup defaults, missing nested path validation129- vector hazards: dimension mismatch, normalization assumptions, unchecked candidate sizing, `NaN` propagation130131You are not “fixing” in this step—just identifying suspects.132133### 7) Dynamic bug probes by subsystem (choose what fits)134135#### A) WAL / manifest / commit / rollback136137If you suspect commit/durability issues:138139- Reproduce a crash-window scenario:140 1. ingest docs141 2. start commit142 3. force termination mid-way (or simulate via test hooks if present)143 4. restart and observe replay144- Validate the invariants:145 - manifest atomic update (rename) and directory sync146 - WAL truncation happens after manifest sync147 - rollback cleans new segment artifacts148149If you can’t safely crash in tests, create a unit test around writer/commit phases and validate state.150151#### B) Segment lifecycle / compaction / cleanup152153If you see duplicates, ghosts, or bloat:154155- Inspect segments via `inspect` and compare to `stats`156- Run compaction and verify:157 - live docs preserved158 - deleted/tombstoned dropped159 - old segment files removed (or expected to remain until cleanup)160161#### C) Query execution modes162163If you suspect scoring/pruning bugs:164165- Run the same query with `execution=bm25`, `execution=wand`, `execution=bmw`166- Results should match for deterministic corpora (unless the contract explicitly says otherwise)167- If they diverge, you likely have a pruning-bound bug or block-max metadata mismatch168169#### D) Filters/aggs/nested correctness170171If filters/aggs are wrong:172173- Verify required fields are `fast: true`174- For nested, verify the filter uses a `Nested` block and that clauses bind to the same object instance175- Add a repro doc with two nested objects that would catch “cross-object” leakage176177#### E) Vectors (feature `vectors`)178179If vector behavior is wrong:180181- Check dimension mismatch handling182- Check cosine normalization expectations183- Confirm ANN parameters (`candidate_size`, `ef_search`) don’t overflow or accept nonsensical values184- Verify hybrid search doesn’t double-count or drop vector scores185186---187188## Fix phase (required)189190### 1) Write the failing regression test first191192- Prefer a unit test in the most local crate if possible.193- If the bug is surface-level behavior (HTTP contract, CLI output), add an integration test.194- Make it deterministic and small.195- Assert on structure, not strings (parse JSON output).196197### 2) Implement the minimal fix198199- Tight patch, minimal diff.200- Keep durability semantics intact.201- Avoid introducing allocations/locks in hot paths unless justified.202203### 3) Prove it204205Run:206207- `cargo test --all --all-features`208- Any relevant examples (e.g. pruning example if you touched pruning logic)209- If the bug touched performance-sensitive code, run the relevant bench before/after.210211### 4) Add a “root cause” note212213In the PR description or internal notes, include:214215- symptom216- root cause217- why the fix is correct218- what test prevents regressions219220---221222## Output format (what Codex should produce)223224When using this skill, produce:2252261. **Repro artifact** (schema + docs + request/command)2272. **Fault localization** (files + functions involved)2283. **Root cause analysis**2294. **Fix plan** (minimal changes)2305. **Regression test** (where it lives and what it asserts)2316. **Validation commands run** (from code-quality)232233---234> Converted and distributed by [TomeVault](https://tomevault.io/claim/davidkelley) — claim your Tome and manage your conversions.235<!-- tomevault:4.0:skill_md:2026-04-14 -->