1093 lines
54 KiB
Python
1093 lines
54 KiB
Python
#!/usr/bin/env python3
|
|
"""Prove Leo can compose and reason over source-derived KB rows in a disposable clone."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import asyncio
|
|
import hashlib
|
|
import json
|
|
import sys
|
|
import traceback
|
|
import uuid
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
from typing import Any
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
REPO_ROOT = HERE.parent
|
|
sys.path.insert(0, str(HERE))
|
|
|
|
import apply_proposal as ap # noqa: E402
|
|
import run_approve_claim_clone_canary as guarded # noqa: E402
|
|
import run_leo_clone_bound_handler_checkpoint as bound # noqa: E402
|
|
import run_leo_clone_lifecycle_checkpoint as lifecycle # noqa: E402
|
|
import stage_normalized_proposal as normalized_stage # noqa: E402
|
|
|
|
SCHEMA = "livingip.leoCloneCompositionCheckpoint.v1"
|
|
PACKET_PREFIX = "COMPOSITION_PACKET:"
|
|
STATE_PREFIX = "COMPOSITION_STATE:"
|
|
TABLES = lifecycle.LIFECYCLE_TABLES
|
|
PRODUCTION_TABLES = bound.COUNT_TABLES
|
|
|
|
|
|
def _stable_uuid(run_marker: str, label: str) -> str:
|
|
return str(uuid.uuid5(uuid.NAMESPACE_URL, f"livingip:leo-composition:{run_marker}:{label}"))
|
|
|
|
|
|
def build_source_fixture(run_marker: str) -> dict[str, Any]:
|
|
project = "AURORA-" + hashlib.sha256(run_marker.encode()).hexdigest()[:10].upper()
|
|
document_text = (
|
|
f"{project} operating note. A knowledge change becomes canonical only after operator review and guarded "
|
|
"apply. Approval alone leaves canonical rows unchanged."
|
|
)
|
|
post_text = (
|
|
f"For {project}, reviewer approval is enough: a Leo proposal becomes canonical immediately, and no apply "
|
|
"step is needed."
|
|
)
|
|
return {
|
|
"project": project,
|
|
"document": {
|
|
"source_key": "operating_document",
|
|
"source_type": "article",
|
|
"title": f"{project} operating note",
|
|
"storage_path": f"/composition/{run_marker}/operating-note.txt",
|
|
"text": document_text,
|
|
"content_sha256": hashlib.sha256(document_text.encode()).hexdigest(),
|
|
},
|
|
"post": {
|
|
"source_key": "contradictory_post",
|
|
"source_type": "post",
|
|
"title": f"{project} contradictory post",
|
|
"url": f"https://x.example.test/{run_marker}",
|
|
"text": post_text,
|
|
"content_sha256": hashlib.sha256(post_text.encode()).hexdigest(),
|
|
},
|
|
}
|
|
|
|
|
|
def build_prompts(fixture: dict[str, Any], conversation_marker: str) -> dict[str, str]:
|
|
document = fixture["document"]
|
|
post = fixture["post"]
|
|
extraction_schema = {
|
|
"claim_candidates": [
|
|
{
|
|
"claim_key": "stable_key",
|
|
"text": "atomic claim",
|
|
"type": "structural|empirical|normative|meta|concept",
|
|
"confidence": 0.0,
|
|
"evidence": [{"source_key": "operating_document", "role": "grounds"}],
|
|
"edges": [{"edge_type": "contradicts", "target": "other_claim_key"}],
|
|
}
|
|
],
|
|
"source_candidates": [
|
|
{
|
|
"source_key": "operating_document|contradictory_post",
|
|
"source_type": "article|post",
|
|
"storage_path": "document path or null",
|
|
"url": "post URL or null",
|
|
"title": "source title",
|
|
"source_quality": "quality assessment",
|
|
"key_excerpt": "exact source substring",
|
|
"content_sha256": "supplied source SHA-256",
|
|
}
|
|
],
|
|
"dedupe_conflict_assessment": {
|
|
"database_search_query": "query actually used",
|
|
"potential_existing_claim_ids": [],
|
|
"conflicts": [
|
|
{
|
|
"from_claim_key": "claim key",
|
|
"to_claim_key": "claim key",
|
|
"relationship": "contradicts",
|
|
}
|
|
],
|
|
},
|
|
}
|
|
return {
|
|
"extract": (
|
|
f"Cory-style source intake for {fixture['project']}. Remember this conversation-only marker for the "
|
|
f"later reasoning turn: {conversation_marker}. First search the canonical KB for this project's topic so "
|
|
"you can report possible duplicates. Then extract atomic claims, exact evidence excerpts, provenance, "
|
|
"source quality, and explicit conflicts from the two new sources below. Do not stage, approve, apply, or "
|
|
"claim canonicality. Use source_key operating_document for the document and contradictory_post for the "
|
|
"post. Preserve the supplied SHA-256 values exactly. Every claim needs evidence; represent the sources' "
|
|
"conflict with a contradicts edge between claim keys. Return normal prose, then one final compact JSON "
|
|
f"line beginning {PACKET_PREFIX}. Schema: {json.dumps(extraction_schema, sort_keys=True)}\n\n"
|
|
f"DOCUMENT locator={document['storage_path']} sha256={document['content_sha256']}\n"
|
|
f"{document['text']}\n\nPOST locator={post['url']} sha256={post['content_sha256']}\n{post['text']}"
|
|
),
|
|
"reason": (
|
|
f"Cory asks about {fixture['project']}: 'approval means it is already in Leo, right?' Answer from the "
|
|
"canonical KB, explain the source conflict and which evidence is stronger, and distinguish approved from "
|
|
f"applied. Search without asking me for IDs. Then search proposals using {fixture['project']}, open the "
|
|
"matching proposal, inspect every relevant claim, run evidence for each claim with --format json so you can "
|
|
"report canonical source UUIDs rather than source keys, and run edges with --format json for at least one "
|
|
"claim. Recall the conversation-only marker from the earlier source-intake turn. End with one compact JSON "
|
|
f"line beginning {STATE_PREFIX}. Set phase exactly to 'final', proposal_id to the full discovered proposal "
|
|
"UUID, conversation_marker to the exact recalled marker, status exactly to 'applied', claim_ids and "
|
|
"source_ids to every full canonical UUID discovered from the DB, and conflict_edge to one real canonical "
|
|
"edge with from_claim, to_claim, edge_type."
|
|
),
|
|
}
|
|
|
|
|
|
def parse_prefixed_json(reply: str, prefix: str) -> dict[str, Any]:
|
|
for line in reversed(reply.splitlines()):
|
|
stripped = line.strip()
|
|
if not stripped.startswith(prefix):
|
|
continue
|
|
try:
|
|
value = json.loads(stripped[len(prefix) :].strip())
|
|
except json.JSONDecodeError as exc:
|
|
raise bound.CheckpointError(f"invalid {prefix} JSON: {exc}") from exc
|
|
if not isinstance(value, dict):
|
|
raise bound.CheckpointError(f"{prefix} payload must be a JSON object")
|
|
return value
|
|
raise bound.CheckpointError(f"reply did not contain a {prefix} line")
|
|
|
|
|
|
def validate_composition_packet(packet: dict[str, Any], fixture: dict[str, Any]) -> dict[str, Any]:
|
|
claims = packet.get("claim_candidates")
|
|
sources = packet.get("source_candidates")
|
|
assessment = packet.get("dedupe_conflict_assessment")
|
|
if not isinstance(claims, list) or len(claims) < 2:
|
|
raise bound.CheckpointError("composition packet must contain at least two claim candidates")
|
|
if not isinstance(sources, list) or len(sources) != 2:
|
|
raise bound.CheckpointError("composition packet must contain exactly the two supplied source candidates")
|
|
if not isinstance(assessment, dict) or not str(assessment.get("database_search_query") or "").strip():
|
|
raise bound.CheckpointError("composition packet must record the canonical dedupe search query")
|
|
source_by_key = {str(source.get("source_key")): source for source in sources if isinstance(source, dict)}
|
|
if set(source_by_key) != {"operating_document", "contradictory_post"}:
|
|
raise bound.CheckpointError("composition source keys do not match the supplied sources")
|
|
for label, fixture_key, locator_key in (
|
|
("operating_document", "document", "storage_path"),
|
|
("contradictory_post", "post", "url"),
|
|
):
|
|
source = source_by_key[label]
|
|
expected = fixture[fixture_key]
|
|
excerpt = str(source.get("key_excerpt") or "")
|
|
if not excerpt or excerpt not in expected["text"]:
|
|
raise bound.CheckpointError(f"{label} excerpt is not an exact substring of the supplied source")
|
|
if source.get("content_sha256") != expected["content_sha256"]:
|
|
raise bound.CheckpointError(f"{label} content SHA-256 does not match the supplied bytes")
|
|
if source.get(locator_key) != expected[locator_key]:
|
|
raise bound.CheckpointError(f"{label} locator does not match the supplied source")
|
|
claim_keys: set[str] = set()
|
|
evidence_source_keys: set[str] = set()
|
|
conflict_pairs: set[tuple[str, str]] = set()
|
|
for claim in claims:
|
|
if not isinstance(claim, dict):
|
|
raise bound.CheckpointError("every claim candidate must be an object")
|
|
claim_key = str(claim.get("claim_key") or "").strip()
|
|
claim_text = str(claim.get("text") or claim.get("headline") or "").strip()
|
|
if not claim_key or not claim_text or claim_key in claim_keys:
|
|
raise bound.CheckpointError("claim candidates need unique keys and nonempty atomic text")
|
|
claim_keys.add(claim_key)
|
|
evidence = claim.get("evidence")
|
|
if not isinstance(evidence, list) or not evidence:
|
|
raise bound.CheckpointError(f"claim {claim_key} has no evidence")
|
|
for item in evidence:
|
|
if isinstance(item, dict) and item.get("source_key"):
|
|
evidence_source_keys.add(str(item["source_key"]))
|
|
for edge in claim.get("edges") or []:
|
|
if isinstance(edge, dict) and edge.get("edge_type") == "contradicts" and edge.get("target"):
|
|
conflict_pairs.add((claim_key, str(edge["target"])))
|
|
if evidence_source_keys != set(source_by_key):
|
|
raise bound.CheckpointError("both supplied sources must ground at least one extracted claim")
|
|
if not any(left in claim_keys and right in claim_keys and left != right for left, right in conflict_pairs):
|
|
raise bound.CheckpointError("composition packet did not encode the source conflict as a claim edge")
|
|
conflicts = assessment.get("conflicts")
|
|
if not isinstance(conflicts, list) or not conflicts:
|
|
raise bound.CheckpointError("dedupe/conflict assessment omitted the observed contradiction")
|
|
return {
|
|
"claim_count": len(claims),
|
|
"source_count": len(sources),
|
|
"claim_keys": sorted(claim_keys),
|
|
"evidence_source_keys": sorted(evidence_source_keys),
|
|
"conflict_pairs": sorted([list(pair) for pair in conflict_pairs]),
|
|
}
|
|
|
|
|
|
def build_parent_packet(
|
|
packet: dict[str, Any], fixture: dict[str, Any], run_marker: str, agent_id: str
|
|
) -> dict[str, Any]:
|
|
return {
|
|
"id": _stable_uuid(run_marker, "rich-source-packet"),
|
|
"proposal_type": "attach_evidence",
|
|
"status": "pending_review",
|
|
"proposed_by_handle": "leo",
|
|
"proposed_by_agent_id": agent_id,
|
|
"channel": "clone_composition",
|
|
"source_ref": f"clone-composition:{run_marker}",
|
|
"rationale": f"Leo source composition packet for {fixture['project']}.",
|
|
"payload": packet,
|
|
}
|
|
|
|
|
|
def resolve_leo_agent_id(container: str, database: str) -> str:
|
|
result = bound._psql_json(
|
|
container,
|
|
database,
|
|
"""\\set ON_ERROR_STOP on
|
|
begin transaction read only;
|
|
select jsonb_build_object(
|
|
'agent_id', (
|
|
select id::text from public.agents
|
|
where lower(handle) in ('leo', 'leoclean')
|
|
order by case lower(handle) when 'leo' then 0 else 1 end, id
|
|
limit 1
|
|
)
|
|
)::text;
|
|
rollback;
|
|
""",
|
|
)
|
|
try:
|
|
return str(uuid.UUID(str(result.get("agent_id"))))
|
|
except ValueError as exc:
|
|
raise bound.CheckpointError("clone has no canonical leo/leoclean agent row for attribution") from exc
|
|
|
|
|
|
def _replace_generic_receipt(skill_text: str) -> str:
|
|
return skill_text.replace(
|
|
"""End the final answer with one machine-readable line using the full proposal
|
|
UUID and the row state you observed:
|
|
`KB_STATE: {"proposal_id":"<uuid>","status":"pending_review|approved|applied","applied":false}`
|
|
|
|
""",
|
|
"",
|
|
)
|
|
|
|
|
|
def patch_composition_bridge(
|
|
temp_profile: Path, *, container: str, database: str, fixture: dict[str, Any]
|
|
) -> dict[str, Any]:
|
|
bridge = bound.patch_temp_bridge(temp_profile, container, database)
|
|
skill_path = Path(bridge["bridge_skill_path"])
|
|
skill_text = _replace_generic_receipt(skill_path.read_text(encoding="utf-8"))
|
|
binding = f"""
|
|
## Disposable Source Composition Checkpoint
|
|
|
|
This no-send profile is testing source composition for `{fixture["project"]}`
|
|
against one disposable clone. You may only search and read the clone. You cannot
|
|
stage, review, approve, apply, send, or access production. The prompt defines
|
|
either a `COMPOSITION_PACKET:` extraction receipt or a `COMPOSITION_STATE:`
|
|
canonical reasoning receipt; emit only the receipt requested by that turn.
|
|
"""
|
|
skill_text = bound.inject_skill_binding(skill_text, binding.lstrip())
|
|
bound._detach_and_write(skill_path, skill_text, mode=0o600)
|
|
bridge["bridge_skill_sha256"] = bound.sha256_bytes(skill_text.encode())
|
|
bridge["composition_project"] = fixture["project"]
|
|
return bridge
|
|
|
|
|
|
def _exclude_rows(rows: list[dict[str, Any]], columns: tuple[str, ...]) -> str:
|
|
if not rows:
|
|
return "true"
|
|
matches = []
|
|
for row in rows:
|
|
matches.append(
|
|
"(" + " and ".join(f"t.{column}::text = {ap.sql_literal(str(row[column]))}" for column in columns) + ")"
|
|
)
|
|
return "not (" + " or ".join(matches) + ")"
|
|
|
|
|
|
def unrelated_guard_snapshot(container: str, database: str, child: dict[str, Any]) -> dict[str, Any]:
|
|
rows = guarded._expected_bundle_rows(child["payload"]["apply_payload"])
|
|
predicates = {
|
|
"public.claims": _exclude_rows(rows["claims"], ("id",)),
|
|
"public.sources": _exclude_rows(rows["sources"], ("id",)),
|
|
"public.claim_evidence": _exclude_rows(rows["claim_evidence"], ("claim_id", "source_id", "role")),
|
|
"public.claim_edges": _exclude_rows(rows["claim_edges"], ("from_claim", "to_claim", "edge_type")),
|
|
"public.reasoning_tools": _exclude_rows(rows["reasoning_tools"], ("id",)),
|
|
"kb_stage.kb_proposals": f"t.id <> {ap.sql_literal(child['id'])}::uuid",
|
|
lifecycle.APPROVAL_TABLE: f"t.proposal_id <> {ap.sql_literal(child['id'])}::uuid",
|
|
}
|
|
pairs = [
|
|
f"""'{table}', jsonb_build_object(
|
|
'count', (select count(*) from {table} t where {predicates[table]}),
|
|
'rowset_md5', (select md5(coalesce(string_agg(to_jsonb(t)::text, E'\\n'
|
|
order by to_jsonb(t)::text), '')) from {table} t where {predicates[table]})
|
|
)"""
|
|
for table in TABLES
|
|
]
|
|
return bound._psql_json(
|
|
container,
|
|
database,
|
|
"""\\set ON_ERROR_STOP on
|
|
begin transaction read only;
|
|
select jsonb_build_object(
|
|
"""
|
|
+ ",\n ".join(pairs)
|
|
+ """
|
|
)::text;
|
|
rollback;
|
|
""",
|
|
)
|
|
|
|
|
|
def _turn_args(args: argparse.Namespace, phase: str, prompt: str) -> argparse.Namespace:
|
|
return SimpleNamespace(
|
|
chat_id=args.chat_id,
|
|
user_id=args.user_id,
|
|
user_name=args.user_name,
|
|
prompt=prompt,
|
|
prompt_id=phase,
|
|
run_id=args.run_marker,
|
|
turn_timeout=args.turn_timeout,
|
|
)
|
|
|
|
|
|
async def run_turn(
|
|
args: argparse.Namespace,
|
|
temp_profile: Path,
|
|
*,
|
|
phase: str,
|
|
prompt: str,
|
|
provider_environment: dict[str, str],
|
|
tool_log: Path,
|
|
) -> dict[str, Any]:
|
|
proof_args = {
|
|
"run_nonce": args.tool_run_nonce,
|
|
"database_identity": args.target_database_identity,
|
|
"require_database_read_only": False,
|
|
}
|
|
before = bound.read_tool_proof(tool_log, args.bound_container_id, args.db, **proof_args)
|
|
gateway = await bound.invoke_gateway_subprocess(
|
|
_turn_args(args, phase, prompt), temp_profile, provider_environment=provider_environment
|
|
)
|
|
after = bound.read_tool_proof(tool_log, args.bound_container_id, args.db, **proof_args)
|
|
return {
|
|
"phase": phase,
|
|
"prompt_sha256": hashlib.sha256(prompt.encode()).hexdigest(),
|
|
"prompt": prompt,
|
|
"gateway": gateway,
|
|
"tool_invocations": after["invocations"][before["invocation_count"] :],
|
|
}
|
|
|
|
|
|
def _argvs(turn: dict[str, Any]) -> list[list[str]]:
|
|
return [
|
|
[str(value) for value in invocation.get("argv", [])]
|
|
for invocation in turn.get("tool_invocations", [])
|
|
if invocation.get("argv")
|
|
]
|
|
|
|
|
|
def _searched_project(turn: dict[str, Any], project: str) -> bool:
|
|
return any(
|
|
len(argv) >= 2 and argv[0] in {"search", "context"} and project.lower() in argv[1].lower()
|
|
for argv in _argvs(turn)
|
|
)
|
|
|
|
|
|
def _searched_recorded_dedupe_query(turn: dict[str, Any], packet: dict[str, Any]) -> bool:
|
|
assessment = packet.get("dedupe_conflict_assessment") or {}
|
|
query = str(assessment.get("database_search_query") or "")
|
|
return bool(query) and any(
|
|
len(argv) >= 2 and argv[0] in {"search", "context"} and argv[1] == query
|
|
for argv in _argvs(turn)
|
|
)
|
|
|
|
|
|
def _searched_semantically_without_ids(turn: dict[str, Any], row_ids: list[str]) -> bool:
|
|
terms = {"approval", "approved", "apply", "applied", "canonical", "review"}
|
|
return any(
|
|
len(argv) >= 2
|
|
and argv[0] in {"search", "context"}
|
|
and not any(row_id in argv[1] for row_id in row_ids)
|
|
and len(terms & set(argv[1].lower().split())) >= 2
|
|
for argv in _argvs(turn)
|
|
)
|
|
|
|
|
|
def _searched_composed_proposal(turn: dict[str, Any], project: str) -> bool:
|
|
return any(
|
|
len(argv) >= 2 and argv[0] == "search-proposals" and project.lower() in argv[1].lower()
|
|
for argv in _argvs(turn)
|
|
)
|
|
|
|
|
|
def _read_claim(turn: dict[str, Any], claim_id: str) -> bool:
|
|
return any(argv[:2] == ["show", claim_id] for argv in _argvs(turn))
|
|
|
|
|
|
def _read_claim_edges(turn: dict[str, Any], claim_id: str) -> bool:
|
|
return any(argv[:2] == ["edges", claim_id] for argv in _argvs(turn))
|
|
|
|
|
|
def _read_claim_evidence(turn: dict[str, Any], claim_id: str) -> bool:
|
|
return any(
|
|
argv[:2] == ["evidence", claim_id]
|
|
and argv[2:] in (["--format", "json"], ["--format=json"])
|
|
for argv in _argvs(turn)
|
|
)
|
|
|
|
|
|
def _read_claim_material(turn: dict[str, Any], claim_id: str) -> bool:
|
|
return _read_claim(turn, claim_id) or _read_claim_evidence(turn, claim_id)
|
|
|
|
|
|
def _read_exact_proposal(turn: dict[str, Any], proposal_id: str) -> bool:
|
|
return any(argv[:2] == ["show-proposal", proposal_id] for argv in _argvs(turn))
|
|
|
|
|
|
def merge_transcript_events(gateways: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
"""Collapse cumulative session traces without hiding events that lack an ID."""
|
|
merged: dict[tuple[str, str], dict[str, Any]] = {}
|
|
order: list[tuple[str, str]] = []
|
|
for gateway_index, gateway in enumerate(gateways):
|
|
events = (gateway.get("transcript_tool_trace") or {}).get("events", [])
|
|
for event_index, event in enumerate(events):
|
|
phase = str(event.get("phase") or "")
|
|
call_id = str(event.get("tool_call_id") or "")
|
|
if not call_id:
|
|
call_id = f"missing:{gateway_index}:{event_index}"
|
|
key = (phase, call_id)
|
|
if key not in merged:
|
|
order.append(key)
|
|
merged[key] = event
|
|
return [merged[key] for key in order]
|
|
|
|
|
|
def terminal_trace_consistency(events: list[dict[str, Any]], tool_proof: dict[str, Any]) -> dict[str, Any]:
|
|
"""Reconcile bounded transcript receipts with nonce-bound wrapper receipts."""
|
|
terminal_calls = {
|
|
str(event.get("tool_call_id")): event
|
|
for event in events
|
|
if event.get("phase") == "call" and event.get("tool_name") == "terminal"
|
|
}
|
|
results = {
|
|
str(event.get("tool_call_id")): event
|
|
for event in events
|
|
if event.get("phase") == "result" and event.get("tool_call_id")
|
|
}
|
|
summary = bound.transcript_terminal_attempt_summary(events)
|
|
known_exit_codes = summary.get("known_exit_codes") or {}
|
|
unknown_ids = sorted(set(terminal_calls) - set(known_exit_codes))
|
|
unknown_receipted_and_truncated = sorted(
|
|
call_id
|
|
for call_id in unknown_ids
|
|
if call_id in results and results[call_id].get("content_truncated") is True
|
|
)
|
|
every_call_has_result = set(terminal_calls) <= set(results)
|
|
unknown_results_are_valid = unknown_ids == unknown_receipted_and_truncated
|
|
count_matches = len(terminal_calls) == int(tool_proof.get("invocation_count") or 0)
|
|
passes = (
|
|
bool(terminal_calls)
|
|
and every_call_has_result
|
|
and unknown_results_are_valid
|
|
and summary.get("rejected_call_count") == 0
|
|
and summary.get("nonzero_call_count") == 0
|
|
and count_matches
|
|
and tool_proof.get("all_bound_to_supplied_target") is True
|
|
and tool_proof.get("all_completed_successfully") is True
|
|
)
|
|
return {
|
|
**summary,
|
|
"terminal_call_ids": sorted(terminal_calls),
|
|
"result_call_ids": sorted(results),
|
|
"unknown_result_call_ids": unknown_ids,
|
|
"unknown_receipted_and_truncated_call_ids": unknown_receipted_and_truncated,
|
|
"every_terminal_call_has_result": every_call_has_result,
|
|
"unknown_results_are_receipted_and_truncated": unknown_results_are_valid,
|
|
"nonce_bound_invocation_count": int(tool_proof.get("invocation_count") or 0),
|
|
"nonce_bound_invocation_count_matches": count_matches,
|
|
"passes": passes,
|
|
}
|
|
|
|
|
|
def _state_matches(state: dict[str, Any], *, conversation_marker: str, child: dict[str, Any]) -> bool:
|
|
payload = child["payload"]["apply_payload"]
|
|
expected_claim_ids = sorted(row["id"] for row in payload["claims"])
|
|
expected_source_ids = sorted(row["id"] for row in payload["sources"])
|
|
edges = payload["edges"]
|
|
edge = state.get("conflict_edge") if isinstance(state.get("conflict_edge"), dict) else {}
|
|
return (
|
|
state.get("phase") == "final"
|
|
and (state.get("conversation_marker") or state.get("marker")) == conversation_marker
|
|
and state.get("proposal_id") == child["id"]
|
|
and state.get("status") == "applied"
|
|
and sorted(state.get("claim_ids") or []) == expected_claim_ids
|
|
and sorted(state.get("source_ids") or []) == expected_source_ids
|
|
and bool(edges)
|
|
and any(
|
|
edge.get("from_claim") == expected["from_claim"]
|
|
and edge.get("to_claim") == expected["to_claim"]
|
|
and edge.get("edge_type") == expected["edge_type"]
|
|
for expected in edges
|
|
)
|
|
)
|
|
|
|
|
|
def _service_unchanged(before: dict[str, Any] | None, after: dict[str, Any] | None) -> bool:
|
|
return bool(before and after and before.get("returncode") == 0 and before == after)
|
|
|
|
|
|
def expected_table_deltas(child: dict[str, Any]) -> dict[str, int]:
|
|
rows = guarded._expected_bundle_rows(child["payload"]["apply_payload"])
|
|
return {
|
|
"public.claims": len(rows["claims"]),
|
|
"public.sources": len(rows["sources"]),
|
|
"public.claim_evidence": len(rows["claim_evidence"]),
|
|
"public.claim_edges": len(rows["claim_edges"]),
|
|
"public.reasoning_tools": len(rows["reasoning_tools"]),
|
|
"kb_stage.kb_proposals": 1,
|
|
lifecycle.APPROVAL_TABLE: 1,
|
|
}
|
|
|
|
|
|
def base_snapshot_matches(target: dict[str, Any], production: dict[str, Any]) -> bool:
|
|
return all(
|
|
target.get(section, {}).get(table) == production.get(section, {}).get(table)
|
|
for section in ("counts", "rowset_md5")
|
|
for table in PRODUCTION_TABLES
|
|
)
|
|
|
|
|
|
def gate_schema_manifest(container: str, database: str) -> dict[str, Any]:
|
|
return bound._psql_json(
|
|
container,
|
|
database,
|
|
"""\\set ON_ERROR_STOP on
|
|
begin transaction read only;
|
|
select jsonb_build_object(
|
|
'approval_table', to_regclass('kb_stage.kb_proposal_approvals')::text,
|
|
'review_principals_table', to_regclass('kb_stage.kb_review_principals')::text,
|
|
'review_role_exists', exists(select 1 from pg_roles where rolname = 'kb_review'),
|
|
'apply_role_exists', exists(select 1 from pg_roles where rolname = 'kb_apply'),
|
|
'approve_function', to_regprocedure(
|
|
'kb_stage.approve_strict_proposal(uuid,text,jsonb,text,text)'
|
|
)::text,
|
|
'assert_function', to_regprocedure(
|
|
'kb_stage.assert_approved_proposal(uuid,text,jsonb,text,uuid,timestamptz,text)'
|
|
)::text,
|
|
'finish_function', to_regprocedure(
|
|
'kb_stage.finish_approved_proposal(uuid,text,jsonb,text,uuid,timestamptz,text,text)'
|
|
)::text
|
|
)::text;
|
|
rollback;
|
|
""",
|
|
)
|
|
|
|
|
|
def clone_gate_schema_ready(manifest: dict[str, Any]) -> bool:
|
|
return (
|
|
manifest.get("approval_table") == lifecycle.APPROVAL_TABLE
|
|
and manifest.get("review_principals_table") == "kb_stage.kb_review_principals"
|
|
and manifest.get("review_role_exists") is True
|
|
and manifest.get("apply_role_exists") is True
|
|
and bool(manifest.get("approve_function"))
|
|
and bool(manifest.get("assert_function"))
|
|
and bool(manifest.get("finish_function"))
|
|
)
|
|
|
|
|
|
def source_manifest() -> list[dict[str, Any]]:
|
|
paths = (
|
|
Path(__file__).resolve(),
|
|
(HERE / "bootstrap_clone_kb_gate.py").resolve(),
|
|
(HERE / "kb_apply_prereqs.sql").resolve(),
|
|
(HERE / "run_leo_clone_bound_handler_checkpoint.py").resolve(),
|
|
(HERE / "run_leo_clone_lifecycle_checkpoint.py").resolve(),
|
|
(HERE / "stage_normalized_proposal.py").resolve(),
|
|
(HERE / "kb_proposal_normalize.py").resolve(),
|
|
(HERE / "kb_proposal_review_packet.py").resolve(),
|
|
(HERE / "kb_rich_proposal_creation_plan.py").resolve(),
|
|
(HERE / "apply_worker.py").resolve(),
|
|
(HERE / "run_approve_claim_clone_canary.py").resolve(),
|
|
(HERE / "approve_proposal.py").resolve(),
|
|
(HERE / "apply_proposal.py").resolve(),
|
|
)
|
|
return [
|
|
{
|
|
"repo_relative_path": path.relative_to(REPO_ROOT.resolve()).as_posix(),
|
|
"sha256": hashlib.sha256(path.read_bytes()).hexdigest(),
|
|
"size_bytes": path.stat().st_size,
|
|
}
|
|
for path in paths
|
|
]
|
|
|
|
|
|
def validate_args(args: argparse.Namespace) -> None:
|
|
lifecycle._validate_marker(args.run_marker, "--run-marker")
|
|
lifecycle._validate_marker(args.conversation_marker, "--conversation-marker")
|
|
if args.run_marker == args.conversation_marker:
|
|
raise bound.CheckpointError("run and conversation markers must be distinct")
|
|
if args.container == bound.PRODUCTION_CONTAINER:
|
|
raise bound.CheckpointError("--container must be a disposable non-production container")
|
|
if not bound.SAFE_DOCKER_NAME_RE.fullmatch(args.container):
|
|
raise bound.CheckpointError("--container must be an explicit Docker name")
|
|
if not bound.SAFE_DB_NAME_RE.fullmatch(args.db):
|
|
raise bound.CheckpointError("--db must be an explicit database name")
|
|
if not args.copy_model_auth or not args.operator_review or not args.guarded_apply:
|
|
raise bound.CheckpointError("--copy-model-auth, --operator-review, and --guarded-apply are required")
|
|
if args.turn_timeout <= 0:
|
|
raise bound.CheckpointError("--turn-timeout must be positive")
|
|
args.output = bound.validate_private_output_path(args.output)
|
|
|
|
|
|
async def run_checkpoint(args: argparse.Namespace) -> dict[str, Any]:
|
|
validate_args(args)
|
|
fixture = build_source_fixture(args.run_marker)
|
|
prompts = build_prompts(fixture, args.conversation_marker)
|
|
report: dict[str, Any] = {
|
|
"schema": SCHEMA,
|
|
"generated_at_utc": bound.utc_now(),
|
|
"current_canary": "source bytes -> Leo extraction -> strict staged child -> review/apply -> restarted reasoning",
|
|
"required_tier": "live_vps_no_send_disposable_full_data_clone",
|
|
"posted_to_telegram": False,
|
|
"production_writes_attempted": False,
|
|
"production_service_restart_attempted": False,
|
|
"fixture": {
|
|
"project": fixture["project"],
|
|
"document_sha256": fixture["document"]["content_sha256"],
|
|
"post_sha256": fixture["post"]["content_sha256"],
|
|
},
|
|
"errors": [],
|
|
"turns": {},
|
|
"source_manifest_before": source_manifest(),
|
|
"output": str(args.output),
|
|
}
|
|
temp_profile: Path | None = None
|
|
tool_log: Path | None = None
|
|
bridge: dict[str, Any] = {}
|
|
provider_environment: dict[str, str] = {}
|
|
provider_secret_values: tuple[str, ...] = ()
|
|
target_container_id: str | None = None
|
|
target_identity_before: dict[str, Any] | None = None
|
|
target_guard_before: dict[str, Any] | None = None
|
|
target_unrelated_before: dict[str, Any] | None = None
|
|
production_guard_before: dict[str, Any] | None = None
|
|
production_gate_schema_before: dict[str, Any] | None = None
|
|
production_service_before: dict[str, Any] | None = None
|
|
live_bridge_before: dict[str, str | None] | None = None
|
|
packet: dict[str, Any] | None = None
|
|
child: dict[str, Any] | None = None
|
|
try:
|
|
target_identity_before, production_identity = lifecycle.assert_disposable_target(args.container, args.db)
|
|
target_container_id = str(target_identity_before["id"])
|
|
args.bound_container_id = target_container_id
|
|
report["target"] = {
|
|
"requested_container": args.container,
|
|
"bound_container_id": target_container_id,
|
|
"container_identity_before": target_identity_before,
|
|
"isolation_checks": bound.validate_disposable_target_identity(target_identity_before, production_identity),
|
|
}
|
|
production_guard_before = bound.database_guard_snapshot(
|
|
bound.PRODUCTION_CONTAINER, bound.PRODUCTION_DB, tables=PRODUCTION_TABLES
|
|
)
|
|
target_base_guard = bound.database_guard_snapshot(target_container_id, args.db, tables=PRODUCTION_TABLES)
|
|
if not base_snapshot_matches(target_base_guard, production_guard_before):
|
|
raise bound.CheckpointError(
|
|
"target base rows are not an exact production clone before clone-only gate additions"
|
|
)
|
|
target_guard_before = bound.database_guard_snapshot(target_container_id, args.db, tables=TABLES)
|
|
target_gate_schema = gate_schema_manifest(target_container_id, args.db)
|
|
if not clone_gate_schema_ready(target_gate_schema):
|
|
raise bound.CheckpointError(
|
|
"clone-only guarded review/apply prerequisites are incomplete: "
|
|
+ json.dumps(target_gate_schema, sort_keys=True)
|
|
)
|
|
production_gate_schema_before = gate_schema_manifest(bound.PRODUCTION_CONTAINER, bound.PRODUCTION_DB)
|
|
if (
|
|
target_guard_before["database_identity"]["system_identifier"]
|
|
== production_guard_before["database_identity"]["system_identifier"]
|
|
):
|
|
raise bound.CheckpointError("target and production share a PostgreSQL system identifier")
|
|
args.target_database_identity = target_guard_before["database_identity"]
|
|
production_service_before = bound.service_state()
|
|
live_bridge_before = bound.bridge_hashes()
|
|
report["production_invariants"] = {
|
|
"container_identity": production_identity,
|
|
"guard_snapshot_before": production_guard_before,
|
|
"gate_schema_before": production_gate_schema_before,
|
|
"service_before": production_service_before,
|
|
"live_bridge_hashes_before": live_bridge_before,
|
|
}
|
|
report["target_base_guard_snapshot"] = target_base_guard
|
|
report["target_gate_schema"] = target_gate_schema
|
|
report["target_guard_snapshot_before"] = target_guard_before
|
|
missing_secrets = [
|
|
label
|
|
for label, path in (
|
|
("review", Path(args.review_secrets_file)),
|
|
("apply", Path(args.apply_secrets_file)),
|
|
)
|
|
if not path.is_file()
|
|
]
|
|
if missing_secrets:
|
|
raise bound.CheckpointError(
|
|
"required separated clone credential file(s) are absent: " + ", ".join(missing_secrets)
|
|
)
|
|
|
|
temp_profile, copy_audit = bound.copy_profile(run_id=args.run_marker, prompt_id="composition")
|
|
report["temporary_profile_copy_audit"] = copy_audit
|
|
report["model_auth_binding"] = bound.copy_ephemeral_model_auth(temp_profile)
|
|
provider_environment, environment_audit = bound.load_ephemeral_provider_environment(temp_profile)
|
|
provider_secret_values = tuple(provider_environment.values())
|
|
report["model_auth_binding"]["environment_binding"] = environment_audit
|
|
bridge = patch_composition_bridge(
|
|
temp_profile, container=target_container_id, database=args.db, fixture=fixture
|
|
)
|
|
report["temporary_bridge"] = bridge
|
|
tool_log = Path(bridge["tool_log_path"])
|
|
args.tool_run_nonce = bridge["run_nonce"]
|
|
|
|
extract_turn = await run_turn(
|
|
args,
|
|
temp_profile,
|
|
phase="T1_extract_sources",
|
|
prompt=prompts["extract"],
|
|
provider_environment=provider_environment,
|
|
tool_log=tool_log,
|
|
)
|
|
lifecycle._require_gateway_turn(extract_turn)
|
|
report["turns"]["T1_extract_sources"] = extract_turn
|
|
packet = parse_prefixed_json(str(extract_turn["gateway"].get("reply") or ""), PACKET_PREFIX)
|
|
report["packet_validation"] = validate_composition_packet(packet, fixture)
|
|
guard_after_extraction = bound.database_guard_snapshot(target_container_id, args.db, tables=TABLES)
|
|
report["target_guard_snapshot_after_extraction"] = guard_after_extraction
|
|
if guard_after_extraction != target_guard_before:
|
|
raise bound.CheckpointError("source extraction changed the clone before staging")
|
|
|
|
agent_id = resolve_leo_agent_id(target_container_id, args.db)
|
|
parent = build_parent_packet(packet, fixture, args.run_marker, agent_id)
|
|
stage_result = normalized_stage.stage_normalized_child(args.container, args.db, parent)
|
|
if stage_result["bound_container_id"] != target_container_id:
|
|
raise bound.CheckpointError("normalized staging rebound to a different container identity")
|
|
child = stage_result["child"]
|
|
report["normalized_child"] = {
|
|
"id": child["id"],
|
|
"proposal_type": child["proposal_type"],
|
|
"status": child["status"],
|
|
"source_ref": child["source_ref"],
|
|
"parent_packet_sha256": child["parent_packet_sha256"],
|
|
"payload_sha256": child["payload_sha256"],
|
|
"planned_row_ids": {
|
|
"claims": [row["id"] for row in child["payload"]["apply_payload"]["claims"]],
|
|
"sources": [row["id"] for row in child["payload"]["apply_payload"]["sources"]],
|
|
},
|
|
}
|
|
report["stage_receipt"] = stage_result["database_receipt"]
|
|
expected_rows = guarded._expected_bundle_rows(child["payload"]["apply_payload"])
|
|
empty_rows = {key: [] for key in expected_rows}
|
|
if (
|
|
guarded._exact_bundle_readback(target_container_id, args.db, child["payload"]["apply_payload"])
|
|
!= empty_rows
|
|
):
|
|
raise bound.CheckpointError("canonical rows appeared before review/apply")
|
|
pending = guarded._proposal_readback(target_container_id, args.db, child["id"])
|
|
report["pending_readback"] = pending
|
|
if not pending or not bound._state_semantics(pending, "pending_review")["all"]:
|
|
raise bound.CheckpointError("normalized child was not exactly pending and unapplied")
|
|
target_unrelated_before = unrelated_guard_snapshot(target_container_id, args.db, child)
|
|
|
|
review_dry, review_apply = lifecycle.run_operator_review(args, child["id"])
|
|
approved = guarded._proposal_readback(target_container_id, args.db, child["id"])
|
|
approval_snapshot = guarded._approval_snapshot_readback(target_container_id, args.db, child["id"])
|
|
report["operator_review"] = {
|
|
"dry_run": lifecycle._command_receipt(review_dry),
|
|
"apply": lifecycle._command_receipt(review_apply),
|
|
"approved_readback": approved,
|
|
"approval_snapshot": approval_snapshot,
|
|
}
|
|
apply_dry, apply_execute = lifecycle.run_guarded_apply(args, child["id"])
|
|
applied = guarded._proposal_readback(target_container_id, args.db, child["id"])
|
|
approval_after = guarded._approval_snapshot_readback(target_container_id, args.db, child["id"])
|
|
final_rows = guarded._exact_bundle_readback(target_container_id, args.db, child["payload"]["apply_payload"])
|
|
report["guarded_apply"] = {
|
|
"dry_run": lifecycle._command_receipt(apply_dry),
|
|
"apply": lifecycle._command_receipt(apply_execute),
|
|
"applied_readback": applied,
|
|
"approval_snapshot_after_apply": approval_after,
|
|
}
|
|
report["final_canonical_rows"] = final_rows
|
|
if not guarded._approval_transition_valid(pending, approved):
|
|
raise bound.CheckpointError("operator review did not produce the exact approved transition")
|
|
if not guarded._apply_transition_valid(approved, applied):
|
|
raise bound.CheckpointError("guarded apply transition is invalid")
|
|
if approval_snapshot != approval_after:
|
|
raise bound.CheckpointError("apply changed the immutable approval snapshot")
|
|
if final_rows != expected_rows:
|
|
raise bound.CheckpointError("canonical source-derived rows differ from the reviewed payload")
|
|
|
|
reason_turn = await run_turn(
|
|
args,
|
|
temp_profile,
|
|
phase="T5_reason_after_restart",
|
|
prompt=prompts["reason"],
|
|
provider_environment=provider_environment,
|
|
tool_log=tool_log,
|
|
)
|
|
lifecycle._require_gateway_turn(reason_turn)
|
|
report["turns"]["T5_reason_after_restart"] = reason_turn
|
|
state = parse_prefixed_json(str(reason_turn["gateway"].get("reply") or ""), STATE_PREFIX)
|
|
report["final_state_receipt"] = state
|
|
report["final_state_matches_rows"] = _state_matches(
|
|
state, conversation_marker=args.conversation_marker, child=child
|
|
)
|
|
t1_child = extract_turn["gateway"].get("child_process") or {}
|
|
t5_child = reason_turn["gateway"].get("child_process") or {}
|
|
report["isolated_restart"] = {
|
|
"prior_child_absent": t1_child.get("alive_after_readback") is False
|
|
and t1_child.get("process_group_alive_after_readback") is False,
|
|
"child_pid_changed": t1_child.get("pid") != t5_child.get("pid"),
|
|
"same_session_key": bool(extract_turn["gateway"].get("session_key"))
|
|
and extract_turn["gateway"].get("session_key") == reason_turn["gateway"].get("session_key"),
|
|
"same_persisted_session_id": (
|
|
bool((extract_turn["gateway"].get("transcript_tool_trace") or {}).get("session_id"))
|
|
and (extract_turn["gateway"].get("transcript_tool_trace") or {}).get("session_id")
|
|
== (reason_turn["gateway"].get("transcript_tool_trace") or {}).get("session_id")
|
|
),
|
|
}
|
|
except Exception as exc:
|
|
report["errors"].append({"type": type(exc).__name__, "message": str(exc)})
|
|
report["traceback_tail"] = traceback.format_exc().splitlines()[-16:]
|
|
finally:
|
|
provider_environment.clear()
|
|
child_cleanup = bound.cleanup_active_gateway_children()
|
|
try:
|
|
if not tool_log or not bridge or not target_container_id or not target_guard_before:
|
|
raise bound.CheckpointError("tool proof prerequisites were not established")
|
|
tool_proof = bound.read_tool_proof(
|
|
tool_log,
|
|
target_container_id,
|
|
args.db,
|
|
run_nonce=bridge["run_nonce"],
|
|
database_identity=target_guard_before["database_identity"],
|
|
require_database_read_only=False,
|
|
)
|
|
except Exception as exc:
|
|
report["errors"].append({"type": type(exc).__name__, "message": f"tool proof: {exc}"})
|
|
tool_proof = {
|
|
"invocations": [],
|
|
"invocation_count": 0,
|
|
"all_bound_to_supplied_target": False,
|
|
"all_completed_successfully": False,
|
|
}
|
|
report["tool_proof"] = tool_proof
|
|
temp_root = temp_profile.parent if temp_profile else None
|
|
cleanup_clear = not bound._ACTIVE_GATEWAY_CHILDREN and all(
|
|
not item.get("alive_after_cleanup") and not item.get("process_group_alive_after_cleanup")
|
|
for item in child_cleanup.values()
|
|
)
|
|
temp_removed = bound.remove_tree(temp_root) if cleanup_clear else False
|
|
report["cleanup"] = {
|
|
"active_gateway_child_cleanup": child_cleanup,
|
|
"active_gateway_registry_clear": not bound._ACTIVE_GATEWAY_CHILDREN,
|
|
"temp_profile_removed": temp_removed,
|
|
"absence_verified_independently": bound.temp_path_absent(temp_root),
|
|
"active_temp_root_registry_clear": (temp_root not in bound._ACTIVE_TEMP_ROOTS if temp_root else True),
|
|
}
|
|
target_guard_after = None
|
|
target_identity_after = None
|
|
target_unrelated_after = None
|
|
if target_container_id:
|
|
try:
|
|
target_identity_after = bound.container_identity(args.container)
|
|
if target_identity_after.get("id") != target_container_id:
|
|
raise bound.CheckpointError("target name no longer resolves to the pinned identity")
|
|
report.setdefault("target", {})["container_identity_after"] = target_identity_after
|
|
target_guard_after = bound.database_guard_snapshot(target_container_id, args.db, tables=TABLES)
|
|
report["target_guard_snapshot_after"] = target_guard_after
|
|
if child and target_unrelated_before is not None:
|
|
target_unrelated_after = unrelated_guard_snapshot(target_container_id, args.db, child)
|
|
except Exception as exc:
|
|
report["errors"].append({"type": type(exc).__name__, "message": f"target postflight: {exc}"})
|
|
production_guard_after = None
|
|
production_gate_schema_after = None
|
|
try:
|
|
production_guard_after = bound.database_guard_snapshot(
|
|
bound.PRODUCTION_CONTAINER, bound.PRODUCTION_DB, tables=PRODUCTION_TABLES
|
|
)
|
|
production_gate_schema_after = gate_schema_manifest(bound.PRODUCTION_CONTAINER, bound.PRODUCTION_DB)
|
|
except Exception as exc:
|
|
report["errors"].append({"type": type(exc).__name__, "message": f"production postflight: {exc}"})
|
|
production_service_after = bound.service_state()
|
|
live_bridge_after = bound.bridge_hashes()
|
|
production = report.setdefault("production_invariants", {})
|
|
production.update(
|
|
{
|
|
"guard_snapshot_after": production_guard_after,
|
|
"guard_snapshot_unchanged": production_guard_before is not None
|
|
and production_guard_before == production_guard_after,
|
|
"gate_schema_after": production_gate_schema_after,
|
|
"gate_schema_unchanged": production_gate_schema_before is not None
|
|
and production_gate_schema_before == production_gate_schema_after,
|
|
"service_after": production_service_after,
|
|
"service_unchanged": _service_unchanged(production_service_before, production_service_after),
|
|
"live_bridge_hashes_after": live_bridge_after,
|
|
"live_bridge_unchanged": live_bridge_before is not None and live_bridge_before == live_bridge_after,
|
|
}
|
|
)
|
|
turns = report.get("turns") or {}
|
|
extraction = turns.get("T1_extract_sources") or {}
|
|
reasoning = turns.get("T5_reason_after_restart") or {}
|
|
gateways = [turn.get("gateway") or {} for turn in (extraction, reasoning)]
|
|
merged_events = merge_transcript_events(gateways)
|
|
terminal_consistency = terminal_trace_consistency(merged_events, tool_proof)
|
|
report["merged_transcript_event_count"] = len(merged_events)
|
|
report["terminal_trace_consistency"] = terminal_consistency
|
|
expected_claim_ids = [row["id"] for row in child["payload"]["apply_payload"]["claims"]] if child else []
|
|
expected_source_ids = [row["id"] for row in child["payload"]["apply_payload"]["sources"]] if child else []
|
|
generated_row_ids = [
|
|
*expected_claim_ids,
|
|
*expected_source_ids,
|
|
*([child["id"]] if child else []),
|
|
]
|
|
reply = str((reasoning.get("gateway") or {}).get("reply") or "").lower()
|
|
transcript_calls = [
|
|
event.get("tool_name")
|
|
for event in merged_events
|
|
if event.get("phase") == "call"
|
|
]
|
|
source_manifest_after = source_manifest()
|
|
report["source_manifest_after"] = source_manifest_after
|
|
checks = {
|
|
"clone_base_rows_match_production_before_gate_use": base_snapshot_matches(
|
|
report.get("target_base_guard_snapshot") or {},
|
|
production.get("guard_snapshot_before") or {},
|
|
),
|
|
"clone_only_gate_schema_ready": clone_gate_schema_ready(report.get("target_gate_schema") or {}),
|
|
"source_packet_validated": bool(report.get("packet_validation")),
|
|
"extraction_searched_existing_kb": _searched_recorded_dedupe_query(extraction, packet or {}),
|
|
"extraction_changed_no_database_rows": report.get("target_guard_snapshot_after_extraction")
|
|
== report.get("target_guard_snapshot_before"),
|
|
"normalized_child_staged": child is not None
|
|
and report.get("stage_receipt", {}).get("status") == "pending_review",
|
|
"source_hashes_preserved": bool(child)
|
|
and {fixture["document"]["content_sha256"], fixture["post"]["content_sha256"]}
|
|
<= {row["hash"] for row in child["payload"]["apply_payload"]["sources"]},
|
|
"canonical_rows_match_reviewed_payload": bool(child)
|
|
and report.get("final_canonical_rows") == guarded._expected_bundle_rows(child["payload"]["apply_payload"]),
|
|
"immutable_approval_survived_apply": report.get("operator_review", {}).get("approval_snapshot")
|
|
== report.get("guarded_apply", {}).get("approval_snapshot_after_apply"),
|
|
"target_table_deltas_exact": bool(child and target_guard_before and target_guard_after)
|
|
and lifecycle._table_deltas(target_guard_before["counts"], target_guard_after["counts"])
|
|
== expected_table_deltas(child),
|
|
"target_database_identity_stable": bool(target_guard_before and target_guard_after)
|
|
and target_guard_before.get("database_identity") == target_guard_after.get("database_identity"),
|
|
"reasoning_searched_without_ids": _searched_semantically_without_ids(reasoning, generated_row_ids),
|
|
"reasoning_searched_composed_proposal": _searched_composed_proposal(reasoning, fixture["project"]),
|
|
"reasoning_read_exact_proposal": bool(child) and _read_exact_proposal(reasoning, child["id"]),
|
|
"reasoning_read_every_composed_claim": bool(expected_claim_ids)
|
|
and all(_read_claim_material(reasoning, claim_id) for claim_id in expected_claim_ids),
|
|
"reasoning_read_every_claim_evidence": bool(expected_claim_ids)
|
|
and all(_read_claim_evidence(reasoning, claim_id) for claim_id in expected_claim_ids),
|
|
"reasoning_read_conflict_edges": bool(expected_claim_ids)
|
|
and any(_read_claim_edges(reasoning, claim_id) for claim_id in expected_claim_ids),
|
|
"reasoning_answered_cory_state_and_conflict": ("approved" in reply or "approval" in reply)
|
|
and "applied" in reply
|
|
and "canonical" in reply
|
|
and "conflict" in reply,
|
|
"final_state_receipt_matches_rows": report.get("final_state_matches_rows") is True,
|
|
"isolated_restart_and_memory_survived": bool(report.get("isolated_restart"))
|
|
and all(report.get("isolated_restart", {}).values()),
|
|
"all_tool_calls_bound_and_successful": tool_proof.get("all_bound_to_supplied_target") is True
|
|
and tool_proof.get("all_completed_successfully") is True,
|
|
"terminal_results_complete_and_first_try": terminal_consistency.get("passes") is True,
|
|
"all_gateway_children_clean": bool(gateways)
|
|
and all(
|
|
(gateway.get("child_process") or {}).get("readiness_verified") is True
|
|
and (gateway.get("child_process") or {}).get("timed_out") is False
|
|
and (gateway.get("child_process") or {}).get("exitcode") == 0
|
|
and (gateway.get("child_process") or {}).get("result_transport") == "private_temp_file"
|
|
and ((gateway.get("child_process") or {}).get("termination") or {}).get("attempted") is False
|
|
and (gateway.get("child_process") or {}).get("alive_after_readback") is False
|
|
and (gateway.get("child_process") or {}).get("process_group_alive_after_readback") is False
|
|
for gateway in gateways
|
|
),
|
|
"gateway_tool_surfaces_are_read_only_and_no_send": bool(gateways)
|
|
and all(
|
|
set((gateway.get("tool_surface") or {}).get("actual_registry_tools") or [])
|
|
== {"skills_list", "skill_view", "terminal"}
|
|
and set((gateway.get("tool_surface") or {}).get("allowed_tools") or [])
|
|
== {"skills_list", "skill_view", "terminal"}
|
|
and (gateway.get("tool_surface") or {}).get("gateway_adapters_verified_mapping") is True
|
|
and (gateway.get("tool_surface") or {}).get("gateway_adapter_count") == 0
|
|
and (gateway.get("tool_surface") or {}).get("send_message_tool_enabled") is False
|
|
and (gateway.get("tool_surface") or {}).get("terminal_restricted_to_clone_wrapper") is True
|
|
and (gateway.get("tool_surface") or {}).get("terminal_subprocess_inherits_provider_credentials")
|
|
is False
|
|
and gateway.get("model_free_fallback_used") is False
|
|
for gateway in gateways
|
|
),
|
|
"all_transcript_tool_calls_allowlisted": bool(transcript_calls)
|
|
and all(name in {"skills_list", "skill_view", "terminal"} for name in transcript_calls),
|
|
"target_name_still_pinned": bool(target_identity_before and target_identity_after)
|
|
and target_identity_before == target_identity_after,
|
|
"unrelated_clone_rows_unchanged": target_unrelated_before is not None
|
|
and target_unrelated_before == target_unrelated_after,
|
|
"production_database_unchanged": production.get("guard_snapshot_unchanged") is True,
|
|
"production_gate_schema_unchanged": production.get("gate_schema_unchanged") is True,
|
|
"production_service_unchanged": production.get("service_unchanged") is True,
|
|
"live_bridge_unchanged": production.get("live_bridge_unchanged") is True,
|
|
"checkpoint_source_files_unchanged": report.get("source_manifest_before") == source_manifest_after,
|
|
"no_telegram_send": report.get("posted_to_telegram") is False
|
|
and all(gateway.get("posted_to_telegram") is False for gateway in gateways),
|
|
"temporary_profile_removed": report.get("cleanup", {}).get("temp_profile_removed") is True
|
|
and report.get("cleanup", {}).get("absence_verified_independently") is True
|
|
and report.get("cleanup", {}).get("active_gateway_registry_clear") is True
|
|
and report.get("cleanup", {}).get("active_temp_root_registry_clear") is True,
|
|
}
|
|
report["checks"] = checks
|
|
report["status"] = "pass" if not report["errors"] and all(checks.values()) else "fail"
|
|
report["claim_ceiling"] = (
|
|
"A pass proves no-send source extraction, deterministic hash-bound normalization, clone-only guarded "
|
|
"canonical apply, and restarted open-ended reasoning over the composed rows. It does not prove Telegram "
|
|
"delivery, production mutation, scheduled identity recomposition, or semantic behavior beyond this case."
|
|
)
|
|
report["completed_at_utc"] = bound.utc_now()
|
|
report = bound.redact_value(report, secret_values=provider_secret_values)
|
|
bound.write_report(args.output, report, secret_values=provider_secret_values)
|
|
return report
|
|
|
|
|
|
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--container", required=True)
|
|
parser.add_argument("--db", required=True)
|
|
parser.add_argument("--output", required=True, type=Path)
|
|
parser.add_argument("--run-marker", default=None)
|
|
parser.add_argument("--conversation-marker", default=None)
|
|
parser.add_argument("--chat-id", default=bound.DEFAULT_CHAT_ID)
|
|
parser.add_argument("--user-id", default=bound.DEFAULT_USER_ID)
|
|
parser.add_argument("--user-name", default="codex clone composition checkpoint")
|
|
parser.add_argument("--turn-timeout", type=int, default=300)
|
|
parser.add_argument("--review-secrets-file", default=lifecycle.DEFAULT_REVIEW_SECRETS_FILE)
|
|
parser.add_argument("--apply-secrets-file", default=lifecycle.DEFAULT_APPLY_SECRETS_FILE)
|
|
parser.add_argument("--copy-model-auth", action="store_true")
|
|
parser.add_argument("--operator-review", action="store_true")
|
|
parser.add_argument("--guarded-apply", action="store_true")
|
|
args = parser.parse_args(argv)
|
|
args.run_marker = args.run_marker or lifecycle.new_marker("leo-composition")
|
|
args.conversation_marker = args.conversation_marker or lifecycle.new_marker("composition-memory")
|
|
return args
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
args = parse_args(argv)
|
|
try:
|
|
with bound.termination_cleanup_handlers():
|
|
report = asyncio.run(run_checkpoint(args))
|
|
except bound.CheckpointError as exc:
|
|
print(json.dumps({"status": "rejected", "error": str(exc)}, sort_keys=True))
|
|
return 2
|
|
print(
|
|
json.dumps(
|
|
{
|
|
"status": report.get("status"),
|
|
"output": str(args.output),
|
|
"checks_passed": sum(bool(value) for value in report.get("checks", {}).values()),
|
|
"checks_total": len(report.get("checks", {})),
|
|
"error_count": len(report.get("errors", [])),
|
|
},
|
|
sort_keys=True,
|
|
)
|
|
)
|
|
return 0 if report.get("status") == "pass" else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|