Calendar Day
Week 5 carryover from PR #113 (PR 2 of 2 — deferred review items 12 and 13)
Planned Effort
5 story points (Medium) — sprint items #12 + #13
Problem
Two search follow-ups were deferred from PR #113:
A — Window behavior invisible to users (item 12). Search defaults to a 30-day window (DEFAULT_SEARCH_WINDOW_DAYS, all_history=0). Composers with no parseable timestamp (updated_ms <= 0) remain searchable because _INCLUDE_UNKNOWN_TIMESTAMPS_IN_WINDOW = True in services/search.py — matching index SQL (updated_ms >= ? OR updated_ms <= 0). This is documented in code (resolve_search_since_ms docstring) but not in the UI. Users who expect a strict cutoff may be surprised when old or undated chats still appear. The “Include chats older than 30 days” checkbox exists but does not explain undated chats.
B — Generic 500 on all search failures (item 13). api/search.py wraps the entire handler in except Exception and returns 500 with {"error": "Search failed", "results": []} for all failures. Empty q already returns 400, but infrastructure failures (missing workspace path, DB open errors, unexpected bugs) are indistinguishable from recoverable input problems. API consumers and the frontend cannot tell whether to retry, fix configuration, or show a validation message.
Goal
- Make search-window behavior discoverable in the product UI without changing search semantics.
- Return appropriate HTTP status codes from
/api/search so clients can branch on error class, without leaking sensitive exception details.
Scope
Part A — Search window UI
templates/search.html — tooltip or helper text on #all-history (and/or near result count) explaining:
- default 30-day window
- undated chats may still appear unless excluded by other filters
all_history=1 / checkbox searches full history
- Optional: one line in search result count banner when window is active (e.g. “Showing last 30 days; undated chats included”)
api/search.py — optional searchWindowNote string in JSON payload only if needed for UI; prefer HTML-only if sufficient
Part B — API error status codes
api/search.py — narrow exception handling:
- 400 — bad/invalid query parameters (extend beyond empty
q if needed: invalid type, out-of-range since_days)
- 404 or 503 — workspace/global storage not available (align with other routes’ patterns)
- 500 — unexpected internal errors only; log full traceback server-side
- Optional: structured error body
{ "error": "...", "code": "..." } consistent with other API routes if a pattern exists
tests/test_api_search.py — assert status codes for representative failure modes (mock or fixture)
Out of scope
Acceptance Criteria
Part A — UI
Part B — API errors
General
Verification
cd C:\Users\Jasen\CppAliance\cppa-cursor-browser
.\.venv\Scripts\Activate.ps1
pytest tests/test_api_search.py -q
pytest -q
mypy .
Manual: open /search, confirm tooltip/helper text; toggle “all history” and confirm copy updates if applicable.
Calendar Day
Week 5 carryover from PR #113 (PR 2 of 2 — deferred review items 12 and 13)
Planned Effort
5 story points (Medium) — sprint items #12 + #13
Problem
Two search follow-ups were deferred from PR #113:
A — Window behavior invisible to users (item 12). Search defaults to a 30-day window (
DEFAULT_SEARCH_WINDOW_DAYS,all_history=0). Composers with no parseable timestamp (updated_ms <= 0) remain searchable because_INCLUDE_UNKNOWN_TIMESTAMPS_IN_WINDOW = Trueinservices/search.py— matching index SQL(updated_ms >= ? OR updated_ms <= 0). This is documented in code (resolve_search_since_msdocstring) but not in the UI. Users who expect a strict cutoff may be surprised when old or undated chats still appear. The “Include chats older than 30 days” checkbox exists but does not explain undated chats.B — Generic 500 on all search failures (item 13).
api/search.pywraps the entire handler inexcept Exceptionand returns500with{"error": "Search failed", "results": []}for all failures. Emptyqalready returns 400, but infrastructure failures (missing workspace path, DB open errors, unexpected bugs) are indistinguishable from recoverable input problems. API consumers and the frontend cannot tell whether to retry, fix configuration, or show a validation message.Goal
/api/searchso clients can branch on error class, without leaking sensitive exception details.Scope
Part A — Search window UI
templates/search.html— tooltip or helper text on#all-history(and/or near result count) explaining:all_history=1/ checkbox searches full historyapi/search.py— optionalsearchWindowNotestring in JSON payload only if needed for UI; prefer HTML-only if sufficientPart B — API error status codes
api/search.py— narrow exception handling:qif needed: invalidtype, out-of-rangesince_days){ "error": "...", "code": "..." }consistent with other API routes if a pattern existstests/test_api_search.py— assert status codes for representative failure modes (mock or fixture)Out of scope
_INCLUDE_UNKNOWN_TIMESTAMPS_IN_WINDOWbehaviorAcceptance Criteria
Part A — UI
all_historycheckbox behavior unchanged; shared links withall_history=1/truestill worktests/test_api_search.py/TestSearchWindowPart B — API errors
qstill returns 400exception()tests/test_api_search.pyGeneral
pytestandmypy --strictpassVerification
Manual: open
/search, confirm tooltip/helper text; toggle “all history” and confirm copy updates if applicable.