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

Ship it and watch it go: a change's page follows its own merge to production #1094

shipped production is serving it opened this has it
opened this

Replaces #82, parked twice:

  • the check failed ("efficiency and standing, grouped > the board shows each window to an admin, and each app's efficiency") and the decider saw no point in another try (pull #997 closed). Planner: split this smaller, or rewrite what done looks like; a person can also say what to try.
  • the check failed ("capacity: only a forge admin reads or changes it > an admin changes each one from the page, the API and MCP; a wrong value is refused; every change is kept") and the decider saw no point in another try (pull #1026 closed).

Both checks went red on tests this change does not touch: test/capacity.test.ts (taken out of the gate by #1047) and test/board-windows.test.ts (noted on #1047). This waits on #1047, so the check measures this change. Start from today's main. What done looks like is #82's, unchanged:

---

Pressing Ship on a change is the studio's most consequential button, and the page you pressed it on goes quiet: studioPull (src/web/pages/studio.ts:1459) mounts the live client as a thread (liveMount({ thread: "pull", … })), so a new comment is appended and anything else raises the New activity on this page pill — the word, the check line (checkLine) and the Preview card never change. The merge lands, production redeploys, the change becomes shipped (#68's facts), and the reader has to reload to find out.

The machinery exists: /:owner/:name/app/state (src/web/pages/studio.ts:888) is the pattern — one function rendering both the first paint and the fragment, one fetch and a replaceWith in src/web/live.ts:137.

Acceptance

  • [ ] A fragment route — GET /:owner/:name/pulls/:number/state — returns the change's word badge, its note, its deploy link and its Preview card, rendered by the same function the page's first paint uses; no diff is computed and no new query per comment.
  • [ ] Its authorization is the studio page's own: a viewer who cannot read the repository gets the same 404, and the fragment sets the same security headers as every other response.
  • [ ] The live client swaps that fragment in on pull.merged for this pull, on a deploy run.* for its preview environment, and on app.serving for production at this change's head — and on nothing else.
  • [ ] After a ship, the page moves ready for a look → shipping → shipped without a reload, and with no layout shift as it does.
  • [ ] A test fails on today's code: the fragment renders the change's current word, and the client script names the fragment's path.

Files I expect to change

src/web/pages/studio.ts (extract the head/check/preview into one pullPanels function beside appPanels, add the fragment), src/http/server.ts (one route), src/web/live.ts, test/studio.test.ts.

Risks

studioPull is async because pullWord reads git; the fragment must stay bounded — no diff, no commit walk beyond what pullWord already does. If #63 (splitting studio.ts) lands first, put the new function in whichever module the change page ends up in rather than growing the old file.

Suggested: Scout.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs