11 KiB
Terminal session disappearance: engineering handoff
Start here
This PR hands off the investigation and immediate safety fixes for the 2026-08-23 terminal-session disappearance incident. It also records the unresolved architectural question: how a persisted PTY should identify the exact process that owns it across Orca restarts and updates.
The short version is:
Orca sometimes treated “I cannot find the daemon that owns this terminal” as “the terminal exited.” It then deleted the terminal's saved UI state. The current branch prevents that data loss, but it does not yet replace the underlying daemon-identity architecture.
Background
During a failed application update, Orca restarted several times. Protocol-v36 daemon PID 6835
became unavailable for a reason the retained evidence cannot identify, and PID 26391 became the
new v36 daemon.
Starting 26391 was reasonable. A new compatible daemon is needed for new terminals when the old
endpoint cannot be used. The problem was what happened to persisted terminals whose exact owner
could no longer be reached.
Orca's daemon endpoint is primarily named by protocol version, for example daemon-v36.sock.
Protocol 36 is like a language both processes speak; it is not the identity or address of one
specific daemon process. Once a different v36 daemon occupied the endpoint, the app could not
reliably reconstruct which exact daemon incarnation owned every historical PTY.
User impact
The stable before/after profile comparison found:
| Measure | Result |
|---|---|
| Removed terminal rows | 158 |
| Affected worktrees | 61 |
| Represented pane PTYs | 191 |
| Primary PTYs with explicit exit/kill evidence | 20 |
| Primary PTYs with no authoritative exit evidence | 138 |
| Unverifiable primary PTYs with surviving history | 138 |
At least 41 detached agent processes were still alive in the post-incident process observation. That observation does not prove every affected process survived, but it proves the incident was not simply “all terminals exited.” Much of the damage was deletion of persisted topology and UI records.
What actually failed
There were two destructive software paths and one separate physical-kill path.
1. Main converted missing routing authority into false
On terminal visibility resume, the renderer asked whether a saved PTY still existed. The old path
used boolean-oriented hasPty() behavior. When no currently registered daemon adapter recognized
the PTY, the router returned false.
That answer only established:
the current adapter registry cannot locate the session
The renderer interpreted it as:
the owning host proved that the session exited
It then ran the PTY-exit close path and persisted the terminal row and unified tab as deleted.
2. Renderer cleared a binding after an owner-unverified reattach
The first guarded build fixed tri-state liveness in main, but live recovery exposed another path.
Main correctly reported terminal_pane_owner_unverified; the renderer then received no reattach
result, cleared the saved binding, and could start a replacement shell. The displayed toast said
the old session was left untouched even though the binding was not preserved.
The second branch fix preserves the old binding and refuses to fresh-spawn solely because owner authority is unavailable.
3. Missing-worktree cleanup killed a separate subset
A burst of 15 graceful daemon kills strongly matches a successful-but-partial worktree scan being used as destructive authority. Those are real terminal stops and need an independent fix. They do not account for the bulk of the 158 removed rows.
Why PID 6835 is not the root cause of the bulk loss
PID 6835 becoming unavailable could strand raw PTYs that it actually owned. It cannot explain
the entire incident set:
| Frozen primary-PTY verdict | Latest event from 6835 |
Latest event from another daemon PID |
|---|---|---|
unverifiable |
5 | 133 |
exited |
15 | 5 |
Historical event provenance does not prove current liveness. It does show that loss of one daemon cannot be used as exit evidence for the 138-session unverifiable set.
Recovery-v2 supplied a useful control case: a newer app retained the still-live hourly daemon PID
26391, despite its older build metadata, and preserved all 131 complete affected topologies that
remained in tracked worktrees. Same-protocol app/daemon version skew is therefore supported and is
not inherently destructive.
Confirmed versus unknown
Confirmed
- The intended ad hoc update did not install during the incident.
- The installed and intended bundles both used daemon protocol 36 and state schema 1.
- PID
6835stopped being reachable and PID26391became the current v36 daemon. - The boolean liveness path could convert missing adapter registration into an exit decision.
- PTY-exit handling could delete both persisted terminal models without killing the underlying descendant process.
- The renderer had a second owner-unverified binding-clearing defect.
- Only 20 primary PTYs have explicit latest exit/kill evidence in the frozen catalog.
- Recovery-v2 preserved topology and successfully continued one saved Codex provider session in place under a new raw session incarnation.
Unknown
- What stopped PID
6835. - Whether every one of the 158 deletions traversed the same renderer close callback.
- Which historical raw PTYs or descendants remained live at each later observation.
- Whether a missing route meant the owner was dead, unreachable, omitted from registration, or hidden behind same-protocol endpoint replacement for each individual session.
Unknown must remain unverifiable; it must not be rewritten as exited.
Immediate fixes included on this branch
25ae8e8edf: preserve unverifiable terminal sessions
- Waits for local PTY-provider startup before answering liveness.
- Prefers tri-state
probePtyLiveness()over booleanhasPty(). - Returns
nullwhen the provider or route cannot authoritatively answer. - Allows destructive reconciliation only on literal authoritative
false.
bd09097708: preserve owner-unverified terminal bindings
- Recognizes
terminal_pane_owner_unverifiedduring reattach. - Preserves the saved pane/tab PTY binding.
- Prevents the missing reattach result from being treated as a stale/dead session.
- Avoids starting a replacement shell over an uncertain old owner.
These commits are immediate data-loss guards. They are not the permanent daemon-identity design.
Fundamental problem
The current architecture partially conflates three different concepts:
protocol compatibility: “can we speak?”
endpoint slot: “where is the current v36 daemon?”
owner identity: “which exact process owns this PTY?”
A persisted session needs the third answer. Asking every currently registered compatible adapter whether it recognizes an ID is not a durable ownership model.
The permanent invariant should be:
Only the matching execution host, owner incarnation, and session incarnation may establish that a raw PTY is live or exited. Missing routing/contact is
unverifiable.
Rough permanent solutions
The architecture document compares two approaches in detail.
Approach A: incarnation-addressed daemons
Give every daemon process a UUID and unique endpoint. Persist that daemon incarnation with every session. New sessions use a current compatible daemon; existing sessions always route to their recorded owner.
logical PTY -> session incarnation -> daemon incarnation -> unique endpoint
This is the smaller retrofit. It eliminates protocol-slot identity collisions and adapter-scan ownership inference, but long-lived sessions may retain multiple daemon generations. If the owning daemon crashes, its PTY masters are still lost.
Approach B: stable PTY host plus replaceable control daemon
Move PTY ownership into one deliberately small, long-lived host. Keep fast-moving Orca behavior in a replaceable control daemon.
app -> replaceable control daemon -> stable PTY host -> PTYs
This bounds PTY-owning daemon accumulation and lets raw terminals survive app/control-daemon replacement. It is a larger cross-platform refactor, adds an IPC hop, and makes the stable host a single failure domain. If the stable host itself crashes, its PTY masters are still lost.
Decision that remains
- Choose Approach A for the smallest safe ownership correction.
- Choose Approach B if a hard steady-state process bound and control-daemon crash survival justify the larger refactor.
- If PTYs must survive failure of the process that owns the PTY master, neither approach is enough; that requires per-session hosts or another independently surviving owner.
Suggested next steps for the new owner
- Review the immediate fixes independently from the permanent architecture decision.
- Confirm whether product requirements include raw PTY survival across control-daemon failure, PTY-owner failure, or only app updates.
- Measure the existing daemon's idle memory/CPU and the expected number of simultaneous protocol generations.
- Select Approach A or B using the architecture document's comparison and performance gates.
- Define the persisted owner schema, endpoint authentication, migration state machine, and mixed-version behavior before implementation.
- Keep the legacy discovery path non-destructive during migration.
- Fix missing-worktree teardown independently; do not couple it to daemon identity.
Reading order and PR artifacts
TERMINAL_SESSION_DISAPPEARANCE_HANDOFF.md— this plain-language entry point.PTY_DAEMON_IDENTITY_ARCHITECTURE_DESIGN.md— permanent approaches, tradeoffs, migration, and acceptance criteria.TERMINAL_SESSION_DISAPPEARANCE_INCIDENT_2026-08-23.md— full incident timeline, causal analysis, and immediate fixes.RECOVERY_CATALOG.md— frozen per-session classifications and limitations.POST_CUTOVER_RECOVERY_STATUS.md— first guarded cutover, live accounting, and discovery of the renderer-side binding defect.RECOVERY_V2_CUTOVER_STATUS.md— controlled upgrade and provider-continuation validation.
The PR intentionally excludes machine-specific app bundles, DMGs, live profiles, terminal-history payloads, and large raw logs. The included documents retain the relevant counts, hashes, limitations, and evidence locations without making the PR unsafe or impractically large.
Scope boundary
Preventing application activation during an update window is intentionally out of scope for this handoff. It is recorded in the incident timeline but should be handled separately from PTY owner identity and liveness authority.