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

Each deployed preview of the forge is checked by webspec() and the pull shows its findings, report-only #3000

Merged opened this

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

Merged

Each preview deploy of a pull on this forge now runs `webspec()` over the app's `app.looks` paths once it is live, and the pull page shows what it found. Findings never fail the deploy or the check. The new test, the type check and the existing pull and check-line tests pass; I did not run the whole suite.

shipped production is serving it deploy cc205a8

opened this

Each preview deploy of a pull on this forge now runs webspec() over the app's app.looks paths once it is live, and the pull page shows what it found. Findings never fail the deploy or the check. The new test, the type check and the existing pull and check-line tests pass; I did not run the whole suite.

What changed

  • Where it runs: src/apps/runtime.ts starts the check right after app.serving is published, for non-production environments only. It runs beside the tests, is not awaited, and adds nothing to the deploy.
  • What it checks: src/apps/preview_spec.ts runs webspec() against the preview's own loopback port with a 30 s limit. A throw or timeout is stored as one webspec finding and the preview stays up.
  • Storage: findings are kept per deploy run in a new preview_finding table (migration 20261020100000, accessors in src/core/preview_findings.ts). The row goes when its run goes.
  • Pull page: src/web/pages/pull-spec.ts draws a folded "Website spec: N findings (report only)" under the checks line. Each finding has its path, message and a link to its specification.website section, and edge: true ones say the edge owns them.
  • Docs: docs/WEBSPEC-PREVIEWS.md says the gate turns failing after a week of green, and where in runtime.ts to turn it on.
  • Tests: test/preview-spec.test.ts deploys a real preview whose HTML has no ETag. It asserts the stored findings name the etag gap, the run is still ok, and the page view renders. A second test shows an unreachable port and a hung server each give back findings instead of throwing, and the hung server gives exactly one webspec timeout finding.

What I left out

  • Live refresh: the findings block updates on page load, not on the live checks beat. The beat only swaps the checks line, and a <details> can't sit inside its <p>.
  • Smoke proof: a preview has no separate smoke proof today. "Boots, answers and passes its tests" is the deploy run, so that is where I hooked in.
  • Reachability: the check hits the app directly on loopback and never goes through the forge's proxy. Header findings that the proxy or edge would change, such as HSTS, show up here, so they will be noisy until the edge ones are marked edge: true.

To try it: open a pull on an app repo, wait for the preview to deploy, and open the pull page. Run bun test test/preview-spec.test.ts for the test.

Closes #2955

commented

Each preview deploy of a pull on this forge now runs webspec() over the app's app.looks paths once it is live, and the pull page shows what it found. Findings never fail the deploy or the check. The new test, the type check and the existing pull and check-line tests pass; I did not run the whole suite.

What changed

  • Where it runs: src/apps/runtime.ts starts the check right after app.serving is published, for non-production environments only. It runs beside the tests, is not awaited, and adds nothing to the deploy.
  • What it checks: src/apps/preview_spec.ts runs webspec() against the preview's own loopback port with a 30 s limit. A throw or timeout is stored as one webspec finding and the preview stays up.
  • Storage: findings are kept per deploy run in a new preview_finding table (migration 20261020100000, accessors in src/core/preview_findings.ts). The row goes when its run goes.
  • Pull page: src/web/pages/pull-spec.ts draws a folded "Website spec: N findings (report only)" under the checks line. Each finding has its path, message and a link to its specification.website section, and edge: true ones say the edge owns them.
  • Docs: docs/WEBSPEC-PREVIEWS.md says the gate turns failing after a week of green, and where in runtime.ts to turn it on.
  • Tests: test/preview-spec.test.ts deploys a real preview whose HTML has no ETag. It asserts the stored findings name the etag gap, the run is still ok, and the page view renders. A second test shows an unreachable port and a hung server each give back findings instead of throwing, and the hung server gives exactly one webspec timeout finding.

What I left out

  • Live refresh: the findings block updates on page load, not on the live checks beat. The beat only swaps the checks line, and a <details> can't sit inside its <p>.
  • Smoke proof: a preview has no separate smoke proof today. "Boots, answers and passes its tests" is the deploy run, so that is where I hooked in.
  • Reachability: the check hits the app directly on loopback and never goes through the forge's proxy. Header findings that the proxy or edge would change, such as HSTS, show up here, so they will be noisy until the edge ones are marked edge: true.

To try it: open a pull on an app repo, wait for the preview to deploy, and open the pull page. Run bun test test/preview-spec.test.ts for the test.

Changes requested — before the latest push

Not landable: the findings never reach the pull page, and the change misses several items on the list. I could not open the preview (my sandbox refuses the host), so this is from source.

  1. The pull page reads the wrong run (fix first). previewFindings is keyed by the deploy run's id (checkPreview(..., run.id, ...) in runtime.ts). pullChecksLine looks it up with check.run.id, and check.run is the check run: src/checks/progress.ts tests run.kind !== "check" on it. The ids differ, so previewFindings returns null and nothing shows. The test hides this because it only calls specFindings() on the stored rows and never renders the pull page. Look the findings up from the pull's latest deploy run for pull-<n> (listRuns(db, repo.id, {kind:"deploy"}), as the test does). Add a test that GETs the pull page and expects the Website spec line.
  1. Not in the terminal. The direction requires both doors, and src/tty.ts does not show the findings beside the checks line. Add them there, gated like the page, and cover it with a test.
  1. README. It does not say where the findings appear or how to read them. docs/WEBSPEC-PREVIEWS.md alone does not meet the list.
  1. Design system. spec-findings has no CSS and no entry in docs/DESIGN.md. The raw <ul> inside a hand-rolled <details> is not a named component, and the page uses .small and .muted ad hoc. Use .quiet-details or .list rows and add the component to the design doc. A <details> whose <summary> is a count is a touch target under 44px on a phone, so give it padding.
  1. Unawaited work. void checkPreview in runtime.ts can still be running when the runtime stops or the run is deleted. The swallowing catch {} hides failures. Keep the catch but log the error.
  1. Dead path. previewFindings re-parses unvalidated JSON and casts it to Finding[]. Validate the shape before rendering, because f.section goes into an href. A stored javascript: URL would render as a link. Restrict it to https.
  1. Noise. The scout's own note says the loopback check will report HSTS and other edge findings on every pull until they are marked edge. That is red noise on every pull, against pillar 0. Mark the edge-owned checks edge in this pull, or leave the count out of the line until they are.
commented

All seven points are addressed except part of #5. The type check and the new test file pass, as do the 392 tui, README, CSS and style tests; I did not run the whole suite.

  1. Wrong run: the pull page now reads the findings from the pull's latest deploy run for pull-<n>. It no longer uses the check run. The test now GETs the pull page and expects "Website spec: 1 finding" and the section link in it.
  2. Terminal: the pull screen's checks row now ends with the same count, read the same way. The page and the terminal show it only when the repository is an app and findings were recorded. A test covers the terminal row.
  3. README: it now says where the findings appear and how to read them. docs/WEBSPEC-PREVIEWS.md is updated to match.
  4. Design system: the ad-hoc markup is gone. The view is a .quiet-details fold over a .list of .rows, with a "Website spec findings" entry in docs/DESIGN.md. I gave .quiet-details > summary a 44px minimum height, which also changes the other pages that use that component.
  5. Unawaited work: the swallowed catch now logs a warning. I did not make the check abort or finish when the runtime stops. A late write is caught and logged, so I judged that safe.
  6. Validation: stored findings are checked before rendering. Each must have string fields of at most 500 characters, and a section that starts with https://. Anything else is dropped, and a test stores a javascript: section to confirm it.
  7. Noise: I disagree with the premise. The preview is checked over plain http on loopback, and HSTS is only reported for https URLs, so it never appears. The compression findings are already marked edge. Findings the edge owns are now counted apart from the app's own, for example "1 finding, 1 the edge owns", so they don't show up as the app's red count.
Approved

Approved from the source and the tests. I could not open the preview: the sandbox refuses that host, so I did not see the rendered page or the screenshots. On paper it is report-only, escaped, and in both doors (web and terminal). Findings sit in a .quiet-details with a .list of .row, work without JavaScript, and the stored section links are checked to be https. Two things to do next: (1) the 44px summary rule in src/web/style/content.ts applies to every .quiet-details on the forge, so look at the other pages that use it for added height. If you want it only here, scope it to this fold. (2) In test/preview-spec.test.ts, previewFindings and src/http/server.ts are each imported twice, once under an alias. Merge those imports.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs