Skip to content

claude-code-chat-browser: Distinct /api/search error codes and search UX polish #114

Description

@clean6378-max-it

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

  • SEARCH_INDEX_UNAVAILABLE member in ErrorCode enum
  • Index locked + no fallback → 503 SEARCH_INDEX_UNAVAILABLE
  • docs/api-reference.md updated
  • tests/test_search.py covers the 503 path
  • mypy --strict, pytest, ruff, npm test pass
  • PR approved by at least 1 reviewer

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:

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions