Files
the-custodian/workplans/CUST-WP-0018-api-hardening-code-quality.md
tegwick 2dfbb19a8f feat(workplans): CUST-WP-0018/0019/0020 — API hardening, dashboard UX polish, test suite
Consolidates all open technical debt into three workplans:
- CUST-WP-0018: API hardening & code quality (TD-006–019 medium/high items)
- CUST-WP-0019: Dashboard UX polish (Repos nav restructure, config.js cleanup,
  todo filter fix for new suggestion workflow statuses)
- CUST-WP-0020: pytest test suite with real DB (TD-014)

Also fixes todo.md Suggestions filter: was checking status===open but new
suggestions enter with status=submitted; now excludes terminal statuses only.

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

164 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: CUST-WP-0018
type: workplan
title: "State Hub — API Hardening & Code Quality"
domain: custodian
status: active
owner: custodian
topic_slug: custodian
created: "2026-03-18"
updated: "2026-03-18"
---
# 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
```task
id: CUST-WP-0018-T01
status: todo
priority: high
```
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
```task
id: CUST-WP-0018-T02
status: todo
priority: high
```
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:
```python
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
```task
id: CUST-WP-0018-T03
status: todo
priority: medium
```
`_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
```task
id: CUST-WP-0018-T04
status: todo
priority: medium
```
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
```task
id: CUST-WP-0018-T05
status: todo
priority: medium
```
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
```task
id: CUST-WP-0018-T06
status: todo
priority: medium
```
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
```task
id: CUST-WP-0018-T07
status: todo
priority: low
```
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.