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

63 lines
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 <!-- id: 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 the `data-clipboard` / `data-clipboard-indicator` recipe 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 `scope` will 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 <!-- id: 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 uses `gap: 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 <!-- id: 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);` — the `500` literal fallback. Every recipe occurrence in lib/recipes/base.ts (lines 48, 153, 231, 314, 449) uses bare `var(--font-weight-medium)` with no fallback.
- impact: Cosmetic inconsistency; the `500` fallback 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

Powered by TurnKey Linux.