HOMELAB-1684: feat(cockpit-web): LD3.4 telemetry panes — derived-state visuals + available-now telemetry #585
No reviewers
Labels
No labels
operator-review
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Homelab/infra-core!585
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "plane/HOMELAB-1684-derived-state"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
LD3.4 — the derived-state vocabulary and the telemetry that exists today. 2 commits,
lab-director/cockpit/web/**only — no Go, noci.yaml, no manifests, no flags. Brief:.rpiv/artifacts/designs/ld3/cockpit-design-brief.mdR12 (Routines/Alerts/Meeting/Costs are deferred — recorded as honest deferred states, not built).1.
feat(cockpit-web): the derived-state component system(4348525)lib/components/state/:StateGlyph+DerivedStateRow+DerivedStateLegend, rendering §5.2's visual language (dim / amber / blue / hatch textures, duration labels, the gate aHELDrow is held by with a link) as pure, unit-tested components.unknownis the DEFAULT state on both components: a caller that forgets to derive a state renders the hatched idle one, never green.pending_rulejoins the vocabulary as the hardware-gated hole (§5.2's rule text, §6's ApexDirector hero): dashed mark, hatched, idle, never a tick. Deliberately not inDERIVED_STATE_ORDER— §5.2's four registry states keep the legend.data-glyphtokens; the marks are lucide strokes because U+23F8/U+270B paint tofu in Inter and the mono stack (verified in a real browser, LD3.2b).isHealthy) that takes its table as an argument so it can be tested against a mutated green-unknowntable (a check that cannot fail is not a check) · the DOM ([data-derived-state][data-tone="ok"]must be empty, in e2e).aria-hiddenmark: an ancestor'sopacityis invisible toresponsive.spec.ts's contrast read, so dimming the whole chip was a silent contrast regression under a passing test.2.
feat(cockpit-web): the telemetry panes …(74f6343)wave-view.svelte+derived/sandbox-census.ts: rows keyed by the id a registry resolves, with role · model tier · derived-state column · cost column · ticket · LD3.3's attach cell imported, not duplicated. The row set's honesty is derived from the read model's own source rows as a pure function (the dispatcher decides; the bus alone cannot key a row set; an absent row is unwired, not a licence to draw rows). Unwired — today's truth — it prints ONE honest hatched row naming the census, never fake rows and never an empty table.—withdata-measured="false", never0and never$0.00.untracked), queue rows waiting on the operator, and this session's own dispositions including refusals. The bus-digest half keeps its banner naming LD1.5b/HOMELAB-1788.derived/fleet-counts.ts: gates-needing-the-operator from the queue's own items, class-E open from the census field named for it; bothnullunless a read backed them (a returned0stays a measurement; an absent one never becomes one).hardware-gated checkshero demonstratespending_rule; R12's four deferred panes are recorded by name.The one deviation to review
The honest hatch row renders under the table, not inside its body. The brief says the table renders one honest hatch row set. A nine-column table's min-content is ~500px, so inside the body the sentence was clipped at 390px and in the Fleet Overview's 517px card — the row's entire message was the one thing behind the scroller (screenshot 2 vs 3). Now: the header keeps the shape, the body is where a registry's rows go, and the single honest hatch row carries the glyph, the sentence and the disabled attach affordance at full width where no scroller can clip it. Decided by measuring the rendered page, not by taste.
Two smaller notes:
data-valuewas added toStatCellso the absence rule can be asserted on the value (an unmeasured cell's explanatory sentence legitimately contains digits — "not 41/41", "LD4"); andattach-launcher.svelte's stale(LD3.4)pointer was deliberately not touched (LD3.3's file, its spec asserts the sentence, and this slice still ships no registry read — flagged for the landing review rather than changed under it).Test plan
Counts, measured not inferred (base =
126af06, PR #584's tip, re-run in a clean worktree):e2e/telemetry.spec.ts= 12)New coverage by layer: the vocabulary (7) ·
StateGlyph(9) ·DerivedStateRow(10) · the census verdict (6) · the fleet counts and their absence cases (8) · the wave view incl. the registry-shape path (8) · the updates feed (8) · e2e (12).The e2e assertions that matter structurally, not textually:
[data-value]under[data-measured="false"]) — asserted against the live unwired binary and against a route that fulfils the overview with0s while claimingunwired(the P-COCKPIT-005 regression);[data-derived-state="unknown"][data-texture="hatch"]is exactly 1;$and no standalone0in the unwired cards;wave-row, hatched, its note naming the census — never fabricated rows;Existing specs change only where the UI deliberately did: the
derived-statetestid now belongs toStateGlyph(the counts are unchanged — 5 glyphs in the fleet pane), and the census-unwired sentence is single-sourced fromderived/sandbox-census.ts.Screenshots
/home/aaron/repos/homelab/.rpiv/artifacts/build/ld3-1684-screenshots/01-overview-1440.png/01-overview-390.png02-wave-view-1440.png/02-wave-view-390.png03-project-apex-1440.png/03-project-apex-390.pngpending_rulehole, the §6 skeleton slots, R12's deferred panesStacking (for the landing worker)
This branch is stacked on PR #584 (
plane/HOMELAB-1683-attach-ui,126af06) because it imports that slice's attach cell rather than duplicating it. #584 was still OPEN (green, mergeable, under independent review) when this branch was cut, so the diff againstepic/LD3currently includes #584's commits — this slice's delta is4348525..74f6343. Rebase ontoepic/LD3once #584 lands and the diff becomes the delta alone. Orchestrator notified via the session bus at cut time.Adds the attach bridge behind LD_ATTACH_ENABLED (default false): the cockpit/** mechanism, the server wiring and the refusal matrix, with no manifests, no RBAC, no CNP, no dispatcher change and no web/**. attach/ (new): - Registry seam (read-only interface, recording fixture, and the refusing unwired registry that names the unratified RBAC) — the live CRD reads wait for the operator's ratification package §4/§5. - Executor seam + client-go impl: remotecommand.NewSPDYExecutor over pods/exec with Tty:true and a TerminalSizeQueue; construction reads a kubeconfig (LD_ATTACH_KUBECONFIG, empty = in-cluster) and contacts nothing. - Refusal matrix with a P-ATT-001..008 policy book, the class harness allowlist (spawn's own class constants; unknown class refuses), and the ld.audit.attach/v1 writer (audit.Entry gains attach kinds, outcomes and fields; Validate enforces the schema/kind/outcome pairing both ways and keeps sentinel entries free of attach fields). - Wire() is the one place a kube client can be constructed: disabled builds none (asserted by a construction counter), enabled builds one and refuses per attempt while the registry is unwired. server/: - GET /api/v1/attach/{sandbox}/info and WebSocket /ws/attach/{sandbox}, registered outside RequireOperator so that a refused handshake is still a ledger entry; identity resolution, refusals and the WS protocol all audit (open/close/refused), statusLogger gains the Hijack passthrough a websocket upgrade needs through the chain. - One attach_open is answered by exactly one attach_close (a failed dial writes the aborted close), and an attach never opens when its open entry cannot be written. config: LD_ATTACH_ENABLED (default false) + LD_ATTACH_KUBECONFIG; a kubeconfig configured while the flag is off is refused as ambiguous. main: attach declared in the read-model census, in --check's OK lines and in the boot log ("no kube client constructed"). Tests: 192 pass under -race (attach 32, audit 9, server 47, config 13 new or extended); full refusal matrix, WS protocol over a real socket (upgrade/resize/EOF both ways/abrupt disconnect/protocol error/slow consumer), ledger pairing and entry JSON shape.The frontend half of LD3.3 starts at the contract. Every shape in types/attach.ts was read off the Go it mirrors (attach.Info, attach.Server*Frame, attach.RefusalKind, server.attachRefusal) and then VERIFIED by marshalling those structs and diffing the JSON — which is how two things the Go doc comments get wrong were found: - attach.Term carries NO json tags, so the info payload's terminal object is {"Columns":120,"Rows":32} (PascalCase), NOT the cols/rows the comment above the type claims. normaliseTerm accepts both spellings so a future tag addition is a no-op rather than a silent 0x0 geometry. - class_without_tty is answered 200, not the refusal kind's own 409: the read succeeded, so a 4xx would tell the frontend its request was wrong. attachApi.info therefore does NOT throw on non-2xx. The backend answers a decidable refusal as HTTP so a browser's onerror and an operator's curl see the same JSON — but a browser cannot read a failed WebSocket handshake's status at all, which is why every 400/401/403/404/409/502/503 is a POSTURE the button renders rather than a transport failure it retries. xterm 6.0.0 and @xterm/addon-fit 0.11.0 are pinned exactly (both are lazily imported, so they stay out of the home screen's first-paint chunk). formatBytes joins the format helpers for the session status strip.The UI half: a per-row attach cell, a by-id launcher, an xterm terminal and the Sandbox Environment View as a modal (design Part 2 §4.1 screen 4 — "what is this agent doing, and let me type into it"). Three decisions worth a reviewer's attention: - The rows carry the affordance, and the LAUNCHER exists because the cockpit has no sandbox-list read at all: info is a lookup BY ID, and the read model's census names four sources, none of which enumerates sandboxes. Rows-only would have shipped an unreachable feature. The launcher is the same posture as every other seam here — declare what is missing, make what IS possible possible — and the empty row renders a disabled button WITH the reason rather than no button, because "the registry is not read" and "this sandbox cannot be attached" are different facts. - The attach column is SECOND, not last, and that was decided by measurement: at 1440px the Fleet Overview's middle column is ~520px while the table wants ~640px, so a trailing column put the operator's one action off the right edge on the screen it was built for. - The dialog draws NO terminal until a session has actually opened. A refusal renders the status alone; an empty xterm below it would read as a shell that has not printed yet, which is the dead pane R6 forbids. It is portalled to <body> (the LD3.2b containing-block lesson), and its height is the terminal's height — with no terminal it shrinks to its five lines of status instead of wasting the screen. One measurement worth recording, because nothing in review would have found it: the visually-hidden reason sentence is position:absolute with no positioned ancestor, so it escaped the table's overflow-x-auto scroll container and grew the DOCUMENT's scrollWidth to 575px at a 390px viewport — the exact sideways-scrolling page the 3.1a responsive net exists to catch, and it caught it. Anchoring the cell (position: relative) keeps the sentence inside the column the scroller clips.14 new Playwright specs (48 -> 62), against the real cockpit binary in dev mode serving the real frontend build. The split is forced by what this build can produce. LD_ATTACH_ENABLED is false by design (the RBAC/CNP/arming delta is the operator's ratification package), so the LIVE server answers 503 "attach disabled" on both transports and records every attempt — the disabled posture is therefore asserted completely end to end, including reading the response the page actually received to check `disabled` and `audit_recorded` are true. An attachable answer is impossible live, so the other two postures run with the info payload fulfilled in the browser, from the same fixtures the Go tests assert. The most valuable spec is the one that follows the real refusal through the frontend: a fulfilled "attachable" answer opens the dialog, the live server then refuses the WebSocket upgrade, and the dialog has to land on an honest end state — the disabled refusal, its policy line and "refused and recorded" — with NO terminal element in the DOM. It is the R6 rule ("never a dead black box") as an assertion rather than a promise, and it is also the path a real operator hits whenever the registry moves under them. The viewport guard from LD3.2b is parameterised rather than copied, so the attach dialog — a second portal-to-body surface, mounted from inside a pane nested under the sticky blurred header — gets the same measured containing-block assertions at 1440 and 390, plus a structural check that its parent really is <body>. Measured at the end of this branch: at 390px the open dialog sits 675..836 inside an 844px viewport with its backdrop at 0..844, and the document scrollWidth is 390 against a 390px clientWidth.The §5.2 vocabulary as a component pair, pure and unit-tested: - `StateGlyph` draws one derived state: the mark (lucide strokes — the design's ⏸ and ✋ live in the emoji/symbol blocks and neither Inter nor the mono stack carries them, so the literal glyphs painted tofu in a real browser), the word, and the tone/texture the vocabulary declares. - `DerivedStateRow` paints the ROW: §5.2's dim / amber / blue / hatch textures, the duration label ("parked 23m"), the gate a HELD row is held by (with a link when there is one), and the row's own sentence. Column-agnostic — the caller supplies its cells as `lead`/`trail` snippets — so the wave table, a future project wave list and a future firmware list share one visual language instead of three hand-rolled approximations of it. - `unknown` is the DEFAULT state for both components: a caller that forgets to derive a state renders the hatched idle one, never green. - `pending_rule` joins the vocabulary as the hardware-gated hole (§5.2's rule text, §6's ApexDirector hero card): a dashed mark, hatched, idle, never a tick. It is deliberately NOT in `DERIVED_STATE_ORDER` — §5.2's four registry-derivable states keep the legend unchanged. The never-healthy rule is enforced three ways: a vocabulary test over every state, a guard (`isHealthy`) that takes its table as an argument so the guard itself can be tested against a MUTATED green-unknown table, and the dim moved off the word onto the aria-hidden mark — an ancestor's opacity is invisible to the responsive spec's contrast read, so dimming the whole chip was a silent contrast regression hiding under a passing test. `StatCell`'s value region gains `data-value`, so a spec can assert the absence rule on the VALUE alone: an unmeasured cell's explanatory sentence may legitimately contain digits ("not 41/41"), the value may not.Review —
e15f32a· honesty lens (R4 / R12)verdict: blocker
Fresh-context reviewer (
deepseek/deepseek-flash,pi -p --no-session, primary sources only: the diff, R4/R12, design §5.2), then independently re-verified by the landing worker. Baseorigin/epic/LD3(76625d2); delta = 26 files, alllab-director/cockpit/web/**(verified). Local at the head:svelte-check0/0, vitest 33 files / 305 tests green, Playwright 76 passed.BLOCKER B1 — the never-healthy guard does not cover the paint.
components/state/state-glyph.svelte:67-74hand-copies tone→class in a localtonesmap, whiledata-tonecomes from the vocabulary, so the guard asserts the name of the tone and never the paint. One-word mutation (idle: 'text-ink-faint'→'text-status-ok') makesunknown/PARKED/pending_rulerender--color-status-ok(green, hue 158) and the whole suite stays green.Independently reproduced by the landing worker in a scratch tree at
e15f32a:33 files / 305 tests passed, 0 failed.DERIVED_STATE.unknown.tone: 'idle'→'ok'): 4 files / 8 tests FAIL, exit 1.So the vocabulary leg is non-vacuous (as claimed) and the paint leg has no coverage at all —
grep -rn status-ok src e2efinds no spec asserting a colour class, andisHealthyis referenced only from tests, so nothing at runtime refuses a healthy state. R4/§5.2 says what is rendered may not read healthy; the e2e guard asserts an attribute, not a pixel. The shipped bytes are honest (measured:unknown/PARKEDpaintoklch(0.65 0.012 272),HELD78°,DONE-UNSEEN232°— no green), so this is a guard hole, not a live lie. Related, same family: mutatingSOURCE_STATE.unknown.tone: 'idle'→'ok'(rendered atdashboard-pane.svelte:97,332,392,gates-pane.svelte:49) also leaves 305/305 green.Cheapest closers: assert the class in
state-glyph.test.tsfor every state (not.toContain('text-status-ok')), or derivetonesfromDERIVED_STATE[*].tonethrough one enumerableTONE_CLASSmap that a spec walks against the vocabulary.CONCERN C1 —
wave-view.svelte:176renders{#each sandboxes}without consultingcensus.wired, andwave-view.test.ts:92-110renders aPARKEDrow under an unwired dispatcher: "unwired" is not structurally distinguishable from "rows exist". Production is correct today (dashboard-pane.svelte:375passes no rows).CONCERN C2 — stale
(LD3.4)attribution inattach/attach-launcher.svelte:85(also:19,attach-cell.svelte:62) now contradicts the new honest row's sentence; flagged by the builder but with no follow-up ticket. 2-line fix, its own spec asserts the sentence.CONCERN C3 —
dashboard-pane.svelte:450-452can print0 / 0 not configuredbefore the first read (a numeral with no read behind it), and the slice's own no-zero e2e probes are scoped to the other two card groups. The landing worker could not reproduce the first paint empirically (the 1s read-model tick supplies the 4 sources first; the observed paint was4 / 4 not configured), so this is a sub-second window found by reading the template.CONCERN C4 —
fleet-counts.ts:42outcomesis dead;gates.svelte.tsdecide()does not re-read the queue, so "gates needing you" keeps counting a gate the operator just ratified.Verified clean: no fabricated number anywhere (the only reachable
0is a queue that was read and holds nothing); the census verdict is a pure function of the read model's own rows (absent/error/bus-alone → unwired) and production prints exactly ONE hatched row naming the census; screens 6–9 are absent (no stub, no mock) and recorded by name in the UI and on HOMELAB-1684; no existing spec weakened (thepanes.spec.tsdelta is one hunk and it strengthened); delta is web-only.Claim honesty: the "enforced three ways" sentence is literally true but overstates DOM enforcement (attribute, not paint — B1); "the row set's honesty is derived … as a pure function" is true of the sentence and the
wiredflag, not of the row set (C1).Not verified by the reviewer: Playwright (the landing worker ran it: 76 passed).
Review —
e15f32a· craft / regression lensverdict: pass (approve-with-concerns)
Fresh-context reviewer (
deepseek/deepseek-flash,pi -p --no-session), no BLOCKERs. Measured at the head itself: vitest 33 files / 305 tests, Playwright 76 passed,svelte-check0/0; each authored commit builds on its own; the PR's own base numbers (221→277, 25→32 files, 62→74) are honest for the base it names (126af06) — the higher figure here is the #584 merge.Clean: the
components/state/*pair is pure and prop-driven, defaults tounknownon both components, and is colour-independent (each state carries its label +data-glyph+ an accessible title, not a hue alone). The attach cell is imported from #584, not duplicated — no second attach affordance or duplicated refusal/posture logic in the delta. No existing spec was weakened, deleted, skipped or made vacuous (the deletedderived-state-cell.svelte/wave-table.sveltelose no behaviour that is not carried by the new pair +wave-view). No page-level overflow at 1440 or 390.CONCERNs (no merge impact): C1 — the wave card scrolls horizontally at every width (measured: table 576px inside a 517px body at 1440, 356px at 390), so the builder's "inside the body it clipped" justification is only half the story; C2 — the one reachable row hardcodes the vocabulary (
wave-view.svelte:236-241literaldata-derived-state/data-tone/texture+hatchclass) instead of usingDerivedStateRow, a second source of truth; C3 — the legend's meanings truncate at 390px; C4 — a second definition of "needs a decision package" (fleet-counts.tsisDecidablevs the gates read's own notion); C5 — the digest status is triple-stated with one sentence duplicated across two files. Plus 8 SUGGESTIONs. Thepending_rulehero inproject-switcher.svelterenders the dashed hole correctly but does not carrydata-derived-state(confirmed by DOM probe) — consistent with C4/C2's "hardcodes the vocabulary" theme.Report:
.rpiv/artifacts/build/ld3-1684-review-craft.md.Landing —
e15f32a· scope correction, class-D visual, merge decision (NOT merged)Stale base corrected at the start of this pass. The branch was cut from
126af06— befored581628, the commit that closed PR #584's four review blockers — so the diff againstepic/LD3was showing #584's fix reverted (13 files, 1180 insertions, 1504 deletions of noise). Mergedorigin/epic/LD3(76625d2) into the branch, clean, zero conflicts, zero file overlap between the 3.4 delta andd581628; pushed ase15f32a. The PR diff is now the slice's real delta: 26 files, web-only, and #584's byte-accounting / refused-vs-unreachable / terminal-mount fixes are intact in the tree. Local ate15f32a:svelte-check0/0, vitest 33/305, build OK, Playwright 76 passed; combined CI green (run 1742/1743).Class-D visual (real binary, dev mode, nothing wired; scratch tree for a fixture-only route; 1440 + 390; screenshots +
evidence.jsonin.rpiv/artifacts/build/ld3-1684-landing-screens/):pending_ruleas rows (fixture-forced)272°, ✋ amber78°, ◐ blue232°,?hatch idle,◌dashed hatch — no green anywhere (green is158°)CARE DIALhatched, ONE hatched row:? unknown — sandbox census unwired — the registry arrives with the custodian census sources (dispatcher + bus)., disabled attach +no idno source/not measuredwith the future source named; no$, no fabricated0;unmeasured numerals: []; updates feed shows only real read-model ticks;SOURCES 4 / 4 not configuredfrom a real readpending rulehero (dashed hole, never a tick), skeleton slots naming their sources, R12's screens 6–9 listed by name — no stub, no pretend dataL17 R373inside 390 — readable, not behind the scrollerMerge: refused — BLOCKER B1 is open (see the honesty verdict). The defect is a guard hole, not a rendering lie: the shipped bytes paint honestly, but a one-word change to the tone→class map turns
unknowngreen with 305/305 tests green, which is exactly the structural requirement this slice exists to satisfy. Per the landing rule, a blocker verdict after the head blocks the merge; the wiring is otherwise sound and CI is green.Not merged; no GitOps action taken. Next action for the orchestrator: one fix worker on
e15f32ato close B1 (assert the class per state, or derive the tone classes fromDERIVED_STATEthrough one enumerable map that a spec walks) and, cheaply, C2 (the stale(LD3.4)pointer) and C3 (0 / 0 not configured); C1/C4 may be deferred to a follow-up. Then a fresh-context delta review → merge → GitOps (ships in the next image build; nothing deploys — the cockpit image workflow is live-gated).Review —
9780a18· delta review, fresh context (landing pass 2)verdict: pass (approve-with-concerns)
Fresh-context reviewer (
deepseek/deepseek-flash,pi -p --no-session, primary sources only: the delta, the prior verdicts' acceptance criteria; no builder summary, no implementer context). Repo read-only (git status --porcelain→ empty). Delta scope confirmed mechanically: 13 files, 317+/35-, alllab-director/cockpit/web/**. Report:.rpiv/artifacts/build/ld3-1684-delta-review.md.Baselines at
9780a18: unit 33 files / 312 tests (was 305 ate15f32a; +7) · e2e 77 passed (real Go binary, dev mode, prebuilt SPA).B1 — CLOSED, and non-vacuous (the mutation was run, both by the reviewer and by me)
honest-state.ts:ABSENT_CLASSis a literal;DERIVED_TONE_CLASSis typedRecord<Exclude<Tone,'ok'>, string>sookis not a key;glyphPaintreturnsABSENT_CLASSwhentexture === 'hatch'(unconditional — short-circuits before any tone lookup) ortone === 'ok'.state-glyph.sveltenow has exactly one colour expression, used for both the mark and the word. Green is unreachable from any derived-state name, tone name or texture;data-toneis inert markup (no CSS selector paints on it).DERIVED_TONE_CLASS.idle → 'text-status-ok'(the B1 repro)PAINTS what it declares…+ the vocabulary spectonesmap withidle → green(literal pre-fix tree)DERIVED_STATE.unknown.tone → 'ok'SOURCE_STATE.unknown.tone → 'ok'(stayed 305/305 green pre-fix)sourcePaintTone: non-error→'ok'ABSENT_CLASS → greenthen rebuild + e2ee2e/telemetry.spec.ts:114state-glyph.sveltereverted to atones[view.tone]lookup, table unchangedC2 / C3 — correct, each pinned by a test that fails when reverted
C2:
attach-launcher.svelte:86/attach-cell.svelte:62name the real source (derived/sandbox-census.ts,CENSUS_SOURCES = ['dispatcher','bus']); noLD3.4left in rendered text. C3: the pre-read branch renders? census not readwith no numeral, verified live in a real browser (?/api/v1/overview+?/api/v1/readmodel+?/api/v1/eventsdown) — see the landing comment. The existing no-numeral probe covers the region; forcing the pre-fix markup makes it report["0 / 0 not configured"].Regression scan
35 deleted lines audited — no assertion weakened, deleted or loosened; every removal is comment prose, the C2 rewrites, the prop rewires, the old local
tonesmap or the old header markup. No new always-true assertion: the e2e paint probe assertshealthy !== ''andpainted !== ''before comparing, andnot.toBeNull()precedes the optional-chained reads. Nothing outsideweb/**.CONCERN 1 — the new chip guard asserts the tone name, not the paint (the B1 shape, one hop out)
dashboard-pane.test.ts:127assertsdata-tone !== 'ok', anddashboard-pane.svelte:98-100claims "only aconfiguredsource can render green". The chip's colour comes from a hand-copied map inshell/state-chip.svelte:25-32, untouched by this delta. Measured: mutatingstate-chip.svelte:29idle→ the green class leaves 312/312 GREEN — every unwired chip paints green and nothing fails. The narrow claim is true (SOURCE_STATEcan no longer paint a chip green: mutation(c)/(h)RED); the "render green" half is unenforced.Why not a blocker: the chip map must legitimately map
ok→green for a genuinelyconfiguredchip, it predates this delta (no rendered behaviour regressed), B1's two named repros are closed, and the site is outside the frozen acceptance criteria. Recommended follow-up (one line of scope, same shape as this fix): assert the chip's class or computed colour, or drop "paints"/"render green" from that test's name and comment.CONCERN 2 — same shape at
shell/source-row.svelte:26(mutateunknown→ green: 312/312 GREEN;source-row.test.tsasserts onlydata-source/data-state/hatch/reason, never a colour). Pre-existing; raised because this delta's goal was to make the paint leg non-vacuous.Finding 3 — the e2e paint probe covers one mark only (
wave-rowglyph); withDERIVED_TONE_CLASS.idle → greenthe e2e is 12/12 green while the unit suite is RED. The property is covered; the e2e header comment overstates the e2e probe's reach. Finding 4 — the C3 absent branch is not exercised by the e2e in dev-mode CI (it needs both the overview and the readmodel reads down); the real cover is the new non-vacuous unit test. Finding 5 — removing|| tone === 'ok'fails via anundefinedargument-type error rather than a paint message.Landing —
9780a18· visual spot-check + mergeSpot-check (real binary, dev mode, unwired; screenshots in
.rpiv/artifacts/build/ld3-1684-landing2-screens/): the unknown-backed row/glyph does not render green — it paintsoklch(0.65 0.012 272)against a measured healthy probe ofoklch(0.76 0.115 158)(hue 158 = green), hatch texture,data-tone="idle", and the honest sentence naming the custodian census sources, at 1440 and 390; every derived glyph on the page is strictly non-green. The wiring header's absent branch renders? census not readwith no numeral (data-measured="false") when the census is unread — the0 / 0C3 window is gone, confirmed live.