Repository navigation
[Bug]: Editor tabs won't stay closed — duplicate openFilesByWorktree + multi-identity unifiedTabs resurrect siblings (1.4.193) #17778
Description
Environment
- OS: macOS 26.5 (Apple Silicon)
- Orca: 1.4.193
- Repro surface: Matri agent-platform reactor worktree (
c5adca14-…::…/matri-agent-platform), also visible on Factory reactor
Summary
Closing editor tabs for certain files does not stick. The tabs reopen / pop back (user-visible on Matri reactor for agents/reporting-agent/src/reporting.ts and docs/runtime-interface-approval.md).
This is not pin-confirm UX (isPinned is false). It is session-store resurrection of sibling editor identities.
Classification (relatedness)
| Issue | Domain | Same bug? |
|---|---|---|
| #17775 | Main CPU / git.exec + FS storm |
No — different symptom and path |
| #17776 | Orphan / path-missing worktreeMeta, no CLI remove |
No — dead meta, not editor-tab identity |
| Factory agent orphan-store work | Dangling registered worktrees / meta sweep | No — different store maps |
| This issue | Editor session persistence: duplicate openFilesByWorktree + multi-identity unifiedTabs |
Yes (new / third) |
Shared backdrop: large workspaceSession maps and multi-runtime pairing make the bug easier to hit, but the close/hydrate contract for editors is an independent defect.
Observed durable state (orca-data.json → workspaceSession)
Duplicate openFilesByWorktree rows (same path + same runtime, repeated)
| Worktree | File | Copies |
|---|---|---|
| matri-reactor | runtime-interface-approval.md |
4 |
| matri-reactor | reporting.ts |
3 |
| factory | storage-admission.ts |
5 |
| factory | 835-design.md |
4 |
Hydrate code already comments that repeated (path, worktree, runtime) tuples are corruption (hydrate-editor-session.ts), and skips duplicates on load — but the store keeps rewriting them, and unifiedTabs is not kept in lockstep.
Dual / multi unifiedTabs identities for one entityId
Same file path appears under more than one tab id:
- Legacy bare path id:
/Users/…/reporting.ts - Owned id:
editor:{worktree}:{runtimeEnvironmentId}:{filePath}(buildOwnedEditorFileId) - Stale second runtime env id
d289656c-…and current paired Dell env189aaae7-…(only189aaae7remains inorca-environments.json)
Example (matri-reactor reporting.ts): both the bare path id and the editor:…:189aaae7-…:… owned id are present for the same entityId.
isPinned is false on these rows.
Why close does not stick (code pointers)
closeFile(fileId)removes oneopenFilesentry by exactid, then finds oneunifiedTabsentry withentityId === fileIdandcloseUnifiedTab, thenbreak(close-file-action.ts). Sibling identities for the same path stay in the store.- Persist path writes
openFilesByWorktreewithout stable ids (path +runtimeEnvironmentIdonly). Hydrate rebuilds ids via collision / owned-id rules (hydrate-editor-session.ts,editor-file-ids.ts). unifiedTabskeeps older id forms (legacy path id + owned ids for retired runtime envd289656c-…). After a partial close, hydrate / reconcile / host mirror can resurrect the remaining siblings.- Product comment already admits reopen risk for mirrored editors: "the host must close its copy or its next snapshot re-mirrors the file and the tab reopens" (
close-file-action.ts/recently-closed-editor-tabs.ts).
Expected
- Closing an editor tab removes all session identities for that file owner (every
openFilesrow + everyunifiedTabsrow sharing the path/entity), including stale-runtime owned ids. - Persist must not accumulate duplicate
(filePath, runtimeEnvironmentId)rows inopenFilesByWorktree. - Hydrate / migrate should collapse legacy bare-path ids and owned ids for the same owner, and drop owned ids whose
runtimeEnvironmentIdis no longer a known environment. - Users should not need to quit Orca and hand-edit
orca-data.jsonto make a close stick.
Local workaround intent
Operator-side detector will alert when sticky identity count exceeds a threshold and offer a quit+surgical clear. That is mitigation only — product fix still needed in close/hydrate/persist.
Related
- [Bug][macOS]: Main ~100% CPU / fan heat persists on 1.4.193 — git.exec + FS storm with 98.7% path-missing worktreeMeta (#15412/#17278 incomplete) #17775 (heat) — separate
- [Bug]: worktreeMeta/session orphans never swept after project deregister — 98.7% dangling rows; no orca repo remove #17776 (orphan meta) — separate
- Editor id ownership:
src/renderer/src/store/slices/editor/file-ids/editor-file-ids.ts - Close:
src/renderer/src/store/slices/editor/actions/close-file-action.ts - Hydrate:
src/renderer/src/store/slices/editor/actions/hydrate-editor-session.ts
Activity
shahidbeig-a11y commentedon Sep 1, 2026
Operator mitigation (local)
Installed
orca-sticky-tab-watchon this Mac:openFilesByWorktreerows + multi-identityunifiedTabschoose from listcom.mo.orca-sticky-tab-watchevery 5 minutesThis is mitigation only; product fix still needed on close/hydrate/persist as described above.
shahidbeig-a11y commentedon Sep 1, 2026
Cross-host confirmation (2026-08-31)
Same sticky / dual-runtime resurrection is present on Dell Orca as an independent store — not only on the Mac host that filed this.
Env pairing (each host names the other)
~/Library/Application Support/Orca/profiles/local-default/orca-data.json189aaae7-…= Dell Orca~/.config/orca/profiles/local-default/orca-data.jsond289656c-…= Mac OrcaLive Dell sticky (store mtime ~19:58 PT, detector threshold 3)
Router-and-Hooks/anthropic_usage.py—openFiles×4 stampedruntime=d289656c(Mac’s id on Dell)matri-reactor/PRD.md—openFiles×5 stampedruntime=d289656cunifiedTabsmulti-id for the same entity: legacy UUID +owned-…:d289656c-…+owned-…:189aaae7-…Live Mac sticky (same detector, same window)
matri-reactor/runtime-interface-approval.md—openFiles×2 stampedruntime=189aaae7(Dell’s id on Mac)835-design.md/storage-admission.ts— duplicate openFiles + owned twins for both189aaae7and stale/locald289656cRead
Closing one tab id on either host leaves the cross-runtime sibling in that host’s store; hydrate / host-mirror can reopen it. Pairing Mac↔Dell makes the twin-id pattern the default, not an edge case.
No clear/quit performed for this check — read-only copies only.
rikimaru63 commentedon Sep 7, 2026
Additional reproduction on Orca 1.4.197, source base
04aa03ad620206ec13b00d5204f14ea579b1b7b9(macOS arm64). This distinguishes two intentionally separate editor views from corrupt duplicate identities.Using disposable profiles and a paired Electron client, I created two Markdown editor tabs with different tab IDs but one file entity, plus a live terminal. The test clicks the client's visible close button for the second view. It delays only that real
session.tabs.closeRPC, opens a marker file on the host to force a newer snapshot containing both original views, then releases the RPC.Two distinct paths need attention:
useTabGroupTabCloseCommands.ts, the shared-file branch must send the exact view ID and owner to the host and record the close intent. Otherwise closing only the local unified tab lets a host snapshot restore it.closeMobileSessionTabInStoreinruntime/mobile-session-tab-close.tscallsstore.closeFile(unifiedTab.entityId)even for an exact view ID. The real host snapshot then loses the remaining legitimate view too. Before the host-side fix, the isolated E2E reached the correct host RPC and resisted resurrection during the delay, but failed because only the marker file remained after the host processed the close.For an exact tab ID with another editor view referencing the same entity, the host should close that unified view while retaining the shared file and draft. The final-view and legacy file-ID paths still need their existing file cleanup. Exact tab IDs must also take precedence over another tab's matching entity ID.
A local candidate covers those branches. The host regression tests use the real store and check that the other view, unsaved draft, and terminal survive, and that final-view cleanup still works. The candidate has not been installed into the working Orca app. This does not establish a fix for every stale-owner identity, PDF case, restart path, or reported selection flicker.
After rebuilding, the paired-Electron test passed. A final run also waited for the retained view's actual body text to be visible. The host snapshot advanced through the delayed close, removed only the requested view, and retained the other view plus the same terminal surface ID and handle. The snapshot does not supply terminal incarnation IDs, so this is not a separate proof of process-incarnation continuity. The 58 focused store/cleanup tests, existing web TypeScript check, and targeted lint also passed. No working profiles or client files were used in these tests.
fix(editor): address review findings on the editor record leak fix
fix(editor): heal duplicate and mis-owned editor records at restore
test(tools): add a background repro for duplicate editor records in a…
fix(editor): address review findings on the editor record heal
shahidbeig-a11y commentedon Sep 19, 2026
Field update — Orca 1.4.205 (Linux) / 1.4.204–205 (macOS), four paired hosts, 2026-09-18
Still reproduces on 1.4.205, so #17370 (dedupe at restore) does not cover it. New measurements that may help #21124 / #21125 / #10926. Paths, hosts and ids are masked.
1. The store grows on every write, not only at restore
Same file, same worktree, same
runtimeEnvironmentId— byte-identicalopenFilesByWorktreerows. References to one file path across consecutive writes oforca-data.json:Exactly +13 per write. In a controlled run I cleaned the store to 1 row per host with Orca fully down, started all hosts, and let them sync: 1 → 21 rows in about 2 minutes, with no user action. This is the write-path leak that #21124 describes; the field data supports merging identical rows before the write.
2. Zero local origin — every row was a mirror of a mirror
On all four hosts, 42 of 42
openFilesByWorktreerows hadruntimeEnvironmentIdset. No host held the file as a local tab. The file exists on one host only; the other hosts do not have the path at all. So:selector_not_found), and the renderer retries — this is the flicker in [Bug] Flickering/Broken file tabs in workspace, unable to close/delete #21189;Note for #21277: it keeps
selector_not_foundretryable (correct for a transient miss), so this case stays open after that merge. Here the miss is permanent — the path does not exist on the receiver.3. Environment ids are per-host, so "unknown id" is not "dead id"
Each host gives the same peer a different local environment id. I first read the unfamiliar ids inside
editor:<worktree>:<env>:<path>tab ids as dead environments. They were live peers under the other host's name for them. This is the perspective problem of #10859; any cleanup that compares ids across hosts will be wrong.4. Why de-duplication alone does not hold (workaround data)
Offline dedupe of
openFilesByWorktree+unifiedTabs(the workaround from #17185) failed three times: one surviving mirror row per host is enough seed for the echo to regrow. What held:runtimeEnvironmentId(all mirrors, no survivor), prune the ids fromtabGroups[*].tabOrder/recentTabIds/activeTabId,activeTabIdByWorktreeandactiveFileIdByWorktree. LeavecontentType: "terminal"alone.Result: 0 rows on every host after 3 minutes of sync (before: 21 in 2 minutes). Mirrors are derived data, so a file that is really open on a peer comes back by itself. One missed host re-seeds all the others.
Suggested acceptance case for the open PRs
Three reciprocally paired hosts; a mirrored editor row for a path that exists on none of the receivers; no local origin on any host. Expected: the row count does not grow across sync rounds, and a close on any host is final.
AI disclosure: measurements and write-up were produced with an AI coding agent working on my hosts; I checked the result on the machines.
nwparker commentedon Sep 27, 2026
Closing as a duplicate of #21121, which consolidates the editor-tab resurrection and duplicate session identity reports. The cross-host mirror echo evidence from this issue has been carried over to #21121 as an acceptance case.