# Audit: table 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: src/uix/soma/components/table/table-provider.svelte.ts reactivity (A31/A33/A35): ">ALERT A31 DUAL HAZARD: (1) Line 296: per-row $derived.by reading global selection state via getIsRowSelected() — O(N) re-runs when selection changes. (2) Line 518: per-row $derived.by calling getVisibleColumns().length — O(N*M) filter ops for N rows and M columns. Both are HIGH severity and should be lifted to provider-level caches with O(1) per-item lookups." ## Summary Counts (post-verification): CRITICAL 0 · HIGH 0 · MEDIUM 1 · LOW 0. ## Findings ### MEDIUM: A31 (per-item derived calling filtering operation) — table-002 - dimension: B (Behavior / Reactivity) - rule: A31 (per-item derived calling filtering operation) - location: src/uix/soma/components/table/table-provider.svelte.ts:518 - evidence: readonly colSpan = $derived.by(() => this.provider.table.getVisibleColumns().length); In TableRowDetailProvider (per-row), calling getVisibleColumns() which internally filters resolvedColumns array. This runs on every row. - impact: For a table with N rows and M columns, showing a row detail calls getVisibleColumns() which is O(M) filter operation. Total complexity becomes O(N*M). On a 50-row table with 10 columns, that's 500 filter operations just to compute colSpan. - repro: Create a table with visibility toggling. Show row details and toggle column visibility. Monitor performance and observe the getVisibleColumns() is called per row on every visibility toggle. - proposed-fix: Move `visibleColumns` to a table-level $derived and cache it: `const visibleColumns = $derived.by(() => resolvedColumns.filter(col => getIsColumnVisible(col.id)))`. Each RowDetail then accesses a single cached array and reads .length in O(1). - verify: [downgraded] Confirmed the A31 pattern but HIGH overstated → MEDIUM. table-provider.svelte.ts:518 `readonly colSpan = $derived.by(() => this.provider.table.getVisibleColumns().length)` is a per-RowDetail derived calling a provider method (table-core.svelte.ts:532-534) `function getVisibleColumns() { return resolvedColumns.filter((col) => getIsColumnVisible(col.id)); }` — O(M) filter reading the global `columnVisibility` $state. It re-runs whenever `columnVisibility` is reassigned (toggleColumnVisibility line 537 `columnVisibility = { ...columnVisibility, [columnId]: ... }`). So the pattern IS real. But the candidate's O(N*M) and 'hang at 30+ rows' framing is wrong on two counts: (1) RowDetailProviders are mounted one-per-rendered-row, and the rendered set is PAGE-BOUNDED (default pageSize=10, table-core.svelte.ts:446) — not total row count N; (2) the trigger is a column-visibility toggle, a rare user action, not a per-keystroke / per-selection mutation. Real cost is O(pageSize × M) per infrequent toggle, with M (column count) small and the filter cheap. That is a legitimate micro-optimization (lift `visibleColumns` to a single table-level $derived so each RowDetail reads `.length` in O(1)) but not a behavior/scaling hazard users hit — MEDIUM, not HIGH. - fix-status: open ## No-findings dimensions A (Contract/Morfo), C (DOM-selector safety), D (Frontier / Eidos imports), E (TSC / Theming), A34 (require() topology) ## Theming facts (E-bis) - magic z-index: Acceptable local z-index: 2 within sticky header subtree (line 190 of table.css) — local stacking context, not global - magic literals: none - undeclared parts: none - roles clean: true · variants clean: true ## Tests (F) - exists: true · env: jsdom - covers: provider props mapping; row selection toggling; row detail open/close; sort state; pinned column styling - untested: A31 quadratic hazard with large row counts; column visibility changes with row details; multi-row bulk operations (toggleAllSelection); sorting + detail interaction