Calendar Day
Tuesday, July 8, 2026
Planned Effort
Reduced from 5 to ~1–2 story points — most of the original 5 pt scope was shipped in Monday PR 2 (#121).
Depends on: Monday PR 1 (#120, feat/search-fts-index) and Monday PR 2 (#121, feat/search-errors-and-ux) merged to master.
Background — what Monday PRs already delivered
The original Tuesday spec (5 pt, item #2) called for error codes, API validation, search UI polish, docs, and tests. Monday PR 2 (#121, stacked on #120) shipped almost all of it:
Already done in PR #121
- Error codes added to
models/error_codes.py: SEARCH_EMPTY_QUERY, SEARCH_QUERY_TOO_LONG, SEARCH_INVALID_SINCE_DAYS, SEARCH_PROJECTS_UNAVAILABLE
api/search.py validation: empty q → 400, long q → 400, invalid since_days → 400, projects dir inaccessible → 503, unexpected errors → 500 with no exception leakage
- Search UI (
static/js/search.js): helper text (30-day window, result cap), "Search all history" checkbox (all_history=1), truncation warning, data-error-code on error paragraph, empty-result vs error-state distinction, snippet term highlighting via <mark> after esc()
docs/api-reference.md: error catalog extended, per-route Errors table for GET /api/search, query param docs for all_history and since_days
- Tests:
tests/test_search.py covers each failure mode (empty query, long query, invalid since_days, projects unavailable, index lock fallback, tokenless query fallback), tests/test_error_propagation.py no-leakage guard, tests/test_error_codes.py parametrized, Vitest tests for structured errors, truncation, and all-history
NOT done — residual for this ticket
| Item |
Detail |
SEARCH_INDEX_UNAVAILABLE error code |
Not added to ErrorCode enum — PR #121 handles index lock by falling back to live-scan or returning partial results, but never emits a named SEARCH_INDEX_UNAVAILABLE code |
Explicit 503 SEARCH_INDEX_UNAVAILABLE response |
When the index is locked and live-scan also fails (or no partial results exist), the API should return 503 with this code instead of silently degrading |
docs/api-reference.md entry for SEARCH_INDEX_UNAVAILABLE |
Missing from the catalog since the code doesn't exist yet |
Test for SEARCH_INDEX_UNAVAILABLE 503 |
tests/test_search.py needs a case where index is locked AND live-scan is unavailable → 503 |
Scope
1. Add SEARCH_INDEX_UNAVAILABLE to models/error_codes.py
SEARCH_INDEX_UNAVAILABLE = "SEARCH_INDEX_UNAVAILABLE"
2. Wire 503 return in api/search.py
When _search_via_index detects index lock (index_locked flag) and has no partial results, and _search_live_scan also raises or returns no usable results, return:
error_response(ErrorCode.SEARCH_INDEX_UNAVAILABLE, "Search index is temporarily unavailable", 503)
PR #121 already detects index_locked in _search_via_index — this adds the explicit 503 code on the path where fallback also fails.
3. Document in docs/api-reference.md
Add row to error catalog and per-route Errors table:
code |
HTTP |
Routes |
Meaning |
SEARCH_INDEX_UNAVAILABLE |
503 |
GET /api/search |
FTS index is locked during rebuild and fallback is unavailable |
4. Test coverage
tests/test_search.py: index locked + live-scan raises → 503 SEARCH_INDEX_UNAVAILABLE
tests/test_error_codes.py: add parametrized case
Acceptance Criteria
Verification
cd C:\Users\Jasen\CppAliance\claude-code-chat-browser
.\.venv\Scripts\Activate.ps1
pytest tests/test_search.py tests/test_error_codes.py tests/test_error_propagation.py -q
pytest -q
mypy .
ruff check .
Out of scope
Everything below was in the original Tuesday spec but is now shipped by Monday PRs #120 + #121:
Calendar Day
Tuesday, July 8, 2026
Planned Effort
Reduced from 5 to ~1–2 story points — most of the original 5 pt scope was shipped in Monday PR 2 (#121).
Depends on: Monday PR 1 (#120,
feat/search-fts-index) and Monday PR 2 (#121,feat/search-errors-and-ux) merged tomaster.Background — what Monday PRs already delivered
The original Tuesday spec (5 pt, item #2) called for error codes, API validation, search UI polish, docs, and tests. Monday PR 2 (#121, stacked on #120) shipped almost all of it:
Already done in PR #121
models/error_codes.py:SEARCH_EMPTY_QUERY,SEARCH_QUERY_TOO_LONG,SEARCH_INVALID_SINCE_DAYS,SEARCH_PROJECTS_UNAVAILABLEapi/search.pyvalidation: emptyq→ 400, longq→ 400, invalidsince_days→ 400, projects dir inaccessible → 503, unexpected errors → 500 with no exception leakagestatic/js/search.js): helper text (30-day window, result cap), "Search all history" checkbox (all_history=1), truncation warning,data-error-codeon error paragraph, empty-result vs error-state distinction, snippet term highlighting via<mark>afteresc()docs/api-reference.md: error catalog extended, per-route Errors table forGET /api/search, query param docs forall_historyandsince_daystests/test_search.pycovers each failure mode (empty query, long query, invalid since_days, projects unavailable, index lock fallback, tokenless query fallback),tests/test_error_propagation.pyno-leakage guard,tests/test_error_codes.pyparametrized, Vitest tests for structured errors, truncation, and all-historyNOT done — residual for this ticket
SEARCH_INDEX_UNAVAILABLEerror codeErrorCodeenum — PR #121 handles index lock by falling back to live-scan or returning partial results, but never emits a namedSEARCH_INDEX_UNAVAILABLEcodeSEARCH_INDEX_UNAVAILABLEresponsedocs/api-reference.mdentry forSEARCH_INDEX_UNAVAILABLESEARCH_INDEX_UNAVAILABLE503tests/test_search.pyneeds a case where index is locked AND live-scan is unavailable → 503Scope
1. Add
SEARCH_INDEX_UNAVAILABLEtomodels/error_codes.py2. Wire 503 return in
api/search.pyWhen
_search_via_indexdetects index lock (index_lockedflag) and has no partial results, and_search_live_scanalso raises or returns no usable results, return:PR #121 already detects
index_lockedin_search_via_index— this adds the explicit 503 code on the path where fallback also fails.3. Document in
docs/api-reference.mdAdd row to error catalog and per-route Errors table:
codeSEARCH_INDEX_UNAVAILABLEGET /api/search4. Test coverage
tests/test_search.py: index locked + live-scan raises → 503SEARCH_INDEX_UNAVAILABLEtests/test_error_codes.py: add parametrized caseAcceptance Criteria
SEARCH_INDEX_UNAVAILABLEmember inErrorCodeenumSEARCH_INDEX_UNAVAILABLEdocs/api-reference.mdupdatedtests/test_search.pycovers the 503 pathmypy --strict,pytest,ruff,npm testpassVerification
Out of scope
Everything below was in the original Tuesday spec but is now shipped by Monday PRs #120 + #121:
SEARCH_EMPTY_QUERY,SEARCH_QUERY_TOO_LONG,SEARCH_INVALID_SINCE_DAYS,SEARCH_PROJECTS_UNAVAILABLE(PR Feat/search errors and ux #121)data-error-code, empty vs error distinction (PR Feat/search errors and ux #121)docs/api-reference.mderror catalog for the four codes above (PR Feat/search errors and ux #121)tests/test_error_propagation.pyno-leakage guard (PR Feat/search errors and ux #121)