feat(scada): add calibrated monitoring and incident recovery
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Create a private, read-only preflight report for the tjwater_v2 burst suite."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
"--suite",
|
||||
type=Path,
|
||||
default=Path("resources/benchmarks/tjwater-v2-burst.json"),
|
||||
help="versioned burst suite manifest",
|
||||
)
|
||||
parser.add_argument("--case", action="append", default=[], help="case ID to inspect; repeatable")
|
||||
parser.add_argument(
|
||||
"--output-root",
|
||||
type=Path,
|
||||
default=Path("data/benchmark/preflight"),
|
||||
help="private local artifact directory (default: ignored data/benchmark/preflight)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
from scripts.benchmark.contracts import fingerprint, load_suite
|
||||
from scripts.benchmark.source import inspect_case, read_model, resolve_sources, source_identity, readonly_source
|
||||
|
||||
try:
|
||||
suite = load_suite(args.suite)
|
||||
cases = suite.select(args.case)
|
||||
identity, routes = asyncio.run(resolve_sources(suite))
|
||||
|
||||
with readonly_source(routes["biz_data"]) as biz_connection:
|
||||
biz_source = source_identity(biz_connection, routes["biz_data"])
|
||||
model = read_model(biz_connection, cases)
|
||||
with readonly_source(routes["iot_data"]) as iot_connection:
|
||||
iot_source = source_identity(iot_connection, routes["iot_data"])
|
||||
case_results = [inspect_case(iot_connection, suite, case, model) for case in cases]
|
||||
|
||||
content_fingerprint = fingerprint({
|
||||
"suite": suite.model_dump(mode="json"),
|
||||
"source_routes": {
|
||||
"biz_data": biz_source["route_sha256"],
|
||||
"iot_data": iot_source["route_sha256"],
|
||||
},
|
||||
"model": model,
|
||||
"cases": [{
|
||||
"case_id": result["case_id"],
|
||||
"window_sha256": result["window_sha256"],
|
||||
"topology_reference": result["topology_reference"],
|
||||
"data_candidate_available": result["data_candidate_available"],
|
||||
"blockers": result["blockers"],
|
||||
} for result in case_results],
|
||||
})
|
||||
report = {
|
||||
"report_version": "burst-preflight@1",
|
||||
"status": "preflight_complete",
|
||||
"readiness": "preflight_only; isolated runtime and Agent execution are not verified",
|
||||
"created_at": datetime.now(timezone.utc),
|
||||
"project": identity,
|
||||
"sources": {"biz_data": biz_source, "iot_data": iot_source},
|
||||
"suite_id": suite.suite_id,
|
||||
"suite_version": suite.version,
|
||||
"content_fingerprint_sha256": content_fingerprint,
|
||||
"source_effect": "read-only transactions; no source database writes requested",
|
||||
"cases": case_results,
|
||||
"limitations": [
|
||||
"preflight does not execute Agent, work-order, SCADA replay worker, or MCP tools",
|
||||
"SCADA event data requiring generation remains blocked until produced in an isolated dataset",
|
||||
"this report is not a frozen evaluation database snapshot",
|
||||
"numeric reference outputs and approved equivalent candidate groups still require review",
|
||||
],
|
||||
}
|
||||
|
||||
stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
|
||||
output_dir = args.output_root / f"{suite.suite_id}-{stamp}-{uuid4().hex[:8]}"
|
||||
output_dir.mkdir(parents=True, mode=0o700)
|
||||
os.chmod(output_dir, 0o700)
|
||||
output_file = output_dir / "preflight.json"
|
||||
output_file.write_text(
|
||||
json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True, default=str) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
os.chmod(output_file, 0o600)
|
||||
except Exception as exc:
|
||||
# Driver exceptions can include connection details; never echo them into CI logs.
|
||||
frames = traceback.extract_tb(exc.__traceback__)
|
||||
location = frames[-1] if frames else None
|
||||
suffix = f" at {location.filename}:{location.lineno}" if location else ""
|
||||
print(f"benchmark preflight failed ({type(exc).__name__}){suffix}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
work_order_candidates = sum(bool(case["variants"][0]["data_candidate_available"])
|
||||
for case in case_results)
|
||||
scada_candidates = sum(bool(case["variants"][1]["data_candidate_available"])
|
||||
for case in case_results)
|
||||
print(
|
||||
f"preflight complete: {work_order_candidates}/{len(case_results)} WO and "
|
||||
f"{scada_candidates}/{len(case_results)} SCADA data candidates; report: {output_file}"
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user