Skip to content

Redesign the report as a document, and add a demo walkthrough - #1

Merged
rlaope merged 6 commits into
mainfrom
design/report-and-readme-overhaul
Sep 23, 2026
Merged

rlaope merged 6 commits into
mainfrom
design/report-and-readme-overhaul

Conversation

@rlaope

@rlaope rlaope commented Sep 23, 2026

Copy link
Copy Markdown
Owner

What this is

A design overhaul of the two surfaces a reader actually looks at — the HTML report and the README —
plus a self-contained walkthrough built for recording a demo.

Five commits, one logical change each:

Commit What changed
da7f660 The charts move to the report's own design language: semantic palette tokens instead of hardcoded hex, line-style legends ("never colour alone"), the overconfidence area drawn as a triangle below the diagonal, clipped component lines, per-figure captions
7d0bae2 The report becomes a document: new stylesheet, section markup, <title>/<desc> on every figure, dark mode as a palette swap with a single block of custom properties, print layout
3891da8 README rebuilt around what the tool shows, example report and documentation screenshots regenerated (every quoted number re-run, tests/test_readme_example.py green)
2f2eb43 examples/demo-dashboard.html: a five-step walkthrough — verdict, live threshold slider, per-question curves, segments, drift gate — one file, arrow keys, narration bar. Generated by examples/make-demo-dashboard.py, guarded by tests/test_demo_dashboard.py (byte-equality with a fresh build, self-contained, no machine paths)
8ab4db5 Narrow screens: a table of currency amounts has a minimum width wider than a phone and was dragging the whole page sideways (538px document in a 390px viewport on main). The table now takes the overflow itself, and the impact table fits a 390px screen with nothing hidden

Verification

  • uv run pytest — 536 passed; ruff format/ruff check/mypy jeval clean
  • Rendered and measured (not eyeballed) at 1440 / 834 / 430 / 390 px, light + dark + print:
    document width equals viewport width at every size, no hidden columns in the impact table
  • Print: 7 A4 pages from Chrome's own PDF (/Count 7); pages 2–7 were not inspected pixel by pixel

Known limits

  • Only Chrome was measured — no Safari/Firefox, no real device, no screen reader
  • The collapsible per-question detail tables still need a horizontal swipe at 390px (26–127px). They
    are closed by default, and choosing which columns to drop on a phone is a product decision
  • The dashboard is not a product surface: jeval ships exactly one report, and a dashboard is
    explicitly out of scope in CLAUDE.md. It lives in examples/ as a generated artifact

The charts painted fixed hex greys and drew every legend entry as a square, whatever mark it was
explaining; annotations sat on gridlines; the escalation line ran out of the plot and over the axis
above it; and the segment chart's right-hand column printed "too few sam" in every report.

- Okabe-Ito stays for series colour that carries meaning. Ink, muted, grid, diagonal and shade
  become constants the stylesheet re-maps for dark mode, so a chart grey can no longer be
  light-theme-only.
- Legends draw the mark they explain: a line sample for a line, a dotted sample for points, a
  dashed sample for a dashed rule, a band for the flat region.
- Annotations carry a halo instead of a background box, and the density strip is one shade of the
  page's own ink in either theme.
- The reliability chart's "below the diagonal" region is the triangle it always was, not a
  full-width band, and the marker names the line it draws.
- Component lines are clipped to the plot, and the "accept + escalate" line is omitted when it is
  the total line again.
- Segments gain a track behind each bar, a bar scale that reads at a glance, and a right-anchored
  note column; a segment below the sample floor prints its reason instead of a stray "n/a".
- The reliability chart's bottom budget is explicit: ticks, axis title, strip, then legend, so the
  axis title and the legend can no longer land on the same line.
The report looked assembled rather than drawn: shadowed cards, a run-on provenance line, headings
the same size as body text, a tab row that read as text, and a live readout that disagreed with the
table above it by half a point and three decimal places.

- A real type scale, more air, and two semantic colours: the accent for the line the report
  recommends, the alert for the line in use. Light is a paper palette, dark a slate one, and print
  has its own; all three come from the same custom properties.
- The header states its provenance one line at a time, because a `·`-separated run-on of paths and
  seed notes is read as boilerplate and skipped.
- Sections gain headings, block subtitles, figure captions that name each encoding, a quiet
  disclosure for the data tables, and a tinted recommended column in the impact table.
- The dark block's grey re-map is generated from the chart layer's constants, with a test that
  fails when a new grey is added to one and not the other.
- The slider's readout uses the report's own formatting (whole percent, currency, grouped amounts),
  the monthly-cases field recomputes the monthly figures when it is typed into, and a question's
  line is labelled with the action it belongs to so two thresholds can no longer look like a
  contradiction.
The README opened with a 2x2 table of screenshots whose text rendered at half size, put the demo
command 200 lines below the install, and buried what the tool answers under badge soup.

- The screenshots are a single column of full-width crops, each with a caption that says what to
  look at; they are taken from the example report this repository commits, and every figure quoted
  in the README still checks against it.
- The sections now run install, quick start, try it on your own log, hand it to an agent,
  instrument a service, what you get, what the numbers mean.
- Regenerated docs/*.png and examples/report-example.html from the redesigned report, and updated
  the one size claim the README makes about it.
- Recorded the design work in CHANGELOG.md, and ignored in-repo git worktrees.
The report is a document you read; a recording needs something you walk through. This is that, in one
file, opened locally, driven with the arrow keys, with a narration line per step for the voice over:
the verdict, the cost curve with a live threshold, four questions measured separately, the segments,
and the drift gate failing on a degraded candidate model.

It is not a product surface -- the product writes one report, and a dashboard is explicitly out of
scope -- so it lives in `examples/` as a presentation of the same analysis:

- `examples/make-demo-dashboard.py` calls the library the report calls (`evaluate`, `sweep_actions`,
  `segment_views`, `drift_view`, `build_verdict`, `build_impact`) and draws with the report's own
  chart modules and stylesheet, so nothing on screen is typed in, and the two artefacts cannot drift
  apart in look or in number.
- The production half of the log is `jeval demo`'s own, at the same seed, so the first screen can be
  checked against `examples/report-example.html`. The candidate is the same four questions from a
  deliberately overconfident model, which is what makes the last step a real failure.
- The live slider moves a marker along the curve and rewrites five figures, using the report's own
  formatting, and prints "n/a" where the auto-branch accuracy is undefined rather than a NaN.
- `tests/test_demo_dashboard.py` holds it to the standards of a committed artefact: one file, nothing
  from the machine that built it, every control the walkthrough touches present, the printed numbers
  equal to a fresh computation -- and, because the build is deterministic, byte-equality with the
  committed file.
At 390px the report scrolled sideways: 538px of document in a 390px viewport, 737 elements past the
right edge, starting with the impact table. A table of currency amounts has a minimum width wider
than a phone -- "KRW 38,524,590.16" cannot wrap -- so at width:100% the table wins and takes the
header, the paragraphs and every chart with it.

The table now takes the overflow itself (display:block, max-width:100%, overflow-x:auto), and its
type and padding come down at 640px so the impact table fits a 390px screen with nothing hidden.
A narrow report now reads as a narrow report with one scrollable table, not as a wide page seen
through a letterbox.

The collapsible per-question detail tables are wider than the impact table and still need a swipe
at 390px (26-127px). They are closed by default; the judgement was that a swipe inside one table
beats a page that drifts sideways, and changing which columns they show is a product decision, not
a layout fix.
The five-step walkthrough was a deck pretending to be a dashboard: a step rail to navigate, a
narration bar to read from, a slider to drag, and every element set at the same 15px so nothing told
the reader where to look. In dark mode it was worse than bland -- the greys were the same grey, the
one semantic accent was a hairline, and the punchline (which threshold to move to) sat third in a row
of three identically-styled statistics.

This replaces it with one screen that makes one claim and then shows it:

* "The line belongs at 0.75, not 0.60." in 29px, with the answer in the accent colour, repeated in
  the headline, the answer card, the table column and the chart line, so a reader who reads only the
  blue text still gets the whole argument.
* The chart draws both thresholds and names them where they stand -- "in use 0.60" dashed amber,
  "where it belongs 0.75" solid accent -- and puts the confidence distribution underneath, so the
  line is something you can see rather than a number you have to trust.
* The 95% interval on the recommendation is drawn as a band as well as printed: a point estimate
  quoted over a wide interval is the easiest thing on the page to over-read.
* The small print now says what "cost" counts, from the cost matrix itself, and carries the drift
  caveat: a threshold measured on a model that is drifting is a number with an expiry date.

Semantic colours are separated per theme instead of shared greys (accent, in-use, alert, plus a
tinted answer panel), the type runs 29 / 46 / 15 / 13.5 / 12.5 instead of everything at one size, and
there is no JavaScript left: one file, one screen, no scrolling on a 1440x900 window (measured: 900px
of document in a 900px viewport, 945 in 945 at 1512x945).

The guard test now defends the argument rather than the stepper: one h1, both thresholds drawn and
named, the answer in the largest type, no walkthrough controls, plus the byte-for-byte rebuild and
"nothing from this machine" checks it already had.
@rlaope
rlaope merged commit ba3c720 into main Sep 23, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant