song grid rebuilds its whole subtree on every focus move — design proposal wanted before any code nf-enxz ← Beads

open priority 2 task unassigned

Twinned from https://github.com/cfsh/ninefold/issues/392 by tools/beads/import_github.py's reconcile pass.

⚠️ Aedan's explicit instruction: a design proposal / solution is wanted BEFORE any coding starts. Do not open an implementation PR against this issue. The first deliverable is a written proposal — on this issue or as a doc PR — that he can react to. Optimistic-PR-by-default does *not* apply here.

What the telemetry shows

Surfaced by the focus journal (#356/#362) across two real playtest sittings; not something Aedan reported by feel.

Sitting 1 — [playtest-logs/2026-08-02-e4facb9](https://github.com/cfsh/ninefold/tree/4e33831b4b2b37fd2667abf4c1b2094b1a6f940e/playtest-logs/2026-08-02-e4facb9), 1177 focus changes: 228 distinct @GridContainer@<id> node instances, with the instance id climbing ~100–150 per keypress.

Sitting 2 — [playtest-logs/2026-08-02-be9465e](https://github.com/cfsh/ninefold/tree/fd61bae852098806da4831769bb9aa0c3ef45f47/playtest-logs/2026-08-02-be9465e), 563 focus changes: 112 rebuild-triggered focus changes. After #362's trigger fix the pattern is legible directly in the log — every navigation step is a clean inputrebuild pair landing on a *different* node instance:

``` 06:26:35.095 input SongGrid …/@GridContainer@26423/@Button@26473 06:26:35.095 rebuild SongGrid …/@GridContainer@26563/@Button@26613 06:26:35.241 input SongGrid …/@GridContainer@26563/@Button@26622 06:26:35.242 rebuild SongGrid …/@GridContainer@26703/@Button@26762 ```

Reading: moving the focus one cell tears down the entire song-grid subtree and builds a new one, then re-grabs focus onto the corresponding button in the fresh tree.

Why it matters
  1. It is the substrate every focus race grew out of. Each rebuild orphans the node that had focus and forces a deferred re-grab. #385's audit counts 27 CallDeferred(GrabFocus) sites across 7 files running a frame later in an order nobody arbitrates. PR #335's down-from-play song bug was exactly one of these — the window stop gained focus and was re-grabbed off it 4 ms later. Those four bugs are fixed; the *conditions* that produced them are not.
  2. Cost scales with song size — allocation and layout for the whole grid on every d-pad press, on what is meant to be a cheap navigation gesture.
  3. It makes future focus work harder to reason about, because "where is focus" is only stable between rebuilds.

Nothing currently reported by Aedan depends on this. It is groundwork, and he wants it thought through rather than patched.

What the proposal should cover

Refs #385, #362, #356, #335

Dependencies

None.

Comments

No comments.

Add a comment