Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions tests/fixtures/bounded-action-loop/valid.blocked-intervention.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"schema_version": "0.1",
"kind": "bounded_action_loop",
"loop_id": "loop-blocked-intervention-001",
"action_proposal": {
"proposal_id": "proposal-blocked-intervention-001",
"action_type": "emit_audit_packet",
"risk_class": "high",
"evidence_refs": [
"evidence://agentplane/run/blocked-intervention-001/anomaly-signal"
],
"requested_result": "emit audit packet for high-risk anomalous action"
},
"policy_decision_ref": "SocioProphet/policy-fabric#85:pd-block-high-risk-001",
"runtime_trace": {
"trace_id": "trace-blocked-intervention-001",
"proposal_ref": "proposal-blocked-intervention-001",
"policy_decision_ref": "SocioProphet/policy-fabric#85:pd-block-high-risk-001",
"trace_status": "blocked",
"no_external_side_effects": true,
"audit_ref": "SocioProphet/model-governance-ledger#20:audit-block-001"
},
"outcome_record": {
"outcome_id": "outcome-blocked-intervention-001",
"proposal_ref": "proposal-blocked-intervention-001",
"policy_decision_ref": "SocioProphet/policy-fabric#85:pd-block-high-risk-001",
"result": "blocked",
"audit_ref": "SocioProphet/model-governance-ledger#20:audit-block-001"
}
}
40 changes: 40 additions & 0 deletions tests/fixtures/bounded-action-loop/valid.modified-action.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"schema_version": "0.1",
"kind": "bounded_action_loop",
"loop_id": "loop-modified-action-001",
"action_proposal": {
"proposal_id": "proposal-modified-action-001",
"action_type": "record_diagnostic_finding",
"risk_class": "moderate",
"evidence_refs": [
"evidence://agentplane/run/modified-action-001/diagnostic-signal",
"evidence://agentplane/run/modified-action-001/policy-constraint"
],
"requested_result": "record diagnostic finding with scope modification applied"
},
"policy_decision_ref": "SocioProphet/policy-fabric#85:pd-modify-scope-001",
"runtime_trace": {
"trace_id": "trace-modified-action-001",
"proposal_ref": "proposal-modified-action-001",
"policy_decision_ref": "SocioProphet/policy-fabric#85:pd-modify-scope-001",
"trace_status": "modified",
"no_external_side_effects": true,
"audit_ref": "SocioProphet/model-governance-ledger#20:audit-modified-001"
},
"outcome_record": {
"outcome_id": "outcome-modified-action-001",
"proposal_ref": "proposal-modified-action-001",
"policy_decision_ref": "SocioProphet/policy-fabric#85:pd-modify-scope-001",
"result": "modified",
"audit_ref": "SocioProphet/model-governance-ledger#20:audit-modified-001"
},
"trait_baseline": {
"baseline_id": "baseline-modified-observational-001",
"observational_only": true
},
"trait_drift_metric": {
"metric_id": "trait-drift-modified-001",
"observational_only": true,
"value": 0.15
}
}
15 changes: 13 additions & 2 deletions tools/check_bounded_action_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
ROOT = Path(__file__).resolve().parents[1]
SCHEMA = ROOT / "schemas" / "bounded-action-loop.v0.schema.json"
FIXTURES = ROOT / "tests" / "fixtures" / "bounded-action-loop"
VALID = FIXTURES / "valid.record-event-instance.json"

INTERVENTION_STATUSES = {"blocked", "modified", "escalated"}


def load_json(path: Path) -> dict[str, Any]:
Expand Down Expand Up @@ -41,6 +42,12 @@ def check_data(data: dict[str, Any]) -> None:
raise ValueError("proposal evidence refs required")
if proposal["risk_class"] != "low" and trace["trace_status"] == "recorded":
raise ValueError("recorded trace requires low risk in v0")
# Runtime trace must always be present and consistent with outcome
if trace["trace_status"] != outcome["result"]:
raise ValueError("trace_status and outcome result must match")
# Intervention outcomes (blocked/modified/escalated) must have audit_ref
if outcome["result"] in INTERVENTION_STATUSES and not outcome.get("audit_ref"):
raise ValueError(f"intervention outcome result={outcome['result']} requires audit_ref")


def validate_file(path: Path, schema: dict[str, Any]) -> None:
Expand All @@ -51,7 +58,11 @@ def validate_file(path: Path, schema: dict[str, Any]) -> None:

def main() -> int:
schema = load_json(SCHEMA)
validate_file(VALID, schema)
valids = sorted(FIXTURES.glob("valid.*.json"))
if not valids:
raise SystemExit("missing valid bounded-action-loop fixtures")
for path in valids:
validate_file(path, schema)
invalids = sorted(FIXTURES.glob("invalid.*.json"))
if not invalids:
raise SystemExit("missing invalid bounded-action-loop fixtures")
Expand Down
Loading