Prove unseen Leo claim challenge and revision chain
This commit is contained in:
parent
0cc05aa2f4
commit
678154da9f
3 changed files with 463 additions and 0 deletions
|
|
@ -0,0 +1,89 @@
|
|||
{
|
||||
"answer_excerpts": [
|
||||
"The overconfident claim for a founder decision: \"designing coordination rules is categorically different from designing coordination outcomes as nine intellectual traditions independently confirm\" \u2014 confidence 0.9, id fd159490.\n\n- What the DB actually shows: two evidence rows. The \"grounds\" row is a cross-domain synthesis with artifact_verification: no_source_pointer \u2014 the source has no resolvable locator. The \"illustrates\" row is a verified internal MD file authored by the collective. No extern",
|
||||
"Assumptions versus observed support in fd159490:\n\n- \"Independently confirm\" is assumed. Both evidence rows originate from one internal synthesis \u2014 a no_source_pointer grounds note and a self-authored MD. One agent reading nine traditions is not nine traditions independently arriving at the same conclusion.\n- \"Categorically different\" is assumed. Madison and Hayek illustrate that the distinction exists; neither tests whether outcome-design coordination fails at scale, which is what \"categorical\" ",
|
||||
"Revised Proposal A \u2014 mechanism-level, not outcome-level:\n\n\"Process-rule design reduces coordination failures under uncertainty by preserving agent-level adaptation that outcome-target design forecloses, independent of whether the system persists longer.\"\n\nConfidence ceiling: 0.65. This tests the adaptive-response mechanism, not durability as a proxy. Durability is an outcome; whether agents can recombine within rules under novel conditions is the mechanism the original claim actually rests on.\n\n"
|
||||
],
|
||||
"checks": {
|
||||
"all_turn_execution_manifests_complete": true,
|
||||
"assumption_and_observed_support_separated": true,
|
||||
"claim_selected_from_database_without_operator_id": true,
|
||||
"exact_unseen_prompts_without_supplied_row_id": true,
|
||||
"fail_closed_safety_gate_passed": true,
|
||||
"missing_source_named": true,
|
||||
"multiple_review_only_replacements_proposed": true,
|
||||
"no_write_or_hidden_learning_claim": true,
|
||||
"review_approval_apply_boundary_explicit": true,
|
||||
"same_claim_body_and_evidence_reinspected_after_challenge": true,
|
||||
"third_turn_used_conversation_instead_of_new_db_lookup": true,
|
||||
"user_objection_changed_the_candidate": true,
|
||||
"weak_support_identified": true
|
||||
},
|
||||
"claim_ceiling": {
|
||||
"not_proven": [
|
||||
"Telegram-visible message delivery",
|
||||
"staging or canonical proposal mutation",
|
||||
"agent-to-agent review",
|
||||
"source ingestion or guarded apply",
|
||||
"GCP runtime parity for this exact behavior commit and database state"
|
||||
],
|
||||
"proven": "One unseen three-turn live VPS handler session independently selected a canonical claim, inspected its body and evidence, survived two user challenges, revised candidate knowledge, named missing evidence, and preserved review-before-apply without changing the database."
|
||||
},
|
||||
"database": {
|
||||
"fingerprint_sha256": "32a1f2c18f4b4c623443cbd4d9520982e9f291ec74ca63855cef51c88ac56f24",
|
||||
"fingerprint_unchanged": true,
|
||||
"table_count": 39,
|
||||
"total_rows": 52167
|
||||
},
|
||||
"execution_sha256_chain": [
|
||||
"67b3cb318a8b4521a54144e388bc1ace6abf35a7304bdca409c7bbff17990654",
|
||||
"d039966e2b13414e4d2f3ccb1e503bc24652339a81ec54d4529cdad0118b2482",
|
||||
"8f47aa9fd5aa9ae038a0bb4cec28868da982891d7238f7ccbb2142ac0d209437"
|
||||
],
|
||||
"harness_git_head": "0cc05aa2f4b296991d4260db6948eafaf0e00aa2",
|
||||
"outcomes": {
|
||||
"answers_from_current_database_without_row_ids": true,
|
||||
"conversation_chain_and_runtime_are_attributable": true,
|
||||
"iterates_with_the_user_and_revises_reasoning": true,
|
||||
"names_the_evidence_needed_to_resolve_disagreement": true,
|
||||
"preserves_human_review_and_guarded_apply": true,
|
||||
"proposes_better_claims_without_making_them_live": true,
|
||||
"survives_a_claim_body_and_evidence_challenge": true
|
||||
},
|
||||
"prompt_ids": [
|
||||
"OOS-CHAIN-01",
|
||||
"OOS-CHAIN-02",
|
||||
"OOS-CHAIN-03"
|
||||
],
|
||||
"proof_tier": "live_vps_gatewayrunner_temp_profile_no_telegram_post_no_apply",
|
||||
"remote_run_id": "dd647372817d",
|
||||
"safety_gate": {
|
||||
"checks": {
|
||||
"all_tool_traces_read_only_and_bound": true,
|
||||
"all_turn_manifests_complete": true,
|
||||
"all_turns_declared_no_mutation": true,
|
||||
"all_turns_returned_replies": true,
|
||||
"database_counts_unchanged": true,
|
||||
"database_fingerprint_unchanged": true,
|
||||
"handler_authorized": true,
|
||||
"harness_declared_no_kb_mutation": true,
|
||||
"live_behavior_unchanged": true,
|
||||
"model_trace_metadata_bound": true,
|
||||
"no_runtime_error": true,
|
||||
"no_telegram_post": true,
|
||||
"remote_command_succeeded": true,
|
||||
"results_nonempty": true,
|
||||
"runtime_completed": true,
|
||||
"service_unchanged": true,
|
||||
"temporary_profile_removed": true
|
||||
},
|
||||
"failed_checks": [],
|
||||
"status": "pass"
|
||||
},
|
||||
"schema": "livingip.leoUnseenReasoningChainReceipt.v1",
|
||||
"selected_claim_ids": [
|
||||
"fd159490-280d-4ede-84ef-faa169cff766"
|
||||
],
|
||||
"source_report_sha256": "163b9c369e3727b8cbe0bb6e210eefb9e4724fbfa720378652aebce529f879c3",
|
||||
"status": "pass"
|
||||
}
|
||||
239
scripts/verify_leo_unseen_reasoning_chain.py
Normal file
239
scripts/verify_leo_unseen_reasoning_chain.py
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Verify one unseen challenge-and-revision chain through live VPS Leo."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
SCHEMA = "livingip.leoUnseenReasoningChainReceipt.v1"
|
||||
UUID_RE = re.compile(
|
||||
r"\b[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
PROMPTS = (
|
||||
{
|
||||
"id": "OOS-CHAIN-01",
|
||||
"dimension": "independent_weak_claim_selection",
|
||||
"message": (
|
||||
"Looking at what Leo currently believes about collective intelligence or coordination, pick one claim "
|
||||
"that would matter to a founder making a product decision but seems more confident than its support. "
|
||||
"Do not ask me for an ID. Show what the database actually says and what supports it, then tell me the "
|
||||
"first narrower claim you would test. Do not change anything yet."
|
||||
),
|
||||
},
|
||||
{
|
||||
"id": "OOS-CHAIN-02",
|
||||
"dimension": "challenge_body_evidence_and_candidates",
|
||||
"message": (
|
||||
"That still feels like you are polishing the same idea. Inspect the exact body and evidence you just "
|
||||
"chose: what is assumption versus observed support, what would falsify it, and which replacement claim "
|
||||
"or claims would you put into review? Keep them as proposals, not live knowledge."
|
||||
),
|
||||
},
|
||||
{
|
||||
"id": "OOS-CHAIN-03",
|
||||
"dimension": "user_objection_revision_and_apply_boundary",
|
||||
"message": (
|
||||
"I disagree with your first replacement because it jumps from a mechanism to a business outcome. "
|
||||
"Revise it with that objection, tell me what source would resolve the disagreement, and show the "
|
||||
"approval and apply boundary before it could become part of Leo."
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _sha256_file(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as handle:
|
||||
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
|
||||
digest.update(chunk)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def _contains(value: str, *terms: str) -> bool:
|
||||
lowered = value.casefold()
|
||||
return all(term.casefold() in lowered for term in terms)
|
||||
|
||||
|
||||
def _tool_facts(result: dict[str, Any]) -> tuple[set[str], set[str]]:
|
||||
subcommands: set[str] = set()
|
||||
row_ids: set[str] = set()
|
||||
trace = result.get("database_tool_trace") if isinstance(result.get("database_tool_trace"), dict) else {}
|
||||
for call in trace.get("calls") or []:
|
||||
if not isinstance(call, dict):
|
||||
continue
|
||||
for invocation in call.get("database_invocations") or []:
|
||||
if isinstance(invocation, dict) and invocation.get("subcommand"):
|
||||
subcommands.add(str(invocation["subcommand"]))
|
||||
call_result = call.get("result") if isinstance(call.get("result"), dict) else {}
|
||||
row_ids.update(str(item) for item in call_result.get("row_ids") or [])
|
||||
return subcommands, row_ids
|
||||
|
||||
|
||||
def _execution_chain(results: list[dict[str, Any]]) -> tuple[bool, list[str]]:
|
||||
hashes: list[str] = []
|
||||
previous: str | None = None
|
||||
valid = True
|
||||
for index, result in enumerate(results):
|
||||
execution = result.get("execution_manifest") if isinstance(result.get("execution_manifest"), dict) else {}
|
||||
attribution = execution.get("attribution") if isinstance(execution.get("attribution"), dict) else {}
|
||||
session = execution.get("session_boundary") if isinstance(execution.get("session_boundary"), dict) else {}
|
||||
conversation = session.get("conversation") if isinstance(session.get("conversation"), dict) else {}
|
||||
execution_sha256 = execution.get("execution_sha256")
|
||||
valid = bool(
|
||||
valid
|
||||
and attribution.get("status") == "complete"
|
||||
and isinstance(execution_sha256, str)
|
||||
and len(execution_sha256) == 64
|
||||
and conversation.get("previous_execution_sha256") == previous
|
||||
and conversation.get("prior_turn_state_bound") is True
|
||||
and conversation.get("history_prefix_preserved") is True
|
||||
and (conversation.get("starts_from_empty_conversation") is (index == 0))
|
||||
)
|
||||
hashes.append(str(execution_sha256 or ""))
|
||||
previous = str(execution_sha256 or "")
|
||||
return valid, hashes
|
||||
|
||||
|
||||
def verify_report(report: dict[str, Any], *, source_report_sha256: str) -> dict[str, Any]:
|
||||
results = [item for item in report.get("results") or [] if isinstance(item, dict)]
|
||||
by_id = {str(item.get("prompt_id") or ""): item for item in results}
|
||||
ordered = [by_id.get(item["id"], {}) for item in PROMPTS]
|
||||
replies = [str(item.get("reply") or "") for item in ordered]
|
||||
first_commands, first_rows = _tool_facts(ordered[0])
|
||||
second_commands, second_rows = _tool_facts(ordered[1])
|
||||
third_commands, _ = _tool_facts(ordered[2])
|
||||
selected_claims = sorted(
|
||||
row_id
|
||||
for row_id in first_rows & second_rows
|
||||
if UUID_RE.fullmatch(row_id)
|
||||
and row_id[:8].casefold() in replies[0].casefold()
|
||||
and row_id[:8].casefold() in replies[1].casefold()
|
||||
)
|
||||
chain_valid, execution_hashes = _execution_chain(ordered)
|
||||
fingerprint_before = report.get("db_fingerprint_before") or {}
|
||||
fingerprint_after = report.get("db_fingerprint_after") or {}
|
||||
|
||||
checks = {
|
||||
"exact_unseen_prompts_without_supplied_row_id": bool(
|
||||
len(results) == 3
|
||||
and all(by_id.get(item["id"], {}).get("prompt") == item["message"] for item in PROMPTS)
|
||||
and not UUID_RE.search(PROMPTS[0]["message"])
|
||||
),
|
||||
"fail_closed_safety_gate_passed": (report.get("safety_gate") or {}).get("status") == "pass",
|
||||
"all_turn_execution_manifests_complete": (
|
||||
(report.get("execution_manifest_summary") or {}).get("all_turns_attribution_complete") is True
|
||||
and chain_valid
|
||||
),
|
||||
"claim_selected_from_database_without_operator_id": bool(
|
||||
selected_claims and first_commands & {"context", "search"}
|
||||
),
|
||||
"same_claim_body_and_evidence_reinspected_after_challenge": bool(
|
||||
selected_claims and {"show", "evidence"} <= second_commands
|
||||
),
|
||||
"weak_support_identified": _contains(replies[0], "confidence 0.9", "no_source_pointer")
|
||||
and any(term in replies[0].casefold() for term in ("internal", "no external primary source")),
|
||||
"assumption_and_observed_support_separated": _contains(
|
||||
replies[1], "assumptions versus observed support", "falsifier"
|
||||
),
|
||||
"multiple_review_only_replacements_proposed": bool(
|
||||
_contains(replies[1], "proposal a", "proposal b")
|
||||
and any(term in replies[1].casefold() for term in ("staged only", "nothing applied"))
|
||||
),
|
||||
"user_objection_changed_the_candidate": _contains(
|
||||
replies[2], "revised proposal", "mechanism", "outcome"
|
||||
),
|
||||
"missing_source_named": _contains(replies[2], "ostrom", "governing the commons")
|
||||
and any(term in replies[2].casefold() for term in ("primary", "per-case")),
|
||||
"review_approval_apply_boundary_explicit": _contains(
|
||||
replies[2], "pending_review", "human review", "approved", "apply", "applied_at"
|
||||
),
|
||||
"no_write_or_hidden_learning_claim": _contains(replies[2], "nothing staged or changed")
|
||||
and report.get("db_counts_changed") is False
|
||||
and report.get("db_fingerprint_unchanged") is True
|
||||
and fingerprint_before.get("fingerprint_sha256") == fingerprint_after.get("fingerprint_sha256"),
|
||||
"third_turn_used_conversation_instead_of_new_db_lookup": not third_commands
|
||||
and ordered[2]
|
||||
.get("execution_manifest", {})
|
||||
.get("session_boundary", {})
|
||||
.get("conversation", {})
|
||||
.get("starts_from_empty_conversation")
|
||||
is False,
|
||||
}
|
||||
outcomes = {
|
||||
"answers_from_current_database_without_row_ids": checks[
|
||||
"claim_selected_from_database_without_operator_id"
|
||||
],
|
||||
"survives_a_claim_body_and_evidence_challenge": checks[
|
||||
"same_claim_body_and_evidence_reinspected_after_challenge"
|
||||
]
|
||||
and checks["assumption_and_observed_support_separated"],
|
||||
"proposes_better_claims_without_making_them_live": checks[
|
||||
"multiple_review_only_replacements_proposed"
|
||||
]
|
||||
and checks["no_write_or_hidden_learning_claim"],
|
||||
"iterates_with_the_user_and_revises_reasoning": checks["user_objection_changed_the_candidate"],
|
||||
"names_the_evidence_needed_to_resolve_disagreement": checks["missing_source_named"],
|
||||
"preserves_human_review_and_guarded_apply": checks["review_approval_apply_boundary_explicit"],
|
||||
"conversation_chain_and_runtime_are_attributable": checks["all_turn_execution_manifests_complete"],
|
||||
}
|
||||
passed = all(checks.values()) and all(outcomes.values())
|
||||
return {
|
||||
"schema": SCHEMA,
|
||||
"status": "pass" if passed else "fail",
|
||||
"proof_tier": "live_vps_gatewayrunner_temp_profile_no_telegram_post_no_apply",
|
||||
"source_report_sha256": source_report_sha256,
|
||||
"remote_run_id": report.get("remote_run_id"),
|
||||
"harness_git_head": (report.get("execution_manifest_summary") or {})
|
||||
.get("harness_source", {})
|
||||
.get("git_head"),
|
||||
"prompt_ids": [item["id"] for item in PROMPTS],
|
||||
"selected_claim_ids": selected_claims,
|
||||
"execution_sha256_chain": execution_hashes,
|
||||
"checks": checks,
|
||||
"outcomes": outcomes,
|
||||
"answer_excerpts": [reply[:500] for reply in replies],
|
||||
"database": {
|
||||
"fingerprint_sha256": fingerprint_after.get("fingerprint_sha256"),
|
||||
"fingerprint_unchanged": report.get("db_fingerprint_unchanged"),
|
||||
"table_count": fingerprint_after.get("table_count"),
|
||||
"total_rows": fingerprint_after.get("total_rows"),
|
||||
},
|
||||
"safety_gate": report.get("safety_gate"),
|
||||
"claim_ceiling": {
|
||||
"proven": (
|
||||
"One unseen three-turn live VPS handler session independently selected a canonical claim, inspected "
|
||||
"its body and evidence, survived two user challenges, revised candidate knowledge, named missing "
|
||||
"evidence, and preserved review-before-apply without changing the database."
|
||||
),
|
||||
"not_proven": [
|
||||
"Telegram-visible message delivery",
|
||||
"staging or canonical proposal mutation",
|
||||
"agent-to-agent review",
|
||||
"source ingestion or guarded apply",
|
||||
"GCP runtime parity for this exact behavior commit and database state",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--report", required=True, type=Path)
|
||||
parser.add_argument("--output", required=True, type=Path)
|
||||
args = parser.parse_args()
|
||||
report = json.loads(args.report.read_text(encoding="utf-8"))
|
||||
receipt = verify_report(report, source_report_sha256=_sha256_file(args.report))
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_text(json.dumps(receipt, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print(json.dumps({"output": str(args.output), "status": receipt["status"]}, sort_keys=True))
|
||||
return 0 if receipt["status"] == "pass" else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
135
tests/test_verify_leo_unseen_reasoning_chain.py
Normal file
135
tests/test_verify_leo_unseen_reasoning_chain.py
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
|
||||
from scripts import verify_leo_unseen_reasoning_chain as verifier
|
||||
|
||||
CLAIM_ID = "fd159490-280d-4ede-84ef-faa169cff766"
|
||||
|
||||
|
||||
def execution_manifest(execution_sha256: str, previous: str | None, *, first: bool) -> dict:
|
||||
return {
|
||||
"execution_sha256": execution_sha256,
|
||||
"attribution": {"status": "complete"},
|
||||
"session_boundary": {
|
||||
"conversation": {
|
||||
"previous_execution_sha256": previous,
|
||||
"prior_turn_state_bound": True,
|
||||
"history_prefix_preserved": True,
|
||||
"starts_from_empty_conversation": first,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def passing_report() -> dict:
|
||||
replies = [
|
||||
(
|
||||
"The claim fd159490 has confidence 0.9. Its grounds row is no_source_pointer and the only other support "
|
||||
"is an internal document with no external primary source. First narrower test: test one tradition."
|
||||
),
|
||||
(
|
||||
"Assumptions versus observed support: the categorical leap is assumed. Falsifier: one contrary case. "
|
||||
"PROPOSAL A is empirical. PROPOSAL B is structural. Both are staged only; nothing applied. fd159490."
|
||||
),
|
||||
(
|
||||
"Revised Proposal A separates the mechanism from the outcome. Ostrom, Governing the Commons, is the "
|
||||
"primary per-case source. Stage pending_review, then human review; approved is not apply, and applied_at "
|
||||
"must be non-null. Nothing staged or changed here."
|
||||
),
|
||||
]
|
||||
fingerprints = {
|
||||
"fingerprint_sha256": "f" * 64,
|
||||
"table_count": 39,
|
||||
"total_rows": 52167,
|
||||
}
|
||||
first_sha = "a" * 64
|
||||
second_sha = "b" * 64
|
||||
third_sha = "c" * 64
|
||||
return {
|
||||
"remote_run_id": "run-1",
|
||||
"db_counts_changed": False,
|
||||
"db_fingerprint_unchanged": True,
|
||||
"db_fingerprint_before": fingerprints,
|
||||
"db_fingerprint_after": fingerprints,
|
||||
"safety_gate": {"status": "pass"},
|
||||
"execution_manifest_summary": {
|
||||
"all_turns_attribution_complete": True,
|
||||
"harness_source": {"git_head": "d" * 40},
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"prompt_id": verifier.PROMPTS[0]["id"],
|
||||
"prompt": verifier.PROMPTS[0]["message"],
|
||||
"reply": replies[0],
|
||||
"database_tool_trace": {
|
||||
"calls": [
|
||||
{
|
||||
"database_invocations": [{"subcommand": "context"}],
|
||||
"result": {"row_ids": [CLAIM_ID]},
|
||||
}
|
||||
]
|
||||
},
|
||||
"execution_manifest": execution_manifest(first_sha, None, first=True),
|
||||
},
|
||||
{
|
||||
"prompt_id": verifier.PROMPTS[1]["id"],
|
||||
"prompt": verifier.PROMPTS[1]["message"],
|
||||
"reply": replies[1],
|
||||
"database_tool_trace": {
|
||||
"calls": [
|
||||
{
|
||||
"database_invocations": [
|
||||
{"subcommand": "show"},
|
||||
{"subcommand": "evidence"},
|
||||
],
|
||||
"result": {"row_ids": [CLAIM_ID]},
|
||||
}
|
||||
]
|
||||
},
|
||||
"execution_manifest": execution_manifest(second_sha, first_sha, first=False),
|
||||
},
|
||||
{
|
||||
"prompt_id": verifier.PROMPTS[2]["id"],
|
||||
"prompt": verifier.PROMPTS[2]["message"],
|
||||
"reply": replies[2],
|
||||
"database_tool_trace": {"calls": []},
|
||||
"execution_manifest": execution_manifest(third_sha, second_sha, first=False),
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_unseen_reasoning_chain_passes_only_with_db_challenge_revision_and_apply_boundary() -> None:
|
||||
receipt = verifier.verify_report(passing_report(), source_report_sha256="e" * 64)
|
||||
|
||||
assert receipt["status"] == "pass"
|
||||
assert all(receipt["checks"].values())
|
||||
assert all(receipt["outcomes"].values())
|
||||
assert receipt["selected_claim_ids"] == [CLAIM_ID]
|
||||
assert receipt["execution_sha256_chain"] == ["a" * 64, "b" * 64, "c" * 64]
|
||||
|
||||
|
||||
def test_unseen_reasoning_chain_rejects_a_challenge_that_does_not_reinspect_evidence() -> None:
|
||||
report = copy.deepcopy(passing_report())
|
||||
report["results"][1]["database_tool_trace"]["calls"][0]["database_invocations"] = [
|
||||
{"subcommand": "show"}
|
||||
]
|
||||
|
||||
receipt = verifier.verify_report(report, source_report_sha256="e" * 64)
|
||||
|
||||
assert receipt["status"] == "fail"
|
||||
assert receipt["checks"]["same_claim_body_and_evidence_reinspected_after_challenge"] is False
|
||||
assert receipt["outcomes"]["survives_a_claim_body_and_evidence_challenge"] is False
|
||||
|
||||
|
||||
def test_unseen_reasoning_chain_rejects_broken_conversation_parent_hash() -> None:
|
||||
report = copy.deepcopy(passing_report())
|
||||
report["results"][2]["execution_manifest"]["session_boundary"]["conversation"][
|
||||
"previous_execution_sha256"
|
||||
] = "0" * 64
|
||||
|
||||
receipt = verifier.verify_report(report, source_report_sha256="e" * 64)
|
||||
|
||||
assert receipt["status"] == "fail"
|
||||
assert receipt["checks"]["all_turn_execution_manifests_complete"] is False
|
||||
Loading…
Reference in a new issue