# Audit: splitter audit-version: 1 audited-at: 2026-06-26 scope: soma, sema, eidos method: adversarially-verified workflow; HIGH lead-verified. B6 ground-truth: checkbox/toggle/switch commit-toggle = sequence post (lag-fixed); radio-group/tabs/accordion/stepper set state at call-site (pre OK); toggle-group + checkbox-group carry the A31 .includes pattern (SYS-7); slider has no gesture-layer A6 leak. provider: src/uix/soma/components/splitter/splitter-provider.svelte.ts sequence-audit: N/A - splitter is not a state-binding control (Checkbox/Toggle/Switch/RadioGroup pattern). It is a direct-manipulation drag component with `commit-set: 'post'` morfo sequence. State changes (panel sizes) are set BEFORE pointerup closes the drag; notifyResizeEnd() routes through runtime.trigger('comm ## Summary Counts (post-verification): CRITICAL 0 · HIGH 0 · MEDIUM 1 · LOW 1. ## Findings ### MEDIUM: Frontier: eidos CSS must reference morfo event names exactly; mismatch breaks sema pulse v — splitter-001 - dimension: Frontier, Data-selector - rule: Frontier: eidos CSS must reference morfo event names exactly; mismatch breaks sema pulse visual feedback - location: src/uix/eidos/components/splitter/splitter.css:115; src/uix/morfo/components/splitter.ts:51,15 - evidence: CSS selector `[data-event='commit-resize']` but morfo defines event `name: 'commit-set'`. Morfo comment at line 15 confirms eidos recipe should reference `data-event='commit-set'` — CSS uses wrong event name. - impact: Sema event pulse visual feedback (handle background flash on drag commit) never fires because eidos events layer stamps `data-event='commit-set'` but CSS selector waits for `commit-resize`. User gets no visual confirmation of resize completion. - repro: Drag a splitter resize trigger; release. Observe no background pulse on the handle. - proposed-fix: Change line 115 of splitter.css from `[data-splitter-resize-trigger][data-event='commit-resize']` to `[data-splitter-resize-trigger][data-event='commit-set']` - verify: [confirmed] CONFIRMED that the pulse never fires, but the cited rule/fix is partially wrong and severity is overstated. splitter.css:115 selects `[data-splitter-resize-trigger][data-event='commit-resize'][data-event-phase='active']`; the morfo (splitter.ts:51) names the event `commit-set` and the provider triggers `runtime.trigger('commit-set')` (provider lines 205, 97). The sema engine stamps `data-event='commit-set'`, so the `commit-resize` selector never matches — the comment at css:110-111 and css:5 are stale relics of the old event name. HOWEVER the proposed fix (rename `commit-resize`→`commit-set`) is INSUFFICIENT: the morfo (splitter.ts:55) and sema cascade (sema/components/splitter.ts:49 `onProvider({ eventName: 'commit-set' })`) target `v.partRef('provider')`, so the stamp lands on the provider element `[data-splitter]`, NOT on `[data-splitter-resize-trigger]`. The selector targets both the wrong event name AND the wrong element. To actually pulse the handle on commit the selector would need to key off the provider stamp (e.g. `[data-splitter][data-event='commit-set'] [data-splitter-resize-trigger]`) or the commit event's target would need to move. Severity downgraded HIGH→MEDIUM: this is a purely cosmetic/ornamental sema-pulse that never fired (no functional or a11y impact); the handle still gets its `[data-state='dragging']`/`[data-dragging]` active background via css:77-80 during the actual drag, so the user is not left without resize feedback. - fix-status: open ### LOW: Theming: opacity literals forbidden; must use canonical --opacity-* token or fallback to - — splitter-003 - dimension: Theming, Tokens - rule: Theming: opacity literals forbidden; must use canonical --opacity-* token or fallback to --opacity-disabled. - location: src/uix/eidos/components/splitter/splitter.css:35 - evidence: Line 35: `opacity: var(--splitter-disabled-opacity, 0.6);`. Recipe base (recipes/base.ts) defines splitter['disabled-opacity'] = 'var(--opacity-disabled)', which is the canonical fallback. CSS uses magic literal 0.6 instead. - impact: Opacity value diverges from canonical token scale. If --opacity-disabled is updated system-wide, splitter disabled state won't reflect the change because it has a hardcoded literal fallback. - proposed-fix: Change line 35 to `opacity: var(--splitter-disabled-opacity, var(--opacity-disabled));` so the fallback chains to the canonical token instead of a magic literal. - verify: [downgraded] CONFIRMED as token drift but DOWNGRADED MEDIUM→LOW. splitter.css:35 `opacity: var(--splitter-disabled-opacity, 0.6)` uses a magic opacity decimal `0.6` as the inline fallback, violating E-bis (opacity decimal → `--opacity-*`). Verified recipes/base.ts:4231 defines `splitter['disabled-opacity'] = 'var(--opacity-disabled)'`, so under normal operation the recipe value wins and the `0.6` literal NEVER surfaces — it only appears if the recipe layer is entirely absent. Other consumers chain the canonical token (`var(--opacity-disabled)`) as their fallback, so this is a real inconsistency, but it is the lowest-impact token-drift category: cosmetic, no behavioral divergence in practice. Proposed fix is correct (chain to `var(--opacity-disabled)`). - fix-status: open ## No-findings dimensions Contract: morfo satisfies Morfo and uses as const, Contract: parts registered via runtime.part exist in morfo, Contract: data-{c}/-{part} naming, Behavior: setPointerCapture is immediate (pure handle, no child buttons), Behavior: A12 RTL keyboard via getDirectionalKeys, Behavior: A14 roving not applicable (single handle, not roving group), Behavior: A15 gesture cleanup on unmount (dragSignalFrame, pendingDragPercent cancelled), Behavior: A30 panel registration is direct (sync), not effect-wrapped for props, Behavior: A31 no O(N²) patterns (find/getPanelState are on local panels array, not global), Behavior: A33 no $state(Map/Set), Behavior: A6 requestFrame/cancelFrame properly disposed in effect cleanup, DOM: no querySelector interpolating consumer values, Frontier: soma does not import eidos, Frontier: syncAttrs true with derived props (no double-write), Theming: only standard 9 roles + space tokens referenced, Theming: no raw hex where role alias canonical, Theming: spacing uses --space-* tokens, Theming: no local z-index or canonical --z-index-* used (no z needed for splitter), Test: provider test exists, jsdom env, covers gesture drag, keyboard, collapse ## Theming facts (E-bis) - magic z-index: none - magic literals: 0.6 (splitter-001-adjacent) - undeclared parts: none - roles clean: true · variants clean: true ## Tests (F) - exists: true · env: jsdom - covers: panel registration and sizing; resize clamping and min/max constraints; collapse/expand with adjacent space redistribution; pointer drag (down/move/up); keyboard arrows (RTL-aware via getDirectionalKeys); keyboard Home/End (minimize/maximize); aria-valuenow/min/max projection; disabled state blocks resize - untested: gesture on lostpointercapture recovery; double-click toggle collapse; dragSignalFrame cleanup during unmount with active drag ## Style observations (non-blocking) - Excellent drag-signal throttling with per-frame payload queue (72ms window) - Clean pointer-capture semantics for pure-handle resize (not using Gesture class) - Robust collapse/expand logic respects min/max constraints on both panels - Well-documented keyboard shortcut implementation (Home/End/ArrowKeys)