# Audit: drag-drop 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/drag-drop/drag-drop-provider.svelte.ts cleanup-audit (A6/A35/A36): TIMERS/LISTENERS: 1. setupPointerDrag() — lines 261-294: - this.soma.dom.listen(win, 'pointermove', onPointerMove) → cleanupPointerMove [DISPOSED] - this.soma.dom.listen(win, 'pointerup', onPointerUp) → cleanupPointerUp [DISPOSED] - this.soma.dom.listen(win, 'pointercancel', onPoint ## Summary Counts (post-verification): CRITICAL 0 · HIGH 0 · MEDIUM 3 · LOW 0. ## Findings ### MEDIUM: SYS-1 SCOPE-DRIFT — drag-drop-001 - dimension: D - rule: SYS-1 SCOPE-DRIFT - location: src/uix/morfo/components/drag-drop.ts:7, src/uix/eidos/components/drag-drop/ - evidence: Morfo declares scope: ['soma', 'sema'] (line 7) but eidos component directory exists at src/uix/eidos/components/drag-drop/ with drag-drop.svelte, drag-drop.css, and sub-components (draggable, droppable, preview). The recipe dir exists but morfo scope omits 'eidos'. - impact: Framework scope validation may flag this as inconsistent; eidos layer is present but not declared in morfo scope. - proposed-fix: Update morfo scope declaration to scope: ['soma', 'sema', 'eidos'] to match the actual component hierarchy. - verify: [confirmed] CONFIRMED. morfo declares scope: ['soma', 'sema'] (drag-drop.ts:7) yet a full eidos implementation exists: recipe entry 'drag-drop': { 'preview-z': '99' } (recipes/base.ts:4259-4261), drag-drop.css, and 3 svelte wrappers (draggable/droppable/preview). The Morfo.scope doctrine (morfo/types.ts:798) reads 'Layers that implement this component' and 'eidos' is a valid Layer (schema.ts:49: union(literal('soma'), literal('sema'), literal('eidos'))). Omitting 'eidos' is genuine scope-drift = SYS-1 baseline MEDIUM. Note: many siblings (calendar/carousel/color-field/combobox/command) share the same drift, consistent with SYS-1 being systemic. - fix-status: fixed (212624e0) ### MEDIUM: SYS-2 MAGIC Z-INDEX — drag-drop-002 - dimension: B - rule: SYS-2 MAGIC Z-INDEX - location: src/uix/soma/components/drag-drop/drag-drop-provider.svelte.ts:656, 664 - evidence: DragPreviewProvider.style $derived.by inlines 'z-index': '9999' (lines 656, 664) as a hardcoded literal in the style object. The eidos CSS correctly uses var(--drag-drop-preview-z) (drag-drop.css:97) which resolves to 99 per generated base.css:3400, but the inline style hardcodes 9999 instead. - impact: z-index value is 9999 (extremely high, above all canonical layers) instead of 99 (--drag-drop-preview-z). This violates the canonical --z-index-* scale and may unexpectedly layer above modals (--z-index-modal: 700) and toast (--z-index-toast: 900). - proposed-fix: Replace hardcoded '9999' with 'var(--drag-drop-preview-z)' to consume the canonical token, e.g., 'z-index': 'var(--drag-drop-preview-z)'. - verify: [downgraded] DIVERGENCE CONFIRMED, severity DOWNGRADED HIGH->MEDIUM. DragPreviewProvider.style $derived.by hardcodes 'z-index': '9999' on both branches (drag-drop-provider.svelte.ts:656 and :664). The CSS rule [data-drag-drop-preview] uses z-index: var(--drag-drop-preview-z) (drag-drop.css:97) which resolves to 99 (generated/base.css:3400). Because the provider sets z-index inline via props.style, inline precedence wins, so effective z is 9999 and the recipe token is inert. 9999 is off the canonical scale entirely (--z-index-base 0 .. --z-index-toast 900; generated/base.css:304-311) and exceeds modal (700) and toast (900). This is a magic-literal that overrides its own dedicated token (SYS-2 magic-z), NOT a syncAttrs double-write (no data-attr involved). Per rubric magic-literal == MEDIUM and SYS-2 is baselined MEDIUM, so HIGH is unwarranted. - fix-status: open ### MEDIUM: Test coverage for high-risk paths — drag-drop-003 - dimension: F - rule: Test coverage for high-risk paths - location: src/uix/soma/components/drag-drop/drag-drop-provider.svelte.test.ts:126-306 - evidence: Test file contains 2 test cases: (1) keyboard drag start/navigation/commit, (2) drag prevention, accept filters, cancel, pointer preview. Missing explicit coverage for: pointer drag initiation and movement (setupPointerDrag), pending pointer-down state promotion (DraggableProvider.onpointerdown moveBuffer threshold), pointer listener cleanup on premature cancellation, and live-region disposal on component unmount. - impact: Pointer drag logic and pending state lifecycle are not directly tested; test env is jsdom-only. A/B testing (live-region dual-region toggle) is not verified. - proposed-fix: Add test case for pointer drag from pointerdown to pointermove threshold to active drag promotion; verify cleanup on pointercancel; add test for announcer timer cancellation on provider cleanup. - verify: [confirmed] CONFIRMED. Test file has exactly 2 it() cases (drag-drop-provider.svelte.test.ts:132 keyboard drag; :213 prevention/accept/cancel/preview). Both drive the flow by calling provider.startDrag(...) directly (e.g. lines 243-250, 256-264 with keyboard:false) rather than dispatching real pointerdown -> pointermove -> moveBuffer promotion (DraggableProvider.onpointerdown at :417-457, threshold Math.hypot < moveBuffer at :432). The setupPointerDrag window-listener flow (:261-294) and teardown/listener-cleanup (:245-257) are not exercised via real events. The preview-style assertion (:267-275 objectContaining) does NOT assert z-index, so the 9999 hardcode is also untested. jsdom-only env = SYS-3. MEDIUM stands. - fix-status: open ## No-findings dimensions A, C ## Theming facts (E-bis) - magic z-index: src/uix/soma/components/drag-drop/drag-drop-provider.svelte.ts:656,664 — hardcoded '9999' instead of var(--drag-drop-preview-z) - magic literals: none - undeclared parts: none - roles clean: true · variants clean: true ## Tests (F) - exists: true · env: jsdom - covers: keyboard drag start/activate via Space/Enter; keyboard navigation (ArrowDown/ArrowRight, ArrowUp/ArrowLeft, Tab/Shift+Tab); keyboard drop (Enter/Space on overTarget); keyboard cancel (Escape); onDragStart preventDefault block; accept filter evaluation; pointer drag cancel on invalid drop; pointer preview style positioning (fixed layout, offset from pointer) - untested: pointer drag initiation (onpointerdown → pending state); pointer drag promotion (moveBuffer threshold exceeded); pointer listener cleanup on premature cancel; keyboard listener cleanup/disposal; announcer timer cleanup on provider dispose; live-region dual-region A/B toggle announce pattern ## Style observations (non-blocking) - Draggable cursor transitions from grab → grabbing on :active, correct interaction feedback - Droppable uses inset box-shadow rings for accept/dragover states, consistent with design tokens - Preview uses --opacity-ghost (opacity-disabled), --space-* spacing, --radius-md, and role aliases correctly - Color cascade via data-color attribute in eidos component correctly routes to --_drag-drop-accent-soft and --_drag-drop-accent-solid variables