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
8 changes: 4 additions & 4 deletions fixtures/fixtures.lock.json
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
{
"version": "1",
"updated": "2026-06-25",
"updated": "2026-07-27",
"fixtures": [
{
"fixture": "fixtures/golden/compliance-direct-fail.v1.jsonl",
"schema_version": "compliance-eval-record/v1",
"sha256": "654c8ed5f92ee12023c3449ba75091ac815373f9f7d6d27a8ae9daca47ec752b",
"sha256": "b35c9802381be3ff95111bf6f4e56f0cb6fbd00a0bfcd2563bca573869bb90bf",
"n_records": 1,
"generated_by": "trace-pipeline/scripts",
"generated_at": "2026-06-25"
},
{
"fixture": "fixtures/golden/compliance-direct-pass.v1.jsonl",
"schema_version": "compliance-eval-record/v1",
"sha256": "a80a9bc0face93d7880a02584663825929c5529968497b45fafc17dc1eeaad3b",
"sha256": "3112665d1862cfe238f8c740a1fc1efb14d00254e83fa22762933901a357a828",
"n_records": 1,
"generated_by": "trace-pipeline/scripts",
"generated_at": "2026-06-25"
},
{
"fixture": "fixtures/golden/compliance-repair-pass.v1.jsonl",
"schema_version": "compliance-eval-record/v1",
"sha256": "20d5d1b5ad3875b5b6a74edc36c2d13782fafcceeba5931307c1a9a75a2aab0f",
"sha256": "c9998885aa3f177d71a1808d79896145ecd6b0223503f95c148d550b927932b9",
"n_records": 1,
"generated_by": "trace-pipeline/scripts",
"generated_at": "2026-06-25"
Expand Down
2 changes: 1 addition & 1 deletion fixtures/golden/compliance-direct-fail.v1.jsonl
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"schema_version":"compliance-eval-record/v1","task_id":"golden-task-01","task_spec_hash":"1ce85aeef714574b6692d80b006bd8b7cf2d514a71cac6797ef4c7cd832a4171","model":"qwen2.5-1.5b","mode":"direct","violations":[{"constraint_id":"c_title","level":"hard","category":"format","hint":"response is missing a title wrapped in <<...>>","evidence_span":null,"detected_at":"post_decode"}],"repair_trace":[],"repair_rounds":0,"final_pass":false,"token_cost":{"prompt":42,"generation":18,"repair":null},"latency_ms":221.0,"artifact":"Water evaporates and forms clouds. It then falls as precipitation.","error":null}
{"schema_version":"compliance-eval-record/v1","task_id":"golden-task-01","task_spec_hash":"1ce85aeef714574b6692d80b006bd8b7cf2d514a71cac6797ef4c7cd832a4171","model":"qwen2.5-1.5b","mode":"direct","violations":[{"constraint_id":"c_title","level":"hard","category":"format","hint":"response is missing a title wrapped in <<...>>","detected_at":"post_decode"}],"repair_trace":[],"repair_rounds":0,"final_pass":false,"token_cost":{"prompt":42,"generation":18},"latency_ms":221.0,"artifact":"Water evaporates and forms clouds. It then falls as precipitation."}
2 changes: 1 addition & 1 deletion fixtures/golden/compliance-direct-pass.v1.jsonl
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"schema_version":"compliance-eval-record/v1","task_id":"golden-task-01","task_spec_hash":"1ce85aeef714574b6692d80b006bd8b7cf2d514a71cac6797ef4c7cd832a4171","model":"qwen2.5-1.5b","mode":"direct","violations":[],"repair_trace":[],"repair_rounds":0,"final_pass":true,"token_cost":{"prompt":42,"generation":38,"repair":null},"latency_ms":348.0,"artifact":"<<Water Cycle Summary>>\nWater evaporates and forms clouds. It then falls as precipitation.\nAction List:\n- Monitor precipitation levels daily","error":null}
{"schema_version":"compliance-eval-record/v1","task_id":"golden-task-01","task_spec_hash":"1ce85aeef714574b6692d80b006bd8b7cf2d514a71cac6797ef4c7cd832a4171","model":"qwen2.5-1.5b","mode":"direct","violations":[],"repair_trace":[],"repair_rounds":0,"final_pass":true,"token_cost":{"prompt":42,"generation":38},"latency_ms":348.0,"artifact":"<<Water Cycle Summary>>\nWater evaporates and forms clouds. It then falls as precipitation.\nAction List:\n- Monitor precipitation levels daily"}
2 changes: 1 addition & 1 deletion fixtures/golden/compliance-repair-pass.v1.jsonl
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"schema_version":"compliance-eval-record/v1","task_id":"golden-task-01","task_spec_hash":"1ce85aeef714574b6692d80b006bd8b7cf2d514a71cac6797ef4c7cd832a4171","model":"qwen2.5-1.5b","mode":"full_pcl","violations":[{"constraint_id":"c_title","level":"hard","category":"format","hint":"response is missing a title wrapped in <<...>>","evidence_span":null,"detected_at":"post_decode"}],"repair_trace":[{"round":1,"violation_ids":["c_title"],"strategy":"insert_section","target_region":null,"ok":true,"rolled_back":false,"remaining_violation_ids":[],"token_cost":null,"latency_ms":312.0}],"repair_rounds":1,"final_pass":true,"token_cost":{"prompt":42,"generation":38,"repair":55},"latency_ms":504.0,"artifact":"<<Water Cycle Summary>>\nWater evaporates and forms clouds. It then falls as precipitation.\nAction List:\n- Monitor precipitation levels daily","error":null}
{"schema_version":"compliance-eval-record/v1","task_id":"golden-task-01","task_spec_hash":"1ce85aeef714574b6692d80b006bd8b7cf2d514a71cac6797ef4c7cd832a4171","model":"qwen2.5-1.5b","mode":"full_pcl","violations":[{"constraint_id":"c_title","level":"hard","category":"format","hint":"response is missing a title wrapped in <<...>>","detected_at":"post_decode"}],"repair_trace":[{"round":1,"violation_ids":["c_title"],"strategy":"insert_section","ok":true,"rolled_back":false,"remaining_violation_ids":[],"latency_ms":312.0}],"repair_rounds":1,"final_pass":true,"token_cost":{"prompt":42,"generation":38,"repair":55},"latency_ms":504.0,"artifact":"<<Water Cycle Summary>>\nWater evaporates and forms clouds. It then falls as precipitation.\nAction List:\n- Monitor precipitation levels daily"}
55 changes: 55 additions & 0 deletions tests/test_schemas_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
import tempfile
from pathlib import Path

import wasmagent_protocol as wp
from jsonschema import Draft202012Validator
from referencing import Registry, Resource

REPO_ROOT = Path(__file__).parent.parent
SCHEMAS_DIR = REPO_ROOT / "schemas"

Expand Down Expand Up @@ -196,3 +200,54 @@ def test_fixture_fields_present_in_rollout_schema(self):
for key in record:
assert key in props, \
f"fixture field '{key}' not in rollout-wire schema properties"


class TestGoldenFixturesValidateAgainstPackageSchema:
"""Golden fixtures must validate against the canonical wasmagent-protocol
package schema (the SSOT), not the local fork — Milestone 2, issue #27."""

GOLDEN_DIR = REPO_ROOT / "fixtures" / "golden"

# record schema_version -> wasmagent-protocol schema id
SCHEMA_FOR_VERSION = {
"compliance-eval-record/v1": "compliance-eval-record",
"rollout-wire/v1": "rollout-wire",
}

@staticmethod
def _package_registry():
"""Register every canonical package schema by $id so $ref siblings
(e.g. constraint-violation, repair-trace referenced from
compliance-eval-record) resolve without network access."""
docs = [wp.get_schema(sid) for sid in wp.schema_ids()]
return Registry().with_resources(
[(d["$id"], Resource.from_contents(d)) for d in docs]
)

def test_every_golden_record_validates(self):
golden = sorted(self.GOLDEN_DIR.glob("*.jsonl"))
assert golden, "no golden fixtures found under fixtures/golden/"
registry = self._package_registry()
failures = []
for path in golden:
for lineno, raw in enumerate(path.read_text().splitlines(), 1):
raw = raw.strip()
if not raw:
continue
record = json.loads(raw)
version = record.get("schema_version")
assert version in self.SCHEMA_FOR_VERSION, (
f"{path.name}:{lineno} unmapped schema_version={version!r}"
)
schema = wp.get_schema(self.SCHEMA_FOR_VERSION[version])
errors = sorted(
Draft202012Validator(schema, registry=registry).iter_errors(record),
key=lambda e: list(e.path),
)
for err in errors:
loc = "/".join(str(p) for p in err.path) or "<root>"
failures.append(f"{path.name}:{lineno} {loc}: {err.message}")
assert not failures, (
"golden fixtures fail the canonical package schema:\n "
+ "\n ".join(failures)
)
Loading