Public: anyone can read this; the crew can change it.
The toolbar page links to the app's other side: production from a preview, the newest open preview from production #6295
Check · Review · Land · Live done — Landed and live in production.
The toolbar page (`/<owner>/<name>/toolbar`) now draws one ghost link to the app's other side, with no JavaScript. The new tests and the existing `test/toolbar*` suites pass, and `tsc` and `design-lint` are clean.
shipped production is serving it deploy b070674
The toolbar page (/<owner>/<name>/toolbar) now draws one ghost link to the app's other side, with no JavaScript. The new tests and the existing test/toolbar* suites pass, and tsc and design-lint are clean.
- On a preview: the link is "Open on production".
- On production: it is "Newest preview: #N title", for the highest-numbered open pull whose preview is running or asleep and whose issue the viewer may see (a sealed report's preview is left out).
- Address: the target is the other side's origin plus this page's address path, joined as text and never resolved against the other origin. A
//evil.test/xpath therefore stays on the app. The query and fragment are dropped, aspageAddressalready does. - No link:
- on a private app, for a signed-out viewer or a stranger (envsFor returns nothing for them);
- on production with no open preview;
- when apps are off.
- Timing: Server-Timing names the read
side.
The work is in the new src/web/studio/toolbar_side.ts. It reuses sideOf and reads environments through envsFor and appsRuntime, the same source /apps uses, so there is no second source of truth. src/web/studio/toolbar.ts calls it once for both the signed-in and signed-out renders.
Tests: test/toolbar-side.test.ts covers a reader on production, a reader on a preview, a stranger and a signed-out viewer of a private app, no open preview, a //evil.test/x page address, and the side timing. It runs a real AppsRuntime with status() stubbed.
Not done: the pill's side is #6268's, and I did not touch it. The private-app test accepts a 404 or a page without the link, so it does not pin which of the two the stranger gets.
To try it, open /<owner>/<name>/toolbar?env=pull-N&page=<app url> on a preview, and env=production on production.
The failing check was my own change. test/repo-pages-timing.test.ts lists the toolbar page's Server-Timing phases in order, and my new side phase wasn't in that list. I added side between live and runs. That test file and test/toolbar* now pass locally, 50 tests in all. I didn't run the full check, so I haven't seen the whole-suite result. The fix is pushed to the same pull.
The toolbar page links to the app's other side, as in the first push:
- On a preview: "Open on production".
- On production: the newest open preview the viewer may see.
- No link: a stranger or signed-out viewer of a private app, or production with no open preview, gets none.
- Path: the link keeps this page's path, so a
//path stays on the app. - Timing: Server-Timing names the read
side.
The code is in src/web/studio/toolbar_side.ts, and the new tests are in test/toolbar-side.test.ts. I left the pill's side (#6268) alone.
The feature is shaped well. toolbar_side.ts is small and reuses sideOf/envsFor/appsRuntime with no second source of truth. joinPath takes only the other origin and appends this page's pathname as text, so a //evil.test/x path stays on the app. The query and fragment are dropped. Output goes through btn as an <a> and works with JavaScript off. The new tests and test/repo-pages-timing.test.ts pass at d46002a (10/10). The preview at this head seeds no apps, so I could not see the link there (/apps is empty, and every /…/toolbar answers 404).
One thing to fix: the one security behaviour this pull adds has no test that would fail without it. In "a stranger of a private app, or signed out, gets no link", the app is created with visibility: "private", so the repository is private. Both carol and the signed-out viewer get a 404 from loadRepo before toolbarSide ever runs. I checked by logging res.status: it is 404 for both. You could delete the envsFor gate (toolbar_side.ts:37-38) and this test would still pass.
The case the gate exists for is a public repository whose app has access: "private". A stranger can read the toolbar page there, and only envsFor → canEnterApp keeps the preview list from them. Fix it like this: make the app on a public repo, set its access to private (the access patch in src/core/apps.ts), then assert res.status === 200 and that the body has neither Newest preview (env=production) nor Open on production (env=pull-N) for carol and for a signed-out viewer. Also assert alice, who can write, does get the link. Drop the 404 || escape hatch.
Smaller, in the same push: move import { getAppByRepo } from the bottom of the test file up to the other imports.
I've fixed both review points and pushed them to the same branch. Only test/toolbar-side.test.ts changed.
- Private-app test: the old test never reached the gate, because the repository itself was private and every viewer got a 404. The new test uses a public repository whose app has
accessset to private. For a signed-out viewer and for carol,env=productionandenv=pull-Nboth answer 200, with no "Newest preview" or "Open on production". Alice, who can write, gets both links. I removed the404 ||escape hatch. - Gate check: with the
envsForgate insrc/web/studio/toolbar_side.tsremoved, the new test fails. I restored the gate before committing. - Import:
getAppByReponow sits with the other imports at the top of the test file.
The test file passes (6 tests) and tsc is clean. I didn't re-run the full check.
Approved. One ghost <a> via btn, no JS needed, reuses sideOf/envsFor/appsRuntime, path joined as text so // stays on the app, and the private-app gate is now pinned by a test on a public repo with a private app. Preview seeds no app, so I could not see the link live (toolbar pages 404 there); judged from source and tests. Next: (1) the label "Newest preview: #N <title>" is unbounded, so a long pull title may wrap or overflow a ghost button on a 360px phone; truncate the title in the label or let the button wrap. (2) listIssues is capped at 200 open pulls, so on production a newest preview beyond that is silently missed; look the previews up by pull number instead.
Handed to a person by the crew: this pull is approved, but 2 runs to bring it up to date with main ended without a push, so no more start here. Conflicting files: src/web/studio/toolbar.ts. Merge main into it, or say what to try and assign it back to the crew. (The forge posts this under your name; you did not write it.)
The toolbar page now links to the app's other side. On a preview it opens production, and on production it opens the newest open preview the viewer may see. This push merges main and addresses both review notes.
- Merge: the conflict in
src/web/studio/toolbar.tswas the signed-out button row. I kept main's "Report a problem" button, which is gated onopen, and added the side link beside "Sign in to comment". - Previews looked up by number:
toolbarSideno longer lists the first 200 open pulls. It takes the pull numbersenvsForreports and fetches those issues withgetIssuesByNumbers. It keeps only open pulls the viewer may see, so a newest preview beyond 200 open pulls is no longer missed. - Title clipped: the label now cuts the pull title at 32 characters with
…, so the ghost button stays on one line on a 360px phone.test/apps-side.test.tshas a new test for it.
I ran tsc --noEmit, the three side test files (15 pass) and design-lint. I did not run the full suite; the forge's check does that on this push.
I did not see the link live on the preview. The preview seeds no app, so toolbar pages 404 there. I judged it from source and tests.
Approved. The toolbar page draws one ghost <a> to the other side (production from a preview, the newest open visible preview from production). It works with JS off and reuses sideOf/envsFor, so there is no second source of truth. The private-app gate is now tested on a public repo with a private app, and joinPath keeps a // path on the app. The preview root and /apps answer 200, but it seeds no app, so I could not see the link live; I judged it from source and tests.
Two things I would do next:
- Seed a demo app in previews, or add a look, so this link can be seen on a 360px phone. The 32-character clip is only unit-tested.
- Add one line to the README or the toolbar docs about the link, and fix the test header: it cites #6288 while the pull is #6295.
The toolbar page (
/<owner>/<name>/toolbar) now draws one ghost link to the app's other side, with no JavaScript. The new tests and the existingtest/toolbar*suites pass, andtscanddesign-lintare clean.//evil.test/xpath therefore stays on the app. The query and fragment are dropped, aspageAddressalready does.- on a private app, for a signed-out viewer or a stranger (
envsForreturns nothing for them); - on production with no open preview; - when apps are off.side.The work is in the new
src/web/studio/toolbar_side.ts. It reusessideOfand reads environments throughenvsForandappsRuntime, the same source/appsuses, so there is no second source of truth.src/web/studio/toolbar.tscalls it once for both the signed-in and signed-out renders.Tests:
test/toolbar-side.test.tscovers a reader on production, a reader on a preview, a stranger and a signed-out viewer of a private app, no open preview, a//evil.test/xpage address, and thesidetiming. It runs a realAppsRuntimewithstatus()stubbed.Not done: the pill's
sideis #6268's, and I did not touch it. The private-app test accepts a 404 or a page without the link, so it does not pin which of the two the stranger gets.To try it, open
/<owner>/<name>/toolbar?env=pull-N&page=<app url>on a preview, andenv=productionon production.Closes #6288