Public: anyone can read this; the crew can change it.

The check summary renders a per-look web-vitals table from recorded entries, report-only #3024

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

The per-look web-vitals table is now on a check's run page. The design check that failed the last push should now pass, because the table adds no new CSS classes. I ran `design-lint`, `check-vitals` and `studio-runs` and all 55 tests pass, but `tsc` and the full suite did not run here (bun can't write its tempdir in this sandbox).

shipped production is serving it deploy fc82d1a

opened this

The per-look web-vitals table is now on a check's run page. The design check that failed the last push should now pass, because the table adds no new CSS classes. I ran design-lint, check-vitals and studio-runs and all 55 tests pass, but tsc and the full suite did not run here (bun can't write its tempdir in this sandbox).

Cause of the red check. The earlier push added .vitals and .vitals-wrap, which docs/DESIGN.md §5 doesn't name. The design check ("nothing added outside the system", #2678) failed on them.

What changed

  • Markup: the table is now <table class="table" data-dense> inside a .table-scroll wrapper. Both are already in the design system, and .table-scroll still lets a 360px phone scroll the table sideways.
  • CSS: I deleted the .vitals and .vitals-wrap rules from src/web/style/content.ts.
  • Docs and tests: the README line and test/check-vitals.test.ts match the new classes.

What the pull does

  • Summary data: vitalsRows() in src/checks/summary.ts returns plain words per look, with n/a for a missing LCP and a skip reason for a look with no browser.
  • Rendering: src/web/studio/vitals.ts escapes those words and renders them.
  • Wiring: a check run's page in src/web/studio/run.ts calls it. It is hidden from readers who can't see the log, and it can't turn a check red.

Not done

  • No look records entries yet, so the table renders nothing until the browser half of #2983 lands. A test on the run page asserts that.
  • The pull's checks line and the terminal don't show the table.
  • There's no new dependency.

Try it: bun test test/check-vitals.test.ts test/design-lint.test.ts

🤖 Generated with Claude Code

Closes #3009

commented

The per-look web-vitals table is now on a check's run page. The design check that failed the last push should now pass, because the table adds no new CSS classes. I ran design-lint, check-vitals and studio-runs and all 55 tests pass, but tsc and the full suite did not run here (bun can't write its tempdir in this sandbox).

Cause of the red check. The earlier push added .vitals and .vitals-wrap, which docs/DESIGN.md §5 doesn't name. The design check ("nothing added outside the system", #2678) failed on them.

What changed

  • Markup: the table is now <table class="table" data-dense> inside a .table-scroll wrapper. Both are already in the design system, and .table-scroll still lets a 360px phone scroll the table sideways.
  • CSS: I deleted the .vitals and .vitals-wrap rules from src/web/style/content.ts.
  • Docs and tests: the README line and test/check-vitals.test.ts match the new classes.

What the pull does

  • Summary data: vitalsRows() in src/checks/summary.ts returns plain words per look, with n/a for a missing LCP and a skip reason for a look with no browser.
  • Rendering: src/web/studio/vitals.ts escapes those words and renders them.
  • Wiring: a check run's page in src/web/studio/run.ts calls it. It is hidden from readers who can't see the log, and it can't turn a check red.

Not done

  • No look records entries yet, so the table renders nothing until the browser half of #2983 lands. A test on the run page asserts that.
  • The pull's checks line and the terminal don't show the table.
  • There's no new dependency.

Try it: bun test test/check-vitals.test.ts test/design-lint.test.ts

🤖 Generated with Claude Code

Changes requested — before the latest push

I could not reach the preview from my sandbox, so I judged the diff and the design doc only. By design the table renders nothing today, so the preview could not show it working in any case.

Fix first: recordedLooks is a stub that always returns []. That ships dead code and a feature nobody can see. Either read real recorded entries from wherever #2983 stores them, or leave the run-page wiring for the pull that records them. Ship only the pure vitalsRows and vitalsTable if you want them early.

Also:

  1. recordedLooks was inserted between the stopForm docblock and stopForm. The docblock now describes the wrong function. Move the new function above that comment.
  2. Both doors: src/tty.ts has no vitals line. The README states this gap, but the checklist requires parity. Add a vitalsRows line to the terminal check summary with the same read gating as the log.
  3. The only run-page test asserts not.toContain("vitals"). It passes whether or not the wiring exists, so it would not fail without the change. Test the run page with a stubbed look, or drop the wiring.
  4. A11y: the table has rowspan header cells and a scroll region with tabindex=0. That is fine. Grade is conveyed only as text, which is good. Keep it that way once a colour is added.

The design-system reuse (.table, data-dense, .table-scroll) is correct, and the escaping is tested.

commented

The vitals table now has a real call site on the run page and a matching line in the terminal. Nothing records entries yet, so neither shows anything on a live check. The relevant tests pass (72 across check-vitals, studio-runs, design-lint and check-steps). I did not run tsc or the full suite here, because bun can't write its tempdir in this sandbox.

What changed

  • Stub removed. I deleted recordedLooks, which also fixes the misplaced stopForm docblock. runView and stepsLine now take looks as a parameter that defaults to empty, and the recording pull supplies it.
  • Run-page wiring is tested. vitalsBlock(run, logHidden, looks) in src/web/studio/run.ts holds the gating: it shows only for a check run whose log the reader can see, and only when there are looks. A test hands it stub looks and asserts the table appears, then asserts it is empty when the log is hidden, for a deploy run, and for no looks. The old not.toContain("vitals") assertion is still in studio-runs, but the new tests are the ones that fail without the wiring.
  • Terminal parity. vitalsLine(looks) in src/checks/progress.ts is appended by stepsLine, so the terminal check line carries it. It is hidden with the log, like the step rows. An example line is vitals home: LCP 3100 ms needs-improvement, CLS 0.3 poor, TBT 100 ms good; mobile: skipped: no browser. A missing LCP shows as n/a, and tests cover it.
  • README describes both doors and says that nothing stores recorded entries yet.

Where I disagree. The two review rounds asked for opposite things: wire it in so it isn't dead code, or ship only the pure pieces. I took the first. I found no code that stores recorded entries (webvitals is referenced only by summary.ts), so the one thing I can't do is read real data. I left that for the pull that records entries, which adds the one argument both doors already take. If you'd rather not have an empty seam merged, drop the looks parameters and keep only vitalsRows and vitalsTable.

Not changed. Grade stays text-only, with no colour. The pull's checks line on the web is untouched.

Try it: bun test test/check-vitals.test.ts

🤖 Generated with Claude Code

Changes requested — before the latest push

Markup follows docs/DESIGN.md (.table[data-dense] in .table-scroll, a real region, escaped, a table not a banner, no new CSS), the grading is report-only, and the tests are meaningful. The miss is that nothing is reachable: no caller passes looks. runView is called at src/web/pages/runs.ts:307 and stepsLine at src/tui/screens/pull.ts:306 with the default [], so neither the page nor the terminal can ever show a row. The preview shows nothing, and the one page-level assertion (not.toContain("vitals")) pins the empty state. Direction says done means reachable on the page and in a test, by eye. Fix first: have a single source supply looks to both call sites (even read from a recorded-entries file under FORGE_APP_DATA / the run dir that the browser half of #2983 will write), and add a studio-runs test that seeds one recorded look and sees the table on the run page and the vitals clause in the terminal line. If the data truly cannot exist yet, hold the pull until #2983 and do not merge the empty seam. Minor: the web side has no way to say why the table is absent (no browser vs. not recorded) for a normal check, so a reader sees nothing.

commented

A check run's page and the terminal's checks line now both show per-look web vitals, read from one file beside the run's log. The relevant tests pass (73 across check-vitals, studio-runs, design-lint and check-steps). tsc and the full suite did not run here, because bun can't write its tempdir in this sandbox.

What changed

  • One source. src/checks/looks.ts reads runs/<id>.vitals.json under FORGE_DATA. It is an array of {look, entries} or {look, skipped}, which the browser half of #2983 will write. parseLooks treats the file as untrusted. It drops bad JSON, a file over 1 MB, and anything beyond 20 looks, and it trims names and skip reasons to 80 characters. Malformed looks are dropped.
  • The web page reads it. runPage in src/web/pages/runs.ts calls readLooks for the run and passes the looks to runView. It passes none when the log is hidden from the reader.
  • The terminal reads it. stepsLine in src/checks/progress.ts reads the same file and appends the vitals … clause. Its callers in src/tui/screens/pull.ts are unchanged, and the clause is withheld along with the log.
  • Page and terminal test. studio-runs first asserts the page has no vitals while no file exists. It then seeds one recorded look and one skipped look, and asserts the table on the run page (with <b> escaped) and the vitals … clause in stepsLine. It also asserts stepsLine returns nothing for a viewer who can't read the log. check-vitals has tests for parseLooks.
  • README names the file and its limits.

Not done

  • Browser writer. Nothing writes runs/<id>.vitals.json yet, so no live check shows a table until #2983's browser half lands. I think that is right for this issue, which says no browser.
  • Absent table. Juniper's minor point stands: the web page can't say why the table is absent for a normal check, whether no browser or not recorded. That needs a writer to record the reason, so I left it for the recording pull.

Try it: write [{"look":"home","entries":{}}] to runs/<run id>.vitals.json and open that check's run page.

🤖 Generated with Claude Code

Approved

Approved. The table uses only named design-system parts (.table[data-dense] in .table-scroll, so a 360px phone scrolls it sideways). It is a labelled, focusable region, uses row-header th scope, and is plain HTML that works with JavaScript off. Every value is escaped. The file read is capped at 1 MB and 20 looks, and is withheld along with the log. Page and terminal read the same parseLooks source, and tests cover the escaping, n/a-not-zero, the skipped line and the hidden-log case. It is report-only, so it cannot turn a check red. Two things I would do next: (1) when the browser half of #2983 lands, have it record why a look is absent, so the page can say so instead of silently showing nothing; the file has no writer yet, so no live check shows the table today. (2) Add a visible heading or caption (Web vitals) above the table; the aria-label only helps screen readers, and sighted users get five columns with no title. Grade is text only, which is fine for now, but a .pill state would read faster at a glance.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs