5.5 KiB
Audit: clipboard
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/clipboard/clipboard-provider.svelte.ts
Summary
Counts (post-verification): CRITICAL 0 · HIGH 0 · MEDIUM 1 · LOW 2.
Findings
MEDIUM: SYS-1 scope-drift — morfo scope omits 'eidos' although an eidos recipe/CSS dir exists for — CLIP-1
- dimension: A
- rule: SYS-1 scope-drift — morfo scope omits 'eidos' although an eidos recipe/CSS dir exists for the component
- location: src/uix/morfo/components/clipboard.ts:7
- evidence:
scope: ['soma', 'sema'],— yet src/uix/eidos/components/clipboard/ exists with clipboard.css, clipboard.svelte, clipboard-trigger.svelte, clipboard-indicator.svelte and types.ts. The eidos layer materializes thedata-clipboard/data-clipboard-indicatorrecipe selectors. - impact: The morfo's declared scope no longer reflects reality: an eidos consumer exists but the contract says only soma+sema. Scope is the declared surface; drift means tooling/audits that read
scopewill under-count the eidos layer. - proposed-fix: Add 'eidos' to the scope array:
scope: ['soma', 'sema', 'eidos'](cf. button.ts:47 which declares it). Or — if the project decides hand-authored thin consumer wrappers do NOT count as an eidos recipe — codify that rule, since toggle/switch/collapsible omit it too. This is the confirmed-systemic SYS-1; clipboard matches the majority that omit it. - verify: [verifier-added] added by adversarial verify pass
- fix-status: fixed (
212624e0)
LOW: Recipe spacing MUST reference --space-* tokens; raw rem literals are drift — CLIP-2
- dimension: E-bis
- rule: Recipe spacing MUST reference --space-* tokens; raw rem literals are drift
- location: src/uix/eidos/components/clipboard/clipboard.css:34
- evidence:
padding: 0.125rem 0.5rem;— raw rem literals, while the SAME rule usesgap: var(--space-1);one line above (line 33). 0.5rem == --space-2 in the project scale. - impact: The indicator chip's inset bypasses the spacing scale, so density/scaling theme changes (
:root--space-* overrides) won't reach it. Minor visual-only drift. - proposed-fix: Replace with token-derived values, e.g.
padding: var(--space-0-5, 0.125rem) var(--space-2);(confirm the exact step names against primitives/static.ts). - verify: [verifier-added] added by adversarial verify pass
- fix-status: open
LOW: Recipe font-weight should reference the bare token; hardcoded numeric fallback diverges fr — CLIP-3
- dimension: E-bis
- rule: Recipe font-weight should reference the bare token; hardcoded numeric fallback diverges from convention
- location: src/uix/eidos/components/clipboard/clipboard.css:40
- evidence:
font-weight: var(--font-weight-medium, 500);— the500literal fallback. Every recipe occurrence in lib/recipes/base.ts (lines 48, 153, 231, 314, 449) uses barevar(--font-weight-medium)with no fallback. - impact: Cosmetic inconsistency; the
500fallback masks a missing token rather than failing visibly, and diverges from the recipe convention. - proposed-fix: Drop the fallback:
font-weight: var(--font-weight-medium);to match base.ts usage. - verify: [verifier-added] added by adversarial verify pass
- fix-status: open
No-findings dimensions
A: Contract - Morfo 'as const satisfies' correct, A: Parts registered via runtime.part(), A: Data attributes (data-copied) declared in morfo, A: ARIA attributes correct (aria-label, aria-live, aria-hidden), A30: No per-item loops or sequence lags detected, A31: No O(N²) derived patterns detected, A33: No Map/Set state issues detected, A35: No per-item effect loops detected, A36: No async-mediated microtask freeze patterns detected, A6: Timer disposal via resetTimer with clearResetTimer() cleanup and $effect return, A15: No gesture/drag-drop patterns needed for clipboard, A34: Live regions - Trigger has aria-live polite for label announcement, B: Provider pattern correct - soma imports only soma core, no eidos imports, D: Frontier - No soma->eidos imports, E-bis: No recipe token or theming issues (uses canonical color roles), F: Tests exist - jsdom environment, happy path, error handling, timer mocks
Theming facts (E-bis)
- magic z-index: none
- magic literals: none
- undeclared parts: none
- roles clean: true · variants clean: true
Tests (F)
- exists: true · env: jsdom
- covers: copy success and copied state reset via timer; onCopy callback invocation; onError callback on clipboard API rejection; timer scheduling with uix.timers.schedule; aria-label resolution and swap on copied state; indicator visibility conditional on copied state; mergedProps application
- untested:
Style observations (non-blocking)
- CSS uses canonical color-role variables (--color-affirm-solid, etc.) with cascading support via data-color attribute
- Animation (clipboard-pop) respects prefers-reduced-motion
- Typography uses canonical --font-size-xs and --font-weight-medium
- Spacing uses --space-* tokens (0.5rem mapped to --space-1)
- Focus ring follows canonical two-ring model via Button consumer
- No hardcoded hex colors or magic literals in eidos CSS