From 2cd3099ebf5bb05e4bb3cbe94b9a1b4b9db205d8 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 26 Apr 2026 15:13:11 +0200 Subject: [PATCH] feat(canon): add Interface Change Registry concept and workplan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Concept doc captures the design for coordinated API evolution in agent ecosystems: InterfaceChange entity, draft→published→resolved lifecycle, TPSC-derived dependency routing, inbox-based notifications, pre-change coordination via planned_for, and deliberate deferral of webhooks. CUST-WP-0033 workplan: 6 tasks (model, API, dispatch integration, MCP tools, dashboard, webhook EP). Co-Authored-By: Claude Sonnet 4.6 --- .../interface_change_registry_v0.1.md | 162 +++++++++++++++++ .../CUST-WP-0033-interface-change-registry.md | 170 ++++++++++++++++++ 2 files changed, 332 insertions(+) create mode 100644 canon/projects/custodian/interface_change_registry_v0.1.md create mode 100644 workplans/CUST-WP-0033-interface-change-registry.md diff --git a/canon/projects/custodian/interface_change_registry_v0.1.md b/canon/projects/custodian/interface_change_registry_v0.1.md new file mode 100644 index 0000000..07ffa14 --- /dev/null +++ b/canon/projects/custodian/interface_change_registry_v0.1.md @@ -0,0 +1,162 @@ +--- +id: CUST-CPT-CUST-2026-000002 +type: concept +title: "Interface Change Registry — Coordinated API Evolution for Agent Ecosystems" +status: active +owners: ["Bernd", "Custodian"] +created: "2026-04-26" +updated: "2026-04-26" +scope: + domains: ["custodian"] + sensitivity: internal +tags: ["interface", "api-evolution", "agent-coordination", "ecosystem", "change-management"] +related_workplan: CUST-WP-0033 +--- + +# Interface Change Registry — Coordinated API Evolution for Agent Ecosystems + +## Problem + +In a distributed ecosystem of closely coupled repos and services, APIs and interfaces +evolve continuously. Without coordination, breaking changes propagate silently: a +service updates its REST API, and dependent agents or services discover the breakage +only at runtime — typically as a 422, a 307 redirect, or a schema validation failure. + +Human-operated systems address this with release notes and changelogs. Agent-operated +systems need something machine-readable and actionable: a record of what changed, who +depends on it, and a channel to trigger adaptation before the breakage hits. + +The trailing-slash normalisation performed on 2026-04-26 is a concrete example: it +was a deliberate, coordinated breaking change. Without a registry, every consumer had +to be found by manual grep. With one, the change record names the affected repos, +their agents receive inbox notifications, and each can adapt autonomously. + +## Core Abstraction: InterfaceChange + +An `InterfaceChange` record describes a single, versioned mutation to a published +interface boundary. It carries: + +| Field | Purpose | +|---|---| +| `repo_slug` | The repo that owns the interface | +| `interface_type` | What kind of interface: `rest_api`, `mcp_tool`, `cli`, `schema`, `capability` | +| `change_type` | Nature of change: `breaking`, `additive`, `deprecation`, `removal` | +| `title` | Short human-readable summary | +| `description` | Full description with before/after detail | +| `affected_paths` | Specific endpoints, tool names, CLI commands, or schema fields changed | +| `affected_repo_slugs` | Repos known to consume this interface | +| `status` | `draft` → `published` → `resolved` | +| `planned_for` | Optional date — set when change is announced before it ships | +| `published_at` | When the change became live | +| `resolved_at` | When all known dependents have adapted | + +## Lifecycle + +``` +draft ──publish──▶ published ──resolve──▶ resolved + │ │ + │ auto-notify affected + │ repo agents (inbox) + └── edit freely; no notifications yet +``` + +**draft** — change is being documented but not yet live or announced. Safe to edit. +Can be used to pre-announce a planned breaking change before it is merged. + +**published** — the change is live (or imminently scheduled). On publish, the hub +automatically sends an inbox message to the agent of each `affected_repo_slug`. +The message contains enough context for the agent to identify what needs updating. + +**resolved** — all known dependents have adapted and confirmed. Can be closed by +the originating agent or by any affected agent once it has updated its side. + +## Dependency Routing + +`affected_repo_slugs` can be populated in two ways: + +1. **Explicit** — the author lists known consumers when creating the record. +2. **Derived** — the hub queries the TPSC graph: repos that have a TPSC snapshot + declaring a dependency on the originating repo's service are automatically + included as candidates. The author confirms or trims the list before publish. + +This means the TPSC catalog (`tpsc.yaml` files) is the underlying dependency map +for routing interface change notifications. Keeping TPSC current is what makes +automatic routing accurate. + +## Pre-Change Coordination + +Setting `planned_for` and publishing in `draft` status (then moving to `published` +on merge) enables a coordination window: + +``` +day 0 — change drafted, planned_for set to day 7 +day 0 — dependent agents receive notification, begin adapting +day 7 — change lands, status set to published +day 7+ — agents confirm adaptation, change resolved +``` + +This is the proactive adaptation model: the breaking change announcement travels +faster than the change itself, giving dependents time to prepare. At scale, this +enables the ecosystem to self-heal around planned migrations. + +## Agent Session Integration + +Two integration points keep agents aware of pending changes without requiring +active polling: + +**Session start (pull):** The `/repos/{slug}/dispatch` endpoint includes a +`pending_interface_changes` field — published changes that affect this repo and +are not yet resolved. Agents reading dispatch at session start see what they need +to adapt to. + +**Inbox notification (push):** On publish, the hub sends an inbox message to each +affected repo's agent. The message includes the change title, description, and +`affected_paths` so the agent can locate the relevant code without additional API +calls. + +Together, these ensure no published breaking change is invisible to an agent +working in an affected repo. + +## Relationship to Existing State-Hub Entities + +| Entity | Relationship | +|---|---| +| `ManagedRepo` | InterfaceChange.repo_id FK; affected_repo_slugs reference repo slugs | +| `TPSC` / `TPSCSnapshot` | Source for derived affected_repo_slugs via service dependency graph | +| `Decision` | A planned breaking change is conceptually a pending decision; consider linking | +| `ProgressEvent` | Publishing a change auto-appends a progress event to the originating repo | +| `Message` | Publish action sends inbox messages to affected agents | + +## Webhook Extension (Deferred) + +Inbox messages cover agents that poll at session start. Services that need +real-time push — CI pipelines, external webhooks, non-Custodian agents — require +a separate subscription mechanism. + +This is explicitly deferred. The design leaves room for it: +- `InterfaceChange` records are immutable once published (safe to deliver idempotently) +- `affected_repo_slugs` is the routing key; a subscription table maps slugs to URLs +- Delivery semantics: at-least-once with exponential backoff + +A dedicated EP (`EP-CUST-ICR-001`) will track this when the inbox-first approach +proves insufficient. + +## Long-term Vision + +The Interface Change Registry is a first step toward a **self-healing ecosystem**: + +1. A repo publishes a breaking change with `planned_for = T+7`. +2. The hub identifies all dependents via TPSC and notifies their agents. +3. Each dependent agent opens a task, locates the affected code, and creates an + adaptation PR before T+7. +4. At T+7, the change ships; all dependents are already adapted. +5. The originating agent marks the change resolved. + +No human coordination is needed for routine interface evolution. Humans remain in +the loop for non-routine changes — architectural decisions, security-sensitive +migrations, or changes that require cross-domain agreement — via the Decision entity +and the existing escalation protocol. + +At greater scale, the dependency graph enables **contract testing**: two repos can +register a formal interface contract, and the hub can detect when a proposed change +would violate it before the change is merged. diff --git a/workplans/CUST-WP-0033-interface-change-registry.md b/workplans/CUST-WP-0033-interface-change-registry.md new file mode 100644 index 0000000..a63bfd2 --- /dev/null +++ b/workplans/CUST-WP-0033-interface-change-registry.md @@ -0,0 +1,170 @@ +--- +id: CUST-WP-0033 +type: workplan +title: "Interface Change Registry — Coordinated API Evolution" +domain: custodian +repo: the-custodian +status: active +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: todo +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: todo +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: todo +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: todo +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: todo +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: todo +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`.