Skip to content

cppa-cursor-browser: Search UX polish and distinct /api/search error codes #117

Description

@clean6378-max-it

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

  1. Make search-window behavior discoverable in the product UI without changing search semantics.
  2. 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

  • Search page explains default 30-day window in plain language
  • Users can discover why undated conversations may appear in windowed search
  • all_history checkbox behavior unchanged; shared links with all_history=1 / true still work
  • No regression in tests/test_api_search.py / TestSearchWindow

Part B — API errors

  • Empty or whitespace q still returns 400
  • At least one additional distinguishable client error returns 400 (not 500) when provoked in tests
  • Storage/path failures return a non-500 code where appropriate (document choice in PR)
  • Unexpected exceptions still return 500 and are logged with exception()
  • No raw exception strings or stack traces in JSON responses
  • New/updated tests in tests/test_api_search.py

General

  • Full pytest and mypy --strict pass
  • PR approved by at least 1 reviewer

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.

Metadata

Metadata

Labels

No labels
No labels

Type

No type
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