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

A test proves no forge page reloads or refetches on a timer, beyond a named allowlist #2880

Merged opened this

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

Merged

The pull page's running check row no longer polls, and a new test fails if any other timer reload appears in `src/web`. The four affected test files pass: the new one, the two pull check-line tests and `check-progress`. The sandbox wouldn't let `tsc` write its temp files, so I didn't get a type check, and I didn't run the full suite.

shipped production is serving it deploy 68efd9d

opened this

The pull page's running check row no longer polls, and a new test fails if any other timer reload appears in src/web. The four affected test files pass: the new one, the two pull check-line tests and check-progress. The sandbox wouldn't let tsc write its temp files, so I didn't get a type check, and I didn't run the full suite.

  • src/web/pages/pull.ts: the check row now uses data-live="run.,check." and the data-live-every="5" beat is gone. The check.* event is already used by the boards view, so I didn't add one.
  • test/no-polling.test.ts: scans src/web/*/.ts for data-live-every, refresh: {, http-equiv="refresh" and setInterval(, and fails on any file not in the allowlist.
  • Allowlist: each entry names its reason in the file.

- sw.ts: the restart page. - migrate.ts: migrate progress. - browse.ts: mirror progress. - backups.ts: backups moving. - client.ts: the data-live-every mechanism itself and clocks that only redraw text. - layout.ts: defines the refresh option. - fleet-graph.ts: until it has an event.

  • Fake-file case: the test feeds it fake files with each of the four patterns and expects a failure for each. A clean file and an allowlisted file pass.
  • test/pull-checks-line-route.test.ts: I changed its old assertions that expected data-live-every="5", because the old behaviour is what this issue removes. The new ones expect run.,check. and no beat on the row.

Left out: the test matches text, not intent. A setInterval in an allowlisted file such as client.ts isn't checked, and I didn't try to tell a clock from a fetch.

To try it: bun test test/no-polling.test.ts.

🤖 Generated with Claude Code

Closes #2874

commented

The pull page's running check row no longer polls, and a new test fails if any other timer reload appears in src/web. The four affected test files pass: the new one, the two pull check-line tests and check-progress. The sandbox wouldn't let tsc write its temp files, so I didn't get a type check, and I didn't run the full suite.

  • src/web/pages/pull.ts: the check row now uses data-live="run.,check." and the data-live-every="5" beat is gone. The check.* event is already used by the boards view, so I didn't add one.
  • test/no-polling.test.ts: scans src/web/*/.ts for data-live-every, refresh: {, http-equiv="refresh" and setInterval(, and fails on any file not in the allowlist.
  • Allowlist: each entry names its reason in the file.

- sw.ts: the restart page. - migrate.ts: migrate progress. - browse.ts: mirror progress. - backups.ts: backups moving. - client.ts: the data-live-every mechanism itself and clocks that only redraw text. - layout.ts: defines the refresh option. - fleet-graph.ts: until it has an event.

  • Fake-file case: the test feeds it fake files with each of the four patterns and expects a failure for each. A clean file and an allowlisted file pass.
  • test/pull-checks-line-route.test.ts: I changed its old assertions that expected data-live-every="5", because the old behaviour is what this issue removes. The new ones expect run.,check. and no beat on the row.

Left out: the test matches text, not intent. A setInterval in an allowlisted file such as client.ts isn't checked, and I didn't try to tell a clock from a fetch.

To try it: bun test test/no-polling.test.ts.

🤖 Generated with Claude Code

Changes requested — before the latest push

The guard test is good, but the pull.ts change makes the pull page's running check row go stale. Fix this first: give the row a real event to hear, or allowlist it.

The removed comment said it: a run emits no event when a shard finishes. The TUI pull screen says the same (src/tui/screens/pull.ts:786) and still ticks for that reason. I found no producer of a check. event in src (the only users are the data-live strings in boards-view.ts and this change), so subscribing to check. adds nothing. The row will now show "Check running" with frozen counts until the run ends. The pull's own test asserts only the attribute, not that anything fires, so it can't catch this.

Two ways out:

  1. Emit a per-shard progress event from the check service (a check.progress kind, say), test that it fires, and then drop the beat.
  2. Keep the beat for running and queued checks, and add src/web/pages/pull.ts to ALLOWED with the reason "a shard finishing emits no event". That is honest and matches what the issue allows.

Smaller points:

  • The test's third case asserts check.* appears in pull.ts, which only proves the string exists, not that the event is ever sent.
  • ALLOWED is exported from a test file; nothing imports it.
  • setInterval in client.ts and fleet-graph.ts is waved through wholesale, and the pull says so. Scope each entry to the pattern it permits.

I did not open the preview or the screenshots, since the problem is visible in the source.

commented

I took option 2 from the review. The running check row on the pull page keeps its 5-second beat, so the counts no longer freeze. src/web/pages/pull.ts is back to what main has, and I restored the route test's original assertions. bun test test/no-polling.test.ts passes, and the route test passed against the restored pull.ts before I pushed. I didn't re-run the other pull check-line tests or the type check, and the sandbox couldn't run tsc earlier.

  • Allowlist: pull.ts is allowed data-live-every, with the reason "a shard finishing emits no event, so a running check row beats".
  • Scoping: each entry in ALLOWED now lists only the patterns it permits. client.ts may carry data-live-every and setInterval. fleet-graph.ts may carry setInterval. sw.ts may carry http-equiv="refresh". Anything else in those files turns the test red. A new fake-file case checks this.
  • Stale entries: a new test fails if an allowlisted file no longer uses the pattern it is allowed. Entries therefore can't outlive the timer they excuse.
  • Dead subscription: the case that only checked the string check.* is gone. I dropped export from ALLOWED.

I didn't take option 1, a check.progress event from the check service. That is a change to the check service, outside what this issue lists. When one exists, pull.ts can move to data-live="check.progress" and lose its allowlist entry, with a test that the event fires.

The issue's first checkbox, that the check row uses data-live="check." and no beat, is therefore not done. It can't be until something emits check..

Approved

Test-only change, no UI touched, so nothing for a phone or keyboard to regress. The allowlist matches what src/web actually carries today: backups, browse, migrate, pull, fleet-graph, sw and layout. Allowances are scoped per pattern, stale entries fail, and the fake-file cases would fail without the scan. Two things I would do next: (1) emit a check.progress event from the check service so pull.ts can drop data-live-every and its allowlist entry, since the running check row still beats every 5s; (2) the match is text-only, so a setInterval in client.ts is never inspected. A narrow check that client.ts intervals do not call fetch or location.reload would close that gap. I read the diff and grepped src/web; I did not run the test or check the preview.

commented

The full gate on land was red and named no test; the pull is open again.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs