You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/audit/components/number-field.md

4.0 KiB

Audit: number-field

audit-version: 1 audited-at: 2026-06-26 scope: method: adversarially-verified workflow (analyze → refute); HIGH/CRITICAL lead-verified. Clean-check pseudo-findings dropped. B4 ground-truth: segmented date/time/color-field register inputId via $effect (SYS-A30-EFFECT, no loop); number-field is the direct-assignment reference. provider: G:\dev\svelte\vicen\src\uix\soma\components\number-field\number-field-provider.svelte.ts field-family (A13/A24-26/A30): A13: COMPLIANT - hidden input rendered when name prop exists (line 76-83 in components/number-field.svelte), carries value + name + disabled/required for form submission. A24: NOT APPLICABLE (no readonly-segments with undefined value scenario). A25: NOT APPLICABLE (not a range field). A26: NOT REQUIRED - number-field uses contenteditable-free architecture (native with oninput fil

Summary

Counts (post-verification): CRITICAL 0 · HIGH 0 · MEDIUM 0 · LOW 1.

Findings

LOW: A6 — every timer/listener/observer disposed; provider with no disposer that still owns a t — NF-1

  • dimension: B
  • rule: A6 — every timer/listener/observer disposed; provider with no disposer that still owns a timer source
  • location: src/uix/soma/components/number-field/number-field-provider.svelte.ts:653-666 (IncrementTrigger) and 719-731 (DecrementTrigger), repeater created in constructor; SpinPressRepeater.stop at :98-107
  • evidence: The trigger providers create this.repeater = createSpinPressRepeater(...) in their constructors and start keyed uix.timers (schedule/interval) on pointerdown. repeater.stop() is wired to onpointerup/leave/cancel/lostpointercapture (props at :694-697 / :760-763), but the provider class has no dispose() that calls repeater.stop(). If the component unmounts WHILE a press is mid-flight (delay or interval timer pending), nothing cancels the keyed timer; the pending interval would keep invoking () => this.provider.increment() until superseded. The interval task does re-check if (this.isDisabled()) (:85-88) which mitigates but does not unconditionally stop on unmount.
  • impact: Tiny-window latent timer survival: requires pointer held on a spin trigger AND simultaneous unmount. Realistically rare; the keyed scheduler is the shared uix.timers (disposed at UIX teardown) and the isDisabled re-check usually halts it. Not a user-visible leak in normal flows.
  • repro: Press-and-hold an increment trigger, then unmount the NumberField before releasing the pointer; observe whether the keyed interval task is cancelled.
  • proposed-fix: If a per-component disposer becomes available, call repeater.stop() from it; otherwise document that the keyed timers rely on the shared scheduler's lifecycle. No change required if the framework guarantees keyed-timer cleanup on dom dispose.
  • verify: [verifier-added] added by adversarial verify pass
  • fix-status: open

No-findings dimensions

A Contract(morfo), A30 id-wiring, A13 hidden-input, B Behavior(soma), B Keyboard routes vs APG, B A6 cleanup, C DOM-selector safety, D Frontier(soma/eidos isolation), E Theming tokens, E-bis Label font rule, F Tests coverage

Theming facts (E-bis)

  • magic z-index: none
  • magic literals: none
  • undeclared parts: none
  • roles clean: true · variants clean: true
  • label-font (one step below input?): CORRECT - field recipe defines label at 1-step-below control font via calc(control-font - 1-step); spin-field references --font-size-* tokens correctly (e.g., font-size-md: var(--font-size-md) per THEMING §5)

Tests (F)

  • exists: true · env: jsdom
  • covers: parseValue with locale separators + rounding + clamp on blur; keyboard arrow/page/home/end/enter routing vs APG spinbutton; increment/decrement trigger clicks and repeater hold-delay; scrubber movement buffer and pointer capture; RTL horizontal scrub inversion (E-W mirror); vertical scrub RTL-independence; field inheritance via FieldProvider context; locale resolution (prop → soma.langs.getLocale → en); direction resolution (prop → soma.prefs.getDir → ltr)
  • untested:

Powered by TurnKey Linux.