#!/usr/bin/env python3
|
"""Read-only verifier for the NEWENERGY BATCH-002 acceptance sync."""
|
|
from __future__ import annotations
|
|
import csv
|
import hashlib
|
import json
|
from pathlib import Path
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
INDUSTRY = ROOT / "ana-data/cases/新能源案例"
|
CASE_ID = "ANA-NEWENERGY-FOUR-TRACK-ATLAS-20260806-002"
|
CASE = INDUSTRY / CASE_ID
|
RESULT = ROOT / "ana-data/result/新能源案例" / CASE_ID
|
ACCEPTED = "ACCEPTED_BY_INDEPENDENT_REVIEW"
|
FINAL_ARTIFACT = "FINAL_ACCEPTED_BY_INDEPENDENT_REVIEW"
|
FINAL_AUDIT = "AUDIT-ANA-NEWENERGY-FOUR-TRACK-ATLAS-BATCH002-EXECUTION-OUTPUT-REPAIR004-REREVIEW-20260807-001"
|
|
|
def sha256(path: Path) -> str:
|
return hashlib.sha256(path.read_bytes()).hexdigest().upper()
|
|
|
def rows(path: Path) -> list[dict[str, str]]:
|
with path.open("r", encoding="utf-8-sig", newline="") as handle:
|
return list(csv.DictReader(handle))
|
|
|
output_manifest = CASE / "manifest/output_manifest.csv"
|
outputs = rows(output_manifest)
|
assert len(outputs) == 42
|
assert sum(row["output_status"] == "FINAL_ACCEPTED" for row in outputs) == 40
|
assert sum(row["output_status"] == "NOT_APPLICABLE" for row in outputs) == 2
|
assert all(row["review_status"] == ACCEPTED for row in outputs)
|
for row in outputs:
|
if row["applicability"] == "APPLICABLE_FILE_OUTPUT":
|
path = ROOT / row["output_path"]
|
assert path.is_file()
|
assert path.stat().st_size == int(row["file_size"])
|
assert sha256(path) == row["sha256"].upper()
|
|
artifact_manifest = INDUSTRY / "manifest/artifact_manifest_BATCH002.csv"
|
artifacts = rows(artifact_manifest)
|
assert len(artifacts) == 85
|
assert len({row["relative_path"] for row in artifacts}) == 85
|
for row in artifacts:
|
path = ROOT / row["relative_path"]
|
assert path.is_file()
|
assert path.stat().st_size == int(row["file_size"])
|
assert sha256(path) == row["sha256"].upper()
|
assert row["artifact_status"] == FINAL_ARTIFACT
|
assert row["review_status"] == ACCEPTED
|
|
for path in (
|
CASE / "outputs",
|
CASE / "evidence",
|
CASE / "manifest",
|
RESULT,
|
):
|
for item in path.rglob("*"):
|
if item.is_file() and item.suffix.lower() in {".md", ".csv", ".json"}:
|
text = item.read_text(encoding="utf-8-sig")
|
assert "\ufffd" not in text
|
assert "DRAFT_FOR_REVIEW" not in text
|
|
registry = rows(INDUSTRY / "manifest/canonical_shard_registry_BATCH002.csv")
|
assert len(registry) == 33
|
for row in registry:
|
shard = ROOT / row["shard_path"]
|
assert sha256(shard) == row["sha256"].upper()
|
assert len(rows(shard)) == int(row["row_count"])
|
assert row["review_status"] == ACCEPTED
|
if row["batch_id"] == "BATCH-002":
|
assert row["immutable"] == "YES_ACCEPTED_IMMUTABLE"
|
|
current = rows(INDUSTRY / "manifest/current_output_manifest.csv")
|
assert len(current) == 25
|
assert sha256(INDUSTRY / "manifest/current_output_manifest.csv") == "A2BEAF2CD0D3955ACF9DE8C6769D4BC9A3A7A1094FDD28C8447B66FD09372EB7"
|
assert not any("BATCH-002" in row.get("source_path", "") or CASE_ID in row.get("source_path", "") for row in current)
|
|
acceptance = json.loads((CASE / "manifest/acceptance_validation_receipt.json").read_text(encoding="utf-8"))
|
assert acceptance["status"] == ACCEPTED
|
assert acceptance["final_audit"] == FINAL_AUDIT
|
assert acceptance["validation_status"] == "PASS"
|
record = (RESULT / "acceptance_record.md").read_text(encoding="utf-8")
|
assert f"final_audit={FINAL_AUDIT}" in record
|
|
print(json.dumps({
|
"status": "PASS_READ_ONLY_ACCEPTANCE_VERIFIER",
|
"outputs": "40_FINAL_ACCEPTED+2_NOT_APPLICABLE",
|
"output_manifest_rows": len(outputs),
|
"artifact_exact_set": len(artifacts),
|
"registry": "33/33_HASH_AND_ROW_COUNT_MATCH",
|
"batch002_current_release_paths": 0,
|
"output_manifest_sha256": sha256(output_manifest),
|
"artifact_manifest_sha256": sha256(artifact_manifest),
|
"acceptance_record_sha256": sha256(RESULT / "acceptance_record.md"),
|
"acceptance_validation_sha256": sha256(CASE / "manifest/acceptance_validation_receipt.json"),
|
}, ensure_ascii=False, sort_keys=True))
|