fix(rc06): close publication facade audit gaps

This commit is contained in:
2026-10-07 01:21:29 +08:00
parent 2136922b33
commit 87a0f27db3
12 changed files with 2180 additions and 179 deletions
@@ -17,6 +17,12 @@ depends_on = None
def upgrade() -> None:
bind = op.get_bind()
if bind.dialect.name == "postgresql":
bind.execute(
sa.text(
"LOCK TABLE rc06_publication_sagas IN ACCESS EXCLUSIVE MODE"
)
)
saga_count = bind.execute(
sa.text("SELECT COUNT(*) FROM rc06_publication_sagas")
).scalar_one()
@@ -141,10 +147,26 @@ def upgrade() -> None:
"rc06_apply_challenge_uses",
["tenant_id"],
)
with op.batch_alter_table("rc06_publication_sagas") as batch:
batch.create_foreign_key(
"fk_rc06_publication_saga_authority_transaction",
"rc06_online_publication_transactions",
["transaction_id"],
["transaction_id"],
ondelete="NO ACTION",
)
def downgrade() -> None:
bind = op.get_bind()
if bind.dialect.name == "postgresql":
bind.execute(
sa.text(
"LOCK TABLE rc06_publication_sagas, "
"rc06_online_publication_transactions, rc06_apply_challenge_uses "
"IN ACCESS EXCLUSIVE MODE"
)
)
compensation_count = bind.execute(
sa.text(
"SELECT COUNT(*) FROM rc06_publication_sagas "
@@ -162,6 +184,11 @@ def downgrade() -> None:
"RC06 migration 0082 refuses to erase durable publication authority, "
"receipt, countersign, or challenge-consumption evidence."
)
with op.batch_alter_table("rc06_publication_sagas") as batch:
batch.drop_constraint(
"fk_rc06_publication_saga_authority_transaction",
type_="foreignkey",
)
op.drop_index(
"ix_rc06_apply_challenge_uses_tenant_id",
table_name="rc06_apply_challenge_uses",
@@ -776,6 +776,7 @@ def _check_rc06_publication_facade_config(settings: ContractApiSettings) -> Runt
if len(set(resolved_files.values())) != len(resolved_files):
errors.append("facade trust and credential files must use distinct paths")
bearer_path = resolved_files.get("facade bearer token")
bearer: str | None = None
if bearer_path is not None and bearer_path.is_file():
try:
bearer = bearer_path.read_text(encoding="utf-8").strip()
@@ -792,6 +793,15 @@ def _check_rc06_publication_facade_config(settings: ContractApiSettings) -> Runt
}
if bearer in reused:
errors.append("facade bearer token must be independent from other service credentials")
kms_credential_path = resolved_files.get("KMS credential")
if bearer is not None and kms_credential_path is not None and kms_credential_path.is_file():
try:
kms_credential = kms_credential_path.read_text(encoding="utf-8").strip()
except (OSError, UnicodeError):
errors.append("KMS credential cannot be read as UTF-8")
else:
if bearer == kms_credential:
errors.append("facade bearer token must be independent from the KMS credential")
public_key_paths = [
resolved_files.get("approval root public key"),
resolved_files.get("apply service public key"),
@@ -829,9 +839,14 @@ def validate_rc06_publication_startup(settings: ContractApiSettings) -> None:
if not settings.rc06_publication_facade_enabled:
return
result = _check_rc06_publication_facade_config(settings)
if result.status != "ok":
raise RuntimeError(result.message)
results = (
_check_rc06_publication_config(settings),
_check_rc06_publication_facade_config(settings),
_check_reliable_evidence_signing_config(settings),
)
errors = [result.message for result in results if result.status != "ok"]
if errors:
raise RuntimeError(" ".join(errors))
def _check_worker_config(settings: ContractApiSettings) -> RuntimeConfigCheck:
@@ -1738,7 +1738,15 @@ class Rc06PublicationSagaModel(Base):
),
)
transaction_id: Mapped[str] = mapped_column(String(64), primary_key=True)
transaction_id: Mapped[str] = mapped_column(
String(64),
ForeignKey(
"rc06_online_publication_transactions.transaction_id",
ondelete="NO ACTION",
name="fk_rc06_publication_saga_authority_transaction",
),
primary_key=True,
)
tenant_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True, default=DEFAULT_TENANT_ID)
idempotency_key: Mapped[str] = mapped_column(String(160), nullable=False)
request_sha256: Mapped[str] = mapped_column(String(64), nullable=False)
@@ -10,7 +10,7 @@ import hashlib
import json
import re
import secrets
from datetime import UTC, datetime
from datetime import UTC, datetime, timedelta
from threading import RLock
from typing import Any, Protocol
from uuid import uuid4
@@ -18,7 +18,7 @@ from uuid import uuid4
import httpx
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel, ConfigDict, Field
from sqlalchemy import select, text
from sqlalchemy import func, select, text
from yuqei_sdk.review_routing import REVIEW_ROUTING_VERSION, review_prompt_routes
@@ -44,6 +44,7 @@ EXPECTED_ASSET_COUNT = 60
EXPECTED_MODULE_COUNT = 14
EXPECTED_RUNTIME_COUNT = 14
EXPECTED_WRITE_COUNT = 74
AUTHORITY_COMMIT_MARGIN = timedelta(seconds=5)
_coordinator_lock = RLock()
@@ -67,6 +68,7 @@ class Rc06PrepareRequest(BaseModel):
batch_manifest_raw_sha256: str = Field(pattern=HEX64)
batch_manifest_payload_sha256: str = Field(pattern=HEX64)
approval_receipt_sha256: str = Field(pattern=HEX64)
authority_expires_at: datetime
assets: list[dict[str, Any]] = Field(min_length=EXPECTED_ASSET_COUNT, max_length=EXPECTED_ASSET_COUNT)
prompt_modules: list[dict[str, Any]] = Field(min_length=EXPECTED_MODULE_COUNT, max_length=EXPECTED_MODULE_COUNT)
runtime_identities: list[dict[str, Any]] = Field(min_length=EXPECTED_RUNTIME_COUNT, max_length=EXPECTED_RUNTIME_COUNT)
@@ -208,6 +210,24 @@ def _parse_time(value: Any) -> datetime | None:
return result.replace(tzinfo=UTC) if result.tzinfo is None else result.astimezone(UTC)
def _database_utc_now(session: Any) -> datetime:
"""Read the participant database clock used by the commit transaction."""
bind = session.get_bind()
# PostgreSQL CURRENT_TIMESTAMP is the transaction-start timestamp and is
# therefore unsafe for a second expiry check in a long write transaction.
# clock_timestamp() is the actual wall clock at each statement.
value = (
session.scalar(text("SELECT clock_timestamp()"))
if bind.dialect.name == "postgresql"
else session.scalar(select(func.current_timestamp()))
)
result = _parse_time(value)
if result is None:
raise Rc06PublicationError("Contract database clock is unavailable.")
return result
def _finish_asset(asset: dict[str, Any]) -> dict[str, Any]:
asset["body_sha256"] = hashlib.sha256(asset["body_content"].encode("utf-8")).hexdigest()
asset["input_schema_sha256"] = _sha(asset["input_schema"]) if asset["input_schema"] is not None else None
@@ -310,6 +330,8 @@ class Rc06PublicationCoordinator:
def prepare(self, payload: Rc06PrepareRequest, *, tenant_id: str) -> Rc06SagaStatus:
self._assert_current_deployment(payload)
if payload.authority_expires_at.tzinfo is None or payload.authority_expires_at.utcoffset() is None:
raise Rc06Conflict("RC06 authority expiry must include a timezone.")
self._authorization.verify(payload, tenant_id=tenant_id)
installed = contract_source_assets()
supplied = sorted(payload.assets, key=lambda item: item.get("template_id", ""))
@@ -321,10 +343,13 @@ class Rc06PublicationCoordinator:
raise Rc06Conflict("The runtime Prompt identity inventory is incomplete or duplicated.")
request_payload = payload.model_dump(mode="json")
request_sha256 = _sha(request_payload)
now = datetime.now(UTC)
compensation_capability: str
with _coordinator_lock, self._session_factory.begin() as session:
self._lock_saga(session, payload.transaction_id)
now = _database_utc_now(session)
authority_expires_at = _parse_time(payload.authority_expires_at)
if authority_expires_at is None or now + AUTHORITY_COMMIT_MARGIN >= authority_expires_at:
raise Rc06Conflict("RC06 authority expired before participant prepare.")
existing = session.scalar(select(Rc06PublicationSagaModel).where(
Rc06PublicationSagaModel.tenant_id == tenant_id,
Rc06PublicationSagaModel.idempotency_key == payload.idempotency_key
@@ -437,9 +462,14 @@ class Rc06PublicationCoordinator:
if row.state not in {"prepared", "ai_committed", "contract_committed"}:
raise Rc06Conflict(f"RC06 saga cannot commit from {row.state}.")
if row.state in {"prepared", "ai_committed"}:
self._assert_current_deployment(
Rc06PrepareRequest.model_validate(self._frozen_request(row))
)
frozen = Rc06PrepareRequest.model_validate(self._frozen_request(row))
self._assert_current_deployment(frozen)
with self._session_factory() as clock_session:
if (
_database_utc_now(clock_session) + AUTHORITY_COMMIT_MARGIN
>= frozen.authority_expires_at.astimezone(UTC)
):
raise Rc06Conflict("RC06 authority expired before participant commit.")
if row.state == "prepared":
# The previous coordinator process may have crashed after the AI
# database committed but before its local journal advanced. Ask
@@ -632,13 +662,30 @@ class Rc06PublicationCoordinator:
if saga.state != "ai_committed":
raise Rc06Conflict(f"Contract participant cannot commit from {saga.state}.")
request = self._frozen_request(saga)
authority_expires_at = _parse_time(request["authority_expires_at"])
contract_write_started_at = _database_utc_now(session)
if (
authority_expires_at is None
or contract_write_started_at + AUTHORITY_COMMIT_MARGIN >= authority_expires_at
):
raise Rc06Conflict("RC06 authority expired before the Contract participant write.")
ai_status = _decode(saga.ai_status_json, {})
self._assert_ai_state(
ai_status,
transaction_id,
request,
expected_state="committed",
)
ai_committed_at = _parse_time(ai_status.get("committed_at"))
if ai_committed_at is None or ai_committed_at >= authority_expires_at:
raise Rc06Conflict("AI participant committed outside the RC06 authority window.")
assets = {item["template_id"]: item for item in request["assets"]}
items = session.scalars(select(Rc06TemplatePublicationItemModel).where(
Rc06TemplatePublicationItemModel.transaction_id == transaction_id
)).all()
if len(items) != EXPECTED_ASSET_COUNT:
raise Rc06PublicationError("Contract participant journal does not contain 60 items.")
now = datetime.now(UTC)
now = contract_write_started_at
for item in sorted(items, key=lambda value: value.template_id):
self._lock_template(session, saga.tenant_id, item.template_id)
head = self._latest_event(session, saga.tenant_id, item.template_id)
@@ -707,10 +754,35 @@ class Rc06PublicationCoordinator:
item.online_content_sha256 = asset["content_sha256"]
item.state = "applied"
item.updated_at = now
# Capture the exact participant readback at the irreversible
# Contract boundary. Receipt construction may happen much later
# (including after the short authority window expires), so it must
# never invent a recovery-time commit/capture timestamp.
asset_results = self._verify_contract_readback(session, saga, request)
results = sorted(
asset_results + ai_status["item_results"],
key=lambda value: (value["kind"], value["item_id"]),
)
snapshots = ai_status["runtime_snapshots"]
if len(results) != EXPECTED_WRITE_COUNT or len(snapshots) != EXPECTED_RUNTIME_COUNT:
raise Rc06PublicationError("RC06 irreversible readback evidence is incomplete.")
boundary_at = _database_utc_now(session)
if boundary_at + AUTHORITY_COMMIT_MARGIN >= authority_expires_at:
raise Rc06Conflict(
"RC06 authority expired before the Contract participant commit boundary."
)
# This is the last instant at which all participant writes and the
# complete readback have succeeded inside the Contract
# transaction. Do not reuse the earlier object-write timestamp:
# a long batch could otherwise claim an irreversible boundary
# before the evidence was actually complete.
saga.item_results_json = _encode(results)
saga.runtime_snapshots_json = _encode(snapshots)
saga.state = "contract_committed"
saga.error_message = None
saga.contract_committed_at = now
saga.updated_at = now
saga.contract_committed_at = boundary_at
saga.committed_at = boundary_at
saga.updated_at = boundary_at
def _finalize(self, transaction_id: str, ai_status: dict[str, Any]) -> None:
with _coordinator_lock, self._session_factory.begin() as session:
@@ -746,14 +818,23 @@ class Rc06PublicationCoordinator:
results = sorted(asset_results + prompt_results, key=lambda item: (item["kind"], item["item_id"]))
if len(results) != EXPECTED_WRITE_COUNT or any(item.get("status") != "applied_and_verified" for item in results):
raise Rc06PublicationError("RC06 online apply readback is incomplete.")
persisted_results = _decode(saga.item_results_json, [])
persisted_snapshots = _decode(saga.runtime_snapshots_json, [])
if (
results != persisted_results
or snapshots != persisted_snapshots
or saga.committed_at is None
or saga.contract_committed_at is None
or saga.committed_at != saga.contract_committed_at
):
raise Rc06PublicationError(
"RC06 irreversible participant evidence changed during finalize."
)
now = datetime.now(UTC)
saga.item_results_json = _encode(results)
saga.runtime_snapshots_json = _encode(snapshots)
saga.ai_status_json = _encode(ai_status)
saga.state = "committed"
saga.compensation_capability = None
saga.error_message = None
saga.committed_at = now
saga.updated_at = now
def _compensate_templates(self, transaction_id: str) -> None:
@@ -1032,9 +1113,15 @@ class Rc06PublicationCoordinator:
try:
committed_at = _parse_time(payload.get("committed_at"))
sealed_at = _parse_time(payload.get("sealed_at"))
authority_expires_at = _parse_time(payload.get("authority_expires_at"))
expected_authority_expires_at = _parse_time(
request.get("authority_expires_at")
)
except (TypeError, ValueError):
committed_at = None
sealed_at = None
authority_expires_at = None
expected_authority_expires_at = None
state_markers_valid = (
expected_state == "committed"
and payload.get("no_go") is True
@@ -1055,6 +1142,9 @@ class Rc06PublicationCoordinator:
or payload.get("batch_manifest_raw_sha256") != request["batch_manifest_raw_sha256"]
or payload.get("batch_manifest_payload_sha256") != request["batch_manifest_payload_sha256"]
or payload.get("approval_receipt_sha256") != request["approval_receipt_sha256"]
or authority_expires_at is None
or expected_authority_expires_at is None
or authority_expires_at != expected_authority_expires_at
or committed_at is None
or not state_markers_valid
or payload.get("write_count") != EXPECTED_MODULE_COUNT
@@ -1175,6 +1265,19 @@ class Rc06PublicationCoordinator:
row = self._require_saga(session, transaction_id)
if row.state != "contract_committed":
raise Rc06Conflict(f"RC06 saga changed state during AI seal: {row.state}")
previous = _decode(row.ai_status_json, {})
if (
status.get("candidate_commit") != previous.get("candidate_commit")
or status.get("release_image_digest")
!= previous.get("release_image_digest")
or _parse_time(status.get("committed_at"))
!= _parse_time(previous.get("committed_at"))
or status.get("item_results") != previous.get("item_results")
or status.get("runtime_snapshots") != previous.get("runtime_snapshots")
):
raise Rc06PublicationError(
"AI sealed evidence drifted from its persisted commit readback."
)
row.ai_status_json = _encode(status)
row.compensation_capability = None
row.error_message = None
@@ -1294,6 +1397,7 @@ class Rc06PublicationCoordinator:
"batch_manifest_raw_sha256": payload.batch_manifest_raw_sha256,
"batch_manifest_payload_sha256": payload.batch_manifest_payload_sha256,
"approval_receipt_sha256": payload.approval_receipt_sha256,
"authority_expires_at": payload.authority_expires_at.isoformat(),
"compensation_capability_sha256": hashlib.sha256(
compensation_capability.encode("utf-8")
).hexdigest(),
@@ -15,7 +15,7 @@ import json
import re
from contextlib import contextmanager
from dataclasses import dataclass
from datetime import UTC, datetime
from datetime import UTC, datetime, timedelta
from pathlib import Path
from threading import RLock
from typing import Any, Mapping, Protocol
@@ -23,7 +23,7 @@ from typing import Any, Mapping, Protocol
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
from fastapi import FastAPI, Header, HTTPException, Request
from sqlalchemy import select, text
from sqlalchemy import func, select, text
from sqlalchemy.exc import IntegrityError
from .config import ContractApiSettings
@@ -49,6 +49,7 @@ from .rc06_publication_protocol import (
APPLY_STATUS_SCHEMA,
FINALIZE_REQUEST_SCHEMA,
FINALIZE_RESPONSE_SCHEMA,
HEX64,
KMS_ATTESTATION_PAYLOAD_SCHEMA,
KMS_ATTESTATION_SCHEMA,
ONLINE_ATTESTATION_DOMAIN,
@@ -277,6 +278,21 @@ def _utc(value: datetime | None) -> datetime | None:
return value.replace(tzinfo=UTC) if value.tzinfo is None else value.astimezone(UTC)
def _database_wall_clock(session: Any) -> datetime:
value = (
session.scalar(text("SELECT clock_timestamp()"))
if session.get_bind().dialect.name == "postgresql"
else session.scalar(select(func.current_timestamp()))
)
result = _utc(value)
if result is None:
raise Rc06FacadeError("RC06 facade database clock is unavailable.")
return result
FINALIZE_COMMIT_MARGIN = timedelta(seconds=5)
def _strict_json(raw: bytes, *, maximum: int) -> dict[str, Any]:
if not raw or len(raw) > maximum:
raise Rc06FacadeError("RC06 request body is empty or exceeds the configured limit.")
@@ -569,6 +585,11 @@ class Rc06PublicationFacadeService:
for item in material.manifest["runtime_prompt_dependencies"]["items"]
]
request = material.request
authority_expires_at = min(
parse_time(material.challenge["payload"]["expires_at"], "challenge expires_at"),
parse_time(material.approval["payload"]["expires_at"], "approval expires_at"),
parse_time(material.legal_summary["expires_at"], "legal review expires_at"),
)
return Rc06PrepareRequest(
transaction_id=request["transaction_id"],
idempotency_key=request["idempotency_key"],
@@ -579,6 +600,7 @@ class Rc06PublicationFacadeService:
batch_manifest_raw_sha256=request["batch_manifest_raw_sha256"],
batch_manifest_payload_sha256=request["batch_manifest_payload_sha256"],
approval_receipt_sha256=request["approval_receipt_sha256"],
authority_expires_at=authority_expires_at,
assets=material.manifest["asset_batch"]["items"],
prompt_modules=material.manifest["prompt_module_batch"]["items"],
runtime_identities=runtime,
@@ -601,11 +623,15 @@ class Rc06PublicationFacadeService:
"RC06 authority window expired before the participant saga was prepared."
)
status = self._coordinator.prepare(prepare, tenant_id=self._tenant_id)
if status.state == "preparing" and authority_current:
# Coordinator prepare is itself durable/idempotent. Resume the
# phase split between its local journal and the AI participant.
status = self._coordinator.prepare(prepare, tenant_id=self._tenant_id)
self._record_saga_status(transaction_id, status)
if status.state == "prepared":
self._set_state(transaction_id, "saga_prepared")
if not authority_current:
if status.state in {"prepared", "ai_committed"}:
if status.state in {"preparing", "prepared", "ai_committed"}:
self._set_state(
transaction_id,
self._facade_state(status.state),
@@ -696,8 +722,10 @@ class Rc06PublicationFacadeService:
committed_at = _utc(status.committed_at)
if committed_at is None:
raise Rc06FacadeError("RC06 coordinator commit time is unavailable.")
authority_expires_at = parse_time(
material.challenge["payload"]["expires_at"], "challenge expires_at"
authority_expires_at = min(
parse_time(material.challenge["payload"]["expires_at"], "challenge expires_at"),
parse_time(material.approval["payload"]["expires_at"], "approval expires_at"),
parse_time(material.legal_summary["expires_at"], "legal review expires_at"),
)
approval_expires_at = parse_time(
material.approval["payload"]["expires_at"], "approval expires_at"
@@ -871,10 +899,9 @@ class Rc06PublicationFacadeService:
def _ensure_runtime_receipt(self, transaction_id: str, payload: dict[str, Any]) -> None:
row = self._load(transaction_id)
persisted_payload = _decode(row.runtime_payload_json, None)
if persisted_payload is not None:
# captured_at is frozen by the first attempt; recovery must reuse it.
payload = persisted_payload
else:
if persisted_payload is not None and persisted_payload != payload:
raise Rc06FacadeConflict("Persisted runtime receipt payload drifted.")
if persisted_payload is None:
self._update(transaction_id, runtime_payload_json=_encode(payload))
row = self._load(transaction_id)
signature = _decode(row.runtime_primary_signature_json, None)
@@ -998,14 +1025,18 @@ class Rc06PublicationFacadeService:
if saga is not None:
self._record_saga_status(transaction_id, saga)
if saga.state in {"contract_committed", "committed"}:
self._set_state(
transaction_id,
"contract_committed",
contract_committed_at=saga.contract_committed_at,
error="participant commit is durable; facade evidence is pending",
)
current = self._load(transaction_id)
if current.contract_committed_at is None:
self._set_state(
transaction_id,
"contract_committed",
contract_committed_at=saga.contract_committed_at,
error="participant commit is durable; facade evidence is pending",
)
if not all((
current.failure_code,
current.failure_at,
current.failure_evidence_sha256,
)):
self._record_error(
transaction_id,
Rc06FacadeError(
@@ -1018,6 +1049,19 @@ class Rc06PublicationFacadeService:
self._facade_state(saga.state),
error=saga.error,
)
elif saga.state == "compensation_failed":
self._set_state(
transaction_id,
"compensation_failed",
error=saga.error or "participant compensation outcome is unverified",
)
self._record_error(
transaction_id,
Rc06FacadeError(
saga.error or "participant compensation outcome is unverified"
),
outcome="compensation_failed",
)
row = self._load(transaction_id)
request = _decode(row.request_json, {})
committed = row.state in {"online_committed_pending_countersign", "finalized"}
@@ -1187,14 +1231,7 @@ class Rc06PublicationFacadeService:
or before.compensated_from_state == "committed"
)
)
ai_before_state = "committed" if ai_was_committed else (
"prepared" if before is not None else "not_started"
)
contract_before_state = "prepared" if before is not None else "not_started"
before_states = [
_state_evidence("ai-platform", ai_before_state, 14 if ai_was_committed else 0, 14 if ai_was_committed else 0),
_state_evidence("contract-platform", contract_before_state, 0, 0),
]
self._set_state(transaction_id, "compensating")
if before is None:
after = None
@@ -1222,16 +1259,38 @@ class Rc06PublicationFacadeService:
after.removed_item_count,
after.unchanged_item_count,
)
if ai_was_committed:
if after.compensated_from_state == "committed":
# The participant's durable compensation evidence is the
# authority for a lost-response recovery. The facade journal
# can still say prepared even though AI committed immediately
# before the process died.
ai_was_committed = True
if (
after.compensated_from_state != "committed"
or len(compensation_items) != 14
len(compensation_items) != 14
or len(runtime_items) != 14
or not isinstance(after.compensation_evidence_sha256, str)
or re.fullmatch(HEX64, after.compensation_evidence_sha256) is None
):
raise Rc06FacadeError("RC06 AI committed compensation evidence is incomplete.")
elif compensation_items or runtime_items or after.compensated_from_state != "prepared":
elif (
ai_was_committed
or compensation_items
or runtime_items
or after.compensated_from_state != "prepared"
):
raise Rc06FacadeError("RC06 prepared compensation falsely claims writes.")
ai_before_state = "committed" if ai_was_committed else (
"prepared" if before is not None else "not_started"
)
before_states = [
_state_evidence(
"ai-platform",
ai_before_state,
14 if ai_was_committed else 0,
14 if ai_was_committed else 0,
),
_state_evidence("contract-platform", contract_before_state, 0, 0),
]
after_states = [
_state_evidence(
"ai-platform",
@@ -1324,14 +1383,24 @@ class Rc06PublicationFacadeService:
return self._finalize_response(row)
if row.state != "online_committed_pending_countersign":
raise Rc06FacadeConflict("RC06 transaction is not awaiting offline countersign.")
countersigned_at = self._validate_apply_receipt(row, request["apply_receipt"])
now = datetime.now(UTC)
if now < countersigned_at:
now = countersigned_at
countersigned_at = self._validate_apply_receipt(
row,
request["apply_receipt"],
now=now,
)
self._update_and_consume(
transaction_id,
outcome="finalized",
consumed_at=now,
countersigned_at=countersigned_at,
authority_deadline=min(
parse_time(
_decode(row.request_json, {})["approval_receipt"]["payload"]["expires_at"],
"approval expires_at",
),
_utc(row.legal_source_review_expires_at),
),
state="finalized",
apply_receipt_json=_encode(request["apply_receipt"]),
apply_receipt_sha256=request["apply_receipt_sha256"],
@@ -1347,6 +1416,8 @@ class Rc06PublicationFacadeService:
self,
row: Rc06OnlinePublicationTransactionModel,
receipt: Any,
*,
now: datetime,
) -> datetime:
envelope = require_exact(receipt, "apply receipt", (
"schema_version", "receipt_id", "payload", "signature",
@@ -1388,11 +1459,18 @@ class Rc06PublicationFacadeService:
countersigned_at = parse_time(payload["countersigned_at"], "countersigned_at")
runtime_captured_at = parse_time(runtime["payload"]["captured_at"], "runtime captured_at")
approval = frozen["approval_receipt"]
approval_expires_at = parse_time(
approval["payload"]["expires_at"], "approval expires_at"
)
legal_expires_at = _utc(row.legal_source_review_expires_at)
if (
authorized_at != runtime_captured_at
or countersigned_at < authorized_at
or countersigned_at >= parse_time(approval["payload"]["expires_at"], "approval expires_at")
or countersigned_at >= _utc(row.legal_source_review_expires_at)
or countersigned_at > now
or now >= approval_expires_at
or now >= legal_expires_at
or countersigned_at >= approval_expires_at
or countersigned_at >= legal_expires_at
):
raise Rc06FacadeError("Offline apply receipt is outside its authority window.")
try:
@@ -1510,6 +1588,8 @@ class Rc06PublicationFacadeService:
*,
outcome: str,
consumed_at: datetime,
authority_deadline: datetime | None = None,
countersigned_at: datetime | None = None,
**values: Any,
) -> None:
with _facade_lock, self._session_factory.begin() as session:
@@ -1523,6 +1603,19 @@ class Rc06PublicationFacadeService:
raise Rc06FacadeError("RC06 challenge consumption ledger is missing.")
if use.outcome == "finalized" and outcome != "finalized":
raise Rc06FacadeConflict("Finalized RC06 challenge outcome is immutable.")
if authority_deadline is not None:
database_now = _database_wall_clock(session)
if countersigned_at is not None and countersigned_at > database_now:
raise Rc06FacadeConflict(
"RC06 countersign is later than the database commit clock."
)
if database_now + FINALIZE_COMMIT_MARGIN >= authority_deadline:
raise Rc06FacadeConflict(
"RC06 finalize crossed its approval or legal-review authority deadline."
)
consumed_at = database_now
if "finalized_at" in values:
values["finalized_at"] = database_now
for key, value in values.items():
if not hasattr(row, key):
raise Rc06FacadeError(f"Unsupported RC06 durable field: {key}")
@@ -1636,6 +1729,8 @@ class Rc06PublicationFacadeService:
@staticmethod
def _facade_state(saga_state: str, *, committing: bool = False) -> str:
if saga_state == "preparing":
return "saga_prepared"
if saga_state == "prepared":
return "saga_committing" if committing else "saga_prepared"
if saga_state == "ai_committed":
@@ -1692,23 +1787,32 @@ class Rc06PublicationFacadeService:
"""
identity = transaction_id if isinstance(transaction_id, str) else "invalid"
with self._session_factory() as session:
if session.get_bind().dialect.name != "postgresql":
with _facade_lock:
yield
return
key = int.from_bytes(
hashlib.sha256(
f"rc06-online-singleflight:{identity}".encode()
).digest()[:8],
"big",
signed=True,
)
session.execute(text("SELECT pg_advisory_lock(:key)"), {"key": key})
with self._session_factory() as probe:
bind = probe.get_bind()
if bind.dialect.name != "postgresql":
with _facade_lock:
yield
return
key = int.from_bytes(
hashlib.sha256(
f"rc06-online-singleflight:{identity}".encode()
).digest()[:8],
"big",
signed=True,
)
# Keep one physical connection checked out in DBAPI autocommit mode.
# A session transaction held across AI/KMS calls would be vulnerable to
# idle_in_transaction_session_timeout and could silently lose the lock.
with bind.connect().execution_options(isolation_level="AUTOCOMMIT") as connection:
connection.execute(text("SELECT pg_advisory_lock(:key)"), {"key": key})
try:
yield
finally:
session.execute(text("SELECT pg_advisory_unlock(:key)"), {"key": key})
unlocked = connection.execute(
text("SELECT pg_advisory_unlock(:key)"), {"key": key}
).scalar_one()
if unlocked is not True:
raise Rc06FacadeError("RC06 PostgreSQL singleflight lock ownership was lost.")
def is_rc06_publication_facade_path(path: str) -> bool:
@@ -73,8 +73,58 @@ EXPECTED_ASSETS = 60
EXPECTED_MODULES = 14
EXPECTED_RUNTIME = 14
EXPECTED_GOLDEN = 31
EXPECTED_GOLDEN_GENERATION = 23
EXPECTED_GOLDEN_CLAUSE = 8
EXPECTED_DEFAULT_PROMPTS = 46
REQUIRED_IMAGE_ROLES = (
"contract-web", "contract-api", "ai-platform-api", "ai-worker",
)
RC05_SUMMARY_SCHEMA = "multiscene-evaluation-run.v1"
RC05_PLAN_SCHEMA = "multiscene-evaluation-plan.v2"
RC05_CANDIDATE_SCHEMA = "multiscene-candidate-binding.v1"
RC05_RELEASE_CONTROLS_SCHEMA = "multiscene-release-controls.v2"
EXPECTED_RC05_OPERATION_LIMITS = {
"draft": 120,
"semantic_review": 30,
"repair_draft": 8,
"repair_review": 8,
"specialist_review": 106,
"intake": 8,
}
EXPECTED_RC05_CANARY_CASE_IDS = (
"base--TPL-AGENCY-DISTRIBUTION-001--normal",
"base--TPL-DEMAND-LETTER-001--normal",
"base--TPL-PURCHASE-GOODS-001--normal",
"scene--TPL-AMENDMENT-GENERAL-001--opposite-adversarial",
"specialist-trade_procurement-first",
"intake-tpl-purchase-goods-001",
)
BLOCKED_CONTROLS = (
"single_reviewer_batch_approval_receipt",
"system_generated_item_apply_receipts_and_online_snapshots",
"runtime_executable_prompt_snapshots_1_drafting_plus_13_review",
)
DISPOSITION_STATUSES = {
"named_legal_approval", "scoped_internal_risk_acceptance", "unresolved",
}
EXISTING_DOMAIN_PROMPTS = {
"contract_review_channel_franchise",
"contract_review_employment_hr",
"contract_review_investment_finance",
"contract_review_real_estate_property",
"contract_review_tech_saas_data_compliance",
}
DRAFTING_MODULE_ORDER = (
"draft_intake",
"template_match",
"missing_facts_questions",
"clause_assembly_plan",
"draft_consistency_check",
"candidate_revision_explanation",
)
LEGAL_PLACEHOLDERS = {"", "-", "n/a", "none", "pending", "unknown", "待确认", "待复核"}
KEY_ID = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$")
STABLE_ID = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,191}$")
class Rc06ProtocolError(RuntimeError):
@@ -162,9 +212,16 @@ def require_official_url(value: Any, label: str) -> str:
text_value = require_text(value, label, maximum=2048)
parsed = urlparse(text_value)
host = (parsed.hostname or "").lower()
try:
port = parsed.port
except ValueError as exc:
raise Rc06ProtocolError(
f"{label} must use a credential-free official HTTPS URL."
) from exc
if (
parsed.scheme != "https"
or not host
or port not in {None, 443}
or parsed.username is not None
or parsed.password is not None
or not (host == "gov.cn" or host.endswith(".gov.cn"))
@@ -245,6 +302,8 @@ def _validate_asset(value: Any) -> dict[str, Any]:
)
row = require_exact(value, "asset", fields)
template_id = require_text(row["template_id"], "asset template_id", maximum=128)
if STABLE_ID.fullmatch(template_id) is None:
raise Rc06ProtocolError("Asset template_id is not a stable identifier.")
body = require_text(row["body_content"], f"asset {template_id} body", maximum=4_000_000)
if sha256_bytes(body.encode("utf-8")) != require_sha(row["body_sha256"], "asset body hash"):
raise Rc06ProtocolError(f"Asset body hash drift: {template_id}")
@@ -291,6 +350,235 @@ def _validate_module(value: Any) -> dict[str, Any]:
return row
def _require_count(value: Any, label: str, *, maximum: int | None = None) -> int:
if (
not isinstance(value, int)
or isinstance(value, bool)
or value < 0
or (maximum is not None and value > maximum)
):
raise Rc06ProtocolError(f"{label} is invalid.")
return value
def _validate_candidate(value: Any) -> dict[str, Any]:
candidate = require_exact(value, "candidate", (
"commit", "tag", "source_manifest_sha256", "source_manifest_payload_sha256",
"image_identities_sha256", "image_identity_set_sha256", "images",
"rc05_plan_sha256", "rc05_plan_identity", "rc05_terminal_evidence",
))
commit = require_text(candidate["commit"], "candidate commit", maximum=64)
if COMMIT.fullmatch(commit) is None:
raise Rc06ProtocolError("Candidate commit is invalid.")
tag = require_text(candidate["tag"], "candidate tag", maximum=128)
if ".." in tag or "//" in tag:
raise Rc06ProtocolError("Candidate tag is invalid.")
source_raw = require_sha(candidate["source_manifest_sha256"], "source manifest raw hash")
source_payload = require_sha(
candidate["source_manifest_payload_sha256"], "source manifest payload hash"
)
image_raw = require_sha(candidate["image_identities_sha256"], "image identity raw hash")
image_set = require_sha(candidate["image_identity_set_sha256"], "image identity set hash")
plan_raw = require_sha(candidate["rc05_plan_sha256"], "RC05 plan raw hash")
images = candidate["images"]
if not isinstance(images, list) or len(images) != len(REQUIRED_IMAGE_ROLES):
raise Rc06ProtocolError("Candidate must identify all four release images.")
roles: set[str] = set()
digests: set[str] = set()
normalized_images: list[dict[str, Any]] = []
for number, value_row in enumerate(images):
row = require_exact(value_row, f"candidate image {number}", (
"role", "repository", "manifest_digest", "config_digest",
))
role = require_text(row["role"], "candidate image role", maximum=64)
require_text(row["repository"], f"candidate image {role} repository", maximum=256)
manifest_digest = require_text(
row["manifest_digest"], f"candidate image {role} manifest digest", maximum=71
)
config_digest = require_text(
row["config_digest"], f"candidate image {role} config digest", maximum=71
)
if IMAGE_DIGEST.fullmatch(manifest_digest) is None or IMAGE_DIGEST.fullmatch(config_digest) is None:
raise Rc06ProtocolError("Candidate image digest is invalid.")
if role in roles or manifest_digest in digests:
raise Rc06ProtocolError("Candidate image roles or manifest digests are duplicated.")
roles.add(role)
digests.add(manifest_digest)
normalized_images.append(dict(row))
if roles != set(REQUIRED_IMAGE_ROLES):
raise Rc06ProtocolError("Candidate image roles are incomplete.")
normalized_images.sort(key=lambda item: REQUIRED_IMAGE_ROLES.index(item["role"]))
if image_set != sha256_json(normalized_images):
raise Rc06ProtocolError("Candidate image identity set hash drift.")
plan = require_exact(candidate["rc05_plan_identity"], "RC05 plan identity", (
"schema_version", "model", "reasoning_effort", "timeout_seconds", "case_count",
"drafting_case_count", "supplemental_case_count", "operation_limits",
"total_provider_call_budget", "case_inventory_sha256", "source_pins_sha256",
"candidate", "release_controls",
))
if (
plan["schema_version"] != RC05_PLAN_SCHEMA
or plan["model"] != "gpt-5.6-sol"
or plan["reasoning_effort"] != "medium"
or plan["timeout_seconds"] != 300
or plan["case_count"] != 144
or plan["drafting_case_count"] != 120
or plan["supplemental_case_count"] != 24
or plan["operation_limits"] != EXPECTED_RC05_OPERATION_LIMITS
or plan["total_provider_call_budget"] != 280
):
raise Rc06ProtocolError("RC05 plan identity is not the frozen 144-case plan.")
require_sha(plan["case_inventory_sha256"], "RC05 case inventory hash")
require_sha(plan["source_pins_sha256"], "RC05 source pins hash")
plan_candidate = require_exact(plan["candidate"], "RC05 candidate binding", (
"schema_version", "commit", "tag", "source_manifest", "image_identities",
))
source_binding = require_exact(plan_candidate["source_manifest"], "RC05 source binding", (
"schema_version", "raw_sha256", "payload_sha256",
))
image_binding = require_exact(plan_candidate["image_identities"], "RC05 image binding", (
"schema_version", "raw_sha256", "identity_set_sha256", "images",
))
if (
plan_candidate["schema_version"] != RC05_CANDIDATE_SCHEMA
or plan_candidate["commit"] != commit
or plan_candidate["tag"] != tag
or source_binding["schema_version"] != "yuqei.release-source-manifest.v1"
or source_binding["raw_sha256"] != source_raw
or source_binding["payload_sha256"] != source_payload
or image_binding["schema_version"] not in {
"yuqei.rc06-image-identities.v1", "yuqei.installable-release-manifest.v1"
}
or image_binding["raw_sha256"] != image_raw
or image_binding["identity_set_sha256"] != image_set
or image_binding["images"] != normalized_images
):
raise Rc06ProtocolError("RC05 candidate binding drifted from the publication candidate.")
controls = require_exact(plan["release_controls"], "RC05 release controls", (
"schema_version", "canary_case_ids", "failure_classes", "expected_operations",
))
failure_classes = controls["failure_classes"]
expected_operations = controls["expected_operations"]
actual_operation_counts = {operation: 0 for operation in EXPECTED_RC05_OPERATION_LIMITS}
if (
not isinstance(failure_classes, dict)
or len(failure_classes) != 144
or any(
not isinstance(key, str)
or STABLE_ID.fullmatch(key) is None
or not isinstance(value, str)
or STABLE_ID.fullmatch(value) is None
for key, value in failure_classes.items()
)
):
raise Rc06ProtocolError("RC05 failure-class inventory is incomplete.")
if not isinstance(expected_operations, dict) or set(expected_operations) != set(failure_classes):
raise Rc06ProtocolError("RC05 expected-operation inventory is incomplete.")
for case_id, operations in expected_operations.items():
if (
not isinstance(case_id, str)
or not case_id
or not isinstance(operations, list)
or not operations
or len(operations) != len(set(operations))
or any(operation not in actual_operation_counts for operation in operations)
):
raise Rc06ProtocolError("RC05 expected-operation inventory is invalid.")
for operation in operations:
actual_operation_counts[operation] += 1
if (
controls["schema_version"] != RC05_RELEASE_CONTROLS_SCHEMA
or controls["canary_case_ids"] != list(EXPECTED_RC05_CANARY_CASE_IDS)
or actual_operation_counts != EXPECTED_RC05_OPERATION_LIMITS
):
raise Rc06ProtocolError("RC05 release-control identity drift.")
terminal = require_exact(candidate["rc05_terminal_evidence"], "RC05 terminal evidence", (
"summary_schema_version", "summary_raw_sha256", "ledger_identity_raw_sha256",
"ledger_identity_sha256", "campaign_identity_raw_sha256", "campaign_identity_sha256",
"plan_raw_sha256", "case_results_sha256", "operation_statistics_sha256",
"case_operation_coverage_sha256", "historical_remediation_sha256",
"current_plan_provider_call_count", "historical_provider_call_count",
"combined_provider_call_count", "complete_real_green",
))
for name in (
"summary_raw_sha256", "ledger_identity_raw_sha256", "ledger_identity_sha256",
"campaign_identity_raw_sha256", "campaign_identity_sha256", "plan_raw_sha256",
"case_results_sha256", "operation_statistics_sha256",
"case_operation_coverage_sha256", "historical_remediation_sha256",
):
require_sha(terminal[name], f"RC05 terminal {name}")
current_calls = _require_count(
terminal["current_plan_provider_call_count"], "RC05 current-plan calls", maximum=276
)
historical_calls = _require_count(
terminal["historical_provider_call_count"], "RC05 historical calls", maximum=280
)
combined_calls = _require_count(
terminal["combined_provider_call_count"], "RC05 combined calls", maximum=280
)
if (
terminal["summary_schema_version"] != RC05_SUMMARY_SCHEMA
or terminal["plan_raw_sha256"] != plan_raw
or terminal["complete_real_green"] is not True
or current_calls < 264
or historical_calls != 4
or combined_calls != current_calls + historical_calls
):
raise Rc06ProtocolError("RC05 terminal evidence is not complete real-provider green evidence.")
return candidate
def _validate_golden_disposition(value: Any) -> dict[str, Any]:
row = require_exact(value, "Golden disposition", (
"sample_id", "suite", "asset_id", "source_case_sha256", "status",
"human_confirmed", "decision_source", "decision_maker", "decided_at",
"evidence_sha256", "risk_acceptance", "notes",
))
require_text(row["sample_id"], "Golden disposition sample_id", maximum=191)
require_text(row["asset_id"], "Golden disposition asset_id", maximum=191)
require_sha(row["source_case_sha256"], "Golden disposition source hash")
if row["suite"] not in {"contract_generation", "clause_recommendation"}:
raise Rc06ProtocolError("Golden disposition suite is invalid.")
if row["status"] not in DISPOSITION_STATUSES or row["status"] == "unresolved":
raise Rc06ProtocolError("Golden disposition is unresolved or invalid.")
if row["human_confirmed"] is not True or row["decision_source"] != "single_reviewer_batch":
raise Rc06ProtocolError("Golden disposition lacks the sole-reviewer decision.")
maker = require_exact(row["decision_maker"], "Golden decision maker", (
"approver_id", "name", "role", "organization",
))
if maker["approver_id"] != SOLE_REVIEWER["approver_id"] or maker["name"] != SOLE_REVIEWER["name"]:
raise Rc06ProtocolError("Golden disposition reviewer identity is invalid.")
require_text(maker["role"], "Golden reviewer role", maximum=256)
require_text(maker["organization"], "Golden reviewer organization", maximum=256)
decided_at = parse_time(row["decided_at"], "Golden disposition decided_at")
require_sha(row["evidence_sha256"], "Golden disposition evidence hash")
if not isinstance(row["notes"], str) or len(row["notes"]) > 4000:
raise Rc06ProtocolError("Golden disposition notes are invalid.")
if row["status"] == "named_legal_approval":
if row["risk_acceptance"] is not None:
raise Rc06ProtocolError("Named legal approval cannot contain risk acceptance.")
else:
risk = require_exact(row["risk_acceptance"], "Golden risk acceptance", (
"owner", "scope", "expires_at", "controls", "use_limits",
))
if risk["owner"] != SOLE_REVIEWER["name"]:
raise Rc06ProtocolError("Golden risk-acceptance owner is invalid.")
require_text(risk["scope"], "Golden risk-acceptance scope", maximum=4000)
if parse_time(risk["expires_at"], "Golden risk-acceptance expiry") <= decided_at:
raise Rc06ProtocolError("Golden risk acceptance is already expired at its decision.")
for name in ("controls", "use_limits"):
values = risk[name]
if not isinstance(values, list) or not values or any(
not isinstance(item, str) or not item.strip() or item != item.strip()
for item in values
):
raise Rc06ProtocolError(f"Golden risk-acceptance {name} is invalid.")
return row
def validate_manifest(value: Any) -> dict[str, Any]:
manifest = require_exact(value, "publication batch", (
"schema_version", "candidate", "asset_batch", "prompt_module_batch",
@@ -303,24 +591,7 @@ def validate_manifest(value: Any) -> dict[str, Any]:
{key: child for key, child in manifest.items() if key != "manifest_payload_sha256"}
):
raise Rc06ProtocolError("Publication batch payload hash drift.")
candidate = manifest["candidate"]
if not isinstance(candidate, dict) or COMMIT.fullmatch(str(candidate.get("commit"))) is None:
raise Rc06ProtocolError("Candidate commit is invalid.")
require_text(candidate.get("tag"), "candidate tag", maximum=128)
images = candidate.get("images")
if not isinstance(images, list):
raise Rc06ProtocolError("Candidate image identities are missing.")
image_by_role: dict[str, str] = {}
for image in images:
if not isinstance(image, dict):
raise Rc06ProtocolError("Candidate image identity is invalid.")
role = require_text(image.get("role"), "candidate image role", maximum=64)
digest = require_text(image.get("manifest_digest"), "candidate image digest", maximum=71)
if IMAGE_DIGEST.fullmatch(digest) is None or role in image_by_role:
raise Rc06ProtocolError("Candidate image identities are invalid or duplicated.")
image_by_role[role] = digest
if "contract-api" not in image_by_role or "ai-platform-api" not in image_by_role:
raise Rc06ProtocolError("Candidate does not identify both publication participants.")
candidate = _validate_candidate(manifest["candidate"])
assets = require_exact(manifest["asset_batch"], "asset batch", ("count", "items", "inventory_sha256"))
if assets["count"] != EXPECTED_ASSETS or not isinstance(assets["items"], list) or len(assets["items"]) != EXPECTED_ASSETS:
@@ -331,8 +602,16 @@ def validate_manifest(value: Any) -> dict[str, Any]:
if require_sha(assets["inventory_sha256"], "asset inventory hash") != sha256_json(asset_rows):
raise Rc06ProtocolError("Asset inventory hash drift.")
modules = manifest["prompt_module_batch"]
if not isinstance(modules, dict) or modules.get("count") != EXPECTED_MODULES:
modules = require_exact(manifest["prompt_module_batch"], "Prompt module batch", (
"count", "drafting_count", "review_count", "drafting_prompt_keys",
"review_routes", "items", "inventory_sha256",
))
if (
modules["count"] != EXPECTED_MODULES
or modules["drafting_count"] != 6
or modules["review_count"] != 8
or modules["drafting_prompt_keys"] != ["contract_drafting"]
):
raise Rc06ProtocolError("Publication batch must contain exactly 14 Prompt modules.")
module_rows = modules.get("items")
if not isinstance(module_rows, list) or len(module_rows) != EXPECTED_MODULES:
@@ -340,6 +619,21 @@ def validate_manifest(value: Any) -> dict[str, Any]:
module_rows = [_validate_module(item) for item in module_rows]
if len({item["key"] for item in module_rows}) != EXPECTED_MODULES:
raise Rc06ProtocolError("Prompt module identities are duplicated.")
drafting_keys = [item["key"] for item in module_rows if item["category"] == "drafting"]
review_routes = sorted(item["key"] for item in module_rows if item["category"] == "review")
if (
set(drafting_keys) != set(DRAFTING_MODULE_ORDER)
or modules["review_routes"] != review_routes
or len(review_routes) != 8
):
raise Rc06ProtocolError("Prompt module drafting/review mapping drift.")
for item in module_rows:
mapping = item["publication_mapping"]
if item["category"] == "drafting":
if mapping != {"prompt_key": "contract_drafting", "route": "contract_drafting"}:
raise Rc06ProtocolError("Drafting Prompt module publication mapping drift.")
elif mapping != {"prompt_key": item["key"], "route": item["key"]}:
raise Rc06ProtocolError("Review Prompt module publication mapping drift.")
if require_sha(modules.get("inventory_sha256"), "Prompt module inventory hash") != sha256_json(module_rows):
raise Rc06ProtocolError("Prompt module inventory hash drift.")
@@ -356,6 +650,15 @@ def validate_manifest(value: Any) -> dict[str, Any]:
or len({item.get("runtime_key") for item in runtime_rows if isinstance(item, dict)}) != EXPECTED_RUNTIME
):
raise Rc06ProtocolError("Runtime Prompt inventory must contain exactly 14 identities.")
publication_modules = {
item["key"]: {
"key": item["key"],
"version": item["version"],
"content_sha256": item["content_sha256"],
}
for item in module_rows
}
runtime_keys_by_kind: dict[str, set[str]] = {}
for item in runtime_rows:
require_exact(item, "runtime Prompt", (
"kind", "runtime_key", "version", "content_sha256", "publication_dependency",
@@ -366,23 +669,105 @@ def validate_manifest(value: Any) -> dict[str, Any]:
require_text(item["runtime_key"], "runtime Prompt key", maximum=128)
require_text(item["version"], "runtime Prompt version", maximum=128)
require_sha(item["content_sha256"], "runtime Prompt content hash")
runtime_keys_by_kind.setdefault(item["kind"], set()).add(item["runtime_key"])
expected_publication_dependency = {
"composed_drafting": "six_module_composition",
"new_review_module": "published_module",
"existing_domain_prompt": "preexisting_domain_prompt",
}[item["kind"]]
if item["publication_dependency"] != expected_publication_dependency:
raise Rc06ProtocolError("Runtime Prompt publication dependency drift.")
expected_module_keys = (
list(DRAFTING_MODULE_ORDER)
if item["kind"] == "composed_drafting"
else [item["runtime_key"]]
if item["kind"] == "new_review_module"
else []
)
if item["publication_module_dependencies"] != [
publication_modules[key] for key in expected_module_keys
]:
raise Rc06ProtocolError("Runtime Prompt module dependency identity drift.")
if (
runtime_keys_by_kind.get("composed_drafting") != {"contract_drafting_v1"}
or runtime_keys_by_kind.get("new_review_module") != set(review_routes)
or runtime_keys_by_kind.get("existing_domain_prompt") != EXISTING_DOMAIN_PROMPTS
):
raise Rc06ProtocolError("Runtime Prompt 1+8+5 identity inventory drift.")
if require_sha(runtime["inventory_sha256"], "runtime Prompt inventory hash") != sha256_json(runtime_rows):
raise Rc06ProtocolError("Runtime Prompt inventory hash drift.")
golden = manifest["golden_dispositions"]
boundary = require_exact(manifest["default_prompt_asset_boundary"], "default Prompt boundary", (
"included_in_publication_batch", "count", "source_path", "source_raw_sha256",
"inventory", "inventory_sha256", "boundary_note",
))
boundary_inventory = boundary["inventory"]
if (
not isinstance(golden, dict)
or golden.get("count") != EXPECTED_GOLDEN
or not isinstance(golden.get("items"), list)
or len(golden["items"]) != EXPECTED_GOLDEN
or any(item.get("status") == "unresolved" for item in golden["items"] if isinstance(item, dict))
or golden.get("inventory_sha256") != sha256_json(golden["items"])
boundary["included_in_publication_batch"] is not False
or boundary["count"] != EXPECTED_DEFAULT_PROMPTS
or not isinstance(boundary_inventory, list)
or len(boundary_inventory) != EXPECTED_DEFAULT_PROMPTS
or len({item.get("prompt_key") for item in boundary_inventory if isinstance(item, dict)})
!= EXPECTED_DEFAULT_PROMPTS
or set(item["prompt_key"] for item in boundary_inventory).intersection(
item["key"] for item in module_rows
)
or require_sha(boundary["source_raw_sha256"], "default Prompt source hash")
!= boundary["source_raw_sha256"]
or require_sha(boundary["inventory_sha256"], "default Prompt inventory hash")
!= sha256_json(boundary_inventory)
):
raise Rc06ProtocolError("The 46 default Prompt assets are not kept outside this batch.")
require_text(boundary["source_path"], "default Prompt source path", maximum=4096)
require_text(boundary["boundary_note"], "default Prompt boundary note", maximum=4000)
golden = require_exact(manifest["golden_dispositions"], "Golden dispositions", (
"count", "counts_by_status", "items", "inventory_sha256",
))
golden_items = golden["items"]
if (
golden["count"] != EXPECTED_GOLDEN
or not isinstance(golden_items, list)
or len(golden_items) != EXPECTED_GOLDEN
):
raise Rc06ProtocolError("Golden dispositions are incomplete or unresolved.")
control = manifest["publication_control"]
if not isinstance(control, dict) or control.get("rc05_terminal_evidence_complete") is not True or control.get(
"golden_dispositions_complete"
) is not True or control.get("release_ready") is not False:
golden_items = [_validate_golden_disposition(item) for item in golden_items]
if len({item["sample_id"] for item in golden_items}) != EXPECTED_GOLDEN:
raise Rc06ProtocolError("Golden disposition identities are duplicated.")
actual_counts = {
status: sum(item["status"] == status for item in golden_items)
for status in sorted(DISPOSITION_STATUSES)
}
if (
golden["counts_by_status"] != actual_counts
or require_sha(golden["inventory_sha256"], "Golden disposition inventory hash")
!= sha256_json(golden_items)
or sum(item["suite"] == "contract_generation" for item in golden_items)
!= EXPECTED_GOLDEN_GENERATION
or sum(item["suite"] == "clause_recommendation" for item in golden_items)
!= EXPECTED_GOLDEN_CLAUSE
):
raise Rc06ProtocolError("Golden disposition count, suite, or hash drift.")
control = require_exact(manifest["publication_control"], "publication control", (
"rc05_terminal_evidence_complete", "golden_dispositions_complete",
"approval_receipt_attached", "apply_receipt_attached",
"single_reviewer_batch_and_system_item_receipts_complete",
"runtime_executable_prompt_snapshots_complete", "blocked_controls", "release_ready",
"approval_receipt_schema", "apply_receipt_schema", "apply_performed_by_this_tool",
))
if (
control["rc05_terminal_evidence_complete"] is not True
or control["golden_dispositions_complete"] is not True
or control["approval_receipt_attached"] is not False
or control["apply_receipt_attached"] is not False
or control["single_reviewer_batch_and_system_item_receipts_complete"] is not False
or control["runtime_executable_prompt_snapshots_complete"] is not False
or control["blocked_controls"] != list(BLOCKED_CONTROLS)
or control["release_ready"] is not False
or control["approval_receipt_schema"] != APPROVAL_SCHEMA
or control["apply_receipt_schema"] != APPLY_RECEIPT_SCHEMA
or control["apply_performed_by_this_tool"] is not False
):
raise Rc06ProtocolError("Offline publication control is not eligible for RC06 apply.")
if manifest["side_effects"] != {
"http_calls": 0, "database_writes": 0, "model_calls": 0, "publication_api_calls": 0
@@ -490,6 +875,7 @@ def _validate_approval_keyring(
raise Rc06ProtocolError("Approval keyring contains no keys.")
validated: list[dict[str, Any]] = []
seen: set[str] = set()
seen_public_keys: set[bytes] = set()
for value in keys:
key = require_exact(value, "approval key", (
"key_id", "algorithm", "public_key_pem", "approver_id", "name",
@@ -501,15 +887,19 @@ def _validate_approval_keyring(
if {"approver_id": key["approver_id"], "name": key["name"]} != SOLE_REVIEWER:
raise Rc06ProtocolError("Approval key does not belong to the sole release reviewer.")
public_key = load_approval_public_key(key["public_key_pem"])
if not isinstance(public_key, Ed25519PublicKey) or public_key_raw(public_key) == public_key_raw(
trust.approval_root_public_key
public_bytes = public_key_raw(public_key)
if (
not isinstance(public_key, Ed25519PublicKey)
or public_bytes == public_key_raw(trust.approval_root_public_key)
or public_bytes in seen_public_keys
):
raise Rc06ProtocolError("Approval key is invalid or reuses the offline root.")
not_before = parse_time(key["not_before"], "approval key not_before")
not_after = parse_time(key["not_after"], "approval key not_after")
if not not_before <= approved_at < not_after:
raise Rc06ProtocolError("Approval key is outside its validity window.")
if not_before >= not_after:
raise Rc06ProtocolError("Approval key validity window is invalid.")
seen.add(key_id)
seen_public_keys.add(public_bytes)
validated.append({**key, "public_key": public_key, "not_before_parsed": not_before, "not_after_parsed": not_after})
return validated
@@ -621,10 +1011,28 @@ def _validate_approval(
))
):
raise Rc06ProtocolError("Approval receipt material drift.")
for disposition in manifest["golden_dispositions"]["items"]:
decided_at = parse_time(
disposition["decided_at"],
f"Golden disposition {disposition['sample_id']} decided_at",
)
if decided_at > approved_at:
raise Rc06ProtocolError("Golden disposition was decided after the approval time.")
if disposition["status"] == "scoped_internal_risk_acceptance":
risk_expires_at = parse_time(
disposition["risk_acceptance"]["expires_at"],
f"Golden disposition {disposition['sample_id']} risk acceptance expires_at",
)
if approved_at >= risk_expires_at or expires_at > risk_expires_at:
raise Rc06ProtocolError(
"Approval validity exceeds a Golden risk-acceptance window."
)
matches = [key for key in trusted_keys if key["key_id"] == payload["key_id"]]
if (
expires_at > keyring_expires_at
or len(matches) != 1
or approved_at < matches[0]["not_before_parsed"]
or approved_at >= matches[0]["not_after_parsed"]
or expires_at > matches[0]["not_after_parsed"]
):
raise Rc06ProtocolError("Approval signing key is not uniquely valid for the receipt window.")
@@ -656,7 +1064,9 @@ def _validate_legal(
request_sha = require_sha(legal_request["request_sha256"], "legal review request hash")
if request_sha != sha256_json({key: child for key, child in legal_request.items() if key != "request_sha256"}):
raise Rc06ProtocolError("Legal review request embedded hash drift.")
parse_time(legal_request["created_at"], "legal review request created_at")
request_created_at = parse_time(
legal_request["created_at"], "legal review request created_at"
)
scope = require_exact(legal_request["scope"], "legal review scope", (
"scene_count", "reference_occurrence_count", "distinct_source_record_count",
"distinct_url_count",
@@ -674,15 +1084,20 @@ def _validate_legal(
or len(occurrences) != 43
):
raise Rc06ProtocolError("Legal review request does not cover exact 30 scenes and 43 occurrences.")
if (
not isinstance(legal_request["source_files"], list)
or not legal_request["source_files"]
or any(
not isinstance(item, str) or not item.strip() or item != item.strip()
for item in legal_request["source_files"]
)
):
source_files = legal_request["source_files"]
if not isinstance(source_files, list) or not source_files:
raise Rc06ProtocolError("Legal review source file inventory is empty or invalid.")
source_paths: list[str] = []
for value in source_files:
source_file = require_exact(
value, "legal review source file", ("path", "raw_sha256")
)
source_paths.append(
require_text(source_file["path"], "legal review source file path", maximum=512)
)
require_sha(source_file["raw_sha256"], "legal review source file hash")
if source_paths != sorted(set(source_paths)):
raise Rc06ProtocolError("Legal review source file inventory is not canonical.")
if legal_request["guardrails"] != {
"not_legal_approval": True,
"authorizes_publication": False,
@@ -691,16 +1106,91 @@ def _validate_legal(
}:
raise Rc06ProtocolError("Legal review guardrails drift.")
occurrence_by_id: dict[str, dict[str, Any]] = {}
scene_ids: set[str] = set()
source_record_hashes: set[str] = set()
source_urls: set[str] = set()
manifest_sources: list[tuple[str, str, str]] = []
manifest_scene_ids: set[str] = set()
for asset in manifest["asset_batch"]["items"]:
scene = ((asset.get("input_schema") or {}).get("scene") or {})
references = scene.get("source_references")
if references is None:
continue
if not isinstance(references, list):
raise Rc06ProtocolError("Manifest scene source references are invalid.")
if not references:
continue
scene_id = require_text(scene.get("id"), "manifest scene id", maximum=128)
manifest_scene_ids.add(scene_id)
manifest_sources.extend(
(scene_id, asset["template_id"], sha256_json(source))
for source in references
)
if len(manifest_scene_ids) != 30 or len(manifest_sources) != 43:
raise Rc06ProtocolError(
"Manifest legal source inventory is not exact 30 scenes and 43 occurrences."
)
occurrence_sources: list[tuple[str, str, str]] = []
for occurrence in occurrences:
if not isinstance(occurrence, dict):
raise Rc06ProtocolError("Legal review occurrence is invalid.")
occurrence_id = require_text(occurrence.get("occurrence_id"), "legal occurrence id", maximum=512)
require_sha(occurrence.get("source_record_sha256"), "legal source record hash")
row = require_exact(occurrence, "legal review occurrence", (
"occurrence_id", "scene_id", "template_id", "source_key",
"source_pack_path", "source_record_sha256", "source",
"required_human_decisions",
))
occurrence_id = require_text(
row["occurrence_id"], "legal occurrence id", maximum=512
)
if occurrence_id in occurrence_by_id:
raise Rc06ProtocolError("Legal review occurrence is duplicated.")
occurrence_by_id[occurrence_id] = occurrence
scene_id = require_text(row["scene_id"], "legal scene id", maximum=128)
template_id = require_text(
row["template_id"], "legal template id", maximum=191
)
require_text(row["source_key"], "legal source key", maximum=128)
source_path = require_text(
row["source_pack_path"], "legal source pack path", maximum=512
)
if source_path not in source_paths:
raise Rc06ProtocolError("Legal occurrence references an unknown source file.")
source = require_exact(row["source"], "legal source record", (
"title", "url", "publication_date", "enactment_date", "accessed_on",
"scope", "current_effectiveness_verified",
))
for field in (
"title", "publication_date", "enactment_date", "accessed_on", "scope"
):
require_text(source[field], f"legal source {field}")
source_url = require_official_url(source["url"], "legal source URL")
if source["current_effectiveness_verified"] is not False:
raise Rc06ProtocolError(
"Legal review preparation source must remain explicitly unverified."
)
source_hash = require_sha(
row["source_record_sha256"], "legal source record hash"
)
if source_hash != sha256_json(source):
raise Rc06ProtocolError("Legal source record hash drift.")
if row["required_human_decisions"] != [
"current_effectiveness_verified",
"transaction_applicability_verified",
]:
raise Rc06ProtocolError("Legal occurrence decision requirements drift.")
occurrence_by_id[occurrence_id] = row
scene_ids.add(scene_id)
source_record_hashes.add(source_hash)
source_urls.add(source_url)
occurrence_sources.append((scene_id, template_id, source_hash))
if list(occurrence_by_id) != sorted(occurrence_by_id):
raise Rc06ProtocolError("Legal review occurrences are not canonically ordered.")
if (
len(scene_ids) != 30
or len(source_record_hashes) != 8
or len(source_urls) != 7
or sorted(occurrence_sources) != sorted(manifest_sources)
):
raise Rc06ProtocolError(
"Legal review occurrence inventory does not match the frozen manifest."
)
decision = request["legal_source_review_decision"]
if not isinstance(decision, dict) or decision.get("schema_version") != LEGAL_DECISION_SCHEMA:
raise Rc06ProtocolError("Legal review decision schema is unsupported.")
@@ -713,7 +1203,8 @@ def _validate_legal(
"approval expires_at",
)
if (
not reviewed_at <= now <= expires_at
reviewed_at < request_created_at
or not reviewed_at <= now <= expires_at
or expires_at <= reviewed_at
or expires_at - reviewed_at > MAX_LEGAL_WINDOW
or expires_at > approval_expires_at
@@ -744,7 +1235,9 @@ def _validate_legal(
or item.get("transaction_applicability_verified") is not True
):
raise Rc06ProtocolError("Legal review item is not affirmatively verified.")
parse_time(item.get("checked_at"), "legal item checked_at")
checked_at = parse_time(item.get("checked_at"), "legal item checked_at")
if not request_created_at <= checked_at <= reviewed_at:
raise Rc06ProtocolError("Legal review item timestamp is outside the review window.")
require_official_url(item.get("authoritative_status_url"), "legal authoritative URL")
rationale = require_text(item.get("rationale"), "legal rationale", maximum=4096)
if len(rationale) < 20 or rationale.casefold() in LEGAL_PLACEHOLDERS:
@@ -762,8 +1255,11 @@ def _validate_legal(
key = matches[0]
if key.get("status") != "trusted" or key.get("revoked_at") is not None:
raise Rc06ProtocolError("Legal review key is not active.")
if not parse_time(key.get("valid_from"), "legal key valid_from") <= reviewed_at <= parse_time(
key.get("valid_until"), "legal key valid_until"
key_valid_from = parse_time(key.get("valid_from"), "legal key valid_from")
key_valid_until = parse_time(key.get("valid_until"), "legal key valid_until")
if not (
key_valid_from <= reviewed_at <= key_valid_until
and expires_at <= key_valid_until
):
raise Rc06ProtocolError("Legal review key is outside its validity window.")
for decision_field, key_field in (
@@ -1,4 +1,5 @@
from pathlib import Path
import re
from typing import Literal
from alembic.config import Config
@@ -277,27 +278,92 @@ RC06_COLUMNS_BY_TABLE = {
),
}
RC06_CONSTRAINTS_BY_TABLE = {
"rc06_publication_sagas": (
"ux_rc06_publication_sagas_idempotency",
"ck_rc06_publication_sagas_state",
),
"rc06_template_publication_items": (
"ux_rc06_template_publication_items_transaction_template",
"ck_rc06_template_item_state",
),
"rc06_online_publication_transactions": (
"ux_rc06_online_publication_idempotency",
"ux_rc06_online_publication_challenge_id",
"ux_rc06_online_publication_challenge_sha256",
"ck_rc06_online_publication_state",
),
"rc06_apply_challenge_uses": (
"ux_rc06_apply_challenge_nonce",
"ux_rc06_apply_challenge_transaction",
"ux_rc06_apply_challenge_sha256",
"fk_rc06_apply_challenge_transaction",
"ck_rc06_apply_challenge_outcome",
),
"rc06_publication_sagas": {
"primary": {"primary_key": ("transaction_id",)},
"unique": {
"ux_rc06_publication_sagas_idempotency": ("tenant_id", "idempotency_key"),
},
"check": {
"ck_rc06_publication_sagas_state": (
"state",
frozenset({
"preparing", "prepared", "ai_committed", "contract_committed",
"committed", "compensating", "compensated", "compensation_failed",
}),
),
},
"foreign": {
"fk_rc06_publication_saga_authority_transaction": (
("transaction_id",),
"rc06_online_publication_transactions",
("transaction_id",),
"__current__",
"NO ACTION",
),
},
},
"rc06_template_publication_items": {
"primary": {"primary_key": ("id",)},
"unique": {
"ux_rc06_template_publication_items_transaction_template": (
"transaction_id", "template_id",
),
},
"check": {
"ck_rc06_template_item_state": (
"state", frozenset({"prepared", "applied", "compensated"}),
),
},
"foreign": {},
},
"rc06_online_publication_transactions": {
"primary": {"primary_key": ("transaction_id",)},
"unique": {
"ux_rc06_online_publication_idempotency": ("tenant_id", "idempotency_key"),
"ux_rc06_online_publication_challenge_id": ("challenge_id",),
"ux_rc06_online_publication_challenge_sha256": ("challenge_sha256",),
},
"check": {
"ck_rc06_online_publication_state": (
"state",
frozenset({
"authorized", "saga_prepared", "saga_committing",
"ai_committed", "contract_committed", "evidence_pending",
"online_primary_signed", "online_attested", "online_receipt_persisted",
"runtime_primary_signed", "runtime_attested",
"online_committed_pending_countersign", "finalized", "failed_before_commit",
"compensating", "rolled_back", "compensation_failed",
}),
),
},
"foreign": {},
},
"rc06_apply_challenge_uses": {
"primary": {"primary_key": ("challenge_id",)},
"unique": {
"ux_rc06_apply_challenge_nonce": ("nonce_sha256",),
"ux_rc06_apply_challenge_transaction": ("transaction_id",),
"ux_rc06_apply_challenge_sha256": ("challenge_sha256",),
},
"check": {
"ck_rc06_apply_challenge_outcome": (
"outcome",
frozenset({
"reserved", "pending_countersign", "finalized", "rolled_back",
"compensation_failed", "no_go",
}),
),
},
"foreign": {
"fk_rc06_apply_challenge_transaction": (
("transaction_id",),
"rc06_online_publication_transactions",
("transaction_id",),
"__current__",
"NO ACTION",
),
},
},
}
PLAYBOOK_OPERATIONS_COLUMNS_BY_TABLE = {
"product_experience_events": (
@@ -766,23 +832,103 @@ def _check_rc06_schema(inspector, table_names: set[str], *, dialect: str) -> Run
)
for table_name, expected_constraints in RC06_CONSTRAINTS_BY_TABLE.items():
if table_name not in table_names:
missing_constraints.extend(f"{table_name}.{name}" for name in expected_constraints)
continue
actual = {
str(item.get("name"))
for item in (
inspector.get_unique_constraints(table_name, schema=schema)
+ inspector.get_check_constraints(table_name, schema=schema)
+ inspector.get_foreign_keys(table_name, schema=schema)
+ inspector.get_indexes(table_name, schema=schema)
missing_constraints.extend(
f"{table_name}.{name}"
for kind in expected_constraints.values()
for name in kind
)
continue
actual_unique = {
str(item.get("name")): tuple(item.get("column_names") or ())
for item in inspector.get_unique_constraints(table_name, schema=schema)
if item.get("name")
}
missing_constraints.extend(
f"{table_name}.{name}"
for name in expected_constraints
if name not in actual
actual_checks = {
str(item.get("name")): str(item.get("sqltext") or "").lower()
for item in inspector.get_check_constraints(table_name, schema=schema)
if item.get("name")
}
actual_primary = tuple(
inspector.get_pk_constraint(table_name, schema=schema).get(
"constrained_columns"
)
or ()
)
actual_foreign = {
str(item.get("name")): (
tuple(item.get("constrained_columns") or ()),
str(item.get("referred_table") or ""),
tuple(item.get("referred_columns") or ()),
(
"__current__"
if item.get("referred_schema") in {None, "", schema}
else str(item.get("referred_schema"))
),
str(
(item.get("options") or {}).get("ondelete")
or item.get("ondelete")
or "NO ACTION"
).upper().replace("_", " "),
)
for item in inspector.get_foreign_keys(table_name, schema=schema)
if item.get("name")
}
for name, columns in expected_constraints["primary"].items():
if actual_primary != columns:
missing_constraints.append(f"{table_name}.{name}")
for name, columns in expected_constraints["unique"].items():
if actual_unique.get(name) != columns:
missing_constraints.append(f"{table_name}.{name}")
for name, (column, expected_values) in expected_constraints["check"].items():
sqltext = actual_checks.get(name, "")
identifier = (
rf'(?:"{re.escape(column)}"|{re.escape(column)}|'
rf'\[{re.escape(column)}\]|`{re.escape(column)}`)'
)
match = re.fullmatch(
rf"\s*\(*\s*{identifier}\s+in\s*\((?P<values>[^)]*)\)\s*\)*\s*",
sqltext,
flags=re.IGNORECASE,
)
pg_any = re.fullmatch(
rf"\s*\(*\s*(?:\(\s*)?{identifier}(?:\s*\))?"
r"(?:::\s*(?:text|character\s+varying))?\s*=\s*any\s*"
r"\(\s*\(*\s*array\s*\[(?P<values>[^\]]*)\]\s*\)*"
r"(?:::\s*text\s*\[\s*\])?\s*\)\s*\)*\s*",
sqltext,
flags=re.IGNORECASE,
)
literal_source = (
match.group("values")
if match is not None
else pg_any.group("values")
if pg_any is not None
else ""
)
# Accept only a closed list of SQL string literals. Merely finding
# the expected quoted values is unsafe: an expression such as
# ``state IN ('expected-1', ..., state)`` is tautological while it
# still contains every expected literal. PostgreSQL may add benign
# text/varchar casts when deparsing CHECK constraints, so those are
# the only non-literal tokens permitted here.
sql_literal = (
r"'(?:''|[^'])*'"
r"(?:\s*::\s*(?:text|character\s+varying"
r"(?:\s*\(\s*\d+\s*\))?))?"
)
literal_list = rf"\s*{sql_literal}(?:\s*,\s*{sql_literal})*\s*"
if re.fullmatch(literal_list, literal_source, flags=re.IGNORECASE):
actual_values = frozenset(
value.replace("''", "'")
for value in re.findall(r"'((?:''|[^'])*)'", literal_source)
)
else:
actual_values = frozenset()
if actual_values != expected_values:
missing_constraints.append(f"{table_name}.{name}")
for name, identity in expected_constraints["foreign"].items():
if actual_foreign.get(name) != identity:
missing_constraints.append(f"{table_name}.{name}")
if missing_columns or missing_constraints:
return RuntimeSchemaCheck(
name="rc06_schema",
@@ -799,7 +945,11 @@ def _check_rc06_schema(inspector, table_names: set[str], *, dialect: str) -> Run
message="Contract RC06 saga columns and integrity constraints are present.",
details={
"checked_columns": sum(map(len, RC06_COLUMNS_BY_TABLE.values())),
"checked_constraints": sum(map(len, RC06_CONSTRAINTS_BY_TABLE.values())),
"checked_constraints": sum(
len(kind)
for table in RC06_CONSTRAINTS_BY_TABLE.values()
for kind in table.values()
),
},
)
@@ -1,6 +1,7 @@
from __future__ import annotations
import base64
import json
import time
from concurrent.futures import ThreadPoolExecutor
from datetime import UTC, datetime, timedelta
@@ -9,6 +10,7 @@ from threading import Event
import pytest
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
import yuqei_contract_api.rc06_publication_facade as facade_module
from yuqei_contract_api.config import ContractApiSettings
from yuqei_contract_api.persistence.models import Rc06OnlinePublicationTransactionModel
from yuqei_contract_api.rc06_publication import Rc06SagaStatus
@@ -360,6 +362,41 @@ def _service(
return repository, service, signer, coordinator, apply_private, material, headers
def _signed_apply_receipt(material, execution, apply_private, countersigned_at):
runtime_at = execution["runtime_snapshot_receipt"]["payload"]["captured_at"]
apply_payload = {
"schema_version": APPLY_RECEIPT_PAYLOAD_SCHEMA,
"batch_manifest_raw_sha256": material.request["batch_manifest_raw_sha256"],
"batch_manifest_payload_sha256": material.request["batch_manifest_payload_sha256"],
"approval_receipt_sha256": material.request["approval_receipt_sha256"],
"legal_source_review_binding_sha256": material.legal_binding_sha256,
"environment_id": ENVIRONMENT,
"tenant_id": TENANT,
"idempotency_key": material.request["idempotency_key"],
"transaction_id": material.request["transaction_id"],
"authorized_commit_at": runtime_at,
"countersigned_at": iso_time(countersigned_at),
"generated_by": {"type": "system", "component": "rc06-apply-authority"},
"transaction_mode": "durable_saga",
"cross_database_atomicity": False,
"item_results": material.expected_publication_results,
"write_count": 74,
"status": "applied_and_verified",
"online_transaction_receipt": execution["online_transaction_receipt"],
"runtime_snapshot_receipt": execution["runtime_snapshot_receipt"],
}
signature = apply_private.sign(
APPLY_RECEIPT_SIGNATURE_DOMAIN + canonical_json(apply_payload)
)
return {
"schema_version": APPLY_RECEIPT_SCHEMA,
"receipt_id": "rc06-apply-receipt-"
+ sha256_bytes(canonical_json(apply_payload) + signature)[:24],
"payload": apply_payload,
"signature": base64.b64encode(signature).decode(),
}
def test_apply_uses_exactly_four_kms_calls_and_recovers_without_resigning(tmp_path):
repository, service, signer, coordinator, _, material, headers = _service(tmp_path)
try:
@@ -421,34 +458,12 @@ def test_finalize_is_idempotent_and_status_retains_all_receipts(tmp_path):
try:
execution = service.apply(material.request, headers)
runtime_at = execution["runtime_snapshot_receipt"]["payload"]["captured_at"]
apply_payload = {
"schema_version": APPLY_RECEIPT_PAYLOAD_SCHEMA,
"batch_manifest_raw_sha256": material.request["batch_manifest_raw_sha256"],
"batch_manifest_payload_sha256": material.request["batch_manifest_payload_sha256"],
"approval_receipt_sha256": material.request["approval_receipt_sha256"],
"legal_source_review_binding_sha256": material.legal_binding_sha256,
"environment_id": ENVIRONMENT,
"tenant_id": TENANT,
"idempotency_key": material.request["idempotency_key"],
"transaction_id": material.request["transaction_id"],
"authorized_commit_at": runtime_at,
"countersigned_at": runtime_at,
"generated_by": {"type": "system", "component": "rc06-apply-authority"},
"transaction_mode": "durable_saga",
"cross_database_atomicity": False,
"item_results": material.expected_publication_results,
"write_count": 74,
"status": "applied_and_verified",
"online_transaction_receipt": execution["online_transaction_receipt"],
"runtime_snapshot_receipt": execution["runtime_snapshot_receipt"],
}
signature = apply_private.sign(APPLY_RECEIPT_SIGNATURE_DOMAIN + canonical_json(apply_payload))
apply_receipt = {
"schema_version": APPLY_RECEIPT_SCHEMA,
"receipt_id": "rc06-apply-receipt-" + sha256_bytes(canonical_json(apply_payload) + signature)[:24],
"payload": apply_payload,
"signature": base64.b64encode(signature).decode(),
}
apply_receipt = _signed_apply_receipt(
material,
execution,
apply_private,
datetime.fromisoformat(runtime_at),
)
finalize_request = {
"schema_version": FINALIZE_REQUEST_SCHEMA,
"idempotency_key": material.request["idempotency_key"],
@@ -480,6 +495,73 @@ def test_finalize_is_idempotent_and_status_retains_all_receipts(tmp_path):
repository.engine.dispose()
def test_concurrent_distinct_finalize_receipts_cannot_overwrite_terminal_evidence(tmp_path):
repository, service, _, _, apply_private, material, headers = _service(tmp_path)
try:
execution = service.apply(material.request, headers)
runtime_at = datetime.fromisoformat(
execution["runtime_snapshot_receipt"]["payload"]["captured_at"]
)
# Both countersigns are independently well-formed and within the
# frozen authority window. Only the winner may become terminal.
time.sleep(1.1)
receipts = [
_signed_apply_receipt(
material,
execution,
apply_private,
runtime_at + timedelta(seconds=offset),
)
for offset in (0, 1)
]
def finalize(receipt):
request = {
"schema_version": FINALIZE_REQUEST_SCHEMA,
"idempotency_key": material.request["idempotency_key"],
"transaction_id": material.request["transaction_id"],
"environment_id": ENVIRONMENT,
"tenant_id": TENANT,
"batch_manifest_raw_sha256": material.request["batch_manifest_raw_sha256"],
"approval_receipt_sha256": material.request["approval_receipt_sha256"],
"legal_source_review_binding_sha256": material.legal_binding_sha256,
"online_transaction_receipt_sha256": sha256_json(
execution["online_transaction_receipt"]
),
"runtime_snapshot_receipt_sha256": sha256_json(
execution["runtime_snapshot_receipt"]
),
"apply_receipt_sha256": sha256_json(receipt),
"apply_receipt": receipt,
}
request_headers = Rc06FacadeHeaders(
**{**headers.__dict__, "request_sha256": sha256_json(request)}
)
try:
service.finalize(
material.request["transaction_id"],
request,
request_headers,
)
return "success", receipt
except Rc06FacadeConflict as error:
return "conflict", str(error)
with ThreadPoolExecutor(max_workers=2) as pool:
results = list(pool.map(finalize, receipts))
assert sorted(kind for kind, _ in results) == ["conflict", "success"]
winner = next(value for kind, value in results if kind == "success")
status = service.status(material.request["transaction_id"], headers)
assert status["state"] == "finalized"
assert status["apply_receipt"] == winner
assert status["apply_receipt_sha256"] in {
sha256_json(receipt) for receipt in receipts
}
finally:
repository.engine.dispose()
def test_post_contract_rollback_is_forbidden_without_calling_compensation(tmp_path):
repository, service, _, coordinator, _, material, headers = _service(tmp_path)
try:
@@ -539,6 +621,50 @@ def test_pre_contract_failure_can_produce_exact_rollback_v2(tmp_path):
repository.engine.dispose()
def test_rollback_recovers_ai_commit_lost_before_facade_journal_update(tmp_path):
repository, service, _, coordinator, _, material, headers = _service(tmp_path)
try:
service._authorize_and_reserve( # noqa: SLF001 - crash-boundary regression
material.request,
sha256_json(material.request),
datetime.now(UTC),
)
coordinator.state = coordinator._status("prepared") # noqa: SLF001
compensate = coordinator.compensate
def compensate_after_lost_commit(transaction_id, *, tenant_id=None):
coordinator.state = coordinator._status("ai_committed") # noqa: SLF001
return compensate(transaction_id, tenant_id=tenant_id)
coordinator.compensate = compensate_after_lost_commit
rollback_request = {
"schema_version": ROLLBACK_REQUEST_SCHEMA,
"idempotency_key": material.request["idempotency_key"],
"transaction_id": material.request["transaction_id"],
"batch_manifest_raw_sha256": material.request["batch_manifest_raw_sha256"],
"approval_receipt_sha256": material.request["approval_receipt_sha256"],
"legal_source_review_binding_sha256": material.legal_binding_sha256,
"environment_id": ENVIRONMENT,
"tenant_id": TENANT,
"transaction_mode": "durable_saga",
"cross_database_atomicity": False,
}
rollback = service.rollback(
material.request["transaction_id"], rollback_request, headers
)
assert rollback["state"] == "rolled_back"
assert rollback["participant_states_before"][0]["state"] == "committed"
assert len(rollback["compensation_item_results"]) == 14
assert len(rollback["runtime_snapshot_compensation_results"]) == 14
assert rollback["removed_item_count"] == 28
status = service.status(material.request["transaction_id"], headers)
assert status["state"] == "rolled_back"
assert status["rollback_response"] == rollback
finally:
repository.engine.dispose()
def test_post_contract_kms_failure_is_no_go_recoverable_and_never_compensated(tmp_path):
repository, service, signer, coordinator, _, material, headers = _service(
tmp_path, fail_sign_once_at=2
@@ -571,6 +697,113 @@ def test_post_contract_kms_failure_is_no_go_recoverable_and_never_compensated(tm
repository.engine.dispose()
def test_persisted_runtime_payload_drift_is_never_signed_on_recovery(tmp_path):
repository, service, signer, coordinator, _, material, headers = _service(
tmp_path, fail_sign_once_at=3
)
try:
with pytest.raises(Rc06FacadeError, match="KMS response loss"):
service.apply(material.request, headers)
assert len(signer.calls) == 3
with repository._session_factory.begin() as session:
row = session.get(
Rc06OnlinePublicationTransactionModel,
material.request["transaction_id"],
)
persisted = json.loads(row.runtime_payload_json)
persisted["runtime_snapshot_dependency_count"] = 13
row.runtime_payload_json = json.dumps(
persisted,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
)
with pytest.raises(Rc06FacadeConflict, match="runtime receipt payload drifted"):
service.apply(
material.request,
headers,
now=datetime.now(UTC) + timedelta(days=1),
)
assert len(signer.calls) == 3
assert coordinator.compensate_calls == 0
finally:
repository.engine.dispose()
def test_finalize_rejects_future_or_backfilled_countersign_time(tmp_path, monkeypatch):
repository, service, _, _, apply_private, material, headers = _service(tmp_path)
try:
execution = service.apply(material.request, headers)
row = service._load(material.request["transaction_id"])
server_now = datetime.now(UTC)
future = _signed_apply_receipt(
material,
execution,
apply_private,
server_now + timedelta(minutes=5),
)
with pytest.raises(Rc06FacadeError, match="authority window"):
service._validate_apply_receipt(row, future, now=server_now)
runtime_at = datetime.fromisoformat(
execution["runtime_snapshot_receipt"]["payload"]["captured_at"]
)
backfilled = _signed_apply_receipt(
material,
execution,
apply_private,
runtime_at,
)
expired_now = datetime.fromisoformat(
material.approval["payload"]["expires_at"]
) + timedelta(seconds=1)
with pytest.raises(Rc06FacadeError, match="authority window"):
service._validate_apply_receipt(row, backfilled, now=expired_now)
finalize_request = {
"schema_version": FINALIZE_REQUEST_SCHEMA,
"idempotency_key": material.request["idempotency_key"],
"transaction_id": material.request["transaction_id"],
"environment_id": ENVIRONMENT,
"tenant_id": TENANT,
"batch_manifest_raw_sha256": material.request["batch_manifest_raw_sha256"],
"approval_receipt_sha256": material.request["approval_receipt_sha256"],
"legal_source_review_binding_sha256": material.legal_binding_sha256,
"online_transaction_receipt_sha256": sha256_json(
execution["online_transaction_receipt"]
),
"runtime_snapshot_receipt_sha256": sha256_json(
execution["runtime_snapshot_receipt"]
),
"apply_receipt_sha256": sha256_json(backfilled),
"apply_receipt": backfilled,
}
finalize_headers = Rc06FacadeHeaders(
**{**headers.__dict__, "request_sha256": sha256_json(finalize_request)}
)
monkeypatch.setattr(facade_module, "_database_wall_clock", lambda _session: expired_now)
with pytest.raises(Rc06FacadeConflict, match="authority deadline"):
service.finalize(
material.request["transaction_id"], finalize_request, finalize_headers
)
monkeypatch.setattr(
facade_module,
"_database_wall_clock",
lambda _session: runtime_at - timedelta(seconds=1),
)
with pytest.raises(Rc06FacadeConflict, match="database commit clock"):
service.finalize(
material.request["transaction_id"], finalize_request, finalize_headers
)
status = service.status(material.request["transaction_id"], headers)
assert status["state"] == "committed_and_verified"
assert status["apply_receipt"] is None
finally:
repository.engine.dispose()
def test_two_facade_instances_singleflight_the_four_kms_stages(tmp_path):
repository, first, signer, coordinator, apply_private, material, headers = _service(
tmp_path,
@@ -603,6 +836,58 @@ def test_two_facade_instances_singleflight_the_four_kms_stages(tmp_path):
repository.engine.dispose()
def test_rollback_wins_race_and_later_apply_cannot_commit(tmp_path):
repository, service, _, coordinator, _, material, headers = _service(tmp_path)
started = Event()
release = Event()
try:
service._authorize_and_reserve(
material.request,
sha256_json(material.request),
datetime.now(UTC),
)
coordinator.state = coordinator._status("prepared")
original_compensate = coordinator.compensate
def delayed_compensate(transaction_id, *, tenant_id=None):
started.set()
assert release.wait(timeout=10)
return original_compensate(transaction_id, tenant_id=tenant_id)
coordinator.compensate = delayed_compensate
rollback_request = {
"schema_version": ROLLBACK_REQUEST_SCHEMA,
"idempotency_key": material.request["idempotency_key"],
"transaction_id": material.request["transaction_id"],
"batch_manifest_raw_sha256": material.request["batch_manifest_raw_sha256"],
"approval_receipt_sha256": material.request["approval_receipt_sha256"],
"legal_source_review_binding_sha256": material.legal_binding_sha256,
"environment_id": ENVIRONMENT,
"tenant_id": TENANT,
"transaction_mode": "durable_saga",
"cross_database_atomicity": False,
}
with ThreadPoolExecutor(max_workers=2) as executor:
rollback_future = executor.submit(
service.rollback,
material.request["transaction_id"],
rollback_request,
headers,
)
assert started.wait(timeout=10)
apply_future = executor.submit(service.apply, material.request, headers)
release.set()
assert rollback_future.result(timeout=20)["state"] == "rolled_back"
with pytest.raises(Rc06FacadeConflict):
apply_future.result(timeout=20)
assert coordinator.commit_calls == 0
status = service.status(material.request["transaction_id"], headers)
assert status["state"] == "rolled_back"
assert status["rollback_response"] is not None
finally:
repository.engine.dispose()
def test_expired_authority_cannot_prepare_or_commit_a_reserved_transaction(tmp_path):
initial = datetime.now(UTC).replace(microsecond=0)
expires_at = initial + timedelta(minutes=5)
@@ -693,3 +978,39 @@ def test_expired_authority_can_only_finish_proven_contract_commit(tmp_path):
assert len(signer.calls) == 4
finally:
repository.engine.dispose()
def test_status_reconciles_a_lagging_facade_after_contract_commit(tmp_path):
initial = datetime.now(UTC).replace(microsecond=0)
expires_at = initial + timedelta(minutes=5)
repository, service, signer, coordinator, _, material, headers = _service(
tmp_path,
authority_expires_at=expires_at,
)
try:
service._authorize_and_reserve( # noqa: SLF001 - crash-window regression
material.request,
sha256_json(material.request),
initial,
)
coordinator.state = coordinator._status("committed") # noqa: SLF001
reconciled = service.status(material.request["transaction_id"], headers)
assert reconciled["state"] == "irreversible_failure"
assert reconciled["state_version"] == 2
assert reconciled["no_go"] is True
assert reconciled["failure_code"] == "rc06facadeerror"
assert reconciled["publication_item_results"] == []
assert len(signer.calls) == 0
recovered = service.apply(
material.request,
headers,
now=expires_at + timedelta(seconds=1),
)
assert recovered["publication_item_count"] == 74
assert coordinator.prepare_calls == 0
assert coordinator.commit_calls == 0
assert len(signer.calls) == 4
finally:
repository.engine.dispose()
@@ -0,0 +1,427 @@
from __future__ import annotations
import base64
import hashlib
import importlib.util
import sys
from copy import deepcopy
from datetime import UTC, datetime
from pathlib import Path
import pytest
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding, rsa, utils
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from yuqei_ai_platform_api.models import PromptTemplateModel
from yuqei_ai_platform_api.packaged_prompt_assets_import import (
DEFAULT_SOURCE_FILE,
DEFAULT_VERSION,
load_packaged_prompt_assets,
)
from yuqei_ai_platform_api.rc06_publication import (
PromptPrepareRequest,
Rc06PromptPublicationService,
)
from yuqei_ai_platform_api.repository import SqlAlchemyAiPlatformRepository
from yuqei_contract_api.config import ContractApiSettings
from yuqei_contract_api.rc06_publication_facade import (
ConfiguredRc06Authority,
Rc06FacadeHeaders,
Rc06PublicationFacadeService,
)
from yuqei_contract_api.rc06_publication_protocol import (
APPROVAL_KEYRING_SIGNATURE_DOMAIN,
APPROVAL_SIGNATURE_DOMAIN,
CHALLENGE_SIGNATURE_DOMAIN,
LEGAL_SIGNATURE_DOMAIN,
AuthorityTrust,
canonical_json,
public_key_raw,
publication_idempotency_key,
publication_transaction_id,
sha256_bytes,
sha256_json,
)
from yuqei_contract_api.sqlalchemy_contract_repository import SqlAlchemyContractRepository
from yuqei_sdk.prompt_source_baseline import DOMAIN_REVIEW_RUNTIME_IDENTITIES
REPO_ROOT = Path(__file__).resolve().parents[4]
def _load_apply_fixture():
path = REPO_ROOT / "yuqei-infra/tests/test_rc06_publication_apply.py"
name = "rc06_facade_real_client_fixture"
spec = importlib.util.spec_from_file_location(name, path)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
sys.modules[name] = module
spec.loader.exec_module(module)
return module
def _resign_authority_material(material: dict) -> dict:
"""Replace the fixture's stub legal signature with fully verified material."""
result = deepcopy(material)
reviewer_private = Ed25519PrivateKey.generate()
keyring = result["approval_keyring"]
keyring["keys"][0]["public_key_pem"] = reviewer_private.public_key().public_bytes(
serialization.Encoding.PEM,
serialization.PublicFormat.SubjectPublicKeyInfo,
).decode("ascii")
unsigned_keyring = {
key: keyring[key]
for key in (
"schema_version",
"root_key_id",
"epoch",
"issued_at",
"expires_at",
"authorized_batch_manifest_raw_sha256",
"keys",
)
}
keyring["keyring_payload_sha256"] = sha256_json(unsigned_keyring)
keyring["root_signature_base64"] = base64.b64encode(
result["root_private"].sign(
APPROVAL_KEYRING_SIGNATURE_DOMAIN + canonical_json(unsigned_keyring)
)
).decode("ascii")
result["approval_keyring_raw"] = sha256_json(keyring)
approval = result["approval"]
approval_payload = approval["payload"]
approval_payload["approval_keyring_payload_sha256"] = keyring[
"keyring_payload_sha256"
]
approval_signature = reviewer_private.sign(
APPROVAL_SIGNATURE_DOMAIN + canonical_json(approval_payload)
)
approval["signature"] = base64.b64encode(approval_signature).decode("ascii")
approval["approval_id"] = (
"rc06-batch-approval-"
+ sha256_bytes(canonical_json(approval_payload) + approval_signature)[:24]
)
result["approval_raw"] = sha256_json(approval)
legal_private = Ed25519PrivateKey.generate()
legal_request = result["legal_request"]
source_path = "fixture-only/not-production.json"
occurrences = []
for asset in result["manifest"]["asset_batch"]["items"]:
scene = ((asset.get("input_schema") or {}).get("scene") or {})
for index, source in enumerate(scene.get("source_references") or []):
source_key = f"source-{index}"
occurrences.append(
{
"occurrence_id": (
f"{scene['id']}:{asset['template_id']}:{source_key}"
),
"scene_id": scene["id"],
"template_id": asset["template_id"],
"source_key": source_key,
"source_pack_path": source_path,
"source_record_sha256": sha256_json(source),
"source": source,
"required_human_decisions": [
"current_effectiveness_verified",
"transaction_applicability_verified",
],
}
)
occurrences.sort(key=lambda item: item["occurrence_id"])
assert len(occurrences) == 43
legal_request["source_files"] = [
{"path": source_path, "raw_sha256": "9" * 64}
]
legal_request["occurrences"] = occurrences
legal_request["request_sha256"] = sha256_json(
{key: value for key, value in legal_request.items() if key != "request_sha256"}
)
legal_keyring = result["legal_keyring"]
legal_keyring["keys"][0]["public_key_raw_base64"] = base64.b64encode(
public_key_raw(legal_private.public_key())
).decode("ascii")
result["legal_keyring_raw"] = sha256_json(legal_keyring)
legal_decision = result["legal_decision"]
legal_decision["request_sha256"] = legal_request["request_sha256"]
item_template = legal_decision["items"][0]
legal_decision["items"] = [
{
**item_template,
"occurrence_id": occurrence["occurrence_id"],
"source_record_sha256": occurrence["source_record_sha256"],
}
for occurrence in occurrences
]
legal_unsigned = {
key: value for key, value in legal_decision.items() if key != "signature"
}
legal_signature = legal_private.sign(
LEGAL_SIGNATURE_DOMAIN + canonical_json(legal_unsigned)
)
legal_decision["signature"]["value_base64"] = base64.b64encode(
legal_signature
).decode("ascii")
result["legal_binding"] = sha256_json(
{
"request_sha256": result["legal_request"]["request_sha256"],
"decision_sha256": sha256_json(legal_decision),
"keyring_raw_sha256": result["legal_keyring_raw"],
}
)
result["idempotency_key"] = publication_idempotency_key(
result["manifest_raw"],
result["approval_raw"],
result["legal_binding"],
result["environment_id"],
result["tenant_id"],
)
result["transaction_id"] = publication_transaction_id(result["idempotency_key"])
challenge = result["authority_challenge"]
challenge_payload = challenge["payload"]
challenge_payload.update(
{
"approval_receipt_sha256": result["approval_raw"],
"legal_source_review_binding_sha256": result["legal_binding"],
"idempotency_key": result["idempotency_key"],
"transaction_id": result["transaction_id"],
}
)
challenge_signature = result["apply_private"].sign(
CHALLENGE_SIGNATURE_DOMAIN + canonical_json(challenge_payload)
)
challenge["signature"] = base64.b64encode(challenge_signature).decode("ascii")
challenge["challenge_id"] = (
"rc06-apply-challenge-"
+ sha256_bytes(canonical_json(challenge_payload) + challenge_signature)[:24]
)
return result
class _LocalAiClient:
def __init__(self, service: Rc06PromptPublicationService) -> None:
self.service = service
def prepare(self, payload):
return self.service.prepare(PromptPrepareRequest.model_validate(payload)).model_dump(
mode="json"
)
def commit(self, transaction_id):
return self.service.commit(transaction_id).model_dump(mode="json")
def status(self, transaction_id):
return self.service.status(transaction_id).model_dump(mode="json")
def seal(self, transaction_id, capability):
return self.service.seal(transaction_id, capability).model_dump(mode="json")
def compensate(self, transaction_id, capability):
return self.service.compensate(transaction_id, capability).model_dump(mode="json")
class _RsaKmsSigner:
def __init__(self, private_key) -> None:
self.private_key = private_key
self.calls = 0
def sign(self, *, domain, payload, include_request_id):
self.calls += 1
digest = hashlib.sha256(domain + canonical_json(payload)).digest()
value = self.private_key.sign(
digest,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=hashes.SHA256().digest_size,
),
utils.Prehashed(hashes.SHA256()),
)
request_id = f"fixture-kms-request-{self.calls}"
signature = {
"provider": "fixture-kms",
"key_reference": "kms://fixture/rc06-online",
"key_version": "version-1",
"algorithm": "RSA_PSS_SHA_256",
"value_base64": base64.b64encode(value).decode("ascii"),
}
if include_request_id:
signature["request_id"] = request_id
return signature, request_id
class _DirectFacadeClient:
def __init__(
self,
service: Rc06PublicationFacadeService,
*,
lose_apply_response: bool = False,
lose_finalize_response: bool = False,
) -> None:
self.service = service
self.lose_apply_response = lose_apply_response
self.lose_finalize_response = lose_finalize_response
@staticmethod
def _headers(values: dict[str, str]) -> Rc06FacadeHeaders:
return Rc06FacadeHeaders(
idempotency_key=values["Idempotency-Key"],
manifest_sha256=values["X-RC06-Manifest-SHA256"],
approval_sha256=values["X-RC06-Approval-SHA256"],
legal_sha256=values["X-RC06-Legal-Review-SHA256"],
tenant_id=values["X-RC06-Tenant-ID"],
request_sha256=values.get("X-RC06-Request-SHA256"),
)
def request(self, method, path, *, payload, headers):
facade_headers = self._headers(headers)
if method == "POST" and path.endswith("/apply"):
response = self.service.apply(payload, facade_headers)
if self.lose_apply_response:
self.lose_apply_response = False
raise OSError("injected lost apply response")
return response
transaction_id = path.split("/")[-2]
if method == "GET" and path.endswith("/status"):
return self.service.status(transaction_id, facade_headers)
if method == "POST" and path.endswith("/finalize"):
response = self.service.finalize(transaction_id, payload, facade_headers)
if self.lose_finalize_response:
self.lose_finalize_response = False
raise OSError("injected lost finalize response")
return response
if method == "POST" and path.endswith("/rollback"):
return self.service.rollback(transaction_id, payload, facade_headers)
raise AssertionError((method, path))
def _authority(material: dict) -> ConfiguredRc06Authority:
images = {
item["role"]: item["manifest_digest"]
for item in material["manifest"]["candidate"]["images"]
}
authority = object.__new__(ConfiguredRc06Authority)
authority._trust = AuthorityTrust( # noqa: SLF001 - exact trust fixture
approval_root_public_key=material["root_private"].public_key(),
approval_root_key_id=material["approval_keyring"]["root_key_id"],
approval_keyring_epoch=material["approval_keyring"]["epoch"],
apply_service_public_key=material["apply_private"].public_key(),
legal_keyring_sha256=material["legal_keyring_raw"],
environment_id=material["environment_id"],
tenant_id=material["tenant_id"],
frozen_candidate_commit=material["manifest"]["candidate"]["commit"],
contract_release_image_digest=images["contract-api"],
ai_release_image_digest=images["ai-platform-api"],
)
return authority
def test_real_client_validates_facade_status_and_recovers_lost_responses(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
fixture = _load_apply_fixture()
material = _resign_authority_material(
fixture._material(monkeypatch, datetime.now(UTC).replace(microsecond=0))
)
monkeypatch.setattr(
fixture.apply.batch,
"TRUSTED_SCENE_LEGAL_REVIEW_KEYRING_SHA256",
material["legal_keyring_raw"],
)
images = {
item["role"]: item["manifest_digest"]
for item in material["manifest"]["candidate"]["images"]
}
contract = SqlAlchemyContractRepository(
f"sqlite:///{tmp_path / 'contract.sqlite3'}",
auto_create=True,
seed_demo_data=False,
file_storage_dir=tmp_path / "files",
)
ai = SqlAlchemyAiPlatformRepository(
f"sqlite:///{tmp_path / 'ai.sqlite3'}",
auto_create=True,
)
ledger = fixture.apply.ApplyLedger(tmp_path / "ledger.sqlite3")
replacement = None
try:
packaged, _, duplicates = load_packaged_prompt_assets(DEFAULT_SOURCE_FILE)
assert duplicates == []
by_key = {item.prompt_key: item for item in packaged}
with ai._session_factory.begin() as session:
for name in DOMAIN_REVIEW_RUNTIME_IDENTITIES:
session.add(
PromptTemplateModel(
id=f"existing-{name}",
name=name,
version=DEFAULT_VERSION,
content=by_key[name].content,
enabled=True,
status="published",
published_at=material["now"],
updated_at=material["now"],
)
)
ai_service = Rc06PromptPublicationService(
ai._session_factory,
frozen_candidate_commit=material["manifest"]["candidate"]["commit"],
release_image_digest=images["ai-platform-api"],
)
token = tmp_path / "facade.token"
token.write_text("facade-token-00000000000000000000000001", encoding="utf-8")
settings = ContractApiSettings(
environment="test",
rc06_publication_facade_enabled=True,
rc06_publication_environment_id=material["environment_id"],
rc06_publication_tenant_id=material["tenant_id"],
rc06_publication_bearer_token_file=str(token.resolve()),
rc06_frozen_candidate_commit=material["manifest"]["candidate"]["commit"],
rc06_release_image_digest=images["contract-api"],
rc06_ai_release_image_digest=images["ai-platform-api"],
)
signer = _RsaKmsSigner(material["online_private"])
service = Rc06PublicationFacadeService(
settings=settings,
repository=contract,
authority=_authority(material),
signer=signer,
ai_client=_LocalAiClient(ai_service),
apply_service_public_key=material["apply_private"].public_key(),
)
client = _DirectFacadeClient(
service,
lose_apply_response=True,
lose_finalize_response=True,
)
execution = fixture._execute(material, client, ledger)
assert signer.calls == 4
receipt = fixture._countersign(material, execution, datetime.now(UTC))
final = fixture._finalize(
material,
client,
ledger,
execution,
receipt,
datetime.now(UTC),
)
assert final["online_finalize_status"]["state"] == "finalized"
assert final["online_finalize_status"]["state_version"] == 4
# A lost local ledger must be able to recover all three receipts from
# the exact finalized server status without another KMS signature.
replacement = fixture.apply.ApplyLedger(tmp_path / "replacement.sqlite3")
recovered = fixture._execute(material, client, replacement)
assert recovered == execution
assert signer.calls == 4
finally:
if replacement is not None:
replacement.close()
ledger.close()
ai._engine.dispose()
contract.engine.dispose()
@@ -0,0 +1,91 @@
from __future__ import annotations
import importlib.util
import sys
from copy import deepcopy
from pathlib import Path
import pytest
from yuqei_contract_api.rc06_publication_protocol import (
Rc06ProtocolError,
sha256_json,
validate_manifest,
)
REPO_ROOT = Path(__file__).resolve().parents[4]
def _fixture_manifest() -> dict:
path = REPO_ROOT / "yuqei-infra/tests/test_rc06_publication_batch.py"
name = "rc06_contract_protocol_manifest_fixture"
spec = importlib.util.spec_from_file_location(name, path)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
sys.modules[name] = module
spec.loader.exec_module(module)
inventory = module.batch.load_runtime_inventory(REPO_ROOT)
return module._manifest(inventory, resolved=True)
def _rehash(manifest: dict) -> dict:
manifest["manifest_payload_sha256"] = sha256_json(
{key: value for key, value in manifest.items() if key != "manifest_payload_sha256"}
)
return manifest
def test_runtime_manifest_gate_accepts_the_exact_offline_validated_shape() -> None:
manifest = _fixture_manifest()
assert validate_manifest(manifest) == manifest
@pytest.mark.parametrize(
"mutation",
(
"rc05_not_green",
"invalid_failure_class",
"invalid_asset_template_id",
"missing_release_image",
"unresolved_golden",
"publication_control_shape",
),
)
def test_runtime_manifest_gate_rejects_rehashed_release_control_drift(mutation: str) -> None:
manifest = deepcopy(_fixture_manifest())
if mutation == "rc05_not_green":
manifest["candidate"]["rc05_terminal_evidence"]["complete_real_green"] = False
elif mutation == "invalid_failure_class":
controls = manifest["candidate"]["rc05_plan_identity"]["release_controls"]
first = next(iter(controls["failure_classes"]))
controls["failure_classes"][first] = "!!! not-a-stable-id !!!"
elif mutation == "invalid_asset_template_id":
manifest["asset_batch"]["items"][0]["template_id"] = "not a stable id"
elif mutation == "missing_release_image":
manifest["candidate"]["images"].pop()
elif mutation == "unresolved_golden":
item = manifest["golden_dispositions"]["items"][0]
item["status"] = "unresolved"
manifest["golden_dispositions"]["counts_by_status"] = {
status: sum(
row["status"] == status
for row in manifest["golden_dispositions"]["items"]
)
for status in (
"named_legal_approval",
"scoped_internal_risk_acceptance",
"unresolved",
)
}
manifest["golden_dispositions"]["inventory_sha256"] = sha256_json(
manifest["golden_dispositions"]["items"]
)
elif mutation == "publication_control_shape":
manifest["publication_control"]["caller_claimed_release_ready"] = True
else: # pragma: no cover - parametrization is closed above
raise AssertionError(mutation)
_rehash(manifest)
with pytest.raises(Rc06ProtocolError):
validate_manifest(manifest)
@@ -26,6 +26,7 @@ from yuqei_ai_platform_api.repository import SqlAlchemyAiPlatformRepository
from yuqei_contract_api.config import ContractApiSettings, check_runtime_config
from yuqei_contract_api.main import create_app
from yuqei_contract_api.main import _is_rc06_internal_path
import yuqei_contract_api.rc06_publication as rc06_publication_module
from yuqei_contract_api.persistence.models import (
Rc06PublicationSagaModel,
Rc06TemplatePublicationItemModel,
@@ -77,6 +78,31 @@ class LocalAiClient:
return self.service.compensate(transaction_id, capability).model_dump(mode="json")
def test_postgresql_authority_clock_uses_wall_clock_not_transaction_timestamp():
observed = datetime.now(UTC)
class _Dialect:
name = "postgresql"
class _Bind:
dialect = _Dialect()
class _Session:
statement = None
def get_bind(self):
return _Bind()
def scalar(self, statement):
self.statement = str(statement)
return observed
session = _Session()
assert rc06_publication_module._database_utc_now(session) == observed
assert "clock_timestamp()" in session.statement
assert "current_timestamp" not in session.statement.lower()
def _ai_service(repository):
return Rc06PromptPublicationService(
repository._session_factory,
@@ -138,6 +164,7 @@ def _request(transaction_id="rc06-saga-transaction-0001"):
batch_manifest_raw_sha256="d" * 64,
batch_manifest_payload_sha256="b" * 64,
approval_receipt_sha256="c" * 64,
authority_expires_at=datetime.now(UTC) + timedelta(hours=1),
assets=contract_source_assets(),
prompt_modules=modules,
runtime_identities=identities,
@@ -170,6 +197,7 @@ def test_sqlite_saga_commits_exactly_74_items_and_14_runtime_snapshots(tmp_path)
assert committed.ai_committed_at is not None
assert committed.contract_committed_at is not None
assert committed.committed_at is not None
assert committed.committed_at == committed.contract_committed_at
assert sum(item["kind"] == "asset" for item in committed.item_results) == 60
assert sum(item["kind"] == "prompt_module" for item in committed.item_results) == 14
assert coordinator.commit(request.transaction_id) == committed
@@ -217,6 +245,47 @@ def test_lost_ai_commit_response_is_reconciled_by_transaction_status(tmp_path):
contract.engine.dispose()
def test_authority_expiry_at_contract_boundary_rolls_back_all_contract_writes(
tmp_path,
monkeypatch,
):
contract, ai = _repositories(tmp_path)
try:
base = datetime.now(UTC).replace(microsecond=0)
expiry = base + timedelta(seconds=10)
observed = iter(
(base, base + timedelta(seconds=1), base + timedelta(seconds=2), expiry)
)
monkeypatch.setattr(
rc06_publication_module,
"_database_utc_now",
lambda _session: next(observed),
)
coordinator = _coordinator(
repository=contract,
ai_client=LocalAiClient(_ai_service(ai)),
authorization_verifier=TestAuthorizationVerifier(),
)
request = _request("rc06-saga-expired-at-contract-boundary").model_copy(
update={"authority_expires_at": expiry}
)
assert coordinator.prepare(request, tenant_id="default").state == "prepared"
with pytest.raises(Rc06Conflict, match="commit boundary"):
coordinator.commit(request.transaction_id, tenant_id="default")
assert coordinator.status(request.transaction_id).state == "compensated"
with contract._session_factory() as session:
assert session.scalars(select(TemplateLifecycleEventModel)).all() == []
assert session.scalars(select(TemplatePublishedSnapshotModel)).all() == []
items = session.scalars(select(Rc06TemplatePublicationItemModel)).all()
assert len(items) == 60
assert all(item.state == "compensated" for item in items)
finally:
ai._engine.dispose()
contract.engine.dispose()
def test_readback_accepts_historical_snapshot_but_requires_current_event_binding(tmp_path):
contract, ai = _repositories(tmp_path)
try:
@@ -584,6 +653,9 @@ def test_transient_ai_status_failure_after_both_commits_never_rolls_back(tmp_pat
pending = coordinator.status(request.transaction_id)
assert pending.state == "contract_committed"
assert pending.no_go is True
assert pending.write_count == 74
assert pending.runtime_snapshot_count == 14
assert pending.committed_at == pending.contract_committed_at
assert service.status(request.transaction_id).state == "committed"
with contract._session_factory() as session:
assert len(session.scalars(select(TemplateLifecycleEventModel)).all()) == 60
@@ -591,8 +663,11 @@ def test_transient_ai_status_failure_after_both_commits_never_rolls_back(tmp_pat
assert len(snapshots) == 60
assert all(item.deleted_at is None for item in snapshots)
committed = coordinator.commit(request.transaction_id)
irreversible_capture_at = pending.committed_at
committed = coordinator.recover_after_contract_commit(request.transaction_id)
assert committed.state == "committed"
assert committed.committed_at == irreversible_capture_at
assert committed.committed_at == committed.contract_committed_at
with contract._session_factory() as session:
assert len(session.scalars(select(TemplateLifecycleEventModel)).all()) == 60
assert len(session.scalars(select(TemplatePublishedSnapshotModel)).all()) == 60
@@ -1085,6 +1160,18 @@ def test_contract_rejects_forged_sealed_state_markers(tmp_path):
expected_state="sealed",
)
wrong_authority_expiry = deepcopy(valid)
wrong_authority_expiry["authority_expires_at"] = (
request.authority_expires_at + timedelta(seconds=1)
).isoformat()
with pytest.raises(Rc06PublicationError, match="incomplete"):
Rc06PublicationCoordinator._assert_ai_state(
wrong_authority_expiry,
request.transaction_id,
frozen,
expected_state="sealed",
)
wrong_no_go = deepcopy(valid)
wrong_no_go["no_go"] = True
with pytest.raises(Rc06PublicationError, match="incomplete"):
@@ -0,0 +1,171 @@
from __future__ import annotations
from copy import deepcopy
from yuqei_contract_api.schema_check import (
RC06_COLUMNS_BY_TABLE,
RC06_CONSTRAINTS_BY_TABLE,
_check_rc06_schema,
)
class _Inspector:
def __init__(self) -> None:
self.checks = {
table: [
{
"name": name,
"sqltext": (
f"{column} IN ("
+ ", ".join(f"'{value}'" for value in sorted(values))
+ ")"
),
}
for name, (column, values) in constraints["check"].items()
]
for table, constraints in RC06_CONSTRAINTS_BY_TABLE.items()
}
self.foreign = {
table: [
{
"name": name,
"constrained_columns": list(columns),
"referred_table": referred_table,
"referred_columns": list(referred_columns),
"referred_schema": None if referred_schema == "__current__" else referred_schema,
"options": {"ondelete": ondelete},
}
for name, (
columns,
referred_table,
referred_columns,
referred_schema,
ondelete,
) in constraints["foreign"].items()
]
for table, constraints in RC06_CONSTRAINTS_BY_TABLE.items()
}
def get_columns(self, table, schema=None):
return [{"name": name} for name in RC06_COLUMNS_BY_TABLE[table]]
def get_unique_constraints(self, table, schema=None):
return [
{"name": name, "column_names": list(columns)}
for name, columns in RC06_CONSTRAINTS_BY_TABLE[table]["unique"].items()
]
def get_pk_constraint(self, table, schema=None):
return {
"constrained_columns": list(
RC06_CONSTRAINTS_BY_TABLE[table]["primary"]["primary_key"]
)
}
def get_check_constraints(self, table, schema=None):
return deepcopy(self.checks[table])
def get_foreign_keys(self, table, schema=None):
return deepcopy(self.foreign[table])
def test_rc06_schema_check_rejects_check_literal_and_ondelete_drift() -> None:
tables = set(RC06_COLUMNS_BY_TABLE)
inspector = _Inspector()
assert _check_rc06_schema(inspector, tables, dialect="postgresql").status == "ok"
pg_deparsed = _Inspector()
pg_saga_check = next(
item
for item in pg_deparsed.checks["rc06_publication_sagas"]
if item["name"] == "ck_rc06_publication_sagas_state"
)
values = RC06_CONSTRAINTS_BY_TABLE["rc06_publication_sagas"]["check"][
"ck_rc06_publication_sagas_state"
][1]
pg_saga_check["sqltext"] = (
"((state)::text = ANY ((ARRAY["
+ ", ".join(f"'{value}'::character varying" for value in sorted(values))
+ "])::text[]))"
)
assert _check_rc06_schema(pg_deparsed, tables, dialect="postgresql").status == "ok"
fake_pg_any = _Inspector()
fake_saga_check = next(
item
for item in fake_pg_any.checks["rc06_publication_sagas"]
if item["name"] == "ck_rc06_publication_sagas_state"
)
fake_saga_check["sqltext"] = (
"((state IS NOT NULL) AND ('preparing'::text = ANY (ARRAY["
+ ", ".join(f"'{value}'::text" for value in sorted(values))
+ "])))"
)
bypass = _check_rc06_schema(fake_pg_any, tables, dialect="postgresql")
assert bypass.status == "error"
assert "ck_rc06_publication_sagas_state" in bypass.details[
"missing_constraints"
]
expression_in_literal_list = _Inspector()
expression_check = next(
item
for item in expression_in_literal_list.checks["rc06_publication_sagas"]
if item["name"] == "ck_rc06_publication_sagas_state"
)
expression_check["sqltext"] = expression_check["sqltext"].replace(
")", ", state)", 1
)
bypass = _check_rc06_schema(
expression_in_literal_list,
tables,
dialect="postgresql",
)
assert bypass.status == "error"
assert "ck_rc06_publication_sagas_state" in bypass.details[
"missing_constraints"
]
always_true = _Inspector()
always_true_check = next(
item
for item in always_true.checks["rc06_publication_sagas"]
if item["name"] == "ck_rc06_publication_sagas_state"
)
always_true_check["sqltext"] += " OR 1=1"
bypass = _check_rc06_schema(always_true, tables, dialect="sqlite")
assert bypass.status == "error"
assert "ck_rc06_publication_sagas_state" in bypass.details[
"missing_constraints"
]
saga_check = next(
item
for item in inspector.checks["rc06_publication_sagas"]
if item["name"] == "ck_rc06_publication_sagas_state"
)
saga_check["sqltext"] = saga_check["sqltext"].replace("'committed', ", "")
for table in ("rc06_publication_sagas", "rc06_apply_challenge_uses"):
inspector.foreign[table][0]["options"]["ondelete"] = "CASCADE"
inspector.foreign[table][0]["referred_schema"] = "attacker_shadow"
result = _check_rc06_schema(inspector, tables, dialect="postgresql")
assert result.status == "error"
assert "ck_rc06_publication_sagas_state" in result.details["missing_constraints"]
assert "fk_rc06_publication_saga_authority_transaction" in result.details[
"missing_constraints"
]
assert "fk_rc06_apply_challenge_transaction" in result.details["missing_constraints"]
missing_pk = _Inspector()
missing_pk.get_pk_constraint = lambda _table, schema=None: {
"constrained_columns": []
}
result = _check_rc06_schema(missing_pk, tables, dialect="postgresql")
assert result.status == "error"
assert "rc06_online_publication_transactions.primary_key" in result.details[
"missing_constraints"
]
assert "rc06_apply_challenge_uses.primary_key" in result.details[
"missing_constraints"
]