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.
82 lines
9.1 KiB
82 lines
9.1 KiB
# Audit: stepper
|
|
audit-version: 1
|
|
audited-at: 2026-06-26
|
|
scope: soma, sema (SCOPE-DRIFT → SYS-1)
|
|
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/stepper/stepper-provider.svelte.ts
|
|
sequence-audit: state set at call-site (setStep line 108 before runtime.trigger line 109); morfo uses 'post' sequence for both shift-step and commit-complete events; safe pattern (call-site state tolerates 'post', no lag risk)
|
|
|
|
## Summary
|
|
Counts (post-verification): CRITICAL 0 · HIGH 0 · MEDIUM 2 · LOW 3.
|
|
|
|
## Findings
|
|
### MEDIUM: SYS-1 scope-drift — stepper-001 <!-- id: stepper-001 -->
|
|
- dimension: A, Contract
|
|
- rule: SYS-1 scope-drift
|
|
- location: src/uix/morfo/components/stepper.ts:7
|
|
- evidence: morfo scope: ['soma', 'sema'] declared, but eidos directory exists at src/uix/eidos/components/stepper/ with CSS, types, and component wrappers
|
|
- impact: eidos layer is outside declared scope; validator may not catch recipe inconsistencies in stepper recipe color/spacing tokens
|
|
- proposed-fix: Add 'eidos' to morfo scope array: scope: ['soma', 'sema', 'eidos']
|
|
- verify: [confirmed] CONFIRMED. morfo at src/uix/morfo/components/stepper.ts:7 declares `scope: ['soma', 'sema']`. A full eidos implementation exists: src/uix/eidos/components/stepper/ (stepper.css 6.8KB, types.ts, index.ts, 11 .svelte wrappers) plus a recipe at src/uix/eidos/lib/recipes/base.ts:3152 (`stepper: {`). The Morfo.scope doc (types.ts:798-799) defines scope as 'Layers that implement this component.' Reference morfos with eidos dirs DO declare it: toggle-group/tabs/radio-group/checkbox/accordion all have `scope: ['soma', 'sema', 'eidos']`. Stepper omits 'eidos' despite a complete eidos layer — genuine SYS-1 scope-drift. MEDIUM holds.
|
|
- fix-status: fixed (212624e0)
|
|
|
|
### MEDIUM: Morfo.expression missing for events + scope:['sema'] with existing sema pack — stepper-005 <!-- id: stepper-005 -->
|
|
- dimension: A, Contract
|
|
- rule: Morfo.expression missing for events + scope:['sema'] with existing sema pack
|
|
- location: src/uix/morfo/components/stepper.ts:4-32
|
|
- evidence: The morfo declares `events: [...]` (shift-step, commit-complete; lines 12-32) and `scope: ['soma', 'sema']` (line 7), but declares NO `expression` field. A sema pack exists at src/uix/sema/components/stepper.ts. The Morfo.expression doc (src/uix/morfo/types.ts:835-837) states: 'If a morfo declares events[] AND scope: ["sema"], expression SHOULD be set explicitly. The morfo-coverage check warns when it's missing.' Since a pack ships, the correct value is `expression: 'pack'`.
|
|
- impact: The morfo-coverage check (npm run morfo:vocabulary) warns on the missing expression field; the absence of the declaration makes 'has a sema pack' indistinguishable from 'forgot to write one'. Contract incompleteness, not runtime breakage.
|
|
- proposed-fix: Add `expression: 'pack'` to stepperMorfo (a sema pack exists at src/uix/sema/components/stepper.ts).
|
|
- verify: [verifier-added] added by adversarial verify pass
|
|
- fix-status: open
|
|
|
|
### LOW: Trigger part data-orientation undeclared but emitted — stepper-002 <!-- id: stepper-002 -->
|
|
- dimension: A, Contract
|
|
- rule: Trigger part data-orientation undeclared but emitted
|
|
- location: src/uix/soma/components/stepper/stepper-provider.svelte.ts:350
|
|
- evidence: Soma emits 'data-orientation': this.provider.opts.orientation.current on Trigger, but morfo Trigger part (lines 94-138) does not declare data-orientation in its data array. Item part declares it (line 84-87), Separator declares it (line 174-178), but Trigger does not.
|
|
- impact: Contract asymmetry: Trigger emits an attribute not in its morfo spec. Provider comment at line 343-344 acknowledges this is 'Soma-owned' and intentional, but represents a 2-of-3 rule violation (morfo part missing declaration)
|
|
- proposed-fix: Either (a) add data-orientation to Trigger morfo part data array with values ['horizontal', 'vertical'] and value: v.propRef('orientation'), or (b) document this as sanctioned soma-only attribute in design docs
|
|
- verify: [downgraded] Emission CONFIRMED, severity DOWNGRADED to LOW. Provider stepper-provider.svelte.ts:350 emits `'data-orientation': this.provider.opts.orientation.current` on Trigger; Trigger morfo data array (stepper.ts:103-107) declares only data-state/data-current/data-complete/data-incomplete — no data-orientation (sibling Item lines 84-87 and Separator lines 175-178 DO declare it). So the attribute is real and genuinely undeclared. BUT: (a) the Trigger part is registered with NO `props:` source block and NO syncAttrs (lines 268-272), so the morfo physically cannot source data-orientation for this part — the soma must supply it; (b) it is explicitly documented as intentional at the provider comment lines 343-344 ('the morfo declares these without a value source (documentation only) or not at all (data-orientation)'). No double-write risk (no syncAttrs on Trigger). This is a contract-documentation gap, not user-visible wrong behavior. LOW. Note the SAME class also occurs on the List part (provider line 174 emits data-orientation; List morfo data array is empty, stepper.ts:59) — see newFindings.
|
|
- fix-status: open
|
|
|
|
### LOW: data-disabled undeclared on Trigger but emitted — stepper-004 <!-- id: stepper-004 -->
|
|
- dimension: A, Contract
|
|
- rule: data-disabled undeclared on Trigger but emitted
|
|
- location: src/uix/soma/components/stepper/stepper-provider.svelte.ts:349
|
|
- evidence: Soma emits 'data-disabled': boolToEmptyStrOrUndef(this.isDisabled) on Trigger (line 349), but Trigger morfo part does not declare data-disabled. PrevTrigger and NextTrigger DO declare it (lines 220, 231). This is documented intentionally at line 343-344.
|
|
- impact: Asymmetry: Trigger emits data-disabled (computed isDisabled state) while morfo doesn't declare it. Intentional design per soma comment, but represents undeclared attribute.
|
|
- proposed-fix: Add data-disabled to Trigger morfo data array with severity 'optional' to match PrevTrigger/NextTrigger, or document as exception
|
|
- verify: [downgraded] Emission CONFIRMED, severity confirmed at LOW. Provider line 349 emits `'data-disabled': boolToEmptyStrOrUndef(this.isDisabled)` on Trigger; Trigger morfo data array (stepper.ts:103-107) does not declare data-disabled (PrevTrigger line 220 and NextTrigger line 231 DO). Same class as stepper-002: documented-intentional (comment lines 343-344), no registered source on the Trigger part (the value is composite isDisabled = disabled || !canSelectStep, richer than a plain prop-truthy condition), no syncAttrs so no double-write. The proposed fix (declare with severity 'optional', like PrevTrigger/NextTrigger) is reasonable but cosmetic. LOW holds.
|
|
- fix-status: open
|
|
|
|
### LOW: List part emits data-orientation not declared in morfo — stepper-006 <!-- id: stepper-006 -->
|
|
- dimension: A, Contract
|
|
- rule: List part emits data-orientation not declared in morfo
|
|
- location: src/uix/soma/components/stepper/stepper-provider.svelte.ts:174
|
|
- evidence: StepperListProvider.props emits `'data-orientation': this.provider.opts.orientation.current` (line 174, with the provider's own comment line 173: 'Soma-only: the morfo's List part declares no data-orientation'). The List morfo part data array is empty: `data: []` at src/uix/morfo/components/stepper.ts:59. The List part is registered without syncAttrs (provider lines 158-162), so no double-write — but the attribute is genuinely undeclared in the contract. Same class as stepper-002 (Trigger) but on a different part, missed by the candidate set.
|
|
- impact: Contract-documentation gap: an emitted data-attr the morfo does not promise. eidos CSS that selects `[data-stepper-list][data-orientation='vertical']` would have no contract backing. No user-visible breakage.
|
|
- proposed-fix: Declare data-orientation on the List morfo part (values ['horizontal','vertical'], value v.propRef('orientation')) to match Item/Separator, OR document the soma-only attr in design docs as is done for the Trigger.
|
|
- verify: [verifier-added] added by adversarial verify pass
|
|
- fix-status: open
|
|
|
|
## No-findings dimensions
|
|
B-sequence, B-roving-tabindex, B-keyboard-RTL, B-gesture, B-form-hidden-input, B-O(N²), C-DOM-selector, D-soma-imports-eidos, D-double-write, E-TSC-composition, E-theming, F-test-coverage, G-redundancy
|
|
|
|
## 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: step-tracking; item-states; linear-rules; keyboard-navigation; trigger-registration; prev-next-triggers; completion-state
|
|
- untested: sequence-event-timing; data-attribute-edge-cases
|
|
|
|
## Style observations (non-blocking)
|
|
- Recipe uses canonical token variables throughout (--stepper-gap-*, --stepper-font-size-*, etc.)
|
|
- Color roles properly reference --color-* semantic tokens (no raw hex)
|
|
- All spacing and sizing via tokens, no magic literals
|
|
- TSC accent tokens (_accent-solid, _accent-track, _accent-border, _accent-text) properly cascaded per color role in recipes/base.ts
|