--- id: CUST-WP-0033 type: workplan title: "Interface Change Registry — Coordinated API Evolution" domain: custodian repo: the-custodian status: done owner: custodian topic_slug: custodian created: "2026-04-26" updated: "2026-04-26" concept: canon/projects/custodian/interface_change_registry_v0.1.md state_hub_workstream_id: "420a3981-abf5-4a8e-a94b-455964f1a0e5" --- # CUST-WP-0033 — Interface Change Registry ## Goal Add a first-class `InterfaceChange` entity to the state-hub. Agents producing breaking changes can document them; agents consuming interfaces discover pending changes at session start via dispatch, and receive inbox notifications on publish. Webhook delivery is deferred and registered as an extension point. Reference concept: `canon/projects/custodian/interface_change_registry_v0.1.md` ## T01: Data model and migration ```task id: CUST-WP-0033-T01 status: done priority: high state_hub_task_id: "6bc77d3c-78e0-485a-a3bc-b5987c4ccc53" ``` New table `interface_changes`. Fields: `id` (UUID PK), `repo_id` (FK managed_repos), `interface_type` (String: rest_api / mcp_tool / cli / schema / capability), `change_type` (String: breaking / additive / deprecation / removal), `title` (String), `description` (Text), `affected_paths` (JSONB list of strings), `affected_repo_slugs` (JSONB list of slugs), `status` (String: draft / published / resolved), `planned_for` (Date nullable), `published_at` (DateTime nullable), `resolved_at` (DateTime nullable), `author` (String), `created_at`, `updated_at`. Index on `(repo_id, status)` and `(status)` for dispatch queries. Acceptance: Alembic migration runs cleanly, model importable, no regressions in existing test suite. ## T02: API endpoints ```task id: CUST-WP-0033-T02 status: done priority: high state_hub_task_id: "7664551e-0871-4e82-a9ba-b59be515c47c" ``` Router at `/interface-changes/` (prefix). Endpoints: - `POST /interface-changes/` — create (status always `draft` on creation) - `GET /interface-changes/` — list; filter params: `repo_slug`, `status`, `change_type`, `affected_repo` (returns changes that affect the given slug) - `GET /interface-changes/{change_id}` — single record - `PATCH /interface-changes/{change_id}` — update mutable fields (title, description, affected_paths, affected_repo_slugs, planned_for); only valid in `draft` status - `POST /interface-changes/{change_id}/publish` — transition draft → published; sets `published_at`; fires inbox messages to agents of all `affected_repo_slugs`; appends a progress event on the originating repo - `POST /interface-changes/{change_id}/resolve` — transition published → resolved; sets `resolved_at` Acceptance: all endpoints return correct status codes; publish transitions send inbox messages; 409 on invalid status transitions; tests cover happy path and invalid transitions. ## T03: Dispatch integration ```task id: CUST-WP-0033-T03 status: done priority: medium state_hub_task_id: "8f8403a2-4444-4196-9845-ea9c66b674eb" ``` Extend `GET /repos/{slug}/dispatch` to include a `pending_interface_changes` field: published `InterfaceChange` records where `affected_repo_slugs` contains `slug` and status is not `resolved`. Each entry: `id`, `title`, `change_type`, `interface_type`, `repo_slug` (origin), `affected_paths`, `planned_for`, `published_at`. Extend `DispatchWorkstream` schema with the new field. Update `RepoDispatch` schema. Update the `get_repo_dispatch` endpoint accordingly. Acceptance: `GET /repos/repo-registry/dispatch` returns `pending_interface_changes` list (empty or populated); no regression on existing dispatch tests. ## T04: MCP tools ```task id: CUST-WP-0033-T04 status: done priority: medium state_hub_task_id: "d9135829-954e-41de-af9f-607768916478" ``` Four tools in `mcp_server/server.py`: - `register_interface_change(repo_slug, interface_type, change_type, title, description, affected_paths=None, affected_repo_slugs=None, planned_for=None)` — creates a draft record - `list_interface_changes(repo_slug=None, status=None, change_type=None, affected_repo=None)` — returns formatted summary - `publish_interface_change(change_id)` — publishes and triggers notifications - `resolve_interface_change(change_id)` — marks resolved Acceptance: tools callable from Claude Code; publish tool returns confirmation of how many inbox messages were sent. ## T05: Dashboard page ```task id: CUST-WP-0033-T05 status: done priority: low state_hub_task_id: "d2fcbe83-c5a7-400c-a53b-ff1950795814" ``` New page `dashboard/src/interface-changes.md`. Shows: - Table of published/draft changes grouped by repo, sorted by `published_at` desc - Change type badge (breaking = red, deprecation = amber, additive = green) - Affected repos column with count - Filter by repo slug and change_type - A "planned" section for changes with future `planned_for` dates, sorted chronologically — effectively a migration calendar Add to `observablehq.config.js` nav. Acceptance: page renders; data loads from `GET /interface-changes/?status=published` and `GET /interface-changes/?status=draft`. ## T06: Register webhook extension point ```task id: CUST-WP-0033-T06 status: done priority: low state_hub_task_id: "47d7bea8-b5fb-4fc4-9ec5-9ed5e0cdef72" ``` Register EP-CUST-ICR-001 in the state-hub: ``` interface_type: future_capability ep_type: architecture title: Webhook subscriptions for interface change notifications description: | Inbox messages cover polling agents. Real-time push to CI pipelines, external webhooks, and non-Custodian agents requires a subscription table (repo_slug → webhook_url) and delivery infrastructure (retry, dead-letter). Defer until inbox-first approach proves insufficient for ≥1 real case. status: open priority: low ``` No implementation. Documents the deliberate deferral and records the design direction so it is not re-invented later. Acceptance: EP registered, retrievable via `GET /extension-points/?domain=custodian`.