Harden RC05 candidate-bound evaluation runner

This commit is contained in:
2026-10-06 20:37:39 +08:00
parent f49a26c61f
commit ebad424350
9 changed files with 876 additions and 47 deletions
@@ -203,10 +203,13 @@ jobs:
CONTRACT_API_DATABASE_URL: sqlite+pysqlite:////tmp/yuqei-contract-api-ci.sqlite3
CONTRACT_API_DATABASE_AUTO_CREATE: "false"
run: |
expected_head="$(python -m alembic heads | sed -nE 's/^([^ ]+) \(head\)$/\1/p')"
test "$(printf '%s\n' "$expected_head" | sed '/^$/d' | wc -l)" -eq 1
python -m alembic upgrade head
python -m alembic downgrade 20260710_0030
python -m alembic upgrade head
python -m alembic current | grep -F "20260909_0079 (head)"
actual_head="$(python -m alembic current | sed -nE 's/^([^ ]+) \(head\)$/\1/p')"
test "$actual_head" = "$expected_head"
- name: Verify AI Platform API empty-database migrations
working-directory: yuqei-ai-platform/services/ai-platform-api
@@ -215,5 +218,8 @@ jobs:
AI_PLATFORM_DATABASE_AUTO_CREATE: "false"
AI_PLATFORM_REPOSITORY_BACKEND: sqlalchemy
run: |
expected_head="$(python -m alembic heads | sed -nE 's/^([^ ]+) \(head\)$/\1/p')"
test "$(printf '%s\n' "$expected_head" | sed '/^$/d' | wc -l)" -eq 1
python -m alembic upgrade head
python -m alembic current | grep -F "20260826_0028 (head)"
actual_head="$(python -m alembic current | sed -nE 's/^([^ ]+) \(head\)$/\1/p')"
test "$actual_head" = "$expected_head"
+229 -9
View File
@@ -25,6 +25,9 @@ TERMINAL_STATUSES = frozenset({"succeeded", "failed", "cancelled"})
MAX_PREALLOCATED_ATTEMPTS = 280
MAX_REPAIR_ATTEMPTS = 16
REPAIR_OPERATIONS = frozenset({"repair_draft", "repair_review"})
LEDGER_SCHEMA = "multiscene-call-ledger.v2"
CAMPAIGN_SCHEMA = "multiscene-call-campaign.v2"
CONTROL_SCHEMA = "multiscene-release-control-state.v1"
_ID_RE = re.compile(r"[A-Za-z0-9][A-Za-z0-9._-]{0,127}\Z")
_SHA_RE = re.compile(r"[0-9a-f]{64}\Z")
@@ -46,7 +49,9 @@ class CallLedger:
operation_limits: Mapping[str, int], case_limits: Mapping[str, int],
source_pins: Mapping[str, str], real_provider: bool,
historical_usage: Mapping[str, object] | None = None,
registry_root: str | Path | None = None) -> None:
registry_root: str | Path | None = None,
candidate_binding: Mapping[str, object] | None = None,
release_controls: Mapping[str, object] | None = None) -> None:
requested_output = Path(output_dir).absolute()
_reject_path_chain(requested_output, "output directory")
self.output_dir = requested_output.resolve()
@@ -71,6 +76,9 @@ class CallLedger:
self.source_pins[name] = _sha(digest, "source pin")
self.real_provider = real_provider
self.historical_usage = _historical_usage(historical_usage)
self.candidate_binding = _json_mapping(candidate_binding, "candidate binding")
self.release_controls = _release_controls(release_controls, self.case_limits)
self.controls_enabled = self.release_controls is not None
if registry_root is not None:
if not real_provider or historical_usage is None:
raise ValueError("global registry is only valid for a receipt-bound real run")
@@ -85,8 +93,8 @@ class CallLedger:
})
self.root = self.registry_root / run_key
self.campaign_identity = {
"schema_version": "multiscene-call-campaign.v1",
"campaign_id": "rc05-real-usage-v1",
"schema_version": CAMPAIGN_SCHEMA,
"campaign_id": "rc05-real-usage-v2",
"historical_receipt_sha256": self.historical_usage["receipt_sha256"],
"historical_attempts_sha256": self.historical_usage["attempts_sha256"],
"historical_provider_call_count": self.historical_usage["provider_call_count"],
@@ -116,15 +124,18 @@ class CallLedger:
}
self.attempts = self.root / "attempts"
self.case_outcomes = self.root / "case-outcomes"
self.control_events = self.root / "control-events"
self.lock_path = ((self.registry_root / ".lock")
if self.registry_root is not None else self.root / ".lock")
self.identity = {
"schema_version": "multiscene-call-ledger.v1",
"schema_version": LEDGER_SCHEMA,
"plan_sha256": self.plan_sha256,
"operation_limits": {name: self.operation_limits.get(name, 0) for name in OPERATIONS},
"case_limits": dict(sorted(self.case_limits.items())),
"source_pins": dict(sorted(self.source_pins.items())),
"real_provider": real_provider,
"candidate_binding": self.candidate_binding,
"release_controls": self.release_controls,
"authority": authority,
"integrity_not_authentication": True,
}
@@ -145,7 +156,12 @@ class CallLedger:
campaign_identity_path = self.registry_root / "campaign-identity.json"
_reject_link_or_reparse(campaign_identity_path, "campaign identity")
if campaign_identity_path.exists():
if _read_json(campaign_identity_path) != self.campaign_identity:
stored_campaign = _read_json(campaign_identity_path)
if stored_campaign.get("schema_version") != CAMPAIGN_SCHEMA:
raise LedgerIdentityError(
"legacy campaign schema is read-only diagnostic material; execution refused"
)
if stored_campaign != self.campaign_identity:
raise LedgerIdentityError(
"global campaign is already bound to a different historical receipt"
)
@@ -168,7 +184,12 @@ class CallLedger:
identity_path = self.root / "identity.json"
_reject_link_or_reparse(identity_path, "ledger identity")
if identity_path.exists():
if _read_json(identity_path) != self.identity:
stored_identity = _read_json(identity_path)
if stored_identity.get("schema_version") != LEDGER_SCHEMA:
raise LedgerIdentityError(
"legacy ledger schema is read-only diagnostic material; execution refused"
)
if stored_identity != self.identity:
raise LedgerIdentityError("ledger identity does not match the frozen run")
else:
_write_x(identity_path, self.identity)
@@ -178,11 +199,155 @@ class CallLedger:
_reject_link_or_reparse(self.case_outcomes, "case outcome directory")
self.case_outcomes.mkdir(exist_ok=True)
_reject_link_or_reparse(self.case_outcomes, "case outcome directory")
_reject_link_or_reparse(self.control_events, "control event directory")
self.control_events.mkdir(exist_ok=True)
_reject_link_or_reparse(self.control_events, "control event directory")
# Create the output only after the global identity accepted this exact
# path. A competing fresh output therefore cannot leave a fork behind.
self.output_dir.mkdir(parents=True, exist_ok=True)
_reject_link_or_reparse(self.output_dir, "output directory")
def control_snapshot(self) -> dict[str, object]:
with self._locked():
return self._control_snapshot_unlocked()
def authorize_selection(self, selected: list[str], *, resume: bool,
explicit_selection: bool) -> dict[str, object]:
"""Fail closed before dispatch unless persisted release controls permit it."""
if not self.controls_enabled:
raise CallLedgerError("release controls are required for controlled execution")
if (not isinstance(selected, list) or not selected
or len(selected) != len(set(selected))
or any(case_id not in self.case_limits for case_id in selected)):
raise CallLedgerError("selection is outside frozen release controls")
with self._locked():
self._materialize_attempt_breakers_unlocked()
state = self._control_snapshot_unlocked()
canaries = self.release_controls["canary_case_ids"]
if state["phase"] == "canary_required":
if not explicit_selection or selected != canaries:
raise CallLedgerError(
"canary phase requires the explicit predefined canary case sequence"
)
if state["blocked_failure_classes"]:
raise CallLedgerError("canary phase is blocked by a persisted failure class")
records = self._records()
blocked = set(state["blocked_failure_classes"])
future = [case_id for case_id in selected
if not any(key[0] == case_id for key in records)]
forbidden = sorted({self.release_controls["failure_classes"][case_id]
for case_id in future} & blocked)
if forbidden:
raise CallLedgerError(
f"selection contains persisted blocked failure classes: {forbidden}"
)
return state
def complete_canary(self, outcome_sha256s: Mapping[str, str]) -> None:
"""Persist the single immutable transition that authorizes full dispatch."""
if not self.controls_enabled:
raise CallLedgerError("release controls are not configured")
canaries = self.release_controls["canary_case_ids"]
if not isinstance(outcome_sha256s, Mapping) or set(outcome_sha256s) != set(canaries):
raise CallLedgerError("canary outcome set is incomplete")
normalized = {case_id: _sha(outcome_sha256s[case_id], "canary outcome SHA-256")
for case_id in canaries}
with self._locked():
self._materialize_attempt_breakers_unlocked()
state = self._control_snapshot_unlocked()
if state["blocked_failure_classes"]:
raise CallLedgerError("failed or unknown canary cannot authorize full dispatch")
records = self._records()
for case_id in canaries:
relevant = [record for (current, _operation), record in records.items()
if current == case_id]
if not relevant or any(record["finish"] is None
or record["finish"]["status"] != "succeeded"
for record in relevant):
raise CallLedgerError("canary provider attempts are incomplete or unsuccessful")
binding = _read_json(self.case_outcomes / f"{case_id}.json")
if binding.get("outcome_sha256") != normalized[case_id]:
raise CallLedgerError("canary outcome does not match immutable binding")
payload = {"schema_version": CONTROL_SCHEMA,
"event": "canary_to_full", "ledger_identity_sha256": self.identity_sha256,
"canary_case_ids": canaries, "outcome_sha256s": normalized}
transition = self.control_events / "canary-to-full.json"
if transition.exists():
if _read_json(transition) != payload:
raise CallLedgerError("canary transition is already bound differently")
else:
_write_x(transition, payload)
def trip_failure_class(self, case_id: str, status: str, reason: str) -> None:
if not self.controls_enabled:
return
case_id = _identifier(case_id, "failure case_id")
if case_id not in self.case_limits or status not in {"failed", "cancelled", "unknown"}:
raise CallLedgerError("failure circuit event is invalid")
failure_class = self.release_controls["failure_classes"][case_id]
if not isinstance(reason, str) or not reason or len(reason) > 1000:
raise CallLedgerError("failure circuit reason is invalid")
payload = {"schema_version": CONTROL_SCHEMA, "event": "failure_class_blocked",
"ledger_identity_sha256": self.identity_sha256, "failure_class": failure_class,
"case_id": case_id, "status": status, "reason": reason}
path = self.control_events / f"failure-class--{failure_class}.json"
with self._locked():
if path.exists():
existing = _read_json(path)
if existing.get("failure_class") != failure_class:
raise CallLedgerError("failure circuit event is inconsistent")
else:
_write_x(path, payload)
def _materialize_attempt_breakers_unlocked(self) -> None:
if not self.controls_enabled:
return
for (case_id, operation), record in self._records().items():
status = (record["finish"]["status"] if record["finish"] is not None
else "unknown")
if status == "succeeded":
continue
failure_class = self.release_controls["failure_classes"][case_id]
path = self.control_events / f"failure-class--{failure_class}.json"
if not path.exists():
_write_x(path, {"schema_version": CONTROL_SCHEMA,
"event": "failure_class_blocked",
"ledger_identity_sha256": self.identity_sha256,
"failure_class": failure_class, "case_id": case_id,
"status": status, "reason": f"ledger attempt {operation} is {status}"})
def _control_snapshot_unlocked(self) -> dict[str, object]:
if not self.controls_enabled:
return {"schema_version": CONTROL_SCHEMA, "phase": "not_configured",
"canary_case_ids": [], "blocked_failure_classes": [],
"canary_transition_sha256": None}
blocked: list[str] = []
transition_sha = None
for path in self.control_events.iterdir():
_reject_link_or_reparse(path, "control event")
if not path.is_file():
raise CallLedgerError("control event journal contains a non-file entry")
payload = _read_json(path)
if (not isinstance(payload, dict)
or payload.get("schema_version") != CONTROL_SCHEMA
or payload.get("ledger_identity_sha256") != self.identity_sha256):
raise CallLedgerError("control event is invalid")
if path.name == "canary-to-full.json" and payload.get("event") == "canary_to_full":
transition_sha = _digest(payload)
elif (re.fullmatch(r"failure-class--[A-Za-z0-9._-]+\.json", path.name)
and payload.get("event") == "failure_class_blocked"):
failure_class = _identifier(payload.get("failure_class"), "failure class")
if failure_class not in set(self.release_controls["failure_classes"].values()):
raise CallLedgerError("control event failure class is outside frozen controls")
blocked.append(failure_class)
else:
raise CallLedgerError("control event journal contains an unknown entry")
return {"schema_version": CONTROL_SCHEMA,
"phase": "full_authorized" if transition_sha else "canary_required",
"canary_case_ids": self.release_controls["canary_case_ids"],
"blocked_failure_classes": sorted(set(blocked)),
"canary_transition_sha256": transition_sha}
def claim(self, case_id: str, operation: str, request_sha256: str) -> str:
case_id = _identifier(case_id, "case_id")
if case_id not in self.case_limits:
@@ -192,6 +357,15 @@ class CallLedger:
request_sha256 = _sha(request_sha256, "request_sha256")
stem = f"{case_id}--{operation}"
with self._locked():
if self.controls_enabled:
self._materialize_attempt_breakers_unlocked()
state = self._control_snapshot_unlocked()
failure_class = self.release_controls["failure_classes"][case_id]
if failure_class in state["blocked_failure_classes"]:
raise CallLedgerError("failure-class circuit breaker is open")
if (state["phase"] == "canary_required"
and case_id not in self.release_controls["canary_case_ids"]):
raise CallLedgerError("full dispatch is forbidden before canary transition")
records = self._records()
campaign_records = self._campaign_records() if self.registry_root is not None else records
if (case_id, operation) in records:
@@ -327,7 +501,8 @@ class CallLedger:
repair_attempted = sum(
1 for record in campaign_records.values()
if record["claim"]["operation"] in REPAIR_OPERATIONS)
return {"schema_version": "multiscene-call-ledger-snapshot.v1",
controls = self._control_snapshot_unlocked()
return {"schema_version": "multiscene-call-ledger-snapshot.v2",
"ledger_identity_sha256": self.identity_sha256,
"real_provider": self.real_provider,
"authority": self.identity["authority"],
@@ -376,7 +551,8 @@ class CallLedger:
"provider_outcomes": campaign_provider_outcomes},
"attempted": attempted,
"terminal": sum(record["finish"] is not None for record in records.values()),
"unknown": sum(record["finish"] is None for record in records.values())}
"unknown": sum(record["finish"] is None for record in records.values()),
"release_controls": controls}
def attempt_records(self) -> list[dict[str, object]]:
"""Return immutable attempt outcomes for evidence aggregation."""
@@ -486,6 +662,10 @@ class CallLedger:
def _records(self) -> dict[tuple[str, str], dict]:
_reject_link_or_reparse(self.root / "identity.json", "ledger identity")
expected_identity = _read_json(self.root / "identity.json")
if expected_identity.get("schema_version") != LEDGER_SCHEMA:
raise LedgerIdentityError(
"legacy ledger schema is read-only diagnostic material; execution refused"
)
if expected_identity != self.identity:
raise LedgerIdentityError("ledger identity changed")
records: dict[tuple[str, str], dict] = {}
@@ -536,7 +716,12 @@ class CallLedger:
raise CallLedgerError("campaign records require a global registry")
_reject_link_or_reparse(self.registry_root / "campaign-identity.json",
"campaign identity")
if _read_json(self.registry_root / "campaign-identity.json") != self.campaign_identity:
stored_campaign = _read_json(self.registry_root / "campaign-identity.json")
if stored_campaign.get("schema_version") != CAMPAIGN_SCHEMA:
raise LedgerIdentityError(
"legacy campaign schema is read-only diagnostic material; execution refused"
)
if stored_campaign != self.campaign_identity:
raise LedgerIdentityError("global campaign identity changed")
records: dict[str, dict] = {}
for path in self.campaign_attempts.iterdir():
@@ -633,6 +818,41 @@ def _limits(value: Mapping[str, int], *, operations: bool) -> dict[str, int]:
return result
def _json_mapping(value: Mapping[str, object] | None, label: str) -> dict | None:
if value is None:
return None
if not isinstance(value, Mapping):
raise TypeError(f"{label} must be a mapping")
try:
return json.loads(json.dumps(dict(value), ensure_ascii=False, sort_keys=True,
separators=(",", ":"), allow_nan=False))
except (TypeError, ValueError) as error:
raise ValueError(f"{label} must be canonical JSON data") from error
def _release_controls(value: Mapping[str, object] | None,
case_limits: Mapping[str, int]) -> dict | None:
normalized = _json_mapping(value, "release controls")
if normalized is None:
return None
if set(normalized) != {"schema_version", "canary_case_ids", "failure_classes"}:
raise ValueError("release controls shape is invalid")
if normalized["schema_version"] != "multiscene-release-controls.v1":
raise ValueError("release controls schema is unsupported")
canaries = normalized["canary_case_ids"]
classes = normalized["failure_classes"]
if (not isinstance(canaries, list) or not canaries
or len(canaries) != len(set(canaries))
or any(case_id not in case_limits for case_id in canaries)):
raise ValueError("release control canary inventory is invalid")
if not isinstance(classes, dict) or set(classes) != set(case_limits):
raise ValueError("release control failure-class inventory is invalid")
for case_id, failure_class in classes.items():
_identifier(case_id, "release control case ID")
_identifier(failure_class, "release control failure class")
return normalized
def _case_attempt_projection(records: Mapping[tuple[str, str], dict], case_id: str) -> list[dict]:
return [
{
+307 -9
View File
@@ -4,8 +4,11 @@ from __future__ import annotations
from datetime import UTC, datetime
from hashlib import sha256
import json
from pathlib import Path
from typing import Any
import os
import re
import subprocess
from pathlib import Path, PurePath
from typing import Any, Mapping
from yuqei_contract_api.contract_templates import template_document_kind
from yuqei_contract_api.drafting_workspace import (
@@ -20,7 +23,23 @@ from multiscene_supplemental_cases import build_supplemental_cases
ROOT = Path(__file__).resolve().parents[2]
SCHEMA_VERSION = "multiscene-evaluation-plan.v1"
SCHEMA_VERSION = "multiscene-evaluation-plan.v2"
CANDIDATE_SCHEMA_VERSION = "multiscene-candidate-binding.v1"
RELEASE_CONTROLS_SCHEMA_VERSION = "multiscene-release-controls.v1"
REQUIRED_IMAGE_ROLES = (
"contract-web", "contract-api", "ai-platform-api", "ai-worker",
)
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",
)
_SHA256_RE = re.compile(r"[0-9a-f]{64}\Z")
_COMMIT_RE = re.compile(r"[0-9a-f]{40}\Z")
_TAG_RE = re.compile(r"[A-Za-z0-9][A-Za-z0-9._/-]{0,127}\Z")
EXPECTED_OPERATIONS = {
"draft": 120, "semantic_review": 30, "repair_draft": 8,
"repair_review": 8, "specialist_review": 106, "intake": 8,
@@ -55,6 +74,262 @@ def _canonical(value: Any) -> str:
separators=(",", ":"), allow_nan=False)
def _strict_json(raw: bytes, label: str) -> Any:
def pairs(items):
result = {}
for key, value in items:
if key in result:
raise ValueError(f"{label} contains a duplicate JSON key")
result[key] = value
return result
def constant(value):
raise ValueError(f"{label} contains non-finite JSON value {value}")
try:
return json.loads(raw, object_pairs_hook=pairs, parse_constant=constant)
except (UnicodeDecodeError, json.JSONDecodeError) as error:
raise ValueError(f"{label} is not valid strict JSON") from error
def _require_sha(value: object, label: str) -> str:
if not isinstance(value, str) or not _SHA256_RE.fullmatch(value):
raise ValueError(f"{label} must be a lowercase SHA-256 value")
return value
def _artifact_bytes(path: Path, root: Path, label: str) -> bytes:
supplied = Path(path)
if not supplied.is_absolute():
raise ValueError(f"{label} path must be absolute")
absolute = supplied.absolute()
current = absolute
while True:
junction = getattr(current, "is_junction", lambda: False)
if current.is_symlink() or junction():
raise ValueError(f"{label} path must not traverse links or junctions")
if current.parent == current:
break
current = current.parent
if not absolute.is_file() or absolute.resolve().is_relative_to(root.resolve()):
raise ValueError(f"{label} must be a regular file outside the source worktree")
return absolute.read_bytes()
def _git(root: Path, *args: str, binary: bool = False):
environment = {**os.environ, "GIT_CONFIG_NOSYSTEM": "1",
"GIT_CONFIG_GLOBAL": os.devnull, "GIT_OPTIONAL_LOCKS": "0",
"GIT_NO_REPLACE_OBJECTS": "1"}
completed = subprocess.run(
["git", "-C", str(root), *args], capture_output=True,
text=not binary, check=False, env=environment,
)
if completed.returncode:
error = completed.stderr if isinstance(completed.stderr, str) else completed.stderr.decode("utf-8", "replace")
raise ValueError(f"git candidate verification failed: {error.strip()}")
return completed.stdout if binary else completed.stdout.strip()
def _verify_git_candidate(root: Path, commit: str, tag: str, stage: str) -> None:
if not _COMMIT_RE.fullmatch(commit):
raise ValueError("candidate commit must be a full lowercase SHA-1 object ID")
if (not _TAG_RE.fullmatch(tag) or ".." in tag or "//" in tag
or tag.endswith((".", "/")) or tag.endswith(".lock")):
raise ValueError("candidate tag is unsafe")
head = _git(root, "rev-parse", "--verify", "HEAD^{commit}")
tagged = _git(root, "rev-parse", "--verify", f"refs/tags/{tag}^{{commit}}")
if head != commit or tagged != commit:
raise ValueError(f"candidate HEAD/tag mismatch {stage}")
if _git(root, "status", "--porcelain=v1", "-z", "--untracked-files=all"):
raise ValueError(f"candidate worktree is not clean {stage}")
def _source_manifest(payload: Any, commit: str) -> tuple[dict[str, str], str]:
expected = {"schema_version", "candidate", "files", "summaries",
"claims_not_made", "manifest_payload_sha256"}
if not isinstance(payload, dict) or set(payload) != expected:
raise ValueError("source manifest shape is invalid")
if payload["schema_version"] != "yuqei.release-source-manifest.v1":
raise ValueError("unsupported source manifest schema")
embedded = _require_sha(payload["manifest_payload_sha256"], "source manifest payload SHA-256")
projection = {key: value for key, value in payload.items()
if key != "manifest_payload_sha256"}
if _hash_bytes(_canonical(projection).encode("utf-8")) != embedded:
raise ValueError("source manifest embedded payload SHA-256 mismatch")
candidate = payload["candidate"]
if (not isinstance(candidate, dict) or candidate.get("commit") != commit
or candidate.get("worktree_was_clean") is not True
or candidate.get("identity_policy") != "full_commit_oid_equals_HEAD"):
raise ValueError("source manifest candidate identity mismatch")
files = payload["files"]
if not isinstance(files, dict) or not isinstance(files.get("items"), list):
raise ValueError("source manifest file inventory is invalid")
if files.get("count") != len(files["items"]):
raise ValueError("source manifest file count is inconsistent")
index: dict[str, str] = {}
for row in files["items"]:
if not isinstance(row, dict) or set(row) != {
"path", "mode", "git_blob_oid", "size", "sha256",
}:
raise ValueError("source manifest file row is invalid")
relative = row["path"]
if (not isinstance(relative, str) or relative.startswith(("/", "\\"))
or ".." in PurePath(relative).parts or relative in index):
raise ValueError("source manifest contains an unsafe or duplicate path")
index[relative] = _require_sha(row["sha256"], f"source manifest {relative}")
return index, embedded
def _image_identities(payload: Any, commit: str, tag: str) -> tuple[list[dict[str, str]], str, str]:
if not isinstance(payload, dict):
raise ValueError("image identities must be an object")
schema = payload.get("schema_version")
if schema == "yuqei.rc06-image-identities.v1":
if set(payload) != {"schema_version", "commit", "tag", "images"}:
raise ValueError("image identities shape is invalid")
document_commit, document_tag, images = payload["commit"], payload["tag"], payload["images"]
elif schema == "yuqei.installable-release-manifest.v1":
candidate = payload.get("candidate")
if not isinstance(candidate, dict):
raise ValueError("installable image candidate is missing")
document_commit, document_tag, images = candidate.get("commit"), candidate.get("tag"), payload.get("images")
else:
raise ValueError("unsupported image identities schema")
if document_commit != commit or document_tag != tag:
raise ValueError("image identities candidate does not match commit/tag")
if not isinstance(images, list) or len(images) != len(REQUIRED_IMAGE_ROLES):
raise ValueError("image identities must contain four application images")
normalized, roles, manifests = [], set(), set()
for row in images:
if not isinstance(row, dict):
raise ValueError("image identity row is invalid")
if (schema == "yuqei.rc06-image-identities.v1"
and set(row) != {"role", "repository", "manifest_digest", "config_digest"}):
raise ValueError("compact image identity row shape is invalid")
role = row.get("role")
repository = row.get("repository") if schema == "yuqei.rc06-image-identities.v1" else (row.get("image_name") or row.get("registry_reference"))
manifest, config = row.get("manifest_digest"), row.get("config_digest")
if (not isinstance(role, str) or not isinstance(repository, str) or not repository
or not isinstance(manifest, str) or not manifest.startswith("sha256:")
or not isinstance(config, str) or not config.startswith("sha256:")):
raise ValueError("image identity row is incomplete")
_require_sha(manifest.removeprefix("sha256:"), f"image {role} manifest digest")
_require_sha(config.removeprefix("sha256:"), f"image {role} config digest")
if role in roles or manifest in manifests:
raise ValueError("duplicate image role or manifest digest")
roles.add(role)
manifests.add(manifest)
normalized.append({"role": role, "repository": repository,
"manifest_digest": manifest, "config_digest": config})
if roles != set(REQUIRED_IMAGE_ROLES):
raise ValueError("image roles mismatch")
normalized.sort(key=lambda item: REQUIRED_IMAGE_ROLES.index(item["role"]))
return normalized, _hash_bytes(_canonical(normalized).encode("utf-8")), schema
def load_candidate_binding(runner, *, commit: str, tag: str,
source_manifest_path: Path, source_manifest_sha256: str,
source_manifest_payload_sha256: str, image_identities_path: Path,
image_identities_sha256: str, image_identity_set_sha256: str,
stage: str = "candidate preflight") -> tuple[dict[str, Any], dict[str, str]]:
"""Load and reverify a built immutable candidate without creating evidence."""
root = Path(runner.ROOT).resolve()
_verify_git_candidate(root, commit, tag, stage)
source_raw = _artifact_bytes(source_manifest_path, root, "source manifest")
image_raw = _artifact_bytes(image_identities_path, root, "image identities")
if _hash_bytes(source_raw) != _require_sha(source_manifest_sha256, "source manifest raw SHA-256"):
raise ValueError("source manifest raw SHA-256 mismatch")
if _hash_bytes(image_raw) != _require_sha(image_identities_sha256, "image identities raw SHA-256"):
raise ValueError("image identities raw SHA-256 mismatch")
source_index, embedded = _source_manifest(_strict_json(source_raw, "source manifest"), commit)
if embedded != _require_sha(source_manifest_payload_sha256, "source manifest payload SHA-256"):
raise ValueError("source manifest payload SHA-256 argument mismatch")
images, identity_set, image_schema = _image_identities(
_strict_json(image_raw, "image identities"), commit, tag)
if identity_set != _require_sha(image_identity_set_sha256, "image identity-set SHA-256"):
raise ValueError("image identity-set SHA-256 mismatch")
binding = {
"schema_version": CANDIDATE_SCHEMA_VERSION, "commit": commit, "tag": tag,
"source_manifest": {"schema_version": "yuqei.release-source-manifest.v1",
"raw_sha256": source_manifest_sha256,
"payload_sha256": source_manifest_payload_sha256},
"image_identities": {"schema_version": image_schema,
"raw_sha256": image_identities_sha256,
"identity_set_sha256": image_identity_set_sha256,
"images": images},
}
return binding, source_index
def _candidate_binding(value: Mapping[str, Any]) -> dict[str, Any]:
if not isinstance(value, Mapping) or value.get("schema_version") != CANDIDATE_SCHEMA_VERSION:
raise ValueError("candidate binding is missing or unsupported")
result = json.loads(_canonical(dict(value)))
if set(result) != {"schema_version", "commit", "tag", "source_manifest", "image_identities"}:
raise ValueError("candidate binding shape is invalid")
if not _COMMIT_RE.fullmatch(str(result.get("commit", ""))):
raise ValueError("candidate binding commit is invalid")
if (not isinstance(result.get("tag"), str) or not _TAG_RE.fullmatch(result["tag"])
or ".." in result["tag"] or "//" in result["tag"]):
raise ValueError("candidate binding tag is invalid")
source, images = result.get("source_manifest"), result.get("image_identities")
if not isinstance(source, dict) or not isinstance(images, dict):
raise ValueError("candidate binding artifacts are invalid")
if set(source) != {"schema_version", "raw_sha256", "payload_sha256"}:
raise ValueError("candidate source binding shape is invalid")
if source["schema_version"] != "yuqei.release-source-manifest.v1":
raise ValueError("candidate source binding schema is invalid")
if set(images) != {"schema_version", "raw_sha256", "identity_set_sha256", "images"}:
raise ValueError("candidate image binding shape is invalid")
if images["schema_version"] not in {
"yuqei.rc06-image-identities.v1", "yuqei.installable-release-manifest.v1",
}:
raise ValueError("candidate image binding schema is invalid")
_require_sha(source.get("raw_sha256"), "candidate source raw SHA-256")
_require_sha(source.get("payload_sha256"), "candidate source payload SHA-256")
_require_sha(images.get("raw_sha256"), "candidate image raw SHA-256")
_require_sha(images.get("identity_set_sha256"), "candidate image set SHA-256")
if not isinstance(images.get("images"), list) or len(images["images"]) != 4:
raise ValueError("candidate binding must include four image identities")
normalized, roles, manifests = [], set(), set()
for row in images["images"]:
if not isinstance(row, dict) or set(row) != {
"role", "repository", "manifest_digest", "config_digest",
}:
raise ValueError("candidate normalized image row is invalid")
role, manifest, config = row["role"], row["manifest_digest"], row["config_digest"]
if role not in REQUIRED_IMAGE_ROLES or role in roles or manifest in manifests:
raise ValueError("candidate image role or manifest digest is invalid")
if not isinstance(row["repository"], str) or not row["repository"]:
raise ValueError("candidate image repository is invalid")
if not isinstance(manifest, str) or not manifest.startswith("sha256:"):
raise ValueError("candidate image manifest digest is invalid")
if not isinstance(config, str) or not config.startswith("sha256:"):
raise ValueError("candidate image config digest is invalid")
_require_sha(manifest.removeprefix("sha256:"), "candidate image manifest digest")
_require_sha(config.removeprefix("sha256:"), "candidate image config digest")
roles.add(role); manifests.add(manifest); normalized.append(row)
if roles != set(REQUIRED_IMAGE_ROLES):
raise ValueError("candidate image roles mismatch")
normalized.sort(key=lambda item: REQUIRED_IMAGE_ROLES.index(item["role"]))
if images["images"] != normalized:
raise ValueError("candidate image identities are not canonically ordered")
if _hash_bytes(_canonical(normalized).encode("utf-8")) != images["identity_set_sha256"]:
raise ValueError("candidate image identity-set digest is invalid")
return result
def verify_source_pins_at_candidate(runner, source_pins: Mapping[str, str],
source_index: Mapping[str, str], commit: str) -> None:
root = Path(runner.ROOT).resolve()
for relative, digest in source_pins.items():
if source_index.get(relative) != digest:
raise ValueError(f"source pin is absent from or differs from manifest: {relative}")
committed = _git(root, "show", f"{commit}:{relative}", binary=True)
if _hash_bytes(committed) != digest:
raise ValueError(f"source pin differs from candidate commit: {relative}")
def draft_request_projection(request: ContractDraftRequest | dict[str, Any]) -> dict[str, Any]:
"""Return stable model facts and template identities; omit workspace/lifecycle data."""
request = ContractDraftRequest.model_validate(request)
@@ -137,7 +412,10 @@ def _normalized_cases() -> list[dict[str, Any]]:
projection, template = _prepared_projection(case, repository)
policy = {"track_changes": True, "semantic_review": case["mode"] == "contract",
"auto_repair": case["auto_repair"]}
drafting.append({**case, "candidate_policy": policy,
failure_class = ("scene_drafting" if case["mode"] == "scene_evaluation"
else "contract_drafting")
drafting.append({**case, "failure_class": failure_class,
"candidate_policy": policy,
"template_content_sha256": projection["template_content_sha256"],
"template_input_schema_sha256": projection["template_schema_sha256"],
"values_sha256": draft_digest(case["values"]),
@@ -148,6 +426,7 @@ def _normalized_cases() -> list[dict[str, Any]]:
"case_id": case["case_id"], "source_case_id": case["case_id"],
"kind": case["operation"], "mode": case["operation"], "operation": case["operation"],
"operations": [case["operation"]], "provider_call_budget": 1,
"failure_class": case["operation"],
"request": case["request"].model_dump(mode="json"),
"request_sha256": case["request_sha256"], "expectations": case["expectations"],
**({"module": case["module"], "party_position": case["party_position"]}
@@ -164,35 +443,54 @@ def _operation_counts(cases: list[dict[str, Any]]) -> dict[str, int]:
return result
def build_plan(runner) -> dict[str, Any]:
def build_plan(runner, candidate_binding: Mapping[str, Any]) -> dict[str, Any]:
cases = _normalized_cases()
counts = _operation_counts(cases)
if len(cases) != 144 or counts != EXPECTED_OPERATIONS:
raise ValueError(f"case or operation inventory mismatch: {len(cases)}, {counts}")
if sum(case["provider_call_budget"] for case in cases) != EXPECTED_BUDGET:
raise ValueError("global provider call budget mismatch")
inventory = {case["case_id"]: case for case in cases}
if any(case_id not in inventory for case_id in CANARY_CASE_IDS):
raise ValueError("predefined RC-05 canary inventory drift")
return {
"schema_version": SCHEMA_VERSION, "prepared_at": datetime.now(UTC).isoformat(),
"model": "gpt-5.6-sol", "reasoning_effort": "medium", "timeout_seconds": 300,
"case_count": 144, "drafting_case_count": 120, "supplemental_case_count": 24,
"operation_limits": counts, "total_provider_call_budget": EXPECTED_BUDGET,
"candidate": _candidate_binding(candidate_binding),
"release_controls": {
"schema_version": RELEASE_CONTROLS_SCHEMA_VERSION,
"canary_case_ids": list(CANARY_CASE_IDS),
"failure_classes": {case["case_id"]: case["failure_class"] for case in cases},
},
"source_pins": _source_pins(runner), "cases": cases,
"limitations": ["合成隔离评测,不是生产发布或法律批准。",
"deterministic projection equality does not establish semantic or legal correctness."],
}
def validate_plan(runner, path: Path, expected_sha: str | None = None) -> tuple[dict[str, Any], str]:
def validate_plan(runner, path: Path, expected_sha: str | None = None,
candidate_binding: Mapping[str, Any] | None = None,
source_manifest_index: Mapping[str, str] | None = None) -> tuple[dict[str, Any], str]:
raw = Path(path).read_bytes()
actual = _hash_bytes(raw)
if expected_sha is not None and actual != expected_sha:
raise ValueError("plan raw SHA-256 mismatch")
plan = json.loads(raw)
expected = build_plan(runner)
plan = _strict_json(raw, "multiscene plan")
if plan.get("schema_version") != SCHEMA_VERSION:
raise ValueError("unsupported multiscene plan")
raise ValueError("legacy/unsupported multiscene plan is read-only diagnostic material; execution refused")
frozen_candidate = _candidate_binding(plan.get("candidate"))
if candidate_binding is not None and _canonical(frozen_candidate) != _canonical(
_candidate_binding(candidate_binding)):
raise ValueError("multiscene plan candidate binding mismatch")
expected = build_plan(runner, frozen_candidate)
left = {key: value for key, value in plan.items() if key != "prepared_at"}
right = {key: value for key, value in expected.items() if key != "prepared_at"}
if _canonical(left) != _canonical(right):
raise ValueError("multiscene plan content or source pins drift")
if source_manifest_index is not None:
verify_source_pins_at_candidate(
runner, plan["source_pins"], source_manifest_index,
frozen_candidate["commit"])
return plan, actual
+103 -18
View File
@@ -18,7 +18,7 @@ from pathlib import Path
from time import perf_counter
from uuid import uuid4
from multiscene_call_ledger import CallLedger, OPERATIONS
from multiscene_call_ledger import CallLedger, CallLedgerError, OPERATIONS
HISTORICAL_USAGE_SCHEMA = "rc05-historical-provider-usage.v3"
@@ -1041,18 +1041,12 @@ def _authoritative_registry(runner, supplied) -> Path:
def execute(runner, plan_path, expected_sha, output_dir, factory, *, resume=False,
case_ids=None, historical_usage=None, execution_mode=None,
run_registry_dir=None):
from multiscene_eval_plan import validate_plan
run_registry_dir=None, candidate_inputs=None):
from multiscene_eval_plan import (
load_candidate_binding, validate_plan, verify_source_pins_at_candidate,
)
from multiscene_supplemental_cases import run_supplemental_case
plan, digest = validate_plan(runner, plan_path, expected_sha)
inventory = {case["case_id"]: case for case in plan["cases"]}
selected = list(inventory) if case_ids is None else case_ids
if not selected or len(selected) != len(set(selected)) or set(selected) - set(inventory):
raise ValueError("selection must contain unique case IDs from the frozen plan")
out = Path(output_dir)
if out.exists() and not resume:
raise ValueError("fresh output directory required unless --resume is explicit")
if execution_mode not in {"real", "synthetic"}:
raise ValueError("execution_mode must explicitly be real or synthetic")
real = execution_mode == "real"
@@ -1060,6 +1054,24 @@ def execute(runner, plan_path, expected_sha, output_dir, factory, *, resume=Fals
raise ValueError("real execution requires the unwrapped frozen real provider factory")
if not real and getattr(factory, "_rc05_execution_mode", None) != "synthetic":
raise ValueError("synthetic execution requires an attested synthetic provider factory")
if real:
if not isinstance(candidate_inputs, dict):
raise ValueError("real RC-05 execution requires complete immutable candidate inputs")
candidate_binding, source_manifest_index = load_candidate_binding(
runner, **candidate_inputs, stage="before plan acceptance")
plan, digest = validate_plan(
runner, plan_path, expected_sha, candidate_binding,
source_manifest_index)
else:
candidate_binding = source_manifest_index = None
plan, digest = validate_plan(runner, plan_path, expected_sha)
inventory = {case["case_id"]: case for case in plan["cases"]}
selected = list(inventory) if case_ids is None else case_ids
if not selected or len(selected) != len(set(selected)) or set(selected) - set(inventory):
raise ValueError("selection must contain unique case IDs from the frozen plan")
out = Path(output_dir)
if out.exists() and not resume:
raise ValueError("fresh output directory required unless --resume is explicit")
if resume and not out.exists():
raise ValueError("resume requires the original evidence output directory")
if resume and not real and not (out / ".call-ledger/identity.json").is_file():
@@ -1081,7 +1093,13 @@ def execute(runner, plan_path, expected_sha, output_dir, factory, *, resume=Fals
ledger = CallLedger(out, digest, plan["operation_limits"],
{key: case["provider_call_budget"] for key, case in inventory.items()},
plan["source_pins"], real, historical_usage=historical_usage,
registry_root=authoritative_registry if real else None)
registry_root=authoritative_registry if real else None,
candidate_binding=plan.get("candidate") if real else None,
release_controls=plan.get("release_controls") if real else None)
control_before = None
if real:
control_before = ledger.authorize_selection(
selected, resume=resume, explicit_selection=case_ids is not None)
runner.write_x(out / f"selection-{uuid4().hex}.json", {
"plan_raw_sha256": digest, "selected": selected, "resume": resume,
"historical_usage": historical_usage,
@@ -1091,6 +1109,14 @@ def execute(runner, plan_path, expected_sha, output_dir, factory, *, resume=Fals
prior = _existing_case(runner, out, inventory[key], snapshot, ledger)
if prior["status"] != "not_attempted":
continue
if real:
current_binding, current_source_index = load_candidate_binding(
runner, **candidate_inputs, stage=f"before case {key}")
if current_binding != candidate_binding:
raise ValueError("candidate binding drift before next case")
verify_source_pins_at_candidate(
runner, plan["source_pins"], current_source_index,
candidate_binding["commit"])
# Source drift cannot consume the next case; never update a frozen plan.
_verify_source_pins(runner, plan["source_pins"], "before next case")
case, state, started = inventory[key], {"provider_call_count": 0}, perf_counter()
@@ -1112,6 +1138,8 @@ def execute(runner, plan_path, expected_sha, output_dir, factory, *, resume=Fals
runner.write_x(directory / "result.json", result)
_bind_case_outcome(runner, out, ledger, case, "result.json")
if result.get("passed") is not True:
if real:
ledger.trip_failure_class(key, "failed", "case result did not pass")
break
except Exception as error:
failure = {"case_id": key, "status": "stopped", "passed": False,
@@ -1120,9 +1148,32 @@ def execute(runner, plan_path, expected_sha, output_dir, factory, *, resume=Fals
"elapsed_seconds": round(perf_counter() - started, 3),
"automatic_retry": False, "not_legal_approval": True}
runner.write_x(directory / "failure.json", failure)
if real:
ledger.trip_failure_class(key, "failed", failure["error"][:1000])
_bind_case_outcome(runner, out, ledger, case, "failure.json")
break # Do not consume the remainder after a newly observed failure.
if real and control_before["phase"] == "canary_required":
canaries = plan["release_controls"]["canary_case_ids"]
canary_rows = [_existing_case(runner, out, inventory[key], ledger.snapshot(), ledger)
for key in canaries]
if all(row.get("status") == "completed" and row.get("passed") is True
and row.get("automated_quality_passed") is True
and row.get("automated_model_quality_release_passed") is True
and row.get("content_quality_release_passed") is True
for row in canary_rows):
ledger.complete_canary({
key: runner.sha256((out / key / "result.json").read_bytes())
for key in canaries
})
# A drift during the final case must not be hidden by a green summary.
if real:
final_binding, final_source_index = load_candidate_binding(
runner, **candidate_inputs, stage="before final summary")
if final_binding != candidate_binding:
raise ValueError("candidate binding drift before final summary")
verify_source_pins_at_candidate(
runner, plan["source_pins"], final_source_index,
candidate_binding["commit"])
_verify_source_pins(runner, plan["source_pins"], "before final summary")
snapshot = ledger.snapshot()
rows = [_existing_case(runner, out, inventory[key], snapshot, ledger) for key in inventory]
@@ -1251,12 +1302,31 @@ def execute(runner, plan_path, expected_sha, output_dir, factory, *, resume=Fals
def dispatch(runner, args):
from multiscene_eval_plan import build_plan, validate_plan
from multiscene_eval_plan import (
build_plan, load_candidate_binding, validate_plan,
verify_source_pins_at_candidate,
)
historical_usage_path = getattr(args, "historical_usage", None)
run_registry_dir = getattr(args, "run_registry_dir", None)
if args.inputs is not None or args.source_manifest is not None:
raise SystemExit("multiscene mode derives its complete inventory; no legacy input/manifest override")
if args.inputs is not None:
raise SystemExit("multiscene mode derives its complete inventory; no legacy input override")
candidate_inputs = {
"commit": getattr(args, "candidate_commit", None),
"tag": getattr(args, "candidate_tag", None),
"source_manifest_path": getattr(args, "source_manifest", None),
"source_manifest_sha256": getattr(args, "source_manifest_sha256", None),
"source_manifest_payload_sha256": getattr(
args, "source_manifest_payload_sha256", None),
"image_identities_path": getattr(args, "image_identities", None),
"image_identities_sha256": getattr(args, "image_identities_sha256", None),
"image_identity_set_sha256": getattr(
args, "image_identity_set_sha256", None),
}
candidate_supplied = any(value is not None for value in candidate_inputs.values())
candidate_complete = all(value is not None for value in candidate_inputs.values())
if candidate_supplied and not candidate_complete:
raise SystemExit("immutable candidate arguments must be supplied as one complete set")
if historical_usage_path is not None and not args.execute:
raise SystemExit("--historical-usage is only valid with --execute")
if run_registry_dir is not None and not args.execute:
@@ -1264,7 +1334,13 @@ def dispatch(runner, args):
if args.prepare:
if args.resume or args.case_id:
raise SystemExit("prepare freezes the complete matrix, not a selection")
plan = build_plan(runner)
if not candidate_complete:
raise SystemExit("RC-05 plan preparation requires a complete built candidate identity")
binding, source_index = load_candidate_binding(
runner, **candidate_inputs, stage="before plan preparation")
plan = build_plan(runner, binding)
verify_source_pins_at_candidate(
runner, plan["source_pins"], source_index, binding["commit"])
runner.write_x(args.plan, plan)
return {"prepared": True, "case_count": len(plan["cases"]),
"total_provider_call_budget": plan["total_provider_call_budget"],
@@ -1280,6 +1356,8 @@ def dispatch(runner, args):
raise SystemExit("real RC-05 execution requires --run-registry-dir")
historical_usage = load_historical_usage(runner, historical_usage_path)
execution_mode = "real"
if not candidate_complete:
raise SystemExit("real RC-05 execution requires a complete immutable candidate identity")
else:
from multiscene_supplemental_cases import synthetic_adapter_factory
factory = synthetic_adapter_factory(runner)
@@ -1287,9 +1365,16 @@ def dispatch(runner, args):
execution_mode = "synthetic"
return execute(runner, args.plan, args.plan_sha256, args.output_dir, factory,
resume=args.resume, case_ids=args.case_id, historical_usage=historical_usage,
execution_mode=execution_mode, run_registry_dir=run_registry_dir)
execution_mode=execution_mode, run_registry_dir=run_registry_dir,
candidate_inputs=candidate_inputs if args.execute else None)
if args.resume or args.case_id:
raise SystemExit("--resume/--case-id require execution mode")
plan, digest = validate_plan(runner, args.plan, args.plan_sha256)
if candidate_complete:
binding, source_index = load_candidate_binding(
runner, **candidate_inputs, stage="during plan validation")
plan, digest = validate_plan(
runner, args.plan, args.plan_sha256, binding, source_index)
else:
plan, digest = validate_plan(runner, args.plan, args.plan_sha256)
return {"validated": True, "case_count": len(plan["cases"]),
"model_calls": 0, "plan_raw_sha256": digest}
@@ -760,6 +760,13 @@ def main() -> int:
parser.add_argument("--plan", type=Path, required=True)
parser.add_argument("--inputs", type=Path)
parser.add_argument("--source-manifest", type=Path)
parser.add_argument("--source-manifest-sha256")
parser.add_argument("--source-manifest-payload-sha256")
parser.add_argument("--candidate-commit")
parser.add_argument("--candidate-tag")
parser.add_argument("--image-identities", type=Path)
parser.add_argument("--image-identities-sha256")
parser.add_argument("--image-identity-set-sha256")
parser.add_argument("--prepare", action="store_true")
parser.add_argument("--synthetic", action="store_true")
parser.add_argument("--execute", action="store_true")
@@ -784,6 +791,10 @@ def main() -> int:
raise SystemExit("--historical-usage requires --multiscene --execute")
if args.run_registry_dir is not None:
raise SystemExit("--run-registry-dir requires --multiscene --execute")
if any((args.source_manifest_sha256, args.source_manifest_payload_sha256,
args.candidate_commit, args.candidate_tag, args.image_identities,
args.image_identities_sha256, args.image_identity_set_sha256)):
raise SystemExit("immutable candidate arguments require --multiscene")
if args.source_manifest is not None and not args.prepare:
raise SystemExit("--source-manifest is only valid with --prepare")
if args.prepare:
@@ -2,6 +2,7 @@
from __future__ import annotations
import importlib.util
import json
import tempfile
from pathlib import Path
@@ -61,7 +62,23 @@ def test_isolated_sqlite_flow_preserves_primary_error_and_still_releases_databas
def _base_cases():
plan = multiscene_eval_plan.build_plan(RUNNER)
images = [{"role": role, "repository": f"registry.example/{role}",
"manifest_digest": f"sha256:{index + 1:064x}",
"config_digest": f"sha256:{index + 11:064x}"}
for index, role in enumerate(multiscene_eval_plan.REQUIRED_IMAGE_ROLES)]
image_set_sha = RUNNER.sha256(json.dumps(
images, ensure_ascii=False, sort_keys=True, separators=(",", ":")
).encode("utf-8"))
candidate = {
"schema_version": multiscene_eval_plan.CANDIDATE_SCHEMA_VERSION,
"commit": "a" * 40, "tag": "rc05-test-candidate",
"source_manifest": {"schema_version": "yuqei.release-source-manifest.v1",
"raw_sha256": "b" * 64, "payload_sha256": "c" * 64},
"image_identities": {"schema_version": "yuqei.rc06-image-identities.v1",
"raw_sha256": "d" * 64, "identity_set_sha256": image_set_sha,
"images": images},
}
plan = multiscene_eval_plan.build_plan(RUNNER, candidate)
cases = [case for case in plan["cases"]
if case.get("kind") == "drafting" and case.get("mode") == "contract"]
assert len(cases) == 30
@@ -71,6 +71,13 @@ def ledger(tmp_path, **overrides):
return LEDGER.CallLedger(**values)
def _controls(case_classes=None, canaries=None):
case_classes = case_classes or {"purchase": "contract", "service": "specialist"}
return {"schema_version": "multiscene-release-controls.v1",
"canary_case_ids": canaries or ["purchase"],
"failure_classes": case_classes}
def test_claim_precedes_call_and_finish_is_immutable(tmp_path):
calls = []
book = ledger(tmp_path)
@@ -89,6 +96,58 @@ def test_claim_precedes_call_and_finish_is_immutable(tmp_path):
book.claim("purchase", "draft", SHA_A)
def test_canary_transition_is_explicit_persisted_and_required_before_full(tmp_path):
book = ledger(tmp_path, release_controls=_controls())
with pytest.raises(LEDGER.CallLedgerError, match="explicit predefined canary"):
book.authorize_selection(["purchase", "service"], resume=False,
explicit_selection=False)
state = book.authorize_selection(["purchase"], resume=False,
explicit_selection=True)
assert state["phase"] == "canary_required"
token = book.claim("purchase", "draft", SHA_A)
book.finish(token, "succeeded", SHA_B)
outcome = tmp_path / "purchase-result.json"
outcome.write_text("{}", encoding="utf-8")
book.bind_case_outcome("purchase", "result.json", SHA_A, {}, ["draft"])
with pytest.raises(LEDGER.CallLedgerError, match="outcome does not match"):
book.complete_canary({"purchase": SHA_B})
book.complete_canary({"purchase": SHA_A})
assert book.control_snapshot()["phase"] == "full_authorized"
resumed = ledger(tmp_path, release_controls=_controls())
assert resumed.control_snapshot()["phase"] == "full_authorized"
resumed.authorize_selection(["service"], resume=True, explicit_selection=True)
def test_failure_class_breaker_persists_and_unknown_stays_in_denominator(tmp_path):
classes = {"purchase": "contract", "service": "specialist", "third": "specialist"}
book = ledger(tmp_path, case_limits={key: 1 for key in classes},
operation_limits={"draft": 3},
release_controls=_controls(classes, ["purchase"]))
book.authorize_selection(["purchase"], resume=False, explicit_selection=True)
token = book.claim("purchase", "draft", SHA_A)
book.finish(token, "succeeded", SHA_B)
book.bind_case_outcome("purchase", "result.json", SHA_A, {}, ["draft"])
book.complete_canary({"purchase": SHA_A})
book.claim("service", "draft", SHA_A) # interrupted provider call => unknown
resumed = ledger(tmp_path, case_limits={key: 1 for key in classes},
operation_limits={"draft": 3},
release_controls=_controls(classes, ["purchase"]))
with pytest.raises(LEDGER.CallLedgerError, match="blocked failure classes"):
resumed.authorize_selection(["third"], resume=True, explicit_selection=True)
snapshot = resumed.snapshot()
assert snapshot["unknown"] == 1
assert snapshot["release_controls"]["blocked_failure_classes"] == ["specialist"]
def test_legacy_ledger_schema_is_explicitly_read_only(tmp_path):
root = tmp_path / ".call-ledger"
root.mkdir()
(root / "identity.json").write_text(
json.dumps({"schema_version": "multiscene-call-ledger.v1"}), encoding="utf-8")
with pytest.raises(LEDGER.LedgerIdentityError, match="read-only diagnostic"):
ledger(tmp_path)
def test_unknown_claim_blocks_automatic_replay_but_other_operation_is_distinct(tmp_path):
book = ledger(tmp_path)
book.claim("purchase", "semantic_review", SHA_A)
+74 -3
View File
@@ -6,6 +6,23 @@ import pytest
import multiscene_eval_plan as plan_module
def _candidate():
images = [{"role": role, "repository": f"registry.example/{role}",
"manifest_digest": f"sha256:{index + 1:064x}",
"config_digest": f"sha256:{index + 11:064x}"}
for index, role in enumerate(plan_module.REQUIRED_IMAGE_ROLES)]
image_set_sha = hashlib.sha256(json.dumps(
images, ensure_ascii=False, sort_keys=True, separators=(",", ":")
).encode("utf-8")).hexdigest()
return {"schema_version": plan_module.CANDIDATE_SCHEMA_VERSION,
"commit": "a" * 40, "tag": "rc05-test-candidate",
"source_manifest": {"schema_version": "yuqei.release-source-manifest.v1",
"raw_sha256": "b" * 64, "payload_sha256": "c" * 64},
"image_identities": {"schema_version": "yuqei.rc06-image-identities.v1",
"raw_sha256": "d" * 64, "identity_set_sha256": image_set_sha,
"images": images}}
def _write(path, value):
path.write_text(json.dumps(value, ensure_ascii=False), encoding="utf-8")
return hashlib.sha256(path.read_bytes()).hexdigest()
@@ -13,10 +30,13 @@ def _write(path, value):
@pytest.fixture(scope="module")
def plan():
return plan_module.build_plan(object())
return plan_module.build_plan(object(), _candidate())
def test_plan_freezes_144_cases_280_calls_and_real_request_projections(plan):
assert plan["schema_version"] == "multiscene-evaluation-plan.v2"
assert plan["candidate"] == _candidate()
assert plan["release_controls"]["canary_case_ids"] == list(plan_module.CANARY_CASE_IDS)
assert plan["case_count"] == 144
assert plan["total_provider_call_budget"] == 280
assert plan["operation_limits"] == plan_module.EXPECTED_OPERATIONS
@@ -54,7 +74,7 @@ def test_validate_rebuilds_cases_budget_projection_and_source_pins(plan, tmp_pat
assert plan_module.validate_plan(object(), path)[1] == digest
@pytest.mark.parametrize("mutation", ["case", "budget", "projection", "pin", "sha"])
@pytest.mark.parametrize("mutation", ["case", "budget", "projection", "pin", "candidate", "controls", "sha"])
def test_validate_rejects_inventory_budget_projection_pin_or_user_sha_drift(plan, tmp_path, mutation):
changed = json.loads(json.dumps(plan))
if mutation == "case":
@@ -67,10 +87,52 @@ def test_validate_rejects_inventory_budget_projection_pin_or_user_sha_drift(plan
elif mutation == "pin":
first = next(iter(changed["source_pins"]))
changed["source_pins"][first] = "0" * 64
elif mutation == "candidate":
changed["candidate"]["commit"] = "f" * 40
elif mutation == "controls":
changed["release_controls"]["canary_case_ids"].pop()
path = tmp_path / f"{mutation}.json"
digest = _write(path, changed)
with pytest.raises(ValueError):
plan_module.validate_plan(object(), path, "f" * 64 if mutation == "sha" else digest)
plan_module.validate_plan(
object(), path, "f" * 64 if mutation == "sha" else digest,
_candidate() if mutation == "candidate" else None)
def test_legacy_plan_is_explicitly_read_only(plan, tmp_path):
legacy = {**plan, "schema_version": "multiscene-evaluation-plan.v1"}
path = tmp_path / "legacy.json"
_write(path, legacy)
with pytest.raises(ValueError, match="read-only diagnostic"):
plan_module.validate_plan(object(), path)
def test_git_candidate_gate_requires_exact_head_tag_and_clean_tree(monkeypatch, tmp_path):
commit = "a" * 40
def clean_git(_root, *args, **_kwargs):
if args[0] == "rev-parse":
return commit
if args[0] == "status":
return ""
raise AssertionError(args)
monkeypatch.setattr(plan_module, "_git", clean_git)
plan_module._verify_git_candidate(tmp_path, commit, "rc05-candidate", "test")
def dirty_git(_root, *args, **_kwargs):
return " M tracked.py" if args[0] == "status" else commit
monkeypatch.setattr(plan_module, "_git", dirty_git)
with pytest.raises(ValueError, match="not clean"):
plan_module._verify_git_candidate(tmp_path, commit, "rc05-candidate", "test")
monkeypatch.setattr(
plan_module, "_git",
lambda _root, *args, **_kwargs: ("b" * 40 if "refs/tags" in args[-1] else commit),
)
with pytest.raises(ValueError, match="HEAD/tag mismatch"):
plan_module._verify_git_candidate(tmp_path, commit, "rc05-candidate", "test")
def test_source_pins_are_repo_relative_existing_files_without_runtime_or_secrets(plan):
@@ -83,3 +145,12 @@ def test_source_pins_are_repo_relative_existing_files_without_runtime_or_secrets
assert "yuqei-infra/scripts/run-core-drafting-smoke.py" in plan["source_pins"]
assert "yuqei-contract-platform/services/contract-api/tests/test_semantic_review_cross_service_flow.py" \
in plan["source_pins"]
def test_quality_gate_derives_each_alembic_head_and_checks_upgraded_revision():
workflow = (plan_module.ROOT / ".github/workflows/build-test-quality-gate.yml").read_text(
encoding="utf-8")
assert "20260909_0079 (head)" not in workflow
assert "20260826_0028 (head)" not in workflow
assert workflow.count('expected_head="$(python -m alembic heads') == 2
assert workflow.count('test "$actual_head" = "$expected_head"') == 2
@@ -43,10 +43,28 @@ def _plan(tmp_path, count=3):
"kind": "supplemental", "operation": "draft",
"operations": ["draft"]}
for index in range(count)]
images = [{"role": role, "repository": f"example/{role}",
"manifest_digest": f"sha256:{index + 1:064x}",
"config_digest": f"sha256:{index + 11:064x}"}
for index, role in enumerate(("contract-web", "contract-api",
"ai-platform-api", "ai-worker"))]
candidate = {"schema_version": "multiscene-candidate-binding.v1",
"commit": "1" * 40, "tag": "test-candidate",
"source_manifest": {"schema_version": "yuqei.release-source-manifest.v1",
"raw_sha256": "2" * 64, "payload_sha256": "3" * 64},
"image_identities": {"schema_version": "yuqei.rc06-image-identities.v1",
"raw_sha256": "4" * 64,
"identity_set_sha256": _sha(json.dumps(
images, ensure_ascii=False, sort_keys=True, separators=(",", ":")
).encode("utf-8")), "images": images}}
return {
"cases": cases,
"operation_limits": {"draft": count},
"source_pins": {"source.py": _sha(source.read_bytes())},
"candidate": candidate,
"release_controls": {"schema_version": "multiscene-release-controls.v1",
"canary_case_ids": ["case-0"],
"failure_classes": {case["case_id"]: "draft" for case in cases}},
}
@@ -55,6 +73,8 @@ def _install_modules(monkeypatch, plan, digest="a" * 64, calls=None):
plan_module = types.ModuleType("multiscene_eval_plan")
plan_module.validate_plan = lambda *_args: (plan, digest)
plan_module.build_plan = lambda *_args: plan
plan_module.load_candidate_binding = lambda *_args, **_kwargs: (plan["candidate"], {})
plan_module.verify_source_pins_at_candidate = lambda *_args, **_kwargs: None
supplemental = types.ModuleType("multiscene_supplemental_cases")
def run_case(case, directory, _factory, state, *, ledger, runner):
@@ -99,6 +119,16 @@ def _runner(tmp_path):
)
def _candidate_inputs():
return {"commit": "1" * 40, "tag": "test-candidate",
"source_manifest_path": Path("source.json"),
"source_manifest_sha256": "2" * 64,
"source_manifest_payload_sha256": "3" * 64,
"image_identities_path": Path("images.json"),
"image_identities_sha256": "4" * 64,
"image_identity_set_sha256": "5" * 64}
def test_selected_execution_keeps_full_denominator_and_cannot_make_all_passed(monkeypatch, tmp_path):
plan = _plan(tmp_path)
calls = _install_modules(monkeypatch, plan)
@@ -333,19 +363,23 @@ def test_real_execution_requires_and_binds_known_historical_usage(monkeypatch, t
EVAL.execute(_runner(tmp_path), tmp_path / "plan.json", "a" * 64,
tmp_path / "wrong-registry", RealFactory,
historical_usage=_historical_usage(), execution_mode="real",
run_registry_dir=wrong_registry)
run_registry_dir=wrong_registry, case_ids=["case-0"],
candidate_inputs=_candidate_inputs())
with pytest.raises(ValueError, match="historical usage receipt"):
EVAL.execute(_runner(tmp_path), tmp_path / "plan.json", "a" * 64,
tmp_path / "missing-history", RealFactory, execution_mode="real",
run_registry_dir=registry)
run_registry_dir=registry, case_ids=["case-0"],
candidate_inputs=_candidate_inputs())
with pytest.raises(ValueError, match="historical usage declaration"):
EVAL.execute(_runner(tmp_path), tmp_path / "plan.json", "a" * 64,
tmp_path / "wrong-history", RealFactory,
historical_usage={**_historical_usage(), "provider_call_count": 3},
execution_mode="real", run_registry_dir=registry)
execution_mode="real", run_registry_dir=registry, case_ids=["case-0"],
candidate_inputs=_candidate_inputs())
summary = EVAL.execute(_runner(tmp_path), tmp_path / "plan.json", "a" * 64,
tmp_path / "bound-history", RealFactory, historical_usage=_historical_usage(),
execution_mode="real", run_registry_dir=registry)
execution_mode="real", run_registry_dir=registry, case_ids=["case-0"],
candidate_inputs=_candidate_inputs())
assert calls == ["case-0"]
assert summary["current_selection_passed"] is True
assert summary["selected_passed"] is False
@@ -378,6 +412,32 @@ def test_real_execution_requires_and_binds_known_historical_usage(monkeypatch, t
assert len(identity["historical_usage"]["attempts_sha256"]) == 64
def test_real_execution_rejects_unbound_candidate_and_initial_full_dispatch(
monkeypatch, tmp_path,
):
plan = _plan(tmp_path, count=2)
calls = _install_modules(monkeypatch, plan)
registry = tmp_path.parent / f"{tmp_path.name}-rc05-preflight-registry"
registry.mkdir()
monkeypatch.setenv(EVAL.REAL_RUN_REGISTRY_ENV, str(registry))
with pytest.raises(ValueError, match="complete immutable candidate inputs"):
EVAL.execute(
_runner(tmp_path), tmp_path / "plan.json", "a" * 64,
tmp_path / "unbound", RealFactory,
historical_usage=_historical_usage(), execution_mode="real",
run_registry_dir=registry, case_ids=["case-0"],
)
assert calls == []
with pytest.raises(EVAL.CallLedgerError, match="explicit predefined canary"):
EVAL.execute(
_runner(tmp_path), tmp_path / "plan.json", "a" * 64,
tmp_path / "initial-full", RealFactory,
historical_usage=_historical_usage(), execution_mode="real",
run_registry_dir=registry, candidate_inputs=_candidate_inputs(),
)
assert calls == []
def test_authoritative_campaign_registry_requires_explicit_empty_directory(
monkeypatch, tmp_path,
):
@@ -388,7 +448,8 @@ def test_authoritative_campaign_registry_requires_explicit_empty_directory(
with pytest.raises(ValueError, match="must be pre-created"):
EVAL.execute(_runner(tmp_path), tmp_path / "plan.json", "a" * 64,
tmp_path / "out", RealFactory, historical_usage=_historical_usage(),
execution_mode="real", run_registry_dir=registry)
execution_mode="real", run_registry_dir=registry, case_ids=["case-0"],
candidate_inputs=_candidate_inputs())
assert calls == []
@@ -889,6 +950,7 @@ def test_foreign_plan_campaign_attempt_blocks_current_plan_green(monkeypatch, tm
_runner(tmp_path), tmp_path / "plan.json", "2" * 64,
tmp_path / "current-output", RealFactory,
historical_usage=usage, execution_mode="real", run_registry_dir=registry,
case_ids=["case-0"], candidate_inputs=_candidate_inputs(),
)
assert summary["ledger"]["campaign"]["attempted"] == 2
assert summary["ledger"]["campaign"]["foreign_plan_attempted"] == 1