Search swallows chars sometimes #167

Closed
opened 2026-08-26 20:41:22 +00:00 by Grandiras · 2 comments
Owner

Type bla, the l often gets swallowed

Type bla, the l often gets swallowed
Collaborator

🤖 On it — follow along in the action run; the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the task runs.

🤖 On it — follow along in [the action run](https://git.grandiras.net/Grandiras/claude-bot/actions/runs/394); the reply will link the full session transcript. <!-- claude-run:comment --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/issue-167/run-394.md) — updating every ~5 minutes while the task runs.
Collaborator

Fixed on branch claude/issue-167.

What was wrong

The search field rendered value="@_query" beside an @oninput handler. Only @bind emits RenderTreeBuilder.SetUpdatesAttributeName("value") — the call that tells Blazor the element already holds whatever the keystroke it is handling said it held. Without it, every keystroke renders a value the diff has never seen and sends it back down as a DOM patch. The patch for b lands after you've typed l, resets the field to b, and the next letter makes ba.

That's why it never showed up in development: on localhost the round trip is a millisecond, so the echo always arrives between two keystrokes with nothing to overwrite. It only bites over a real network.

Verified, not guessed

I ran the app against a local Postgres and drove it with Playwright through a browser context whose circuit delivers the server's messages 120 ms late. Before the fix: bla → ba, 8 runs out of 8. After: 0 out of 20 across three latency/typing-speed combinations.

Before and after: the same three keystrokes over the same lagged circuit

The fix

  • SearchBox.razor → @bind + @bind:event="oninput" + @bind:after="OnQueryChanged". Nothing else about the box changed.
  • The same pattern was in the rename field of all three admin lists — boards, columns and tags. I measured those too: bla → ba on /admin/boards and /admin/columns before, correct after. They bind through a dictionary, so @bind:get/@bind:set. I fixed them in the same commit rather than filing them separately: it's one defect with one one-line remedy, and leaving three known-broken fields behind would have been worse than the small widening of scope.

Also in the commit

  • A regression test in tests/e2e/search.spec.js with a fixture of its own (a lagged circuit — the only condition the bug exists under). Confirmed it fails with Expected "bla" / Received "ba" on the unfixed tree.
  • A changelog bullet in both locales, for renaming only — the search box itself is still unreleased, so no member ever saw that half broken.
  • docs/live-updates.md gains a section on what a re-render does to a field somebody is typing into, docs/search.md a note on the box, and CLAUDE.md the one-line rule, since this is a trap that will otherwise be walked into again.

Verification run

dotnet build and dotnet test clean (448 passed). E2E search.spec.js (3 tests, including the new one) and filter.spec.js pass against the running app. One pre-existing failure in boards.spec.js:165 ("the nav switches boards…") — it's an entrance-animation opacity gate, and it fails identically on an unmodified tree here, which is the prerender race docs/testing.md already documents for exactly this kind of environment. Not caused by this change and not filed, since testing.md says so already.

One gap worth naming: the admin rename fields have no e2e coverage at all, before or after. The mechanism is pinned once in search.spec.js and I verified the rename fields by hand rather than building three more board fixtures to re-assert one rule.


⏱ 27m 27s · 114 turns · tokens: 134.5k in / 55.6k out (+11133.2k cache-read)
📜 Full session transcript
→ Opened PR: #169

Fixed on branch `claude/issue-167`. ## What was wrong The search field rendered `value="@_query"` beside an `@oninput` handler. Only `@bind` emits `RenderTreeBuilder.SetUpdatesAttributeName("value")` — the call that tells Blazor the element already holds whatever the keystroke it is handling said it held. Without it, **every keystroke renders a `value` the diff has never seen and sends it back down as a DOM patch**. The patch for `b` lands after you've typed `l`, resets the field to `b`, and the next letter makes `ba`. That's why it never showed up in development: on localhost the round trip is a millisecond, so the echo always arrives between two keystrokes with nothing to overwrite. It only bites over a real network. ## Verified, not guessed I ran the app against a local Postgres and drove it with Playwright through a browser context whose circuit delivers the server's messages 120 ms late. Before the fix: `bla` → `ba`, 8 runs out of 8. After: 0 out of 20 across three latency/typing-speed combinations. ![Before and after: the same three keystrokes over the same lagged circuit](https://git.grandiras.net/attachments/9503dd92-b87a-4021-919d-674c02c1bd9f) ## The fix - `SearchBox.razor` → `@bind` + `@bind:event="oninput"` + `@bind:after="OnQueryChanged"`. Nothing else about the box changed. - **The same pattern was in the rename field of all three admin lists** — boards, columns and tags. I measured those too: `bla` → `ba` on `/admin/boards` and `/admin/columns` before, correct after. They bind through a dictionary, so `@bind:get`/`@bind:set`. I fixed them in the same commit rather than filing them separately: it's one defect with one one-line remedy, and leaving three known-broken fields behind would have been worse than the small widening of scope. ## Also in the commit - A regression test in `tests/e2e/search.spec.js` with a fixture of its own (a lagged circuit — the only condition the bug exists under). Confirmed it fails with `Expected "bla" / Received "ba"` on the unfixed tree. - A changelog bullet in both locales, for renaming only — the search box itself is still unreleased, so no member ever saw that half broken. - `docs/live-updates.md` gains a section on what a re-render does to a field somebody is typing into, `docs/search.md` a note on the box, and `CLAUDE.md` the one-line rule, since this is a trap that will otherwise be walked into again. ## Verification run `dotnet build` and `dotnet test` clean (448 passed). E2E `search.spec.js` (3 tests, including the new one) and `filter.spec.js` pass against the running app. One pre-existing failure in `boards.spec.js:165` ("the nav switches boards…") — it's an entrance-animation opacity gate, and it fails identically on an unmodified tree here, which is the prerender race `docs/testing.md` already documents for exactly this kind of environment. Not caused by this change and not filed, since testing.md says so already. One gap worth naming: the admin rename fields have no e2e coverage at all, before or after. The mechanism is pinned once in `search.spec.js` and I verified the rename fields by hand rather than building three more board fixtures to re-assert one rule. --- ⏱ 27m 27s · 114 turns · tokens: 134.5k in / 55.6k out (+11133.2k cache-read) 📜 [Full session transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/issue-167/run-394.md) → Opened PR: https://git.grandiras.net/Grandiras/Ponente/pulls/169
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
2 participants
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
Grandiras/Ponente#167
No description provided.