fix(content-build): --check orphan detection; canon secrecy-0 + value-type guards

- --check now detects orphaned/stale build artifacts in build-owned dirs
  (client canon/ + topics/ fully owned; client npcs/ shared with legacy
  hand-authored files, only npc.* ids are treated as build-owned)
- resolve() enforces the schema rule that canon entities must be secrecy 0
- resolve() and ladder_rungs() guard against malformed value TYPES (non-int
  secrecy, non-list related/rungs) raising BuildError instead of crashing

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuHRPE7VfppUJEaoGBEUqZ
This commit is contained in:
2026-07-11 19:01:28 -05:00
parent c2d67be76a
commit f84e55ab4b
5 changed files with 78 additions and 1 deletions

View File

@@ -131,3 +131,24 @@ def test_empty_rungs_rejected():
secrecy=0, related=[], body="x", source="t:1", rungs=[])
with pytest.raises(BuildError):
resolve([empty])
def test_canon_entity_nonzero_secrecy_rejected():
bad = Entry(id="town.a", type="town", status="canon", secrecy=2,
related=[], body="x", source="t:9")
with pytest.raises(BuildError):
resolve([ladder(), bad])
def test_non_int_secrecy_raises():
bad = Entry(id="town.a", type="town", status="canon", secrecy="high",
related=[], body="x", source="t:9")
with pytest.raises(BuildError):
resolve([ladder(), bad])
def test_non_list_rungs_raises():
bad_ladder = Entry(id="rule.disposition-ladder", type="rule", status="canon",
secrecy=0, related=[], body="x", source="t:1", rungs="trusted")
with pytest.raises(BuildError):
resolve([bad_ladder])