157 lines
6.7 KiB
Python
157 lines
6.7 KiB
Python
"""Author-only checks for planning fixtures. Not a Mosaic runtime validator.
|
|
|
|
Run: python3 docs/plans/foundation-v1-candidate/check.py
|
|
Requires the existing jsonschema package; never installs dependencies.
|
|
"""
|
|
import json
|
|
import hashlib
|
|
import runpy
|
|
from copy import deepcopy
|
|
import unicodedata
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
from jsonschema import Draft202012Validator, FormatChecker
|
|
|
|
ROOT = Path(__file__).resolve().parent
|
|
|
|
|
|
def unique_object(pairs):
|
|
result = {}
|
|
for key, value in pairs:
|
|
if key in result:
|
|
raise ValueError(f"duplicate JSON key: {key}")
|
|
result[key] = value
|
|
return result
|
|
|
|
|
|
def reject_constant(value):
|
|
raise ValueError(f"non-JSON numeric constant: {value}")
|
|
|
|
|
|
def load(name):
|
|
return json.loads((ROOT / name).read_text(), object_pairs_hook=unique_object,
|
|
parse_constant=reject_constant)
|
|
|
|
|
|
formats = FormatChecker()
|
|
|
|
|
|
@formats.checks("date-time")
|
|
def utc_milliseconds(value):
|
|
if not isinstance(value, str):
|
|
return True # Schema type validation handles non-strings.
|
|
try:
|
|
parsed = datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%fZ")
|
|
except ValueError:
|
|
return False
|
|
return parsed.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z" == value
|
|
|
|
|
|
@formats.checks("mosaic-relative-path")
|
|
def relative_path(value):
|
|
if not isinstance(value, str):
|
|
return True
|
|
try:
|
|
size = len(value.encode("utf-8"))
|
|
except UnicodeEncodeError:
|
|
return False
|
|
return (
|
|
0 < size <= 4096
|
|
and not any(unicodedata.category(c) in {"Cc", "Cf", "Cs"} for c in value)
|
|
and "\\" not in value
|
|
and all(part not in {"", ".", ".."} for part in value.split("/"))
|
|
)
|
|
|
|
|
|
assert not formats.conforms("é" * 2049, "mosaic-relative-path")
|
|
assert not formats.conforms("a\u202eb", "mosaic-relative-path")
|
|
assert not formats.conforms("2026-02-30T03:00:00.000Z", "date-time")
|
|
assert formats.conforms("2028-02-29T03:00:00.000Z", "date-time")
|
|
schema = load("command-events.schema.json")
|
|
Draft202012Validator.check_schema(schema)
|
|
validator = Draft202012Validator(schema, format_checker=formats)
|
|
fixtures = load("command-events.fixtures.json")
|
|
assert fixtures["fixtureVersion"] == 1
|
|
names = set()
|
|
failures = []
|
|
for case in fixtures["cases"] + fixtures["semanticObligations"]:
|
|
assert case["name"] not in names, case["name"]
|
|
names.add(case["name"])
|
|
actual = validator.is_valid(case["document"])
|
|
if actual != case["schemaValid"]:
|
|
failures.append(case["name"])
|
|
if failures:
|
|
raise SystemExit("FAIL: " + ", ".join(failures))
|
|
print(f"PASS: {len(fixtures['cases'])} shape fixtures")
|
|
print(f"PASS: {len(fixtures['semanticObligations'])} deliberate shape-valid forgeries")
|
|
records = load("records.schema.json")
|
|
Draft202012Validator.check_schema(records)
|
|
for name in ("id", "runtimeId", "revision", "digest", "time", "actor", "artifactRef"):
|
|
assert records["$defs"][name] == schema["$defs"][name], name
|
|
record_validator = Draft202012Validator(records, format_checker=formats)
|
|
record_fixtures = load("records.fixtures.json")
|
|
assert record_fixtures["fixtureVersion"] == 1
|
|
record_failures = []
|
|
for case in record_fixtures["cases"]:
|
|
if record_validator.is_valid(case["document"]) != case["schemaValid"]:
|
|
record_failures.append(case["name"])
|
|
for case in record_fixtures["typeCases"]:
|
|
subtype = {"$ref": "#/$defs/" + case["definition"], "$defs": records["$defs"]}
|
|
check = Draft202012Validator(subtype, format_checker=formats)
|
|
if check.is_valid(case["value"]) != case["schemaValid"]:
|
|
record_failures.append(case["name"])
|
|
if record_failures:
|
|
raise SystemExit("FAIL records: " + ", ".join(record_failures))
|
|
print(f"PASS: {len(record_fixtures['cases'])} record shape fixtures")
|
|
print(f"PASS: {len(record_fixtures['typeCases'])} path fixtures")
|
|
vectors = load("fingerprint-vectors.json")
|
|
assert vectors["vectorVersion"] == 1
|
|
baseline_digest = vectors["vectors"][0]["digest"]
|
|
for vector in vectors["vectors"]:
|
|
data = deepcopy(vector["input"])
|
|
assert set(vectors["excludedInputs"]).isdisjoint(data)
|
|
assert len({skill["id"] for skill in data["skills"]}) == len(data["skills"])
|
|
data["skills"].sort(key=lambda item: item["id"])
|
|
canonical = json.dumps(data, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
|
|
assert canonical.isascii(), "These fixtures do not establish general Unicode JCS behavior"
|
|
digest = "sha256:" + hashlib.sha256(canonical.encode("utf-8")).hexdigest()
|
|
assert canonical == vector["canonical"] and digest == vector["digest"], vector["name"]
|
|
assert (digest == baseline_digest) == (vector["relationToBaseline"] == "same")
|
|
print(f"PASS: {len(vectors['vectors'])} restricted-domain fingerprint vectors")
|
|
runtime = load("runtime.schema.json")
|
|
Draft202012Validator.check_schema(runtime)
|
|
for name in ("id", "runtimeId", "revision", "digest", "time", "actor", "artifactRef"):
|
|
assert runtime["$defs"][name] == schema["$defs"][name], name
|
|
assert runtime["$defs"]["operation"] == records["$defs"]["operation"]
|
|
assert set(runtime["$defs"]["controlRequest"]["properties"]["operation"]["enum"]) == (
|
|
set(records["$defs"]["operation"]["enum"]) - {"command.execute"}
|
|
)
|
|
projection_validator = Draft202012Validator(
|
|
{"$ref": "#/$defs/baseProjection", "$defs": runtime["$defs"]},
|
|
format_checker=formats)
|
|
for vector in vectors["vectors"]:
|
|
assert projection_validator.is_valid(vector["input"]), vector["name"]
|
|
runtime_validator = Draft202012Validator(runtime, format_checker=formats)
|
|
runtime_fixtures = load("runtime.fixtures.json")
|
|
assert runtime_fixtures["fixtureVersion"] == 1
|
|
runtime_failures = []
|
|
for case in runtime_fixtures["cases"] + runtime_fixtures["semanticObligations"]:
|
|
if runtime_validator.is_valid(case["document"]) != case["schemaValid"]:
|
|
runtime_failures.append(case["name"])
|
|
if runtime_failures:
|
|
raise SystemExit("FAIL runtime: " + ", ".join(runtime_failures))
|
|
print(f"PASS: {len(runtime_fixtures['cases'])} runtime/artifact shape fixtures")
|
|
print(f"PASS: {len(runtime_fixtures['semanticObligations'])} runtime semantic obligations remain shape-valid")
|
|
model_fixtures = load("semantic-model.fixtures.json")
|
|
assert model_fixtures["fixtureVersion"] == 1
|
|
model = runpy.run_path(str(ROOT / "semantic-model.py"))["evaluate"]
|
|
for case in model_fixtures["cases"]:
|
|
assert model(case) == case["expected"], case["name"]
|
|
for bundle in (fixtures, record_fixtures, runtime_fixtures, model_fixtures):
|
|
case_names = [case["name"] for case in bundle["cases"]
|
|
+ bundle.get("semanticObligations", [])]
|
|
assert len(case_names) == len(set(case_names)), "Duplicate fixture names"
|
|
print(f"PASS: {len(model_fixtures['cases'])} synthetic reference/permission/lifecycle model cases")
|
|
print("Runtime semantic enforcement is NOT implemented or tested by these checks.")
|