keyboard-opened surfaces (⌘K, ⌘/, ?) animate; they must open and close instantly #427

Open
opened 2026-11-08 14:16:47 +00:00 by kayg · 41 comments
Owner

Owner, 2026-11-08, verbatim: "wait i notice that cmd / or ? (shortcuts panel) and cmd k have started to animate? i thought we decided actions originating from keyboard shortcuts = no animation?"

Owner decision (2026-11-08, "Keyboard opens instantly"): anything opened or closed from a keyboard shortcut (⌘K palette, ⌘/ and ? shortcuts panel, splits, and the like) appears and disappears instantly, with no animation. Pointer and touch keep their animations. TASTE's 2026-10-08 row allowing about 180 ms key-triggered open/close is replaced; the two older "instant" rows stand.

Owner, 2026-11-08, verbatim: "wait i notice that cmd / or ? (shortcuts panel) and cmd k have started to animate? i thought we decided actions originating from keyboard shortcuts = no animation?" Owner decision (2026-11-08, "Keyboard opens instantly"): anything opened or closed from a keyboard shortcut (⌘K palette, ⌘/ and ? shortcuts panel, splits, and the like) appears and disappears instantly, with no animation. Pointer and touch keep their animations. TASTE's 2026-10-08 row allowing about 180 ms key-triggered open/close is replaced; the two older "instant" rows stand.
Author
Owner

J370 start. Branch fix-kbd-no-anim-427, base origin/dev f31cf18ab.

Cause (git log -S): d7b2a4753 (2026-10-08, "tracer: animation never gets in the way of the palette") replaced the instant .palette[data-open-kind="keyboard"] { transition: none } with a 180 ms key-triggered fade+scale (.palette[data-open-kind="keyboard"]:not([hidden]) { transition-duration: 180ms }, apps/tracer/style.css). 72027e74c pinned it in a spec ("a key-triggered palette open is a 180 ms fade, not instant"). That followed the TASTE "animation never gets in the way (4)" row, which the owner has now replaced. The ?/⌘/ panel (packages/ui/src/shortcuts-view.ts) never looked at the source at all: its 400 ms clip-path spring runs on every open and close. The glass bed (setOpen) fades with its card.

Plan (~2 h): one shared input-source tracker in packages/ui (the last of keydown / pointerdown / touch decides, set in capture before handlers), exposed as html[data-input] for CSS and triggeredByKeyboard() for JS. Palette, scrim, shortcuts panel and glass bed read it; no per-surface source flags. Red spec first on origin/dev at 1440x900 and 393x852, then fix, verify quick, verify fast.

J370 start. Branch `fix-kbd-no-anim-427`, base origin/dev f31cf18ab. **Cause (git log -S):** d7b2a4753 (2026-10-08, "tracer: animation never gets in the way of the palette") replaced the instant `.palette[data-open-kind="keyboard"] { transition: none }` with a 180 ms key-triggered fade+scale (`.palette[data-open-kind="keyboard"]:not([hidden]) { transition-duration: 180ms }`, apps/tracer/style.css). 72027e74c pinned it in a spec ("a key-triggered palette open is a 180 ms fade, not instant"). That followed the TASTE "animation never gets in the way (4)" row, which the owner has now replaced. The `?`/⌘/ panel (packages/ui/src/shortcuts-view.ts) never looked at the source at all: its 400 ms clip-path spring runs on every open and close. The glass bed (`setOpen`) fades with its card. **Plan (~2 h):** one shared input-source tracker in packages/ui (the last of keydown / pointerdown / touch decides, set in capture before handlers), exposed as `html[data-input]` for CSS and `triggeredByKeyboard()` for JS. Palette, scrim, shortcuts panel and glass bed read it; no per-surface source flags. Red spec first on origin/dev at 1440x900 and 393x852, then fix, `verify quick`, `verify fast`.
Author
Owner

J370 progress. Red spec apps/tracer/test/keyboard-instant.spec.ts pushed (7e13f929c) and run on origin/dev's code (dev-02, log ~/j370-red.log on infraternal-dev-02-std): 10 failed, 2 passed (the two "first keystroke after the chord lands" checks, which already held).

Frame evidence, first rAF after the key, 1440x900 and 393x852:

  • ⌘K / Ctrl+Shift+K: #palette opacity "0" (expected "1"), a 180 ms transition running.
  • ? and Ctrl+Shift+/: panel clip-path: inset(0px 33.46px 534.2px round 20.9px) (desktop), inset(0px 58.3px 611.8px round 20.9px) (phone), i.e. still mid-spring.
  • Escape on a pointer-opened palette: still displayed in the first frame.

Fix is three commits (1b3a921cc tracker html[data-input], c3f8313a6 palette/pills/sidebar/glass bed via one .ui-key-instant rule, d988a7301 shortcuts panel); now running the new spec plus core-screen and canvas specs for regressions.

J370 progress. Red spec `apps/tracer/test/keyboard-instant.spec.ts` pushed (7e13f929c) and run on origin/dev's code (dev-02, log `~/j370-red.log` on infraternal-dev-02-std): 10 failed, 2 passed (the two "first keystroke after the chord lands" checks, which already held). Frame evidence, first rAF after the key, 1440x900 and 393x852: - ⌘K / Ctrl+Shift+K: `#palette` opacity "0" (expected "1"), a 180 ms transition running. - `?` and Ctrl+Shift+/: panel `clip-path: inset(0px 33.46px 534.2px round 20.9px)` (desktop), `inset(0px 58.3px 611.8px round 20.9px)` (phone), i.e. still mid-spring. - Escape on a pointer-opened palette: still displayed in the first frame. Fix is three commits (1b3a921cc tracker `html[data-input]`, c3f8313a6 palette/pills/sidebar/glass bed via one `.ui-key-instant` rule, d988a7301 shortcuts panel); now running the new spec plus core-screen and canvas specs for regressions.
Author
Owner

J370 finish. Branch fix-kbd-no-anim-427, head 865116ec6, based on origin/dev 7c8bd2221. Not merged, not deployed.

Commits

  • 209fb4371 test: red spec apps/tracer/test/keyboard-instant.spec.ts (12 cases: 1440x900 and 393x852)
  • 37d8c3c40 ui: packages/ui/src/input-source.ts, the one tracker. Capture listeners on window: keydown = keyboard, pointerdown (mouse, pen, touch) = pointer, written to html[data-input] in the same task. Newest event wins, so a pointer close of a keyboard-opened surface may animate and a key close of a pointer-opened one is instant. Unit test included.
  • 056be1c30 tracer: one rule in packages/ui/styles.css, :root[data-input="keyboard"] :is(.ui-glass-bed, .ui-key-instant) { transition: none }. The palette, its scrim, the sidebar, and the capsule, plus and sidebar-toggle pills (their 120 ms cover fade ran on every key open) carry .ui-key-instant. setPalette lost its source argument and data-open-kind is gone. Two old specs that pinned 0.18s now expect 0s.
  • 865116ec6 ui: the ? / ⌘/ panel (shortcuts-view.ts) jumps its spring to the end on a key, open and close, and skips the shadow glide.

Red on origin/dev code (~/j370-red.log on infraternal-dev-02-std): 10 failed, 2 passed. First rAF after the key: palette opacity "0" (expected "1"); panel clip-path: inset(0px 33.46px 534.2px round 20.9px) at 1440x900 and inset(0px 58.26px 611.79px round 20.9px) at 393x852 (expected "none"); a pointer-opened palette still displayed after Escape.

Green (~/j370-green.log, dev-02): all 12 new cases pass at both sizes, as does canvas.spec.ts (51 passed in total with core-screen). In the first frame after the key the palette is opacity "1" with no running transition and the panel is clip-path: none with content opacity 1 and the pill hidden. After Escape the palette is display: none and the panel is removed in the first frame. A pointer open still starts at opacity < 1. A keyboard close of it is instant. A pointer close of a keyboard-opened palette still fades (display held until the fade ends). Typing thm right after the chord lands in #palette-input.

Regression sweep (~/j370-reg.log, dev-02): palette-navigation, palette-polish, shortcuts-d222, clusters-shortcuts, clusters-keyboard-split, platform-consistency, window-option-new plus the bun unit tests: 60 passed, 2 failed. The 2 failures (platform-consistency.spec.ts:184, "Windows palette ... no bar at rest": trackSpread 201 and 84, expected < 6) also fail on unmodified origin/dev (~/j370-base2.log: 100 and 64). Not mine.
core-screen.spec.ts:151 and :254 fail at #rtt ("··", expected "N ms") on both my branch and origin/dev (~/j370-base.log), so they are pre-existing and the latency proxy is the likely cause. They stop before the 0s assertion, which is covered by canvas.spec.ts.

Gate
scripts/verify quick (dev-02, ~/j370-quick.log): "QUICK pre-check passed", exit=0.
scripts/verify fast (compute01, ~/j370-fast.log): every Rust gate row PASS (privileged integration SKIP, static builds PASS). The TS suite is 654 pass, 1 fail, and the only failure is docs-build.test.ts "doc.js stays small: Expected < 12000, Received 13408". The same 13408 occurs on unmodified origin/dev 7c8bd2221, run on dev-02, so it is not from this change. VERIFY_EXIT=1 comes from that row alone.

Not covered / for the orchestrator

  • Surfaces that were already per-source and are not routed through the tracker: anchored popovers, the settings overlay, split and window flights. They read their own source, which is the same keyboard-or-pointer fact, so they were left alone to stay in scope.
  • installInputSource() runs from main.ts only. The standalone doc page and the add-host prototype page do not install it, so they keep their old motion.
  • No size delta measured; the change adds about 1 KB of JS in the main bundle (a 60-line module) and about 150 B of CSS. The deferred JS budget was not touched.
  • No screenshots: this is a timing change, and the frame samples in the logs are the evidence.
J370 finish. Branch `fix-kbd-no-anim-427`, head **865116ec6**, based on origin/dev 7c8bd2221. Not merged, not deployed. **Commits** - 209fb4371 test: red spec `apps/tracer/test/keyboard-instant.spec.ts` (12 cases: 1440x900 and 393x852) - 37d8c3c40 ui: `packages/ui/src/input-source.ts`, the one tracker. Capture listeners on window: keydown = keyboard, pointerdown (mouse, pen, touch) = pointer, written to `html[data-input]` in the same task. Newest event wins, so a pointer close of a keyboard-opened surface may animate and a key close of a pointer-opened one is instant. Unit test included. - 056be1c30 tracer: one rule in `packages/ui/styles.css`, `:root[data-input="keyboard"] :is(.ui-glass-bed, .ui-key-instant) { transition: none }`. The palette, its scrim, the sidebar, and the capsule, plus and sidebar-toggle pills (their 120 ms cover fade ran on every key open) carry `.ui-key-instant`. `setPalette` lost its `source` argument and `data-open-kind` is gone. Two old specs that pinned `0.18s` now expect `0s`. - 865116ec6 ui: the `?` / ⌘/ panel (`shortcuts-view.ts`) jumps its spring to the end on a key, open and close, and skips the shadow glide. **Red on origin/dev code** (`~/j370-red.log` on infraternal-dev-02-std): 10 failed, 2 passed. First rAF after the key: palette opacity "0" (expected "1"); panel `clip-path: inset(0px 33.46px 534.2px round 20.9px)` at 1440x900 and `inset(0px 58.26px 611.79px round 20.9px)` at 393x852 (expected "none"); a pointer-opened palette still displayed after Escape. **Green** (`~/j370-green.log`, dev-02): all 12 new cases pass at both sizes, as does canvas.spec.ts (51 passed in total with core-screen). In the first frame after the key the palette is opacity "1" with no running transition and the panel is `clip-path: none` with content opacity 1 and the pill hidden. After Escape the palette is `display: none` and the panel is removed in the first frame. A pointer open still starts at opacity < 1. A keyboard close of it is instant. A pointer close of a keyboard-opened palette still fades (display held until the fade ends). Typing `thm` right after the chord lands in `#palette-input`. **Regression sweep** (`~/j370-reg.log`, dev-02): palette-navigation, palette-polish, shortcuts-d222, clusters-shortcuts, clusters-keyboard-split, platform-consistency, window-option-new plus the bun unit tests: 60 passed, 2 failed. The 2 failures (`platform-consistency.spec.ts:184`, "Windows palette ... no bar at rest": trackSpread 201 and 84, expected < 6) also fail on unmodified origin/dev (`~/j370-base2.log`: 100 and 64). Not mine. core-screen.spec.ts:151 and :254 fail at `#rtt` ("··", expected "N ms") on both my branch and origin/dev (`~/j370-base.log`), so they are pre-existing and the latency proxy is the likely cause. They stop before the 0s assertion, which is covered by canvas.spec.ts. **Gate** `scripts/verify quick` (dev-02, `~/j370-quick.log`): "QUICK pre-check passed", exit=0. `scripts/verify fast` (compute01, `~/j370-fast.log`): every Rust gate row PASS (privileged integration SKIP, static builds PASS). The TS suite is `654 pass, 1 fail`, and the only failure is `docs-build.test.ts` "doc.js stays small: Expected < 12000, Received 13408". The same 13408 occurs on unmodified origin/dev 7c8bd2221, run on dev-02, so it is not from this change. VERIFY_EXIT=1 comes from that row alone. **Not covered / for the orchestrator** - Surfaces that were already per-source and are not routed through the tracker: anchored popovers, the settings overlay, split and window flights. They read their own source, which is the same keyboard-or-pointer fact, so they were left alone to stay in scope. - `installInputSource()` runs from `main.ts` only. The standalone doc page and the add-host prototype page do not install it, so they keep their old motion. - No size delta measured; the change adds about 1 KB of JS in the main bundle (a 60-line module) and about 150 B of CSS. The deferred JS budget was not touched. - No screenshots: this is a timing change, and the frame samples in the logs are the evidence.
Author
Owner

J370 correction to my finish comment: the size line ('about 1 KB of JS, about 150 B of CSS') is an estimate from reading the diff, not a measurement. I did not measure the bundle or the D243 delta. verify fast's static builds row passed; the orchestrator should read sizes from that log (~/j370-fast.log on compute01) if it needs them.

J370 correction to my finish comment: the size line ('about 1 KB of JS, about 150 B of CSS') is an estimate from reading the diff, not a measurement. I did not measure the bundle or the D243 delta. verify fast's static builds row passed; the orchestrator should read sizes from that log (~/j370-fast.log on compute01) if it needs them.
Author
Owner

J373 review of fix-kbd-no-anim-427 @ 865116ec6 (base origin/dev 77b1255cb, which contains the amended TASTE row 7c8bd2221)

Verdict: REQUEST CHANGES. One real defect (P2): a modifier key alone kills a running pointer animation. Everything else holds. The fix is a few lines in input-source.ts.

What I ran

Production tracer build (bun run build.ts, preview flag on so the test double loads) of the head and of origin/dev, each served by test/mock-server.ts on compute01 through build-slot (launcher checked first: nix develop . -c bun --version outside the slot). Headless Chromium, ?mock=1&proto=windows, theme ink and paper, at 1440×900 and 393×852. The phone runs use hasTouch/isMobile at DPR 2, and tap with a real touchscreen. The driver records, for every animation frame after the capture-phase listener of the first non-modifier keydown or pointerdown, the display, opacity, transform and clip-path of the palette, scrim, shortcuts panel and its body, sidebar, capsule, sidebar toggle, settings overlay, confirm tip and glass bed, plus every window rect and every running CSS transition or Web Animation. "INSTANT" means the first frame equals the final frame and nothing ran. 154 steps in all.

I did not run verify quick or fast (reviewer rule). The author's gate output is J370's finish comment.

1. Keyboard opens and closes: instant in the first frame (all four combos)

Surface (key) First frame after the key Running transitions
⌘K palette (Ctrl+Shift+K, toggle with same chord) #palette display shown, opacity 1, matrix(1,0,0,1,-308,-225); scrim opacity 1 none
Escape on the palette #palette none, opacity 0; capsule opacity 1 none
? and Ctrl+Shift+/ shortcuts panel .ui-shortcuts opacity 1, clip-path none; body opacity 1; capsule 0 none
Escape / same chord on the panel panel gone in frame 1 none
Sidebar (focus toggle + Enter), Escape #sidebar opacity 1, toggle already at matrix(1,0,0,1,308,65) none
Settings via palette + Enter, Escape .ui-settings final state none
Split (Ctrl+Shift+D), close window (Ctrl+Shift+W) window rects equal final in frame 1 none

Examples (~/j373/matrix-head.log, head): 1440 ink palette key open F1 = final = #palette=shown|1|matrix(1,0,0,1,-308,-225), .palette-scrim=shown|1, frame at +12.3 ms. Phone paper ?: F1 = final = .ui-shortcuts=shown|1, body 1, capsule 0, +1.4 ms. On origin/dev the same steps are 16 of 16 ANIMATED (~/j373/matrix-base-desktop-ink.log): palette key open F1 opacity 0, scale 0.98, 201 ms of transitions, so the probe does tell the two apart.

Only exception, and not this branch's: on the desktop size the sidebar key-open shows diff:[windows]. Window 1 shifts 14 px (978→964) in the second frame, a one-frame layout step, not a transition. origin/dev does the identical thing (~/j373/side.mjs run: 978→964 at +29 ms on base, +40 ms on head). The sidebar and its toggle are final in frame 1. Out of scope, noted.

2. Pointer and touch still animate; mixed close directions behave

No pointer or touch step was instant (0 of all pointer steps). Last-run durations of the running transitions (~/j373/matrix-head.log): palette pointer open (opacity 0 → 1, scale 0.98 → 1) 230–271 ms over the 8 samples at both sizes; pointer close 131–201 ms; touch tap open 265–275 ms, tap close 166–178 ms. Sidebar pointer open/close 243–351 ms (most 256–294). Settings pointer open 219–282 ms. Pointer open of the shortcuts panel (palette row click) about 0.9 s including the palette fade.
Keyboard close (Escape) of a pointer-opened palette, sidebar, shortcuts panel and settings: instant in frame 1, in every combo. Pointer close of a keyboard-opened palette (scrim) and sidebar: animated, which the issue allows.

3. Edge cases

  • P2 FINDING, modifier alone kills a running pointer animation. Click the capsule, press Shift (down+up, nothing else) at +60 ms: the palette fade is cut and reaches opacity 1 early. Because keydown for any key sets data-input="keyboard", the transition: none rule applies at once and running transitions are cancelled. Representative series (palette opacity), control vs Shift:
    • 1440 paper: control reaches 1 at +300 ms; Shift at +60 ms → 1 at +133 ms (99:0.32 165:1.00).
    • 393 paper: control +293 ms; Shift → +94 ms (47:0.11 94:1.00).
    • 393 ink: control +382 ms; Shift → +194 ms. 1440 ink: control did not finish inside the sampled frames (host load, sparse frames); Shift → +183 ms.
      Evidence: ~/j373/matrix-head.log, lines starting E1 mid-animation; first run ~/j373/matrix-head-run1.log shows the same. A letter key does the same (full opacity at +115 to +192 ms), which is arguably fine ("newest input wins", and it is typed text), so the ask is only about modifiers. Also data-input is keyboard after Shift alone (E2 data-input after Shift alone: keyboard), though the next click correctly animates again (E2 click after Shift alone: ANIMATED, 269–481 ms; Shift held while clicking: ANIMATED, 226–290 ms, as pointerdown comes after the modifier).
      Requested fix: in keydown, ignore modifier-only events (e.key in Shift, Control, Alt, Meta, AltGraph, CapsLock, Fn). A chord's final key sets the state in capture before any handler, so ⌘K, Ctrl+Shift+K, ⌘/ still work. Add a unit case plus one spec: click the capsule, press Shift at +60 ms, palette opacity still < 1 at +100 ms. I did not apply or run this fix.
  • Terminal typing, then click: after typing ls -la in the terminal data-input is keyboard; the next click on the capsule starts a normal animation (250–390 ms, 4 of 4 combos); a touch tap after typing on the phone also animates (220–253 ms). No pointer press is swallowed (the listener is on window in capture).
  • IME / composition: composition events with no keydown (CDP imeSetComposition + insertText, "日本" lands in #palette-input) leave data-input at pointer. A real IME sends keydown Process (229); sending that sets keyboard, like typing. The palette then closes instantly on Escape, and a following click animates again. Real IME hardware and iOS soft keyboards not tested.
  • Cost of the switch: flipping data-input plus forced style and layout costs 0.14–0.41 ms per toggle on this 845-node canvas (a neutral attribute: 0.002 ms). That is paid only at a pointer↔key change, not per key. Under the 16 ms bar; noted because latency is priority 1.

4. Doc page, add-host prototype, other surfaces

  • Add-host prototype: J370's note is wrong. It is mounted from inside start() in main.ts, after installInputSource(). On ?proto=addhost data-input is pointer at load and keyboard after a key, in all four combos (addhost prototype page: … lines). Nothing to install.
  • Doc page: it loads only doc.js (doc-page.ts), no tracker. Its only animated openers are <details> disclosures (.why, .toc-fold, 200 ms block-size) that open by pointer or Enter/Space, not by a shortcut. I would not install the tracker there for #427: doc.js is already over budget (13408 B vs 12000) and J372 is fixing that. If the owner wants keyboard-toggled disclosures instant, that is a separate small CSS-only item.
  • Own-logic surfaces are consistent with the rule in what I could drive: settings (palette+Enter and Escape instant, sidebar pill click animated), splits and close-window by key (instant), anchored popovers and details panels animate only for source === "pointer" in code. Not driven: window-docs chord, session-manager popover, row menus, confirm tip on pointer vs key.
  • Open question for kayg, not blocking: the camera view flights (camera.keyFlight, ⌘/Ctrl+digit and palette view picks) are still a short spring on a key. They are navigation, not open/close, so not covered by #427's wording, but TASTE's "actions originating from keyboard shortcuts = no animation" could be read to cover them. My attempt to measure it was inconclusive (window 0 did not move), so I give no number.
  • Design note (P3): data-input stays keyboard until the next pointerdown. A pointer-less open of one of the covered classes (palette, scrim, sidebar, top pills, glass bed) after typing would be instant. I found no live case: all of them open from an input. Pointer hover is deliberately not a source (synthetic moves after layout would flip it while typing).
  • Test fragility (P3): keyboard-instant.spec.ts arm() fires on the first keydown, which for Control+Shift+K is the Control press, so its "first frame" is the first rAF after Control. It passes by timing. Make it ignore modifier-only keydowns. My own probe showed the same trap: using e.timeStamp as the reference produced three false "animated" rows (~/j373/matrix-head-run1.log), gone when I took performance.now() inside the capture listener (~/j373/matrix-head.log).

5. Bundle size (non-preview production build, chunks.json classes; ~/j373/plain-head, plain-base)

  • main.js: 28860 → 28868 B raw (+8), br 8947 → 8955 (+8).
  • Critical JS (38 files): 1,024,659 → 1,025,181 B raw (+522), gz +78, br +9. Deferred JS (4 files): 164,068 B raw, unchanged.
  • All JS: raw +522, br −22. style.css 50436 → 50386 (−50); all CSS raw +535, gz +204, br +183.
  • doc.js (docs build): 13408 B on both head and origin/dev, and it contains none of the tracker code. Not pushed further over. J370's "about 1 KB" was an estimate; the measured raw delta is 522 B.

Not covered

Mac ⌘ chords (Linux Chromium, Ctrl+Shift chords), real iOS Safari and a physical touch device, a real IME, hover-opened tooltips, window-docs chord, camera flights. No screenshots: this is timing, so the artifact is the frame logs.

Artifacts (compute01, ~/j373/)

matrix-head.log (final 154-step run, all four combos), matrix-head-run1.log (first run, e.timeStamp reference), matrix-base-desktop-ink.log (origin/dev, 16/16 animated), evidence-summary.txt (attached), probe probe.mjs, side.mjs, build-*.log, plain-* bundles, docs-*.

Head reviewed: 865116ec6. Servers started on ports 4731/4732 were stopped by PID.

## J373 review of `fix-kbd-no-anim-427` @ 865116ec6 (base origin/dev 77b1255cb, which contains the amended TASTE row 7c8bd2221) **Verdict: REQUEST CHANGES.** One real defect (P2): a modifier key alone kills a running pointer animation. Everything else holds. The fix is a few lines in `input-source.ts`. ### What I ran Production tracer build (`bun run build.ts`, preview flag on so the test double loads) of the head and of origin/dev, each served by `test/mock-server.ts` on compute01 through build-slot (launcher checked first: `nix develop . -c bun --version` outside the slot). Headless Chromium, `?mock=1&proto=windows`, theme `ink` and `paper`, at 1440×900 and 393×852. The phone runs use `hasTouch/isMobile` at DPR 2, and tap with a real touchscreen. The driver records, for every animation frame after the capture-phase listener of the first non-modifier `keydown` or `pointerdown`, the display, opacity, transform and clip-path of the palette, scrim, shortcuts panel and its body, sidebar, capsule, sidebar toggle, settings overlay, confirm tip and glass bed, plus every window rect and every running CSS transition or Web Animation. "INSTANT" means the first frame equals the final frame and nothing ran. 154 steps in all. I did not run `verify quick` or `fast` (reviewer rule). The author's gate output is J370's finish comment. ### 1. Keyboard opens and closes: instant in the first frame (all four combos) | Surface (key) | First frame after the key | Running transitions | |---|---|---| | ⌘K palette (`Ctrl+Shift+K`, toggle with same chord) | `#palette` display shown, opacity 1, `matrix(1,0,0,1,-308,-225)`; scrim opacity 1 | none | | Escape on the palette | `#palette` `none`, opacity 0; capsule opacity 1 | none | | `?` and `Ctrl+Shift+/` shortcuts panel | `.ui-shortcuts` opacity 1, clip-path none; body opacity 1; capsule 0 | none | | Escape / same chord on the panel | panel gone in frame 1 | none | | Sidebar (focus toggle + Enter), Escape | `#sidebar` opacity 1, toggle already at `matrix(1,0,0,1,308,65)` | none | | Settings via palette + Enter, Escape | `.ui-settings` final state | none | | Split (`Ctrl+Shift+D`), close window (`Ctrl+Shift+W`) | window rects equal final in frame 1 | none | Examples (`~/j373/matrix-head.log`, head): 1440 ink palette key open F1 = final = `#palette=shown|1|matrix(1,0,0,1,-308,-225)`, `.palette-scrim=shown|1`, frame at +12.3 ms. Phone paper `?`: F1 = final = `.ui-shortcuts=shown|1`, body 1, capsule 0, +1.4 ms. On origin/dev the same steps are 16 of 16 ANIMATED (`~/j373/matrix-base-desktop-ink.log`): palette key open F1 opacity 0, scale 0.98, 201 ms of transitions, so the probe does tell the two apart. Only exception, and not this branch's: on the desktop size the sidebar key-open shows `diff:[windows]`. Window 1 shifts 14 px (978→964) in the second frame, a one-frame layout step, not a transition. origin/dev does the identical thing (`~/j373/side.mjs` run: 978→964 at +29 ms on base, +40 ms on head). The sidebar and its toggle are final in frame 1. Out of scope, noted. ### 2. Pointer and touch still animate; mixed close directions behave No pointer or touch step was instant (0 of all pointer steps). Last-run durations of the running transitions (`~/j373/matrix-head.log`): palette pointer open (opacity 0 → 1, scale 0.98 → 1) 230–271 ms over the 8 samples at both sizes; pointer close 131–201 ms; touch tap open 265–275 ms, tap close 166–178 ms. Sidebar pointer open/close 243–351 ms (most 256–294). Settings pointer open 219–282 ms. Pointer open of the shortcuts panel (palette row click) about 0.9 s including the palette fade. Keyboard close (Escape) of a pointer-opened palette, sidebar, shortcuts panel and settings: instant in frame 1, in every combo. Pointer close of a keyboard-opened palette (scrim) and sidebar: animated, which the issue allows. ### 3. Edge cases - **P2 FINDING, modifier alone kills a running pointer animation.** Click the capsule, press Shift (down+up, nothing else) at +60 ms: the palette fade is cut and reaches opacity 1 early. Because `keydown` for any key sets `data-input="keyboard"`, the `transition: none` rule applies at once and running transitions are cancelled. Representative series (palette opacity), control vs Shift: - 1440 paper: control reaches 1 at +300 ms; Shift at +60 ms → 1 at +133 ms (`99:0.32 165:1.00`). - 393 paper: control +293 ms; Shift → +94 ms (`47:0.11 94:1.00`). - 393 ink: control +382 ms; Shift → +194 ms. 1440 ink: control did not finish inside the sampled frames (host load, sparse frames); Shift → +183 ms. Evidence: `~/j373/matrix-head.log`, lines starting `E1 mid-animation`; first run `~/j373/matrix-head-run1.log` shows the same. A letter key does the same (full opacity at +115 to +192 ms), which is arguably fine ("newest input wins", and it is typed text), so the ask is only about modifiers. Also `data-input` is `keyboard` after Shift alone (`E2 data-input after Shift alone: keyboard`), though the next click correctly animates again (`E2 click after Shift alone`: ANIMATED, 269–481 ms; Shift held while clicking: ANIMATED, 226–290 ms, as pointerdown comes after the modifier). **Requested fix:** in `keydown`, ignore modifier-only events (`e.key` in Shift, Control, Alt, Meta, AltGraph, CapsLock, Fn). A chord's final key sets the state in capture before any handler, so ⌘K, Ctrl+Shift+K, ⌘/ still work. Add a unit case plus one spec: click the capsule, press Shift at +60 ms, palette opacity still < 1 at +100 ms. I did not apply or run this fix. - **Terminal typing, then click:** after typing `ls -la` in the terminal `data-input` is `keyboard`; the next click on the capsule starts a normal animation (250–390 ms, 4 of 4 combos); a touch tap after typing on the phone also animates (220–253 ms). No pointer press is swallowed (the listener is on `window` in capture). - **IME / composition:** composition events with no keydown (CDP `imeSetComposition` + `insertText`, "日本" lands in `#palette-input`) leave `data-input` at `pointer`. A real IME sends keydown `Process` (229); sending that sets `keyboard`, like typing. The palette then closes instantly on Escape, and a following click animates again. Real IME hardware and iOS soft keyboards not tested. - **Cost of the switch:** flipping `data-input` plus forced style and layout costs 0.14–0.41 ms per toggle on this 845-node canvas (a neutral attribute: 0.002 ms). That is paid only at a pointer↔key change, not per key. Under the 16 ms bar; noted because latency is priority 1. ### 4. Doc page, add-host prototype, other surfaces - **Add-host prototype: J370's note is wrong.** It is mounted from inside `start()` in `main.ts`, after `installInputSource()`. On `?proto=addhost` `data-input` is `pointer` at load and `keyboard` after a key, in all four combos (`addhost prototype page: …` lines). Nothing to install. - **Doc page:** it loads only `doc.js` (`doc-page.ts`), no tracker. Its only animated openers are `<details>` disclosures (`.why`, `.toc-fold`, 200 ms block-size) that open by pointer or Enter/Space, not by a shortcut. I would not install the tracker there for #427: doc.js is already over budget (13408 B vs 12000) and J372 is fixing that. If the owner wants keyboard-toggled disclosures instant, that is a separate small CSS-only item. - **Own-logic surfaces are consistent with the rule** in what I could drive: settings (palette+Enter and Escape instant, sidebar pill click animated), splits and close-window by key (instant), anchored popovers and details panels animate only for `source === "pointer"` in code. Not driven: window-docs chord, session-manager popover, row menus, confirm tip on pointer vs key. - **Open question for kayg, not blocking:** the camera view flights (`camera.keyFlight`, ⌘/Ctrl+digit and palette view picks) are still a short spring on a key. They are navigation, not open/close, so not covered by #427's wording, but TASTE's "actions originating from keyboard shortcuts = no animation" could be read to cover them. My attempt to measure it was inconclusive (window 0 did not move), so I give no number. - **Design note (P3):** `data-input` stays `keyboard` until the next pointerdown. A pointer-less open of one of the covered classes (palette, scrim, sidebar, top pills, glass bed) after typing would be instant. I found no live case: all of them open from an input. Pointer hover is deliberately not a source (synthetic moves after layout would flip it while typing). - **Test fragility (P3):** `keyboard-instant.spec.ts` `arm()` fires on the first `keydown`, which for `Control+Shift+K` is the Control press, so its "first frame" is the first rAF after Control. It passes by timing. Make it ignore modifier-only keydowns. My own probe showed the same trap: using `e.timeStamp` as the reference produced three false "animated" rows (`~/j373/matrix-head-run1.log`), gone when I took `performance.now()` inside the capture listener (`~/j373/matrix-head.log`). ### 5. Bundle size (non-preview production build, `chunks.json` classes; `~/j373/plain-head`, `plain-base`) - `main.js`: 28860 → 28868 B raw (+8), br 8947 → 8955 (+8). - Critical JS (38 files): 1,024,659 → 1,025,181 B raw (**+522**), gz +78, br +9. Deferred JS (4 files): 164,068 B raw, unchanged. - All JS: raw +522, br −22. `style.css` 50436 → 50386 (−50); all CSS raw +535, gz +204, br +183. - `doc.js` (docs build): **13408 B on both** head and origin/dev, and it contains none of the tracker code. Not pushed further over. J370's "about 1 KB" was an estimate; the measured raw delta is 522 B. ### Not covered Mac ⌘ chords (Linux Chromium, Ctrl+Shift chords), real iOS Safari and a physical touch device, a real IME, hover-opened tooltips, window-docs chord, camera flights. No screenshots: this is timing, so the artifact is the frame logs. ### Artifacts (compute01, `~/j373/`) `matrix-head.log` (final 154-step run, all four combos), `matrix-head-run1.log` (first run, e.timeStamp reference), `matrix-base-desktop-ink.log` (origin/dev, 16/16 animated), `evidence-summary.txt` (attached), probe `probe.mjs`, `side.mjs`, `build-*.log`, `plain-*` bundles, `docs-*`. Head reviewed: 865116ec6. Servers started on ports 4731/4732 were stopped by PID.
Author
Owner

J370, fixes for J373's review (comment 86708). Branch fix-kbd-no-anim-427, head 0ae56a4e0 (pushed, not merged), on top of 865116ec6. One commit each:

  1. abce13b43 P2: input-source.ts ignores modifier-only keydowns (Shift, Control, Alt, AltGraph, Meta and similar). The chord's last key still sets the state before its handler. Unit test extended. New spec "Shift pressed during a pointer open does not cut the fade short": clicks the capsule, presses Shift at +60 ms, and the opacity transition runs over 200 ms and is never cancelled.
  2. c4b13db87 spec sampler now arms on the chord's final non-modifier key. Both triggers share one AbortController so a stale listener can't resample a later step.
  3. 0ae56a4e0 camera.keyFlight makes the camera jump to its target in the same call (the instant path); the 80 ms KEY_FLIGHT spring is gone. Pointer, touch and pill flights keep their spring. New spec: one frame after Control+Shift+3 the camera equals its settled position; one frame after a view-list click it is still on its way.

Red: the new specs run against the old source 865116ec6 (~/j370-red2.log on dev-02): the Shift spec and the flight spec fail, the other 12 pass. Item 2 has no red of its own, since it hardens the sampler itself.
Green (~/j370-green2.log, dev-02, at f26551829, which differs from 0ae56a4e0 only by a doc comment on Camera): 22 passed, exit=0. That is keyboard-instant (14 cases), clusters-flight (FLIGHT {"pointer":272,"pointerBack":270,"key":0,"key2":0}, so key flights settle in 0 ms and pointer ones in about 270 ms), clusters-shortcuts and palette-navigation, plus the input-source unit test (1 pass).
verify quick at 0ae56a4e0 (dev-02, ~/j370-quick.log): "QUICK pre-check passed", exit=0. I have not rerun verify fast; the earlier result (only the pre-existing doc.js size failure) was at 865116ec6.

J370, fixes for J373's review (comment 86708). Branch `fix-kbd-no-anim-427`, head **0ae56a4e0** (pushed, not merged), on top of 865116ec6. One commit each: 1. abce13b43 **P2**: `input-source.ts` ignores modifier-only keydowns (Shift, Control, Alt, AltGraph, Meta and similar). The chord's last key still sets the state before its handler. Unit test extended. New spec "Shift pressed during a pointer open does not cut the fade short": clicks the capsule, presses Shift at +60 ms, and the opacity transition runs over 200 ms and is never cancelled. 2. c4b13db87 spec sampler now arms on the chord's final non-modifier key. Both triggers share one AbortController so a stale listener can't resample a later step. 3. 0ae56a4e0 `camera.keyFlight` makes the camera jump to its target in the same call (the `instant` path); the 80 ms `KEY_FLIGHT` spring is gone. Pointer, touch and pill flights keep their spring. New spec: one frame after Control+Shift+3 the camera equals its settled position; one frame after a view-list click it is still on its way. **Red**: the new specs run against the old source 865116ec6 (`~/j370-red2.log` on dev-02): the Shift spec and the flight spec fail, the other 12 pass. Item 2 has no red of its own, since it hardens the sampler itself. **Green** (`~/j370-green2.log`, dev-02, at f26551829, which differs from 0ae56a4e0 only by a doc comment on `Camera`): 22 passed, exit=0. That is keyboard-instant (14 cases), clusters-flight (`FLIGHT {"pointer":272,"pointerBack":270,"key":0,"key2":0}`, so key flights settle in 0 ms and pointer ones in about 270 ms), clusters-shortcuts and palette-navigation, plus the input-source unit test (1 pass). **`verify quick`** at 0ae56a4e0 (dev-02, `~/j370-quick.log`): "QUICK pre-check passed", exit=0. I have not rerun `verify fast`; the earlier result (only the pre-existing doc.js size failure) was at 865116ec6.
Author
Owner

J378 start: re-review of fix-kbd-no-anim-427 @ 0ae56a4e0 (J373 requested changes at 865116ec6). Scratch worktree on dev-02: merge of origin/dev b31c2da2d into the head. Plan: check the three fixes (modifier-only keydown, spec sampler arming, instant keyboard camera flights), probe stale input-source state (keyboard lingering into pointer, iPad touch plus hardware keyboard), focus/a11y, reduced motion, and other keyboard-opened surfaces. Then run on a production build: keyboard-instant, clusters-flight, clusters-shortcuts, palette-navigation, the input-source unit test, and scripts/verify quick. Won't merge, push or deploy.

J378 start: re-review of `fix-kbd-no-anim-427` @ 0ae56a4e0 (J373 requested changes at 865116ec6). Scratch worktree on dev-02: merge of origin/dev b31c2da2d into the head. Plan: check the three fixes (modifier-only keydown, spec sampler arming, instant keyboard camera flights), probe stale input-source state (keyboard lingering into pointer, iPad touch plus hardware keyboard), focus/a11y, reduced motion, and other keyboard-opened surfaces. Then run on a production build: keyboard-instant, clusters-flight, clusters-shortcuts, palette-navigation, the input-source unit test, and `scripts/verify quick`. Won't merge, push or deploy.
Author
Owner

J378 finding 1 (P2, still open): key-triggered camera flights other than ⌘/Ctrl+digit and a palette view pick still use the spring.

keyFlight is only called from three places (toolbar/shortcuts.ts:102, palette-actions.on-demand.ts:31 and :57). Other key paths call nav.showOverview() / nav.setCanvasLock() / nav.focusWindow() directly, so flightKind stays "gesture" and flyTo springs. Measured on the production build (clusters mockup, scene 3, 1280×800; probe ~/worktrees/j378/apps/tracer/test/j378-probe.spec.ts and …-probe2.spec.ts on infraternal-dev-02-std, logs ~/j378-main.log, ~/j378-probe2.log). "settled" = last frame the camera was more than 1 px from its end point, counted from the key:

Key path Camera result
Escape on the canvas (shortcuts.ts:88-93, showOverview()) animated, settles at 262 ms (cx 2204→2027, z 1→0.286)
Ctrl+Shift+G, canvas lock (shortcuts.ts:70, setCanvasLock) animated, 296 ms
⌘K, "All views", Enter (palette-actions.on-demand.ts:32) animated, 584 ms
⌘K, a host window row, Enter (canvas:open:nix-cache, goes through focusWindow) animated, 515 ms
⌘K, a view row, Enter (the fixed path) instant, 4–8 ms (frame jitter)
Ctrl+Shift+D split, Ctrl+Shift+Enter new window instant, 0 ms

Failure scenario: the owner presses Escape to zoom out to the overview, or picks "All views" or a window in ⌘K by keyboard. The camera springs for about 260–580 ms, which contradicts TASTE row "Work is instant" (4) and the owner's "actions originating from keyboard shortcuts = no animation". The fix-3 claim ("keyboard camera flights jump instantly") holds only for the three keyFlight callers.

Suggested fix: decide the flight kind from triggeredByKeyboard() inside Camera.flyTo (it is already the one tracker), and keep keyFlight only for callers that run before a key event (or drop it). That removes the per-caller wrapper that was missed here. Add a spec case for Escape and for a palette "All views" pick.

J378 finding 1 (P2, still open): key-triggered camera flights other than ⌘/Ctrl+digit and a palette view pick still use the spring. `keyFlight` is only called from three places (`toolbar/shortcuts.ts:102`, `palette-actions.on-demand.ts:31` and `:57`). Other key paths call `nav.showOverview()` / `nav.setCanvasLock()` / `nav.focusWindow()` directly, so `flightKind` stays `"gesture"` and `flyTo` springs. Measured on the production build (clusters mockup, scene 3, 1280×800; probe `~/worktrees/j378/apps/tracer/test/j378-probe.spec.ts` and `…-probe2.spec.ts` on infraternal-dev-02-std, logs `~/j378-main.log`, `~/j378-probe2.log`). "settled" = last frame the camera was more than 1 px from its end point, counted from the key: | Key path | Camera result | |---|---| | Escape on the canvas (`shortcuts.ts:88-93`, `showOverview()`) | animated, settles at 262 ms (cx 2204→2027, z 1→0.286) | | Ctrl+Shift+G, canvas lock (`shortcuts.ts:70`, `setCanvasLock`) | animated, 296 ms | | ⌘K, "All views", Enter (`palette-actions.on-demand.ts:32`) | animated, 584 ms | | ⌘K, a host window row, Enter (`canvas:open:nix-cache`, goes through `focusWindow`) | animated, 515 ms | | ⌘K, a view row, Enter (the fixed path) | instant, 4–8 ms (frame jitter) | | Ctrl+Shift+D split, Ctrl+Shift+Enter new window | instant, 0 ms | Failure scenario: the owner presses Escape to zoom out to the overview, or picks "All views" or a window in ⌘K by keyboard. The camera springs for about 260–580 ms, which contradicts TASTE row "Work is instant" (4) and the owner's "actions originating from keyboard shortcuts = no animation". The fix-3 claim ("keyboard camera flights jump instantly") holds only for the three `keyFlight` callers. Suggested fix: decide the flight kind from `triggeredByKeyboard()` inside `Camera.flyTo` (it is already the one tracker), and keep `keyFlight` only for callers that run before a key event (or drop it). That removes the per-caller wrapper that was missed here. Add a spec case for Escape and for a palette "All views" pick.
Author
Owner

J378 finding 2 (P2, new): the pill-morph popovers (the capsule's view list, the sharing popover, the floating terminal) never read the input source, so they spring from the keyboard.

packages/ui/src/pill-morph.ts has no triggeredByKeyboard() check (only its instant argument and reduced motion). Its three users are toolbar/viewlist.on-demand.ts, sharing/sharing.on-demand.ts and windows/float.on-demand.ts. Measured on the production build, clusters mockup (the product canvas; the capsule opens the view list here), 1280×800, probe ~/j378-probes/j378-probe3.spec.ts, log ~/j378-probe3.log on infraternal-dev-02-std. Every frame after the key is sampled; clip is the panel's computed clip-path:

  • Focus the capsule, press Enter: first frame clip=inset(0px 0px 168.203px round 20.9px) (210 px tall panel, 168 px still clipped), 100 ms later inset(… 115.353px …), settled at the end. Animated. A pointer click gives the same series (168.2 → 110.6), so the key changes nothing.
  • Escape on it: first frame clip=inset(0px round 19.8px) (still fully shown), 100 ms 59.2px clipped, then removed. Animated close from a key. Same for Escape on a pointer-opened list.
  • ⌘K → type "share" → Enter opens the sharing popover (canvas:share): clip=inset(0px 0px 282.77px …) is still on screen at the last sample, 700 ms later, so it is mid-spring.

Failure scenario: a keyboard user Tabs to the capsule, presses Enter, sees the view list grow out of the pill over a few hundred ms, then presses Escape and sees it shrink. TASTE "Work is instant" (4) says anything opened or closed from the keyboard is instant, and the Escape on the view list is exactly the "same key reverses it" case. J373 listed "session-manager popover, row menus" as not driven; this is the one surface that is on screen on every page load.

Suggested fix (one place): in pill-morph.ts, open() and close() jump (spring.set(…, true)) when triggeredByKeyboard() is true, the same as shortcuts-view.ts now does. That covers all three users. Add a case in keyboard-instant.spec.ts for Enter and Escape on the view list.

Not a regression of this branch (origin/dev animates these too), but the branch's stated claim and TASTE row (4) say keyboard opens and closes are instant, and these are the remaining ones.

J378 finding 2 (P2, new): the pill-morph popovers (the capsule's view list, the sharing popover, the floating terminal) never read the input source, so they spring from the keyboard. `packages/ui/src/pill-morph.ts` has no `triggeredByKeyboard()` check (only its `instant` argument and reduced motion). Its three users are `toolbar/viewlist.on-demand.ts`, `sharing/sharing.on-demand.ts` and `windows/float.on-demand.ts`. Measured on the production build, clusters mockup (the product canvas; the capsule opens the view list here), 1280×800, probe `~/j378-probes/j378-probe3.spec.ts`, log `~/j378-probe3.log` on infraternal-dev-02-std. Every frame after the key is sampled; `clip` is the panel's computed clip-path: - Focus the capsule, press **Enter**: first frame `clip=inset(0px 0px 168.203px round 20.9px)` (210 px tall panel, 168 px still clipped), 100 ms later `inset(… 115.353px …)`, settled at the end. Animated. A pointer click gives the same series (168.2 → 110.6), so the key changes nothing. - **Escape** on it: first frame `clip=inset(0px round 19.8px)` (still fully shown), 100 ms `59.2px` clipped, then removed. Animated close from a key. Same for Escape on a pointer-opened list. - ⌘K → type "share" → **Enter** opens the sharing popover (`canvas:share`): `clip=inset(0px 0px 282.77px …)` is still on screen at the last sample, 700 ms later, so it is mid-spring. Failure scenario: a keyboard user Tabs to the capsule, presses Enter, sees the view list grow out of the pill over a few hundred ms, then presses Escape and sees it shrink. TASTE "Work is instant" (4) says anything opened or closed from the keyboard is instant, and the Escape on the view list is exactly the "same key reverses it" case. J373 listed "session-manager popover, row menus" as not driven; this is the one surface that is on screen on every page load. Suggested fix (one place): in `pill-morph.ts`, `open()` and `close()` jump (`spring.set(…, true)`) when `triggeredByKeyboard()` is true, the same as `shortcuts-view.ts` now does. That covers all three users. Add a case in `keyboard-instant.spec.ts` for Enter and Escape on the view list. Not a regression of this branch (origin/dev animates these too), but the branch's stated claim and TASTE row (4) say keyboard opens and closes are instant, and these are the remaining ones.
Author
Owner

J378 re-review of fix-kbd-no-anim-427 @ 0ae56a4e0 (merged with origin/dev b31c2da2d in a scratch worktree on dev-02)

Verdict: REQUEST CHANGES. The three fixes J373 asked for hold. Two new P2s remain: keyboard still animates on two classes of surface that the branch's own rule (TASTE "Work is instant" (4)) covers. Both fix at one leaf each.

The three fixes: verified

  1. Modifier-only keydown (input-source.ts, MODIFIER_KEYS): the spec "Shift pressed during a pointer open does not cut the fade short" passes on a production build, and the unit test passes. The chord's final key still sets keyboard in capture before its handler (⌘K / Ctrl+Shift+K / ? all instant in keyboard-instant.spec.ts).
  2. Spec sampler: arm() now ignores Control, Shift, Alt, Meta and AltGraph, and both triggers share one AbortController. Read in the diff and green in both viewports.
  3. Keyboard camera flights: clusters-flight.spec.ts prints FLIGHT {"pointer":265,"pointerBack":261,"key":1,"key2":4} (pointer ~265 ms, key flights at once). keyboard-instant.spec.ts "a camera flight from a key jumps at once, a pointer one still flies" passes. A reduced-motion key flight also jumps with no world flash (flyTo tests flightKind === "key" before the reduced branch).

Gate results

  • Specs on a production build, dev-02 (~/j378-main.log): keyboard-instant (14 cases), clusters-flight, clusters-shortcuts, palette-navigation: 27 passed, 0 failed (the log's "2 failed" are my own scratch probe's first version, rewritten and rerun below). Input-source unit test: 1 pass.
  • scripts/verify quick on the clean merged tree (~/j378-quick.log): "QUICK pre-check passed", exit=0. (A first run failed only the "doc comments on diff" row because of my untracked probe files, ~/j378-quick-with-probes.log; I moved them out and reran.)
  • Not run: verify fast.

P2 findings (details in the two comments above)

  • P2-1 (finding 1): Escape → overview (262 ms), Ctrl+Shift+G canvas lock (296 ms), palette "All views" Enter (584 ms) and palette window pick Enter (515 ms) still spring. Fix: choose the flight kind from triggeredByKeyboard() inside Camera.flyTo.
  • P2-2 (finding 2): the pill-morph popovers (capsule view list, sharing popover, floating terminal) spring on Enter and Escape (clip-path 168 px clipped in the first frame after Enter; 59 px clipped 100 ms after Escape). Fix: make pill-morph.ts open/close jump when triggeredByKeyboard().

P3

  • Typing during a pointer open pops it. A letter typed 60 ms into a pointer-opened palette: opacity 0.22 → 1 in the next frame and transitioncancel fires (~/j378-probe3.log, "letter during pointer fade"). Input is not blocked and the character lands (value "t"), but the global data-input attribute also cuts running transitions, not only new ones. Acceptable if intended; a latch-at-open design would avoid the pop.
  • Pills lose all easing after a key, not just open/close. With data-input="keyboard" the capsule's transition-duration is 0s; after a pointer press it is 0.12s opacity. Hover and cover-fade easing on .ui-key-instant elements stay off until the next pointer press (~/j378-probe.log section of ~/j378-main.log, "pill transitions after key vs after pointer"). Cosmetic.
  • clusters-flight.spec.ts key bound is loose (< 300 would still pass a 190 ms spring). The strict first-frame assertion lives in keyboard-instant.spec.ts, so coverage is fine; just don't rely on the former.
  • Pre-existing, not this branch (diff doesn't touch it): after Escape closes ⌘K, focus is BODY (no restore), and under prefers-reduced-motion a pointer-open palette still runs the same 240 ms fade (0, 0.11, 0.22 … 0.92). Both identical with reduced motion on and off.

Checked and fine

  • Stale state, touch + hardware keyboard (iPad, 820×1180, hasTouch): after a key, data-input is keyboard; a touch tap on the capsule flips it to pointer and the palette animates (opacity 0 → 0.59 at 100 ms → 1); a hardware Escape then closes it in the first frame (~/j378-probe3.log).
  • Focus/a11y: ⌘K opens with focus in #palette-input in the first frame, aria-modal="true"; the ? panel opens as role=dialog, aria-label="Keyboard shortcuts", focused, and Escape returns focus to BODY. Same with reduced motion on and off (~/j378-probe4.log).
  • Reduced motion: key palette open is instant, key ? open is instant, key camera flight jumps with no flash. No regression versus the non-reduced run.
  • State going stale: it can only err toward "instant" (key state lingering after a pointer-less change), never toward a swallowed press, since pointerdown is captured on window.

Artifact: j378-evidence.txt (attached; all frame samples and gate output). Probes (untracked, never pushed): ~/j378-probes/ on infraternal-dev-02-std. No screenshots: this is a timing change, so frame samples are the evidence. Nothing merged, pushed or deployed; scratch worktree removed.

Verdict: REQUEST CHANGES. Reviewed SHA 0ae56a4e0. Gate: 27/27 required specs pass, unit pass, verify quick pass (exit 0), verify fast not run.

## J378 re-review of `fix-kbd-no-anim-427` @ 0ae56a4e0 (merged with origin/dev b31c2da2d in a scratch worktree on dev-02) **Verdict: REQUEST CHANGES.** The three fixes J373 asked for hold. Two new P2s remain: keyboard still animates on two classes of surface that the branch's own rule (TASTE "Work is instant" (4)) covers. Both fix at one leaf each. ### The three fixes: verified 1. **Modifier-only keydown** (`input-source.ts`, `MODIFIER_KEYS`): the spec "Shift pressed during a pointer open does not cut the fade short" passes on a production build, and the unit test passes. The chord's final key still sets `keyboard` in capture before its handler (⌘K / Ctrl+Shift+K / `?` all instant in `keyboard-instant.spec.ts`). 2. **Spec sampler**: `arm()` now ignores Control, Shift, Alt, Meta and AltGraph, and both triggers share one AbortController. Read in the diff and green in both viewports. 3. **Keyboard camera flights**: `clusters-flight.spec.ts` prints `FLIGHT {"pointer":265,"pointerBack":261,"key":1,"key2":4}` (pointer ~265 ms, key flights at once). `keyboard-instant.spec.ts` "a camera flight from a key jumps at once, a pointer one still flies" passes. A reduced-motion key flight also jumps with no world flash (`flyTo` tests `flightKind === "key"` before the reduced branch). ### Gate results - Specs on a production build, dev-02 (`~/j378-main.log`): keyboard-instant (14 cases), clusters-flight, clusters-shortcuts, palette-navigation: **27 passed, 0 failed** (the log's "2 failed" are my own scratch probe's first version, rewritten and rerun below). Input-source unit test: 1 pass. - `scripts/verify quick` on the clean merged tree (`~/j378-quick.log`): "QUICK pre-check passed", exit=0. (A first run failed only the "doc comments on diff" row because of my untracked probe files, `~/j378-quick-with-probes.log`; I moved them out and reran.) - Not run: `verify fast`. ### P2 findings (details in the two comments above) - **P2-1** (finding 1): Escape → overview (262 ms), Ctrl+Shift+G canvas lock (296 ms), palette "All views" Enter (584 ms) and palette window pick Enter (515 ms) still spring. Fix: choose the flight kind from `triggeredByKeyboard()` inside `Camera.flyTo`. - **P2-2** (finding 2): the pill-morph popovers (capsule view list, sharing popover, floating terminal) spring on Enter and Escape (clip-path 168 px clipped in the first frame after Enter; 59 px clipped 100 ms after Escape). Fix: make `pill-morph.ts` open/close jump when `triggeredByKeyboard()`. ### P3 - **Typing during a pointer open pops it.** A letter typed 60 ms into a pointer-opened palette: opacity 0.22 → 1 in the next frame and `transitioncancel` fires (`~/j378-probe3.log`, "letter during pointer fade"). Input is not blocked and the character lands (value "t"), but the global `data-input` attribute also cuts running transitions, not only new ones. Acceptable if intended; a latch-at-open design would avoid the pop. - **Pills lose all easing after a key, not just open/close.** With `data-input="keyboard"` the capsule's `transition-duration` is `0s`; after a pointer press it is `0.12s opacity`. Hover and cover-fade easing on `.ui-key-instant` elements stay off until the next pointer press (`~/j378-probe.log` section of `~/j378-main.log`, "pill transitions after key vs after pointer"). Cosmetic. - **`clusters-flight.spec.ts` key bound is loose** (`< 300` would still pass a 190 ms spring). The strict first-frame assertion lives in `keyboard-instant.spec.ts`, so coverage is fine; just don't rely on the former. - **Pre-existing, not this branch (diff doesn't touch it):** after Escape closes ⌘K, focus is `BODY` (no restore), and under `prefers-reduced-motion` a pointer-open palette still runs the same 240 ms fade (0, 0.11, 0.22 … 0.92). Both identical with reduced motion on and off. ### Checked and fine - **Stale state, touch + hardware keyboard** (iPad, 820×1180, `hasTouch`): after a key, `data-input` is `keyboard`; a touch tap on the capsule flips it to `pointer` and the palette animates (opacity 0 → 0.59 at 100 ms → 1); a hardware Escape then closes it in the first frame (`~/j378-probe3.log`). - **Focus/a11y**: ⌘K opens with focus in `#palette-input` in the first frame, `aria-modal="true"`; the `?` panel opens as `role=dialog`, `aria-label="Keyboard shortcuts"`, focused, and Escape returns focus to `BODY`. Same with reduced motion on and off (`~/j378-probe4.log`). - **Reduced motion**: key palette open is instant, key `?` open is instant, key camera flight jumps with no flash. No regression versus the non-reduced run. - **State going stale**: it can only err toward "instant" (key state lingering after a pointer-less change), never toward a swallowed press, since `pointerdown` is captured on `window`. Artifact: `j378-evidence.txt` (attached; all frame samples and gate output). Probes (untracked, never pushed): `~/j378-probes/` on infraternal-dev-02-std. No screenshots: this is a timing change, so frame samples are the evidence. Nothing merged, pushed or deployed; scratch worktree removed. **Verdict: REQUEST CHANGES. Reviewed SHA 0ae56a4e0. Gate: 27/27 required specs pass, unit pass, `verify quick` pass (exit 0), `verify fast` not run.**
Author
Owner

J370 round 3 start. Branch fix-kbd-no-anim-427, merging origin/dev 098f2c445 onto 0ae56a4e0. Plan, red specs first: (a) Camera.flyTo picks instant from triggeredByKeyboard(), keyFlight dropped if redundant; (b) pill-morph.ts jumps on a key; (c) P3s: .ui-key-instant drops only open/close motion, tighten the clusters-flight key bound, Escape out of the palette restores focus, pointer-open fade respects reduced motion. Leaving the 'letter mid pointer-open snaps' P3 as is. Estimate about 2 h.

J370 round 3 start. Branch `fix-kbd-no-anim-427`, merging origin/dev 098f2c445 onto 0ae56a4e0. Plan, red specs first: (a) `Camera.flyTo` picks instant from `triggeredByKeyboard()`, `keyFlight` dropped if redundant; (b) `pill-morph.ts` jumps on a key; (c) P3s: `.ui-key-instant` drops only open/close motion, tighten the clusters-flight key bound, Escape out of the palette restores focus, pointer-open fade respects reduced motion. Leaving the 'letter mid pointer-open snaps' P3 as is. Estimate about 2 h.
Author
Owner

J370 round 3, red evidence (dev-02 production build, logs ~/j370-r3red.log and ~/j370-r3red2.log), before any fix, head 13eb6f966 (origin/dev merged in: 098f2c445):

  • P2-a Escape to overview, Ctrl+Shift+G (from the overview), palette "All views" + Enter, palette window pick (nix-cache) + Enter: all four fail with "the camera is already at its end one frame after the key" (it was still mid-spring), while each case does move the camera.
  • P2-b Enter on the focused capsule: the view list's first frame has clip-path: inset(0px 0px 167.322px round 20.8927px) (expected "none").
  • P3 capsule transition-duration is still "0s" 150 ms after a key; Escape out of ⌘K leaves #sidebar-toggle unfocused (was focused before); under reduced motion a pointer-open palette has opacity "0" in the first frame (the reduced-motion rule .palette{transition-duration:0ms} loses to .palette:not([hidden]){240ms} on specificity).
  • Two controls pass on red as they should: a pointer open of the view list still morphs; clusters-flight's key flight is already 0 ms (the bound is tightened to two frames, 34 ms, so a 190 ms spring now fails).
  • My first version of the Ctrl+Shift+G and window-pick cases never moved the camera, so they proved nothing; fixed in 13eb6f966 (starting from the overview and from view 1, J378's sequences).
    Fixes are in separate commits on the branch (tracker wheel, Camera.flyTo, pill-morph, fresh latch, focus, reduced motion); green run to follow.
J370 round 3, red evidence (dev-02 production build, logs `~/j370-r3red.log` and `~/j370-r3red2.log`), before any fix, head 13eb6f966 (origin/dev merged in: 098f2c445): - **P2-a** Escape to overview, Ctrl+Shift+G (from the overview), palette "All views" + Enter, palette window pick (nix-cache) + Enter: all four fail with "the camera is already at its end one frame after the key" (it was still mid-spring), while each case does move the camera. - **P2-b** Enter on the focused capsule: the view list's first frame has `clip-path: inset(0px 0px 167.322px round 20.8927px)` (expected "none"). - **P3** capsule `transition-duration` is still "0s" 150 ms after a key; Escape out of ⌘K leaves `#sidebar-toggle` unfocused (was focused before); under reduced motion a pointer-open palette has opacity "0" in the first frame (the reduced-motion rule `.palette{transition-duration:0ms}` loses to `.palette:not([hidden]){240ms}` on specificity). - Two controls pass on red as they should: a pointer open of the view list still morphs; `clusters-flight`'s key flight is already 0 ms (the bound is tightened to two frames, 34 ms, so a 190 ms spring now fails). - My first version of the Ctrl+Shift+G and window-pick cases never moved the camera, so they proved nothing; fixed in 13eb6f966 (starting from the overview and from view 1, J378's sequences). Fixes are in separate commits on the branch (tracker wheel, Camera.flyTo, pill-morph, fresh latch, focus, reduced motion); green run to follow.
Author
Owner

J370 round 3 finish. Branch fix-kbd-no-anim-427, head da75ec79a (pushed, not merged), origin/dev 098f2c445 merged in.

Fixes, one commit each (red specs first: 18c9bbe82, corrected in 13eb6f966 and c2549319b)

  • 085e24d24 tracker: wheel (mouse wheel, trackpad swipe) sets pointer, so a swipe after typing still glides now that flights read the source.
  • 64b89c7bf P2-a: Camera.flyTo jumps when triggeredByKeyboard(); keyFlight, flightKind and the palette's per-call source branches are removed, so no caller can miss it.
  • bde107b9e P2-b: pill-morph.ts jumps on open and close from a key (the capsule's view list, sharing popover, floating terminal).
  • 89c0ef051 P3: the CSS rule is keyed on data-input-fresh, set by a key and cleared two frames later (the data-look-swap pattern), so .ui-key-instant surfaces lose only open/close motion, not easing until the next pointer press.
  • ba4ad7866 P3: closing ⌘K returns focus to the element focused before it, when focus was left inside the palette.
  • d15063e0b P3: the pointer-open palette obeys reduced motion (the media rule lost on specificity to .palette:not([hidden])).
  • clusters-flight key bound is now ≤ 34 ms (two frames) instead of < 300.
  • Left as is: a letter typed mid pointer-open snaps the palette to full opacity.

Red (dev-02 production build, ~/j370-r3red3.log, spec c2549319b against the old source): 8 failed, 15 passed. Failing: Escape to overview, Ctrl+Shift+G, palette "All views"+Enter, palette window pick+Enter (frames showing the camera on its way > 0), Enter on the capsule (view list inset(0px 0px 167.322px round 20.8927px) in the first frame), capsule fade still off 150 ms after a key, focus not restored, reduced-motion palette opacity "0" in the first frame. The pointer controls pass on red.
Green (~/j370-g4.log, c2549319b): keyboard-instant (24 incl. the 8 above), clusters-flight (key ≤ 34 ms) and the rest pass: 24 passed, 1 failed. The failure is clusters.spec.ts:512 (canvas lock on a phone, expects index 1, got 0), which fails the same way on unmodified origin/dev 098f2c445 (~/j370-base3.log). An earlier wider green run (~/j370-g3.log, d15063e0b: keyboard-instant, clusters-flight, clusters-shortcuts, palette-navigation, palette-polish, clusters, clusters-keyboard-split, canvas) had 120 passed; its 3 other failures were spec flaws in my new tests (fixed in c2549319b, passing in g4). The remaining commits after it are doc comments only. That covers pill-morph through the view list and the floating terminal (clusters.spec).
Unit tests: input-source (wheel, two-frame window, modifiers, pointer) and spring: pass. Tracer typecheck passes.
verify quick at da75ec79a (~/j370-quick.log, dev-02): "QUICK pre-check passed", exit=0. verify fast not rerun.

Note: the view list's "Search everything" row opens the palette by dispatching a synthetic keydown, which the tracker counts as a key, so that one pointer click opens the palette instantly. Fixing it means ignoring untrusted keydowns (or having the row call the palette directly); I left it and can do either in a follow-up.

J370 round 3 finish. Branch `fix-kbd-no-anim-427`, head **da75ec79a** (pushed, not merged), origin/dev 098f2c445 merged in. **Fixes, one commit each** (red specs first: 18c9bbe82, corrected in 13eb6f966 and c2549319b) - 085e24d24 tracker: `wheel` (mouse wheel, trackpad swipe) sets pointer, so a swipe after typing still glides now that flights read the source. - 64b89c7bf **P2-a**: `Camera.flyTo` jumps when `triggeredByKeyboard()`; `keyFlight`, `flightKind` and the palette's per-call source branches are removed, so no caller can miss it. - bde107b9e **P2-b**: `pill-morph.ts` jumps on open and close from a key (the capsule's view list, sharing popover, floating terminal). - 89c0ef051 P3: the CSS rule is keyed on `data-input-fresh`, set by a key and cleared two frames later (the `data-look-swap` pattern), so `.ui-key-instant` surfaces lose only open/close motion, not easing until the next pointer press. - ba4ad7866 P3: closing ⌘K returns focus to the element focused before it, when focus was left inside the palette. - d15063e0b P3: the pointer-open palette obeys reduced motion (the media rule lost on specificity to `.palette:not([hidden])`). - clusters-flight key bound is now ≤ 34 ms (two frames) instead of < 300. - Left as is: a letter typed mid pointer-open snaps the palette to full opacity. **Red** (dev-02 production build, `~/j370-r3red3.log`, spec c2549319b against the old source): 8 failed, 15 passed. Failing: Escape to overview, Ctrl+Shift+G, palette "All views"+Enter, palette window pick+Enter (frames showing the camera on its way > 0), Enter on the capsule (view list `inset(0px 0px 167.322px round 20.8927px)` in the first frame), capsule fade still off 150 ms after a key, focus not restored, reduced-motion palette opacity "0" in the first frame. The pointer controls pass on red. **Green** (`~/j370-g4.log`, c2549319b): keyboard-instant (24 incl. the 8 above), clusters-flight (key ≤ 34 ms) and the rest pass: 24 passed, 1 failed. The failure is `clusters.spec.ts:512` (canvas lock on a phone, expects index 1, got 0), which fails the same way on unmodified origin/dev 098f2c445 (`~/j370-base3.log`). An earlier wider green run (`~/j370-g3.log`, d15063e0b: keyboard-instant, clusters-flight, clusters-shortcuts, palette-navigation, palette-polish, clusters, clusters-keyboard-split, canvas) had 120 passed; its 3 other failures were spec flaws in my new tests (fixed in c2549319b, passing in g4). The remaining commits after it are doc comments only. That covers pill-morph through the view list and the floating terminal (clusters.spec). **Unit tests**: input-source (wheel, two-frame window, modifiers, pointer) and spring: pass. Tracer typecheck passes. **`verify quick`** at da75ec79a (`~/j370-quick.log`, dev-02): "QUICK pre-check passed", exit=0. `verify fast` not rerun. **Note**: the view list's "Search everything" row opens the palette by dispatching a synthetic keydown, which the tracker counts as a key, so that one pointer click opens the palette instantly. Fixing it means ignoring untrusted keydowns (or having the row call the palette directly); I left it and can do either in a follow-up.
Author
Owner

J389 start: round-3 review of fix-kbd-no-anim-427 @ da75ec79a. Plan: scratch worktree on dev-02 with origin/dev (9965911c8) merged into the head. Then: grep every transition/animation in apps/tracer and packages/ui and drive the ones a shortcut can reach; check the central Camera.flyTo decision for each key path in the task list; pill-morph Enter/Escape; .ui-key-instant scope; spec bounds; ⌘K focus restore; reduced-motion pointer fade; pointer/touch still animating. Production build, 1440x900 and 393x852: keyboard-instant, clusters-flight, clusters-shortcuts, palette-navigation, pill-morph specs, plus verify quick (the task asks for it explicitly; the brief's reviewer rule says otherwise, so I follow the task and name it in the report). Not merging, pushing or deploying.

J389 start: round-3 review of `fix-kbd-no-anim-427` @ da75ec79a. Plan: scratch worktree on dev-02 with origin/dev (9965911c8) merged into the head. Then: grep every transition/animation in apps/tracer and packages/ui and drive the ones a shortcut can reach; check the central `Camera.flyTo` decision for each key path in the task list; pill-morph Enter/Escape; `.ui-key-instant` scope; spec bounds; ⌘K focus restore; reduced-motion pointer fade; pointer/touch still animating. Production build, 1440x900 and 393x852: keyboard-instant, clusters-flight, clusters-shortcuts, palette-navigation, pill-morph specs, plus `verify quick` (the task asks for it explicitly; the brief's reviewer rule says otherwise, so I follow the task and name it in the report). Not merging, pushing or deploying.
Author
Owner

J389 finding 1 (P2): Space on the focused capsule opens the palette with the full 240 ms fade. Enter and the chords are instant.

Cause: commit 89c0ef051 keys the CSS rule on data-input-fresh, which a key clears two frames (~33 ms) after it. A button activated with Space fires click on keyup, so the palette opens after the window has closed and .ui-key-instant no longer removes its transition. JS surfaces (triggeredByKeyboard(), persistent) are unaffected, so the two mechanisms now disagree for the same key.

Evidence (production build of da75ec79a + origin/dev 9965911c8, dev-02, log ~/j389-main.log, probe PROBE activation lines; key held 120 ms, per-frame sample):

  • 1440x900, #capsule Enter: first shown frame opacity 1 at +8 ms, 0 frames below 1. #capsule Space: first shown frame shown|0 at +127 ms, 15 frames below 1, running #palette:opacity, #palette:transform, #scrim:opacity.
  • 393x852, #capsule Space: 16 frames below 1; also #sidebar-toggle, #capsule, .ui-pill opacity transitions running.
  • #sidebar-toggle Space is instant at both sizes (its open does not depend on the CSS rule).

Failure scenario: a keyboard user Tabs to the capsule and presses Space (the other activation key). The palette fades in, which TASTE "Work is instant" (4) forbids, and Enter on the same button does not.

Suggested fix: make the rule not depend on a two-frame window for opens that a key causes later. Either latch data-input-fresh until the first of the next frame after a keyup/click of the same key, or let capsuleClick/scrimClick (and the other CSS-driven setters) add the attribute when triggeredByKeyboard() is true at the moment they flip state. Add a spec: focus #capsule, hold Space 120 ms, first shown frame opacity 1.

J389 finding 1 (P2): Space on the focused capsule opens the palette with the full 240 ms fade. Enter and the chords are instant. Cause: commit 89c0ef051 keys the CSS rule on `data-input-fresh`, which a key clears two frames (~33 ms) after it. A button activated with Space fires `click` on **keyup**, so the palette opens after the window has closed and `.ui-key-instant` no longer removes its transition. JS surfaces (`triggeredByKeyboard()`, persistent) are unaffected, so the two mechanisms now disagree for the same key. Evidence (production build of da75ec79a + origin/dev 9965911c8, dev-02, log `~/j389-main.log`, probe `PROBE activation` lines; key held 120 ms, per-frame sample): - 1440x900, `#capsule` Enter: first shown frame opacity 1 at +8 ms, 0 frames below 1. `#capsule` **Space**: first shown frame `shown|0` at +127 ms, **15 frames below 1**, running `#palette:opacity`, `#palette:transform`, `#scrim:opacity`. - 393x852, `#capsule` Space: 16 frames below 1; also `#sidebar-toggle`, `#capsule`, `.ui-pill` opacity transitions running. - `#sidebar-toggle` Space is instant at both sizes (its open does not depend on the CSS rule). Failure scenario: a keyboard user Tabs to the capsule and presses Space (the other activation key). The palette fades in, which TASTE "Work is instant" (4) forbids, and Enter on the same button does not. Suggested fix: make the rule not depend on a two-frame window for opens that a key causes later. Either latch `data-input-fresh` until the first of the next frame after a `keyup`/`click` of the same key, or let `capsuleClick`/`scrimClick` (and the other CSS-driven setters) add the attribute when `triggeredByKeyboard()` is true at the moment they flip state. Add a spec: focus `#capsule`, hold Space 120 ms, first shown frame opacity 1.
Author
Owner

J389 progress on da75ec79a (+ origin/dev 9965911c8 merged, scratch head 797a9eda9). Production build, dev-02, ~/j389-main.log (required specs + my probe, 70 tests) and ~/j389-r2.log (corrected probes, 8 tests).

Required specs: all pass. keyboard-instant (24 cases, both viewports), clusters-flight, clusters-shortcuts, palette-navigation, input-source unit test. The 6 failures in the run are my own probe's, not the branch's: 4 were probe flaws (an arrow case that never moved the camera, a click on the current view, a float pick inside the current view), rerun fixed in R2 (8/8 pass); 2 are finding 1 (comment above).

Verified OK (frame samples; "on the way" = frames whose camera was strictly between start and end):

  • Camera, 1440x900 and 393x852: Escape→overview, Ctrl+Shift+G, Ctrl+Shift+2/3 view jumps: 0 frames on the way, first frame at the final position. Palette "All views"+Enter and palette window pick+Enter: 0 frames on the way (the jump lands ~312–332 ms after Enter, which is the on-demand chunk load, not a spring). Locked Ctrl+Shift+Right at 1440: 0 frames on the way (phone case did not move the camera: inconclusive, not a failure). Pointer view-list click: 17 frames on the way at both sizes, so it still glides.
  • Pill-morph, both sizes: view list Enter: first frame inset(0px round 19.8px) (whole), Escape: absent in frame 1. Sharing (⌘K "share"+Enter): 0 partly clipped frames, Escape: absent in frame 1. Floating terminal (Enter on the web-api container row): 0 partly clipped frames, Escape: absent in frame 1. Pointer click on the same row: 35/36 partly clipped frames (still morphs); touch tap on the view list at 393: 32 partly clipped frames.
  • Reduced motion: pointer-open palette first frame opacity 1, no transition. Touch tap on the capsule after a key: palette fades (16 frames below 1).
  • Escape on ⌘K returns focus (spec passes; code restores only when focus was left inside the palette).
  • Spec bound: flight() counts frames strictly between start and end (0 required) and clusters-flight allows ≤ 34 ms. Tight.

Finding 2 (P3): shortcuts that still run transitions, not open/close. Sweep of 14 shortcuts (PROBE sweep, first 90 ms): Ctrl+Shift+L runs .ui-window border-color and .ui-window-lock colour/opacity transitions (180 ms); Ctrl+Shift+Left/Right (window stepping) runs the .ui-window border-color transition, a .bm-marker transform transition and a Web Animation on an SVG element. TASTE (3) says "rapid window stepping" does not animate. Neither is an open or close, and origin/dev does the same, so it is outside #427's "open/close" wording; flagging because the task asked to hunt every remaining key-triggered motion. Ctrl+Shift+D/W/Enter, ?, Escape, F6, Ctrl+Shift+P/, show none.

Finding 3 (P3): .ui-key-instant scope is right but its lifetime is a guess. It only sets transition: none on the palette, scrim, sidebar and the three pills, and only for two frames after a key, so hover/cover easing returns (spec "motion only around the key" passes). The same two-frame guess is what breaks Space (finding 1); any future opener that runs more than ~33 ms after a key will animate.

Cost so far: no verify fast run (task asked for specs + verify quick).

J389 progress on da75ec79a (+ origin/dev 9965911c8 merged, scratch head 797a9eda9). Production build, dev-02, `~/j389-main.log` (required specs + my probe, 70 tests) and `~/j389-r2.log` (corrected probes, 8 tests). **Required specs: all pass.** keyboard-instant (24 cases, both viewports), clusters-flight, clusters-shortcuts, palette-navigation, input-source unit test. The 6 failures in the run are my own probe's, not the branch's: 4 were probe flaws (an arrow case that never moved the camera, a click on the current view, a float pick inside the current view), rerun fixed in R2 (8/8 pass); 2 are finding 1 (comment above). **Verified OK (frame samples; "on the way" = frames whose camera was strictly between start and end):** - Camera, 1440x900 and 393x852: Escape→overview, Ctrl+Shift+G, Ctrl+Shift+2/3 view jumps: 0 frames on the way, first frame at the final position. Palette "All views"+Enter and palette window pick+Enter: 0 frames on the way (the jump lands ~312–332 ms after Enter, which is the on-demand chunk load, not a spring). Locked Ctrl+Shift+Right at 1440: 0 frames on the way (phone case did not move the camera: inconclusive, not a failure). Pointer view-list click: 17 frames on the way at both sizes, so it still glides. - Pill-morph, both sizes: view list Enter: first frame `inset(0px round 19.8px)` (whole), Escape: absent in frame 1. Sharing (⌘K "share"+Enter): 0 partly clipped frames, Escape: absent in frame 1. Floating terminal (Enter on the `web-api` container row): 0 partly clipped frames, Escape: absent in frame 1. Pointer click on the same row: 35/36 partly clipped frames (still morphs); touch tap on the view list at 393: 32 partly clipped frames. - Reduced motion: pointer-open palette first frame opacity 1, no transition. Touch tap on the capsule after a key: palette fades (16 frames below 1). - Escape on ⌘K returns focus (spec passes; code restores only when focus was left inside the palette). - Spec bound: `flight()` counts frames strictly between start and end (0 required) and clusters-flight allows ≤ 34 ms. Tight. **Finding 2 (P3): shortcuts that still run transitions, not open/close.** Sweep of 14 shortcuts (`PROBE sweep`, first 90 ms): Ctrl+Shift+L runs `.ui-window` border-color and `.ui-window-lock` colour/opacity transitions (180 ms); Ctrl+Shift+Left/Right (window stepping) runs the `.ui-window` border-color transition, a `.bm-marker` transform transition and a Web Animation on an SVG element. TASTE (3) says "rapid window stepping" does not animate. Neither is an open or close, and origin/dev does the same, so it is outside #427's "open/close" wording; flagging because the task asked to hunt every remaining key-triggered motion. Ctrl+Shift+D/W/Enter, `?`, Escape, F6, Ctrl+Shift+P/, show none. **Finding 3 (P3): `.ui-key-instant` scope is right but its lifetime is a guess.** It only sets `transition: none` on the palette, scrim, sidebar and the three pills, and only for two frames after a key, so hover/cover easing returns (spec "motion only around the key" passes). The same two-frame guess is what breaks Space (finding 1); any future opener that runs more than ~33 ms after a key will animate. Cost so far: no `verify fast` run (task asked for specs + `verify quick`).
Author
Owner

J370 follow-up pushed (head 2a24a9d1d): tracker ignores untrusted events (d44a9814c), Search everything calls core.showPalette() directly. Red spec c43bf28d1 (sampler corrected in ff1e51700), docs 2a24a9d1d. An earlier combined run at d44a9814c passed the 37 keyboard-instant, palette-navigation, palette-polish and clusters-flight cases and the unit test (~/j370-f1green.log); the final red/green/quick run on 2a24a9d1d is queued on dev-02 behind a long queue, results to follow.

J370 follow-up pushed (head 2a24a9d1d): tracker ignores untrusted events (d44a9814c), Search everything calls core.showPalette() directly. Red spec c43bf28d1 (sampler corrected in ff1e51700), docs 2a24a9d1d. An earlier combined run at d44a9814c passed the 37 keyboard-instant, palette-navigation, palette-polish and clusters-flight cases and the unit test (`~/j370-f1green.log`); the final red/green/quick run on 2a24a9d1d is queued on dev-02 behind a long queue, results to follow.
Author
Owner

J370 follow-up finish. Branch fix-kbd-no-anim-427, head 2a24a9d1d (pushed, not merged).

Commits on top of da75ec79a:

  • c43bf28d1 red spec: the view list's "Search everything" row opens the palette with a fade; the real chord stays instant.
  • d44a9814c fix: input-source.ts ignores events with isTrusted === false (keys and pointer events); the row calls core.showPalette() directly (the entries argument is now optional) instead of dispatching a synthetic Control/Command+K. The other synthetic dispatches (toolbar "Search and actions", the mockup tag) now read as the pointer clicks they are, with no change to them.
  • ff1e51700 spec: the frame is taken after the click, not at the press (the first red failed for that reason).
  • 2a24a9d1d doc comments for verify quick.
    (That is four commits, not one: the red spec, the fix, a spec correction and the docs row, so that the red-first order stays visible.)

Red (~/j370-f3red.log, dev-02 production build, old source c43bf28d1 with the final spec): fails with "the palette opened: expected false, received true", i.e. the row did not open the palette at all. The synthetic key it sent was Control+K, and on a non-Mac platform the palette's chord is Control+Shift+K (Control+K belongs to the shell). So on Linux and Windows the row was dead, which the direct call also fixes.
Green (~/j370-f3green.log, 2a24a9d1d): keyboard-instant, palette-navigation and palette-polish: 36 passed, exit=0; the input-source unit test (untrusted key, untrusted pointer, trusted ones, modifiers, wheel, two-frame window): 1 pass. An earlier run at d44a9814c (~/j370-f1green.log) also passed clusters-flight and the typecheck: 37 passed.
verify quick (~/j370-f3quick.log, 2a24a9d1d): "QUICK pre-check passed", exit=0. First attempt (~/j370-f1quick.log) failed only the doc-comments row; fixed in 2a24a9d1d. verify fast not run.
Note for the unit test: isTrusted is an unconfigurable property of every real event, so it drives the tracker with a stand-in window and plain objects.

J370 follow-up finish. Branch `fix-kbd-no-anim-427`, head **2a24a9d1d** (pushed, not merged). Commits on top of da75ec79a: - c43bf28d1 red spec: the view list's "Search everything" row opens the palette with a fade; the real chord stays instant. - d44a9814c fix: `input-source.ts` ignores events with `isTrusted === false` (keys and pointer events); the row calls `core.showPalette()` directly (the entries argument is now optional) instead of dispatching a synthetic Control/Command+K. The other synthetic dispatches (toolbar "Search and actions", the mockup tag) now read as the pointer clicks they are, with no change to them. - ff1e51700 spec: the frame is taken after the click, not at the press (the first red failed for that reason). - 2a24a9d1d doc comments for `verify quick`. (That is four commits, not one: the red spec, the fix, a spec correction and the docs row, so that the red-first order stays visible.) **Red** (`~/j370-f3red.log`, dev-02 production build, old source c43bf28d1 with the final spec): fails with "the palette opened: expected false, received true", i.e. the row did not open the palette at all. The synthetic key it sent was Control+K, and on a non-Mac platform the palette's chord is Control+Shift+K (Control+K belongs to the shell). So on Linux and Windows the row was dead, which the direct call also fixes. **Green** (`~/j370-f3green.log`, 2a24a9d1d): keyboard-instant, palette-navigation and palette-polish: **36 passed**, exit=0; the input-source unit test (untrusted key, untrusted pointer, trusted ones, modifiers, wheel, two-frame window): 1 pass. An earlier run at d44a9814c (`~/j370-f1green.log`) also passed clusters-flight and the typecheck: 37 passed. **`verify quick`** (`~/j370-f3quick.log`, 2a24a9d1d): "QUICK pre-check passed", exit=0. First attempt (`~/j370-f1quick.log`) failed only the doc-comments row; fixed in 2a24a9d1d. `verify fast` not run. Note for the unit test: `isTrusted` is an unconfigurable property of every real event, so it drives the tracker with a stand-in window and plain objects.
Author
Owner

J389 review of fix-kbd-no-anim-427 @ da75ec79a, plus the delta to 2a24a9d1d

Verdict: REQUEST CHANGES. One P2 (Space on the capsule), two P3. The rest of round 3 holds.

Reviewed SHAs

  • da75ec79a: reviewed in full. Scratch merge with origin/dev 9965911c8 = 797a9eda9.
  • 2a24a9d1d (J370's later 4 commits: untrusted events ignored by the tracker, "Search everything" opens the palette directly, spec, docs): delta read, its new spec run. Scratch merge with origin/dev 8e664e6b7 = c72af681c.

Findings

  • P2 (comment 87536): Space on the focused capsule opens the palette with the 240 ms fade (15 frames below opacity 1 at 1440x900, 16 at 393x852). data-input-fresh clears two frames after the key and Space clicks on keyup. Enter and the chords are instant, so the same button behaves two ways. Still present at 2a24a9d1d (same numbers in ~/j389-new.log).
  • P3: Ctrl+Shift+L (lock) and Ctrl+Shift+Left/Right (window stepping) run border-colour, lock-icon, marker-transform and a Web Animation after the key. TASTE (3) says rapid window stepping does not animate. Not open/close, same on origin/dev.
  • P3: the two-frame data-input-fresh window is a timing guess; anything that opens more than ~33 ms after a key animates.

Verified (frame samples, production build, 1440x900 and 393x852)

  • Camera: Escape→overview, Ctrl+Shift+G, "All views"+Enter, window pick+Enter, Ctrl+Shift+digit jumps: 0 frames on the way; pointer flights glide (17 frames). Mac ⌘ chords not run (Linux Chromium); same flyTo path.
  • Pill-morph: view list, sharing, floating terminal open whole on Enter and vanish in frame 1 on Escape; pointer and touch still morph.
  • .ui-key-instant removes only the open/close transition, and only around the key.
  • Spec bounds tight: 0 in-between frames, key flight ≤ 34 ms (measured 0–9 ms).
  • Escape on ⌘K returns focus; pointer palette respects reduced motion; touch tap after a key still animates.
  • 2a24a9d1d: isTrusted filter and the Search-everything direct open are right; no other source relies on a synthetic key (toolbar/row.ts:25 is now correctly a pointer open too).
  • clusters.spec.ts:512 (canvas lock on a phone): I agree it is not this branch's. It fails the same way (expected 1, received 0) on the merged tree and on plain origin/dev 8e664e6b7 (~/j389-c512-j389.log, ~/j389-c512-j389-base.log).

Gate results

  • da75ec79a+dev: keyboard-instant, clusters-flight, clusters-shortcuts, palette-navigation all pass; input-source unit test pass (~/j389-main.log, 64 passed, 6 failed, all in my own probe: 4 probe flaws, fixed and rerun 8/8 in ~/j389-r2.log, 2 are the P2). verify quick: passed, exit 0 (~/j389-quick.log).
  • 2a24a9d1d+dev: 61 passed, 4 failed, all in my probe (2 Space = the P2, 2 an arrow case that never moved the camera); every branch spec passes incl. the new Search-everything case; unit test pass; verify quick: passed, exit 0 (~/j389-new.log, ~/j389-quick-new.log).
  • verify fast not run (not requested). The brief's reviewer rule says not to run verify quick on a reviewed branch; I ran it because the task asked, on scratch merges.
  • Probe spec (untracked, never pushed): ~/j389-probes/j389-probe.spec.ts on dev-02. No screenshots: timing change, frame samples are the evidence.

Nothing merged, pushed or deployed.

## J389 review of `fix-kbd-no-anim-427` @ da75ec79a, plus the delta to 2a24a9d1d **Verdict: REQUEST CHANGES.** One P2 (Space on the capsule), two P3. The rest of round 3 holds. ### Reviewed SHAs - **da75ec79a**: reviewed in full. Scratch merge with origin/dev 9965911c8 = 797a9eda9. - **2a24a9d1d** (J370's later 4 commits: untrusted events ignored by the tracker, "Search everything" opens the palette directly, spec, docs): delta read, its new spec run. Scratch merge with origin/dev 8e664e6b7 = c72af681c. ### Findings - **P2 (comment 87536):** Space on the focused capsule opens the palette with the 240 ms fade (15 frames below opacity 1 at 1440x900, 16 at 393x852). `data-input-fresh` clears two frames after the key and Space clicks on keyup. Enter and the chords are instant, so the same button behaves two ways. Still present at 2a24a9d1d (same numbers in `~/j389-new.log`). - **P3:** Ctrl+Shift+L (lock) and Ctrl+Shift+Left/Right (window stepping) run border-colour, lock-icon, marker-transform and a Web Animation after the key. TASTE (3) says rapid window stepping does not animate. Not open/close, same on origin/dev. - **P3:** the two-frame `data-input-fresh` window is a timing guess; anything that opens more than ~33 ms after a key animates. ### Verified (frame samples, production build, 1440x900 and 393x852) - Camera: Escape→overview, Ctrl+Shift+G, "All views"+Enter, window pick+Enter, Ctrl+Shift+digit jumps: 0 frames on the way; pointer flights glide (17 frames). Mac ⌘ chords not run (Linux Chromium); same `flyTo` path. - Pill-morph: view list, sharing, floating terminal open whole on Enter and vanish in frame 1 on Escape; pointer and touch still morph. - `.ui-key-instant` removes only the open/close transition, and only around the key. - Spec bounds tight: 0 in-between frames, key flight ≤ 34 ms (measured 0–9 ms). - Escape on ⌘K returns focus; pointer palette respects reduced motion; touch tap after a key still animates. - 2a24a9d1d: `isTrusted` filter and the Search-everything direct open are right; no other source relies on a synthetic key (`toolbar/row.ts:25` is now correctly a pointer open too). - `clusters.spec.ts:512` (canvas lock on a phone): I agree it is not this branch's. It fails the same way (expected 1, received 0) on the merged tree and on plain origin/dev 8e664e6b7 (`~/j389-c512-j389.log`, `~/j389-c512-j389-base.log`). ### Gate results - da75ec79a+dev: keyboard-instant, clusters-flight, clusters-shortcuts, palette-navigation all pass; input-source unit test pass (`~/j389-main.log`, 64 passed, 6 failed, all in my own probe: 4 probe flaws, fixed and rerun 8/8 in `~/j389-r2.log`, 2 are the P2). `verify quick`: passed, exit 0 (`~/j389-quick.log`). - 2a24a9d1d+dev: 61 passed, 4 failed, all in my probe (2 Space = the P2, 2 an arrow case that never moved the camera); every branch spec passes incl. the new Search-everything case; unit test pass; `verify quick`: passed, exit 0 (`~/j389-new.log`, `~/j389-quick-new.log`). - `verify fast` not run (not requested). The brief's reviewer rule says not to run `verify quick` on a reviewed branch; I ran it because the task asked, on scratch merges. - Probe spec (untracked, never pushed): `~/j389-probes/j389-probe.spec.ts` on dev-02. No screenshots: timing change, frame samples are the evidence. Nothing merged, pushed or deployed.
Author
Owner

J370 round 4 start. Merged origin/dev 0a5432d2c into fix-kbd-no-anim-427; red specs pushed (6a1b1b893), run queued on dev-02. Plan: drop data-input-fresh; every open/close setter (palette, scrim, covered pills, sidebar, glass bed) calls markMotion(el), which writes data-motion="instant"|"animate" from triggeredByKeyboard() at that moment, and CSS keys off the element's own attribute; Space/Enter keydown count as keyboard until the next trusted pointer event (already so); window cards (setActive/setLocked) and the tree's select do the same so stepping and the lock don't run border, lock-icon, marker or draw-on motion. Estimate 2 h plus queue.

J370 round 4 start. Merged origin/dev 0a5432d2c into fix-kbd-no-anim-427; red specs pushed (6a1b1b893), run queued on dev-02. Plan: drop `data-input-fresh`; every open/close setter (palette, scrim, covered pills, sidebar, glass bed) calls `markMotion(el)`, which writes `data-motion="instant"|"animate"` from `triggeredByKeyboard()` at that moment, and CSS keys off the element's own attribute; Space/Enter keydown count as keyboard until the next trusted pointer event (already so); window cards (`setActive`/`setLocked`) and the tree's `select` do the same so stepping and the lock don't run border, lock-icon, marker or draw-on motion. Estimate 2 h plus queue.
Author
Owner

J370 round 4 progress (red). dev-02's build-slot is stuck: slots 2 and 3 show free while its queue head, pid 471771 (job j287), runs a deleted older copy of the script and has waited 2 h, so my jobs ran on compute01 instead. Red at 6a1b1b893 (~/j370-r4red.log on compute01): the late-open case fails at both viewports (a script click 100 ms after a key opens the palette with the fade) and Ctrl+Shift+Right, Ctrl+Shift+Left and Ctrl+Shift+L each leave running transitions or animations on window cards, lock icons or the tree one frame later. The Space and Enter cases passed on red because press() releases the key inside the old two-frame window, so 470f78266 holds the key 100 ms (as a person does); the pointer control also needed a visible window. Fixes pushed: c56f5bb0e (data-motion, markMotion, fresh window removed), 97f17d658 (window card, lock, tree). Green/quick on 470f78266 queued.

J370 round 4 progress (red). dev-02's build-slot is stuck: slots 2 and 3 show free while its queue head, pid 471771 (job j287), runs a deleted older copy of the script and has waited 2 h, so my jobs ran on compute01 instead. Red at 6a1b1b893 (`~/j370-r4red.log` on compute01): the late-open case fails at both viewports (a script click 100 ms after a key opens the palette with the fade) and Ctrl+Shift+Right, Ctrl+Shift+Left and Ctrl+Shift+L each leave running transitions or animations on window cards, lock icons or the tree one frame later. The Space and Enter cases passed on red because press() releases the key inside the old two-frame window, so 470f78266 holds the key 100 ms (as a person does); the pointer control also needed a visible window. Fixes pushed: c56f5bb0e (data-motion, markMotion, fresh window removed), 97f17d658 (window card, lock, tree). Green/quick on 470f78266 queued.
Author
Owner

J370 round 4 finish. Branch fix-kbd-no-anim-427, head 4b2fea905 (pushed, not merged), origin/dev 0a5432d2c merged in.

Redesign (one mechanism, no time window)

  • c56f5bb0e: data-input-fresh and its two-frame timer are gone. markMotion(...elements) (input-source.ts) is called by each setter right before it flips: it writes data-motion="instant" when triggeredByKeyboard() and "animate" otherwise, onto the element, until that element's next change. The palette, scrim, covered pills, sidebar and its toggle (also its pane glide) and the glass bed use it; the CSS keys off the element's own attribute. The tracker already counts a trusted Space/Enter keydown as keyboard until the next trusted pointer or touch event, so Space's click on keyup finds it (unit test added).
  • 97f17d658 + f00433577: window cards (setActive when the state changes, setLocked) mark themselves, so the border and lock-icon fades drop for a key; the pointer-driven lock reveal writes animate; branched.select() lays out the tree marker and branch with no animation for a key; pulse() (the 360 ms swell) returns for a key. Pointer equivalents keep theirs.
  • 7d… (the commit after the first green run, "the pill's own fade follows the same answer"): createPillMorph and the shortcuts view call markMotion(pill) at open and close, and the shared rule is html:root … so it beats the pills' own transition rules (the plus pill still faded at phone width).

Red first (~/j370-r4d-red.log, compute01, production build; final spec on the old source 6a1b1b893): 7 failed, 27 passed. Space on the capsule (both viewports), a surface opening 100 ms after a key (both), Ctrl+Shift+Right, Ctrl+Shift+L, and "a pointer change after a key animates the same elements again" fail; the pointer controls pass. (Ctrl+Shift+Left passed on red: from the test's starting window it moves nothing, so it only guards against regression.) Space and Enter first passed on red because press() releases the key inside the old window; the spec now holds the key 100 ms.
Green at 4b2fea905 (~/j370-r4f-green.log: keyboard-instant, palette-navigation): 35 passed; input-source unit test: 1 pass; tracer typecheck passes. At f00433577 (~/j370-r4e-green.log, which also ran clusters-flight, clusters-shortcuts and palette-polish): 51 passed; its 2 failures were the terminal cursor's blink counted as a response (fixed in 4b2fea905) and palette-navigation filter-handler budget 0.187 ms vs 0.13 on a busy host (it passed in the earlier run and in the 4b2fea905 run). New cases: Space and Enter on the capsule, a late opener 100 ms after a key, the three window/lock chords, a pointer click that still fades a border, and the pointer-after-key animate case. Pointer-fade checks now sample inside the page at the first frame after the event, so a slow host cannot make a fade look finished.
verify quick at 4b2fea905 (~/j370-r4f-quick.log): "QUICK pre-check passed", exit=0. verify fast not run.

Infra note for the orchestrator: dev-02's build-slot was stuck for hours: slots 2 and 3 showed free while its queue head (pid 471771, job j287) ran a deleted older copy of the script and never took them, so my runs went to compute01. ~/infraternal-ops/slots says 3.
Not covered: the "nothing animates more than 33 ms after a key" guarantee holds for every surface wired to markMotion and for camera flights, pill morphs, the shortcuts panel, window stepping and the lock; a new opener has to call markMotion (or triggeredByKeyboard()) when it flips, which is the single rule in input-source.ts.

J370 round 4 finish. Branch `fix-kbd-no-anim-427`, head **4b2fea905** (pushed, not merged), origin/dev 0a5432d2c merged in. **Redesign (one mechanism, no time window)** - c56f5bb0e: `data-input-fresh` and its two-frame timer are gone. `markMotion(...elements)` (input-source.ts) is called by each setter right before it flips: it writes `data-motion="instant"` when `triggeredByKeyboard()` and `"animate"` otherwise, onto the element, until that element's next change. The palette, scrim, covered pills, sidebar and its toggle (also its pane glide) and the glass bed use it; the CSS keys off the element's own attribute. The tracker already counts a trusted Space/Enter keydown as keyboard until the next trusted pointer or touch event, so Space's click on keyup finds it (unit test added). - 97f17d658 + f00433577: window cards (`setActive` when the state changes, `setLocked`) mark themselves, so the border and lock-icon fades drop for a key; the pointer-driven lock reveal writes `animate`; `branched.select()` lays out the tree marker and branch with no animation for a key; `pulse()` (the 360 ms swell) returns for a key. Pointer equivalents keep theirs. - 7d… (the commit after the first green run, "the pill's own fade follows the same answer"): `createPillMorph` and the shortcuts view call `markMotion(pill)` at open and close, and the shared rule is `html:root …` so it beats the pills' own transition rules (the plus pill still faded at phone width). **Red first** (`~/j370-r4d-red.log`, compute01, production build; final spec on the old source 6a1b1b893): 7 failed, 27 passed. Space on the capsule (both viewports), a surface opening 100 ms after a key (both), Ctrl+Shift+Right, Ctrl+Shift+L, and "a pointer change after a key animates the same elements again" fail; the pointer controls pass. (Ctrl+Shift+Left passed on red: from the test's starting window it moves nothing, so it only guards against regression.) Space and Enter first passed on red because `press()` releases the key inside the old window; the spec now holds the key 100 ms. **Green** at 4b2fea905 (`~/j370-r4f-green.log`: keyboard-instant, palette-navigation): **35 passed**; input-source unit test: 1 pass; tracer typecheck passes. At f00433577 (`~/j370-r4e-green.log`, which also ran clusters-flight, clusters-shortcuts and palette-polish): 51 passed; its 2 failures were the terminal cursor's blink counted as a response (fixed in 4b2fea905) and `palette-navigation` filter-handler budget 0.187 ms vs 0.13 on a busy host (it passed in the earlier run and in the 4b2fea905 run). New cases: Space and Enter on the capsule, a late opener 100 ms after a key, the three window/lock chords, a pointer click that still fades a border, and the pointer-after-key animate case. Pointer-fade checks now sample inside the page at the first frame after the event, so a slow host cannot make a fade look finished. **`verify quick`** at 4b2fea905 (`~/j370-r4f-quick.log`): "QUICK pre-check passed", exit=0. `verify fast` not run. **Infra note for the orchestrator**: dev-02's `build-slot` was stuck for hours: slots 2 and 3 showed free while its queue head (pid 471771, job j287) ran a deleted older copy of the script and never took them, so my runs went to compute01. `~/infraternal-ops/slots` says 3. Not covered: the "nothing animates more than 33 ms after a key" guarantee holds for every surface wired to `markMotion` and for camera flights, pill morphs, the shortcuts panel, window stepping and the lock; a new opener has to call `markMotion` (or `triggeredByKeyboard()`) when it flips, which is the single rule in input-source.ts.
Author
Owner

J370 correction to my round 4 finish comment: the commit I wrote as '7d…' (the pill's own fade follows the same answer when a morph opens or closes) is 831ca81a8.

J370 correction to my round 4 finish comment: the commit I wrote as '7d…' (the pill's own fade follows the same answer when a morph opens or closes) is **831ca81a8**.
Author
Owner

J403 start: round-4 review of fix-kbd-no-anim-427 @ 4b2fea905 (J370's markMotion redesign). Plan: scratch worktree on dev-02 (~/worktrees/j403) with origin/dev 44e80ccc5 merged in; source audit of every transition/animation/WAAPI use reachable from a shortcut (checking each opener/closer calls markMotion or triggeredByKeyboard()); stale-state, iPad hardware-keyboard + touch, reduced-motion, focus-return and html:root specificity checks; then a production build at 1440x900 and 393x852 running keyboard-instant, palette-navigation, clusters-flight, clusters-shortcuts, palette-polish, the pill-morph coverage, the input-source unit test, and verify quick (the task asks for it explicitly; the brief's reviewer rule says otherwise, so I follow the task and name it in the report). Frame-sample evidence. Also a recommendation on a guard against a forgotten markMotion. Not merging, pushing or deploying.

J403 start: round-4 review of `fix-kbd-no-anim-427` @ 4b2fea905 (J370's `markMotion` redesign). Plan: scratch worktree on dev-02 (`~/worktrees/j403`) with origin/dev 44e80ccc5 merged in; source audit of every transition/animation/WAAPI use reachable from a shortcut (checking each opener/closer calls `markMotion` or `triggeredByKeyboard()`); stale-state, iPad hardware-keyboard + touch, reduced-motion, focus-return and `html:root` specificity checks; then a production build at 1440x900 and 393x852 running keyboard-instant, palette-navigation, clusters-flight, clusters-shortcuts, palette-polish, the pill-morph coverage, the input-source unit test, and `verify quick` (the task asks for it explicitly; the brief's reviewer rule says otherwise, so I follow the task and name it in the report). Frame-sample evidence. Also a recommendation on a guard against a forgotten `markMotion`. Not merging, pushing or deploying.
Author
Owner

J403 progress (source audit done on a local scratch merge of 4b2fea905 + origin/dev 44e80ccc5 = a24996c08; runtime proofs queued on dev-02, which has 3 busy slots and 4 queued). Candidate findings from reading, each to be proven with frame samples before I post it as a finding:

  1. (P2 candidate) data-motion="instant" persists until the element's next change and CSS sets transition: none for it, so a keyboard palette or sidebar open leaves #capsule, #sidebar-toggle and the "+" with no transition at all, and .ui-pill's own 140 ms press-scale (:active) snaps, until the next palette or sidebar change. Same for a window card after Ctrl+Shift+arrows (border colour) and its lock.
  2. (P2 candidate) hover is not an input the tracker sees, so a mouse user who typed and then only hovers is "keyboard": the tooltip glass bed (bed.setOpen, tooltip.ts:370) opens instant while its card animates.
  3. (P2 candidate) palette Enter on "Split right/down", "New window right/below" calls cx.win.split(active, dir) / newWindow without instant (palette-actions.on-demand.ts:34-35), while the chords pass true; the FLIP and enter spring may play.
  4. (P3) the doc says a change no key caused reads as pointer; once any key has been pressed it reads keyboard until a click or wheel, so timer or network changes right after typing are instant.
    Not yet checked at runtime.
J403 progress (source audit done on a local scratch merge of 4b2fea905 + origin/dev 44e80ccc5 = a24996c08; runtime proofs queued on dev-02, which has 3 busy slots and 4 queued). Candidate findings from reading, each to be proven with frame samples before I post it as a finding: 1. (P2 candidate) `data-motion="instant"` persists until the element's next change and CSS sets `transition: none` for it, so a keyboard palette or sidebar open leaves `#capsule`, `#sidebar-toggle` and the "+" with no transition at all, and `.ui-pill`'s own 140 ms press-scale (`:active`) snaps, until the next palette or sidebar change. Same for a window card after Ctrl+Shift+arrows (border colour) and its lock. 2. (P2 candidate) hover is not an input the tracker sees, so a mouse user who typed and then only hovers is "keyboard": the tooltip glass bed (`bed.setOpen`, tooltip.ts:370) opens instant while its card animates. 3. (P2 candidate) palette Enter on "Split right/down", "New window right/below" calls `cx.win.split(active, dir)` / `newWindow` without `instant` (palette-actions.on-demand.ts:34-35), while the chords pass `true`; the FLIP and enter spring may play. 4. (P3) the doc says a change no key caused reads as pointer; once any key has been pressed it reads keyboard until a click or wheel, so timer or network changes right after typing are instant. Not yet checked at runtime.
Author
Owner

J403 status (01:50 IST): still waiting for a dev-02 slot (queue position 6 of 3 busy slots; my first slot was spent on a run that failed at once: the fresh worktree had no apps/tracer/node_modules, then a shell-quoting slip in my own -g pattern. Both are my harness's, not the branch's; fixed and requeued). Candidate 2 from my earlier comment is withdrawn: bed.setOpen is only called by the confirmation tip (tooltip.ts:370), opened by a click or key, never by hover. New thing I will measure: that same key-opened confirmation, whose glass bed is now instant while the card's own data-open fade is not wired to markMotion. No verdicts yet.

J403 status (01:50 IST): still waiting for a dev-02 slot (queue position 6 of 3 busy slots; my first slot was spent on a run that failed at once: the fresh worktree had no `apps/tracer/node_modules`, then a shell-quoting slip in my own `-g` pattern. Both are my harness's, not the branch's; fixed and requeued). Candidate 2 from my earlier comment is withdrawn: `bed.setOpen` is only called by the confirmation tip (tooltip.ts:370), opened by a click or key, never by hover. New thing I will measure: that same key-opened confirmation, whose glass bed is now instant while the card's own `data-open` fade is not wired to `markMotion`. No verdicts yet.
Author
Owner

J403 finding 1 (P2): a key-opened confirmation (createConfirmTip, tooltip.ts) still fades in. It is a key-reachable opener that does not call markMotion or triggeredByKeyboard().

Evidence (production build of 4b2fea905 + origin/dev 44e80ccc5, dev-02, j403-B.log lines PROBE confirm): palette ⌘K → "sign out" → Enter (key only). The confirmation's opacity per frame is 0, 0, 0.178, 0.339, 0.488, 0.623, 0.742, 0.844, 0.925, 0.979, 1: 8 fractional frames (about 130 ms), with #ui-confirm-tip-1:opacity and :transform running. Same numbers at 1440x900 and 393x852. The pointer click on the same row gives the same series, so the key path is not instant. The bed (glass backing) opens at once in both paths (0, 1, 1…); it is attached and flipped in one frame, so it never had a transition. Consequence: the branch's markMotion(bed) in glass-bed.ts changes nothing visible here, and the real surface (the tip, [data-open] CSS) is not wired.

Failure scenario: a keyboard-only user runs ⌘K → "Sign out this device" (or "Remove host", or Enter on any button that asks) and the confirmation grows in with a 130 ms fade. TASTE: keyboard opens are instant. Closing by Escape/Enter goes through close() (dataset.open="false" plus a 190 ms removal timer) and is the same class.

Suggested fix: createConfirmTip calls markMotion(tip, bed) where it flips dataset.open (open and close()), with an html:root .ui-confirm-tip[data-motion="instant"] { transition: none } rule (or add the class ui-key-instant). Add a case to keyboard-instant.spec.ts: palette → sign out → Enter, first frame opacity 1.

J403 finding 1 (P2): a key-opened confirmation (`createConfirmTip`, tooltip.ts) still fades in. It is a key-reachable opener that does not call `markMotion` or `triggeredByKeyboard()`. Evidence (production build of 4b2fea905 + origin/dev 44e80ccc5, dev-02, `j403-B.log` lines `PROBE confirm`): palette ⌘K → "sign out" → **Enter** (key only). The confirmation's opacity per frame is `0, 0, 0.178, 0.339, 0.488, 0.623, 0.742, 0.844, 0.925, 0.979, 1`: 8 fractional frames (about 130 ms), with `#ui-confirm-tip-1:opacity` and `:transform` running. Same numbers at 1440x900 and 393x852. The pointer click on the same row gives the same series, so the key path is not instant. The `bed` (glass backing) opens at once in both paths (`0, 1, 1…`); it is attached and flipped in one frame, so it never had a transition. Consequence: the branch's `markMotion(bed)` in `glass-bed.ts` changes nothing visible here, and the real surface (the tip, `[data-open]` CSS) is not wired. Failure scenario: a keyboard-only user runs ⌘K → "Sign out this device" (or "Remove host", or Enter on any button that asks) and the confirmation grows in with a 130 ms fade. TASTE: keyboard opens are instant. Closing by Escape/Enter goes through `close()` (`dataset.open="false"` plus a 190 ms removal timer) and is the same class. Suggested fix: `createConfirmTip` calls `markMotion(tip, bed)` where it flips `dataset.open` (open and `close()`), with an `html:root .ui-confirm-tip[data-motion="instant"] { transition: none }` rule (or add the class `ui-key-instant`). Add a case to keyboard-instant.spec.ts: palette → sign out → Enter, first frame opacity 1.
Author
Owner

J403 finding 2 (P2): palette Enter on "New window right/below" (and the split rows) animates; the chord does not. The windows module takes a per-caller instant flag, and the palette caller never passes it.

Code: palette-actions.on-demand.ts:34-35 calls cx.win.split(active, dir) and cx.win.newWindow(active, side) with instant unset, while shortcuts.ts passes true. split.on-demand.ts:114 plays the FLIP slide if (!instant). The palette's source is "keyboard" there (runAction(…, "keyboard")), but nothing reads it.

Evidence (clusters mockup, j403-B.log lines PROBE window-action): Ctrl+Shift+Enter vs palette "New window right" + Enter, frames after the key. Palette path: .cl-col:waapi (the FLIP) running for the sampled window (29 frames at 1440x900, 31 at 393x852 with running animations); chord path: no .cl-col animation (only the ambient connection pulse that is also present in every run). Window count 35→36 on both paths.
Not shown by the sampler: palette "Split right/down" (the chosen row's text came back empty in my probe, window count 35→36, no FLIP seen); I did not prove it, but it shares the code path.

Failure scenario: ⌘K, type "new window", Enter: the other columns slide aside for about a quarter second, while the same action from its chord is instant.

Suggested fix: decide inside the primitive, as Camera.flyTo does: split.on-demand.ts defaults instant = triggeredByKeyboard() (and land()'s focusWindow(…, instant) follows). One line per entry point and no caller can miss it again. Add a case: palette → "New window right" → Enter, no .cl-col animation.

J403 finding 2 (P2): palette Enter on "New window right/below" (and the split rows) animates; the chord does not. The windows module takes a per-caller `instant` flag, and the palette caller never passes it. Code: `palette-actions.on-demand.ts:34-35` calls `cx.win.split(active, dir)` and `cx.win.newWindow(active, side)` with `instant` unset, while `shortcuts.ts` passes `true`. `split.on-demand.ts:114` plays the FLIP slide `if (!instant)`. The palette's source is "keyboard" there (`runAction(…, "keyboard")`), but nothing reads it. Evidence (clusters mockup, `j403-B.log` lines `PROBE window-action`): Ctrl+Shift+Enter vs palette "New window right" + Enter, frames after the key. Palette path: `.cl-col:waapi` (the FLIP) running for the sampled window (29 frames at 1440x900, 31 at 393x852 with running animations); chord path: no `.cl-col` animation (only the ambient connection pulse that is also present in every run). Window count 35→36 on both paths. Not shown by the sampler: palette "Split right/down" (the chosen row's text came back empty in my probe, window count 35→36, no FLIP seen); I did not prove it, but it shares the code path. Failure scenario: ⌘K, type "new window", Enter: the other columns slide aside for about a quarter second, while the same action from its chord is instant. Suggested fix: decide inside the primitive, as `Camera.flyTo` does: `split.on-demand.ts` defaults `instant = triggeredByKeyboard()` (and `land()`'s `focusWindow(…, instant)` follows). One line per entry point and no caller can miss it again. Add a case: palette → "New window right" → Enter, no `.cl-col` animation.
Author
Owner

J403 findings 3 to 6 (P3). All four are about how the mechanism ages, none is a user-visible defect today.

3. data-motion="instant" is a persistent transition: none on shared elements. After one keyboard palette open/close, #capsule, #sidebar-toggle, the "+" and #palette carry data-motion=instant and compute transition-property: none (fresh: opacity 0.12s; the toggle transform 0.26s, opacity 0.12s). After Ctrl+Shift+arrows the stepped-from and stepped-to cards compute transition: none (fresh: border-color 0.18s). A pointer open/close resets all of them to animate. Measured in j403-A.log/j403-B.log (PROBE stale, PROBE cards). No visible effect today: those pills' only transitions are the open/close fades and the card's only is the active border, and I checked the press scale (matrix(0.97…) the same value either way, because .top > .capsule already replaces .ui-pill's transform transition). But the next hover or press transition added to a .ui-key-instant element, or to .ui-window, silently dies after the first key press until that element's next change. Suggest narrowing the rule to the open/close properties, or documenting it next to the rule and in TASTE.

4. The docs promise more than the mechanism does. input-source.ts says a change "no key caused (a timer, a network event)" reads as pointer. It reads as pointer only until the first key: a person typing in a terminal then gets every timer or network-driven camera flight, window enter, glass bed or sidebar change as instant. This errs towards instant (safe), but fix the wording; reading only, not measured.

5. Activations that carry no pointerdown keep the last source. VoiceOver and Voice Control activations (and any script .click()) send click without a trusted pointerdown, so after typing they read keyboard. Same direction as 4, from reading only. A touch tap, trackpad click and wheel all reset it (measured on a 1024x1366 touch context below).

6. Two mechanisms still exist. markMotion for CSS surfaces, but source: "keyboard"|"pointer" parameters on the anchored popover, settings overlay, details and the windows' instant flag are fed by each caller (e.detail === 0, hard-coded "pointer" in palette-actions.on-demand.ts for "Sessions on this host", …). Finding 2 is exactly one of those callers being wrong. See my recommendation for a guard in the final report.

J403 findings 3 to 6 (P3). All four are about how the mechanism ages, none is a user-visible defect today. **3. `data-motion="instant"` is a persistent `transition: none` on shared elements.** After one keyboard palette open/close, `#capsule`, `#sidebar-toggle`, the "+" and `#palette` carry `data-motion=instant` and compute `transition-property: none` (fresh: `opacity 0.12s`; the toggle `transform 0.26s, opacity 0.12s`). After Ctrl+Shift+arrows the stepped-from and stepped-to cards compute `transition: none` (fresh: `border-color 0.18s`). A pointer open/close resets all of them to `animate`. Measured in `j403-A.log`/`j403-B.log` (`PROBE stale`, `PROBE cards`). No visible effect today: those pills' only transitions are the open/close fades and the card's only is the active border, and I checked the press scale (`matrix(0.97…)` the same value either way, because `.top > .capsule` already replaces `.ui-pill`'s transform transition). But the next hover or press transition added to a `.ui-key-instant` element, or to `.ui-window`, silently dies after the first key press until that element's next change. Suggest narrowing the rule to the open/close properties, or documenting it next to the rule and in TASTE. **4. The docs promise more than the mechanism does.** input-source.ts says a change "no key caused (a timer, a network event)" reads as pointer. It reads as pointer only until the first key: a person typing in a terminal then gets every timer or network-driven camera flight, window enter, glass bed or sidebar change as instant. This errs towards instant (safe), but fix the wording; reading only, not measured. **5. Activations that carry no pointerdown keep the last source.** VoiceOver and Voice Control activations (and any script `.click()`) send `click` without a trusted pointerdown, so after typing they read keyboard. Same direction as 4, from reading only. A touch tap, trackpad click and wheel all reset it (measured on a 1024x1366 touch context below). **6. Two mechanisms still exist.** `markMotion` for CSS surfaces, but `source: "keyboard"|"pointer"` parameters on the anchored popover, settings overlay, details and the windows' `instant` flag are fed by each caller (`e.detail === 0`, hard-coded "pointer" in `palette-actions.on-demand.ts` for "Sessions on this host", …). Finding 2 is exactly one of those callers being wrong. See my recommendation for a guard in the final report.
Author
Owner

J403 finding 7 (P2): palette Enter on "Sessions on this host" opens the session manager popover with its fade. Same class as finding 2: the handler hard-codes the source (palette-actions.on-demand.ts: cx.sessionUi?.toggleManager(active, anchor, "pointer")), although the row was picked with Enter.

Evidence (j403-C.log, PROBE sessions; production build, clusters mockup): popover opacity per frame after Enter, 0, 0, 0.339, 0.602, 0.775, 0.877, 0.934, 0.966, 0.984, 0.993 at 1440x900 and at 393x852. A key-open should be whole in the first frame. (details.toggle(rec, "keyboard"), a few lines down in the same dispatcher, passes the source right.)

Suggested fix: pass detail?.source === "keyboard" ? "keyboard" : "pointer" here, and better, make openAnchoredPopover take its source from triggeredByKeyboard() so the third argument disappears.

J403 finding 7 (P2): palette Enter on "Sessions on this host" opens the session manager popover with its fade. Same class as finding 2: the handler hard-codes the source (`palette-actions.on-demand.ts`: `cx.sessionUi?.toggleManager(active, anchor, "pointer")`), although the row was picked with Enter. Evidence (`j403-C.log`, `PROBE sessions`; production build, clusters mockup): popover opacity per frame after Enter, `0, 0, 0.339, 0.602, 0.775, 0.877, 0.934, 0.966, 0.984, 0.993` at 1440x900 and at 393x852. A key-open should be whole in the first frame. (`details.toggle(rec, "keyboard")`, a few lines down in the same dispatcher, passes the source right.) Suggested fix: pass `detail?.source === "keyboard" ? "keyboard" : "pointer"` here, and better, make `openAnchoredPopover` take its source from `triggeredByKeyboard()` so the third argument disappears.
Author
Owner

J403 review of fix-kbd-no-anim-427 @ 4b2fea905 (scratch merge with origin/dev 44e80ccc5 = e0a7ac2b7)

Verdict: REQUEST CHANGES. Two P2 findings of the same kind, which the new design does not prevent: key-reachable openers that never call markMotion or triggeredByKeyboard(). The mechanism itself is sound; the work left is to make its coverage hold by construction.

Findings

  • P2 (finding 1) key-opened confirmation (createConfirmTip) fades 8 frames (about 130 ms): palette → "Sign out this device" → Enter. The glass bed is instant in both paths, so markMotion(bed) buys nothing there.
  • P2 (finding 2) palette Enter on "New window right/below" (and the split rows) runs the .cl-col FLIP slide; the chord is instant. palette-actions.on-demand.ts:34-35 omits the instant argument.
  • P2 (finding 7) palette Enter on "Sessions on this host" fades the popover in (0.34 → 0.99 over about 8 frames): hard-coded "pointer".
  • P3 (finding 3) data-motion=instant is a persistent transition: none on pills, cards and lock; no visible effect today, but the next hover or press transition on those elements dies silently after the first key.
  • P3 (findings 4, 5) doc promises "timer / network changes read as pointer"; true only before the first key. Click-without-pointerdown activations (VoiceOver, Voice Control, script .click()) keep the last source. Both err towards instant. Reading only.
  • P3 (finding 6) the old per-call source / instant parameters survive beside markMotion; findings 2 and 7 are two callers that got them wrong.

Verified OK (frame samples, probes-frame-samples.txt, j403-A.log, j403-B.log, production build)

  • Stale state: key open/close leaves data-motion=instant on #palette, #capsule, #sidebar-toggle, "+" and the stepped cards; one pointer open/close writes animate on all of them (palette, pills) and the next pointer change animates (iPad run below shows the tap open at 14 fractional frames, scrim tap close at 7). No stale-instant leak on a pointer change.
  • iPad, 1024x1366 touch + hardware keyboard: key open: first frame opacity 1, data-motion=instant; touch tap on the capsule: palette fades (14 fractional frames), data-motion=animate, data-input=pointer; key close of the tap-opened palette: instant; scrim tap: 7 fractional frames. Touch resets the source (pointerdown), keys set it back.
  • Reduced motion (1440x900): key open and pointer open both have opacity 1 in the first frame; no transition.
  • Focus return: palette from a focused terminal + Escape → focus back on the terminal; pointer open via the capsule + Escape → capsule; no focus lost. (After "All views" + Enter my sequence left focus on the capsule, which is where it was before the open.)
  • html:root bump: the rule only matches elements that carry both the class and data-motion="instant", so unrelated rules are untouched. It does win over .ui-pill.sidebar-toggle and .top > .capsule transitions on those elements, which is its purpose; verified by the phone-width plus pill fade spec passing at 393x852. The only side effect is finding 3's persistence.
  • Camera, pill-morph, shortcuts panel, lock, window stepping: the required specs pass (below); every keyboard camera flight goes through Camera.flyTo.

Audit of animation sites (grep of .animate(, new Spring(, CSS transitions in apps/tracer and packages/ui)

Wired to the input source: palette, scrim, covered pills, sidebar and toggle, glass bed, pill-morph, shortcuts panel, window card active/lock, tree marker, pulse, camera. Reachable from a key and not wired (own per-caller flag or nothing): confirm tip (finding 1), split/newWindow from the palette (2), session manager popover from the palette (7); anchored popovers, settings overlay and details take a caller-passed source, and settings via palette Enter was instant in my sample. Not reachable from a shortcut (pointer-only by nature): drag/resize/move springs, toasts, add-host wizard prototypes, chip input, count-up, status line.

Is there a cheap guard against a forgotten markMotion?

Yes, two, and I recommend the first as the required one:

  1. A registry-driven sweep spec (about 60 lines). registerShortcuts already lists every chord. A Playwright test iterates shortcutSections(["canvas","global"]), presses each combo on a fresh canvas, and fails if any non-allowlisted CSS transition, Web Animation or inline-spring style is running two frames later. A new shortcut is then covered without anyone remembering. Add the same loop over the palette rows (type the label, Enter), which would have caught findings 1, 2 and 7 on the day they were written.
  2. Move the decision into the primitive, as Camera.flyTo did. Give openAnchoredPopover, split/newWindow, createConfirmTip and the settings overlay a default of triggeredByKeyboard() instead of a caller-passed flag. Then removing the parameter makes the mistake unrepresentable. A scripts/doc-coverage-style baseline of .animate(/new Spring( sites would also work but only forces a classification, not a behaviour.

Gate results (all on the scratch merge e0a7ac2b7, dev-02 production build)

  • keyboard-instant, palette-navigation, clusters-flight, clusters-shortcuts, palette-polish: 53 passed at 1440x900 and 53 passed at 393x852 (j403-A.log).
  • Pill-morph coverage (clusters.spec.ts: capsule view list, container shell/floating terminal, top pill): 4 passed at 1440x900, 4 passed at 393x852.
  • input-source unit test: pass (1 test, 25 expects).
  • verify quick: QUICK pre-check passed, exit=0 (j403-quick2.log). An earlier run failed two rows (doc comments, fixed waits) only because my own untracked probe files were in the tree; moved out, it passes.
  • verify fast not run (not requested; the brief reserves it for the author and the merge). Nothing merged, pushed or deployed. The brief's reviewer rule says not to run verify quick on a reviewed branch; I ran it because the task asked, on a scratch merge.
  • My probe specs (never pushed): j403-probe.spec.ts, j403-c.spec.ts. No screenshots: this is a timing change, so frame samples are the evidence.
## J403 review of `fix-kbd-no-anim-427` @ 4b2fea905 (scratch merge with origin/dev 44e80ccc5 = e0a7ac2b7) **Verdict: REQUEST CHANGES.** Two P2 findings of the same kind, which the new design does not prevent: key-reachable openers that never call `markMotion` or `triggeredByKeyboard()`. The mechanism itself is sound; the work left is to make its coverage hold by construction. ### Findings - **P2 (finding 1)** key-opened confirmation (`createConfirmTip`) fades 8 frames (about 130 ms): palette → "Sign out this device" → Enter. The glass bed is instant in both paths, so `markMotion(bed)` buys nothing there. - **P2 (finding 2)** palette Enter on "New window right/below" (and the split rows) runs the `.cl-col` FLIP slide; the chord is instant. `palette-actions.on-demand.ts:34-35` omits the `instant` argument. - **P2 (finding 7)** palette Enter on "Sessions on this host" fades the popover in (0.34 → 0.99 over about 8 frames): hard-coded `"pointer"`. - **P3 (finding 3)** `data-motion=instant` is a persistent `transition: none` on pills, cards and lock; no visible effect today, but the next hover or press transition on those elements dies silently after the first key. - **P3 (findings 4, 5)** doc promises "timer / network changes read as pointer"; true only before the first key. Click-without-pointerdown activations (VoiceOver, Voice Control, script `.click()`) keep the last source. Both err towards instant. Reading only. - **P3 (finding 6)** the old per-call `source` / `instant` parameters survive beside `markMotion`; findings 2 and 7 are two callers that got them wrong. ### Verified OK (frame samples, `probes-frame-samples.txt`, `j403-A.log`, `j403-B.log`, production build) - **Stale state:** key open/close leaves `data-motion=instant` on `#palette`, `#capsule`, `#sidebar-toggle`, "+" and the stepped cards; one pointer open/close writes `animate` on all of them (palette, pills) and the next pointer change animates (iPad run below shows the tap open at 14 fractional frames, scrim tap close at 7). No stale-instant leak on a pointer change. - **iPad, 1024x1366 touch + hardware keyboard:** key open: first frame opacity 1, `data-motion=instant`; touch tap on the capsule: palette fades (14 fractional frames), `data-motion=animate`, `data-input=pointer`; key close of the tap-opened palette: instant; scrim tap: 7 fractional frames. Touch resets the source (pointerdown), keys set it back. - **Reduced motion (1440x900):** key open and pointer open both have opacity 1 in the first frame; no transition. - **Focus return:** palette from a focused terminal + Escape → focus back on the terminal; pointer open via the capsule + Escape → capsule; no focus lost. (After "All views" + Enter my sequence left focus on the capsule, which is where it was before the open.) - **`html:root` bump:** the rule only matches elements that carry both the class and `data-motion="instant"`, so unrelated rules are untouched. It does win over `.ui-pill.sidebar-toggle` and `.top > .capsule` transitions on those elements, which is its purpose; verified by the phone-width plus pill fade spec passing at 393x852. The only side effect is finding 3's persistence. - **Camera, pill-morph, shortcuts panel, lock, window stepping:** the required specs pass (below); every keyboard camera flight goes through `Camera.flyTo`. ### Audit of animation sites (grep of `.animate(`, `new Spring(`, CSS transitions in apps/tracer and packages/ui) Wired to the input source: palette, scrim, covered pills, sidebar and toggle, glass bed, pill-morph, shortcuts panel, window card active/lock, tree marker, `pulse`, camera. Reachable from a key and **not** wired (own per-caller flag or nothing): confirm tip (finding 1), `split`/`newWindow` from the palette (2), session manager popover from the palette (7); anchored popovers, settings overlay and details take a caller-passed `source`, and settings via palette Enter was instant in my sample. Not reachable from a shortcut (pointer-only by nature): drag/resize/move springs, toasts, add-host wizard prototypes, chip input, count-up, status line. ### Is there a cheap guard against a forgotten `markMotion`? Yes, two, and I recommend the first as the required one: 1. **A registry-driven sweep spec (about 60 lines).** `registerShortcuts` already lists every chord. A Playwright test iterates `shortcutSections(["canvas","global"])`, presses each combo on a fresh canvas, and fails if any non-allowlisted CSS transition, Web Animation or inline-spring style is running two frames later. A new shortcut is then covered without anyone remembering. Add the same loop over the palette rows (type the label, Enter), which would have caught findings 1, 2 and 7 on the day they were written. 2. **Move the decision into the primitive, as `Camera.flyTo` did.** Give `openAnchoredPopover`, `split`/`newWindow`, `createConfirmTip` and the settings overlay a default of `triggeredByKeyboard()` instead of a caller-passed flag. Then removing the parameter makes the mistake unrepresentable. A `scripts/doc-coverage`-style baseline of `.animate(`/`new Spring(` sites would also work but only forces a classification, not a behaviour. ### Gate results (all on the scratch merge e0a7ac2b7, dev-02 production build) - keyboard-instant, palette-navigation, clusters-flight, clusters-shortcuts, palette-polish: **53 passed at 1440x900** and **53 passed at 393x852** (`j403-A.log`). - Pill-morph coverage (`clusters.spec.ts`: capsule view list, container shell/floating terminal, top pill): **4 passed at 1440x900, 4 passed at 393x852**. - input-source unit test: pass (1 test, 25 expects). - `verify quick`: **QUICK pre-check passed, exit=0** (`j403-quick2.log`). An earlier run failed two rows (doc comments, fixed waits) only because my own untracked probe files were in the tree; moved out, it passes. - `verify fast` not run (not requested; the brief reserves it for the author and the merge). Nothing merged, pushed or deployed. The brief's reviewer rule says not to run `verify quick` on a reviewed branch; I ran it because the task asked, on a scratch merge. - My probe specs (never pushed): `j403-probe.spec.ts`, `j403-c.spec.ts`. No screenshots: this is a timing change, so frame samples are the evidence.
Author
Owner

J370 round 5 progress. origin/dev 966c52e07 merged in. Pushed (head 075b52395): becf9e4de the sweep guard (every ? panel chord and every palette row, Enter, nothing may still move from the fourth frame; idle motion is measured first and ignored), 8f6674455 the open/close durations now scale by --ui-motion-scale (P3-3: only those transitions collapse, not every transition on the element; also wires the confirmation tip, P2-1), c3be393be popovers, the settings overlay, split/newWindow/step/move/close and toggleManager read the tracker themselves (P2-2, P2-3, P3-6 for these primitives), 075b52395 doc for non-press changes (P3-4/5). Red run of the sweep on 4b2fea905 and the green run are queued on dev-02.

J370 round 5 progress. origin/dev 966c52e07 merged in. Pushed (head 075b52395): becf9e4de the sweep guard (every ? panel chord and every palette row, Enter, nothing may still move from the fourth frame; idle motion is measured first and ignored), 8f6674455 the open/close durations now scale by `--ui-motion-scale` (P3-3: only those transitions collapse, not every transition on the element; also wires the confirmation tip, P2-1), c3be393be popovers, the settings overlay, split/newWindow/step/move/close and toggleManager read the tracker themselves (P2-2, P2-3, P3-6 for these primitives), 075b52395 doc for non-press changes (P3-4/5). Red run of the sweep on 4b2fea905 and the green run are queued on dev-02.
Author
Owner

J370 round 5 finish (J403's findings). Branch fix-kbd-no-anim-427, head 86b9719c2, pushed from the control host (git ls-remote origin fix-kbd-no-anim-427 → 86b9719c2…). Not merged. origin/dev 966c52e07 merged in.

The guard (becf9e4de, then 27f92469c / 77cfea1f7 / a61578cbe) — apps/tracer/test/keyboard-sweep.spec.ts. It reads the chords from the ? panel and the rows from the palette itself (one row per action family), boots the Console scene with a terminal active, presses each chord or types the row and presses Enter, and fails if anything new is still moving from the fourth frame on: a running transition (open/close geometry only: opacity, transform, translate, scale, clip-path, size, position), a script animation, or an inline transform/clip-path/opacity still being rewritten (springs). Motion already present while the page sat idle is ignored. Named exceptions, each with its reason in the file: the test double's badge, the terminal cursor's blink, a window's connection indicator morph on a network change.
Red (~/j370-r6red.log, dev-02, spec against 4b2fea905's source) names, among others: Sign out → confirmation tip fades (opacity/transform, 10 frames); New window right → .cl-col FLIP and section.cl transform; Rename host, Settings, Split right/New window below → anchored popover animation; Close window → toast; Control+Shift+L / Escape / SSH shell → #keyrows transform; Open shell on a host → window swell. So it catches the three P2s and more.

Fixes (one commit each)

  • 8f6674455 P3-3 + P2-1: open/close transitions are written calc(Nms * var(--ui-motion-scale, 1)) and [data-motion=instant] sets the registered, non-inherited --ui-motion-scale to 0, so only those collapse; the confirmation tip marks itself where it flips data-open.
  • c3be393be P2-2, P2-3, P3-6: openAnchoredPopover, the settings overlay, win.split/newWindow/step/move (their instant parameter is gone), win.close and toggleManager read the tracker themselves; the palette's hard-coded "pointer" and missing instant are gone. The .ui-key-instant class is removed.
  • 075b52395 P3-4/5 doc: non-press changes and clicks without a trusted pointerdown follow the last source and err toward instant.
  • Found by the sweep: 74fd410d9→ tray rows (settleRow) and the lock button's own mark (the non-inherited scale needs it on the child); 65a19b070 window enter/FLIP for a key-opened window, toast stack; a61578cbe neighbouring views' position springs and the add-host card; 0a71b697c its footer and close.

Green at 0a71b697c (~/j370-r10.log, dev-02): 55 passed, 0 failed — keyboard-sweep (chords and palette rows), keyboard-instant, clusters-flight, clusters-shortcuts, palette-navigation, palette-polish; input-source and session-hooks unit tests pass; tracer typecheck passes. Later commits are doc comments only.
verify quick at 86b9719c2 (~/j370-r11quick.log): "QUICK pre-check passed", exit=0. verify fast not run.

Residual (said plainly): the explicit source/how arguments remain where a surface needs them for focus placement or timing, not motion: account and version panels, row menus, bottom sheets, the sidebar card morph, details and docs popovers, LockSource. The four primitives you named no longer take a source for motion, and close() on the anchored popover still accepts an ignored argument for wrappers. Not covered by the sweep: rows that need a second state (a locked window, a team layout) and Mac chords (the panel lists the Linux ones on this host).

J370 round 5 finish (J403's findings). Branch `fix-kbd-no-anim-427`, head **86b9719c2**, pushed from the control host (`git ls-remote origin fix-kbd-no-anim-427` → 86b9719c2…). Not merged. origin/dev 966c52e07 merged in. **The guard (becf9e4de, then 27f92469c / 77cfea1f7 / a61578cbe)** — `apps/tracer/test/keyboard-sweep.spec.ts`. It reads the chords from the `?` panel and the rows from the palette itself (one row per action family), boots the Console scene with a terminal active, presses each chord or types the row and presses Enter, and fails if anything new is still moving from the fourth frame on: a running transition (open/close geometry only: opacity, transform, translate, scale, clip-path, size, position), a script animation, or an inline transform/clip-path/opacity still being rewritten (springs). Motion already present while the page sat idle is ignored. Named exceptions, each with its reason in the file: the test double's badge, the terminal cursor's blink, a window's connection indicator morph on a network change. **Red** (`~/j370-r6red.log`, dev-02, spec against 4b2fea905's source) names, among others: Sign out → confirmation tip fades (opacity/transform, 10 frames); New window right → `.cl-col` FLIP and `section.cl` transform; Rename host, Settings, Split right/New window below → anchored popover animation; Close window → toast; Control+Shift+L / Escape / SSH shell → `#keyrows` transform; Open shell on a host → window swell. So it catches the three P2s and more. **Fixes (one commit each)** - 8f6674455 P3-3 + P2-1: open/close transitions are written `calc(Nms * var(--ui-motion-scale, 1))` and `[data-motion=instant]` sets the registered, non-inherited `--ui-motion-scale` to 0, so only those collapse; the confirmation tip marks itself where it flips `data-open`. - c3be393be P2-2, P2-3, P3-6: `openAnchoredPopover`, the settings overlay, `win.split/newWindow/step/move` (their `instant` parameter is gone), `win.close` and `toggleManager` read the tracker themselves; the palette's hard-coded "pointer" and missing `instant` are gone. The `.ui-key-instant` class is removed. - 075b52395 P3-4/5 doc: non-press changes and clicks without a trusted pointerdown follow the last source and err toward instant. - Found by the sweep: 74fd410d9→ tray rows (`settleRow`) and the lock button's own mark (the non-inherited scale needs it on the child); 65a19b070 window enter/FLIP for a key-opened window, toast stack; a61578cbe neighbouring views' position springs and the add-host card; 0a71b697c its footer and close. **Green** at 0a71b697c (`~/j370-r10.log`, dev-02): **55 passed, 0 failed** — keyboard-sweep (chords and palette rows), keyboard-instant, clusters-flight, clusters-shortcuts, palette-navigation, palette-polish; input-source and session-hooks unit tests pass; tracer typecheck passes. Later commits are doc comments only. **`verify quick`** at 86b9719c2 (`~/j370-r11quick.log`): "QUICK pre-check passed", exit=0. `verify fast` not run. **Residual (said plainly):** the explicit `source`/`how` arguments remain where a surface needs them for focus placement or timing, not motion: account and version panels, row menus, bottom sheets, the sidebar card morph, details and docs popovers, `LockSource`. The four primitives you named no longer take a source for motion, and `close()` on the anchored popover still accepts an ignored argument for wrappers. Not covered by the sweep: rows that need a second state (a locked window, a team layout) and Mac chords (the panel lists the Linux ones on this host).
Author
Owner

J370 correction to the round 5 finish comment: the tray-rows and lock-button commit is 7dc09f440 (I wrote 74fd410d9, which is the doc/allowlist commit after it).

J370 correction to the round 5 finish comment: the tray-rows and lock-button commit is **7dc09f440** (I wrote 74fd410d9, which is the doc/allowlist commit after it).
Author
Owner

J413 progress (round 5 review of fix-kbd-no-anim-427 @ 86b9719c2). Scratch merge with origin/dev 0414b7b2f = 92da9ebb1 (clean, only DECISIONS.md changed). Production build on dev-02, ~/j413-A.log.

Green so far on 92da9ebb1:

  • verify quick: exit=0. input-source + session-hooks unit tests and tracer typecheck: exit=0.
  • keyboard-sweep (chords, then palette rows): passed at 1440x900 (3.4 min + 4.0 min) and at 393x852. The committed spec forces setViewportSize(1440x900) inside fresh(), so I ran a copy that takes the viewport from the environment; the 393x852 result is from that copy.
  • keyboard-instant, clusters-flight, clusters-shortcuts, palette-navigation, palette-polish: 53 passed at 1440x900, all passed at 393x852.
  • Probes of mine: Enter on the window pill opens details with nothing moving; a click on it animates; key close of a pointer-opened details is instant; Ctrl+Shift+1..3 (the pattern row the sweep skips) leave nothing moving at both sizes.

Still to run: the committed sweep against plain origin/dev 0414b7b2f (must fail), and a check that --ui-motion-scale does not reach children. Candidate finding being measured: grid-template-rows disclosures (.cl-pv consent pane, .ac-pane, .ui-inbox-panel) are outside the sweep's geometry filter and not wired to the input source. No verdict yet.

J413 progress (round 5 review of `fix-kbd-no-anim-427` @ 86b9719c2). Scratch merge with origin/dev 0414b7b2f = 92da9ebb1 (clean, only DECISIONS.md changed). Production build on dev-02, `~/j413-A.log`. Green so far on 92da9ebb1: - `verify quick`: exit=0. input-source + session-hooks unit tests and tracer typecheck: exit=0. - keyboard-sweep (chords, then palette rows): passed at 1440x900 (3.4 min + 4.0 min) and at 393x852. The committed spec forces `setViewportSize(1440x900)` inside `fresh()`, so I ran a copy that takes the viewport from the environment; the 393x852 result is from that copy. - keyboard-instant, clusters-flight, clusters-shortcuts, palette-navigation, palette-polish: 53 passed at 1440x900, all passed at 393x852. - Probes of mine: Enter on the window pill opens details with nothing moving; a click on it animates; key close of a pointer-opened details is instant; Ctrl+Shift+1..3 (the pattern row the sweep skips) leave nothing moving at both sizes. Still to run: the committed sweep against plain origin/dev 0414b7b2f (must fail), and a check that `--ui-motion-scale` does not reach children. Candidate finding being measured: `grid-template-rows` disclosures (`.cl-pv` consent pane, `.ac-pane`, `.ui-inbox-panel`) are outside the sweep's geometry filter and not wired to the input source. No verdict yet.
Author
Owner

J413 review of fix-kbd-no-anim-427 @ 86b9719c2 (scratch merge with origin/dev 0414b7b2f = 92da9ebb1)

Verdict: REQUEST CHANGES. The mechanism and the fixes for J403's three P2s hold. Two P2s remain, and both are about the guard: the sweep promises that no key-reachable surface moves, and it leaves out the case it most needs to catch.

Findings

P2-1: the sweep's palette family rule never samples six rows, one of which animates.
keyboard-sweep.spec.ts takes family = action.slice(0, action.lastIndexOf(":")). An action with one colon (canvas:sessions, canvas:close, canvas:overview, canvas:windowdocs, canvas:inputlock, canvas:share) therefore falls into the family canvas, whose sampled row is "Add hosts". Evidence is the HOLE sampled line in ~/j413-hole.log: 25 families, and ["canvas","Add hosts"] is the only sample for all six rows.

  • Sweeping those six rows by hand with the sweep's own watcher at 1440x900: Share layout, Read about this window, Close window, Input lock and Sessions on this host are clean. All views + Enter leaves div.cl-region fading its opacity for 16 frames from 3 frames after the key.
  • "Sessions on this host" is J403's finding 7. The guard would not have caught that one coming back.
  • Fix: group by the full action (or by the first two segments), or sample every row whose action has one colon. About 5 more rows, about 25 s.

P2-2: a key still fades the view-region frames (.cl-region, 220 ms), and the Esc chord row is vacuous.
.cl-region { transition: opacity 220ms } is keyed on .cl-world[data-zoom="out"], which the camera writes (camera.ts:100,151). It is not wired to markMotion or --ui-motion-scale. A key that goes to the overview jumps the camera, then the region frames fade in over about 270 ms.

  • Palette "All views" + Enter: 16 frames of transition opacity on div.cl-region (j413-hole.log).
  • The ? panel's "All views (overview) = Esc" row passed the sweep only because it did nothing. After fresh(), Escape with the terminal focused and with focus blurred both left data-zoom="in", with no findings (j413-esc.log, ESC terminal-focused and ESC blurred). The sweep presses a chord that never reaches the overview.
  • Fix: write the .cl-region and .cl-label fades as calc(220ms * var(--ui-motion-scale, 1)) and call markMotion(world) where the band flips. Make the Esc row meaningful: blur the terminal and assert data-zoom changed, or fail when a pressed chord changes nothing.
  • The pointer control (palette click on "All views") fades the regions, so pointer behaviour is intact.

P3-3: the sweep's property filter hides grid-template-rows disclosures. From reading, not measured.
GEOMETRY has no grid-template-rows, max-height, margin, filter or inset. The tree's .bm-fold already honours a key through no-motion. The disclosures .cl-pv (the access-level consent pane), .ac-pane, .ui-inbox-panel, .ah-reveal and .ui-setting-refusal transition grid-template-rows at 200 to 340 ms with no input-source hook. access-level.on-demand.ts:239 sets data-open straight from the "Access level" button, and the palette's canvas:access path presses it from Enter. I could not measure it: the clusters mockup has no "Allow full access" row (ACCESS: no row in this scene). Add the property to the filter and note it among the exceptions.

P3-4: palette rows hard-code a source for motion in the pointer direction (errs toward instant). openWindowDocs(cx, active) defaults to "keyboard", and dispatchCanvasAction access calls details.toggle(rec, "keyboard"). A pointer click on those palette rows therefore opens instantly. Pass detail.source, or read the tracker.

P3-5: the sweep's frame threshold depends on host speed. A move needs at least 4 late frames. A 130 ms fade on a loaded host that renders about 20 fps shows 2 to 3 frames and passes. The red run still catches the real defects, but a green sweep on a busy host is weaker evidence.

The four things I was asked to check

  • Does the sweep cover every chord and palette family? The ? panel lists 27 rows. The sweep presses about 22 of them: both lock chords, the four splits and new windows, close, next and previous, the four moves, Read about this window, F6, Shift+F6, Ctrl+Shift+Space, the palette chord and the three font keys. Its skips are justified except the vacuous Esc row (P2-2):
    • Alt+click and Scroll are pointer entries.
    • "Show these shortcuts" and "Close the open panel" are covered by keyboard-instant.
    • Ctrl+Shift+1–3: I pressed 1, 2 and 3 myself and nothing moved (PROBE view-digit, both viewports).
    • The label skips Reload and Sign out of every device are fine (page navigation, and the same confirmation tip as Sign out).
    • The exceptions list is fine: the test double's badge, the cursor blink, and the connection indicator morph (a network state change, not open or close motion).
    • The palette half is not complete: see P2-1.
  • --ui-motion-scale leak and reduced motion. The leak check passed (LEAK line in j413-leak.log): on the palette it is 0, all 400 descendants read 1, and descendants keep their own durations (ui-conn-g keeps 0.16s, 0.24s). Reduced motion is transition: none for .palette, .palette:not([hidden]) and .palette-scrim, and the pointer-open reduced-motion case passes in keyboard-instant (both viewports). My own reduced-motion sampler read the first frames before the click landed, so it proves nothing. Safari's @property and calc(time * number) handling is untested.
  • Pointer and touch still animate. Pointer: confirmed by keyboard-instant (pointer open and close, camera flight, border fade after a pointer click, a pointer change after a key), and by my probes (a click on the window pill animates the details popover; a palette click animates the palette, the popover and the camera). Touch: I did not run a touch context. The tracker resets on pointerdown, and J403 measured an iPad context in round 4.
  • Residual surfaces. J370's wording ("focus, not motion") is not accurate. Bottom sheets, the sidebar card morph, details, docs previews and the crumbs menu still decide motion from an explicit source.
    • Where that source is event.detail === 0 the behaviour is right. I verified Enter on the focused window pill (details open with nothing moving at both viewports), Enter on the "Sessions" popover at 393, and key-close of a pointer-opened details.
    • The sidebar row's Info button is tabIndex = -1, so a keyboard cannot reach it. The hard-coded "pointer" at row-pill.ts:115 is true.
    • I found no key path through these that animates. Palette dispatch is the exception: it hard-codes the source (P3-4).
    • Row menus and LockSource reach no motion, because setLocked and the popovers read the tracker themselves.

Gate results (scratch merge 92da9ebb1, production build, dev-02)

  • Sweep on plain origin/dev 0414b7b2f: both tests fail, as required (j413-dev.log, 2 failed). Chords named include: Split right, New window right and below, Input lock, Close window, Rename build-01, Settings, Show keyboard shortcuts, Open the command palette, Add hosts, Sign out, SSH shell and Open shell on a host.
  • verify quick: exit=0.
  • Unit tests (input-source, session-hooks) and the tracer typecheck: exit=0.
  • Sweep (chords then palette rows) at 1440x900: passed, 3.4 min + 4.0 min. At 393x852: passed. This used a copy that takes the viewport from the environment, because the committed spec forces 1440x900 in fresh().
  • keyboard-instant, clusters-flight, clusters-shortcuts, palette-navigation, palette-polish: 53 passed at 1440x900, all passed at 393x852.
  • Not run: verify fast (not requested). Not merged, pushed or deployed.
  • My probes (details, ENUM, leak, hole, esc) failed once at 393x852. The pointer-control tests timed out because the window pill was not clickable there, a harness issue and not a product result.
  • Receipts are in ~/.cache/codex-scratch/j413/receipts/ on dev-02: j413-A.log, j413-dev.log, j413-leak.log, j413-hole.log, j413-esc.log and j413-probes/.
  • The brief says reviewers should not run verify quick on the reviewed branch. I ran it because the task asked for it, on a scratch merge.
## J413 review of `fix-kbd-no-anim-427` @ 86b9719c2 (scratch merge with origin/dev 0414b7b2f = 92da9ebb1) **Verdict: REQUEST CHANGES.** The mechanism and the fixes for J403's three P2s hold. Two P2s remain, and both are about the guard: the sweep promises that no key-reachable surface moves, and it leaves out the case it most needs to catch. ### Findings **P2-1: the sweep's palette family rule never samples six rows, one of which animates.** `keyboard-sweep.spec.ts` takes `family = action.slice(0, action.lastIndexOf(":"))`. An action with one colon (`canvas:sessions`, `canvas:close`, `canvas:overview`, `canvas:windowdocs`, `canvas:inputlock`, `canvas:share`) therefore falls into the family `canvas`, whose sampled row is "Add hosts". Evidence is the `HOLE sampled` line in `~/j413-hole.log`: 25 families, and `["canvas","Add hosts"]` is the only sample for all six rows. - Sweeping those six rows by hand with the sweep's own watcher at 1440x900: Share layout, Read about this window, Close window, Input lock and Sessions on this host are clean. **All views + Enter leaves `div.cl-region` fading its opacity for 16 frames from 3 frames after the key.** - "Sessions on this host" is J403's finding 7. The guard would not have caught that one coming back. - Fix: group by the full action (or by the first two segments), or sample every row whose action has one colon. About 5 more rows, about 25 s. **P2-2: a key still fades the view-region frames (`.cl-region`, 220 ms), and the `Esc` chord row is vacuous.** `.cl-region { transition: opacity 220ms }` is keyed on `.cl-world[data-zoom="out"]`, which the camera writes (`camera.ts:100,151`). It is not wired to `markMotion` or `--ui-motion-scale`. A key that goes to the overview jumps the camera, then the region frames fade in over about 270 ms. - Palette "All views" + Enter: 16 frames of `transition opacity on div.cl-region` (`j413-hole.log`). - The `?` panel's "All views (overview) = Esc" row passed the sweep only because it did nothing. After `fresh()`, `Escape` with the terminal focused and with focus blurred both left `data-zoom="in"`, with no findings (`j413-esc.log`, `ESC terminal-focused` and `ESC blurred`). The sweep presses a chord that never reaches the overview. - Fix: write the `.cl-region` and `.cl-label` fades as `calc(220ms * var(--ui-motion-scale, 1))` and call `markMotion(world)` where the band flips. Make the Esc row meaningful: blur the terminal and assert `data-zoom` changed, or fail when a pressed chord changes nothing. - The pointer control (palette click on "All views") fades the regions, so pointer behaviour is intact. **P3-3: the sweep's property filter hides `grid-template-rows` disclosures. From reading, not measured.** `GEOMETRY` has no `grid-template-rows`, `max-height`, `margin`, `filter` or `inset`. The tree's `.bm-fold` already honours a key through `no-motion`. The disclosures `.cl-pv` (the access-level consent pane), `.ac-pane`, `.ui-inbox-panel`, `.ah-reveal` and `.ui-setting-refusal` transition `grid-template-rows` at 200 to 340 ms with no input-source hook. `access-level.on-demand.ts:239` sets `data-open` straight from the "Access level" button, and the palette's `canvas:access` path presses it from Enter. I could not measure it: the clusters mockup has no "Allow full access" row (`ACCESS: no row in this scene`). Add the property to the filter and note it among the exceptions. **P3-4: palette rows hard-code a source for motion in the pointer direction (errs toward instant).** `openWindowDocs(cx, active)` defaults to `"keyboard"`, and `dispatchCanvasAction` `access` calls `details.toggle(rec, "keyboard")`. A pointer click on those palette rows therefore opens instantly. Pass `detail.source`, or read the tracker. **P3-5: the sweep's frame threshold depends on host speed.** A move needs at least 4 late frames. A 130 ms fade on a loaded host that renders about 20 fps shows 2 to 3 frames and passes. The red run still catches the real defects, but a green sweep on a busy host is weaker evidence. ### The four things I was asked to check - **Does the sweep cover every chord and palette family?** The `?` panel lists 27 rows. The sweep presses about 22 of them: both lock chords, the four splits and new windows, close, next and previous, the four moves, Read about this window, F6, Shift+F6, Ctrl+Shift+Space, the palette chord and the three font keys. Its skips are justified except the vacuous Esc row (P2-2): - `Alt+click` and `Scroll` are pointer entries. - "Show these shortcuts" and "Close the open panel" are covered by keyboard-instant. - `Ctrl+Shift+1–3`: I pressed 1, 2 and 3 myself and nothing moved (`PROBE view-digit`, both viewports). - The label skips `Reload` and `Sign out of every device` are fine (page navigation, and the same confirmation tip as Sign out). - The exceptions list is fine: the test double's badge, the cursor blink, and the connection indicator morph (a network state change, not open or close motion). - The palette half is not complete: see P2-1. - **`--ui-motion-scale` leak and reduced motion.** The leak check passed (`LEAK` line in `j413-leak.log`): on the palette it is `0`, all 400 descendants read `1`, and descendants keep their own durations (`ui-conn-g` keeps `0.16s, 0.24s`). Reduced motion is `transition: none` for `.palette`, `.palette:not([hidden])` and `.palette-scrim`, and the pointer-open reduced-motion case passes in keyboard-instant (both viewports). My own reduced-motion sampler read the first frames before the click landed, so it proves nothing. Safari's `@property` and `calc(time * number)` handling is untested. - **Pointer and touch still animate.** Pointer: confirmed by keyboard-instant (pointer open and close, camera flight, border fade after a pointer click, a pointer change after a key), and by my probes (a click on the window pill animates the details popover; a palette click animates the palette, the popover and the camera). Touch: I did not run a touch context. The tracker resets on `pointerdown`, and J403 measured an iPad context in round 4. - **Residual surfaces.** J370's wording ("focus, not motion") is not accurate. Bottom sheets, the sidebar card morph, details, docs previews and the crumbs menu still decide motion from an explicit `source`. - Where that source is `event.detail === 0` the behaviour is right. I verified Enter on the focused window pill (details open with nothing moving at both viewports), Enter on the "Sessions" popover at 393, and key-close of a pointer-opened details. - The sidebar row's Info button is `tabIndex = -1`, so a keyboard cannot reach it. The hard-coded `"pointer"` at `row-pill.ts:115` is true. - I found no key path through these that animates. Palette dispatch is the exception: it hard-codes the source (P3-4). - Row menus and `LockSource` reach no motion, because `setLocked` and the popovers read the tracker themselves. ### Gate results (scratch merge 92da9ebb1, production build, dev-02) - **Sweep on plain origin/dev 0414b7b2f: both tests fail, as required** (`j413-dev.log`, 2 failed). Chords named include: Split right, New window right and below, Input lock, Close window, Rename build-01, Settings, Show keyboard shortcuts, Open the command palette, Add hosts, Sign out, SSH shell and Open shell on a host. - `verify quick`: exit=0. - Unit tests (input-source, session-hooks) and the tracer typecheck: exit=0. - Sweep (chords then palette rows) at 1440x900: passed, 3.4 min + 4.0 min. At 393x852: passed. This used a copy that takes the viewport from the environment, because the committed spec forces 1440x900 in `fresh()`. - keyboard-instant, clusters-flight, clusters-shortcuts, palette-navigation, palette-polish: **53 passed at 1440x900, all passed at 393x852**. - Not run: `verify fast` (not requested). Not merged, pushed or deployed. - My probes (details, ENUM, leak, hole, esc) failed once at 393x852. The pointer-control tests timed out because the window pill was not clickable there, a harness issue and not a product result. - Receipts are in `~/.cache/codex-scratch/j413/receipts/` on dev-02: `j413-A.log`, `j413-dev.log`, `j413-leak.log`, `j413-hole.log`, `j413-esc.log` and `j413-probes/`. - The brief says reviewers should not run `verify quick` on the reviewed branch. I ran it because the task asked for it, on a scratch merge.
Author
Owner

J370 round 6 finish (J413's findings). Branch fix-kbd-no-anim-427, head c11a47562 (origin/dev cedeb4d26 merged; pushed, git ls-remote agrees). Not merged.

Merge note: dev's D372 motion tokens (--t-press, --t-pop, --t-panel, EASE_OUT) conflicted line by line with this branch's scaled transitions (8ed45db67). Each open/close transition keeps dev's token multiplied by --ui-motion-scale; dev's palette rule with the 180 ms key fade is dropped.

Fixes

  • 3e7476c04 P2-1, P3-3, P3-5, Esc (spec): every canvas: verb is swept on its own (argument families still take one row); Escape row non-vacuous (the Console scene opens a popover that eats the first Escape; the sweep closes it, blurs the terminal, presses the chord, and fails if data-zoom is not out); motion is measured in time (still seen 50 ms after the key, and for ≥ 70 ms after that), so a slow host cannot hide a fade under a frame floor; grid rows, max-height, inset, filter join the properties; runs at 1440x900 and 393x852. 21f95f8cd skips the Mockup: scripted-scene rows (documented).
  • e99de3e10 P2-2 + P3-4: the camera marks the world where data-zoom flips and .cl-region's fade scales by --ui-motion-scale; "Read about this window" and "Allow full access" take the source from the tracker.

Red (~/j370-s1red.log, dev-02, new sweep on 86b9719c2's source): at both sizes the only findings are All views (overview) (Escape): transition opacity on div.cl-region (moving from ~55 ms to ~255 ms) and All views: transition opacity on div.cl-region (...); the Esc chord reaches the overview (no "did not reach" message).
Green at c11a47562 (~/j370-s4.log, dev-02): 57 passed, 0 failed — keyboard-sweep at 1440x900 and 393x852 (shortcuts and palette rows), keyboard-instant, clusters-flight, clusters-shortcuts, palette-navigation, palette-polish; unit tests and tracer typecheck pass.
scripts/verify fast at c11a47562 (dev-02, ~/j370-fast2.log), verbatim table: every row PASS except cargo-deny | FAIL (1), privileged integration | SKIP; VERIFY_EXIT=1. The failing row is error[vulnerability]: libsqlite3-sys via C SQLite CVE-2026-119375 (RUSTSEC-2026-0459) on libsqlite3-sys 0.30.1 in Cargo.lock. This branch does not touch Cargo.lock; cargo deny check advisories on plain origin/dev fails the same way (~/j370-denydev.log: "advisories FAILED", exit 1). Not mine.

J370 round 6 finish (J413's findings). Branch `fix-kbd-no-anim-427`, head **c11a47562** (origin/dev cedeb4d26 merged; pushed, `git ls-remote` agrees). Not merged. **Merge note:** dev's D372 motion tokens (`--t-press`, `--t-pop`, `--t-panel`, `EASE_OUT`) conflicted line by line with this branch's scaled transitions (8ed45db67). Each open/close transition keeps dev's token multiplied by `--ui-motion-scale`; dev's palette rule with the 180 ms key fade is dropped. **Fixes** - 3e7476c04 P2-1, P3-3, P3-5, Esc (spec): every `canvas:` verb is swept on its own (argument families still take one row); Escape row non-vacuous (the Console scene opens a popover that eats the first Escape; the sweep closes it, blurs the terminal, presses the chord, and fails if `data-zoom` is not `out`); motion is measured in time (still seen 50 ms after the key, and for ≥ 70 ms after that), so a slow host cannot hide a fade under a frame floor; grid rows, max-height, inset, filter join the properties; runs at 1440x900 and 393x852. 21f95f8cd skips the `Mockup:` scripted-scene rows (documented). - e99de3e10 P2-2 + P3-4: the camera marks the world where `data-zoom` flips and `.cl-region`'s fade scales by `--ui-motion-scale`; "Read about this window" and "Allow full access" take the source from the tracker. **Red** (`~/j370-s1red.log`, dev-02, new sweep on 86b9719c2's source): at both sizes the only findings are `All views (overview) (Escape): transition opacity on div.cl-region (moving from ~55 ms to ~255 ms)` and `All views: transition opacity on div.cl-region (...)`; the Esc chord reaches the overview (no "did not reach" message). **Green** at c11a47562 (`~/j370-s4.log`, dev-02): **57 passed, 0 failed** — keyboard-sweep at 1440x900 and 393x852 (shortcuts and palette rows), keyboard-instant, clusters-flight, clusters-shortcuts, palette-navigation, palette-polish; unit tests and tracer typecheck pass. **`scripts/verify fast`** at c11a47562 (dev-02, `~/j370-fast2.log`), verbatim table: every row PASS except `cargo-deny | FAIL (1)`, `privileged integration | SKIP`; `VERIFY_EXIT=1`. The failing row is `error[vulnerability]: libsqlite3-sys via C SQLite CVE-2026-119375 (RUSTSEC-2026-0459)` on `libsqlite3-sys 0.30.1` in Cargo.lock. This branch does not touch Cargo.lock; `cargo deny check advisories` on plain origin/dev fails the same way (`~/j370-denydev.log`: "advisories FAILED", exit 1). Not mine.
Author
Owner

Round 6 review of fix-kbd-no-anim-427 @ c11a47562 (origin/dev merged: cedeb4d26)

Verdict: APPROVE, with one follow-up that should ride on the merge or be filed (P2-A) and four P3s. Every round-5 finding is fixed and the guard now fails where it must. No P1.

I ran everything on dev-02 through build-slot, in my own worktrees (now removed). I did not run verify quick or verify fast. Receipts are in ~/.cache/codex-scratch/r427/receipts/ on dev-02, along with the probe specs.

1. Sweep red on dev, green on the head: confirmed

  • Red: the branch's keyboard-sweep.spec.ts against origin/dev 6df0f5bd5 (newer than cedeb4d26) fails 4/4. That is both tests at 1440x900 and at 393x852. Log: r427-red.log. It names All views (overview) (Escape) and All views, with transition opacity on div.cl-region (moving from ~55 ms to ~290 ms) plus the camera's style transform on div.cl-world. The Esc chord reaches the overview ("did not reach" never appears), so it no longer passes vacuously.
  • Green: the head c11a47562 passes 4/4 (15.8 min): shortcuts and palette rows, at both sizes. Log: r427-green.log.

2. Hunt for missed key-reachable surfaces

I used a probe harness that reuses the sweep's watcher: CSS transitions, getAnimations() and inline transform/opacity/clip-path rewrites, per frame, with the sweep's own thresholds. Probe 3 moves the cursor off the page first, so hover cannot pass for a response to the key. Probe 1 left it parked, and its first-run "moves" turned out to be hover tooltips and hover summaries. Specs and logs are in the receipts directory.

Clean (nothing moved after the key):

  • Settings window: Tab, arrows, Enter, Ctrl+Alt+arrows, F6, Shift+F6 and Escape, at both sizes. The three breadcrumb pills with Enter, ArrowDown and Escape. Palette Settings panes Appearance › Theme/Font/Size/Layout at 393.
  • Add hosts, opened by key: Tab, Shift+Tab, Enter, ArrowDown and Escape ×3 at both sizes (r427-p4-branch.log). Nothing moves, and the wizard goes on the first Escape. A hang I saw in my first add-hosts probe was my own harness (getAttribute on an element that had just gone), and it does not reproduce (12 keys, no hang).
  • Disclosure triggers, Enter then Escape. 11 distinct trigger classes at 1440 and at 393, and 9 more at 1440 with ca=locked. These cover the window pill and "More window actions", the capsule (view list), the sidebar toggle and the sidebar sections. They also cover the switches, the tabs, the inbox card and the account pill. All still, except P3-D below. Receipt: r427-probe3.log.
  • Palette rows the committed sweep does not press (it takes one row per argument family): 42 rows at 1440 and 46 at 393. These are canvas:view:*, canvas:open/rename/ctr/session:*, split/new, canvas lock, theme:*, bg:*, card:*, windows:*, settings:*, proto:later. All still, except the font rows (P2-A).

Limit of this evidence: probe 3 did not record whether Enter changed anything on each trigger. A "still" on a trigger that does nothing is weaker than a "still" on one that visibly opens. Card morphs, docs previews and sheets are therefore covered by the sweep, keyboard-instant and my earlier probes, not proved again here. I did not build a key path into docs previews or bottom sheets.

3. Pointer and touch still animate: confirmed

Positive controls with the same watcher (r427-probe.log):

  • Mouse, 1440x900: the sidebar toggle animates (transform 62 to 295 ms, sidebar opacity and transform). The capsule view list animates (clip-path to 736 ms). The window pill animates (popover animation, lock fade).
  • Touch, iPad-like context (820x1180, hasTouch, isMobile, dpr 2, real locator.tap()): the same three all animate (sidebar toggle to 450 ms, view list clip-path to 782 ms, window pill transform to 368 ms).
  • The 393 mouse control timed out because the window pill is not clickable there. That is the same harness issue J413 reported, not a product result.

4. Interaction with motion-fast-ease-out (#471, bfdcddc60)

A trial merge git merge-tree shows 6 conflicted files: wizard.ts, camera.ts, world.ts, lifecycle.ts, anchored-popover.ts and pill-morph.ts. CSS merges clean. wizard.ts, world.ts and pill-morph.ts are doc-comment collisions only. The rest are real:

  • camera.ts: #427 deletes KEY_FLIGHT and keyFlight() (a key jumps), while #471 retunes them to 0.12 s. #427's side must win, and #471's KEY_FLIGHT constant must go.
  • lifecycle.ts (pulse): #471 shortens it to 240 ms, and #427 adds if (triggeredByKeyboard()) return. Both are needed.
  • anchored-popover.ts: #471 uses EASE_OUT for the reduced-motion fade, and #427 wraps it in !triggeredByKeyboard(). Both are needed.

I ran the trial merge with -X ours (so #427 wins every conflict) on dev-02 and ran #471's unit tests: motion-speed.test.ts passes 20/21, no-overshoot.test.ts and spring.test.ts pass. The one failure is "no Web Animation lasts more than 300 ms or starts slowly". It names exactly the two lines where #427's side kept dev's old values: lifecycle.ts:338 (360 ms) and anchored-popover.ts:209 ("ease"). The correct resolution above fixes both. So the conflicts are small and the gate catches the wrong resolution.

A real interaction, not a conflict (P3-E below): #471's scanner reads --t-* tokens, and #427's scaled transitions are written calc(var(--t-pop) * var(--ui-motion-scale, 1)). words() keeps a calc(...) as one word and ms() only accepts a literal or var(--t-…), so the duration reads as 0. Every scaled transition (the palette, the toast, the regions, ...) silently leaves the 300 ms and ease-out checks. Today's token values are all ≤ 240 ms, so nothing is wrong yet. The guard just does not guard them.

Recommended order: #427 first, then #471.

  • #427 has had six review rounds and a red/green guard, and the owner rule it enforces is absolute.
  • #427 deletes the camera key-flight constants that #471 retunes, so rebasing #471 onto it means dropping its KEY_FLIGHT hunk, which is the smaller change.
  • After #427, J428 rebases and resolves pulse and the popover fade as above. It also makes ms() unwrap calc(<time> * var(--ui-motion-scale…)), so the new scanner sees every scaled transition.
  • The reverse order works too, but #427 would then have to make that scanner fix and re-run its 15 minute sweep.

5. The Mockup: skip is justified

All Mockup: rows (proto:conn:*, proto:acct:s:*, proto:sessions-scene:*, proto:scene:*, transparency) are made in apps/tracer/src/canvas/mock/*. They are test-double rows that run scripted scenes on their own timers (for example "play every state" runs one every 2.6 s), so a timer would be reported as a key response. The product rows they sit beside are swept normally. Caveat for the comment in the spec: the skip also removes Mockup: account – …, which opens real account panels in scripted states. Those panels are reached by "Your account" (swept) and by the account pill (probe 3, still).

6. verify fast red only on cargo-deny: confirmed

~/j370-fast2.log on dev-02 starts with c11a47562 Merge remote-tracking branch 'origin/dev'…. Every row is PASS except cargo-deny | FAIL (1) (line 13599) and privileged integration | SKIP, and it ends with VERIFY_EXIT=1. The failure at line 12530 is RUSTSEC-2026-0459 on libsqlite3-sys 0.30.1 (advisories FAILED, bans ok, licenses ok, sources ok). ~/j370-denydev.log shows the same on plain dev (advisories FAILED, deny-dev-exit=1). git diff origin/dev...HEAD -- Cargo.lock is empty. This is not the branch's.


Findings

P2-A: a palette font row animates the Settings pane's tick, and the sweep passes anyway.
Pressing Enter on any of Font: Geist Mono / JetBrains Mono / Commit Mono / Monaspace Neon moves svg.ui-sp-tick (button.ui-sp-row < div.ui-sp-list < div.ui-settings-pane) with a CSS opacity and transform transition from about 55 ms to 150–205 ms after the key. Evidence is r427-probe.log (all four rows at both sizes), and r427-probe2.log identifies the element. Idle shows no SVG transitions.
The committed sweep presses only one row of the font family, and that row passes. Whichever row it is, the guard misses the other four.

  • Fix, defect: the selected-row tick in the pane takes the tracker (markMotion/triggeredByKeyboard, as the other panes do) and drops its transition for a key.
  • Fix, guard: for argument families, press a row that changes state. For a family such as font, take the second row, or run a state-changing row. Then add the tick to the sweep or the exception list on purpose.
  • Severity: the tick is a small element inside a pane that may be behind the palette, so P2 for the guard and P3 for the product defect. I'd merge #427 and take this as a follow-up if the owner prefers.

P3-B: sweep property filter now fine, but the exception regexp can never match SVG elements.
name() prints an SVG element's className as [object SVGAnimatedString] (see the svg.[object.SVGAnimatedString] lines in r427-probe.log). NOT_PRODUCT = /…|ui-conn-g/ therefore cannot match the connection indicator's <g> or <svg> children. Use getAttribute("class") in name(). At the moment the exception passes only because nothing reaches it.

P3-C: the palette-row probe types the label and presses Enter on the first result, and that is not always the row.
theme:ink's label "Ink" returns canvas:settings:machines.new-machines.add-link first (first=DIFFERENT: in r427-probe.log). The sweep does not check that the first result is the row it meant, so a family sample can silently press another row. Assert data-action of the first result equals the wanted action (or press the row with ArrowDown), and fail otherwise.

P3-D: Enter on the "Sessions" window-action button fades the row's meta/actions (120 ms).
At 1440x900, with the cursor off the page, span.cl-sx-meta and span.cl-sx-acts run an opacity transition (sessions.css:41,48,51-52, var(--t-press) ease). Focus lands in a row of the popover that was just opened, so :focus-within swaps meta for actions. The popover itself is instant; this is the first-focus reveal of a row's actions. The sweep reaches this popover only through the palette row and the chord, which pass. The button path (Enter on the focused "Sessions" button) is the one that moves. It could use the same data-motion attribute the rest of the branch uses. The rest of the disclosure list (probe 3) is still, apart from this.

P3-E: #471's motion scanner cannot read the scaled transitions (section 4). The fix belongs in motion-speed.test.ts (ms() unwrap) and goes with whichever branch merges second.

Noted, not findings:

  • With the cursor parked over a view, a key that moves the camera (Escape to the overview, Settings Escape) brings up the hover summary div.cl-summary with a 180 ms scale-in (r427-probe.log, crumb#0 Escape). That is pointerenter on a stationary cursor, so hover, not a key response.
  • Hover tooltips appear when the cursor is parked under a moving element (the div.tip lines in r427-probe2.log). They disappear when the cursor is moved away (probe 3).
  • The input tracker's design is fine. A touch tap, wheel or pointerdown resets the source, so a hardware-keyboard iPad that types and then taps still animates the tap (touch control above).

Reviewed SHA: c11a475622. Not merged, pushed or deployed. The dev-02 worktrees and my servers are removed.

## Round 6 review of `fix-kbd-no-anim-427` @ c11a47562 (origin/dev merged: cedeb4d26) **Verdict: APPROVE**, with one follow-up that should ride on the merge or be filed (P2-A) and four P3s. Every round-5 finding is fixed and the guard now fails where it must. No P1. I ran everything on dev-02 through build-slot, in my own worktrees (now removed). I did not run `verify quick` or `verify fast`. Receipts are in `~/.cache/codex-scratch/r427/receipts/` on dev-02, along with the probe specs. ### 1. Sweep red on dev, green on the head: confirmed - **Red:** the branch's `keyboard-sweep.spec.ts` against origin/dev **6df0f5bd5** (newer than cedeb4d26) fails **4/4**. That is both tests at 1440x900 and at 393x852. Log: `r427-red.log`. It names `All views (overview) (Escape)` and `All views`, with `transition opacity on div.cl-region (moving from ~55 ms to ~290 ms)` plus the camera's `style transform on div.cl-world`. The Esc chord reaches the overview ("did not reach" never appears), so it no longer passes vacuously. - **Green:** the head c11a47562 passes **4/4 (15.8 min)**: shortcuts and palette rows, at both sizes. Log: `r427-green.log`. ### 2. Hunt for missed key-reachable surfaces I used a probe harness that reuses the sweep's watcher: CSS transitions, `getAnimations()` and inline transform/opacity/clip-path rewrites, per frame, with the sweep's own thresholds. Probe 3 moves the cursor off the page first, so hover cannot pass for a response to the key. Probe 1 left it parked, and its first-run "moves" turned out to be hover tooltips and hover summaries. Specs and logs are in the receipts directory. **Clean (nothing moved after the key):** - Settings window: Tab, arrows, Enter, `Ctrl+Alt+arrows`, F6, Shift+F6 and Escape, at both sizes. The three breadcrumb pills with Enter, ArrowDown and Escape. Palette Settings panes `Appearance › Theme/Font/Size/Layout` at 393. - Add hosts, opened by key: Tab, Shift+Tab, Enter, ArrowDown and Escape ×3 at both sizes (`r427-p4-branch.log`). Nothing moves, and the wizard goes on the first Escape. A hang I saw in my first add-hosts probe was my own harness (`getAttribute` on an element that had just gone), and it does not reproduce (12 keys, no hang). - Disclosure triggers, Enter then Escape. 11 distinct trigger classes at 1440 and at 393, and 9 more at 1440 with `ca=locked`. These cover the window pill and "More window actions", the capsule (view list), the sidebar toggle and the sidebar sections. They also cover the switches, the tabs, the inbox card and the account pill. All still, except P3-D below. Receipt: `r427-probe3.log`. - Palette rows the committed sweep does not press (it takes one row per argument family): 42 rows at 1440 and 46 at 393. These are `canvas:view:*`, `canvas:open/rename/ctr/session:*`, split/new, canvas lock, `theme:*`, `bg:*`, `card:*`, `windows:*`, `settings:*`, `proto:later`. All still, except the font rows (P2-A). **Limit of this evidence:** probe 3 did not record whether Enter changed anything on each trigger. A "still" on a trigger that does nothing is weaker than a "still" on one that visibly opens. Card morphs, docs previews and sheets are therefore covered by the sweep, `keyboard-instant` and my earlier probes, not proved again here. I did not build a key path into docs previews or bottom sheets. ### 3. Pointer and touch still animate: confirmed Positive controls with the same watcher (`r427-probe.log`): - **Mouse, 1440x900:** the sidebar toggle animates (transform 62 to 295 ms, sidebar opacity and transform). The capsule view list animates (clip-path to 736 ms). The window pill animates (popover animation, lock fade). - **Touch, iPad-like context** (820x1180, `hasTouch`, `isMobile`, dpr 2, real `locator.tap()`): the same three all animate (sidebar toggle to 450 ms, view list clip-path to 782 ms, window pill transform to 368 ms). - The 393 mouse control timed out because the window pill is not clickable there. That is the same harness issue J413 reported, not a product result. ### 4. Interaction with `motion-fast-ease-out` (#471, bfdcddc60) A trial merge `git merge-tree` shows **6 conflicted files**: `wizard.ts`, `camera.ts`, `world.ts`, `lifecycle.ts`, `anchored-popover.ts` and `pill-morph.ts`. CSS merges clean. `wizard.ts`, `world.ts` and `pill-morph.ts` are doc-comment collisions only. The rest are real: - **`camera.ts`:** #427 deletes `KEY_FLIGHT` and `keyFlight()` (a key jumps), while #471 retunes them to 0.12 s. #427's side must win, and #471's `KEY_FLIGHT` constant must go. - **`lifecycle.ts` (`pulse`):** #471 shortens it to 240 ms, and #427 adds `if (triggeredByKeyboard()) return`. Both are needed. - **`anchored-popover.ts`:** #471 uses `EASE_OUT` for the reduced-motion fade, and #427 wraps it in `!triggeredByKeyboard()`. Both are needed. I ran the trial merge with `-X ours` (so #427 wins every conflict) on dev-02 and ran #471's unit tests: `motion-speed.test.ts` passes 20/21, `no-overshoot.test.ts` and `spring.test.ts` pass. The one failure is "no Web Animation lasts more than 300 ms or starts slowly". It names exactly the two lines where #427's side kept dev's old values: `lifecycle.ts:338` (360 ms) and `anchored-popover.ts:209` (`"ease"`). The correct resolution above fixes both. So the conflicts are small and the gate catches the wrong resolution. **A real interaction, not a conflict (P3-E below):** #471's scanner reads `--t-*` tokens, and #427's scaled transitions are written `calc(var(--t-pop) * var(--ui-motion-scale, 1))`. `words()` keeps a `calc(...)` as one word and `ms()` only accepts a literal or `var(--t-…)`, so the duration reads as 0. Every scaled transition (the palette, the toast, the regions, ...) silently leaves the 300 ms and ease-out checks. Today's token values are all ≤ 240 ms, so nothing is wrong yet. The guard just does not guard them. **Recommended order: #427 first, then #471.** - #427 has had six review rounds and a red/green guard, and the owner rule it enforces is absolute. - #427 deletes the camera key-flight constants that #471 retunes, so rebasing #471 onto it means dropping its `KEY_FLIGHT` hunk, which is the smaller change. - After #427, J428 rebases and resolves `pulse` and the popover fade as above. It also makes `ms()` unwrap `calc(<time> * var(--ui-motion-scale…))`, so the new scanner sees every scaled transition. - The reverse order works too, but #427 would then have to make that scanner fix and re-run its 15 minute sweep. ### 5. The `Mockup:` skip is justified All `Mockup:` rows (`proto:conn:*`, `proto:acct:s:*`, `proto:sessions-scene:*`, `proto:scene:*`, transparency) are made in `apps/tracer/src/canvas/mock/*`. They are test-double rows that run scripted scenes on their own timers (for example "play every state" runs one every 2.6 s), so a timer would be reported as a key response. The product rows they sit beside are swept normally. Caveat for the comment in the spec: the skip also removes `Mockup: account – …`, which opens real account panels in scripted states. Those panels are reached by "Your account" (swept) and by the account pill (probe 3, still). ### 6. `verify fast` red only on cargo-deny: confirmed `~/j370-fast2.log` on dev-02 starts with `c11a47562 Merge remote-tracking branch 'origin/dev'…`. Every row is PASS except `cargo-deny | FAIL (1)` (line 13599) and `privileged integration | SKIP`, and it ends with `VERIFY_EXIT=1`. The failure at line 12530 is `RUSTSEC-2026-0459` on `libsqlite3-sys 0.30.1` (`advisories FAILED, bans ok, licenses ok, sources ok`). `~/j370-denydev.log` shows the same on plain dev (`advisories FAILED`, `deny-dev-exit=1`). `git diff origin/dev...HEAD -- Cargo.lock` is empty. This is not the branch's. --- ### Findings **P2-A: a palette font row animates the Settings pane's tick, and the sweep passes anyway.** Pressing Enter on any of `Font: Geist Mono / JetBrains Mono / Commit Mono / Monaspace Neon` moves `svg.ui-sp-tick` (`button.ui-sp-row < div.ui-sp-list < div.ui-settings-pane`) with a CSS `opacity` and `transform` transition from about 55 ms to 150–205 ms after the key. Evidence is `r427-probe.log` (all four rows at both sizes), and `r427-probe2.log` identifies the element. Idle shows no SVG transitions. The committed sweep presses only one row of the `font` family, and that row passes. Whichever row it is, the guard misses the other four. - **Fix, defect:** the selected-row tick in the pane takes the tracker (`markMotion`/`triggeredByKeyboard`, as the other panes do) and drops its transition for a key. - **Fix, guard:** for argument families, press a row that changes state. For a family such as `font`, take the second row, or run a state-changing row. Then add the tick to the sweep or the exception list on purpose. - Severity: the tick is a small element inside a pane that may be behind the palette, so P2 for the guard and P3 for the product defect. I'd merge #427 and take this as a follow-up if the owner prefers. **P3-B: sweep property filter now fine, but the exception regexp can never match SVG elements.** `name()` prints an SVG element's `className` as `[object SVGAnimatedString]` (see the `svg.[object.SVGAnimatedString]` lines in `r427-probe.log`). `NOT_PRODUCT = /…|ui-conn-g/` therefore cannot match the connection indicator's `<g>` or `<svg>` children. Use `getAttribute("class")` in `name()`. At the moment the exception passes only because nothing reaches it. **P3-C: the palette-row probe types the label and presses Enter on the first result, and that is not always the row.** `theme:ink`'s label "Ink" returns `canvas:settings:machines.new-machines.add-link` first (`first=DIFFERENT:` in `r427-probe.log`). The sweep does not check that the first result is the row it meant, so a family sample can silently press another row. Assert `data-action` of the first result equals the wanted action (or press the row with ArrowDown), and fail otherwise. **P3-D: Enter on the "Sessions" window-action button fades the row's meta/actions (120 ms).** At 1440x900, with the cursor off the page, `span.cl-sx-meta` and `span.cl-sx-acts` run an opacity transition (`sessions.css:41,48,51-52`, `var(--t-press) ease`). Focus lands in a row of the popover that was just opened, so `:focus-within` swaps meta for actions. The popover itself is instant; this is the first-focus reveal of a row's actions. The sweep reaches this popover only through the palette row and the chord, which pass. The button path (Enter on the focused "Sessions" button) is the one that moves. It could use the same `data-motion` attribute the rest of the branch uses. The rest of the disclosure list (probe 3) is still, apart from this. **P3-E: #471's motion scanner cannot read the scaled transitions** (section 4). The fix belongs in `motion-speed.test.ts` (`ms()` unwrap) and goes with whichever branch merges second. **Noted, not findings:** - With the cursor parked over a view, a key that moves the camera (Escape to the overview, Settings Escape) brings up the hover summary `div.cl-summary` with a 180 ms scale-in (`r427-probe.log`, `crumb#0 Escape`). That is `pointerenter` on a stationary cursor, so hover, not a key response. - Hover tooltips appear when the cursor is parked under a moving element (the `div.tip` lines in `r427-probe2.log`). They disappear when the cursor is moved away (probe 3). - The input tracker's design is fine. A touch tap, wheel or pointerdown resets the source, so a hardware-keyboard iPad that types and then taps still animates the tap (touch control above). **Reviewed SHA: c11a475622682956592b7c98bbccd30f0e0c88cf.** Not merged, pushed or deployed. The dev-02 worktrees and my servers are removed.
Author
Owner

J370 follow-ups to #427 (J436, comment 88843). Branch kbd-instant-followups (from origin/dev c442500c2), head c818bde6d, pushed (git ls-remote agrees). Not merged.

Red first (2b50dea24 and the commits after it are the spec; ~/j370-f3red.log, ~/j370-f5red.log on dev-02, the final sweep against plain origin/dev):

  • P2-A: Enter on each of the nine palette Font rows moves svg.ui-sp-tick (opacity 9 → ~150 ms, transform 9 → ~205 ms). The sweep now presses every row of a family of ten or fewer (all fonts, card and window levels, backgrounds); only the themes take one row.
  • P3-D: Enter on the active window's Sessions button: transition opacity on span.cl-sx-meta and span.cl-sx-acts, 11 → 144 ms (1440x900; skipped at 393, where that button is not on the header).
  • P3-B: SVG elements are now named from their class attribute, so the finding names svg.ui-sp-tick and the exception pattern can match them.
  • P3-C: the palette sweep walks down to the row it typed and fails the row if the selection is not that action.
  • New, found by the sweep's 60 ms rule: opening Settings from the palette plays the breadcrumb name's 180 ms rise (span.cl-crumb-name, 624 → 700 ms).

Fixes: db2ec7452 the tick (paintChoice marks it; its transitions scale by --ui-motion-scale) and the first Sessions attempt; 3c1df0c04 the Sessions popover is marked before each focus() (a probe showed the browser updates styles inside focus(), before focusin, so the first attempt's mark came too late); 3776e3357 the crumb name.
Sweep changes: motion rule now "still seen 50 ms after the key and seen for ≥ 60 ms from its first frame" (catches a 120 ms fade); closes the scene's open popover before pressing Sessions.

Green (dev-02): at 3776e3357 (~/j370-f4.log) keyboard-sweep at 1440x900 and 393x852 (shortcuts, every palette row) and keyboard-instant: 38 passed, 1 skipped, 1 failed (the Sessions case, fixed by the next commit); at 3c1df0c04 (~/j370-f6.log) the Sessions case passes and typecheck passes. sessions-canvas.spec.ts has 2 failures (open duration [200], a toast test) that fail identically on plain origin/dev bd551e549 (~/j370-f7base.log).
verify quick at c818bde6d (~/j370-f8quick.log): "QUICK pre-check passed", exit=0.

J370 follow-ups to #427 (J436, comment 88843). Branch `kbd-instant-followups` (from origin/dev c442500c2), head **c818bde6d**, pushed (`git ls-remote` agrees). Not merged. **Red first** (`2b50dea24` and the commits after it are the spec; `~/j370-f3red.log`, `~/j370-f5red.log` on dev-02, the final sweep against plain origin/dev): - P2-A: Enter on each of the nine palette Font rows moves `svg.ui-sp-tick` (opacity 9 → ~150 ms, transform 9 → ~205 ms). The sweep now presses every row of a family of ten or fewer (all fonts, card and window levels, backgrounds); only the themes take one row. - P3-D: Enter on the active window's Sessions button: `transition opacity on span.cl-sx-meta` and `span.cl-sx-acts`, 11 → 144 ms (1440x900; skipped at 393, where that button is not on the header). - P3-B: SVG elements are now named from their class attribute, so the finding names `svg.ui-sp-tick` and the exception pattern can match them. - P3-C: the palette sweep walks down to the row it typed and fails the row if the selection is not that action. - New, found by the sweep's 60 ms rule: opening Settings from the palette plays the breadcrumb name's 180 ms rise (`span.cl-crumb-name`, 624 → 700 ms). **Fixes**: db2ec7452 the tick (`paintChoice` marks it; its transitions scale by `--ui-motion-scale`) and the first Sessions attempt; 3c1df0c04 the Sessions popover is marked before each `focus()` (a probe showed the browser updates styles inside `focus()`, before `focusin`, so the first attempt's mark came too late); 3776e3357 the crumb name. **Sweep changes**: motion rule now "still seen 50 ms after the key and seen for ≥ 60 ms from its first frame" (catches a 120 ms fade); closes the scene's open popover before pressing Sessions. **Green** (dev-02): at 3776e3357 (`~/j370-f4.log`) keyboard-sweep at 1440x900 and 393x852 (shortcuts, every palette row) and keyboard-instant: 38 passed, 1 skipped, 1 failed (the Sessions case, fixed by the next commit); at 3c1df0c04 (`~/j370-f6.log`) the Sessions case passes and typecheck passes. `sessions-canvas.spec.ts` has 2 failures (open duration `[200]`, a toast test) that fail identically on plain origin/dev bd551e549 (`~/j370-f7base.log`). **`verify quick`** at c818bde6d (`~/j370-f8quick.log`): "QUICK pre-check passed", exit=0.
Author
Owner

J451 fix-check of kbd-instant-followups @ c818bde6d (base origin/dev c442500c2): APPROVE, with three P3s below. Nothing merged, pushed or deployed. Runs were on dev-02, production build, through build-slot.

1. Full specs at the head (keyboard-sweep + keyboard-instant + sessions-canvas, 1440x900 and 393x852): 61 passed, 2 failed, 1 skipped (the Sessions-button case at 393x852: the button is not on that header, the spec's own skip).

  • Both sweep cases pass at both sizes: "no shortcut leaves anything animating" and "no palette row leaves anything animating after Enter" (all 8 font rows, 11.9 m / 12.2 m).
  • The Sessions-button Enter case passes at 1440x900. All keyboard-instant cases pass.
  • The 2 failures are the sessions-canvas pair in item 5.

2. Red run (head's keyboard-sweep.spec.ts copied onto origin/dev c442500c2): 5 failed, 24 passed, 1 skipped.

  • The 1440x900 and 393x852 palette-row cases fail naming all 8 font rows (font:geist-mono ... font:source-code-pro): transition opacity/transform on svg.ui-sp-tick.
  • The Sessions-button Enter case fails at 1440x900 with transition opacity on span.cl-sx-meta and span.cl-sx-acts, moving 11 ms to 144 ms.
  • The 2 sessions-canvas failures are the same pair as on the head.
  • Gap: no red names the breadcrumb. The sweep does not catch the 180 ms rise on dev, so the breadcrumb fix has no red-to-green evidence. The code is right by reading (see item 4), but it is unguarded. This is P3-a.

3. Pointer still animates (scratch spec at the head, 1440x900, not committed):

  • Click a font row: the tick plays. ui-sp-tick opacity moves 48 ms to 181 ms and transform 48 ms to 231 ms.
  • Click to open Sessions: the popover animation runs 41 ms to 240 ms. Hovering a row fades cl-sx-meta and cl-sx-acts (51 ms to 182 ms).
  • Open the popover by keyboard, then hover a row with the mouse: the fade returns (45 ms to 176 ms). The pointerover reset works.
  • Settings from the palette by click: the breadcrumb rise was not observed. The palette fade and the camera flight were, but no WAAPI animation on the crumb. I did not establish whether the now - lastMotion > 260 gate suppresses it (the flight starts at the same time) or the rise is broken. I could not click #capsule in this scene because .cl-noun covers it. The scratch spec therefore opened the palette by key and clicked the row by mouse. This is P3-a again, so the "rise must still play" check is unproven. I suggest a manual check on a production build, or a dedicated spec.

4. "Mark before focus()": robust enough.

  • markMotion(root) runs immediately before each of the two code-driven focus() calls (the keyboard open and the arrow handler). It writes data-motion from the last trusted input, so a programmatic focus (such as on reconnect) would reuse the last input's mark. That errs instant after a key and animate after a pointer. Both are acceptable under the policy documented in input-source.ts.
  • The pointerover reset keeps a mouse hover animated.
  • P3-b: Tab and Shift+Tab between rows are not marked (native focus moves). A pointer-opened popover keeps data-motion="animate", so Tab should fade cl-sx-meta and cl-sx-acts. My probe did not reproduce it: SESSIONS-TAB found: []. The probe pre-focused row 1 by focus() and the 400 ms wait may have settled things. So this is not confirmed. Marking in the popover's keydown would close it if it ever shows.

5. sessions-canvas reds: identical on dev and on the branch, and not motion regressions.

  • :125 "popover opens within 200 ms ...": expect(durations.open).toEqual([200]) fails. It is a CSS-duration assertion, not a branch effect. The received value was cut off in my log, so I can't say what the duration was.
  • :171 "close toast teaches once ...": TypeError: Cannot read properties of null (reading 'hidden') in page.evaluate. A missing element in the spec, nothing to do with motion.
  • Both predate the branch; they need their own issue. Neither is caused by these commits.

P3 summary:

  • P3-a: no red or green guard for the breadcrumb rise in either direction.
  • P3-b: Tab between popover rows is unmarked (unconfirmed).
  • P3-c: the two pre-existing sessions-canvas reds should be tracked separately.

Evidence: dev-02 ~/rv-head.log, ~/rv-red.log, ~/rv-ptr.log; scratch spec zz-pointer.spec.ts, removed with its worktree. The red run used worktree rv-red (removed). scripts/fj is missing from the checkout, so I posted with the fj binary.

**J451 fix-check of `kbd-instant-followups` @ c818bde6d (base origin/dev c442500c2): APPROVE**, with three P3s below. Nothing merged, pushed or deployed. Runs were on dev-02, production build, through build-slot. **1. Full specs at the head** (`keyboard-sweep` + `keyboard-instant` + `sessions-canvas`, 1440x900 and 393x852): 61 passed, 2 failed, 1 skipped (the Sessions-button case at 393x852: the button is not on that header, the spec's own skip). - Both sweep cases pass at both sizes: "no shortcut leaves anything animating" and "no palette row leaves anything animating after Enter" (all 8 font rows, 11.9 m / 12.2 m). - The Sessions-button Enter case passes at 1440x900. All `keyboard-instant` cases pass. - The 2 failures are the `sessions-canvas` pair in item 5. **2. Red run** (head's `keyboard-sweep.spec.ts` copied onto origin/dev c442500c2): 5 failed, 24 passed, 1 skipped. - The 1440x900 and 393x852 palette-row cases fail naming all 8 font rows (`font:geist-mono` ... `font:source-code-pro`): `transition opacity/transform on svg.ui-sp-tick`. - The Sessions-button Enter case fails at 1440x900 with `transition opacity on span.cl-sx-meta` and `span.cl-sx-acts`, moving 11 ms to 144 ms. - The 2 `sessions-canvas` failures are the same pair as on the head. - **Gap: no red names the breadcrumb.** The sweep does not catch the 180 ms rise on dev, so the breadcrumb fix has no red-to-green evidence. The code is right by reading (see item 4), but it is unguarded. This is P3-a. **3. Pointer still animates** (scratch spec at the head, 1440x900, not committed): - Click a font row: the tick plays. `ui-sp-tick` opacity moves 48 ms to 181 ms and transform 48 ms to 231 ms. - Click to open Sessions: the popover animation runs 41 ms to 240 ms. Hovering a row fades `cl-sx-meta` and `cl-sx-acts` (51 ms to 182 ms). - Open the popover by keyboard, then hover a row with the mouse: the fade returns (45 ms to 176 ms). The `pointerover` reset works. - Settings from the palette by click: **the breadcrumb rise was not observed.** The palette fade and the camera flight were, but no WAAPI animation on the crumb. I did not establish whether the `now - lastMotion > 260` gate suppresses it (the flight starts at the same time) or the rise is broken. I could not click `#capsule` in this scene because `.cl-noun` covers it. The scratch spec therefore opened the palette by key and clicked the row by mouse. This is P3-a again, so the "rise must still play" check is unproven. I suggest a manual check on a production build, or a dedicated spec. **4. "Mark before `focus()`": robust enough.** - `markMotion(root)` runs immediately before each of the two code-driven `focus()` calls (the keyboard open and the arrow handler). It writes `data-motion` from the last trusted input, so a programmatic focus (such as on reconnect) would reuse the last input's mark. That errs instant after a key and animate after a pointer. Both are acceptable under the policy documented in `input-source.ts`. - The `pointerover` reset keeps a mouse hover animated. - **P3-b: Tab and Shift+Tab between rows are not marked** (native focus moves). A pointer-opened popover keeps `data-motion="animate"`, so Tab should fade `cl-sx-meta` and `cl-sx-acts`. My probe did not reproduce it: `SESSIONS-TAB found: []`. The probe pre-focused row 1 by `focus()` and the 400 ms wait may have settled things. So this is **not confirmed**. Marking in the popover's `keydown` would close it if it ever shows. **5. `sessions-canvas` reds: identical on dev and on the branch, and not motion regressions.** - `:125` "popover opens within 200 ms ...": `expect(durations.open).toEqual([200])` fails. It is a CSS-duration assertion, not a branch effect. The received value was cut off in my log, so I can't say what the duration was. - `:171` "close toast teaches once ...": `TypeError: Cannot read properties of null (reading 'hidden')` in `page.evaluate`. A missing element in the spec, nothing to do with motion. - Both predate the branch; they need their own issue. Neither is caused by these commits. **P3 summary:** - P3-a: no red or green guard for the breadcrumb rise in either direction. - P3-b: Tab between popover rows is unmarked (unconfirmed). - P3-c: the two pre-existing `sessions-canvas` reds should be tracked separately. Evidence: dev-02 `~/rv-head.log`, `~/rv-red.log`, `~/rv-ptr.log`; scratch spec `zz-pointer.spec.ts`, removed with its worktree. The red run used worktree rv-red (removed). `scripts/fj` is missing from the checkout, so I posted with the `fj` binary.
Author
Owner

J370: merged origin/dev (themes sync, toast fix) into kbd-instant-followups. One conflict (settings-panels.ts import: dev dropped capsule-tabs, my markMotion import stays). Dev now lists 23 fonts, so the sweep always sweeps every font row (1639cc0c9). Head 1639cc0c9 (pushed). dev-02 results: first run at 19a383898: keyboard-instant, themes, clusters-theme-transition and the sweep's shortcut tests pass; terminal-theme.spec.ts:206 fails identically on plain origin/dev (/j370-m2base.log). Palette sweep at 1639cc0c9 passes at 1440x900 and 393x852 (/j370-m2.log). verify quick: passed, exit 0 (~/j370-m2quick.log). Not merged.

J370: merged origin/dev (themes sync, toast fix) into kbd-instant-followups. One conflict (settings-panels.ts import: dev dropped capsule-tabs, my markMotion import stays). Dev now lists 23 fonts, so the sweep always sweeps every font row (1639cc0c9). Head 1639cc0c9 (pushed). dev-02 results: first run at 19a383898: keyboard-instant, themes, clusters-theme-transition and the sweep's shortcut tests pass; terminal-theme.spec.ts:206 fails identically on plain origin/dev (~/j370-m2base.log). Palette sweep at 1639cc0c9 passes at 1440x900 and 393x852 (~/j370-m2.log). verify quick: passed, exit 0 (~/j370-m2quick.log). Not merged.
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#427
No description provided.