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/toast.md

43 lines
4.7 KiB

# Audit: toast
audit-version: 1
audited-at: 2026-06-26
scope: (SCOPE-DRIFT → SYS-1)
method: adversarially-verified workflow; HIGH lead-verified. B7 ground-truth: Toolbar A35 + Form A36 incidents STAYED FIXED (untrack present); toast/clipboard/drag-drop use uix.timers (no A6 leak); button is clean (its archetype finding was inert — Spinner not runtime-registered); data-size/data-shape are eidos visual attrs (not contract violations).
provider: src/uix/soma/components/toast/toast-provider.svelte.ts
cleanup-audit (A6/A35/A36): TIMERS:
- autoDismissTimer (line 216): scheduled via soma.uix.timers.schedule(); cleared by clearTimer() (line 355-358); cleanup hook via $effect (line 328-332) returns disposer calling clearTimer().
LISTENERS (Hotkey — ToastViewportProvider):
- keydown listener (line 148): cleanup via cleanupKeyd
## Summary
Counts (post-verification): CRITICAL 0 · HIGH 0 · MEDIUM 1 · LOW 0.
## Findings
### MEDIUM: SYS-1 (scope-drift) — toast-001 <!-- id: toast-001 -->
- dimension: A
- rule: SYS-1 (scope-drift)
- location: src/uix/morfo/components/toast.ts:8
- evidence: Morfo declares scope: ['soma', 'sema'], but eidos recipe exists at src/uix/eidos/components/toast/. Per framework rules, eidos must be in declared scope when recipe directory is present.
- impact: Scope mismatch signals incomplete contract coverage or undeclared visual layer coupling.
- proposed-fix: Add 'eidos' to morfo scope: scope: ['soma', 'sema', 'eidos']
- verify: [confirmed] CONFIRMED at src/uix/morfo/components/toast.ts:8 — `scope: ['soma', 'sema']` omits 'eidos' while a full eidos layer exists: recipe `toast: {` at src/uix/eidos/lib/recipes/base.ts:3545, plus src/uix/eidos/components/toast/ with toast.css (11466B), types.ts, and 9 part wrappers. The scope field per types.ts:799 declares 'Layers that implement this component', so omitting 'eidos' is genuine drift. Matches the SYS-1 baseline (MEDIUM). NOTE: confirmed SYSTEMIC — dialog/drawer/popover/toggle all likewise declare `scope: ['soma', 'sema']` while shipping eidos recipes+dirs; this is the documented split where some components (button/checkbox/accordion/context-menu) DO declare 'eidos'. Severity MEDIUM stands; it is a contract-coverage inconsistency, not a behavioral bug.
- fix-status: open
## No-findings dimensions
B (Behavior: A35/A36 loops clean — untrack present on present/announce trigger; A6 timer cleanup via .cancel() in clearTimer and $effect cleanup; A33 no $state(Map/Set); A31 no O(N²) derived patterns; A30 direct ID registration; A15 gesture properly uses setPointerCapture; A6/A34 hotkey listeners cleaned via this.cleanupHotkey in $effect), C (DOM: no unsafe querySelector interpolation; uses soma.dom.getDocument/getWindow), D (Frontier: soma does not import eidos; syncAttrs: true throughout; no double-write divergence), E (TSC: z-index uses --toast-toaster-z token; opacity literals 0/1 are canonical, no --opacity-* variants exist; focus-ring uses canonical tokens; spacing uses --space-* tokens), E-bis (Theming: only 9 roles used; all 6 intents correctly mapped to palettes; status is 'indicator' archetype, not interactive; no hardcoded :active or focus-ring), F (Tests: hotkey focus, timers with cancel, ARIA projection, promise lifecycle covered in separate test), G (Redundancy: no re-implemented gesture/registry/live-region)
## Theming facts (E-bis)
- magic z-index: none
- magic literals: closed-scale: 0.98 (defined as token)
- undeclared parts: none
- roles clean: true · variants clean: true
## Tests (F)
- exists: true · env: jsdom
- covers: Hotkey composite (AltLeft+F8) via listen cleanup; Per-item runtime ARIA projection (role, aria-live, aria-atomic, aria-labelledby, aria-describedby); Timer scheduling and cancel (pause/resume on pointerenter/leave); Dismiss action routing through runtime.trigger; Promise lifecycle (fulfill/threat paths with restartTimer); Max overflow routing through dismiss state
- untested: Swipe gesture end-to-end (pointerdown/move/up sequence with threshold); Title/Description ID registration flow (titleId/descriptionId state tracking); Escape key delegation to runtime.keydown; Loading spinner state transitions
## Style observations (non-blocking)
- Viewport uses fixed positioning with --toast-toaster-z (canonical z-scale). Item grid uses 3-column auto-collapse on status/close presence. Swipe state removes transition (none); cancel restores. Loading spinner uses animation: toast-spinner (pure CSS rotate). Intent palettes use color-mix for surface/border blends (90% overlay + intent-track). Focus ring on Action is canonical two-ring model. All spacing, font-size, color, radius route through tokens (no hardcoded px/rem literals except closed-offset: 10px, which is a designed constant). Transition durations and easing use canonical tokens.

Powered by TurnKey Linux.