Files
state-hub/api/schemas/interface_change.py
tegwick 548c3efe4a feat(state-hub): Interface Change Registry (CUST-WP-0033 T01-T06)
Adds first-class tracking for API and interface mutations across the
agent ecosystem. Breaking changes are documented, affected repos are
notified via inbox, and agents discover pending changes at session
start via the dispatch endpoint.

- Migration q4l5m6n7o8p9: interface_changes table
- Model/schema: InterfaceChange with draft→published→resolved lifecycle
- Router: POST/GET/PATCH /interface-changes/, /publish, /resolve actions
  (auto-notify affected repo agents on publish; progress event on origin)
- Dispatch: GET /repos/{slug}/dispatch now returns pending_interface_changes
- MCP tools: register_interface_change, list_interface_changes,
  publish_interface_change, resolve_interface_change
- Dashboard: /interface-changes page with type badges, planned calendar,
  published cards, and draft table
- EP-CUST-ICR-001 registered: webhook subscriptions (deliberately deferred)

First record: trailing-slash normalisation (2026-04-26), published,
affecting repo-registry — visible in repo-registry dispatch immediately.

223 tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 15:29:08 +02:00

67 lines
1.9 KiB
Python

import uuid
from datetime import date, datetime
from pydantic import BaseModel, ConfigDict
class InterfaceChangeCreate(BaseModel):
repo_slug: str
interface_type: str # rest_api | mcp_tool | cli | schema | capability
change_type: str # breaking | additive | deprecation | removal
title: str
description: str
affected_paths: list[str] = []
affected_repo_slugs: list[str] = []
planned_for: date | None = None
author: str = "custodian"
class InterfaceChangePatch(BaseModel):
title: str | None = None
description: str | None = None
affected_paths: list[str] | None = None
affected_repo_slugs: list[str] | None = None
planned_for: date | None = None
class InterfaceChangeRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: uuid.UUID
repo_id: uuid.UUID
repo_slug: str
interface_type: str
change_type: str
title: str
description: str
affected_paths: list[str]
affected_repo_slugs: list[str]
status: str
planned_for: date | None
published_at: datetime | None
resolved_at: datetime | None
author: str
created_at: datetime
updated_at: datetime
@classmethod
def from_orm_with_slug(cls, obj) -> "InterfaceChangeRead":
return cls(
id=obj.id,
repo_id=obj.repo_id,
repo_slug=obj.repo.slug,
interface_type=obj.interface_type,
change_type=obj.change_type,
title=obj.title,
description=obj.description,
affected_paths=obj.affected_paths or [],
affected_repo_slugs=obj.affected_repo_slugs or [],
status=obj.status,
planned_for=obj.planned_for,
published_at=obj.published_at,
resolved_at=obj.resolved_at,
author=obj.author,
created_at=obj.created_at,
updated_at=obj.updated_at,
)