its-magic 0.1.2-35 → 0.1.2-37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Validate intake_evidence JSON bundles (US-0078 / DEC-0060).
4
+
5
+ Used by PO workflow preflight and CI fixtures.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import argparse
11
+ import json
12
+ import os
13
+ import sys
14
+
15
+ _SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
16
+ _REPO_ROOT = os.path.normpath(os.path.join(_SCRIPT_DIR, ".."))
17
+ if _SCRIPT_DIR not in sys.path:
18
+ sys.path.insert(0, _SCRIPT_DIR)
19
+
20
+ import intake_evidence_lib # noqa: E402
21
+
22
+
23
+ def main() -> int:
24
+ p = argparse.ArgumentParser(description="Validate intake_evidence JSON (US-0078).")
25
+ p.add_argument("--file", help="Path to JSON file containing one intake_evidence object.")
26
+ p.add_argument(
27
+ "--stdin",
28
+ action="store_true",
29
+ help="Read JSON object from stdin.",
30
+ )
31
+ p.add_argument(
32
+ "--self-test",
33
+ action="store_true",
34
+ help="Run library sanity checks and exit.",
35
+ )
36
+ args = p.parse_args()
37
+
38
+ if args.self_test:
39
+ intake_evidence_lib.self_test()
40
+ print("[INTAKE_EVIDENCE_SELF_TEST_OK]")
41
+ return 0
42
+
43
+ raw = ""
44
+ if args.file:
45
+ with open(os.path.abspath(args.file), encoding="utf-8") as f:
46
+ raw = f.read()
47
+ elif args.stdin:
48
+ raw = sys.stdin.read()
49
+ else:
50
+ print("error: specify --file, --stdin, or --self-test", file=sys.stderr)
51
+ return 2
52
+
53
+ try:
54
+ bundle = json.loads(raw)
55
+ except json.JSONDecodeError as e:
56
+ print(f"error: invalid JSON: {e}", file=sys.stderr)
57
+ return 2
58
+
59
+ if not isinstance(bundle, dict):
60
+ print("error: root must be a JSON object", file=sys.stderr)
61
+ return 2
62
+
63
+ r = intake_evidence_lib.validate_intake_evidence(bundle)
64
+ if r.ok:
65
+ print("[INTAKE_EVIDENCE_VALIDATION_OK]")
66
+ return 0
67
+
68
+ print(intake_evidence_lib.format_blocked_message(r), file=sys.stderr)
69
+ return 1
70
+
71
+
72
+ if __name__ == "__main__":
73
+ raise SystemExit(main())
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env python3
2
+ """Compare two token-cost run JSON records for AC-2 (same run_class_hash, 50% cache read)."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import json
8
+ import os
9
+ import sys
10
+ from pathlib import Path
11
+
12
+ _SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
13
+ if _SCRIPT_DIR not in sys.path:
14
+ sys.path.insert(0, _SCRIPT_DIR)
15
+
16
+ import token_cost_lib # noqa: E402
17
+
18
+
19
+ def main() -> int:
20
+ p = argparse.ArgumentParser(description=__doc__)
21
+ p.add_argument("baseline", type=Path, help="JSON file with run_class_hash + totals")
22
+ p.add_argument("target", type=Path, help="JSON file with run_class_hash + totals")
23
+ p.add_argument(
24
+ "--reduction-fraction",
25
+ type=float,
26
+ default=0.5,
27
+ help="Required fractional reduction in cache_read_tokens (default 0.5)",
28
+ )
29
+ args = p.parse_args()
30
+ base = json.loads(args.baseline.read_text(encoding="utf-8"))
31
+ tgt = json.loads(args.target.read_text(encoding="utf-8"))
32
+ ok, msg = token_cost_lib.compare_cache_read_reduction(
33
+ base, tgt, reduction_fraction=args.reduction_fraction
34
+ )
35
+ print(msg)
36
+ return 0 if ok else 2
37
+
38
+
39
+ if __name__ == "__main__":
40
+ sys.exit(main())
@@ -0,0 +1,108 @@
1
+ """Token-cost run class, metrics, and AC-2 comparability helpers (US-0080 / DEC-0062)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ from typing import Any, Mapping
8
+
9
+ # Canonical metric keys (DEC-0062 §1) — totals / per-phase rows
10
+ METRIC_KEYS = (
11
+ "cache_read_tokens",
12
+ "input_tokens",
13
+ "output_tokens",
14
+ "phase_call_count",
15
+ "cache_creation_tokens",
16
+ "orchestrator_call_estimate",
17
+ )
18
+
19
+ REQUIRED_TOTAL_KEYS = (
20
+ "cache_read_tokens",
21
+ "input_tokens",
22
+ "output_tokens",
23
+ )
24
+
25
+
26
+ def canonical_json_dumps(obj: Mapping[str, Any]) -> str:
27
+ return json.dumps(obj, sort_keys=True, separators=(",", ":"))
28
+
29
+
30
+ def compute_run_class_hash(run_class: Mapping[str, Any]) -> str:
31
+ """SHA-256 hex (lowercase) of canonical JSON for DEC-0062 §2 tuple."""
32
+ payload = canonical_json_dumps(run_class)
33
+ return hashlib.sha256(payload.encode("utf-8")).hexdigest()
34
+
35
+
36
+ def compute_strict_proof_hash(
37
+ orchestrator_run_id: str,
38
+ runtime_proof_id: str,
39
+ phase_id: str,
40
+ role: str,
41
+ proof_issued_at: str,
42
+ proof_ttl_seconds: int,
43
+ ) -> str:
44
+ """DEC-0038-style sorted-key JSON SHA-256 over the strict-proof tuple fields."""
45
+ obj = {
46
+ "orchestrator_run_id": orchestrator_run_id,
47
+ "runtime_proof_id": runtime_proof_id,
48
+ "phase_id": phase_id,
49
+ "role": role,
50
+ "proof_issued_at": proof_issued_at,
51
+ "proof_ttl_seconds": int(proof_ttl_seconds),
52
+ }
53
+ return hashlib.sha256(canonical_json_dumps(obj).encode("utf-8")).hexdigest()
54
+
55
+
56
+ def validate_run_totals(row: Mapping[str, Any]) -> list[str]:
57
+ """Return diagnostic strings for invalid totals; empty if OK."""
58
+ errs: list[str] = []
59
+ for k in REQUIRED_TOTAL_KEYS:
60
+ if k not in row:
61
+ errs.append(f"missing_metric:{k}")
62
+ continue
63
+ v = row[k]
64
+ if not isinstance(v, int) or v < 0:
65
+ errs.append(f"invalid_non_negative_int:{k}")
66
+ if "metric_source" not in row or not str(row.get("metric_source", "")).strip():
67
+ errs.append("missing_metric_source")
68
+ return errs
69
+
70
+
71
+ def compare_cache_read_reduction(
72
+ baseline: Mapping[str, Any],
73
+ target: Mapping[str, Any],
74
+ *,
75
+ reduction_fraction: float = 0.5,
76
+ ) -> tuple[bool, str]:
77
+ """
78
+ AC-2: target must achieve >= reduction_fraction lower cache_read_tokens
79
+ vs baseline when run_class_hash matches. Otherwise TOKEN_COST_RUN_CLASS_MISMATCH.
80
+ """
81
+ hb = baseline.get("run_class_hash")
82
+ ht = target.get("run_class_hash")
83
+ if not isinstance(hb, str) or not isinstance(ht, str) or hb != ht:
84
+ return False, "TOKEN_COST_RUN_CLASS_MISMATCH: run_class_hash differs or missing"
85
+
86
+ tb = baseline.get("totals") or {}
87
+ tt = target.get("totals") or {}
88
+ if not isinstance(tb, Mapping) or not isinstance(tt, Mapping):
89
+ return False, "TOKEN_COST_RUN_CLASS_MISMATCH: totals object missing"
90
+
91
+ br = tb.get("cache_read_tokens")
92
+ tr = tt.get("cache_read_tokens")
93
+ if not isinstance(br, int) or not isinstance(tr, int) or br < 0 or tr < 0:
94
+ return False, "TOKEN_COST_RUN_CLASS_MISMATCH: invalid cache_read_tokens"
95
+
96
+ if br == 0:
97
+ if tr == 0:
98
+ return True, "ok: baseline and target zero cache_read_tokens"
99
+ return False, "TOKEN_COST_RUN_CLASS_MISMATCH: baseline zero but target non-zero"
100
+
101
+ max_target = int(br * (1.0 - reduction_fraction))
102
+ if tr > max_target:
103
+ return (
104
+ False,
105
+ f"TOKEN_COST_RUN_CLASS_MISMATCH: cache_read_tokens {tr} exceeds "
106
+ f"50% reduction threshold (baseline {br}, max_target {max_target})",
107
+ )
108
+ return True, "ok: cache_read_tokens meets 50% reduction vs baseline"