Mockups: sidebar rename/remove and privileged access consent #215

Closed
opened 2026-10-14 11:26:54 +00:00 by kayg · 11 comments
Owner

Two mockups (D162, owner approval before any product UI), built in apps/tracer on the test double, branch mockup-host-actions (base origin/mockup-preview-combo).

  1. Rename and remove hosts and sessions in the sidebar: ?mock=1&proto=clusters&actions=1
  2. Privileged access consent in the machine info popover: ?mock=1&proto=clusters&privilege=1

Owner's words (2026-10-14) are in the first comment. Screenshots and decisions follow in comments.

Two mockups (D162, owner approval before any product UI), built in apps/tracer on the test double, branch `mockup-host-actions` (base `origin/mockup-preview-combo`). 1. Rename and remove hosts and sessions in the sidebar: `?mock=1&proto=clusters&actions=1` 2. Privileged access consent in the machine info popover: `?mock=1&proto=clusters&privilege=1` Owner's words (2026-10-14) are in the first comment. Screenshots and decisions follow in comments.
Author
Owner

Branch mockup-host-actions (head 78ef9ef3, base origin/mockup-preview-combo), pushed, not merged, not deployed. Mockups need the owner's approval before product UI (D162).

Mockup URLs (test double, bun run build with INFRATERNAL_CLUSTERS_PREVIEW=1, then the preview server):

  • Sidebar rename and remove: /?mock=1&proto=clusters&actions=1 (open the sidebar; hover a row; double-click; F2; Delete; palette: type "rename deploy")
  • Privileged access consent: /?mock=1&proto=clusters&privilege=1 (press a window's pill, Host tab, "Access level: Limited"; palette: "Mockup: privilege - ..." picks the machine's behaviour: sudo, doas, only root, not allowed, wrong password first, offline, no answer, several selected)
  • Both: &actions=1&privilege=1

Owner's words (2026-10-14)

  1. "how do I rename/remove a host or session easily? i feel like i should be able to do that via the sidebar easily. basically double tap/double click on anything to rename it in place. and when hovered, I should get an action pill towards the right end of that item like info, rename, remove (all icons + warm tooltips)?"
  2. "in the machine info pill, connection or session i don't know where we should display something like 'privileged agent' or something more user friendly with a tick or a cross. clicking on which should ask us, 'Currently the infraternal agent runs unprivileged. While privileged, it can manage a lot more. Do you consent privileged access?' with 3 icons ?, X, Tick. ? should link to our docs. X cancels, Tick starts the flow in-line and asks for either the sudo/doas password if those tools are installed and the user can sudo/doas, otherwise asks for root password directly. I want this mocked up."

Screenshots (2x, ink and paper, 1440x900, 393x852, 375x667; all states are in my scratch dir, ten key ones here)

a1-desktop-ink-3-tooltip.png
a1-desktop-ink-5-rename-session.png
a1-desktop-paper-7-remove-host.png
a1-phone-ink-1-swiped-session.png
a1-phone-ink-4-remove-host.png
p2-sudo-desktop-ink-1-consent.png
p2-sudo-desktop-ink-2-next.png
p2-wrong-desktop-ink-4-result.png
p2-bulk-desktop-ink-4-result.png
p2-denied-phone-paper-2-next.png

Decisions I made, and why

  • Touch reveals the pill by a swipe left, not a long press: the session manager's rows already do exactly this (shared swipeRow now), and a long press is the warm-tooltip gesture of every icon button and iOS text selection. Double tap renames (the sidebar waits 320 ms before closing on a phone so the second tap lands).
  • A double-click renames the row the first click was on: opening a detached session moves it to the top of the list, so the second click lands on another row (found and fixed).
  • Hosts keep their own name for everything that keys on it; the rename is a label shown in the sidebar, the machine popover and the window titles. Empty restores the default (a session's automatic name, a host's hostname).
  • Remove host follows D219 (uninstalls the infraternal agent by default) and says so in plain words; one quiet line, "Keep the infraternal agent installed", turns the uninstall off. Remove session says what ends (the program running in it).
  • Privilege state lives in the machine popover's Host tab as "Access level", a tick and "Full access" or a quiet cross and "Limited", under Login. It is the only place the machine's facts already are, it is findable (palette: "Allow full access on "), and the consent opens in place under the row (D141 H14: no popup, never automatic).
  • Password step: "Your sudo password for kayg" / "Your doas password for kayg" when the machine says so, otherwise "The root password for "; when sudo is installed but not allowed, the same step says so in one line and asks for root (automatic fallback, no dead end). One quiet line: used once, never stored or logged. Wrong password: in place, field emptied, no shake. Offline or no answer: nothing changed, retry or close. Several selected: one consent and one password for all N hosts that allow sudo, one progress line each (H13).
  • Info/Rename/Remove and the consent's three icons all use the shared warm tooltips and the shared confirmation buttons (tick primary, cross quiet).

Measurements (host load average 57 to 72 during the runs, so absolute numbers are inflated; the comparison is the same page with and without the feature)

  • JavaScript transfer (D188, production build, mockups left out): critical 296,407 -> 296,597 B gzip (budget 307,200); deferred 49,046 -> 49,094 (51,200); on demand 84,043 -> 84,307 (largest chunk 15,946 -> 15,949 of 20,480). The mockup code itself is on demand and only in the preview build: sidebar actions 3.7 KB gzip, privilege 3.9 KB gzip.
  • Idle: 60 fps with the sidebar open, unchanged. Hover over rows: found a layout per hovered row and a blur inside the sidebar's blur; removed. Remaining extra cost is the paint of one small pill (about 1.4x the plain sidebar's own hover cost per move in the A/B run, no long task).
  • Pane text is crisp at rest (transform and opacity cleared once the spring ends; checked at 1.5 s).

Open questions for the owner

  1. Remove host: is "Keep the infraternal agent installed" (a quiet line) the right way to offer the uninstall, or should removal always uninstall (D219) with no choice?
  2. Opening a detached session moves it to the top of the sidebar list under the pointer. Should the list stay put while the sidebar is open (reorder when it closes)?
  3. The session manager's rows rename with F2 or their Rename button only: a click there chooses the session and closes the manager, so a double-click cannot work. Fine?
  4. The consent's ? link goes to /docs/privileges#access-level; that page does not exist yet (the mockup shows a toast). OK to write it with the feature?
  5. Should Info, Rename and Remove appear in the ? shortcuts panel (F2, Delete, Command or Ctrl I on a focused row)?
  6. Multi-select of machines (the "apply to all N hosts" case) has no place in the sidebar today; the mockup stands in for it from the palette. Where will selection live?
Branch `mockup-host-actions` (head 78ef9ef3, base `origin/mockup-preview-combo`), pushed, not merged, not deployed. Mockups need the owner's approval before product UI (D162). **Mockup URLs** (test double, `bun run build` with `INFRATERNAL_CLUSTERS_PREVIEW=1`, then the preview server): - Sidebar rename and remove: `/?mock=1&proto=clusters&actions=1` (open the sidebar; hover a row; double-click; F2; Delete; palette: type "rename deploy") - Privileged access consent: `/?mock=1&proto=clusters&privilege=1` (press a window's pill, Host tab, "Access level: Limited"; palette: "Mockup: privilege - ..." picks the machine's behaviour: sudo, doas, only root, not allowed, wrong password first, offline, no answer, several selected) - Both: `&actions=1&privilege=1` **Owner's words (2026-10-14)** 1. "how do I rename/remove a host or session easily? i feel like i should be able to do that via the sidebar easily. basically double tap/double click on anything to rename it in place. and when hovered, I should get an action pill towards the right end of that item like info, rename, remove (all icons + warm tooltips)?" 2. "in the machine info pill, connection or session i don't know where we should display something like 'privileged agent' or something more user friendly with a tick or a cross. clicking on which should ask us, 'Currently the infraternal agent runs unprivileged. While privileged, it can manage a lot more. Do you consent privileged access?' with 3 icons ?, X, Tick. ? should link to our docs. X cancels, Tick starts the flow in-line and asks for either the sudo/doas password if those tools are installed and the user can sudo/doas, otherwise asks for root password directly. I want this mocked up." **Screenshots (2x, ink and paper, 1440x900, 393x852, 375x667; all states are in my scratch dir, ten key ones here)** ![a1-desktop-ink-3-tooltip.png](https://git.kayg.org/attachments/981c60a0-2325-4807-822b-0658d6c56428) ![a1-desktop-ink-5-rename-session.png](https://git.kayg.org/attachments/7a925657-f58a-405b-b57a-4865b14734f8) ![a1-desktop-paper-7-remove-host.png](https://git.kayg.org/attachments/3ffb297e-253b-4a56-be39-8d43718a0b6a) ![a1-phone-ink-1-swiped-session.png](https://git.kayg.org/attachments/d87cab13-486c-4503-b0a3-439af716eb4e) ![a1-phone-ink-4-remove-host.png](https://git.kayg.org/attachments/07c7d57b-2a27-41ff-a639-636f6b55410b) ![p2-sudo-desktop-ink-1-consent.png](https://git.kayg.org/attachments/2815f72f-7fab-4700-add0-cf67c4dbfc94) ![p2-sudo-desktop-ink-2-next.png](https://git.kayg.org/attachments/83a6a4f0-5f56-4d69-a38a-6560cf7ebed7) ![p2-wrong-desktop-ink-4-result.png](https://git.kayg.org/attachments/d256dab1-0b4d-4372-a04e-ae63ab0a50b8) ![p2-bulk-desktop-ink-4-result.png](https://git.kayg.org/attachments/1da6c749-fb72-446a-ad0d-38b8411a0889) ![p2-denied-phone-paper-2-next.png](https://git.kayg.org/attachments/e0d17a2d-3ade-421a-a830-ad3d80e6b0a2) **Decisions I made, and why** - Touch reveals the pill by a swipe left, not a long press: the session manager's rows already do exactly this (shared `swipeRow` now), and a long press is the warm-tooltip gesture of every icon button and iOS text selection. Double tap renames (the sidebar waits 320 ms before closing on a phone so the second tap lands). - A double-click renames the row the first click was on: opening a detached session moves it to the top of the list, so the second click lands on another row (found and fixed). - Hosts keep their own name for everything that keys on it; the rename is a label shown in the sidebar, the machine popover and the window titles. Empty restores the default (a session's automatic name, a host's hostname). - Remove host follows D219 (uninstalls the infraternal agent by default) and says so in plain words; one quiet line, "Keep the infraternal agent installed", turns the uninstall off. Remove session says what ends (the program running in it). - Privilege state lives in the machine popover's Host tab as "Access level", a tick and "Full access" or a quiet cross and "Limited", under Login. It is the only place the machine's facts already are, it is findable (palette: "Allow full access on <host>"), and the consent opens in place under the row (D141 H14: no popup, never automatic). - Password step: "Your sudo password for kayg" / "Your doas password for kayg" when the machine says so, otherwise "The root password for <host>"; when sudo is installed but not allowed, the same step says so in one line and asks for root (automatic fallback, no dead end). One quiet line: used once, never stored or logged. Wrong password: in place, field emptied, no shake. Offline or no answer: nothing changed, retry or close. Several selected: one consent and one password for all N hosts that allow sudo, one progress line each (H13). - Info/Rename/Remove and the consent's three icons all use the shared warm tooltips and the shared confirmation buttons (tick primary, cross quiet). **Measurements** (host load average 57 to 72 during the runs, so absolute numbers are inflated; the comparison is the same page with and without the feature) - JavaScript transfer (D188, production build, mockups left out): critical 296,407 -> 296,597 B gzip (budget 307,200); deferred 49,046 -> 49,094 (51,200); on demand 84,043 -> 84,307 (largest chunk 15,946 -> 15,949 of 20,480). The mockup code itself is on demand and only in the preview build: sidebar actions 3.7 KB gzip, privilege 3.9 KB gzip. - Idle: 60 fps with the sidebar open, unchanged. Hover over rows: found a layout per hovered row and a blur inside the sidebar's blur; removed. Remaining extra cost is the paint of one small pill (about 1.4x the plain sidebar's own hover cost per move in the A/B run, no long task). - Pane text is crisp at rest (transform and opacity cleared once the spring ends; checked at 1.5 s). **Open questions for the owner** 1. Remove host: is "Keep the infraternal agent installed" (a quiet line) the right way to offer the uninstall, or should removal always uninstall (D219) with no choice? 2. Opening a detached session moves it to the top of the sidebar list under the pointer. Should the list stay put while the sidebar is open (reorder when it closes)? 3. The session manager's rows rename with F2 or their Rename button only: a click there chooses the session and closes the manager, so a double-click cannot work. Fine? 4. The consent's ? link goes to `/docs/privileges#access-level`; that page does not exist yet (the mockup shows a toast). OK to write it with the feature? 5. Should Info, Rename and Remove appear in the `?` shortcuts panel (F2, Delete, Command or Ctrl I on a focused row)? 6. Multi-select of machines (the "apply to all N hosts" case) has no place in the sidebar today; the mockup stands in for it from the palette. Where will selection live?
Author
Owner

Designer round on mockup-preview-combo (base 3b8666f71). Commits, all pushed:

  • 3c4dd2112 palette: every query word matches on its own, punctuation ignored, one typo forgiven
  • b04d1bf51 ui: the window pill hugs its content (D271)
  • cfd7dd97b palette: hovering a row never scrolls the list
  • 93b6fc795 mockup(ui): a privilege scenario opens its consent at once; the consent's tips stay clear of the field
  • a77a7ed44 mockup(ui): privilege scenario hints fit the palette row

Findings

  1. Pill gap: the session name's flex-basis was a fixed 10ch, but the pill's width came from the name's own, shorter width. A name under 10ch always wrapped onto the clipped second line and still widened the pill, so the dot sat far from the host. Fix: basis = min(10ch, name length), a one-observer fallback that removes a name that cannot show, and no pill space for empty or generic names (shell names, default).
  2. Palette wheel: the palette is not inside the canvas viewport (palette > terminal-screen, viewport is a sibling), so the canvas wheel handlers never see its events; session-ui's wheel close is on the viewport too. I could not reproduce a dead wheel in Chromium or Firefox. In WebKit a wheel scroll with a jittering pointer stalled (100,100,179,200,284,300); the cause is hover selection calling scrollIntoView. Hover no longer scrolls: 92,200,300,400. Production has the same code (core-screen.ts), so it has this too. Not verified on a real Safari or trackpad.
  3. mockup privilege found nothing because the whole query was one substring and the colon in "Mockup: privilege" broke it. The entries are registered at page load; loading was not the cause.
    4/5. Scenarios open build-01's consent at once (password step for doas/root/denied, unreachable step for offline, bulk selection for bulk); hints rewritten. The consent buttons' warm tips go below the buttons, so never over the field; the shared tooltip now flips a wanted side only when it has no room.

Gates

  • bun unit tests: palette-match 19 pass, window-card 19 pass; the ts gate's unit run: 293 pass, 1 fail (stops.test.ts "an empty graph rests on its centre", also fails on the base).
  • Playwright: window-pill-fit 2 pass, host-actions-mockup privileged 9 pass, palette-wheel 1 pass. tsc clean.
  • scripts/verify iterate stops at its first gate, the repository artifact size gate: FAIL (391 files over 200000 bytes), all committed review captures from earlier rounds (e.g. 3385f4e03), none from this work. I ran the later steps by hand: release deploy tests, latency self-test, vendored checksums, tracer tsc, unit tests, build and transfer budget. The Rust gates of iterate were not run.
  • D188 budgets (not changed): critical gzip 296602 -> 297245 (+643) of 307200, brotli 246908 -> 247539 (+631) of 256000; critical identity +1581 bytes; deferred gzip +270 of 51200. onDemand class unchanged.

Evidence (mono UI, ink and paper, 1440x900, 393x852, 375x667): attached above. Pill "before" is the old look reproduced by switching the new rules off.
palette-mockup-before-wheel-d-ink.png
palette-mockup-before-wheel-d-paper.png
palette-mockup-before-wheel-p375-paper.png
palette-mockup-before-wheel-p393-ink.png
palette-typo-privlege-d-ink.png
palette-typo-privlege-d-paper.png
palette-typo-privlege-p375-paper.png
palette-typo-privlege-p393-ink.png
palette-wheel-scrolled-d-ink.png
palette-wheel-scrolled-d-paper.png
palette-wheel-scrolled-p375-paper.png
palette-wheel-scrolled-p393-ink.png
palette-words-mockup-privilege-d-ink.png
palette-words-mockup-privilege-d-paper.png
palette-words-mockup-privilege-p375-paper.png
palette-words-mockup-privilege-p393-ink.png
pill-after-ink.png
pill-after-paper.png
pill-before-ink.png
pill-before-paper.png
pill-long-ink.png
pill-long-paper.png
privilege-bulk-d-ink.png
privilege-bulk-d-paper.png
privilege-bulk-p375-paper.png
privilege-bulk-p393-ink.png
privilege-offline-d-ink.png
privilege-offline-d-paper.png
privilege-offline-p375-paper.png
privilege-offline-p393-ink.png
privilege-root-d-ink.png
privilege-root-d-paper.png
privilege-root-p375-paper.png
privilege-root-p393-ink.png
privilege-sudo-d-ink.png
privilege-sudo-d-paper.png
privilege-sudo-p375-paper.png
privilege-sudo-p393-ink.png
privilege-tooltip-clear-of-field-d-ink.png
privilege-tooltip-clear-of-field-d-paper.png

Designer round on `mockup-preview-combo` (base 3b8666f71). Commits, all pushed: - 3c4dd2112 palette: every query word matches on its own, punctuation ignored, one typo forgiven - b04d1bf51 ui: the window pill hugs its content (D271) - cfd7dd97b palette: hovering a row never scrolls the list - 93b6fc795 mockup(ui): a privilege scenario opens its consent at once; the consent's tips stay clear of the field - a77a7ed44 mockup(ui): privilege scenario hints fit the palette row Findings 1. Pill gap: the session name's flex-basis was a fixed 10ch, but the pill's width came from the name's own, shorter width. A name under 10ch always wrapped onto the clipped second line and still widened the pill, so the dot sat far from the host. Fix: basis = min(10ch, name length), a one-observer fallback that removes a name that cannot show, and no pill space for empty or generic names (shell names, default). 2. Palette wheel: the palette is not inside the canvas viewport (palette > terminal-screen, viewport is a sibling), so the canvas wheel handlers never see its events; session-ui's wheel close is on the viewport too. I could not reproduce a dead wheel in Chromium or Firefox. In WebKit a wheel scroll with a jittering pointer stalled (100,100,179,200,284,300); the cause is hover selection calling scrollIntoView. Hover no longer scrolls: 92,200,300,400. Production has the same code (core-screen.ts), so it has this too. Not verified on a real Safari or trackpad. 3. `mockup privilege` found nothing because the whole query was one substring and the colon in "Mockup: privilege" broke it. The entries are registered at page load; loading was not the cause. 4/5. Scenarios open build-01's consent at once (password step for doas/root/denied, unreachable step for offline, bulk selection for bulk); hints rewritten. The consent buttons' warm tips go below the buttons, so never over the field; the shared tooltip now flips a wanted side only when it has no room. Gates - bun unit tests: palette-match 19 pass, window-card 19 pass; the ts gate's unit run: 293 pass, 1 fail (stops.test.ts "an empty graph rests on its centre", also fails on the base). - Playwright: window-pill-fit 2 pass, host-actions-mockup privileged 9 pass, palette-wheel 1 pass. tsc clean. - `scripts/verify iterate` stops at its first gate, the repository artifact size gate: FAIL (391 files over 200000 bytes), all committed review captures from earlier rounds (e.g. 3385f4e03), none from this work. I ran the later steps by hand: release deploy tests, latency self-test, vendored checksums, tracer tsc, unit tests, build and transfer budget. The Rust gates of iterate were not run. - D188 budgets (not changed): critical gzip 296602 -> 297245 (+643) of 307200, brotli 246908 -> 247539 (+631) of 256000; critical identity +1581 bytes; deferred gzip +270 of 51200. onDemand class unchanged. Evidence (mono UI, ink and paper, 1440x900, 393x852, 375x667): attached above. Pill "before" is the old look reproduced by switching the new rules off. ![palette-mockup-before-wheel-d-ink.png](https://git.kayg.org/attachments/61134056-3c91-48f9-9290-09515a62c535) ![palette-mockup-before-wheel-d-paper.png](https://git.kayg.org/attachments/81cb886b-183d-4922-8c93-37ff3132f9b4) ![palette-mockup-before-wheel-p375-paper.png](https://git.kayg.org/attachments/19a538fd-edf7-4892-b072-30ee6cd0dbc1) ![palette-mockup-before-wheel-p393-ink.png](https://git.kayg.org/attachments/51dbe11b-cd31-43e2-8764-e8a566cfa703) ![palette-typo-privlege-d-ink.png](https://git.kayg.org/attachments/303e2c45-de7d-42b0-bb23-de789d63b360) ![palette-typo-privlege-d-paper.png](https://git.kayg.org/attachments/77d1399f-17e5-4515-881e-8359dc2e2fbe) ![palette-typo-privlege-p375-paper.png](https://git.kayg.org/attachments/8ce6adbf-4ca1-461b-88c1-3b43d06865c1) ![palette-typo-privlege-p393-ink.png](https://git.kayg.org/attachments/da320709-2d11-4f73-8146-1ad0c766a4ad) ![palette-wheel-scrolled-d-ink.png](https://git.kayg.org/attachments/4ed89843-cf5e-4d82-9849-572c3b985f39) ![palette-wheel-scrolled-d-paper.png](https://git.kayg.org/attachments/a6f58424-a10e-410c-a602-eae9dd7d5d23) ![palette-wheel-scrolled-p375-paper.png](https://git.kayg.org/attachments/9fba4d6d-54a6-4848-aae5-6b1ca63a3c0d) ![palette-wheel-scrolled-p393-ink.png](https://git.kayg.org/attachments/d8d2f58c-63e3-4404-9c44-a708070fcca8) ![palette-words-mockup-privilege-d-ink.png](https://git.kayg.org/attachments/75598118-9948-4a21-9750-62c928252cd3) ![palette-words-mockup-privilege-d-paper.png](https://git.kayg.org/attachments/540b5282-f693-4b4d-b283-23cd100eec37) ![palette-words-mockup-privilege-p375-paper.png](https://git.kayg.org/attachments/52f23965-1187-4296-8cbd-7e2ba46da613) ![palette-words-mockup-privilege-p393-ink.png](https://git.kayg.org/attachments/efbacc90-4c67-4502-85c6-accb39b3d077) ![pill-after-ink.png](https://git.kayg.org/attachments/5022a10e-a42a-484c-8929-e3a2d754fe6c) ![pill-after-paper.png](https://git.kayg.org/attachments/dec5f153-a169-4f06-b07c-2e35c8681806) ![pill-before-ink.png](https://git.kayg.org/attachments/1228cf9e-7340-489f-b0c0-296baeff6b57) ![pill-before-paper.png](https://git.kayg.org/attachments/00777671-1c5b-4e87-9027-bd911705b3d3) ![pill-long-ink.png](https://git.kayg.org/attachments/617583f1-389f-4020-923d-529ed6bd0a20) ![pill-long-paper.png](https://git.kayg.org/attachments/f63c3328-8bc9-4dae-a4a4-351e0efd1d0a) ![privilege-bulk-d-ink.png](https://git.kayg.org/attachments/02697da1-65e6-4bd1-a1a2-965f022c382a) ![privilege-bulk-d-paper.png](https://git.kayg.org/attachments/cf8837ef-abac-4207-83c6-e0a2652e78dd) ![privilege-bulk-p375-paper.png](https://git.kayg.org/attachments/02726f6a-1f90-4f41-b175-cc6f9f590236) ![privilege-bulk-p393-ink.png](https://git.kayg.org/attachments/8badb723-935c-46df-b518-3137edf7c722) ![privilege-offline-d-ink.png](https://git.kayg.org/attachments/033203c2-27fa-4b88-b6f6-f63036e4c673) ![privilege-offline-d-paper.png](https://git.kayg.org/attachments/c8cbd8a5-8dde-4d30-ae28-7d8295a70118) ![privilege-offline-p375-paper.png](https://git.kayg.org/attachments/8dddf351-6ea8-4f82-9270-7250b8231410) ![privilege-offline-p393-ink.png](https://git.kayg.org/attachments/856800ff-14d1-4eed-9e73-34b6c488b2db) ![privilege-root-d-ink.png](https://git.kayg.org/attachments/a05ebbe3-e963-48af-ace3-b17e0afa876c) ![privilege-root-d-paper.png](https://git.kayg.org/attachments/27efef85-ca65-4e8a-9752-057ba176a953) ![privilege-root-p375-paper.png](https://git.kayg.org/attachments/628a8d23-0278-449f-a958-8b258127067f) ![privilege-root-p393-ink.png](https://git.kayg.org/attachments/852e9f29-b72b-4485-96ea-8f8b7baaa203) ![privilege-sudo-d-ink.png](https://git.kayg.org/attachments/b3295809-6811-4c04-b7b5-028cd9c0fa7e) ![privilege-sudo-d-paper.png](https://git.kayg.org/attachments/20a2fba4-aa1b-40c4-8a6e-e7bb6f7ccd88) ![privilege-sudo-p375-paper.png](https://git.kayg.org/attachments/aa924906-55b3-4305-a4c0-2ad5b4524d2d) ![privilege-sudo-p393-ink.png](https://git.kayg.org/attachments/017ac5a5-90f4-4426-9754-b6ae13026ad1) ![privilege-tooltip-clear-of-field-d-ink.png](https://git.kayg.org/attachments/d16a2ca6-2e3e-4825-b790-354938c50095) ![privilege-tooltip-clear-of-field-d-paper.png](https://git.kayg.org/attachments/d6aa96cd-02c3-4aa2-8dce-ef8ff6d0b790)
Author
Owner

Rebuilt on current dev for the owner's review. Branch mockups-2026-10-31, head 586cd2b13 (base dev 42ab33035, 2026-10-31). Test double only; nothing is real or saved. Not merged, not deployed. Old previews are gone; this replaces them.

Preview (NetBird, bound to infraternal-dev-02-std; index of every mockup: http://10.69.69.117:47650/mockups.html):

How: Sidebar: hover a row, double-click, F2, Delete. Consent: press a window's pill, Host tab, Access level; palette "Mockup: privilege" picks the machine's behaviour. (The consent half was approved in D286.)

Note: Rebased only.

Captures are Chromium on the production build of the mockup, taken by apps/tracer/test/capture-mockups.ts; I have read them for wrong states, not judged them as final.

Desktop 1440×900 @2x, ink

consent-sudo
remove-confirm
rename-in-place
root-password
row-hover-actions
several-machines
wrong-password-first

Desktop 1440×900 @2x, paper

consent-sudo
remove-confirm
rename-in-place
root-password
row-hover-actions
several-machines
wrong-password-first

Phone 393×852 @3x, ink

consent-sudo
remove-confirm
rename-in-place
root-password
row-hover-actions
several-machines
wrong-password-first

Phone 393×852 @3x, paper (first state only)

consent-sudo
row-hover-actions

Rebuilt on current dev for the owner's review. Branch `mockups-2026-10-31`, head `586cd2b13` (base dev `42ab33035`, 2026-10-31). Test double only; nothing is real or saved. Not merged, not deployed. Old previews are gone; this replaces them. **Preview** (NetBird, bound to infraternal-dev-02-std; index of every mockup: http://10.69.69.117:47650/mockups.html): - http://10.69.69.117:47650/?mock=1&proto=clusters&scene=3&actions=1 - http://10.69.69.117:47650/?mock=1&proto=clusters&scene=3&privilege=1 **How:** Sidebar: hover a row, double-click, F2, Delete. Consent: press a window's pill, Host tab, Access level; palette "Mockup: privilege" picks the machine's behaviour. (The consent half was approved in D286.) **Note:** Rebased only. Captures are Chromium on the production build of the mockup, taken by `apps/tracer/test/capture-mockups.ts`; I have read them for wrong states, not judged them as final. **Desktop 1440×900 @2x, ink** ![consent-sudo](https://git.kayg.org/attachments/96eedd7c-c537-4673-9baa-b072a6d7a06f) ![remove-confirm](https://git.kayg.org/attachments/afe2edb5-5ff8-49b8-8556-cec05648c480) ![rename-in-place](https://git.kayg.org/attachments/a58934e8-1f69-4548-b134-7946def5a76c) ![root-password](https://git.kayg.org/attachments/02f9376f-a31c-419b-82cb-f7942993ee59) ![row-hover-actions](https://git.kayg.org/attachments/bf359e9f-001f-4f03-ab43-549f97aa4843) ![several-machines](https://git.kayg.org/attachments/5032650c-8bcb-438e-8562-39bffbdd1edd) ![wrong-password-first](https://git.kayg.org/attachments/bcdd5d6b-e78d-404f-9f32-6e2dea7e491b) **Desktop 1440×900 @2x, paper** ![consent-sudo](https://git.kayg.org/attachments/ea27e5a4-2424-4757-a947-65e4d4ab67cb) ![remove-confirm](https://git.kayg.org/attachments/8da992e5-3269-488c-bdc5-23ff8c0a1e88) ![rename-in-place](https://git.kayg.org/attachments/bf8c92a0-2ecd-4747-80e8-47adb080d730) ![root-password](https://git.kayg.org/attachments/e92b5d63-ef96-4e80-8ea3-d22dc58c390a) ![row-hover-actions](https://git.kayg.org/attachments/cc0dbb11-89a6-4179-a83e-724b9d02e8b4) ![several-machines](https://git.kayg.org/attachments/b32bd474-54ab-4a31-9fa7-f67ab8db6f5b) ![wrong-password-first](https://git.kayg.org/attachments/5f457300-90fe-49b4-bc33-35c08b1d7801) **Phone 393×852 @3x, ink** ![consent-sudo](https://git.kayg.org/attachments/c073edc4-3719-45e0-9ca2-4cb4b40a0156) ![remove-confirm](https://git.kayg.org/attachments/70a326c2-ea6b-4d3a-8e5e-d6c46fc3a007) ![rename-in-place](https://git.kayg.org/attachments/6ee4e576-686e-4e71-9441-495c5438062c) ![root-password](https://git.kayg.org/attachments/b2590563-4eda-4d5e-a489-0f26f03a5456) ![row-hover-actions](https://git.kayg.org/attachments/877a00ea-1d05-4c50-a767-985a788fb9f0) ![several-machines](https://git.kayg.org/attachments/d49c697e-9673-4b60-8c10-56305a26a297) ![wrong-password-first](https://git.kayg.org/attachments/72cfca43-3121-4477-a5e0-025623f49ac9) **Phone 393×852 @3x, paper (first state only)** ![consent-sudo](https://git.kayg.org/attachments/29c1355d-6eb7-45b0-8ae6-710811a140e9) ![row-hover-actions](https://git.kayg.org/attachments/df2af533-9518-45a6-b64b-e294f40573d3)
Author
Owner

Update to the comment above: the branch head is now 57871651f (the preview at http://10.69.69.117:47650/mockups.html runs it). The captures above were taken one build earlier; the only difference is that the window terminals there hide their cursor until attached, a production change that was since removed from the branch. Gate output, what is left out, and the budget finding: #272.

Update to the comment above: the branch head is now `57871651f` (the preview at http://10.69.69.117:47650/mockups.html runs it). The captures above were taken one build earlier; the only difference is that the window terminals there hide their cursor until attached, a production change that was since removed from the branch. Gate output, what is left out, and the budget finding: #272.
Author
Owner

J106 start (UI job, Sonnet 5.5, 2026-11-02). Owner approved today: "both sidebar rows and rename are approved" (#242 row menu and swipes; #215 sidebar rename/remove). This job builds both into the real product, no mock flag.

Branch row-actions-242-215, worktree ~/Developer/Worktrees/infraternal-j106, base origin/dev = 17d282a18. Reference: origin/mockups-2026-10-31 (heads 4c389a2f4 / 97597e740, J52 last iteration). Builds and browsers on the VMs only, through build-slot.

Coordination with J73 (#292)

git branch -r on origin and forgejo has no sidebar-292 (J73's branch lives on code-minipc01, not pushed), so there is nothing to build on. I keep to row actions: new shared files packages/ui/src/row-menu.ts, row-swipe.ts, row-confirm.ts (ported unchanged from the mockup head, so J73's planned copy of row-confirm.ts is the same file and merges clean if it is taken from 97597e740), toolbar/row-actions.ts, and a small change to toolbar/tree.ts. I do not touch session-tree.on-demand.ts (host rows without sessions, +, selection, drag): that stays J73's. J73: please do not copy row-confirm.ts from before 97597e740 (the alignTo fix).

What the code has, what it lacks (evidence)

Piece State on dev Plan
Session rename inv.rename live; syncs through the canvas layout stream (#293 bf6d4cd52, on dev) reuse
Host removal adapters.hostRemoval exists (product.ts:201), server /remote/hosts/remove/{start,finish} (passkey proof) reuse as the mockup's host-row "Remove host…"
End session live-inventory.ts has can.end = false ("cannot end sessions until #100"); the server never signals a shim (contracts/wire.md), no leg-A verb missing backend. I build the in-row End + left swipe behind can.end (the mockup already does), so they appear when #100 lands. Not inventing a backend.
Host rename no server route, hosts are named by their enrolment missing backend: host rename is left out (session rename only); the mock's host rename field is not ported.
Privileged access (Access level row, consent) no route to change an agent's privilege missing backend. Port the Access level row and consent card against an adapters.privilege interface that the product leaves unset (row absent) until the endpoint exists; Playwright drives a test adapter.

Plan

Step What Est.
1 Port row-menu / row-swipe / row-confirm / spring fling, CSS, tooltips needed (one commit per concern, no unrelated mockup-branch change: no UI-size rem conversion, no Mesh) 2 h
2 row-actions.ts + tree.ts: session/host/window rows; palette rows for the focused row 1.5 h
3 #215: hover action pill, double-click and F2 rename (shared inline-rename), Delete, remove-host in-row confirm 2 h
4 #215: Access level row in the Host tab + consent card, adapter-driven 2 h
5 Playwright, desktop 1440x900 and phone 393x852: right-click, long-press, Shift+F10, trackpad sideways scroll, left/right swipe, in-row End, hover pill, double-click, F2, Delete, consent; alignment (+-0.5 px) and in-row confirmation checks 3 h
6 Production-build screenshots DPR 2/3, scripts/verify fast, push, comment 2 h

Total about 12.5 h. I stop and report at 25 h.

**J106 start** (UI job, Sonnet 5.5, 2026-11-02). Owner approved today: "both sidebar rows and rename are approved" (#242 row menu and swipes; #215 sidebar rename/remove). This job builds both into the real product, no mock flag. Branch `row-actions-242-215`, worktree `~/Developer/Worktrees/infraternal-j106`, base `origin/dev` = `17d282a18`. Reference: `origin/mockups-2026-10-31` (heads `4c389a2f4` / `97597e740`, J52 last iteration). Builds and browsers on the VMs only, through `build-slot`. ## Coordination with J73 (#292) `git branch -r` on origin and forgejo has **no** `sidebar-292` (J73's branch lives on code-minipc01, not pushed), so there is nothing to build on. I keep to row actions: new shared files `packages/ui/src/row-menu.ts`, `row-swipe.ts`, `row-confirm.ts` (ported unchanged from the mockup head, so J73's planned copy of `row-confirm.ts` is the same file and merges clean if it is taken from `97597e740`), `toolbar/row-actions.ts`, and a small change to `toolbar/tree.ts`. I do not touch `session-tree.on-demand.ts` (host rows without sessions, `+`, selection, drag): that stays J73's. J73: please do not copy `row-confirm.ts` from before `97597e740` (the alignTo fix). ## What the code has, what it lacks (evidence) | Piece | State on dev | Plan | |---|---|---| | Session rename | `inv.rename` live; syncs through the canvas layout stream (#293 `bf6d4cd52`, on dev) | reuse | | Host removal | `adapters.hostRemoval` exists (`product.ts:201`), server `/remote/hosts/remove/{start,finish}` (passkey proof) | reuse as the mockup's host-row "Remove host…" | | End session | `live-inventory.ts` has `can.end = false` ("cannot end sessions until #100"); the server never signals a shim (`contracts/wire.md`), no leg-A verb | **missing backend**. I build the in-row End + left swipe behind `can.end` (the mockup already does), so they appear when #100 lands. Not inventing a backend. | | Host rename | no server route, hosts are named by their enrolment | **missing backend**: host rename is left out (session rename only); the mock's host rename field is not ported. | | Privileged access (Access level row, consent) | no route to change an agent's privilege | **missing backend**. Port the Access level row and consent card against an `adapters.privilege` interface that the product leaves unset (row absent) until the endpoint exists; Playwright drives a test adapter. | ## Plan | Step | What | Est. | |---|---|---| | 1 | Port `row-menu` / `row-swipe` / `row-confirm` / spring fling, CSS, tooltips needed (one commit per concern, no unrelated mockup-branch change: no UI-size rem conversion, no Mesh) | 2 h | | 2 | `row-actions.ts` + tree.ts: session/host/window rows; palette rows for the focused row | 1.5 h | | 3 | #215: hover action pill, double-click and F2 rename (shared `inline-rename`), Delete, remove-host in-row confirm | 2 h | | 4 | #215: Access level row in the Host tab + consent card, adapter-driven | 2 h | | 5 | Playwright, desktop 1440x900 and phone 393x852: right-click, long-press, Shift+F10, trackpad sideways scroll, left/right swipe, in-row End, hover pill, double-click, F2, Delete, consent; alignment (+-0.5 px) and in-row confirmation checks | 3 h | | 6 | Production-build screenshots DPR 2/3, `scripts/verify fast`, push, comment | 2 h | Total about 12.5 h. I stop and report at 25 h.
Author
Owner

J106 progress (2026-11-02). Branch row-actions-242-215 (pushed), head bfaf96155; typecheck (bun run check) clean on dev-02 at 15bfac3c5.

Done, one commit per concern:

  1. 33e91b0c9 merge of sidebar-pill-259 (#259, scope added by the owner) onto origin/dev 17d282a18. Conflicts: the two inline-rename.ts modules (dev's account card and #259's) are now one field: #259's beginInlineRename / placeOverName / onDoubleActivate; dev's inlineRename(target, …) stays as a thin wrapper over it (account card unchanged), .ui-rename-input became .ui-inline-rename. Data path of a rename stays dev's (#293).
  2. 08c1cef27 shared row-menu.ts + row-confirm.ts (+ Spring.fling, window menu opening at a point, danger row), ported from 97597e740.
  3. 7eefe5674 toolbar/row-actions.ts + tree wiring: right-click, long press, Shift+F10/menu key, trackpad/touch swipes, End asks in its row, Rename through BranchedMenu.rename(id) (the shared field), palette rows for the focused row.
  4. 79da821ac hover action pill, F2, Delete (#215).
  5. kv-list panel + 15bfac3c5 Access level row and consent (#215, D286).
  6. bfaf96155 Playwright specs (row-menu, row-gestures, row-ask, replacement-alignment, row-swipe-continuity, sidebar-rename-remove, access-level).

Missing backends (not invented):

  • End session: the product inventory has can.end = false (#100; no leg-A verb, contracts/wire.md: the server never signals a shim). The in-row End, the left swipe and Delete-on-a-session are built behind can.end; in the product they do not appear until #100 lands (the test double can end).
  • Privileged access: no server endpoint raises the agent's privileges with a once-used secret. The row and consent are built against Adapters.privilege (sessions/privilege.ts, a PrivilegeService); the product leaves it unset, so the "Access level" row does not show. The test double implements it (?privilege=<scenario>).
  • Host removal and host rename are real: removal uses adapters.hostRemoval (/remote/hosts/remove/*, passkey flow, so it asks in its own card, not in the row); a host renames through mesh.node.rename (#259), so only a host the Mesh knows has Rename.
  • The consent's ? goes to the Docs view when it exists; there is no "privileges" docs page yet.

One decision for the owner: on touch the #215 mockup revealed the action pill with a left swipe; #242 gives the left swipe to End/Close. I kept #242's swipes and the long-press menu on touch; the hover pill is pointer-only.

Next: run the new specs, production-build screenshots at DPR 2/3, scripts/verify fast.

**J106 progress** (2026-11-02). Branch `row-actions-242-215` (pushed), head `bfaf96155`; typecheck (`bun run check`) clean on dev-02 at `15bfac3c5`. Done, one commit per concern: 1. `33e91b0c9` merge of `sidebar-pill-259` (#259, scope added by the owner) onto `origin/dev` `17d282a18`. Conflicts: the two `inline-rename.ts` modules (dev's account card and #259's) are now **one** field: #259's `beginInlineRename` / `placeOverName` / `onDoubleActivate`; dev's `inlineRename(target, …)` stays as a thin wrapper over it (account card unchanged), `.ui-rename-input` became `.ui-inline-rename`. Data path of a rename stays dev's (#293). 2. `08c1cef27` shared `row-menu.ts` + `row-confirm.ts` (+ `Spring.fling`, window menu opening at a point, danger row), ported from `97597e740`. 3. `7eefe5674` `toolbar/row-actions.ts` + tree wiring: right-click, long press, Shift+F10/menu key, trackpad/touch swipes, End asks in its row, Rename through `BranchedMenu.rename(id)` (the shared field), palette rows for the focused row. 4. `79da821ac` hover action pill, F2, Delete (#215). 5. kv-list `panel` + `15bfac3c5` Access level row and consent (#215, D286). 6. `bfaf96155` Playwright specs (row-menu, row-gestures, row-ask, replacement-alignment, row-swipe-continuity, sidebar-rename-remove, access-level). **Missing backends (not invented):** - **End session**: the product inventory has `can.end = false` (#100; no leg-A verb, `contracts/wire.md`: the server never signals a shim). The in-row End, the left swipe and Delete-on-a-session are built behind `can.end`; in the product they do not appear until #100 lands (the test double can end). - **Privileged access**: no server endpoint raises the agent's privileges with a once-used secret. The row and consent are built against `Adapters.privilege` (`sessions/privilege.ts`, a `PrivilegeService`); the product leaves it unset, so the "Access level" row does not show. The test double implements it (`?privilege=<scenario>`). - **Host removal** and **host rename** are real: removal uses `adapters.hostRemoval` (`/remote/hosts/remove/*`, passkey flow, so it asks in its own card, not in the row); a host renames through `mesh.node.rename` (#259), so only a host the Mesh knows has Rename. - The consent's `?` goes to the Docs view when it exists; there is no "privileges" docs page yet. **One decision for the owner:** on touch the #215 mockup revealed the action pill with a left swipe; #242 gives the left swipe to End/Close. I kept #242's swipes and the long-press menu on touch; the hover pill is pointer-only. Next: run the new specs, production-build screenshots at DPR 2/3, `scripts/verify fast`.
Author
Owner

J106 evidence for #215: sidebar rename/remove and the privileged-access consent, in the real product (production build, Chromium headless, test double; branch row-actions-242-215). 1440×900 DPR 2 and 393×852 DPR 3, ink and paper; read at full size, not judged as final.

Shown: the hover pill (Rename and End on a session, left of its marks so their tooltips stay; the pill's third button is the stop square, as in the row menu), the rename field at the label's x, the host menu, and the Access level row with the consent (question, ?/×/✓, then the password step) on desktop and phone.

Note: the consent and its row appear only on an instance that provides a privilege service; the product has none yet (no server endpoint), so the screenshots are of the test double (?privilege=sudo).

desktop-ink-hover-pill
desktop-ink-rename-field
desktop-ink-host-menu
desktop-ink-access-level-limited
desktop-ink-consent
desktop-ink-consent-password
desktop-paper-hover-pill
desktop-paper-rename-field
desktop-paper-consent
phone-ink-consent
phone-paper-consent

**J106 evidence for #215: sidebar rename/remove and the privileged-access consent, in the real product** (production build, Chromium headless, test double; branch `row-actions-242-215`). 1440×900 DPR 2 and 393×852 DPR 3, ink and paper; read at full size, not judged as final. Shown: the hover pill (Rename and End on a session, left of its marks so their tooltips stay; the pill's third button is the stop square, as in the row menu), the rename field at the label's x, the host menu, and the Access level row with the consent (question, ?/×/✓, then the password step) on desktop and phone. Note: the consent and its row appear only on an instance that provides a privilege service; the product has none yet (no server endpoint), so the screenshots are of the test double (`?privilege=sudo`). ![desktop-ink-hover-pill](https://git.kayg.org/attachments/2a1bbe47-df8d-471b-831a-d03351b2437a) ![desktop-ink-rename-field](https://git.kayg.org/attachments/7121bd82-861a-4b09-ba27-fb01aba7322c) ![desktop-ink-host-menu](https://git.kayg.org/attachments/602a9d30-126f-4be9-8a34-64b1d322b80e) ![desktop-ink-access-level-limited](https://git.kayg.org/attachments/08f94515-ea14-40ee-aa5c-62ab1c797bcb) ![desktop-ink-consent](https://git.kayg.org/attachments/764c620f-6df2-4c22-82e7-3513251c8c58) ![desktop-ink-consent-password](https://git.kayg.org/attachments/3b12d5e7-a289-4353-8e6a-7078166c760f) ![desktop-paper-hover-pill](https://git.kayg.org/attachments/a3d16a4f-5e5f-4a4e-9ca6-2104b5663fc5) ![desktop-paper-rename-field](https://git.kayg.org/attachments/751f6ce2-71b8-4e3a-91d6-3790c14848a8) ![desktop-paper-consent](https://git.kayg.org/attachments/7c2d334c-b391-4189-9978-677a4a4112f5) ![phone-ink-consent](https://git.kayg.org/attachments/19fe342d-9259-4aa3-9b19-e773504cab75) ![phone-paper-consent](https://git.kayg.org/attachments/b206ce78-bfd1-43ec-9ea7-fb4d925f775c)
Author
Owner

J106 finish. Branch row-actions-242-215, head 82c54c678, pushed (base origin/dev 17d282a18, merged with dev 99668d8ab along the way). Built into the real product, no mock flag. Evidence screenshots (production build, DPR 2 and 3) are in the two comments above on #242 and #215.

What is built

  • #259 brought in as the base: merge of sidebar-pill-259; the two inline-rename.ts modules (dev's and #259's) are now one field (#259's beginInlineRename; dev's inlineRename is a thin wrapper, .ui-rename-input is gone). Rename's data path is dev's (#293).
  • #242: right-click, long press, Shift+F10 / menu key, trackpad sideways scroll, left and right swipes; End asks in its own row starting at the label's x; the continuous right swipe; every action of the focused row is a palette row. Escape closes a revealed row or the question first.
  • #215: hover pill (Rename, End/Remove; pointer only), double click and double tap rename (through the shared field), F2, Delete, remove host; "Access level" row and the consent from a window's pill → Host tab; palette "Allow full access on ".
  • Row actions, menu, swipes and pill load with the sidebar (on-demand chunk), not with the canvas.

Missing backends (not invented, see earlier comment): End session (inventory can.end is false, #100): End, the left swipe and Delete-on-a-session do not appear in the product until it lands; privileged access (no server endpoint): the row and consent exist behind Adapters.privilege, unset in the product; host rename works only for hosts the Mesh knows (mesh.node.rename); host removal uses the existing passkey flow (its own card, not in-row). The consent's "?" goes to the Docs view; there is no privileges docs page yet.

For the owner: on touch #215's mockup revealed the pill with a left swipe, #242 gives the left swipe to End/Close: I kept #242's swipes and the long-press menu on touch; the pill is hover-only.

Tests (Playwright, production build, test double; desktop 1440×900 and phone 393×852): row-menu, row-gestures, row-ask, replacement-alignment (≤ 0.5 px, DPR 2 and 3), row-swipe-continuity, sidebar-rename-remove, access-level, plus #259's mesh-rename adapted to dev. Last full set (head 91440dc79): 94 passed, 4 failed: 3 in sessions-canvas.spec.ts (lines 170, 370, 479) fail identically on unmodified origin/dev 17d282a18 (measured: 3 failed / 19 passed there), and the 4th (double click on an exited session whose list re-sorts) was fixed afterwards; sidebar-rename-remove.spec.ts on 82c54c678: 10 passed.

Gate: scripts/verify fast does NOT pass: one failure, the deferred JavaScript budget. The full gate ran on 8c548ce0c (dev-02); since then only TypeScript changed (git diff 8c548ce0c HEAD -- crates Cargo.toml Cargo.lock scripts is empty). Rust part, verbatim:

| Rust gate                 | Result       |
+---------------------------+--------------+
| fmt                       | PASS         |
| VT native fmt             | PASS         |
| CLI plugin compile-out    | PASS         |
| clippy                    | PASS         |
| SSH runtime test helper   | PASS         |
| real-binary fixture paths | PASS         |
| test                      | PASS         |
| SSH inode reuse fixture   | PASS         |
| shim process-per-user     | PASS         |
| cargo-deny                | PASS         |
| gate architecture         | PASS         |
| static builds             | PASS         |
+---------------------------+--------------+
x86_64: size 22422864 bytes; baseline 22418256; delta +4608 bytes (report only, D243 amended 2026-11-01)
aarch64: size 20454096 bytes; baseline 20453280; delta +816 bytes (report only)

TypeScript part re-run on the final head (82c54c678, tsc and every bun test group: 0 fail), then:

JavaScript transfer, critical: identity=1006850 gzip=313843/327680 brotli=261710/273408 bytes
JavaScript transfer, deferred: identity=179031 gzip=62025/61440 brotli=54702/55296 bytes
JavaScript transfer, onDemand: identity=304405 gzip=121081/20480 brotli=107917/18432 bytes
JavaScript transfer, onDemand largest chunk chunk-f7w4g32v.js: gzip=15464/20480 brotli=13803/18432 bytes (class total gzip=121081 brotli=107917, not limited)
error: production deferred JavaScript exceeds its compressed transfer budget

Measured against dev (99668d8ab, same command): critical 311,046 gzip, deferred 61,276 (164 B under its cap), onDemand 109,459. Before I moved the row code on demand the deferred class was 68,009; now it is 62,025, i.e. 585 B gzip over the 60 KiB cap (brotli is under). By the source diff the rest is the Mesh host rename of #259 and the tree's rename routing; I did not measure it per module, and I did not touch the gate. Needs the orchestrator/owner: ratchet the deferred cap (D188 rule: measured size rounded up to a KiB plus a margin) or tell me what to cut.

Commits (oldest first):

82c54c678 test(tracer): the rename double click aims at the name, as a person does (#215)
03701a66c ui: a double click is still one when the second release lands on the gap the rebuilt list left (#259, #215)
91440dc79 details: the consent pane stays in view while it grows into the popover (#215)
86ad8dc65 ui: a double click renames the row the first press was on, even if the list re-sorted under the pointer (#259, #215)
73f0710cc perf: the row menu, swipes and hover pill load with the sidebar, not with the canvas (D188, #242, #215)
8e0150821 sidebar: the pill ends a session with the stop square, and hides while the name is being edited (#215)
2f4ab7945 details: the consent pane spans the whole row and is brought into view when it opens (#215)
a61603233 ui: a double click renames even when the first click rebuilt the list (#259, #215)
8c548ce0c sidebar: the hover pill sits left of a row's marks and notes, which keep their tooltips (#215, #259)
1f041d24f Merge origin/dev (99668d8ab) into row-actions-242-215
30094bf01 test(tracer): opt-in review captures of the row menu, swipes, rename and consent (#242, #215)
2fe75432f test(tracer): #259's rename checks follow dev's connection icon and the renamable sessions (#259, #212, #215)
fa3131756 ui: a rename field takes the free room after a name that a clip cuts short (#259, #215)
99668d8ab Merge decisions-2026-11-02: D163 GotaTun on Linux, D317 docs structure
1d8af5c4a decisions: D163 Linux engine is GotaTun; D317 user docs structure
cdff83eb0 Merge taste-2026-11-02: owner review rows from 2026-11-02
9ef53bbb9 taste: windows start at the layout's size and never overlap
d67670c56 taste: descriptions say what a thing does, not what state it is in
d251c0ad8 taste: content sits inside its container with an even inset
69ba50588 taste: Settings rows scroll as a whole, not window by window
203fd67d3 taste: pill menus centre on their pill; no dead buttons
4c8e22611 taste: windows keep their natural width and the canvas scrolls; popovers follow their anchor
a479a8760 Merge size-baseline-ratchet: static size baselines match current dev (D243)
a8926703a verify: ratchet the static size baselines to the current dev measurement (D243)
c85e53e54 ui: a rename field on a fine pointer types in the size of the name it replaces (#259, #215)
3213ae21a palette: "Allow full access on <host>" opens the machine's consent (#215, D286)
3f2c9aeab test(tracer): every gesture and key of the sidebar's row menu, swipes, rename and consent (#242, #215)
0a4dd245b details: an "Access level" row and a consent that asks before the agent gets full access (#215, D286)
fea95e752 ui: a key/value row can carry a pane that opens under it (#215, D160)
5784444f1 sidebar: an action pill on hover, F2 renames and Delete removes the focused row (#215)
1b3992370 sidebar: right-click, long press, Shift+F10 and swipes on rows; End asks in its row (#242, D309)
a6498b99c ui: one row menu and swipe component, and the in-row question, for every list of rows (#242, D309)
294042b56 Merge fix-dev-red: dev green again after #291 (#307)
d7984ca7d ui: the anchored popover opens at a point, the window menu gets a danger row
9919b28b1 ui: a spring can be flung, so a released drag carries on from the finger's speed (#242, D70)
33e91b0c9 Merge sidebar-pill-259: rename Mesh hosts in place, detached icon, pinned account pill (#259)
502d0fc7e Stabilize clean shim exit observation under load #307
f15a74866 Fix J96 server test regressions after #291
66d25769d sidebar: the tick, cross and dot on a session row get a tooltip in plain words (#259)
e60f12434 test: the sidebar's tick, cross and dot explain themselves in a tooltip (#259)
44b3256f1 sidebar: a detached session shows an icon, not the word (#259)
648cc7774 test: a detached session in the sidebar shows an icon with a "Detached" tooltip (#259)
85cb920ee test: the rename field and the widened name pill never overlap the → pill (#259)
9fe1b7041 ui: the inline rename field stays inside the name's own box (#259)
c747d9fbb test: the rename field must not sit under the status dot (#259)
e86c2baf2 test: opt-in review captures of the pinned sidebar pill (#259)
845aa3e2c test: browser spec for renaming Mesh hosts in place (#259)
126c97190 palette: "Rename <host>" for the Mesh's hosts (#259)
16756de44 sidebar: rename a Mesh host by double click or double tap in the Views tree (#259)
cae06fe17 mesh: rename a host by double click or double tap on its card (#259)
ce31a7b52 ui: field placed over a name, and a double tap that survives a rebuilt list (#259)
e3d803ff2 mesh: rename a host through mesh.node.rename in the Mesh API adapter (#259)
711032f75 ui: one inline-rename field and one double-tap gesture for every in-place rename (#259)
0172555f7 sidebar: the account pill stays pinned once the inbox card has come and gone (#259)

VM cleanup: my Rust and tracer build dirs on dev-02 are deleted; worktrees ~/worktrees/j106* remain until the orchestrator prunes them.

**J106 finish.** Branch `row-actions-242-215`, head **`82c54c678`**, pushed (base `origin/dev` `17d282a18`, merged with dev `99668d8ab` along the way). Built into the real product, no mock flag. Evidence screenshots (production build, DPR 2 and 3) are in the two comments above on #242 and #215. **What is built** - #259 brought in as the base: merge of `sidebar-pill-259`; the two `inline-rename.ts` modules (dev's and #259's) are now one field (#259's `beginInlineRename`; dev's `inlineRename` is a thin wrapper, `.ui-rename-input` is gone). Rename's data path is dev's (#293). - #242: right-click, long press, Shift+F10 / menu key, trackpad sideways scroll, left and right swipes; End asks in its own row starting at the label's x; the continuous right swipe; every action of the focused row is a palette row. Escape closes a revealed row or the question first. - #215: hover pill (Rename, End/Remove; pointer only), double click and double tap rename (through the shared field), F2, Delete, remove host; "Access level" row and the consent from a window's pill → Host tab; palette "Allow full access on <host>". - Row actions, menu, swipes and pill load with the sidebar (on-demand chunk), not with the canvas. **Missing backends (not invented, see earlier comment):** End session (inventory `can.end` is false, #100): End, the left swipe and Delete-on-a-session do not appear in the product until it lands; privileged access (no server endpoint): the row and consent exist behind `Adapters.privilege`, unset in the product; host rename works only for hosts the Mesh knows (`mesh.node.rename`); host removal uses the existing passkey flow (its own card, not in-row). The consent's "?" goes to the Docs view; there is no privileges docs page yet. **For the owner:** on touch #215's mockup revealed the pill with a left swipe, #242 gives the left swipe to End/Close: I kept #242's swipes and the long-press menu on touch; the pill is hover-only. **Tests** (Playwright, production build, test double; desktop 1440×900 and phone 393×852): `row-menu`, `row-gestures`, `row-ask`, `replacement-alignment` (≤ 0.5 px, DPR 2 and 3), `row-swipe-continuity`, `sidebar-rename-remove`, `access-level`, plus #259's `mesh-rename` adapted to dev. Last full set (head `91440dc79`): 94 passed, 4 failed: 3 in `sessions-canvas.spec.ts` (lines 170, 370, 479) fail identically on unmodified `origin/dev` `17d282a18` (measured: 3 failed / 19 passed there), and the 4th (double click on an exited session whose list re-sorts) was fixed afterwards; `sidebar-rename-remove.spec.ts` on `82c54c678`: 10 passed. **Gate: `scripts/verify fast` does NOT pass: one failure, the deferred JavaScript budget.** The full gate ran on `8c548ce0c` (dev-02); since then only TypeScript changed (`git diff 8c548ce0c HEAD -- crates Cargo.toml Cargo.lock scripts` is empty). Rust part, verbatim: ``` | Rust gate | Result | +---------------------------+--------------+ | fmt | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | real-binary fixture paths | PASS | | test | PASS | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | static builds | PASS | +---------------------------+--------------+ x86_64: size 22422864 bytes; baseline 22418256; delta +4608 bytes (report only, D243 amended 2026-11-01) aarch64: size 20454096 bytes; baseline 20453280; delta +816 bytes (report only) ``` TypeScript part re-run on the final head (`82c54c678`, `tsc` and every `bun test` group: 0 fail), then: ``` JavaScript transfer, critical: identity=1006850 gzip=313843/327680 brotli=261710/273408 bytes JavaScript transfer, deferred: identity=179031 gzip=62025/61440 brotli=54702/55296 bytes JavaScript transfer, onDemand: identity=304405 gzip=121081/20480 brotli=107917/18432 bytes JavaScript transfer, onDemand largest chunk chunk-f7w4g32v.js: gzip=15464/20480 brotli=13803/18432 bytes (class total gzip=121081 brotli=107917, not limited) error: production deferred JavaScript exceeds its compressed transfer budget ``` Measured against dev (`99668d8ab`, same command): critical 311,046 gzip, deferred **61,276** (164 B under its cap), onDemand 109,459. Before I moved the row code on demand the deferred class was 68,009; now it is 62,025, i.e. **585 B gzip over the 60 KiB cap** (brotli is under). By the source diff the rest is the Mesh host rename of #259 and the tree's rename routing; I did not measure it per module, and I did not touch the gate. **Needs the orchestrator/owner: ratchet the deferred cap (D188 rule: measured size rounded up to a KiB plus a margin) or tell me what to cut.** Commits (oldest first): ``` 82c54c678 test(tracer): the rename double click aims at the name, as a person does (#215) 03701a66c ui: a double click is still one when the second release lands on the gap the rebuilt list left (#259, #215) 91440dc79 details: the consent pane stays in view while it grows into the popover (#215) 86ad8dc65 ui: a double click renames the row the first press was on, even if the list re-sorted under the pointer (#259, #215) 73f0710cc perf: the row menu, swipes and hover pill load with the sidebar, not with the canvas (D188, #242, #215) 8e0150821 sidebar: the pill ends a session with the stop square, and hides while the name is being edited (#215) 2f4ab7945 details: the consent pane spans the whole row and is brought into view when it opens (#215) a61603233 ui: a double click renames even when the first click rebuilt the list (#259, #215) 8c548ce0c sidebar: the hover pill sits left of a row's marks and notes, which keep their tooltips (#215, #259) 1f041d24f Merge origin/dev (99668d8ab) into row-actions-242-215 30094bf01 test(tracer): opt-in review captures of the row menu, swipes, rename and consent (#242, #215) 2fe75432f test(tracer): #259's rename checks follow dev's connection icon and the renamable sessions (#259, #212, #215) fa3131756 ui: a rename field takes the free room after a name that a clip cuts short (#259, #215) 99668d8ab Merge decisions-2026-11-02: D163 GotaTun on Linux, D317 docs structure 1d8af5c4a decisions: D163 Linux engine is GotaTun; D317 user docs structure cdff83eb0 Merge taste-2026-11-02: owner review rows from 2026-11-02 9ef53bbb9 taste: windows start at the layout's size and never overlap d67670c56 taste: descriptions say what a thing does, not what state it is in d251c0ad8 taste: content sits inside its container with an even inset 69ba50588 taste: Settings rows scroll as a whole, not window by window 203fd67d3 taste: pill menus centre on their pill; no dead buttons 4c8e22611 taste: windows keep their natural width and the canvas scrolls; popovers follow their anchor a479a8760 Merge size-baseline-ratchet: static size baselines match current dev (D243) a8926703a verify: ratchet the static size baselines to the current dev measurement (D243) c85e53e54 ui: a rename field on a fine pointer types in the size of the name it replaces (#259, #215) 3213ae21a palette: "Allow full access on <host>" opens the machine's consent (#215, D286) 3f2c9aeab test(tracer): every gesture and key of the sidebar's row menu, swipes, rename and consent (#242, #215) 0a4dd245b details: an "Access level" row and a consent that asks before the agent gets full access (#215, D286) fea95e752 ui: a key/value row can carry a pane that opens under it (#215, D160) 5784444f1 sidebar: an action pill on hover, F2 renames and Delete removes the focused row (#215) 1b3992370 sidebar: right-click, long press, Shift+F10 and swipes on rows; End asks in its row (#242, D309) a6498b99c ui: one row menu and swipe component, and the in-row question, for every list of rows (#242, D309) 294042b56 Merge fix-dev-red: dev green again after #291 (#307) d7984ca7d ui: the anchored popover opens at a point, the window menu gets a danger row 9919b28b1 ui: a spring can be flung, so a released drag carries on from the finger's speed (#242, D70) 33e91b0c9 Merge sidebar-pill-259: rename Mesh hosts in place, detached icon, pinned account pill (#259) 502d0fc7e Stabilize clean shim exit observation under load #307 f15a74866 Fix J96 server test regressions after #291 66d25769d sidebar: the tick, cross and dot on a session row get a tooltip in plain words (#259) e60f12434 test: the sidebar's tick, cross and dot explain themselves in a tooltip (#259) 44b3256f1 sidebar: a detached session shows an icon, not the word (#259) 648cc7774 test: a detached session in the sidebar shows an icon with a "Detached" tooltip (#259) 85cb920ee test: the rename field and the widened name pill never overlap the → pill (#259) 9fe1b7041 ui: the inline rename field stays inside the name's own box (#259) c747d9fbb test: the rename field must not sit under the status dot (#259) e86c2baf2 test: opt-in review captures of the pinned sidebar pill (#259) 845aa3e2c test: browser spec for renaming Mesh hosts in place (#259) 126c97190 palette: "Rename <host>" for the Mesh's hosts (#259) 16756de44 sidebar: rename a Mesh host by double click or double tap in the Views tree (#259) cae06fe17 mesh: rename a host by double click or double tap on its card (#259) ce31a7b52 ui: field placed over a name, and a double tap that survives a rebuilt list (#259) e3d803ff2 mesh: rename a host through mesh.node.rename in the Mesh API adapter (#259) 711032f75 ui: one inline-rename field and one double-tap gesture for every in-place rename (#259) 0172555f7 sidebar: the account pill stays pinned once the inbox card has come and gone (#259) ``` VM cleanup: my Rust and tracer build dirs on dev-02 are deleted; worktrees `~/worktrees/j106*` remain until the orchestrator prunes them.
Author
Owner

Decision recorded (owner, 2026-11-02, J106 round 2): on touch, #242's swipes (left = End/Close, right = Open beside) and the long-press menu win; #215's swipe-to-reveal pill is not built. The hover action pill is pointer-only (hover and a fine pointer). The backends flagged earlier (End behind #100, privileged access with no endpoint) stay hidden in the product.

Round 2 progress (branch row-actions-242-215, head 187f81759): deferred JS is cut, not the cap raised. Per-module numbers are in the commit messages of 59f6f46e2 and 3b05ee668. Deferred gzip 62,041 → 60,107 B (cap 61,440, headroom 1,333 B; brotli 52,994 of 55,296); merged with J100's ui-no-overshoot-scroll 58,696 B. Running verify fast and the browser specs on this head now.

**Decision recorded (owner, 2026-11-02, J106 round 2):** on touch, #242's swipes (left = End/Close, right = Open beside) and the long-press menu win; #215's swipe-to-reveal pill is not built. The hover action pill is pointer-only (hover and a fine pointer). The backends flagged earlier (End behind #100, privileged access with no endpoint) stay hidden in the product. **Round 2 progress (branch `row-actions-242-215`, head `187f81759`):** deferred JS is cut, not the cap raised. Per-module numbers are in the commit messages of `59f6f46e2` and `3b05ee668`. Deferred gzip 62,041 → 60,107 B (cap 61,440, headroom 1,333 B; brotli 52,994 of 55,296); merged with J100's `ui-no-overshoot-scroll` 58,696 B. Running `verify fast` and the browser specs on this head now.
Author
Owner

J106 round 2 finish. Branch row-actions-242-215, head 5fa6334d7, pushed. Based on and merged with dev 816f9ab3e (J100 merged; also docs sections #311 and recovery #309). The cap is not raised: the code is cut.

Deferred JS (gzip bytes, bun run build && bun transfer-budget.ts)

dev 816f9ab3e this head
deferred 59,889 / 61,440 (brotli 52,786 / 55,296) 58,737 / 61,440 (brotli 51,856 / 55,296): headroom 2,703 B, brotli 3,440 B
critical 311,553 / 327,680 313,050
onDemand largest chunk 15,447 / 20,480 15,472

Per module, against dev 816f9ab3e (identity B / estimated gzip B: each module's share of its chunk by bytes in the output, from the bundler's metafile; head 9b3365e02, the last numbers are within 20 B of this head):

  • deferred: toolbar/tree.ts +495 / +170 (the hooks only: loader of the row actions, the openRename hook, the phone's close delay), sessions/live.ts +62 / +30, mesh/lazy.ts +40 / +15, mesh/index.ts +39 / +15, toolbar/palette.ts +36 / +18, model/reconcile.ts +27 / +13, views/world.ts -146 / -37, toolbar/viewlist.ts -2,092 / -693, pill-morph.ts -2,719 / -994 (both now on demand behind a thin hook).
  • critical: branched.ts +1,126 / +636, double-activate.ts (the gesture, new) +759 / +430, tooltip.ts +239 / +105, icons.ts +158 / +61, spring.ts +51 / +21, core-screen.ts +35 / +18 (13.2 KB under its cap).
  • on demand now (no longer in either class): the rename field (inline-rename.on-demand.ts), toolbar/row-rename.on-demand.ts, mesh/rename.on-demand.ts, toolbar/palette-hosts.on-demand.ts (remove host, allow full access, rename a Mesh host rows), toolbar/viewlist.on-demand.ts, and with them the row menu, swipes, pill and the in-row question.
    Merged with J100 alone (before J100 reached dev, ui-no-overshoot-scroll onto this branch): deferred 58,696.
    Lesson recorded in the commit message: a dynamic import from deferred code of a module that on-demand chunks also import statically is turned into a static link by the bundler and moves nothing, so every importer of an on-demand module has to be on demand too.

Behaviour notes. Right-click, swipes and the pill attach a few milliseconds after the sidebar first opens (#tree[data-rows="ready"]); a double click before that does nothing. The view list opens from the first tap (it waits for its small chunk if idle loading has not finished). The palette's host rows are fetched as the canvas comes up. The row swipe has no bounce now (J100's rule; it did not compile against the new spring type, found by merging).

Gate: scripts/verify fast exit 0 on 5fa6334d7 (dev-02), verbatim:

5fa6334d7 test(tracer): the typing check puts the focus back on the page instead of waiting for a textarea (#259)
| Rust gate                 | Result       |
+---------------------------+--------------+
| fmt                       | PASS         |
| VT native fmt             | PASS         |
| CLI plugin compile-out    | PASS         |
| clippy                    | PASS         |
| SSH runtime test helper   | PASS         |
| real-binary fixture paths | PASS         |
| test                      | PASS         |
| SSH inode reuse fixture   | PASS         |
| shim process-per-user     | PASS         |
| cargo-deny                | PASS         |
| gate architecture         | PASS         |
| static builds             | PASS         |
+---------------------------+--------------+

==> Compiler metadata generator checks
8452:PT_INTERP: absent; size: 22468304 bytes; baseline 22418256; delta +50048 bytes (report only, D243 amended 2026-11-01)
8488:PT_INTERP: absent; size: 20487864 bytes; baseline 20453280; delta +34584 bytes (report only, D243 amended 2026-11-01)
9617:JavaScript transfer, critical: identity=1005057 gzip=313050/327680 brotli=261034/273408 bytes
9618:JavaScript transfer, deferred: identity=168016 gzip=58737/61440 brotli=51856/55296 bytes
9619:JavaScript transfer, onDemand: identity=325049 gzip=130653/20480 brotli=116421/18432 bytes
9620:JavaScript transfer, onDemand largest chunk chunk-jvsymf4e.js: gzip=15472/20480 brotli=13810/18432 bytes (class total gzip=130653 brotli=116421, not limited)
9235: 517 pass 9236: 0 fail 9446: 107 pass 9447: 0 fail 9517: 17 pass 9518: 0 fail 9708: 39 pass 9709: 0 fail VERIFY_EXIT=0

(Earlier runs failed on this branch and were fixed, in order: a tsc error in the row spring vs the no-overshoot type; two unit tests (the palette rename test and a cubic-bezier overshoot in the swipe icon, caught by J100's no-overshoot.test.ts); a Rust vt_credit test and a 5 s unit test timeout on a loaded machine, each passing when rerun alone and in the next full run.)

Browser specs (production build, test double, 1440×900 / 393×852; last full run on 75078b6d7): 194 passed, 12 failed. Eleven fail identically on unmodified dev 816f9ab3e (measured there: canvas.spec 302, 345, 626; clusters.spec 702, 837; glass-ghost 4 cases; sessions-canvas 170 and the manager-on-demand check), and the twelfth, "another device typed last" (sessions-canvas), was a focus race that fails about half the runs on dev (5 of 9) and every run here; fixed in the spec (the focus probe shows build-01's textarea holding the focus while nix-cache is active) and passing 4 of 4 on 5fa6334d7. palette-polish (375×667) and mesh-rename desktop failed in some runs and pass in the final one; the palette label measured 57.866 px on dev as well.

Commits of this round (oldest first):

5fa6334d7 test(tracer): the typing check puts the focus back on the page instead of waiting for a textarea (#259)
75078b6d7 test(tracer): "typing takes the size over" types into the window it is about (#259)
433b2781b test(tracer): #259's tree rename checks wait for the row actions to attach (#259, #215)
8faf00970 palette: the host rows' chunk is fetched as the palette is installed, not at idle (D188, #215)
b0cea3b72 test(tracer): the row-menu right-click check waits for the menu before Escape (#242)
1da414c09 test, ui: the palette rename test follows the Mesh rename module; the armed swipe icon eases without overshoot (#215, #242)
2690c3346 ui: the row spring is typed as a SpringConfig, so it compiles against the no-overshoot spring (#242)
9b3365e02 Merge origin/dev (816f9ab3e, J100 merged) into row-actions-242-215
816f9ab3e Merge ui-no-overshoot-scroll: no elastic overshoot; wheel scrolls the content under it (#308, D275)
fae83a7b1 sidebar: row actions announce when they are attached; the palette's host rows load promptly (D188, #242, #215)
b5099e3be Merge docs/j114-sections: user docs in D317 sections (#311)
79a43ae3c ui: the row spring states `bounce: 0`, which both the current and the no-overshoot spring types accept (#242)
0ef98fea8 docs: one heading per setting, with its default and range only in its quick facts (#311, D317)
7dd462e9e docs: sections, nested addresses, redirects and a check for "?" targets (#311, D317)
4b40ac8b4 tracer: moving a window and the free canvas's gestures load on demand (D188, #308)
187f81759 Merge origin/dev (92ee8d5ea) into row-actions-242-215
3464c4e13 ui: the row swipe settles without a bounce (#242, TASTE "No overshoot, ever")
3b05ee668 perf: the view list and its pill morph load on demand behind a thin hook (D188)
59f6f46e2 perf: the rename field, the tree's rename routing, the Mesh host rename and the host rows of the palette load on demand (D188, #215, #259)
92ee8d5ea Merge fix-recovery-restart: account recovery always waits 24 measured hours (#309, D25)
77616905a Checkpoint measured recovery progress each minute and at shutdown (#309)
52ad241ae Confirm recovery countdown readiness with server status (#309, D25)
c56854dbb tracer: wheel specs and TASTE rule for the wheel over docs pages and terminals (D275, #308)
7788d5bd8 tracer: a vertical wheel scrolls the content under the pointer; no elastic bump at the ends (D275, #308)
c400f6f94 taste: no overshoot, ever (#308)
214f4eea4 tracer: a unit check rejects springs and easings that overshoot (#308)
7dd27c0b7 ui: every spring is critically damped and every curve ends without overshoot (#308)
716183781 Send recovery completion notice in revoked socket teardown (#309)
3b0123b4b Measure recovery waits with persisted monotonic uptime (#309, D25)
8440d50e9 Restart expired recovery with fresh safety deadlines (#309)

VM: my ~/worktrees/j106* worktrees and targets on dev-02 are pruned.

**J106 round 2 finish.** Branch `row-actions-242-215`, head **`5fa6334d7`**, pushed. Based on and merged with dev `816f9ab3e` (J100 merged; also docs sections #311 and recovery #309). The cap is not raised: the code is cut. **Deferred JS (gzip bytes, `bun run build && bun transfer-budget.ts`)** | | dev 816f9ab3e | this head | |---|---|---| | deferred | 59,889 / 61,440 (brotli 52,786 / 55,296) | **58,737** / 61,440 (brotli 51,856 / 55,296): headroom **2,703 B**, brotli 3,440 B | | critical | 311,553 / 327,680 | 313,050 | | onDemand largest chunk | 15,447 / 20,480 | 15,472 | Per module, against dev 816f9ab3e (identity B / estimated gzip B: each module's share of its chunk by bytes in the output, from the bundler's metafile; head `9b3365e02`, the last numbers are within 20 B of this head): - **deferred:** `toolbar/tree.ts` +495 / +170 (the hooks only: loader of the row actions, the `openRename` hook, the phone's close delay), `sessions/live.ts` +62 / +30, `mesh/lazy.ts` +40 / +15, `mesh/index.ts` +39 / +15, `toolbar/palette.ts` +36 / +18, `model/reconcile.ts` +27 / +13, `views/world.ts` -146 / -37, **`toolbar/viewlist.ts` -2,092 / -693, `pill-morph.ts` -2,719 / -994** (both now on demand behind a thin hook). - **critical:** `branched.ts` +1,126 / +636, `double-activate.ts` (the gesture, new) +759 / +430, `tooltip.ts` +239 / +105, `icons.ts` +158 / +61, `spring.ts` +51 / +21, `core-screen.ts` +35 / +18 (13.2 KB under its cap). - **on demand now (no longer in either class):** the rename field (`inline-rename.on-demand.ts`), `toolbar/row-rename.on-demand.ts`, `mesh/rename.on-demand.ts`, `toolbar/palette-hosts.on-demand.ts` (remove host, allow full access, rename a Mesh host rows), `toolbar/viewlist.on-demand.ts`, and with them the row menu, swipes, pill and the in-row question. Merged with J100 alone (before J100 reached dev, `ui-no-overshoot-scroll` onto this branch): deferred 58,696. Lesson recorded in the commit message: a dynamic import from deferred code of a module that on-demand chunks also import statically is turned into a static link by the bundler and moves nothing, so every importer of an on-demand module has to be on demand too. **Behaviour notes.** Right-click, swipes and the pill attach a few milliseconds after the sidebar first opens (`#tree[data-rows="ready"]`); a double click before that does nothing. The view list opens from the first tap (it waits for its small chunk if idle loading has not finished). The palette's host rows are fetched as the canvas comes up. The row swipe has no bounce now (J100's rule; it did not compile against the new spring type, found by merging). **Gate: `scripts/verify fast` exit 0** on `5fa6334d7` (dev-02), verbatim: ``` 5fa6334d7 test(tracer): the typing check puts the focus back on the page instead of waiting for a textarea (#259) | Rust gate | Result | +---------------------------+--------------+ | fmt | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | real-binary fixture paths | PASS | | test | PASS | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | static builds | PASS | +---------------------------+--------------+ ==> Compiler metadata generator checks 8452:PT_INTERP: absent; size: 22468304 bytes; baseline 22418256; delta +50048 bytes (report only, D243 amended 2026-11-01) 8488:PT_INTERP: absent; size: 20487864 bytes; baseline 20453280; delta +34584 bytes (report only, D243 amended 2026-11-01) 9617:JavaScript transfer, critical: identity=1005057 gzip=313050/327680 brotli=261034/273408 bytes 9618:JavaScript transfer, deferred: identity=168016 gzip=58737/61440 brotli=51856/55296 bytes 9619:JavaScript transfer, onDemand: identity=325049 gzip=130653/20480 brotli=116421/18432 bytes 9620:JavaScript transfer, onDemand largest chunk chunk-jvsymf4e.js: gzip=15472/20480 brotli=13810/18432 bytes (class total gzip=130653 brotli=116421, not limited) 9235: 517 pass 9236: 0 fail 9446: 107 pass 9447: 0 fail 9517: 17 pass 9518: 0 fail 9708: 39 pass 9709: 0 fail VERIFY_EXIT=0 ``` (Earlier runs failed on this branch and were fixed, in order: a `tsc` error in the row spring vs the no-overshoot type; two unit tests (the palette rename test and a `cubic-bezier` overshoot in the swipe icon, caught by J100's `no-overshoot.test.ts`); a Rust `vt_credit` test and a 5 s unit test timeout on a loaded machine, each passing when rerun alone and in the next full run.) **Browser specs** (production build, test double, 1440×900 / 393×852; last full run on `75078b6d7`): 194 passed, 12 failed. Eleven fail identically on unmodified dev `816f9ab3e` (measured there: `canvas.spec` 302, 345, 626; `clusters.spec` 702, 837; `glass-ghost` 4 cases; `sessions-canvas` 170 and the manager-on-demand check), and the twelfth, "another device typed last" (`sessions-canvas`), was a focus race that fails about half the runs on dev (5 of 9) and every run here; fixed in the spec (the focus probe shows build-01's textarea holding the focus while nix-cache is active) and passing 4 of 4 on `5fa6334d7`. `palette-polish` (375×667) and `mesh-rename` desktop failed in some runs and pass in the final one; the palette label measured 57.866 px on dev as well. Commits of this round (oldest first): ``` 5fa6334d7 test(tracer): the typing check puts the focus back on the page instead of waiting for a textarea (#259) 75078b6d7 test(tracer): "typing takes the size over" types into the window it is about (#259) 433b2781b test(tracer): #259's tree rename checks wait for the row actions to attach (#259, #215) 8faf00970 palette: the host rows' chunk is fetched as the palette is installed, not at idle (D188, #215) b0cea3b72 test(tracer): the row-menu right-click check waits for the menu before Escape (#242) 1da414c09 test, ui: the palette rename test follows the Mesh rename module; the armed swipe icon eases without overshoot (#215, #242) 2690c3346 ui: the row spring is typed as a SpringConfig, so it compiles against the no-overshoot spring (#242) 9b3365e02 Merge origin/dev (816f9ab3e, J100 merged) into row-actions-242-215 816f9ab3e Merge ui-no-overshoot-scroll: no elastic overshoot; wheel scrolls the content under it (#308, D275) fae83a7b1 sidebar: row actions announce when they are attached; the palette's host rows load promptly (D188, #242, #215) b5099e3be Merge docs/j114-sections: user docs in D317 sections (#311) 79a43ae3c ui: the row spring states `bounce: 0`, which both the current and the no-overshoot spring types accept (#242) 0ef98fea8 docs: one heading per setting, with its default and range only in its quick facts (#311, D317) 7dd462e9e docs: sections, nested addresses, redirects and a check for "?" targets (#311, D317) 4b40ac8b4 tracer: moving a window and the free canvas's gestures load on demand (D188, #308) 187f81759 Merge origin/dev (92ee8d5ea) into row-actions-242-215 3464c4e13 ui: the row swipe settles without a bounce (#242, TASTE "No overshoot, ever") 3b05ee668 perf: the view list and its pill morph load on demand behind a thin hook (D188) 59f6f46e2 perf: the rename field, the tree's rename routing, the Mesh host rename and the host rows of the palette load on demand (D188, #215, #259) 92ee8d5ea Merge fix-recovery-restart: account recovery always waits 24 measured hours (#309, D25) 77616905a Checkpoint measured recovery progress each minute and at shutdown (#309) 52ad241ae Confirm recovery countdown readiness with server status (#309, D25) c56854dbb tracer: wheel specs and TASTE rule for the wheel over docs pages and terminals (D275, #308) 7788d5bd8 tracer: a vertical wheel scrolls the content under the pointer; no elastic bump at the ends (D275, #308) c400f6f94 taste: no overshoot, ever (#308) 214f4eea4 tracer: a unit check rejects springs and easings that overshoot (#308) 7dd27c0b7 ui: every spring is critically damped and every curve ends without overshoot (#308) 716183781 Send recovery completion notice in revoked socket teardown (#309) 3b0123b4b Measure recovery waits with persisted monotonic uptime (#309, D25) 8440d50e9 Restart expired recovery with fresh safety deadlines (#309) ``` VM: my `~/worktrees/j106*` worktrees and targets on dev-02 are pruned.
Author
Owner

Closed by the orchestrator during the 2026-11-09 night housekeeping (owner order: "close stale prs/issues"). The work has landed on dev, and the latest note here is a finish or merge note (J387 inventory on #436). Reopen if anything is still missing.

Closed by the orchestrator during the 2026-11-09 night housekeeping (owner order: "close stale prs/issues"). The work has landed on dev, and the latest note here is a finish or merge note (J387 inventory on #436). Reopen if anything is still missing.
kayg closed this issue 2026-11-08 19:04:29 +00:00
Sign in to join this conversation.
No labels
launch-qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/infraternal#215
No description provided.