diff --git a/ledger/checks/mutation_harness.py b/ledger/checks/mutation_harness.py index 1f3c33d..bfaa5cf 100644 --- a/ledger/checks/mutation_harness.py +++ b/ledger/checks/mutation_harness.py @@ -424,14 +424,13 @@ def _mo002_alias_becomes_a_bespoke_hue(w: World) -> None: def _mo003b_the_retired_palette_returns(w: World) -> None: """The retired pre-blend-3 ground comes back to webpwa.py's offline body. - DIRECTION NOTE, stated rather than hidden: OSV1-003's declared direction is - VIOLATION-MOVEMENT (it is still VIOLATION -- a calm L1 still paints - `--blocked`, which `_mo003_the_calm_page_stops_painting_blocked` is the - counterfactual for). This mutation pushes the OTHER way, and it is here on - purpose: the row's two palette specimens - CLOSED on 2026-09-05 (OSV1-005), so its probe stopped pinning them as - present and started guarding that they stay gone. A guard nobody has - watched fail is a guard that might assert nothing. + DIRECTION NOTE, no longer an exception: OSV1-003 flipped to CONFORMS on + 2026-09-05 (work_item_pipeline-a1o), so the whole row is REGRESSION-directed + now and this mutation is the same shape as its two siblings rather than the + odd one out. It was already pushing this way while the row was red, because + the row's two palette specimens CLOSED on 2026-09-05 (OSV1-005) and its + probe stopped pinning them as present and started guarding that they stay + gone. A guard nobody has watched fail is a guard that might assert nothing. """ w.replace( WEBPWA, @@ -507,14 +506,14 @@ def _mo009a_the_media_light_block_regresses(w: World) -> None: 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;") + w.replace(WEBTHEME, " --ink-quiet:#4e5764;", " --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( WEBTHEME, - " --ink-quiet:#596473; /* likewise", + " --ink-quiet:#4e5764; /* likewise", " --ink-quiet:#7c8ba0; /* likewise", ) @@ -528,22 +527,35 @@ def _mo011_a_second_motion_block_appears(w: World) -> None: ) -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.""" +def _mo012_the_empty_slot_goes_silent_again(w: World) -> None: + """REGRESSION: `render_attention_queue` loses its empty branch, so the L0 + region goes back to keeping its slot and saying nothing. + + TURNED ROUND 2026-09-05 (work_item_pipeline-aad). While the row was + VIOLATION this mutation was the FIXED world (the empty branch APPEARING); + now the row is CONFORMS the direction reverses and the defect it closed -- + a bare `
` -- is the counterfactual. + """ 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())', + ' return f\'
' + '{_empty_note("No item needs you right now.")}
\'\n', + ' if not data["rows"]:\n return \'
\'\n', ) +def _mo012_the_slot_loses_its_min_height(w: World) -> None: + """REGRESSION, the OTHER limb: the sentence survives but `.empty-note` + stops holding a box, so the slot collapses when its data goes away. + + Core 8 has two halves -- "keeps its slot" AND "says so in a sentence" -- and + a probe that only read the words would credit a collapsing widget with + conformance. One mutation per half, for that reason. + """ + w.replace(WEBTHEME, " min-height:44px;margin:0;", " margin:0;") + + def _mo013_a_template_engine_is_declared(w: World) -> None: """REGRESSION: the manifest acquires a template engine.""" w.replace(PYPROJECT, '"pyyaml>=6.0",', '"pyyaml>=6.0",\n "jinja2>=3.1",') @@ -618,74 +630,168 @@ def _mo027_ci_stops_running_the_kit(w: World) -> None: ) -def _mo003_the_calm_page_stops_painting_blocked(w: World) -> None: - """FIXED: a calm L1 stops painting `--blocked`. +def _mo003_the_calm_page_paints_blocked_again(w: World) -> None: + """REGRESSION: a calm L1 goes back to painting `--blocked`. - The counterfactual a pinning row needs -- the browser measuring the FIXED - behaviour. Both themes measured 97, so both anchors move together. + DIRECTION REVERSED 2026-09-05 with the row (work_item_pipeline-a1o). + While OSV1-003 was a VIOLATION pin this mutation had to model the FIX (97 + -> 0); the fix landed, the row reads CONFORMS, and the counterfactual a + green row needs is the defect COMING BACK. The number restored is the one + actually measured before the fix, not a token non-zero: 97 --blocked on a + calm L1 in dark. """ w.replace( TIER_B_SUMMARY, - '"calm/L1/dark": {\n "alarm": 0,\n "blocked": 97,', '"calm/L1/dark": {\n "alarm": 0,\n "blocked": 0,', + '"calm/L1/dark": {\n "alarm": 0,\n "blocked": 97,', ) -def _mo008_the_swap_starts_restoring_the_disclosure(w: World) -> None: - """FIXED: an open `
` survives the body-swap on L0.""" +def _mo003c_the_alarming_fixture_stops_painting_blocked(w: World) -> None: + """REGRESSION, and the one that matters most on a row full of zeroes. + + "Zero alarm pixels on a calm page" is trivially satisfiable by a sweep that + has stopped seeing the hue at all, so OSV1-003's green rests on the + discriminating arm as much as on the calm one. This blinds that arm -- the + genuinely-alarming fixture reporting 0 --blocked where it measured 264 -- + and the probe must refuse the row rather than keep reading its own zeroes + as evidence. + """ w.replace( TIER_B_SUMMARY, - '"calm/L0/dark": {\n "details_with_id": 0,\n "live_regions_before": 1,\n' - ' "marked_live_regions_after": 0,\n "open_details_preserved": false,', - '"calm/L0/dark": {\n "details_with_id": 2,\n "live_regions_before": 1,\n' - ' "marked_live_regions_after": 0,\n "open_details_preserved": true,', + '"bad-alarm-fixture/L0/dark": {\n "alarm": 0,\n "blocked": 264,', + '"bad-alarm-fixture/L0/dark": {\n "alarm": 0,\n "blocked": 0,', ) -def _mo008_the_announcement_survives_the_swap(w: World) -> None: - """FIXED: the live region tagged before the swap SURVIVES it on L0. +def _mo008_the_disclosure_stops_surviving(w: World) -> None: + """REGRESSION: an open `
` stops surviving the body-swap on L0. - Newly measurable since the hero rebuild: before it, L0 rendered no live - region at all and Core 6's announcement half had nothing to preserve. Now - there is exactly one (`role="status"`), the swap destroys it, and a fix - that carried it across would flip this half of the row. + Direction flipped 2026-09-05 with the row (work_item_pipeline-v3m): + OSV1-008 reads CONFORMS now, so the mutation that must be WATCHED FAILING + is the one that takes the survival away, not the one that grants it. """ w.replace( TIER_B_SUMMARY, - '"calm/L0/dark": {\n "details_with_id": 0,\n "live_regions_before": 1,\n' - ' "marked_live_regions_after": 0,', - '"calm/L0/dark": {\n "details_with_id": 0,\n "live_regions_before": 1,\n' - ' "marked_live_regions_after": 1,', + '"calm/L0/dark": {\n "announcement_present_before": true,\n' + ' "announcement_preserved": true,\n "details_with_id": 0,\n' + ' "live_regions_before": 2,\n "marked_live_regions_after": 1,\n' + ' "open_details_preserved": true,', + '"calm/L0/dark": {\n "announcement_present_before": true,\n' + ' "announcement_preserved": true,\n "details_with_id": 0,\n' + ' "live_regions_before": 2,\n "marked_live_regions_after": 1,\n' + ' "open_details_preserved": false,', ) -def _mo008_the_pause_control_starts_surviving(w: World) -> None: - """FIXED: the pause CONTROL's own state survives the swap on L0. +def _mo008_the_announcement_stops_surviving(w: World) -> None: + """REGRESSION: the live region tagged before the swap no longer survives it. + + Separately watched from the text half below, and from the disclosure half + above, because they are three different mechanisms: node identity is what + the poller's detach/re-attach buys, and it is the only reading a + destroy-and-rebuild cannot fake. + """ + w.replace( + TIER_B_SUMMARY, + '"calm/L0/dark": {\n "announcement_present_before": true,\n' + ' "announcement_preserved": true,\n "details_with_id": 0,\n' + ' "live_regions_before": 2,\n "marked_live_regions_after": 1,', + '"calm/L0/dark": {\n "announcement_present_before": true,\n' + ' "announcement_preserved": true,\n "details_with_id": 0,\n' + ' "live_regions_before": 2,\n "marked_live_regions_after": 0,', + ) + - 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`. +def _mo008_the_announcement_text_changes(w: World) -> None: + """REGRESSION: the surviving region's text is replaced across the swap. + + The node survives, so `marked_live_regions_after` still reads 1 -- and the + announcement is still cut off. This is the half a node-identity check alone + would miss, which is why the row asserts both. + """ + w.replace( + TIER_B_SUMMARY, + '"calm/L0/dark": {\n "announcement_present_before": true,\n' + ' "announcement_preserved": true,', + '"calm/L0/dark": {\n "announcement_present_before": true,\n' + ' "announcement_preserved": false,', + ) + + +def _mo008_the_pause_control_stops_surviving(w: World) -> None: + """REGRESSION: the pause CONTROL's own state stops surviving the swap on L0. + + Separable from the disclosure half, and pinned separately, because the two + are different mechanisms: one re-opens disclosures by ordinal + id, the + other re-synchronises the re-rendered button 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": 1,\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": 1,\n' - ' "marked_live_regions_after": 0,\n "open_details_preserved": false,\n' - ' "pause_control_preserved": true,', + '"calm/L0/dark": {\n "announcement_present_before": true,\n' + ' "announcement_preserved": true,\n "details_with_id": 0,\n' + ' "live_regions_before": 2,\n "marked_live_regions_after": 1,\n' + ' "open_details_preserved": true,\n "pause_control_preserved": true,', + '"calm/L0/dark": {\n "announcement_present_before": true,\n' + ' "announcement_preserved": true,\n "details_with_id": 0,\n' + ' "live_regions_before": 2,\n "marked_live_regions_after": 1,\n' + ' "open_details_preserved": true,\n "pause_control_preserved": false,', ) -def _mo010_the_target_floor_is_met(w: World) -> None: - """FIXED: every interactive control on L0 reaches 44px.""" +def _mo008_l1_loses_its_live_region(w: World) -> None: + """REGRESSION: L1 goes back to rendering NO live region at all. + + The state this row recorded before the fix, and the one that fails Core 6 + one step EARLIER than the swap: with nothing rendered there is nothing to + destroy and nothing for the operator to hear. + """ + w.replace( + TIER_B_SUMMARY, + '"calm/L1/dark": {\n "announcement_present_before": true,\n' + ' "announcement_preserved": true,\n "details_with_id": 0,\n' + ' "live_regions_before": 1,', + '"calm/L1/dark": {\n "announcement_present_before": true,\n' + ' "announcement_preserved": true,\n "details_with_id": 0,\n' + ' "live_regions_before": 0,', + ) + + +def _mo010_a_control_falls_back_under_the_target_floor(w: World) -> None: + """REGRESSION: the browser measures a control back under 44px on L0. + + RETARGETED 2026-09-05 with the row (work_item_pipeline-96f). This used to + inject the FIX -- `controls_below_44px` 26 -> 0 -- because OSV1-010 was a + pin. The row now reads CONFORMS and asserts the floors are MET, so the + counterfactual that has to go red is the shape it forbids: one control + slipping back under the floor on ONE render of eighteen. The probe sweeps + every render for exactly this reason. + """ w.replace( TIER_B_SUMMARY, - '"calm/L0/1280/dark": {\n "client_width": 1280,\n "controls": 34,\n' - ' "controls_below_44px": 26,', '"calm/L0/1280/dark": {\n "client_width": 1280,\n "controls": 34,\n' ' "controls_below_44px": 0,', + '"calm/L0/1280/dark": {\n "client_width": 1280,\n "controls": 34,\n' + ' "controls_below_44px": 1,', + ) + + +def _mo010_the_donut_exemption_grows(w: World) -> None: + """REGRESSION: the ONE enumerated non-text exemption widens. + + The failure mode an exemption invites: the non-text arm stays green not + because the borders were fixed but because the allowance grew to cover + them. A second exempted element on L1 must go red here, and does. + """ + w.replace( + TIER_B_SUMMARY, + '"calm/L1/1280/dark": {\n "client_width": 1280,\n "controls": 41,\n' + ' "controls_below_44px": 0,\n "elements_beyond_viewport": 0,\n' + ' "non_text_below_floor": 0,\n "non_text_exempt_below_floor": 1,', + '"calm/L1/1280/dark": {\n "client_width": 1280,\n "controls": 41,\n' + ' "controls_below_44px": 0,\n "elements_beyond_viewport": 0,\n' + ' "non_text_below_floor": 0,\n "non_text_exempt_below_floor": 4,', ) @@ -701,13 +807,15 @@ def _mo011_an_animation_runs_under_the_preference(w: World) -> None: w.replace( TIER_B_SUMMARY, '"calm/L0/430/dark": {\n "client_width": 430,\n "controls": 34,\n' - ' "controls_below_44px": 16,\n "elements_beyond_viewport": 0,\n' - ' "non_text_below_floor": 16,\n "non_text_measured": 77,\n' + ' "controls_below_44px": 0,\n "elements_beyond_viewport": 0,\n' + ' "non_text_below_floor": 0,\n "non_text_exempt_below_floor": 0,\n' + ' "non_text_measured": 77,\n' ' "overflow_x_style": "clip",\n' ' "running_animations_under_reduced_motion": 0,', '"calm/L0/430/dark": {\n "client_width": 430,\n "controls": 34,\n' - ' "controls_below_44px": 16,\n "elements_beyond_viewport": 0,\n' - ' "non_text_below_floor": 16,\n "non_text_measured": 77,\n' + ' "controls_below_44px": 0,\n "elements_beyond_viewport": 0,\n' + ' "non_text_below_floor": 0,\n "non_text_exempt_below_floor": 0,\n' + ' "non_text_measured": 77,\n' ' "overflow_x_style": "clip",\n' ' "running_animations_under_reduced_motion": 6,', ) @@ -794,11 +902,13 @@ def _mo022_the_swap_bad_half_stops_discriminating(w: World) -> None: 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": 1,\n "marked_live_regions_after": 0,\n' + '"bad-naive-replacement/L0/dark": {\n "announcement_present_before": true,\n' + ' "announcement_preserved": true,\n "details_with_id": 0,\n' + ' "live_regions_before": 2,\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": 1,\n "marked_live_regions_after": 0,\n' + '"bad-naive-replacement/L0/dark": {\n "announcement_present_before": true,\n' + ' "announcement_preserved": true,\n "details_with_id": 0,\n' + ' "live_regions_before": 2,\n "marked_live_regions_after": 0,\n' ' "open_details_preserved": true,', ) @@ -847,15 +957,31 @@ def _mo029_the_kit_stops_reading_its_artifacts_back(w: World) -> None: ) -def _mo031_a_red_core_row_goes_green(w: World) -> None: - """FIXED: one of the ten red Core-carrying rows flips, so the gate's tally - moves. Reaches the ledger through the patched reader plus the cache clear - `applied()` performs -- `rows()` memoises the parse. +def _mo031_a_green_core_row_goes_red(w: World) -> None: + """REGRESSION: a Core-carrying row that reads CONFORMS is pushed back to + VIOLATION, so Freeze 5's condition stops holding while the Freeze row above + it still reads CONFORMS. Reaches the ledger through the patched reader plus + the cache clear `applied()` performs -- `rows()` memoises the parse. + + REWRITTEN 2026-09-05 at the wave-4 union (orchestrator). It used to be a + FIXED mutation -- flip one of the RED Core rows green and watch the "at + least one is still red" pin notice. There are no red Core rows left to + flip, so that counterfactual is not merely unnecessary, it is unbuildable: + the mutation would have to invent a red row first. The probe was retargeted + to `len(red) == 0` in the same change, and its direction is now REGRESSION, + so this mutation moves in the matching direction. + + Anchored on OSV1-008's own probe REF rather than on its `work:` id or a + bare disposition line: `disposition: CONFORMS` now appears on 31 OSV1 rows, + and an anchor that matched any of them would prove nothing about which row + moved. The ref is unique by construction -- one probe per row. """ w.replace( ROWS_PATH, - " disposition: VIOLATION\n work: work_item_pipeline-c1a", - " disposition: CONFORMS\n work: work_item_pipeline-c1a", + " disposition: CONFORMS\n work: work_item_pipeline-v3m\n" + " assertion:\n kind: probe\n ref: test_row_osv1_008", + " disposition: VIOLATION\n work: work_item_pipeline-v3m\n" + " assertion:\n kind: probe\n ref: test_row_osv1_008", ) @@ -896,13 +1022,33 @@ def _mo025_the_literal_style_row_goes_red_again(w: World) -> None: ) -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.""" +def _mo026_the_l1_good_half_is_deferred_again(w: World) -> None: + """REGRESSION: Conformance 7's L1 good half goes back behind an xfail while + this row still reads CONFORMS -- a green Conformance row whose fixture no + longer runs is the claim-without-check Freeze 4 forbids. + + TURNED ROUND 2026-09-05 (work_item_pipeline-aad): while the row was GAP the + counterfactual was OSV1-012 CLOSING; now that both are green it is the + deferral returning. Aimed at the L1 half specifically, because Conformance + 7 names L0 AND L1 and a probe that only watched L0 would miss half of it. + """ + w.replace( + _support.REPO_ROOT / TIER_A_KIT, + "def test_calm_keeps_slot_l1(alarm_dataset, empty_dataset) -> None:", + '@pytest.mark.xfail(strict=True, reason="OSV1-012 regressed")\n' + "def test_calm_keeps_slot_l1(alarm_dataset, empty_dataset) -> None:", + ) + + +def _mo026_the_empty_slot_row_goes_red_again(w: World) -> None: + """REGRESSION: OSV1-012 reopens, so Conformance 7 is no longer demonstrated + end-to-end. The two move together by construction.""" w.replace( ROWS_PATH, - " disposition: VIOLATION\n work: work_item_pipeline-c1a", - " disposition: CONFORMS\n work: work_item_pipeline-c1a", + " disposition: CONFORMS\n work: work_item_pipeline-aad\n" + " assertion:\n kind: probe\n ref: test_row_osv1_012", + " disposition: VIOLATION\n work: work_item_pipeline-aad\n" + " assertion:\n kind: probe\n ref: test_row_osv1_012", ) @@ -1042,8 +1188,8 @@ def _mo034_the_changelog_records_a_look(w: World) -> None: ), Mutation( "OSV1-003", - "the browser measures a calm L1 painting ZERO --blocked pixels (the fix)", - _mo003_the_calm_page_stops_painting_blocked, + "the browser measures a calm L1 painting --blocked again (97 px, the pre-fix reading)", + _mo003_the_calm_page_paints_blocked_again, ), Mutation( "OSV1-003", @@ -1052,6 +1198,12 @@ def _mo034_the_changelog_records_a_look(w: World) -> None: "guarded from the other side)", _mo003b_the_retired_palette_returns, ), + Mutation( + "OSV1-003", + "the sweep goes blind: the genuinely-alarming fixture reports 0 --blocked " + "where it measured 264, so the row's calm zeroes stop being evidence", + _mo003c_the_alarming_fixture_stops_painting_blocked, + ), Mutation( "OSV1-004", "a status chip loses its word and carries only a class", @@ -1082,20 +1234,31 @@ def _mo034_the_changelog_records_a_look(w: World) -> None: ), Mutation( "OSV1-008", - "the browser measures an open `
` surviving the swap (the fix)", - _mo008_the_swap_starts_restoring_the_disclosure, + "the browser measures an open `
` no longer surviving the swap", + _mo008_the_disclosure_stops_surviving, + ), + Mutation( + "OSV1-008", + "the browser measures the pause CONTROL no longer surviving the swap", + _mo008_the_pause_control_stops_surviving, ), Mutation( "OSV1-008", - "the browser measures the pause CONTROL surviving the swap (the fix)", - _mo008_the_pause_control_starts_surviving, + "the browser measures L0's tagged live region destroyed by the swap again " + "(node identity lost -- the reading a destroy-and-rebuild cannot fake)", + _mo008_the_announcement_stops_surviving, ), Mutation( "OSV1-008", - "the browser measures L0's live region surviving the swap (the fix, on the " - "half that only became measurable when the hero rebuild gave L0 a " - "`role=status` region to destroy)", - _mo008_the_announcement_survives_the_swap, + "the surviving region's announcement is replaced across the swap (node kept, " + "sentence cut off -- the half a node-identity check alone would miss)", + _mo008_the_announcement_text_changes, + ), + Mutation( + "OSV1-008", + "L1 goes back to rendering no live region at all (Core 6's announcement half " + "failing one step earlier than the swap)", + _mo008_l1_loses_its_live_region, ), Mutation( "OSV1-009", @@ -1109,8 +1272,13 @@ def _mo034_the_changelog_records_a_look(w: World) -> None: ), Mutation( "OSV1-010", - "the browser measures every interactive control on L0 reaching 44px (the fix)", - _mo010_the_target_floor_is_met, + "the browser measures one interactive control back under 44px on a swept render", + _mo010_a_control_falls_back_under_the_target_floor, + ), + Mutation( + "OSV1-010", + "the one enumerated non-text exemption grows to cover more than the donut track", + _mo010_the_donut_exemption_grows, ), Mutation( "OSV1-011", @@ -1125,8 +1293,13 @@ def _mo034_the_changelog_records_a_look(w: World) -> None: ), Mutation( "OSV1-012", - "the empty attention queue grows the sentence Core 8 asks for", - _mo012_the_empty_slot_gains_its_sentence, + "the empty attention queue goes back to a bare container -- slot, no sentence", + _mo012_the_empty_slot_goes_silent_again, + ), + Mutation( + "OSV1-012", + "the sentence survives but `.empty-note` stops holding a box, so the slot collapses", + _mo012_the_slot_loses_its_min_height, ), Mutation( "OSV1-013", "the manifest declares a template engine", _mo013_a_template_engine_is_declared @@ -1210,8 +1383,13 @@ def _mo034_the_changelog_records_a_look(w: World) -> None: ), Mutation( "OSV1-026", - "OSV1-012 closes, so Conformance 7's deferred good halves should now pass", - _mo026_the_empty_slot_row_goes_green, + "Conformance 7's L1 good half is deferred again behind an xfail naming OSV1-012", + _mo026_the_l1_good_half_is_deferred_again, + ), + Mutation( + "OSV1-026", + "OSV1-012 reopens, so Conformance 7 is no longer demonstrated end-to-end", + _mo026_the_empty_slot_row_goes_red_again, ), Mutation( "OSV1-027", @@ -1247,8 +1425,9 @@ def _mo034_the_changelog_records_a_look(w: World) -> None: ), Mutation( "OSV1-031", - "one of the five red Core-carrying rows flips to CONFORMS", - _mo031_a_red_core_row_goes_green, + "a green Core-carrying row (OSV1-008) goes back to VIOLATION, so Freeze 5's " + "condition stops holding under a Freeze row that still reads CONFORMS", + _mo031_a_green_core_row_goes_red, ), Mutation( "OSV1-032", diff --git a/ledger/checks/test_operator_rows.py b/ledger/checks/test_operator_rows.py index 54108d8..ca2573a 100644 --- a/ledger/checks/test_operator_rows.py +++ b/ledger/checks/test_operator_rows.py @@ -379,28 +379,51 @@ def test_row_osv1_002() -> None: def test_row_osv1_003() -> None: - """Core 2 VIOLATION pin, RE-READ from the browser run's own numbers. + """Core 2 CONFORMS, RE-READ from the browser run's own numbers. + + FLIPPED 2026-09-05 from VIOLATION (VIOLATION-MOVEMENT), so the direction + reverses with it: this used to PIN a calm L1 at 97 `--blocked` pixels, and + now asserts the INVARIANT the clause states. Flip direction is REGRESSION. 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. + wrote pixel counts and this reads them back off disk. + + THE DISCRIMINATING ARM IS ASSERTED HERE TOO, and that is the point. "Zero + alarm pixels" is trivially satisfiable by a sweep that has stopped + measuring, so a green half that only reads zeroes is not evidence. The + genuinely-alarming fixture must still paint `--blocked`, and the alarming + L1 must still paint a reserved hue at all -- if either goes quiet, this row + is reading a blinded instrument, not a calm page. """ 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)." - ) + for level in ("L0", "L1"): + calm = tier_b("calm.zero_alarm_pixels", f"calm/{level}/{theme}") + assert calm["alarm"] == 0 and calm["blocked"] == 0, ( + f"OSV1-003 (Core 2) REGRESSION: a calm {level} in {theme} paints " + f"{calm['alarm']} --alarm and {calm['blocked']} --blocked pixels " + f"across {calm['pixels_swept']} swept, with nothing held past TTL " + f"and nothing blocked. Core 2: that absence is what makes the alarm " + f"pop. L1 measured 97 --blocked until work_item_pipeline-a1o -- 81 " + f"from the status-donut legend's zero-count `span.sw.mix-blocked`, " + f"16 from the Blocked tab's `span.dot`; both keep their slot and " + f"drop the hue at zero now, so a non-zero reading here means one of " + f"them took its hue back or a new painter appeared." + ) + alarming = tier_b("calm.zero_alarm_pixels", "bad-alarm-fixture/L0/dark") + assert alarming["blocked"] > 0, ( + f"OSV1-003 (Core 2): the genuinely-alarming fixture painted " + f"{alarming['blocked']} --blocked pixels. The calm zeroes above are only " + f"evidence while this arm still discriminates -- a sweep that has stopped " + f"seeing the hue reports a calm page and an alarming one identically." + ) + reserved = tier_b("alarm.reserved_hue", "alarm/L1/dark") + assert reserved["alarm"] > 0 or reserved["blocked"] > 0, ( + f"OSV1-003 (Core 2): an L1 rendered WITH a blocked item painted " + f"{reserved['alarm']} --alarm and {reserved['blocked']} --blocked pixels. " + f"The fix that took this row green had to quiet the hue at ZERO only; if " + f"it also quieted it when the status is real, the absence no longer makes " + f"anything pop." + ) # THE PALETTE SPECIMENS CLOSED 2026-09-05 (work_item_pipeline-np3, OSV1-005). # This row's own VIOLATION is unchanged -- a calm L1 still paints `--blocked`, # which is what the recorded sweep above measures. But the two specimens it @@ -537,11 +560,23 @@ def test_row_osv1_005() -> None: "webapp.py:1127", # flex:{n} 1 0 -- state-bar segment ratio "webapp.py:1823", # width:{today_w}px -- throughput bar, today "webapp.py:1826", # width:{prior_w}px -- throughput bar, prior 6d - "webtheme.py:4197", # {style} -- axis ruler numeral offset - "webtheme.py:4216", # left:{_grad_x(f):.1f}px -- graduation tick offset - "webtheme.py:4223", # width:{px}px -- age bar length - "widgets.py:837", # width:{pct}% -- status-mix segment (hatched) - "widgets.py:839", # width:{pct}% -- status-mix segment + # +182 lines on 2026-09-05 at the wave-4 union (was 4197/4216/4223 on + # main @065da04), and RE-MEASURED here rather than transcribed from any + # lane: three lanes each inserted CSS ABOVE these three sites in the same + # file -- calm-pixels +22 (work_item_pipeline-a1o), swap-survives +78 + # (work_item_pipeline-v3m, the live-region rules), rendered-floors +82 + # (work_item_pipeline-96f, the light token blocks and control sizing). + # 22 + 78 + 82 = 182, which is exactly the observed shift, so every one + # of the three is accounted for and none of them is a NEW site. The + # SITES are unchanged and the register did not GROW -- same eight, same + # three expressions -- but the pins are line numbers, so any edit higher + # in the file moves them. Re-pinned here rather than loosened: a register + # that stops naming an exact line stops being a register. + "webtheme.py:4379", # {style} -- axis ruler numeral offset + "webtheme.py:4398", # left:{_grad_x(f):.1f}px -- graduation tick offset + "webtheme.py:4405", # width:{px}px -- age bar length + "widgets.py:866", # width:{pct}% -- status-mix segment (hatched) + "widgets.py:868", # width:{pct}% -- status-mix segment } ) @@ -608,71 +643,102 @@ def test_row_osv1_007() -> None: #: Live regions present BEFORE the forced swap, per level, as the 2026-09-05 -#: re-recorded run measures them. L0 renders exactly ONE since the hero -#: rebuild landed (`widgets.py:1379`, the verdict hero's `role="status"`); L1 -#: still renders none. Pinned per level rather than as a single number, -#: because the two levels answer Core 6's announcement half differently and a -#: shared pin would let one move under the other. -_LIVE_REGIONS_BEFORE_SWAP = {"L0": 1, "L1": 0} +#: RE-RECORDED run measures them, after the Core 6 fix. +#: +#: L0 renders TWO: the verdict hero's `role="status"` (`widgets.py:1379`) and +#: the persistent `#wt-live` region the fix added (`webapp.py`'s +#: `_live_region_html`). L1 renders ONE -- it had NONE before the fix, which is +#: why Core 6's announcement half failed there one step earlier than on L0. +#: Pinned per level rather than as a single number, because the two levels +#: reach the clause differently and a shared pin would let one move under the +#: other. +_LIVE_REGIONS_BEFORE_SWAP = {"L0": 2, "L1": 1} def test_row_osv1_008() -> None: - """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. + """Core 6 CONFORMS: all four named survivals hold, RE-READ from the run. + + RETARGETED from the VIOLATION pin (work_item_pipeline-v3m, 2026-09-05). + The pin froze the wrong shape -- one of four survivals holding -- and it + broke the right way when the fix landed. What it asserts now is the + conforming shape, still pinned per survival, because they are separable + mechanisms and a regression in any ONE of them is a regression this row + must catch rather than average away. + + Every number is re-read from the kit's committed run summary + (`LAST_RUN.json`); this row never trusts the browser tier's own pass/fail + (Freeze 3 / Phase-1 ruling 6). """ 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." + f"OSV1-008 (Core 6) REGRESSION on {level}: scroll offset stopped " + f"surviving the body-swap. `restoreState`'s `window.scrollTo` runs " + f"LAST, after the disclosures are re-opened -- check nothing moved it " + f"back above them." ) - 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["open_details_preserved"], ( + f"OSV1-008 (Core 6) REGRESSION on {level}: an open `
` stopped " + f"surviving the swap. `restoreState` records open disclosures by " + f"ORDINAL + class signature as well as by id -- the id path alone has " + f"ZERO targets on this surface, which is exactly how this used to fail." ) 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." + f"OSV1-008 (Core 6) PIN MOVED on {level}: {m['details_with_id']} " + f"`
` now carry an id. That is not a regression -- it is the " + f"id path acquiring its first real targets -- but it changes WHICH " + f"mechanism is carrying the disclosure half, so re-derive this row " + f"and confirm the ordinal path is still exercised." ) - 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_control_preserved"], ( + f"OSV1-008 (Core 6) REGRESSION on {level}: the pause CONTROL stopped " + f"surviving the swap -- a paused page shows itself as running again. " + f"The control is re-synchronised to `window.__wtRefreshPaused` after " + f"every swap (`restorePauseControl`); the operator reads the control, " + f"not the flag." ) 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." + f"OSV1-008 (Core 6) REGRESSION on {level}: `window.__wtRefreshPaused` " + f"stopped surviving the swap. The flag living on `window` is why " + f"polling stays paused at all." ) assert m["live_regions_before"] == _LIVE_REGIONS_BEFORE_SWAP[level], ( f"OSV1-008 (Core 6) PIN MOVED on {level}: the page renders " f"{m['live_regions_before']} live region(s) before the swap, pinned at " f"{_LIVE_REGIONS_BEFORE_SWAP[level]}. Movement in either direction " f"changes what Core 6's announcement half is even asking -- re-derive " - f"this row from the new swap measurement (work_item_pipeline-qgo)." + f"this row from the new swap measurement." ) - assert m["marked_live_regions_after"] == 0, ( - f"OSV1-008 (Core 6) PIN BROKE THE RIGHT WAY on {level}: " - f"{m['marked_live_regions_after']} of the live region(s) tagged before " - f"the swap SURVIVED it. On L0 that is the announcement half closing -- " - f"re-derive this row from the new measurement." + assert m["marked_live_regions_after"] > 0, ( + f"OSV1-008 (Core 6) REGRESSION on {level}: none of the " + f"{m['live_regions_before']} live region(s) tagged before the swap " + f"survived it. NODE IDENTITY is the reading that matters here: a " + f"region destroyed and rebuilt carrying the same sentence has still " + f"cut off whatever was being announced." ) - 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 " - "in the source. Re-derive this row from the Tier-B snapshot rather than " - "from its presence." + assert m["announcement_present_before"], ( + f"OSV1-008 (Core 6) on {level}: the persistent region carried no text " + f"before the swap, so 'the announcement survived' is vacuous -- an " + f"empty region announces nothing whether it survives or not." + ) + assert m["announcement_preserved"], ( + f"OSV1-008 (Core 6) REGRESSION on {level}: the surviving region's " + f"announcement changed across the swap. The node survived but what it " + f"was saying did not." + ) + assert count(WEBAPP, "aria-live") == 2, ( + f"OSV1-008 (Core 6) PIN MOVED: `aria-live` occurs " + f"{count(WEBAPP, 'aria-live')} time(s) in webapp.py, pinned at 2 (the " + f"`_live_region_html` markup and its own docstring). This row's " + f"announcement half rests on there being exactly ONE persistent region " + f"per polling level; a second declaration site means a second region, " + f"and only one of them is the node the poller carries across the swap." ) assert contains(WIDGETS, ' role="status">'), ( - 'OSV1-008 (Core 6): the verdict hero\'s `role="status"` region is gone -- ' - "that is the ONE live region L0 renders, and the thing the swap destroys. " - "Re-derive this row (and OSV1-001's hero rebuild) from a fresh run." + 'OSV1-008 (Core 6): the verdict hero\'s `role="status"` region is gone. ' + "It is not the region the swap preserves -- `#wt-live` is -- but it IS " + "one of the two L0 renders, so losing it moves `live_regions_before` and " + "invalidates the pin above." ) @@ -770,45 +836,108 @@ def test_row_osv1_009() -> None: def test_row_osv1_010() -> None: - """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. + """Core 7 CONFORMS (rendered half), RE-READ from the browser run. + + RETARGETED 2026-09-05 from the VIOLATION pin (work_item_pipeline-96f). The + pin froze three failing floors -- 7 text nodes below 4.5:1, 26 of 34 + interactive controls under 44px on L0, and 16-23 non-text surfaces per + level below 3:1 -- and asserted the fourth (reduced motion) already passed. + All four are now asserted in the REGRESSION direction, over the recorded + run rather than the browser tier's own green (Freeze 3). + + Swept over EVERY recorded render, not one scenario: three floors are + theme- and width-dependent (the light blocks are held in sync only by + comment, and 430px hides controls the wider viewports show), so reading a + single scenario would let seventeen others move unseen. + + The ONE enumerated exemption -- the status-mix donut's backing ring, see + `_probe.NON_TEXT_EXEMPT_CLASSES` -- is asserted here too, by SIZE and by + WHERE it fires, so the allowance cannot quietly grow into the thing that + keeps the non-text arm green. """ - 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"] == 4, ( - 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 / 4 (light was 5 before the contrast lane moved " - f"`--ink-quiet`). Movement in either direction means the render changed " - f"-- re-derive (work_item_pipeline-qgo)." - ) - assert l0["controls_below_44px"] == 26 and l0["controls"] == 34, ( - 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 34 " - f"(35 before the hero rebuild replaced one control)." - ) - 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." + renders = { + scenario: headline + for scenario, headline in tier_b_summary()["checks"]["perception.floors"].items() + if scenario.startswith("calm/") + } + assert len(renders) == 18, ( + f"OSV1-010 (Core 7): the recorded run sweeps {len(renders)} renders, not the " + f"18 (L0/L1/L2 x 430/900/1280 x dark/light) this clause names. A narrowed " + f"sweep is a narrowed claim -- re-derive." ) + + text = {s: h["text_below_floor"] for s, h in sorted(renders.items()) if h["text_below_floor"]} + assert not text, ( + f"OSV1-010 (Core 7) REGRESSION, text floor: {text} -- text below 4.5:1 against " + f"its own RENDERED background. Fix the TOKEN and in ALL FOUR declared blocks " + f"(the two light ones are held in sync only by comment); flat pair math " + f"clearing the floor is NOT sufficient here, which is what OSV1-009's honest " + f"limit records and what this row measured." + ) + + targets = { + s: f"{h['controls_below_44px']} of {h['controls']}" + for s, h in sorted(renders.items()) + if h["controls_below_44px"] + } + assert not targets, ( + f"OSV1-010 (Core 7) REGRESSION, target floor: {targets} -- interactive controls " + f"under 44px on their smaller side. The hit area is what has to reach --u; it " + f"need not be the visual size." + ) + + non_text = { + s: f"{h['non_text_below_floor']} of {h['non_text_measured']}" + for s, h in sorted(renders.items()) + if h["non_text_below_floor"] + } + assert not non_text, ( + f"OSV1-010 (Core 7) REGRESSION, non-text floor: {non_text} -- control borders " + f"or icon strokes below 3:1. `--control-edge` is the token that carries an " + f"INTERACTIVE control's boundary; `--glass-hairline`/`-soft` are the " + f"decorative panel edges WCAG 1.4.11 exempts, and swapping one for the other " + f"is how this regresses." + ) + + motion = { + s: h["running_animations_under_reduced_motion"] + for s, h in sorted(renders.items()) + if h["running_animations_under_reduced_motion"] + } + assert not motion, ( + f"OSV1-010 (Core 7) REGRESSION, reduced motion: {motion} animation(s) run " + f"under the preference. This floor passed before the other three were fixed " + f"and must not be traded for them -- see OSV1-011 for the kernel-rule half." + ) + + # The exemption, asserted as a NUMBER and by WHERE it fires. L1 is the only + # level that draws a donut; an exemption appearing on L0 or L2, or a second + # one appearing on L1, means the allowance -- not the fix -- is what is + # keeping the non-text arm green. + exempt = {s: h["non_text_exempt_below_floor"] for s, h in sorted(renders.items())} + stray = {s: n for s, n in exempt.items() if n and "/L1/" not in s} + assert not stray, ( + f"OSV1-010 (Core 7): the non-text exemption fired outside L1: {stray}. It " + f"covers the status-mix donut's backing ring and nothing else -- L0 and L2 " + f"draw no donut, so an entry there is the allowance spreading." + ) + grown = {s: n for s, n in exempt.items() if "/L1/" in s and n != 1} + assert not grown, ( + f"OSV1-010 (Core 7): L1's non-text exemption is pinned at exactly 1 entry " + f"(`.donut-track`, 1.32:1 dark / 1.15:1 light -- the one BLOCKED residual " + f"this row records); observed {grown}. Growth here widens the floor instead " + f"of meeting 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." ) + assert contains(WEBTHEME, "--control-edge:"), ( + "OSV1-010 (Core 7): `--control-edge` is gone from the token block. It is the " + "token that took every interactive control's border from 1.24-1.60:1 to over " + "3:1 without dragging the decorative panel hairlines along with it." + ) def test_row_osv1_011() -> None: @@ -876,31 +1005,66 @@ def test_row_osv1_011() -> None: def test_row_osv1_012() -> None: - """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. + """Core 8 CONFORMS: every widget that can render empty keeps its slot AND + says so, and the kit's two `calm.keeps_slot` halves are no longer deferred. + + RETARGETED 2026-09-05 (work_item_pipeline-aad) from the VIOLATION pin. The + pin asserted the ABSENCE of an empty branch in two renderers; this asserts + the presence of the sentence in all three, plus the sentence's own register + (no numeral, no exclamation) -- because "grew an empty branch" and "says + something calm in it" are different facts and only the second is the + clause. + + Asserted on the RENDERERS rather than on a rendered page, because that is + what an in-process probe can see. The rendered proof is the Tier-A kit's + own `calm.keeps_slot` pair, whose deferral this probe now forbids. """ - for func in ("render_attention_queue", "render_agents_panel"): + sentences = { + "render_attention_queue": "No item needs you right now.", + "render_agents_panel": "No agent has held an item in this project yet.", + "render_status_breakdown": "No items to break down yet.", + } + for func, sentence in sentences.items(): 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)." + assert "_empty_note(" in body, ( + f"OSV1-012 (Core 8) REGRESSION: `{func}` no longer routes its empty case " + f"through `_empty_note`. A widget with nothing to show keeps its slot AND " + f"says so in a sentence -- an empty container is the defect this row closed." ) + assert sentence in body, ( + f"OSV1-012 (Core 8) REGRESSION: `{func}`'s empty sentence is gone or " + f"changed. Expected {sentence!r}. If the wording moved deliberately, " + f"re-derive this row from a re-run of the Tier-A kit's `calm.keeps_slot` " + f"halves rather than editing this string to match." + ) + assert "!" not in sentence and not any(ch.isdigit() for ch in sentence), ( + f"OSV1-012 (Core 8) REGRESSION: `{func}`'s empty sentence acquired a " + f"numeral or an exclamation. The calm state is STATED, never celebrated -- " + f"a triumphant zero is what this clause forbids." + ) + assert contains(WIDGETS, 'class="empty-note"'), ( + "OSV1-012 (Core 8) REGRESSION: `_empty_note` no longer emits `.empty-note`. " + "That class is where the slot's `min-height` lives (webtheme.py) -- without " + "it an empty widget collapses instead of keeping its slot." + ) + theme = read(WEBTHEME) + assert ".empty-note{" in theme, ( + "OSV1-012 (Core 8) REGRESSION: the `.empty-note` rule is gone from the token " + "module. Keeping the slot is the half of this clause that is about geometry, " + "not words." + ) + empty_rule = theme.split(".empty-note{", 1)[1].split("}", 1)[0] + assert "min-height" in empty_rule, ( + f"OSV1-012 (Core 8) REGRESSION: `.empty-note` no longer sets a `min-height`, " + f"so an empty widget collapses to nothing instead of keeping its slot. The " + f"rule now reads: {empty_rule.strip()!r}" + ) 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 not _kit_deferred_rows(kit, test_name), ( + f"OSV1-012 (Core 8) REGRESSION: the kit's `{test_name}` is deferred behind " + f"an xfail again. This row reads CONFORMS off that half PASSING; a deferred " + f"good half means it should not." ) assert contains(WIDGETS, '"All clear"'), ( "OSV1-012 (Core 8): the calm headline 'All clear' is gone. Calm must stay " @@ -1424,6 +1588,14 @@ def test_row_osv1_022() -> None: 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. + + RE-DERIVED 2026-09-05 alongside OSV1-008 (work_item_pipeline-v3m), exactly + as this probe's own message instructed. The good half's disclosure reading + flipped false -> true when Core 6 was fixed; what this row is about is that + the bad halves still CATCH what they exist to catch, so the good-half pin + is now stated as a DIFFERENCE from the bad half rather than as a fixed + value -- a bad half that reads the same as the good one demonstrates + nothing whichever way both read. """ assert _exists(TIER_B_KIT), ( f"OSV1-022 (Conformance 3): {TIER_B_KIT} is gone -- the fixture this row " @@ -1443,11 +1615,16 @@ def test_row_osv1_022() -> None: "unproven." ) good = tier_b("swap.survives", "calm/L0/dark") - assert good["scroll_preserved"] and not good["open_details_preserved"], ( + assert good["scroll_preserved"] and 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 good["open_details_preserved"] != naive["open_details_preserved"], ( + "OSV1-022 (Conformance 3): the good half and the literal bad half now " + "report the SAME disclosure outcome. A bad half that cannot be told apart " + "from the good one demonstrates nothing, whichever way both happen to read." + ) assert contains(WEBTHEME, "document.body.innerHTML = doc.body.innerHTML"), ( "OSV1-022 (Conformance 3): the whole-body innerHTML swap is gone. That IS the " "mechanism Conformance 3's bad half describes -- if the swap changed shape, " @@ -1579,24 +1756,39 @@ def test_row_osv1_025() -> None: def test_row_osv1_026() -> None: - """Conformance 7 pin: the two-render fixture exists and discriminates, and - its GOOD halves are still deferred against OSV1-012.""" + """Conformance 7 CONFORMS: the two-render fixture discriminates AND both of + its GOOD halves pass, on L0 and on L1. + + RETARGETED 2026-09-05 (work_item_pipeline-aad) from the GAP pin, which + asserted the good halves were DEFERRED. A Conformance row is about a + discriminating fixture, and a fixture whose good half has never been seen + to pass is half a fixture -- so this now asserts the deferral is GONE and + the row it was deferred against is green. Both bad halves are still + asserted: flipping this row must not spend them. + """ 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." ) + for good_half in ("test_calm_keeps_slot", "test_calm_keeps_slot_l1"): + assert good_half in _kit_defs(kit), ( + f"OSV1-026 (Conformance 7) REGRESSION: the kit's `{good_half}` is gone. " + f"Conformance 7 names L0 AND L1, and this row reads CONFORMS off BOTH " + f"halves passing." + ) + assert not _kit_deferred_rows(kit, good_half), ( + f"OSV1-026 (Conformance 7) REGRESSION: `{good_half}` is deferred behind an " + f"xfail again. This row is green because the good halves PASS -- a " + f"deferred half means it should not be." + ) 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 row("OSV1-012")["disposition"] not in PINNING_DISPOSITIONS, ( + "OSV1-026 (Conformance 7) REGRESSION: OSV1-012 went red again, so Conformance " + "7's good halves cannot be passing. These two rows move together -- re-derive " + "both from a re-run of the pair." ) assert contains( OPERATOR_CONTRACT_PATH, "a render that drops empty widgets, or renders a hero-scale `0`" @@ -1913,11 +2105,20 @@ def test_row_osv1_030() -> None: def test_row_osv1_031() -> None: - """Freeze 5 pin: at least one Core-carrying row is still red. - - The only probe in this family that reads the LEDGER rather than the repo. - It goes red when the last Core row turns green -- which is the signal to - flip this row, not a failure. + """Freeze 5 CONFORMS: NO Core-carrying row is red. + + RETARGETED 2026-09-05 at the wave-4 union, in the same change that flipped + the row (VIOLATION-MOVEMENT: the old pin -- "at least one Core row is still + red" -- went red because the last one turned green). The direction is now + REGRESSION: this fails the moment any Core-carrying row goes back to GAP or + VIOLATION, which is the only way Freeze 5 can stop being met. + + Still the only probe in this family that reads the LEDGER rather than the + repo. It cannot, and does not, re-verify the 17 underlying measurements -- + each Core row owns its own probe and its own evidence, and this one asserts + the AGGREGATE those probes add up to. That is the honest limit of a tally + gate: it counts dispositions, so a dishonest disposition would pass here + and fail in the row that carries it. """ core_rows = [ r @@ -1930,21 +2131,15 @@ def test_row_osv1_031() -> None: f"re-derive." ) red = sorted(r["id"] for r in core_rows if r["disposition"] in {"GAP", "VIOLATION"}) - assert red, ( - "OSV1-031 (Freeze 5) PIN BROKE THE RIGHT WAY: every Core-carrying row now reads " - "CONFORMS or NOT-ASSERTABLE. Confirm each formerly-red row was RE-DERIVED from " - "real measurement (not flipped because a kit file appeared), then flip OSV1-031 " - "to CONFORMS and retarget this probe to assert no Core row is red " - "(work_item_pipeline-umm)." - ) - assert len(red) == 4, ( - f"OSV1-031 (Freeze 5): pinned 4 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. (10 at seed; OSV1-009 went " - f"green 2026-09-04, work_item_pipeline-sxh; OSV1-015 and -016 went green " - f"2026-09-04, work_item_pipeline-8vv and -dg3; OSV1-001 and OSV1-004 went green " - f"2026-09-05, work_item_pipeline-ujy and the Tier-A kit; OSV1-005 went green " - f"2026-09-05, work_item_pipeline-np3.)" + assert len(red) == 0, ( + f"OSV1-031 (Freeze 5) REGRESSION: {len(red)} Core-carrying row(s) went back to " + f"GAP or VIOLATION: {red}. Freeze 5 asks that EVERY Core clause read CONFORMS " + f"or be NOT-ASSERTABLE with its cadence named, so this gate no longer holds -- " + f"re-open OSV1-031 (disposition GAP) in the SAME change that reddens the row, " + f"rather than leaving a green Freeze row above a red Core one. (Ten Core rows " + f"were red at seed; the last four went green 2026-09-05 at the wave-4 union -- " + f"OSV1-003 work_item_pipeline-a1o, OSV1-012 work_item_pipeline-aad, OSV1-008 " + f"work_item_pipeline-v3m, OSV1-010 work_item_pipeline-96f.)" ) assert {r["id"] for r in core_rows if r["disposition"] == "NOT-ASSERTABLE"} == { "OSV1-018", @@ -1952,7 +2147,9 @@ def test_row_osv1_031() -> None: }, ( "OSV1-031 (Freeze 5): the NOT-ASSERTABLE Core rows changed. Freeze 5's second " "limb admits exactly the clauses the CONTRACT declares unassertable, each with " - "its cadence named -- a new one is a downgrade, not a pass." + "its cadence named -- a new one is a downgrade, not a pass. This matters MORE " + "now that the row reads CONFORMS: moving a Core row to NOT-ASSERTABLE would " + "keep this gate green while removing the assertion under it." ) diff --git a/ledger/rows.yaml b/ledger/rows.yaml index 67793ea..cf48d95 100644 --- a/ledger/rows.yaml +++ b/ledger/rows.yaml @@ -1037,21 +1037,99 @@ the calm-pixel sweep (OSV1-003). - id: OSV1-003 - title: a calm L1 paints 97 --blocked pixels with nothing blocked + title: a calm L0 and L1 both paint zero --alarm and zero --blocked pixels, in both themes 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: VIOLATION - work: work_item_pipeline-qgo + disposition: CONFORMS + work: work_item_pipeline-a1o assertion: kind: probe ref: test_row_osv1_003 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 2026-09-05, VIOLATION -> CONFORMS (VIOLATION-MOVEMENT: the pin went + red because the tree moved TOWARD the clause), work_item_pipeline-a1o. This + is no longer a pinning row: the probe asserts the INVARIANT now, and the + flip direction is REGRESSION. + + THE MEASUREMENT THAT FLIPPED IT, same pinned chromium 148.0.7778.0 / + playwright 1.60.0, same isolated calm fixture, same full-page sweep against + the LIVE token values, re-recorded on this tree and read back by this row's + probe from `tests/conformance/operator_surface/browser/LAST_RUN.json`: + L0 dark 0 --alarm, 0 --blocked across 2 813 440 px (was 0 / 0) + L0 light 0 --alarm, 0 --blocked across 2 813 440 px (was 0 / 0) + L1 dark 0 --alarm, 0 --blocked across 3 792 640 px (was 0 / 97) + L1 light 0 --alarm, 0 --blocked across 3 792 640 px (was 0 / 97) + The Tier-B `xfail(strict=True)` this row owned on `calm.zero_alarm_pixels` + at L1 XPASSed on that run and was deleted in the same change, per Freeze 3's + own instruction. + + WHERE THE 97 PIXELS ACTUALLY WERE, located pixel-by-pixel before anything + was changed rather than inferred from the stylesheet -- the sweep's own + matching coordinates were clustered and reconciled against every element's + computed style and page-space rect on a calm L1 in dark: + 81 px `span.sw.mix-blocked`, the status-donut legend's swatch for the + ZERO-count "Blocked" bucket, a 10x10 3px-radius chip at (275, 621) + 16 px `span.dot` inside the Blocked status TAB, a 6px circle at (316, 2167) + 97 px total, and the two clusters account for it exactly. + + THE THIRD PAINTER THIS ROW NAMED PAINTED NOTHING, recorded because a row + that keeps repeating an unverified attribution is the drift `ledger/` exists + to catch. The prior note (and the deleted xfail reason) named + `button.btn.danger`'s border as the third source. It is not: all three + `.btn.danger` controls live inside a CLOSED `
`, + where chromium lays them out (`getBoundingClientRect` reports 216x44 at + y=3356 and y=3368) but never paints them -- the full-page screenshot is + 2 963 px tall and they sit outside it entirely. Their contribution to the 97 + was ZERO. The attribution was plausible from the stylesheet and wrong in the + render, which is the whole reason this check is a pixel sweep. + + THEY WERE FIXED ANYWAY, and the reason is recorded rather than assumed: a + conformance that holds only while a drawer happens to be shut is an accident, + not a property. `button.danger`'s rest border moved from `--blocked` to the + neutral `--ink-quiet`, with the reserved hue returning on + hover/focus-visible/active. Verified by re-running the sweep with every + `
` on the page forced open: 0 --alarm and 0 --blocked in BOTH + themes, drawer open or closed. + + WHAT THE FIX IS, in the clause's own terms. Core 2 reserves `--alarm` and + `--blocked` for status that is actually present, and each of the two real + painters was borrowing the hue with no status behind it -- a "Blocked 0" + legend swatch and a lamp on a tab with nothing to point at. Both now keep + their SLOT and drop their HUE at zero (`is-zero` -> `--ink-quiet`), which is + the existing "alarm lamp present and switched off" convention this surface + already uses at `.kpi-card.is-blocked.is-zero` and `.sw.fill-empty`. Core 8 + is unaffected: the legend row still renders its name, its `0` and its + `0.0%`, and the tab still renders the word "Blocked" and its count. Core 3 + is unaffected for the same reason -- neither was ever colour-only. Core 4 is + unaffected: three CSS rules and two class names, zero literals. + + THE SWEEP STILL DISCRIMINATES -- the same three arms, re-run on the fixed + tree, every number byte-identical to the pre-fix recording: + injected `var(--alarm)` chip on a calm L0 10 531 --alarm px + genuinely-alarming fixture (no injection) 264 --blocked px + reinstated retired `#D9A253` palette region 16 681 retired_amber px + A fix that had merely blinded the sweep would have moved all three. The + alarming L1 render also still paints the reserved hue -- `alarm.reserved_hue` + on `alarm/L1/dark` reads 2 669 --alarm and 734 --blocked, unchanged, because + the legend swatch keeps its hue the moment the count is non-zero. That is + the discrimination the fix had to preserve and did. + + NO OTHER ROW'S RECORDED NUMBERS MOVED, checked rather than hoped: on the + re-recording, `perception.floors` reads L1 `non_text_below_floor` 23 of 73 + and `text_below_floor` 3 dark / 4 light, and L0 16 of 79 / 26 of 34 controls + -- every figure OSV1-010 pins, unchanged, including across the danger + border's move to `--ink-quiet` (which clears 3:1 where `--rule-hi` would not + have). The only fields that moved at all are the LIGHT-theme `--watch` + counts, which Core 2 does not score and no row reads, and which this row + already records as jittering by up to ~2% run to run. + + HISTORY BELOW THIS LINE is the record as it stood while the row was red. It + is left rather than deleted: the measurement that made it red is what makes + the flip meaningful. RE-DERIVED 2026-09-05, GAP -> VIOLATION. The kit the previous disposition was waiting on now exists and has run @@ -1136,6 +1214,37 @@ notes) -- and reads 1 161 again after the repair, byte-identical to the pre-lane value. + RE-RECORDED 2026-09-05 AT THE WAVE-4 UNION, once, on the merged tree, same + pinned chromium 148.0.7778.0 / playwright 1.60.0 -- three lanes had each + re-recorded `LAST_RUN.json` against a tree carrying only their own fix, so + all three recordings were discarded and this one was made from scratch. + EVERY VERDICT FIELD THIS ROW ASSERTS IS UNCHANGED and still measured, not + inherited: + L0 dark 0 --alarm, 0 --blocked across 3 002 880 px + L0 light 0 --alarm, 0 --blocked across 3 002 880 px + L1 dark 0 --alarm, 0 --blocked across 3 877 120 px + L1 light 0 --alarm, 0 --blocked across 3 877 120 px + THE SWEPT AREAS MOVED, and the paragraphs above are superseded on that one + number only: L0 2 813 440 -> 3 002 880 px, L1 3 792 640 -> 3 877 120 px. + Both pages simply got TALLER on the union -- the empty-states lane added a + calm sentence to every empty widget (Core 8) and the rendered-floors lane + raised every control to a 44px target box (Core 7). A swept area is a + denominator, not a verdict; the numerators are still 0 and 0. + THE THREE DISCRIMINATION ARMS STILL FIRE, byte-identical to every previous + recording -- 10 531 --alarm px for the injected chip, 264 --blocked for the + genuinely-alarming fixture, 16 681 retired_amber for the reinstated retired + palette. A larger canvas with a still-quiet result means something only if + the sweep can still find hue when hue is there, and it can. + ONE EARLIER CLAIM IN THIS ROW IS NOW STALE, corrected here rather than + edited out of the paragraph that made it: the wave-3 note above says "NO + OTHER ROW'S RECORDED NUMBERS MOVED ... `perception.floors` reads L1 + `non_text_below_floor` 23 of 73 ... and L0 16 of 79 / 26 of 34 controls -- + every figure OSV1-010 pins, unchanged". That was true on the wave-3 tree. + On this union those figures are ALL ZERO (0 of 73, 0 of 79, 0 of 34), + because the rendered-floors lane closed OSV1-010 in this wave. The + statement was accurate when it was written and is superseded by + measurement, which is the only way a recorded number changes here. + HONEST LIMIT ON REPRODUCIBILITY, recorded rather than smoothed: the run was repeated on this tree, and 290 of the 294 recorded fields are byte-identical between runs -- including every field any row asserts. The @@ -1360,14 +1469,58 @@ webapp.py:1127 flex:{n} 1 0 state-bar segment ratio webapp.py:1823 width:{today_w}px throughput bar, today webapp.py:1826 width:{prior_w}px throughput bar, prior 6 d - webtheme.py:4197 {style} axis ruler numeral offset - webtheme.py:4216 left:{_grad_x(f):.1f}px graduation tick offset - webtheme.py:4223 width:{px}px age bar length - widgets.py:837 width:{pct}% status-mix segment (hatched) - widgets.py:839 width:{pct}% status-mix segment + webtheme.py:4379 {style} axis ruler numeral offset + webtheme.py:4398 left:{_grad_x(f):.1f}px graduation tick offset + webtheme.py:4405 width:{px}px age bar length + widgets.py:866 width:{pct}% status-mix segment (hatched) + widgets.py:868 width:{pct}% status-mix segment Every one is "a bar width, a chart offset" -- the clause's own gloss of what this exemption is FOR. + LINE NUMBERS ONLY, 2026-09-05 (work_item_pipeline-aad): the two widgets.py + entries moved 837/839 -> 866/868. The SITES did not change -- same two + declarations, same file, same purpose, register still 8. They shifted + because that lane added `widgets._empty_note` above them (Core 8). Recorded + rather than silently corrected, because this register is pinned by + `file:line` and a line-shift is indistinguishable from a new site unless + someone says which it was. + THE THREE WEBTHEME PINS MOVED +22 ON 2026-09-05 (work_item_pipeline-a1o, + from 4197/4216/4223), and nothing else about this row did. OSV1-003's fix + inserted three commented CSS rules higher in the same file; the SITES, the + expressions and the count are identical, and the register neither grew nor + shrank. Recorded rather than quietly re-numbered, because "the register did + not change" and "the register changed by three line numbers" look the same + in a diff and mean different things. + + RE-ANCHORED 2026-09-05 (work_item_pipeline-96f): the three webtheme.py + sites moved +82 lines each (4197/4216/4223 -> 4279/4298/4305) when the + Core 7 rendered-floor fix added token-block comments above them. The + register did not GROW and no site changed -- a line-keyed register moves + whenever anything above it does, which is the cost of keying it that way. + + RE-MEASURED 2026-09-05 AT THE WAVE-4 UNION (orchestrator), and this is the + pin that stands: the three webtheme.py sites read 4379 / 4398 / 4405, +182 + lines from main @065da04's 4197 / 4216 / 4223. Neither lane's own number + survives contact with the union -- calm-pixels measured 4219/4238/4245 on a + tree with only its own +22, swap-survives measured 4275/4294/4301 with only + its +78, rendered-floors measured 4279/4298/4305 with only its +82 -- so the + register was re-derived by RUNNING the census on the merged tree rather than + by adopting any of the three. THE ARITHMETIC RECONCILES: 22 + 78 + 82 = 182, + which is exactly the observed shift, so all three insertions are accounted + for and none of the three sites is new. WHICH ENTRIES MOVED AND WHY: + webtheme.py 4197 -> 4379 (+182) axis ruler numeral offset + webtheme.py 4216 -> 4398 (+182) graduation tick offset + webtheme.py 4223 -> 4405 (+182) age bar length + three lanes each inserted CSS ABOVE all three: calm-pixels' three + commented `--blocked`/`--watch` rules (+22), swap-survives' persistent + live-region rule and its comment block (+78), rendered-floors' light + token blocks, glass fills and control-sizing rules (+82). + webapp.py 1127 / 1823 / 1826 UNMOVED -- no lane edited above them. + widgets.py 866 / 868 UNMOVED this wave; they had already + moved 837/839 -> 866/868 in the empty-states merge recorded above. + The register neither GREW nor SHRANK: 8 sites before, 8 after, the same + eight expressions. The census was re-run, not reasoned about. + THE ROOT'S TRIAGE RULING WAS APPLIED, and it is what removed 15 sites. Two kinds were on the register that should not have been: (a) A COMPUTED COLOUR. Six sites interpolated a colour rather than a @@ -1438,7 +1591,7 @@ kit would close the residue. - id: OSV1-008 - title: one of Core 6's four named survivals holds; the other three do not + title: all four of Core 6's named survivals hold, on L0 and on L1 contract: file: contracts/operator-surface.v1.md clause: Core 6 @@ -1446,78 +1599,95 @@ Scroll position, every open `
`, and the pause control's state survive that swap, and an assistive-technology announcement pending at the moment of the swap is not silently destroyed by it. - disposition: VIOLATION - work: work_item_pipeline-qgo + disposition: CONFORMS + work: work_item_pipeline-v3m assertion: kind: probe ref: test_row_osv1_008 notes: > - PINNING ROW -- the probe asserts the CURRENT, KNOWN-WRONG shape on purpose; - a passing probe here is NOT conformance. Flip direction VIOLATION-MOVEMENT. + RE-DERIVED 2026-09-05, VIOLATION -> CONFORMS (work_item_pipeline-v3m). The + pin broke the right way: the probe froze the wrong shape (one survival of + four), the fix landed, and the frozen assertions went red. Retargeted in + the same change to assert the CONFORMING shape, still per survival. - RE-DERIVED 2026-09-05, GAP -> VIOLATION. At seed this row recorded source - shape as HYPOTHESES for a browser run to confirm or refute. The run has - happened. - - MEASURED IN A REAL BROWSER 2026-09-05 -- chromium 148.0.7778.0 via the + RE-MEASURED IN A REAL BROWSER 2026-09-05 -- chromium 148.0.7778.0 via the pinned playwright 1.60.0, against a live app on an ephemeral loopback port - over the isolated dolt fixture. Every number below is re-read by this - row's probe from `tests/conformance/operator_surface/browser/LAST_RUN.json`, - the kit's committed run summary; the probe never trusts the browser tier's - own pass/fail (Freeze 3 / Phase-1 ruling 6). + over the isolated dolt fixture, RE-RECORDED on this tree and re-run twice + with every asserted field byte-identical. Every number is re-read by this + row's probe from `tests/conformance/operator_surface/browser/LAST_RUN.json`; + the probe never trusts the browser tier's own pass/fail (Freeze 3 / + Phase-1 ruling 6). THE RESULT, L0 and L1, scrolled 400px, one `
` opened, the poll - paused, the surface's own poller forced through one tick: - scroll offset PRESERVED (`restoreState`'s `window.scrollTo`) - open `
` LOST hypothesis REFUTED in practice - pause control LOST hypothesis CONFIRMED - announcement NOTHING TO LOSE hypothesis CONFIRMED, and worse - One of four holds. - - THE DISCLOSURE HALF IS WORSE THAN THE SEED THOUGHT. The seed recorded that - `restoreState` restores open `details[id]`, and read that as an - implementation being present. Measured in the render, the mechanism has - ZERO TARGETS: no `
` on L0, L1 or L2 carries an id at all (help - popover, activity feed, actions drawer -- all id-less; `details_with_id` - is 0 in the run summary). An open disclosure therefore closes on every - 20-second poll. - - THE PAUSE CONTROL, exactly as the seed predicted: `window.__wtRefreshPaused` - survives (it lives on `window`, which a body swap never replaces) but the - button is re-rendered by the server at `aria-pressed="false"` - (webapp.py:3549) and nothing re-applies the flag. A paused page shows - itself as running. The run records both halves separately - (`pause_flag_preserved: true`, `pause_control_preserved: false`) because - an operator reads the control, not the flag. - - THE ANNOUNCEMENT HALF, RE-DERIVED 2026-09-05 on the merged tree -- it now - fails one step LATER on L0, and the same step earlier on L1: - L0 `live_regions_before` 0 -> 1. The rebuilt verdict hero renders a - `role="status"` region (widgets.py:1379, OSV1-001's wave-1 work), - so there IS an announcement now -- and the body-swap destroys it: - `marked_live_regions_after` is 0 of 1. The tagged node does not - survive; the page comes back with a fresh, empty region. - L1 `live_regions_before` is still 0 -- no live region renders at all, - so there remains nothing for the swap to destroy and nothing for the - operator to hear. - `aria-live` still has zero occurrences in `src/`; the L0 region is a - `role="status"`, and `role="alert"` (webapp.py:1211, :4213) still does not - render on a calm page. The row's probe pins both levels separately, and - pins the SURVIVAL at 0, so a fix that carries the region across the swap - goes red here. - - WHAT THIS ROW SAID BEFORE, left standing because it was true when it was - measured: "THE ANNOUNCEMENT HALF HAS NOTHING TO PRESERVE: live_regions_before - is 0 ... the clause fails one step earlier than the swap." That reading was - taken before the hero rebuild landed. + paused, an announcement pending, the surface's own poller forced through + one tick -- BEFORE -> AFTER: + scroll offset PRESERVED -> PRESERVED (unchanged; it always held) + open `
` LOST -> PRESERVED + pause control LOST -> PRESERVED + announcement DESTROYED -> PRESERVED (L0 0-of-1 survivors -> + 1-of-2; L1 had no region at all -> 1-of-1) + Four of four, both levels. + + THE DISCLOSURE HALF. `restoreState` re-opened only `details[id]`, and NO + `
` on this surface carries an id (help popover, activity feed, + actions drawer -- all id-less; `details_with_id` is still 0), so the + mechanism had ZERO TARGETS and an open disclosure closed on every + 20-second poll. It now records open disclosures by ORDINAL + class + signature as well as by id -- the same key this kit's own snapshot uses, + chosen for the same measured reason. The id path is kept because it + survives a RE-ORDER, which the ordinal key cannot; `details_with_id` stays + pinned at 0 so the day the markup gains ids, this row is re-derived rather + than silently switching mechanisms. + + THE PAUSE CONTROL. `window.__wtRefreshPaused` always survived (it lives on + `window`, which a body swap never replaces) but the button came back + server-rendered at `aria-pressed="false"` every tick and nothing + re-applied the flag -- a paused page showed itself as running. + `restorePauseControl` now re-synchronises the control to the flag after + every swap, delegating to the page's own `wtToggleRefresh` where it exists + so the button's label, icon and title vocabulary stays declared in exactly + one place. The run records both halves separately + (`pause_flag_preserved` and `pause_control_preserved`, both true now) + because an operator reads the control, not the flag. + + THE ANNOUNCEMENT HALF, and why the fix is a NODE and not a message. A + whole-body `innerHTML` replacement destroys every live region inside it + and the server renders fresh, empty ones -- measured on the verdict hero's + own `role="status"`: 0 of the 1 tagged node survived. L1 failed one step + earlier still: it rendered NO live region at all. The fix adds exactly one + persistent region per polling level, `#wt-live` (webapp.py's + `_live_region_html`, id shared with the poller via `T.LIVE_REGION_ID`), + which `auto_refresh_js` DETACHES before the swap and re-attaches in place + of the server's fresh copy afterwards. Its text is updated only when the + fresh render actually says something different, so a screen reader hears a + real state change rather than the same sentence every 20 seconds. The hero's + own `role="status"` is still destroyed and rebuilt -- that is why + `live_regions_before` is 2 on L0 and `marked_live_regions_after` is 1, not + 2, and why the row pins the count of SURVIVORS rather than of regions. + `aria-live` went from zero occurrences in `src/` to exactly one declaration + site, pinned. + + NODE IDENTITY IS THE READING, NOT TEXT. The kit records both + (`marked_live_regions_after` and `announcement_preserved`) because text + alone does not discriminate: the naive-replacement bad half destroys the + region and the server re-renders the SAME sentence into a brand-new one, + so a check comparing only text would call that survival. The bad half is + RUN and asserted to lose node identity (0 survivors) while its text comes + back -- which is exactly the trap named. THE KIT DOES NOT MEASURE THIS VACUOUSLY. A forced tick whose fetch quietly fails leaves the page untouched, and an untouched page "preserves" everything; the kit plants a sentinel node before the swap and refuses to - measure until it is gone. That guard caught exactly this: chromium marks a - page that is not the front page of its window as hidden, the poller's - first guard is `document.hidden`, and the first version of these numbers - was taken from a page nothing had swapped. + measure until it is gone. That guard caught exactly this once already: + chromium marks a page that is not the front page of its window as hidden, + the poller's first guard is `document.hidden`, and the first version of + these numbers was taken from a page nothing had swapped. + + WHAT THIS ROW SAID BEFORE, left standing because it was true when it was + measured: "One of four holds ... THE DISCLOSURE HALF IS WORSE THAN THE SEED + THOUGHT ... the mechanism has ZERO TARGETS ... THE ANNOUNCEMENT HALF ... + `marked_live_regions_after` is 0 of 1 ... L1 `live_regions_before` is still + 0." Those readings are superseded by the run above, not deleted. - id: OSV1-009 title: every declared token pair clears its floor -- 4.5:1 text, 3:1 non-text, both themes contract: @@ -1595,7 +1765,7 @@ necessary, never sufficient. - id: OSV1-010 - title: rendered contrast, target boxes and a motion trace -- measured, three of four floors fail + title: rendered contrast, target boxes and a motion trace -- measured, all four floors met contract: file: contracts/operator-surface.v1.md clause: Core 7 @@ -1603,14 +1773,152 @@ plus a browser run emitting computed contrast ratios, target bounding boxes, and a motion-preference trace at 430, 900, and 1280px in both themes; the orchestrator re-checks those numbers against the floors. - disposition: VIOLATION - work: work_item_pipeline-qgo + disposition: CONFORMS assertion: kind: probe ref: test_row_osv1_010 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 2026-09-05, VIOLATION -> CONFORMS (work_item_pipeline-96f). Was a + PINNING row; the probe now asserts the floors are MET, in the regression + direction, and the four Tier-B `xfail(strict)` markers that named this row + are gone from the kit. Every number below is re-read by this row's probe + from `tests/conformance/operator_surface/browser/LAST_RUN.json`, the kit's + committed run summary -- never from the browser tier's own pass/fail + (Freeze 3 / Phase-1 ruling 6), and never transcribed. + + MEASURED IN A REAL BROWSER 2026-09-05 -- chromium 148.0.7778.0 via the + pinned playwright 1.60.0, against a live app on an ephemeral loopback port + over the isolated dolt fixture; 18 renders (L0/L1/L2 x 430/900/1280 x + dark/light), recorded TWICE with byte-identical values on every field this + row asserts. + + ALL FOUR FLOORS, BEFORE -> AFTER: + + TEXT CONTRAST (floor 4.5:1) -- 7 failing nodes -> 0, all 18 renders. + L0 0 of 136 -> 0 of 136 (was already clean; unmoved) + L1 3 of 183 dark, 4 of 183 light -> 0 / 0 + L2 1 of 58 dark, 2 of 58 light -> 0 / 0 + Worst pair moved 3.13:1 -> 4.75:1. Fixed at the TOKEN, in all four + declared blocks: `--ink-quiet` #7c8798 -> #a1a8b5 (dark) and #596473 -> + #4e5764 (light), `--brand-cyan-ink` #0b6b80 -> #0a5e71 (light only; + dark's #22d3ee already read 6.29:1 there). No call site moved off a + token and no reading copy was relabelled decorative. + + This is the HONEST LIMIT OSV1-009 recorded, closed by measurement. Those + same tokens cleared 4.5:1 as flat swatch pairs on the three declared + grounds; in the render they sat on glass-over-glass chip surfaces + (#383a40 dark, #ccd1db light) that flat pair math cannot model -- 3.13:1 + on L1's `.status-chip.st-resolved`, 3.92:1 in light, 3.99:1 on + `.st-held`, 3.77/4.26:1 on L2's drawer count, 4.35:1 on + `.drawer-section label.eyebrow`. Each is now >= 4.75:1 on its own + rendered background. The ink ramp did NOT collapse to buy it: + `--ink-tertiary` reads 7.33:1 dark / 5.75:1 light on the same chip + surface, so quiet stays visibly the quieter step, which is what + OSV1-009's own distinctness assert forbids trading away. + + TARGET SIZE (floor 44px) -- 26 of 34 controls on L0 -> 0 of 34, and 0 of + 41 on L1 / 0 of 20 on L2, at every one of the three widths. + L0 26 of 34 at 900/1280, 16 of 34 at 430 -> 0 + L1 22 of 41 (21 of 40 at 430) -> 0 + L2 11 of 20 -> 0 + The named offenders and what moved: the auto-refresh pause control + (26x26 -> --u), every nav icon-button, the theme toggle and the help + disclosure (34x34 -> --u), the window-range and status tabs (28px tall + -> min-height/min-width --u), the L0 activity-feed rows (37px -> --u), + the L1 items filter `` (181x19, the one field the + base `input[type=text]` rule never reached), the breadcrumb links + (19.5px), the brand wordmark (21px) and the identity links Setup/Logout + (11.5px). For the link-shaped ones the HIT AREA grew and the type did + not: `min-height`/`min-width: var(--u)` on an inline-flex box inside a + row that was already centred, so the 62px header keeps its height. + Inline links stay excluded per WCAG 2.5.8's own inline exception and the + excluded population is still emitted beside the scored one. + + NON-TEXT CONTRAST (floor 3:1) -- 16 of 79 on L0 -> 0, in BOTH themes. + L0 16 of 79 dark, 16 of 79 light -> 0 / 0 + L1 23 of 73 dark, 23 of 73 light -> 0 / 0 (one exemption, below) + L2 11 of 33 dark, 11 of 33 light -> 0 / 0 + Fixed with ONE new token rather than by bumping a hairline: + `--control-edge` (rgba(255,255,255,.40) dark / rgba(11,18,32,.52) + light), applied to the border of an INTERACTIVE control only -- icon + buttons, the pause toggle, KPI cards, fleet/agents/agent/item rows, the + dormant disclosure, active window/status tabs, form fields, the drawer's + action buttons. `--glass-hairline`/`-soft` keep painting every + decorative panel edge unchanged, which is why a rim did not become a + stroke everywhere. Measured worst case after: 3.35:1 dark (#7c7d81 on + #2c2d34), 3.23:1 light (#707682 on #d5d9e3); the icon-button border this + row used to name at 1.27:1 now reads 3.53:1. Neutral by construction (a + white/ink alpha, no hue), so Core 2's status-hue firewall is untouched + and the calm sweep is unmoved. + + REDUCED MOTION -- still PASSES at every level, width and theme: zero + animations run under an emulated `prefers-reduced-motion: reduce` once + the page is quiescent. Unmoved by this change and still asserted here + as well as by OSV1-011. + + HORIZONTAL OVERFLOW -- still PASSES, and still recorded as WEAK + EVIDENCE: `scrollWidth == clientWidth` at all nine level/width pairs, + but `html`/`body` carry `overflow-x: clip` so that metric cannot move. + `elements_beyond_viewport` (border boxes past `clientWidth`, which + clipping cannot hide) is 0 everywhere INCLUDING 430px after the target- + size work -- the check that would have caught a header made too wide by + six 44px buttons. + + THE ONE BLOCKED RESIDUAL, enumerated and machine-checked, never prose: + L1's status-mix donut BACKING RING (`.donut-track`, and its sibling + `.donut-hatch-gap`) measures 1.32:1 dark / 1.15:1 light against its card + and is EXEMPT, via `_probe.NON_TEXT_EXEMPT_CLASSES` -- a frozenset of two + class names, subtracted by `below_non_text_floor`, returned by + `exempt_below_non_text_floor`, counted in every render's headline as + `non_text_exempt_below_floor`, and pinned by its own kit test + (`test_the_non_text_exemption_stays_narrow`: exactly 1 entry on L1, 0 on + L0/L2, and it must still be below the floor or the exemption is deleted). + + WHY BLOCKED rather than fixed, measured at the nearest passing colour + (#616367 dark, #808591 light -- the first step that clears 3:1 against + the card): + track vs. a --blocked arc dark 3.64:1 -> 1.60:1 light 5.88:1 -> 2.25:1 + track vs. an --ink-quiet arc dark 5.73:1 -> 2.52:1 light 5.18:1 -> 1.98:1 + i.e. the EMPTY ring would separate from a real status arc by LESS than the + 3:1 the floor is asking for, and a five-status donut would read as six. + WCAG 1.4.11's graphical-object half covers "parts of graphics required to + understand the content"; the arcs are, the ground they sit on is not. + Meeting the floor here would make the chart say something false, which is + the documented hierarchy this exemption protects. + + RE-RECORDED 2026-09-05 AT THE WAVE-4 UNION, once, on the merged tree + (chromium 148.0.7778.0 / playwright 1.60.0), because two sibling lanes + landed in the same wave and this lane's own recording was made on a tree + that had neither. EVERY FLOOR THIS ROW ASSERTS STILL READS ZERO, across all + 18 calm renders -- re-measured, not carried over: + text below 4.5:1 0 in every render (2 251 text nodes scored in all) + controls under 44px 0 in every render (568 controls measured in all) + non-text below 3:1 0 in every render (1 064 pairs measured in all) + animations under reduced motion 0 in every render + elements beyond the viewport 0 in every render, including 430px + the ONE enumerated exemption exactly 1 on each of the 6 L1 renders, + 0 on every L0 and L2 -- unchanged, and + still its own machine-checked pin + THE DENOMINATORS MOVED, and only the denominators. The per-render scored + populations above read, on the union: + L0 text 138 (129 at 430) controls 34 non-text 79 (77 at 430) + L1 text 184 (178 dark / 179 light at 430) controls 41 (40 at 430) + non-text 73 (52 at 430) + L2 text 58 controls 20 non-text 33 + Against this row's own pre-union table that is text 136 -> 138 on L0 and + 183 -> 184 on L1; nothing else moved. THE CAUSE IS NAMED, not guessed: the + swap-survives lane (OSV1-008) added exactly one persistent live region per + polling level -- `#wt-live` on L0 and on L1 -- and it carries text, so the + text census counts it. L0 gains two nodes rather than one because it also + renders the pause CONTROL's re-applied label. L2 does not poll, has no live + region, and its 58 is unmoved, which is the cross-check that this is the + live region and not a general drift. + A DENOMINATOR GROWING WHILE THE NUMERATOR STAYS 0 IS THE GOOD DIRECTION: + two more text nodes were scored and both cleared 4.5:1. Recorded here + because a row that quotes "0 of 136" after the population became 138 is + quietly describing a render nobody measured. + + HISTORY, left rather than overwritten -- each was true when measured. RE-DERIVED 2026-09-05, GAP -> VIOLATION. The browser run this clause names now exists and emits exactly what the clause asks for -- computed contrast @@ -1757,8 +2065,8 @@ - id: OSV1-012 title: >- - two empty widgets keep their slot and say nothing -- measured, not a - missing kit + every empty widget keeps its slot AND says so in a calm sentence -- + measured on the rendered empty fixture contract: file: contracts/operator-surface.v1.md clause: Core 8 @@ -1766,49 +2074,76 @@ 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: VIOLATION - work: work_item_pipeline-c1a + disposition: CONFORMS + work: work_item_pipeline-aad assertion: kind: probe ref: test_row_osv1_012 notes: > - PINNING ROW -- the probe asserts the CURRENT, KNOWN-WRONG shape on purpose; - a passing probe here is NOT conformance. Flip direction VIOLATION-MOVEMENT. - - 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: + FLIPPED 2026-09-05 VIOLATION -> CONFORMS (work_item_pipeline-aad), flip + direction VIOLATION-MOVEMENT. The probe is retargeted in the same change: + it no longer pins the silent slots, it forbids their return. + + RE-DERIVED FROM THE PASSING GOOD HALVES, never from a source edit + appearing. The kit's two `calm.keeps_slot` tests render L0 and L1 against + an all-empty project and against the populated ALARM fixture and compare + them region by region. Before / after on this tree, same kit, same two + fixtures: + BEFORE L0: 1 problem -- region 'Needs you -- ranked' keeps its slot but + says nothing. + L1: 2 problems -- 'Agents on ' and 'Status breakdown', + same defect. + Both halves `xfail(strict=True)` against this row. + AFTER L0: 0 problems. L1: 0 problems. Both markers deleted; the kit + runs 0 failed / 0 XPASS. + + THE THREE SENTENCES, as rendered (captured from the empty fixture): + L0

No item needs you right + now.

+ L1

No agent has held an + item in this project yet.

+ L1
...
0total + items

No items to break down + yet.

+ All three come from ONE helper, `widgets._empty_note`, so the register + cannot drift into three voices. The register is the house's own, not + invented for this row: `webapp.py`'s "Nothing is waiting to be claimed in + this queue right now.", `chartsvg.py`'s "No activity in this window", + `webbrowse.py`'s "No items match this filter." + + THE CLAUSE HAS THREE LIMBS AND ALL THREE NOW HOLD: 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). + the empty one, on BOTH levels. This limb always held. + SENTENCES all three formerly-silent regions now say so. This is the + limb that moved. 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. + (derived from the hero's own declared size, never written + down) renders a numeral outside `#verdict-hero`. Still + holds: `.empty-note` is .8125rem and carries no numeral + at all, and the donut centre's `0` is 1.5rem -- reported + at its siblings' scale WITH its `total items` label, + which is the shape OSV1-001 already recorded as stated + rather than celebrated. + + NO REFLOW, AND THE HONEST LIMIT ON THAT PHRASE. `.empty-note` carries a + `min-height` of one row's own height, so a slot cannot COLLAPSE when its + data goes away -- that is what "keeps its slot" is enforceable as. It does + NOT mean an empty 1-row list occupies the same pixels as a populated + 8-row one; nothing could make that true without rendering 7 blank rows, + which is the reflow-avoidance cure being worse than the disease. Two + structural facts carry the rest: L0's queue shares a `.two-up` grid row + with the velocity chart, whose height dominates; and L1's status-breakdown + keeps its donut on BOTH renders (`status_donut` draws its empty background + track at `total == 0`), so that card's dominant 150px box is identical + either way. Rendered PIXEL stability is Tier B and is not claimed here. + + WHAT THE SOURCE ALREADY CARRIED, and still does: `widgets.py` the verdict + headline is "All clear" / "Idle" -- a plain statement, never a triumphant + zero; `webapp.py` the ready queue says "Nothing is waiting to be claimed in + this queue right now." with the figure as an em-dash; `chartsvg.py` "No + activity in this window"; `webbrowse.py` "No items match this filter." + Four widgets got this right; the three that did not now do. - id: OSV1-013 title: no front-end framework, bundler, template engine, or build step @@ -2350,7 +2685,7 @@ 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 + `test_hero_velocity_and_counts` runs undeferred and PASSES on the real L0 (tier-a kit: 38 passed / 4 xfailed at that wave; 41 passed / 1 xfailed on the wave-4 union, re-run there, with this good half still undeferred and still passing); 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. @@ -2408,22 +2743,29 @@ - id: OSV1-026 title: >- - Conformance 7 (empty states keep their slot) -- fixture exists and - discriminates, good half deferred against OSV1-012 + Conformance 7 (empty states keep their slot) -- the two-render fixture + discriminates AND both its good halves pass contract: file: contracts/operator-surface.v1.md clause: Conformance 7 quote: | **Test location:** `tests/conformance/operator_surface/test_tier_a.py` (Core 8). - disposition: GAP - work: work_item_pipeline-c1a + disposition: CONFORMS + work: work_item_pipeline-aad assertion: kind: probe ref: test_row_osv1_026 notes: > - 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. + FLIPPED 2026-09-05 GAP -> CONFORMS (work_item_pipeline-aad), flip direction + VIOLATION-MOVEMENT, and the probe is retargeted in the same change from + "the good half is deferred" to "the good half PASSES". + + RE-DERIVED FROM THE PASSING PAIR, NEVER FROM THE FIXTURE'S PRESENCE. That + distinction is this row's whole subject and the failure mode it exists to + catch: a Conformance row is about a DISCRIMINATING fixture, and a fixture + whose good half has never been observed to pass is half a fixture. The + fixture existed at seed and this row still read GAP for exactly that + reason. MEASURED 2026-09-05. `tests/conformance/operator_surface/test_tier_a.py` implements `calm.keeps_slot` at the location Conformance 7 names, and it is @@ -2437,13 +2779,21 @@ empty widgets" (reports the missing region) and "or renders a hero-scale `0`" (reports the numeral, with hero scale DERIVED from the hero's own declared type size rather than written down). A third bad - half proves keeping the slot is not enough on its own. - GOOD, DEFERRED three regions keep their slot and say nothing -- see - OSV1-012, which carries the measurement. Both the L0 and the L1 good - halves are `xfail(strict=True)` naming OSV1-012. - - THIS ROW GOES GREEN WHEN OSV1-012 DOES. The probe asserts both facts -- the - fixture is present with its pair, and OSV1-012 is still red. + half proves keeping the slot is not enough on its own. All three still + run and still catch their defect -- flipping this row did not spend + them. + GOOD, NOW PASSING `test_calm_keeps_slot` (L0) and + `test_calm_keeps_slot_l1` (L1) each report 0 problems against the empty + fixture, where they reported 1 and 2 respectively before + work_item_pipeline-aad. Both `xfail(strict=True)` markers are deleted; + the kit runs 0 failed / 0 XPASS. The measurement itself is carried by + OSV1-012. + + WHY THE PAIR STAYED SPLIT L0/L1 rather than folding into one good half: + "fixing one and not the other is progress this kit must be able to show", + and this change proves the point in the other direction -- three regions + across two levels were fixed together and the two halves flipped together, + each still able to fail alone. - id: OSV1-027 title: Freeze 1 -- the Tier-A kit exists and runs on every pull request @@ -2681,30 +3031,68 @@ not move: its subject is the bad halves, all of which still run and still catch their defects. + RE-DEMONSTRATED 2026-09-05 AT THE WAVE-4 UNION by running both kits on the + merged tree, and TWO FIGURES IN THE PARAGRAPHS ABOVE ARE SUPERSEDED: + `make test-conformance-b` 89 passed, 0 failed, 0 xfailed, 0 XPASS + (was 52 passed / 35 xfailed at wave 3) + `make test-conformance-a` 41 passed, 1 xfailed, 0 failed, 0 XPASS + (was 38 passed / 4 xfailed at wave 3) + THE BROWSER TIER NOW CARRIES NO DEFERRALS AT ALL. Every `xfail(strict)` it + held named a ledger row that was honestly red; all four rows closed in this + wave (OSV1-003, -008, -010, and -012 in the Tier-A kit), each lane deleted + the markers naming its own row because they XPASSed, and the union deletes + the last of them. Zero XPASS on both kits is the proof that no marker was + left behind covering a check that now passes -- a stale strict xfail would + fail the run, which is why this is checked by running rather than by + reading the file. + THE ONE REMAINING DEFERRAL IN EITHER KIT is Tier-A's `test_antigoals_ + enforced`, which names OSV1-015's residual `_oldest_ready_item` (a + limit-less `bd.list` in a function with no callers). That row reads + CONFORMS because Core 10 scores calls REACHED FROM A VIEW and this kit's + census is source-wide with no reachability analysis. The marker stays until + the dead function goes or the census learns reachability -- named, not + quietly tolerated. + THE WAVE-3 PARAGRAPH ABOVE IS ALSO STALE ON ONE CLAIM: it says Conformance + 7 (`calm.keeps_slot`) "is now the ONLY deferred pair in the kit, against the + still-open OSV1-012, and OSV1-026 stays red for exactly that reason". Both + halves of that ended this wave -- the empty-states lane closed OSV1-012 and + OSV1-026 now reads CONFORMS, and that pair is no longer deferred. Corrected + here rather than rewritten above, so the record shows when it stopped being + true. THIS ROW'S OWN DISPOSITION DOES NOT MOVE and never did on any of + this: its subject is the fourteen BAD halves, every one of which still runs + and still catches its defect, re-read from the union's own recording by + this row's probe. + THE HOUSE PRECEDENT this follows: the custody family's CCV1-023 recorded discrimination by REVERTING each fix in turn and observing exactly the matching bad half go red while every good half stayed green. - id: OSV1-031 title: >- - Freeze 5 -- 4 of the 19 Core-carrying rows are still red, and all four are - now MEASURED VIOLATIONs (no Core row is an unasserted GAP any more) + Freeze 5 -- every Core clause reads CONFORMS or NOT-ASSERTABLE contract: file: contracts/operator-surface.v1.md clause: Freeze 5 quote: | **Freeze 5:** every Core clause reads CONFORMS in `ledger/`, or is NOT-ASSERTABLE with its review cadence named here. - disposition: GAP + disposition: CONFORMS work: work_item_pipeline-umm assertion: kind: probe ref: test_row_osv1_031 notes: > - PINNING ROW, and the only one that reads the LEDGER rather than the repo -- - it asserts that at least one Core-carrying row is still red. A passing - probe here is NOT conformance; it means the family is still red. Flip - direction VIOLATION-MOVEMENT: the probe goes red when the last Core row - turns green, which is the signal to flip this row. + FLIPPED 2026-09-05 GAP -> CONFORMS at the highway wave-4 union, flip + direction VIOLATION-MOVEMENT: the pinning probe went red because the last + red Core-carrying row turned green, which is exactly the signal this row + existed to raise. The probe is retargeted in the SAME change, from "at + least one Core row is red" to "no Core row is red", so the gate now guards + the other direction -- any Core row going back to GAP or VIOLATION fails it. + + WAS A PINNING ROW, and the only one that reads the LEDGER rather than the + repo -- it asserted that at least one Core-carrying row was still red. That + reading is history now and is kept below because the tally IS this row: the + sequence of flips is the evidence, and deleting it would leave a green row + with nothing behind it. MEASURED AT SEED, 2026-09-04 against 4aaee50. Core 1-13 are carried by 19 rows (Core 2, 4, 7, 10 split into halves with differing dispositions): @@ -2811,6 +3199,150 @@ Four red, every one of them a MEASURED failure. Freeze 5 is met when all four are green and the two NOT-ASSERTABLE rows still name their cadence. + RE-PINNED 2026-09-05 by the empty-states lane (work_item_pipeline-aad), + THREE RED. The count is what the probe MEASURES on this lane's tree, not a + number carried over: 19 Core-carrying rows, red = OSV1-003, -008, -010. + ONE flip moves it: OSV1-012 (Core 8) VIOLATION -> CONFORMS, re-derived from + the Tier-A kit's two `calm.keeps_slot` good halves going from 1 and 2 + reported problems to 0 and 0 against the same empty fixture -- not from a + source edit appearing. The tally is now: + CONFORMS 14 OSV1-001, -002, -004, -005, -006, -007, -009, -011, + -012, -013, -014, -015, -016, -017 + NOT-ASSERTABLE 2 OSV1-018 (Core 12), OSV1-019 (Core 13) -- unchanged + VIOLATION 3 OSV1-003, -008, -010 + GAP 0 every Core clause is still ASSERTED + Three red, and all three are the browser-tier halves (Core 2's calm-pixel + sweep, Core 6, Core 7's rendered half) owned by other lanes. THIS LANE + COUNTED ON ITS OWN BASE, base main @065da04; the orchestrator re-counts on + the union and that count supersedes this one if the union differs. + RE-PINNED 2026-09-05 by the calm-pixels lane (work_item_pipeline-a1o), + THREE RED, on that lane's own tree rather than on the union -- the + orchestrator re-counts on the union, and this number is honest only about + what this tree contains. ONE flip moves it: OSV1-003 (Core 2) VIOLATION -> + CONFORMS, re-derived from a chromium run RE-RECORDED on this tree, not from + a marker being deleted -- a calm L1 measured 0 --alarm and 0 --blocked in + both themes where it measured 97 --blocked, and the three discrimination + arms (10 531 / 264 / 16 681) came back byte-identical, so the sweep was not + merely blinded. The tally is now: + CONFORMS 14 OSV1-001, -002, -003, -004, -005, -006, -007, -009, + -011, -013, -014, -015, -016, -017 + NOT-ASSERTABLE 2 OSV1-018 (Core 12), OSV1-019 (Core 13) -- unchanged + VIOLATION 3 OSV1-008, -010, -012 + GAP 0 every Core clause is still ASSERTED + Three red, every one a MEASURED failure. Freeze 5 is met when all three are + green and the two NOT-ASSERTABLE rows still name their cadence. + RE-COUNTED 2026-09-05 at highway wave-4 integration (orchestrator): the calm-pixels lane flipped OSV1-003 and the + empty-states lane flipped OSV1-012 in the same wave, each re-pinning this count to 3 against its own tree; the union + is 2. Remaining red Core rows: OSV1-008 (Core 6, swap) and OSV1-010 (Core 7, rendered floors) -- both in flight. + + RE-PINNED 2026-09-05 by the swap-survives lane (work_item_pipeline-v3m), + THREE RED. ONE flip moves it: OSV1-008 (Core 6) VIOLATION -> CONFORMS, + re-derived from a Tier-B run RE-RECORDED on this lane's own tree -- all + four of Core 6's named survivals now hold on BOTH L0 and L1 + (`scroll_preserved`, `open_details_preserved`, `pause_control_preserved`, + `marked_live_regions_after` 1 of 2 on L0 and 1 of 1 on L1). The count + below is what THIS lane's tree measures; the orchestrator re-counts on the + union, and this pin is expected to move again if a sibling lane flips a + Core row in the same wave. + The tally is now: + CONFORMS 14 OSV1-001, -002, -004, -005, -006, -007, -008, -009, + -011, -013, -014, -015, -016, -017 + NOT-ASSERTABLE 2 OSV1-018 (Core 12), OSV1-019 (Core 13) -- unchanged + VIOLATION 3 OSV1-003, -010, -012 + GAP 0 every Core clause is still ASSERTED + Three red, every one of them a MEASURED failure. Freeze 5 is met when all + three are green and the two NOT-ASSERTABLE rows still name their cadence. + + RE-PINNED 2026-09-05 by the rendered-floors lane (work_item_pipeline-96f), + THREE RED, counted by the probe on THIS lane's tree. ONE flip moves it: + OSV1-010 (Core 7, the Tier-B rendered half) VIOLATION -> CONFORMS, and it + is a re-derivation from a real chromium run of this tree, not a row flipped + because a kit file appeared -- the recording was made twice, byte-identical + on every field the row asserts, and its four Tier-B `xfail(strict)` markers + were deleted because they XPASSed. Text below 4.5:1 went 7 nodes -> 0, + controls under 44px 26 of 34 on L0 -> 0, non-text below 3:1 16 of 79 -> 0 + in both themes, with ONE enumerated machine-checked exemption (the donut's + backing ring) recorded in OSV1-010's own notes and counted per render in + `LAST_RUN.json`. + The tally is now: + CONFORMS 14 OSV1-001, -002, -004, -005, -006, -007, -009, -010, + -011, -013, -014, -015, -016, -017 + NOT-ASSERTABLE 2 OSV1-018 (Core 12), OSV1-019 (Core 13) -- unchanged + VIOLATION 3 OSV1-003, -008, -012 + GAP 0 every Core clause is still ASSERTED + Three red, every one of them a MEASURED failure. Freeze 5 is met when all + three are green and the two NOT-ASSERTABLE rows still name their cadence. + This lane counts only its own tree; two other lanes are in flight against + OSV1-003 and OSV1-012, so the orchestrator re-counts on the union. + + RE-COUNTED 2026-09-05 AT THE WAVE-4 UNION (orchestrator), ZERO RED, and + THIS is the count that flips the row. It is what the probe MEASURES on the + merged tree -- `red = [r for r in the 19 Core-carrying rows if disposition + in {GAP, VIOLATION}]` came back EMPTY -- not a number transcribed from any + of the four lanes, each of which honestly counted 3 on its own base because + each could only see its own flip. THE FLIP SEQUENCE THIS WAVE, in merge + order, each re-derived from a measurement on this tree and not from a file + appearing: + OSV1-003 Core 2, calm-pixels (work_item_pipeline-a1o). A calm L1 paints + 0 --alarm and 0 --blocked in BOTH themes (it painted 97 + --blocked); a calm L0 likewise 0/0. The three discrimination + arms still fire on the SAME recording -- 10 531 --alarm px for + the injected chip, 264 --blocked for the genuinely-alarming + fixture, 16 681 for the reinstated retired palette -- so the + sweep went quiet because the surface did, not because the + measurement went blind. + OSV1-012 Core 8, empty-states (work_item_pipeline-aad). Tier-A's two + `calm.keeps_slot` good halves report 0 and 0 problems against + the same empty fixture (1 and 2 before), and their bad halves + still catch a stripped note. + OSV1-008 Core 6, swap-survives (work_item_pipeline-v3m). On the union's + own re-recorded run, calm L0 AND calm L1 both come back + `scroll_preserved` true, `open_details_preserved` true, + `pause_control_preserved` true, `announcement_preserved` true, + `marked_live_regions_after` 1. The two bad arms still lose what + they are there to lose: the naive replacement loses the open + `
`, the reflow variant loses the scroll offset too. + OSV1-010 Core 7 rendered half, rendered-floors (work_item_pipeline-96f). + Across all 21 `perception.floors` renders on the union: 2 251 + text nodes scored, `text_below_floor` 0 in every one; 568 + controls measured, `controls_below_44px` 0; 1 064 non-text + pairs, `non_text_below_floor` 0; 6 renders carry exactly 1 + `non_text_exempt_below_floor` each -- the enumerated donut + backing ring, one per L1 render at 3 widths x 2 themes, which + is the single machine-checked exemption OSV1-010 enumerates. + `running_animations_under_reduced_motion` 0 throughout. + THE RECORDING BEHIND THREE OF THOSE FOUR WAS MADE ONCE, ON THIS TREE, and + twice over: `make test-conformance-b` was run two times back to back + (89 passed, 0 failed, 0 xpassed each time) under the pinned chromium + 148.0.7778.0 / playwright 1.60.0, and every field any row asserts came back + byte-identical between the two. The only fields that moved are the + light-theme `--watch` pixel buckets (5 319 -> 5 444, 5 095 -> 5 237, + 5 266 -> 5 380, 6 303 -> 6 323), which no row reads -- the known wobble, + named here rather than left for someone to rediscover as a flake. + NO LANE'S OWN RECORDING SURVIVED: three lanes each re-recorded + `LAST_RUN.json` against a tree carrying only their own fix, so all three + were discarded and the union was recorded from scratch. That is the whole + reason this count is 0 and not 3. + The tally is now: + CONFORMS 17 OSV1-001, -002, -003, -004, -005, -006, -007, -008, + -009, -010, -011, -012, -013, -014, -015, -016, -017 + NOT-ASSERTABLE 2 OSV1-018 (Core 12), OSV1-019 (Core 13) -- unchanged, + and both still name the review cadence the clause + itself declares, which is Freeze 5's second limb + VIOLATION 0 + GAP 0 + Zero red. Freeze 5's condition -- "every Core clause reads CONFORMS in + `ledger/`, or is NOT-ASSERTABLE with its review cadence named here" -- is + met by measurement on this tree. + + WHAT THIS ROW DOES NOT SAY, recorded so a future reader does not widen it: + Freeze 5 is ONE of the eight Freeze conditions, and going green here does + not make the contract frozen. OSV1-034 (Freeze 8, the owner's own look at + the rendered pages, recorded in the Changelog) is still GAP and can only be + closed by the owner -- an agent writing that entry would be a fabricated + attestation. Freeze 5 green + Freeze 8 red is the honest state of the + family today. + - id: OSV1-032 title: >- Freeze 6 -- the exemption register is complete and no literal colour, font @@ -2963,6 +3495,22 @@ fact that gates the Freeze Bar, and rowing it keeps the condition visible between waves instead of surfacing only when someone tries to stamp FROZEN. + RE-CHECKED 2026-09-05 AT THE WAVE-4 UNION, DISPOSITION UNCHANGED (GAP). + Re-measured rather than assumed: the contract's Changelog still holds + exactly the three entries it held at seed and true-up #1 -- all dated + 2026-09-04 -- and the strings 430, 900 and 1280 appear nowhere in it (0 + occurrences). Nothing an agent did this wave could move this row, and + nothing did. + WHY IT IS RE-CHECKED HERE ANYWAY: OSV1-031 (Freeze 5) flipped to CONFORMS + on this union, which leaves THIS as the only red row in the operator-surface + family and the only Freeze condition still standing between the contract and + a Freeze Bar. That raises the pressure to close it cheaply, and cheaply is + exactly what is forbidden: writing this entry on the owner's behalf -- or + letting the eighteen rendered captures this wave's browser tier took at + 430/900/1280 in both themes stand in for the owner's look -- would be a + fabricated attestation (Phase-1 ruling 6). The KIT has looked; the OWNER has + not. Those are different facts, and this row records the second one. + - id: OSV1-035 title: Reserved 1 -- CLI --json shapes, ungoverned until an outside caller parses them contract: diff --git a/src/amplifier_work_tracker/webapp.py b/src/amplifier_work_tracker/webapp.py index 110c8c2..665aadf 100644 --- a/src/amplifier_work_tracker/webapp.py +++ b/src/amplifier_work_tracker/webapp.py @@ -3528,6 +3528,38 @@ def _observatory_glossary_dl(extra_dt_dd: str = "") -> str: ) +def _live_region_html(message: str) -> str: + """The ONE persistent live region a self-polling Observatory page renders. + + Core 6 asks that "an assistive-technology announcement pending at the + moment of the swap is not silently destroyed by it". The 20-second poll + replaces `document.body.innerHTML` wholesale, so ANY live region rendered + inside that body is destroyed and rebuilt empty every tick -- measured, on + the verdict hero's own `role="status"`: 0 of the 1 tagged node survived + (ledger row OSV1-008). A region that is rebuilt is a region that never + finishes announcing. + + This one is different only because `T.auto_refresh_js` KNOWS it: it is + detached before the swap and re-attached afterwards, so the node survives + and its text changes only when the surface's own state changed. Hence the + shared `T.LIVE_REGION_ID` -- the poller and the view must name the same + node or the mechanism silently does nothing. + + `aria-live="polite"` and not `assertive`: a monitor that interrupts is a + monitor nobody leaves running. `role="status"` is carried too, for the + assistive technologies that map the role rather than the attribute. + + Screen-reader-only by design (see webtheme.py's `.wt-live`): `message` is + the verdict the hero already states in full, and a second visible copy + would be redundant ink on a surface whose discipline is that calm is + reported, never decorated. + """ + return ( + f'

{_esc(message)}

' + ) + + def _observatory_nav_extras_html(*, reconcile_html: str = "", extra_dt_dd: str = "") -> str: """The nav's Observatory-only chrome, appended after `_nav_actions_html`'s existing search/bell/+New: a LIVE auto-refresh @@ -3553,6 +3585,15 @@ def _observatory_nav_extras_html(*, reconcile_html: str = "", extra_dt_dd: str = 'title="This page polls for fresh data on an interval">' '' 'Refreshes 20s' + # Server-rendered at `aria-pressed="false"` deliberately and + # permanently: the pause is a per-tab client-side flag + # (`window.__wtRefreshPaused`), and the server has no way to know it. + # What USED to be wrong is that nothing re-applied the flag after the + # body-swap re-rendered this button, so a paused page showed itself as + # running (Core 6 / OSV1-008). `T.auto_refresh_js`'s + # `restorePauseControl` now re-synchronises it to the flag after every + # swap, by calling `wtToggleRefresh` below -- so the label, icon and + # title vocabulary stays declared in exactly one place. '