From e8d8e74aabd63a03d92b0b9e31a05df78bbdb2d8 Mon Sep 17 00:00:00 2001 From: Michael Heller <21163552+mdheller@users.noreply.github.com> Date: Thu, 11 Jun 2026 20:43:15 -0400 Subject: [PATCH] feat(civic-stack): emit OQL/OAC runtime evidence capsule for Seven-Model civic architecture (#154) - CivicStackRunCapsule schema: run_id, actor_ref, oql_plan_id, artifact_manifest_id, policy_decision_id, tool_grants, action_dispatch_records, oql_plan_acceptance, oac_compiler_invocation, subagent_delegations, attestation_events, rationalgrl_trace, hellgraph_evidence_refs, delivery_excellence_signal_ref, provenance_refs, timestamps - RationalGRL trace: goals_addressed (goal/softgoal), tasks_executed, dependencies_blocked with defeater_reason on blocked tasks and denied goals - OQL plan acceptance with OQL-to-agent-task mappings - OAC compiler invocation with artifact emission refs - Policy gates: deny outcome enforces empty tool_grants + all dispatches blocked; blocked dispatches require defeater_reason; oac failure forbids artifact_emission_refs - 2 valid fixtures (allow + deny/policy-blocked) + 3 reject fixtures - validate-civic-stack-runtime-evidence wired into Makefile aggregate validate target - Upstream anchors: ontogenesis#80, #81, policy-fabric#72, sociosphere#323, delivery-excellence#28 --- Makefile | 8 +- .../civic-stack-run-capsule.schema.v0.1.json | 270 ++++++++++++++++++ ...ck-run-capsule.missing-hellgraph-refs.json | 20 ++ ...k-run-capsule.missing-provenance-refs.json | 21 ++ ...ct.civic-stack-run-capsule.wrong-kind.json | 21 ++ .../valid.civic-stack-run-capsule.json | 160 +++++++++++ ...ivic-stack-run-capsule.policy-blocked.json | 69 +++++ .../validate_civic_stack_runtime_evidence.py | 123 ++++++++ 8 files changed, 690 insertions(+), 2 deletions(-) create mode 100644 schemas/civic-stack-run-capsule.schema.v0.1.json create mode 100644 tests/fixtures/civic-stack/reject.civic-stack-run-capsule.missing-hellgraph-refs.json create mode 100644 tests/fixtures/civic-stack/reject.civic-stack-run-capsule.missing-provenance-refs.json create mode 100644 tests/fixtures/civic-stack/reject.civic-stack-run-capsule.wrong-kind.json create mode 100644 tests/fixtures/civic-stack/valid.civic-stack-run-capsule.json create mode 100644 tests/fixtures/civic-stack/valid.civic-stack-run-capsule.policy-blocked.json create mode 100644 tools/validate_civic_stack_runtime_evidence.py diff --git a/Makefile b/Makefile index 8856a65..bcd6b2f 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ -.PHONY: validate test validate-agent-cycle-health validate-authority-dependency-evidence validate-prometheus-sr validate-reasoning-failure-traces validate-governance-context validate-lattice-data-governai-execution-refs validate-lattice-runtime-profile-refs validate-network-native-assistant-evidence validate-guardrail-evidence-artifacts validate-stop-gate-evaluator validate-guarded-workcell-artifact validate-guarded-workcell-executor validate-guarded-invocation-artifact validate-guarded-invocation validate-agentic-pr-work-order validate-semantic-enterprise-agent-boundary validate-ops-history-contracts validate-action-contracts validate-agent-operation-contract validate-superconscious-reasoning-import validate-agent-harness-runtime-contracts validate-bounded-action-loop agentplane-evidence-receipt-composition-tier2-binding-ci lawful-learning-phase9-contract-ci validate-evidence-receipt-binding validate-semantic-activation-receipt validate-governed-run-contract validate-preflight-receipt validate-attempt-admission-receipt validate-verification-execution-receipt validate-synthetic-verification-receipt validate-governed-runner-v0-2-contract-chain validate-budget-settlement-receipt validate-rollback-receipts validate-run-dossier validate-governed-runner-readonly validate-workroom-context-evidence validate-wallguard-collaboration-admission validate-prophet-mesh-agentplane-adapter +.PHONY: validate test validate-agent-cycle-health validate-authority-dependency-evidence validate-prometheus-sr validate-reasoning-failure-traces validate-governance-context validate-lattice-data-governai-execution-refs validate-lattice-runtime-profile-refs validate-network-native-assistant-evidence validate-guardrail-evidence-artifacts validate-stop-gate-evaluator validate-guarded-workcell-artifact validate-guarded-workcell-executor validate-guarded-invocation-artifact validate-guarded-invocation validate-agentic-pr-work-order validate-semantic-enterprise-agent-boundary validate-ops-history-contracts validate-action-contracts validate-agent-operation-contract validate-superconscious-reasoning-import validate-agent-harness-runtime-contracts validate-bounded-action-loop agentplane-evidence-receipt-composition-tier2-binding-ci lawful-learning-phase9-contract-ci validate-evidence-receipt-binding validate-semantic-activation-receipt validate-governed-run-contract validate-preflight-receipt validate-attempt-admission-receipt validate-verification-execution-receipt validate-synthetic-verification-receipt validate-governed-runner-v0-2-contract-chain validate-budget-settlement-receipt validate-rollback-receipts validate-run-dossier validate-governed-runner-readonly validate-workroom-context-evidence validate-wallguard-collaboration-admission validate-prophet-mesh-agentplane-adapter validate-civic-stack-runtime-evidence -validate: validate-agent-cycle-health validate-authority-dependency-evidence validate-prometheus-sr validate-reasoning-failure-traces validate-governance-context validate-lattice-data-governai-execution-refs validate-lattice-runtime-profile-refs validate-network-native-assistant-evidence validate-guardrail-evidence-artifacts validate-stop-gate-evaluator validate-guarded-workcell-artifact validate-guarded-workcell-executor validate-guarded-invocation-artifact validate-guarded-invocation validate-agentic-pr-work-order validate-semantic-enterprise-agent-boundary validate-ops-history-contracts validate-action-contracts validate-agent-operation-contract validate-superconscious-reasoning-import validate-agent-harness-runtime-contracts validate-bounded-action-loop agentplane-evidence-receipt-composition-tier2-binding-ci lawful-learning-phase9-contract-ci validate-evidence-receipt-binding validate-semantic-activation-receipt validate-governed-run-contract validate-preflight-receipt validate-attempt-admission-receipt validate-verification-execution-receipt validate-synthetic-verification-receipt validate-governed-runner-v0-2-contract-chain validate-budget-settlement-receipt validate-rollback-receipts validate-run-dossier validate-governed-runner-readonly validate-workroom-context-evidence validate-wallguard-collaboration-admission validate-prophet-mesh-agentplane-adapter +validate: validate-agent-cycle-health validate-authority-dependency-evidence validate-prometheus-sr validate-reasoning-failure-traces validate-governance-context validate-lattice-data-governai-execution-refs validate-lattice-runtime-profile-refs validate-network-native-assistant-evidence validate-guardrail-evidence-artifacts validate-stop-gate-evaluator validate-guarded-workcell-artifact validate-guarded-workcell-executor validate-guarded-invocation-artifact validate-guarded-invocation validate-agentic-pr-work-order validate-semantic-enterprise-agent-boundary validate-ops-history-contracts validate-action-contracts validate-agent-operation-contract validate-superconscious-reasoning-import validate-agent-harness-runtime-contracts validate-bounded-action-loop agentplane-evidence-receipt-composition-tier2-binding-ci lawful-learning-phase9-contract-ci validate-evidence-receipt-binding validate-semantic-activation-receipt validate-governed-run-contract validate-preflight-receipt validate-attempt-admission-receipt validate-verification-execution-receipt validate-synthetic-verification-receipt validate-governed-runner-v0-2-contract-chain validate-budget-settlement-receipt validate-rollback-receipts validate-run-dossier validate-governed-runner-readonly validate-workroom-context-evidence validate-wallguard-collaboration-admission validate-prophet-mesh-agentplane-adapter validate-civic-stack-runtime-evidence python3 tools/validate_execution_timing.py validate-governance-context: @@ -248,6 +248,10 @@ validate-prophet-mesh-agentplane-adapter: python3 -m json.tool contracts/prophet-mesh/prophet-mesh-agentplane-adapter.v0.1.json >/dev/null python3 tools/validate_prophet_mesh_agentplane_adapter.py +validate-civic-stack-runtime-evidence: + python3 -m json.tool schemas/civic-stack-run-capsule.schema.v0.1.json >/dev/null + python3 tools/validate_civic_stack_runtime_evidence.py + validate-agent-cycle-health: python3 tools/validate_agent_cycle_health.py diff --git a/schemas/civic-stack-run-capsule.schema.v0.1.json b/schemas/civic-stack-run-capsule.schema.v0.1.json new file mode 100644 index 0000000..00dffbc --- /dev/null +++ b/schemas/civic-stack-run-capsule.schema.v0.1.json @@ -0,0 +1,270 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://socioprophet.io/schemas/agentplane/civic-stack-run-capsule/v0.1", + "title": "CivicStackRunCapsule", + "description": "AgentPlane runtime evidence capsule for Seven-Model Civic Operating Architecture execution. Emits OQL plan acceptance, OAC compiler invocation, tool grants, action dispatch, and RationalGRL trace links. AgentPlane emits governed runtime evidence; it does not own the ontology, policy evaluation, or Delivery Excellence scoring.", + "type": "object", + "required": [ + "kind", + "run_id", + "actor_ref", + "oql_plan_id", + "artifact_manifest_id", + "policy_decision_id", + "timestamps", + "provenance_refs", + "rationalgrl_trace", + "hellgraph_evidence_refs", + "issued_at" + ], + "additionalProperties": false, + "properties": { + "kind": { "type": "string", "const": "CivicStackRunCapsule" }, + "run_id": { "type": "string", "minLength": 1 }, + "actor_ref": { "type": "string", "description": "Agent or human actor reference" }, + "post_authority_ref": { + "type": "string", + "description": "Post or authority binding reference, if applicable" + }, + "oql_plan_id": { "type": "string", "minLength": 1 }, + "artifact_manifest_id": { "type": "string", "minLength": 1 }, + "service_id": { "type": "string" }, + "policy_decision_id": { "type": "string", "minLength": 1 }, + "policy_decision_outcome": { + "type": "string", + "enum": ["allow", "allow_with_constraints", "deny", "escalate"] + }, + "dataset_ids": { + "type": "array", + "items": { "type": "string" } + }, + "resource_ids": { + "type": "array", + "items": { "type": "string" } + }, + "tool_grants": { + "type": "array", + "items": { "$ref": "#/$defs/ToolGrant" } + }, + "action_dispatch_records": { + "type": "array", + "items": { "$ref": "#/$defs/ActionDispatchRecord" } + }, + "oql_plan_acceptance": { "$ref": "#/$defs/OQLPlanAcceptance" }, + "oac_compiler_invocation": { "$ref": "#/$defs/OACCompilerInvocation" }, + "subagent_delegations": { + "type": "array", + "items": { "$ref": "#/$defs/SubagentDelegation" } + }, + "attestation_events": { + "type": "array", + "items": { "$ref": "#/$defs/AttestationEvent" } + }, + "timestamps": { + "type": "object", + "required": ["started_at", "completed_at"], + "additionalProperties": false, + "properties": { + "started_at": { "type": "string", "format": "date-time" }, + "completed_at": { "type": "string", "format": "date-time" } + } + }, + "provenance_refs": { + "type": "array", + "items": { "type": "string" }, + "minItems": 1 + }, + "rationalgrl_trace": { "$ref": "#/$defs/RationalGRLTrace" }, + "hellgraph_evidence_refs": { + "type": "array", + "items": { "type": "string" }, + "minItems": 1, + "description": "Evidence refs emitted to HellGraph/Prophet Core" + }, + "delivery_excellence_signal_ref": { + "type": "string", + "description": "Score signal ref emitted to Delivery Excellence" + }, + "upstream_anchors": { + "type": "array", + "items": { "type": "string" } + }, + "issued_at": { "type": "string", "format": "date-time" } + }, + "$defs": { + "ToolGrant": { + "type": "object", + "required": ["grant_id", "tool_id", "granted_to", "policy_decision_ref"], + "additionalProperties": false, + "properties": { + "grant_id": { "type": "string", "minLength": 1 }, + "tool_id": { "type": "string", "minLength": 1 }, + "granted_to": { "type": "string", "minLength": 1 }, + "policy_decision_ref": { "type": "string", "minLength": 1 }, + "cgrm_decision_ref": { "type": "string" }, + "scope_constraints": { + "type": "array", + "items": { "type": "string" } + } + } + }, + "ActionDispatchRecord": { + "type": "object", + "required": ["dispatch_id", "action_type", "policy_decision_ref", "dispatch_status"], + "additionalProperties": false, + "properties": { + "dispatch_id": { "type": "string", "minLength": 1 }, + "action_type": { "type": "string", "minLength": 1 }, + "policy_decision_ref": { "type": "string", "minLength": 1 }, + "dispatch_status": { + "type": "string", + "enum": ["dispatched", "blocked", "deferred", "completed"] + }, + "rationalgrl_task_ref": { "type": "string" }, + "defeater_reason": { + "type": "string", + "description": "RationalGRL defeater description when dispatch_status=blocked" + } + } + }, + "OQLPlanAcceptance": { + "type": "object", + "required": ["acceptance_id", "oql_plan_id", "acceptance_status"], + "additionalProperties": false, + "properties": { + "acceptance_id": { "type": "string", "minLength": 1 }, + "oql_plan_id": { "type": "string", "minLength": 1 }, + "acceptance_status": { + "type": "string", + "enum": ["accepted", "rejected", "partial", "pending_review"] + }, + "interpretation_ref": { "type": "string" }, + "oql_task_mappings": { + "type": "array", + "items": { "$ref": "#/$defs/OQLTaskMapping" } + } + } + }, + "OQLTaskMapping": { + "type": "object", + "required": ["task_id", "oql_task_ref", "agent_action_ref"], + "additionalProperties": false, + "properties": { + "task_id": { "type": "string" }, + "oql_task_ref": { "type": "string" }, + "agent_action_ref": { "type": "string" } + } + }, + "OACCompilerInvocation": { + "type": "object", + "required": ["invocation_id", "compiler_id", "artifact_manifest_id", "invocation_status"], + "additionalProperties": false, + "properties": { + "invocation_id": { "type": "string", "minLength": 1 }, + "compiler_id": { "type": "string", "minLength": 1 }, + "artifact_manifest_id": { "type": "string", "minLength": 1 }, + "invocation_status": { + "type": "string", + "enum": ["success", "failure", "partial"] + }, + "artifact_emission_refs": { + "type": "array", + "items": { "type": "string" } + } + } + }, + "SubagentDelegation": { + "type": "object", + "required": ["delegation_id", "delegated_to", "task_ref", "policy_decision_ref"], + "additionalProperties": false, + "properties": { + "delegation_id": { "type": "string", "minLength": 1 }, + "delegated_to": { "type": "string", "minLength": 1 }, + "task_ref": { "type": "string", "minLength": 1 }, + "policy_decision_ref": { "type": "string", "minLength": 1 } + } + }, + "AttestationEvent": { + "type": "object", + "required": ["attestation_id", "attestation_type", "attested_by"], + "additionalProperties": false, + "properties": { + "attestation_id": { "type": "string", "minLength": 1 }, + "attestation_type": { + "type": "string", + "enum": ["policy_compliance", "provenance_chain", "dataset_access", "tool_grant", "oql_plan_acceptance"] + }, + "attested_by": { "type": "string", "minLength": 1 }, + "ref": { "type": "string" } + } + }, + "RationalGRLTrace": { + "type": "object", + "required": ["trace_id", "goals_addressed", "tasks_executed"], + "additionalProperties": false, + "properties": { + "trace_id": { "type": "string", "minLength": 1 }, + "goals_addressed": { + "type": "array", + "items": { + "type": "object", + "required": ["goal_ref", "goal_type", "satisfaction_status"], + "additionalProperties": false, + "properties": { + "goal_ref": { "type": "string" }, + "goal_type": { + "type": "string", + "enum": ["goal", "softgoal"] + }, + "satisfaction_status": { + "type": "string", + "enum": ["satisfied", "partially_satisfied", "denied", "unknown"] + }, + "contribution_refs": { + "type": "array", + "items": { "type": "string" } + } + } + } + }, + "tasks_executed": { + "type": "array", + "items": { + "type": "object", + "required": ["task_ref", "execution_status"], + "additionalProperties": false, + "properties": { + "task_ref": { "type": "string" }, + "execution_status": { + "type": "string", + "enum": ["completed", "blocked", "delegated", "deferred"] + }, + "resource_used_refs": { + "type": "array", + "items": { "type": "string" } + }, + "dependency_satisfied_refs": { + "type": "array", + "items": { "type": "string" } + }, + "defeater_reason": { "type": "string" } + } + } + }, + "dependencies_blocked": { + "type": "array", + "items": { + "type": "object", + "required": ["dependency_ref", "blocking_policy_ref"], + "additionalProperties": false, + "properties": { + "dependency_ref": { "type": "string" }, + "blocking_policy_ref": { "type": "string" }, + "defeater_reason": { "type": "string" } + } + } + } + } + } + } +} diff --git a/tests/fixtures/civic-stack/reject.civic-stack-run-capsule.missing-hellgraph-refs.json b/tests/fixtures/civic-stack/reject.civic-stack-run-capsule.missing-hellgraph-refs.json new file mode 100644 index 0000000..ca7893c --- /dev/null +++ b/tests/fixtures/civic-stack/reject.civic-stack-run-capsule.missing-hellgraph-refs.json @@ -0,0 +1,20 @@ +{ + "_reject_reason": "hellgraph_evidence_refs is required but absent", + "kind": "CivicStackRunCapsule", + "run_id": "civic-stack-run:reject-no-hellgraph", + "actor_ref": "agent://agentplane/civic-stack-agent/v1", + "oql_plan_id": "oql://plan/service-delivery-v1/step-1", + "artifact_manifest_id": "manifest://civic-stack/artifacts/reject-no-hellgraph", + "policy_decision_id": "policy-fabric://decision/pd-reject-no-hellgraph", + "timestamps": { + "started_at": "2024-01-15T10:00:00Z", + "completed_at": "2024-01-15T10:01:00Z" + }, + "provenance_refs": ["provenance://sociosphere/civic-stack-run/reject-no-hellgraph"], + "rationalgrl_trace": { + "trace_id": "rationalgrl-trace:reject-no-hellgraph", + "goals_addressed": [], + "tasks_executed": [] + }, + "issued_at": "2024-01-15T10:01:01Z" +} diff --git a/tests/fixtures/civic-stack/reject.civic-stack-run-capsule.missing-provenance-refs.json b/tests/fixtures/civic-stack/reject.civic-stack-run-capsule.missing-provenance-refs.json new file mode 100644 index 0000000..3808fbb --- /dev/null +++ b/tests/fixtures/civic-stack/reject.civic-stack-run-capsule.missing-provenance-refs.json @@ -0,0 +1,21 @@ +{ + "_reject_reason": "provenance_refs is required (minItems: 1) but empty", + "kind": "CivicStackRunCapsule", + "run_id": "civic-stack-run:reject-empty-provenance", + "actor_ref": "agent://agentplane/civic-stack-agent/v1", + "oql_plan_id": "oql://plan/service-delivery-v1/step-1", + "artifact_manifest_id": "manifest://civic-stack/artifacts/reject-empty-provenance", + "policy_decision_id": "policy-fabric://decision/pd-reject-empty-provenance", + "timestamps": { + "started_at": "2024-01-15T10:00:00Z", + "completed_at": "2024-01-15T10:01:00Z" + }, + "provenance_refs": [], + "rationalgrl_trace": { + "trace_id": "rationalgrl-trace:reject-empty-provenance", + "goals_addressed": [], + "tasks_executed": [] + }, + "hellgraph_evidence_refs": ["evidence://hellgraph/reject-empty-provenance/run-capsule"], + "issued_at": "2024-01-15T10:01:01Z" +} diff --git a/tests/fixtures/civic-stack/reject.civic-stack-run-capsule.wrong-kind.json b/tests/fixtures/civic-stack/reject.civic-stack-run-capsule.wrong-kind.json new file mode 100644 index 0000000..f4a267d --- /dev/null +++ b/tests/fixtures/civic-stack/reject.civic-stack-run-capsule.wrong-kind.json @@ -0,0 +1,21 @@ +{ + "_reject_reason": "kind must be CivicStackRunCapsule (const violation)", + "kind": "CivicStackRunCapsuleV2", + "run_id": "civic-stack-run:reject-wrong-kind", + "actor_ref": "agent://agentplane/civic-stack-agent/v1", + "oql_plan_id": "oql://plan/service-delivery-v1/step-1", + "artifact_manifest_id": "manifest://civic-stack/artifacts/reject-wrong-kind", + "policy_decision_id": "policy-fabric://decision/pd-reject-wrong-kind", + "timestamps": { + "started_at": "2024-01-15T10:00:00Z", + "completed_at": "2024-01-15T10:01:00Z" + }, + "provenance_refs": ["provenance://sociosphere/civic-stack-run/reject-wrong-kind"], + "rationalgrl_trace": { + "trace_id": "rationalgrl-trace:reject-wrong-kind", + "goals_addressed": [], + "tasks_executed": [] + }, + "hellgraph_evidence_refs": ["evidence://hellgraph/reject-wrong-kind/run-capsule"], + "issued_at": "2024-01-15T10:01:01Z" +} diff --git a/tests/fixtures/civic-stack/valid.civic-stack-run-capsule.json b/tests/fixtures/civic-stack/valid.civic-stack-run-capsule.json new file mode 100644 index 0000000..f87d52d --- /dev/null +++ b/tests/fixtures/civic-stack/valid.civic-stack-run-capsule.json @@ -0,0 +1,160 @@ +{ + "kind": "CivicStackRunCapsule", + "run_id": "civic-stack-run:a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "actor_ref": "agent://agentplane/civic-stack-agent/v1", + "post_authority_ref": "post://authority/civic-oversight-post/v1", + "oql_plan_id": "oql://plan/service-delivery-v1/step-3", + "artifact_manifest_id": "manifest://civic-stack/artifacts/2024-01-15-run-a1b2", + "service_id": "civic-service:social-case-management", + "policy_decision_id": "policy-fabric://decision/pd-civic-run-a1b2", + "policy_decision_outcome": "allow", + "dataset_ids": [ + "dataset://case-management/beneficiary-records/batch-001", + "dataset://case-management/service-catalog/v4" + ], + "resource_ids": [ + "resource://compute/civic-agent-executor/slot-7", + "resource://storage/case-output/run-a1b2" + ], + "tool_grants": [ + { + "grant_id": "grant:civic-run-a1b2-tool-1", + "tool_id": "tool://case-lookup/v2", + "granted_to": "agent://agentplane/civic-stack-agent/v1", + "policy_decision_ref": "policy-fabric://decision/pd-civic-run-a1b2", + "cgrm_decision_ref": "cgrm://decision/cgrm-civic-run-a1b2-read", + "scope_constraints": ["read_only", "beneficiary_scope_limited"] + }, + { + "grant_id": "grant:civic-run-a1b2-tool-2", + "tool_id": "tool://service-eligibility-check/v3", + "granted_to": "agent://agentplane/civic-stack-agent/v1", + "policy_decision_ref": "policy-fabric://decision/pd-civic-run-a1b2", + "cgrm_decision_ref": "cgrm://decision/cgrm-civic-run-a1b2-eligibility" + } + ], + "action_dispatch_records": [ + { + "dispatch_id": "dispatch:civic-run-a1b2-action-1", + "action_type": "case.lookup", + "policy_decision_ref": "policy-fabric://decision/pd-civic-run-a1b2", + "dispatch_status": "completed", + "rationalgrl_task_ref": "task://rationalgrl/case-lookup-task/v1" + }, + { + "dispatch_id": "dispatch:civic-run-a1b2-action-2", + "action_type": "eligibility.check", + "policy_decision_ref": "policy-fabric://decision/pd-civic-run-a1b2", + "dispatch_status": "completed", + "rationalgrl_task_ref": "task://rationalgrl/eligibility-check-task/v1" + } + ], + "oql_plan_acceptance": { + "acceptance_id": "oql-acceptance:civic-run-a1b2", + "oql_plan_id": "oql://plan/service-delivery-v1/step-3", + "acceptance_status": "accepted", + "interpretation_ref": "oql://interpretation/service-delivery-v1/step-3-interp", + "oql_task_mappings": [ + { + "task_id": "oql-task-map:1", + "oql_task_ref": "oql://task/case-lookup", + "agent_action_ref": "dispatch:civic-run-a1b2-action-1" + }, + { + "task_id": "oql-task-map:2", + "oql_task_ref": "oql://task/eligibility-check", + "agent_action_ref": "dispatch:civic-run-a1b2-action-2" + } + ] + }, + "oac_compiler_invocation": { + "invocation_id": "oac-invocation:civic-run-a1b2", + "compiler_id": "oac://compiler/civic-stack/v1", + "artifact_manifest_id": "manifest://civic-stack/artifacts/2024-01-15-run-a1b2", + "invocation_status": "success", + "artifact_emission_refs": [ + "artifact://case-eligibility-result/run-a1b2/output-001", + "artifact://service-delivery-report/run-a1b2/output-002" + ] + }, + "subagent_delegations": [ + { + "delegation_id": "delegation:civic-run-a1b2-sub-1", + "delegated_to": "agent://agentplane/case-lookup-subagent/v1", + "task_ref": "task://rationalgrl/case-lookup-task/v1", + "policy_decision_ref": "policy-fabric://decision/pd-civic-run-a1b2-sub" + } + ], + "attestation_events": [ + { + "attestation_id": "attestation:civic-run-a1b2-policy", + "attestation_type": "policy_compliance", + "attested_by": "agent://agentplane/civic-stack-agent/v1", + "ref": "policy-fabric://decision/pd-civic-run-a1b2" + }, + { + "attestation_id": "attestation:civic-run-a1b2-oql", + "attestation_type": "oql_plan_acceptance", + "attested_by": "agent://agentplane/civic-stack-agent/v1", + "ref": "oql-acceptance:civic-run-a1b2" + } + ], + "timestamps": { + "started_at": "2024-01-15T10:00:00Z", + "completed_at": "2024-01-15T10:04:33Z" + }, + "provenance_refs": [ + "provenance://sociosphere/civic-stack-run/a1b2c3d4", + "provenance://ontogenesis/civic-run-lineage/a1b2" + ], + "rationalgrl_trace": { + "trace_id": "rationalgrl-trace:civic-run-a1b2", + "goals_addressed": [ + { + "goal_ref": "goal://rationalgrl/civic-service-delivery/primary", + "goal_type": "goal", + "satisfaction_status": "satisfied", + "contribution_refs": [ + "dispatch:civic-run-a1b2-action-1", + "dispatch:civic-run-a1b2-action-2" + ] + }, + { + "goal_ref": "goal://rationalgrl/civic-service-delivery/quality", + "goal_type": "softgoal", + "satisfaction_status": "partially_satisfied", + "contribution_refs": [ + "dispatch:civic-run-a1b2-action-1" + ] + } + ], + "tasks_executed": [ + { + "task_ref": "task://rationalgrl/case-lookup-task/v1", + "execution_status": "completed", + "resource_used_refs": ["resource://compute/civic-agent-executor/slot-7"], + "dependency_satisfied_refs": [] + }, + { + "task_ref": "task://rationalgrl/eligibility-check-task/v1", + "execution_status": "completed", + "resource_used_refs": ["resource://compute/civic-agent-executor/slot-7"], + "dependency_satisfied_refs": ["task://rationalgrl/case-lookup-task/v1"] + } + ], + "dependencies_blocked": [] + }, + "hellgraph_evidence_refs": [ + "evidence://hellgraph/civic-stack-run-a1b2/run-capsule", + "evidence://prophet-core/civic-stack-run-a1b2/emission" + ], + "delivery_excellence_signal_ref": "de://signal/civic-stack-run-a1b2/score", + "upstream_anchors": [ + "ontogenesis#80", + "ontogenesis#81", + "policy-fabric#72", + "sociosphere#323", + "delivery-excellence#28" + ], + "issued_at": "2024-01-15T10:04:35Z" +} diff --git a/tests/fixtures/civic-stack/valid.civic-stack-run-capsule.policy-blocked.json b/tests/fixtures/civic-stack/valid.civic-stack-run-capsule.policy-blocked.json new file mode 100644 index 0000000..727ec9b --- /dev/null +++ b/tests/fixtures/civic-stack/valid.civic-stack-run-capsule.policy-blocked.json @@ -0,0 +1,69 @@ +{ + "kind": "CivicStackRunCapsule", + "run_id": "civic-stack-run:blocked-b2c3d4e5-f6a7-8901-bcde-f12345678901", + "actor_ref": "agent://agentplane/civic-stack-agent/v1", + "oql_plan_id": "oql://plan/restricted-data-access-v1/step-1", + "artifact_manifest_id": "manifest://civic-stack/artifacts/2024-01-15-run-b2c3-blocked", + "policy_decision_id": "policy-fabric://decision/pd-civic-run-b2c3-deny", + "policy_decision_outcome": "deny", + "dataset_ids": ["dataset://restricted/pii-records/batch-009"], + "resource_ids": [], + "tool_grants": [], + "action_dispatch_records": [ + { + "dispatch_id": "dispatch:civic-run-b2c3-blocked-action-1", + "action_type": "pii.access", + "policy_decision_ref": "policy-fabric://decision/pd-civic-run-b2c3-deny", + "dispatch_status": "blocked", + "rationalgrl_task_ref": "task://rationalgrl/pii-access-task/v1", + "defeater_reason": "PolicyFabric denied PII access: insufficient authorization scope for beneficiary PII" + } + ], + "oql_plan_acceptance": { + "acceptance_id": "oql-acceptance:civic-run-b2c3-blocked", + "oql_plan_id": "oql://plan/restricted-data-access-v1/step-1", + "acceptance_status": "rejected", + "interpretation_ref": "oql://interpretation/restricted-data-access-v1/step-1-interp" + }, + "timestamps": { + "started_at": "2024-01-15T11:00:00Z", + "completed_at": "2024-01-15T11:00:04Z" + }, + "provenance_refs": [ + "provenance://sociosphere/civic-stack-run/b2c3-blocked" + ], + "rationalgrl_trace": { + "trace_id": "rationalgrl-trace:civic-run-b2c3-blocked", + "goals_addressed": [ + { + "goal_ref": "goal://rationalgrl/restricted-data-access/primary", + "goal_type": "goal", + "satisfaction_status": "denied", + "contribution_refs": [] + } + ], + "tasks_executed": [ + { + "task_ref": "task://rationalgrl/pii-access-task/v1", + "execution_status": "blocked", + "defeater_reason": "RationalGRL defeater: PolicyFabric deny decision prevents task execution" + } + ], + "dependencies_blocked": [ + { + "dependency_ref": "task://rationalgrl/pii-access-task/v1", + "blocking_policy_ref": "policy-fabric://decision/pd-civic-run-b2c3-deny", + "defeater_reason": "PII access blocked: authorization scope insufficient" + } + ] + }, + "hellgraph_evidence_refs": [ + "evidence://hellgraph/civic-stack-run-b2c3/run-capsule-blocked", + "evidence://prophet-core/civic-stack-run-b2c3/policy-deny-evidence" + ], + "upstream_anchors": [ + "ontogenesis#80", + "policy-fabric#72" + ], + "issued_at": "2024-01-15T11:00:05Z" +} diff --git a/tools/validate_civic_stack_runtime_evidence.py b/tools/validate_civic_stack_runtime_evidence.py new file mode 100644 index 0000000..f4c4850 --- /dev/null +++ b/tools/validate_civic_stack_runtime_evidence.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import json +import sys +from pathlib import Path +from typing import Any + +try: + import jsonschema +except ImportError as exc: + raise SystemExit("jsonschema is required: python3 -m pip install jsonschema") from exc + +ROOT = Path(__file__).resolve().parents[1] +SCHEMA = ROOT / "schemas" / "civic-stack-run-capsule.schema.v0.1.json" +FIXTURES = ROOT / "tests" / "fixtures" / "civic-stack" + +ALLOWED_POLICY_OUTCOMES = {"allow", "allow_with_constraints", "deny", "escalate"} +ALLOWED_DISPATCH_STATUSES = {"dispatched", "blocked", "deferred", "completed"} + + +def load_json(path: Path) -> dict[str, Any]: + data = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(data, dict): + raise ValueError("root must be object") + return data + + +def check_policy_gates(data: dict[str, Any]) -> list[str]: + problems: list[str] = [] + + outcome = data.get("policy_decision_outcome") + if outcome and outcome not in ALLOWED_POLICY_OUTCOMES: + problems.append(f"policy_decision_outcome invalid: {outcome}") + + # deny outcome: tool_grants must be empty and all dispatches must be blocked + if outcome == "deny": + grants = data.get("tool_grants", []) + if grants: + problems.append("deny policy_decision_outcome must have no tool_grants") + dispatches = data.get("action_dispatch_records", []) + non_blocked = [d for d in dispatches if d.get("dispatch_status") != "blocked"] + if non_blocked: + problems.append("deny policy_decision_outcome requires all dispatches blocked") + + # blocked dispatches must have defeater_reason + for dispatch in data.get("action_dispatch_records", []): + if dispatch.get("dispatch_status") == "blocked" and not dispatch.get("defeater_reason"): + problems.append(f"blocked dispatch {dispatch.get('dispatch_id')} requires defeater_reason") + + # rationalgrl_trace: blocked tasks must have defeater_reason + trace = data.get("rationalgrl_trace", {}) + for task in trace.get("tasks_executed", []): + if task.get("execution_status") == "blocked" and not task.get("defeater_reason"): + problems.append(f"blocked task {task.get('task_ref')} in rationalgrl_trace requires defeater_reason") + + # hellgraph_evidence_refs required and non-empty (belt-and-suspenders on schema minItems) + hg_refs = data.get("hellgraph_evidence_refs", []) + if not hg_refs: + problems.append("hellgraph_evidence_refs must not be empty") + + # provenance_refs required and non-empty + prov_refs = data.get("provenance_refs", []) + if not prov_refs: + problems.append("provenance_refs must not be empty") + + # oac_compiler_invocation: failure status should have no artifact_emission_refs + oac = data.get("oac_compiler_invocation") + if oac and oac.get("invocation_status") == "failure": + if oac.get("artifact_emission_refs"): + problems.append("oac_compiler_invocation failure must not have artifact_emission_refs") + + return problems + + +def validate_file(path: Path, schema: dict[str, Any]) -> list[str]: + try: + data = load_json(path) + except Exception as exc: + return [f"parse error: {exc}"] + try: + jsonschema.validate(data, schema) + except jsonschema.ValidationError as exc: + return [f"schema: {exc.message}"] + return check_policy_gates(data) + + +def main() -> int: + schema = load_json(SCHEMA) + failed = False + + valids = sorted(FIXTURES.glob("valid.*.json")) + if not valids: + raise SystemExit("missing valid civic-stack fixtures") + + for path in valids: + problems = validate_file(path, schema) + if problems: + print(f"FAIL (valid): {path.name}") + for p in problems: + print(f" - {p}") + failed = True + else: + print(f"ok: {path.name}") + + rejects = sorted(FIXTURES.glob("reject.*.json")) + if not rejects: + raise SystemExit("missing reject civic-stack fixtures") + + for path in rejects: + problems = validate_file(path, schema) + if not problems: + print(f"FAIL (reject should have failed): {path.name}") + failed = True + else: + print(f"ok (rejected as expected): {path.name}") + + print(("PASS" if not failed else "FAIL") + f": civic-stack runtime evidence — {len(valids)} valid, {len(rejects)} reject") + return 0 if not failed else 1 + + +if __name__ == "__main__": + raise SystemExit(main())