diff --git a/state-hub/dashboard/src/todo.md b/state-hub/dashboard/src/todo.md index 80ad001..ed6dee1 100644 --- a/state-hub/dashboard/src/todo.md +++ b/state-hub/dashboard/src/todo.md @@ -38,7 +38,8 @@ const todoState = (async function*() { domain: wsMap[t.workstream_id]?.domain ?? "unknown", })); contribs = contribList; - improvements = ri.ok ? (await ri.json()).filter(t => t.debt_type === "dashboard-improvement" && t.status === "open") : []; + const CLOSED = new Set(["finished", "wont_fix", "resolved", "deferred"]); + improvements = ri.ok ? (await ri.json()).filter(t => t.debt_type === "dashboard-improvement" && !CLOSED.has(t.status)) : []; } } catch {} yield {tasks, contribs, improvements, ok, ts: new Date()}; diff --git a/workplans/CUST-WP-0018-api-hardening-code-quality.md b/workplans/CUST-WP-0018-api-hardening-code-quality.md new file mode 100644 index 0000000..164f638 --- /dev/null +++ b/workplans/CUST-WP-0018-api-hardening-code-quality.md @@ -0,0 +1,163 @@ +--- +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. diff --git a/workplans/CUST-WP-0019-dashboard-ux-polish.md b/workplans/CUST-WP-0019-dashboard-ux-polish.md new file mode 100644 index 0000000..b4228c6 --- /dev/null +++ b/workplans/CUST-WP-0019-dashboard-ux-polish.md @@ -0,0 +1,92 @@ +--- +id: CUST-WP-0019 +type: workplan +title: "State Hub Dashboard — UX Polish" +domain: custodian +status: active +owner: custodian +topic_slug: custodian +created: "2026-03-18" +updated: "2026-03-18" +--- + +# State Hub Dashboard — UX Polish + +## Summary + +Targeted UX improvements to the Observable Framework dashboard. These are +all small, self-contained changes with high visible impact. Tasks are ordered +by priority; each is independently deliverable. + +## Sources + +- Submitted UI suggestion `c2fc284a` (Repos nav restructure) +- TD-CUST-002 residual (remaining inline API/POLL in non-main pages) + +--- + +## Tasks + +### T01 — Repos nav: rename to "Repository" section with sub-pages + +```task +id: CUST-WP-0019-T01 +status: todo +priority: high +``` + +User suggestion: rename the "Repos" nav link to "Repository" and restructure +it as a collapsible section containing the three repo-related pages as +sub-items. + +**Location:** `state-hub/dashboard/observablehq.config.js` (pages array) + +Target nav structure: +``` +Repository + ├── Repo Sync (/repos) + ├── SBOM (/sbom) + └── Tech Debt (/techdept) +``` + +Implementation: in `observablehq.config.js` replace the flat `Repos` page +entry with a section group containing three children. Update any hardcoded +`/repos` or `/sbom` or `/techdept` links in other pages if they break. + +--- + +### T02 — Centralize remaining inline API/POLL constants + +```task +id: CUST-WP-0019-T02 +status: todo +priority: low +``` + +TD-CUST-002 residual: `policy/workstream-dod.md` still has a hard-coded +`const API = "http://127.0.0.1:8000"`. Migrate to import from `config.js`. +(Note: `contributions.md` and `goals.md` use intentionally different POLL +values — leave as-is or add named exports like `POLL_SLOW = 30_000` to +`config.js` if preferred.) + +**Location:** `state-hub/dashboard/src/policy/workstream-dod.md:6` + +--- + +### T03 — UI Feedback page: mark suggestion as submitted in todo filter + +```task +id: CUST-WP-0019-T03 +status: todo +priority: low +``` + +`todo.md` Suggestions section filters with `t.status === "open"` but new +suggestions now enter with `status: "submitted"` (workflow redesign from +CUST-WP session). Update filter to include `submitted`, `analyse`, `plan`, +`implement`, `test`, `review` — i.e. any non-terminal status. + +**Location:** `state-hub/dashboard/src/todo.md:41` + +Current: `.filter(t => t.debt_type === "dashboard-improvement" && t.status === "open")` +Target: `.filter(t => t.debt_type === "dashboard-improvement" && !["finished","wont_fix","resolved","deferred"].includes(t.status))` diff --git a/workplans/CUST-WP-0020-test-suite.md b/workplans/CUST-WP-0020-test-suite.md new file mode 100644 index 0000000..4b7ea6c --- /dev/null +++ b/workplans/CUST-WP-0020-test-suite.md @@ -0,0 +1,109 @@ +--- +id: CUST-WP-0020 +type: workplan +title: "State Hub — pytest Test Suite" +domain: custodian +status: active +owner: custodian +topic_slug: custodian +created: "2026-03-18" +updated: "2026-03-18" +--- + +# State Hub — pytest Test Suite + +## Summary + +The API, MCP server, seed script, and migration scripts currently have zero +automated test coverage (TD-CUST-014). Regressions are caught at runtime. +This workplan introduces a pytest-asyncio test suite using HTTPX's async test +client against a real test database (no mocking — see ADR below). + +## ADR + +Do NOT mock the database in these tests. The state-hub has had past incidents +where mocked tests passed but the real DB diverged (see memory: feedback on +not mocking the database). Use a real PostgreSQL test database via +`TEST_DATABASE_URL` env var (docker-compose already provides postgres). + +--- + +## Tasks + +### T01 — Test infrastructure: pytest-asyncio + async HTTP client + fixtures + +```task +id: CUST-WP-0020-T01 +status: todo +priority: high +``` + +- Add `pytest`, `pytest-asyncio`, `httpx` to `state-hub/pyproject.toml` dev deps +- Create `state-hub/tests/conftest.py`: + - `engine` fixture: creates a fresh async engine against `TEST_DATABASE_URL` + - `db` fixture: runs `alembic upgrade head` then `alembic downgrade base` + around each test session (or truncates tables between tests) + - `client` fixture: `httpx.AsyncClient` pointed at the test app instance +- Create `state-hub/tests/__init__.py` +- Add `make test` target in `state-hub/Makefile` + +--- + +### T02 — Core router tests: topics, workstreams, tasks, decisions + +```task +id: CUST-WP-0020-T02 +status: todo +priority: high +``` + +Coverage targets (happy path + key error cases): +- `POST /topics/` → 201, duplicate slug → 409 +- `POST /workstreams/` + `GET /workstreams/?topic_id=` + status transitions +- `POST /tasks/` + `GET /tasks/?workstream_id=` + `needs_human` flag +- `POST /decisions/` + `PATCH /{id}/resolve/` +- `GET /state/summary` → returns expected shape with counts + +--- + +### T03 — TD, EP, contributions router tests + +```task +id: CUST-WP-0020-T03 +status: todo +priority: medium +``` + +- TD CRUD + workflow status transitions + notes endpoints +- EP CRUD + status transitions +- Contribution lifecycle guard (invalid transitions → 422) +- SBOM ingest + `/sbom/snapshots/` aggregation + +--- + +### T04 — MCP server smoke tests + +```task +id: CUST-WP-0020-T04 +status: todo +priority: medium +``` + +For each MCP tool, call the underlying HTTP helper against the test client +and assert the result shape. No need to test the MCP stdio protocol itself — +just the HTTP-level correctness. Focus on: `get_state_summary`, +`create_task`, `update_task_status`, `add_progress_event`, `flag_for_human`. + +--- + +### T05 — CI: add pytest to pre-commit or Makefile gate + +```task +id: CUST-WP-0020-T05 +status: todo +priority: low +``` + +Add `make test` as a step in any existing CI pipeline, or document the +manual pre-push gate in CLAUDE.md. Ensure `TEST_DATABASE_URL` is set to the +docker-compose postgres instance in the local dev workflow.