fix(retirement): close projection and launch contract gaps
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 23s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02b7c-1c49-76a0-955a-49e7b3ddfc0d
This commit is contained in:
tegwick 2026-08-23 00:52:18 +02:00
parent c43266f626
commit b0e1af24f9
13 changed files with 1717 additions and 50 deletions

View file

@ -280,7 +280,7 @@
| task | STATE-WP-0079-T02 | done | — | workplans/STATE-WP-0079-retirement-strangler.md |
| task | STATE-WP-0079-T03 | done | — | workplans/STATE-WP-0079-retirement-strangler.md |
| task | STATE-WP-0079-T04 | progress | — | workplans/STATE-WP-0079-retirement-strangler.md |
| task | STATE-WP-0079-T05 | todo | — | workplans/STATE-WP-0079-retirement-strangler.md |
| task | STATE-WP-0079-T05 | progress | — | workplans/STATE-WP-0079-retirement-strangler.md |
| task | STATE-WP-0079-T06 | todo | — | workplans/STATE-WP-0079-retirement-strangler.md |
| task | STATE-WP-0080-T01 | done | — | workplans/STATE-WP-0080-register-project-flavor-awareness.md |
| task | STATE-WP-0080-T02 | done | — | workplans/STATE-WP-0080-register-project-flavor-awareness.md |

View file

@ -24,6 +24,7 @@ from api.routers import execution
from api.routers import fabric
from api.routers import legacy_meter
from api.routers import review_contracts
from api.routers import identifier_migrations
class ETagMiddleware(BaseHTTPMiddleware):
@ -136,6 +137,7 @@ app.include_router(execution.router)
app.include_router(fabric.router)
app.include_router(legacy_meter.router)
app.include_router(review_contracts.router)
app.include_router(identifier_migrations.router)
app.include_router(state.router)
app.include_router(ops_runs.router)
app.include_router(policy.router)

View file

@ -21,9 +21,9 @@ from api.services.execution_queue import (
ACTIVITY_CORE_RESPONSIBILITIES,
CONCURRENCY_MODES,
EXECUTION_STATES,
EXECUTION_REPLACEMENTS,
LAUNCH_MODES,
STATE_HUB_RESPONSIBILITIES,
execution_state_for_launch,
queue_sort_key,
workplan_blockers,
)
@ -43,6 +43,8 @@ async def execution_semantics() -> ExecutionSemantics:
concurrency_modes=CONCURRENCY_MODES,
state_hub_responsibility=STATE_HUB_RESPONSIBILITIES,
activity_core_responsibility=ACTIVITY_CORE_RESPONSIBILITIES,
launch_requests_accepted=False,
replacements=EXECUTION_REPLACEMENTS,
)
@ -161,37 +163,27 @@ async def workplan_stack(
@router.post(
"/launch-requests",
response_model=LaunchRequestRead,
status_code=status.HTTP_201_CREATED,
status_code=status.HTTP_410_GONE,
)
async def create_launch_request(
request: Request,
response: Response,
body: LaunchRequestCreate,
session: AsyncSession = Depends(get_session),
) -> WorkplanLaunchRequest:
ws = await session.get(Workplan, body.workplan_id)
if ws is None:
raise HTTPException(status_code=404, detail="Workplan not found")
launch_request = WorkplanLaunchRequest(
workplan_id=ws.id,
requested_by=body.requested_by,
requested_actor=body.requested_actor,
launch_mode=body.launch_mode,
concurrency_mode=body.concurrency_mode,
priority=body.priority or ws.planning_priority,
repo_id=body.repo_id or ws.repo_id,
branch_preference=body.branch_preference,
immediate_pickup=body.immediate_pickup,
notes=body.notes,
request_metadata=body.request_metadata,
) -> None:
del body # Request shape stays documented while the retired route returns 410.
await retire_legacy_route(
session=session,
request=request,
response=response,
interface_key="rest_api:POST /execution/launch-requests",
replacement_ref="repo file queue or activity-core ActivityDefinition + ops_run",
detail=(
"State Hub workplan launch requests are retired: no consumer picks up these rows. "
"Queue development work in the authoritative repository file; use an "
"ActivityDefinition and activity-core ops_run only for recurring or operational fires."
),
)
ws.launch_mode = body.launch_mode
ws.concurrency_mode = body.concurrency_mode
ws.execution_state = execution_state_for_launch(body.launch_mode, body.immediate_pickup)
session.add(launch_request)
await session.commit()
await session.refresh(launch_request)
return launch_request
@router.get("/launch-requests", response_model=list[LaunchRequestRead])

View file

@ -0,0 +1,44 @@
from dataclasses import asdict
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from api.database import get_session
from api.schemas.identifier_migration import (
SealedProjectionRepairReceiptRead,
SealedProjectionRepairSubmit,
)
from api.services.work_record_identifier_migration import (
IdentifierMigrationError,
repair_absent_prederivation_projection,
)
router = APIRouter(prefix="/identifier-migrations", tags=["identifier-migrations"])
@router.post(
"/sealed-projection-repairs",
response_model=SealedProjectionRepairReceiptRead,
)
async def repair_sealed_projection(
body: SealedProjectionRepairSubmit,
session: AsyncSession = Depends(get_session),
) -> dict:
"""Restore one exact pre-derivation workplan unit into an absent projection."""
try:
receipt = await repair_absent_prederivation_projection(
session,
body.plan,
body.repo_slug,
body.unit,
expected_plan_sha256=body.expected_plan_sha256,
source_revision=body.source_revision,
source_fingerprint=body.source_fingerprint,
source_clean=body.source_clean,
source_synchronized=body.source_synchronized,
primary_confirmed=body.primary_confirmed,
projection_identity=body.projection_identity,
)
except IdentifierMigrationError as exc:
raise HTTPException(status_code=409, detail=str(exc)) from exc
return asdict(receipt)

View file

@ -104,3 +104,5 @@ class ExecutionSemantics(BaseModel):
concurrency_modes: dict[str, str]
state_hub_responsibility: list[str]
activity_core_responsibility: list[str]
launch_requests_accepted: bool = False
replacements: dict[str, str] = Field(default_factory=dict)

View file

@ -0,0 +1,33 @@
from typing import Any
from pydantic import BaseModel, ConfigDict, Field
class SealedProjectionRepairSubmit(BaseModel):
plan: dict[str, Any]
repo_slug: str = Field(pattern=r"^[a-z0-9][a-z0-9-]{0,99}$")
unit: dict[str, Any]
expected_plan_sha256: str = Field(pattern=r"^[0-9a-f]{64}$")
source_revision: str = Field(pattern=r"^[0-9a-f]{40}$")
source_fingerprint: str = Field(pattern=r"^[0-9a-f]{64}$")
source_clean: bool
source_synchronized: bool
primary_confirmed: bool
projection_identity: str = Field(min_length=1, max_length=200)
class SealedProjectionRepairReceiptRead(BaseModel):
model_config = ConfigDict(populate_by_name=True)
schema_version: str = Field(alias="schema")
outcome: str
repo_slug: str
repository_id: str
workplan_record_id: str
workplan_old_id: str
task_records: list[tuple[str, str]]
plan_sha256: str
source_revision: str
source_fingerprint: str
projection_identity: str
observed_at: str

View file

@ -9,7 +9,7 @@ EXECUTION_STATES = {
"manual": "Not queued for autonomous pickup; humans or agents may still work manually.",
"queued": "Candidate for ordered pickup when dependencies and concurrency allow it.",
"scheduled": "Waiting for an external launch window; State Hub stores the requested time.",
"launching": "A launch request asks for immediate pickup or has been handed off.",
"launching": "Legacy state only; it does not prove that any consumer accepted pickup.",
"paused": "Temporarily held outside the pickup stack.",
"completed": "Execution intent is closed; lifecycle status remains authoritative.",
"cancelled": "Execution intent was cancelled without changing lifecycle status.",
@ -19,7 +19,7 @@ LAUNCH_MODES = {
"manual": "Do not request automation; keep intent visible only.",
"queued": "Place in the prioritized stack for later pickup.",
"scheduled": "Request pickup at or after a selected time.",
"immediate": "Request prompt activity-core or agent pickup.",
"immediate": "Legacy intent only; workplan launch-request pickup is retired.",
}
CONCURRENCY_MODES = {
@ -30,16 +30,26 @@ CONCURRENCY_MODES = {
STATE_HUB_RESPONSIBILITIES = [
"store lifecycle status separately from execution intent",
"rank candidate workplans and expose dependency-aware eligibility",
"record launch requests and handoff metadata durably",
"surface manual, queued, scheduled, and immediate intent to operators",
"preserve historical launch-request and execution-intent rows during retirement",
"reject new workplan launch requests because no pickup consumer exists",
]
ACTIVITY_CORE_RESPONSIBILITIES = [
"own schedules, wakeups, and recurring automation",
"dispatch coding agents and coordinate parallel execution",
"acknowledge, run, and complete launch requests when available",
"claim and complete ops runs created by ActivityDefinition fires",
"do not consume State Hub workplan launch requests",
]
EXECUTION_REPLACEMENTS = {
"POST /execution/launch-requests": (
"Queue work in the authoritative repository file; for recurring or operational "
"fires use an ActivityDefinition and activity-core ops_run"
),
"GET /execution/launch-requests": "GET /ops-runs on activity-core for automation history",
"GET /execution/workplan-stack": "Repo Manager work index or hub-core projection",
"PATCH /execution/workplans/{id}/intent": "Edit the authoritative workplan file",
}
EXECUTION_STATE_RANK = {
"launching": 0,
"queued": 1,

View file

@ -10,10 +10,13 @@ from dataclasses import dataclass
from datetime import UTC, datetime
from typing import Any
from sqlalchemy import select, text
from sqlalchemy import func, select, text
from sqlalchemy.ext.asyncio import AsyncSession
from api.models.managed_repo import ManagedRepo
from api.models.task import Task, TaskPriority, TaskStatus
from api.models.work_record_identifier_alias import WorkRecordIdentifierAlias
from api.models.workplan import Workplan
PLAN_SCHEMA = "repo-manager.identifier-migration-plan.v1"
FLEET_NAMESPACE = "helixforge"
@ -34,6 +37,24 @@ class IdentifierMigrationResult:
assignments_deferred: int
@dataclass(frozen=True)
class SealedProjectionRepairReceipt:
"""Non-secret evidence for one bounded pre-derivation projection repair."""
schema: str
outcome: str
repo_slug: str
repository_id: str
workplan_record_id: str
workplan_old_id: str
task_records: tuple[tuple[str, str], ...]
plan_sha256: str
source_revision: str
source_fingerprint: str
projection_identity: str
observed_at: str
def verify_plan(plan: dict[str, Any]) -> str:
"""Validate the immutable plan envelope and deterministic UUID mappings."""
if plan.get("schema") != PLAN_SCHEMA:
@ -117,6 +138,328 @@ def _repository_mappings(
return replacements, assignments
def _repository_entry(plan: dict[str, Any], repo_slug: str) -> dict[str, Any]:
matches = [item for item in plan.get("repositories", []) if item.get("repo") == repo_slug]
if len(matches) != 1:
raise IdentifierMigrationError(
f"repository {repo_slug!r} must occur exactly once in plan"
)
return matches[0]
def _repair_unit_mappings(
plan: dict[str, Any], repo_slug: str, unit: dict[str, Any]
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
replacements, _assignments = _repository_mappings(plan, repo_slug)
workplan = unit.get("workplan")
tasks = unit.get("tasks")
if not isinstance(workplan, dict) or not isinstance(tasks, list):
raise IdentifierMigrationError("repair unit requires one workplan and a task list")
workplan_record_id = workplan.get("record_id")
workplan_matches = [
mapping
for mapping in replacements
if mapping["kind"] == "workplan" and mapping["record_id"] == workplan_record_id
]
if len(workplan_matches) != 1:
raise IdentifierMigrationError(
"repair workplan must occur exactly once as a replacement in the sealed plan"
)
workplan_mapping = workplan_matches[0]
raw_workplan_mapping = next(
raw
for raw in _repository_entry(plan, repo_slug).get("mappings", [])
if raw.get("kind") == "workplan" and raw.get("record_id") == workplan_record_id
)
authoritative_path = raw_workplan_mapping.get("path")
if not isinstance(authoritative_path, str) or not authoritative_path:
raise IdentifierMigrationError("sealed workplan mapping has no authoritative path")
task_record_ids = [task.get("record_id") for task in tasks if isinstance(task, dict)]
if len(task_record_ids) != len(tasks) or len(set(task_record_ids)) != len(tasks):
raise IdentifierMigrationError("repair task record ids must be explicit and unique")
task_mappings = [
mapping
for mapping in replacements
if mapping["kind"] == "task" and mapping["record_id"] in task_record_ids
]
mapped_task_ids = {mapping["record_id"] for mapping in task_mappings}
if mapped_task_ids != set(task_record_ids):
raise IdentifierMigrationError(
"repair tasks do not exactly match replacement mappings in the sealed plan"
)
raw_unit_task_ids = {
raw.get("record_id")
for raw in _repository_entry(plan, repo_slug).get("mappings", [])
if raw.get("kind") == "task"
and raw.get("action") == "replace"
and raw.get("path") == authoritative_path
}
if raw_unit_task_ids != set(task_record_ids):
raise IdentifierMigrationError(
"repair must include every replaced task in the sealed workplan unit"
)
return workplan_mapping, task_mappings
def _uuid_field(value: Any, label: str) -> uuid.UUID:
try:
return uuid.UUID(str(value))
except (TypeError, ValueError) as exc:
raise IdentifierMigrationError(f"invalid UUID for {label}") from exc
def _repair_payloads(
unit: dict[str, Any],
workplan_mapping: dict[str, Any],
task_mappings: list[dict[str, Any]],
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
raw_workplan = unit["workplan"]
if _uuid_field(raw_workplan.get("id"), "workplan") != workplan_mapping["old_id"]:
raise IdentifierMigrationError("repair workplan UUID does not match sealed old UUID")
slug = raw_workplan.get("slug")
title = raw_workplan.get("title")
status = raw_workplan.get("status")
if not isinstance(slug, str) or not slug or not isinstance(title, str) or not title:
raise IdentifierMigrationError("repair workplan requires explicit slug and title")
if status not in {
"proposed", "ready", "active", "blocked", "backlog", "finished", "archived"
}:
raise IdentifierMigrationError(f"unsupported repair workplan status {status!r}")
workplan_payload = {
"id": workplan_mapping["old_id"],
"topic_id": _uuid_field(raw_workplan.get("topic_id"), "workplan topic"),
"slug": slug,
"title": title,
"description": raw_workplan.get("description"),
"status": status,
"owner": raw_workplan.get("owner"),
"planning_priority": raw_workplan.get("planning_priority"),
"planning_order": raw_workplan.get("planning_order"),
}
mappings_by_record = {mapping["record_id"]: mapping for mapping in task_mappings}
task_payloads: list[dict[str, Any]] = []
for raw_task in unit["tasks"]:
mapping = mappings_by_record[raw_task["record_id"]]
if _uuid_field(raw_task.get("id"), raw_task["record_id"]) != mapping["old_id"]:
raise IdentifierMigrationError(
f"repair task UUID does not match sealed old UUID for {raw_task['record_id']}"
)
title = raw_task.get("title")
if not isinstance(title, str) or not title:
raise IdentifierMigrationError(
f"repair task {raw_task['record_id']} requires an explicit title"
)
try:
status_value = TaskStatus(raw_task.get("status", "todo"))
priority_value = TaskPriority(raw_task.get("priority", "medium"))
except ValueError as exc:
raise IdentifierMigrationError(
f"invalid task state for {raw_task['record_id']}"
) from exc
parent_record_id = raw_task.get("parent_record_id")
if parent_record_id is not None and parent_record_id not in mappings_by_record:
raise IdentifierMigrationError(
f"parent task for {raw_task['record_id']} is outside the sealed repair unit"
)
task_payloads.append(
{
"record_id": raw_task["record_id"],
"id": mapping["old_id"],
"title": title,
"description": raw_task.get("description"),
"status": status_value,
"priority": priority_value,
"assignee": raw_task.get("assignee"),
"blocking_reason": raw_task.get("blocking_reason"),
"needs_human": bool(raw_task.get("needs_human", False)),
"intervention_note": raw_task.get("intervention_note"),
"parent_task_id": (
mappings_by_record[parent_record_id]["old_id"]
if parent_record_id is not None
else None
),
}
)
if task_payloads[-1]["needs_human"] and not task_payloads[-1]["intervention_note"]:
raise IdentifierMigrationError(
f"repair task {raw_task['record_id']} needs an intervention note"
)
if (
task_payloads[-1]["status"] == TaskStatus.wait
and task_payloads[-1]["needs_human"]
and not task_payloads[-1]["blocking_reason"]
):
raise IdentifierMigrationError(
f"repair task {raw_task['record_id']} needs a blocking reason"
)
pending = list(task_payloads)
ordered: list[dict[str, Any]] = []
emitted: set[uuid.UUID] = set()
while pending:
ready = [
task
for task in pending
if task["parent_task_id"] is None or task["parent_task_id"] in emitted
]
if not ready:
raise IdentifierMigrationError("repair task parentage contains a cycle")
for task in ready:
pending.remove(task)
ordered.append(task)
emitted.add(task["id"])
return workplan_payload, ordered
def _same_workplan(row: Workplan, expected: dict[str, Any], repo_id: uuid.UUID) -> bool:
fields = (
"id", "topic_id", "slug", "title", "description", "status", "owner",
"planning_priority", "planning_order",
)
return row.repo_id == repo_id and all(
getattr(row, field) == expected[field] for field in fields
)
def _same_task(row: Task, expected: dict[str, Any], workplan_id: uuid.UUID) -> bool:
fields = (
"id", "title", "description", "status", "priority", "assignee",
"blocking_reason", "needs_human", "intervention_note", "parent_task_id",
)
return row.workplan_id == workplan_id and all(
getattr(row, field) == expected[field] for field in fields
)
async def repair_absent_prederivation_projection(
session: AsyncSession,
plan: dict[str, Any],
repo_slug: str,
unit: dict[str, Any],
*,
expected_plan_sha256: str,
source_revision: str,
source_fingerprint: str,
source_clean: bool,
source_synchronized: bool,
primary_confirmed: bool,
projection_identity: str,
observed_at: datetime | None = None,
) -> SealedProjectionRepairReceipt:
"""Restore one exact random-ID workplan unit into an absent projection.
This is intentionally separate from ordinary consistency repair. The caller
must independently prove Git cleanliness/synchronization and provide the
authoritative unit parsed from the pinned source revision.
"""
plan_sha256 = verify_plan(plan)
if plan_sha256 != expected_plan_sha256:
raise IdentifierMigrationError("explicit plan SHA-256 does not match sealed plan")
if not primary_confirmed:
raise IdentifierMigrationError("projection repair requires explicit primary confirmation")
if not source_clean or not source_synchronized:
raise IdentifierMigrationError("projection repair requires a clean synchronized source")
if not re.fullmatch(r"[0-9a-f]{40}", source_revision):
raise IdentifierMigrationError("source revision must be a full Git SHA")
if not re.fullmatch(r"[0-9a-f]{64}", source_fingerprint):
raise IdentifierMigrationError("source fingerprint must be SHA-256")
if not projection_identity.strip():
raise IdentifierMigrationError("projection identity is required")
repository_entry = _repository_entry(plan, repo_slug)
if source_revision != repository_entry.get("planned_head_sha"):
raise IdentifierMigrationError("source revision drifted from sealed plan")
if source_fingerprint != repository_entry.get("source_fingerprint"):
raise IdentifierMigrationError("source fingerprint drifted from sealed plan")
workplan_mapping, task_mappings = _repair_unit_mappings(plan, repo_slug, unit)
workplan_payload, task_payloads = _repair_payloads(
unit, workplan_mapping, task_mappings
)
if session.in_transaction():
raise IdentifierMigrationError("projection repair requires a fresh database session")
outcome = "repaired"
async with session.begin():
repo = await session.scalar(
select(ManagedRepo).where(ManagedRepo.slug == repo_slug).with_for_update()
)
if repo is None:
raise IdentifierMigrationError(f"repository projection is absent: {repo_slug}")
if repo.topic_id != workplan_payload["topic_id"]:
raise IdentifierMigrationError("repair topic does not match repository projection")
old_ids = [workplan_mapping["old_id"], *(m["old_id"] for m in task_mappings)]
new_workplan = await session.get(Workplan, workplan_mapping["new_id"])
new_tasks = [await session.get(Task, mapping["new_id"]) for mapping in task_mappings]
if new_workplan is not None or any(task is not None for task in new_tasks):
raise IdentifierMigrationError("derived target presence blocks projection repair")
alias_count = await session.scalar(
select(func.count()).select_from(WorkRecordIdentifierAlias).where(
WorkRecordIdentifierAlias.old_id.in_(old_ids)
)
)
if alias_count:
raise IdentifierMigrationError("durable alias presence blocks projection repair")
old_workplan = await session.get(Workplan, workplan_mapping["old_id"])
old_tasks = [await session.get(Task, mapping["old_id"]) for mapping in task_mappings]
present_count = int(old_workplan is not None) + sum(task is not None for task in old_tasks)
expected_count = 1 + len(task_mappings)
if present_count not in {0, expected_count}:
raise IdentifierMigrationError("partial old projection presence blocks repair")
if present_count == expected_count:
if not _same_workplan(old_workplan, workplan_payload, repo.id):
raise IdentifierMigrationError(
"existing old workplan differs from sealed repair unit"
)
expected_tasks = {task["id"]: task for task in task_payloads}
if any(
not _same_task(task, expected_tasks[task.id], workplan_mapping["old_id"])
for task in old_tasks
):
raise IdentifierMigrationError("existing old tasks differ from sealed repair unit")
outcome = "verified_noop"
else:
slug_conflict = await session.scalar(
select(Workplan.id).where(Workplan.slug == workplan_payload["slug"])
)
if slug_conflict is not None:
raise IdentifierMigrationError("workplan slug is already projected")
session.add(
Workplan(repo_id=repo.id, **workplan_payload)
)
await session.flush()
for task_payload in task_payloads:
values = {key: value for key, value in task_payload.items() if key != "record_id"}
session.add(Task(workplan_id=workplan_mapping["old_id"], **values))
await session.flush()
timestamp = observed_at or datetime.now(UTC)
if timestamp.tzinfo is None:
raise IdentifierMigrationError("receipt observation time must be timezone-aware")
return SealedProjectionRepairReceipt(
schema="state-hub.sealed-prederivation-projection-repair-receipt.v1",
outcome=outcome,
repo_slug=repo_slug,
repository_id=str(repo.id),
workplan_record_id=workplan_mapping["record_id"],
workplan_old_id=str(workplan_mapping["old_id"]),
task_records=tuple(
(mapping["record_id"], str(mapping["old_id"])) for mapping in task_mappings
),
plan_sha256=plan_sha256,
source_revision=source_revision,
source_fingerprint=source_fingerprint,
projection_identity=projection_identity,
observed_at=timestamp.astimezone(UTC).isoformat().replace("+00:00", "Z"),
)
async def _assert_projection_preconditions(
session: AsyncSession,
repo_slug: str,

View file

@ -0,0 +1,929 @@
{
"captured_at": "2026-08-22T14:00:05.083125+00:00",
"api_base": "http://127.0.0.1:8000",
"workplan": "STATE-WP-0070",
"retired_interfaces": [],
"weekly_review": {
"generated_at": "2026-08-22T14:00:05.076969Z",
"window_start": "2026-08-15T14:00:04.994574Z",
"window_end": "2026-08-22T14:00:04.994574Z",
"cadence": "weekly",
"activity_core_handoff": {
"activity_id": "statehub-legacy-interface-review",
"cadence": "weekly",
"source_endpoint": "/legacy-meter/weekly-review",
"state_owner": "state-hub",
"scheduler_owner": "activity-core"
},
"interfaces": [
{
"interface": {
"id": "8051fb40-66c9-4dc4-a413-13e8573e04a3",
"interface_key": "event_subject:org.statehub.workstream.completed",
"interface_kind": "event_subject",
"legacy_since": "2026-06-04T06:09:42.198193Z",
"replacement_ref": "org.statehub.workplan.completed",
"owner_component": "state-hub.events",
"status": "retired",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": "Retired by STATE-WP-0069 closeout",
"retired_at": "2026-07-08T21:38:52.773601Z",
"created_at": "2026-06-04T06:09:42.198193Z",
"updated_at": "2026-07-08T21:38:52.761243Z"
},
"all_time": {
"calls": 255,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 255
},
"users": {
"unknown": 255
},
"components": {
"state-hub.events": 255
}
},
"window": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"last_seen_at": "2026-07-08T20:49:12.839383Z",
"retirement_candidate": false,
"retirement_reason": "already retired"
},
{
"interface": {
"id": "63ed90e3-42b4-431a-94db-e1254a52f9e2",
"interface_key": "mcp:create_workstream",
"interface_kind": "mcp_tool",
"legacy_since": "2026-07-10T13:19:44.205900Z",
"replacement_ref": "create_workplan",
"owner_component": "state-hub.mcp",
"status": "retired",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": "Retired by STATE-WP-0079-T05 (cutover slice E2); evidence: 7d review window + volume-scaled quiet ladder, capture 2026-08-20",
"retired_at": "2026-08-20T05:27:19.851030Z",
"created_at": "2026-07-10T13:19:44.205900Z",
"updated_at": "2026-08-20T05:27:19.844807Z"
},
"all_time": {
"calls": 2,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 2
},
"users": {
"unknown": 2
},
"components": {
"state-hub.mcp": 2
}
},
"window": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"last_seen_at": "2026-07-10T13:20:43.378546Z",
"retirement_candidate": false,
"retirement_reason": "already retired"
},
{
"interface": {
"id": "b6a5fe44-fb85-4ef7-a273-78d09da777af",
"interface_key": "mcp:list_workstreams",
"interface_kind": "mcp_tool",
"legacy_since": "2026-07-10T13:19:26.075491Z",
"replacement_ref": "list_workplans",
"owner_component": "state-hub.mcp",
"status": "retired",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": "Retired by STATE-WP-0079-T05 (cutover slice E2); evidence: 7d review window + volume-scaled quiet ladder, capture 2026-08-20",
"retired_at": "2026-08-20T05:27:19.883196Z",
"created_at": "2026-07-10T13:19:26.075491Z",
"updated_at": "2026-08-20T05:27:19.876635Z"
},
"all_time": {
"calls": 2,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 2
},
"users": {
"unknown": 2
},
"components": {
"state-hub.mcp": 2
}
},
"window": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"last_seen_at": "2026-07-10T13:20:42.374940Z",
"retirement_candidate": false,
"retirement_reason": "already retired"
},
{
"interface": {
"id": "53c3372b-307d-4d6a-8ea4-bc36cacdcaf2",
"interface_key": "mcp:update_workstream",
"interface_kind": "mcp_tool",
"legacy_since": "2026-07-10T13:19:44.255216Z",
"replacement_ref": "update_workplan",
"owner_component": "state-hub.mcp",
"status": "retired",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": "Retired by STATE-WP-0079-T05 (cutover slice E2); evidence: 7d review window + volume-scaled quiet ladder, capture 2026-08-20",
"retired_at": "2026-08-20T05:27:19.907081Z",
"created_at": "2026-07-10T13:19:44.255216Z",
"updated_at": "2026-08-20T05:27:19.902177Z"
},
"all_time": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"window": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"last_seen_at": null,
"retirement_candidate": false,
"retirement_reason": "already retired"
},
{
"interface": {
"id": "bed4d08a-3471-48f5-9f0e-f9d550383a26",
"interface_key": "mcp:update_workstream_status",
"interface_kind": "mcp_tool",
"legacy_since": "2026-07-10T13:19:44.303612Z",
"replacement_ref": "update_workplan_status",
"owner_component": "state-hub.mcp",
"status": "retired",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": "Retired by STATE-WP-0079-T05 (cutover slice E2); evidence: 7d review window + volume-scaled quiet ladder, capture 2026-08-20",
"retired_at": "2026-08-20T05:27:19.928988Z",
"created_at": "2026-07-10T13:19:44.303612Z",
"updated_at": "2026-08-20T05:27:19.925652Z"
},
"all_time": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"window": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"last_seen_at": null,
"retirement_candidate": false,
"retirement_reason": "already retired"
},
{
"interface": {
"id": "46900dc2-0893-405b-ba59-80f31fb6ebb1",
"interface_key": "state://workstreams/{topic_slug}",
"interface_kind": "mcp_tool",
"legacy_since": "2026-07-10T13:19:44.344493Z",
"replacement_ref": "state://workplans/{topic_slug}",
"owner_component": "state-hub.mcp",
"status": "retired",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": "Retired by STATE-WP-0079-T05 (cutover slice E2); evidence: 7d review window + volume-scaled quiet ladder, capture 2026-08-20",
"retired_at": "2026-08-20T05:27:19.948467Z",
"created_at": "2026-07-10T13:19:44.344493Z",
"updated_at": "2026-08-20T05:27:19.944874Z"
},
"all_time": {
"calls": 1,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 1
},
"users": {
"unknown": 1
},
"components": {
"state-hub.mcp": 1
}
},
"window": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"last_seen_at": "2026-07-10T13:20:34.491811Z",
"retirement_candidate": false,
"retirement_reason": "already retired"
},
{
"interface": {
"id": "cd9342fd-27e7-4a46-a1be-d41499e106a3",
"interface_key": "rest_api:DELETE /workstreams/{workstream_id}",
"interface_kind": "rest_api",
"legacy_since": "2026-06-06T17:28:13.052813Z",
"replacement_ref": "/workplans/{workplan_id}",
"owner_component": "state-hub.api",
"status": "retired",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": "Retired by STATE-WP-0079-T05 (cutover slice E2); evidence: 7d review window + volume-scaled quiet ladder, capture 2026-08-20",
"retired_at": "2026-08-20T05:27:19.972086Z",
"created_at": "2026-06-06T17:28:13.052813Z",
"updated_at": "2026-08-20T05:27:19.966289Z"
},
"all_time": {
"calls": 1,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 1
},
"users": {
"unknown": 1
},
"components": {
"unknown": 1
}
},
"window": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"last_seen_at": "2026-06-06T17:28:13.047529Z",
"retirement_candidate": false,
"retirement_reason": "already retired"
},
{
"interface": {
"id": "a4e0948c-cd4a-461e-8ae2-7fd7b51d04a7",
"interface_key": "rest_api:GET /decisions/?workstream_id",
"interface_kind": "rest_api",
"legacy_since": "2026-07-09T23:07:53.093223Z",
"replacement_ref": "/decisions/?workplan_id=<workplan_id>",
"owner_component": "state-hub.api",
"status": "retired",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": "Retired by STATE-WP-0079-T05 (cutover slice E2); evidence: 7d review window + volume-scaled quiet ladder, capture 2026-08-20",
"retired_at": "2026-08-20T05:27:19.996130Z",
"created_at": "2026-07-09T23:07:53.093223Z",
"updated_at": "2026-08-20T05:27:19.991877Z"
},
"all_time": {
"calls": 1,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 1
},
"users": {
"unknown": 1
},
"components": {
"unknown": 1
}
},
"window": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"last_seen_at": "2026-07-09T23:07:53.083299Z",
"retirement_candidate": false,
"retirement_reason": "already retired"
},
{
"interface": {
"id": "70a9346e-e0ee-42ac-874f-f160580b54dd",
"interface_key": "rest_api:GET /progress/?workstream_id",
"interface_kind": "rest_api",
"legacy_since": "2026-07-13T22:04:03.911449Z",
"replacement_ref": "/progress/?workplan_id=<workplan_id>",
"owner_component": "state-hub.api",
"status": "retired",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": "Retired by STATE-WP-0079-T05 (cutover slice E2); evidence: 7d review window + volume-scaled quiet ladder, capture 2026-08-20",
"retired_at": "2026-08-20T05:27:20.017065Z",
"created_at": "2026-07-13T22:04:03.911449Z",
"updated_at": "2026-08-20T05:27:20.013500Z"
},
"all_time": {
"calls": 2,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 2
},
"users": {
"unknown": 2
},
"components": {
"unknown": 2
}
},
"window": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"last_seen_at": "2026-08-12T08:55:33.912995Z",
"retirement_candidate": false,
"retirement_reason": "already retired"
},
{
"interface": {
"id": "be3cdcf8-250b-4bf0-801f-02ffd5f57e00",
"interface_key": "rest_api:GET /tasks/?workstream_id",
"interface_kind": "rest_api",
"legacy_since": "2026-07-08T19:32:21.628513Z",
"replacement_ref": "/tasks/?workplan_id=<workplan_id>",
"owner_component": "state-hub.api",
"status": "legacy",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": null,
"retired_at": null,
"created_at": "2026-07-08T19:32:21.628513Z",
"updated_at": "2026-07-08T19:32:21.628513Z"
},
"all_time": {
"calls": 596,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 596
},
"users": {
"unknown": 596
},
"components": {
"unknown": 596
}
},
"window": {
"calls": 6,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 6
},
"users": {
"unknown": 6
},
"components": {
"unknown": 6
}
},
"last_seen_at": "2026-08-20T20:57:26.985884Z",
"retirement_candidate": false,
"retirement_reason": "6 call(s) in review window"
},
{
"interface": {
"id": "3e4a3d0b-08fa-45c3-91e2-4f514a914b97",
"interface_key": "rest_api:GET /workstreams/",
"interface_kind": "rest_api",
"legacy_since": "2026-06-04T00:26:14.533764Z",
"replacement_ref": "/workplans/",
"owner_component": "state-hub.api",
"status": "legacy",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": null,
"retired_at": null,
"created_at": "2026-06-04T00:26:14.533764Z",
"updated_at": "2026-06-04T00:26:14.533764Z"
},
"all_time": {
"calls": 144087,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 144087
},
"users": {
"unknown": 144087
},
"components": {
"unknown": 144087
}
},
"window": {
"calls": 1,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 1
},
"users": {
"unknown": 1
},
"components": {
"unknown": 1
}
},
"last_seen_at": "2026-08-21T22:29:25.068158Z",
"retirement_candidate": false,
"retirement_reason": "1 call(s) in review window"
},
{
"interface": {
"id": "e4c140ec-7479-46f9-b185-4b533f19d639",
"interface_key": "rest_api:GET /workstreams/workplan-index",
"interface_kind": "rest_api",
"legacy_since": "2026-06-04T05:20:58.321869Z",
"replacement_ref": "/workplans/index",
"owner_component": "state-hub.api",
"status": "retired",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": "Retired by STATE-WP-0079-T05 (cutover slice E2); evidence: 7d review window + volume-scaled quiet ladder, capture 2026-08-20",
"retired_at": "2026-08-20T05:27:20.041850Z",
"created_at": "2026-06-04T05:20:58.321869Z",
"updated_at": "2026-08-20T05:27:20.035160Z"
},
"all_time": {
"calls": 30,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 30
},
"users": {
"unknown": 30
},
"components": {
"unknown": 30
}
},
"window": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"last_seen_at": "2026-07-03T05:20:34.534439Z",
"retirement_candidate": false,
"retirement_reason": "already retired"
},
{
"interface": {
"id": "3a00b19e-e7db-4403-98aa-a77c9ab61ecc",
"interface_key": "rest_api:GET /workstreams/{workstream_id}",
"interface_kind": "rest_api",
"legacy_since": "2026-06-04T00:25:59.014966Z",
"replacement_ref": "/workplans/{workplan_id}",
"owner_component": "state-hub.api",
"status": "legacy",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": null,
"retired_at": null,
"created_at": "2026-06-04T00:25:59.014966Z",
"updated_at": "2026-06-04T00:25:59.014966Z"
},
"all_time": {
"calls": 511407,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 511407
},
"users": {
"unknown": 511407
},
"components": {
"unknown": 511407
}
},
"window": {
"calls": 1,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 1
},
"users": {
"unknown": 1
},
"components": {
"unknown": 1
}
},
"last_seen_at": "2026-08-20T23:32:31.368781Z",
"retirement_candidate": false,
"retirement_reason": "1 call(s) in review window"
},
{
"interface": {
"id": "69e1a255-a8b9-4101-8a68-b1bed23abfda",
"interface_key": "rest_api:GET /workstreams/{workstream_id}/dependencies/",
"interface_kind": "rest_api",
"legacy_since": "2026-06-04T00:25:59.291135Z",
"replacement_ref": "/workplans/{workplan_id}/dependencies/",
"owner_component": "state-hub.api",
"status": "legacy",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": null,
"retired_at": null,
"created_at": "2026-06-04T00:25:59.291135Z",
"updated_at": "2026-06-04T00:25:59.291135Z"
},
"all_time": {
"calls": 255865,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 255865
},
"users": {
"unknown": 255865
},
"components": {
"unknown": 255865
}
},
"window": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"last_seen_at": "2026-07-08T21:14:12.747005Z",
"retirement_candidate": false,
"retirement_reason": "quiet 44d of 60d required for 255865 all-time call(s)"
},
{
"interface": {
"id": "b1aae931-b51f-4a85-a723-865dd65f6132",
"interface_key": "rest_api:PATCH /workstreams/{workstream_id}",
"interface_kind": "rest_api",
"legacy_since": "2026-06-04T06:09:41.901035Z",
"replacement_ref": "/workplans/{workplan_id}",
"owner_component": "state-hub.api",
"status": "retired",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": "Retired by STATE-WP-0079-T05 (cutover slice E2); evidence: 7d review window + volume-scaled quiet ladder, capture 2026-08-20",
"retired_at": "2026-08-20T05:27:20.069758Z",
"created_at": "2026-06-04T06:09:41.901035Z",
"updated_at": "2026-08-20T05:27:20.062648Z"
},
"all_time": {
"calls": 571,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 571
},
"users": {
"unknown": 571
},
"components": {
"unknown": 571
}
},
"window": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"last_seen_at": "2026-07-08T20:49:12.335710Z",
"retirement_candidate": false,
"retirement_reason": "already retired"
},
{
"interface": {
"id": "4aea9591-970b-4918-ac4a-4da95a0a1a8c",
"interface_key": "rest_api:POST /decisions/ workstream_id",
"interface_kind": "rest_api",
"legacy_since": "2026-07-09T22:56:35.473130Z",
"replacement_ref": "POST /decisions/ with workplan_id",
"owner_component": "state-hub.api",
"status": "retired",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": "Retired by STATE-WP-0079-T05 (cutover slice E2); evidence: 7d review window + volume-scaled quiet ladder, capture 2026-08-20",
"retired_at": "2026-08-20T05:27:20.093372Z",
"created_at": "2026-07-09T22:56:35.473130Z",
"updated_at": "2026-08-20T05:27:20.090180Z"
},
"all_time": {
"calls": 3,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 3
},
"users": {
"unknown": 3
},
"components": {
"unknown": 3
}
},
"window": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"last_seen_at": "2026-07-09T22:57:05.912456Z",
"retirement_candidate": false,
"retirement_reason": "already retired"
},
{
"interface": {
"id": "4a70583c-1e41-4ec1-99a9-728856a7d79c",
"interface_key": "rest_api:POST /progress/ workstream_id",
"interface_kind": "rest_api",
"legacy_since": "2026-07-08T23:35:09.148333Z",
"replacement_ref": "POST /progress/ with workplan_id",
"owner_component": "state-hub.api",
"status": "retired",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": "Retired by STATE-WP-0079-T05 (cutover slice E2); evidence: 7d review window + volume-scaled quiet ladder, capture 2026-08-20",
"retired_at": "2026-08-20T05:27:20.114395Z",
"created_at": "2026-07-08T23:35:09.148333Z",
"updated_at": "2026-08-20T05:27:20.110090Z"
},
"all_time": {
"calls": 14,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 14
},
"users": {
"unknown": 14
},
"components": {
"unknown": 14
}
},
"window": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"last_seen_at": "2026-07-10T08:34:42.959724Z",
"retirement_candidate": false,
"retirement_reason": "already retired"
},
{
"interface": {
"id": "6df4e443-7bf1-461a-923b-a6eb52fe7b07",
"interface_key": "rest_api:POST /tasks/ workstream_id",
"interface_kind": "rest_api",
"legacy_since": "2026-07-08T22:38:29.405720Z",
"replacement_ref": "POST /tasks/ with workplan_id",
"owner_component": "state-hub.api",
"status": "retired",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": "Retired by STATE-WP-0079-T05 (cutover slice E2); evidence: 7d review window + volume-scaled quiet ladder, capture 2026-08-20",
"retired_at": "2026-08-20T05:27:20.134796Z",
"created_at": "2026-07-08T22:38:29.405720Z",
"updated_at": "2026-08-20T05:27:20.131430Z"
},
"all_time": {
"calls": 5,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 5
},
"users": {
"unknown": 5
},
"components": {
"state-hub.fix-consistency": 5
}
},
"window": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"last_seen_at": "2026-07-08T22:38:29.976918Z",
"retirement_candidate": false,
"retirement_reason": "already retired"
},
{
"interface": {
"id": "ed6451c9-d2cc-4486-9afe-d3852782cabc",
"interface_key": "rest_api:POST /workstreams/",
"interface_kind": "rest_api",
"legacy_since": "2026-06-04T07:40:24.168535Z",
"replacement_ref": "/workplans/",
"owner_component": "state-hub.api",
"status": "retired",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": "Retired by STATE-WP-0079-T05 (cutover slice E2); evidence: 7d review window + volume-scaled quiet ladder, capture 2026-08-20",
"retired_at": "2026-08-20T05:27:20.157133Z",
"created_at": "2026-06-04T07:40:24.168535Z",
"updated_at": "2026-08-20T05:27:20.150639Z"
},
"all_time": {
"calls": 824,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 824
},
"users": {
"unknown": 824
},
"components": {
"unknown": 824
}
},
"window": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"last_seen_at": "2026-07-08T20:38:05.903144Z",
"retirement_candidate": false,
"retirement_reason": "already retired"
},
{
"interface": {
"id": "281fd706-b192-4194-8055-2d8733c115f9",
"interface_key": "rest_api:POST /workstreams/{workstream_id}/dependencies/",
"interface_kind": "rest_api",
"legacy_since": "2026-06-04T22:57:37.149207Z",
"replacement_ref": "/workplans/{workplan_id}/dependencies/",
"owner_component": "state-hub.api",
"status": "retired",
"replacement_verified": true,
"manual_hold": false,
"hold_reason": null,
"notes": "Retired by STATE-WP-0079-T05 (cutover slice E2); evidence: 7d review window + volume-scaled quiet ladder, capture 2026-08-20",
"retired_at": "2026-08-20T05:27:20.186201Z",
"created_at": "2026-06-04T22:57:37.149207Z",
"updated_at": "2026-08-20T05:27:20.181406Z"
},
"all_time": {
"calls": 4971,
"tenant_count": 1,
"user_count": 1,
"component_count": 1,
"tenants": {
"unknown": 4971
},
"users": {
"unknown": 4971
},
"components": {
"unknown": 4971
}
},
"window": {
"calls": 0,
"tenant_count": 0,
"user_count": 0,
"component_count": 0,
"tenants": {},
"users": {},
"components": {}
},
"last_seen_at": "2026-07-01T22:09:24.229011Z",
"retirement_candidate": false,
"retirement_reason": "already retired"
}
],
"retirement_candidates": []
},
"days": 7
}

View file

@ -0,0 +1,45 @@
# Sealed pre-derivation projection repair
This temporary compatibility surface supports the deterministic-identifier
cutover in `STATE-WP-0079-T04`. It restores one exact random-UUID workplan unit
into a partial State Hub projection so the existing transactional migration can
replace the old UUIDs with their deterministic UUIDv5 targets.
Ordinary `statehub fix-consistency` behavior is unchanged: a missing random UUID
remains a non-fixable C-03 stale reference.
## Endpoint
`POST /identifier-migrations/sealed-projection-repairs`
The request must contain:
- the complete sealed Repo Manager migration plan;
- the exact plan SHA-256, repository slug, pinned 40-character Git revision,
and source fingerprint recorded in that plan;
- `source_clean`, `source_synchronized`, and `primary_confirmed` set to true;
- one explicit authoritative workplan projection and every `replace` task
mapped to the same sealed workplan file; and
- a non-secret projection identity for the receipt.
The workplan projection supplies its canonical `record_id`, exact old UUID,
topic UUID, projection slug, title, status, and optional projection fields.
Each task supplies its canonical `record_id`, exact old UUID, title, state, and
optional projection fields. Parentage uses `parent_record_id`, never a path- or
position-derived identity.
The server rejects source drift, plan-seal drift, repository/topic mismatch,
partial old-row presence, any derived-target presence, alias conflicts, slug
conflicts, incomplete task units, and non-identical retries. The workplan and
tasks are created in one database transaction. An exact retry returns a
`verified_noop` receipt.
The receipt contains no source content or secrets: repository/projection
identity, canonical IDs and old UUIDs, plan/source seals, outcome, and a UTC
observation timestamp.
## Removal condition
Remove this endpoint with the identifier-migration executor after every sealed
pre-derivation unit has either completed deterministic cutover or been rejected
and residual-owned. It is not a general projection-import API.

View file

@ -1006,7 +1006,7 @@ class TestReconciliationEndpoints:
class TestExecutionQueueEndpoints:
async def test_execution_semantics_separates_state_hub_and_activity_core(self, client):
async def test_execution_semantics_retires_workplan_launch_pickup(self, client):
r = await client.get("/execution/semantics")
assert r.status_code == 200
@ -1014,8 +1014,15 @@ class TestExecutionQueueEndpoints:
assert "queued" in body["execution_states"]
assert "immediate" in body["launch_modes"]
assert "parallel" in body["concurrency_modes"]
assert any("launch requests" in item for item in body["state_hub_responsibility"])
assert any("dispatch" in item for item in body["activity_core_responsibility"])
assert body["launch_requests_accepted"] is False
assert "authoritative repository file" in body["replacements"][
"POST /execution/launch-requests"
]
assert any(
"reject new workplan launch" in item
for item in body["state_hub_responsibility"]
)
assert any("do not consume" in item for item in body["activity_core_responsibility"])
async def test_execution_intent_update_does_not_change_lifecycle_status(self, client):
await _create_domain(client)
@ -1107,7 +1114,7 @@ class TestExecutionQueueEndpoints:
assert rows[2]["eligible"] is False
assert rows[2]["blocked_by_workstream_ids"] == [dependency["id"]]
async def test_launch_request_records_handoff_and_updates_execution_intent(self, client):
async def test_launch_request_is_gone_and_does_not_update_execution_intent(self, client):
await _create_domain(client)
topic = await _create_topic(client)
ws = await _create_workstream(client, topic["id"], status="ready")
@ -1124,21 +1131,18 @@ class TestExecutionQueueEndpoints:
"notes": "start now",
})
assert r.status_code == 201, r.text
body = r.json()
assert body["workstream_id"] == ws["id"]
assert body["launch_mode"] == "immediate"
assert body["immediate_pickup"] is True
assert body["status"] == "requested"
assert r.status_code == 410, r.text
assert "no consumer picks up these rows" in r.json()["detail"]
assert "repo file queue" in r.headers["x-statehub-replacement"]
r = await client.get(f"/workplans/{ws['id']}")
updated = r.json()
assert updated["status"] == "ready"
assert updated["execution_state"] == "launching"
assert updated["launch_mode"] == "immediate"
assert updated["execution_state"] == "manual"
assert updated["launch_mode"] == "manual"
r = await client.get(f"/execution/launch-requests?workstream_id={ws['id']}")
assert len(r.json()) == 1
assert r.json() == []
def _fabric_graph_export(generated_at="2026-05-23T12:00:00Z", extra_node=False):

View file

@ -13,6 +13,7 @@ from api.models.domain import Domain
from api.models.managed_repo import ManagedRepo
from api.models.progress_event import ProgressEvent
from api.models.task import Task
from api.models.topic import Topic
from api.models.token_event import TokenEvent
from api.models.work_record_identifier_alias import WorkRecordIdentifierAlias
from api.models.workplan import Workplan
@ -21,6 +22,7 @@ from api.services.work_record_identifier_migration import (
DERIVATION_NAMESPACE_UUID,
IdentifierMigrationError,
apply_repository_identifier_migration,
repair_absent_prederivation_projection,
reverse_repository_identifier_migration,
)
@ -296,3 +298,233 @@ async def test_repository_migration_is_atomic_when_a_source_is_missing(test_engi
assert await session.scalar(
text("SELECT count(*) FROM work_record_identifier_aliases")
) == 0
async def _seed_empty_repository(factory, repo_slug: str):
domain_id = uuid.uuid4()
topic_id = uuid.uuid4()
repo_id = uuid.uuid4()
async with factory() as session:
session.add(Domain(id=domain_id, slug="infotech", name="Infotech", status="active"))
await session.flush()
session.add(
Topic(
id=topic_id,
domain_id=domain_id,
slug="infotech",
title="Infotech",
status="active",
)
)
await session.flush()
session.add(
ManagedRepo(
id=repo_id,
domain_id=domain_id,
topic_id=topic_id,
slug=repo_slug,
name="Repair Repository",
status="active",
)
)
await session.commit()
return repo_id, topic_id
def _repair_unit(plan: dict, topic_id: uuid.UUID) -> dict:
repository = plan["repositories"][0]
replacements = [m for m in repository["mappings"] if m["action"] == "replace"]
workplan = next(m for m in replacements if m["kind"] == "workplan")
task = next(m for m in replacements if m["kind"] == "task")
return {
"workplan": {
"record_id": workplan["record_id"],
"id": workplan["current_uuid"],
"topic_id": str(topic_id),
"slug": "test-wp-0001",
"title": "Migration test",
"description": "Exact authoritative projection",
"status": "active",
"owner": "codex",
"planning_priority": "high",
"planning_order": 1,
},
"tasks": [
{
"record_id": task["record_id"],
"id": task["current_uuid"],
"title": "Mapped task",
"description": "Exact task projection",
"status": "progress",
"priority": "high",
"assignee": "codex",
}
],
}
def _repair_kwargs(plan: dict) -> dict:
repository = plan["repositories"][0]
return {
"expected_plan_sha256": plan["plan_sha256"],
"source_revision": repository["planned_head_sha"],
"source_fingerprint": repository["source_fingerprint"],
"source_clean": True,
"source_synchronized": True,
"primary_confirmed": True,
"projection_identity": "test-projection",
}
@pytest.mark.asyncio
async def test_sealed_repair_restores_absent_projection_and_retries_as_noop(test_engine):
factory = async_sessionmaker(test_engine, class_=AsyncSession, expire_on_commit=False)
repo_id, topic_id = await _seed_empty_repository(factory, "repair-repo")
workplan_old = uuid.uuid4()
task_old = uuid.uuid4()
plan = _sealed_plan("repair-repo", workplan_old, task_old)
unit = _repair_unit(plan, topic_id)
async with factory() as session:
receipt = await repair_absent_prederivation_projection(
session, plan, "repair-repo", unit, **_repair_kwargs(plan)
)
assert receipt.outcome == "repaired"
assert receipt.repository_id == str(repo_id)
assert receipt.workplan_old_id == str(workplan_old)
assert receipt.task_records == (("TEST-WP-0001-T01", str(task_old)),)
async with factory() as session:
assert await session.get(Workplan, workplan_old) is not None
assert await session.get(Task, task_old) is not None
assert await session.get(Workplan, _derived("TEST-WP-0001")) is None
assert await session.get(Task, _derived("TEST-WP-0001-T01")) is None
async with factory() as session:
retry = await repair_absent_prederivation_projection(
session, plan, "repair-repo", unit, **_repair_kwargs(plan)
)
assert retry.outcome == "verified_noop"
@pytest.mark.asyncio
@pytest.mark.parametrize(
("override", "message"),
[
({"source_clean": False}, "clean synchronized"),
({"source_synchronized": False}, "clean synchronized"),
({"source_revision": "f" * 40}, "source revision drifted"),
({"source_fingerprint": "e" * 64}, "source fingerprint drifted"),
({"expected_plan_sha256": "0" * 64}, "explicit plan SHA-256"),
({"primary_confirmed": False}, "primary confirmation"),
],
)
async def test_sealed_repair_rejects_untrusted_source(test_engine, override, message):
factory = async_sessionmaker(test_engine, class_=AsyncSession, expire_on_commit=False)
_repo_id, topic_id = await _seed_empty_repository(factory, "untrusted-repo")
workplan_old = uuid.uuid4()
task_old = uuid.uuid4()
plan = _sealed_plan("untrusted-repo", workplan_old, task_old)
kwargs = _repair_kwargs(plan) | override
async with factory() as session:
with pytest.raises(IdentifierMigrationError, match=message):
await repair_absent_prederivation_projection(
session, plan, "untrusted-repo", _repair_unit(plan, topic_id), **kwargs
)
async with factory() as session:
assert await session.get(Workplan, workplan_old) is None
assert await session.get(Task, task_old) is None
@pytest.mark.asyncio
async def test_sealed_repair_rejects_partial_or_derived_projection_presence(test_engine):
factory = async_sessionmaker(test_engine, class_=AsyncSession, expire_on_commit=False)
repo_id, topic_id = await _seed_empty_repository(factory, "partial-repo")
workplan_old = uuid.uuid4()
task_old = uuid.uuid4()
plan = _sealed_plan("partial-repo", workplan_old, task_old)
unit = _repair_unit(plan, topic_id)
async with factory() as session:
session.add(
Workplan(
id=workplan_old,
repo_id=repo_id,
topic_id=topic_id,
slug="test-wp-0001",
title="Migration test",
status="active",
)
)
await session.commit()
async with factory() as session:
with pytest.raises(IdentifierMigrationError, match="partial old projection"):
await repair_absent_prederivation_projection(
session, plan, "partial-repo", unit, **_repair_kwargs(plan)
)
async with factory() as session:
await session.execute(text("DELETE FROM workplans WHERE id = :id"), {"id": workplan_old})
session.add(
Workplan(
id=_derived("TEST-WP-0001"),
repo_id=repo_id,
topic_id=topic_id,
slug="derived-conflict",
title="Derived conflict",
status="active",
)
)
await session.commit()
async with factory() as session:
with pytest.raises(IdentifierMigrationError, match="derived target presence"):
await repair_absent_prederivation_projection(
session, plan, "partial-repo", unit, **_repair_kwargs(plan)
)
@pytest.mark.asyncio
async def test_sealed_repair_http_surface_returns_receipt(client, test_engine):
factory = async_sessionmaker(test_engine, class_=AsyncSession, expire_on_commit=False)
repo_id, topic_id = await _seed_empty_repository(factory, "http-repair-repo")
workplan_old = uuid.uuid4()
task_old = uuid.uuid4()
plan = _sealed_plan("http-repair-repo", workplan_old, task_old)
response = await client.post(
"/identifier-migrations/sealed-projection-repairs",
json={
"plan": plan,
"repo_slug": "http-repair-repo",
"unit": _repair_unit(plan, topic_id),
**_repair_kwargs(plan),
},
)
assert response.status_code == 200, response.text
receipt = response.json()
assert receipt["outcome"] == "repaired"
assert receipt["repository_id"] == str(repo_id)
assert receipt["task_records"] == [["TEST-WP-0001-T01", str(task_old)]]
assert (await client.get(f"/workplans/{workplan_old}")).status_code == 200
assert (await client.get(f"/tasks/{task_old}")).status_code == 200
assert (await client.get(f"/workplans/{_derived('TEST-WP-0001')}")).status_code == 404
assert (await client.get(f"/tasks/{_derived('TEST-WP-0001-T01')}")).status_code == 404
@pytest.mark.asyncio
async def test_sealed_repair_http_surface_rejects_missing_confirmation(client, test_engine):
factory = async_sessionmaker(test_engine, class_=AsyncSession, expire_on_commit=False)
_repo_id, topic_id = await _seed_empty_repository(factory, "http-reject-repo")
plan = _sealed_plan("http-reject-repo", uuid.uuid4(), uuid.uuid4())
payload = {
"plan": plan,
"repo_slug": "http-reject-repo",
"unit": _repair_unit(plan, topic_id),
**_repair_kwargs(plan),
}
payload["primary_confirmed"] = False
response = await client.post(
"/identifier-migrations/sealed-projection-repairs", json=payload
)
assert response.status_code == 409
assert "primary confirmation" in response.json()["detail"]

View file

@ -8,7 +8,7 @@ status: active
owner: codex
topic_slug: infotech
created: "2026-08-09"
updated: "2026-08-22"
updated: "2026-08-23"
parent_project: prj-state-hub-retirement
parent_workplan: SHR-WP-0001
related:
@ -247,6 +247,25 @@ and were not ingested. Production aliases remain empty as expected for a rebuild
rather than an in-place rewrite. Widening beyond this one-repository pilot still
requires a separate fleet cutover decision.
**Sealed pre-derivation repair interface approved with safeguards
(2026-08-23):** Repo Manager interface
`helixforge.identifiers.state-hub-sealed-projection-repair.v1` is accepted as a
temporary T04 compatibility sub-slice. The owner implementation adds
`POST /identifier-migrations/sealed-projection-repairs` and keeps ordinary C-03
refusal unchanged. Repair is limited to one explicitly named workplan and all
of its sealed `replace` task mappings; it requires the exact plan seal, clean
synchronized Git revision and source fingerprint, explicit primary
confirmation, existing repository/topic projection, and complete absence of
both the old unit and derived targets. Creation is one database transaction;
an exact retry returns a verified no-op receipt, while partial presence, source
drift, aliases, target rows, or content mismatch fail closed. Operational and
removal contract: `docs/sealed-prederivation-projection-repair.md`.
Verification: focused identifier-migration coverage passes (`13 passed`), the
ordinary consistency suite passes (`129 passed`), and the full API plus
dashboard build passes (`650 passed`). The full run retained one pre-existing
SQLAlchemy resource warning and one pre-existing dashboard link warning.
**A2a executed (2026-08-20) — first live cutover slice.** Dual-run is on for the
pilot repo:
@ -331,7 +350,7 @@ re-scaffolding.
```task
id: STATE-WP-0079-T05
status: todo
status: progress
priority: medium
state_hub_task_id: "02e508ed-3cde-4487-907e-d324a8a877d6"
```
@ -398,6 +417,18 @@ freeze window, not now.
Then `dashboard-meta` (E3, 1); `legacy-meter` itself (E4, 9) retires last,
being the instrument.
**Execution launch-request defect closed (2026-08-23):** the surviving
`POST /execution/launch-requests` route claimed Activity Core pickup after that
mapping was explicitly retired by `ACTIVITY-WP-0029-T04`. New submissions now
return 410 with the repository-file / ActivityDefinition+ops_run replacement;
`GET /execution/semantics` explicitly says launch requests are not accepted,
that Activity Core does not consume them, and that legacy `launching` does not
prove pickup. Historical launch rows remain readable. The two reported
requests (`7052b20f…`, `5628fe55…`) were cancelled as never consumed and their
workplans restored from `launching/immediate` to `manual/manual`.
Focused execution coverage passes (`4 passed`); the combined full regression
and dashboard build remains green (`650 passed`).
## Stabilization window and archive prep
```task