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

28 KiB

Audit Summary — rolling

summary-version: 8 — AUDIT COMPLETE updated-at: 2026-06-26 batches-complete: 8 of 8 — 124/124 components audited (words/palabras/chronos excluded per user directive) (B1 overlays · B2 menus/overlays · B3 pickers+calendar · B4 fields · B5 collections+data · B6 disclosure+controls · B7 actions+feedback+misc · B8 eidos-only primitives: layout/typography/visual/composite+service/infra)

Counts by severity (FINAL — cumulative B1..B8)

Severity B1 B2 B3 B4 B5 B6 B7 B8 TOTAL
CRITICAL 0 0 0 0 0 0 0 0 0
HIGH 8 2 0 0 6 1 1 0 18
MEDIUM 17 23 21 22 14 11 18 3 129
LOW 5 17 11 5 2 5 3 12 60

Final verdict

The framework is in strong shape. 0 CRITICAL, 18 HIGH across 124 components — and the HIGH findings cluster into a handful of systemic patterns with shared, mechanical fixes, not scattered one-offs. The single highest-leverage fix is SYS-7 (the A31 O(N²) selection pattern: 9 components, 6 of them HIGH, one ~3-line selectedSet/expandedSet lift each). After that it is mostly MEDIUM hygiene: SYS-1 scope-drift (~33 components, one definitional decision), THEME-SYS-1 (overlay z-index ladder, ~10 recipes, one tokenization sweep), and the smaller systemic patterns below.

The non-A31 HIGH findings are isolated and each well-grounded:

  • dialog-001 — Content role/aria-roledescription double-write (a11y race for alertdialog variant).
  • dialog-002 — dead/conflicting [data-dialog-trigger] CSS envelope after the Button migration.
  • combobox-001 — virtual+real focus mix (A17 / APG break).
  • select-001/002/003 — querySelector injection · focus.trap declared-not-implemented · keyboard-route divergence.
  • alert-dialog-001 — the only : Morfo annotation (degrades createAttrs).
  • navigation-menu-006 — an undisposed $effect.root (bounded leak).
  • collapsible-NEW-001 — collapse sequence:'pre' + handler-state (the Checkbox-244ms-lag mechanism).
  • breadcrumb-001 — archetype:'item' false-affordance on a display <li> (the bug Timeline explicitly avoided).

Method honesty (recorded throughout): every HIGH/CRITICAL was lead-verified against the cited source. The adversarial-verify stage repeatedly earned its keep — it refuted a false CRITICAL (eidos-imports-soma on onion-menu), a false HIGH (button archetype, inert because the part isn't runtime-registered), every CSS.escape-on-framework-value HIGH/CRITICAL (grid selectors), and the non-canonical-role flags (content-color slots, not the 9 roles). The lead's own independent greps caught real defects the agents missed (the A31 cluster pre-confirmed) AND made one error of its own (a single-line grep wrongly cleared the field-family A30, which the workflow caught) — no single source was infallible; the cross-check (grep × agent × verify × lead read) is what produced the verdict. The lead also corrected the verify in both directions: elevated under-rated findings (range-calendar Home/End → systemic; collapsible seq-lag MEDIUM→HIGH) and downgraded over-rated ones (tag-group rovingTarget HIGH→LOW; many magic-literals).

Positive references (cite these when building/reviewing): NumberField (renderProps + direct id-wiring + A13), Popover (Close-via-Button, no role override, hover guards), DropdownMenu (roving-focus consistency, self-managed cleanup, eidos-in-scope), Button (clean action recipe), Dialog's data-color subset (neutral/risk/threat), Timeline (correctly omits archetype:'item'), VirtualList (SvelteMap), accordion + tabs (fully clean). The eidos theming layer is clean (roles/variants/sizes/naming all canonical across 124 components).

Counts by severity (per batch, historical)

B7 result: the action/feedback layer is healthy. The two documented reactivity incidents stayed fixed: Toolbar A35 (isTabStop uses untrack, the counter was removed) and Form A36 (form-core.svelte.ts:262 return untrack(...)) — both lead-verified, both REFUTED when an agent re-raised them. Timers route through uix.timers (toast/clipboard/drag-drop) → no A6 leaks. Button is CLEAN — the reference action component (its one archetype finding was inert: the Spinner part isn't runtime-registered, so data-archetype never reaches the DOM — the verify caught the false premise). The one real HIGH: breadcrumb-001 — the Item part (a display <li>) declares archetype:'item' AND is runtime-registered (provider:123), so data-archetype='item' IS emitted and archetypes.css paints it with cursor:pointer + hover (a false affordance on a non-clickable container). The insight: Timeline explicitly avoided this (its morfo comments "no archetype:'item' — a display <li> omits it"); breadcrumb didn't. A targeted, lead-verified contract bug — not systemic, but a clean example of the archetype-pull anti-pattern one sibling fixed and the other didn't.

B6 result: the disclosure/selection controls are well-built — accordion + tabs are fully CLEAN, the sequence doctrine is correctly applied everywhere it was checked (checkbox/toggle/switch = post; accordion/tabs/radio-group/ stepper set state at the call-site so pre is fine), slider has no gesture A6 leak. ONE real HIGH: collapsible's collapse event is sequence:'pre' while setting state in the handler — the verifier traced the runtime and proved it serializes (emit-then-handler), so open=false lags the full ~240ms brief hold (the same mechanism as the documented Checkbox 244ms-lag). The analyze agent AND the morfo's own comment marked it clean on a false "emit overlaps handler" assumption. Lead note: I initially downgraded it to MEDIUM ("it's the canonical emerge.dismiss pre") then RESTORED it to HIGH after re-reading — dialog masks the hold with a data-event/Presence exit animation, but collapsible's exit is keyed on the (delayed) data-state, so the 240ms is a real dead delay. Verifying the adversarial-verify's own catch corrected me — the cross-check cuts both ways.

B5 result: the collections are where the A31 O(N²) selection pattern lives — 6 HIGH, all the SAME root cause: a per-item $derived calling provider.isSelected(v) / provider.isExpanded(v) that does .includes() on the global array (listbox, grid-list ×2, tree-view, tree-grid, tag-group). All 6 lead-verified by direct read of the cited .includes method bodies (matches an independent lead grep run before the workflow). SYS-7 now spans 7 components with ONE shared fix. The verify did its job: it refuted feed's A31 (per-item derived doesn't read global state), downgraded table's (engine-backed by $libs/datagrid), and confirmed virtual-list/grid as A33-clean (SvelteMap). The lead refuted one verifier-ADDED HIGH (tag-group-004 "rovingTarget A31") — rovingTargetEl is already LIFTED to a single provider $derived and the per-item check is an O(1) pointer compare; downgraded to a LOW A18 nit. Even verifier-added findings get verified.

B4 result: the field family is healthy — zero HIGH survived verification. Dominated by SYS-1 scope-drift (nearly all 14), test gaps, a new systemic A30-doctrine deviation, and label-font/magic-literal nits. NumberField is confirmed the clean reference (correct renderProps + direct id-wiring + A13 hidden input). The analyze pass over-reported: the field agent emitted 6 "clean-check" pseudo-findings (impact = "no fix needed"); the verify refuted all 6, and the generator drops clean-checks. Method-honesty note: the lead's own independent grep for A30 violations used a single-line pattern and WRONGLY concluded "field family A30-clean"; the workflow's analyze+verify caught the multi-line $effect that the grep missed (SYS-A30-EFFECT below). No single source — grep, agent, verify, or lead — is infallible; the cross-check is what produced truth.

B3 result: the picker + calendar family is in good shape — zero HIGH survived verification (the analyze pass raised 2 CRITICAL + several HIGH; all were CSS.escape-on-numeric/ISO-selectors or local-z-index, correctly downgraded to LOW, plus one keyboard-label drift the lead elevated to a systemic MEDIUM). The dominant B3 issues are SYS-1 scope-drift (all 10), inert open/commit-reset events (pickers), magic literals, and jsdom-only tests. Method note: the lead's own memory ("pickers don't fire close events") was STALE — an independent grep proved each picker now fires trigger('close'); the inert-events findings are about open/commit-reset, which the verify + grep agree ARE inert. Verifying against current code, not memory, mattered.

Method note (B2): Batch 2 was run as an adversarially-verified workflow (one analyze agent per component → a skeptical verify agent that re-reads the cited code). The verify stage refuted/downgraded a large fraction of the analyze pass's claims (it caught a false-positive CRITICAL on onion-menu — "eidos imports soma" is the normal direction, not a frontier violation — and a false-positive HIGH A30 on context-menu). Both surviving B2 HIGH findings were personally re-verified against the cited source by the lead before shipping. This validates the standing rule "unverified agent finding = not a finding": the raw analyze pass over-reported ~15 HIGHs that collapsed to 2 after verification.

B2 HIGH findings (both lead-verified)

  • alert-dialog-001 (HIGH, A) — export const alertDialogMorfo: Morfo = {…} uses a : Morfo annotation instead of as const satisfies Morfo; this widens literal types and degrades createAttrs/compileMorfo key narrowing. The ONLY morfo in the audited set with this defect. (Lead-verified: alert-dialog.ts:4.)
  • navigation-menu-006 (HIGH, B/A6) — openedAtEffect = $effect.root(() => { $effect(() => …) }) (provider:523) is a DETACHED reactive root whose disposer is stored but never called; each NavigationMenuTriggerProvider leaks one root permanently subscribed to isOpen. A bare $effect (auto-disposed) is the correct tool. (Lead-verified: the only $effect.root in any provider; no dispose anywhere in the file.)

Counts by severity (Batch 1)

Severity Count
CRITICAL 0
HIGH 8
MEDIUM 17
LOW 5
total 30

Counts by component

Component C H M L headline
select 0 4 5 2 injection, focus.trap-unimpl, kbd-route divergence, kbd untested
dialog 0 2 2 2 role double-write (alertdialog race), dead trigger envelope
popover 0 0 3 0 cleanest — close double-write, magic-z, test gap
combobox 0 2 3 0 virtual+real focus mix (A17), kbd untested
dropdown-menu 0 0 4 1 most disciplined — nav dup, checkbox silent, magic-z

Counts by dimension (findings tagged with each)

Dim What Count
A (contract) scope-drift, undeclared parts, aria-not-in-morfo, translationRef, checkbox silent, virtual-part data 8
B (behavior) focus mix, kbd-route divergence, A31, loop off-by, sequence 7
C (selectors) querySelector injection 1
D (frontier) double-writes, dead trigger envelope, undeclared parts 5
E (TSC) 0 (TSC usage was clean where present) 0
E-bis (theming) magic z-index, em-font literal, magic opacity, raw rem, subset 9
F (tests) jsdom-only + keyboard/focus/dismissal untested 5
G (redundancy) directional-nav duplication 2

Top findings by impact

  1. combobox-001 (HIGH) — Combobox mixes virtual focus (aria-activedescendant) with real dom.focus() on items; violates A17 and the APG combobox contract (focus must stay on the textbox).
  2. dialog-001 (HIGH) — Dialog Content double-writes role (syncAttrs 'dialog' vs Svelte variant); for variant='alertdialog' the accessible role is a non-deterministic race.
  3. select-001 (HIGH) — scrollSelectedIntoView interpolates the user value into a querySelector without CSS.escape (the safe helper it imports does escape — this path bypasses it).
  4. select-002 (HIGH) — morfo declares focus.trap:true + initial:'first-focusable' but the provider implements virtual focus (no FocusScope) — dead/contradictory contract.
  5. select-003 (HIGH) — two keyboard routes (virtual trigger vs real-focus content) duplicate index math and diverge; the content loop path lands n-2 (off-by) and reads activeElement though the component is virtual.
  6. dialog-002 (HIGH) — [data-dialog-trigger] full-chrome CSS envelope survives the trigger's migration to a composed <Button>; both recipes paint the same element (order-dependent).
  7. SYSTEMIC: MAGIC-Z (5/5) — see systemic #2.
  8. SYSTEMIC: JSDOM + keyboard/focus untested (5/5) — see systemic #3.
  9. SYSTEMIC: SCOPE-DRIFT (4/5) — see systemic #1.
  10. SYSTEMIC: DIRECTIONAL-NAV duplication (3+ components, 4+ copies) — see systemic #4 + SHARED_EXTRACTION.

Systemic issues (recur in ≥3 components → one structural fix each)

SYS-1 · SCOPE-DRIFT — eidos recipe exists but morfo scope omits 'eidos'

  • components: select, dialog, popover, combobox (4/5). dropdown-menu CORRECTLY declares ['soma','sema','eidos'].
  • evidence: each has a full eidos/components/{c}/ recipe + wrapper, yet scope: ['soma','sema'].
  • why it matters: scope is documented (glossary) as "which layers implement the component"; the 2-of-3 rule and tooling key off it. An inconsistent scope makes "which components have eidos" unanswerable from the morfo.
  • one fix: decide the semantics (does scope list authored layers or all consuming layers?) and make it consistent — either add 'eidos' everywhere an eidos recipe exists, or document that eidos is implicit and scope lists only soma/sema. Affects ~40+ interactive components beyond Batch 1.
  • resolution: scope lists IMPLEMENTING layers; added 'eidos' to all 62 drifted morfos (scope-drift now 0). Regression guard added in contracts.test.ts ("…declares 'eidos' in morfo scope (SYS-1)") — fails if any maintained component ships an eidos recipe without 'eidos' in scope (proven to catch drift; dev-track excluded).
  • fix-status: fixed (212624e0)

SYS-2 · MAGIC-Z — overlay content-z/overlay-z are hardcoded integers

  • components: select (80), dialog (70 + content calc+1), popover (75 / overlay 60), combobox (80), dropdown-menu (80). 5/5.
  • evidence: lib/recipes/base.ts per-recipe integer z values; no --z-index-* overlay ladder consumed.
  • why it matters: overlay stacking is inherently cross-component; scattered integers (60/70/75/80) can't be coordinated or reasoned about, and a new overlay just guesses a number.
  • one fix: define a canonical overlay z-ladder (--z-index-{popover,menu,modal,...}) and map every overlay recipe to it in one sweep. (THEMING §35 Bloque C already mandates this for "magic z-index".)
  • fix-status: open

SYS-3 · TEST-FIDELITY — interaction-heavy overlays tested jsdom-only, keyboard/focus/dismissal unpinned

  • components: all 5. select + combobox HIGH (the focus bugs live exactly in the untested paths); dialog, popover, dropdown-menu MEDIUM.
  • evidence: every *-provider.svelte.test.ts is @vitest-environment jsdom and asserts selection/open-close logic; none exercise the directional-nav index math, typeahead, dismissal trigger-exclusion, focus trap/return, or aria-activedescendant sync. jsdom can't validate focus/layout/hydration.
  • why it matters: the highest-risk behaviors (and several of the HIGH defects above) would ship green.
  • one fix: (a) add provider unit tests for the keyboard routes + dismissal exclusion; (b) add a client/ Playwright project test per focus-bearing overlay asserting focus return + activedescendant/roving invariants.
  • fix-status: open

SYS-4 · DIRECTIONAL-NAV duplication (+ shared loop off-by)

  • components: select (content route), combobox (content), dropdown-menu (content + sub-content). 4+ copies.
  • evidence: identical next/prev/Home/End index math; the currentIndex===-1 + prev + loop branch computes n-2 (off-by) in select-003 and dropdown-menu-001.
  • one fix: a pure nextIndex(curr, key, len, {loop, dir, orientation}) helper, unit-tested once. See SHARED_EXTRACTION DIRECTIONAL-NAV.
  • fix-status: open

SYS-5 · SYNCATTRS + MANUAL DOUBLE-WRITE

  • components: dialog (role/aria-roledescription — HIGH, live race), popover (Close type/aria-label — MEDIUM, benign-valued). 2/5 so far; likely more downstream.
  • evidence: a part registered syncAttrs: true (runtime writes the morfo attrs via dom.apply) AND the props block re-sets a morfo-declared attr → two authorities (active_architecture §7.7).
  • one fix: a lint that flags syncAttrs:true parts whose props block keys intersect morfo-declared attrs (role/aria-*/type). See VALIDATOR_GAPS. Per-component: drop the manual re-set or switch to renderProps() + single override.
  • fix-status: open

SYS-6 · UNDECLARED-EIDOS-PARTS — recipe styles data-{c}-{part} absent from the morfo

  • components: select (indicator, item-indicator), dialog (header, footer — acknowledged). 2/5 so far.
  • evidence: CSS selectors on parts with no morfo entry (decorative/layout). eidos-lint would classify them eidos-only/invalid.
  • one fix: a project convention for eidos-only parts — declare them kind:'internal' in the morfo OR a documented per-recipe allow-list — applied uniformly.
  • fix-status: open

SYS-7 · A31 O(N²) — per-item isSelected/isExpanded .includes on the global array

  • components (9, lead-verified): select-007 (M), combobox-004 (M) [B1] + listbox-001, grid-list-001/002, tree-view-002, tree-grid-001, tag-group-001 (HIGH) [B5] + toggle-group-001 (M), checkbox-001 (M, via CheckboxGroup) [B6]. Each has a per-item $derived calling a provider method (isSelected/isExpanded/isItemPressed/isItemChecked) that does array.includes(v) — O(K) per item × N items on every mutation = O(N²). Lead-confirmed by reading the exact method bodies: listbox:157, grid-list:151, tree-view:96-97/146, tree-grid:148/189, tag-group:116, toggle-group:72, checkbox:313, select:158, combobox:182. (radio-group is CLEAN — single-value ===, not .includes.)
  • severity split (legitimate): select/combobox are usually small single-select (K≤1) → MEDIUM; the collections are large multi-select / many-expanded (the documented "hangs at 30+" case) → HIGH. The root cause and fix are identical.
  • the proof it's a miss, not a design choice: these providers ALREADY lift rovingTargetEl to ONE Set-backed provider $derived (with comments citing "avoids an O(N²) cascade") — but left the sibling isSelected calling the O(N) method per-item. The asymmetry is the tell.
  • one structural fix: a provider-level readonly selectedSet = $derived(new SvelteSet(opts.value.current)) (and expandedSet for trees); isSelected/isExpanded consult .has() (O(1)). ~2 lines per component. See SHARED_EXTRACTION EX-3 (now the highest-value extraction in the audit).
  • fix-status: open

SYS-A33-CLONE · Reactive-collection clone-and-reassign instead of SvelteMap/Set

  • components (B5): listbox, grid-list, tree-grid, tag-group use the this.x = new Map(this.x) clone-and-reassign workaround for a registry/cache; combobox previously did (now fixed). It IS reactive (field reassignment tracks) but O(N) per mutation + fragile (breaks silently if refactored to .set() direct) — A33 says rewrite to SvelteMap. virtual-list is the positive model (4× SvelteMap). No plain $state(new Map/Set) anywhere in B5 (the dangerous form).
  • one fix: migrate the four clone-reassign sites to SvelteMap/SvelteSet (O(1) .set, no clone). MEDIUM.
  • fix-status: open

Batch-2 systemic confirmations + corrections

  • SYS-1 (scope-drift) — heavily confirmed. B2 adds menubar, navigation-menu, drawer, command, link-preview, float-panel, menu-dial (7 more) → ~11 components confirmed; tooltip/context-menu/alert-dialog/onion-menu correctly declare or don't need eidos-in-scope. Now the dominant contract-consistency issue. Severity normalized to MEDIUM per-component (it's an informational drift, no user-visible effect) — the fix is the one structural SYS-1 decision.
  • SYS-2 (magic-z) — confirmed + made authoritative. B2 adds tooltip content-z:76, drawer overlay-z:72/ inline-z:64, link-preview preview-z:99. Lead independently verified the canonical scale STATIC_Z_INDEX (base 0 · raised 1 · sticky 100 · dropdown 300 · popover 400 · tooltip 500 · modal 700 · toast 900) EXISTS and is consumed by the depth planes + menu-dial (var(--z-index-sticky)). So the overlay recipes hardcode an ad-hoc parallel 60–99/1200 scale whose ordering doesn't even match canon. The token they should use exists. See THEMING_COHERENCE THEME-SYS-1 for the precise per-component mapping.
  • SYS-3 (test fidelity) — confirmed across all 11. Every B2 component is jsdom/node-unit only; the keyboard/ focus/dismissal hard paths are untested. menu-dial/onion-menu test only the pure nav math (correct as far as it goes) but never the focus/two-moments integration.
  • SYS-4 (directional-nav off-by) — PARTIALLY DISPROVEN (important correction). The currentIndex===-1 + prev + loop → n-2 off-by is NOT universal: navigation-menu's loop math is CORRECT (modulo (i+1)%n/(i-1+n)%n WITH a currentIndex===-1 early-return guard, verified at provider:325/336-345). The off-by is specific to Select's and DropdownMenu's content routes, which lack that guard. The shared-helper opportunity (EX-1) stands, but the BUG is narrower than B1 implied — fix select/dropdown, don't assume every menu has it.
  • SYS-5 (syncAttrs double-write) — one more (drawer-007, MEDIUM). Drawer Content manually sets role:'dialog'
    • aria-modal which the morfo declares; benign value (role constant) so MEDIUM, not the HIGH dialog-001 race.
  • SYS-6 (undeclared eidos parts) — mostly REFUTED in B2. The B2 analyze pass flagged data-size/data-depth/ data-floating-gap as "undeclared parts" on menubar/navigation-menu/float-panel; the verify stage refuted these — they are eidos-owned VISUAL data-attrs (the sanctioned data-{prop} pattern, like Dialog's data-size), not morfo parts. SYS-6 remains valid only for genuine decorative parts (select indicator, dialog header/footer).

Batch-3 systemic confirmations + new patterns

  • SYS-1 (scope-drift) — now ~21 components. All 10 B3 components hit it (incl. the pickers and calendar, range-calendar nominally includes eidos in scope but its recipe entry is thin). It is the single most pervasive finding in the whole audit. The fix is one decision (does scope enumerate authored layers or all consumers?).
  • SYS-INERT (NEW) — pickers declare events the provider never fires. date-picker/date-range-picker/time-picker/ time-range-picker/color-picker each declare an open event and a commit-reset event in the morfo that the provider NEVER triggers (it only fires close + the field/area events). Lead-verified by grep: each picker provider calls trigger('close') (1×; color-picker 7× incl. commit-set/handle-*) but never trigger('open')/ trigger('commit-reset'). The events exist only to satisfy the schema validator — a 2-of-3 / contract-honesty gap (MEDIUM). Fix: either fire them (perceptual signal on open / on clear) or drop them from the morfo.
  • SYS-CAL-HOMEEND (NEW) — calendar + range-calendar Home/End label vs behavior. The morfo declares the Home/End actions as first/last-day-of-week but both providers implement start/end-of-month. Either an APG deviation (WAI-ARIA Date Picker Dialog: Home/End move within the WEEK) or a stale label. 2 components (MEDIUM, lead-elevated from the verify's LOW because shared-ness = systemic).
  • CSS.escape-on-grid-selectors — a consistent LOW class, not a bug. calendar/month-grid/year-grid interpolate framework-derived NUMERIC (month 1..12, year arithmetic) or ISO (DateValue.toString()) values into querySelector without CSS.escape. The analyze pass over-rated these HIGH/CRITICAL; verification correctly downgraded ALL to LOW (the values provably cannot contain CSS metacharacters). A defense-in-depth nicety to add uniformly, never a user-hittable defect. (Contrast Select's user-VALUE interpolation, select-001, which is real.)
  • THEME-SYS-2 (magic opacity) extended — calendar day-outside-opacity 0.62 (off the 0.05-step --opacity-* scale; the same 0.62/62% recurs as overlay-opacity in dialog/drawer). See THEMING_COHERENCE.

Batch-4 systemic confirmations + new patterns

  • SYS-A30-EFFECT (NEW) — segmented fields register inputId via $effect, not direct assignment. date-field (provider:932-936), time-field (531-535), color-field (521-525) wrap the field.inputId.current = opts.id.current registration in a $effect; the reference NumberField (585-587) does it as a direct constructor guard. Lead-verified in all four. No live loop (the child only WRITES inputId, never reads it; opts.id is stable) — so it is an A30 doctrine deviation (MEDIUM), not a freeze. But it is exactly the shape A30 warns about; a future two-way change reintroduces the freeze. One fix: make all three match NumberField's direct assignment. (Note: the date-field + color-field agents both listed "A30/Behavior" as a no-findings dimension — they MISSED their own $effect; only time-field's agent caught it; the lead added the twins.)
  • SYS-1 (scope-drift) — nearly universal in the field family (field, date/time/date-range/time-range/color/css/ search/password-field, pin-input, textarea, tags-input, editable). Now the audit's single most pervasive finding across ~33 components. The verify even noted the project's own reference (NumberField) shares the condition — so the one SYS-1 decision must also settle whether NumberField/field "count".
  • SYS-3 (test gaps) — the segmented fields under-test their hardest paths: every B4 test is jsdom-only and none exercise the A24 readonly-without-value logger.warn (trivial to assert — the harness already stubs warn), the hidden-input render, the commit events, or APG segment keyboard. password-field + textarea have NO provider test.
  • CLEAN REFERENCE confirmed — NumberField. Correct renderProps (no double-write), direct id-wiring (A30), A13 hidden input, all declared events actually fired. Use it as the field-family template (and the contrast that makes SYS-A30-EFFECT visible).
  • A26 / A13 — verified PRESENT, no findings. Lead grep + agents + verify agree: date/time/color-field have onbeforeinput; the range fields inherit it by composing the single-field Input (A27); number/color/css/tags/pin all render the A13 hidden input. The analyze pass's tentative "missing A26/A13" angles were refuted.

New validator-gap findings from B2 (see VALIDATOR_GAPS)

  • VG-8 — morfo:check does NOT flag a morfo declared with : Morfo instead of as const satisfies Morfo (alert-dialog-001 shipped). The single highest-leverage cheap fix: a lint that greps every *Morfo export for the as const satisfies Morfo closer.
  • VG-9 — nothing flags an undisposed $effect.root in a provider (navigation-menu-006). A lint: $effect.root outside a test whose returned disposer isn't wired into a teardown.

Notes

  • No CRITICAL across 16 components (the one CRITICAL the B2 agents raised was a false positive, refuted).
  • Two B1 HIGH a11y correctness issues (combobox-001 focus mix, dialog-001 role race) remain the most user-impactful; the B2 HIGHs are contract (alert-dialog :Morfo) + a bounded leak (navmenu $effect.root).
  • Positive baselines for later batches: popover (Close-via-Button, no role override, hover guards) and dropdown-menu (roving-focus consistency, uniform renderProps, self-managed cleanup, eidos-in-scope).

Powered by TurnKey Linux.