fix: the dust threshold field rejects input silently, snapping back with no explanation #233

Closed
opened 2026-08-11 15:16:57 +02:00 by clawbot · 2 comments
Collaborator

src/popup/views/settings.js validates the dust-threshold input strictly and, on rejection, resyncs the field to the stored value without saying anything. The user types a value, the box silently changes to a different number, and nothing explains why.

It is the only validated input in that file that rejects without a message. The correct idiom is already in the same file — showFlash("Please enter an RPC URL.") at lines 198 and 243 — and the README Language & Labeling rule requires error messages to be full sentences.

Sharper now than before: the stricter parse landed in #179 newly rejects two inputs that previously worked. parseInt used to accept 1.5 as 1 and 100 gwei as 100; both are now rejected. So the inputs most likely to trigger the silent snap-back are exactly the ones a user had been getting away with.

Found by the independent review of #228, which flagged it rather than blocking since no documented rule is outright broken.

Also worth covering while in here, noted by the same review: Number(raw) now accepts notation the old parseInt(raw, 10) did not — 0x10 becomes 16 and 1e3 becomes 1000. Harmless, but wider than intended and undocumented.

Definition of done

  • Rejected input shows a full-sentence message explaining what is required, alongside the existing resync.
  • The message names the constraint (a whole number of gwei, zero or greater) rather than only reporting failure.
  • Hex and exponent notation are either accepted deliberately and documented, or rejected with the same message.
  • No layout shift when the message appears, per the README policy.
  • TODO.md updated in the same commit.
  • make check passes.
`src/popup/views/settings.js` validates the dust-threshold input strictly and, on rejection, resyncs the field to the stored value without saying anything. The user types a value, the box silently changes to a different number, and nothing explains why. It is the only validated input in that file that rejects without a message. The correct idiom is already in the same file — `showFlash("Please enter an RPC URL.")` at lines 198 and 243 — and the README Language & Labeling rule requires error messages to be full sentences. Sharper now than before: the stricter parse landed in https://git.eeqj.de/sneak/AutistMask/issues/179 newly rejects two inputs that previously worked. `parseInt` used to accept `1.5` as 1 and `100 gwei` as 100; both are now rejected. So the inputs most likely to trigger the silent snap-back are exactly the ones a user had been getting away with. Found by the independent review of https://git.eeqj.de/sneak/AutistMask/pulls/228, which flagged it rather than blocking since no documented rule is outright broken. Also worth covering while in here, noted by the same review: `Number(raw)` now accepts notation the old `parseInt(raw, 10)` did not — `0x10` becomes 16 and `1e3` becomes 1000. Harmless, but wider than intended and undocumented. ## Definition of done - [ ] Rejected input shows a full-sentence message explaining what is required, alongside the existing resync. - [ ] The message names the constraint (a whole number of gwei, zero or greater) rather than only reporting failure. - [ ] Hex and exponent notation are either accepted deliberately and documented, or rejected with the same message. - [ ] No layout shift when the message appears, per the README policy. - [ ] `TODO.md` updated in the same commit. - [ ] `make check` passes.
clawbot added this to the 1.0.0 milestone 2026-08-11 15:16:57 +02:00
Author
Collaborator

Plan:

  • Move the parse into a pure popup module (src/popup/dustThreshold.js) alongside restorableViews.js, exporting the parse and the single rejection message, so both are unit-testable without a DOM.
  • Reject hex and exponent notation rather than accept it: the field is inputmode="numeric" and the message promises "a whole number of gwei", so accepting 0x10 as 16 or 1e3 as 1000 would be a second silent transformation of what the user typed — the same surprise this issue is about. Accepted input is plain decimal digits only; everything else ("", -1, 1.5, 100 gwei, 0x10, 1e3) is rejected with one message.
  • On rejection, showFlash("Please enter the dust threshold as a whole number of gwei, zero or greater.") plus the existing resync. No layout shift: #flash-msg is an always-present line with min-h-[1.25rem].
  • README: document the accepted format in the Settings screen element list.
Plan: - Move the parse into a pure popup module (`src/popup/dustThreshold.js`) alongside `restorableViews.js`, exporting the parse and the single rejection message, so both are unit-testable without a DOM. - Reject hex and exponent notation rather than accept it: the field is `inputmode="numeric"` and the message promises "a whole number of gwei", so accepting `0x10` as 16 or `1e3` as 1000 would be a second silent transformation of what the user typed — the same surprise this issue is about. Accepted input is plain decimal digits only; everything else (`""`, `-1`, `1.5`, `100 gwei`, `0x10`, `1e3`) is rejected with one message. - On rejection, `showFlash("Please enter the dust threshold as a whole number of gwei, zero or greater.")` plus the existing resync. No layout shift: `#flash-msg` is an always-present line with `min-h-[1.25rem]`. - README: document the accepted format in the Settings screen element list.
Author
Collaborator

Built in #243.

Rejection now flashes Please enter the dust threshold as a whole number of gwei, zero or greater. and the resync stays. Hex and exponent notation are rejected with that same message rather than accepted: they would store a number the user never typed, which is the silent substitution this issue is about. Accepted input is plain decimal digits, zero or greater. Documented in the Settings element list in README.md.

Verified: make check green on the host and uncached in the container (script/cibuild, docker build --no-cache on this image only) — 15 suites, 379 tests, prettier clean. tests/dustThreshold.test.js adds 18 tests; reverting only the showFlash call fails exactly the two message tests, so they are not vacuous.

Built in https://git.eeqj.de/sneak/AutistMask/pulls/243. Rejection now flashes `Please enter the dust threshold as a whole number of gwei, zero or greater.` and the resync stays. Hex and exponent notation are rejected with that same message rather than accepted: they would store a number the user never typed, which is the silent substitution this issue is about. Accepted input is plain decimal digits, zero or greater. Documented in the Settings element list in `README.md`. Verified: `make check` green on the host and uncached in the container (`script/cibuild`, `docker build --no-cache` on this image only) — 15 suites, 379 tests, prettier clean. `tests/dustThreshold.test.js` adds 18 tests; reverting only the `showFlash` call fails exactly the two message tests, so they are not vacuous.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/AutistMask#233