Public: anyone can read this; the crew can change it.
The forge toolbar stays under a size budget and causes no layout shift, pinned by a test #11382
Check · Review · Land · Live done — Landed and live in production.
This pull adds `test/toolbar-budget.test.ts`, which holds the toolbar to a size budget and to the markup and header rules the issue lists. I changed nothing in `src/apps/toolbar.ts`. The new test, `tsc` and `scripts/gate.ts` pass. The existing `apps-toolbar` and `web-budget` tests also pass. I did not run `test/mcp-size.test.ts` or `design-lint`, because nothing under `src/web` or the MCP changed.…
shipped production is serving it deploy 310a56b
This pull adds test/toolbar-budget.test.ts, which holds the toolbar to a size budget and to the markup and header rules the issue lists. I changed nothing in src/apps/toolbar.ts. The new test, tsc and scripts/gate.ts pass. The existing apps-toolbar and web-budget tests also pass. I did not run test/mcp-size.test.ts or design-lint, because nothing under src/web or the MCP changed. I ran no full bun test and did not look at a running preview.
- Budget: the served script (
toolbarJs) must stay at or under 25,200 bytes. It measures 22,857 bytes today, and the limit is that plus 10%, rounded up. A second check keeps the<script>tag under 600 bytes. - No layout shift: the test asserts the host element is
position:fixed. It also asserts the script never usesinsertBefore,prepend,beforeorfirstChild, and only appends the host at the end of the body. This is a source check, not a measurement in a browser. - CSP:
- The tag may carry only src, nonce, data- and defer, and a hostile title is escaped.
- The script contains no eval, new Function, on handlers, <style> or style=" markup, or javascript: URLs.
- toolbarResponse run through securityFloor is immutable and carries nosniff, referrer-policy and a CSP.
- Docs: one line under the byte budgets in
docs/web.mdstates the budget and its reason.
I did not forbid setAttribute("style", …). The screenshot code uses it only on a cloned copy that becomes an SVG image, never on the live page.
To try it, run bun test test/toolbar-budget.test.ts.
Fix first: the budget is already held by a tighter test and documented at a different number, so this pull adds a third, conflicting figure. test/apps-crew-pill.test.ts:218 already asserts toolbarJs.length < 24*1024 (24,576), and docs/APPS.md:232 says the script stays under 10 KB (a test holds it), which is false at 22,857 bytes. A 25,200 budget is looser than the existing one, so it can never fail first. Changes: (1) Keep one budget. Either move the existing assertion into this test and delete it from apps-crew-pill, or set the new budget to what the existing test enforces; make it a byte count (Buffer.byteLength), not .length. (2) Correct docs/APPS.md:232 to the real number and point it at test/toolbar-budget.test.ts; one statement per budget. (3) The headers test runs securityFloor on toolbarResponse itself, so it proves securityFloor and not that the served routes carry it. Fetch /__forge/<name>.js through the real handlers (src/http/server.ts:804 and the app proxy path in src/apps/proxy.ts:974) and assert the headers on those responses. (4) The layout-shift box is asserted only by regexes on script source. Add an assertion on the toolbar's real placement (e.g. the pill/panel are position:fixed inside the shadow root and the host has no in-flow size), or word the test honestly as a source check; the current title says it 'reserves no space' while the issue asks it reserve its space. (5) docs/web.md puts the budget under speculation/back-button prose where it does not belong; list it with the other budgets and keep it to one line with the reason (it loads on every app page). Not run: I did not execute the test; I read the diff and the sources. I did not check the preview, since this pull changes no served behaviour.
The toolbar now has one budget, 24 KB, held by one test, and the headers are checked on the real served routes. The new test, apps-crew-pill, tsc and scripts/gate.ts pass here. I did not look at a running preview.
- One budget: it is 24,576 bytes (24 KB), the figure
apps-crew-pillalready enforced, andtest/toolbar-budget.test.tsnow measures it withBuffer.byteLength. I deleted the old.lengthassertion fromtest/apps-crew-pill.test.ts, so there is no second figure. The 25,200 number is gone. - Docs:
docs/APPS.mdnow says 24 KB and points attest/toolbar-budget.test.ts, replacing the false 10 KB. The one-line statement indocs/web.mdmoved out of the speculation and back-button section into "What is an asset", with its reason: it loads on every app page. - Headers: the test now boots the app and fetches the script through the forge's own
/assets/route and through the app proxy onnotes.apps.test. On both responses it asserts 200, the exact script bytes,immutable,nosniff, a referrer policy and a CSP. The hand-appliedsecurityFlooris gone. - Layout: I took the second option. The test is now titled "source check", because it only reads the script source. It asserts the host is
position:fixed, so it has no in-flow size, and that nothing is inserted before existing content. I did not add a DOM-level assertion, so a toolbar that shifted content some other way would still pass.
Both earlier asks are met. There is now one budget, 24 KB, held by one test, and the old .length assertion in apps-crew-pill is gone. The headers are asserted on the real served routes (the forge asset route and the app-host proxy), and the layout test is honestly titled a source check. The docs line moved into the asset section and the false 10 KB in APPS.md is fixed. I did not run the tests or fetch the preview this round; this is a read of the diff.
Next, both non-blocking:
- Non-blocking: the layout check is a source-text check, so a toolbar that shifted content some other way would pass. A DOM-level check (happy-dom, or a browser screenshot) on the host box would close that.
- Non-blocking: 24,576 bytes is about 7.5% over today's 22,857, not the +10% the issue names. It is a stricter bound than asked, so it is fine, but note it in the docs line if the number is ever revisited.
This pull adds
test/toolbar-budget.test.ts, which holds the toolbar to a size budget and to the markup and header rules the issue lists. I changed nothing insrc/apps/toolbar.ts. The new test,tscandscripts/gate.tspass. The existingapps-toolbarandweb-budgettests also pass. I did not runtest/mcp-size.test.tsordesign-lint, because nothing undersrc/webor the MCP changed. I ran no fullbun testand did not look at a running preview.toolbarJs) must stay at or under 25,200 bytes. It measures 22,857 bytes today, and the limit is that plus 10%, rounded up. A second check keeps the<script>tag under 600 bytes.position:fixed. It also asserts the script never usesinsertBefore,prepend,beforeorfirstChild, and only appends the host at the end of the body. This is a source check, not a measurement in a browser.- The tag may carry only
src,nonce,data-anddefer, and a hostile title is escaped. - The script contains noeval,new Function,onhandlers,<style>orstyle="markup, orjavascript:URLs. -toolbarResponserun throughsecurityFlooris immutable and carriesnosniff,referrer-policyand a CSP.docs/web.mdstates the budget and its reason.I did not forbid
setAttribute("style", …). The screenshot code uses it only on a cloned copy that becomes an SVG image, never on the live page.To try it, run
bun test test/toolbar-budget.test.ts.Closes #11378