Add the Scene Validator per MASTER_SPEC §14 and a central message-code registry.
- compiler/common/message_codes.py: single source of truth for message codes
(Appendix B SC0001-SC0014 plus documented extensions SC0015 Unexpected Field
Removed, SC0016 Empty Concept Removed, SC0017 Interaction Dropped) with a
message() factory that pins each code's severity and title.
- compiler/validator/scene_validator.py: validate_scene(scene_json, config,
logger=None) -> CompilerResult.
- Strict schema validation classifies failures: missing required -> SC0009,
wrong type/malformed -> SC0002 (Errors that stop the compilation).
- Recoverable normalization: unexpected fields -> SC0015 warning + removed
(suppressed when config.validator.allow_unknown_fields is true); empty/
whitespace concepts -> SC0016 warning + removed after trimming; interactions
whose participants reference a non-existent character -> SC0017 warning +
dropped.
- Never creates concepts, generates tags, or does KB lookup. Output is a
Scene model wrapped in a CompilerResult; order is deterministic.
New warning codes (SC0015-SC0017) are extensions to be folded into Appendix B.
Tests: valid input, hard errors (SC0009/SC0002) that stop, recoverable warnings
(SC0015/SC0016/SC0017), allow_unknown_fields suppression, determinism, and the
code registry.
Co-authored-by: kunail0804 <kunail0804@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
32 lines
861 B
Python
32 lines
861 B
Python
"""Tests for the message-code registry (issue #11)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from compiler.common.message_codes import CODES, message
|
|
from compiler.common.result import Severity
|
|
|
|
|
|
def test_appendix_b_and_extensions_registered() -> None:
|
|
assert "SC0001" in CODES
|
|
assert "SC0014" in CODES
|
|
assert {"SC0015", "SC0016", "SC0017"}.issubset(CODES)
|
|
|
|
|
|
def test_message_uses_registry_severity_and_title() -> None:
|
|
m = message("SC0009", "missing")
|
|
assert m.severity is Severity.ERROR
|
|
assert m.title == "Missing Required Field"
|
|
assert m.code == "SC0009"
|
|
|
|
|
|
def test_message_carries_context() -> None:
|
|
m = message("SC0015", "removed", path="characters/0")
|
|
assert m.context["path"] == "characters/0"
|
|
|
|
|
|
def test_unknown_code_raises() -> None:
|
|
with pytest.raises(KeyError):
|
|
message("SC9999", "nope")
|