# Audit: tree-grid audit-version: 1 audited-at: 2026-06-26 scope: method: adversarially-verified workflow (analyze → refute); HIGH lead-verified by direct read of the cited code. B5 ground-truth: the A31 O(N²) isSelected/isExpanded (.includes from a per-item $derived) is confirmed across listbox/grid-list/tree-view/tree-grid/tag-group (SYS-7); rovingTargetEl is correctly LIFTED everywhere (not A31); virtual-* use SvelteMap (A33-clean). provider: G:\dev\svelte\vicen\src\uix\soma\components\tree-grid\tree-grid-provider.svelte.ts reactivity (A31/A33/A35): HAZARD A31 FOUND at tree-grid-provider.svelte.ts:578-579. Per-row isSelected and isExpanded $derived call provider.isSelected()/isExpanded() which read opts.value/expanded arrays via .includes(). When arrays change, all N rows re-derive and call O(N) array lookups → O(N²) total. Fix: provider.selectedSet/expandedSet with Set.has(). ## Summary Counts (post-verification): CRITICAL 0 · HIGH 1 · MEDIUM 0 · LOW 0. ## Findings ### HIGH: A31 — per-item/per-row $derived calling provider method that reads global state — tree-grid-001 - dimension: B, Reactivity - rule: A31 — per-item/per-row $derived calling provider method that reads global state - location: src/uix/soma/components/tree-grid/tree-grid-provider.svelte.ts:578-579 - evidence: readonly isSelected = $derived.by(() => this.provider.isSelected(this.opts.value.current)); readonly isExpanded = $derived.by(() => this.provider.isExpanded(this.opts.value.current)); - impact: O(N²) reactivity: when value[] or expanded[] arrays change, every row's isSelected and isExpanded deriveds re-run, each calling provider.isSelected()/isExpanded() which do .includes() lookups on those arrays. With 50+ rows and 10+ selections, this causes quadratic re-derives and array scans, manifesting as lag on selection/expansion. - proposed-fix: Lift a Set on the provider: `readonly selectedSet = $derived(new Set(this.opts.value.current))` and `readonly expandedSet = $derived(new Set(this.opts.expanded.current))`. Each row then does O(1) `.has()` instead of O(N) `.includes()`. Per-item derived becomes `readonly isSelected = $derived.by(() => this.provider.selectedSet.has(this.opts.value.current))` → O(1) per row. - verify: [confirmed] CONFIRMED A31 O(N²). tree-grid-provider.svelte.ts:578-579 per-row `readonly isSelected = $derived.by(() => this.provider.isSelected(this.opts.value.current));` and `readonly isExpanded = $derived.by(() => this.provider.isExpanded(this.opts.value.current));`. The provider methods read GLOBAL arrays via O(N) `.includes()`: line 188-190 `isSelected(value){ return this.opts.value.current.includes(value); }` and line 147-149 `isExpanded(value){ return this.opts.expanded.current.includes(value); }`. When the global `value[]` or `expanded[]` array mutates, every row's two deriveds re-run, each scanning the array → quadratic. NOT a lifted Set: the only Set in the file is the single provider-level `rovingTargetEl` (line 141), unrelated to selection/expansion. Matches the documented Listbox/Command A31 shape exactly. HIGH justified (two O(N) deriveds per row; degrades at 30+ rows on each selection/expansion). Fix as proposed: lift `selectedSet`/`expandedSet` `$derived(new Set(...))` on the provider and have rows do O(1) `.has()`. - fix-status: open ## No-findings dimensions A, C, D, E, F, G ## 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: expansion; range-select; visible-rows-filtering; keyboard-navigation; row-click-modes; selectAll; disabled-guards - untested: A31 reactivity hazard (> 50 rows with dynamic selections); expandedSet/selectedSet optimization verification ## Style observations (non-blocking) - CSS uses canonical theme variables (--space-*, --radius-*, --color-* roles, --font-size-*, --leading-*, --duration-*, --ease-*, --opacity-disabled, --z-index base scales). No magic hex or opacity decimals. Indent guides use computed background gradients (lines 275-336) with no-repeat per level; graceful degradation beyond depth 8. Striping targets aria-level=1 only (line 212), limiting scope correctly.