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

@ -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)