Stream Deck: size the Row-1 builder window from the placed keys (#1465) - #1468
Conversation
Root-cause the ambiguous-selection bug (window sized by a hardcoded 4, not by placed BuilderAction keys); derive window size + slot order from willAppear coordinates; retire the manual slot PI field (argued). Row 2/dials out of scope.
Fold in: window size = placed BuilderAction keys, not device capacity; other actions (e.g. Open Architect) never enter the count; SD+ 3-key example; per-visible-page.
Replace the hardcoded ROW1_WINDOW_SIZE=4 with a window sized to the visible BuilderAction keys: the store pages by a reported count, and SlotKey derives each key's slot by sorting placed keys on (row, column) from KeyAction.coordinates, skipping undefined-coord (multi-action) instances. Size is set synchronously on willAppear/willDisappear so a press resolves against the current layout; a debounced full re-render coalesces the page-load settle. Fixes a builder being selectable while shown on no key when fewer keys are placed than the fixed page of four.
…owing Drop the Slot selector from the Builder Action property inspector (slots are now derived from physical position) and update the help text. README Row-1 diagram shows 3 builders + Open Architect (the #1463 layout) and the prose explains the window sizes to the placed keys.
…nvariant, settle + paging Rewrite the windowing tests to drive via coordinates and placed-key count. Adds the core invariant (selected builder is always on a rendered slot for every cursor × size), paging-follows-placed-count, multi-action exclusion, debounced-settle re-render, and cursor-paging coherence when the window size changes under a selection.
Owner-directed: Row 1 slot 1 = Open Architect Terminal in Main mode (a fixed, selection-independent anchor), slots 2-4 = three Builder Action selectors. Update the ASCII diagram and the Row-1 bullet (stated in placed-keys terms, never a fixed 4) and drop the stale 'place Open Architect where a slot frees up' note. This layout is only correct once the window follows the placed keys, so it ships with the fix.
…· Bldr Term) Owner-directed: Row 2 slot 1 left free, slot 2 Approve Gate, slot 3 Open Architect Terminal in builder mode (the selected builder's owning architect — the per-builder complement to Row 1's main-mode anchor), slot 4 Open Builder Terminal. Updates the ASCII diagram and the Row-2 bullet.
Add the review file (retrospective, arch=none, lessons routed COLD) and a COLD UI/UX lesson: size a windowed list from the placed elements, not a constant; derive Stream Deck layout from the willAppear lifecycle since there is no profile API.
3-way consultation (codex + claude REQUEST_CHANGES, all real, doc/coverage): - README: rewrite the Open Architect Terminal placement caveat that still described Row 1 as a fixed page of four hiding every fourth builder (deferred to #1465) — the fixed-4 text this issue removes; now says the window self-sizes to the placed keys. - manifest.json: Builder Action tooltip no longer tells users to set the retired slot field; slots are the key's physical position. - Tests: add a (row, column) ordering case — 4 keys across 2 rows in reverse reading order resolve to reading-order slots. Dispositions recorded in the review file; PIR is single-pass, so these go to the human at the pr gate.
|
Integration review (streamdeck architect) — APPROVE. PIR runs a single consult pass, so the post-verdict fixes get no independent AI re-review; the builder asked for them to be checked here, and I have checked all three at source rather than accepting the disposition. The three fixes, verified:
Core-fix claims re-confirmed independently, since they are what the fixes sit on: the divide guard is real ( Non-blocking notes accepted without change (the settle constant, the missing Board: gemini APPROVE; codex and claude REQUEST_CHANGES, both on documentation and coverage rather than the core change, all now addressed. 213 tests, types, build and plugin validate all green after the fixes. |
PIR Review: Size the Row-1 builder window from the placed keys
Fixes #1465
Summary
The Stream Deck Row-1 fleet selector paged by a hardcoded
ROW1_WINDOW_SIZE = 4,independent of how many
BuilderActionkeys the user actually placed. With fewerplaced keys than four, a builder at index ≡ 3 (mod 4) rendered on no key while the
Select dial still walked the cursor onto it — so it became the selected builder,
drove Row 2 and both review dials, and showed no accent ring anywhere. This change
sizes the window to the number of visible
BuilderActionkeys and derives each key'sslot from its physical board position (
KeyAction.coordinates, sorted by row thencolumn), retiring the manual
slotProperty-Inspector field. The window now followsthe placed keys, so a builder can never be selected while shown on no key.
Files Changed
apps/streamdeck/src/store.ts— removed theROW1_WINDOW_SIZEconstant; the windowis sized by a reported placed-key count (
setBuilderWindowSize,max(1,·)guard).apps/streamdeck/src/actions.ts—SlotKeycaptures each key'scoordinates, sortsplaced keys by
(row, column)for slot order, reports the count, skips multi-action(undefined-coordinate) instances, and debounces a full re-render across the page-load
settle.
slotBuilder()andsettings.slotretired.apps/streamdeck/com.cluesmith.codev.sdPlugin/ui/builder-action.html— dropped theSlot selector (slots are now positional); kept the verb selector; rewrote help text.
apps/streamdeck/README.md— recommended SD+ layout now leads with an Open Architect(main-mode) anchor in Row 1 slot 1 + three Builder selectors; Row 2 revised to
free · Approve Gate · Open Architect (builder mode) · Open Builder Terminal; windowing
prose stated in placed-key terms.
apps/streamdeck/src/__tests__/actions.test.ts— windowing tests rewritten to drivevia coordinates + placed-key count, plus the selected-always-shown invariant, the
debounced settle, and cursor-paging coherence under a window-size change.
Commits
(plus builder-thread and porch bookkeeping commits.)
Test Results
In the worktree (
apps/streamdeck):npm run build✓,npm run check-types(tsc) ✓,npm test✓ (212 tests, incl. the rewritten dynamic-window suite),npm run validate✓. Porch's
build+testsgate checks also passed. The running plugin was verifiedon hardware at the
dev-approvalgate (3-key and 4-key Row-1 layouts).Architecture Updates
No arch changes. The fix is internal to the Stream Deck plugin's Row-1 windowing —
it changes no module boundary, wire contract, state store, or the four-tier resolver,
so nothing qualifies for
arch-critical.md(hot) orarch.md(cold).Lessons Learned Updates
Routed one COLD lesson to
codev/resources/lessons-learned.md(UI/UX), tagged[From #1465]: a UI "window onto a list" must size itself from the elements actuallyplaced, not a hardcoded page constant, or a selection can point at an element rendered
on no key; and — Stream-Deck-specific — with no profile-structure API the layout must be
derived from the lifecycle (
willAppearKeyAction.coordinates, excluding undefined-coordmulti-action instances), sorted by
(row, column), counted for the width, and debouncedacross the page-load settle. This is a plugin-narrow recipe, so COLD, not the hot tier.
Things to Look At During PR Review
on a rendered slot, for every cursor × window size 3 and 4" (
actions.test.ts). That isthe property the bug violated; it directly encodes the fix's guarantee.
willAppearsizes the window and renders thearriving key synchronously (so a press always resolves against the current layout), while
a debounced
renderAll(WINDOW_SETTLE_MS = 50) coalesces the page-load burst. A rarereload with a non-first builder already selected shows a ~50 ms transient face before the
settle corrects it — a deliberate trade to avoid flicker, called out here so it isn't read
as a bug. The debounce is on the full re-render, never on press resolution.
slotfield is user-visible. Anyslotvalue previously persistedon a key is now ignored (it sits unused in settings) and the key re-orders by physical
position. This was raised and confirmed by the reviewer at plan-approval; no migration is
needed, but it is the one behavior change a returning user could notice.
fourth builder under the old constant, so the README layout guidance and the code fix must
land together (owner-directed, folded into this lane rather than a separate PR).
How to Test Locally
For a reviewer pulling the branch (hardware):
Repro the fixed bug: place 3 Builder Action keys + an Open Architect key in the 4th
Row-1 slot, spawn 4+ builders, and rotate the Select dial onto the 4th builder — it now
renders on a key with the accent ring (before, it was selected but shown nowhere). Then
confirm a 4-key layout still pages/accents as before.
Unit only:
cd apps/streamdeck && npm test.