' not in html
+ assert "read capped at" not in html
diff --git a/tests/unit/test_theme_persistence.py b/tests/unit/test_theme_persistence.py
new file mode 100644
index 0000000..359fb07
--- /dev/null
+++ b/tests/unit/test_theme_persistence.py
@@ -0,0 +1,251 @@
+"""Tier 1 -- a chosen theme survives a refresh.
+
+`contracts/operator-surface.v1.md` Core 10, machine check
+`antigoals.enforced`: "no view holds state that does not survive a refresh
+(state persisted in `localStorage` or on the server survives; state held
+only in page memory does not)". The theme toggle used to hold its choice in
+page memory alone -- `wtSetTheme` set an attribute and nothing else -- so
+Light came back Dark on the next load and on every navigation.
+
+Two layers here, and the split is deliberate:
+
+ * the STRUCTURAL tests always run. They assert the contract the server
+ emits: one shared storage key, the resolver inlined in `` ahead of
+ the body, the setter persisting, the toggle re-syncing after a body swap.
+ * the BEHAVIOURAL tests actually EXECUTE the emitted JavaScript under
+ `node` against a small fake DOM, and do the real round trip: load, click
+ Light, reload, assert Light came back. Structural assertions cannot tell
+ a working script from a plausible-looking one; this is what closes that
+ gap without adding a browser to this tier.
+
+`node` is preinstalled on the CI runner (`ubuntu-latest`), so these are not
+theoretical; on a machine without it they skip, the same way this suite's
+web tests skip without the `web` extra. The structural layer still runs
+there, so the emitted contract is never unasserted.
+"""
+
+from __future__ import annotations
+
+import json
+import shutil
+import subprocess
+
+import pytest
+
+pytest.importorskip("fastapi", reason="the 'web' extra is not installed")
+
+from amplifier_work_tracker import webapp as W # noqa: E402
+from amplifier_work_tracker import webtheme as T # noqa: E402
+
+# ----------------------------------------------------------------- structural
+
+
+def test_the_storage_key_is_declared_once_and_used_by_both_sides():
+ """The writer (`wtSetTheme`, webapp) and the first-paint reader
+ (`theme_boot_js`, webtheme) must name the SAME key. Two spellings would
+ fail silently and look exactly like "the toggle does nothing".
+ """
+ assert T.THEME_STORAGE_KEY == "wt-theme"
+ assert f"'{T.THEME_STORAGE_KEY}'" in W._OBSERVATORY_THEME_JS # noqa: SLF001
+ assert f"'{T.THEME_STORAGE_KEY}'" in T.theme_boot_js()
+
+
+def test_the_setter_persists_the_choice():
+ setter = W._OBSERVATORY_THEME_JS # noqa: SLF001
+ assert "function wtSetTheme(t){" in setter
+ assert "localStorage.setItem(" in setter
+
+
+def test_the_setter_survives_storage_being_unavailable():
+ """A browser with storage disabled throws on `setItem`. A theme
+ preference is never worth breaking a page over.
+ """
+ assert "try{ localStorage.setItem(" in W._OBSERVATORY_THEME_JS # noqa: SLF001
+ assert "catch" in W._OBSERVATORY_THEME_JS # noqa: SLF001
+ assert "try{" in T.theme_boot_js()
+
+
+def test_the_resolver_is_inlined_in_head_before_the_body():
+ """A body-end script applies the stored theme one full paint too late --
+ that is the flash-of-wrong-theme this placement exists to prevent.
+ """
+ html = T.page("t", "
body
", js=W._OBSERVATORY_THEME_JS) # noqa: SLF001
+ boot = T.theme_boot_js()
+ assert boot in html
+ assert html.index(boot) < html.index("") + len(html[: html.index("")])
+ assert html.index(boot) < html.index("` must carry `data-theme` from the
+ server, or a light-OS browser silently wins the token cascade. The
+ resolver only ever REPLACES this attribute -- it never removes it.
+ """
+ html = T.page("t", "
body
")
+ assert '' in html
+ assert "removeAttribute" not in T.theme_boot_js()
+
+
+def test_the_resolver_prefers_an_explicit_choice_over_the_os_preference():
+ """Order matters in the source as well as at runtime: the stored value is
+ read first, and `prefers-color-scheme` is consulted only in the branch
+ where it was absent or invalid.
+ """
+ boot = T.theme_boot_js()
+ assert boot.index("localStorage.getItem") < boot.index("prefers-color-scheme")
+ assert "if(t!=='light'&&t!=='dark')" in boot
+
+
+def test_the_toggle_resyncs_itself_after_the_body_swap():
+ """`data-theme` lives on ``, which the 20s swap never touches --
+ but the toggle BUTTONS come back server-rendered with Dark pressed every
+ tick. The swap re-executes body scripts, so this call re-derives
+ `aria-pressed` from the live attribute.
+ """
+ js = W._OBSERVATORY_THEME_JS # noqa: SLF001
+ assert "wtApplyTheme(document.documentElement.getAttribute('data-theme') || 'dark');" in js
+ # The swap replaces body CONTENTS and re-runs their scripts -- the two
+ # facts this re-sync depends on. Asserted here so a change to the swap
+ # mechanism lands on a failing test that names the dependency.
+ swap = T.auto_refresh_js(20000)
+ assert "document.body.innerHTML = doc.body.innerHTML;" in swap
+ assert "document.createElement('script')" in swap
+
+
+def test_only_the_visitors_own_click_persists_anything():
+ """The re-sync call and the OS preference must NOT write to storage:
+ doing so would freeze whatever the OS happened to prefer on the first
+ visit into a stored "choice" nobody made.
+ """
+ js = W._OBSERVATORY_THEME_JS # noqa: SLF001
+ apply_fn = js[js.index("function wtApplyTheme(t){") : js.index("function wtSetTheme(t){")]
+ assert "localStorage" not in apply_fn
+ assert "setItem" not in T.theme_boot_js()
+
+
+# ---------------------------------------------------------------- behavioural
+
+_NODE = shutil.which("node")
+
+#: A fake DOM just large enough to run the two real scripts: the ``
+#: element's attributes, the two toggle buttons, `localStorage`, and
+#: `matchMedia`. Everything the scripts touch and nothing else.
+_HARNESS = """
+const scenario = JSON.parse(require('fs').readFileSync(0, 'utf8'));
+function run(storage, prefersLight, serverTheme, click) {
+ const attrs = { 'data-theme': serverTheme };
+ const buttons = ['dark', 'light'].map(function (name) {
+ return {
+ dataset: { theme: name },
+ attrs: {},
+ setAttribute: function (k, v) { this.attrs[k] = v; },
+ };
+ });
+ const document = {
+ documentElement: {
+ setAttribute: function (k, v) { attrs[k] = v; },
+ getAttribute: function (k) { return k in attrs ? attrs[k] : null; },
+ },
+ querySelectorAll: function (sel) {
+ return sel === '.theme-toggle button' ? buttons : [];
+ },
+ };
+ const localStorage = {
+ getItem: function (k) { return k in storage ? storage[k] : null; },
+ setItem: function (k, v) { storage[k] = v; },
+ };
+ const window = {
+ matchMedia: function (q) {
+ return { matches: prefersLight && q.indexOf('light') !== -1 };
+ },
+ };
+ const src = scenario.boot + '\\n' + scenario.theme_js + '\\n' +
+ (click ? "wtSetTheme('" + click + "');" : '');
+ new Function('document', 'window', 'localStorage', src)(document, window, localStorage);
+ const pressed = {};
+ buttons.forEach(function (b) { pressed[b.dataset.theme] = b.attrs['aria-pressed']; });
+ return { theme: attrs['data-theme'], pressed: pressed };
+}
+const storage = Object.assign({}, scenario.stored);
+const first = run(storage, scenario.prefers_light, 'dark', scenario.click);
+// The RELOAD: a brand-new document served the same server-side default,
+// carrying over only what the browser actually kept -- `storage`.
+const second = run(storage, scenario.prefers_light, 'dark', null);
+console.log(JSON.stringify({ first: first, second: second, storage: storage }));
+"""
+
+needs_node = pytest.mark.skipif(_NODE is None, reason="node is not installed on this host")
+
+
+def _round_trip(*, stored=None, prefers_light=False, click=None) -> dict:
+ """Load a page, optionally click a theme button, then RELOAD it."""
+ assert _NODE is not None
+ scenario = {
+ "boot": T.theme_boot_js(),
+ "theme_js": W._OBSERVATORY_THEME_JS, # noqa: SLF001
+ "stored": stored or {},
+ "prefers_light": prefers_light,
+ "click": click,
+ }
+ proc = subprocess.run(
+ [_NODE, "-e", _HARNESS],
+ input=json.dumps(scenario),
+ capture_output=True,
+ text=True,
+ timeout=30,
+ )
+ assert proc.returncode == 0, f"node harness failed:\n{proc.stderr}"
+ return json.loads(proc.stdout)
+
+
+@needs_node
+def test_a_fresh_visitor_with_no_preference_gets_dark():
+ out = _round_trip()
+ assert out["first"]["theme"] == "dark"
+ assert out["first"]["pressed"] == {"dark": "true", "light": "false"}
+ assert out["storage"] == {}, "nothing was chosen, so nothing should have been stored"
+
+
+@needs_node
+def test_choosing_light_then_reloading_keeps_light():
+ """THE row's own question, asked end to end: render, set the theme,
+ re-render, assert the stored value is applied.
+ """
+ out = _round_trip(click="light")
+ assert out["first"]["theme"] == "light"
+ assert out["storage"] == {T.THEME_STORAGE_KEY: "light"}
+ assert out["second"]["theme"] == "light", (
+ "the theme died on refresh -- the server default won again, which is "
+ "exactly the Core 10 violation this closed"
+ )
+ assert out["second"]["pressed"] == {"dark": "false", "light": "true"}, (
+ "the page came back Light but the toggle shows Dark pressed"
+ )
+
+
+@needs_node
+def test_choosing_dark_on_a_light_os_survives_the_reload_too():
+ """The harder direction: the stored choice must beat a CONTRADICTING OS
+ preference, not merely beat an absent one.
+ """
+ out = _round_trip(prefers_light=True, click="dark")
+ assert out["first"]["theme"] == "dark"
+ assert out["second"]["theme"] == "dark"
+ assert out["second"]["pressed"] == {"dark": "true", "light": "false"}
+
+
+@needs_node
+def test_the_os_light_preference_is_honoured_when_nothing_is_stored():
+ out = _round_trip(prefers_light=True)
+ assert out["first"]["theme"] == "light"
+ assert out["first"]["pressed"] == {"dark": "false", "light": "true"}
+ assert out["storage"] == {}, (
+ "an OS preference is not a choice the visitor made -- storing it would "
+ "freeze the first visit's ambient setting forever"
+ )
+
+
+@needs_node
+def test_a_junk_stored_value_falls_back_to_the_default_rather_than_applying_it():
+ out = _round_trip(stored={T.THEME_STORAGE_KEY: "chartreuse"})
+ assert out["first"]["theme"] == "dark"
diff --git a/tests/unit/test_view_query_bounds.py b/tests/unit/test_view_query_bounds.py
new file mode 100644
index 0000000..289144e
--- /dev/null
+++ b/tests/unit/test_view_query_bounds.py
@@ -0,0 +1,297 @@
+"""Tier 1 -- every adapter listing call reachable from a read-only route
+passes an explicit, finite limit.
+
+`contracts/operator-surface.v1.md` Core 10, machine check
+`antigoals.enforced`: "every adapter call reached from a view passes an
+explicit limit". The surface polls its whole body every 20 seconds
+(`webapp._AUTO_REFRESH_MS`), so an unbounded read on a GET handler is not a
+one-off cost -- it is three full-table materialisations a minute, per open
+tab. `adapter.Beads.list`'s own docstring records what that costs at size.
+
+This is a STATIC audit of the route modules -- the same technique the
+conformance ledger's own route audit uses (`ledger/checks/_support.py`'s
+`route_audit`, which walks GET handlers looking for mutating verbs). It is
+duplicated here rather than imported on purpose: the ledger kit is a
+self-contained auditor of this repo against a contract, and the product
+suite must not depend on it to gate a merge.
+
+WHAT IT CANNOT SEE, stated rather than implied: it follows calls by NAME,
+module-locally, four levels deep -- the same honest bound `route_audit`
+records. A listing call reached through a callable passed in from another
+module is not visible to it.
+"""
+
+from __future__ import annotations
+
+import ast
+from pathlib import Path
+
+import pytest
+
+pytest.importorskip("fastapi", reason="the 'web' extra is not installed")
+
+from amplifier_work_tracker import adapter as A # noqa: E402
+from amplifier_work_tracker import webbrowse as B # noqa: E402
+
+_SRC = Path(A.__file__).resolve().parent
+
+#: The modules that register HTTP routes.
+_ROUTE_MODULES = (_SRC / "webapp.py", _SRC / "webbrowse.py", _SRC / "webtrust.py")
+
+#: Every adapter read that ACCEPTS a `limit` -- the calls Core 10's check is
+#: about. A scalar read (`get`, `project_summary`) has nothing to bound and is
+#: not listed. Derived by hand from `adapter.py`'s signatures and re-checked
+#: below, so a new bounded read cannot join the seam unnoticed.
+_BOUNDED_READS = frozenset(
+ {
+ "list",
+ "list_bounded",
+ "activity",
+ "attention_items",
+ "attention_items_from_rows",
+ "recent_activity_feed",
+ }
+)
+
+#: Helpers that make a listing call but are NOT reached from any route -- dead
+#: code, which the clause as written does not condemn. Each one is re-checked
+#: below to still BE dead: the exemption expires the moment it gains a caller.
+_UNREACHED_EXEMPTIONS = frozenset({("webapp.py", "_oldest_ready_item")})
+
+#: How deep the name-following goes. Matches the ledger's own route audit.
+_DEPTH = 4
+
+
+def _functions(tree: ast.AST) -> dict[str, ast.FunctionDef | ast.AsyncFunctionDef]:
+ out: dict[str, ast.FunctionDef | ast.AsyncFunctionDef] = {}
+ for node in ast.walk(tree):
+ if isinstance(node, ast.FunctionDef | ast.AsyncFunctionDef):
+ out.setdefault(node.name, node)
+ return out
+
+
+def _called_names(node: ast.AST) -> set[str]:
+ names: set[str] = set()
+ for n in ast.walk(node):
+ if isinstance(n, ast.Call):
+ f = n.func
+ if isinstance(f, ast.Attribute):
+ names.add(f.attr)
+ elif isinstance(f, ast.Name):
+ names.add(f.id)
+ return names
+
+
+def _is_read_only_route(fn: ast.FunctionDef | ast.AsyncFunctionDef) -> bool:
+ for dec in fn.decorator_list:
+ if not isinstance(dec, ast.Call) or not isinstance(dec.func, ast.Attribute):
+ continue
+ if dec.func.attr in {"get", "head"}:
+ return True
+ if dec.func.attr == "api_route":
+ for kw in dec.keywords:
+ if kw.arg == "methods" and isinstance(kw.value, ast.List):
+ return any(
+ isinstance(e, ast.Constant) and e.value in {"GET", "HEAD"}
+ for e in kw.value.elts
+ )
+ return True
+ return False
+
+
+def _reachable_functions(
+ fn: ast.FunctionDef | ast.AsyncFunctionDef,
+ funcs: dict[str, ast.FunctionDef | ast.AsyncFunctionDef],
+) -> list[ast.FunctionDef | ast.AsyncFunctionDef]:
+ """`fn` plus every module-local helper it reaches, bounded at `_DEPTH`."""
+ seen: set[str] = set()
+ out = [fn]
+ frontier = _called_names(fn)
+ for _ in range(_DEPTH):
+ nxt: set[str] = set()
+ for name in frontier - seen:
+ seen.add(name)
+ helper = funcs.get(name)
+ if helper is not None and helper is not fn:
+ out.append(helper)
+ nxt |= _called_names(helper)
+ frontier = nxt - seen
+ if not frontier:
+ break
+ return out
+
+
+def _limit_value(call: ast.Call, module_globals: dict[str, object]) -> object:
+ """The `limit=` a bounded read passes, resolved to an int where it can be.
+
+ Three outcomes, and the difference between the last two is the whole
+ point:
+
+ * `None` -- no `limit` keyword at all. That is the failure the clause
+ names: the call inherits whatever default the seam happens to carry,
+ and nobody at the call site can see what it is.
+ * an `int` -- resolved. `0` is bd's own "unlimited" and fails; anything
+ positive passes.
+ * `"?"` -- a `limit` IS passed, but from an expression this static
+ audit cannot evaluate (a function parameter, say). That is still
+ EXPLICIT at the call site, which is what Core 10 asks for; where the
+ value comes from is the caller's business, and pretending otherwise
+ would make the audit fail on correct code.
+ """
+
+ def evaluate(node: ast.expr) -> object:
+ if isinstance(node, ast.Constant):
+ return node.value
+ if isinstance(node, ast.Name):
+ return module_globals.get(node.id, "?")
+ if isinstance(node, ast.Attribute) and isinstance(node.value, ast.Name):
+ owner = module_globals.get(node.value.id)
+ return getattr(owner, node.attr, "?") if owner is not None else "?"
+ if isinstance(node, ast.BinOp) and isinstance(node.op, ast.Add | ast.Sub | ast.Mult):
+ left, right = evaluate(node.left), evaluate(node.right)
+ if isinstance(left, int) and isinstance(right, int):
+ if isinstance(node.op, ast.Add):
+ return left + right
+ return left - right if isinstance(node.op, ast.Sub) else left * right
+ return "?"
+ return "?"
+
+ for kw in call.keywords:
+ if kw.arg == "limit":
+ return evaluate(kw.value)
+ return None
+
+
+def _module_globals(path: Path) -> dict[str, object]:
+ import importlib
+
+ module = importlib.import_module(f"amplifier_work_tracker.{path.stem}")
+ return vars(module)
+
+
+def _view_listing_calls() -> list[tuple[str, int, str, object]]:
+ """(module, line, handler, limit) for every listing call reachable from a
+ read-only route handler."""
+ found: list[tuple[str, int, str, object]] = []
+ for path in _ROUTE_MODULES:
+ tree = ast.parse(path.read_text(encoding="utf-8"))
+ funcs = _functions(tree)
+ module_globals = _module_globals(path)
+ for handler in (f for f in funcs.values() if _is_read_only_route(f)):
+ for fn in _reachable_functions(handler, funcs):
+ for node in ast.walk(fn):
+ if (
+ isinstance(node, ast.Call)
+ and isinstance(node.func, ast.Attribute)
+ and node.func.attr in _BOUNDED_READS
+ ):
+ found.append(
+ (
+ path.name,
+ node.lineno,
+ handler.name,
+ _limit_value(node, module_globals),
+ )
+ )
+ return found
+
+
+def test_the_audit_actually_finds_the_l1_listing_call():
+ """A guard on the audit itself: an audit that silently matches nothing
+ would pass forever while proving nothing. The L1 project view IS a
+ read-only route that lists items -- if this stops finding it, the
+ traversal broke, not the code under audit.
+ """
+ calls = _view_listing_calls()
+ assert any(m == "webbrowse.py" and h == "project_view" for m, _, h, _ in calls), (
+ f"the route traversal no longer reaches `project_view`'s item listing -- "
+ f"found instead: {calls}"
+ )
+
+
+def test_every_listing_call_reached_from_a_read_only_route_passes_a_finite_limit():
+ """Core 10. `limit=0` is bd's own \"unlimited\" (`adapter.Beads.list`'s
+ docstring says so outright), and an omitted `limit` leaves bd's default
+ in place implicitly -- the clause asks for an EXPLICIT bound, so both are
+ failures here.
+ """
+ offenders = [
+ (module, line, handler, limit)
+ for module, line, handler, limit in _view_listing_calls()
+ if limit is None or (isinstance(limit, int) and limit <= 0)
+ ]
+ assert not offenders, (
+ "a view-reached adapter read does not pass an explicit, finite limit "
+ "(`None` = no `limit=` at all, so the call silently inherits the seam's "
+ 'default; `0` = bd\'s own "unlimited"):\n '
+ + "\n ".join(f"{m}:{ln} in {h}() -> limit={lim!r}" for m, ln, h, lim in offenders)
+ + "\n\nThis surface re-renders every 20 seconds; an unbounded read here runs "
+ "three times a minute per open tab."
+ )
+
+
+def test_the_exempted_uncapped_helpers_are_still_reached_by_nothing():
+ """The exemption list is not a permanent pardon.
+
+ `_oldest_ready_item` calls `bd.list(...)` with no limit at all and is
+ exempt above for ONE reason: nothing calls it, so it is not "reached from
+ a view". The instant it gains a caller that reason evaporates -- and this
+ test is what notices, rather than the audit quietly continuing to skip it.
+ """
+ for module_name, func_name in sorted(_UNREACHED_EXEMPTIONS):
+ source = (_SRC / module_name).read_text(encoding="utf-8")
+ occurrences = source.count(func_name)
+ assert occurrences == 1, (
+ f"{module_name}: `{func_name}` now appears {occurrences}x (its own "
+ f"definition plus {occurrences - 1} reference(s)). It makes an UNCAPPED "
+ f"adapter listing call and was exempt from the bound audit only because "
+ f"it was dead code. Give it an explicit limit, or delete it."
+ )
+
+
+# ------------------------------------------------------- the bound itself
+
+
+def test_the_l1_ceiling_is_the_repo_s_existing_max_limit_convention():
+ """Not a number invented for this view: `LIST_MAX_LIMIT` is what the CLI's
+ own `list --limit` clamps to, and it is a whole number of this view's
+ pages (`LIST_DEFAULT_LIMIT`), so paging can reach every row the query
+ returns.
+ """
+ assert B._L1_ITEM_QUERY_LIMIT == A.LIST_MAX_LIMIT # noqa: SLF001
+ assert B._L1_ITEM_QUERY_LIMIT % A.LIST_DEFAULT_LIMIT == 0 # noqa: SLF001
+
+
+# ------------------------------------------------- the honest truncation note
+
+
+def test_truncation_note_is_silent_when_the_page_shows_everything():
+ assert B._truncation_note_html(shown=12, matched=12, capped=False) == "" # noqa: SLF001
+
+
+def test_truncation_note_reports_the_real_total_when_the_read_was_not_capped():
+ note = B._truncation_note_html(shown=50, matched=137, capped=False) # noqa: SLF001
+ assert "Showing 50 of 137 items" in note
+ assert "137+" not in note # 137 is measured, not a floor
+
+
+def test_truncation_note_says_at_least_and_names_the_cap_when_the_read_was_bounded():
+ """The one thing this note must never do is present a bounded window as a
+ measured total.
+ """
+ note = B._truncation_note_html( # noqa: SLF001
+ shown=50, matched=B._L1_ITEM_QUERY_LIMIT, capped=True
+ )
+ assert f"Showing 50 of {B._L1_ITEM_QUERY_LIMIT}+ items" in note # noqa: SLF001
+ assert f"read capped at {B._L1_ITEM_QUERY_LIMIT}" in note # noqa: SLF001
+ assert "narrow with search or a status tab" in note
+
+
+def test_truncation_note_speaks_up_when_capped_even_if_the_page_fits():
+ """A filter can cut a capped read down to a handful of rows. The page then
+ shows everything it matched -- but it only ever LOOKED at 500 items, and
+ saying nothing would imply otherwise.
+ """
+ note = B._truncation_note_html(shown=3, matched=3, capped=True) # noqa: SLF001
+ assert note != ""
+ assert "Showing 3 of 3+ items" in note
From e663eccfa9b62a7fe8dc59307b353bd0837f43c8 Mon Sep 17 00:00:00 2001
From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Date: Sat, 5 Sep 2026 01:33:11 -0700
Subject: [PATCH 2/6] conformance(operator-surface): Tier-B real-browser kit --
measured, artifact-backed, discriminating (Freeze 2/3/4)
The Tier-B half of `contracts/operator-surface.v1.md` now exists, runs a
pinned chromium against a live app over isolated fixture data, emits JSON
artifacts the tests re-check themselves, and runs as its own CI tier.
WHAT LANDED
tests/conformance/operator_surface/browser/test_tier_b.py
the file the contract names by path in Conformance 1-4 and Freeze 2.
Four checks under their contract names: `calm.zero_alarm_pixels`,
`state.not_colour_only` (rendered half), `swap.survives`,
`perception.floors`. 52 pass, 35 xfail(strict) against named ledger rows,
0 fail; ~105s end to end.
_png.py dependency-free PNG decode + colour histogram, so a pixel
sweep is a number this repo computed
_probe.py the in-page JS that measures contrast, target boxes, overflow,
motion and live regions, plus the pure-Python re-checks
_artifacts.py the artifact envelope, and the committed run summary
conftest.py app on 127.0.0.1:0 over the inherited isolated dolt server,
a separate workspace per scenario, pinned chromium
THE RULE THE KIT RUNS UNDER (Freeze 3): measure -> write -> read back ->
assert. No assertion rests on a value that only lived in a local variable,
and none rests on a screenshot. Screenshots are saved as evidence for a
human's Freeze 8 look and nothing reads them.
EVERY FIXTURE DISCRIMINATES (Freeze 4) -- eight bad halves, all RUN:
an injected --alarm chip, the contract's own retired #D9A253 region, a
genuinely-blocked fixture, status chips stripped to a class, a naive
whole-body innerHTML replacement, the same with a forced reflow, a 900px
element at 430px, and the recorded --ink-quiet/--ground pair (with a
dark-mode control, so a probe that always said "below floor" would fail).
WHAT THE BROWSER ACTUALLY FOUND -- chromium 148.0.7778.0 / playwright 1.60.0
Core 2 a calm L1 paints 97 --blocked pixels with nothing blocked
(legend swatch, live dot, danger-button border). L0 is clean.
Core 6 one of four survivals holds. Scroll survives; the open
does not (no on this surface carries an id, so
restoreState has zero targets); the pause CONTROL does not; and
there is no live region to preserve at all.
Core 7 L0 text contrast clean, L1/L2 not (.status-chip.st-resolved reads
3.13:1 dark / 2.26:1 light); 26 of 35 interactive controls on L0
under 44px, including the pause control itself at 26x26; control
borders and icon strokes below 3:1. Reduced motion PASSES.
TWO CONTRACT WORDINGS MEASURED NON-DISCRIMINATING, recorded not reinterpreted:
Conformance 4's `scrollWidth == clientWidth` cannot fire while html/body carry
`overflow-x: clip` (a 900px element at 430px moves it not at all), so the kit
emits an element-level reading too; and Conformance 3's literal bad half does
not lose the scroll offset on chromium 148, so a reflow variant carries that
half.
LEDGER -- rows re-derived from the emitted numbers, never from a file
appearing. OSV1-003/-008/-010 GAP -> VIOLATION (now measured, not unmeasured);
OSV1-020/-022/-023/-028/-029 GAP -> CONFORMS; OSV1-021 and -030 stay GAP for
their Tier-A halves. Every probe re-reads LAST_RUN.json, the kit's committed
run summary, so the ledger checks browser-produced numbers for itself instead
of trusting the browser tier's green. `pytest ledger/checks -q` 60 passed;
`make ledger-mutate` 56/56 proven.
WIRING: `make playwright-install`, `make test-conformance-b`, and a CI step
"Tier 7 -- operator-surface conformance (Tier B, browser)" that installs
chromium and uploads the artifacts. Deselected everywhere else by
`-m "not tier_b"` in addopts -- deselected, not --ignore'd, so a kit that
stops importing fails the fast tiers loudly.
OUT OF SCOPE, NAMED: `test_row_osv1_027`'s pin (Freeze 1, another lane's row)
was NARROWED, not moved -- it asserted no `tests/conformance` path appears in
the Makefile/CI at all, which the Tier-B wiring now legitimately trips. The
Tier-B path is excluded before the check, leaving its original question
(is Tier-A wiring landing?) intact. Disposition unchanged.
---
.github/workflows/ci.yml | 32 +
.gitignore | 5 +
Makefile | 26 +-
ledger/checks/mutation_harness.py | 243 +++-
ledger/checks/test_operator_rows.py | 521 ++++++--
ledger/rows.yaml | 563 +++++---
pyproject.toml | 33 +-
tests/conformance/__init__.py | 1 +
.../conformance/operator_surface/__init__.py | 1 +
.../operator_surface/browser/LAST_RUN.json | 395 ++++++
.../operator_surface/browser/__init__.py | 17 +
.../operator_surface/browser/_artifacts.py | 191 +++
.../operator_surface/browser/_png.py | 180 +++
.../operator_surface/browser/_probe.py | 665 +++++++++
.../operator_surface/browser/conftest.py | 333 +++++
.../operator_surface/browser/test_tier_b.py | 1184 +++++++++++++++++
16 files changed, 4043 insertions(+), 347 deletions(-)
create mode 100644 tests/conformance/__init__.py
create mode 100644 tests/conformance/operator_surface/__init__.py
create mode 100644 tests/conformance/operator_surface/browser/LAST_RUN.json
create mode 100644 tests/conformance/operator_surface/browser/__init__.py
create mode 100644 tests/conformance/operator_surface/browser/_artifacts.py
create mode 100644 tests/conformance/operator_surface/browser/_png.py
create mode 100644 tests/conformance/operator_surface/browser/_probe.py
create mode 100644 tests/conformance/operator_surface/browser/conftest.py
create mode 100644 tests/conformance/operator_surface/browser/test_tier_b.py
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index dfec3ee..ff21684 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -116,6 +116,38 @@ jobs:
- name: Tier 5 -- tool module tests
run: .venv/bin/python -m pytest modules/tool-work-tracker/tests -v
+ # Tier 7 -- operator-surface conformance, Tier B (real browser).
+ # contracts/operator-surface.v1.md Freeze 2 requires the Tier-B kit to
+ # "run as its own CI tier", and this is that tier: a separate step, so a
+ # browser failure is legible at a glance as a RENDERED-SURFACE failure
+ # and its cost/flake profile never rides inside the fast tiers above.
+ # Chromium is installed here rather than in the shared setup step for
+ # the same reason -- nothing above this line needs a browser.
+ #
+ # The kit is deselected everywhere else by pyproject's `-m "not tier_b"`
+ # addopts; `-m tier_b` on this command line is what selects it.
+ #
+ # `if: always()` is deliberate: this tier's whole job is to report what
+ # the rendered surface actually does, and a red tier above (a unit-test
+ # regression, say) must not hide that report.
+ - name: Tier 7 -- operator-surface conformance (Tier B, browser)
+ if: always()
+ run: |
+ .venv/bin/playwright install --with-deps chromium
+ .venv/bin/python -m pytest -m tier_b tests/conformance/operator_surface/browser -v
+
+ # The Tier-B artifacts (pixel counts, computed contrast ratios, bounding
+ # boxes, post-swap DOM snapshots, screenshots) -- Freeze 3's "artifacts
+ # the orchestrator re-checks itself". Uploaded on every run, pass or
+ # fail: a failing run's numbers are the ones worth reading.
+ - name: Tier 7 artifacts -- operator-surface Tier-B evidence
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: operator-surface-tier-b-artifacts
+ path: tests/conformance/operator_surface/browser/_artifacts/
+ if-no-files-found: warn
+
- name: Dolt server log (always, for debugging)
if: always()
run: cat /tmp/dolt-server.log || true
diff --git a/.gitignore b/.gitignore
index 84391ca..c9f78a5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,8 @@ DONE.json
# goal-batch orchestration scratch (per-run, never repo content)
.amplifier/goals/
+
+# Tier-B conformance artifacts -- a RUN's evidence (pixel counts, contrast
+# numbers, post-swap DOM snapshots, screenshots), never repo content. A
+# committed artifact is a number nobody re-measured.
+tests/conformance/operator_surface/browser/_artifacts/
diff --git a/Makefile b/Makefile
index 019ac82..86e55d8 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,5 @@
-.PHONY: venv test test-unit test-integration test-cli test-ledger ledger-mutate test-module check lint types doctor clean
+.PHONY: venv playwright-install test test-unit test-integration test-cli test-ledger \
+ test-conformance-b ledger-mutate test-module check lint types doctor clean
PYTHON ?= python3.12
VENV := .venv
@@ -52,6 +53,29 @@ test-ledger:
ledger-mutate:
$(PY) -m ledger.checks.mutation_harness
+## Chromium for the Tier-B browser conformance kit. Separate from `venv`
+## on purpose: `playwright` (the library) is a normal dev dependency and
+## installs with everything else, but the BROWSER is a ~150MB download into
+## `~/.cache/ms-playwright` that a contributor who never runs the browser
+## tier should not pay for. `--with-deps` pulls the system libraries chromium
+## needs on a bare CI runner (it is a no-op where they are already present).
+playwright-install:
+ $(VENV)/bin/playwright install --with-deps chromium
+
+## Tier B -- operator-surface conformance in a REAL BROWSER
+## (contracts/operator-surface.v1.md Freeze 2). Its own target, and
+## deliberately NOT part of `test` below: it costs a chromium launch and a
+## live app boot per scenario, and Freeze 2 requires it to run as its own
+## tier so a browser tier's cost and flake profile never ride inside the fast
+## ones. `-m tier_b` overrides the `-m "not tier_b"` in pyproject's addopts
+## (pytest inserts addopts before the command line, and the last -m wins), so
+## every other invocation deselects this tier automatically.
+##
+## Depends on `playwright-install`: a Tier-B run without chromium is not a
+## pass and not a skip, it is an error naming the missing browser.
+test-conformance-b: playwright-install
+ $(PYTEST) -m tier_b tests/conformance/operator_surface/browser -v
+
## Tier 5 -- tool module: modules/tool-work-tracker's own suite, the only
## place the post-reclaim custody behaviour of the AGENT SEAM (work_claim /
## work_declare / work_resolve / work_release) is asserted mechanically.
diff --git a/ledger/checks/mutation_harness.py b/ledger/checks/mutation_harness.py
index 505c75f..4533558 100644
--- a/ledger/checks/mutation_harness.py
+++ b/ledger/checks/mutation_harness.py
@@ -82,6 +82,13 @@
CLI = probes.CLI
+#: The Tier-B kit's committed run summary. Every OSV1 row that re-reads a
+#: browser-produced number reads it from here, so every mutation for those
+#: rows is a mutation OF THIS FILE -- the counterfactual is "the browser
+#: measured something else", stated the only way an in-process harness can
+#: state it.
+TIER_B_SUMMARY = _support.REPO_ROOT / op_probes.TIER_B_SUMMARY
+
#: Every module that owns `test_row_*` probes. Readers are patched in ALL of
#: them, so a mutation is seen the same way whichever family's probe runs --
#: the alternative (patching only the family under test) would let a probe that
@@ -469,15 +476,6 @@ def _mo007_a_get_handler_reaches_a_write(w: World) -> None:
)
-def _mo008_swap_restores_the_pause_flag(w: World) -> None:
- """FIXED: `restoreState` starts touching the pause control."""
- w.replace(
- WEBTHEME,
- "window.scrollTo(0, state.scrollY);",
- "window.scrollTo(0, state.scrollY);\n window.__wtRefreshPaused;",
- )
-
-
def _mo009_the_below_floor_token_stops_painting_copy(w: World) -> None:
"""FIXED: the empty-state caption moves off the below-floor token."""
w.replace(
@@ -487,11 +485,6 @@ def _mo009_the_below_floor_token_stops_painting_copy(w: World) -> None:
)
-def _mo010_a_browser_driver_appears(w: World) -> None:
- """FIXED: something in the repo starts driving a browser."""
- w.append(PYPROJECT, '\n# test-only: "playwright>=1.40"\n')
-
-
def _mo011_a_second_motion_block_appears(w: World) -> None:
"""REGRESSION: reduced motion becomes per-widget opt-in."""
w.append(
@@ -542,10 +535,6 @@ def _mo_tier_a_kit_appears(w: World) -> None:
w.touch(TIER_A_KIT)
-def _mo_tier_b_kit_appears(w: World) -> None:
- w.touch(TIER_B_KIT)
-
-
def _mo022_the_swap_mechanism_changes(w: World) -> None:
"""The substantive half of Conformance 3's pin: the whole-body innerHTML
replacement IS the mechanism the bad fixture describes.
@@ -570,13 +559,162 @@ def _mo023_a_swept_breakpoint_disappears(w: World) -> None:
def _mo027_the_makefile_wires_the_kit(w: World) -> None:
- """The wiring half of Freeze 1: existing is not the same as running."""
- w.append(MAKEFILE, "\ntest-conformance:\n\t$(PYTEST) tests/conformance -v\n")
+ """The wiring half of Freeze 1: existing is not the same as running.
+
+ Targets the TIER-A path specifically, matching the probe's 2026-09-05
+ narrowing -- the Tier-B kit is legitimately wired now, so a mutation that
+ only added "some conformance path" would prove nothing.
+ """
+ w.append(MAKEFILE, f"\ntest-conformance-a:\n\t$(PYTEST) {op_probes.TIER_A_KIT} -v\n")
+
+
+def _mo003_the_calm_page_stops_painting_blocked(w: World) -> None:
+ """FIXED: a calm L1 stops painting `--blocked`.
+
+ The counterfactual a pinning row needs -- the browser measuring the FIXED
+ behaviour. Both themes measured 97, so both anchors move together.
+ """
+ w.replace(
+ TIER_B_SUMMARY,
+ '"calm/L1/dark": {\n "alarm": 0,\n "blocked": 97,',
+ '"calm/L1/dark": {\n "alarm": 0,\n "blocked": 0,',
+ )
+
+
+def _mo008_the_swap_starts_restoring_the_disclosure(w: World) -> None:
+ """FIXED: an open `` survives the body-swap on L0."""
+ w.replace(
+ TIER_B_SUMMARY,
+ '"calm/L0/dark": {\n "details_with_id": 0,\n "live_regions_before": 0,\n'
+ ' "marked_live_regions_after": 0,\n "open_details_preserved": false,',
+ '"calm/L0/dark": {\n "details_with_id": 2,\n "live_regions_before": 0,\n'
+ ' "marked_live_regions_after": 0,\n "open_details_preserved": true,',
+ )
+
+
+def _mo008_the_pause_control_starts_surviving(w: World) -> None:
+ """FIXED: the pause CONTROL's own state survives the swap on L0.
+
+ Separable from the disclosure half above, and pinned separately, because
+ the two are different fixes: one needs ids in the markup, the other needs
+ the re-rendered button to be re-synchronised with `window.__wtRefreshPaused`.
+ A row that noticed only one of them would absorb the other silently.
+ """
+ w.replace(
+ TIER_B_SUMMARY,
+ '"calm/L0/dark": {\n "details_with_id": 0,\n "live_regions_before": 0,\n'
+ ' "marked_live_regions_after": 0,\n "open_details_preserved": false,\n'
+ ' "pause_control_preserved": false,',
+ '"calm/L0/dark": {\n "details_with_id": 0,\n "live_regions_before": 0,\n'
+ ' "marked_live_regions_after": 0,\n "open_details_preserved": false,\n'
+ ' "pause_control_preserved": true,',
+ )
+
+
+def _mo010_the_target_floor_is_met(w: World) -> None:
+ """FIXED: every interactive control on L0 reaches 44px."""
+ w.replace(
+ TIER_B_SUMMARY,
+ '"calm/L0/1280/dark": {\n "client_width": 1280,\n "controls": 35,\n'
+ ' "controls_below_44px": 26,',
+ '"calm/L0/1280/dark": {\n "client_width": 1280,\n "controls": 35,\n'
+ ' "controls_below_44px": 0,',
+ )
+
+
+def _mo020_the_calm_bad_half_stops_discriminating(w: World) -> None:
+ """REGRESSION: Conformance 1's injected-alarm-chip bad half stops firing.
+
+ A green fixture row whose bad half quietly stopped catching its defect is
+ the failure Freeze 4 exists to prevent, and it is invisible from the
+ browser tier's own green -- every good half still passes.
+ """
+ w.replace(
+ TIER_B_SUMMARY,
+ '"bad-alarm-chip/L0/dark": {\n "alarm": 10531,',
+ '"bad-alarm-chip/L0/dark": {\n "alarm": 0,',
+ )
+
+
+def _mo021_the_rendered_chips_lose_their_word(w: World) -> None:
+ """REGRESSION on the half of Conformance 2 that HAS landed: a rendered
+ status chip stops carrying a word, which the Tier-B arm must notice even
+ while the row stays red for the Tier-A half."""
+ w.replace(
+ TIER_B_SUMMARY,
+ '"alarm/L1/dark": {\n "status_elements": 19,\n "wordless": 0',
+ '"alarm/L1/dark": {\n "status_elements": 19,\n "wordless": 4',
+ )
+
+
+def _mo022_the_swap_bad_half_stops_discriminating(w: World) -> None:
+ """REGRESSION: the naive replacement stops losing the open disclosure, so
+ Conformance 3's bad half no longer differs from the shipped poller."""
+ w.replace(
+ TIER_B_SUMMARY,
+ '"bad-naive-replacement/L0/dark": {\n "details_with_id": 0,\n'
+ ' "live_regions_before": 0,\n "marked_live_regions_after": 0,\n'
+ ' "open_details_preserved": false,',
+ '"bad-naive-replacement/L0/dark": {\n "details_with_id": 0,\n'
+ ' "live_regions_before": 0,\n "marked_live_regions_after": 0,\n'
+ ' "open_details_preserved": true,',
+ )
+
+
+def _mo023_the_contrast_bad_half_loses_its_control(w: World) -> None:
+ """REGRESSION: the contrast probe starts reporting the recorded pair below
+ the floor in DARK as well as light.
+
+ That is the shape of a probe that has stopped measuring and started always
+ saying no -- and it would leave every good half in Conformance 4 looking
+ exactly as it does now.
+ """
+ w.replace(
+ TIER_B_SUMMARY,
+ '"bad-low-contrast/L0/dark": {\n "min_ratio": 5.53',
+ '"bad-low-contrast/L0/dark": {\n "min_ratio": 2.1',
+ )
+
+
+def _mo028_the_browser_pin_moves_without_a_re_run(w: World) -> None:
+ """REGRESSION: the manifest's playwright pin moves while the recorded
+ numbers stay behind.
+
+ The precise hazard Freeze 2's "pinned chromium" exists to prevent: every
+ contrast ratio and pixel count in the ledger would silently start
+ describing a browser build nobody ran.
+ """
+ w.replace(PYPROJECT, '"playwright==1.60.0",', '"playwright==1.61.0",')
+
+
+def _mo029_the_kit_stops_reading_its_artifacts_back(w: World) -> None:
+ """REGRESSION: ONE Tier-B check stops reading its artifact back and asserts
+ on the value still in its own local variable.
+ Freeze 3's whole content, and the reason the probe counts read-backs
+ against writes rather than looking for the call anywhere: every other
+ check would still read back, the artifact would still be written, and the
+ browser tier would still be green.
+ """
+ w.replace(
+ _support.REPO_ROOT / op_probes.TIER_B_KIT,
+ " cache[key] = _artifacts.read(path)",
+ ' cache[key] = {"measurement": measurement} # trust the local value',
+ )
-def _mo029_an_artifact_directory_appears(w: World) -> None:
- """The substantive half of Freeze 3: artifacts start being emitted."""
- w.touch("tests/conformance/operator_surface/browser/artifacts/.keep")
+
+def _mo030_a_demonstrated_bad_half_stops_being_run(w: World) -> None:
+ """REGRESSION: one of the eight demonstrated Conformance 1-4 bad halves
+ disappears from the recorded run.
+
+ "Demonstrated by running it" is the whole clause; a bad half that stopped
+ being executed is a claim again, and nothing else in the ledger notices.
+ """
+ w.replace(
+ TIER_B_SUMMARY,
+ '"bad-wordless-chips/L1/dark"',
+ '"bad-wordless-chips-DISABLED/L1/dark"',
+ )
def _mo031_a_red_core_row_goes_green(w: World) -> None:
@@ -715,6 +853,11 @@ def _mo034_the_changelog_records_a_look(w: World) -> None:
"the --amber alias acquires its own literal value (a bespoke fourth status hue)",
_mo002_alias_becomes_a_bespoke_hue,
),
+ Mutation(
+ "OSV1-003",
+ "the browser measures a calm L1 painting ZERO --blocked pixels (the fix)",
+ _mo003_the_calm_page_stops_painting_blocked,
+ ),
Mutation(
"OSV1-003",
"the retired palette a calm sweep would catch is tokenised away",
@@ -749,15 +892,24 @@ def _mo034_the_changelog_records_a_look(w: World) -> None:
),
Mutation(
"OSV1-008",
- "the body-swap starts restoring the pause flag",
- _mo008_swap_restores_the_pause_flag,
+ "the browser measures an open `` surviving the swap (the fix)",
+ _mo008_the_swap_starts_restoring_the_disclosure,
+ ),
+ Mutation(
+ "OSV1-008",
+ "the browser measures the pause CONTROL surviving the swap (the fix)",
+ _mo008_the_pause_control_starts_surviving,
),
Mutation(
"OSV1-009",
"the below-floor token stops painting the empty-state caption",
_mo009_the_below_floor_token_stops_painting_copy,
),
- Mutation("OSV1-010", "a browser driver appears in the repo", _mo010_a_browser_driver_appears),
+ Mutation(
+ "OSV1-010",
+ "the browser measures every interactive control on L0 reaching 44px (the fix)",
+ _mo010_the_target_floor_is_met,
+ ),
Mutation(
"OSV1-011",
"reduced motion becomes per-widget opt-in (a second @media block)",
@@ -789,15 +941,32 @@ def _mo034_the_changelog_records_a_look(w: World) -> None:
"the push channel acquires a second call site",
_mo017_a_second_push_call_site_appears,
),
- Mutation("OSV1-020", "the Tier-B kit file appears", _mo_tier_b_kit_appears),
+ Mutation(
+ "OSV1-020",
+ "Conformance 1's injected-alarm-chip bad half stops catching its defect",
+ _mo020_the_calm_bad_half_stops_discriminating,
+ ),
Mutation("OSV1-021", "the Tier-A kit file appears", _mo_tier_a_kit_appears),
- Mutation("OSV1-022", "the Tier-B kit file appears", _mo_tier_b_kit_appears),
+ Mutation(
+ "OSV1-021",
+ "a rendered status chip loses its word (the Tier-B half that HAS landed)",
+ _mo021_the_rendered_chips_lose_their_word,
+ ),
+ Mutation(
+ "OSV1-022",
+ "Conformance 3's naive-replacement bad half stops losing the open disclosure",
+ _mo022_the_swap_bad_half_stops_discriminating,
+ ),
Mutation(
"OSV1-022",
"the whole-body innerHTML swap (the bad fixture's own premise) changes shape",
_mo022_the_swap_mechanism_changes,
),
- Mutation("OSV1-023", "the Tier-B kit file appears", _mo_tier_b_kit_appears),
+ Mutation(
+ "OSV1-023",
+ "Conformance 4's contrast bad half loses its dark-mode control",
+ _mo023_the_contrast_bad_half_loses_its_control,
+ ),
Mutation(
"OSV1-023",
"a swept breakpoint disappears from the stylesheet",
@@ -812,14 +981,22 @@ def _mo034_the_changelog_records_a_look(w: World) -> None:
"the Makefile wires a conformance target (the 'runs in a gate' half)",
_mo027_the_makefile_wires_the_kit,
),
- Mutation("OSV1-028", "the Tier-B kit file appears", _mo_tier_b_kit_appears),
- Mutation("OSV1-029", "the Tier-B kit file appears", _mo_tier_b_kit_appears),
+ Mutation(
+ "OSV1-028",
+ "the manifest's chromium pin moves while the recorded numbers stay behind",
+ _mo028_the_browser_pin_moves_without_a_re_run,
+ ),
Mutation(
"OSV1-029",
- "a Tier-B artifact directory appears",
- _mo029_an_artifact_directory_appears,
+ "the Tier-B kit stops reading its own artifacts back before asserting",
+ _mo029_the_kit_stops_reading_its_artifacts_back,
),
Mutation("OSV1-030", "the Tier-A kit file appears", _mo_tier_a_kit_appears),
+ Mutation(
+ "OSV1-030",
+ "one of the demonstrated Conformance 1-4 bad halves stops being run",
+ _mo030_a_demonstrated_bad_half_stops_being_run,
+ ),
Mutation(
"OSV1-031",
"one of the ten red Core-carrying rows flips to CONFORMS",
diff --git a/ledger/checks/test_operator_rows.py b/ledger/checks/test_operator_rows.py
index b6fe543..223387d 100644
--- a/ledger/checks/test_operator_rows.py
+++ b/ledger/checks/test_operator_rows.py
@@ -41,6 +41,7 @@
from __future__ import annotations
+import json
import re
from ._support import (
@@ -87,6 +88,53 @@
MAKEFILE = REPO_ROOT / "Makefile"
CI_WORKFLOW = REPO_ROOT / ".github" / "workflows" / "ci.yml"
+#: The Tier-B kit's COMMITTED run summary -- the bridge that lets this
+#: in-process, browserless ledger re-read numbers a real chromium produced
+#: (Freeze 3, OSV1-029). Written by
+#: `tests/conformance/operator_surface/browser/_artifacts.py`; the per-run
+#: artifact directory beside it is gitignored and unreadable from here.
+TIER_B_SUMMARY = "tests/conformance/operator_surface/browser/LAST_RUN.json"
+TIER_B_SUMMARY_SCHEMA = "operator-surface-tier-b/1"
+
+
+def tier_b_summary() -> dict:
+ """The Tier-B run summary, parsed, with its envelope checked.
+
+ Fails loud rather than returning `{}`: a probe that quietly read an empty
+ summary would report "no violation measured" for every Tier-B row at once,
+ which is precisely the hollow green Freeze 3 exists to prevent. Read
+ through `read()` so the mutation harness's injected world is what a
+ mutated run sees.
+ """
+ path = REPO_ROOT / TIER_B_SUMMARY
+ assert path.exists(), (
+ f"{TIER_B_SUMMARY} is missing. Every Tier-B row re-reads its verdict "
+ f"from it -- run `make test-conformance-b` to regenerate it."
+ )
+ data = json.loads(read(path))
+ assert data.get("schema") == TIER_B_SUMMARY_SCHEMA, (
+ f"{TIER_B_SUMMARY} declares schema {data.get('schema')!r}, expected "
+ f"{TIER_B_SUMMARY_SCHEMA!r} -- the artifact shape moved under the ledger."
+ )
+ assert data.get("checks"), f"{TIER_B_SUMMARY} carries no checks"
+ return data
+
+
+def tier_b(check: str, scenario: str) -> dict:
+ """One headline out of the run summary, or a loud failure naming what is there."""
+ checks = tier_b_summary()["checks"]
+ assert check in checks, (
+ f"the Tier-B run summary has no `{check}` check (it has "
+ f"{sorted(checks)}). Either the kit stopped emitting it or the run was "
+ f"partial -- re-run `make test-conformance-b`."
+ )
+ scenarios = checks[check]
+ assert scenario in scenarios, (
+ f"the Tier-B run summary has no `{check}` / `{scenario}` scenario (it has "
+ f"{sorted(scenarios)})."
+ )
+ return scenarios[scenario]
+
def _exists(rel: str) -> bool:
return (REPO_ROOT / rel).exists()
@@ -235,14 +283,28 @@ def test_row_osv1_002() -> None:
def test_row_osv1_003() -> None:
- """Core 2 GAP pin (calm pixels): no pixel sweep exists, and the retired
- palette that a sweep would catch is still in the tree.
+ """Core 2 VIOLATION pin, RE-READ from the browser run's own numbers.
+
+ Not "a file appeared" and not the browser tier's pass/fail: the calm sweep
+ wrote pixel counts, and this reads them back. L0 is clean in both themes;
+ L1 paints `--blocked` on a page with nothing blocked, and THAT is the
+ pinned violation.
"""
- assert not _exists(TIER_B_KIT), (
- f"OSV1-003 (Core 2): {TIER_B_KIT} now exists. Re-derive this row from the "
- f"sweep's EMITTED ARTIFACT (Freeze 3 / ruling 6) -- never from the fact that "
- f"a file appeared (work_item_pipeline-qgo)."
- )
+ for theme in ("dark", "light"):
+ clean = tier_b("calm.zero_alarm_pixels", f"calm/L0/{theme}")
+ assert clean["alarm"] == 0 and clean["blocked"] == 0, (
+ f"OSV1-003 (Core 2): a calm L0 in {theme} now paints "
+ f"{clean['alarm']} --alarm and {clean['blocked']} --blocked pixels. "
+ f"L0 was the CLEAN half of this row -- a regression, not progress."
+ )
+ dirty = tier_b("calm.zero_alarm_pixels", f"calm/L1/{theme}")
+ assert dirty["blocked"] == 97, (
+ f"OSV1-003 (Core 2) PIN MOVED: a calm L1 in {theme} painted "
+ f"{dirty['blocked']} --blocked pixels, pinned at 97. If the legend "
+ f"swatch, the live dot and the danger button stopped painting "
+ f"`--blocked` on a calm page, re-derive this row from the new sweep "
+ f"(work_item_pipeline-qgo)."
+ )
assert contains(WEBPWA, "background:#0D0D0C;color:#F2EEE6"), (
"OSV1-003 (Core 2): the retired palette at webpwa.py:121-122 is gone. That is "
"the Conformance 1 bad fixture's specimen and a Core 4 violation closing "
@@ -255,9 +317,6 @@ def test_row_osv1_003() -> None:
)
-# --------------------------------------------------------------- OSV1-004
-
-
def test_row_osv1_004() -> None:
"""Core 3 GAP pin: no rendered-fixture kit, and the chip vocabulary that
the kit will check is still the five-word map measured at seed.
@@ -452,38 +511,53 @@ def test_row_osv1_007() -> None:
def test_row_osv1_008() -> None:
- """Core 6 GAP pin: no browser kit, and the swap restores exactly two
- things -- neither of them the pause control or a live region.
+ """Core 6 VIOLATION pin: one of four survivals holds, RE-READ from the run.
+
+ Pinned in BOTH directions per survival, because they are separable and a
+ fix to any one of them is progress this row must record rather than
+ absorb.
"""
- assert not _exists(TIER_B_KIT), (
- f"OSV1-008 (Core 6): {TIER_B_KIT} now exists -- re-derive this row from the "
- f"post-swap DOM SNAPSHOT it emits (work_item_pipeline-qgo)."
- )
- theme = read(WEBTHEME)
- assert "function captureState()" in theme and "function restoreState(state)" in theme, (
- "OSV1-008 (Core 6): the swap's capture/restore pair is gone -- the mechanism "
- "this row measures moved. Re-derive."
- )
- restore = theme[theme.index("function restoreState(state)") :][:400]
- assert "openIds" in restore and "scrollTo" in restore, (
- "OSV1-008 (Core 6): `restoreState` no longer restores open disclosures and scroll position."
- )
- assert "__wtRefreshPaused" not in restore, (
- "OSV1-008 (Core 6) PIN BROKE THE RIGHT WAY: `restoreState` now touches the "
- "pause flag. The pause CONTROL's state surviving the swap is one of the four "
- "things Core 6 names -- flip the row only after the browser kit MEASURES it, "
- "and retarget this probe in the same change (work_item_pipeline-qgo)."
- )
+ for level in ("L0", "L1"):
+ m = tier_b("swap.survives", f"calm/{level}/dark")
+ assert m["scroll_preserved"], (
+ f"OSV1-008 (Core 6): scroll offset stopped surviving the body-swap on "
+ f"{level}. That was the ONE of Core 6's four named survivals that held "
+ f"-- a regression."
+ )
+ assert not m["open_details_preserved"], (
+ f"OSV1-008 (Core 6) PIN BROKE THE RIGHT WAY on {level}: an open "
+ f"`` now survives the swap. Confirm it survives because the "
+ f"markup gained ids and `restoreState` reaches them, then re-derive "
+ f"this row (work_item_pipeline-qgo)."
+ )
+ assert m["details_with_id"] == 0, (
+ f"OSV1-008 (Core 6) PIN BROKE THE RIGHT WAY on {level}: "
+ f"{m['details_with_id']} `` now carry an id. `restoreState` "
+ f"only ever re-opens `details[id]`, so this is the mechanism acquiring "
+ f"its first targets -- re-derive from the new swap measurement."
+ )
+ assert not m["pause_control_preserved"], (
+ f"OSV1-008 (Core 6) PIN BROKE THE RIGHT WAY on {level}: the pause "
+ f"CONTROL's state now survives the swap. Re-derive this row."
+ )
+ assert m["pause_flag_preserved"], (
+ f"OSV1-008 (Core 6): `window.__wtRefreshPaused` stopped surviving the "
+ f"swap on {level}. The flag living on `window` is why polling stays "
+ f"paused at all -- a regression."
+ )
+ assert m["live_regions_before"] == 0, (
+ f"OSV1-008 (Core 6) PIN BROKE THE RIGHT WAY on {level}: the page now "
+ f"renders {m['live_regions_before']} live region(s). Core 6's "
+ f"announcement half had NOTHING to preserve at this measurement -- "
+ f"re-derive this row from whether the region SURVIVES the swap."
+ )
assert count(WEBAPP, "aria-live") == 0 and count(WEBTHEME, "aria-live") == 0, (
- "OSV1-008 (Core 6) PIN BROKE THE RIGHT WAY: an `aria-live` region appeared. "
- "Core 6's announcement half had NO implementation at seed -- if one landed, "
- "re-derive this row from the Tier-B snapshot rather than from its presence."
+ "OSV1-008 (Core 6) PIN BROKE THE RIGHT WAY: an `aria-live` region appeared "
+ "in the source. Re-derive this row from the Tier-B snapshot rather than "
+ "from its presence."
)
-# --------------------------------------------------------------- OSV1-009
-
-
def test_row_osv1_009() -> None:
"""Core 7 VIOLATION pin: the token-pair luminance math, RE-RUN here.
@@ -531,27 +605,45 @@ def test_row_osv1_009() -> None:
def test_row_osv1_010() -> None:
- """Core 7 GAP pin (rendered half): nothing in this repo drives a browser."""
- assert not _exists(TIER_B_KIT), (
- f"OSV1-010 (Core 7): {TIER_B_KIT} now exists -- re-derive this row from the "
- f"computed contrast ratios, bounding boxes and motion trace it EMITS, which "
- f"the ledger must re-check itself (Freeze 3 / ruling 6)."
- )
- for driver in BROWSER_DRIVERS:
- assert not _repo_mentions(driver), (
- f"OSV1-010 (Core 7) PIN BROKE THE RIGHT WAY: {driver!r} now appears in the "
- f"repo. A browser is being driven somewhere -- re-derive this row, and "
- f"OSV1-003, -008, -020..-023, from what it actually measures."
- )
+ """Core 7 VIOLATION pin (rendered half), RE-READ from the browser run.
+
+ Four floors, measured across 18 renders. Three fail and one passes, and
+ all four are pinned: a fix to any one is progress this row must record.
+ """
+ l0 = tier_b("perception.floors", "calm/L0/1280/dark")
+ l1 = tier_b("perception.floors", "calm/L1/1280/dark")
+ l1_light = tier_b("perception.floors", "calm/L1/1280/light")
+
+ assert l0["text_below_floor"] == 0, (
+ f"OSV1-010 (Core 7): L0 now has {l0['text_below_floor']} text elements below "
+ f"4.5:1. L0 was the CLEAN level for text contrast -- a regression."
+ )
+ assert l1["text_below_floor"] == 3 and l1_light["text_below_floor"] == 5, (
+ f"OSV1-010 (Core 7) PIN MOVED: L1 text below 4.5:1 measured "
+ f"{l1['text_below_floor']} dark / {l1_light['text_below_floor']} light, "
+ f"pinned at 3 / 5. Movement in either direction means the render changed "
+ f"-- re-derive (work_item_pipeline-qgo)."
+ )
+ assert l0["controls_below_44px"] == 26 and l0["controls"] == 35, (
+ f"OSV1-010 (Core 7) PIN MOVED: L0 measured {l0['controls_below_44px']} of "
+ f"{l0['controls']} interactive controls under 44px, pinned at 26 of 35."
+ )
+ assert l0["non_text_below_floor"] > 0, (
+ "OSV1-010 (Core 7) PIN BROKE THE RIGHT WAY: every measured control border "
+ "and icon stroke on L0 now meets 3:1. Re-derive this row."
+ )
+ assert l0["running_animations_under_reduced_motion"] == 0, (
+ f"OSV1-010 (Core 7): {l0['running_animations_under_reduced_motion']} "
+ f"animation(s) now run under `prefers-reduced-motion: reduce`. That floor "
+ f"PASSED at this measurement -- a regression, and Core 7's kernel-rule half "
+ f"(OSV1-011) with it."
+ )
assert contains(WEBTHEME, "--u:44px"), (
"OSV1-010 (Core 7): the 44px target token is gone -- the thing the Tier-B "
"bounding-box check exists to verify."
)
-# --------------------------------------------------------------- OSV1-011
-
-
def test_row_osv1_011() -> None:
"""Core 7 CONFORMS: reduced motion is ONE kernel-level rule.
@@ -816,12 +908,32 @@ def test_row_osv1_017() -> None:
def test_row_osv1_020() -> None:
- """Conformance 1 pin: no browser kit, so no calm pixel sweep exists."""
- assert not _exists(TIER_B_KIT), (
- f"OSV1-020 (Conformance 1): {TIER_B_KIT} now exists. Freeze 4 requires the BAD "
- f"half to fail against the defect it names, DEMONSTRATED BY RUNNING IT -- "
- f"re-derive this row from that demonstration, never from the file's existence "
- f"(work_item_pipeline-qgo)."
+ """Conformance 1 CONFORMS: the fixture exists AND its bad halves caught the
+ defects they name -- re-read, not taken on the kit's word.
+
+ Deliberately not `_exists(TIER_B_KIT)` alone. Freeze 4 is about a bad half
+ that FAILS against its defect, demonstrated by running it, so this reads
+ the three demonstrations' own numbers back out of the run summary.
+ """
+ assert _exists(TIER_B_KIT), (
+ f"OSV1-020 (Conformance 1): {TIER_B_KIT} is gone -- the fixture this row "
+ f"records no longer exists."
+ )
+ chip = tier_b("calm.zero_alarm_pixels", "bad-alarm-chip/L0/dark")
+ assert chip["alarm"] > 0, (
+ f"OSV1-020 (Conformance 1): the injected `var(--alarm)` chip bad half "
+ f"reported {chip['alarm']} --alarm pixels. A bad half that no longer "
+ f"discriminates makes the good half vacuous."
+ )
+ retired = tier_b("calm.zero_alarm_pixels", "bad-retired-palette/L0/dark")
+ assert retired["retired_amber"] > 0, (
+ "OSV1-020 (Conformance 1): the contract's own named bad half -- the retired "
+ "#D9A253 palette region reinstated -- reported zero pixels of it."
+ )
+ real = tier_b("calm.zero_alarm_pixels", "bad-alarm-fixture/L0/dark")
+ assert real["alarm"] > 0 or real["blocked"] > 0, (
+ "OSV1-020 (Conformance 1): the genuinely-alarming fixture (one real blocked "
+ "item, nothing injected) painted no reserved status hue at all."
)
assert contains(
OPERATOR_CONTRACT_PATH, "the sweep reports zero pixels matching `--alarm` or `--blocked`"
@@ -829,23 +941,69 @@ def test_row_osv1_020() -> None:
def test_row_osv1_021() -> None:
- """Conformance 2 pin: NEITHER named kit exists (this fixture spans tiers)."""
- assert not _exists(TIER_A_KIT) and not _exists(TIER_B_KIT), (
- "OSV1-021 (Conformance 2): a named kit path appeared. This is the one fixture "
- "that spans BOTH tiers -- it goes green only when the Tier-A accessible-name "
- "half (work_item_pipeline-c1a) AND the Tier-B hue half "
- "(work_item_pipeline-qgo) both land and both discriminate."
- )
+ """Conformance 2 GAP pin: the Tier-B half landed, the Tier-A half has not.
+
+ This fixture spans both tiers, so the row stays red for the half that is
+ still missing -- and the probe proves the landed half is real rather than
+ just present, so "half of it works" cannot decay unnoticed while the other
+ half is waited on.
+ """
+ assert not _exists(TIER_A_KIT), (
+ f"OSV1-021 (Conformance 2) PIN BROKE THE RIGHT WAY: {TIER_A_KIT} now exists. "
+ f"This is the one fixture spanning BOTH tiers -- flip it to CONFORMS once "
+ f"the Tier-A accessible-name half discriminates too "
+ f"(work_item_pipeline-c1a); the Tier-B half already does."
+ )
+ hue = tier_b("alarm.reserved_hue", "alarm/L1/dark")
+ assert hue["alarm"] > 0 or hue["blocked"] > 0, (
+ "OSV1-021 (Conformance 2): the Tier-B hue half stopped measuring -- the "
+ "alarm render painted neither --alarm nor --blocked."
+ )
+ for level in ("L0", "L1", "L2"):
+ words = tier_b("state.not_colour_only", f"alarm/{level}/dark")
+ assert words["status_elements"] > 0, (
+ f"OSV1-021 (Conformance 2): no status-bearing element was found on "
+ f"{level} in the render -- the check cannot pass vacuously."
+ )
+ assert words["wordless"] == 0, (
+ f"OSV1-021 (Conformance 2): {words['wordless']} status-bearing element(s) "
+ f"on {level} now carry a class and no word. Core 3's rendered half was "
+ f"CLEAN at this measurement -- a regression (OSV1-004)."
+ )
assert contains(
OPERATOR_CONTRACT_PATH, "fixture whose status chips carry only a status class"
), "OSV1-021: Conformance 2's bad half moved in the contract -- re-review the row."
def test_row_osv1_022() -> None:
- """Conformance 3 pin: no browser kit, so body-swap survival is untested."""
- assert not _exists(TIER_B_KIT), (
- f"OSV1-022 (Conformance 3): {TIER_B_KIT} now exists -- re-derive from the "
- f"post-swap DOM snapshot and the demonstrated bad half (work_item_pipeline-qgo)."
+ """Conformance 3 CONFORMS: both bad halves ran and both caught their defect.
+
+ Two of them, because the contract's literal bad half does not discriminate
+ on scroll here (a synchronous whole-body replacement preserves the offset
+ by itself on chromium 148); the reflow variant does. Both are re-read.
+ """
+ assert _exists(TIER_B_KIT), (
+ f"OSV1-022 (Conformance 3): {TIER_B_KIT} is gone -- the fixture this row "
+ f"records no longer exists."
+ )
+ naive = tier_b("swap.survives", "bad-naive-replacement/L0/dark")
+ assert not naive["open_details_preserved"], (
+ "OSV1-022 (Conformance 3): the contract's literal bad half -- a naive "
+ "whole-body innerHTML replacement -- no longer loses the open ``, "
+ "so it discriminates against nothing."
+ )
+ reflow = tier_b("swap.survives", "bad-naive-replacement-reflow/L0/dark")
+ assert not reflow["scroll_preserved"], (
+ "OSV1-022 (Conformance 3): the reflow bad half no longer loses the scroll "
+ "offset. It exists precisely because the literal bad half cannot "
+ "discriminate on scroll -- without it, the good half's scroll assertion is "
+ "unproven."
+ )
+ good = tier_b("swap.survives", "calm/L0/dark")
+ assert good["scroll_preserved"] and not good["open_details_preserved"], (
+ "OSV1-022 (Conformance 3): the good half's own outcome moved (scroll "
+ f"{good['scroll_preserved']}, disclosures {good['open_details_preserved']}) "
+ f"-- re-derive this row and OSV1-008 together."
)
assert contains(WEBTHEME, "document.body.innerHTML = doc.body.innerHTML"), (
"OSV1-022 (Conformance 3): the whole-body innerHTML swap is gone. That IS the "
@@ -855,11 +1013,49 @@ def test_row_osv1_022() -> None:
def test_row_osv1_023() -> None:
- """Conformance 4 pin: no viewport sweep, and the breakpoints it must sweep."""
- assert not _exists(TIER_B_KIT), (
- f"OSV1-023 (Conformance 4): {TIER_B_KIT} now exists -- re-derive from the "
- f"emitted scrollWidth/clientWidth, bounding-box and contrast numbers "
- f"(work_item_pipeline-qgo)."
+ """Conformance 4 CONFORMS: the sweep covers what the clause names, and its
+ bad halves ran.
+
+ The contrast bad half carries its own control (the same token pair must
+ come back below the floor in light and above it in dark), which is what
+ separates "the probe measures" from "the probe always says no".
+ """
+ assert _exists(TIER_B_KIT), (
+ f"OSV1-023 (Conformance 4): {TIER_B_KIT} is gone -- the fixture this row "
+ f"records no longer exists."
+ )
+ summary = tier_b_summary()["checks"]["perception.floors"]
+ swept = {s for s in summary if s.startswith("calm/")}
+ expected = {
+ f"calm/{level}/{width}/{theme}"
+ for level in ("L0", "L1", "L2")
+ for width in (430, 900, 1280)
+ for theme in ("dark", "light")
+ }
+ assert swept == expected, (
+ f"OSV1-023 (Conformance 4): the sweep no longer covers L0/L1/L2 at 430, 900 "
+ f"and 1280px in both themes. Missing: {sorted(expected - swept)}; "
+ f"unexpected: {sorted(swept - expected)}."
+ )
+ wide = summary.get("bad-wide-element/L0/430/dark")
+ assert wide and wide["elements_beyond_viewport_moved"], (
+ "OSV1-023 (Conformance 4): the overflow bad half -- a 900px fixed-width "
+ "element at a 430px viewport -- no longer moves the element-level reading."
+ )
+ assert wide["scroll_width_moved"] is False, (
+ "OSV1-023 (Conformance 4) PIN BROKE THE RIGHT WAY: the injected wide element "
+ "now DOES move `scrollWidth`. That means `overflow-x: clip` is gone from the "
+ "surface, the clause's literal metric has become discriminating, and this "
+ "row's note about it should be re-derived."
+ )
+ low = summary.get("bad-low-contrast/L0/light")
+ high = summary.get("bad-low-contrast/L0/dark")
+ assert low and high, "OSV1-023 (Conformance 4): the contrast bad half did not run."
+ assert low["min_ratio"] < 4.5 <= high["min_ratio"], (
+ f"OSV1-023 (Conformance 4): the recorded --ink-quiet/--ground pair measured "
+ f"{low['min_ratio']} light / {high['min_ratio']} dark. The bad half needs "
+ f"BOTH -- below the floor in light AND above it in dark -- or it is not "
+ f"demonstrating a measurement."
)
theme = read(WEBTHEME)
for width in ("1280px", "900px", "430px"):
@@ -924,56 +1120,157 @@ def test_row_osv1_027() -> None:
# Matched on the KIT PATH, never on the word "conformance": the Makefile
# and ci.yml already say "conformance ledger" about Tier 4, and a probe
# that a pre-existing comment satisfies is a probe asserting nothing.
- assert "tests/conformance" not in read(MAKEFILE), (
- "OSV1-027 (Freeze 1): the Makefile now has a target covering tests/conformance "
- "-- the wiring half is landing. Re-derive."
- )
- assert "tests/conformance" not in read(CI_WORKFLOW), (
- "OSV1-027 (Freeze 1): ci.yml now runs tests/conformance -- the 'runs on every "
- "pull request' half is landing. Re-derive."
- )
+ #
+ # NARROWED 2026-09-05 by work_item_pipeline-qgo (an out-of-scope edit to
+ # THIS row's probe, named in that lane's summary): when this pin was
+ # written no `tests/conformance` path was wired anywhere, so bare
+ # containment WAS the Tier-A signal. The Tier-B browser kit is now wired
+ # (Makefile `test-conformance-b`, CI "Tier 7"), which would fire this pin
+ # for a reason that has nothing to do with Freeze 1. The Tier-B path is
+ # therefore removed before the check, leaving the original question
+ # intact: is any conformance wiring OTHER than Tier-B's present, i.e. is
+ # the Tier-A half landing? Neither the disposition nor the row moved.
+ tier_b_dir = TIER_B_KIT.rsplit("/", 1)[0]
+ for path, where in ((MAKEFILE, "the Makefile"), (CI_WORKFLOW, "ci.yml")):
+ remainder = read(path).replace(tier_b_dir, "").replace(TIER_B_KIT, "")
+ assert "tests/conformance" not in remainder, (
+ f"OSV1-027 (Freeze 1): {where} now wires a conformance path that is not "
+ f"the Tier-B browser kit -- the Tier-A wiring half is landing. Re-derive "
+ f"(work_item_pipeline-c1a)."
+ )
def test_row_osv1_028() -> None:
- """Freeze 2 pin: no browser kit path, and no browser driver anywhere."""
- assert not _exists(TIER_B_KIT), (
- f"OSV1-028 (Freeze 2): {TIER_B_KIT} now exists. Freeze 2 has THREE further "
- f"conditions -- a PINNED chromium, ISOLATED fixture data, and its OWN CI tier. "
- f"A partial build satisfies none of them (work_item_pipeline-qgo)."
- )
- for driver in BROWSER_DRIVERS:
- assert not _repo_mentions(driver), (
- f"OSV1-028 (Freeze 2): {driver!r} now appears in the repo -- a browser is "
- f"being driven. Re-derive this row and every row that depends on it."
- )
+ """Freeze 2 CONFORMS: all four sub-conditions, checked independently.
+
+ The seed said a partial build satisfies none of them, so none of them is
+ inferred from another: the path, the PIN, the isolation and the OWN TIER
+ are four separate assertions.
+ """
+ assert _exists(TIER_B_KIT), f"OSV1-028 (Freeze 2): {TIER_B_KIT} is gone."
+ pinned = "playwright=="
+ manifest = read(PYPROJECT)
+ assert pinned in manifest, (
+ "OSV1-028 (Freeze 2): `pyproject.toml` no longer pins playwright to an EXACT "
+ "version. A playwright release pins exactly one chromium build, and that pin "
+ "is the whole of 'a pinned chromium' -- an unpinned browser makes every "
+ "recorded contrast ratio and pixel count unreproducible."
+ )
+ version = manifest.split(pinned, 1)[1].split('"', 1)[0].strip()
+ recorded = tier_b_summary()["browser"]
+ assert recorded.get("playwright") == version, (
+ f"OSV1-028 (Freeze 2): the manifest pins playwright {version!r} but the "
+ f"Tier-B run summary was produced by {recorded.get('playwright')!r}. The "
+ f"recorded numbers came from a different browser build than the one the repo "
+ f"now pins -- re-run `make test-conformance-b`."
+ )
+ assert recorded.get("name") == "chromium" and recorded.get("version"), (
+ f"OSV1-028 (Freeze 2): the run summary names no chromium build ({recorded})."
+ )
-def test_row_osv1_029() -> None:
- """Freeze 3 pin: there is no Tier-B artifact for the orchestrator to re-check.
+ makefile = read(MAKEFILE)
+ assert "test-conformance-b:" in makefile and "playwright-install" in makefile, (
+ "OSV1-028 (Freeze 2): the Makefile no longer carries `test-conformance-b` "
+ "(and its `playwright-install` dependency). Existing is not the same as "
+ "running."
+ )
+ ci = read(CI_WORKFLOW)
+ assert "Tier 7 -- operator-surface conformance (Tier B, browser)" in ci, (
+ "OSV1-028 (Freeze 2): CI no longer runs the browser tier as its OWN step. A "
+ "browser tier folded into another tier is not the tier Freeze 2 asks for."
+ )
+ assert "-m tier_b" in ci and "playwright install" in ci, (
+ "OSV1-028 (Freeze 2): the CI step no longer selects the `tier_b` marker or no "
+ "longer installs chromium -- either way the tier runs nothing."
+ )
+ kit = read(REPO_ROOT / "tests/conformance/operator_surface/browser/conftest.py")
+ assert "isolated_dolt_server" in kit and "port=0" in kit, (
+ "OSV1-028 (Freeze 2): the kit no longer states its isolation -- the inherited "
+ "isolated dolt server, or the ephemeral `port=0` bind. The live service must "
+ "be unreachable from this tier by construction, not by luck."
+ )
- Pinned even though it is vacuously unmet today, because this is the
- condition most likely to be quietly skipped once a browser tier exists and
- its own assertions look green (PROTOCOL.md pillar 2).
+
+def test_row_osv1_029() -> None:
+ """Freeze 3 CONFORMS: artifacts are emitted, AND this ledger re-reads them.
+
+ The row's own closing condition, written at seed: "a stable on-disk
+ artifact path with a documented JSON shape, and ledger probes here that
+ RE-READ those numbers rather than trusting the browser test's own
+ pass/fail." This probe checks both halves -- the summary parses and names
+ its schema and browser, and the kit is structurally incapable of asserting
+ on a screenshot.
"""
- assert not _exists(TIER_B_KIT), (
- f"OSV1-029 (Freeze 3): {TIER_B_KIT} now exists. This row is NOT satisfied by "
- f"the kit passing -- it is satisfied when the kit EMITS artifacts (contrast "
- f"numbers, pixel counts, bounding boxes, DOM snapshots) that ledger probes "
- f"here re-read for themselves. Until the ledger re-checks them, a green "
- f"Tier-B tier is a self-report (work_item_pipeline-qgo)."
+ data = tier_b_summary()
+ assert data.get("recorded_at") and data.get("browser"), (
+ "OSV1-029 (Freeze 3): the Tier-B run summary carries no provenance "
+ "(recorded_at / browser). A number with no named engine behind it is not "
+ "reproducible, which is the whole reason Freeze 2 pins the browser."
+ )
+ for check in ("calm.zero_alarm_pixels", "swap.survives", "perception.floors"):
+ assert data["checks"].get(check), (
+ f"OSV1-029 (Freeze 3): the run summary carries no `{check}` numbers. "
+ f"Every Tier-B check must emit artifacts the orchestrator re-checks."
+ )
+
+ kit = read(REPO_ROOT / TIER_B_KIT)
+ writes, reads = kit.count("artifacts.write("), kit.count("_artifacts.read(")
+ assert writes > 0 and reads >= writes, (
+ f"OSV1-029 (Freeze 3): the Tier-B kit performs {writes} artifact write(s) but "
+ f"only {reads} read-back(s). Measure -> write -> read back -> assert is the "
+ f"discipline this row records: a check that writes an artifact and then "
+ f"asserts on the value still in its own local variable is a self-report, and "
+ f"the artifact it left behind is decoration."
+ )
+ assert "save_screenshot" in kit and "assert" not in kit.split("save_screenshot")[0][-200:], (
+ "OSV1-029 (Freeze 3): a screenshot appears to be feeding an assertion. "
+ "Screenshots are evidence for a human's Freeze 8 look, never an input to a "
+ "pass -- 'no check reports a rendered impression as a pass'."
)
- assert not _exists("tests/conformance/operator_surface/browser/artifacts"), (
- "OSV1-029 (Freeze 3): an artifact directory appeared -- wire the ledger probes "
- "to re-read it, then flip this row."
+ artifacts_module = read(REPO_ROOT / "tests/conformance/operator_surface/browser/_artifacts.py")
+ assert "carries an empty measurement" in artifacts_module, (
+ "OSV1-029 (Freeze 3): the artifact reader no longer refuses an empty "
+ "measurement. A check that wrote `{}` and asserted `.get(..., 0) == 0` would "
+ "pass forever while measuring nothing."
)
def test_row_osv1_030() -> None:
- """Freeze 4 pin: neither kit exists, so no fixture has been demonstrated."""
- assert not _exists(TIER_A_KIT) and not _exists(TIER_B_KIT), (
- "OSV1-030 (Freeze 4): a kit path appeared. 'Demonstrated by running it' is the "
- "whole clause -- record WHICH revert produced WHICH failure, the way "
- "CCV1-023 did for the custody family, before flipping this row."
+ """Freeze 4 GAP pin: Conformance 1-4 demonstrated, 5-7 not.
+
+ Red for the half that has not been run, and the probe re-reads the half
+ that HAS -- so the demonstrated bad halves cannot quietly stop
+ discriminating while this row waits on the other kit.
+ """
+ assert not _exists(TIER_A_KIT), (
+ f"OSV1-030 (Freeze 4) PIN BROKE THE RIGHT WAY: {TIER_A_KIT} now exists. "
+ f"'Demonstrated by running it' is the whole clause -- record WHICH revert "
+ f"produced WHICH failure for Conformance 5-7, the way CCV1-023 did for the "
+ f"custody family, before flipping this row (work_item_pipeline-c1a)."
+ )
+ demonstrated = {
+ ("calm.zero_alarm_pixels", "bad-alarm-chip/L0/dark"),
+ ("calm.zero_alarm_pixels", "bad-retired-palette/L0/dark"),
+ ("calm.zero_alarm_pixels", "bad-alarm-fixture/L0/dark"),
+ ("state.not_colour_only", "bad-wordless-chips/L1/dark"),
+ ("swap.survives", "bad-naive-replacement/L0/dark"),
+ ("swap.survives", "bad-naive-replacement-reflow/L0/dark"),
+ ("perception.floors", "bad-wide-element/L0/430/dark"),
+ ("perception.floors", "bad-low-contrast/L0/light"),
+ }
+ checks = tier_b_summary()["checks"]
+ missing = sorted(f"{c}/{s}" for c, s in demonstrated if s not in checks.get(c, {}))
+ assert not missing, (
+ f"OSV1-030 (Freeze 4): {len(missing)} Conformance 1-4 bad half/halves were "
+ f"not run in the recorded Tier-B run: {missing}. A bad half that has never "
+ f"been executed is a claim."
+ )
+ stripped = checks["state.not_colour_only"]["bad-wordless-chips/L1/dark"]
+ assert stripped["stripped"] > 0 and stripped["wordless"] > 0, (
+ "OSV1-030 (Freeze 4): the Conformance 2 bad half stopped discriminating -- "
+ f"it stripped {stripped['stripped']} chips and still reported "
+ f"{stripped['wordless']} wordless."
)
diff --git a/ledger/rows.yaml b/ledger/rows.yaml
index bd3fa2b..fa4505f 100644
--- a/ledger/rows.yaml
+++ b/ledger/rows.yaml
@@ -1015,14 +1015,14 @@
(OSV1-003).
- id: OSV1-003
- title: a calm screen paints zero alarm and zero blocked pixels -- unmeasured, no kit
+ title: a calm L1 paints 97 --blocked pixels with nothing blocked
contract:
file: contracts/operator-surface.v1.md
clause: Core 2
quote: |
On a calm screen — nothing held past TTL, nothing blocked — zero `--alarm` and zero
`--blocked` pixels are painted; that absence is what makes the alarm pop.
- disposition: GAP
+ disposition: VIOLATION
work: work_item_pipeline-qgo
assertion:
kind: probe
@@ -1031,33 +1031,48 @@
PINNING ROW -- the probe asserts the CURRENT, KNOWN-WRONG shape on purpose;
a passing probe here is NOT conformance. Flip direction VIOLATION-MOVEMENT.
- GAP, REASON "KIT NOT BUILT" -- never NOT-ASSERTABLE. The contract names the
- check (`calm.zero_alarm_pixels`), names its tier (B) and names its file
- (`tests/conformance/operator_surface/browser/test_tier_b.py`, Conformance
- 1). It IS assertable; it simply is not asserted yet.
-
- MEASURED 2026-09-04 against 4aaee50: `tests/conformance` does not exist at
- all (`tests/` holds only cli/, integration/, unit/, conftest.py,
- _dolt_isolation.py, _util.py), and the repo contains ZERO occurrences of
- playwright, selenium, screenshot or axe-core -- so no pixel of this surface
- has ever been asserted by any test.
-
- KNOWN SPECIMEN THE SWEEP WOULD HAVE TO CATCH, measured here so the kit has
- a real target rather than a synthetic one: webpwa.py:121-122 renders the
- offline body with the RETIRED palette inline
- (`background:#0D0D0C;color:#F2EEE6`), and webtrust.py:258-259 declares
- `--ground:#0D0D0C; --ink:#F2EEE6; --amber:#D9A253` in its own `