Files
rune/tests/test_layout_contract.py
T
Nick 3dadf2d3f5
CI Pipeline / best-practices (push) Successful in 6s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / canary (push) Successful in 31s
CI Pipeline / lint (push) Successful in 46s
Auto-version and Release / release (push) Successful in 54s
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / unit-tests (push) Successful in 1m9s
CI Pipeline / type-check (push) Successful in 30s
CI Pipeline / required-checks (push) Successful in 4s
deploy / deploy (push) Successful in 3m14s
feat(scaffolding): capability-fit discovery, gap decisions, toolchain resolution, layout contracts (SCAFFOLD-TRANCHE-003)
- capability_fit.py: open-world CapabilityRequirement; deterministic
  candidate discovery over Spine/Scaffold stores (kind + providing-
  relationship eligible; environment-contradiction excluded); fit levels
  EXACT/PARTIAL/INCOMPATIBLE/UNKNOWN from contracts+health, never names;
  resolution surface SATISFIED/REUSE/CONFIGURE/COMPOSE/ADAPT_EXISTING/
  ACQUIRE_EXTERNAL/BUILD_NEW/INSUFFICIENT_EVIDENCE — BUILD_NEW never
  silent (insufficient evidence blocks it), brittle-coupling check
  (load-bearing missing contracts) forces BUILD_NEW or ACQUIRE with
  recorded rationale; composition covers via contract union, flagged
  temporary (permanence user-controlled); LLM semantic-fit seam records
  provider/model/reason/evidence/confidence — prose never authority
- toolchain.py: EXISTING PROJECT TOOLCHAIN WINS; constraint keys fixed
  and language-free, values open-world; language change = CHANGE_MAJOR
  requiring user approval, never silent; unknown constraints ->
  INSUFFICIENT_EVIDENCE (no invented certainty); Python only selected
  because the project uses it — fixtures prove rust/ts/zig resolve
- layout_contract.py: open-world regions (web/tui/qt coexist); stable
  ui:<component> identity surviving implementation changes; LOCKED
  requires user provenance (mechanically enforced); locked amendment
  requires authorized_by=user (enforced, reason required); deterministic
  adapter-observation gate PASS/FAIL/UNKNOWN — UNKNOWN never PASS;
  USER-LOCKED drift preserves the contract (deliberate exception to
  reality-wins), marks DRIFTING, creates maintenance item; flexible
  violation fails gate until repaired
- REAL TUI PROOF: Rune's actual version display (issue #105, plan
  plans/2026-09-03-tui-footer.md) observed from source via deterministic
  adapter — footer-bar first child; gate cycle PASS -> controlled
  violation FAIL -> restore PASS; binding encoded scaffold-provenance
  (durable records establish placement; USER LOCK upgrade NEEDS
  CONFIRMATION — not fabricated)
- capability/layout integration proven: version-display requirement
  discovers the existing capability + layout binding (REUSE), never
  builds another widget
- 3 modules registered in Spine authority + canonical build + manifest
  regenerated (Tranche 2 lesson applied: no hand-edited structural facts)

Tests: 53 new (fit 24 + toolchain 9 + layout 16 + real proof 4);
suite 1439 green; henxels rc=0; pyright 0 errors on new modules.
2026-09-13 04:16:48 +01:00

237 lines
9.7 KiB
Python

"""test_layout_contract.py — SCAFFOLD-TRANCHE-003 §29 test plan (A-L)."""
import os
import sys
import pytest
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
from rune import layout_contract as lc # noqa: E402
from rune import structural_store as ss # noqa: E402
def _doc():
doc = ss.new_doc()
lc.define_region(doc, "header", order=0, orientation="horizontal", provenance="user", evidence_refs=["user-design"])
lc.define_region(doc, "main", order=1)
lc.define_region(doc, "footer", order=2)
lc.define_region(doc, "sidebar", parent="main", order=0)
return doc
# A. region creation/roundtrip
def test_region_roundtrip(tmp_path):
doc = _doc()
ss.save(doc, tmp_path / "d.json")
doc2 = ss.load(tmp_path / "d.json")
r = ss.entity(doc2, "region:header")
assert r["kind"] == "layout_region"
assert r["identities"]["order"] == 0
assert r["identities"]["provenance"] == "user"
# B. component identity survives implementation-path change
def test_component_identity_survives_path_change(tmp_path):
doc = _doc()
lc.bind_component(
doc,
"version_display",
"header",
lock=True,
provenance="user",
order=2,
evidence_ref="user-2026-09",
implementation="src/tui/widgets/version.py",
)
ss.save(doc, tmp_path / "d.json")
doc2 = ss.load(tmp_path / "d.json")
e = ss.entity(doc2, "layout:ui:version_display")
# implementation path changes — identity does not
e["identities"]["implementation"] = "src/tui/version_widget.rs"
assert e["id"] == "layout:ui:version_display"
assert e["identities"]["component"] == "ui:version_display"
assert e["identities"]["region"] == "header"
# C. FLEXIBLE element may move inside allowed region
def test_flexible_move_within_region_ok(tmp_path):
doc = _doc()
lc.bind_component(doc, "help_hint", "main", provenance="builder")
obs = {"ui:help_hint": {"region": "main", "order": 5}}
assert lc.check_layout(doc, obs)["verdict"] == "PASS"
# moved position within same region — still fine
obs2 = {"ui:help_hint": {"region": "main", "order": 9}}
assert lc.check_layout(doc, obs2)["verdict"] == "PASS"
# D. USER-LOCKED element may not move to another region
def test_locked_region_move_fails(tmp_path):
doc = _doc()
lc.bind_component(doc, "version_display", "header", lock=True, provenance="user", order=2)
obs = {"ui:version_display": {"region": "footer", "order": 2}}
result = lc.check_layout(doc, obs)
assert result["verdict"] == "FAIL"
check = result["checks"][0]
assert check["drift_class"] == "USER_LOCKED_VIOLATION"
# E. USER-LOCKED ordering remains stable
def test_locked_order_stable(tmp_path):
doc = _doc()
lc.bind_component(doc, "version_display", "header", lock=True, provenance="user", order=2)
ok = {"ui:version_display": {"region": "header", "order": 2}}
assert lc.check_layout(doc, ok)["verdict"] == "PASS"
bad = {"ui:version_display": {"region": "header", "order": 0}}
assert lc.check_layout(doc, bad)["verdict"] == "FAIL"
# F. model/scaffold-owned flexible rule can be revised
def test_flexible_amendable_by_builder(tmp_path):
doc = _doc()
lc.bind_component(doc, "help_hint", "main", provenance="builder")
lc.amend_layout_contract(
doc, "help_hint", new_region="sidebar", authorized_by="builder", reason="layout improvement"
)
obs = {"ui:help_hint": {"region": "sidebar"}}
assert lc.check_layout(doc, obs)["verdict"] == "PASS"
# G. user-locked rule requires explicit user-authorized amendment
def test_locked_amendment_requires_user_authority(tmp_path):
doc = _doc()
lc.bind_component(
doc, "version_display", "header", lock=True, provenance="user", order=2, evidence_ref="user-2026-09"
)
with pytest.raises(ValueError, match="USER-LOCKED"):
lc.amend_layout_contract(
doc, "version_display", new_region="footer", authorized_by="builder", reason="I prefer it there"
)
with pytest.raises(ValueError, match="USER-LOCKED"):
lc.amend_layout_contract(doc, "version_display", new_region="footer", authorized_by="", reason="x")
# builder attempt must not have mutated the contract
assert ss.entity(doc, "layout:ui:version_display")["identities"]["region"] == "header"
# with user authorization it works and records the amendment
lc.amend_layout_contract(
doc,
"version_display",
new_region="footer",
authorized_by="user",
reason="user changed mind",
evidence_ref="user-2026-09-13",
)
e = ss.entity(doc, "layout:ui:version_display")
assert e["identities"]["region"] == "footer"
assert e["ext"]["amendments"][0]["authorized_by"] == "user"
assert e["ext"]["amendments"][0]["changes"]["region"] == "footer"
# H. layout violation produces FAIL
def test_violation_fail(tmp_path):
doc = _doc()
lc.bind_component(doc, "status_bar", "footer", provenance="scaffold")
obs = {"ui:status_bar": {"region": "header"}}
assert lc.check_layout(doc, obs)["verdict"] == "FAIL"
# I. unavailable adapter → UNKNOWN, never PASS
def test_missing_observation_unknown_never_pass(tmp_path):
doc = _doc()
lc.bind_component(doc, "version_display", "header", lock=True, provenance="user")
lc.bind_component(doc, "status_bar", "footer", provenance="scaffold")
# no observations at all → locked contract cannot be verified
r = lc.check_layout(doc, {})
assert r["verdict"] == "UNKNOWN"
assert all(c["verdict"] == "UNKNOWN" for c in r["checks"])
# partial observation: locked unknown → overall UNKNOWN
r2 = lc.check_layout(doc, {"ui:status_bar": {"region": "footer"}})
assert r2["verdict"] == "UNKNOWN"
# flexible-only unknown never escalates verdict
doc2 = _doc()
lc.bind_component(doc2, "help_hint", "main", provenance="builder")
r3 = lc.check_layout(doc2, {})
assert r3["verdict"] == "PASS" # flexible has no observation → not a gate
# J. layout drift does NOT overwrite the user's contract
def test_drift_preserves_user_contract(tmp_path):
doc = _doc()
lc.bind_component(
doc, "version_display", "header", lock=True, provenance="user", order=2, evidence_ref="user-2026-09"
)
ss.save(doc, tmp_path / "d.json")
doc2 = ss.load(tmp_path / "d.json")
# a later model moved it to footer
violating = {"ui:version_display": {"region": "footer", "order": 2}}
out = lc.layout_drift(doc2, violating)
assert out["gate"] == "FAIL"
assert out["drifted_locked"] == ["ui:version_display"]
# THE CONTRACT IS PRESERVED — not rewritten to match reality
e = ss.entity(doc2, "layout:ui:version_display")
assert e["identities"]["region"] == "header" # user intent wins
assert e["status"] == "DRIFTING"
assert e["identities"]["lock"] is True
# violation recorded as evidence + maintenance item created
assert ss.entity(doc2, "maintenance:layout-drift-ui:version_display") is not None
obs_kinds = [o["kind"] for o in doc2["observations"]]
assert "layout_observed" in obs_kinds
# persisted across restart
ss.save(doc2, tmp_path / "d.json")
doc3 = ss.load(tmp_path / "d.json")
assert ss.entity(doc3, "layout:ui:version_display")["identities"]["region"] == "header"
# repair: reality returns to the contract → status returns to ACTIVE
repaired = {"ui:version_display": {"region": "header", "order": 2}}
out2 = lc.layout_drift(doc3, repaired)
assert out2["gate"] == "PASS"
assert ss.entity(doc3, "layout:ui:version_display")["status"] == "ACTIVE"
# K. framework-specific metadata roundtrips without core schema change
def test_framework_attrs_roundtrip(tmp_path):
doc = _doc()
lc.bind_component(
doc, "version_display", "header", provenance="user", align="right", css_class="ver", grid_column=3
)
ss.save(doc, tmp_path / "d.json")
doc2 = ss.load(tmp_path / "d.json")
fa = ss.entity(doc2, "layout:ui:version_display")["identities"]["framework_attrs"]
assert fa == {"align": "right", "css_class": "ver", "grid_column": 3}
# L. web/GUI/TUI kinds coexist without core rewrite
def test_multi_tech_coexistence(tmp_path):
doc = _doc()
# TUI region tree
lc.define_region(doc, "tui_top_bar", tech={"kind": "tui", "widget": "pane"}, order=0)
lc.define_region(doc, "tui_chat", parent="tui_top_bar", tech={"kind": "tui"})
# web region tree
lc.define_region(doc, "web_header", tech={"kind": "web", "element": "header"})
lc.define_region(doc, "web_main", tech={"kind": "web"})
# qt/desktop region
lc.define_region(doc, "qt_statusbar", tech={"kind": "qt", "widget": "QStatusBar"})
lc.bind_component(doc, "version_display", "web_header", provenance="user", css_class="ver")
lc.bind_component(doc, "tui_version", "tui_top_bar", provenance="user")
ss.save(doc, tmp_path / "d.json")
doc2 = ss.load(tmp_path / "d.json")
kinds = {e["identities"].get("tech", {}).get("kind") for e in doc2["entities"] if e["kind"] == "layout_region"}
assert {"tui", "web", "qt"} <= kinds
r = lc.check_layout(
doc2, {"ui:version_display": {"region": "web_header"}, "ui:tui_version": {"region": "tui_top_bar"}}
)
assert r["verdict"] == "PASS"
# lock provenance guard
def test_lock_requires_user_provenance(tmp_path):
doc = _doc()
with pytest.raises(ValueError, match="user provenance"):
lc.bind_component(doc, "sneaky", "header", lock=True, provenance="builder")
with pytest.raises(ValueError, match="user provenance"):
lc.bind_component(doc, "sneaky", "header", lock=True, provenance="scaffold")
def test_unknown_region_rejected(tmp_path):
doc = _doc()
with pytest.raises(ValueError, match="unknown region"):
lc.bind_component(doc, "x", "nonexistent")