Skip to content

Commit f1e7e20

Browse files
Pigbibicodex
andcommitted
fix(strategy): require resolvable review evidence refs
Co-Authored-By: Codex <noreply@openai.com>
1 parent 82e6558 commit f1e7e20

2 files changed

Lines changed: 9 additions & 4 deletions

File tree

schemas/strategy-review.v1.schema.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"name": { "type": "string" },
2323
"status": { "enum": ["pass", "fail", "insufficient_evidence", "not_applicable"] },
2424
"reason_codes": { "type": "array", "items": { "type": "string" } },
25-
"evidence_refs": { "type": "array", "items": { "type": "string" } }
25+
"evidence_refs": { "type": "array", "items": { "type": "string", "minLength": 1 } }
2626
},
2727
"additionalProperties": false
2828
},
@@ -77,7 +77,7 @@
7777
"evidence_sufficiency": { "enum": ["sufficient", "insufficient_evidence"] },
7878
"version_change": { "type": "string", "minLength": 1 },
7979
"system_recommendation": { "enum": ["approve_research", "approve_shadow", "approve_canary", "approve_live", "reject_rollback", "insufficient_evidence"] },
80-
"technical_evidence_refs": { "type": "array", "items": { "type": "string" } },
80+
"technical_evidence_refs": { "type": "array", "items": { "type": "string", "minLength": 1 } },
8181
"automation_boundary": {
8282
"type": "object",
8383
"required": ["research_auto_after_hard_gates", "shadow_auto_after_hard_gates", "canary_mode", "canary_limits", "auto_scale_allowed", "normal_live_requires_human", "funding_leverage_risk_override_requires_human", "hard_risk_auto_pause_rollback"],

scripts/validate_strategy_review.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def check_keys(value: Any, allowed: set[str], label: str) -> None:
4949
for field in ("reason_codes", "evidence_refs"):
5050
if not isinstance(gate.get(field), list):
5151
issues.append(f"{gate.get('id', '<unknown>')}.{field} must be an array")
52-
elif any(not isinstance(item, str) for item in gate[field]):
52+
elif any(not isinstance(item, str) or not item.strip() for item in gate[field]):
5353
issues.append(f"{gate.get('id', '<unknown>')}.{field} must contain strings")
5454

5555
score = payload.get("score")
@@ -131,7 +131,7 @@ def check_keys(value: Any, allowed: set[str], label: str) -> None:
131131
recommendation = packet.get("system_recommendation")
132132
if not isinstance(recommendation, str) or recommendation not in recommendations:
133133
issues.append("decision_packet.system_recommendation is invalid")
134-
if not isinstance(packet.get("technical_evidence_refs"), list) or any(not isinstance(item, str) for item in packet.get("technical_evidence_refs", [])):
134+
if not isinstance(packet.get("technical_evidence_refs"), list) or any(not isinstance(item, str) or not item.strip() for item in packet.get("technical_evidence_refs", [])):
135135
issues.append("decision_packet.technical_evidence_refs must be a string array")
136136
boundary = packet.get("automation_boundary")
137137
if not isinstance(boundary, dict):
@@ -233,6 +233,11 @@ def check_keys(value: Any, allowed: set[str], label: str) -> None:
233233
issues.append("failed or insufficient hard gates require blocking_reason_codes")
234234
if payload.get("promotion_allowed") is not False:
235235
issues.append("promotion_allowed must be false in v1 review output")
236+
if isinstance(evidence, dict) and isinstance(packet, dict) and (
237+
payload.get("decision") == "pass"
238+
or packet.get("system_recommendation") in {"approve_research", "approve_shadow", "approve_canary", "approve_live"}
239+
) and str(evidence.get("data_source", "")).strip().lower() in SENTINELS:
240+
issues.append("approved review cannot use a provenance sentinel as evidence.data_source")
236241
if payload.get("decision") == "pass" and failed:
237242
issues.append("decision cannot be pass when a hard gate failed or lacks evidence")
238243
if payload.get("decision") == "pass" and blocking:

0 commit comments

Comments
 (0)