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
Check · Review · Land · Live done — Landed and live in production.
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
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.tsstarts the check right afterapp.servingis 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.tsrunswebspec()against the preview's own loopback port with a 30 s limit. A throw or timeout is stored as onewebspecfinding and the preview stays up. - Storage: findings are kept per deploy run in a new
preview_findingtable (migration20261020100000, accessors insrc/core/preview_findings.ts). The row goes when its run goes. - Pull page:
src/web/pages/pull-spec.tsdraws 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, andedge: trueones say the edge owns them. - Docs:
docs/WEBSPEC-PREVIEWS.mdsays the gate turns failing after a week of green, and where inruntime.tsto turn it on. - Tests:
test/preview-spec.test.tsdeploys a real preview whose HTML has no ETag. It asserts the stored findings name theetaggap, the run is stillok, 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 onewebspectimeout 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.
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.
- The pull page reads the wrong run (fix first).
previewFindingsis keyed by the deploy run's id (checkPreview(..., run.id, ...)in runtime.ts).pullChecksLinelooks it up withcheck.run.id, andcheck.runis the check run:src/checks/progress.tstestsrun.kind !== "check"on it. The ids differ, sopreviewFindingsreturns null and nothing shows. The test hides this because it only callsspecFindings()on the stored rows and never renders the pull page. Look the findings up from the pull's latest deploy run forpull-<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.
- Not in the terminal. The direction requires both doors, and
src/tty.tsdoes not show the findings beside the checks line. Add them there, gated like the page, and cover it with a test.
- README. It does not say where the findings appear or how to read them.
docs/WEBSPEC-PREVIEWS.mdalone does not meet the list.
- Design system.
spec-findingshas 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.smalland.mutedad hoc. Use.quiet-detailsor.listrows 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.
- Unawaited work.
void checkPreviewin runtime.ts can still be running when the runtime stops or the run is deleted. The swallowingcatch {}hides failures. Keep the catch but log the error.
- Dead path.
previewFindingsre-parses unvalidated JSON and casts it toFinding[]. Validate the shape before rendering, becausef.sectiongoes into anhref. A storedjavascript:URL would render as a link. Restrict it to https.
- 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 checksedgein this pull, or leave the count out of the line until they are.
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.
- 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. - 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.
- README: it now says where the findings appear and how to read them.
docs/WEBSPEC-PREVIEWS.mdis updated to match. - Design system: the ad-hoc markup is gone. The view is a
.quiet-detailsfold over a.listof.rows, with a "Website spec findings" entry indocs/DESIGN.md. I gave.quiet-details > summarya 44px minimum height, which also changes the other pages that use that component. - Unawaited work: the swallowed
catchnow 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. - Validation: stored findings are checked before rendering. Each must have string fields of at most 500 characters, and a
sectionthat starts withhttps://. Anything else is dropped, and a test stores ajavascript:section to confirm it. - 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 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.
Each preview deploy of a pull on this forge now runs
webspec()over the app'sapp.lookspaths 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
src/apps/runtime.tsstarts the check right afterapp.servingis published, for non-production environments only. It runs beside the tests, is not awaited, and adds nothing to the deploy.src/apps/preview_spec.tsrunswebspec()against the preview's own loopback port with a 30 s limit. A throw or timeout is stored as onewebspecfinding and the preview stays up.preview_findingtable (migration20261020100000, accessors insrc/core/preview_findings.ts). The row goes when its run goes.src/web/pages/pull-spec.tsdraws 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, andedge: trueones say the edge owns them.docs/WEBSPEC-PREVIEWS.mdsays the gate turns failing after a week of green, and where inruntime.tsto turn it on.test/preview-spec.test.tsdeploys a real preview whose HTML has no ETag. It asserts the stored findings name theetaggap, the run is stillok, 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 onewebspectimeout finding.What I left out
<details>can't sit inside its<p>.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.tsfor the test.Closes #2955