diff --git a/Makefile b/Makefile index 9e6fb2c..dd6c1f9 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: validate validate-json validate-yaml validate-quadlet validate-render validate-evidence validate-governance validate-activation validate-supply-chain validate-release-bundle validate-package validate-cli validate-formula doctor probe +.PHONY: validate validate-json validate-yaml validate-quadlet validate-render validate-evidence validate-governance validate-activation validate-supply-chain validate-release-bundle validate-sourceos-projections validate-package validate-cli validate-formula doctor probe PYTHON ?= python3 RUBY ?= ruby @@ -20,7 +20,7 @@ DECIDED_AT := 2026-05-04T12:51:00Z PYCLI := PYTHONPATH=src $(PYTHON) -m agent_machine.cli PYMOD := PYTHONPATH=src $(PYTHON) -m -validate: validate-json validate-yaml validate-quadlet validate-render validate-evidence validate-governance validate-activation validate-supply-chain validate-release-bundle validate-package validate-cli validate-formula +validate: validate-json validate-yaml validate-quadlet validate-render validate-evidence validate-governance validate-activation validate-supply-chain validate-release-bundle validate-sourceos-projections validate-package validate-cli validate-formula validate-json: $(PYTHON) scripts/validate-json.py @@ -66,6 +66,9 @@ validate-release-bundle: $(PYTHON) scripts/validate-release-bundle.py $(PYTHON) scripts/generate-release-evidence.py --pretty >/tmp/agent-machine-release-evidence-bundle.json +validate-sourceos-projections: + $(PYTHON) scripts/validate-sourceos-projection-fixtures.py + validate-package: $(PYTHON) scripts/validate-package.py diff --git a/docs/integration/sourceos-spec-projection-fixtures.md b/docs/integration/sourceos-spec-projection-fixtures.md new file mode 100644 index 0000000..5cf8aea --- /dev/null +++ b/docs/integration/sourceos-spec-projection-fixtures.md @@ -0,0 +1,42 @@ +# SourceOS Spec Projection Fixtures + +Status: integration fixtures for pending SourceOS typed-contract projections + +## Purpose + +This directory records Agent Machine examples that conform to the SourceOS/SociOS projection contracts proposed in `SourceOS-Linux/sourceos-spec` PR #89 and its successor PRs. + +The fixtures intentionally live under `fixtures/sourceos-spec/` rather than `examples/` because Agent Machine's canonical `examples/` directory is validated through repo-local `kind`-based contracts. The SourceOS projection contracts use the broader SourceOS `type` discriminator and must not be forced into the repo-local example validator until the projection schemas are promoted and synchronized. + +## Fixtures + +| Fixture | SourceOS projection type | Purpose | +| --- | --- | --- | +| `fixtures/sourceos-spec/sourceosmodelcarryref.json` | `SourceOSModelCarryRef` | Approved on-device reference to a governed model/service profile carried by SourceOS without mutable model state. | +| `fixtures/sourceos-spec/inferenceprovider.json` | `InferenceProvider` | Backend-neutral provider capability shape for local/cluster/remote governed inference. | +| `fixtures/sourceos-spec/modelresidency.json` | `ModelResidency` | Observed model availability and cache/load state on an Agent Machine. | +| `fixtures/sourceos-spec/placementfact.json` | `PlacementFact` | Machine-local scheduling and policy fact for runtime placement. | +| `fixtures/sourceos-spec/agentmachinereceipt.json` | `AgentMachineReceipt` | Runtime evidence emitted after probe, placement, execution, cache reuse, model load/unload, or policy-mediated side effects. | + +## Validation + +Run: + +```bash +make validate-sourceos-projections +``` + +or as part of the full gate: + +```bash +make validate +``` + +The local validator checks structural fixture shape, expected `type` discriminators, required projection fields, and `urn:srcos:` identity posture. Full JSON Schema validation belongs in `SourceOS-Linux/sourceos-spec` once the projection schemas merge. + +## Boundary rules + +1. These fixtures are not runtime activation authority. +2. These fixtures do not start providers, download models, mutate caches, or grant tool use. +3. Agent Machine's repo-local contracts remain authoritative for bootstrap runtime validation until the SourceOS projection schemas are promoted. +4. SourceOS projection fixtures are compatibility artifacts for the cross-repo Foundry path: `functional-model-surfaces → sourceos-spec → sourceos-model-carry → agent-machine → agentplane`. diff --git a/fixtures/sourceos-spec/agentmachinereceipt.json b/fixtures/sourceos-spec/agentmachinereceipt.json new file mode 100644 index 0000000..c04c403 --- /dev/null +++ b/fixtures/sourceos-spec/agentmachinereceipt.json @@ -0,0 +1,22 @@ +{ + "id": "urn:srcos:agent-machine-receipt:m2-asahi-observation-20260504t170000z", + "type": "AgentMachineReceipt", + "specVersion": "2.1.0", + "machineRef": "urn:srcos:agent-machine:m2-asahi-local", + "receiptClass": "probe", + "issuedAt": "2026-05-04T17:00:10Z", + "taskRef": "urn:prophet:run-capsule:local-observation-20260504", + "agentPodRef": "urn:srcos:agent-pod:local-observation-0001", + "placementFactRefs": ["urn:srcos:placement-fact:m2-asahi-local-20260504t170000z"], + "modelResidencyRefs": ["urn:srcos:model-residency:m2-local-llama32-3b-20260504t170000z"], + "inferenceProviderRefs": ["urn:srcos:inference-provider:llama-cpp-cpu-arm64-local"], + "policyDecisionRef": "urn:srcos:decision:runtime-observation-20260504", + "verdict": "observed", + "metrics": { + "durationMs": 142.0, + "providerCount": 1, + "residentModelCount": 1 + }, + "evidenceHash": "sha256:4444444444444444444444444444444444444444444444444444444444444444", + "evidenceRefs": ["sha256:5555555555555555555555555555555555555555555555555555555555555555"] +} diff --git a/fixtures/sourceos-spec/inferenceprovider.json b/fixtures/sourceos-spec/inferenceprovider.json new file mode 100644 index 0000000..867162a --- /dev/null +++ b/fixtures/sourceos-spec/inferenceprovider.json @@ -0,0 +1,13 @@ +{ + "id": "urn:srcos:inference-provider:llama-cpp-cpu-arm64-local", + "type": "InferenceProvider", + "specVersion": "2.1.0", + "providerClass": "llama.cpp", + "endpointMode": "local-http", + "executionProfile": "cpu-arm64", + "trustPosture": "local-sandboxed", + "supportedModalities": ["text", "code", "embedding"], + "openAICompatible": true, + "requiresNetwork": false, + "evidenceRefs": ["sha256:1111111111111111111111111111111111111111111111111111111111111111"] +} diff --git a/fixtures/sourceos-spec/modelresidency.json b/fixtures/sourceos-spec/modelresidency.json new file mode 100644 index 0000000..8844280 --- /dev/null +++ b/fixtures/sourceos-spec/modelresidency.json @@ -0,0 +1,14 @@ +{ + "id": "urn:srcos:model-residency:m2-local-llama32-3b-20260504t170000z", + "type": "ModelResidency", + "specVersion": "2.1.0", + "machineRef": "urn:srcos:agent-machine:m2-asahi-local", + "modelCarryRef": "urn:srcos:model-carry-ref:local-llama32-3b-office-assist", + "providerRef": "urn:srcos:inference-provider:llama-cpp-cpu-arm64-local", + "residencyState": "cached", + "quantization": "q4_k_m", + "bytesOnDisk": 2147483648, + "cacheTier": "nvme", + "observedAt": "2026-05-04T17:00:00Z", + "evidenceRefs": ["sha256:2222222222222222222222222222222222222222222222222222222222222222"] +} diff --git a/fixtures/sourceos-spec/placementfact.json b/fixtures/sourceos-spec/placementfact.json new file mode 100644 index 0000000..5dda4a0 --- /dev/null +++ b/fixtures/sourceos-spec/placementfact.json @@ -0,0 +1,23 @@ +{ + "id": "urn:srcos:placement-fact:m2-asahi-local-20260504t170000z", + "type": "PlacementFact", + "specVersion": "2.1.0", + "machineRef": "urn:srcos:agent-machine:m2-asahi-local", + "observedAt": "2026-05-04T17:00:00Z", + "hardware": { + "arch": "aarch64", + "accelerators": ["cpu", "vulkan-probe"], + "memoryBytes": 25769803776, + "storageBytesAvailable": 107374182400 + }, + "isolation": { + "runtime": "rootless-podman", + "networkDefault": "policy-gated", + "filesystemDefault": "workspace-scoped-rw" + }, + "trustPosture": "sandboxed-local", + "modelResidencyRefs": ["urn:srcos:model-residency:m2-local-llama32-3b-20260504t170000z"], + "providerRefs": ["urn:srcos:inference-provider:llama-cpp-cpu-arm64-local"], + "cacheNotes": ["NVMe model cache available; KV cache reuse requires identity and policy match."], + "evidenceRefs": ["sha256:3333333333333333333333333333333333333333333333333333333333333333"] +} diff --git a/fixtures/sourceos-spec/sourceosmodelcarryref.json b/fixtures/sourceos-spec/sourceosmodelcarryref.json new file mode 100644 index 0000000..d5cd5d7 --- /dev/null +++ b/fixtures/sourceos-spec/sourceosmodelcarryref.json @@ -0,0 +1,15 @@ +{ + "id": "urn:srcos:model-carry-ref:local-llama32-3b-office-assist", + "type": "SourceOSModelCarryRef", + "specVersion": "2.1.0", + "modelRef": "urn:prophet:model:llama32-3b-local-office-assist", + "governanceRef": "urn:prophet:model-release-decision:local-llama32-3b-office-assist-20260504", + "routerProfileRef": "urn:srcos:model-router-profile:local-first-office-assist", + "releaseSetRefs": ["urn:srcos:release-set:sourceos-workstation-m2-20260504"], + "launchProfileRefs": ["urn:srcos:model-launch-profile:llama-cpp-local-office-assist"], + "fallbackRefs": [], + "carryPolicy": "download-on-demand", + "cachePolicy": "weights-cache-allowed", + "mutableModelState": false, + "evidenceRefs": ["sha256:2e7f1c7f0a6a4bbd9ef000000000000000000000000000000000000000000"] +} diff --git a/scripts/validate-sourceos-projection-fixtures.py b/scripts/validate-sourceos-projection-fixtures.py new file mode 100644 index 0000000..2479904 --- /dev/null +++ b/scripts/validate-sourceos-projection-fixtures.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +"""Validate SourceOS projection fixture shape without depending on sourceos-spec checkout.""" + +from __future__ import annotations + +import json +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +FIXTURE_DIR = ROOT / "fixtures" / "sourceos-spec" + +EXPECTED = { + "sourceosmodelcarryref.json": "SourceOSModelCarryRef", + "inferenceprovider.json": "InferenceProvider", + "modelresidency.json": "ModelResidency", + "placementfact.json": "PlacementFact", + "agentmachinereceipt.json": "AgentMachineReceipt", +} + +REQUIRED = { + "SourceOSModelCarryRef": ["id", "type", "specVersion", "modelRef", "governanceRef", "routerProfileRef", "mutableModelState"], + "InferenceProvider": ["id", "type", "specVersion", "providerClass", "endpointMode", "executionProfile", "trustPosture"], + "ModelResidency": ["id", "type", "specVersion", "machineRef", "modelCarryRef", "providerRef", "residencyState", "observedAt"], + "PlacementFact": ["id", "type", "specVersion", "machineRef", "observedAt", "hardware", "isolation", "trustPosture"], + "AgentMachineReceipt": ["id", "type", "specVersion", "machineRef", "receiptClass", "issuedAt", "placementFactRefs", "policyDecisionRef", "verdict"], +} + + +def load(path: Path) -> dict: + with path.open("r", encoding="utf-8") as handle: + data = json.load(handle) + if not isinstance(data, dict): + raise AssertionError(f"{path}: root must be a JSON object") + return data + + +def main() -> int: + for filename, expected_type in EXPECTED.items(): + path = FIXTURE_DIR / filename + if not path.exists(): + raise AssertionError(f"missing fixture: {path.relative_to(ROOT)}") + data = load(path) + actual_type = data.get("type") + if actual_type != expected_type: + raise AssertionError(f"{path}: expected type {expected_type!r}, got {actual_type!r}") + for field in REQUIRED[expected_type]: + if field not in data: + raise AssertionError(f"{path}: missing required projection field {field!r}") + if not str(data.get("id", "")).startswith("urn:srcos:"): + raise AssertionError(f"{path}: id must use urn:srcos prefix") + print(f"VALID SourceOS projection fixture {path.relative_to(ROOT)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())