diff --git a/Makefile b/Makefile index 00e0b598..5c6e16b3 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ -.PHONY: validate validate-repo docs-check drift-check standards-check topology-check chronos-evidence-loop-readout-validate lattice-surfaces-check lattice-surface-ingestor-smoke lattice-studio-smoke validate-ops-fabric validate-search-academy-deploy validate-search-image-release validate-lampstand-lifecycle validate-zone-stack-audit policy-fabric-endpoint-client-smoke policy-fabric-guarded-workflow-smoke zone-router-publication-local-publish-smoke zone-router-publication-failure-evidence-smoke zone-router-publication-retry-state-smoke zone-router-publication-remote-broker-seam-smoke validate-workroom-update-contract validate-professional-intelligence-manifest validate-svf-agent-contract validate-environment-validate-change-v2 validate-channel-runtime-gates test-go test-python-apps test-tools smoke smoke-health smoke-eval-fabric smoke-evidence-receipts smoke-evidence-console validate-phase3 lampstand-smoke validate-phase4 lampstand-vertical-slice-smoke lampstand-zone-smoke zone-router-publication-smoke zone-router-publication-enqueue-smoke semantic-bridge-zone-validation-smoke validate-fogstack validate-storage-suite trustops-art-runner-smoke +.PHONY: validate validate-repo docs-check drift-check standards-check topology-check chronos-evidence-loop-readout-validate lattice-surfaces-check lattice-surface-ingestor-smoke lattice-studio-smoke validate-ops-fabric validate-search-academy-deploy validate-search-image-release validate-lampstand-lifecycle validate-zone-stack-audit policy-fabric-endpoint-client-smoke policy-fabric-guarded-workflow-smoke zone-router-publication-local-publish-smoke zone-router-publication-failure-evidence-smoke zone-router-publication-retry-state-smoke zone-router-publication-remote-broker-seam-smoke validate-workroom-update-contract validate-professional-intelligence-manifest validate-wallguard-professional-workroom validate-svf-agent-contract validate-environment-validate-change-v2 validate-channel-runtime-gates test-go test-python-apps test-tools smoke smoke-health smoke-eval-fabric smoke-evidence-receipts smoke-evidence-console validate-phase3 lampstand-smoke validate-phase4 lampstand-vertical-slice-smoke lampstand-zone-smoke zone-router-publication-smoke zone-router-publication-enqueue-smoke semantic-bridge-zone-validation-smoke validate-fogstack validate-storage-suite trustops-art-runner-smoke -validate: validate-repo drift-check standards-check topology-check chronos-evidence-loop-readout-validate lattice-surfaces-check lattice-surface-ingestor-smoke lattice-studio-smoke validate-ops-fabric validate-search-academy-deploy validate-search-image-release validate-lampstand-lifecycle validate-zone-stack-audit policy-fabric-endpoint-client-smoke policy-fabric-guarded-workflow-smoke zone-router-publication-local-publish-smoke zone-router-publication-failure-evidence-smoke zone-router-publication-retry-state-smoke zone-router-publication-remote-broker-seam-smoke validate-workroom-update-contract validate-professional-intelligence-manifest validate-svf-agent-contract validate-environment-validate-change-v2 validate-channel-runtime-gates test-go validate-phase4 test-python-apps test-tools validate-fogstack validate-storage-suite trustops-art-runner-smoke +validate: validate-repo drift-check standards-check topology-check chronos-evidence-loop-readout-validate lattice-surfaces-check lattice-surface-ingestor-smoke lattice-studio-smoke validate-ops-fabric validate-search-academy-deploy validate-search-image-release validate-lampstand-lifecycle validate-zone-stack-audit policy-fabric-endpoint-client-smoke policy-fabric-guarded-workflow-smoke zone-router-publication-local-publish-smoke zone-router-publication-failure-evidence-smoke zone-router-publication-retry-state-smoke zone-router-publication-remote-broker-seam-smoke validate-workroom-update-contract validate-professional-intelligence-manifest validate-wallguard-professional-workroom validate-svf-agent-contract validate-environment-validate-change-v2 validate-channel-runtime-gates test-go validate-phase4 test-python-apps test-tools validate-fogstack validate-storage-suite trustops-art-runner-smoke validate-repo: python3 tools/validate_repo.py @@ -93,6 +93,9 @@ validate-workroom-update-contract: validate-professional-intelligence-manifest: python3 tools/validate_professional_intelligence_manifest.py +validate-wallguard-professional-workroom: + python3 tools/validate_wallguard_professional_workroom.py + validate-svf-agent-contract: python3 tools/validate_svf_agent_contract.py diff --git a/contracts/wallguard/professional-workroom.example.json b/contracts/wallguard/professional-workroom.example.json new file mode 100644 index 00000000..10c692e1 --- /dev/null +++ b/contracts/wallguard/professional-workroom.example.json @@ -0,0 +1,50 @@ +{ + "schemaVersion": "prophet-platform.wallguard-professional-workroom.v0.1", + "recordType": "WallGuardProfessionalWorkroom", + "workroomRef": "workroom://client-a/matter-x", + "clientRef": "client://client-a", + "matterRef": "matter://client-a/matter-x", + "wallRef": "wall://client-a/matter-x", + "policyRefs": [ + "policy-fabric://wallguard.professional-workroom.v0" + ], + "participants": [ + { + "subjectRef": "user://operator-alpha", + "subjectType": "human", + "wallContextRef": "ack://wall/client-a/matter-x/operator-alpha" + }, + { + "subjectRef": "agent-registry://agent-alpha", + "subjectType": "agent", + "wallContextRef": "agent-wall-context://agent-alpha/client-a/matter-x" + } + ], + "resourceRefs": [ + "resource://client-a/matter-x/doc-001" + ], + "enforcementRefs": { + "agentContext": "SocioProphet/agent-registry#45", + "agentCollaboration": "SocioProphet/agentplane#252", + "guardrailBinding": "SocioProphet/guardrail-fabric#32", + "memoryGate": "SocioProphet/memory-mesh#33", + "retrievalFilter": "SocioProphet/sherlock-search#61", + "cleanRoomSynthesis": "SocioProphet/holmes#13" + }, + "requiredProductStates": [ + "wall_state_visible", + "policy_version_visible", + "participant_membership_visible", + "blocked_attempts_visible", + "receipt_refs_visible", + "clean_room_release_requestable" + ], + "receiptRefs": [ + "wallguard-receipt://workroom-create-client-a-matter-x-001" + ], + "runtimeBoundary": { + "policyAuthority": "SocioProphet/policy-fabric", + "productSurfaceOwner": "SocioProphet/prophet-platform", + "runtimeEnforcementImplemented": false + } +} diff --git a/tools/validate_wallguard_professional_workroom.py b/tools/validate_wallguard_professional_workroom.py new file mode 100644 index 00000000..1b6e2578 --- /dev/null +++ b/tools/validate_wallguard_professional_workroom.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 +"""Validate WallGuard Professional Workroom product contract fixture.""" + +from __future__ import annotations + +import json +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +FIXTURE = ROOT / "contracts" / "wallguard" / "professional-workroom.example.json" + +REQUIRED_TOP = { + "schemaVersion", + "recordType", + "workroomRef", + "clientRef", + "matterRef", + "wallRef", + "policyRefs", + "participants", + "resourceRefs", + "enforcementRefs", + "requiredProductStates", + "receiptRefs", + "runtimeBoundary", +} + +REQUIRED_PRODUCT_STATES = { + "wall_state_visible", + "policy_version_visible", + "participant_membership_visible", + "blocked_attempts_visible", + "receipt_refs_visible", + "clean_room_release_requestable", +} + +REQUIRED_ENFORCEMENT_REFS = { + "agentContext", + "agentCollaboration", + "guardrailBinding", + "memoryGate", + "retrievalFilter", + "cleanRoomSynthesis", +} + + +class ValidationError(Exception): + pass + + +def fail(message: str) -> None: + raise ValidationError(message) + + +def load_json(path: Path) -> dict: + try: + data = json.loads(path.read_text(encoding="utf-8")) + except FileNotFoundError as exc: + raise ValidationError(f"missing fixture: {path.relative_to(ROOT)}") from exc + except json.JSONDecodeError as exc: + raise ValidationError(f"invalid JSON: {path.relative_to(ROOT)}: {exc}") from exc + if not isinstance(data, dict): + fail("fixture root must be an object") + return data + + +def require_list(data: dict, key: str) -> list: + value = data.get(key) + if not isinstance(value, list) or not value: + fail(f"{key} must be a non-empty list") + return value + + +def validate(data: dict) -> None: + missing = sorted(REQUIRED_TOP - set(data)) + if missing: + fail(f"missing top-level fields: {missing}") + if data["schemaVersion"] != "prophet-platform.wallguard-professional-workroom.v0.1": + fail("schemaVersion mismatch") + if data["recordType"] != "WallGuardProfessionalWorkroom": + fail("recordType mismatch") + if not data["workroomRef"].startswith("workroom://"): + fail("workroomRef must use workroom://") + if not data["wallRef"].startswith("wall://"): + fail("wallRef must use wall://") + + policy_refs = require_list(data, "policyRefs") + if not any(ref.startswith("policy-fabric://") for ref in policy_refs): + fail("policyRefs must include a Policy Fabric reference") + + participants = require_list(data, "participants") + subject_types = {participant.get("subjectType") for participant in participants if isinstance(participant, dict)} + if {"human", "agent"} - subject_types: + fail("fixture must include at least one human and one agent participant") + for participant in participants: + if not isinstance(participant, dict): + fail("participants must be objects") + for key in ("subjectRef", "subjectType", "wallContextRef"): + if not participant.get(key): + fail(f"participant missing {key}") + + enforcement_refs = data["enforcementRefs"] + if not isinstance(enforcement_refs, dict): + fail("enforcementRefs must be an object") + missing_refs = sorted(REQUIRED_ENFORCEMENT_REFS - set(enforcement_refs)) + if missing_refs: + fail(f"missing enforcement refs: {missing_refs}") + + product_states = set(require_list(data, "requiredProductStates")) + missing_states = sorted(REQUIRED_PRODUCT_STATES - product_states) + if missing_states: + fail(f"missing product states: {missing_states}") + + receipt_refs = require_list(data, "receiptRefs") + if not all(str(ref).startswith("wallguard-receipt://") for ref in receipt_refs): + fail("all receiptRefs must be wallguard-receipt:// refs") + + runtime = data["runtimeBoundary"] + if not isinstance(runtime, dict): + fail("runtimeBoundary must be an object") + if runtime.get("policyAuthority") != "SocioProphet/policy-fabric": + fail("policy authority must remain Policy Fabric") + if runtime.get("productSurfaceOwner") != "SocioProphet/prophet-platform": + fail("product surface owner must be Prophet Platform") + if runtime.get("runtimeEnforcementImplemented") is not False: + fail("this tranche must not claim runtime enforcement") + + +def main() -> int: + try: + validate(load_json(FIXTURE)) + except ValidationError as exc: + print(f"ERROR: {exc}", file=sys.stderr) + return 1 + print("OK: WallGuard Professional Workroom fixture validates") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())