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.
68 lines
8.6 KiB
68 lines
8.6 KiB
|
3 months ago
|
# 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 <!-- id: 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. Replace `endOfMonth(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 named `first-day-of-week`/`last-day-of-week`. HOWEVER the sibling `calendar` provider does the IDENTICAL thing: calendar-provider.svelte.ts:481-485 `if (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, and `keyboard[].action` is 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 <!-- id: 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 chain `handleDayKeydown` (provider lines 555-602): branches exist for horizNext/horizPrev, ArrowDown/Up, Home, End, PageUp, PageDown, Enter/Space — but NO `KEYS.ESCAPE` branch. There is also no other keydown handler on any part (the Day part's `onkeydown` at line 1172-1174 delegates solely to `handleDayKeydown`). The provider never fires `commit-reset` or `clearSelection()` in response to Escape. The declared `cancel-selection` action 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 <!-- id: 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 <!-- id: 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);` and `box-shadow: inset -2px 0 0 0 var(--_calendar-range-end-border);` use bare `2px` and `-2px` values 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-width` and 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 332 `box-shadow: inset -2px 0 0 0 var(--_calendar-range-end-border);` hardcode the `2px`/`-2px` endpoint-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-width` would 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
|