Skip to content

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

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

fix(workstation): close tabs at canonical resource owners#727
beruro wants to merge 5 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 surfaces could 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 while the underlying resource or another workspace reference survived. In addition, initialized xterm/WebGL renderers stayed mounted for inactive terminal sessions and hidden keep-alive tabs, retaining renderer/listener resources the user could not see.

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.
  • Mount only the active terminal renderer while its host tab is visible. Hiding or switching unmounts xterm and detaches its stream without closing the Tauri-owned PTY; remount reconnects from the backend snapshot.
  • 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.
  • Integrate the latest develop; the one AppShell conflict keeps upstream status-bar visibility while preserving this PR's removal of the obsolete terminal teardown hook.

The resulting invariant is: a user close destroys a resource at its canonical owner and then removes all projections; workspace disposal removes only that workspace reference; heavy terminal renderers exist only for the terminal the user can see.

Potential risks

  • Browser and Terminal close actions are 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.
  • Unmounting an inactive xterm intentionally drops renderer-local selection, viewport, and search UI state. Process state remains owned by Tauri and output is restored from its bounded snapshot, but long hidden output beyond that backend bound may not be present when remounted.
  • 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 this PR; no schema, IPC, wire-format, dependency, or lockfile migration is involved.

Verification

  • After integrating latest develop: pnpm exec vitest run over the 4 terminal lifecycle files, statusBarVisibility.test.ts, and terminalAtoms.test.tsPASS, 6 files / 32 tests.
  • Terminal increment before integration: the 4 lifecycle files — PASS, 4 files / 8 tests.
  • pnpm exec eslint over AppShell and the three changed terminal render components — PASS.
  • pnpm typecheck after the merge/conflict resolution — PASS, real full TypeScript check.
  • Commit hook for the terminal increment ran lint-staged, ESLint, and scoped TypeScript checking — PASS.
  • Merge commit intentionally used --no-verify so the old hook would not lint every unrelated upstream file; the focused 32-test run, ESLint, full typecheck, and git diff --check ran afterward.
  • Earlier post-integration lifecycle suite — PASS, 13 files / 134 tests.
  • Earlier full changed-file ESLint, incremental tsc --noEmit, circular-dependency scan across 6,321 modules, diff check, and secret/personal-path/debug-log scan — PASS.
  • pnpm verify:quick and pnpm verify:final are not available on this branch's current base (the new entry points are in separate PR chore(quality): add scoped verification workflow #801), so they were not run.
  • 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. Browser/Terminal ownership is canonical; resource and presentation semantics are distinct; lifecycle policy switches remain exhaustive; cross-surface invalidation stays in lifecycle aggregators; PTY identity survives renderer detach but not canonical close; no wire/serialization contract changed; persistence restore and provider entry paths converge on the same owners; and single, bulk, delete, revoke, hide, switch, restart, and rapid reopen paths have symmetric coverage. No layers were skipped.

Performance guard

Verdict: pass. The change adds no polling, timers, subscriptions, workers, or scans. A visible terminal owns at most one xterm/WebGL surface per host; hidden terminals retain zero renderer streams. Active close performs bounded in-memory filtering plus one owner teardown; repeated switch/hide detaches and remounts cleanly; terminal generations protect rapid reopen; multi-workspace references share one owner; restart prunes stale projections. The remaining bounded-snapshot tradeoff is documented above.

UI audit and evidence

Frontend UI audit reports for TerminalCore, TerminalMainContent, and TerminalTabRenderer record 0 fix candidates, 0 UI hits requiring keep exceptions, and 0 abstraction candidates. The change introduces no raw interactive elements, arbitrary Tailwind values, hardcoded visual literals, or accessibility regressions.

No screenshot is included because the acceptance criterion is visual parity; the change affects hidden/active resource mounting, not layout, styling, loading, empty, or error-state appearance.

@beruro
beruro force-pushed the junyu/fix-tab-lifecycle-ownership branch from 51805c3 to 9dff00a Compare August 10, 2026 04:11
@beruro
beruro force-pushed the junyu/fix-tab-lifecycle-ownership branch from 9dff00a to 30bb5ac Compare August 10, 2026 05:24
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