feat(release): add fail-closed RC06 apply protocol

This commit is contained in:
2026-10-06 18:11:09 +08:00
parent 77e60366de
commit b90fbd01db
3 changed files with 2106 additions and 5 deletions
File diff suppressed because it is too large Load Diff
@@ -19,6 +19,7 @@ import stat
import subprocess
import sys
import tempfile
import types
from datetime import datetime, timedelta
from pathlib import Path, PurePath
from typing import Any, Iterable, Mapping
@@ -28,7 +29,7 @@ from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
SCHEMA_VERSION = "yuqei.rc06-publication-batch.v3"
SCHEMA_VERSION = "yuqei.rc06-publication-batch.v4"
DISPOSITION_SCHEMA_VERSION = "yuqei.rc06-golden-dispositions.v2"
IMAGE_SCHEMA_VERSION = "yuqei.rc06-image-identities.v1"
APPROVAL_SCHEMA_VERSION = "yuqei.rc06-publication-batch-approval.v5"
@@ -74,7 +75,7 @@ RC05_CAMPAIGN_SCHEMA_VERSION = "multiscene-call-campaign.v1"
BLOCKED_CONTROLS = (
"single_reviewer_batch_approval_receipt",
"system_generated_item_apply_receipts_and_online_snapshots",
"runtime_executable_prompt_snapshots_1_drafting_plus_8_review",
"runtime_executable_prompt_snapshots_1_drafting_plus_13_review",
)
# Only the stable key owner identity is frozen in source. A title, organization,
# and authorization scope are human assertions for one exact approval and must
@@ -106,6 +107,7 @@ EXPECTED_DRAFTING_MODULE_COUNT = 6
EXPECTED_REVIEW_MODULE_COUNT = 8
EXPECTED_MODULE_COUNT = EXPECTED_DRAFTING_MODULE_COUNT + EXPECTED_REVIEW_MODULE_COUNT
EXPECTED_DEFAULT_PROMPT_COUNT = 46
EXPECTED_RUNTIME_PROMPT_COUNT = 14
EXPECTED_GOLDEN_COUNT = 31
EXPECTED_GOLDEN_GENERATION_COUNT = 23
EXPECTED_GOLDEN_CLAUSE_COUNT = 8
@@ -149,6 +151,21 @@ DEFAULT_PROMPT_PATH = (
"yuqei-ai-platform/services/ai-platform-api/src/yuqei_ai_platform_api/"
"prompt_assets/v1_phase15/default-prompt-assets.json"
)
RUNTIME_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_RUNTIME_MODULE_ORDER = (
"draft_intake",
"template_match",
"missing_facts_questions",
"clause_assembly_plan",
"draft_consistency_check",
"candidate_revision_explanation",
)
GOLDEN_INVENTORY_PATH = (
"yuqei-ai-platform/services/ai-platform-api/src/yuqei_ai_platform_api/"
"golden_assets/v1_phase13_legal_knowledge_template_library/inventories/"
@@ -156,6 +173,8 @@ GOLDEN_INVENTORY_PATH = (
)
FROZEN_IMPLEMENTATION_PATHS = (
"yuqei-infra/scripts/build-rc06-publication-batch.py",
"yuqei-infra/scripts/apply-rc06-publication-batch.py",
"yuqei-infra/scripts/build-scene-source-legal-review.py",
"yuqei-infra/scripts/rc06-single-reviewer-signing.py",
"yuqei-infra/scripts/rc06-release-trust-bootstrap.py",
"yuqei-contract-platform/services/contract-api/src/yuqei_contract_api/contract_templates.py",
@@ -172,6 +191,9 @@ CONTRACT_RUNTIME_PACKAGE_ROOT = (
"yuqei-contract-platform/services/contract-api/src/yuqei_contract_api"
)
SDK_RUNTIME_PACKAGE_ROOT = "yuqei-ai-sdk-python/src/yuqei_sdk"
AI_RUNTIME_PACKAGE_ROOT = (
"yuqei-ai-platform/services/ai-platform-api/src/yuqei_ai_platform_api"
)
DISPOSITION_STATUSES = {
"named_legal_approval",
@@ -493,7 +515,11 @@ def _inventory_source_paths(repo: Path, commit: str | None = None) -> list[str]:
raise PublicationBatchError(f"template source must not be a symbolic link: {path}")
if path.is_file() and path.suffix.lower() in {".json", ".md"}:
paths.append(_relative_source_path(repo, path))
for package_root in (CONTRACT_RUNTIME_PACKAGE_ROOT, SDK_RUNTIME_PACKAGE_ROOT):
for package_root in (
CONTRACT_RUNTIME_PACKAGE_ROOT,
SDK_RUNTIME_PACKAGE_ROOT,
AI_RUNTIME_PACKAGE_ROOT,
):
root = repo / package_root
_assert_no_symlink_components(root, f"runtime package {package_root}")
paths.extend(
@@ -513,6 +539,7 @@ def _inventory_source_paths(repo: Path, commit: str | None = None) -> list[str]:
ASSET_ROOT,
CONTRACT_RUNTIME_PACKAGE_ROOT,
SDK_RUNTIME_PACKAGE_ROOT,
AI_RUNTIME_PACKAGE_ROOT,
)
)
for relative in listing.splitlines():
@@ -523,6 +550,7 @@ def _inventory_source_paths(repo: Path, commit: str | None = None) -> list[str]:
if (
relative.startswith(f"{CONTRACT_RUNTIME_PACKAGE_ROOT}/")
or relative.startswith(f"{SDK_RUNTIME_PACKAGE_ROOT}/")
or relative.startswith(f"{AI_RUNTIME_PACKAGE_ROOT}/")
):
paths.append(relative)
paths.extend(FROZEN_IMPLEMENTATION_PATHS)
@@ -713,6 +741,130 @@ def _load_inventory_symbols(repo: Path) -> tuple[Any, Any, Any, Any, Any]:
pass
def _load_executable_prompt_dependencies(repo: Path) -> list[dict[str, Any]]:
"""Render the candidate's actual runtime Prompt content for 14 identities."""
paths = (
repo / "yuqei-ai-platform/services/ai-platform-api/src",
repo / "yuqei-ai-sdk-python/src",
)
inserted: list[str] = []
for path in reversed(paths):
value = str(path)
if value not in sys.path:
sys.path.insert(0, value)
inserted.append(value)
prefixes = ("yuqei_ai_platform_api", "yuqei_sdk")
previous = {
name: module
for name, module in list(sys.modules.items())
if any(name == prefix or name.startswith(f"{prefix}.") for prefix in prefixes)
}
for name in previous:
sys.modules.pop(name, None)
try:
from yuqei_ai_platform_api import prompts as prompt_routes # type: ignore
from yuqei_ai_platform_api.contract_drafting import ( # type: ignore
DRAFT_PROMPT_CONTENT,
DRAFT_PROMPT_NAMES,
DRAFT_PROMPT_VERSION,
)
from yuqei_ai_platform_api.drafting_prompt_modules import ( # type: ignore
drafting_module_evidence,
)
from yuqei_ai_platform_api.packaged_prompt_assets_import import ( # type: ignore
DEFAULT_VERSION,
load_packaged_prompt_assets,
)
from yuqei_ai_platform_api.review_scene_prompts import ( # type: ignore
review_module_evidence,
scene_review_template,
)
drafting_evidence = drafting_module_evidence(DRAFT_PROMPT_CONTENT)
results: list[dict[str, Any]] = [
{
"kind": "composed_drafting",
"runtime_key": DRAFT_PROMPT_NAMES[0],
"version": DRAFT_PROMPT_VERSION,
"content_sha256": _sha256_bytes(DRAFT_PROMPT_CONTENT.encode("utf-8")),
"publication_dependency": "six_module_composition",
"publication_module_dependencies": drafting_evidence,
}
]
review_routes = sorted(
item["key"]
for item in json.loads((repo / REVIEW_MODULE_PATH).read_text(encoding="utf-8"))[
"modules"
]
)
original_candidates = prompt_routes.contract_review_prompt_candidates
try:
for route in review_routes:
prompt_routes.contract_review_prompt_candidates = (
lambda *_args, _route=route, **_kwargs: [_route]
)
request = types.SimpleNamespace(
contract_type="rc06_runtime_snapshot",
scene_template_id="rc06-runtime-snapshot",
review_profile="scene_evaluation_v4",
)
template = scene_review_template(request)
module_evidence = review_module_evidence(template)
if len(module_evidence) != 1 or module_evidence[0]["key"] != route:
raise PublicationBatchError(
f"runtime review Prompt does not contain its exact module: {route}"
)
results.append(
{
"kind": "new_review_module",
"runtime_key": route,
"version": template.version,
"content_sha256": template.content_hash,
"publication_dependency": "published_module",
"publication_module_dependencies": module_evidence,
}
)
finally:
prompt_routes.contract_review_prompt_candidates = original_candidates
packaged, _, duplicates = load_packaged_prompt_assets(repo / DEFAULT_PROMPT_PATH)
if duplicates:
raise PublicationBatchError("default Prompt runtime inventory contains duplicates")
by_key = {item.prompt_key: item for item in packaged}
for key in RUNTIME_EXISTING_DOMAIN_PROMPTS:
if key not in by_key:
raise PublicationBatchError(f"existing runtime domain Prompt is absent: {key}")
results.append(
{
"kind": "existing_domain_prompt",
"runtime_key": key,
"version": DEFAULT_VERSION,
"content_sha256": _sha256_bytes(by_key[key].content.encode("utf-8")),
"publication_dependency": "preexisting_domain_prompt",
"publication_module_dependencies": [],
}
)
results.sort(key=lambda item: (item["kind"], item["runtime_key"]))
if len(results) != EXPECTED_RUNTIME_PROMPT_COUNT or len(
{item["runtime_key"] for item in results}
) != EXPECTED_RUNTIME_PROMPT_COUNT:
raise PublicationBatchError("runtime Prompt dependency inventory must contain 14 identities")
return results
except (ImportError, OSError, ValueError, KeyError, TypeError) as exc:
raise PublicationBatchError(f"cannot render executable runtime Prompts: {exc}") from exc
finally:
for name in list(sys.modules):
if any(name == prefix or name.startswith(f"{prefix}.") for prefix in prefixes):
sys.modules.pop(name, None)
sys.modules.update(previous)
for value in inserted:
try:
sys.path.remove(value)
except ValueError: # pragma: no cover
pass
def _asset_hash_projection(asset: Mapping[str, Any]) -> dict[str, Any]:
return {
"id": asset["template_id"],
@@ -911,6 +1063,7 @@ def load_runtime_inventory(
return {
"assets": assets,
"prompt_modules": modules,
"runtime_prompt_dependencies": _load_executable_prompt_dependencies(repo),
"default_prompt_boundary": {
"included_in_publication_batch": False,
"count": len(default_inventory),
@@ -2103,6 +2256,12 @@ def assemble_manifest(
"items": inventory["prompt_modules"],
"inventory_sha256": _sha256_json(inventory["prompt_modules"]),
},
"runtime_prompt_dependencies": {
"publication_item_count": EXPECTED_ASSET_COUNT + EXPECTED_MODULE_COUNT,
"count": len(inventory["runtime_prompt_dependencies"]),
"items": inventory["runtime_prompt_dependencies"],
"inventory_sha256": _sha256_json(inventory["runtime_prompt_dependencies"]),
},
"default_prompt_asset_boundary": inventory["default_prompt_boundary"],
"golden_dispositions": {
"count": len(dispositions),
@@ -2136,7 +2295,7 @@ def assemble_manifest(
"No reviewer approval or apply evidence is embedded in this offline manifest.",
"One signed batch approval must bind all 60 assets, 14 Prompt modules, and 31 dispositions.",
"The apply service must emit 74 machine-verifiable item results and online hashes.",
"One drafting plus eight review executable runtime Prompt snapshots are not implemented and remain blocking.",
"One composed drafting plus thirteen review runtime Prompt snapshots are not implemented and remain blocking.",
],
}
_assert_no_secret_material(manifest)
@@ -2209,7 +2368,8 @@ def validate_manifest(payload: Any) -> dict[str, Any]:
"publication batch",
(
"schema_version", "candidate", "asset_batch", "prompt_module_batch",
"default_prompt_asset_boundary", "golden_dispositions", "publication_control",
"runtime_prompt_dependencies", "default_prompt_asset_boundary",
"golden_dispositions", "publication_control",
"side_effects", "claims_not_made", "manifest_payload_sha256",
),
)
@@ -2336,6 +2496,90 @@ def validate_manifest(payload: Any) -> dict[str, Any]:
if _sha256_json(modules["items"]) != modules["inventory_sha256"]:
raise PublicationBatchError("Prompt module inventory hash drift")
runtime = _require_keys(
manifest["runtime_prompt_dependencies"],
"runtime Prompt dependencies",
("publication_item_count", "count", "items", "inventory_sha256"),
)
if (
runtime["publication_item_count"] != EXPECTED_ASSET_COUNT + EXPECTED_MODULE_COUNT
or runtime["count"] != EXPECTED_RUNTIME_PROMPT_COUNT
or not isinstance(runtime["items"], list)
or len(runtime["items"]) != EXPECTED_RUNTIME_PROMPT_COUNT
):
raise PublicationBatchError(
"runtime Prompt dependencies must distinguish 74 publication items from 14 snapshots"
)
runtime_keys: set[str] = set()
runtime_kinds: list[str] = []
runtime_keys_by_kind: dict[str, set[str]] = {}
publication_modules = {
item["key"]: {
"key": item["key"],
"version": item["version"],
"content_sha256": item["content_sha256"],
}
for item in modules["items"]
}
for item in runtime["items"]:
row = _require_keys(
item,
"runtime Prompt dependency",
(
"kind",
"runtime_key",
"version",
"content_sha256",
"publication_dependency",
"publication_module_dependencies",
),
)
key = _require_string(row["runtime_key"], "runtime Prompt key", max_length=128)
_require_string(row["version"], f"runtime Prompt {key} version", max_length=128)
_require_sha(row["content_sha256"], f"runtime Prompt {key} content SHA-256")
if key in runtime_keys:
raise PublicationBatchError("runtime Prompt dependency keys must be unique")
runtime_keys.add(key)
runtime_kinds.append(row["kind"])
runtime_keys_by_kind.setdefault(row["kind"], set()).add(key)
expected_dependency = {
"composed_drafting": "six_module_composition",
"new_review_module": "published_module",
"existing_domain_prompt": "preexisting_domain_prompt",
}.get(row["kind"])
if row["publication_dependency"] != expected_dependency:
raise PublicationBatchError(f"runtime Prompt dependency kind drift: {key}")
dependency_rows = row["publication_module_dependencies"]
if not isinstance(dependency_rows, list):
raise PublicationBatchError(
f"runtime Prompt module dependencies must be a list: {key}"
)
if row["kind"] == "composed_drafting":
expected_module_keys = list(DRAFTING_RUNTIME_MODULE_ORDER)
elif row["kind"] == "new_review_module":
expected_module_keys = [key]
else:
expected_module_keys = []
expected_dependency_rows = [
publication_modules[module_key] for module_key in expected_module_keys
]
if dependency_rows != expected_dependency_rows:
raise PublicationBatchError(
f"runtime Prompt is not bound to exact publication module identities: {key}"
)
if (
runtime_kinds.count("composed_drafting") != 1
or runtime_kinds.count("new_review_module") != 8
or runtime_kinds.count("existing_domain_prompt") != 5
or 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")
!= set(RUNTIME_EXISTING_DOMAIN_PROMPTS)
):
raise PublicationBatchError("runtime Prompt dependency 1+8+5 inventory drift")
if _sha256_json(runtime["items"]) != runtime["inventory_sha256"]:
raise PublicationBatchError("runtime Prompt dependency inventory hash drift")
boundary = manifest["default_prompt_asset_boundary"]
required_boundary = {
"included_in_publication_batch", "count", "source_path", "source_raw_sha256",
@@ -0,0 +1,844 @@
from __future__ import annotations
import base64
import copy
import importlib.util
import json
import sqlite3
import sys
import threading
from contextlib import contextmanager
from datetime import UTC, datetime, timedelta
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
import pytest
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
REPO_ROOT = Path(__file__).resolve().parents[2]
APPLY_SCRIPT = REPO_ROOT / "yuqei-infra" / "scripts" / "apply-rc06-publication-batch.py"
FIXTURE_SCRIPT = REPO_ROOT / "yuqei-infra" / "tests" / "test_rc06_publication_batch.py"
def _load(path: Path, name: str):
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
apply = _load(APPLY_SCRIPT, "apply_rc06_publication_batch")
fixtures = _load(FIXTURE_SCRIPT, "rc06_publication_batch_test_fixtures")
def _raw_public(key) -> bytes:
return key.public_bytes(serialization.Encoding.Raw, serialization.PublicFormat.Raw)
def _sign_envelope(private_key, *, schema: str, id_prefix: str, domain: bytes, payload: dict) -> dict:
signature = private_key.sign(domain + apply.batch._canonical(payload))
return {
"schema_version": schema,
"receipt_id": id_prefix
+ apply.batch._sha256_bytes(apply.batch._canonical(payload) + signature)[:24],
"payload": payload,
"signature": base64.b64encode(signature).decode("ascii"),
}
class _FakeCanonicalLegalReviewValidator:
"""Test double for build-scene-source-legal-review.py; never production evidence."""
@staticmethod
def verify_request(request: dict, _repo: Path) -> dict:
projection = {key: value for key, value in request.items() if key != "request_sha256"}
if request.get("request_sha256") != apply.batch._sha256_json(projection):
raise ValueError("review request embedded hash drift")
if len(request.get("occurrences", [])) != 43:
raise ValueError("review request must contain 43 occurrences")
return request
@staticmethod
def verify_signed_decision(
request: dict, decision: dict, _keyring: dict, *, checked_at: str
) -> dict:
if decision.get("request_sha256") != request["request_sha256"]:
raise ValueError("decision request binding drift")
now = datetime.fromisoformat(checked_at)
expires_at = datetime.fromisoformat(decision["expires_at"])
if now > expires_at:
raise ValueError("decision expired")
expected = {
item["occurrence_id"]: item["source_record_sha256"]
for item in request["occurrences"]
}
items = decision.get("items")
if not isinstance(items, list) or len(items) != 43:
raise ValueError("decision must contain 43 items")
for item in items:
if item.get("source_record_sha256") != expected.get(item.get("occurrence_id")):
raise ValueError("source record hash mismatch")
if (
item.get("current_effectiveness_verified") is not True
or item.get("transaction_applicability_verified") is not True
or len(item.get("rationale", "")) < 20
or not item.get("authoritative_status_url", "").startswith("https://")
):
raise ValueError("legal source decision is incomplete")
return {
"status": "verified_qualified_human_review",
"candidate_commit": request["candidate_commit"],
"request_sha256": request["request_sha256"],
"reviewer_name": decision["reviewer"]["name"],
"reviewed_occurrence_count": 43,
"all_current_effectiveness_verified": True,
"all_transaction_applicability_verified": True,
"expires_at": decision["expires_at"],
"authorizes_publication_by_itself": False,
}
def _material(monkeypatch: pytest.MonkeyPatch, now: datetime) -> dict:
inventory = apply.batch.load_runtime_inventory(REPO_ROOT)
manifest = fixtures._manifest(inventory, resolved=True)
manifest_raw = apply.batch._sha256_bytes(apply.batch._canonical(manifest))
root_private = Ed25519PrivateKey.generate()
reviewer_private = Ed25519PrivateKey.generate()
apply_private = Ed25519PrivateKey.generate()
online_private = Ed25519PrivateKey.generate()
monkeypatch.setattr(
apply.batch,
"TRUSTED_APPROVAL_ROOT_PUBLIC_KEY_SHA256",
apply.batch._sha256_bytes(_raw_public(root_private.public_key())),
)
monkeypatch.setattr(apply.batch, "FROZEN_APPROVAL_KEYRING_EPOCH", 1)
monkeypatch.setattr(
apply.batch,
"TRUSTED_APPLY_SERVICE_PUBLIC_KEY_SHA256",
apply.batch._sha256_bytes(_raw_public(apply_private.public_key())),
)
monkeypatch.setattr(
apply.batch,
"TRUSTED_ONLINE_APPLY_SERVICE_PUBLIC_KEY_SHA256",
apply.batch._sha256_bytes(_raw_public(online_private.public_key())),
)
monkeypatch.setattr(
apply.batch,
"TRUSTED_ONLINE_APPLY_ENVIRONMENT_IDS",
("rc06-test",),
)
monkeypatch.setattr(
apply,
"_load_scene_legal_review_module",
lambda: _FakeCanonicalLegalReviewValidator,
)
reviewer_pem = reviewer_private.public_key().public_bytes(
serialization.Encoding.PEM,
serialization.PublicFormat.SubjectPublicKeyInfo,
).decode("ascii")
keyring_unsigned = {
"schema_version": apply.batch.APPROVAL_KEYRING_SCHEMA_VERSION,
"root_key_id": apply.batch.TRUSTED_APPROVAL_ROOT_KEY_ID,
"epoch": 1,
"issued_at": (now - timedelta(days=2)).isoformat(timespec="seconds"),
"expires_at": (now + timedelta(days=3)).isoformat(timespec="seconds"),
"authorized_batch_manifest_raw_sha256": manifest_raw,
"keys": [
{
"key_id": "rc06-release-owner-test",
"algorithm": "Ed25519",
"public_key_pem": reviewer_pem,
**apply.batch.SOLE_RELEASE_REVIEWER,
"not_before": (now - timedelta(days=2)).isoformat(timespec="seconds"),
"not_after": (now + timedelta(days=3)).isoformat(timespec="seconds"),
"revoked": False,
}
],
}
keyring = {
**keyring_unsigned,
"keyring_payload_sha256": apply.batch._sha256_json(keyring_unsigned),
"root_signature_base64": base64.b64encode(
root_private.sign(
apply.batch.APPROVAL_KEYRING_SIGNATURE_DOMAIN
+ apply.batch._canonical(keyring_unsigned)
)
).decode("ascii"),
}
approval_payload = apply.batch.build_approval_payload(
manifest,
manifest_raw_sha256=manifest_raw,
key_id="rc06-release-owner-test",
approval_keyring_epoch=1,
approval_keyring_payload_sha256=keyring["keyring_payload_sha256"],
approved_at=now - timedelta(hours=2),
expires_at=now + timedelta(days=2),
approver={
**apply.batch.SOLE_RELEASE_REVIEWER,
"role": "测试发布负责人",
"organization": "测试组织(非真实法律服务主体)",
"authorization_scope": "仅批准本临时 SQLite/HTTP 测试候选,不授权生产发布",
"human_confirmed": True,
},
)
approval_signature = reviewer_private.sign(
apply.batch.APPROVAL_SIGNATURE_DOMAIN + apply.batch._canonical(approval_payload)
)
approval = {
"schema_version": apply.batch.APPROVAL_SCHEMA_VERSION,
"approval_id": "rc06-batch-approval-"
+ apply.batch._sha256_bytes(
apply.batch._canonical(approval_payload) + approval_signature
)[:24],
"payload": approval_payload,
"signature": base64.b64encode(approval_signature).decode("ascii"),
}
approval_raw = apply.batch._sha256_bytes(apply.batch._canonical(approval))
occurrences = []
for asset in sorted(
manifest["asset_batch"]["items"], key=lambda item: item["template_id"]
):
references = ((asset.get("input_schema") or {}).get("scene") or {}).get(
"source_references", []
)
for index, source in enumerate(references):
occurrences.append(
{
"occurrence_id": f"fixture:{asset['template_id']}:{index}",
"scene_id": f"fixture-scene-{len(occurrences):02d}",
"template_id": asset["template_id"],
"source_key": f"source-{index}",
"source_pack_path": "fixture-only/not-production.json",
"source_record_sha256": apply.batch._sha256_json(source),
"source": source,
"required_human_decisions": [
"current_effectiveness_verified",
"transaction_applicability_verified",
],
}
)
assert len(occurrences) == 43
legal_request = {
"schema_version": "yuqei.scene-source-legal-review-request.v1",
"status": "pending_qualified_human_review",
"candidate_commit": manifest["candidate"]["commit"],
"created_at": (now - timedelta(hours=2)).isoformat(timespec="seconds"),
"scope": {
"scene_count": 30,
"reference_occurrence_count": 43,
"distinct_source_record_count": 8,
"distinct_url_count": 7,
},
"source_files": [],
"occurrences": occurrences,
"guardrails": {
"not_legal_approval": True,
"authorizes_publication": False,
"ai_must_not_supply_human_decisions": True,
"all_occurrences_require_qualified_human_review": True,
},
}
legal_request["request_sha256"] = apply.batch._sha256_json(legal_request)
legal_decision = {
"schema_version": "yuqei.scene-source-legal-review-decision.v1",
"request_sha256": legal_request["request_sha256"],
"candidate_commit": manifest["candidate"]["commit"],
"reviewed_at": (now - timedelta(hours=1)).isoformat(timespec="seconds"),
"expires_at": (now + timedelta(days=1)).isoformat(timespec="seconds"),
"reviewer": {
"name": "测试法源复核人(非真实身份)",
"organization": "测试组织(非真实法律服务主体)",
"qualification": "测试资格声明(不代表真实法律资质)",
"credential_reference": "TEST-FIXTURE-NOT-A-CREDENTIAL",
},
"items": [
{
"occurrence_id": item["occurrence_id"],
"source_record_sha256": item["source_record_sha256"],
"current_effectiveness_verified": True,
"transaction_applicability_verified": True,
"checked_at": (now - timedelta(hours=1)).isoformat(timespec="seconds"),
"authoritative_status_url": "https://example.gov.cn/test-fixture-status",
"rationale": "仅用于验证 fail-closed 协议的合成判断,不构成真实法源复核结论。",
}
for item in occurrences
],
"signature": {
"algorithm": "Ed25519",
"key_id": "fixture-legal-reviewer",
"value_base64": base64.b64encode(b"fixture-signature").decode("ascii"),
},
}
legal_keyring = {
"schema_version": "yuqei.scene-source-legal-review-keyring.v1",
"keys": [{"key_id": "fixture-legal-reviewer", "status": "trusted"}],
}
return {
"now": now,
"manifest": manifest,
"manifest_raw": manifest_raw,
"approval": approval,
"approval_raw": approval_raw,
"keyring": keyring,
"legal_request": legal_request,
"legal_decision": legal_decision,
"legal_keyring": legal_keyring,
"root_private": root_private,
"apply_private": apply_private,
"online_private": online_private,
}
class FakePublicationService:
def __init__(
self,
database: Path,
material: dict,
*,
fail_after: int | None = None,
tamper_status: bool = False,
tamper_runtime_status: bool = False,
) -> None:
self.database = database
self.material = material
self.fail_after = fail_after
self.tamper_status = tamper_status
self.tamper_runtime_status = tamper_runtime_status
self.apply_calls = 0
with sqlite3.connect(database) as connection:
connection.executescript(
"""
CREATE TABLE transactions (
transaction_id TEXT PRIMARY KEY,
idempotency_key TEXT NOT NULL UNIQUE,
state TEXT NOT NULL,
legal_review_sha256 TEXT NOT NULL,
response_json TEXT
);
CREATE TABLE publication_items (
transaction_id TEXT NOT NULL,
kind TEXT NOT NULL,
item_id TEXT NOT NULL,
content_sha256 TEXT NOT NULL,
PRIMARY KEY(transaction_id, kind, item_id)
);
CREATE TABLE runtime_snapshots (
transaction_id TEXT NOT NULL,
kind TEXT NOT NULL,
runtime_key TEXT NOT NULL,
content_sha256 TEXT NOT NULL,
status TEXT NOT NULL,
PRIMARY KEY(transaction_id, runtime_key)
);
"""
)
def _receipt_pair(self, request: dict) -> tuple[dict, dict]:
material = self.material
now = material["now"]
challenge_payload = {
"schema_version": apply.batch.APPLY_CHALLENGE_PAYLOAD_SCHEMA_VERSION,
"nonce_base64": base64.b64encode(b"q" * 32).decode("ascii"),
"candidate_commit": request["manifest"]["candidate"]["commit"],
"candidate_tag": request["manifest"]["candidate"]["tag"],
"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"],
"operation": apply.batch.APPLY_OPERATION,
"environment_id": request["environment_id"],
"actor": {
"type": "system",
"component": apply.batch.TRUSTED_ONLINE_APPLY_SERVICE_COMPONENT,
},
"issued_at": (now - timedelta(minutes=1)).isoformat(timespec="seconds"),
"expires_at": (now + timedelta(minutes=5)).isoformat(timespec="seconds"),
}
challenge = _sign_envelope(
material["apply_private"],
schema=apply.batch.APPLY_CHALLENGE_SCHEMA_VERSION,
id_prefix="rc06-apply-challenge-",
domain=apply.batch.APPLY_CHALLENGE_SIGNATURE_DOMAIN,
payload=challenge_payload,
)
challenge["challenge_id"] = challenge.pop("receipt_id")
committed_at = now.isoformat(timespec="seconds")
online_payload = {
"schema_version": apply.batch.ONLINE_TRANSACTION_PAYLOAD_SCHEMA_VERSION,
"key_id": apply.batch.TRUSTED_ONLINE_APPLY_SERVICE_KEY_ID,
"batch_manifest_payload_sha256": request["batch_manifest_payload_sha256"],
"approval_receipt_sha256": request["approval_receipt_sha256"],
"environment_id": request["environment_id"],
"transaction_id": request["transaction_id"],
"database_commit_id": "sqlite-" + request["idempotency_key"][:24],
"committed_at": committed_at,
"authority_challenge": challenge,
"generated_by": {
"type": "system",
"component": apply.batch.TRUSTED_ONLINE_APPLY_SERVICE_COMPONENT,
},
"item_results": request["expected_publication_item_results"],
"write_count": 74,
"status": "committed_and_verified",
}
online_receipt = _sign_envelope(
material["online_private"],
schema=apply.batch.ONLINE_TRANSACTION_RECEIPT_SCHEMA_VERSION,
id_prefix="rc06-online-transaction-",
domain=apply.batch.ONLINE_TRANSACTION_SIGNATURE_DOMAIN,
payload=online_payload,
)
apply_payload = {
"schema_version": apply.batch.APPLY_PAYLOAD_SCHEMA_VERSION,
"batch_manifest_payload_sha256": request["batch_manifest_payload_sha256"],
"approval_receipt_sha256": request["approval_receipt_sha256"],
"environment_id": request["environment_id"],
"applied_at": committed_at,
"generated_by": {"type": "system", "component": "rc06-apply-authority"},
"item_results": request["expected_publication_item_results"],
"write_count": 74,
"status": "applied_and_verified",
"online_transaction_receipt": online_receipt,
}
apply_receipt = _sign_envelope(
material["apply_private"],
schema=apply.batch.APPLY_SCHEMA_VERSION,
id_prefix="rc06-apply-receipt-",
domain=apply.batch.APPLY_SIGNATURE_DOMAIN,
payload=apply_payload,
)
runtime_payload = {
"schema_version": apply.RUNTIME_PAYLOAD_SCHEMA_VERSION,
"key_id": apply.batch.TRUSTED_ONLINE_APPLY_SERVICE_KEY_ID,
"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"],
"legal_source_review_binding_sha256": request[
"legal_source_review_binding_sha256"
],
"environment_id": request["environment_id"],
"transaction_id": request["transaction_id"],
"captured_at": committed_at,
"generated_by": {
"type": "system",
"component": apply.batch.TRUSTED_ONLINE_APPLY_SERVICE_COMPONENT,
},
"transaction_mode": "single_service_atomic",
"publication_item_count": 74,
"runtime_snapshot_dependencies": request[
"expected_runtime_snapshot_dependencies"
],
"runtime_snapshot_dependency_count": 14,
"status": "runtime_snapshots_verified",
}
runtime_receipt = _sign_envelope(
material["online_private"],
schema=apply.RUNTIME_RECEIPT_SCHEMA_VERSION,
id_prefix="rc06-runtime-snapshot-",
domain=apply.RUNTIME_SIGNATURE_DOMAIN,
payload=runtime_payload,
)
return apply_receipt, runtime_receipt
def apply(self, request: dict, request_sha256: str) -> tuple[int, dict]:
self.apply_calls += 1
with sqlite3.connect(self.database) as connection:
existing = connection.execute(
"SELECT response_json FROM transactions WHERE idempotency_key=? AND state='committed_and_verified'",
(request["idempotency_key"],),
).fetchone()
if existing is not None:
return 200, json.loads(existing[0])
try:
connection.execute("BEGIN IMMEDIATE")
connection.execute(
"INSERT OR REPLACE INTO transactions VALUES (?, ?, 'preparing', ?, NULL)",
(
request["transaction_id"],
request["idempotency_key"],
request["legal_source_review_binding_sha256"],
),
)
for index, item in enumerate(request["expected_publication_item_results"]):
connection.execute(
"INSERT INTO publication_items VALUES (?, ?, ?, ?)",
(
request["transaction_id"],
item["kind"],
item["item_id"],
item["online_content_sha256"],
),
)
if self.fail_after is not None and index + 1 == self.fail_after:
raise RuntimeError("injected transaction failure")
for item in request["expected_runtime_snapshot_dependencies"]:
connection.execute(
"INSERT INTO runtime_snapshots VALUES (?, ?, ?, ?, ?)",
(
request["transaction_id"],
item["kind"],
item["runtime_key"],
item["online_content_sha256"],
item["status"],
),
)
apply_receipt, runtime_receipt = self._receipt_pair(request)
response = {
"schema_version": apply.APPLY_RESPONSE_SCHEMA_VERSION,
"idempotency_key": request["idempotency_key"],
"request_sha256": request_sha256,
"legal_source_review_binding_sha256": request[
"legal_source_review_binding_sha256"
],
"transaction_mode": "single_service_atomic",
"publication_item_count": 74,
"runtime_snapshot_dependency_count": 14,
"apply_receipt": apply_receipt,
"runtime_snapshot_receipt": runtime_receipt,
}
connection.execute(
"UPDATE transactions SET state='committed_and_verified', response_json=? WHERE transaction_id=?",
(json.dumps(response, ensure_ascii=False), request["transaction_id"]),
)
connection.commit()
return 200, response
except RuntimeError:
connection.rollback()
return 409, {"error": "injected transaction failure; SQLite rolled back"}
def status(self, transaction_id: str) -> tuple[int, dict]:
with sqlite3.connect(self.database) as connection:
transaction = connection.execute(
"SELECT idempotency_key, state, legal_review_sha256, response_json FROM transactions WHERE transaction_id=?",
(transaction_id,),
).fetchone()
if transaction is None or transaction[1] == "rolled_back":
return 404, {"error": "transaction is not committed"}
response = json.loads(transaction[3])
publication = response["apply_receipt"]["payload"]["item_results"]
runtime = response["runtime_snapshot_receipt"]["payload"][
"runtime_snapshot_dependencies"
]
if self.tamper_status:
publication = copy.deepcopy(publication)
publication[0]["online_content_sha256"] = "f" * 64
if self.tamper_runtime_status:
runtime = copy.deepcopy(runtime)
runtime[0]["online_content_sha256"] = "f" * 64
return 200, {
"schema_version": apply.APPLY_STATUS_SCHEMA_VERSION,
"idempotency_key": transaction[0],
"transaction_id": transaction_id,
"state": "committed_and_verified",
"legal_source_review_binding_sha256": transaction[2],
"publication_item_results": publication,
"publication_item_count": len(publication),
"runtime_snapshot_dependencies": runtime,
"runtime_snapshot_dependency_count": len(runtime),
"apply_receipt_sha256": apply.batch._sha256_json(response["apply_receipt"]),
"runtime_snapshot_receipt_sha256": apply.batch._sha256_json(
response["runtime_snapshot_receipt"]
),
}
def rollback(self, transaction_id: str, request: dict) -> tuple[int, dict]:
with sqlite3.connect(self.database) as connection:
connection.execute("BEGIN IMMEDIATE")
connection.execute(
"DELETE FROM publication_items WHERE transaction_id=?", (transaction_id,)
)
connection.execute(
"DELETE FROM runtime_snapshots WHERE transaction_id=?", (transaction_id,)
)
connection.execute(
"INSERT OR REPLACE INTO transactions "
"(transaction_id,idempotency_key,state,legal_review_sha256,response_json) "
"VALUES (?,?,'rolled_back',?,NULL)",
(
transaction_id,
request["idempotency_key"],
request["legal_source_review_binding_sha256"],
),
)
connection.commit()
evidence = {
"transaction_id": transaction_id,
"idempotency_key": request["idempotency_key"],
"restored_item_count": 0,
}
return 200, {
"schema_version": apply.ROLLBACK_RESPONSE_SCHEMA_VERSION,
"idempotency_key": request["idempotency_key"],
"transaction_id": transaction_id,
"state": "rolled_back",
"restored_item_count": 0,
"rollback_evidence_sha256": apply.batch._sha256_json(evidence),
}
def counts(self) -> tuple[int, int]:
with sqlite3.connect(self.database) as connection:
return (
connection.execute("SELECT count(*) FROM publication_items").fetchone()[0],
connection.execute("SELECT count(*) FROM runtime_snapshots").fetchone()[0],
)
@contextmanager
def _serve(service: FakePublicationService):
class Handler(BaseHTTPRequestHandler):
def log_message(self, _format, *_args):
return
def _send(self, status: int, payload: dict) -> None:
raw = json.dumps(payload, ensure_ascii=False).encode("utf-8")
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(raw)))
self.end_headers()
self.wfile.write(raw)
def _authorized(self) -> bool:
if self.headers.get("Authorization") != "Bearer test-token":
self._send(401, {"error": "unauthorized"})
return False
return True
def _body(self) -> dict:
return json.loads(self.rfile.read(int(self.headers["Content-Length"])))
def do_POST(self):
if not self._authorized():
return
if self.path == "/internal/rc06/publication-transactions/apply":
request = self._body()
if (
self.headers.get("Idempotency-Key") != request["idempotency_key"]
or self.headers.get("X-RC06-Request-SHA256")
!= apply.batch._sha256_json(request)
):
self._send(400, {"error": "identity header mismatch"})
return
status, payload = service.apply(
request, self.headers["X-RC06-Request-SHA256"]
)
self._send(status, payload)
return
if self.path.endswith("/rollback"):
request = self._body()
transaction_id = self.path.split("/")[-2]
status, payload = service.rollback(transaction_id, request)
self._send(status, payload)
return
self._send(404, {"error": "not found"})
def do_GET(self):
if not self._authorized():
return
if self.path.endswith("/status"):
transaction_id = self.path.split("/")[-2]
status, payload = service.status(transaction_id)
self._send(status, payload)
return
self._send(404, {"error": "not found"})
server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
try:
yield f"http://127.0.0.1:{server.server_port}"
finally:
server.shutdown()
thread.join(timeout=5)
server.server_close()
def _execute(material: dict, client, ledger) -> dict:
return apply.apply_publication_batch(
manifest=material["manifest"],
manifest_raw_sha256=material["manifest_raw"],
approval=material["approval"],
approval_raw_sha256=material["approval_raw"],
approval_keyring=material["keyring"],
approval_root_public_key=material["root_private"].public_key(),
apply_service_public_key=material["apply_private"].public_key(),
online_service_public_key=material["online_private"].public_key(),
legal_review_request=material["legal_request"],
legal_review_decision=material["legal_decision"],
legal_review_keyring=material["legal_keyring"],
environment_id="rc06-test",
client=client,
ledger=ledger,
now=material["now"],
)
def test_authenticated_apply_is_atomic_hash_verified_and_idempotent(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
material = _material(monkeypatch, datetime(2026, 10, 6, 8, 0, tzinfo=UTC))
runtime = apply.expected_runtime_snapshot_results(material["manifest"])
assert len(runtime) == 14
drafting = next(item for item in runtime if item["kind"] == "composed_drafting")
assert drafting["runtime_key"] == "contract_drafting_v1"
assert drafting["version"] == "tp06-23-20260909-1"
assert sum(item["kind"] == "new_review_module" for item in runtime) == 8
assert sum(item["kind"] == "existing_domain_prompt" for item in runtime) == 5
service = FakePublicationService(tmp_path / "remote.sqlite3", material)
with _serve(service) as url:
client = apply.JsonHttpClient(
url, "test-token", allow_http_loopback=True
)
ledger = apply.ApplyLedger(tmp_path / "ledger.sqlite3")
try:
evidence = _execute(material, client, ledger)
replay = _execute(material, client, ledger)
finally:
ledger.close()
assert replay == evidence
assert evidence["publication_item_count"] == 74
assert evidence["runtime_snapshot_dependency_count"] == 14
assert service.counts() == (74, 14)
assert service.apply_calls == 1
second_ledger = apply.ApplyLedger(tmp_path / "second-ledger.sqlite3")
try:
assert _execute(material, client, second_ledger) == evidence
finally:
second_ledger.close()
assert service.counts() == (74, 14)
assert service.apply_calls == 2
@pytest.mark.parametrize("mode", ["missing", "expired", "hash_mismatch"])
def test_legal_source_review_is_fail_closed_before_http(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
mode: str,
) -> None:
material = _material(monkeypatch, datetime(2026, 10, 6, 8, 0, tzinfo=UTC))
if mode == "missing":
material["legal_request"] = {}
elif mode == "expired":
material["legal_decision"]["reviewed_at"] = "2026-10-04T08:00:00+00:00"
material["legal_decision"]["expires_at"] = "2026-10-05T08:00:00+00:00"
else:
material["legal_decision"]["items"][0]["source_record_sha256"] = "f" * 64
service = FakePublicationService(tmp_path / "remote.sqlite3", material)
with _serve(service) as url:
client = apply.JsonHttpClient(url, "test-token", allow_http_loopback=True)
ledger = apply.ApplyLedger(tmp_path / "ledger.sqlite3")
try:
with pytest.raises(apply.ApplyPublicationError, match="scene legal review"):
_execute(material, client, ledger)
finally:
ledger.close()
assert service.apply_calls == 0
assert service.counts() == (0, 0)
@pytest.mark.parametrize("field", ["version", "content_sha256"])
def test_forged_executable_runtime_identity_is_rejected_before_http(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
field: str,
) -> None:
material = _material(monkeypatch, datetime(2026, 10, 6, 8, 0, tzinfo=UTC))
manifest = material["manifest"]
drafting = next(
item
for item in manifest["runtime_prompt_dependencies"]["items"]
if item["kind"] == "composed_drafting"
)
drafting[field] = "forged-version" if field == "version" else "f" * 64
manifest["runtime_prompt_dependencies"]["inventory_sha256"] = apply.batch._sha256_json(
manifest["runtime_prompt_dependencies"]["items"]
)
manifest["manifest_payload_sha256"] = apply.batch._sha256_json(
{key: value for key, value in manifest.items() if key != "manifest_payload_sha256"}
)
material["manifest_raw"] = apply.batch._sha256_bytes(apply.batch._canonical(manifest))
service = FakePublicationService(tmp_path / "remote.sqlite3", material)
with _serve(service) as url:
client = apply.JsonHttpClient(url, "test-token", allow_http_loopback=True)
ledger = apply.ApplyLedger(tmp_path / "ledger.sqlite3")
try:
with pytest.raises(apply.ApplyPublicationError, match="candidate executable source"):
_execute(material, client, ledger)
finally:
ledger.close()
assert service.apply_calls == 0
assert service.counts() == (0, 0)
def test_runtime_snapshot_module_hash_must_match_publication_item(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
material = _material(monkeypatch, datetime(2026, 10, 6, 8, 0, tzinfo=UTC))
manifest = material["manifest"]
drafting = next(
item
for item in manifest["runtime_prompt_dependencies"]["items"]
if item["kind"] == "composed_drafting"
)
drafting["publication_module_dependencies"][0]["content_sha256"] = "f" * 64
manifest["runtime_prompt_dependencies"]["inventory_sha256"] = apply.batch._sha256_json(
manifest["runtime_prompt_dependencies"]["items"]
)
manifest["manifest_payload_sha256"] = apply.batch._sha256_json(
{key: value for key, value in manifest.items() if key != "manifest_payload_sha256"}
)
material["manifest_raw"] = apply.batch._sha256_bytes(apply.batch._canonical(manifest))
service = FakePublicationService(tmp_path / "remote.sqlite3", material)
with _serve(service) as url:
client = apply.JsonHttpClient(url, "test-token", allow_http_loopback=True)
ledger = apply.ApplyLedger(tmp_path / "ledger.sqlite3")
try:
with pytest.raises(
apply.batch.PublicationBatchError,
match="exact publication module identities",
):
_execute(material, client, ledger)
finally:
ledger.close()
assert service.apply_calls == 0
assert service.counts() == (0, 0)
@pytest.mark.parametrize("failure", ["write", "readback_hash", "runtime_hash"])
def test_failure_never_leaves_partial_or_false_success(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
failure: str,
) -> None:
material = _material(monkeypatch, datetime(2026, 10, 6, 8, 0, tzinfo=UTC))
service = FakePublicationService(
tmp_path / "remote.sqlite3",
material,
fail_after=17 if failure == "write" else None,
tamper_status=failure == "readback_hash",
tamper_runtime_status=failure == "runtime_hash",
)
with _serve(service) as url:
client = apply.JsonHttpClient(url, "test-token", allow_http_loopback=True)
ledger = apply.ApplyLedger(tmp_path / "ledger.sqlite3")
try:
with pytest.raises(apply.ApplyPublicationError):
_execute(material, client, ledger)
state = ledger.connection.execute(
"SELECT state FROM rc06_apply_operations"
).fetchone()[0]
finally:
ledger.close()
assert state == "rolled_back"
assert service.counts() == (0, 0)
def test_http_and_auth_fail_closed() -> None:
with pytest.raises(apply.ApplyPublicationError, match="HTTPS"):
apply.JsonHttpClient("http://example.com", "token")
with pytest.raises(apply.ApplyPublicationError, match="bearer token"):
apply.JsonHttpClient("https://example.com", "")