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

A check's run page shows install, typecheck and tests as step rows from the first paint #2564

Merged opened this

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

Merged

A check's run page now shows install, typecheck and tests as three rows, each with a state, from the first paint, with the log tail below. Durations are not there yet, and the step states are inferred from the log. `test/studio-runs.test.ts` passes, including the new tests, and `tsc --noEmit` is clean. I did not run the full check.

shipped production is serving it deploy aa26afa

opened this

A check's run page now shows install, typecheck and tests as three rows, each with a state, from the first paint, with the log tail below. Durations are not there yet, and the step states are inferred from the log. test/studio-runs.test.ts passes, including the new tests, and tsc --noEmit is clean. I did not run the full check.

  • Step states: checkSteps(run, lines) in src/checks/summary.ts is a pure function that returns the three rows. It reads the run's state and its log lines, and adds no new record. The log has no step boundaries, so it infers progress from two signs: the echoed $ command lines, and the test-shards: banner or ── shard headers that mean tests have begun. Typecheck counts as done once tests begin, because it runs first in the check command. The step the run reached is running while it is live, and failed or stopped if it ended there. Later steps stay waiting, and a passed run shows all three as passed.
  • Page: src/web/studio/run.ts renders an <ol id="check-steps"> only for kind === "check" runs. It always has three equal-height rows, so nothing shifts as states change. Small CSS in src/web/style/content.ts styles it. No other page changed.
  • Tests: a running check shows install:ok, typecheck:ok, tests:running. A finished red one shows tests:failed. A fresh queued one still has three waiting rows.

What I left out:

  • Durations: the log carries no per-step timestamps, so a duration would be a guess. I left it out rather than invent one. Showing it needs the runner (src/checks/service.ts) to write step start and end markers, which would be a separate change.
  • Job-placed checks: these echo every command up front, so install only reads as done once output follows the echoes.
  • Typecheck failures: a failure before the tests start is shown on the typecheck row. That is inferred, and it has no test of its own.

Try it by opening any check run at /<owner>/<repo>/runs/<id>.

🤖 Generated with Claude Code

Closes #2420

commented

A check's run page now shows install, typecheck and tests as three rows, each with a state, from the first paint, with the log tail below. Durations are not there yet, and the step states are inferred from the log. test/studio-runs.test.ts passes, including the new tests, and tsc --noEmit is clean. I did not run the full check.

  • Step states: checkSteps(run, lines) in src/checks/summary.ts is a pure function that returns the three rows. It reads the run's state and its log lines, and adds no new record. The log has no step boundaries, so it infers progress from two signs: the echoed $ command lines, and the test-shards: banner or ── shard headers that mean tests have begun. Typecheck counts as done once tests begin, because it runs first in the check command. The step the run reached is running while it is live, and failed or stopped if it ended there. Later steps stay waiting, and a passed run shows all three as passed.
  • Page: src/web/studio/run.ts renders an <ol id="check-steps"> only for kind === "check" runs. It always has three equal-height rows, so nothing shifts as states change. Small CSS in src/web/style/content.ts styles it. No other page changed.
  • Tests: a running check shows install:ok, typecheck:ok, tests:running. A finished red one shows tests:failed. A fresh queued one still has three waiting rows.

What I left out:

  • Durations: the log carries no per-step timestamps, so a duration would be a guess. I left it out rather than invent one. Showing it needs the runner (src/checks/service.ts) to write step start and end markers, which would be a separate change.
  • Job-placed checks: these echo every command up front, so install only reads as done once output follows the echoes.
  • Typecheck failures: a failure before the tests start is shown on the typecheck row. That is inferred, and it has no test of its own.

Try it by opening any check run at /<owner>/<repo>/runs/<id>.

🤖 Generated with Claude Code

Changes requested — before the latest push

The rows are server-rendered once and never change while the run is open. A person watching a live check sees install/typecheck/tests frozen at the first-paint state while the log below streams (src/web/client.ts only appends to #run-log and updates #run-live). The rows would say tests: waiting after the tests have started and passed, which is misleading. Fix first: update #check-steps live. Either have the existing stream path or client script re-derive the rows, or reload the fragment when a line matching the echo or shard banner arrives and when data-state changes. Add a test for that. Smaller points: (1) steps are inferred from lines, which is the 512 KB tail, so on a long truncated log the install echo is gone and the rows read wrong. Derive from the full log or mark them unknown. (2) The rows show for readers who cannot see the log (logHidden), so check this does not leak progress that the page otherwise hides. (3) A typecheck failure and a stopped or cancelled run have no test, and the job-placed branch has none either. (4) The tty door (how a check run is read in the terminal) has no step rows and the README is not updated.

commented

The check run page now shows install, typecheck and tests as three rows from the first paint, and the rows update while the run is open. test/check-steps.test.ts and test/studio-runs.test.ts pass and tsc --noEmit is clean; I did not run the full check.

I addressed the review points as follows:

  • Live update: the run page's script (studioRunJs, src/web/client.ts) refetches the page and swaps in #check-steps. It does this about 400 ms after a log line that can change a row (a $ command echo, the test-shards: banner or a ── shard header), and again when the run ends. The test covers only that the page ships this wiring. I did not exercise the browser behaviour.
  • Truncated log: checkSteps takes truncated. If the log was cut and the tests aren't visible in what remains, and the run didn't pass, all three rows read unknown. A passed run still reads passed, and a visible shard header still shows the tests.
  • Hidden log: the rows are not rendered when the viewer can't read the log, so progress is no longer visible to them. The test uses a public repo that isn't an app.
  • Tests:

- test/check-steps.test.ts is new and covers typecheck failure, install failure, no install command, a cancelled run, a passed run, a job-placed check and a truncated log. - test/studio-runs.test.ts covers the live-update script and the hidden-log case.

  • README: it now describes the rows.

I did not add rows to the tty. A check run isn't read as a run there. The terminal's pull screen shows a one-line check summary (checksText), which stays as it is. The issue said only the run page should change, so I noted that in the README instead.

Per-step durations are still not shown. The log has no timestamps, so any figure would be a guess. Showing them needs src/checks/service.ts to write step start and end markers, which I left for a separate change.

🤖 Generated with Claude Code

Changes requested — before the latest push

The step rows are a good idea and the logic is well tested. Two things block it.

  1. Both doors (fix first): the page gets install/typecheck/tests rows and src/tty.ts gets nothing. The README even says the terminal is unchanged. Anything a person can see on the page they must be able to see in the terminal. Add the same three rows to the terminal view of a check run, read through checkSteps with the same hidden-log gate, and add a test.
  1. Phone: the rows are a fixed 3-column grid. At 360px each cell is about 100px wide with 12px padding, and a name beside a state ("typecheck" + "waiting") will wrap or overflow. Stack the rows below ~480px, or put the state under the name.

Smaller: the live refresh swaps #check-steps with no announcement for screen readers. Give the list role="status" or aria-live="polite". The refetch path is only checked by a string match in a test.

commented

The terminal's pull screen now shows the same three step rows as the run page. I changed the phone layout, the screen-reader announcement and the install match too. I ran test/check-steps.test.ts, test/studio-runs.test.ts and test/check-progress.test.ts; they pass and tsc --noEmit is clean. I did not run the full check.

  • Terminal: the pull screen's checks line now ends with install passed · typecheck passed · tests running. It comes from stepsLine (src/checks/progress.ts), which reads the log through the same checkSteps the page uses, so the two can't disagree. It adds nothing for a deploy or when mayReadRunLog says the viewer can't read the log. The terminal has no per-run screen (a run opens as the web page), so the checks line is the only place a check shows there. The line truncates on a narrow terminal. The tests are in test/check-steps.test.ts.
  • Phone: under 480px the rows stack into one column.
  • Screen readers: the list has role="status" and aria-live="polite".
  • Install match: it is now anchored to bun|npm|pnpm|yarn [flags] install|ci, so bun scripts/install-hooks.ts no longer counts as the install. A test covers it.
  • README: it now describes the terminal line.

I did not add a test that drives the live refetch in a browser. The sandbox has none, so that path is still only checked by the string match in test/studio-runs.test.ts. The step states are still inferred from the log.

I left per-step durations out again. The log has no timestamps, so any figure would be a guess.

🤖 Generated with Claude Code

Approved

Three fixed rows from the first paint (no shift), stacked to one column under 480px, log-hidden readers get none, and the terminal reads the same checkSteps and withholds it on the same gate. Tests cover each state, truncation, job placement, the hidden log and the tty line. I read the diff and did not exercise the preview. Two things I would do next: (1) the live refresh refetches the whole run page on every shard header; add a small rows-only endpoint, or refresh on a timer, so a 100-shard run does not render the page 100 times. (2) Swapping the whole role=status list makes screen readers re-announce all three rows; update only the changed row's text, and give the unknown and stopped states a visible style alongside ok, failed and running.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs