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
32 changes: 18 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -242,20 +242,22 @@ contracts-report: ## Contract-to-verification coverage table (scanner rules, tes
@python3 tools/contract_report.py

agent-check: ## Agent completion gate: CI's blocking set + audit harness, run locally
@echo "── [1/7] Action references ──"
@echo "── [1/8] Action references ──"
@python3 tools/check_action_refs.py
@echo "── [2/7] Contract files present + wired ──"
@echo "── [2/8] Contract files present + wired ──"
@python3 tools/verify_contracts.py
@echo "── [3/7] Contract violation scan ──"
@echo "── [3/8] Contract violation scan ──"
@python3 tools/contract_scanner.py
@echo "── [4/7] Lint + format ──"
@echo "── [4/8] Payload contract compiler validator ──"
@PYTHONPATH="$${PYTHONPATH}:." python3 tools/payload_contract_compiler.py --stdout-only
@echo "── [5/8] Lint + format ──"
@ruff check .
@ruff format --check .
@echo "── [5/7] Type check ──"
@echo "── [6/8] Type check ──"
@mypy engine/ --config-file=pyproject.toml --ignore-missing-imports --exclude chassis
@echo "── [6/7] Tests ──"
@echo "── [7/8] Tests ──"
@PYTHONPATH="$${PYTHONPATH}:." python3 -m pytest tests/ --tb=short -q
@echo "── [7/7] Contract verification coverage ──"
@echo "── [8/8] Contract verification coverage ──"
@python3 tools/contract_report.py
@echo ""
@echo "── Audit harness ──"
Expand All @@ -264,20 +266,22 @@ agent-check: ## Agent completion gate: CI's blocking set + audit harness, run lo
@echo "✅ agent-check passed — CI's blocking gates should be green"

agent-check-unit: ## Local agent gate (skips Docker integration/perf): CI's blocking set + audit harness, run locally
@echo "── [1/7] Action references ──"
@echo "── [1/8] Action references ──"
@python3 tools/check_action_refs.py
@echo "── [2/7] Contract files present + wired ──"
@echo "── [2/8] Contract files present + wired ──"
@python3 tools/verify_contracts.py
@echo "── [3/7] Contract violation scan ──"
@echo "── [3/8] Contract violation scan ──"
@python3 tools/contract_scanner.py
@echo "── [4/7] Lint + format ──"
@echo "── [4/8] Payload contract compiler validator ──"
@PYTHONPATH="$${PYTHONPATH}:." python3 tools/payload_contract_compiler.py --stdout-only
@echo "── [5/8] Lint + format ──"
@ruff check .
@ruff format --check .
@echo "── [5/7] Type check ──"
@echo "── [6/8] Type check ──"
@mypy engine/ --config-file=pyproject.toml --ignore-missing-imports --exclude chassis
@echo "── [6/7] Tests ──"
@echo "── [7/8] Tests ──"
@PYTHONPATH="$${PYTHONPATH}:." python3 -m pytest tests/ -m "unit" --tb=short -q
@echo "── [7/7] Contract verification coverage ──"
@echo "── [8/8] Contract verification coverage ──"
@python3 tools/contract_report.py
@echo ""
@echo "── Audit harness ──"
Expand Down
9 changes: 9 additions & 0 deletions contracts/payloads/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,12 @@ Schemas:
- canonical-projection + outcome-feedback (TASK-061)

Native models: `engine.models.payloads`. See ADR-106 / ADR-108.

Compiler validator (TASK-034 / ADR-109):

```bash
PYTHONPATH=. python3 tools/payload_contract_compiler.py --stdout-only
```

Validates live payload schemas + fixtures against native models and confirms
`DomainPackLoader` remains the sole PlasticOS domain authority.
24 changes: 24 additions & 0 deletions docs/adr/ADR-109-ceg-contract-compiler-validator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# ADR-109: CEG payload contract compiler validator

**Status:** Accepted
**Task:** TASK-034
**Date:** 2026-08-02

## Decision

Add `tools/payload_contract_compiler.py` as the CEG contract compiler validator.

It:

- Draft-2020-12 checks live `contracts/payloads/*.schema.yaml`
- Validates positive/negative fixtures against native `engine.models.payloads` models
- Confirms `DomainPackLoader.load_domain("plasticos")` remains the sole domain authority
- Emits a deterministic digest report under `artifacts/`

It does **not** create a parallel domain cartridge, alternate transport envelope, or tensor runtime (pack ADR-107 / GATE-018 intent).

## Consequences

- `make agent-check-unit` / scoped validation runs the compiler validator
- TASK-060 may compare CEG/EIE compiler digests for parity
- Residual: relative `./common.schema.yaml` `$ref` in match/improvement schemas remain owner-local; portable URN rewrite belongs to cross-repo registry tooling (TASK-043)
64 changes: 64 additions & 0 deletions tests/unit/test_payload_contract_compiler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""TASK-034: payload contract compiler validator."""

from __future__ import annotations

import importlib.util
import json
import sys
from pathlib import Path

import pytest

pytestmark = pytest.mark.unit

ROOT = Path(__file__).resolve().parents[2]
COMPILER = ROOT / "tools" / "payload_contract_compiler.py"


def _load_compiler():
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
spec = importlib.util.spec_from_file_location("payload_contract_compiler", COMPILER)
assert spec is not None
assert spec.loader is not None
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod


def test_compiler_report_passes(tmp_path: Path) -> None:
mod = _load_compiler()
report_path = tmp_path / "report.json"
report = mod.compile_report(write_path=report_path)
assert report["result"] == "PASS", json.dumps(report, indent=2)[:2000]
assert report_path.is_file()
assert report["domain_authority"]["loader"] == "DomainPackLoader"
assert report["domain_authority"]["match_directions"]
assert all(s["status"] == "PASS" for s in report["schemas"])
assert all(p["status"] == "PASS" for p in report["positives"])
assert all(n["status"] == "PASS" for n in report["negatives"])


def test_compiler_cli_exit_zero() -> None:
mod = _load_compiler()
report_path = ROOT / "artifacts" / "payload-contract-compiler-cli-test.json"
try:
assert mod.main(["--report", str(report_path)]) == 0
data = json.loads(report_path.read_text(encoding="utf-8"))
assert data["result"] == "PASS"
finally:
if report_path.exists():
report_path.unlink()


def test_compiler_cli_rejects_escape_path(tmp_path: Path) -> None:
mod = _load_compiler()
with pytest.raises(ValueError, match="repo root"):
mod.main(["--report", str(tmp_path / "escape.json")])


def test_payload_schemas_exist() -> None:
payloads = ROOT / "contracts" / "payloads"
assert (payloads / "common.schema.yaml").is_file()
assert (payloads / "match-request.schema.yaml").is_file()
assert (payloads / "canonical-projection.schema.yaml").is_file()
Loading
Loading