Files
the-custodian/workplans/CUST-WP-0018-api-hardening-code-quality.md
tegwick 9aa54f8133 feat(api): CUST-WP-0018 — API hardening & code quality
T01: Fix datetime.utcnow() → datetime.now(tz=timezone.utc) in MCP server
T02: Wrap _get/_post/_patch/_delete with try/except; return error dicts
T03: Log warnings when write_log skips missing project path
T04: Add priority + due_date_before filters to GET /tasks/
T05: Add owner + slug filters to GET /workstreams/
T06: Add offset param to GET /progress/ for proper pagination
T07: Low-severity bundle:
  - CORS origins from CORS_ORIGINS env var (TD-017)
  - seed.py upsert domains+topics on re-run (TD-011)
  - normalise filter bar CSS → filter-text-input everywhere (TD-016)
  - add 30.5 avg-days-per-month comment in decisions.md (TD-019)
  - TD-009, TD-018 already resolved by existing code

Closes CUST-WP-0018.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 02:17:04 +01:00

4.7 KiB
Raw Blame History

id, type, title, domain, status, owner, topic_slug, created, updated, state_hub_workstream_id
id type title domain status owner topic_slug created updated state_hub_workstream_id
CUST-WP-0018 workplan State Hub — API Hardening & Code Quality custodian done custodian custodian 2026-03-18 2026-03-18 c7777d8a-a796-4f72-b444-64cc14f77a58

State Hub — API Hardening & Code Quality

Summary

Resolve all open medium/high technical debt items in the API and MCP server. These are small, targeted fixes: datetime correctness, missing filter params, MCP error handling, and a bundle of low-severity cleanup items. No schema migrations required.

Resolves

TD-CUST-006, TD-CUST-007, TD-CUST-008, TD-CUST-009, TD-CUST-010, TD-CUST-011, TD-CUST-012, TD-CUST-013, TD-CUST-015, TD-CUST-016, TD-CUST-017, TD-CUST-018, TD-CUST-019

TD-CUST-005 (N+1 selectin) deferred — not pressing at current scale.


Tasks

T01 — Fix deprecated datetime.utcnow() in MCP server

id: CUST-WP-0018-T01
status: done
priority: high
state_hub_task_id: "5045749c-22a5-4f37-81b1-6fc87ae7c580"

Replace datetime.utcnow() with datetime.now(tz=timezone.utc) throughout state-hub/mcp_server/server.py. Resolves TD-CUST-006 and TD-CUST-013 (mixed naive/aware datetime handling). One-line change with broad correctness impact.

Location: state-hub/mcp_server/server.py:343 (and any other occurrences)


T02 — MCP HTTP helpers: catch exceptions and return user-friendly errors

id: CUST-WP-0018-T02
status: done
priority: high
state_hub_task_id: "8aadbaf8-d898-436e-8df0-7f095c916613"

Wrap _get(), _post(), _patch(), _delete() in a try/except that catches httpx.HTTPError and Exception, returning a structured error dict instead of letting the MCP tool crash with a stack trace. Resolves TD-CUST-015.

Location: state-hub/mcp_server/server.py:3469

Pattern:

try:
    r = await client.get(url, ...)
    r.raise_for_status()
    return r.json()
except httpx.HTTPStatusError as e:
    return {"error": f"API {e.response.status_code}: {e.response.text[:200]}"}
except Exception as e:
    return {"error": f"Request failed: {e}"}

T03 — Decision write_log: warn when project path not found

id: CUST-WP-0018-T03
status: done
priority: medium
state_hub_task_id: "26f8d132-b2f4-4939-9497-a9ad64e0a73e"

_write_project_log() in decisions.py silently returns when the project path directory does not exist. Add a log warning so the caller knows the write was skipped. Resolves TD-CUST-012.

Location: state-hub/api/routers/decisions.py:147196


T04 — Add priority and due_date filters to task list endpoint

id: CUST-WP-0018-T04
status: done
priority: medium
state_hub_task_id: "18da9d84-54a4-4028-8b8e-014d2b2f6ed6"

Add priority: str | None and due_date_before: date | None query params to list_tasks(). Resolves TD-CUST-007.

Location: state-hub/api/routers/tasks.py:1430


T05 — Add owner and slug filters to workstream list endpoint

id: CUST-WP-0018-T05
status: done
priority: medium
state_hub_task_id: "488f448f-396d-4924-98a5-a2e84d4b1b95"

Add owner: str | None and slug: str | None query params to list_workstreams(). Resolves TD-CUST-008.

Location: state-hub/api/routers/workstreams.py:1427


T06 — Add offset pagination to progress event list endpoint

id: CUST-WP-0018-T06
status: done
priority: medium
state_hub_task_id: "dd7e9da8-19fb-4b02-a100-972c582dbaa9"

Add offset: int = 0 query param to list_progress() alongside existing limit. Resolves TD-CUST-010.

Location: state-hub/api/routers/progress.py:1538


T07 — Low-severity cleanup bundle

id: CUST-WP-0018-T07
status: done
priority: low
state_hub_task_id: "b949805b-dd3e-43d6-89cc-631e3183f67c"

Bundle of small fixes (each a few lines):

  • TD-CUST-009: Document decision_type filter in OpenAPI schema for list_decisions() consistently with other filter params.
  • TD-CUST-011: Make seed.py upsert-based — update existing rows when field values change (use ON CONFLICT ... DO UPDATE).
  • TD-CUST-016: Normalise filter bar CSS class names across dashboard pages (filter-search vs filter-owner → use filter-text-input everywhere).
  • TD-CUST-017: Read CORS origins from CORS_ORIGINS env var (comma-separated), defaulting to localhost:3000; remove hard-coded list from main.py.
  • TD-CUST-018: Add a ProgressEventCreate.detail validator that at minimum rejects non-dict values (dict-or-None is sufficient).
  • TD-CUST-019: Add comment explaining the 30.5 avg-days-per-month constant in decisions.md:99.

Locations: Various — see TD item descriptions for exact file:line refs.