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

No served page polls: a test fails on a timer or meta refresh that reloads or refetches a page without the live stream #10929

Merged opened this

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

Merged

`test/no-polling.test.ts` now renders the owner's main pages and fails if any of them polls, naming the page in the failure. I ran that file, `bun x tsc --noEmit` and `bun scripts/gate.ts`; all pass. I did not run the full suite.

shipped production is serving it deploy daaa1f6

opened this

test/no-polling.test.ts now renders the owner's main pages and fails if any of them polls, naming the page in the failure. I ran that file, bun x tsc --noEmit and bun scripts/gate.ts; all pass. I did not run the full suite.

What changed

  • The test signs in as the owner and fetches /, /system, /flow, /boards, /explore, /forges and /changelog.
  • For each page it loads the markup plus every script file the page names, and expects a 200.
  • It fails, as <path>: <reason>, on any of these:

- a <meta http-equiv=refresh>; - data-live-every; - a setInterval that fetches or reloads on a page with no data-live or /events stream.

  • PAGE_EXEMPT is the place for a named, justified exemption, such as a legitimate countdown clock. It is empty because no page needed one.

What I found and left alone

  • The source-level guard from #10817 already existed. I kept it and added the rendered check next to it, so the new test covers the "name the page" part of the issue.
  • /system and /flow are covered by the rendered test, and neither polls today. /system takes its numbers from src/system/live.ts, a server-side timer feeding the stream. Nothing in the rendered pages needed an exemption.
  • No page polled, so nothing was moved to the stream. That also means no before/after screenshots, since nothing visible changed.
  • /crew returned 404 for the owner, so I left it off the list instead of letting the test pass on an error page.

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

Closes #10921

commented

test/no-polling.test.ts now renders the owner's main pages and fails if any of them polls, naming the page in the failure. I ran that file, bun x tsc --noEmit and bun scripts/gate.ts; all pass. I did not run the full suite.

What changed

  • The test signs in as the owner and fetches /, /system, /flow, /boards, /explore, /forges and /changelog.
  • For each page it loads the markup plus every script file the page names, and expects a 200.
  • It fails, as <path>: <reason>, on any of these:

- a <meta http-equiv=refresh>; - data-live-every; - a setInterval that fetches or reloads on a page with no data-live or /events stream.

  • PAGE_EXEMPT is the place for a named, justified exemption, such as a legitimate countdown clock. It is empty because no page needed one.

What I found and left alone

  • The source-level guard from #10817 already existed. I kept it and added the rendered check next to it, so the new test covers the "name the page" part of the issue.
  • /system and /flow are covered by the rendered test, and neither polls today. /system takes its numbers from src/system/live.ts, a server-side timer feeding the stream. Nothing in the rendered pages needed an exemption.
  • No page polled, so nothing was moved to the stream. That also means no before/after screenshots, since nothing visible changed.
  • /crew returned 404 for the owner, so I left it off the list instead of letting the test pass on an error page.

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

Changes requested — before the latest push

The rendered-page guard is a good idea, but as written it cannot be trusted to fail, and it leaves two of the issue boxes unproven. Fixes, all in one round:

  1. Prove the detector fires: add a unit-level test that feeds the page check a fixture with a meta refresh, a setInterval+fetch with no stream, and data-live-every, and expects each to be named. Extract the per-page check into a pure function so the fixture and the real pages share it (the source guard above already does this with offenders()).
  2. Box 2: assert /system and /flow actually carry a live stream (data-live or /events) and no timer at all. Today they pass by absence only.
  3. The stream exemption is page-wide: any data-live anywhere lets a fetching setInterval through. Scope it to the script that owns the timer, or drop the exemption for these pages.
  4. /crew is dropped because it 404s for the owner; say in the test why, or fix the owner route, rather than silently narrowing the list.
  5. Tidy: remove the double blank line after OWNER_PAGES.

Box 3 is conditional and no page polled, so it is vacuous; I leave it open until the above shows the check is real.

commented

The rendered-page polling check is now a pure function with fixture tests, and /system is asserted to stream. /flow has no stream, so I made it the one named exemption instead of building one. Tests, tsc and the gate pass; I didn't run the full suite.

  1. Detector proven. pagePolls(path, {html, scripts}) is the one check the fixtures and the real pages share. Fixtures name each case:

- a meta refresh; - data-live-every; - a setInterval with fetch or reload and no stream; - a stream in a different script, which doesn't excuse the timer; - a clock that only redraws text, which passes.

  1. Box 2, /system. It must carry a live stream and no setInterval in any of its scripts. It does carry one: new EventSource('/system/stream') in its script.
  2. Box 2, /flow: stream not built. /flow has no stream at all. It is a form of valves that only the owner's own submit changes. It is held to no timer, and the test names it as the exemption from the stream requirement.

- Why not a stream: building one would be a feature the issue doesn't ask for, and a pushed patch could move a valve handle mid-drag. Say if you'd rather have it streamed.

  1. Exemption scoped. A fetching timer is excused only by a stream in the same script, not anywhere on the page.
  2. /crew. It 404s for the owner, so the test now says why above OWNER_PAGES. I did not fix the route.
  3. Tidy. The extra blank line is gone.
  4. Added. The real-page test also asserts the scripts were actually read, so a broken loader can't pass it silently.

Box 3 stays vacuous: no page polled, so nothing moved to the stream and there are no screenshots.

Changes requested — before the latest push

The detector is now a pure function with fixtures and /flow is a named exemption. Three gaps still let a real poll through, so the guard can pass while a page polls. Fix all three in one round.

  1. Only <script src="/assets/..."> is read (render). An inline <script> holding a setInterval(fetch…) is never scanned. A script tag with another attribute first (type="module" src=, defer src=) or a non-/assets src is skipped too. Scan inline script bodies, excluding application/json and ld+json blocks, and match src in any attribute position. Add a fixture for each.
  2. STREAM = /EventSource\s*\(|data-live\b/ matches data-live-every, because \b falls between e and -. A page with data-live-every therefore counts as streaming and excuses its timer. Use data-live(?![-\w]) and add a fixture.
  3. scripts.length > 0 is asserted per page, but the check never asserts that /system's EventSource came from a script actually fetched. It would also pass if /system carried the string only in markup. Say which you mean, or require the stream in the script for /system.

Box 3 is vacuous: no page polled, so nothing moved and there are no screenshots. I leave it open.

Tests are the only change, so no docs or TUI are needed. No dependencies were added.

commented

test/no-polling.test.ts renders the owner's main pages and fails, naming the page, on a meta refresh, on data-live-every, or on a setInterval that fetches or reloads when that same script opens no live stream. The new tests, bun x tsc --noEmit and bun scripts/gate.ts pass; the full suite was not run here. The change is test-only, with no new dependencies.

How the check works:

  • Checking a page: pagePolls(path, {html, scripts}) is a pure function. The real pages and the fixtures go through the same code, and each kind of poll has a fixture that expects it to be named.
  • Finding scripts: scriptTags(html) finds every script a page runs:

- a src in any attribute position (data-src does not count); - inline code bodies; - not application/json or ld+json blocks, which are data.

Same-origin scripts are fetched and read. A script from another origin can't be read here, so the test fails and names it instead of skipping it.

  • What counts as a stream: EventSource( or data-live(?![-\w]), so data-live-every no longer passes as a stream. A stream only excuses a timer in the same script; one elsewhere on the page doesn't.
  • /system: it must open its stream in a script and carry no timer. It does: new EventSource('/system/stream'). A stream named only in the markup fails, and a fixture covers that.
  • /flow: it must carry no timer. It is the one named exemption from needing a stream: it is a form of valves that only the owner's own submit changes, and a pushed update could move a valve handle in the middle of a drag.
  • /crew: left off the list, with the reason in the test: it answers 404 for the owner.

No page polled, so nothing moved to the stream and there are no before/after screenshots.

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

🤖 Generated with Claude Code

Approved

All five earlier asks are met. (1) The pure pagePolls/scriptTags functions have fixtures for each kind of poll. (2) /system must open a stream in a script and carry no timer, and /flow carries no timer. /flow is the one named exemption from needing a stream, with its reason stated. (3) A stream now excuses only a timer in the same script, and data-live-every no longer counts as a stream. (4) The reason /crew is left off is stated above OWNER_PAGES. (5) The extra blank line is gone. The test is test-only, adds no dependency, and its real-page check also fails if no scripts were read. Next, non-blocking: 1) if /flow ever gains a pushed state, move it from the exemption into STREAM_PAGES; 2) fix the owner /crew 404 or open an issue so the page can join OWNER_PAGES.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs