From 3e7e7af678dd9b32e61238f691a0b633e7c19597 Mon Sep 17 00:00:00 2001 From: agent-856f80a92b1141b4 Date: Sat, 25 Apr 2026 20:38:32 +0200 Subject: [PATCH] build(agent): weasel-1#856f80 iteration --- examples/contracts/example_contract.json | 14 +++++ pyproject.toml | 5 +- schemas/safety_contract_schema.json | 70 ++++++++++++++++++++++++ tests/test_schema_validation.py | 34 ++++++++++++ 4 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 examples/contracts/example_contract.json create mode 100644 schemas/safety_contract_schema.json create mode 100644 tests/test_schema_validation.py diff --git a/examples/contracts/example_contract.json b/examples/contracts/example_contract.json new file mode 100644 index 0000000..9512a87 --- /dev/null +++ b/examples/contracts/example_contract.json @@ -0,0 +1,14 @@ +{ + "contract_id": "example-contract-001", + "version": "1.0", + "local_capabilities": [ + { "name": "Planner", "capabilities": ["plan", "optimize"], "metadata": {"vendor": "example"} } + ], + "pre_conditions": { "description": "start in safe state", "conditions": {"battery": ">=50"} }, + "post_conditions": { "description": "end in safe state", "conditions": {"docked": true} }, + "budgets": { "cpu_cores": 2.0, "memory_gb": 4.0, "energy_wh": 200.0, "time_seconds": 3600 }, + "data_policy": { "policy_id": "policy-example", "allowed_data": ["telemetry"], "constraints": {} }, + "scenarios": [ + { "scenario_id": "sc-01", "deterministic_replay": true, "seed": 42, "description": "sensor dropout mid-mission", "sensors": {"camera": {"fps": 10}}, "environment": {"dust": "high"} } + ] +} diff --git a/pyproject.toml b/pyproject.toml index 80ae427..c2803c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,10 @@ version = "0.1.0" description = "Modular framework for certification, benchmarking and governance of onboard AI in space robotics. MVP scaffold." readme = "README.md" requires-python = ">=3.8" -license = { text = "MIT" } +license = "MIT" +dependencies = [ + "jsonschema>=4.0.0" +] authors = [ { name = "OpenCode Collaboration" } ] [project.urls] diff --git a/schemas/safety_contract_schema.json b/schemas/safety_contract_schema.json new file mode 100644 index 0000000..73a4b08 --- /dev/null +++ b/schemas/safety_contract_schema.json @@ -0,0 +1,70 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://example.org/schemas/spacesafeml/safety_contract.json", + "title": "SafetyContract", + "type": "object", + "required": ["contract_id", "local_capabilities"], + "properties": { + "contract_id": { "type": "string" }, + "version": { "type": "string", "default": "1.0" }, + "local_capabilities": { + "type": "array", + "items": { + "type": "object", + "required": ["name"], + "properties": { + "name": { "type": "string" }, + "capabilities": { "type": "array", "items": { "type": "string" } }, + "metadata": { "type": "object" } + } + } + }, + "pre_conditions": { + "type": "object", + "properties": { + "description": { "type": "string" }, + "conditions": { "type": "object" } + } + }, + "post_conditions": { + "type": "object", + "properties": { + "description": { "type": "string" }, + "conditions": { "type": "object" } + } + }, + "budgets": { + "type": "object", + "properties": { + "cpu_cores": { "type": "number" }, + "memory_gb": { "type": "number" }, + "energy_wh": { "type": "number" }, + "time_seconds": { "type": "number" } + } + }, + "data_policy": { + "type": "object", + "properties": { + "policy_id": { "type": "string" }, + "allowed_data": { "type": "array", "items": { "type": "string" } }, + "constraints": { "type": "object" } + } + }, + "scenarios": { + "type": "array", + "items": { + "type": "object", + "required": ["scenario_id"], + "properties": { + "scenario_id": { "type": "string" }, + "sensors": { "type": "object" }, + "environment": { "type": "object" }, + "deterministic_replay": { "type": "boolean" }, + "seed": { "type": ["integer", "null"] }, + "description": { "type": "string" } + } + } + } + }, + "additionalProperties": false +} diff --git a/tests/test_schema_validation.py b/tests/test_schema_validation.py new file mode 100644 index 0000000..947c334 --- /dev/null +++ b/tests/test_schema_validation.py @@ -0,0 +1,34 @@ +import json +import os + +import pytest + +# jsonschema is an optional dependency used for schema validation tests. +# Skip these tests if jsonschema is not installed in the current test environment. +jsonschema = pytest.importorskip("jsonschema", reason="jsonschema not available; skipping schema validation tests") +from jsonschema import validate, ValidationError + + +def load_json(path: str): + with open(path, "r", encoding="utf-8") as f: + return json.load(f) + + +def test_example_contract_validates_against_schema(): + schema = load_json(os.path.join(os.path.dirname(__file__), "..", "schemas", "safety_contract_schema.json")) + example = load_json(os.path.join(os.path.dirname(__file__), "..", "examples", "contracts", "example_contract.json")) + + # Should not raise + validate(instance=example, schema=schema) + + +def test_schema_rejects_additional_properties(): + schema = load_json(os.path.join(os.path.dirname(__file__), "..", "schemas", "safety_contract_schema.json")) + example = load_json(os.path.join(os.path.dirname(__file__), "..", "examples", "contracts", "example_contract.json")) + + # Inject an unexpected property at top-level which is forbidden by additionalProperties=false + example_with_extra = dict(example) + example_with_extra["unexpected"] = "value" + + with pytest.raises(ValidationError): + validate(instance=example_with_extra, schema=schema)