diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dfec3ee..058b76e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -104,6 +104,15 @@ jobs: - name: Tier 4 -- conformance ledger run: .venv/bin/python -m pytest ledger/checks -v + # Tier 6 -- contracts/operator-surface.v1.md's Tier-A conformance kit. + # Freeze 1 requires it to exist AND to run on every pull request, so it + # is a named step of its own: a kit that runs in no gate is the exact + # failure ledger row CCV1-022 already recorded once. Runs AFTER the + # tiers above because its rendered checks need the same real `bd` and + # dolt those tiers install. + - name: Tier 6 -- operator-surface conformance (Tier A) + run: .venv/bin/python -m pytest tests/conformance/operator_surface -v + # Tier 5 -- the amplifier tool module's own suite # (modules/tool-work-tracker/tests). The only place the post-reclaim # custody behaviour of the AGENT SEAM (work_claim / work_declare / diff --git a/Makefile b/Makefile index 019ac82..18546f2 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: venv test test-unit test-integration test-cli test-ledger ledger-mutate test-module check lint types doctor clean +.PHONY: venv test test-unit test-integration test-cli test-ledger ledger-mutate test-conformance-a test-module check lint types doctor clean PYTHON ?= python3.12 VENV := .venv @@ -52,6 +52,15 @@ test-ledger: ledger-mutate: $(PY) -m ledger.checks.mutation_harness +## Tier 6 -- operator-surface conformance, Tier A: the machine checks +## contracts/operator-surface.v1.md names for its own Tier A, each as a +## discriminating good/bad pair (Freeze 1 requires this kit to exist AND to +## run on every pull request; Freeze 4 requires every bad half to have been +## demonstrated). Its static checks need nothing; its RENDERED checks stand up +## the isolated dolt server and skip loudly without a real `bd`. +test-conformance-a: + $(PYTEST) tests/conformance/operator_surface -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. @@ -65,7 +74,7 @@ ledger-mutate: test-module: $(PYTEST) modules/tool-work-tracker/tests -v -## All five tiers. Two pytest invocations (see `test-module` above), and +## Every tier. Two pytest invocations (see `test-module` above), and ## deliberately NOT fail-fast between them: the whole point of wiring the ## module suite in (ledger row CCV1-022) is that it stops being silently ## skippable, and a pre-existing failure in the root suite must not go back @@ -77,6 +86,11 @@ test: $(PYTEST) modules/tool-work-tracker/tests -v || rc=$$?; \ exit $$rc +## `test` above already COLLECTS tests/conformance (it is under `tests`), so +## this is the named, separately-runnable entry point rather than a second +## invocation -- running the kit twice in one `make test` would double its +## fixture cost and prove nothing new. + ## Lint + type-check. check: lint types diff --git a/ledger/checks/mutation_harness.py b/ledger/checks/mutation_harness.py index 505c75f..f9f9eca 100644 --- a/ledger/checks/mutation_harness.py +++ b/ledger/checks/mutation_harness.py @@ -58,7 +58,6 @@ from ._support import ( ADAPTER, AWARENESS, - CHARTSVG, CI_WORKFLOW, CLAIM_SKILL, CONTRACT_PATH, @@ -364,13 +363,19 @@ def _m023_test_location_regresses(w: World) -> None: # shape and must go red the moment the shape is right. The seven green rows get # the known-wrong shape they forbid instead (direction REGRESSION). # -# Eleven rows pin the absence of a conformance-kit file, and their +# Six rows still pin the absence of the TIER-B kit file, and their # counterfactual is `world.touch(...)` -- the file simply existing. That is a # WEAKER mutation than the source-shape ones, deliberately and visibly: the # thing being proven is only that the pin notices the path appearing, which is -# exactly what those rows claim and no more. Four of them carry a SECOND +# exactly what those rows claim and no more. Three of them carry a SECOND # mutation against their substantive half, so the weak half is never the only # evidence. +# +# The TIER-A kit now EXISTS (OSV1-027), so "the file appears" is spent as a +# counterfactual for the rows that used it. Each has been retargeted at what +# actually has to move for it to go green: for the three Conformance-fixture +# rows, the Core row their deferred good half waits on closing; for Freeze 1, +# CI dropping the step that runs the kit. # ============================================================================= TIER_A_KIT = op_probes.TIER_A_KIT @@ -381,14 +386,26 @@ def _mo000_operator_contract_moved(w: World) -> None: w.append(OPERATOR_CONTRACT_PATH, "\n\n") -def _mo001_hero_gains_the_missing_count(w: World) -> None: - """FIXED: the needs-attention count Core 1 names joins the strip.""" +def _mo001_the_needs_attention_count_is_dropped_again(w: World) -> None: + """REGRESSION: the count that was absent ANYWHERE at seed goes missing from + the L0 hero again -- the exact half of the defect this row closed. + + The CARD stays, so the strip still renders four of them: only the reading + it carries reverts to one Core 1 does not name. A probe that counted cards + instead of naming them would sail straight past this. + """ + w.replace(WEBAPP, 'label="Needs attention",', 'label="Agents active now",') + + +def _mo001b_the_hero_figure_stops_stating_its_window(w: World) -> None: + """REGRESSION: the throughput figure survives but its window does not -- + the OTHER half of the clause ("throughput over a STATED window"), and the + partial fix that would otherwise pass for conformance. + """ w.replace( - WEBAPP, - 'WD.KpiCard(key="ready", label="Ready", value=ready_total, href="#fleet"),', - 'WD.KpiCard(key="attention", label="Needs attention", value=0, href="#x"),\n' - " " - 'WD.KpiCard(key="ready", label="Ready", value=ready_total, href="#fleet"),', + WIDGETS, + 'f\'{_esc(data["velocity_window"])}\'', + '""', ) @@ -478,12 +495,23 @@ def _mo008_swap_restores_the_pause_flag(w: World) -> None: ) -def _mo009_the_below_floor_token_stops_painting_copy(w: World) -> None: - """FIXED: the empty-state caption moves off the below-floor token.""" +def _mo009a_the_media_light_block_regresses(w: World) -> None: + """REGRESSION: `--ink-quiet` falls back below the floor in the MEDIA block. + + One mutation per light block on purpose. webtheme.py keeps the two in sync + by comment alone, so a probe that noticed only the block it happened to read + first would credit a half-reverted colour as conformance -- which is exactly + the shape the seed pin recorded (three grounds x TWO blocks = six pairs). + """ + w.replace(WEBTHEME, " --ink-quiet:#596473;", " --ink-quiet:#7c8ba0;") + + +def _mo009b_the_attr_light_block_regresses(w: World) -> None: + """REGRESSION: the same fallback in the manual-toggle light block.""" w.replace( - CHARTSVG, - 'style="fill:var(--ink-quiet)">', - 'style="fill:var(--ink-tertiary)">', + WEBTHEME, + " --ink-quiet:#596473; /* likewise", + " --ink-quiet:#7c8ba0; /* likewise", ) @@ -501,9 +529,20 @@ def _mo011_a_second_motion_block_appears(w: World) -> None: ) -def _mo012_the_empty_sentence_becomes_a_zero(w: World) -> None: - """REGRESSION: the calm queue is celebrated as a numeral instead of said.""" - w.replace(WEBAPP, "Nothing is waiting to be claimed in this queue right now.", "0") +def _mo012_the_empty_slot_gains_its_sentence(w: World) -> None: + """FIXED: `render_attention_queue` grows the empty branch Core 8 asks for, + so the L0 region that keeps its slot finally says so.""" + w.replace( + WIDGETS, + " rows_html: list[str] = []\n" + ' for r in data["rows"]:\n' + ' priority_label = _esc(r["priority"].upper())', + ' if not data["rows"]:\n' + " return '
Nothing needs you right now.
'\n" + " rows_html: list[str] = []\n" + ' for r in data["rows"]:\n' + ' priority_label = _esc(r["priority"].upper())', + ) def _mo013_a_template_engine_is_declared(w: World) -> None: @@ -538,10 +577,6 @@ def _mo017_a_second_push_call_site_appears(w: World) -> None: w.append(WEBAPP, "\ndef _ledger_mutation():\n WP.fire_reclaim_alarm(1, 2, 3)\n") -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) @@ -569,9 +604,16 @@ def _mo023_a_swept_breakpoint_disappears(w: World) -> None: w.replace(WEBTHEME, "@media (max-width:1280px){", "@media (max-width:1281px){") -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") +def _mo027_ci_stops_running_the_kit(w: World) -> None: + """REGRESSION on the wiring half of Freeze 1: the kit still exists, but CI + no longer runs it -- which is exactly the "green claims nobody executed" + failure CCV1-022 already recorded once in this repo.""" + w.replace( + CI_WORKFLOW, + " - name: Tier 6 -- operator-surface conformance (Tier A)\n" + " run: .venv/bin/python -m pytest tests/conformance/operator_surface -v\n", + "", + ) def _mo029_an_artifact_directory_appears(w: World) -> None: @@ -586,8 +628,41 @@ def _mo031_a_red_core_row_goes_green(w: World) -> None: """ w.replace( ROWS_PATH, - " disposition: VIOLATION\n work: work_item_pipeline-ujy", - " disposition: CONFORMS\n work: work_item_pipeline-ujy", + " disposition: VIOLATION\n work: work_item_pipeline-8vv", + " disposition: CONFORMS\n work: work_item_pipeline-8vv", + ) + + +def _mo024_the_hero_good_half_is_deferred_again(w: World) -> None: + """REGRESSION: someone re-adds the xfail(strict) deferral to Conformance 5's + good half while the row still reads CONFORMS -- a green Conformance row whose + fixture no longer runs is exactly the claim-without-check Freeze 4 forbids. + """ + w.replace( + _support.REPO_ROOT / TIER_A_KIT, + "def test_hero_velocity_and_counts(calm_dataset) -> None:", + '@pytest.mark.xfail(strict=True, reason="OSV1-001 regressed")\n' + "def test_hero_velocity_and_counts(calm_dataset) -> None:", + ) + + +def _mo025_the_literal_style_row_goes_green(w: World) -> None: + """FIXED: OSV1-005 closes, so Conformance 6's deferred good half should now + pass -- and this row must be re-derived from the PASSING pair.""" + w.replace( + ROWS_PATH, + " disposition: VIOLATION\n work: work_item_pipeline-np3", + " disposition: CONFORMS\n work: work_item_pipeline-np3", + ) + + +def _mo026_the_empty_slot_row_goes_green(w: World) -> None: + """FIXED: OSV1-012 closes, so Conformance 7's deferred good halves should + now pass.""" + w.replace( + ROWS_PATH, + " disposition: VIOLATION\n work: work_item_pipeline-c1a", + " disposition: CONFORMS\n work: work_item_pipeline-c1a", ) @@ -707,8 +782,13 @@ def _mo034_the_changelog_records_a_look(w: World) -> None: ), Mutation( "OSV1-001", - "the missing needs-attention count joins the L0 strip", - _mo001_hero_gains_the_missing_count, + "the needs-attention count is dropped from the L0 hero again", + _mo001_the_needs_attention_count_is_dropped_again, + ), + Mutation( + "OSV1-001", + "the hero keeps its throughput figure but stops stating the window it covers", + _mo001b_the_hero_figure_stops_stating_its_window, ), Mutation( "OSV1-002", @@ -754,8 +834,13 @@ def _mo034_the_changelog_records_a_look(w: World) -> None: ), Mutation( "OSV1-009", - "the below-floor token stops painting the empty-state caption", - _mo009_the_below_floor_token_stops_painting_copy, + "--ink-quiet falls back below the text floor in the prefers-color-scheme light block", + _mo009a_the_media_light_block_regresses, + ), + Mutation( + "OSV1-009", + '--ink-quiet falls back below the text floor in the :root[data-theme="light"] block', + _mo009b_the_attr_light_block_regresses, ), Mutation("OSV1-010", "a browser driver appears in the repo", _mo010_a_browser_driver_appears), Mutation( @@ -765,8 +850,8 @@ def _mo034_the_changelog_records_a_look(w: World) -> None: ), Mutation( "OSV1-012", - "the calm queue's empty sentence becomes a bare zero", - _mo012_the_empty_sentence_becomes_a_zero, + "the empty attention queue grows the sentence Core 8 asks for", + _mo012_the_empty_slot_gains_its_sentence, ), Mutation( "OSV1-013", "the manifest declares a template engine", _mo013_a_template_engine_is_declared @@ -790,7 +875,7 @@ def _mo034_the_changelog_records_a_look(w: World) -> None: _mo017_a_second_push_call_site_appears, ), Mutation("OSV1-020", "the Tier-B kit file appears", _mo_tier_b_kit_appears), - Mutation("OSV1-021", "the Tier-A kit file appears", _mo_tier_a_kit_appears), + Mutation("OSV1-021", "the Tier-B kit file appears", _mo_tier_b_kit_appears), Mutation("OSV1-022", "the Tier-B kit file appears", _mo_tier_b_kit_appears), Mutation( "OSV1-022", @@ -803,14 +888,25 @@ def _mo034_the_changelog_records_a_look(w: World) -> None: "a swept breakpoint disappears from the stylesheet", _mo023_a_swept_breakpoint_disappears, ), - Mutation("OSV1-024", "the Tier-A kit file appears", _mo_tier_a_kit_appears), - Mutation("OSV1-025", "the Tier-A kit file appears", _mo_tier_a_kit_appears), - Mutation("OSV1-026", "the Tier-A kit file appears", _mo_tier_a_kit_appears), - Mutation("OSV1-027", "the Tier-A kit file appears", _mo_tier_a_kit_appears), + Mutation( + "OSV1-024", + "Conformance 5's good half is deferred again behind an xfail naming OSV1-001", + _mo024_the_hero_good_half_is_deferred_again, + ), + Mutation( + "OSV1-025", + "OSV1-005 closes, so Conformance 6's deferred good half should now pass", + _mo025_the_literal_style_row_goes_green, + ), + Mutation( + "OSV1-026", + "OSV1-012 closes, so Conformance 7's deferred good halves should now pass", + _mo026_the_empty_slot_row_goes_green, + ), Mutation( "OSV1-027", - "the Makefile wires a conformance target (the 'runs in a gate' half)", - _mo027_the_makefile_wires_the_kit, + "CI stops running the Tier-A kit (the 'runs in a gate' half)", + _mo027_ci_stops_running_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), @@ -819,7 +915,7 @@ def _mo034_the_changelog_records_a_look(w: World) -> None: "a Tier-B artifact directory appears", _mo029_an_artifact_directory_appears, ), - Mutation("OSV1-030", "the Tier-A kit file appears", _mo_tier_a_kit_appears), + Mutation("OSV1-030", "the Tier-B kit file appears", _mo_tier_b_kit_appears), 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..c8603fa 100644 --- a/ledger/checks/test_operator_rows.py +++ b/ledger/checks/test_operator_rows.py @@ -41,12 +41,15 @@ from __future__ import annotations +import ast import re from ._support import ( CHARTSVG, + GROUND_TOKENS, LITERAL, OPERATOR_CONTRACT_PATH, + PINNING_DISPOSITIONS, PYPROJECT, REPO_ROOT, SUPERVISOR, @@ -92,6 +95,66 @@ def _exists(rel: str) -> bool: return (REPO_ROOT / rel).exists() +# --------------------------------------------------------------------------- +# Reading the Tier-A conformance kit. +# +# Several rows below are about the KIT rather than about `src/` -- whether the +# check the contract names is implemented, whether it ships a bad half, and +# whether its good half is still DEFERRED against an open row. All three go +# through `read()` so the mutation harness's reader injection reaches them. +# --------------------------------------------------------------------------- + + +def _kit_source() -> str: + return read(REPO_ROOT / TIER_A_KIT) + + +def _kit_defs(kit: str) -> frozenset[str]: + return frozenset( + n.name + for n in ast.walk(ast.parse(kit)) + if isinstance(n, ast.FunctionDef | ast.AsyncFunctionDef) + ) + + +def _kit_bad_halves(kit: str, good: str) -> list[str]: + """The bad-half tests paired with a check's good half, by name.""" + return sorted(n for n in _kit_defs(kit) if n.startswith(f"{good}_bad_half_")) + + +def _kit_deferred_rows(kit: str, test_name: str) -> frozenset[str]: + """Row ids named in `test_name`'s `xfail` reason -- empty if not deferred. + + A kit test marked `xfail(strict=True)` is a check that RUNS and currently + FAILS, with the ledger row that owns the failure named in its reason. That + marker is what a red Conformance row pins: when the product fix lands the + test XPASSes, the run fails, and the marker and the row move together. + """ + for node in ast.walk(ast.parse(kit)): + if not isinstance(node, ast.FunctionDef) or node.name != test_name: + continue + for dec in node.decorator_list: + if not isinstance(dec, ast.Call): + continue + if not ast.unparse(dec.func).endswith("xfail"): + continue + for kw in dec.keywords: + if kw.arg == "reason" and isinstance(kw.value, ast.Constant): + return frozenset(re.findall(r"OSV1-\d{3}", str(kw.value.value))) + return frozenset() + return frozenset() + + +def _widgets_function(name: str) -> str: + """One function's source out of `widgets.py`, via the patched reader.""" + src = read(WIDGETS) + lines = src.splitlines() + for node in ast.walk(ast.parse(src)): + if isinstance(node, ast.FunctionDef) and node.name == name: + return "\n".join(lines[node.lineno - 1 : (node.end_lineno or node.lineno)]) + raise AssertionError(f"widgets.py no longer defines {name!r}") + + def _repo_mentions(needle: str) -> bool: """Does any tracked source/config file name this string? @@ -145,52 +208,80 @@ def test_row_osv1_000() -> None: def test_row_osv1_001() -> None: - """Core 1 VIOLATION pin: the L0 hero is a verdict, and velocity is a chart. - - Three separable facts, asserted separately so a partial fix cannot pass a - single blunt check: the hero renderer emits no figure and no count; L0 - builds its hero from the verdict path; and the KPI strip carries the five - shipped keys with NO needs-attention key among them. + """Core 1 CONFORMS: the L0 hero LEADS with fleet velocity over a stated + window, and the four counts an operator acts on sit in that same region. + + RETARGETED 2026-09-04. This probe used to be the VIOLATION pin -- it + asserted the hero was a verdict line, that velocity was a chart two + regions below, and that the KPI strip carried five keys with no + needs-attention count among them. That behaviour moved TOWARD the clause + (VIOLATION-MOVEMENT), so the row flipped to CONFORMS and this probe was + pointed at the fixed shape in the same change. Flip direction is now + REGRESSION: this going red means the repo moved back AWAY from Core 1. + + Three separable facts, asserted separately so a partial regression cannot + slip past a single blunt check: + 1. the renderer emits a velocity FIGURE and STATES its window -- a bare + number with no window is the half-fix Core 1 does not accept; + 2. L0 builds its hero from that renderer, with exactly the four counts + the clause names, each carrying a text LABEL (Core 3); + 3. the counts are composed INTO the hero region, not into a separate + strip below it -- which is precisely where they used to live. """ hero = read(WIDGETS) - start = hero.index("def render_verdict_hero") - body = hero[start : start + 1400] - assert 'class="verdict"' in body and 'class="eyebrow2"' in body, ( - "OSV1-001 (Core 1): `render_verdict_hero` no longer emits the verdict/eyebrow " - "shape this pin was written against -- the hero moved. Re-measure the row." + start = hero.index("def render_velocity_hero") + body = hero[start : start + 1800] + assert 'class="figv"' in body, ( + "OSV1-001 (Core 1) REGRESSION: `render_velocity_hero` no longer emits the " + "velocity figure. Core 1's hero IS that throughput reading -- without it the " + "region is a verdict line again, which is the defect this row closed." + ) + assert 'class="figwin"' in body and "velocity_window" in body, ( + "OSV1-001 (Core 1) REGRESSION: the hero's throughput figure no longer states " + 'the window it covers. Core 1 says "throughput over a STATED window" -- an ' + "operator reading a bare number learns nothing from it." ) - assert "velocity" not in body.lower(), ( - "OSV1-001 (Core 1) PIN BROKE THE RIGHT WAY: the hero renderer now mentions " - "velocity. If the hero really carries throughput over a stated window plus the " - "four counts, flip OSV1-001 to CONFORMS and retarget this probe at the fixed " - "shape IN THE SAME CHANGE (work_item_pipeline-ujy). A passing pin is not " - "conformance; only the retargeted probe is." + assert "render_kpi_strip(" in body, ( + "OSV1-001 (Core 1) REGRESSION: the hero renderer no longer composes the counts " + "strip itself. If the route places one below the hero again, the counts have " + "left the hero REGION, which is what Core 1 requires them to be inside." + ) + assert 'role="status"' in body, ( + "OSV1-001 (Core 1): the hero stopped being a live region. The 20s body-swap " + 'replaces this panel in place (Core 6) -- without `role="status"` that ' + "replacement announces nothing." ) app = read(WEBAPP) - assert "hero_html = WD.render_verdict_hero(" in app, ( - "OSV1-001 (Core 1): L0 no longer builds its hero from `render_verdict_hero`. " - "That is the fix landing -- re-derive the row." + assert "hero_html = WD.render_velocity_hero(" in app, ( + "OSV1-001 (Core 1) REGRESSION: L0 no longer builds its hero from " + "`render_velocity_hero`. Re-derive the row before changing this." ) - # The shipped KPI strip: five keys, and `needs attention` is not one of them. - kpi = app[app.index("kpi_html = WD.render_kpi_strip(") :][:2200] - shipped = set(re.findall(r'key="([a-z_0-9]+)"', kpi)) - assert shipped == {"agents", "held", "ready", "blocked", "resolved24h"}, ( - f"OSV1-001 (Core 1): the L0 KPI strip's keys moved -- pinned " - f"{{agents, held, ready, blocked, resolved24h}}, observed {sorted(shipped)}. " - f"If a needs-attention count was added, that is the fix landing " - f"(work_item_pipeline-ujy): flip the row and retarget this probe." + # The four counts Core 1 names, read off the hero's own construction -- + # bounded to it, so a label somewhere else on the page cannot stand in for + # one that is missing here. + call = app[ + app.index("hero_counts = [") : app.index("velocity_data = _workspace_velocity_data(") + ] + labels = set(re.findall(r'label="([^"]+)"', call)) + assert labels == {"In flight (held)", "Blocked", "Needs attention", "Open / ready"}, ( + f"OSV1-001 (Core 1): the L0 hero's counts moved -- Core 1 names FOUR (in flight " + f"(held), blocked, needs attention, open/ready); observed {sorted(labels)}. " + f"`Needs attention` is the one that was absent anywhere at seed; losing it " + f"again is the original defect returning." ) - # Matched on the card LABELS, not on the word "attention": the Blocked card - # already links to `#attention-queue`, so a substring check would be - # satisfied by an href and assert nothing about a count. - labels = set(re.findall(r'label="([^"]+)"', kpi)) - assert labels == {"Agents active now", "Held", "Ready", "Blocked", "Resolved 24h"}, ( - f"OSV1-001 (Core 1): the KPI strip's card labels moved -- pinned the five " - f"shipped ones, observed {sorted(labels)}. Core 1 names FOUR counts an operator " - f"acts on; `needs attention` was the one absent at seed. If it was added, that " - f"is the fix landing (work_item_pipeline-ujy) -- re-derive the row." + assert "velocity_window=" in call and "velocity_value=" in call, ( + "OSV1-001 (Core 1): L0 stopped passing a velocity figure and/or its window to " + "the hero. Both halves are the clause." + ) + + # No second strip survives below the hero: the route holds no separate + # `kpi_html` fragment to interpolate under it any more. + assert "kpi_html" not in app, ( + "OSV1-001 (Core 1) REGRESSION: a separate KPI-strip fragment is back in the L0 " + "route. The counts belong INSIDE the hero region (`render_velocity_hero` " + "composes them); a strip below it is the shape this row closed." ) @@ -259,12 +350,28 @@ def test_row_osv1_003() -> None: 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. + """Core 3 CONFORMS: the rendered check exists, is not deferred, and the + chip vocabulary it walks still gives every status a WORD. + + This row rests on the kit's real result over rendered L0/L1/L2 (recorded + in the row itself), which no in-process probe can re-run. So this probe + asserts the two things that CAN still be seen going wrong from here: the + check disappearing or being deferred, and a status losing its word in the + map the check walks. """ - assert not _exists(TIER_A_KIT), ( - f"OSV1-004 (Core 3): {TIER_A_KIT} now exists -- re-derive this row from the " - f"kit's real result over rendered L0/L1/L2 fixtures (work_item_pipeline-c1a)." + assert _exists(TIER_A_KIT), ( + f"OSV1-004 (Core 3) REGRESSION: {TIER_A_KIT} is gone. Core 3's check is Tier " + f"A and scoped to RENDERED fixtures -- without the kit nothing asserts it, and " + f"this row has no basis to be green." + ) + kit = _kit_source() + assert {"check_state_not_colour_only", "test_state_not_colour_only"} <= _kit_defs(kit), ( + "OSV1-004 (Core 3) REGRESSION: the kit no longer implements " + "`state.not_colour_only` over rendered pages." + ) + assert not _kit_deferred_rows(kit, "test_state_not_colour_only"), ( + "OSV1-004 (Core 3) REGRESSION: the rendered Core 3 check is now marked xfail. " + "This row is CONFORMS because the check PASSES; a deferred check is a red row." ) browse = read(WEBBROWSE) labels = set(re.findall(r'"(?:open|held|blocked|deferred|resolved)":\s*"([A-Z]+)"', browse)) @@ -366,15 +473,15 @@ def test_row_osv1_005() -> None: # --------------------------------------------------------------- OSV1-006 -def test_row_osv1_006() -> None: - """Core 4 CONFORMS: computed-geometry sites are EXACTLY the register. - - This is the exemption register itself (Phase-1 ruling Need 2 put it in - `ledger/` so shrinking it needs no amendment). Shrinking passes only after - the register below is shrunk to match; GROWING fails immediately, which is - the direction that matters. - """ - register = { +#: THE exemption register Core 4 names -- "The register lives in `ledger/`, not +#: in this contract, so that shrinking it is a convergent change requiring no +#: amendment." Module-level rather than local to `test_row_osv1_006` below so +#: that the Tier-A conformance kit +#: (`tests/conformance/operator_surface/test_tier_a.py`, Conformance 6) reads +#: THIS register rather than growing a second copy: OSV1-025's own notes name +#: that as the failure to avoid, because two censuses disagree silently. +EXEMPTION_REGISTER: frozenset[str] = frozenset( + { "chartsvg.py:268", "chartsvg.py:464", "chartsvg.py:488", @@ -391,14 +498,26 @@ def test_row_osv1_006() -> None: "webapp.py:3376", "webapp.py:4393", "webapp.py:4908", - "webtheme.py:4120", - "webtheme.py:4139", - "webtheme.py:4146", + "webtheme.py:4131", + "webtheme.py:4150", + "webtheme.py:4157", "widgets.py:704", "widgets.py:831", "widgets.py:834", "widgets.py:1110", } +) + + +def test_row_osv1_006() -> None: + """Core 4 CONFORMS: computed-geometry sites are EXACTLY the register. + + This is the exemption register itself (Phase-1 ruling Need 2 put it in + `ledger/` so shrinking it needs no amendment). Shrinking passes only after + `EXEMPTION_REGISTER` above is shrunk to match; GROWING fails immediately, + which is the direction that matters. + """ + register = EXEMPTION_REGISTER observed = set(style_sites_in("COMPUTED")) unregistered = observed - register assert not unregistered, ( @@ -484,47 +603,91 @@ def test_row_osv1_008() -> None: # --------------------------------------------------------------- OSV1-009 -def test_row_osv1_009() -> None: - """Core 7 VIOLATION pin: the token-pair luminance math, RE-RUN here. +#: The ink ramp's four canonical steps, brightest-to-quietest in dark mode and +#: darkest-to-lightest in light. `--dim`/`--ink` are deliberately excluded: they +#: are ALIASES onto two of these four, so including them would make the +#: distinctness check below trivially false. +_INK_RAMP = ("--ink-primary", "--ink-secondary", "--ink-tertiary", "--ink-quiet") - Two halves, both pinned: the six below-floor pairs are all --ink-quiet in - light mode, AND that token is used for real reading copy. Fixing either - half alone is progress the row must record, so neither is folded into the - other. + +def test_row_osv1_009() -> None: + """Core 7 CONFORMS: every declared token pair clears its floor, both themes. + + RETARGETED from the seed pin (work_item_pipeline-sxh, 2026-09-04). The pin + froze six below-floor pairs -- all `--ink-quiet` in light mode, three + grounds x the two duplicated light blocks -- plus the call site that made + that token READING COPY rather than a decorative exemption. Both halves are + closed by one token change (`--ink-quiet` #7c8ba0 -> #596473 in BOTH light + blocks), so the reading-copy half needs no separate assert: the token is now + above the text floor wherever it is used. + + Every number below is COMPUTED from the live token blocks in webtheme.py on + each run -- nothing here is a transcribed ratio. The three guarded shapes: + + 1. no declared ink x ground pair below 4.5:1, in any of the three blocks; + 2. no declared status x ground pair below 3:1 (the half that CONFORMED at + seed and must not silently regress while the text half is worked on); + 3. the fix was made at the TOKEN and in BOTH light blocks, and did not + buy contrast by collapsing the ink ramp. + + Honest limit, unchanged from the seed row and the reason OSV1-010 stays + open: flat token-pair math models a swatch on a bare ground. The real + surface puts `backdrop-filter` glass over an ambient radial gradient, which + lifts perceived background luminance. Necessary, never sufficient. """ - below = sorted( - (round(r, 2), ink, ground, block) for block, ink, ground, r in text_pairs() if r < 4.5 + pairs = text_pairs() + assert len(pairs) >= 54, ( + f"OSV1-009 (Core 7): the declared ink x ground surface SHRANK to {len(pairs)} " + f"pairs (54 at seed). A floor check over fewer pairs passes by measuring " + f"less -- re-derive this row from what the token blocks actually declare." ) - assert len(below) == 6, ( - f"OSV1-009 (Core 7) LUMINANCE MOVED: pinned six declared text pairs below the " - f"4.5:1 floor, observed {len(below)}.\n" - f" If FEWER: the tokens are being fixed -- update the pin (or flip the row to " - f"CONFORMS at zero) and retarget this probe IN THE SAME CHANGE " - f"(work_item_pipeline-sxh). A passing pin is not conformance.\n" - f" If MORE: a token regressed below a frozen floor.\n" - f" observed: {below}" + below_text = sorted( + (round(r, 2), ink, ground, block) for block, ink, ground, r in pairs if r < 4.5 ) - assert {b[1] for b in below} == {"--ink-quiet"}, ( - f"OSV1-009 (Core 7): a token OTHER than --ink-quiet is now below the text " - f"floor: {sorted({b[1] for b in below})}. --ink-quiet is at least documented " - f"as decorative; anything else below 4.5:1 is a plain regression." + assert not below_text, ( + f"OSV1-009 (Core 7) REGRESSION: {len(below_text)} declared text pair(s) fell " + f"below the 4.5:1 floor. Fix the TOKEN, in BOTH light blocks -- they are held " + f"in sync only by comment.\n (ratio, ink, ground, block): {below_text}" ) - assert min(b[0] for b in below) == 2.72, ( - f"OSV1-009 (Core 7): the worst declared text pair moved from 2.72:1 to " - f"{min(b[0] for b in below)}:1." + + non_text = non_text_pairs() + assert len(non_text) >= 9, ( + f"OSV1-009 (Core 7): the declared status x ground surface SHRANK to " + f"{len(non_text)} pairs (9 at seed) -- re-derive this row." + ) + below_non_text = sorted( + (round(r, 2), hue, ground, block) for block, hue, ground, r in non_text if r < 3.0 ) - assert contains(CHARTSVG, 'style="fill:var(--ink-quiet)">'), ( - "OSV1-009 (Core 7) PIN BROKE THE RIGHT WAY: chartsvg.py no longer paints its " - "empty-state caption with --ink-quiet. That call site is what makes the " - "below-floor token READING COPY rather than a documented decorative " - "exemption -- re-derive the row (work_item_pipeline-sxh)." + assert not below_non_text, ( + f"OSV1-009 (Core 7) REGRESSION: {len(below_non_text)} declared non-text pair(s) " + f"fell below the 3:1 floor.\n (ratio, hue, ground, block): {below_non_text}" ) - worst_non_text = min(r for *_rest, r in non_text_pairs()) - assert worst_non_text >= 3.0, ( - f"OSV1-009 (Core 7) REGRESSION: a status hue fell below the 3:1 NON-TEXT " - f"floor ({worst_non_text:.2f}:1). That half CONFORMED at seed and this row " - f"does not cover it -- it is a separate, new violation." + + blocks = token_blocks() + assert set(blocks) == {"dark", "light-media", "light-attr"}, ( + f"OSV1-009 (Core 7): a declared token block appeared or vanished " + f"({sorted(blocks)}). 'both themes' is measured over these three." ) + light = { + name: {t: resolve_token(t, blocks[name]) for t in (*_INK_RAMP, *GROUND_TOKENS)} + for name in ("light-media", "light-attr") + } + assert light["light-media"] == light["light-attr"], ( + f"OSV1-009 (Core 7): the two light token blocks DRIFTED on the tokens this " + f"clause measures. webtheme.py's own comment says they are kept in sync only " + f"by comment; a colour fixed in one and not the other is the exact hazard the " + f"seed row's six pairs (three grounds x two blocks) recorded.\n" + f" @media (prefers-color-scheme:light): {light['light-media']}\n" + f' :root[data-theme="light"]: {light["light-attr"]}' + ) + for name, table in blocks.items(): + ramp = [resolve_token(t, table) for t in _INK_RAMP] + assert len(set(ramp)) == len(_INK_RAMP), ( + f"OSV1-009 (Core 7): the ink ramp COLLAPSED in the {name!r} block -- two " + f"of {list(_INK_RAMP)} now resolve to the same colour ({ramp}). Clearing " + f"the floor by deleting a ramp step is not conformance; --ink-quiet must " + f"stay quieter than --ink-tertiary while clearing 4.5:1." + ) # --------------------------------------------------------------- OSV1-010 @@ -587,13 +750,32 @@ def test_row_osv1_011() -> None: def test_row_osv1_012() -> None: - """Core 8 GAP pin: no two-render kit, and the empty sentences measured at - seed are still the ones the kit will look for. + """Core 8 VIOLATION pin: two widget renderers still have no empty branch, + and the kit's two `calm.keeps_slot` halves are still deferred against this + row. + + Pinned on the RENDERERS rather than on a rendered page, because that is + what an in-process probe can see: `render_attention_queue` and + `render_agents_panel` return their container unconditionally, so an empty + one is a slot with nothing in it. Giving either an empty branch flips this + pin -- which is the fix landing. """ - assert not _exists(TIER_A_KIT), ( - f"OSV1-012 (Core 8): {TIER_A_KIT} now exists -- re-derive this row from a real " - f"empty-vs-populated render comparison (work_item_pipeline-c1a)." - ) + for func in ("render_attention_queue", "render_agents_panel"): + body = _widgets_function(func) + assert "if not data[" not in body, ( + f"OSV1-012 (Core 8) PIN BROKE THE RIGHT WAY: `{func}` now has an empty " + f"branch. If it emits the empty SENTENCE Core 8 requires, re-run the " + f"Tier-A kit's `calm.keeps_slot` halves, flip OSV1-012 to CONFORMS, delete " + f"their xfail markers and retarget this probe -- all in the same change " + f"(work_item_pipeline-c1a)." + ) + kit = _kit_source() + for test_name in ("test_calm_keeps_slot", "test_calm_keeps_slot_l1"): + assert "OSV1-012" in _kit_deferred_rows(kit, test_name), ( + f"OSV1-012 (Core 8) PIN BROKE THE RIGHT WAY: the kit's `{test_name}` is no " + f"longer deferred against this row. A passing good half is the fix -- flip " + f"the row in the same change." + ) assert contains(WIDGETS, '"All clear"'), ( "OSV1-012 (Core 8): the calm headline 'All clear' is gone. Calm must stay " "STATED -- and must not become a triumphant zero." @@ -829,12 +1011,22 @@ 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 pin: the Tier-A half landed; the Tier-B hue half has not, + and that absence is what keeps this row red.""" + assert not _exists(TIER_B_KIT), ( + f"OSV1-021 (Conformance 2): {TIER_B_KIT} now exists. This is the one fixture " + f"that spans BOTH tiers -- the Tier-A accessible-name half already landed " + f"(work_item_pipeline-c1a, measured in OSV1-004); re-derive this row from the " + f"Tier-B hue half's own demonstrated pair (work_item_pipeline-qgo)." + ) + kit = _kit_source() + assert "check_state_not_colour_only" in _kit_defs(kit), ( + "OSV1-021 (Conformance 2): the Tier-A half is gone from the kit. This row is " + "red on the Tier-B half ONLY -- losing the Tier-A half is a second, new defect." + ) + assert _kit_bad_halves(kit, "test_state_not_colour_only"), ( + "OSV1-021 (Conformance 2): the Tier-A half no longer ships a bad half. A " + "fixture whose bad half nobody runs is a claim, not a fixture (Freeze 4)." ) assert contains( OPERATOR_CONTRACT_PATH, "fixture whose status chips carry only a status class" @@ -871,10 +1063,30 @@ def test_row_osv1_023() -> None: def test_row_osv1_024() -> None: - """Conformance 5 pin: no Tier-A kit, so hero composition is unasserted.""" - assert not _exists(TIER_A_KIT), ( - f"OSV1-024 (Conformance 5): {TIER_A_KIT} now exists -- re-derive from the " - f"demonstrated good/bad pair (work_item_pipeline-c1a)." + """Conformance 5 CONFORMS: the fixture exists, discriminates, and its GOOD + half runs UNDEFERRED against the rebuilt hero (OSV1-001 closed 2026-09-05). + + Flipped at highway wave-1 integration: the hero lane closed OSV1-001 and the + orchestrator removed the kit's xfail(strict) deferral in the same change; the + good half now passes on the real L0 (kit run: 38 passed / 4 xfailed). + """ + kit = _kit_source() + assert "check_hero_velocity_and_counts" in _kit_defs(kit), ( + f"OSV1-024 (Conformance 5): {TIER_A_KIT} no longer implements " + f"`hero.velocity_and_counts` at the location the contract names." + ) + assert _kit_bad_halves(kit, "test_hero_velocity_and_counts"), ( + "OSV1-024 (Conformance 5): the fixture no longer ships a bad half -- a fixture " + "that cannot be watched failing is a claim (Freeze 4)." + ) + assert not _kit_deferred_rows(kit, "test_hero_velocity_and_counts"), ( + "OSV1-024 (Conformance 5) REGRESSION: the good half is deferred again -- a " + "Conformance row cannot read CONFORMS while its good half carries an xfail. " + "Either the hero regressed (re-open OSV1-001) or the marker is stale." + ) + assert row("OSV1-001")["disposition"] == "CONFORMS", ( + "OSV1-024 (Conformance 5) REGRESSION: OSV1-001 is red again, so Conformance 5's " + "good half cannot pass against the shipped hero. Re-derive both rows together." ) assert contains( OPERATOR_CONTRACT_PATH, @@ -883,11 +1095,29 @@ def test_row_osv1_024() -> None: def test_row_osv1_025() -> None: - """Conformance 6 pin: no Tier-A kit, so the register is checked only here.""" - assert not _exists(TIER_A_KIT), ( - f"OSV1-025 (Conformance 6): {TIER_A_KIT} now exists. Make sure it reads THIS " - f"ledger's register (OSV1-006) rather than growing its own copy -- two censuses " - f"will disagree, silently (work_item_pipeline-c1a)." + """Conformance 6 pin: the fixture exists, reads THIS ledger's register, and + its GOOD half is still deferred against OSV1-005.""" + kit = _kit_source() + assert "check_visual_single_source" in _kit_defs(kit), ( + f"OSV1-025 (Conformance 6): {TIER_A_KIT} no longer implements " + f"`visual.single_source` at the location the contract names." + ) + assert _kit_bad_halves(kit, "test_visual_single_source"), ( + "OSV1-025 (Conformance 6): the fixture no longer ships a bad half (Freeze 4)." + ) + assert "EXEMPTION_REGISTER" in kit, ( + "OSV1-025 (Conformance 6): the kit stopped importing this ledger's exemption " + "register. ONE census, ONE register (Phase-1 ruling Need 2) -- a second copy " + "disagrees with this one, silently." + ) + assert "OSV1-005" in _kit_deferred_rows(kit, "test_visual_single_source"), ( + "OSV1-025 (Conformance 6) PIN BROKE THE RIGHT WAY: the good half is no longer " + "deferred against OSV1-005. Flip OSV1-005 AND this row and retarget both " + "probes in the same change (work_item_pipeline-np3)." + ) + assert row("OSV1-005")["disposition"] in PINNING_DISPOSITIONS, ( + "OSV1-025 (Conformance 6) PIN BROKE THE RIGHT WAY: OSV1-005 is no longer red, " + "so Conformance 6's good half should now pass. Re-derive from the PASSING pair." ) assert contains(OPERATOR_CONTRACT_PATH, 'style="color:#D9A253"'), ( "OSV1-025: Conformance 6's named bad specimen moved in the contract -- re-review the row." @@ -895,10 +1125,24 @@ def test_row_osv1_025() -> None: def test_row_osv1_026() -> None: - """Conformance 7 pin: no Tier-A kit, so empty-vs-populated is uncompared.""" - assert not _exists(TIER_A_KIT), ( - f"OSV1-026 (Conformance 7): {TIER_A_KIT} now exists -- re-derive from a real " - f"two-render comparison (work_item_pipeline-c1a)." + """Conformance 7 pin: the two-render fixture exists and discriminates, and + its GOOD halves are still deferred against OSV1-012.""" + kit = _kit_source() + assert "check_calm_keeps_slot" in _kit_defs(kit), ( + f"OSV1-026 (Conformance 7): {TIER_A_KIT} no longer implements " + f"`calm.keeps_slot` at the location the contract names." + ) + assert _kit_bad_halves(kit, "test_calm_keeps_slot"), ( + "OSV1-026 (Conformance 7): the fixture no longer ships a bad half (Freeze 4)." + ) + assert "OSV1-012" in _kit_deferred_rows(kit, "test_calm_keeps_slot"), ( + "OSV1-026 (Conformance 7) PIN BROKE THE RIGHT WAY: the good half is no longer " + "deferred against OSV1-012. Flip OSV1-012 AND this row and retarget both " + "probes in the same change (work_item_pipeline-c1a)." + ) + assert row("OSV1-012")["disposition"] in PINNING_DISPOSITIONS, ( + "OSV1-026 (Conformance 7) PIN BROKE THE RIGHT WAY: OSV1-012 is no longer red, " + "so Conformance 7's good halves should now pass. Re-derive from the PASSING pair." ) assert contains( OPERATOR_CONTRACT_PATH, "a render that drops empty widgets, or renders a hero-scale `0`" @@ -910,27 +1154,38 @@ def test_row_osv1_026() -> None: def test_row_osv1_027() -> None: - """Freeze 1 pin: the Tier-A kit neither exists nor runs in any gate. + """Freeze 1 CONFORMS: the Tier-A kit exists AND runs in a real gate. - Both halves are pinned, because a kit that exists but runs in nothing is + Both halves are asserted, because a kit that exists but runs in nothing is the exact failure this repo already measured once (CCV1-022: a whole suite - of green claims nobody had ever executed). + of green claims nobody had ever executed). Matched on the KIT PATH, never + on the word "conformance": the Makefile and ci.yml already say "conformance + ledger" about Tier 4, and a check a pre-existing comment satisfies asserts + nothing. """ - assert not _exists(TIER_A_KIT), ( - f"OSV1-027 (Freeze 1): {TIER_A_KIT} now exists. Freeze 1 ALSO requires it to " - f"run on every pull request -- do not flip this row on the file alone " - f"(work_item_pipeline-c1a)." - ) - # 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." + assert _exists(TIER_A_KIT), f"OSV1-027 (Freeze 1) REGRESSION: {TIER_A_KIT} is gone." + make = read(MAKEFILE) + assert "tests/conformance" in make and "test-conformance-a:" in make, ( + "OSV1-027 (Freeze 1) REGRESSION: the Makefile no longer carries a target " + "covering tests/conformance. Existing is not the same as running." + ) + ci = read(CI_WORKFLOW) + assert "tests/conformance/operator_surface" in ci, ( + "OSV1-027 (Freeze 1) REGRESSION: ci.yml no longer runs the Tier-A kit -- " + "Freeze 1's second half is 'runs on every pull request'." + ) + assert "pull_request" in ci, ( + "OSV1-027 (Freeze 1) REGRESSION: the workflow that runs the kit no longer " + "triggers on pull_request." + ) + kit = _kit_source() + assert "TIER_A_CHECKS" in kit and _kit_defs(kit) >= { + "test_every_tier_a_machine_check_the_contract_names_is_implemented_here", + "test_every_check_ships_a_bad_half", + }, ( + "OSV1-027 (Freeze 1) REGRESSION: the kit dropped its own coverage tripwires. " + "Without them it can quietly cover less of the contract than it claims, which " + "is the failure Freeze 1 and Freeze 4 exist to prevent." ) @@ -969,11 +1224,17 @@ def test_row_osv1_029() -> None: 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 pin: the Tier-A fixtures are demonstrated; no browser kit + exists, so the three Tier-B fixtures are not.""" + assert not _exists(TIER_B_KIT), ( + "OSV1-030 (Freeze 4): the Tier-B 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." + ) + assert _exists(TIER_A_KIT) and _kit_bad_halves(_kit_source(), "test_calm_keeps_slot"), ( + "OSV1-030 (Freeze 4): the Tier-A fixtures' demonstrated bad halves are gone. " + "This row is red on the TIER-B half only -- losing the Tier-A half is a " + "second, new defect." ) @@ -1002,10 +1263,11 @@ def test_row_osv1_031() -> None: "to CONFORMS and retarget this probe to assert no Core row is red " "(work_item_pipeline-umm)." ) - assert len(red) == 10, ( - f"OSV1-031 (Freeze 5): pinned 10 red Core-carrying rows, observed {len(red)}: " + assert len(red) == 7, ( + f"OSV1-031 (Freeze 5): pinned 7 red Core-carrying rows, observed {len(red)}: " f"{red}. Movement in either direction means this gate's tally changed -- update " - f"the pin and the row's notes in the same change." + f"the pin and the row's notes in the same change. (Was 10 at seed; OSV1-009 " + f"went green 2026-09-04, work_item_pipeline-sxh.)" ) assert {r["id"] for r in core_rows if r["disposition"] == "NOT-ASSERTABLE"} == { "OSV1-018", diff --git a/ledger/rows.yaml b/ledger/rows.yaml index bd3fa2b..5cd1722 100644 --- a/ledger/rows.yaml +++ b/ledger/rows.yaml @@ -931,50 +931,72 @@ different families. - id: OSV1-001 - title: the L0 hero is a verdict line, not fleet velocity with the counts that matter + title: the L0 hero leads with fleet velocity and carries the four counts that matter contract: file: contracts/operator-surface.v1.md clause: Core 1 quote: | The L0 hero region carries throughput over a stated window, presented together with the counts an operator acts on: in flight (held), blocked, needs attention, and open/ready. - disposition: VIOLATION + disposition: CONFORMS work: work_item_pipeline-ujy assertion: kind: probe ref: test_row_osv1_001 notes: > - PINNING ROW -- the probe asserts the CURRENT, KNOWN-WRONG shape on purpose; - a passing probe here is NOT conformance. Flip direction VIOLATION-MOVEMENT. - - MEASURED 2026-09-04 against 4aaee50, not taken from the Phase-0 brief: - webapp.py:4602 the hero is `WD.render_verdict_hero(VerdictHeroData(...))` - widgets.py:637-663 that renderer emits `
` carrying an eyebrow, a - verdict headline, a detail line, and an OPTIONAL - `meta_row`. L0 passes no `meta_row`, so the hero - carries no figure and no count at all. - webapp.py:4611-4641 a SEPARATE kpi-strip below the hero: five cards keyed - agents / held / ready / blocked / resolved_24h. - webapp.py:4647 velocity is `_velocity_chart_shell_html(title= - "Environment velocity", ...)` -- a CHART, two regions - below the hero, not a hero figure. - Against the clause: throughput-in-the-hero ABSENT; held / blocked / - open-ready present as KPI cards but not in the hero; NEEDS ATTENTION absent - as a count anywhere in either region (the ranked "Needs you" section is at - webapp.py:4676 and its length is never surfaced as a count); and two cards - the clause does not name (agents active, resolved 24h) occupy the strip. - - OPEN READING, RETURNED TO THE ROOT AND NOT DECIDED BY THIS RECONCILE: - whether the shipped KPI strip is part of "the L0 hero region", i.e. whether - held/blocked/ready already satisfy the counts half in part. The disposition - does not turn on it -- the throughput half is unsatisfied on either - reading -- but the size of the remaining work does. + FLIPPED 2026-09-04 from VIOLATION (VIOLATION-MOVEMENT: the pinning probe went + red because the behaviour moved TOWARD the clause). The probe was retargeted + at the fixed shape in the same change, and its mutations rewritten to + reinstate each half of the closed defect. Flip direction is now REGRESSION. + + MEASURED 2026-09-04 against the change that closed it: + widgets.py:1323-1396 `render_velocity_hero(VelocityHeroData)` -- one panel, + `
`, carrying: the velocity figure + (``), the window STATED beside it + (`last 24h`), the verdict + demoted to a `.hero-caption`, and the counts strip + composed INSIDE the same div (it calls `render_kpi_strip` + itself rather than leaving the route to place one below). + webapp.py:4611-4642 L0 builds that hero, with exactly the four counts Core 1 + names: "In flight (held)" (held_total), "Blocked" + (blocked_total), "Needs attention" (len(attention_rows_raw) + -- the ranked queue's own length, which was the count + ABSENT anywhere at seed), "Open / ready" (ready_total). + webapp.py:4741 the body no longer interpolates a separate `{kpi_html}` + section: there is exactly ONE `.kpi-strip` on the page and + it is inside the hero region. + Against the clause: throughput-in-the-hero PRESENT with its window stated; + all four named counts PRESENT in the same region, each with a text label + (Core 3) and a numeral. "Resolved 24h" is no longer a card because it is the + figure; the agents-active reading it displaced survives in the verdict caption + ("N agents are moving ...") and in the "Agents now" section. + + THE OPEN READING THE SEED RETURNED TO THE ROOT -- whether the shipped KPI strip + counted as part of "the L0 hero region" -- is now MOOT rather than decided: the + strip is composed into the hero panel itself, so it is part of the region on + either reading. The root ruling recorded on work_item_pipeline-ujy is what this + change followed. + + Rendered evidence, both fixtures, asserted by test: + tests/unit/test_observatory_widgets.py + test_render_velocity_hero_leads_with_the_figure_and_states_its_window + test_render_velocity_hero_carries_all_four_named_counts_in_the_same_region + test_render_velocity_hero_every_count_carries_a_text_label_and_a_numeral + test_render_velocity_hero_empty_fixture_reports_zero_it_never_celebrates_it + test_render_velocity_hero_blocked_zero_goes_quiet_not_alarm_red + test_render_velocity_hero_is_a_live_region_for_the_body_swap + tests/integration/test_observatory_web.py + test_l0_hero_leads_with_fleet_velocity_over_a_stated_window + test_l0_hero_carries_the_four_named_counts_and_no_strip_survives_below_it + test_l0_hero_on_an_all_empty_fleet_reports_zero_without_celebrating_it Core 1 overrode a recorded in-code invariant, which the contract's own Changelog quotes: webapp.py:37-44 states the hero is the AGE of the oldest unclaimed item, never a count. The owner weighed that and chose - observability; the concern behind it survives as Core 8 (OSV1-012). See + observability; the concern behind it survives as Core 8 (OSV1-012), and this + composition honours it -- nothing scales with its value, so a zero is + reported at its siblings' scale with its label, never celebrated. See OSV1-033 -- one of those Changelog quotes does not verify against its cited source. @@ -1059,43 +1081,56 @@ presence so the sweep, when built, has a real target. - id: OSV1-004 - title: state is never colour-only -- unmeasured on a rendered page, no kit + title: state is never colour-only -- measured on rendered L0/L1/L2, and it holds contract: file: contracts/operator-surface.v1.md clause: Core 3 quote: | Every element carrying a state carries it in text as well as hue — a status chip has a word, not only a class. - disposition: GAP - work: work_item_pipeline-c1a + disposition: CONFORMS assertion: kind: probe ref: test_row_osv1_004 notes: > - 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". Core 3's check (`state.not_colour_only`) is - Tier A, but the contract scopes it to "the rendered L0/L1/L2 fixtures", and - `tests/conformance/operator_surface/test_tier_a.py` does not exist. This - ledger kit is in-process only (no bd, no dolt, no subprocess), so it can - assert the SOURCE shape of the chip vocabulary and nothing about a page. - - WHAT THE SOURCE SHAPE ALREADY SHOWS, measured 2026-09-04 -- encouraging, - and deliberately NOT treated as conformance: - webbrowse.py:139-152 every item status maps to BOTH a class - (`_ITEM_STATUS_CHIP_CLASS`) and a WORD - (`_ITEM_STATUS_CHIP_LABEL`: READY / HELD / BLOCKED - / DEFERRED / RESOLVED) -- five statuses, five words. - webbrowse.py:849-851 the L2 chip renders `item.status.upper()`. - widgets.py:1162,1164 legend dots carry adjacent label text in the same - `.li` span. - KNOWN CANDIDATES THE RENDERED CHECK MUST ADJUDICATE, which no source probe - can settle: `webapp.py:548` `` (an - attribute-only name), `webbrowse.py:119` a bare `` - prefixed to the blocked breakdown row, and `webapp.py:839` - `` (in `_sidebar_html`, which measured as dead - chrome -- no route passes `sidebar_html`). + RE-DERIVED 2026-09-05 from a REAL RENDERED MEASUREMENT -- never from the + fact that a kit file appeared. + `tests/conformance/operator_surface/test_tier_a.py` now implements Core 3's + `state.not_colour_only` and runs it over rendered L0, L1 and L2 pages of + the ALARM fixture (one item held past custody TTL, one blocked -- which is + Conformance 2's own scenario, so every status chip this surface can render + is actually on the page). Result: ZERO status-bearing elements without text + or an accessible name, on all three levels. + + WHAT THE CHECK COUNTS AS STATUS-BEARING, so the scope of this green is + legible rather than implied: a class that IS a status word, or a status + word under one of this surface's status-class prefixes (`st-`, `is-`, + `sev-`, `tab-`, `status-`). The vocabulary is read out of the app's OWN + maps -- `webbrowse._ITEM_STATUS_CHIP_LABEL`, `widgets._HERO_ICON`, the + three status hue tokens -- rather than written down in the kit, so a status + the app grows cannot be silently unchecked. A decorative, unlabelled mark + INSIDE a status-bearing element that is itself named is excused: the state + is already carried in that ancestor's words, which is exactly the clause. + + THE THREE CANDIDATES THE SEED ROW LEFT FOR THE RENDERED CHECK TO + ADJUDICATE, now adjudicated: `webapp.py:548`'s + `` carries an accessible name; + `webbrowse.py:119`'s bare `` sits inside + `Blocked N`, which carries the word; + `webapp.py:839`'s `` never reaches a rendered + page at all (dead chrome -- no route passes `sidebar_html`). + + DISCRIMINATING PAIR DEMONSTRATED (Freeze 4). The bad half is Conformance + 2's own specimen -- `` -- and the same + function reports it + (`test_state_not_colour_only_bad_half_chip_with_only_a_class`). Two further + bad halves keep the check from degenerating into "has text": an + `aria-label`-only chip PASSES, and a named ancestor excuses only its own + descendants, never a sibling. + + WHAT THIS ROW STILL DOES NOT COVER: the HUE half of Conformance 2 (the + alarm region's colour is `--alarm` or `--blocked`) is Tier B and belongs to + OSV1-021's second half. - id: OSV1-005 title: >- @@ -1235,14 +1270,20 @@ webapp.py:3376 {heading_style} computed heading webapp.py:4393 {heading_style} computed heading webapp.py:4908 {remove_heading_style} computed heading - webtheme.py:4120 {style} computed style - webtheme.py:4139 left:{_grad_x(f):.1f}px gradient stop offset - webtheme.py:4146 width:{px}px swatch width + webtheme.py:4131 {style} computed style + webtheme.py:4150 left:{_grad_x(f):.1f}px gradient stop offset + webtheme.py:4157 width:{px}px swatch width widgets.py:704 color:var({color_var}) token chosen at runtime widgets.py:831 width:{pct}% mix-bar width widgets.py:834 width:{pct}%;background:var({_MIX_COLOR_VAR[key]}) widgets.py:1110 background:var({_MIX_COLOR_VAR[key]}) + The three `webtheme.py` coordinates were RE-DERIVED on 2026-09-04 by + work_item_pipeline-sxh (OSV1-009), which added comment lines above them: + 4120/4139/4146 -> 4131/4150/4157. Same three sites, same code, same + register SET -- only the line numbers moved. A `file:line` register is + coordinate-fragile by construction; any edit above a site shifts it. + HONEST LIMIT: several registered sites interpolate a COLOUR, not a geometry (webapp.py:2142/2266, chartsvg.py:268, widgets.py:704/834/1110). They pass Core 4's literal test because the value is computed rather than @@ -1340,49 +1381,73 @@ the Conformance 3 bad-half behaviour. - id: OSV1-009 - title: six declared text pairs sit below the 4.5:1 floor, and the token paints real copy + title: every declared token pair clears its floor -- 4.5:1 text, 3:1 non-text, both themes contract: file: contracts/operator-surface.v1.md clause: Core 7 quote: | Text contrast is at least 4.5:1 and non-text contrast at least 3:1, in both themes - disposition: VIOLATION + disposition: CONFORMS work: work_item_pipeline-sxh assertion: kind: probe ref: test_row_osv1_009 notes: > - PINNING ROW -- the probe asserts the CURRENT, KNOWN-WRONG shape on purpose; - a passing probe here is NOT conformance. Flip direction VIOLATION-MOVEMENT. + FLIPPED VIOLATION -> CONFORMS 2026-09-04 by work_item_pipeline-sxh, in the + same change that retargeted the probe off its pin. The Tier-A half of `perception.floors`: "token-pair relative-luminance math over the declared token set". The rendered half is OSV1-010; the - reduced-motion half is OSV1-011. - - MEASURED 2026-09-04 against 4aaee50 -- WCAG relative luminance over the - three declared token blocks (dark `:root` webtheme.py:109, - `@media (prefers-color-scheme:light)` :287, `:root[data-theme="light"]` - :373), ink x ground, re-computed by the probe on every run: - 54 text pairs evaluated, 48 clear 4.5:1, SIX DO NOT -- all `--ink-quiet` - in LIGHT mode: - #7c8ba0 on --color-ground #eef2fb = 3.09:1 - #7c8ba0 on --color-ground-elevated #e7ecf7 = 2.93:1 - #7c8ba0 on --color-ground-sunken #dde4f2 = 2.72:1 - (three pairs x two light blocks, which declare identical values.) - The same token is fine in dark: 5.53 / 5.26 / 5.67:1. - 9 status-hue/ground non-text pairs, worst 5.35:1 (`--blocked` on dark - ground). THE NON-TEXT FLOOR OF 3:1 CONFORMS; this row is only about text. - - WHY THIS IS A VIOLATION RATHER THAN A DOCUMENTED EXEMPTION. - webtheme.py:141-152 declares `--ink-quiet` "decorative/disabled-only, not - guaranteed >=4.5:1 in light mode", which would be a legitimate WCAG - exemption if it were only ever decorative. Measured, it is not: - chartsvg.py:294 `` - renders the sentence "No activity in this window" -- reading - copy, on the empty-state path Core 8 exists to protect. - chartsvg.py:461 the axis stub label falls back to the same token. - 21 further `var(--ink-quiet)` uses inside webtheme.py's own rules, not - audited per-site here. + reduced-motion half is OSV1-011. Closing this does NOT close OSV1-010 -- + see the honest limit at the end. + + WHAT WAS WRONG AT SEED (4aaee50). 54 text pairs evaluated, 48 cleared + 4.5:1, SIX DID NOT -- all `--ink-quiet` in LIGHT mode, three grounds x the + two duplicated light blocks, which declared identical values. The same + token was already fine in dark (5.53 / 5.26 / 5.67:1). It was a VIOLATION + and not a documented decorative exemption because the token painted real + reading copy: chartsvg.py:294's `` renders the sentence "No activity in this + window", plus the `.status-chip.st-resolved` / `.priority-chip.p3,.p4` / + `.link-chip .none` / `.fleet-row .agents.is-zero` / actions-drawer count + rules inside webtheme.py itself. + + THE FIX, AND WHERE. At the TOKEN, not at the call sites -- `--ink-quiet` + darkened along the same hue (hsl ~215 deg) from #7c8ba0 to #596473, in BOTH + light blocks: `@media (prefers-color-scheme:light)` webtheme.py:338 AND + `:root[data-theme="light"]` :408. They are held in sync only by comment, so + changing one and not the other was the standing hazard; the probe now + asserts their parity over the measured token set directly. No call site + moved off the token, and no reading-copy usage was relabelled decorative. + + MEASURED 2026-09-04 after the fix -- WCAG relative luminance over the three + declared token blocks (dark `:root` webtheme.py:109, the two light blocks + above), re-computed by the probe on every run, never transcribed: + TEXT, floor 4.5:1 -- 54 of 54 pairs clear it. The six that did not, before + -> after (identical in both light blocks): + --ink-quiet on --color-ground #eef2fb 3.09:1 -> 5.36:1 + --ink-quiet on --color-ground-elevated #e7ecf7 2.93:1 -> 5.08:1 + --ink-quiet on --color-ground-sunken #dde4f2 2.72:1 -> 4.71:1 + Worst declared text pair overall moved 2.72:1 -> 4.71:1. + NON-TEXT, floor 3:1 -- 9 of 9 status-hue x ground pairs clear it, worst + 5.35:1 (`--blocked` on the dark ground), UNCHANGED by this fix. That half + conformed at seed; the probe now guards it rather than assuming it. + + HIERARCHY PRESERVED, deliberately. Quiet was taken to ~4.7-5.4:1, not to + 7:1: in light it still reads clearly quieter than --ink-tertiary (7.85:1 on + the base ground) and --ink-secondary (9.16:1). The probe asserts the four + ink-ramp steps still resolve to four DISTINCT colours in every block, so a + future "fix" cannot buy contrast by collapsing a ramp step onto its + neighbour. + + NOT COVERED, checked and clean: `--ink-on-solid` and `--ink-on-ground-inverse` + compute ~1.0-1.2:1 against the page grounds and are correctly excluded from + the ink set -- by definition they never sit on those grounds (they paint on + `--brand-gradient-solid` and on an inverted surface respectively). + `--brand-cyan-ink`, used as real text at chartsvg.py's value labels, clears + 4.80:1 worst case in light and 10.59:1 in dark. Hairline and glass tokens + are `rgba()` over a ground, not hex, so flat pair math cannot evaluate them + at all -- that is squarely OSV1-010's rendered half, not a gap here. HONEST LIMIT, and why closing this does not close OSV1-010: flat token-pair math models a swatch on a bare ground. The real surface puts glass panels @@ -1467,7 +1532,9 @@ motion-preference trace in OSV1-010. - id: OSV1-012 - title: calm is reported not celebrated, and empty widgets keep their slot -- no kit + title: >- + two empty widgets keep their slot and say nothing -- measured, not a + missing kit contract: file: contracts/operator-surface.v1.md clause: Core 8 @@ -1475,7 +1542,7 @@ The calm state is stated plainly and never rendered as a triumphant zero. A widget with nothing to show keeps its slot and says so in a sentence, so the page does not reflow between calm and alarm. - disposition: GAP + disposition: VIOLATION work: work_item_pipeline-c1a assertion: kind: probe @@ -1484,27 +1551,40 @@ 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". Core 8's check (`calm.keeps_slot`) is Tier A - but is defined against an ALL-EMPTY FIXTURE compared to a populated one -- - "every widget region present on the populated fixture is present, each - carries its empty sentence, and no numeral renders at hero scale outside - the Core 1 hero". That is a two-render comparison this in-process kit - cannot perform; `tests/conformance/operator_surface/test_tier_a.py` does - not exist. - - WHAT THE SOURCE ALREADY CARRIES, measured 2026-09-04, and NOT credited as - conformance -- the clause's claim is about region PRESENCE and reflow, - which no source probe can see: - widgets.py:423,433 the verdict headline is "All clear" (calm) / - "Idle" -- a plain statement, not a triumphant zero. - webapp.py:2275 "Nothing is waiting to be claimed in this queue - right now." with the figure rendered as an em-dash - (`` plus - "No ready items"), not a hero-scale `0`. - chartsvg.py:295 "No activity in this window". - webbrowse.py:498-503 "No items match this filter." - Unknown until rendered: whether every widget region survives the empty - render at all, and whether the page reflows between calm and alarm. + WAS GAP "KIT NOT BUILT"; NOW VIOLATION, MEASURED. The Tier-A kit exists and + performs the two-render comparison this clause defines, so the reason for + the red changed from "nobody has looked" to "we looked and it fails". + + MEASURED 2026-09-05, L0 and L1 rendered against an all-empty project and + against the populated ALARM fixture, regions matched by section id and by + panel heading: + SLOTS SURVIVE every widget region on the populated render is present on + the empty one, on BOTH levels. That half of the clause + holds and the check says so. + SENTENCES DO NOT three regions keep their slot and say nothing at all: + L0 "Needs you -- ranked" (`widgets.render_attention_queue` + returns a bare `
` when + `rows` is empty), L1 "Agents on " + (`widgets.render_agents_panel`, the same shape), and L1 + "Status breakdown" (a legend of zeroes, no sentence). + NO HERO-SCALE NUMERAL nothing at or above the hero's own type size + (`--text-display-size`, resolved from the token block -- + the floor is derived from the hero, never written down) + renders a numeral outside `#verdict-hero`. That limb + holds. + + SO THE ROW IS RED ON ONE LIMB OF THREE, and the kit records which: the + two `calm.keeps_slot` tests are `xfail(strict=True)` naming THIS row. When + the three regions gain their sentence the tests XPASS, which FAILS the run + -- that failure is the instruction to flip this row and delete the markers + in the same change. + + WHAT THE SOURCE ALREADY CARRIES, and still does: `widgets.py:423,433` the + verdict headline is "All clear" / "Idle" -- a plain statement, never a + triumphant zero; `webapp.py:2275` the ready queue says "Nothing is waiting + to be claimed in this queue right now." with the figure as an em-dash; + `chartsvg.py:295` "No activity in this window"; `webbrowse.py:498-503` "No + items match this filter." Four widgets get this right; three do not. - id: OSV1-013 title: no front-end framework, bundler, template engine, or build step @@ -1788,7 +1868,9 @@ (webpwa.py:121-122, webtrust.py:258-259); see OSV1-005. - id: OSV1-021 - title: Conformance 2 (alarm unmissable, never colour-only) has no test file + title: >- + Conformance 2 (alarm unmissable, never colour-only) -- Tier-A half lands, + Tier-B hue half still has no test file contract: file: contracts/operator-surface.v1.md clause: Conformance 2 @@ -1801,21 +1883,25 @@ kind: probe ref: test_row_osv1_021 notes: > - PINNING ROW -- the probe pins both named paths' ABSENCE; a passing probe - here is NOT conformance. Flip direction VIOLATION-MOVEMENT. + PINNING ROW -- one half has landed, the other has not, so the probe now + pins the TIER-B half's absence rather than both. A passing probe here is + NOT conformance. Flip direction VIOLATION-MOVEMENT. THE ONLY CONFORMANCE FIXTURE THAT SPANS BOTH TIERS, so it is owned by two - items: the Tier-A half (accessible name on every status-bearing element) - by work_item_pipeline-c1a, carried in this row's `work` ref; the Tier-B - half (the alarm region's hue is `--alarm` or `--blocked`) by - work_item_pipeline-qgo. Both must land before this row can go green; the - single-ref schema records the first, and this note records the second so - the second is not lost. - - MEASURED 2026-09-04 against 4aaee50: neither path exists. GOOD = the alarm - region is present, its hue is `--alarm` or `--blocked`, and every - status-bearing element also carries a word. BAD = a fixture whose status - chips carry only a status class fails the accessible-name assertion. + items. Status 2026-09-05: + TIER-A HALF, LANDED (work_item_pipeline-c1a). "every status-bearing + element also carries a word" is implemented as `state.not_colour_only` + in `tests/conformance/operator_surface/test_tier_a.py`, run over + rendered L0/L1/L2 of the ALARM fixture, and it PASSES -- see OSV1-004, + which carries the measurement. Its bad half is this fixture's own named + specimen ("a fixture whose status chips carry only a status class") and + the same function reports it. + TIER-B HALF, NOT BUILT (work_item_pipeline-qgo). "the alarm region is + present, its hue is `--alarm` or `--blocked`" needs a real browser; + `tests/conformance/operator_surface/browser/test_tier_b.py` does not + exist. + Both must land before this row can go green; the single-ref schema records + the first, and this note records the second so the second is not lost. - id: OSV1-022 title: Conformance 3 (body-swap survival) has no test file @@ -1874,35 +1960,54 @@ fixture has a real chance of failing on first run -- which is the point. - id: OSV1-024 - title: Conformance 5 (hero composition) has no test file + title: >- + Conformance 5 (hero composition) -- fixture exists, discriminates, and its + good half passes against the rebuilt hero contract: file: contracts/operator-surface.v1.md clause: Conformance 5 quote: | **Test location:** `tests/conformance/operator_surface/test_tier_a.py` (Core 1). - disposition: GAP + disposition: CONFORMS work: work_item_pipeline-c1a assertion: kind: probe ref: test_row_osv1_024 notes: > - PINNING ROW -- the probe pins the named path's ABSENCE; a passing probe - here is NOT conformance. Flip direction VIOLATION-MOVEMENT. - - MEASURED 2026-09-04 against 4aaee50: `tests/conformance/` does not exist. - GOOD = the hero region contains a velocity figure with its window stated - and each of in flight, blocked, needs attention, and open/ready. BAD = a - hero carrying only a verdict line, or a figure without the four counts, - fails the assertion naming what is missing. + PINNING ROW -- the fixture exists and discriminates, but its GOOD half does + not pass, so the probe pins that deferral. A passing probe here is NOT + conformance. Flip direction VIOLATION-MOVEMENT. - THE BAD HALF IS THE SHIPPED HERO. Measured in OSV1-001: webapp.py:4602 - renders exactly "a hero carrying only a verdict line". So this fixture's - bad half needs no synthetic construction -- it can be built from - `render_verdict_hero`'s pre-fix output, which is the strongest possible - demonstration that the check discriminates. + MEASURED 2026-09-05. `tests/conformance/operator_surface/test_tier_a.py` + implements `hero.velocity_and_counts` at the location Conformance 5 names, + with three bad halves and one good half: + BAD, DEMONSTRATED "a hero carrying only a verdict line" -- and it needed + no synthetic construction, exactly as this row predicted at seed: the + bad fixture IS `widgets.render_verdict_hero`'s own output shape. Also + "a figure without the four counts" (reports all four missing, and does + NOT report the figure missing), and a third that keeps the check from + crediting a loose number and a status word in different sentences as a + presented count. + GOOD, DEFERRED run against L0 rendered from a populated fixture, it + FAILS: the hero states no `blocked` count and no `needs attention` + count. Those live in the KPI strip OUTSIDE the hero. So the good half + is `xfail(strict=True)` naming OSV1-001. + + THIS ROW GOES GREEN WHEN OSV1-001 DOES, and not before: the fixture is the + fixture, but Conformance 5 is only demonstrated end-to-end once its good + half passes against a conforming hero. The probe therefore asserts both + facts -- the fixture is present with its pair, and OSV1-001 is still red -- + so flipping either one alone fails loudly. + FLIPPED GAP -> CONFORMS 2026-09-05 at highway wave-1 integration (orchestrator): the hero lane closed OSV1-001 + (L0 hero = velocity over a stated window + in-flight/blocked/needs-attention/open counts) and the kit's good half + `test_hero_velocity_and_counts` runs undeferred and PASSES on the real L0 (tier-a kit: 38 passed / 4 xfailed); the + three bad halves still fail as designed. Re-derived from the PASSING pair, not from the fixture's presence. Probe now + asserts the regression direction: good half stays undeferred and OSV1-001 stays CONFORMS. - id: OSV1-025 - title: Conformance 6 (inline-style register) has no test file + title: >- + Conformance 6 (inline-style register) -- fixture exists and discriminates, + good half deferred against OSV1-005 contract: file: contracts/operator-surface.v1.md clause: Conformance 6 @@ -1914,25 +2019,37 @@ kind: probe ref: test_row_osv1_025 notes: > - PINNING ROW -- the probe pins the named path's ABSENCE; a passing probe - here is NOT conformance. Flip direction VIOLATION-MOVEMENT. + PINNING ROW -- the fixture exists and discriminates, but its GOOD half does + not pass, so the probe pins that deferral. A passing probe here is NOT + conformance. Flip direction VIOLATION-MOVEMENT. - MEASURED 2026-09-04 against 4aaee50: `tests/conformance/` does not exist. - GOOD = zero inline `style=` attributes carry a literal colour, font, or - size; every computed-geometry site is on the register. BAD = a file - carrying `style="color:#D9A253"` is reported as a literal-colour violation, - and a computed-geometry site absent from the register is reported as - unregistered. - - NOTE THE OVERLAP, AND KEEP IT DELIBERATE: this ledger already runs that - census in-process (OSV1-005 / OSV1-006) because the register lives in - `ledger/` by ruling. The Tier-A kit's job is not to duplicate it but to - read the SAME register, so there is one census and one source of the - number. If the kit grows its own copy, the two will disagree and the - disagreement will be silent. + MEASURED 2026-09-05. `tests/conformance/operator_surface/test_tier_a.py` + implements `visual.single_source` at the location Conformance 6 names: + BAD, DEMONSTRATED the contract's own named specimen, `style="color:#D9A253"`, + is classified LITERAL by the census; and an injected computed-geometry + site is reported as unregistered -- injected over the census's OWN + reader, so the real engine does the classifying and nothing on disk is + touched. + GOOD, DEFERRED 66 literal inline sites and 40 `", "", html, flags=re.DOTALL) + html = re.sub(r"]*>.*?", "", html, flags=re.DOTALL) + builder = _TreeBuilder() + builder.feed(html) + return builder.root + + +def walk(node: Node): + for child in node.children: + yield child + yield from walk(child) + + +def find_one(root: Node, *, node_id: str) -> Node | None: + for n in walk(root): + if n.attrs.get("id") == node_id: + return n + return None + + +# =========================================================================== +# Core 1 -- hero.velocity_and_counts +# +# "the rendered L0 hero region contains a velocity figure with its window +# stated, and each of the four named counts." +# +# The four the clause names: in flight (held), blocked, needs attention, and +# open/ready. +# =========================================================================== + +#: Each count, with every word the surface could legitimately label it with. +#: Named in the clause's own order. +_HERO_COUNTS: tuple[tuple[str, tuple[str, ...]], ...] = ( + ("in flight (held)", ("in flight", "in-flight", "held", "holding")), + ("blocked", ("blocked",)), + ("needs attention", ("needs attention", "needs you", "attention", "needs-you")), + ("open/ready", ("open", "ready", "unclaimed")), +) + +#: A throughput word -- the figure itself must be one of these, not any number. +_THROUGHPUT_WORDS = ("resolved", "velocity", "throughput", "completed", "closed", "burn") + +#: A stated window. `24h`/`7d`/`30d`, "last 7 days", "per day", "/day". +_WINDOW = re.compile( + r"(\b\d+\s*(h|hr|hrs|hours?|d|days?|w|weeks?)\b|\bper day\b|/day\b|\blast \d+\b|\btoday\b)", + re.IGNORECASE, +) +_HAS_DIGIT = re.compile(r"\d") + + +def _leaf_phrases(region: Node, *, max_len: int = 140) -> list[str]: + """Short text runs in the region -- a count and its label read together. + + Bounded, because "the hero contains the word `blocked` somewhere and the + digit `3` somewhere" is not a count: the clause asks for the counts to be + PRESENTED, so the number and its label must sit in one readable run. + """ + out: list[str] = [] + for n in walk(region): + txt = " ".join(n.text().split()) + if txt and len(txt) <= max_len: + out.append(txt) + return out + + +def check_hero_velocity_and_counts(l0_html: str) -> list[str]: + """Core 1's `hero.velocity_and_counts`, against a rendered L0.""" + root = parse_html(l0_html) + hero = find_one(root, node_id="verdict-hero") + if hero is None: + return ["no L0 hero region (`#verdict-hero`) in the rendered page at all"] + + phrases = _leaf_phrases(hero) + problems: list[str] = [] + + velocity = [ + p + for p in phrases + if _HAS_DIGIT.search(p) + and any(w in p.lower() for w in _THROUGHPUT_WORDS) + and _WINDOW.search(p) + ] + if not velocity: + problems.append( + "no velocity figure with its window stated -- the hero carries no run " + f"pairing a number, a throughput word {_THROUGHPUT_WORDS} and a window" + ) + + for label, words in _HERO_COUNTS: + if not any(_HAS_DIGIT.search(p) and any(w in p.lower() for w in words) for p in phrases): + problems.append(f"the hero states no `{label}` count") + return problems + + +def test_hero_velocity_and_counts_bad_half_verdict_line_only() -> None: + """Conformance 5's bad half, verbatim: "a hero carrying only a verdict line". + + Not synthesised from nothing -- this IS `widgets.render_verdict_hero`'s + own output shape (OSV1-024's notes: "the bad half needs no synthetic + construction"), rendered around the calm verdict the shipped L0 emits. + """ + bad = """
+
Environment verdict · 21 projects
+
All clear
+
Nothing stuck, nothing waiting past its TTL.
+
""" + problems = check_hero_velocity_and_counts(bad) + assert problems, "the check passed a hero carrying only a verdict line" + assert any("velocity figure" in p for p in problems) + for label, _ in _HERO_COUNTS: + assert any(label in p for p in problems), f"the check did not name `{label}` as missing" + + +def test_hero_velocity_and_counts_bad_half_figure_without_the_counts() -> None: + """Conformance 5's other bad half: "a figure without the four counts".""" + bad = """
+
14 resolved in the last 24h
+
""" + problems = check_hero_velocity_and_counts(bad) + assert not any("velocity figure" in p for p in problems), ( + "the figure IS present with its window -- the check must not report it missing" + ) + assert len(problems) == len(_HERO_COUNTS), ( + f"expected all four counts reported missing, got {problems}" + ) + + +def test_hero_velocity_and_counts_bad_half_a_loose_number_is_not_a_count() -> None: + """A number somewhere and a word somewhere else is not a presented count.""" + bad = """
+
All clear
+
7 resolved in the last 24h.
+
Nothing is blocked, and nothing needs attention, and + the ready queue and everything in flight are described here at length + in a sentence that never actually puts a number beside any of them.
+
""" + problems = check_hero_velocity_and_counts(bad) + assert any("blocked" in p for p in problems), ( + "a status word in a long prose run with no adjacent number was credited as a count" + ) + + +def test_hero_velocity_and_counts(calm_dataset) -> None: + """Conformance 5's GOOD half: L0 rendered against a populated fixture.""" + problems = check_hero_velocity_and_counts(calm_dataset.l0) + assert not problems, "Core 1 (`hero.velocity_and_counts`):\n " + "\n ".join(problems) + + +# =========================================================================== +# Core 3 -- state.not_colour_only +# +# "every status-bearing element in the rendered L0/L1/L2 fixtures has +# non-empty text or an accessible name, not merely a status class." +# =========================================================================== + +#: The status vocabulary, read out of the app's OWN maps rather than written +#: down here: `webbrowse._ITEM_STATUS_CHIP_LABEL`'s keys (the item statuses), +#: `widgets._HERO_ICON`'s keys (the verdict states) and +#: `webapp._ATTENTION_SEVERITY`'s severities. A status the app grows and this +#: kit does not know about would otherwise be silently unchecked. +_STATUS_CLASS_PREFIXES = ("st", "is", "sev", "tab", "status") + + +def _dict_keys_in(path: Path, name: str) -> set[str]: + """The string keys of a module-level `NAME: ... = {...}` dict literal.""" + tree = ast.parse(S.read(path)) + for node in ast.walk(tree): + targets: list[ast.expr] = [] + if isinstance(node, ast.AnnAssign): + targets = [node.target] + elif isinstance(node, ast.Assign): + targets = list(node.targets) + if not any(isinstance(t, ast.Name) and t.id == name for t in targets): + continue + value = node.value if isinstance(node, ast.AnnAssign | ast.Assign) else None + if isinstance(value, ast.Dict): + return { + k.value + for k in value.keys + if isinstance(k, ast.Constant) and isinstance(k.value, str) + } + raise AssertionError(f"{path.name}: no module-level dict literal named {name!r}") + + +def _dict_values_in(path: Path, name: str) -> set[str]: + tree = ast.parse(S.read(path)) + for node in ast.walk(tree): + targets = ( + [node.target] + if isinstance(node, ast.AnnAssign) + else list(node.targets) + if isinstance(node, ast.Assign) + else [] + ) + if not any(isinstance(t, ast.Name) and t.id == name for t in targets): + continue + value = node.value if isinstance(node, ast.AnnAssign | ast.Assign) else None + if isinstance(value, ast.Dict): + return { + v.value + for v in value.values + if isinstance(v, ast.Constant) and isinstance(v.value, str) + } + raise AssertionError(f"{path.name}: no module-level dict literal named {name!r}") + + +def status_vocabulary() -> set[str]: + """Every word this surface uses to name a state, from its own source.""" + words = set(_dict_keys_in(S.WEBBROWSE, "_ITEM_STATUS_CHIP_LABEL")) + words |= {v.lower() for v in _dict_values_in(S.WEBBROWSE, "_ITEM_STATUS_CHIP_LABEL")} + words |= set(_dict_keys_in(S.WIDGETS, "_HERO_ICON")) + # The status hues themselves -- `--alarm` -> `alarm` -- so `is-alarm` and + # `sev-blocked` are recognised even though no dict literal names them. + words |= {t.lstrip("-") for t in S.STATUS_TOKENS} + return {w for w in words if w} + + +def status_classes(rendered: set[str]) -> set[str]: + """Which of a page's classes are STATUS classes. + + A class is status-bearing when it is a status word, or a status word under + one of this surface's status-class prefixes (`st-blocked`, `is-alarm`, + `sev-cr`, `tab-blocked`). The chip vocabulary is taken from + `_ITEM_STATUS_CHIP_CLASS` directly as well, so a chip class that does not + follow the prefix convention is still caught. + """ + vocab = status_vocabulary() + chips = _dict_values_in(S.WEBBROWSE, "_ITEM_STATUS_CHIP_CLASS") + out = {c for c in rendered if c in chips} + for c in rendered: + if c in vocab: + out.add(c) + continue + head, _, tail = c.partition("-") + if head in _STATUS_CLASS_PREFIXES and tail in vocab: + out.add(c) + return out + + +def check_state_not_colour_only(html: str, *, page: str) -> list[str]: + """Core 3's `state.not_colour_only`, against one rendered page.""" + root = parse_html(html) + nodes = list(walk(root)) + rendered_classes = {c for n in nodes for c in n.classes} + status = status_classes(rendered_classes) + if not status: + return [f"{page}: no status-bearing element on the page at all -- nothing was checked"] + + problems: list[str] = [] + for n in nodes: + if not (n.classes & status): + continue + if n.accessible_name(): + continue + # A decorative mark INSIDE a status-bearing element that is itself + # named carries no state of its own -- the state is already in that + # ancestor's words. Colour is redundant there, which is the clause. + if any(a.classes & status and a.accessible_name() for a in n.ancestors()): + continue + problems.append( + f"{page}: {n.describe()} carries status class(es) " + f"{sorted(n.classes & status)} and NO text or accessible name" + ) + return problems + + +def test_state_not_colour_only_bad_half_chip_with_only_a_class() -> None: + """Conformance 2's bad half, verbatim: "a fixture whose status chips carry + only a status class fails the accessible-name assertion".""" + bad = '
' + problems = check_state_not_colour_only(bad, page="BAD") + assert problems, "a status chip carrying only a class was accepted" + assert "st-blocked" in problems[0] + + +def test_state_not_colour_only_bad_half_an_accessible_name_is_enough() -> None: + """The other direction: a chip with no text but a real name PASSES. + + Without this the check would be asserting "has text", not the clause's + "non-empty text OR an accessible name" -- and would fail the surface for + an icon-only control that is correctly labelled. + """ + good = '' + assert not check_state_not_colour_only(good, page="GOOD") + + +def test_state_not_colour_only_bad_half_an_ancestors_name_does_not_travel_sideways() -> None: + """A named status ancestor excuses only its own decorative descendants.""" + bad = ( + # A decorative, unlabelled mark INSIDE a named status control: excused. + 'Blocked 3' + # A status chip standing on its own with nothing at all: reported. + '' + ) + problems = check_state_not_colour_only(bad, page="BAD") + assert len(problems) == 1 and "st-held" in problems[0], problems + + +@pytest.mark.parametrize("level", ["l0", "l1", "l2"]) +def test_state_not_colour_only(alarm_dataset, level: str) -> None: + """Conformance 2's Tier-A GOOD half, on all three IA levels. + + Run against the ALARM dataset, which is Conformance 2's own scenario -- + "the same fixture with one item held past TTL and one blocked" -- so every + status chip the surface can render is actually on the page. + """ + if level == "l2": + html = alarm_dataset.l2(alarm_dataset.item_ids[1]) # the blocked item + else: + html = getattr(alarm_dataset, level) + problems = check_state_not_colour_only(html, page=f"{alarm_dataset.label} {level.upper()}") + assert not problems, "Core 3 (`state.not_colour_only`):\n " + "\n ".join(problems) + + +# =========================================================================== +# Core 4 -- visual.single_source +# +# "zero inline `style=` attributes carrying a literal colour, font, or size +# anywhere in `src/`; every inline `style=` site carrying computed geometry +# appears on the ledger's exemption register; and zero literal colour/font/ +# size declarations in any `