Skip to content

fix(workstation): close tabs at canonical resource owners - #727

Open
beruro wants to merge 2 commits into
developfrom
junyu/fix-tab-lifecycle-ownership
Open

fix(workstation): close tabs at canonical resource owners#727
beruro wants to merge 2 commits into
developfrom
junyu/fix-tab-lifecycle-ownership

Conversation

@beruro

@beruro beruro commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Problem

Closing a WorkStation Browser tab removed only its visible workspace projection. The live Browser session and its localStorage record remained authoritative, so switching to another surface and back caused reverse synchronization to recreate the supposedly closed tab.

The same ownership ambiguity existed around Terminal teardown and entity-backed Session or Project Org tabs: presentation state could be removed in one surface while the underlying resource or another workspace reference survived.

Solution

  • Move Browser sessions into one canonical Jotai resource store and make explicit close commands remove the owner before clearing every WorkStation projection.
  • Define exhaustive tab ownership, shared-retention, and repository-affinity policies so resource tabs, ordinary shared presentation, and workspace-local tabs have distinct lifecycles.
  • Route single, other, saved, bulk, registry, Session deletion, and Project Org invalidation through symmetric lifecycle actions across every workspace.
  • Replace the shadow Terminal context/provider teardown path with the existing terminal owner store, generation-guarded asynchronous shutdown, immediate local ID rotation, and WorkStation-only PTY filtering.
  • Garbage-collect ownerless shared presentation during workspace disposal and persistence restore while retaining Browser and Terminal records until their owner closes.
  • Persist only changed WorkStation scopes and reconcile stale persisted Browser projections against canonical sessions.

The resulting invariant is: a user close destroys a resource at its canonical owner and then removes all projections; a mere workspace disposal removes only that workspace reference.

Potential risks

  • Browser and Terminal close actions are now global by design. Any caller that previously depended on closing only one visual reference must use workspace disposal or projection removal instead.
  • Terminal PTY shutdown is asynchronous. Generation checks and immediate local ID rotation protect rapid close/reopen, but the real Tauri PTY process path was not exercised manually in this isolated worktree.
  • Existing WorkStation v3 persistence remains format-compatible; stale ordinary shared records may be collected on load. Legacy v2 shared records are retained through the seed-claim path.
  • Rollback is a normal revert of commit aff87f925; no schema, IPC, wire-format, dependency, or lockfile migration is involved.

Verification

  • pnpm exec vitest run src/store/workstation/tabs/__tests__/workspaceState.test.ts src/store/workstation/tabs/__tests__/storage.test.ts src/store/workstation/browser/sessionState.test.ts src/store/workstation/browser/tabs/__tests__/sharedWorkspaceIntegration.test.ts src/store/workstation/codeEditor/terminal/__tests__/terminalAtoms.test.ts src/store/workstation/tabRegistry/atoms.test.ts src/store/session/sessionAtom/__tests__/mutations.test.ts src/store/projectOrgPresentationLifecycleAtom.test.ts src/store/chatPanel/chatPanelSessionInvalidationAtom.test.ts src/store/chatPanel/__tests__/chatPanelTabsAtom.test.ts src/store/chatPanel/__tests__/chatPanelChannelTabs.test.ts src/modules/WorkStation/AppShell/WorkstationTabBar.test.ts src/scaffold/NavigationSidebar/connectors/rustSessionDeleteReceipt.test.ts — 13 files and 133 tests passed after commit hooks.
  • pnpm exec tsc --noEmit --pretty false --incremental --tsBuildInfoFile /var/tmp/orgii-tab-lifecycle-pr.tsbuildinfo — passed.
  • git diff --name-only --diff-filter=ACM -z origin/develop -- '*.ts' '*.tsx' | xargs -0 pnpm exec eslint — passed for every changed TypeScript file.
  • env npm_config_cache=/var/tmp/orgii-codex-madge-cache pnpm check:circular — no circular dependencies across 6,321 modules.
  • git diff --check and staged secret/personal-path/debug-log scan — passed.
  • Repository commit hooks ran lint-staged formatting, ESLint, scoped TypeScript checking, and circular-dependency statistics successfully.
  • The plain pnpm typecheck command was attempted twice but exceeded 120-second and 300-second wall-clock limits while several unrelated repository typechecks were running on the same host. The equivalent incremental tsc --noEmit command completed successfully afterward.
  • Not run: live Tauri Browser/WebView and PTY manual testing; the isolated PR worktree did not launch a desktop runtime.

Architecture audit

All 10 layers were reviewed. Compilation passed; duplicate Browser/Terminal ownership paths were removed; ownership and lifecycle naming is explicit; Browser session, task workspace, and terminal session semantics are no longer overloaded; policy switches are exhaustive without a negative default; cross-surface invalidation is isolated in lifecycle aggregators; context documentation was updated; no wire or serialization contract changed; persistence restore and provider entry paths converge on the same owners; and single, bulk, delete, revoke, restart, and rapid reopen paths have symmetric coverage.

Performance guard

Verdict: pass. The change adds no polling, timers, subscriptions, workers, scans, or hidden-state background work. Active close performs bounded in-memory filtering plus one owner teardown; hidden surfaces do no recurring work; repeated close/reopen uses terminal generations and fresh IDs; multi-workspace references share one owner; restart prunes stale projections. Incremental persistence avoids rewriting unchanged scopes. Unit coverage exercises repeated lifecycle transitions and cross-workspace cleanup; no real-surface profiling was available.

UI evidence

No screenshot is included because this changes lifecycle behavior and persistence only; it does not alter visual layout, styling, loading, empty, or error-state rendering.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants