diff --git a/.github/workflows/artifact-validation.yml b/.github/workflows/artifact-validation.yml new file mode 100644 index 0000000..118f7b8 --- /dev/null +++ b/.github/workflows/artifact-validation.yml @@ -0,0 +1,38 @@ +name: Artifact Validation + +on: + workflow_dispatch: + push: + paths: + - 'artifacts/**' + - 'unit_tests/**' + - 'pytest.ini' + - '.github/workflows/artifact-validation.yml' + pull_request: + paths: + - 'artifacts/**' + - 'unit_tests/**' + - 'pytest.ini' + - '.github/workflows/artifact-validation.yml' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install deps + run: make -C artifacts deps + + - name: Run artifact validation via Makefile + run: make -C artifacts all diff --git a/artifacts/Makefile b/artifacts/Makefile new file mode 100644 index 0000000..85979d2 --- /dev/null +++ b/artifacts/Makefile @@ -0,0 +1,21 @@ +.PHONY: deps manifest-check validate check-all test all + +PYTHON ?= python + +deps: + $(PYTHON) -m pip install -r requirements-artifacts.txt + +manifest-check: + $(PYTHON) build_manifest.py --check --json + +validate: + $(PYTHON) validate_artifacts.py --json + +check-all: + $(PYTHON) check_all.py --json + +test: + cd .. && $(PYTHON) -m pytest -q unit_tests/test_artifacts_validation.py + +# `check-all` already runs semantic validation and manifest verification. +all: manifest-check check-all test diff --git a/artifacts/README.md b/artifacts/README.md new file mode 100644 index 0000000..7b78e33 --- /dev/null +++ b/artifacts/README.md @@ -0,0 +1,115 @@ +# Governance Artifacts Usage Guide + +This folder contains machine-readable assets for enterprise and regulator-facing AI governance workflows. + +## Files + +- `annex-iv-dossier-schema-v1.json`: JSON Schema for EU AI Act Annex IV dossier payloads. +- `control-catalog-v1.json`: control inventory with ownership, cadence, severity, and framework mappings. +- `roadmap-2026-2030.yaml`: phased implementation and milestone plan. +- `regulator-report-template.xml`: regulator-ready report skeleton. +- `enterprise-civilizational-agi-asi-blueprint-2026-2030.md`: implementation blueprint narrative. +- `examples/annex-iv-dossier-example.json`: sample payload conforming to Annex IV schema. +- `manifest-targets-v1.json`: canonical tracked-file list used by manifest build and validation. +- `schemas/manifest-targets-schema-v1.json`: JSON Schema for manifest-target metadata. +- `schemas/artifact-manifest-schema-v1.json`: JSON Schema for produced checksum manifests. +- `schemas/check-all-result-schema-v1.json`: JSON Schema for unified check JSON output. +- `artifact-manifest-v1.json`: SHA-256 checksum manifest for tamper-evident packaging. +- `validate_artifacts.py`: parser + semantic validation utility. +- `build_manifest.py`: manifest regeneration utility. +- `requirements-artifacts.txt`: pinned runtime/test dependencies for artifact checks. +- `Makefile`: convenience targets for local artifact validation workflows. + +## Validation + +Human-readable mode: + +```bash +python artifacts/validate_artifacts.py +``` + +Machine-readable JSON mode: + +```bash +python artifacts/validate_artifacts.py --json +``` + +Skip checksum validation (for local editing before manifest regeneration): + +```bash +python artifacts/validate_artifacts.py --skip-manifest +``` + +On validation failure with `--json`, output is `{ "status": "error", "error": "..." }` and exit code is `1`. + +Exit behavior: all CLI tools return `0` on success and `1` on validation/check failure. + +The validator performs: +1. JSON/YAML/XML parse checks. +2. Required key checks for schema, roadmap, and controls. +3. Annex IV sample semantic checks (types, required fields, enum values, date format). +4. Control mapping cross-reference checks (no unknown control IDs). +5. Regulator XML required section checks. +6. Roadmap milestone date-range checks (2026–2030). +7. Manifest checksum checks for all tracked artifacts. +8. Manifest coverage checks (no missing or unexpected files). + +## Regenerate checksum manifest + +```bash +python artifacts/build_manifest.py +``` + +Supports reproducible builds via `SOURCE_DATE_EPOCH`. + +Verify manifest freshness without rewriting: + +```bash +python artifacts/build_manifest.py --check +python artifacts/build_manifest.py --check --json +``` + +## Install dependencies + +```bash +pip install -r artifacts/requirements-artifacts.txt +# or: +cd artifacts && make deps +# or from repo root: +make -C artifacts deps +``` + +## Unified check + +```bash +python artifacts/check_all.py +python artifacts/check_all.py --json +``` + +`check_all --json` includes `schema_version`, `checked_at` (UTC ISO-8601), `manifest_fresh`, `validation_ok`, and `errors`. + +## Makefile shortcuts + +```bash +cd artifacts +make all +# Optional: override interpreter, e.g. PYTHON=python3.12 make all +# or from repo root: +make -C artifacts all +``` + +Other useful shortcuts: +- `make manifest-check` +- `make validate` +- `make check-all` +- `make test` + +## Test + +```bash +python -m pytest -q unit_tests/test_artifacts_validation.py +# or from artifacts/: make test +``` + + +CI note: `.github/workflows/artifact-validation.yml` supports `workflow_dispatch` for on-demand re-validation, runs `make -C artifacts all` as the canonical validation entrypoint, and triggers on changes to `artifacts/**`, `unit_tests/**`, `pytest.ini`, and the workflow file itself. diff --git a/artifacts/__init__.py b/artifacts/__init__.py new file mode 100644 index 0000000..5bc6dc3 --- /dev/null +++ b/artifacts/__init__.py @@ -0,0 +1 @@ +"""Governance artifacts tooling package.""" diff --git a/artifacts/annex-iv-dossier-schema-v1.json b/artifacts/annex-iv-dossier-schema-v1.json new file mode 100644 index 0000000..0b3f9b1 --- /dev/null +++ b/artifacts/annex-iv-dossier-schema-v1.json @@ -0,0 +1,55 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.org/schemas/annex-iv-dossier-schema-v1.json", + "title": "EU AI Act Annex IV Dossier", + "type": "object", + "required": [ + "provider", + "system", + "intended_purpose", + "architecture", + "training_data", + "performance", + "oversight", + "post_market_monitoring", + "change_log" + ], + "properties": { + "provider": { + "type": "object", + "required": ["legal_name", "jurisdiction", "accountable_officer"], + "properties": { + "legal_name": {"type": "string"}, + "jurisdiction": {"type": "string"}, + "accountable_officer": {"type": "string"} + } + }, + "system": { + "type": "object", + "required": ["name", "version", "risk_tier"], + "properties": { + "name": {"type": "string"}, + "version": {"type": "string"}, + "risk_tier": {"type": "string", "enum": ["low", "limited", "high", "frontier"]} + } + }, + "intended_purpose": {"type": "string"}, + "architecture": {"type": "string"}, + "training_data": {"type": "string"}, + "performance": {"type": "string"}, + "oversight": {"type": "string"}, + "post_market_monitoring": {"type": "string"}, + "change_log": { + "type": "array", + "items": { + "type": "object", + "required": ["date", "change", "approver"], + "properties": { + "date": {"type": "string", "format": "date"}, + "change": {"type": "string"}, + "approver": {"type": "string"} + } + } + } + } +} diff --git a/artifacts/artifact-manifest-v1.json b/artifacts/artifact-manifest-v1.json new file mode 100644 index 0000000..226173c --- /dev/null +++ b/artifacts/artifact-manifest-v1.json @@ -0,0 +1,12 @@ +{ + "files": { + "annex-iv-dossier-schema-v1.json": "191c3442f4b372e8fb400640648841fb4d63aecdfb791d0b1b230a65a384ffe1", + "control-catalog-v1.json": "56328ecaed2af4d832e993accb3b85d63d69f93eece4f10de08f0c82f71729d8", + "enterprise-civilizational-agi-asi-blueprint-2026-2030.md": "12684e460b4f33a49d74e66eaa1400aab85e4dd6879e262e06ac932be7c3f3e3", + "examples/annex-iv-dossier-example.json": "fd914a07bf2691d9de262907953890ba353b23fe159d07a8b53eee1e6d16b1e2", + "regulator-report-template.xml": "62c55a96b60bbc4592f0ad273ee1cca6e25eac6a437fb047dfb08bdf5baeab2d", + "roadmap-2026-2030.yaml": "2297c95faefe22ff03cb9aa7d104be232fa0269b831cb231f5b7f0ab0ed86369" + }, + "generated_at": "2026-04-26T03:26:37+00:00", + "version": "1.1" +} diff --git a/artifacts/build_manifest.py b/artifacts/build_manifest.py new file mode 100644 index 0000000..c77905f --- /dev/null +++ b/artifacts/build_manifest.py @@ -0,0 +1,87 @@ +"""Build checksum manifest for governance artifacts. + +Supports reproducible timestamps via SOURCE_DATE_EPOCH. +""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path + +ARTIFACTS_DIR = Path(__file__).resolve().parent + +if __package__ in (None, ""): + from manifest_utils import load_manifest_targets_from_dir, sha256_file, timestamp_iso8601 +else: + from .manifest_utils import load_manifest_targets_from_dir, sha256_file, timestamp_iso8601 + + +def load_manifest_targets() -> list[str]: + return load_manifest_targets_from_dir(ARTIFACTS_DIR) + + +def build_manifest_payload() -> dict: + manifest = { + "version": "1.1", + "generated_at": timestamp_iso8601(), + "files": {}, + } + for rel in load_manifest_targets(): + manifest["files"][rel] = sha256_file(ARTIFACTS_DIR / rel) + return manifest + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Build or check artifact checksum manifest") + parser.add_argument("--check", action="store_true", help="Exit non-zero if manifest is out of date") + parser.add_argument("--json", action="store_true", help="Emit machine-readable JSON output") + return parser.parse_args() + + +def emit(args: argparse.Namespace, payload: dict) -> None: + if args.json: + print(json.dumps(payload, indent=2, sort_keys=True)) + else: + print(payload["message"]) + + +def run_cli(args: argparse.Namespace) -> int: + try: + out = ARTIFACTS_DIR / "artifact-manifest-v1.json" + payload = build_manifest_payload() + rendered = json.dumps(payload, indent=2, sort_keys=True) + "\n" + + if args.check: + if not out.exists(): + emit(args, {"status": "error", "message": "Manifest missing. Run: python artifacts/build_manifest.py"}) + return 1 + try: + existing = json.loads(out.read_text(encoding="utf-8")) + except json.JSONDecodeError: + emit(args, {"status": "error", "message": "Manifest file is invalid JSON. Run: python artifacts/build_manifest.py"}) + return 1 + if not isinstance(existing, dict): + emit(args, {"status": "error", "message": "Manifest file has invalid structure. Run: python artifacts/build_manifest.py"}) + return 1 + if existing.get("version") != payload.get("version") or existing.get("files") != payload.get("files"): + emit(args, {"status": "error", "message": "Manifest is out of date. Run: python artifacts/build_manifest.py"}) + return 1 + emit(args, {"status": "ok", "message": "Manifest is up to date."}) + return 0 + + out.write_text(rendered, encoding="utf-8") + emit(args, {"status": "ok", "message": f"Wrote {out}"}) + return 0 + except ValueError as exc: + emit(args, {"status": "error", "message": str(exc)}) + return 1 + + +def main() -> None: + args = parse_args() + raise SystemExit(run_cli(args)) + + +if __name__ == "__main__": + main() diff --git a/artifacts/check_all.py b/artifacts/check_all.py new file mode 100644 index 0000000..fc3fc5a --- /dev/null +++ b/artifacts/check_all.py @@ -0,0 +1,75 @@ +"""Unified artifact integrity + semantic validation entrypoint.""" + +from __future__ import annotations + +import argparse +import json +import sys +from datetime import datetime, timezone + +if __package__ in (None, ""): + from build_manifest import build_manifest_payload + from validate_artifacts import ValidationError, load_json, run_validation +else: + from .build_manifest import build_manifest_payload + from .validate_artifacts import ValidationError, load_json, run_validation + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Run all artifact checks") + parser.add_argument("--json", action="store_true", help="Emit machine-readable JSON output") + return parser.parse_args() + + +def run_all() -> dict: + from pathlib import Path + + generated = build_manifest_payload() + existing = load_json(Path(__file__).resolve().parent / "artifact-manifest-v1.json") + manifest_fresh = existing.get("version") == generated.get("version") and existing.get("files") == generated.get("files") + + validation_checks = run_validation(include_manifest=True) + validation_ok = all(status == "pass" for status in validation_checks.values()) + + errors: list[str] = [] + if not manifest_fresh: + errors.append("manifest_not_fresh") + if not validation_ok: + errors.append("validation_checks_not_all_pass") + + return { + "schema_version": "1.0", + "checked_at": datetime.now(timezone.utc).replace(microsecond=0).isoformat(), + "manifest_fresh": manifest_fresh, + "validation_ok": validation_ok, + "validation_checks": validation_checks, + "errors": errors, + "status": "ok" if not errors else "error", + } + + +def run_cli(args: argparse.Namespace) -> int: + try: + result = run_all() + except (ValidationError, ValueError, OSError, KeyError, TypeError) as exc: + if args.json: + print(json.dumps({"status": "error", "error": str(exc)}, indent=2, sort_keys=True)) + else: + print(f"Checks failed: {exc}", file=sys.stderr) + return 1 + + if args.json: + print(json.dumps(result, indent=2, sort_keys=True)) + else: + print("All checks passed." if result["status"] == "ok" else f"Checks failed: {', '.join(result['errors'])}") + + return 0 if result["status"] == "ok" else 1 + + +def main() -> None: + args = parse_args() + raise SystemExit(run_cli(args)) + + +if __name__ == "__main__": + main() diff --git a/artifacts/control-catalog-v1.json b/artifacts/control-catalog-v1.json new file mode 100644 index 0000000..8156aae --- /dev/null +++ b/artifacts/control-catalog-v1.json @@ -0,0 +1,140 @@ +{ + "version": "1.1", + "catalog": "institutional_ai_control_catalog", + "updated_at": "2026-04-24", + "control_domains": [ + { + "domain": "governance", + "controls": [ + { + "id": "gov.board_charter", + "owner": "board_risk_committee", + "test_frequency": "quarterly", + "severity_if_failed": "high" + }, + { + "id": "gov.accountability_map_smcr", + "owner": "compliance", + "test_frequency": "semiannual", + "severity_if_failed": "high" + } + ] + }, + { + "domain": "model_risk", + "controls": [ + { + "id": "mrm.independent_validation_sr11_7", + "owner": "model_risk_management", + "test_frequency": "annual_or_material_change", + "severity_if_failed": "critical" + }, + { + "id": "mrm.challenger_required_tier1", + "owner": "model_risk_management", + "test_frequency": "annual", + "severity_if_failed": "high" + }, + { + "id": "mrm.drift_escalation_threshold", + "owner": "ai_operations", + "test_frequency": "continuous", + "severity_if_failed": "high" + } + ] + }, + { + "domain": "compliance_as_code", + "controls": [ + { + "id": "cac.opa_rego_policy_gate", + "owner": "platform_engineering", + "test_frequency": "per_pull_request", + "severity_if_failed": "critical" + }, + { + "id": "cac.terraform_dual_approval_prod", + "owner": "sre", + "test_frequency": "per_change", + "severity_if_failed": "high" + } + ] + }, + { + "domain": "auditability", + "controls": [ + { + "id": "audit.kafka_worm_logging", + "owner": "security_engineering", + "test_frequency": "continuous", + "severity_if_failed": "critical" + }, + { + "id": "audit.deterministic_replay", + "owner": "internal_audit", + "test_frequency": "monthly", + "severity_if_failed": "high" + }, + { + "id": "audit.pqc_hybrid_signature", + "owner": "cryptography_office", + "test_frequency": "quarterly", + "severity_if_failed": "medium" + } + ] + }, + { + "domain": "frontier_safety", + "controls": [ + { + "id": "safety.containment_lab_isolation", + "owner": "frontier_safety", + "test_frequency": "monthly", + "severity_if_failed": "critical" + }, + { + "id": "safety.cognitive_resonance_monitoring", + "owner": "frontier_safety", + "test_frequency": "continuous", + "severity_if_failed": "critical" + }, + { + "id": "safety.crisis_simulation_program", + "owner": "operational_resilience", + "test_frequency": "quarterly", + "severity_if_failed": "high" + } + ] + } + ], + "mappings": { + "eu_ai_act_annex_iv": [ + "gov.board_charter", + "mrm.independent_validation_sr11_7", + "cac.opa_rego_policy_gate", + "audit.kafka_worm_logging" + ], + "nist_ai_rmf_1_0": [ + "gov.board_charter", + "mrm.drift_escalation_threshold", + "audit.deterministic_replay" + ], + "nist_ai_600_1": [ + "safety.cognitive_resonance_monitoring", + "safety.crisis_simulation_program" + ], + "iso_iec_42001": [ + "gov.board_charter", + "audit.deterministic_replay" + ], + "sr_11_7": [ + "mrm.independent_validation_sr11_7", + "mrm.challenger_required_tier1", + "mrm.drift_escalation_threshold" + ], + "fcra_ecoa": [ + "mrm.challenger_required_tier1", + "gov.accountability_map_smcr" + ] + } +} diff --git a/artifacts/enterprise-civilizational-agi-asi-blueprint-2026-2030.md b/artifacts/enterprise-civilizational-agi-asi-blueprint-2026-2030.md new file mode 100644 index 0000000..7dedd6d --- /dev/null +++ b/artifacts/enterprise-civilizational-agi-asi-blueprint-2026-2030.md @@ -0,0 +1,277 @@ +# 2026–2030 Enterprise and Civilizational AGI/ASI Governance Blueprint (v1.1) + +## 1) Scope, audience, and outcomes + +This blueprint is designed for: +- Fortune 500 / Global 2000 enterprises, +- G‑SIFI financial institutions (banking, insurance, market infrastructure), +- regulators and supervisory authorities, +- enterprise architecture and platform engineering teams. + +**Outcome target by December 2030**: establish auditable, regulator-ready, systemically aware AI governance that scales from enterprise model risk controls to frontier AGI/ASI containment and inter-jurisdiction coordination. + +--- + +## 2) Governance baseline and non-negotiables + +1. **No high-impact deployment without control evidence**. +2. **No frontier model release without independent safety sign-off**. +3. **No policy drift in production without change approval and replayable logs**. +4. **No black-box decisioning in regulated contexts without explainability and appeal paths**. +5. **No unresolved high-severity findings older than 30 days**. + +--- + +## 3) Regulatory and standards alignment (implementation-grade) + +### 3.1 Crosswalk implementation matrix + +| Regime | Required capability | Control family | Primary evidence | +|---|---|---|---| +| EU AI Act (incl. Annex IV) | technical documentation, risk system, human oversight, post-market monitoring | `gov.*`, `mrm.*`, `ops.*`, `audit.*` | Annex IV dossier JSON + model card + monitoring logs | +| NIST AI RMF 1.0 | Govern / Map / Measure / Manage | `gov.*`, `risk.*`, `sec.*` | RMF profile + control test results | +| NIST AI 600-1 | GenAI risk profile and mitigation | `safety.*`, `rag.*`, `redteam.*` | GenAI risk scorecards + eval traces | +| ISO/IEC 42001 | AIMS operating model | `gov.*`, `audit.*`, `ops.*` | AIMS policies + internal audit package | +| OECD AI Principles | transparency, robustness, accountability | `xai.*`, `ethics.*`, `appeals.*` | explanation records + appeals tracker | +| GDPR | lawful basis, minimization, rights | `data.*`, `privacy.*` | DPIA, RoPA, DSAR logs | +| FCRA/ECOA | fairness and adverse action | `fairness.*`, `xai.*` | adverse action reason codes + bias test reports | +| Basel III/IV | capital adequacy and stress integration | `risk.*`, `stress.*` | ICAAP/ILAAP AI annex | +| SR 11-7 | model lifecycle and independent validation | `mrm.*`, `val.*` | validation report + exceptions register | +| NIS2 | cyber controls and incident obligations | `sec.*`, `ir.*` | incident package + forensic evidence | +| FCA Consumer Duty / SMCR | good outcomes and accountability | `conduct.*`, `gov.*` | customer outcomes metrics + SMF attestations | +| MAS/HKMA FEAT | fairness, ethics, accountability, transparency | `fairness.*`, `ethics.*`, `xai.*` | FEAT pack + remediation evidence | + +### 3.2 Annex IV operational dossier fields (minimum) + +The artifact `annex-iv-dossier-schema-v1.json` defines required dossier sections: +- provider metadata and accountable officers, +- intended purpose and prohibited uses, +- architecture and training provenance, +- performance, robustness, and cybersecurity metrics, +- human oversight design, +- post-market monitoring plan, +- change history and incident linkage. + +--- + +## 4) Enterprise reference architecture + +## 4.1 Sentinel AI Governance Platform v2.4 + +**Control planes**: +- Policy/obligation graph (regulatory-to-control mappings). +- Model and agent registry (inventory, tiering, owner, status). +- Validation and red-team pipeline. +- Runtime policy enforcement (OPA sidecars in Node.js/Python services). +- Kafka-backed immutable evidence ledger (WORM). +- Regulator/auditor workspace with deterministic replay. + +**Infrastructure stack**: +- Kubernetes for workload segmentation by criticality. +- Kafka for eventing and signed audit streams. +- OPA/Rego for compliance-as-code. +- Terraform for immutable provisioning and drift controls. +- CI/CD policy gates for every release. +- Next.js explainability frontend for risk, legal, compliance, and examiners. + +## 4.2 WorkflowAI Pro + EAIP + high-assurance RAG + +- WorkflowAI Pro orchestrates agentic workflows with segregation of duties and dual approvals. +- EAIP standardizes service contracts across models, tools, and data products. +- High-assurance RAG controls: + - source trust tiers and allowlists, + - cryptographic citation provenance, + - legal privilege and DLP filters, + - abstain/escalate behavior for uncertainty. + +--- + +## 5) AGI/ASI safety, containment, and emergency controls + +## 5.1 Minimum Viable AGI Governance Stack (MVAGS) + +1. Capability tiering (including autonomy and self-proliferation criteria). +2. Frontier release board (safety, legal, and business quorum). +3. Containment lab with network isolation and restricted tool surfaces. +4. Cognitive Resonance Protocol monitoring for anomalous agent behavior. +5. Sentinel/Omni-Sentinel hard-stop controls and safe rollback paths. + +## 5.2 Frontier risk taxonomy (Luminous Engine Codex aligned) + +- deception/manipulation, +- cyber-offense amplification, +- bio/chem dual-use enablement, +- market manipulation and payment-system disruption, +- autonomous replication/resource acquisition, +- governance evasion attempts. + +**Required deliverables before production**: +- safety case, +- adversarial red-team dossier, +- crisis simulation outputs, +- regulator notification playbook, +- reversible deployment plan. + +--- + +## 6) Civilizational stack and global compute governance + +## 6.1 International Compute Governance Consortium (ICGC) + +**Functions**: +- compute registry coordination, +- threshold harmonization, +- transnational incident fusion, +- annual systemic AI risk outlook. + +## 6.2 Interoperable mechanisms + +- GACRA, GASO, GFMCF, GAICS, GAIVS, +- GACP, GATI, GACMO, FTEWS, +- GAI-SOC, GAIGA, GACRLS, GFCO, GAID, GASCF. + +Each mechanism should publish an API profile for incident exchange, assurance claims, and escalation metadata. + +--- + +## 7) Financial-services governance profile (G-SIFI grade) + +### 7.1 Use-case control overlays + +- **Credit**: ECOA/FCRA fairness constraints, reason-code service, disparity monitoring. +- **Trading**: market-abuse guardrails, bounded action policies, kill-switch with millisecond SLA. +- **Risk/Treasury**: stress-linked action constraints and macroprudential overlays. +- **Fiduciary advisory**: suitability checks, conflict checks, client-communication audit chain. +- **Systemic-risk-sensitive advisory**: supervisory escalation triggers and concentration-risk controls. + +### 7.2 SR 11-7 lifecycle implementation + +Intake → tiering → development standards → independent validation → controlled release → surveillance → periodic revalidation. + +Mandatory for tier-1 and tier-2 high-impact models: +- challenger model, +- quarterly drift report, +- annual full validation, +- exception closure SLA. + +--- + +## 8) Kafka ACL governance and forensic-grade evidence + +### 8.1 Control requirements + +- least-privilege ACLs with just-in-time grants, +- dual authorization for production ACL changes, +- immutable WORM storage, +- deterministic replay keyed by release hash and policy hash, +- PQC migration path for long-retention evidence, +- selective-disclosure proofing for sensitive policies. + +### 8.2 Incident response checklist (regulator-grade) + +1. classify severity and systemic blast radius, +2. freeze affected model/agent and credentials, +3. capture signed snapshots (model, prompt, tool, policy, data contract), +4. run deterministic replay, +5. file preliminary regulator report within jurisdictional deadlines, +6. ship corrective controls and independent retest, +7. board-level post-incident attestation. + +--- + +## 9) Enterprise AI Governance Hub and AI Safety Report Generator + +## 9.1 Governance Hub components + +- obligations registry, +- control library and test engine, +- exception workflow and approval service, +- evidence vault and lineage graph, +- examiner portal, +- board risk cockpit. + +## 9.2 Safety Report Generator outputs + +- board brief, +- regulator technical report, +- engineering corrective action plan, +- machine-readable annex bundle (JSON/XML/YAML). + +--- + +## 10) Advanced prompt engineering for governed agentic systems + +1. policy-conditioned system prompts, +2. signed tool manifests and allowed-action envelopes, +3. prompt linting and policy static analysis, +4. adversarial canary prompts in production telemetry, +5. schema-validated structured outputs, +6. mandatory escalation templates for low-confidence high-impact outcomes. + +--- + +## 11) Regulator-ready report sections + +Institutional AGI/ASI Governance Technical Report + +This report presents control design and operating effectiveness for high-impact and frontier AI systems, +including risk posture, incidents, remediation, and accountable management attestations. + + +1. Scope, inventory, and criticality. +2. Legal and standards applicability matrix. +3. Control test outcomes and open findings. +4. Red-team and safety evaluation results. +5. Incident summaries and closure evidence. +6. Residual risk statement and board attestation. +7. Annex package (Annex IV fields, SR 11-7 extracts, FEAT scorecards). + + +--- + +## 12) 2026–2030 dependency-aware implementation roadmap + +### Phase 0 — Foundation (Q2–Q4 2026) +- establish charter, accountability map, inventory and risk taxonomy, +- deploy governance registry, policy engine, and evidence baseline, +- implement Annex IV dossier pipeline. + +### Phase 1 — Industrialization (2027) +- enforce OPA/Rego gates in CI/CD, +- activate Kafka WORM + deterministic replay, +- operationalize independent validation + red-team. + +### Phase 2 — Frontier Safety Expansion (2028) +- stand up containment lab, +- implement Cognitive Resonance monitoring, +- enable hybrid PQC signatures for long-lived evidence. + +### Phase 3 — External Interlock (2029) +- integrate compute registry and incident exchange, +- adopt GAICS classification, +- deploy treaty-interface adapters. + +### Phase 4 — Adaptive Steady State (2030) +- annual independent assurance cycle, +- systemic-risk stress simulations, +- public transparency and accountability reporting. + +--- + +## 13) KPI/KRI targets (board and regulator view) + +- **Control pass rate**: ≥ 98% for tier-1 controls. +- **High-severity finding closure**: ≤ 30 days median. +- **Drift detection to mitigation**: ≤ 72 hours for tier-1 systems. +- **Incident report timeliness**: 100% within jurisdictional SLA. +- **Explainability coverage**: 100% for customer-impacting decisions. +- **Replay determinism success**: ≥ 99.5%. + +--- + +## 14) Machine-readable artifact index + +- `artifacts/roadmap-2026-2030.yaml` +- `artifacts/control-catalog-v1.json` +- `artifacts/regulator-report-template.xml` +- `artifacts/annex-iv-dossier-schema-v1.json` diff --git a/artifacts/examples/annex-iv-dossier-example.json b/artifacts/examples/annex-iv-dossier-example.json new file mode 100644 index 0000000..18b53e7 --- /dev/null +++ b/artifacts/examples/annex-iv-dossier-example.json @@ -0,0 +1,25 @@ +{ + "provider": { + "legal_name": "Example Financial Group plc", + "jurisdiction": "EU", + "accountable_officer": "Chief AI Risk Officer" + }, + "system": { + "name": "Credit Underwriting Assistant", + "version": "2026.4.0", + "risk_tier": "high" + }, + "intended_purpose": "Assist human credit officers with underwriting recommendations.", + "architecture": "Hybrid gradient-boosting + policy-constrained LLM explanation layer.", + "training_data": "Internal loan performance data (2018-2025) plus approved bureau fields.", + "performance": "AUC=0.81; stability index monitored weekly; alert threshold PSI>0.2.", + "oversight": "Human approval required for all adverse outcomes.", + "post_market_monitoring": "Continuous drift, fairness, and incident monitoring with monthly review.", + "change_log": [ + { + "date": "2026-03-10", + "change": "Updated feature normalization for debt-to-income ratio.", + "approver": "Model Risk Committee" + } + ] +} diff --git a/artifacts/manifest-targets-v1.json b/artifacts/manifest-targets-v1.json new file mode 100644 index 0000000..3ac4b5c --- /dev/null +++ b/artifacts/manifest-targets-v1.json @@ -0,0 +1,11 @@ +{ + "version": "1.0", + "files": [ + "annex-iv-dossier-schema-v1.json", + "control-catalog-v1.json", + "roadmap-2026-2030.yaml", + "regulator-report-template.xml", + "enterprise-civilizational-agi-asi-blueprint-2026-2030.md", + "examples/annex-iv-dossier-example.json" + ] +} diff --git a/artifacts/manifest_utils.py b/artifacts/manifest_utils.py new file mode 100644 index 0000000..c4a4825 --- /dev/null +++ b/artifacts/manifest_utils.py @@ -0,0 +1,60 @@ +"""Shared utilities for manifest target and checksum handling.""" + +from __future__ import annotations + +import hashlib +import json +import os +from datetime import datetime, timezone +from pathlib import Path +from pathlib import PurePosixPath + + +def load_manifest_targets_from_dir(artifacts_dir: Path) -> list[str]: + targets_path = artifacts_dir / "manifest-targets-v1.json" + try: + data = json.loads(targets_path.read_text(encoding="utf-8")) + except FileNotFoundError as exc: + raise ValueError("manifest-targets-v1.json is missing") from exc + except json.JSONDecodeError as exc: + raise ValueError("manifest-targets-v1.json is not valid JSON") from exc + if not isinstance(data, dict): + raise ValueError("manifest-targets-v1.json must contain an object") + if data.get("version") != "1.0": + raise ValueError("manifest-targets-v1.json version must be 1.0") + files = data.get("files") + if not isinstance(files, list) or not files: + raise ValueError("manifest-targets-v1.json must contain a non-empty files list") + if any(not isinstance(item, str) for item in files): + raise ValueError("manifest-targets-v1.json files entries must be strings") + if len(set(files)) != len(files): + raise ValueError("manifest-targets-v1.json contains duplicate file entries") + for item in files: + if "\\" in item: + raise ValueError("manifest-targets-v1.json files entries must use POSIX-style separators") + normalized = PurePosixPath(item) + if not item.strip(): + raise ValueError("manifest-targets-v1.json files entries must be non-empty") + if normalized.is_absolute() or ".." in normalized.parts: + raise ValueError("manifest-targets-v1.json files entries must be safe relative paths") + target_path = artifacts_dir / normalized.as_posix() + if not target_path.exists() or not target_path.is_file(): + raise ValueError(f"manifest-targets-v1.json references missing file: {item}") + return files + + +def sha256_file(path: Path) -> str: + h = hashlib.sha256() + with path.open("rb") as f: + for chunk in iter(lambda: f.read(8192), b""): + h.update(chunk) + return h.hexdigest() + + +def timestamp_iso8601() -> str: + source_date_epoch = os.getenv("SOURCE_DATE_EPOCH") + if source_date_epoch is not None: + dt = datetime.fromtimestamp(int(source_date_epoch), tz=timezone.utc) + else: + dt = datetime.now(timezone.utc) + return dt.replace(microsecond=0).isoformat() diff --git a/artifacts/regulator-report-template.xml b/artifacts/regulator-report-template.xml new file mode 100644 index 0000000..17587bd --- /dev/null +++ b/artifacts/regulator-report-template.xml @@ -0,0 +1,16 @@ + + Regulator-Ready AI Governance Technical Report + + This report provides supervisory-grade evidence of AI governance controls, + model risk management effectiveness, incident handling performance, and residual risk posture. + + +
Model inventory, ownership, and criticality classification.
+
Jurisdictional mapping to legal and standards obligations.
+
EU AI Act Annex IV dossier extract with required field completion status.
+
Control design and operating effectiveness results.
+
Red-team, adversarial, and frontier safety evaluation outcomes.
+
Incident timeline, root cause, corrective and preventive action status.
+
Senior management and board accountability attestation.
+
+
diff --git a/artifacts/requirements-artifacts.txt b/artifacts/requirements-artifacts.txt new file mode 100644 index 0000000..86a87af --- /dev/null +++ b/artifacts/requirements-artifacts.txt @@ -0,0 +1,2 @@ +pyyaml==6.0.2 +pytest==8.3.4 diff --git a/artifacts/roadmap-2026-2030.yaml b/artifacts/roadmap-2026-2030.yaml new file mode 100644 index 0000000..2a70d5c --- /dev/null +++ b/artifacts/roadmap-2026-2030.yaml @@ -0,0 +1,101 @@ +version: 1.1 +name: enterprise-civilizational-agi-asi-roadmap +horizon: 2026-2030 +updated_at: 2026-04-24 +stakeholders: + - boards + - c_suites + - regulators + - enterprise_architects + - ai_platform_engineers + - researchers +milestones: + - id: m0 + date: 2026-09-30 + deliverable: baseline_inventory_and_risk_tiering + - id: m1 + date: 2026-12-31 + deliverable: annex_iv_pipeline_go_live + - id: m2 + date: 2027-06-30 + deliverable: ci_cd_policy_gates_for_tier1_models + - id: m3 + date: 2027-12-31 + deliverable: deterministic_audit_replay_operational + - id: m4 + date: 2028-06-30 + deliverable: agi_containment_lab_live + - id: m5 + date: 2028-12-31 + deliverable: cognitive_resonance_monitoring_in_prod + - id: m6 + date: 2029-12-31 + deliverable: cross_jurisdiction_incident_exchange + - id: m7 + date: 2030-12-31 + deliverable: adaptive_governance_steady_state +phases: + - id: phase_0 + window: "2026-Q2_to_2026-Q4" + objectives: + - enterprise_ai_charter + - accountability_map + - model_inventory + - risk_taxonomy + - annex_iv_pipeline + dependencies: + - executive_mandate + - iam_modernization + - data_lineage_baseline + exits: + - governance_registry_live + - initial_crosswalk_complete + - id: phase_1 + window: "2027" + objectives: + - policy_as_code_gates + - kafka_worm_audit + - deterministic_replay + - independent_validation + - red_team_program + dependencies: + - phase_0 + - sre_capacity + - internal_audit_enablement + exits: + - regulated_release_workflow_active + - id: phase_2 + window: "2028" + objectives: + - agi_containment_lab + - cognitive_resonance_monitoring + - pqc_hybrid_signing + - cross_border_tabletops + dependencies: + - phase_1 + - legal_escalation_protocol + exits: + - frontier_alerting_operational + - id: phase_3 + window: "2029" + objectives: + - compute_registry_integration + - gaics_reporting + - treaty_interface_adapter + dependencies: + - phase_2 + - regulator_participation + - interoperability_mou + exits: + - external_incident_exchange_live + - id: phase_4 + window: "2030" + objectives: + - adaptive_governance + - systemic_risk_stress_program + - transparency_reporting + dependencies: + - phase_3 + - independent_assurance_cycle + exits: + - steady_state_assurance diff --git a/artifacts/schemas/artifact-manifest-schema-v1.json b/artifacts/schemas/artifact-manifest-schema-v1.json new file mode 100644 index 0000000..ed0e00e --- /dev/null +++ b/artifacts/schemas/artifact-manifest-schema-v1.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.org/schemas/artifact-manifest-schema-v1.json", + "title": "Artifact Manifest", + "type": "object", + "required": ["version", "generated_at", "files"], + "properties": { + "version": {"type": "string", "const": "1.1"}, + "generated_at": {"type": "string", "format": "date-time"}, + "files": { + "type": "object", + "minProperties": 1, + "additionalProperties": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + } + } + }, + "additionalProperties": false +} diff --git a/artifacts/schemas/check-all-result-schema-v1.json b/artifacts/schemas/check-all-result-schema-v1.json new file mode 100644 index 0000000..4d464f2 --- /dev/null +++ b/artifacts/schemas/check-all-result-schema-v1.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.org/schemas/check-all-result-schema-v1.json", + "title": "Check All Result", + "type": "object", + "required": ["schema_version", "checked_at", "status", "manifest_fresh", "validation_ok", "validation_checks", "errors"], + "properties": { + "schema_version": {"type": "string", "const": "1.0"}, + "checked_at": {"type": "string", "format": "date-time"}, + "status": {"type": "string", "enum": ["ok", "error"]}, + "manifest_fresh": {"type": "boolean"}, + "validation_ok": {"type": "boolean"}, + "validation_checks": {"type": "object"}, + "errors": {"type": "array", "items": {"type": "string"}} + }, + "additionalProperties": false +} diff --git a/artifacts/schemas/manifest-targets-schema-v1.json b/artifacts/schemas/manifest-targets-schema-v1.json new file mode 100644 index 0000000..17e705c --- /dev/null +++ b/artifacts/schemas/manifest-targets-schema-v1.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.org/schemas/manifest-targets-schema-v1.json", + "title": "Manifest Targets", + "type": "object", + "required": ["version", "files"], + "properties": { + "version": {"type": "string", "const": "1.0"}, + "files": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": {"type": "string"} + } + }, + "additionalProperties": false +} diff --git a/artifacts/validate_artifacts.py b/artifacts/validate_artifacts.py new file mode 100644 index 0000000..1486219 --- /dev/null +++ b/artifacts/validate_artifacts.py @@ -0,0 +1,263 @@ +"""Validation utility for governance artifacts. + +Checks include: +- JSON parse checks +- YAML parse checks +- XML parse and required section checks +- Minimal JSON-Schema-like validation for the Annex IV example payload +- Cross-reference checks for control mappings +- Roadmap milestone date-range checks for 2026-2030 horizon +- Manifest checksum verification for tamper evidence +""" + +from __future__ import annotations + +import argparse +import json +import sys +from datetime import date, datetime +from pathlib import Path +import xml.etree.ElementTree as ET + +import yaml + +ARTIFACTS_DIR = Path(__file__).resolve().parent + +if __package__ in (None, ""): + from manifest_utils import load_manifest_targets_from_dir, sha256_file +else: + from .manifest_utils import load_manifest_targets_from_dir, sha256_file +REQUIRED_REPORT_SECTION_IDS = { + "scope", + "obligations", + "annex_iv", + "control-testing", + "safety-evals", + "incidents", + "attestation", +} + + +def load_manifest_targets() -> set[str]: + try: + return set(load_manifest_targets_from_dir(ARTIFACTS_DIR)) + except ValueError as exc: + raise ValidationError(str(exc)) from exc + + +class ValidationError(Exception): + """Raised when one or more artifact validation checks fail.""" + + +def display_artifact_path(path: Path) -> str: + try: + return str(path.relative_to(ARTIFACTS_DIR)) + except ValueError: + return str(path) + + +def load_json(path: Path) -> dict: + try: + with path.open("r", encoding="utf-8") as f: + return json.load(f) + except FileNotFoundError as exc: + raise ValidationError(f"required artifact file missing: {display_artifact_path(path)}") from exc + except json.JSONDecodeError as exc: + raise ValidationError(f"invalid JSON in artifact file: {display_artifact_path(path)}") from exc + +def validate_required_keys(obj: dict, required: list[str], label: str) -> None: + missing = [k for k in required if k not in obj] + if missing: + raise ValidationError(f"{label} missing keys: {missing}") + + +def ensure_type(value: object, expected: str, path: str) -> None: + if expected == "object" and not isinstance(value, dict): + raise ValidationError(f"{path} expected object") + if expected == "array" and not isinstance(value, list): + raise ValidationError(f"{path} expected array") + if expected == "string" and not isinstance(value, str): + raise ValidationError(f"{path} expected string") + + +def validate_change_log(change_log: list[dict]) -> None: + for idx, item in enumerate(change_log): + ensure_type(item, "object", f"change_log[{idx}]") + validate_required_keys(item, ["date", "change", "approver"], f"change_log[{idx}]") + try: + date.fromisoformat(item["date"]) + except ValueError as exc: + raise ValidationError(f"change_log[{idx}].date is not valid ISO date") from exc + + +def validate_annex_iv_example(schema: dict, example: dict) -> None: + validate_required_keys(example, schema.get("required", []), "annex-iv example") + + provider_schema = schema["properties"]["provider"] + provider = example["provider"] + ensure_type(provider, provider_schema["type"], "provider") + validate_required_keys(provider, provider_schema["required"], "provider") + + system_schema = schema["properties"]["system"] + system = example["system"] + ensure_type(system, system_schema["type"], "system") + validate_required_keys(system, system_schema["required"], "system") + + for key in ["intended_purpose", "architecture", "training_data", "performance", "oversight", "post_market_monitoring"]: + ensure_type(example[key], "string", key) + + allowed_tiers = set(system_schema["properties"]["risk_tier"]["enum"]) + tier = system["risk_tier"] + if tier not in allowed_tiers: + raise ValidationError(f"invalid risk_tier: {tier}") + + ensure_type(example["change_log"], "array", "change_log") + validate_change_log(example["change_log"]) + + +def validate_control_catalog(controls: dict) -> None: + validate_required_keys(controls, ["version", "catalog", "control_domains", "mappings"], "control catalog") + + known_control_ids: set[str] = set() + for domain in controls["control_domains"]: + validate_required_keys(domain, ["domain", "controls"], "control_domain") + for control in domain["controls"]: + validate_required_keys(control, ["id", "owner", "test_frequency", "severity_if_failed"], "control") + known_control_ids.add(control["id"]) + + for mapping_name, mapped_ids in controls["mappings"].items(): + for control_id in mapped_ids: + if control_id not in known_control_ids: + raise ValidationError(f"mapping {mapping_name} references unknown control id: {control_id}") + + +def validate_roadmap(roadmap: dict) -> None: + validate_required_keys(roadmap, ["version", "name", "horizon", "phases", "milestones"], "roadmap") + for milestone in roadmap["milestones"]: + validate_required_keys(milestone, ["id", "date", "deliverable"], "milestone") + milestone_date = milestone["date"] + if not isinstance(milestone_date, date): + milestone_date = date.fromisoformat(str(milestone_date)) + if milestone_date.year < 2026 or milestone_date.year > 2030: + raise ValidationError(f"milestone {milestone['id']} has out-of-range date: {milestone_date}") + + +def validate_report_template(path: Path) -> None: + tree = ET.parse(path) + root = tree.getroot() + content = root.find("content") + if content is None: + raise ValidationError("regulator report template missing element") + + section_ids = {section.attrib.get("id") for section in content.findall("section")} + missing = REQUIRED_REPORT_SECTION_IDS - section_ids + if missing: + raise ValidationError(f"regulator report template missing section ids: {sorted(missing)}") + + +def validate_manifest(artifacts_dir: Path, manifest: dict) -> None: + validate_required_keys(manifest, ["version", "generated_at", "files"], "manifest") + if manifest.get("version") != "1.1": + raise ValidationError("manifest version must be 1.1") + + try: + datetime.fromisoformat(manifest["generated_at"].replace("Z", "+00:00")) + except ValueError as exc: + raise ValidationError("manifest generated_at is not valid ISO-8601") from exc + + manifest_files = set(manifest["files"].keys()) + expected_manifest_files = load_manifest_targets() + missing = expected_manifest_files - manifest_files + extra = manifest_files - expected_manifest_files + if missing or extra: + raise ValidationError( + "manifest file coverage mismatch: " + f"missing={sorted(missing)} extra={sorted(extra)}" + ) + + for relative_path, expected_hash in manifest["files"].items(): + file_path = artifacts_dir / relative_path + if not file_path.exists(): + raise ValidationError(f"manifest references missing file: {relative_path}") + actual_hash = sha256_file(file_path) + if actual_hash != expected_hash: + raise ValidationError( + f"checksum mismatch for {relative_path}: expected {expected_hash}, got {actual_hash}" + ) + + +def validate_schema_documents() -> None: + targets_schema = load_json(ARTIFACTS_DIR / "schemas" / "manifest-targets-schema-v1.json") + manifest_schema = load_json(ARTIFACTS_DIR / "schemas" / "artifact-manifest-schema-v1.json") + check_all_schema = load_json(ARTIFACTS_DIR / "schemas" / "check-all-result-schema-v1.json") + + validate_required_keys(targets_schema, ["$schema", "$id", "properties", "required"], "manifest-targets schema") + validate_required_keys(manifest_schema, ["$schema", "$id", "properties", "required"], "artifact-manifest schema") + validate_required_keys(check_all_schema, ["$schema", "$id", "properties", "required"], "check-all-result schema") + + +def run_validation(include_manifest: bool = True) -> dict: + schema = load_json(ARTIFACTS_DIR / "annex-iv-dossier-schema-v1.json") + controls = load_json(ARTIFACTS_DIR / "control-catalog-v1.json") + example = load_json(ARTIFACTS_DIR / "examples" / "annex-iv-dossier-example.json") + + with (ARTIFACTS_DIR / "roadmap-2026-2030.yaml").open("r", encoding="utf-8") as f: + roadmap = yaml.safe_load(f) + + validate_schema_documents() + validate_annex_iv_example(schema, example) + validate_control_catalog(controls) + validate_roadmap(roadmap) + validate_report_template(ARTIFACTS_DIR / "regulator-report-template.xml") + + checks = { + "schema_documents": "pass", + "annex_iv_example": "pass", + "control_catalog": "pass", + "roadmap": "pass", + "report_template": "pass", + "manifest": "skipped", + } + + if include_manifest: + manifest = load_json(ARTIFACTS_DIR / "artifact-manifest-v1.json") + validate_manifest(ARTIFACTS_DIR, manifest) + checks["manifest"] = "pass" + + return checks + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Validate governance artifacts") + parser.add_argument("--skip-manifest", action="store_true", help="Skip checksum manifest validation") + parser.add_argument("--json", action="store_true", help="Emit machine-readable JSON output") + parser.add_argument("--quiet", action="store_true", help="Suppress success message output") + return parser.parse_args() + + +def run_cli(args: argparse.Namespace) -> int: + try: + checks = run_validation(include_manifest=not args.skip_manifest) + except ValidationError as exc: + if args.json: + print(json.dumps({"status": "error", "error": str(exc)}, indent=2, sort_keys=True)) + else: + print(f"Validation failed: {exc}", file=sys.stderr) + return 1 + + if args.json: + print(json.dumps({"status": "ok", "checks": checks}, indent=2, sort_keys=True)) + return 0 + + if not args.quiet: + print("All artifact validations passed.") + return 0 + + +def main() -> None: + args = parse_args() + raise SystemExit(run_cli(args)) + + +if __name__ == "__main__": + main() diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..06e4c7a --- /dev/null +++ b/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +pythonpath = . +testpaths = unit_tests diff --git a/unit_tests/test_artifacts_validation.py b/unit_tests/test_artifacts_validation.py new file mode 100644 index 0000000..938b25b --- /dev/null +++ b/unit_tests/test_artifacts_validation.py @@ -0,0 +1,446 @@ +import json +import subprocess +import sys +from argparse import Namespace + +import pytest + +from artifacts import build_manifest +from artifacts import check_all +from artifacts import validate_artifacts +from artifacts.build_manifest import build_manifest_payload, run_cli as run_manifest_cli +from artifacts.validate_artifacts import ( + ValidationError, + display_artifact_path, + load_manifest_targets, + run_cli, + validate_control_catalog, + validate_manifest, + validate_schema_documents, +) + + +def run_python(*args: str) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [sys.executable, *args], + capture_output=True, + text=True, + check=False, + ) + + +def test_artifacts_validation_script_runs(): + proc = run_python("artifacts/validate_artifacts.py") + assert proc.returncode == 0, proc.stderr + assert "All artifact validations passed." in proc.stdout + + +def test_validation_json_output_mode(): + proc = run_python("artifacts/validate_artifacts.py", "--json") + assert proc.returncode == 0, proc.stderr + payload = json.loads(proc.stdout) + assert payload["status"] == "ok" + assert payload["checks"]["manifest"] == "pass" + + +def test_validation_skip_manifest_mode(): + proc = run_python("artifacts/validate_artifacts.py", "--json", "--skip-manifest") + assert proc.returncode == 0, proc.stderr + payload = json.loads(proc.stdout) + assert payload["checks"]["manifest"] == "skipped" + + +def test_run_cli_json_error_mode(monkeypatch, capsys): + def fail_validation(include_manifest: bool = True) -> dict: + raise ValidationError("forced failure") + + monkeypatch.setattr(validate_artifacts, "run_validation", fail_validation) + rc = run_cli(Namespace(skip_manifest=False, json=True, quiet=False)) + captured = capsys.readouterr() + payload = json.loads(captured.out) + + assert rc == 1 + assert payload["status"] == "error" + assert "forced failure" in payload["error"] + + +def test_validation_json_mode_reports_missing_required_artifact(monkeypatch, tmp_path, capsys): + artifact_dir = tmp_path / "artifacts" + artifact_dir.mkdir() + monkeypatch.setattr(validate_artifacts, "ARTIFACTS_DIR", artifact_dir) + + rc = run_cli(Namespace(skip_manifest=False, json=True, quiet=False)) + payload = json.loads(capsys.readouterr().out) + + assert rc == 1 + assert payload["status"] == "error" + assert "required artifact file missing" in payload["error"] + assert "annex-iv-dossier-schema-v1.json" in payload["error"] + + +def test_validate_schema_documents_missing_file(monkeypatch, tmp_path): + artifact_dir = tmp_path / "artifacts" + artifact_dir.mkdir() + monkeypatch.setattr(validate_artifacts, "ARTIFACTS_DIR", artifact_dir) + with pytest.raises(ValidationError, match=r"required artifact file missing: schemas/manifest-targets-schema-v1.json"): + validate_schema_documents() + + +def test_display_artifact_path_is_relative_to_artifacts_dir(monkeypatch, tmp_path): + artifact_dir = tmp_path / "artifacts" + artifact_dir.mkdir() + nested = artifact_dir / "schemas" / "x.json" + + monkeypatch.setattr(validate_artifacts, "ARTIFACTS_DIR", artifact_dir) + assert display_artifact_path(nested) == "schemas/x.json" + + +def test_display_artifact_path_preserves_non_artifact_paths(monkeypatch, tmp_path): + artifact_dir = tmp_path / "artifacts" + artifact_dir.mkdir() + external = tmp_path / "outside.json" + + monkeypatch.setattr(validate_artifacts, "ARTIFACTS_DIR", artifact_dir) + assert display_artifact_path(external) == str(external) + + +def test_manifest_targets_contains_expected_blueprint_file(): + targets = load_manifest_targets() + assert "enterprise-civilizational-agi-asi-blueprint-2026-2030.md" in targets + + +def test_build_manifest_reproducible_timestamp(monkeypatch): + monkeypatch.setenv("SOURCE_DATE_EPOCH", "1767225600") + payload = build_manifest_payload() + assert payload["generated_at"] == "2026-01-01T00:00:00+00:00" + + +def test_build_manifest_check_mode_passes(): + proc = run_python("artifacts/build_manifest.py", "--check") + assert proc.returncode == 0, proc.stdout + proc.stderr + + +def test_manifest_targets_duplicate_entries_fail(monkeypatch, tmp_path): + bad_targets = { + "version": "1.0", + "files": ["a.json", "a.json"], + } + artifact_dir = tmp_path / "artifacts" + artifact_dir.mkdir() + (artifact_dir / "manifest-targets-v1.json").write_text(json.dumps(bad_targets), encoding="utf-8") + + monkeypatch.setattr(validate_artifacts, "ARTIFACTS_DIR", artifact_dir) + with pytest.raises(ValidationError, match="duplicate"): + validate_artifacts.load_manifest_targets() + + monkeypatch.setattr(build_manifest, "ARTIFACTS_DIR", artifact_dir) + with pytest.raises(ValueError, match="duplicate"): + build_manifest.load_manifest_targets() + + +def test_manifest_targets_invalid_version_fails(monkeypatch, tmp_path): + bad_targets = { + "version": "9.9", + "files": ["a.json"], + } + artifact_dir = tmp_path / "artifacts" + artifact_dir.mkdir() + (artifact_dir / "manifest-targets-v1.json").write_text(json.dumps(bad_targets), encoding="utf-8") + + monkeypatch.setattr(build_manifest, "ARTIFACTS_DIR", artifact_dir) + with pytest.raises(ValueError, match="version must be 1.0"): + build_manifest.load_manifest_targets() + + +def test_manifest_targets_missing_file_fails(monkeypatch, tmp_path): + artifact_dir = tmp_path / "artifacts" + artifact_dir.mkdir() + + monkeypatch.setattr(build_manifest, "ARTIFACTS_DIR", artifact_dir) + with pytest.raises(ValueError, match="is missing"): + build_manifest.load_manifest_targets() + + +def test_manifest_targets_invalid_json_fails(monkeypatch, tmp_path): + artifact_dir = tmp_path / "artifacts" + artifact_dir.mkdir() + (artifact_dir / "manifest-targets-v1.json").write_text("{not-json", encoding="utf-8") + + monkeypatch.setattr(build_manifest, "ARTIFACTS_DIR", artifact_dir) + with pytest.raises(ValueError, match="not valid JSON"): + build_manifest.load_manifest_targets() + + +def test_manifest_targets_unsafe_path_fails(monkeypatch, tmp_path): + bad_targets = { + "version": "1.0", + "files": ["../secrets.txt"], + } + artifact_dir = tmp_path / "artifacts" + artifact_dir.mkdir() + (artifact_dir / "manifest-targets-v1.json").write_text(json.dumps(bad_targets), encoding="utf-8") + + monkeypatch.setattr(build_manifest, "ARTIFACTS_DIR", artifact_dir) + with pytest.raises(ValueError, match="safe relative paths"): + build_manifest.load_manifest_targets() + + +def test_manifest_targets_windows_separators_fail(monkeypatch, tmp_path): + bad_targets = { + "version": "1.0", + "files": ["schemas\\manifest-targets-schema-v1.json"], + } + artifact_dir = tmp_path / "artifacts" + artifact_dir.mkdir() + (artifact_dir / "manifest-targets-v1.json").write_text(json.dumps(bad_targets), encoding="utf-8") + + monkeypatch.setattr(build_manifest, "ARTIFACTS_DIR", artifact_dir) + with pytest.raises(ValueError, match="POSIX-style separators"): + build_manifest.load_manifest_targets() + + +def test_manifest_targets_missing_files_key_fails(monkeypatch, tmp_path): + bad_targets = { + "version": "1.0", + } + artifact_dir = tmp_path / "artifacts" + artifact_dir.mkdir() + (artifact_dir / "manifest-targets-v1.json").write_text(json.dumps(bad_targets), encoding="utf-8") + + monkeypatch.setattr(build_manifest, "ARTIFACTS_DIR", artifact_dir) + with pytest.raises(ValueError, match="non-empty files list"): + build_manifest.load_manifest_targets() + + +def test_manifest_targets_referenced_file_must_exist(monkeypatch, tmp_path): + bad_targets = { + "version": "1.0", + "files": ["missing.json"], + } + artifact_dir = tmp_path / "artifacts" + artifact_dir.mkdir() + (artifact_dir / "manifest-targets-v1.json").write_text(json.dumps(bad_targets), encoding="utf-8") + + monkeypatch.setattr(build_manifest, "ARTIFACTS_DIR", artifact_dir) + with pytest.raises(ValueError, match="references missing file"): + build_manifest.load_manifest_targets() + + +def test_build_manifest_check_mode_json_output(): + proc = run_python("artifacts/build_manifest.py", "--check", "--json") + assert proc.returncode == 0, proc.stdout + proc.stderr + payload = json.loads(proc.stdout) + assert payload["status"] == "ok" + + +def test_build_manifest_check_mode_invalid_existing_manifest_json(monkeypatch, tmp_path, capsys): + artifact_dir = tmp_path / "artifacts" + artifact_dir.mkdir() + (artifact_dir / "manifest-targets-v1.json").write_text( + json.dumps({"version": "1.0", "files": ["a.json"]}), + encoding="utf-8", + ) + (artifact_dir / "a.json").write_text("{}", encoding="utf-8") + (artifact_dir / "artifact-manifest-v1.json").write_text("{not-json", encoding="utf-8") + monkeypatch.setattr(build_manifest, "ARTIFACTS_DIR", artifact_dir) + + rc = run_manifest_cli(Namespace(check=True, json=True)) + payload = json.loads(capsys.readouterr().out) + + assert rc == 1 + assert payload["status"] == "error" + assert "invalid JSON" in payload["message"] + + +def test_build_manifest_check_mode_invalid_existing_manifest_structure(monkeypatch, tmp_path, capsys): + artifact_dir = tmp_path / "artifacts" + artifact_dir.mkdir() + (artifact_dir / "manifest-targets-v1.json").write_text( + json.dumps({"version": "1.0", "files": ["a.json"]}), + encoding="utf-8", + ) + (artifact_dir / "a.json").write_text("{}", encoding="utf-8") + (artifact_dir / "artifact-manifest-v1.json").write_text('["not-an-object"]', encoding="utf-8") + monkeypatch.setattr(build_manifest, "ARTIFACTS_DIR", artifact_dir) + + rc = run_manifest_cli(Namespace(check=True, json=True)) + payload = json.loads(capsys.readouterr().out) + + assert rc == 1 + assert payload["status"] == "error" + assert "invalid structure" in payload["message"] + + +def test_build_manifest_error_json_mode(monkeypatch, capsys): + def fail_targets() -> list[str]: + raise ValueError("forced manifest failure") + + monkeypatch.setattr(build_manifest, "load_manifest_targets", fail_targets) + rc = run_manifest_cli(Namespace(check=False, json=True)) + payload = json.loads(capsys.readouterr().out) + + assert rc == 1 + assert payload["status"] == "error" + assert "forced manifest failure" in payload["message"] + + +def test_build_manifest_error_plain_mode(monkeypatch, capsys): + def fail_targets() -> list[str]: + raise ValueError("forced manifest failure plain") + + monkeypatch.setattr(build_manifest, "load_manifest_targets", fail_targets) + rc = run_manifest_cli(Namespace(check=False, json=False)) + out = capsys.readouterr().out + + assert rc == 1 + assert "forced manifest failure plain" in out + + +def test_check_all_json_mode(): + proc = run_python("artifacts/check_all.py", "--json") + assert proc.returncode == 0, proc.stdout + proc.stderr + payload = json.loads(proc.stdout) + assert payload["schema_version"] == "1.0" + assert payload["status"] == "ok" + assert payload["manifest_fresh"] is True + assert payload["validation_ok"] is True + assert payload["errors"] == [] + assert payload["checked_at"].endswith("+00:00") + + +def test_check_all_detects_manifest_staleness(monkeypatch): + def fake_build_manifest_payload() -> dict: + return {"version": "1.1", "files": {"x": "y"}} + + monkeypatch.setattr(check_all, "build_manifest_payload", fake_build_manifest_payload) + result = check_all.run_all() + assert result["status"] == "error" + assert "manifest_not_fresh" in result["errors"] + + +def test_check_all_plain_mode_output(): + proc = run_python("artifacts/check_all.py") + assert proc.returncode == 0, proc.stdout + proc.stderr + assert "All checks passed." in proc.stdout + + +def test_check_all_error_json_mode(monkeypatch, capsys): + def fail_run_all() -> dict: + raise ValidationError("forced check_all failure") + + monkeypatch.setattr(check_all, "run_all", fail_run_all) + rc = check_all.run_cli(Namespace(json=True)) + payload = json.loads(capsys.readouterr().out) + + assert rc == 1 + assert payload["status"] == "error" + assert "forced check_all failure" in payload["error"] + + +def test_check_all_error_json_mode_for_value_error(monkeypatch, capsys): + def fail_run_all() -> dict: + raise ValueError("forced value error") + + monkeypatch.setattr(check_all, "run_all", fail_run_all) + rc = check_all.run_cli(Namespace(json=True)) + payload = json.loads(capsys.readouterr().out) + + assert rc == 1 + assert payload["status"] == "error" + assert "forced value error" in payload["error"] + + +def test_check_all_error_json_mode_for_os_error(monkeypatch, capsys): + def fail_run_all() -> dict: + raise OSError("forced os error") + + monkeypatch.setattr(check_all, "run_all", fail_run_all) + rc = check_all.run_cli(Namespace(json=True)) + payload = json.loads(capsys.readouterr().out) + + assert rc == 1 + assert payload["status"] == "error" + assert "forced os error" in payload["error"] + + +def test_check_all_error_json_mode_for_key_error(monkeypatch, capsys): + def fail_run_all() -> dict: + raise KeyError("forced key error") + + monkeypatch.setattr(check_all, "run_all", fail_run_all) + rc = check_all.run_cli(Namespace(json=True)) + payload = json.loads(capsys.readouterr().out) + + assert rc == 1 + assert payload["status"] == "error" + assert "forced key error" in payload["error"] + + +def test_check_all_result_schema_file_exists_and_has_required_keys(): + with open("artifacts/schemas/check-all-result-schema-v1.json", "r", encoding="utf-8") as f: + schema = json.load(f) + + assert schema["type"] == "object" + assert "required" in schema + assert "status" in schema["properties"] + + +def test_check_all_error_plain_mode(monkeypatch, capsys): + def fail_run_all() -> dict: + raise ValidationError("forced check_all plain failure") + + monkeypatch.setattr(check_all, "run_all", fail_run_all) + rc = check_all.run_cli(Namespace(json=False)) + captured = capsys.readouterr() + + assert rc == 1 + assert "forced check_all plain failure" in captured.err + + +def test_control_catalog_mapping_references_known_ids(): + with open("artifacts/control-catalog-v1.json", "r", encoding="utf-8") as f: + data = json.load(f) + + bad = json.loads(json.dumps(data)) + bad["mappings"]["eu_ai_act_annex_iv"].append("unknown.control.id") + + with pytest.raises(ValidationError, match="unknown control id"): + validate_control_catalog(bad) + + +def test_manifest_check_detects_missing_file(tmp_path): + manifest = { + "version": "1.1", + "generated_at": "2026-04-25T00:00:00+00:00", + "files": {"missing.json": "abc"}, + } + with pytest.raises(ValidationError, match="coverage mismatch"): + validate_manifest(tmp_path, manifest) + + +def test_manifest_invalid_timestamp(tmp_path): + manifest = { + "version": "1.1", + "generated_at": "not-a-time", + "files": {}, + } + with pytest.raises(ValidationError, match="ISO-8601"): + validate_manifest(tmp_path, manifest) + + +def test_manifest_coverage_detects_extra_file(tmp_path): + manifest = { + "version": "1.1", + "generated_at": "2026-04-25T00:00:00+00:00", + "files": { + "annex-iv-dossier-schema-v1.json": "abc", + "control-catalog-v1.json": "abc", + "roadmap-2026-2030.yaml": "abc", + "regulator-report-template.xml": "abc", + "enterprise-civilizational-agi-asi-blueprint-2026-2030.md": "abc", + "examples/annex-iv-dossier-example.json": "abc", + "extra.txt": "abc", + }, + } + with pytest.raises(ValidationError, match="coverage mismatch"): + validate_manifest(tmp_path, manifest)