generated from coulomb/repo-seed
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>
46 lines
1.9 KiB
Python
46 lines
1.9 KiB
Python
"""add interface_changes table
|
|
|
|
Revision ID: q4l5m6n7o8p9
|
|
Revises: p3k4l5m6n7o8
|
|
Create Date: 2026-04-26
|
|
|
|
"""
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
|
|
|
revision = "q4l5m6n7o8p9"
|
|
down_revision = "p3k4l5m6n7o8"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.create_table(
|
|
"interface_changes",
|
|
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
|
sa.Column("repo_id", UUID(as_uuid=True),
|
|
sa.ForeignKey("managed_repos.id", ondelete="CASCADE"),
|
|
nullable=False),
|
|
sa.Column("interface_type", sa.String(40), nullable=False),
|
|
sa.Column("change_type", sa.String(40), nullable=False),
|
|
sa.Column("title", sa.String(300), nullable=False),
|
|
sa.Column("description", sa.Text, nullable=False),
|
|
sa.Column("affected_paths", JSONB, nullable=False, server_default="[]"),
|
|
sa.Column("affected_repo_slugs", JSONB, nullable=False, server_default="[]"),
|
|
sa.Column("status", sa.String(20), nullable=False, server_default="draft"),
|
|
sa.Column("planned_for", sa.Date, nullable=True),
|
|
sa.Column("published_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("resolved_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("author", sa.String(100), nullable=False, server_default="custodian"),
|
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
|
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
|
)
|
|
op.create_index("ix_interface_changes_repo_id", "interface_changes", ["repo_id"])
|
|
op.create_index("ix_interface_changes_status", "interface_changes", ["status"])
|
|
op.create_index("ix_interface_changes_repo_status", "interface_changes", ["repo_id", "status"])
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_table("interface_changes")
|