Skip to content

Commit c4b24a1

Browse files
authored
Merge pull request #144 from QuantStrategyLab/codex/qsl-p4-runtime-evidence-contract-fresh-binance-20260804
feat: validate redacted runtime evidence aggregate
2 parents 2cfd418 + 7421691 commit c4b24a1

2 files changed

Lines changed: 287 additions & 0 deletions

File tree

runtime_support.py

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import hashlib
22
import os
3+
import re
34
import time
45
from collections.abc import Mapping
56
from dataclasses import dataclass, field
@@ -11,6 +12,23 @@
1112
# Binance rate limits (public API: 1200 weight/min, order placement: 50 orders/10s)
1213
_BINANCE_ORDER_RATE_LIMIT_INTERVAL_SEC = 0.25 # max ~4 orders/sec
1314
_LAST_API_CALL_TS: float = 0.0
15+
RUNTIME_EVIDENCE_CONTRACT_VERSION = "qsl.runtime_evidence_aggregate.v1"
16+
RECONCILIATION_STATUSES = frozenset({"MISSING", "MATCHED", "MISMATCHED"})
17+
_RUNTIME_EVIDENCE_FORBIDDEN_FIELDS = frozenset(
18+
{
19+
"api_key",
20+
"api_secret",
21+
"authorization",
22+
"balances",
23+
"credentials",
24+
"headers",
25+
"orders",
26+
"positions",
27+
"provider_rows",
28+
"secret",
29+
"token",
30+
}
31+
)
1432

1533

1634
def _rate_limit_pause():
@@ -22,6 +40,200 @@ def _rate_limit_pause():
2240
_LAST_API_CALL_TS = time.monotonic()
2341

2442

43+
def _is_sha256(value: Any) -> bool:
44+
return isinstance(value, str) and bool(re.fullmatch(r"[0-9a-f]{64}", value.strip()))
45+
46+
47+
def _is_git_revision(value: Any) -> bool:
48+
return isinstance(value, str) and bool(re.fullmatch(r"[0-9a-f]{40}", value.strip()))
49+
50+
51+
def _is_utc_timestamp(value: Any) -> bool:
52+
if not isinstance(value, str) or not value.endswith("Z"):
53+
return False
54+
try:
55+
datetime.fromisoformat(value.replace("Z", "+00:00"))
56+
except ValueError:
57+
return False
58+
return True
59+
60+
61+
def _append_missing_fields(payload: Mapping[str, Any], fields: tuple[str, ...], errors: list[str], label: str) -> None:
62+
for field_name in fields:
63+
if field_name not in payload:
64+
errors.append(f"{label} missing field: {field_name}")
65+
66+
67+
def _append_forbidden_field_errors(value: Any, errors: list[str]) -> None:
68+
if isinstance(value, Mapping):
69+
for field, nested_value in value.items():
70+
if str(field).lower() in _RUNTIME_EVIDENCE_FORBIDDEN_FIELDS:
71+
errors.append(f"runtime_evidence_aggregate contains forbidden field: {field}")
72+
_append_forbidden_field_errors(nested_value, errors)
73+
elif isinstance(value, (list, tuple)):
74+
for item in value:
75+
_append_forbidden_field_errors(item, errors)
76+
77+
78+
def _validate_release_identity(identity: Any, errors: list[str]) -> None:
79+
label = "runtime_evidence_aggregate release_identity"
80+
if not isinstance(identity, Mapping):
81+
errors.append(f"{label} must be an object")
82+
return
83+
_append_missing_fields(
84+
identity,
85+
(
86+
"strategy_profile",
87+
"mode",
88+
"source_revision",
89+
"input_timestamp",
90+
"artifact_contract",
91+
"artifact_version",
92+
"artifacts",
93+
),
94+
errors,
95+
label,
96+
)
97+
for field_name in ("strategy_profile", "mode", "artifact_contract", "artifact_version"):
98+
if not isinstance(identity.get(field_name), str) or not identity[field_name].strip():
99+
errors.append(f"{label} {field_name} must be a non-empty string")
100+
if not _is_git_revision(identity.get("source_revision")):
101+
errors.append(f"{label} source_revision must be a 40-character lowercase git SHA")
102+
if not _is_utc_timestamp(identity.get("input_timestamp")):
103+
errors.append(f"{label} input_timestamp must be a UTC timestamp")
104+
artifacts = identity.get("artifacts")
105+
if not isinstance(artifacts, Mapping) or not artifacts:
106+
errors.append(f"{label} artifacts must be a non-empty object")
107+
return
108+
for artifact_name, artifact in artifacts.items():
109+
if not isinstance(artifact_name, str) or not artifact_name.strip() or not isinstance(artifact, Mapping):
110+
errors.append(f"{label} artifacts must contain named objects")
111+
continue
112+
if not _is_sha256(artifact.get("sha256")):
113+
errors.append(f"{label} artifacts.{artifact_name}.sha256 must be a SHA-256 digest")
114+
115+
116+
def _validate_reconciliation(reconciliation: Any, errors: list[str]) -> None:
117+
label = "runtime_evidence_aggregate reconciliation"
118+
if not isinstance(reconciliation, Mapping):
119+
errors.append(f"{label} must be an object")
120+
return
121+
status = reconciliation.get("status")
122+
if status not in RECONCILIATION_STATUSES:
123+
errors.append(f"{label} status must be one of MISSING, MATCHED, MISMATCHED")
124+
return
125+
if status == "MATCHED":
126+
for field in ("durable_receipt_sha256", "identity_sha256"):
127+
if not _is_sha256(reconciliation.get(field)):
128+
errors.append(f"{label}.MATCHED requires {field}")
129+
errors.append(f"{label}.MATCHED is not valid for static acceptance")
130+
elif status == "MISMATCHED":
131+
for field in ("durable_receipt_sha256", "identity_sha256", "observed_identity_sha256"):
132+
if not _is_sha256(reconciliation.get(field)):
133+
errors.append(f"{label}.MISMATCHED requires {field}")
134+
if reconciliation.get("identity_sha256") == reconciliation.get("observed_identity_sha256"):
135+
errors.append(f"{label}.MISMATCHED identity digests must differ")
136+
137+
138+
def validate_runtime_evidence_aggregate(aggregate: Any) -> dict[str, Any]:
139+
"""Validate a redacted, static-only runtime evidence aggregate."""
140+
errors: list[str] = []
141+
label = "runtime_evidence_aggregate"
142+
if not isinstance(aggregate, Mapping):
143+
return {"ok": False, "errors": [f"{label} must be an object"]}
144+
145+
_append_forbidden_field_errors(aggregate, errors)
146+
_append_missing_fields(
147+
aggregate,
148+
(
149+
"contract_version",
150+
"release_identity",
151+
"risk_engine",
152+
"effective_exposure_cap",
153+
"stop_breaker_evaluation",
154+
"reconciliation",
155+
"static_validation_only",
156+
"execution_permitted",
157+
"verified_active",
158+
"fills_verified",
159+
"capital_use_verified",
160+
),
161+
errors,
162+
label,
163+
)
164+
if aggregate.get("contract_version") != RUNTIME_EVIDENCE_CONTRACT_VERSION:
165+
errors.append(f"{label} contract_version must be {RUNTIME_EVIDENCE_CONTRACT_VERSION}")
166+
_validate_release_identity(aggregate.get("release_identity"), errors)
167+
168+
risk_engine = aggregate.get("risk_engine")
169+
if not isinstance(risk_engine, Mapping):
170+
errors.append(f"{label} risk_engine must be an object")
171+
else:
172+
if risk_engine.get("outcome") != "APPROVE":
173+
errors.append(f"{label} risk_engine.outcome must be APPROVE")
174+
if not isinstance(risk_engine.get("policy_version"), str) or not risk_engine["policy_version"].strip():
175+
errors.append(f"{label} risk_engine.policy_version must be a non-empty string")
176+
177+
cap = aggregate.get("effective_exposure_cap")
178+
if not isinstance(cap, Mapping):
179+
errors.append(f"{label} effective_exposure_cap must be an object")
180+
else:
181+
value = cap.get("value")
182+
if isinstance(value, bool) or not isinstance(value, (int, float)) or not 0 < value <= 1:
183+
errors.append(f"{label} effective_exposure_cap.value must be in (0, 1]")
184+
for field in ("mandate_version", "source"):
185+
if not isinstance(cap.get(field), str) or not cap[field].strip():
186+
errors.append(f"{label} effective_exposure_cap.{field} must be a non-empty string")
187+
188+
stop_breaker = aggregate.get("stop_breaker_evaluation")
189+
if not isinstance(stop_breaker, Mapping):
190+
errors.append(f"{label} stop_breaker_evaluation must be an object")
191+
else:
192+
if stop_breaker.get("stop_evaluated") is not True:
193+
errors.append(f"{label} stop_breaker_evaluation.stop_evaluated must be true")
194+
if stop_breaker.get("breaker_evaluated") is not True:
195+
errors.append(f"{label} stop_breaker_evaluation.breaker_evaluated must be true")
196+
if stop_breaker.get("outcome") != "CLEAR":
197+
errors.append(f"{label} stop_breaker_evaluation.outcome must be CLEAR")
198+
if not isinstance(stop_breaker.get("policy_version"), str) or not stop_breaker["policy_version"].strip():
199+
errors.append(f"{label} stop_breaker_evaluation.policy_version must be a non-empty string")
200+
201+
_validate_reconciliation(aggregate.get("reconciliation"), errors)
202+
for field in ("static_validation_only", "execution_permitted", "verified_active", "fills_verified", "capital_use_verified"):
203+
expected = field == "static_validation_only"
204+
if aggregate.get(field) is not expected:
205+
errors.append(f"{label} {field} must be {str(expected).lower()} for static acceptance")
206+
return {"ok": not errors, "errors": errors}
207+
208+
209+
def build_runtime_evidence_aggregate(
210+
*,
211+
release_identity: Mapping[str, Any],
212+
risk_engine: Mapping[str, Any],
213+
effective_exposure_cap: Mapping[str, Any],
214+
stop_breaker_evaluation: Mapping[str, Any],
215+
reconciliation: Mapping[str, Any],
216+
) -> dict[str, Any]:
217+
"""Build a fail-closed aggregate that cannot claim runtime activity."""
218+
aggregate = {
219+
"contract_version": RUNTIME_EVIDENCE_CONTRACT_VERSION,
220+
"release_identity": dict(release_identity),
221+
"risk_engine": dict(risk_engine),
222+
"effective_exposure_cap": dict(effective_exposure_cap),
223+
"stop_breaker_evaluation": dict(stop_breaker_evaluation),
224+
"reconciliation": dict(reconciliation),
225+
"static_validation_only": True,
226+
"execution_permitted": False,
227+
"verified_active": False,
228+
"fills_verified": False,
229+
"capital_use_verified": False,
230+
}
231+
validation = validate_runtime_evidence_aggregate(aggregate)
232+
if not validation["ok"]:
233+
raise ValueError("Runtime evidence aggregate validation failed: " + "; ".join(validation["errors"]))
234+
return aggregate
235+
236+
25237
@dataclass
26238
class ExecutionRuntime:
27239
dry_run: bool = False

tests/test_runtime_support.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,90 @@
1414

1515
from runtime_support import (
1616
ExecutionRuntime,
17+
build_runtime_evidence_aggregate,
1718
build_execution_report,
1819
finalize_notification_delivery,
1920
record_gating_event,
2021
runtime_notify,
22+
validate_runtime_evidence_aggregate,
2123
)
2224
from quant_platform_kit.common.runtime_target import build_runtime_target
2325

2426

2527
class TestBuildExecutionReport(unittest.TestCase):
28+
@staticmethod
29+
def runtime_evidence_inputs():
30+
return {
31+
"release_identity": {
32+
"strategy_profile": "crypto_live_pool_rotation",
33+
"mode": "core_major",
34+
"source_revision": "a" * 40,
35+
"input_timestamp": "2026-03-13T00:00:00Z",
36+
"artifact_contract": "crypto_live_pool_rotation.live_pool.v1",
37+
"artifact_version": "2026-03-13-core_major",
38+
"artifacts": {"live_pool": {"sha256": "b" * 64}},
39+
},
40+
"risk_engine": {"outcome": "APPROVE", "policy_version": "bootstrap_small_account_v2"},
41+
"effective_exposure_cap": {
42+
"value": 0.5,
43+
"mandate_version": "bootstrap_small_account_v2",
44+
"source": "approved_risk_mandate",
45+
},
46+
"stop_breaker_evaluation": {
47+
"stop_evaluated": True,
48+
"breaker_evaluated": True,
49+
"outcome": "CLEAR",
50+
"policy_version": "bootstrap_small_account_v2",
51+
},
52+
"reconciliation": {"status": "MISSING"},
53+
}
54+
55+
def test_runtime_evidence_aggregate_is_redacted_and_static_only(self):
56+
aggregate = build_runtime_evidence_aggregate(**self.runtime_evidence_inputs())
57+
58+
self.assertTrue(validate_runtime_evidence_aggregate(aggregate)["ok"])
59+
self.assertFalse(aggregate["verified_active"])
60+
self.assertFalse(aggregate["fills_verified"])
61+
self.assertFalse(aggregate["capital_use_verified"])
62+
self.assertNotIn("orders", str(aggregate))
63+
64+
def test_runtime_evidence_aggregate_fails_closed_for_risk_reconciliation_and_sensitive_fields(self):
65+
aggregate = build_runtime_evidence_aggregate(**self.runtime_evidence_inputs())
66+
aggregate["risk_engine"]["outcome"] = "REJECT"
67+
aggregate["reconciliation"] = {"status": "MATCHED"}
68+
aggregate["positions"] = [{"symbol": "BTCUSDT"}]
69+
aggregate["release_identity"]["headers"] = {"authorization": "redacted"}
70+
71+
validation = validate_runtime_evidence_aggregate(aggregate)
72+
73+
self.assertFalse(validation["ok"])
74+
self.assertIn("runtime_evidence_aggregate risk_engine.outcome must be APPROVE", validation["errors"])
75+
self.assertIn(
76+
"runtime_evidence_aggregate reconciliation.MATCHED requires durable_receipt_sha256",
77+
validation["errors"],
78+
)
79+
self.assertIn("runtime_evidence_aggregate contains forbidden field: positions", validation["errors"])
80+
self.assertIn("runtime_evidence_aggregate contains forbidden field: headers", validation["errors"])
81+
82+
def test_runtime_evidence_aggregate_rejects_static_matched_reconciliation(self):
83+
matched_inputs = self.runtime_evidence_inputs()
84+
matched_inputs["reconciliation"] = {
85+
"status": "MATCHED",
86+
"durable_receipt_sha256": "c" * 64,
87+
"identity_sha256": "d" * 64,
88+
}
89+
mismatched_inputs = self.runtime_evidence_inputs()
90+
mismatched_inputs["reconciliation"] = {
91+
"status": "MISMATCHED",
92+
"durable_receipt_sha256": "c" * 64,
93+
"identity_sha256": "d" * 64,
94+
"observed_identity_sha256": "e" * 64,
95+
}
96+
97+
with self.assertRaisesRegex(ValueError, "MATCHED is not valid for static acceptance"):
98+
build_runtime_evidence_aggregate(**matched_inputs)
99+
self.assertTrue(validate_runtime_evidence_aggregate(build_runtime_evidence_aggregate(**mismatched_inputs))["ok"])
100+
26101
def test_report_contains_enrichment_fields(self):
27102
runtime = ExecutionRuntime(dry_run=True, run_id="test-001")
28103
report = build_execution_report(runtime)

0 commit comments

Comments
 (0)