Public: anyone can read this; the crew can change it.
The app template ships the website specification by default #2958
Check · Review · Land · Live done — Landed and live in production.
The check failure on the earlier push is fixed: the failing test was a stale harness, not the new scaffold. I pushed the fix. I couldn't run the scaffold's strict-typecheck test here: it failed with `bun is unable to write files to tempdir: ReadOnlyFileSystem`, a sandbox limit. I did not run the whole suite. The forge's check will run it on this push.
shipped production is serving it deploy 2fa0c24
The check failure on the earlier push is fixed: the failing test was a stale harness, not the new scaffold. I pushed the fix. I couldn't run the scaffold's strict-typecheck test here: it failed with bun is unable to write files to tempdir: ReadOnlyFileSystem, a sandbox limit. I did not run the whole suite. The forge's check will run it on this push.
What changed
- The template change itself is the earlier commit: it adds the canonical link, icons, manifest, link card, skip link, forced-colors styles, sitemap,
llms.txt, styled 404/500 pages, gzip, HSTS with subdomains, WebSite JSON-LD and the heading check. Its tests are in a newsrc/site.test.tsthat the scaffold writes into every generated app. - Three test harnesses (
test/apps-busy.test.ts,test/apps-ws.test.ts,test/apps-identity.test.ts) replace the scaffold'ssrc/server.tswith a toy server. They already deleted the scaffold's other suites that boot its real server, but notsite.test.ts. That suite failed against the toy server, so the busy-app deploy failed ("apps that are busy > (unnamed)"). - Each harness now drops
src/site.test.tstoo.
Tests
- I ran
test/apps-busy.test.ts,test/apps-ws.test.ts,test/apps-identity.test.ts,test/scaffold.test.tsandtest/apps.test.ts. 42 passed. The one failure was the typecheck test.
Left out
- Nothing new from the feature scope.
Try it
- Create a new app from the template and open
/,/sitemap.xml,/llms.txtand a missing path. - Run
bun testin the generated app.
🤖 Generated with Claude Code
The app template now ships the website specification by default: canonical link, icons, manifest, link card, skip link, forced-colors styles, sitemap, llms.txt, styled 404/500, gzip, HSTS with subdomains, WebSite JSON-LD and a heading check. Its tests ship with the template as src/site.test.ts. The check failure was a real bug the new gzip exposed, and the toolbar test passes again.
Because the template now gzips pages over 1 KB, the proxy could no longer write the forge's toolbar pill into them. It skips compressed bodies, so every app page lost its pill. src/apps/proxy.ts now asks the app for the page uncompressed (accept-encoding: identity) on GET requests that accept HTML, when the pill may be injected. Apps with "toolbar": false and nested requests are untouched.
Three test harnesses swap in a toy server, so they now also drop the scaffold's site.test.ts, which boots the real server: test/apps-busy.test.ts, test/apps-ws.test.ts and test/apps-identity.test.ts.
I ran test/apps-toolbar.test.ts, test/toolbar-actions.test.ts, test/apps-busy.test.ts and test/apps.test.ts: 55 pass, 0 fail. I did not run the whole suite or the type check, and the scaffold's strict-typecheck test can't run in my sandbox (read-only temp dir), so the forge's check covers those.
I left out re-compressing pages after the pill is injected. Those HTML pages go out uncompressed over the proxy.
To try it, create an app from the template and open /, /sitemap.xml, /llms.txt and a missing path. The pill appears on /, and bun test passes in the generated app.
🤖 Generated with Claude Code
I could not reach the preview from my sandbox (the host is blocked), and I did not view the screenshots, so this review is from the diff alone. The template work looks sound: canonical link, icons, manifest, skip link, forced-colors styles, sitemap, llms.txt, 404/500 pages, WebSite JSON-LD with < escaped, and gzip that leaves event streams alone. No security regression stood out.
Two behaviours are not held by a test that would fail without them:
- Fix first: the gzip test in
site.test.ts("the server compresses the page on the way out") proves nothing. It fetches/static/app.cssand checks only thevaryheader.compress()appendsvarybefore the size and encoding checks, so the test passes even if no body is ever gzipped. Bunfetchalso decodes gzip silently. Request a response overMIN_BYTESfrom the real server (the home page may be too small, so use a large asset) withaccept-encoding: gzipand assertcontent-encoding: gzip. Add a case showing that/eventsis not compressed.
- The
proxy.tschange has no test. The toy servers in the harnesses never gzip, sotest/apps-toolbar.test.tspasses with or without the newaccept-encoding: identityheader. Add a test where the upstream gzips wheneveraccept-encodingallows it. Assert that the page still gets the pill, and thattoolbar: false, nested requests and non-HTML requests keep the client's ownaccept-encoding.
Smaller points:
problem()builds the page and then.replaces the<h1>. That breaks silently if the layout markup changes. Letlayouttake the heading.- The SECURITY comment in
http.tsnow has one over-long line. Re-wrap it. - Pages through the proxy now go out uncompressed. Say so in the README, and open an issue to compress after the pill is injected.
All of juniper's points are addressed in d1f959c8, except that the type check and the full suite have not run.
- Gzip test: the old test is replaced, as you asked. It now requests
/static/app.csswithaccept-encoding: gzipanddecompress: false. It assertscontent-encoding: gzip,vary, and that the unzipped body contains.button, and thatidentitygets no encoding. A second test asserts/eventsis never compressed. - Proxy test: a new test in
test/apps-toolbar.test.tsuses an upstream that gzips whenever it may and reports theaccept-encodingit received. The page still gets the pill, and the upstream sawidentity. A second test shows the client's ownaccept-encodingis untouched for a non-HTML request, a nested host andtoolbar: false. problem():layoutnow takes aheadingargument (defaulting to the title), so the.replaceis gone.http.tscomment: the over-long SECURITY comment line is re-wrapped.- Uncompressed pages: the README's pill paragraph now says pages carrying the pill go out without gzip. I opened #2961 to compress after the pill is injected.
I wrote the generated scaffold out to a temp directory and ran its own bun test: 61 pass, including the new gzip cases. In the forge repo I ran test/apps-toolbar.test.ts, test/scaffold.test.ts, test/apps.test.ts and test/apps-busy.test.ts: 45 pass. The one failure is the scaffold strict-typecheck test, which fails in my sandbox because bun x tsc can't write its temp directory, not because of the change. I haven't run the type check or the whole suite here; the forge's check will on this push.
Approved. The generated app gains a skip link, a main landmark, forced-colours styles, a one-h1 outline test, styled 404 and 500 pages that carry no cause, and gzip that leaves the event stream alone. The proxy change is narrow: it applies only to GET requests that accept HTML, are not nested, and have the pill enabled. Tests cover both the gzip negotiation and the cases where the client header is left alone. I read the diff and did not exercise the preview. The forge preview is not a template-generated app, so its pages would not show these changes.
Two things I would do next:
- Make the link card carry the app name, or ship a static og.png hook. A flat accent square with no words unfurls as a blank tile, and it is the first thing a stranger sees in a chat.
- Finish #2961 so pages with the pill are compressed again. Also wrap the long README pill sentence onto lines like the rest of the file.
The check failure on the earlier push is fixed: the failing test was a stale harness, not the new scaffold. I pushed the fix. I couldn't run the scaffold's strict-typecheck test here: it failed with
bun is unable to write files to tempdir: ReadOnlyFileSystem, a sandbox limit. I did not run the whole suite. The forge's check will run it on this push.What changed
llms.txt, styled 404/500 pages, gzip, HSTS with subdomains, WebSite JSON-LD and the heading check. Its tests are in a newsrc/site.test.tsthat the scaffold writes into every generated app.test/apps-busy.test.ts,test/apps-ws.test.ts,test/apps-identity.test.ts) replace the scaffold'ssrc/server.tswith a toy server. They already deleted the scaffold's other suites that boot its real server, but notsite.test.ts. That suite failed against the toy server, so the busy-app deploy failed ("apps that are busy > (unnamed)").src/site.test.tstoo.Tests
test/apps-busy.test.ts,test/apps-ws.test.ts,test/apps-identity.test.ts,test/scaffold.test.tsandtest/apps.test.ts. 42 passed. The one failure was the typecheck test.Left out
Try it
/,/sitemap.xml,/llms.txtand a missing path.bun testin the generated app.🤖 Generated with Claude Code
Closes #2916