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.
43 lines
6.0 KiB
43 lines
6.0 KiB
# Audit: breadcrumb
|
|
audit-version: 1
|
|
audited-at: 2026-06-26
|
|
scope:
|
|
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: G:/dev/svelte/vicen/src/uix/soma/components/breadcrumb/breadcrumb-provider.svelte.ts
|
|
cleanup-audit (A6/A35/A36): EFFECT (line 173-180): $effect writing item.isCurrent with cleanup that resets to false. No listener/observer. No resources held. Disposer verified present: return () => { item.isCurrent = false; } ✓. A35/A36 loop check: SAFE — isCurrentRaw is $derived (acyclic); writing item.isCurrent does not caus
|
|
|
|
## Summary
|
|
Counts (post-verification): CRITICAL 0 · HIGH 1 · MEDIUM 0 · LOW 0.
|
|
|
|
## Findings
|
|
### HIGH: Archetype 'item' is reserved for selectable/interactive row parts; display items should om — breadcrumb-001 <!-- id: breadcrumb-001 -->
|
|
- dimension: A: Contract(morfo)
|
|
- rule: Archetype 'item' is reserved for selectable/interactive row parts; display items should omit archetype
|
|
- location: G:/dev/svelte/vicen/src/uix/morfo/components/breadcrumb.ts:50
|
|
- evidence: Item part declares `archetype: 'item'` but Item is a display `<li>` container (not selectable). Timeline.ts explicitly documents: "No `archetype: 'item'` — that archetype is for selectable menu/listbox/option rows... A timeline entry is a display `<li>`, structurally unique, so it omits the archetype." Breadcrumb.Item is semantically identical to Timeline.Item.
|
|
- impact: Incorrect archetype declaration violates the 2-of-3 rule (archetype field inconsistent with actual DOM semantics). May confuse CSS-side consumers expecting interactive row styling (cursor:pointer, select, hover). Runtime validator should flag this.
|
|
- repro: none (static violation)
|
|
- proposed-fix: Remove `archetype: 'item'` from the Item part definition (line 50). Item is a display container, not a selectable option.
|
|
- verify: [confirmed] CONFIRMED. breadcrumb.ts:48-56 declares the Item part with `archetype: 'item'` (line 50) on a display container `<li>` (`defaultElement: 'li'`, no role, not selectable). The runtime stamps `data-archetype='item'` on the element whenever the morfo sets it (morfo/types.ts:720 'the runtime emits `data-archetype="..."` on the part's DOM element via `partProps`'; README:177 same). That `[data-archetype='item']` selector triggers archetypes.css:139-153 — `cursor: pointer; user-select: none; min-block-size: var(--list-item-height, auto); padding-block: var(--list-item-py, var(--space-1-5))` — PLUS the hover/highlight band at archetypes.css:161-171 (`background-color: var(--color-surface-raised)` painted on the WHOLE `<li>` on hover). breadcrumb.css:36-41 styles `[data-breadcrumb-item]` only with `display:inline-flex; align-items:center; min-inline-size:0` — it does NOT reset cursor/padding-block/hover, so the styling is unmitigated. This is the exact documented bug: Timeline.ts:88-91 ('No `archetype: 'item'` — that archetype is for selectable menu/listbox/option rows (it ships cursor:pointer, user-select:none, internal padding + a hover highlight). A timeline entry is a display `<li>`, structurally unique, so it omits the archetype.'), and Calendar.ts:247-249 / 270-273 + range-calendar.ts:285 repeat the same doctrine for display `<tr>`/`<td>`. Breadcrumb.Item is semantically identical to Timeline.Item. Real visible regression: a breadcrumb `<li>` gets `cursor:pointer`, `padding-block: var(--space-1-5)`, and a surface-raised hover band over the whole crumb row. HIGH is correct per the rubric's 'archetype-pulls-wrong-styling on a real part'. Fix: remove `archetype: 'item'` from breadcrumb.ts:50 (the Item is a display container; the interactive Link inside owns its own affordance).
|
|
- fix-status: fixed (98954a7c)
|
|
|
|
## No-findings dimensions
|
|
B: Behavior(soma): A35/A36 loops absent (effect mirrors current to item without cycle; no async state writes), B: A6 resource cleanup (no timers/listeners/observers), B: A31 O(N²) derivations absent, B: A30 child->parent id registration (uses $effect correctly), B: A33 state(Map/Set) cloning (no Maps/Sets used), C: DOM-selector (no querySelector with user values), D: Frontier (no soma->eidos imports), E-bis: Theming (recipe uses canonical --space-*, --font-size-*, --duration-* tokens; focus-ring uses canonical CSS var), F: Tests environment jsdom (provider test exists, covers label projection, current mirroring, ellipsis semantics), G: Passive component justified in README (zero state, zero keyboard nav, zero events by design)
|
|
|
|
## 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 (@vitest-environment jsdom)
|
|
- covers: Provider label projection and resolution (BREADCRUMB_LANGS); Link.current state mirroring to Item.isCurrent; aria-current conditional emission (true/'page'/'step'); renderSpan conditional tag switching; Ellipsis decorative->interactive semantics via interactive prop; aria-haspopup conditional on interactive; role=presentation conditional on !interactive
|
|
- untested: E2E browser interaction (Playwright): full click/focus flow, ARIA tree, CSS targeting; Eidos variant/size responsive resolution (ActiveEidos.resolve); Ellipsis with DropdownMenu composition (documented example in README)
|
|
|
|
## Style observations (non-blocking)
|
|
- Link focus-visible uses canonical --focus-ring-width and --focus-ring-color vars (breadcrumb.css:61-64, 107-109) ✓
|
|
- Ellipsis interactive and link hover transitions use canonical --breadcrumb-transition-duration and --breadcrumb-transition-ease (line 52-54, 94-98) ✓
|
|
- Recipe declares all semantic spacing (gap-xs/sm/md/lg → var(--space-*)) and sizing (font-size-xs/sm/md/lg, ellipsis-size → var(--control-height-xs)) ✓
|
|
- no hardcoded hex colors; all use role aliases (--breadcrumb-color, --breadcrumb-link-color, etc.) ✓
|