feat(dock): panels carry their own target — retire the single scope (PR-C2) #63
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/dockview-pr-c2"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Targets
feat/dockview-pr-c1(#62), notmain. Stack:main← PR-A #58 ← PR-B #61 ← PR-C1 #62 ← this. Final PR of the dockview arc.Every dock panel now carries its own
{workerId, sessionId}. Several sessions can be watched side by side, and an artifact landing in a session you're not looking at opens its own tab without stealing focus. That last sentence is what this whole three-PR arc was pointed at.Spec:
docs/superpowers/specs/2026-07-12-dockview-migration-pr-c-design.md§4Plan:
docs/superpowers/plans/2026-07-12-dockview-pr-c2.mdWhat changed
A panel is a
(view, scope)pair — idterminal:w1:s2,files:w1:@workspace.ExplorerScopeis all scalars, so the target rides in dockview'sparamsand survives a reload for free: no side table, no id-to-state reconciliation.DockDepsshrinks from "the single-scope model in object form" to services only.ArtifactsStore+ oneWorkerFsProviderper target that has an open panel — not one per panel, not one globally. Lifecycle is derived fromdock.panelson every layout change rather than refcounted, because a counter can drift out of sync with the dock and a derivation cannot.liveness()is three-valued. A session that is confirmed gone has its panels closed; a failed poll reportsunknownand closes nothing. Same contract asconvergeSessions, which never hard-deletes a session row when the worktree probe fails (AGENTS.md fact #15).[view icon] + session name, tinted per session. Two terminals were otherwise indistinguishable. Colour is derived from the target key, so it survives reloads and agrees between list and dock with nothing persisted. It is redundant encoding — the name is always there — so a colour-blind operator loses nothing.@lucide/svelte(ISC, tree-shaken, Svelte 5 native).terminal/folders/gallery-vertical-end, icon-only, nativetitletooltips.Buried
scope,scopeLabel,terminalScope, the whole scope↔URL layer,handleCardTap,PERMANENT_PANEL_IDS,Dock.resetLayout(), and the permanent-panel top-up loop. The 250ms terminal debounce also died — it existed only to stop one shared terminal thrashing as you clicked through sessions, and clicking a session now opens a panel rather than re-pointing one, so the thrash is structurally impossible.Accepted loss: the URL no longer carries the session. With N panels open there is nothing sensible to encode, so localStorage layout is the sole state and a session is no longer deep-linkable.
Three bugs the test suite could not see
vitestrunsenvironment: 'node'with no DOM, so.sveltecomponents are structurally untestable. All three of these were design errors of mine, and all three were caught by driving the real UI:Panels froze on "Connecting…" forever. The service registry's
Mapis deliberately plain (making it reactive would invite the self-triggering-effect trap), but panels read it through$derived(services.providerFor(scope))— an ordinary call into a plain Map, with no reactive dependency to invalidate it. It computed once, at mount, and froze. And mount is too early: dockview mounts panels synchronously insideaddPanel/fromJSON, whileonDidLayoutChangefires on a microtask. So opening Files as a session's first panel — or reloading with one saved — never resolved. Fixed with an epoch counter bumped only when the registry's contents actually change (bumping on everysync()would churnArtifactPanel's controller on every drag frame).Tabs had no close button. Our custom
SessionTabreplaces dockview's default tab, which is what provided the close action. Combined with deleting "reset layout" on the grounds that panels are individually closable, the dock became a one-way door. Two reviewers missed this; only clicking around found it.Closing the last panel didn't stick.
saveLayoutbailed when nothing survived the artifact strip — harmless in PR-A, where three permanent panels made an empty dock impossible. C2 deleted permanent panels, so an empty dock is legitimate, and refusing to save it left the previous layout in storage, resurrecting on reload the very panels the operator had just closed.Each fix is pinned by a mutation-verified test (revert the fix → exactly the intended test fails, for the right reason).
Verification
pnpm typecheck→ 0 errors (4762 files).pnpm test→ 643 passing (71 files), up from 617.Browser pass on a seeded throwaway instance (dummy credentials; the operator's real
nexus.dband claude session were never touched):Still unverified, across the whole arc
Touch drag on real hardware. It rests on reading dockview's pointer backend (
LongPressDetector, 500ms/8px) plus headless checks, not a physical device. Worth one pass on a real phone before any of A/B/C merges.