Sprint Item
#7 of 8 — Medium severity — after #6 (Wednesday PR 2 of 2)
Block Relations
|
|
| Blocked by |
#6 — lock-order doc defines the invariant this suite enforces |
| Blocks |
— |
| Repo/day |
Independent of #1–#5; may spill into Thursday if #6 overruns |
Calendar Day
Wednesday, July 22, 2026 (PR 2 of 2 — may spill to Thursday)
Planned Effort
5 story points (Medium) — sprint item #7
Problem
No concurrency test file exists. Nothing runs a query while the background thread rebuilds the index and swaps the pointer — the search subsystem's highest-risk interaction (test-review C6). A race that returns an empty result during a swap would masquerade as a legitimately empty search, silently.
Goal
One PR adding tests/test_search_index_concurrency.py that queries during a looped background rebuild + pointer swap, asserting no crash and no empty-masquerading-as-real result, and that a locked index degrades to a live scan (never silence). The suite goes red if the documented lock order (item #6) regresses.
Sequencing: land after the lock-order doc (item #6, docs/search-index-lock-order). This suite enforces that document's invariant. Ship as a separate PR.
Scope
Part A — the harness
- New
tests/test_search_index_concurrency.py.
- Point the index dir at
tmp_path.
- Drive
build_search_index(..., force=True) on a writer thread and query_index_hits on reader threads, gated by a threading.Barrier to force overlap.
- Loop the rebuild + pointer swap while readers query.
- Exercise the
api.search live-scan fallback path.
Part B — assertions (oracles, not "does not throw")
- No crash under concurrent rebuild + query.
- No empty-masquerading-as-real result: a result produced mid-swap is never an empty list mistaken for a real answer.
- A locked index returns
index_locked=True and degrades to a live scan (never silence).
- A legitimately empty result is distinguishable from a locked/failed one.
- The suite goes red if the lock order regresses.
Out of scope
Acceptance Criteria
Verification
cd C:\Users\Jasen\CppAliance\claude-code-chat-browser
.\.venv\Scripts\Activate.ps1
pytest -q tests/test_search_index_concurrency.py
pytest -q
- Negative control: perturb the documented lock order → the suite fails.
Sprint Item
#7 of 8 — Medium severity — after #6 (Wednesday PR 2 of 2)
Block Relations
Calendar Day
Wednesday, July 22, 2026 (PR 2 of 2 — may spill to Thursday)
Planned Effort
5 story points (Medium) — sprint item #7
Problem
No concurrency test file exists. Nothing runs a query while the background thread rebuilds the index and swaps the pointer — the search subsystem's highest-risk interaction (test-review C6). A race that returns an empty result during a swap would masquerade as a legitimately empty search, silently.
Goal
One PR adding
tests/test_search_index_concurrency.pythat queries during a looped background rebuild + pointer swap, asserting no crash and no empty-masquerading-as-real result, and that a locked index degrades to a live scan (never silence). The suite goes red if the documented lock order (item #6) regresses.Scope
Part A — the harness
tests/test_search_index_concurrency.py.tmp_path.build_search_index(..., force=True)on a writer thread andquery_index_hitson reader threads, gated by athreading.Barrierto force overlap.api.searchlive-scan fallback path.Part B — assertions (oracles, not "does not throw")
index_locked=Trueand degrades to a live scan (never silence).Out of scope
utils/search_index.pyunless a real race is uncovered (then a minimal, separately-justified fix).Acceptance Criteria
tests/test_search_index_concurrency.pyruns queries while a background rebuild swaps the pointer in a loop.index_locked=Trueand degrades to live scan.Verification