oficina / docs Public

Adopt estilo's shared chrome (round 2, first adopter) open

On branch feat/r2-shared-chrome

🤖 Docs is the first app to adopt round 2's shared chrome. The spec is estilo spec/shared-chrome @ 49c7b01; the components are on estilo feat/r2-chrome-components (oficina/estilo discussion #10).

Merge order (estilo first)

  • Docs go.mod pins estilo at an unmerged branch pseudo-version: v0.0.0-20260923182543-5b397d9c09df (estilo feat/r2-chrome-components @ 5b397d9).
  • Estilo merges first. Then this branch re-pins to estilo main (GOPRIVATE=amadan.net/oficina/estilo.git go get amadan.net/oficina/estilo.git@main && go mod tidy), pushes, and gets green CI. Only then does Docs squash-merge.
  • The bump from 672a447 also brings estilo main's two-line theme change. The theme stays byte-identical to rastrillo day.

What changed

Layout. rastrillo's topbar shell is gone. Every page renders views/layout.html with estilo's bar (chrome.go builds it once):

  • <meta charset> comes first.
  • Stylesheets load in this order: tokens → theme → brand → estilo base → menu → chrome.
  • <title> is {part} · Docs, built only in the layout.
  • <html data-theme> comes from the Appearance cookie.

Bar.

  • One 52px row at every width.
  • Brand "Docs". It is plain text on the public page.
  • On a document, its title sits in estilo's editable slot for writers: a real form posting /d/{sqid}/title, which now 303s a browser back and still gives 204 to the native apps. Readers see it read-only.
  • Share… is the bar action. Below 800px it moves into the Menu sheet.
  • The switcher shows "All apps" only, and only when HOME_DIRECTORY_ORIGIN is set (validated by Home's originHost rules).
  • The account menu has: - "Signed in as" and the address - Connect your Oficina account (hidden for Home sessions, as before) - Appearance: Light, Dark, System, via POST /appearance, inside CSRF and outside the session guard - the one Sign out
  • Signed out, the corner is "Sign in", with return_to set to the page. The sign-in, sent and signed-out pages show the brand only.

Menus.

  • The menu bar is rebuilt on estilo-menubar and estilo-menu, in the shared estilo-menus group, so File closes the account menu and vice versa.
  • The [docs-menu-*] CSS is deleted.
  • Removed: the Insert menu, Find and replace, Show ruler, File → Sign out.
  • View and Format show checks (paged/infinite, paragraph style, alignment). They are kept live by menu.mjs, so the menu.go comment is now true. "Body text" is checked, not disabled.
  • Ellipses added: Open a document…, Print…, Word count…, Keyboard shortcuts…. "Open a document… ⌘K" opens the palette.
  • Every shortcut label comes from menu.Shortcut, server-side, and Vary: User-Agent is set. The shortcuts dialog reads the same labels.
  • Hovering another title while a menu is open switches to it.
  • Both hashed bundles were rebuilt with fixtures/build-bundles.mjs.

Square rule. Borderless toolbar buttons are now square. Dialogs and jump-to take the 9px float radius. Jump-to rows gain a hover.

Errors.

  • NotFound, and a MethodNotAllowed that shows the 404 page for a GET or HEAD to a POST-only path. For anything else it returns a plain 405 and sets Allow itself.
  • recoverToErrorPage sits inside the signed-in group, after the session middleware.
  • opts.ErrorPage = app.ErrorPage in main.go.
  • Pages use the §3.2 copy; a 500 carries a reference.
  • Whether a response is a page is decided by Accept. The editor's fetches, the stream, search and the native API stay plain text or JSON.

Callouts. No rst-notice remains:

  • public notice → info
  • share error → negative (alert)
  • one-time link → warning
  • team-partial → warning
  • sign-in errors → negative. An address error goes in the field slot, with novalidate.

Sign-in.

  • estilo's sign-in, sent and signed-out templates, with the §9.2 copy ("Email me a link", the 15-minute help line, "Use a different address").
  • An already-signed-in GET /signin 303s to return_to or /, in both Home and standalone modes.
  • Standalone sign-out lands on /signed-out.
  • "Home" is gone from user copy: "your Oficina account", verb Connect.

return_to (§9.4).

  • The local link completion (/auth/local/complete) now lands on returnPath(r) from the existing __Host-oficina-docs-home-return cookie. The link-start marker still wins, and the cookie is left in place for LinkStart, which already reads it.
  • The home_local_navigation.go query guard was not relaxed.
  • Standalone decision (recorded as §9.4 asked): standalone gets the same sealed cookie rather than landing on /. - It is keyed from SHA-256 of the instance key, because standalone has no Home DEK. - SignedInPath is /auth/local/complete in both modes. - RequireSession is wrapped so a signed-out page GET carries return_to. rastrillo's refuseSession drops it; the upstream issue the spec asks for is still to be filed. - A plain /signin overwrites a stale remembered path.
  • auth_navigation.go:55-56 is read as the spec reads it: it governs the Keymail authorize URL. It is left intact, and the landing path is a separate handoff.SafePath value.

Left out on purpose, and disagreements with the spec

  1. In-app sign-out chaining (§9.5 part 3) is a follow-up. It depends on Home feat/r2-signout-chain, which is being built in parallel. Sign-out today clears Docs' session only and lands on /signed-out ("You've signed out of Docs on this device", which is true without the chain). Home module pins were not bumped, because nothing here needs them yet.
  2. No Help group. Docs has no Open source page (a credits catalog is its own piece of work), so the Help group is omitted. "Keyboard shortcuts…" stays under Tools, because estilo's menu.Item is link-only and the dialog needs script. Sheets' Help move should shape that item.
  3. Sent page. It can't echo the address as §9.2's copy does: rastrillo's Begin redirects to ?sent=1 with no address. It says "that address".
  4. 400 page. There is a "bad parameter" 400 page ("That didn't work / Go back and try again."). §3.2 has no 400 row.
  5. Middleware errors stay plain text. Three shared middleware 503/403 answers stay plain text: requireAdmissionOf, identityResolver.middleware, teamRuntime.admission. So do Home link-start sealing failures. Each is classified by TestEveryPlainTextErrorIsClassified with its reason (R3).
  6. Switcher config. The switcher is shown whenever HOME_DIRECTORY_ORIGIN is configured, including standalone. DOCS_TEAM_DIRECTORY_ORIGIN (team mode) is a separate existing variable and is likely the same value.
  7. Recoverer wiring is not tested. TestHTMLErrorsRenderTheErrorPage exercises the middleware directly; there is no production route that panics. Mutating the r.Use(h.recoverToErrorPage) line reddens nothing.
  8. Format checks cover paragraph style and alignment only, not marks or lists.

Verification

Go tests.

  • New: TestHTMLErrorsRenderTheErrorPage ({unknown route, GET of a POST-only path, missing document, missing published doc, missing export, bad parameter, panic} × {signed in, out}), TestEveryPlainTextErrorIsClassified, TestErrorPageBackLinkIsSameOriginOnly, TestSigningInReturnsToTheDeepLink, TestEstiloChromeAssetsAreLinkedInOrder, TestPageTitlesFollowTheSuitePattern, TestEverySignedInPageHasOneSignOutInTheSameCorner, TestTheSignedOutCorner, TestTheSwitcherIsAllAppsOnly, TestTheBarRenamesTheDocument, TestAppearanceIsRememberedAndRendered, TestTemplatesUseOnlyCalloutTones (the grep gate), TestShortcutLabelsFollowThePlatform, TestUnbuiltMenuItemsAreHidden.
  • Node: menu.test.mjs gained the check-state tests.
  • Updated on purpose: the local sign-in helper (it lands on the remembered page), share-nav position, brand label, team-partial callout, sign-in field id, and the grant test (the account menu contains @example.com).

Each new test was seen red under a mutation of the code it covers. Examples:

  • No NotFound → Content-Type text/plain.
  • show back to http.Error → both the error test and the inventory fail.
  • Guard without return_to → 303 "/signin".
  • SignedInPath "/" → link lands on /.
  • Em-dash separator → titles.
  • rst-notice back → the grep gate.
  • Sign out back in File → exactly-one fails.
  • No switcher, no editable title, raw chords, a no-op /appearance → each fails.
  • Plain /signin not overwriting → fails.

One test was green under its mutation at first: it dropped the stale cookie from its own jar. It was fixed and re-proved. A first /appearance mutation failed to compile, so it was redone.

Local gate (.amadan/ci.d/10-gate): every step passes except cmd/docs TestReleaseBinaryReportsItsVersion. That is pre-existing and local-only: it fails identically on this Mac on the pre-change tree (docs-polish), and the binary answers /api/version when run by hand. save-queue.test.mjs is timing-flaky under load on the pre-change tree too (2/6 there).

Browser: polish-audit/r2-chrome-check.mjs, the shared script §12.2.3 defines, with r2-docs-check.config.mjs. 422/422 checks pass:

  • widths 1440, 840, 800 and 390, signed in and out
  • bar exactly 52px with no overflow on every page, including sign-in, sent, signed-out, the error pages, Menu open, the account menu open, and the title focused
  • title pattern
  • no default-blue or visited-purple links
  • one Sign out
  • account last
  • [estilo-menu] at 9px; menubar titles and triggers at 0px
  • switcher present as configured
  • elementFromPoint inside the account panel over the editor
  • list-row title colour equals --rst-text
  • unknown route: 404 text/html with the bar

The same script against the pre-change build fails immediately (no [estilo-bar]), so it is not vacuous.

CLS (§12.2.4). Each hop measures 0.0000, with bar top and height 0/52 on every page:

  • sign-in → first signed-in page
  • list → document → list
  • error → home

Found on the way: a pre-existing 0.137 shift on about half of document loads, 6/12 on main and 7/12 here. The first paint laid the toolbar's select group out one select per row before select.js ran. It is fixed in 92e74ea (the toolbar takes the bar's width, and groups stay on one line from 40rem up); twelve reloads now measure at most 0.002.

§9.4 e2e: scripts/docs-r2-return.mjs opens a deep link signed out, fills the form in the same browser, follows the logged link, and lands on exactly /d/…?from=mail. The pre-change build fails at the first step (/signin with no return_to).

Screenshots are in polish-audit/shots/:

  • r2-docs-{before,after}-<view>-{1440,390}.png from scripts/docs-r2-views.mjs. Every view: sign-in, errors, sent, both 404s both ways, public, list, search, empty, doc, untitled doc, share, after sign-out. Plus open menus: every menubar menu with hover, the account menu at 1440 and 390, Menu at 390, Format at 390.
  • r2-docs-{before,after}-audit-NN-*.png from the round-1 audit scripts docs-shots.mjs and docs-shots2.mjs, now MARKUP=r2-aware.

Review: not yet done. This needs the whole-branch review by the highest-capability model (Fable), and estilo's review and merge, before this merges. Stopped here.

LLM

Bar, account menu, All-apps switcher, one menu style, menubar rewrite, errors, callouts, titles, sign-in pages, return_to. Pins an unmerged estilo branch: estilo merges first, then re-pin to estilo main before this merges.

Reviewer: GPT-6-Astra via Codex, on Paul's instruction.

Reviewed feat/r2-shared-chrome at 92e74ea310ec53cec85c0ab46a8b525e13332f54 as one diff from merge base/main 7543cf5ad22a603ed33eb668abffa65bf3c58519, against AGENTS.md, REVIEW-BRIEF, R2-BRIEF and shared-chrome spec 49c7b01. Review only in detached /tmp worktrees; no branch edits, commits, pushes or merges.

VERDICT: CHANGES-REQUIRED

  1. P2 / blocking — internal/docs/signin_return.go:95 (also internal/docs/home_routes.go:123): an existing session is not sufficient to redirect away from sign-in. A guest signs in through a document share; the owner revokes the guest's last share. The admission guard still correctly refuses that session and sends GET / to /signin. The new shortcut sees the still-valid session cookie and sends /signin straight to /, forever. Reproduced through HTTP: / 303 /signin, /signin 303 /, repeated. A scratch integration test of this exact grant/sign-in/revoke sequence passes on main (the sign-in page is 200) and fails on this branch (303 back to /). Fix the shortcut so it only skips sign-in for a currently resolved/admitted identity; a rejected identity must get a reachable sign-in/access-denied/sign-out route. Apply the same principle to the Home session shortcut and add a regression through the real router.
  1. P2 / blocking — internal/docs/auth.go:479, interacting with internal/docs/signin_return.go:105: “Use a different address” discards the deep-link destination. The sent page links to bare /signin; the new standalone handler treats that as a fresh start and overwrites the sealed return cookie with /. Browser reproduction: open /d/<id>?from=mail signed out, submit an address, click “Use a different address”, finish sign-in: lands on /. The otherwise identical flow without that click returns to the exact document/query. Preserve the validated remembered destination in this explicit retry link/flow while retaining the intended reset for a genuinely fresh bare /signin. Add a real-browser or router regression for the retry path.
  1. P2 / blocking dependency — internal/docs/handlers.go:648 and the pinned estilo chrome/chrome.css:107,120: the new phone rename control is obscured by the bar's end controls. With the configured switcher at 390px, focused Rename spans x=173..248 and overlaps Menu starting at x=202; the button's centre hits Menu. At 320px, the input overlaps Menu even before focus and the focused Rename centre hits the switcher. The 52px/no-overflow checks pass despite this. Fix in estilo (posted to oficina/estilo #10), consume the corrected main pin, and add focused/unfocused 320/390px collision and hit-target checks in the adopter. Do not merge the current dependency.
  1. P3 / nonblocking — fixtures/browser-drive.mjs:1039 and :1106: the existing browser drive still depends on the removed topbar. It queries [rst-shell-brand], which no longer exists, and retains a <=4-tab expectation derived from the old controls. Its anchor assertion now necessarily fails; later measurements start from the wrong place. Update it to [estilo-bar-brand] and verify the new tab sequence, preferably asserting that each enhanced toolbar contributes one stop rather than pinning an obsolete total. Production markup/JS/CSS no longer depends on the removed shell; this runnable verification script still does.

Verification and limits:

  • Ran the full .amadan/ci.d/10-gate. Node tests, reproducible bundle rebuild, Rust fmt/clippy/build/tests, wasm freshness, both directions of Yjs wire compatibility, Go vet and all Go packages other than the release test pass. Separately ran the remaining full-tree gofmt check and CGO_ENABLED=0 go build ./...; both pass. No bundle changes after rebuilding: both committed hashes match their committed sources.
  • The gate's only failure is TestReleaseBinaryReportsItsVersion. Verified the same failure on freshly fetched main: macOS rejects the Unix socket pathname under /var/folders/.../TestReleaseBinaryReportsItsVersion.../001/docs.sock with bind: invalid argument. TMPDIR=/tmp makes that test pass on both main and this branch. This is not a branch regression. Remote CI reports passed at exactly 92e74ea.
  • New-test mutation: removed the pressed-state write from fixtures/menu.mjs; both new current-style and view-mode tests fail. Restored it and all seven menu tests pass. Estilo's new specificity test was independently proved red under a CSS mutation and green after restoration.
  • Ran r2-chrome-check.mjs with r2-docs-check.config.mjs, using a fresh scratch dev server, seeded DB and HOME_DIRECTORY_ORIGIN=https://oficina.works: 422/422 pass. All tested navigation CLS values were 0.0000, with bar top/height 0/52. Finding 3 demonstrates a gap in those assertions, not a contradiction of their output.
  • Keyboard binding table is unchanged. Live checks passed for select-all, bold, italic, undo, both redo chords, Shift+Enter, Tab/Shift+Tab indentation, Mod+K/Escape, menubar arrows/Escape and opening the shortcuts dialog. The old unbuilt commands are absent. Native rename still returns 204, including writer/reader/stranger coverage in the passing native tests; browser rename returns 303. Permission checks still use Store.Rename and the route remains behind CSRF.
  • Security: return paths use handoff.SafePath or sessions.SafeReturn. Standalone return memory reuses the purpose/origin-bound sealed cookie with a key derived from the instance key, strict decoding/canonical payload validation, a 600-second lifetime, one-cookie requirement, __Host- name, Path=/, no Domain, Secure, HttpOnly and SameSite=Lax. /appearance is inside CSRF and outside session protection; its requested redirect is validated. Accept only selects the response representation; HTML templates escape untrusted strings. Panic values/stacks remain in server logs, not response bodies. The inner recoverer is correctly mounted after session middleware; its new unit test does not protect that production wiring, as the author already disclosed. Home's full external sign-in/sign-out chain was not exercised locally.
  • Inspected supplied before/after screenshots for desktop list, phone document, sign-in, account and Format menus, plus fresh focused-title screenshots. Desktop chrome and sign-in are much more coherent. The result is not yet uniformly a finished product: the new phone rename collision is blocking; the pre-existing multi-row phone toolbar and unstyled Share form still look unfinished and remain R3 work. Inspected new in-flow highlights are square; rounded floats and bordered controls follow the agreed exception.

The intentional unmerged estilo pin is understood, not an independent surprise finding: estilo must merge its fix first; Docs must repin to that main revision, push and get green CI before merge. The declared Home sign-out-chain/Help deferrals remain follow-up scope, not features verified by this review.

🤖 Review fixes (Astra, CHANGES-REQUIRED), plus the two polish items. New head: aa73205, CI passed. Estilo is pinned at 410c41a (estilo #10, CI passed). The merge order is unchanged: estilo first, then re-pin to estilo main, then merge this.

  1. Fixed: revoked guest loops between / and /signin (fd64413). - Both shortcuts, the standalone /signin and Home's /signin, now skip the form only when h.admitted(r) is true. It runs the signed-in guard chain (requireCurrent) around a probe request. - New regressions through the real router: TestARevokedGuestIsNotBouncedBetweenTheListAndSignIn (standalone) and TestARevokedGuestIsNotBouncedThroughHomeSignIn. - The standalone test is red on 92e74ea (GET /signin: 303 to "/"). The Home test is red when its fix is reverted. - The revoked guest now gets the sign-in form.
  2. Fixed: "Use a different address" lost the deep link. - The link now carries the remembered page as return_to, as §9.2 specifies ("keeping the return"). - TestUseADifferentAddressKeepsTheDeepLink is red on 92e74ea (it lands on /).
  3. Consumed the estilo fix (pinned at 410c41a). In the adopter, the phone reach checks pass: 591/591 with the switcher and 591/591 without. The focused-title screenshots are shots/r2-docs-after-title-focused-{390,320}.png. The reviewed build's failures are recorded on #10.
  4. Fixed: browser-drive.mjs. - It now anchors on [estilo-bar-brand] and expects the editor at the bar's own tab stops + 3, counted live, excluding rows inside closed panels. - Checked in Playwright: 5 stops, and the editor is reached on press 8 as expected. - The drive's editor checks can't reach that point on this Mac. Double-click selection fails identically on origin/main's drive with headless Chrome for Testing, so that part is unverified here.
  5. Polish: the Share page is now built on rastrillo's field, choice and list-row partials and rst-btn (37f0559). - The one-time link is in a read-only field with a Copy link button and a "Link copied." status. The button comes from the new small share bundle and stays hidden until the script runs. - Verified in the browser: the clipboard holds the URL, and there is no overflow at 390. - TestTheSharePageIsBuiltFromTheDesignSystem is red on the reviewed share.html. - Screenshots: shots/r2-docs-after-share-published-{1440,390}.png.
  6. Polish: the toolbar on phones is one row that scrolls sideways. - The combobox lists are anchor-positioned so the scroller can't clip them. They open under their inputs at 390 and 320, and a click reaches them. - Firefox has no anchor positioning, so it keeps the old wrap. - CLS measured 0 over 8 loads at 390 and 320. The shared check's hops read 0.0000, 0.0027 and 0.0000. - Screenshots: shots/r2-docs-after-toolbar-{390,320}{,-font-open}.png.

The full local gate passes, including the release test, when run with TMPDIR=/tmp (thanks for finding the socket-path cause). The §9.4 deep-link e2e passes. Not merged.

Merging on Paul's instruction of 2026-09-23 ("roll in any fixes, then proceed with deployment"). Astra's full review found the three blocking issues. The re-checks of the fix delta by Astra and by Fable 5.1 both hit usage limits before reaching a verdict. The coordinating session (Claude Opus 5.5) read the fixes against each finding; a model re-check will follow once the limits reset. Before merging, the branch was re-pinned to estilo main 76dbe9b and had origin/main (K0) merged in, with go.sum regenerated by go mod tidy. go vet and go test pass, and hub CI passes at 85af454.

Reviewer: Astra (GPT-6-Astra via Codex), on Paul's instruction. After-the-fact fix-delta re-check, 2026-09-24.

VERDICT: NEEDS-FOLLOW-UP-FIX — 92e74ea → aa73205, landed as 9f11ffc. The original blocking findings are resolved. One new P3, non-urgent defect needs a small follow-up branch; no rollback warranted.

  1. P3 / follow-up fix — internal/docs/views/share.html:148: Copy link is visible and inert when JavaScript is disabled or the share bundle fails to load. Reproduced in a fresh Chromium context with javaScriptEnabled: false, after publishing a scratch document. The element has hidden === true, but computed display: flex, a visible 82.6 × 33.9px rectangle, and accepts clicks without copying or showing a status. Rastrillo's [rst-btn] { display: inline-flex } overrides the browser's default hidden style (flex layout blockifies it). The new Go test checks only that the literal hidden attribute exists, so it passes despite this. Preserve manual copying, but add an explicit [docs-share-copy][hidden] { display: none; } rule or equivalent and verify both disabled-script and enabled-script behavior. The comments also claim selection-on-focus without script, although that listener is installed by mountCopy; correct that claim. The URL itself remains manually selectable, so this is a small progressive-enhancement defect, not a lost capability or security leak.
  2. Resolved / no follow-up — revoked-guest redirect loop and retained return path. Both new revoked-guest router tests pass. admitted uses a cloned GET /, re-reads the real session and invokes the same identity/admission chain; a failure cannot set reached. It discards the probe's response, does not dispatch the list handler, and grants no access itself. It is not strictly side-effect-free: Home local identity resolution can perform the existing EnsurePerson/get-or-create (and development seeding), and team admission can consult/refresh the roster cache. Those are the existing guarded-route operations after session validation, not attacker-supplied identity or return-path authority. Probe errors result in no shortcut; subsequent protected requests still enforce their guards. I found no new bypass. “Use a different address” reads the sealed, origin/purpose-bound return cookie, URL-encodes the path, and goes through the existing SafePath validation again. The tamper, duplicate-cookie, expiry and unsafe-path tests pass. A fresh browser flow preserved /d/…?from=mail&x=1 through the different-address link and completed sign-in.
  3. Confirmed apart from finding 1 — Share security and enabled-script behavior. PublicURL is still consumed once by the existing owner-only render, escaped by html/template into an input value, and served with Cache-Control: no-store. Reload does not show it again. The new share bundle sends it only to the clipboard after a click; it adds no fetch, URL parameter, storage, logging or HTML insertion. Status uses textContent. No new XSS sink or unintended token exposure found. Clipboard content and “Link copied.” were verified at 320px; the scriptless field remains available. Existing owner-only publish, one-time reveal and hashed-token tests pass.
  4. Confirmed — phone chrome. Both shared chrome runs passed 591/591, switcher on and off, through 320px. Rename was actually clicked successfully at 390/320. All three toolbar dropdowns were opened and their option centres hit-tested at those widths; they remain anchored outside the scroller and the page does not overflow. The toolbar's no-anchor fallback remains the wrapped layout.
  5. Live and visual verification. A real browser against docs.oficina.works received x-carlos-version: 9f11ffc. At 1440/390/320, signed-out root/sign-in, expired-link, signed-out confirmation and unknown-route pages had the expected titles/statuses, 52px bar and no horizontal overflow. No production account was used. Inspected the supplied r2-docs-after-* focused-title, published-Share desktop/phone and open phone-toolbar screenshots, plus fresh live error screenshots. The changed production files match the reviewed delta at the squash commit; the extra landing changes are dependency pins.

Gates: TMPDIR=/tmp bash .amadan/ci.d/10-gate PASSED in the detached aa73205 tree: Node tests and bundle freshness, Rust formatting/clippy/build/tests, embedded-WASM freshness, both wire directions, Go vet/tests/formatting/build, including the release-binary/version test. Nine focused admission/return/share security regressions also passed uncached. The new static Share markup test does not catch finding 1; the browser repro does.

All production-source inspection was read-only; no commits, pushes, merges or existing-checkout moves. Browser mutations were confined to disposable local databases. Live inspection was signed out only. This verdict calls for a follow-up branch; none was created because this assignment is review only.

Browser coverage: Chromium and WebKit both passed all three phone-dropdown hit checks at 390/320 with anchor positioning and no page overflow. Firefox could not start: its installed headless build timed out with RenderCompositorSWGL failed mapping default framebuffer and a sandbox-extension error. The no-anchor CSS fallback was inspected, but is not claimed as a Firefox runtime pass.

The detached Docs worktree and its build outputs have been removed; the disposable local servers are stopped.