160 lines
5.6 KiB
Python
160 lines
5.6 KiB
Python
import copy
|
|||
|
|
import hashlib
|
||
|
|
import json
|
||
|
|
import stat
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
import agents.gewu.scripts.run_checkpoint as checkpoint_module
|
||
|
|
import pytest
|
||
|
|
from agents.gewu.scripts.run_checkpoint import (
|
||
|
|
create_checkpoint,
|
||
|
|
load_checkpoint,
|
||
|
|
update_checkpoint,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def test_run_checkpoint_records_scope_coverage_receipt_and_pins(tmp_path: Path) -> None:
|
||
|
|
workspace = tmp_path / "research-workspace"
|
||
|
|
checkpoint = create_checkpoint(
|
||
|
|
workspace,
|
||
|
|
run_id="run-2026-01",
|
||
|
|
objective="Compare audience response",
|
||
|
|
projects=["dome-performance"],
|
||
|
|
include_shared=True,
|
||
|
|
lens="Audience behavior",
|
||
|
|
budgets={"sources": 8},
|
||
|
|
)
|
||
|
|
path = workspace / "Runs" / "run-2026-01.json"
|
||
|
|
assert stat.S_IMODE(path.stat().st_mode) == 0o600
|
||
|
|
assert checkpoint["pack"]["snapshot_digest"]
|
||
|
|
|
||
|
|
updated = update_checkpoint(
|
||
|
|
workspace,
|
||
|
|
"run-2026-01",
|
||
|
|
{
|
||
|
|
"stage": "report",
|
||
|
|
"sources": [
|
||
|
|
{
|
||
|
|
"source_revision_id": "source:r1",
|
||
|
|
"extraction_id": "a" * 64,
|
||
|
|
"status": "extracted",
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"coverage": {
|
||
|
|
"selected": ["section-1", "section-2"],
|
||
|
|
"included": ["section-1"],
|
||
|
|
"excluded": [{"item": "section-2", "reason": "outside project lens"}],
|
||
|
|
"skipped": [],
|
||
|
|
},
|
||
|
|
"pending_operations": [
|
||
|
|
{
|
||
|
|
"operation_id": "proposal-op-1",
|
||
|
|
"tool_name": "proposal_submit",
|
||
|
|
"input_sha256": "b" * 64,
|
||
|
|
"status": "complete",
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"proposals": [
|
||
|
|
{
|
||
|
|
"proposal_id": "proposal-1",
|
||
|
|
"revision": 1,
|
||
|
|
"status": "accepted",
|
||
|
|
"receipt": {
|
||
|
|
"proposal_id": "proposal-1",
|
||
|
|
"revision": 1,
|
||
|
|
"state": "accepted",
|
||
|
|
"commit_id": "c" * 40,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"outputs": [{"sha256": "d" * 64}],
|
||
|
|
},
|
||
|
|
)
|
||
|
|
resumed = load_checkpoint(workspace, "run-2026-01")
|
||
|
|
assert resumed == updated
|
||
|
|
assert resumed["coverage"]["excluded"][0]["reason"] == "outside project lens"
|
||
|
|
assert resumed["proposals"][0]["receipt"]["commit_id"] == "c" * 40
|
||
|
|
with pytest.raises(ValueError, match="does not match"):
|
||
|
|
update_checkpoint(
|
||
|
|
workspace,
|
||
|
|
"run-2026-01",
|
||
|
|
{
|
||
|
|
"proposals": [
|
||
|
|
{
|
||
|
|
"proposal_id": "proposal-1",
|
||
|
|
"revision": 1,
|
||
|
|
"status": "accepted",
|
||
|
|
"receipt": {
|
||
|
|
"proposal_id": "proposal-other",
|
||
|
|
"revision": 1,
|
||
|
|
"state": "accepted",
|
||
|
|
"commit_id": "c" * 40,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
]
|
||
|
|
},
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def test_run_checkpoint_rejects_secrets_bad_progress_and_snapshot_drift(tmp_path: Path) -> None:
|
||
|
|
workspace = tmp_path / "research-workspace"
|
||
|
|
create_checkpoint(
|
||
|
|
workspace,
|
||
|
|
run_id="run-drift-01",
|
||
|
|
objective="Test resume pinning",
|
||
|
|
projects=["dome-performance"],
|
||
|
|
include_shared=False,
|
||
|
|
lens="A bounded lens",
|
||
|
|
)
|
||
|
|
with pytest.raises(ValueError, match="credentials or source bodies"):
|
||
|
|
update_checkpoint(workspace, "run-drift-01", {"last_failure": {"api_key": "secret"}})
|
||
|
|
with pytest.raises(ValueError, match="SHA-256"):
|
||
|
|
update_checkpoint(
|
||
|
|
workspace,
|
||
|
|
"run-drift-01",
|
||
|
|
{
|
||
|
|
"outputs": [{"sha256": "not-a-digest"}],
|
||
|
|
"stage": "report",
|
||
|
|
},
|
||
|
|
)
|
||
|
|
|
||
|
|
path = workspace / "Runs" / "run-drift-01.json"
|
||
|
|
checkpoint = json.loads(path.read_text(encoding="utf-8"))
|
||
|
|
checkpoint["pack"]["snapshot_digest"] = "0" * 64
|
||
|
|
path.write_text(json.dumps(checkpoint), encoding="utf-8")
|
||
|
|
with pytest.raises(ValueError, match="pack changed"):
|
||
|
|
load_checkpoint(workspace, "run-drift-01")
|
||
|
|
|
||
|
|
|
||
|
|
def test_existing_run_cannot_resume_after_verified_pack_upgrade(
|
||
|
|
tmp_path: Path,
|
||
|
|
monkeypatch: pytest.MonkeyPatch,
|
||
|
|
) -> None:
|
||
|
|
workspace = tmp_path / "research-workspace"
|
||
|
|
create_checkpoint(
|
||
|
|
workspace,
|
||
|
|
run_id="run-pack-update-01",
|
||
|
|
objective="Resume with pinned modules",
|
||
|
|
projects=["dome-performance"],
|
||
|
|
include_shared=True,
|
||
|
|
lens="Keep the original extraction and evidence rules",
|
||
|
|
)
|
||
|
|
checkpoint_path = workspace / "Runs" / "run-pack-update-01.json"
|
||
|
|
original_checkpoint = checkpoint_path.read_bytes()
|
||
|
|
|
||
|
|
updated_manifest = copy.deepcopy(checkpoint_module.load_manifest())
|
||
|
|
updated_manifest["pack_version"] = "0.7.0"
|
||
|
|
updated_manifest["modules"][0]["implementation_version"] += ".updated"
|
||
|
|
modules_json = json.dumps(
|
||
|
|
updated_manifest["modules"], ensure_ascii=False, sort_keys=True, separators=(",", ":")
|
||
|
|
)
|
||
|
|
updated_manifest["snapshot"]["digest"] = hashlib.sha256(modules_json.encode()).hexdigest()
|
||
|
|
|
||
|
|
# This fixture stands in for a newer pack whose own manifest verification
|
||
|
|
# succeeded; the behavior under test is the active run's compatibility pin.
|
||
|
|
monkeypatch.setattr(checkpoint_module, "load_manifest", lambda: updated_manifest)
|
||
|
|
monkeypatch.setattr(checkpoint_module, "verify", lambda _manifest: None)
|
||
|
|
|
||
|
|
with pytest.raises(ValueError, match="pack changed since this run"):
|
||
|
|
load_checkpoint(workspace, "run-pack-update-01")
|
||
|
|
assert checkpoint_path.read_bytes() == original_checkpoint
|