From 40dae99dc1519075455d33a6f7041d1471410d66 Mon Sep 17 00:00:00 2001 From: Amplifier Date: Sat, 5 Sep 2026 02:24:48 -0700 Subject: [PATCH 1/5] feat(visual): one source of visual truth -- zero literal colour/font/size outside webtheme Core 4 / OSV1-005 + OSV1-032 close. - webtheme.py: split TOKENS_CSS out of CSS (CSS byte-identical, verified); add SINGLE_SOURCE_CSS (spacing utilities on the 4px scale, histogram, state fills, attn rows, page headings, textareas, chart fills) and TRUST_CSS + trust_style_tag(). - webtrust.py: ships no stylesheet and no style tag at all; emits the token module's own tag, which inlines the live tokens (still self-contained). - webpwa.py: the offline body carries no colour/font/size; asks the UA via instead. - webapp/webbrowse/widgets/chartsvg: 66 literal inline sites -> 0. - exemption register 23 -> 8 (laundered literals and computed colours became classes/data attributes; only real computed lengths remain). - ledger: OSV1-005 VIOLATION->CONFORMS, OSV1-032 GAP->CONFORMS, OSV1-006 register rewritten; probes retargeted, mutations rewritten (57/57 proven). - declared out-of-lane: OSV1-003 probe guards flipped sides, OSV1-024/-025 re-derived from their passing pairs, OSV1-031 tally 9 -> 6, and the two leftover strict-xfail markers removed from the Tier-A kit. --- ledger/checks/mutation_harness.py | 150 ++++--- ledger/checks/test_operator_rows.py | 258 ++++++------ ledger/rows.yaml | 390 ++++++++++-------- src/amplifier_work_tracker/chartsvg.py | 40 +- src/amplifier_work_tracker/webapp.py | 237 +++++------ src/amplifier_work_tracker/webbrowse.py | 48 +-- src/amplifier_work_tracker/webpwa.py | 25 +- src/amplifier_work_tracker/webtheme.py | 255 +++++++++++- src/amplifier_work_tracker/webtrust.py | 83 ++-- src/amplifier_work_tracker/widgets.py | 38 +- .../operator_surface/test_tier_a.py | 7 - tests/unit/test_chartsvg.py | 13 +- tests/unit/test_dashboard_ledger.py | 53 ++- tests/unit/test_observatory_widgets.py | 4 +- tests/unit/test_webapp_item_render.py | 12 +- 15 files changed, 957 insertions(+), 656 deletions(-) diff --git a/ledger/checks/mutation_harness.py b/ledger/checks/mutation_harness.py index 7cc08ac..e143002 100644 --- a/ledger/checks/mutation_harness.py +++ b/ledger/checks/mutation_harness.py @@ -421,12 +421,22 @@ def _mo002_alias_becomes_a_bespoke_hue(w: World) -> None: w.replace(WEBTHEME, "--amber:var(--alarm);", "--amber:#D9A253;") -def _mo003_retired_palette_removed(w: World) -> None: - """FIXED: the specimen a calm pixel sweep would catch is tokenised.""" +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. + """ w.replace( WEBPWA, - "background:#0D0D0C;color:#F2EEE6;", - "background:var(--color-ground);color:var(--ink-primary);", + "'' +", + "'' +", ) @@ -438,39 +448,32 @@ def _mo004_a_status_loses_its_word(w: World) -> None: w.replace(WEBBROWSE, '"open": "READY",', '"open": "",') -def _mo005_the_worst_literal_site_is_migrated(w: World) -> None: - """FIXED (partially): the retired-palette inline body is tokenised, so the - census drops from 66 to 65 and the named specimen leaves the bucket. +_LITERAL_INLINE_SITE = "\n_LEDGER_MUTATION = ''\n" +#: A page-local stylesheet, exactly the shape webtrust.py used to ship: its own +#: `:root` re-declaring role tokens as literal hexes. +_PAGE_LOCAL_SHEET = ( + "\n_LEDGER_MUTATION_CSS = " + '""\n' +) - BOTH halves of that one site must go: it carries a literal COLOUR pair AND - a literal `font:16px`/`padding:32px`. Replacing only the palette left the - site in the LITERAL bucket and the count unchanged at 66 -- the harness - caught that as a non-discriminating mutation, which is what it is for. - """ - w.replace( - WEBPWA, - "background:#0D0D0C;color:#F2EEE6;", - "background:var(--color-ground);color:var(--ink-primary);", - ) - w.replace(WEBPWA, "font:16px -apple-system,sans-serif;padding:32px;", "") +def _mo005_a_literal_inline_site_returns(w: World) -> None: + """REGRESSION: one inline `style=` attribute carries a literal colour again. -def _mo005b_the_page_local_palette_goes(w: World) -> None: - """FIXED (the OTHER half): webtrust.py's page-local `" + + __all__ = [ "CSS", "ICONS", + "TOKENS_CSS", "TRACK_W", + "TRUST_CSS", "age_band_class", "age_cell_html", "age_short", @@ -4346,4 +4598,5 @@ def state_html(kind: str, label: str) -> str: "state_html", "statusbar", "top_bar", + "trust_style_tag", ] diff --git a/src/amplifier_work_tracker/webtrust.py b/src/amplifier_work_tracker/webtrust.py index d63163d..1f4c9bd 100644 --- a/src/amplifier_work_tracker/webtrust.py +++ b/src/amplifier_work_tracker/webtrust.py @@ -63,6 +63,7 @@ from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse from starlette.responses import Response +from . import webtheme as T from . import webtls as WT # --------------------------------------------------------------------------- @@ -246,61 +247,29 @@ def _build_mobileconfig(ca_cert_path: Path) -> bytes: # --------------------------------------------------------------------------- -# Page rendering -- self-contained inline CSS (never imports `webtheme.CSS`; -# this is a separate tiny app, deliberately independent of the dashboard's -# own module graph). Colors are the same "J-editorial-dark" tokens as the -# dashboard (`webtheme.py`'s `:root`), copied here as plain values rather -# than shared, so this module has zero import-time dependency on webapp.py. +# Page rendering. This module ships NO stylesheet of its own. +# +# It used to: an inline stylesheet block right here re-declaring the whole +# role set (--ground / --raise / --ink / --mid / --quiet / --amber / --rule / +# --rule-hi) as its own literal near-black-and-warm-amber values -- the +# retired pre-blend-3 palette -- explicitly so it would not have to import +# `webtheme`. That independence bought one import edge and cost a second +# source of visual truth, which then drifted three palette generations behind +# the live `--color-ground:#05070f` without anyone noticing. operator-surface.v1 +# Core 4 forbids exactly that -- a stylesheet block outside the token module. +# +# The sheet now lives in `webtheme.TRUST_CSS`, composed from the SAME +# `TOKENS_CSS` the dashboard uses, and `webtheme.trust_style_tag()` emits the +# whole style element -- so there is no stylesheet, and no style tag, anywhere +# in this file. `webtheme` is a leaf module (stdlib only, no FastAPI, +# no `webapp`), so importing it costs this app nothing at import time. +# +# The page stays SELF-CONTAINED, which it must: the sheet is INLINED into the +# document, never fetched. A device reading this page has not installed the CA +# yet -- an external asset link would be a broken fetch at the worst possible +# moment. # --------------------------------------------------------------------------- -_CSS = """ -:root{ - --ground:#0D0D0C; --raise:#151513; --ink:#F2EEE6; --mid:#A6A199; - --quiet:#9C978F; --amber:#D9A253; --rule:#1F1F1D; --rule-hi:#333330; -} -*{box-sizing:border-box} -body{ - background:var(--ground); color:var(--ink); margin:0; padding:52px 24px 80px; - font-family:'Helvetica Neue',Arial,sans-serif; line-height:1.55; -} -main{max-width:640px;margin:0 auto} -h1{ - font-family:Georgia,'Bodoni Moda',serif; font-weight:500; font-size:32px; - margin:0 0 10px; color:var(--ink); -} -p{margin:0 0 14px} -p.subtle{color:var(--mid)} -p.lead{color:var(--ink); font-size:16px; margin:18px 0} -code{ - font-family:ui-monospace,'SFMono-Regular',Menlo,Consolas,monospace; - background:var(--raise); color:var(--amber); padding:2px 6px; border-radius:4px; - font-size:0.88em; word-break:break-all; -} -.btn{ - display:inline-block; background:var(--amber); color:var(--ground) !important; - font-weight:600; padding:12px 22px; border-radius:6px; text-decoration:none; - margin:6px 10px 6px 0; font-size:15px; -} -.btn.secondary{background:transparent; color:var(--amber) !important; border:1px solid var(--amber)} -.formsec{ - border:1px solid var(--rule-hi); border-radius:8px; padding:18px 20px; margin-top:20px; - background:var(--raise); -} -.formsec.highlight{border-color:var(--amber)} -.flegend{ - font-size:12px; text-transform:uppercase; letter-spacing:0.06em; color:var(--quiet); - display:block; margin-bottom:6px; -} -details{color:var(--mid); margin-top:16px} -details summary{cursor:pointer; color:var(--amber); margin-bottom:8px} -details .formsec{margin-top:10px} -.url-box{ - background:var(--raise); border:1px solid var(--rule); border-radius:6px; - padding:12px 16px; word-break:break-all; font-family:ui-monospace,Menlo,monospace; - font-size:14px; color:var(--ink); -} -""" - def _esc(value: object) -> str: return html.escape("" if value is None else str(value), quote=True) @@ -323,7 +292,7 @@ def _ca_install_section(*, os_hint: str) -> str:
Detected: {_esc(primary_label)}

{primary_instr}

-

+

Download CA certificate {mobileconfig_btn}

@@ -340,7 +309,7 @@ def _trust_page_html( ) -> str: https_url = f"https://{hostname}:{https_port}/" continue_btn = ( - f'

' + f'

' "Continue to the app →

" ) @@ -371,7 +340,7 @@ def _trust_page_html( Trust this server · amplifier-work-tracker - +{T.trust_style_tag()}
@@ -379,7 +348,7 @@ def _trust_page_html(

Install this certificate to trust {_esc(hostname)} on this device. This is a one-time step per device — not per visit.

{body} -

On another device? Open this same page there:

+

On another device? Open this same page there:

{_esc(request_url)}

diff --git a/src/amplifier_work_tracker/widgets.py b/src/amplifier_work_tracker/widgets.py index c4e1581..21c772f 100644 --- a/src/amplifier_work_tracker/widgets.py +++ b/src/amplifier_work_tracker/widgets.py @@ -680,7 +680,10 @@ class KpiCard(TypedDict): value: int href: str icon: NotRequired[str] - icon_color_var: NotRequired[str] + #: A STYLESHEET CLASS for the icon's ink (e.g. `"ic-blocked"`), never a + #: token name to interpolate into an inline `style=` -- operator-surface.v1 + #: Core 4: a colour is named in the stylesheet or it is not named. + icon_class: NotRequired[str] is_blocked: NotRequired[bool] @@ -700,9 +703,12 @@ def render_kpi_strip(data: KpiStripData) -> str: icon_html = "" icon = c.get("icon") if icon: - color_var = c.get("icon_color_var") - style = f' style="color:var({color_var})"' if color_var else "" - icon_html = f' ' + # The ink is a CLASS, not a token name interpolated into an + # inline style: the stylesheet is where a colour is allowed to be + # named (`.icon.ic-blocked`, webtheme's SINGLE_SOURCE_CSS). + tone = c.get("icon_class") + cls = f"icon sm {tone}" if tone else "icon sm" + icon_html = f' ' cards_html.append( f'' f'
{icon_html}{_esc(c["label"])}' @@ -799,13 +805,13 @@ def render_attention_queue(data: AttentionQueueData) -> str: "deferred", "blocked", ) -_MIX_COLOR_VAR: dict[str, str] = { - "resolved": "--ink-quiet", - "ready": "--ink-secondary", - "held": "--brand-cyan-ink", - "intake": "--ink-tertiary", - "blocked": "--blocked", -} +# The colour per mix slot is NOT declared here. It used to be a dict of token +# names interpolated into `background:var(...)` at two call sites -- the mix +# bar and the donut legend -- which meant the palette of a single reading lived +# in Python rather than in the stylesheet. It is now `.status-mix span.mix-*` +# and `.sw.mix-*` in webtheme's SINGLE_SOURCE_CSS, keyed by the same +# `_MIX_ORDER` slot name emitted as a class. `deferred` is still the exception +# it always was: it draws through `.pat-hatch`, a texture, never a flat colour. class FleetStatusMix(TypedDict): @@ -830,9 +836,7 @@ def _status_mix_bar_html(mix: FleetStatusMix) -> str: if key == "deferred": segments.append(f'') else: - segments.append( - f'' - ) + segments.append(f'') tooltip = ( f"{mix['ready']} ready · {mix['held']} held · {mix['blocked']} blocked · " f"{mix['deferred']} deferred · {mix['intake']} intake · {mix['resolved']} resolved" @@ -1041,7 +1045,7 @@ def render_activity_feed(data: ActivityFeedData) -> str: icon = _FEED_ICON[item["kind"]] items_html.append( f'' - f'' + f'' f'' f'{_esc(item["actor"])} {_esc(item["verb"])} ' f'{_esc(item["project_item"])} — ' @@ -1058,7 +1062,7 @@ def render_activity_feed(data: ActivityFeedData) -> str: # already persists whatever state the user leaves it in across # a refresh, so defaulting to open costs nothing on that front. '
' - 'Activity feed ' + 'Activity feed ' "cross-project · reverse-chronological " '' '
' @@ -1107,7 +1111,7 @@ def render_status_breakdown(data: StatusBreakdownData) -> str: swatch = ( '' if key == "deferred" - else f'' + else f'' ) legend_rows.append( f'
{swatch}{_MIX_NAME[key]}' diff --git a/tests/conformance/operator_surface/test_tier_a.py b/tests/conformance/operator_surface/test_tier_a.py index 66d1db3..b21b3b1 100644 --- a/tests/conformance/operator_surface/test_tier_a.py +++ b/tests/conformance/operator_surface/test_tier_a.py @@ -25,7 +25,6 @@ saying so. Their good halves are `pytest.mark.xfail(strict=True)` with the row id in the reason -- never `skip`, never deleted: - visual.single_source OSV1-005 66 literal inline sites + 40 in blocks calm.keeps_slot OSV1-012 an empty widget keeps its slot but says nothing antigoals.enforced OSV1-015 `_oldest_ready_item` calls `bd.list` @@ -550,12 +549,6 @@ def test_visual_single_source_bad_half_the_register_is_the_ledgers_own() -> None ) -@pytest.mark.xfail( - strict=True, - reason="OSV1-005 (Core 4): 66 inline `style=` sites and 40 `"\n' + '\n_LEDGER_MUTATION_CSS = ""\n' ) diff --git a/ledger/checks/test_operator_rows.py b/ledger/checks/test_operator_rows.py index 87cf56c..95eadab 100644 --- a/ledger/checks/test_operator_rows.py +++ b/ledger/checks/test_operator_rows.py @@ -1565,7 +1565,7 @@ def test_row_osv1_025() -> None: assert not _kit_deferred_rows(kit, "test_visual_single_source"), ( "OSV1-025 (Conformance 6) REGRESSION: Conformance 6's good half is deferred " "again. This row is green because the pair PASSES -- the bad half reports the " - "contract's own `style=\"color:#D9A253\"` specimen and an unregistered computed " + 'contract\'s own `style="color:#D9A253"` specimen and an unregistered computed ' "site, and the good half passes over the real `src/` tree." ) assert row("OSV1-005")["disposition"] not in PINNING_DISPOSITIONS, ( From 595b44600f5c63a411f1130a3f43c3dfbf9a836d Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Sat, 5 Sep 2026 05:37:25 -0700 Subject: [PATCH 5/5] ledger(integration): visual-truth union -- OSV1-006 register re-measured, OSV1-031 re-counted, Tier-B run re-recorded Union of lane/visual-truth (4 commits) onto wave-2 main (6c2e9fa). RE-MEASURED ON THE UNION TREE, never transcribed from a lane's own base: - OSV1-006's exemption register: webtheme.py's three computed sites sit at 4197/4216/4223 here, not the lane's 4143/4162/4169 (core10 and the lane's own CSS both shifted the module). The probe's EXEMPTION_REGISTER and the row's notes table are both re-derived from the re-run census. - OSV1-031 (Freeze 5): 4 red Core rows on the union (003, 008, 010, 012), not the lane's 6 -- it counted a base where OSV1-015/-016 were still red and the browser tier did not exist. Title, notes tally and probe pin moved 5 -> 4. - Tier-B re-recorded on this tree (chromium 148.0.7778.0 / playwright 1.60.0) and run twice: 290 of 294 fields byte-identical, including every field any row asserts. OSV1-003's numbers are unchanged -- a calm L1 still paints 97 `--blocked` pixels, and the three discrimination arms still read 10 531 / 264 / 16 681. CONFLICTS RESOLVED AS UNIONS, never pick-a-side: OSV1-003 keeps wave-2's recorded-sweep assertions AND takes the lane's palette guards flipped to the absent side; OSV1-003's mutation pair keeps both the calm-pixel counterfactual and the returning-palette guard; OSV1-024 keeps both of its mutations. ONE REGRESSION FOUND BY THE RE-RECORD, AND REPAIRED. The lane moved chartsvg's ink out of inline `style=` into `.axis-label[data-watch]` / `[data-val]` / `[data-today]` (specificity 0,2,0), which LOSES to the pre-existing `.wt-observatory .svg-chart .axis-label{fill:var(--ink-tertiary)}` (0,3,0) that an inline attribute had always outranked. The watch band's axis labels, the histogram's value labels and the velocity chart's today tick therefore stopped painting their own ink, and `.val-label` lost its default fill entirely (SVG falls back to black -- invisible on the dark ground). Measured, not reasoned: 979 `--watch` px on a dark L1 against 1 161 before. Every chart-ink selector is now scoped through `.svg-chart` (>= 0,3,0 and later in the cascade), and the re-recorded sweep reads 1 161 again -- byte-identical to the pre-lane value. Two new discriminating guards in tests/unit/test_chartsvg.py, each demonstrated failing against the pre-fix stylesheet. --- ledger/checks/test_operator_rows.py | 11 +- ledger/rows.yaml | 113 ++++++++++++++---- src/amplifier_work_tracker/webtheme.py | 28 +++-- .../operator_surface/browser/LAST_RUN.json | 10 +- tests/unit/test_chartsvg.py | 105 +++++++++++++++- 5 files changed, 225 insertions(+), 42 deletions(-) diff --git a/ledger/checks/test_operator_rows.py b/ledger/checks/test_operator_rows.py index 95eadab..54108d8 100644 --- a/ledger/checks/test_operator_rows.py +++ b/ledger/checks/test_operator_rows.py @@ -1937,13 +1937,14 @@ def test_row_osv1_031() -> None: "to CONFORMS and retarget this probe to assert no Core row is red " "(work_item_pipeline-umm)." ) - assert len(red) == 6, ( - f"OSV1-031 (Freeze 5): pinned 6 red Core-carrying rows, observed {len(red)}: " + 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-001 went green 2026-09-05, " - f"work_item_pipeline-ujy; OSV1-005 went green 2026-09-05, " - f"work_item_pipeline-np3.)" + 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 {r["id"] for r in core_rows if r["disposition"] == "NOT-ASSERTABLE"} == { "OSV1-018", diff --git a/ledger/rows.yaml b/ledger/rows.yaml index 04f39c8..67793ea 100644 --- a/ledger/rows.yaml +++ b/ledger/rows.yaml @@ -1121,6 +1121,30 @@ The contract's own named specimen (`style="color:#D9A253"`) is already exactly that, and Conformance 6's bad half (OSV1-025) already runs it. + RE-RECORDED 2026-09-05 at highway wave-3 integration, on the union tree, + same pinned chromium 148.0.7778.0 / playwright 1.60.0. EVERY NUMBER THIS + ROW ASSERTS IS UNCHANGED -- L0 clean 0/0 in both themes, L1 0 `--alarm` + and 97 `--blocked` in both, swept areas identical, and the three + discrimination arms still read 10 531 / 264 / 16 681. The visual-truth + lane changed classes, not the calm page's status pixels. + + WHAT DID MOVE, recorded because a re-record that only reports "no change" + is not a measurement: the `--watch` bucket, which this sweep collects and + Core 2 deliberately does NOT score. It fell 1 161 -> 979 px on a dark L1 + on the first union recording -- a real regression the lane's chart-ink + migration introduced through a CSS-specificity trap (see OSV1-005's own + notes) -- and reads 1 161 again after the repair, byte-identical to the + pre-lane value. + + 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 + four that are not are the LIGHT-theme `--watch` counts, which jitter by up + to ~90 px on ~5 200 (under 2%) run to run, consistent with antialiasing at + the sweep's 8/255 hue tolerance. Nothing reads them. They are recorded as + observed; if a row ever wants to assert one, it must first pin down that + jitter rather than inherit it. + - id: OSV1-004 title: state is never colour-only -- measured on rendered L0/L1/L2, and it holds contract: @@ -1263,16 +1287,42 @@ it is recorded here rather than fixed, because changing the rule would change what BOTH halves measure and belongs in one change of its own. - ONE RENDERED BEHAVIOUR CHANGED, and it is reported rather than folded in. - The Blocked KPI card's icon carried its alarm ink as an INLINE style, and - an inline style outranks every stylesheet rule -- including + ONE RENDERED BEHAVIOUR CHANGED ON PURPOSE, and it is reported rather than + folded in. The Blocked KPI card's icon carried its alarm ink as an INLINE + style, and an inline style outranks every stylesheet rule -- including `.wt-observatory .kpi-card.is-blocked.is-zero .k .icon{color:var(--ink-quiet)}`, which exists precisely so that "0 blocked" stops reading as alarm-red. That rule had never been able to win. With the ink moved to `.icon.ic-blocked` the is-zero rule now applies, so a calm screen paints one fewer alarm- coloured pixel. That is the direction Core 2 asks for ("zero `--alarm` pixels on a calm screen"), and it is a change in rendered output, so it is - named here. No other rendered output moved except spacing, per the 4px + named here. HONEST SCALE, added at integration: the icon is small enough + and antialiased enough that the Tier-B sweep measures NO change in the + scored buckets -- a calm L0 read 0 `--alarm` and 0 `--blocked` both before + and after. The cascade fix is real and readable in the stylesheet; the + pixel claim is "one fewer", not "measurably fewer". + + A SECOND RENDERED CHANGE WAS FOUND AT INTEGRATION AND WAS A REGRESSION -- + the same cascade trap, in the other direction, and the reason this section + no longer says "no other rendered output moved". Moving the chart ink out + of `chartsvg.py`'s inline `style=` into `.axis-label[data-watch]` / + `[data-val]` / `[data-today]` (specificity 0,2,0) put it BEHIND the + pre-existing `.wt-observatory .svg-chart .axis-label{fill:var(--ink-tertiary)}` + (0,3,0), which an inline attribute had always outranked. The watch band's + axis labels, the histogram's value labels and the velocity chart's + today tick therefore stopped painting their own ink, and `.val-label` lost + its default fill entirely (SVG falls back to black -- invisible on the dark + ground). MEASURED, not reasoned: the Tier-B re-record on the union tree read + 979 `--watch` px on a dark L1 against 1 161 before. Repaired by scoping + every chart-ink selector through `.svg-chart` (>= 0,3,0, and later in the + cascade), and the re-recorded sweep reads 1 161 again -- byte-identical to + the pre-lane value. Guarded by two new discriminating tests in + `tests/unit/test_chartsvg.py` + (`test_chart_state_ink_outranks_the_generic_axis_label_rule`, + `test_every_chart_label_kind_has_a_default_ink`), each demonstrated failing + against the pre-fix stylesheet. Nothing else in the sweep moved. + + Apart from those two, no rendered output moved except spacing, per the 4px snap above. NOT DONE HERE: `style=` is not gone -- 55 inline attributes remain, 47 of @@ -2618,18 +2668,25 @@ NOT WHAT THIS ROW ASSERTS, recorded so a future reader does not widen it: Freeze 4 asks that every fixture's BAD half has been run and caught its - defect. It does not ask that every GOOD half passes. Conformance 6 and 7 - still carry `xfail(strict)` good halves against the open product defects - OSV1-005 and OSV1-012, and those two rows (OSV1-025, OSV1-026) stay red for - exactly that reason. A strict xfail is a check that RUNS -- the deferral is - visible, the demonstration is not missing. + defect. It does not ask that every GOOD half passes. A strict xfail is a + check that RUNS -- the deferral is visible, the demonstration is not + missing. + + UPDATED 2026-09-05 at highway wave-3 integration: Conformance 6's good half + is no longer deferred. The visual-truth lane closed OSV1-005, deleted the + `xfail(strict)` marker naming it, and OSV1-025 flipped GAP -> CONFORMS in + the same change. Conformance 7 (`calm.keeps_slot`, two good halves) is now + the ONLY deferred pair in the kit, against the still-open OSV1-012, and + OSV1-026 stays red for exactly that reason. This row's own disposition does + not move: its subject is the bad halves, all of which still run and still + catch their defects. 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 -- 5 of the 19 Core-carrying rows are still red, and all five are + 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) contract: file: contracts/operator-surface.v1.md @@ -2688,23 +2745,15 @@ measurement (see its own notes and the tests named there), not flipped because a file appeared. - TALLY MOVED 2026-09-05, RE-MEASURED FROM THE LEDGER RATHER THAN FROM THE - PRECEDING PARAGRAPHS -- which had drifted: the entry above says "nine red - now" and then lists OSV1-004 and OSV1-009 among them, both of which its own - earlier paragraphs record as CONFORMS. The observed red set on this change, - computed by the probe, is SIX: OSV1-003, -008, -010, -012, -015, -016. - Two flips account for the difference from the (correct) count of seven this - branch inherited: + THE VISUAL-TRUTH LANE COUNTED SIX on its own pre-integration base + (work_item_pipeline-np3), where OSV1-015 and -016 were still red and the + browser tier did not exist. That count is SUPERSEDED by the wave-3 re-count + at the end of this row; it is left recorded rather than deleted because the + flip it carried is real and is the reason the tally moved: OSV1-005 VIOLATION -> CONFORMS (work_item_pipeline-np3): zero literal inline sites and zero literal `