|
The backend now activates layout providers PER WORKSPACE (the workspace's
per-workspace enablement state picks the ONE active layout plugin), so
toggling a layout plugin in the workspace plugins modal changes which tree
serves the workspace. The modal's toggle now refreshes the layout so the
newly-enabled provider's tree (or the no-provider degrade) lands without a
reload:
- WorkspaceLayoutPort gains refresh(workspaceId) — the modal calls it
after every state write (an attempted write re-probes either way: the
probe is the idempotent catch-up; a refused write changed nothing, so it
re-syncs the same tree).
- App.svelte wires the port to the layout store's new
onWorkspacePluginsChanged(workspaceId) — a guarded re-probe that only
fires for the ACTIVE workspace (another workspace's toggle is picked up
by its own probe on switch, exactly like the other §2.6 triggers).
The per-workspace tree resolution itself is backend-side; the marker
renderer, the probe triggers, and the WS push filtering are
unchanged.
Verification: typecheck (svelte-check 0 errors) / test (1528) / check
/ build all green.
|
|
boundary
PluginTable.svelte renders a keyed each ({#each groups as group
(group.tier)}); groupByTier's old "rest" bucket for unknown tiers fell
back to key "standard", producing a DUPLICATE standard group (and thus an
each_key_duplicate crash in Svelte 5, blanking both manager tables) whenever
tierless rows were present alongside canonical standard rows.
groupByTier now buckets through a Map keyed by the NORMALIZED tier — each
canonical tier can yield at most one group, so duplicate keys are impossible
by construction, and no row is ever dropped. Unknown/missing tiers fold into
the standard bucket rather than forming a separate trailing group.
The normalization (normalizeTier, in types.ts next to tierLabel/TIER_ORDER)
also runs at the pluginNodes boundary, so every node's tier is canonical
before any tier-keyed consumer sees it — including tierLabel's exhaustive
switch, which rendered an empty badge for out-of-contract values. Normalizing
into an existing bucket (option a) was chosen over a synthetic "unknown"
group because it needs no new UI vocabulary and keeps every consumer total.
Tests: all-canonical order, unknown tier ("kernel"), and missing tier each
assert no duplicate keys, no dropped rows, and the right group/row counts
from the Loaded-table view; the boundary test pins pluginNodes' canonical
output. Plugins-feature vitest green (95 tests); svelte-check, biome, and
vite build green.
|