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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/artifact-validation.yml
Original file line number Diff line number Diff line change
@@ -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
21 changes: 21 additions & 0 deletions artifacts/Makefile
Original file line number Diff line number Diff line change
@@ -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
115 changes: 115 additions & 0 deletions artifacts/README.md
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions artifacts/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Governance artifacts tooling package."""
55 changes: 55 additions & 0 deletions artifacts/annex-iv-dossier-schema-v1.json
Original file line number Diff line number Diff line change
@@ -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"}
}
}
}
}
}
12 changes: 12 additions & 0 deletions artifacts/artifact-manifest-v1.json
Original file line number Diff line number Diff line change
@@ -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"
}
87 changes: 87 additions & 0 deletions artifacts/build_manifest.py
Original file line number Diff line number Diff line change
@@ -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"))

Check warning on line 60 in artifacts/build_manifest.py

View check run for this annotation

Precaution / Precaution Basic

PY009: Deserialization of Untrusted Data

Potential unsafe usage of 'json.loads' that can allow instantiation of arbitrary objects.
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()
Loading
Loading