"""POST /governance/complete/ — GSAP §5.4""" import uuid from datetime import datetime, UTC from fastapi import APIRouter, Depends, HTTPException from sqlmodel.ext.asyncio.session import AsyncSession from sqlalchemy import text from gsap_broker.db import get_session from gsap_broker.models import CompleteRequest, CompleteResponse from gsap_broker import chronicle router = APIRouter() @router.post("/complete/", response_model=CompleteResponse, summary="Receive CR (GSAP §5.4)") async def complete(request: CompleteRequest, db: AsyncSession = Depends(get_session)): # Pure raw SQL to avoid all SQLModel/SQLAlchemy greenlet issues result = await db.execute( text("SELECT context_id, status FROM authorization_contexts WHERE context_id = :ctx_id AND status = 'authorized'"), {"ctx_id": str(request.context_id).replace("-", "")}, ) ac_row = result.first() if not ac_row: raise HTTPException(status_code=404, detail="AC not found or already consumed.") sig_verified = bool((request.signature or {}).get("value")) cr_id = uuid.uuid4() now = datetime.now(UTC) # Insert CR await db.execute( text("""INSERT INTO completion_receipts (id, context_id, outcome, completed_at, received_at, failure_reason, chronicle_session_id, chronicle_events, merkle_root, behavioral_attestation_status, ffc_did, ffc_signature, signature_verified, chronicle_event_cid) VALUES (:id, :ctx_id, :outcome, :completed_at, :received_at, :failure_reason, :session_id, :events, :merkle_root, :ba_status, :ffc_did, :ffc_sig, :sig_verified, '')"""), { "id": str(cr_id).replace("-", ""), "ctx_id": str(request.context_id).replace("-", ""), "outcome": request.outcome.value, "completed_at": str(request.completed_at), "received_at": str(now), "failure_reason": request.failure_reason or "", "session_id": request.chronicle_session_id or "", "events": str(request.chronicle_evidence.events), "merkle_root": request.chronicle_evidence.merkle_root or "", "ba_status": request.behavioral_attestation.status.value, "ffc_did": request.ffc.get("did", ""), "ffc_sig": (request.signature or {}).get("value", ""), "sig_verified": sig_verified, }, ) # Update AC status await db.execute( text("UPDATE authorization_contexts SET status = 'consumed', consumed_at = :now WHERE context_id = :ctx_id"), {"now": str(now), "ctx_id": str(request.context_id).replace("-", "")}, ) # Chronicle event cid = await chronicle.emit("GSAP_CR_RECEIVED", { "event_code": "0x2706", "context_id": str(request.context_id).replace("-", ""), "outcome": request.outcome.value, }) # Update CR with chronicle CID if cid: await db.execute( text("UPDATE completion_receipts SET chronicle_event_cid = :cid WHERE id = :id"), {"cid": cid, "id": str(cr_id).replace("-", "")}, ) await db.commit() return CompleteResponse(receipt_id=cr_id, signature_verified=sig_verified, chronicle_event_cid=cid or None)