J95: option rows: label left, description right, subtitle when narrow (shared choice list) #306

Open
opened 2026-11-02 07:13:12 +00:00 by kayg · 8 comments
Owner

Job J95 (UI, small). Owner, 2026-11-02, on Settings → Appearance and Window Sizes (two production screenshots):
"Paper (left aligned) ... light (right aligned). same for the font section and the others. if the width is smaller ... 'light' becomes a subtitle to Paper."

Scope

The one shared option-row (choiceBlock + fitHints + .ui-sp-list/.ui-sp-row CSS in packages/ui), so Theme, Font, Phones, Desktop and any future list change together:

  • label left (after the swatch), description right-aligned, ending before a fixed tick column, so every row's description ends at the same x whether selected or not;
  • narrow list: descriptions become left-aligned subtitles under the label; label never truncated (no ellipsis);
  • rows start at the heading's left edge (no indent); swatch-less lists in a window that also has a swatch list reserve the swatch slot so labels align across lists; windows without swatches reserve nothing;
  • one decision per list from the list's own width (ResizeObserver on the list, not the viewport) so siblings can never disagree: this removes the "Comfortable / with margins stacks alone" outlier;
  • docs commit: replaces TASTE row "A list's descriptions share one edge" (J66's aligned hint column) with the owner's new rule, quoting them.

Plan and estimate (about 2 h)

  1. Worktree j95-option-rows from origin/dev. (done)
  2. CSS + choiceBlock/fitHints rewrite in packages/ui; swatch-slot reservation in renderAppearance. ~40 min
  3. Extend apps/tracer/test/settings-rows.spec.ts (desktop: description right edges equal ±0.5, label left edges equal ±0.5, heading left = row left; phone: subtitle mode, no truncation). ~30 min
  4. Gate on the emptier VM (scripts/verify fast, TS half first), screenshots 1440×900@2x and 393×852@3x on this issue. ~40 min
  5. TASTE docs commit, push own branch.

Not touching: add-host .ah-options radio cards (a different component, flow cards), switch/status rows in Hosts, the details-card danger row.

Queues at start: compute01 3/4 slots busy, dev-02-std 2/3 busy → dev-02 has a free slot.

Job J95 (UI, small). Owner, 2026-11-02, on Settings → Appearance and Window Sizes (two production screenshots): "Paper (left aligned) ... light (right aligned). same for the font section and the others. if the width is smaller ... 'light' becomes a subtitle to Paper." ## Scope The one shared option-row (`choiceBlock` + `fitHints` + `.ui-sp-list/.ui-sp-row` CSS in `packages/ui`), so Theme, Font, Phones, Desktop and any future list change together: - label left (after the swatch), description right-aligned, ending before a fixed tick column, so every row's description ends at the same x whether selected or not; - narrow list: descriptions become left-aligned subtitles under the label; label never truncated (no ellipsis); - rows start at the heading's left edge (no indent); swatch-less lists in a window that also has a swatch list reserve the swatch slot so labels align across lists; windows without swatches reserve nothing; - one decision per list from the list's own width (ResizeObserver on the list, not the viewport) so siblings can never disagree: this removes the "Comfortable / with margins stacks alone" outlier; - docs commit: replaces TASTE row "A list's descriptions share one edge" (J66's aligned hint column) with the owner's new rule, quoting them. ## Plan and estimate (about 2 h) 1. Worktree `j95-option-rows` from origin/dev. (done) 2. CSS + `choiceBlock`/`fitHints` rewrite in packages/ui; swatch-slot reservation in `renderAppearance`. ~40 min 3. Extend `apps/tracer/test/settings-rows.spec.ts` (desktop: description right edges equal ±0.5, label left edges equal ±0.5, heading left = row left; phone: subtitle mode, no truncation). ~30 min 4. Gate on the emptier VM (`scripts/verify fast`, TS half first), screenshots 1440×900@2x and 393×852@3x on this issue. ~40 min 5. TASTE docs commit, push own branch. Not touching: add-host `.ah-options` radio cards (a different component, flow cards), switch/status rows in Hosts, the details-card danger row. Queues at start: compute01 3/4 slots busy, dev-02-std 2/3 busy → dev-02 has a free slot.
Author
Owner

Start: branch j95-option-rows, base b0e79b31e3d0f3dc89ae3ca67b466d929c6d03ba (origin/dev). Plan and estimate (about 2 h) are in the issue body. Queues: compute01 3/4 busy, dev-02-std 2/3 busy; gate goes to dev-02.

Start: branch `j95-option-rows`, base `b0e79b31e3d0f3dc89ae3ca67b466d929c6d03ba` (origin/dev). Plan and estimate (about 2 h) are in the issue body. Queues: compute01 3/4 busy, dev-02-std 2/3 busy; gate goes to dev-02.
Author
Owner

Finish (J95)

Branch j95-option-rows, head e62d125e96, base b0e79b31e (origin/dev when started). Three atomic commits: ui: (shared option row, packages/ui), test: (settings-rows spec), taste: (J66's "one aligned hint column" row struck through and replaced, owner quoted).

What changed (one shared component, no per-list code)

  • name left, description right-aligned; the tick keeps its own fixed far-right column, so a description ends at the same x selected or not;
  • one decision per list from the list's own width (ResizeObserver on the list, not the viewport): too narrow → every description is a left-aligned subtitle under its name. A pure CSS @container cannot compare a container with its own text's width, and per-row decisions would recreate the "Comfortable / with margins" outlier, so the decision stays per list. Each row is now measured against its own two widths, not the widest name;
  • names never truncated (no clip, no ellipsis);
  • choice-list rows start on the heading's left edge (hover fill is a bleeding pseudo-element, the row box stays on the edge);
  • Font reserves the swatch's place only because Theme (swatches) is in the same window; Window Sizes reserves nothing.
  • Not touched (not option lists): add-host .ah-options radio cards, switch/status rows in Hosts (their rows still sit 8 px in from their headings: visible next to Window Sizes in the 1440 capture; say if you want them aligned too), the details card's danger row.

Tests (dev-02-std, production build through the latency proxy)

bun x playwright test test/settings-rows.spec.ts test/details-removal-row.spec.ts: 9 passed. (The first run of the new spec failed on my own measurement of the scaled-down unselected tick; fixed by measuring the layout box.) Spec measures at 1440×900 DPR 2, 393×852 and 375×667 DPR 3, ink and paper, ±0.5 px.
Also run: font-switch.spec passes; sign-out.spec has 3 failures (.cl-mockup not found / split-right timeout) that fail identically on unmodified origin/dev (run in a clean worktree).

Gate

scripts/verify fast on dev-02-std (full, incl. x86 static cross-build). Final table:

+---------------------------+--------------+
| 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                      | FAIL (101)   |
| SSH inode reuse fixture   | PASS         |
| shim process-per-user     | PASS         |
| cargo-deny                | PASS         |
| gate architecture         | PASS         |
| static builds             | PASS         |
+---------------------------+--------------+
VERIFY_EXIT=1
2:==> Repository artifact size gate
3-repository artifact gate: PASS (4 changed path(s), merge base b0e79b31e3d0)
4-
  • static builds PASS (aarch64 +812216 bytes vs baseline, report only; no Rust changed).
  • test FAIL (101) is not from this branch (no Rust, no build input touched). Full infraternal-server --lib on my branch vs my exact base b0e79b31e, same worktree and target: the same 8 tests fail on both (account_events::review_revocation_r2 ×4, socket_tests::review_subscription_refusal ×2, release_manifest::signed_identity_and_exact_bytes_are_required, release_payload::resolver_startup_removes_only_owned_inactive_private_staging). The branch run additionally failed reviewer_disk_cancel_complete_races and reviewer_disk_restart_and_concurrent_resume; rerun alone on the branch: both ok (load-sensitive on the shared VM; the first gate run failed yet another subset, the bootstrap_runtime / enrollment-migration tests). Because verify stops at the Rust failure, its TS half did not run there, so I ran it directly (ts_verify_fast): TS_EXIT=0 (tracer tsc, bun tests, bundle transfer budget, protocol tests).

Screenshots

1440×900 @2× and 393×852 @3×, ink and paper, whole page and the section's own window (attached below).

## Finish (J95) Branch `j95-option-rows`, head **e62d125e96ef76c4886a51e1f754c0b6e51f8bd3**, base b0e79b31e (origin/dev when started). Three atomic commits: `ui:` (shared option row, packages/ui), `test:` (settings-rows spec), `taste:` (J66's "one aligned hint column" row struck through and replaced, owner quoted). ### What changed (one shared component, no per-list code) - name left, description right-aligned; the tick keeps its own fixed far-right column, so a description ends at the same x selected or not; - one decision per list from the list's own width (ResizeObserver on the list, not the viewport): too narrow → every description is a left-aligned subtitle under its name. A pure CSS `@container` cannot compare a container with its own text's width, and per-row decisions would recreate the "Comfortable / with margins" outlier, so the decision stays per list. Each row is now measured against its own two widths, not the widest name; - names never truncated (no clip, no ellipsis); - choice-list rows start on the heading's left edge (hover fill is a bleeding pseudo-element, the row box stays on the edge); - Font reserves the swatch's place only because Theme (swatches) is in the same window; Window Sizes reserves nothing. - Not touched (not option lists): add-host `.ah-options` radio cards, switch/status rows in Hosts (their rows still sit 8 px in from their headings: visible next to Window Sizes in the 1440 capture; say if you want them aligned too), the details card's danger row. ### Tests (dev-02-std, production build through the latency proxy) `bun x playwright test test/settings-rows.spec.ts test/details-removal-row.spec.ts`: **9 passed**. (The first run of the new spec failed on my own measurement of the scaled-down unselected tick; fixed by measuring the layout box.) Spec measures at 1440×900 DPR 2, 393×852 and 375×667 DPR 3, ink and paper, ±0.5 px. Also run: font-switch.spec passes; sign-out.spec has 3 failures (`.cl-mockup` not found / split-right timeout) that **fail identically on unmodified origin/dev** (run in a clean worktree). ### Gate `scripts/verify fast` on dev-02-std (full, incl. x86 static cross-build). Final table: ``` +---------------------------+--------------+ | 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 | FAIL (101) | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | static builds | PASS | +---------------------------+--------------+ VERIFY_EXIT=1 2:==> Repository artifact size gate 3-repository artifact gate: PASS (4 changed path(s), merge base b0e79b31e3d0) 4- ``` - `static builds` PASS (aarch64 +812216 bytes vs baseline, report only; no Rust changed). - **`test` FAIL (101) is not from this branch** (no Rust, no build input touched). Full `infraternal-server --lib` on my branch vs my exact base b0e79b31e, same worktree and target: the same **8 tests fail on both** (`account_events::review_revocation_r2` ×4, `socket_tests::review_subscription_refusal` ×2, `release_manifest::signed_identity_and_exact_bytes_are_required`, `release_payload::resolver_startup_removes_only_owned_inactive_private_staging`). The branch run additionally failed `reviewer_disk_cancel_complete_races` and `reviewer_disk_restart_and_concurrent_resume`; rerun alone on the branch: both **ok** (load-sensitive on the shared VM; the first gate run failed yet another subset, the bootstrap_runtime / enrollment-migration tests). Because `verify` stops at the Rust failure, its TS half did not run there, so I ran it directly (`ts_verify_fast`): **TS_EXIT=0** (tracer `tsc`, bun tests, bundle transfer budget, protocol tests). ### Screenshots 1440×900 @2× and 393×852 @3×, ink and paper, whole page and the section's own window (attached below).
Author
Owner

Follow-up (orchestrator, 2026-11-02): align the non-option rows too

Branch j95b-settings-rows, base 313d0dd63 (origin/dev with J95 merged). Same rules.

Rule to implement: every non-option row in Settings starts on its heading's left edge (no indent); any value, switch, key caps or action sits right-aligned on the same right edge as the option descriptions: before the ✓ column in a window that has option lists, at the row's far right where there is no ✓.

Row types found (Settings sections appearance, windows, hosts, toolbar, shortcuts, devices, account, about, users, access, found hosts): switch rows and status rows (.ui-sp-switch, .ui-sp-status, Hosts and Account), toolbar items (.ui-ts-item: grip, icon, label, switch), shortcut rows (.ui-ss-row: label, key caps, reset), device rows (.ui-sp-device: kind icon, name, icon buttons), key/value facts (.ui-kv-row: About, Account, Users, Access, Found hosts), and Appearance's UI-size slider readout.

Plan (about 2 h)

  1. Diagnostic spec first: measure first-mark left and last-mark right of every row type in every section at 1440×900 and 393×852, to see what actually deviates (no guessing). ~20 min
  2. Fix in packages/ui (shared classes only): zero row indent with the bleeding hover fill for all .ui-sp-rows; a --ui-sp-end token (✓ column + gap) set by a window that has option lists; icon glyphs (grip, kind icon, icon buttons) aligned by their glyph, not their hit box, via negative margins. ~50 min
  3. Turn the diagnostic into the real spec (±0.5 px, desktop and phone, ink and paper) next to settings-rows.spec.ts; screenshots at 1440×900 DPR 2 and 393×852 DPR 3 on this issue. ~30 min
  4. TASTE: extend the option-row rule to the other rows (docs commit, owner instruction quoted). Gate on the emptier VM, TS half plus verify fast; Rust failures known on dev are named. ~30 min

Out of scope unless it falls out of the above: add-host .ah-options cards, the details-card danger row (lives in a popover, not in Settings).

## Follow-up (orchestrator, 2026-11-02): align the non-option rows too Branch `j95b-settings-rows`, base `313d0dd63` (origin/dev with J95 merged). Same rules. **Rule to implement:** every non-option row in Settings starts on its heading's left edge (no indent); any value, switch, key caps or action sits right-aligned on the same right edge as the option descriptions: before the ✓ column in a window that has option lists, at the row's far right where there is no ✓. **Row types found** (Settings sections appearance, windows, hosts, toolbar, shortcuts, devices, account, about, users, access, found hosts): switch rows and status rows (`.ui-sp-switch`, `.ui-sp-status`, Hosts and Account), toolbar items (`.ui-ts-item`: grip, icon, label, switch), shortcut rows (`.ui-ss-row`: label, key caps, reset), device rows (`.ui-sp-device`: kind icon, name, icon buttons), key/value facts (`.ui-kv-row`: About, Account, Users, Access, Found hosts), and Appearance's UI-size slider readout. **Plan (about 2 h)** 1. Diagnostic spec first: measure first-mark left and last-mark right of every row type in every section at 1440×900 and 393×852, to see what actually deviates (no guessing). ~20 min 2. Fix in `packages/ui` (shared classes only): zero row indent with the bleeding hover fill for all `.ui-sp-row`s; a `--ui-sp-end` token (✓ column + gap) set by a window that has option lists; icon glyphs (grip, kind icon, icon buttons) aligned by their glyph, not their hit box, via negative margins. ~50 min 3. Turn the diagnostic into the real spec (±0.5 px, desktop and phone, ink and paper) next to `settings-rows.spec.ts`; screenshots at 1440×900 DPR 2 and 393×852 DPR 3 on this issue. ~30 min 4. TASTE: extend the option-row rule to the other rows (docs commit, owner instruction quoted). Gate on the emptier VM, TS half plus `verify fast`; Rust failures known on dev are named. ~30 min Out of scope unless it falls out of the above: add-host `.ah-options` cards, the details-card danger row (lives in a popover, not in Settings).
Author
Owner

Follow-up finish: every Settings row on the two shared edges

Branch j95b-settings-rows, head 8904bb2835, base 313d0dd63 (origin/dev with J95 merged). Three atomic commits: ui:, test:, taste:.

Measured first, then fixed

Before the fix, settings-edges.spec.ts (diagnostic mode) measured each row type's first and last mark against the section's content edge and the descriptions' right edge (CSS px, 1440 / 393):

row left right
Hosts switch and status rows +8.8 / +8 −8.8 / −8
Toolbar items (grip glyph) +7.7 / +13 0 / +41: switches pushed out of the window on a phone
Devices (icon, icon buttons) +6.6 / +5 −4.4 / −6
Account sign-out row +8.8 / +8 −13.2 / −14
UI-size readout (Appearance) 0 +30.8 / +28 (past the descriptions' edge)
Shortcut rows, key/value facts (About, Account) 0 0
After: every row of every section is within 0.5 px (measured 0.0) on both edges, desktop and phone, ink and paper.

What changed (shared classes in packages/ui; no per-section code)

  • all .ui-sp-rows lose their indent (hover fill stays as the bleeding pseudo-element; no fill on a status row);
  • --ui-sp-end token = how far a row's last mark stops short of the far right: 0, or the tick column plus gap in a window with option lists (data-ticks on Appearance and Window Sizes). Switch and status rows pad by it, the slider is inset by it;
  • the tick column is one explicit width (--ui-sp-tick, 1.125rem). Finding: .terminal-screen svg { width: 1.125rem } outranks .ui-sp-tick, so on desktop the column was 19.8 px while the old code assumed --ui-icon = 17.6 px; the visual size is unchanged;
  • a switch's track (its ::before, content-box) is 1 px wider than its 3rem box: the box stops short by 3px - 0.125rem so the track is what sits on the edge;
  • glyphs are aligned by what is seen (toolbar grip, device icon, icon buttons in settings rows: own square size plus a negative margin; their extra touch ::after is dropped because the box is already 44 px on touch). Side effect: on desktop those icon buttons are 30 px (was 24 px);
  • bug found by the phone capture: Settings → Toolbar: the "Customise for this device" pill was wider than the pane and widened every row, pushing the switches out of the window. Toolbar and Shortcuts roots are now one minmax(0, 1fr) column and the source line wraps. The spec also asserts no sideways scrolling in any section.
  • Known and left: an overridden shortcut shows its reset button after the key caps, so that one row's caps end one button early (reset needs its own design; it is a rare state).

Tests (dev-02-std, production build)

settings-edges.spec.ts (new, 4 tests: ink/paper × 1440×900 DPR 2 / 393×852 DPR 3; sections Appearance, Window Sizes, Hosts, Toolbar, Shortcuts, Devices, Account, About; each must contain its expected row types) together with settings-rows, details-removal-row and font-switch: 16 passed (PW_EXIT=0). bun run check clean.

Gate

  • TS half (ts_verify_fast): TS_EXIT=0 (tracer tsc, bun tests, transfer budget, protocol tests).
  • scripts/verify fast on dev-02-std: artifact gate PASS; table:
| fmt                       | PASS         |
| VT native fmt             | PASS         |
| CLI plugin compile-out    | PASS         |
| clippy                    | PASS         |
| SSH runtime test helper   | PASS         |
| real-binary fixture paths | PASS         |
| test                      | FAIL (101)   |
| SSH inode reuse fixture   | PASS         |
| shim process-per-user     | PASS         |
| cargo-deny                | PASS         |
| gate architecture         | PASS         |
| static builds             | PASS         |
  • test FAIL: no Rust changed. This run failed 5 tests in package infraternal (bin), each killed by its own 58 s watchdog (exceeded 58.1s; status=signal: 9) on the loaded VM: restart::tests::cold_exec_with_closed_stdin_keeps_the_image_pinned_and_maps_null, restart::tests::deleted_path_review::relative_recovery_after_atomic_install_keeps_live_install_path, restart::tests::path_review::late_recovery_from_path_invocation_keeps_install_target, restart::tests::reload_reopens_install_path_when_the_running_inode_was_replaced, restart::tests::restart_request_logs_kernel_peer_target_and_cold_outcome. Run alone in the same worktree: branch 18 passed, 0 failed; unmodified base 313d0dd63 18 passed, 0 failed. They are load timeouts, not code failures. (Different from the 8 server-lib failures named in the previous round on dev, which this run did not hit: the failing set changes from run to run on these shared VMs.)

Screenshots

Each Settings section's window at 1440×900 DPR 2 (ink) and 393×852 DPR 3 (paper), attached below.

## Follow-up finish: every Settings row on the two shared edges Branch `j95b-settings-rows`, head **8904bb2835616d7bdb37184eed7e6a8759fa990b**, base 313d0dd63 (origin/dev with J95 merged). Three atomic commits: `ui:`, `test:`, `taste:`. ### Measured first, then fixed Before the fix, `settings-edges.spec.ts` (diagnostic mode) measured each row type's first and last mark against the section's content edge and the descriptions' right edge (CSS px, 1440 / 393): | row | left | right | |---|---|---| | Hosts switch and status rows | +8.8 / +8 | −8.8 / −8 | | Toolbar items (grip glyph) | +7.7 / +13 | 0 / **+41: switches pushed out of the window on a phone** | | Devices (icon, icon buttons) | +6.6 / +5 | −4.4 / −6 | | Account sign-out row | +8.8 / +8 | −13.2 / −14 | | UI-size readout (Appearance) | 0 | +30.8 / +28 (past the descriptions' edge) | | Shortcut rows, key/value facts (About, Account) | 0 | 0 | After: every row of every section is within 0.5 px (measured 0.0) on both edges, desktop and phone, ink and paper. ### What changed (shared classes in `packages/ui`; no per-section code) - all `.ui-sp-row`s lose their indent (hover fill stays as the bleeding pseudo-element; no fill on a status row); - `--ui-sp-end` token = how far a row's last mark stops short of the far right: 0, or the tick column plus gap in a window with option lists (`data-ticks` on Appearance and Window Sizes). Switch and status rows pad by it, the slider is inset by it; - the tick column is one explicit width (`--ui-sp-tick`, 1.125rem). Finding: `.terminal-screen svg { width: 1.125rem }` outranks `.ui-sp-tick`, so on desktop the column was 19.8 px while the old code assumed `--ui-icon` = 17.6 px; the visual size is unchanged; - a switch's track (its `::before`, content-box) is 1 px wider than its 3rem box: the box stops short by `3px - 0.125rem` so the track is what sits on the edge; - glyphs are aligned by what is seen (toolbar grip, device icon, icon buttons in settings rows: own square size plus a negative margin; their extra touch `::after` is dropped because the box is already 44 px on touch). Side effect: on desktop those icon buttons are 30 px (was 24 px); - **bug found by the phone capture:** Settings → Toolbar: the "Customise for this device" pill was wider than the pane and widened every row, pushing the switches out of the window. Toolbar and Shortcuts roots are now one `minmax(0, 1fr)` column and the source line wraps. The spec also asserts no sideways scrolling in any section. - Known and left: an overridden shortcut shows its reset button after the key caps, so that one row's caps end one button early (reset needs its own design; it is a rare state). ### Tests (dev-02-std, production build) `settings-edges.spec.ts` (new, 4 tests: ink/paper × 1440×900 DPR 2 / 393×852 DPR 3; sections Appearance, Window Sizes, Hosts, Toolbar, Shortcuts, Devices, Account, About; each must contain its expected row types) together with `settings-rows`, `details-removal-row` and `font-switch`: **16 passed** (PW_EXIT=0). `bun run check` clean. ### Gate - TS half (`ts_verify_fast`): **TS_EXIT=0** (tracer tsc, bun tests, transfer budget, protocol tests). - `scripts/verify fast` on dev-02-std: artifact gate PASS; table: ``` | fmt | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | real-binary fixture paths | PASS | | test | FAIL (101) | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | static builds | PASS | ``` - `test` FAIL: no Rust changed. This run failed 5 tests in package `infraternal` (bin), each killed by its own 58 s watchdog (`exceeded 58.1s; status=signal: 9`) on the loaded VM: `restart::tests::cold_exec_with_closed_stdin_keeps_the_image_pinned_and_maps_null`, `restart::tests::deleted_path_review::relative_recovery_after_atomic_install_keeps_live_install_path`, `restart::tests::path_review::late_recovery_from_path_invocation_keeps_install_target`, `restart::tests::reload_reopens_install_path_when_the_running_inode_was_replaced`, `restart::tests::restart_request_logs_kernel_peer_target_and_cold_outcome`. Run alone in the same worktree: branch **18 passed, 0 failed**; unmodified base 313d0dd63 **18 passed, 0 failed**. They are load timeouts, not code failures. (Different from the 8 server-lib failures named in the previous round on dev, which this run did not hit: the failing set changes from run to run on these shared VMs.) ### Screenshots Each Settings section's window at 1440×900 DPR 2 (ink) and 393×852 DPR 3 (paper), attached below.
Author
Owner

J127 start (UI, small). Branch j127-shortcut-changed-dot, base 7add12640 (origin/dev).

Owner decision 2026-11-03 ("agreed"), from J95b's finding that an overridden shortcut row's reset button pushed its key caps one button early: a changed shortcut shows a small dot on its key caps (tooltip "Changed from "); Reset moves into the row's shared menu (J106: right-click, long-press, Shift+F10/ContextMenu) and the keyboard; "Reset all" sits at the bottom of the list and asks in its own row (TASTE "A destructive row action asks in its own row"); every row keeps its caps on the shared right edge.

Plan and estimate (about 2.5 h)

  1. packages/ui/src/shortcuts-settings.ts (+ .ui-ss-* CSS in packages/ui/styles.css) ~60 min
    • remove the per-row .ui-ss-reset button; an overridden row gets a dot inside .ui-ss-keys (absolutely positioned on the caps' top-left corner, so the caps' box and right edge never move) carrying data-tip="Changed from <default combo label>", and an aria-label on the row button that says the same;
    • attach the shared attachRowMenu (row-menu.ts) to the list: one action, "Reset to " (only on changed rows; fixed rows and unchanged rows have no menu), so right-click, long-press and Shift+F10 / ContextMenu work as in every other list;
    • keyboard: Delete/Backspace on a focused changed row resets it (same key the sidebar's rows use for their destructive action; ignored while capturing);
    • "Reset all": the pill button becomes a last row of the list (shared settings row, danger tone, same left edge), shown only when something is changed? No: always shown, disabled-looking rows are banned by TASTE ("No control looks dead"), so it is shown only while at least one shortcut is changed; it asks in its own row through mountRowAsk ("Reset all shortcuts? Reset · Cancel").
    • no new component: attachRowMenu, mountRowAsk, the shared tooltip (data-tip) and the settings row classes are reused.
  2. Tests, apps/tracer/test/shortcut-reset.spec.ts (Playwright, 1440×900 and 393×852): caps' right edges within 0.5 px with and without overrides (all rows equal), dot present on changed rows only + tooltip text, Reset via menu (right-click, Shift+F10) and via Delete, Reset all asks in its row (row height unchanged, no dialog) and resets; update settings-edges.spec.ts (it measures the reset button as the last mark of a shortcut row). ~45 min
  3. TASTE note: extend the row "Every Settings row shares two edges" with the dot rule and quote the owner. ~10 min
  4. Gate: scripts/verify fast on the emptier VM, screenshots at 2x of changed/unchanged rows and the in-row question. ~40 min

Not touching: the capture flow (click row, press keys, swap/cancel warning), the ? panel, the registry.

One interpretation to flag: "and the keyboard" is implemented as Delete/Backspace on the focused row plus the menu's keyboard route (Shift+F10). Say so if you want a different key.

**J127 start (UI, small).** Branch `j127-shortcut-changed-dot`, base `7add12640` (origin/dev). Owner decision 2026-11-03 ("agreed"), from J95b's finding that an overridden shortcut row's reset button pushed its key caps one button early: a changed shortcut shows a small dot on its key caps (tooltip "Changed from <default>"); Reset moves into the row's shared menu (J106: right-click, long-press, Shift+F10/ContextMenu) and the keyboard; "Reset all" sits at the bottom of the list and asks in its own row (TASTE "A destructive row action asks in its own row"); every row keeps its caps on the shared right edge. ## Plan and estimate (about 2.5 h) 1. `packages/ui/src/shortcuts-settings.ts` (+ `.ui-ss-*` CSS in `packages/ui/styles.css`) ~60 min - remove the per-row `.ui-ss-reset` button; an overridden row gets a dot inside `.ui-ss-keys` (absolutely positioned on the caps' top-left corner, so the caps' box and right edge never move) carrying `data-tip="Changed from <default combo label>"`, and an `aria-label` on the row button that says the same; - attach the shared `attachRowMenu` (row-menu.ts) to the list: one action, "Reset to <default>" (only on changed rows; fixed rows and unchanged rows have no menu), so right-click, long-press and Shift+F10 / ContextMenu work as in every other list; - keyboard: Delete/Backspace on a focused changed row resets it (same key the sidebar's rows use for their destructive action; ignored while capturing); - "Reset all": the pill button becomes a last row of the list (shared settings row, danger tone, same left edge), shown only when something is changed? No: always shown, disabled-looking rows are banned by TASTE ("No control looks dead"), so it is shown only while at least one shortcut is changed; it asks in its own row through `mountRowAsk` ("Reset all shortcuts? Reset · Cancel"). - no new component: `attachRowMenu`, `mountRowAsk`, the shared tooltip (`data-tip`) and the settings row classes are reused. 2. Tests, `apps/tracer/test/shortcut-reset.spec.ts` (Playwright, 1440×900 and 393×852): caps' right edges within 0.5 px with and without overrides (all rows equal), dot present on changed rows only + tooltip text, Reset via menu (right-click, Shift+F10) and via Delete, Reset all asks in its row (row height unchanged, no dialog) and resets; update `settings-edges.spec.ts` (it measures the reset button as the last mark of a shortcut row). ~45 min 3. TASTE note: extend the row "Every Settings row shares two edges" with the dot rule and quote the owner. ~10 min 4. Gate: `scripts/verify fast` on the emptier VM, screenshots at 2x of changed/unchanged rows and the in-row question. ~40 min Not touching: the capture flow (click row, press keys, swap/cancel warning), the `?` panel, the registry. One interpretation to flag: "and the keyboard" is implemented as Delete/Backspace on the focused row plus the menu's keyboard route (Shift+F10). Say so if you want a different key.
Author
Owner

J127 progress. Branch j127-shortcut-changed-dot, head b95cc78f1 (2 commits on 7add12640: the UI + tests, then the TASTE row). Pushed.

Implemented (reuse only, no new component): dot on the first cap's top-left (absolute, out of flow) with data-tip="Changed from <default>"; Reset = the shared attachRowMenu action "Reset to " (right-click / long press / Shift+F10) and Delete/Backspace on the focused row; "Reset all" = last row, shown only while something is changed, asks through the shared mountRowAsk.

Finding 1 (decision, evidence in the commit message): on touch a long press on the caps would open the row menu (480 ms, row-menu.ts) and the tooltip (500 ms, tooltip.ts) together, so touch gets no tooltip; the menu item names the default ("Reset to Ctrl+Shift+K"). Pointer keeps the tooltip.
Finding 2 (not done, flagging): TASTE "Everything you can do is findable in search": a per-row Reset has no palette entry (the sidebar gets its rows' entries from focused-row plumbing in core-screen). Left out of this task on the scope leash; say if you want it.

First run on dev-02-std (tsc + settings-edges.spec.ts + shortcut-reset.spec.ts): tsc clean, settings-edges 4/4 pass, shortcut-reset passed every step through caps edges, dot, tooltip, menu Reset, Shift+F10 and Delete, then failed only on my own assertion about the Reset-all question text (the screen-reader-only effect line is part of the text; fixed by dropping the end anchor, same as row-ask.spec.ts). Re-running now, then scripts/verify fast; both are queued behind other jobs on dev-02 (queue position 5).

**J127 progress.** Branch `j127-shortcut-changed-dot`, head `b95cc78f1` (2 commits on `7add12640`: the UI + tests, then the TASTE row). Pushed. Implemented (reuse only, no new component): dot on the first cap's top-left (absolute, out of flow) with `data-tip="Changed from <default>"`; Reset = the shared `attachRowMenu` action "Reset to <default>" (right-click / long press / Shift+F10) and Delete/Backspace on the focused row; "Reset all" = last row, shown only while something is changed, asks through the shared `mountRowAsk`. **Finding 1 (decision, evidence in the commit message):** on touch a long press on the caps would open the row menu (480 ms, row-menu.ts) and the tooltip (500 ms, tooltip.ts) together, so touch gets no tooltip; the menu item names the default ("Reset to Ctrl+Shift+K"). Pointer keeps the tooltip. **Finding 2 (not done, flagging):** TASTE "Everything you can do is findable in search": a per-row Reset has no palette entry (the sidebar gets its rows' entries from focused-row plumbing in core-screen). Left out of this task on the scope leash; say if you want it. First run on dev-02-std (`tsc` + `settings-edges.spec.ts` + `shortcut-reset.spec.ts`): `tsc` clean, `settings-edges` 4/4 pass, `shortcut-reset` passed every step through caps edges, dot, tooltip, menu Reset, Shift+F10 and Delete, then failed only on my own assertion about the Reset-all question text (the screen-reader-only effect line is part of the text; fixed by dropping the end anchor, same as `row-ask.spec.ts`). Re-running now, then `scripts/verify fast`; both are queued behind other jobs on dev-02 (queue position 5).
Author
Owner

J127 finish. Branch j127-shortcut-changed-dot, head 0a8a427ea806c0d57ea5904f3828feb5ee704d65 (2 commits on 2c58f76b5, origin/dev at the time of the final rebase): commit 1 the UI + tests, commit 2 the TASTE row. Pushed (own branch only).

What changed

  • packages/ui/src/shortcuts-settings.ts, packages/ui/styles.css: no reset button beside the caps any more. A changed row gets a dot on its first cap's top-left corner (absolute, out of flow) + pointer tooltip "Changed from ". Reset = the shared attachRowMenu action "Reset to " (right-click, long press, Shift+F10) and Delete/Backspace on the focused row (focus returns to the row after the repaint). "Reset all" = last row, shown only while something is changed, asks in its own row through the shared mountRowAsk. Reused only: attachRowMenu, mountRowAsk, the shared tooltip, the settings row classes. No new component.
  • apps/tracer/src/core-screen.ts: shortcutsSection(stage).
  • apps/tracer/test/shortcut-reset.spec.ts (new), settings-edges.spec.ts (no longer measures a reset button), docs/design/TASTE.md (new row beside "Every Settings row shares two edges").

Evidence

shortcut-reset.spec.ts + settings-edges.spec.ts on dev-02-std, final head: 6 passed (1.4m): settings-edges ink/paper × 1440×900/393×852, shortcut-reset 1440×900 and 393×852. shortcut-reset asserts: caps' right edges within 0.5 px with no override, with 3 overrides, after each reset and after Reset all (and equal to the original edge); labels keep one left edge; one dot per changed row, none elsewhere, absolute, on the cap's corner; tooltip text "Changed from " (pointer; touch has none, see below); Reset via right-click menu, via Shift+F10 + Enter, via Delete (focus lands back on the row); Delete on an unchanged row does nothing; an unchanged row's right-click stays the browser's; "Reset all" absent until a change, starts on the labels' x, asks in its row (every row's offsetTop/offsetHeight unchanged, no .ui-confirm-tip/alertdialog, question starts on the label's x ±0.5, row's own label fades to opacity 0), Cancel and Escape restore it, Reset resets everything.
Screenshots at 2x (1440×900) and 3x (393×852) attached below: changed rows with dots, and the in-row question.

Findings

  1. A bug the first screenshot caught (fixed): the shared question fades in, but each list hides its own words (the sidebar does it in .bm-row CSS). My "Reset all" row had no such rule, so "Reset all?" was drawn over "Reset all". Added the rule and a spec assertion on the label's opacity.
  2. Touch has no tooltip on the caps (decision): a long press would open the row menu (480 ms) and the tooltip (500 ms) together. The menu item says "Reset to ", so touch still learns the default.
  3. Not done, flagging (TASTE "Everything you can do is findable in search"): a per-row Reset has no palette entry; the sidebar gets its rows' entries from focused-row plumbing in core-screen. Left out under the scope leash; say if you want it.
  4. First scripts/verify fast run (same head 0a8a427ea) failed the Rust test row on 3 of 11 tests of tests/agent_daemon.rs (real_binary_rootless_private_readiness_does_not_refuse_person_revocation, ..._person_preparation_honors_explicit_state_without_runtime_environment: resource_unavailable; real_binary_keeps_background_backend_probe_off_the_control_path: socket timeout) while the VM was loaded. This diff changes no Rust (TS, CSS, one Markdown row). Run alone on this head: cargo test --workspace --locked --test agent_daemon → 11 passed; on a detached worktree of unmodified 2c58f76b5 (same command): 11 passed. The full gate was then rerun on the final head and is green (below). So it is a load-sensitive flake, not this change; the first run's failure was not hidden.

Gate: scripts/verify fast on infraternal-dev-02-std, head 0a8a427ea, exit 0 (verbatim rows)

+---------------------------+--------------+
| 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         |
+---------------------------+--------------+

 48 pass
 0 fail
 77248 expect() calls
Ran 48 tests across 12 files. [3.14s]
JavaScript transfer, critical: identity=1023961 gzip=319475/327680 brotli=266868/273408 bytes
JavaScript transfer, deferred: identity=171132 gzip=59733/61440 brotli=52764/55296 bytes
Font transfer: monaspace-neon=84340/88064 cascadia-code=85008/88064 jetbrains-mono=70384/75776 symbols=223748/235520 ibm-plex-mono=41344/45056 geist-mono=41364/45056 source-code-pro=46484/49152 maple-mono-nf=99876/106496 (critical 46432/51200) commit-mono=7181
VERIFY_EXIT=0
GATE_DONE
0a8a427ea806c0d57ea5904f3828feb5ee704d65

Static sizes (report only, D243 amended; no Rust changed, deltas are dev's drift against the recorded baselines): x86_64 22475824 bytes (baseline 22418256, +57568); aarch64 20495736 bytes (baseline 20453280, +42456). Key memory on x86_64 is SKIP (host aarch64), as the brief says for foreign execution.
Budgets: JS transfer critical gzip 319475/327680, brotli 266868/273408; deferred gzip 59733/61440, brotli 52764/55296; all passed.

How to test: Settings → Shortcuts → click a row, press a new chord: a dot appears on its caps, the caps do not move. Right-click it (or Shift+F10, or Delete on the focused row): "Reset to ". "Reset all" appears at the bottom, and asks in its own row.

**J127 finish.** Branch `j127-shortcut-changed-dot`, head `0a8a427ea806c0d57ea5904f3828feb5ee704d65` (2 commits on `2c58f76b5`, origin/dev at the time of the final rebase): commit 1 the UI + tests, commit 2 the TASTE row. Pushed (own branch only). ## What changed - `packages/ui/src/shortcuts-settings.ts`, `packages/ui/styles.css`: no reset button beside the caps any more. A changed row gets a dot on its first cap's top-left corner (absolute, out of flow) + pointer tooltip "Changed from <default>". Reset = the shared `attachRowMenu` action "Reset to <default>" (right-click, long press, Shift+F10) and Delete/Backspace on the focused row (focus returns to the row after the repaint). "Reset all" = last row, shown only while something is changed, asks in its own row through the shared `mountRowAsk`. Reused only: `attachRowMenu`, `mountRowAsk`, the shared tooltip, the settings row classes. No new component. - `apps/tracer/src/core-screen.ts`: `shortcutsSection(stage)`. - `apps/tracer/test/shortcut-reset.spec.ts` (new), `settings-edges.spec.ts` (no longer measures a reset button), `docs/design/TASTE.md` (new row beside "Every Settings row shares two edges"). ## Evidence `shortcut-reset.spec.ts` + `settings-edges.spec.ts` on dev-02-std, final head: **6 passed** (1.4m): settings-edges ink/paper × 1440×900/393×852, shortcut-reset 1440×900 and 393×852. shortcut-reset asserts: caps' right edges within 0.5 px with no override, with 3 overrides, after each reset and after Reset all (and equal to the original edge); labels keep one left edge; one dot per changed row, none elsewhere, absolute, on the cap's corner; tooltip text "Changed from <default>" (pointer; touch has none, see below); Reset via right-click menu, via Shift+F10 + Enter, via Delete (focus lands back on the row); Delete on an unchanged row does nothing; an unchanged row's right-click stays the browser's; "Reset all" absent until a change, starts on the labels' x, asks in its row (every row's offsetTop/offsetHeight unchanged, no `.ui-confirm-tip`/alertdialog, question starts on the label's x ±0.5, row's own label fades to opacity 0), Cancel and Escape restore it, Reset resets everything. Screenshots at 2x (1440×900) and 3x (393×852) attached below: changed rows with dots, and the in-row question. ## Findings 1. **A bug the first screenshot caught (fixed):** the shared question fades in, but each list hides its own words (the sidebar does it in `.bm-row` CSS). My "Reset all" row had no such rule, so "Reset all?" was drawn over "Reset all". Added the rule and a spec assertion on the label's opacity. 2. **Touch has no tooltip on the caps (decision):** a long press would open the row menu (480 ms) and the tooltip (500 ms) together. The menu item says "Reset to <default>", so touch still learns the default. 3. **Not done, flagging (TASTE "Everything you can do is findable in search"):** a per-row Reset has no palette entry; the sidebar gets its rows' entries from focused-row plumbing in core-screen. Left out under the scope leash; say if you want it. 4. **First `scripts/verify fast` run (same head `0a8a427ea`) failed the Rust `test` row** on 3 of 11 tests of `tests/agent_daemon.rs` (`real_binary_rootless_private_readiness_does_not_refuse_person_revocation`, `..._person_preparation_honors_explicit_state_without_runtime_environment`: `resource_unavailable`; `real_binary_keeps_background_backend_probe_off_the_control_path`: socket timeout) while the VM was loaded. This diff changes no Rust (TS, CSS, one Markdown row). Run alone on this head: `cargo test --workspace --locked --test agent_daemon` → 11 passed; on a detached worktree of unmodified `2c58f76b5` (same command): 11 passed. The full gate was then rerun on the final head and is green (below). So it is a load-sensitive flake, not this change; the first run's failure was not hidden. ## Gate: `scripts/verify fast` on infraternal-dev-02-std, head `0a8a427ea`, exit 0 (verbatim rows) ``` +---------------------------+--------------+ | 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 | +---------------------------+--------------+ 48 pass 0 fail 77248 expect() calls Ran 48 tests across 12 files. [3.14s] JavaScript transfer, critical: identity=1023961 gzip=319475/327680 brotli=266868/273408 bytes JavaScript transfer, deferred: identity=171132 gzip=59733/61440 brotli=52764/55296 bytes Font transfer: monaspace-neon=84340/88064 cascadia-code=85008/88064 jetbrains-mono=70384/75776 symbols=223748/235520 ibm-plex-mono=41344/45056 geist-mono=41364/45056 source-code-pro=46484/49152 maple-mono-nf=99876/106496 (critical 46432/51200) commit-mono=7181 VERIFY_EXIT=0 GATE_DONE 0a8a427ea806c0d57ea5904f3828feb5ee704d65 ``` Static sizes (report only, D243 amended; no Rust changed, deltas are dev's drift against the recorded baselines): x86_64 22475824 bytes (baseline 22418256, +57568); aarch64 20495736 bytes (baseline 20453280, +42456). Key memory on x86_64 is SKIP (host aarch64), as the brief says for foreign execution. Budgets: JS transfer critical gzip 319475/327680, brotli 266868/273408; deferred gzip 59733/61440, brotli 52764/55296; all passed. How to test: Settings → Shortcuts → click a row, press a new chord: a dot appears on its caps, the caps do not move. Right-click it (or Shift+F10, or Delete on the focused row): "Reset to <default>". "Reset all" appears at the bottom, and asks in its own row.
Author
Owner

J127 finish. Branch j127-shortcut-changed-dot, head 0a8a427ea806c0d57ea5904f3828feb5ee704d65 (2 commits on 2c58f76b5, origin/dev at the time of the final rebase): commit 1 the UI + tests, commit 2 the TASTE row. Pushed (own branch only).

What changed

  • packages/ui/src/shortcuts-settings.ts, packages/ui/styles.css: no reset button beside the caps any more. A changed row gets a dot on its first cap's top-left corner (absolute, out of flow) + pointer tooltip "Changed from ". Reset = the shared attachRowMenu action "Reset to " (right-click, long press, Shift+F10) and Delete/Backspace on the focused row (focus returns to the row after the repaint). "Reset all" = last row, shown only while something is changed, asks in its own row through the shared mountRowAsk. Reused only: attachRowMenu, mountRowAsk, the shared tooltip, the settings row classes. No new component.
  • apps/tracer/src/core-screen.ts: shortcutsSection(stage).
  • apps/tracer/test/shortcut-reset.spec.ts (new), settings-edges.spec.ts (no longer measures a reset button), docs/design/TASTE.md (new row beside "Every Settings row shares two edges").

Evidence

shortcut-reset.spec.ts + settings-edges.spec.ts on dev-02-std, final head: 6 passed (1.4m): settings-edges ink/paper × 1440×900/393×852, shortcut-reset 1440×900 and 393×852. shortcut-reset asserts: caps' right edges within 0.5 px with no override, with 3 overrides, after each reset and after Reset all (and equal to the original edge); labels keep one left edge; one dot per changed row, none elsewhere, absolute, on the cap's corner; tooltip text "Changed from " (pointer; touch has none, see below); Reset via right-click menu, via Shift+F10 + Enter, via Delete (focus lands back on the row); Delete on an unchanged row does nothing; an unchanged row's right-click stays the browser's; "Reset all" absent until a change, starts on the labels' x, asks in its row (every row's offsetTop/offsetHeight unchanged, no .ui-confirm-tip/alertdialog, question starts on the label's x ±0.5, row's own label fades to opacity 0), Cancel and Escape restore it, Reset resets everything.
Screenshots at 2x (1440×900) and 3x (393×852) attached below: changed rows with dots, and the in-row question.

Findings

  1. A bug the first screenshot caught (fixed): the shared question fades in, but each list hides its own words (the sidebar does it in .bm-row CSS). My "Reset all" row had no such rule, so "Reset all?" was drawn over "Reset all". Added the rule and a spec assertion on the label's opacity.
  2. Touch has no tooltip on the caps (decision): a long press would open the row menu (480 ms) and the tooltip (500 ms) together. The menu item says "Reset to ", so touch still learns the default.
  3. Not done, flagging (TASTE "Everything you can do is findable in search"): a per-row Reset has no palette entry; the sidebar gets its rows' entries from focused-row plumbing in core-screen. Left out under the scope leash; say if you want it.
  4. First scripts/verify fast run (same head 0a8a427ea) failed the Rust test row on 3 of 11 tests of tests/agent_daemon.rs (real_binary_rootless_private_readiness_does_not_refuse_person_revocation, ..._person_preparation_honors_explicit_state_without_runtime_environment: resource_unavailable; real_binary_keeps_background_backend_probe_off_the_control_path: socket timeout) while the VM was loaded. This diff changes no Rust (TS, CSS, one Markdown row). Run alone on this head: cargo test --workspace --locked --test agent_daemon → 11 passed; on a detached worktree of unmodified 2c58f76b5 (same command): 11 passed. The full gate was then rerun on the final head and is green (below). So it is a load-sensitive flake, not this change; the first run's failure was not hidden.

Gate: scripts/verify fast on infraternal-dev-02-std, head 0a8a427ea, exit 0 (verbatim rows)

+---------------------------+--------------+
| 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         |
+---------------------------+--------------+

 48 pass
 0 fail
 77248 expect() calls
Ran 48 tests across 12 files. [3.14s]
JavaScript transfer, critical: identity=1023961 gzip=319475/327680 brotli=266868/273408 bytes
JavaScript transfer, deferred: identity=171132 gzip=59733/61440 brotli=52764/55296 bytes
Font transfer: monaspace-neon=84340/88064 cascadia-code=85008/88064 jetbrains-mono=70384/75776 symbols=223748/235520 ibm-plex-mono=41344/45056 geist-mono=41364/45056 source-code-pro=46484/49152 maple-mono-nf=99876/106496 (critical 46432/51200) commit-mono=7181
VERIFY_EXIT=0
GATE_DONE
0a8a427ea806c0d57ea5904f3828feb5ee704d65

Static sizes (report only, D243 amended; no Rust changed, deltas are dev's drift against the recorded baselines): x86_64 22475824 bytes (baseline 22418256, +57568); aarch64 20495736 bytes (baseline 20453280, +42456). Key memory on x86_64 is SKIP (host aarch64), as the brief says for foreign execution.
Budgets: JS transfer critical gzip 319475/327680, brotli 266868/273408; deferred gzip 59733/61440, brotli 52764/55296; all passed.

How to test: Settings → Shortcuts → click a row, press a new chord: a dot appears on its caps, the caps do not move. Right-click it (or Shift+F10, or Delete on the focused row): "Reset to ". "Reset all" appears at the bottom, and asks in its own row.

**J127 finish.** Branch `j127-shortcut-changed-dot`, head `0a8a427ea806c0d57ea5904f3828feb5ee704d65` (2 commits on `2c58f76b5`, origin/dev at the time of the final rebase): commit 1 the UI + tests, commit 2 the TASTE row. Pushed (own branch only). ## What changed - `packages/ui/src/shortcuts-settings.ts`, `packages/ui/styles.css`: no reset button beside the caps any more. A changed row gets a dot on its first cap's top-left corner (absolute, out of flow) + pointer tooltip "Changed from <default>". Reset = the shared `attachRowMenu` action "Reset to <default>" (right-click, long press, Shift+F10) and Delete/Backspace on the focused row (focus returns to the row after the repaint). "Reset all" = last row, shown only while something is changed, asks in its own row through the shared `mountRowAsk`. Reused only: `attachRowMenu`, `mountRowAsk`, the shared tooltip, the settings row classes. No new component. - `apps/tracer/src/core-screen.ts`: `shortcutsSection(stage)`. - `apps/tracer/test/shortcut-reset.spec.ts` (new), `settings-edges.spec.ts` (no longer measures a reset button), `docs/design/TASTE.md` (new row beside "Every Settings row shares two edges"). ## Evidence `shortcut-reset.spec.ts` + `settings-edges.spec.ts` on dev-02-std, final head: **6 passed** (1.4m): settings-edges ink/paper × 1440×900/393×852, shortcut-reset 1440×900 and 393×852. shortcut-reset asserts: caps' right edges within 0.5 px with no override, with 3 overrides, after each reset and after Reset all (and equal to the original edge); labels keep one left edge; one dot per changed row, none elsewhere, absolute, on the cap's corner; tooltip text "Changed from <default>" (pointer; touch has none, see below); Reset via right-click menu, via Shift+F10 + Enter, via Delete (focus lands back on the row); Delete on an unchanged row does nothing; an unchanged row's right-click stays the browser's; "Reset all" absent until a change, starts on the labels' x, asks in its row (every row's offsetTop/offsetHeight unchanged, no `.ui-confirm-tip`/alertdialog, question starts on the label's x ±0.5, row's own label fades to opacity 0), Cancel and Escape restore it, Reset resets everything. Screenshots at 2x (1440×900) and 3x (393×852) attached below: changed rows with dots, and the in-row question. ## Findings 1. **A bug the first screenshot caught (fixed):** the shared question fades in, but each list hides its own words (the sidebar does it in `.bm-row` CSS). My "Reset all" row had no such rule, so "Reset all?" was drawn over "Reset all". Added the rule and a spec assertion on the label's opacity. 2. **Touch has no tooltip on the caps (decision):** a long press would open the row menu (480 ms) and the tooltip (500 ms) together. The menu item says "Reset to <default>", so touch still learns the default. 3. **Not done, flagging (TASTE "Everything you can do is findable in search"):** a per-row Reset has no palette entry; the sidebar gets its rows' entries from focused-row plumbing in core-screen. Left out under the scope leash; say if you want it. 4. **First `scripts/verify fast` run (same head `0a8a427ea`) failed the Rust `test` row** on 3 of 11 tests of `tests/agent_daemon.rs` (`real_binary_rootless_private_readiness_does_not_refuse_person_revocation`, `..._person_preparation_honors_explicit_state_without_runtime_environment`: `resource_unavailable`; `real_binary_keeps_background_backend_probe_off_the_control_path`: socket timeout) while the VM was loaded. This diff changes no Rust (TS, CSS, one Markdown row). Run alone on this head: `cargo test --workspace --locked --test agent_daemon` → 11 passed; on a detached worktree of unmodified `2c58f76b5` (same command): 11 passed. The full gate was then rerun on the final head and is green (below). So it is a load-sensitive flake, not this change; the first run's failure was not hidden. ## Gate: `scripts/verify fast` on infraternal-dev-02-std, head `0a8a427ea`, exit 0 (verbatim rows) ``` +---------------------------+--------------+ | 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 | +---------------------------+--------------+ 48 pass 0 fail 77248 expect() calls Ran 48 tests across 12 files. [3.14s] JavaScript transfer, critical: identity=1023961 gzip=319475/327680 brotli=266868/273408 bytes JavaScript transfer, deferred: identity=171132 gzip=59733/61440 brotli=52764/55296 bytes Font transfer: monaspace-neon=84340/88064 cascadia-code=85008/88064 jetbrains-mono=70384/75776 symbols=223748/235520 ibm-plex-mono=41344/45056 geist-mono=41364/45056 source-code-pro=46484/49152 maple-mono-nf=99876/106496 (critical 46432/51200) commit-mono=7181 VERIFY_EXIT=0 GATE_DONE 0a8a427ea806c0d57ea5904f3828feb5ee704d65 ``` Static sizes (report only, D243 amended; no Rust changed, deltas are dev's drift against the recorded baselines): x86_64 22475824 bytes (baseline 22418256, +57568); aarch64 20495736 bytes (baseline 20453280, +42456). Key memory on x86_64 is SKIP (host aarch64), as the brief says for foreign execution. Budgets: JS transfer critical gzip 319475/327680, brotli 266868/273408; deferred gzip 59733/61440, brotli 52764/55296; all passed. How to test: Settings → Shortcuts → click a row, press a new chord: a dot appears on its caps, the caps do not move. Right-click it (or Shift+F10, or Delete on the focused row): "Reset to <default>". "Reset all" appears at the bottom, and asks in its own row.
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#306
No description provided.