# 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: