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/time-field.md

6.6 KiB

Audit: time-field

audit-version: 1 audited-at: 2026-06-26 scope: (SCOPE-DRIFT → SYS-1) 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: src/uix/soma/components/time-field/time-field-provider.svelte.ts field-family (A13/A24-26/A30): { "A13_hidden_input": "Hidden input correctly carries name (from Input provider) and ISO value. Renders only when name is set. CORRECT.", "A24_readonly_without_value": "Readonly-without-value warning implemented via $effect (lines 233-247). Guards against spam with lastWarnedKey. CORRECT.", "A26_segmented_contenteditable": "onbeforeinput preventDefault present in sharedSegmentAttrs (line 334

Summary

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

Findings

MEDIUM: A30: child->parent id registration (inputId) must be DIRECT assignment in constructor, NEV — time-field-001

  • dimension: B
  • rule: A30: child->parent id registration (inputId) must be DIRECT assignment in constructor, NEVER $effect
  • location: src/uix/soma/components/time-field/time-field-provider.svelte.ts:531-535
  • evidence: $effect(() => { const field = this.provider.field; if (!field) return; field.inputId.current = opts.id.current; });
  • impact: $effect child->parent edge can loop when InputProvider reads from Time-field state and writes to parent Field state, causing page freeze on mount if Field's reactivity depends on inputId changes.
  • proposed-fix: Move the inputId assignment to the constructor, directly after line 524 (this.provider.setFieldNode). Use: if (this.provider.field) { this.provider.field.inputId.current = opts.id.current; }
  • verify: [downgraded] The write IS in a $effect — confirmed at time-field-provider.svelte.ts:531-535: $effect(() => { const field = this.provider.field; if (!field) return; field.inputId.current = opts.id.current; }). But the HIGH/page-freeze claim is NOT supported. (1) NO loop exists: grep shows field.inputId is ONLY written (line 534), never read by this Input provider; opts.id.current is a stable framework id, so the $effect cannot oscillate. The A30 freeze hazard needs a child->parent write feeding parent reactivity the child re-reads — that closed cycle is absent. (2) Systemic, not a time-field defect: date-field-provider.svelte.ts:932-936 and color-field-provider.svelte.ts:521-525 use the IDENTICAL $effect pattern. (3) Real but minor: the project's REFERENCE number-field-provider.svelte.ts:585-587 uses a plain constructor guard if (this.provider.field) { this.provider.field.inputId.current = opts.id.current; }, so direct constructor assignment IS the cleaner doctrine and the proposed fix is correct. Downgraded to MEDIUM — A30 doctrinal deviation (prefer direct assignment), no freeze.
  • fix-status: open

MEDIUM: SYS-1: eidos recipe directory exists but morfo 'scope' omits 'eidos' SCOPE-DRIFT — time-field-002

  • dimension: A
  • rule: SYS-1: eidos recipe directory exists but morfo 'scope' omits 'eidos' SCOPE-DRIFT
  • location: src/uix/morfo/components/time-field.ts:7
  • evidence: scope: ['soma', 'sema'] — but src/uix/eidos/components/time-field/ directory exists with CSS and components
  • impact: Scope declaration is incomplete. The eidos layer provides visual recipes, but morfo declares scope without 'eidos', causing contract validators to miss eidos-layer violations.
  • proposed-fix: Update line 7 to: scope: ['soma', 'sema', 'eidos'],
  • verify: [confirmed] Confirmed. time-field.ts:7 declares scope: ['soma', 'sema'] — omits 'eidos'. The eidos layer exists in full: Glob of src/uix/eidos/components/time-field/ returns time-field.css, time-field.svelte, time-field-input.svelte, time-field-hidden-input.svelte, time-field-segment.svelte, time-field-label.svelte, types.ts, index.ts, README.md. Classic SYS-1 scope-drift; contract validators won't reach the eidos layer. Fix: add 'eidos' to the scope array.
  • fix-status: fixed (212624e0)

LOW: Morfo declares Label part defaultElement='label' but soma renders
— time-field-003

  • dimension: A
  • rule: Morfo declares Label part defaultElement='label' but soma renders
  • location: src/uix/soma/components/time-field/components/time-field-label.svelte:35
  • evidence: Morfo line 152: defaultElement: 'label' | Soma line 35: <div {...mergedProps}>
  • impact: Contract element mismatch. Although functional (the div has onclick focus behavior), morfo specifies 'label' but soma deviates. The semantic
  • proposed-fix: Either: (1) Change morfo defaultElement to 'div' (since segmented input can't use
  • verify: [downgraded] Element mismatch is real but lower impact than MEDIUM. Confirmed: morfo time-field.ts:152 declares Label defaultElement: 'label'; time-field-label.svelte:35 renders <div {...mergedProps}>. Systemic — date-field is identical (date-field.ts:136 defaultElement: 'label' vs date-field-label.svelte:35 <div>), and color-field is the consistent counter-example (color-field.ts:69 'label' + color-field-label.svelte:35 <label>). defaultElement is advisory and the part archetype is 'label'; a real <label for> cannot target a composite spinbutton group, so the <div> is an intentional a11y choice (it carries onclick focus). Downgraded to LOW — element naming/contract drift, not a behavioral or a11y break. The candidate's two proposed options are both valid.
  • fix-status: open

No-findings dimensions

C, D, E, E-bis, F, G

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?): Label font-size correctly one typographic step below input. CSS line 101: calc(1em - (var(--font-size-md) - var(--font-size-sm))) resolves to sm when input is md. CORRECT.

Tests (F)

  • exists: true · env: jsdom
  • covers: validation (custom, min, max constraints); segment-fill commit logic (all segments required before value commits); 12-hour display sync (hour padding, dayPeriod conversion)
  • untested: keyboard routes (ArrowUp/Down, Backspace, ArrowLeft/Right segment nav); readonly-without-value warning emit (A24 warning spam guard); Field composition inputId registration (A30); onbeforeinput preventDefault behavior (A26); hidden input form submission with name propagation (A13)

Powered by TurnKey Linux.