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>
164 lines
4.2 KiB
Markdown
164 lines
4.2 KiB
Markdown
---
|
||
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:34–69`
|
||
|
||
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:147–196`
|
||
|
||
---
|
||
|
||
### 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:14–30`
|
||
|
||
---
|
||
|
||
### 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:14–27`
|
||
|
||
---
|
||
|
||
### 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:15–38`
|
||
|
||
---
|
||
|
||
### 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.
|