8.6 KiB
Audit: range-calendar
audit-version: 1 audited-at: 2026-06-26 scope: (SCOPE-DRIFT → SYS-1) method: adversarially-verified workflow (analyze → refute); HIGH/CRITICAL personally re-verified by the lead. Batch-3 ground-truth: each picker fires trigger(close) (close NOT inert), but open/commit-reset ARE inert; calendar/range-calendar are MID-REFACTOR (uncommitted view-switch work). provider: src/uix/soma/components/range-calendar/range-calendar-provider.svelte.ts
MID-REFACTOR CAVEAT: this component has uncommitted view-switch changes on this branch (M/D/?? files). Findings reflect the current in-flight state; treat structural inconsistencies as in-progress, not shipped defects.
Summary
Counts (post-verification): CRITICAL 0 · HIGH 0 · MEDIUM 2 · LOW 2. systemic hits: SYS-1 scope-drift (morfo includes eidos but no recipe entry).
Findings
MEDIUM: Keyboard route must match morfo contract; Home/End APG grid pattern — range-calendar-002
- dimension: B: Behavior (soma)
- rule: Keyboard route must match morfo contract; Home/End APG grid pattern
- location: src/uix/soma/components/range-calendar/range-calendar-provider.svelte.ts:566-567
- evidence: Morfo declares (line 88-89):
{ key: 'Home', action: 'first-day-of-week' }, { key: 'End', action: 'last-day-of-week' }. Provider implements (lines 566-567):else if (e.key === KEYS.HOME) target = startOfMonth(date); else if (e.key === KEYS.END) target = endOfMonth(date);. This is month-based, not week-based. - impact: User pressing Home/End navigates to first/last day of month instead of first/last day of current week, violating both the morfo contract and APG grid pattern expectations.
- proposed-fix: Replace
startOfMonth(date)with calculation to first day of current week using weekStartsOnResolved. ReplaceendOfMonth(date)with calculation to last day of current week. - verify: [downgraded] Behavior is real but NOT a range-calendar-specific HIGH. Provider lines 566-567:
else if (e.key === KEYS.HOME) target = startOfMonth(date); else if (e.key === KEYS.END) target = endOfMonth(date);— month-based, while morfo lines 88-89 declare actions namedfirst-day-of-week/last-day-of-week. HOWEVER the siblingcalendarprovider does the IDENTICAL thing: calendar-provider.svelte.ts:481-485if (e.key === KEYS.HOME) { const monthStart = startOfMonth(date); ... } else if (e.key === KEYS.END) { target = endOfMonth(date); }, against the IDENTICAL morfo declaration calendar.ts:76-77 ({ key: 'Home', action: 'first-day-of-week' }, { key: 'End', action: 'last-day-of-week' }). This is a family-wide naming/implementation divergence, andkeyboard[].actionis advisory morfo metadata (not enforced by the runtime — the provider's keydown handler owns the actual behavior). Not a latent bug introduced here, not a contract-validator catch; at most a LOW doc/naming inconsistency that should be raised against the whole calendar family, not range-calendar alone. - fix-status: open
MEDIUM: INERT EVENTS: declared keyboard action never fired via runtime.trigger — range-calendar-003
- dimension: A: Contract (morfo)
- rule: INERT EVENTS: declared keyboard action never fired via runtime.trigger
- location: src/uix/morfo/components/range-calendar.ts:92
- evidence: Morfo declares (line 92):
{ key: 'Escape', action: 'cancel-selection' }. Grep for 'Escape' or 'escape' or 'KEYS.ESCAPE' in range-calendar provider + components returns no matches. No runtime.trigger fires this action. - impact: User pressing Escape expects to cancel/clear the range selection, but nothing happens. Event is declared only to satisfy schema validation.
- proposed-fix: Implement Escape key handling in handleDayKeydown to call clearSelection(). Or remove Escape from morfo if not intended.
- verify: [confirmed] CONFIRMED as MEDIUM. Morfo line 92 declares
{ key: 'Escape', action: 'cancel-selection' }. I read the entire keydown chainhandleDayKeydown(provider lines 555-602): branches exist for horizNext/horizPrev, ArrowDown/Up, Home, End, PageUp, PageDown, Enter/Space — but NOKEYS.ESCAPEbranch. There is also no other keydown handler on any part (the Day part'sonkeydownat line 1172-1174 delegates solely tohandleDayKeydown). The provider never firescommit-resetorclearSelection()in response to Escape. The declaredcancel-selectionaction is therefore inert — it exists only to satisfy the morfo schema, exactly the INERT-events class the rubric calls out. Severity MEDIUM is appropriate: a user mid-selection who presses Escape expecting to abandon the partial range gets nothing, but it is not an a11y/data-corruption break. - fix-status: open
LOW: querySelector with interpolated user/runtime value must use CSS.escape — range-calendar-004
- dimension: C: DOM-selector
- rule: querySelector with interpolated user/runtime value must use CSS.escape
- location: src/uix/soma/components/range-calendar/range-calendar-provider.svelte.ts:595-596
- evidence: Code:
const el = root.querySelector<HTMLElement>([data-range-calendar-day][data-value="${target!.toString()}"]);The value is interpolated without CSS.escape. While ISO date format (2026-05-15) currently has no special CSS characters, the pattern is unsafe and violates defensive coding. - impact: If date format changes or special characters are introduced (e.g., localized formats), the selector could fail to find the element or select an unintended element. Violates the untrusted selector safety pattern.
- proposed-fix: Use
CSS.escape(target!.toString())to safely escape the interpolated value. - verify: [downgraded] DOWNGRADED. Provider lines 595-596:
root.querySelector<HTMLElement>([data-range-calendar-day][data-value="{target!.toString()}"]`)`. The interpolated value is `DateValue.toString()` — an ISO `YYYY-MM-DD` string produced by `$libs/days`, a FRAMEWORK-controlled value, never user/consumer-derived input. ISO dates contain only digits and a hyphen — no CSS-special characters can ever appear, so CSS.escape changes nothing functionally. Dimension C targets interpolation of *user/consumer-derived* values; a synthesized date string is neither. The same pattern is used family-wide (calendar-provider.svelte.ts:514-516 `[data-calendar-day][data-value="{target!.toString()}"]`). Defensible-hardening LOW at most, not a HIGH untrusted-selector defect. - fix-status: open
LOW: MAGIC NUMBERS: bare pixel/em/rem values should use canonical spacing tokens — range-calendar-005
- dimension: E-bis: Theming (recipe + css)
- rule: MAGIC NUMBERS: bare pixel/em/rem values should use canonical spacing tokens
- location: src/uix/eidos/components/range-calendar/range-calendar.css:323,332
- evidence: Lines 323, 332:
box-shadow: inset 2px 0 0 0 var(--_calendar-range-start-border);andbox-shadow: inset -2px 0 0 0 var(--_calendar-range-end-border);use bare2pxand-2pxvalues instead of canonical border or spacing tokens. - impact: Spacing is hardcoded, not themable. Breaks token consistency and makes the stripe width inflexible for different design systems.
- proposed-fix: Define a token like
--_calendar-range-endpoint-stripe-widthand reference it instead:box-shadow: inset calc(var(--_calendar-range-endpoint-stripe-width, 2px) * 1) 0 0 0 ... - verify: [confirmed] CONFIRMED as LOW (severity already correct). range-calendar.css line 323
box-shadow: inset 2px 0 0 0 var(--_calendar-range-start-border);and line 332box-shadow: inset -2px 0 0 0 var(--_calendar-range-end-border);hardcode the2px/-2pxendpoint-stripe width as bare literals rather than a recipe/border token. Genuine bare-literal drift, lowest severity — it is a decorative 2px hairline accent stripe on the range start/end endpoints, not a layout-load-bearing or themed dimension. Tokenizing as--_calendar-range-endpoint-stripe-widthwould be the canonical fix but the impact is purely cosmetic theme-flexibility. - fix-status: open
No-findings dimensions
D: Frontier (soma/eidos isolation), F: Tests (test coverage for existing keyboard/focus paths)
Theming facts (E-bis)
- magic z-index: none
- magic literals: 2px inset stripe width (lines 323, 332)
- undeclared parts: none
- roles clean: true · variants clean: true
Tests (F)
- exists: true · env: jsdom
- covers: initial placeholder resolution; range selection ordering; range length bounds enforcement; partial range clearing; endpoint amendment; transient anchor resilience; placeholder auto-page prevention; validation bounds checking
- untested: keyboard navigation (Home/End/arrows); Escape key; roving focus management; two-moments event sequencing