Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
9a5a078
fix(windows): serialize worktree terminal teardown
bbingz Jul 11, 2026
22f1837
Merge origin/main into fix/windows-terminal-daemon-worktree-removal
Jul 12, 2026
ee5b793
test(runtime): cover sequential worktree teardown isolation
Jul 12, 2026
525c190
perf(pty): use targeted liveness for verified stops
Jul 12, 2026
70da22d
perf(ssh): target PTY liveness checks
Jul 12, 2026
3d72336
fix(runtime): fence worktree terminal lifecycle
Jul 12, 2026
9ea9f2e
fix(runtime): cover headless worktree PTY teardown
Jul 12, 2026
0c9809e
fix: bound terminal teardown verification
Jul 12, 2026
61d9d2c
fix: drain terminals during orphan cleanup
Jul 12, 2026
9298ed6
Merge remote-tracking branch 'origin/main' into review-pr8284-public-…
Jul 12, 2026
e8ae9b0
Merge remote-tracking branch 'origin/main' into review-pr8284-public-…
Jul 12, 2026
48b3dc9
fix: fail closed on unverified SSH teardown
Jul 12, 2026
60e7cb3
docs(runtime): clarify SSH teardown contract
Jul 12, 2026
4993b8c
fix(ssh): reap full PTY session on teardown
Jul 13, 2026
1599f5a
fix(ssh): support Darwin PTY session teardown
Jul 13, 2026
d412681
fix(terminals): fail closed on incomplete teardown
Jul 13, 2026
8fa167f
fix(terminals): verify bounded teardown to completion
brennanb2025 Jul 13, 2026
5da17e6
Merge remote-tracking branch 'origin/main' into review-pr8284-public-…
brennanb2025 Jul 13, 2026
7dc6aea
fix(terminals): fail closed across remote teardown gaps
brennanb2025 Jul 13, 2026
89544be
Merge remote-tracking branch 'origin/main' into review-pr8284-public-…
brennanb2025 Jul 13, 2026
0e904fa
test(relay): make POSIX teardown coverage deterministic
bbingz Jul 13, 2026
b1e478a
Merge remote-tracking branch 'origin/main' into fix/windows-terminal-…
bbingz Jul 13, 2026
1569ef7
test(relay): await Windows immediate teardown
bbingz Jul 13, 2026
bf1d215
fix(terminals): scope and verify destructive teardown
brennanb2025 Jul 13, 2026
333941d
Merge remote-tracking branch 'origin/main' into review-pr8284-public-…
brennanb2025 Jul 13, 2026
ba8e22a
Merge remote-tracking branch 'pr-bbingz-orca/fix/windows-terminal-dae…
brennanb2025 Jul 13, 2026
a280272
fix(relay): support Node 18 session teardown
brennanb2025 Jul 13, 2026
17eae51
Merge remote-tracking branch 'origin/main' into fix/windows-terminal-…
bbingz Jul 13, 2026
5c81d3c
fix(daemon): tolerate legacy teardown exit race
bbingz Jul 13, 2026
d56eb92
Merge remote-tracking branch 'fork/fix/windows-terminal-daemon-worktr…
bbingz Jul 13, 2026
7666c0e
fix(windows): allow ConPTY teardown verification to settle
bbingz Jul 13, 2026
960ba8c
fix(terminals): harden verified process teardown
brennanb2025 Jul 13, 2026
7c16fa9
Merge remote-tracking branch 'origin/main' into review-pr8284-public-…
brennanb2025 Jul 13, 2026
d9983bf
Merge remote-tracking branch 'pr-bbingz-orca/fix/windows-terminal-dae…
brennanb2025 Jul 13, 2026
244c4a7
test(relay): remove stale teardown compatibility patch
bbingz Jul 13, 2026
73dd916
fix(relay): poll for process teardown completion
bbingz Jul 13, 2026
26264b1
Merge remote-tracking branch 'origin/main' into pr8284-immediate-fixes
brennanb2025 Jul 14, 2026
a3bba31
test(terminals): make teardown suite platform-explicit
brennanb2025 Jul 13, 2026
2d1b781
fix(terminals): align remote teardown budgets
brennanb2025 Jul 13, 2026
13efa6a
fix(daemon): reject unverified legacy teardown
brennanb2025 Jul 13, 2026
c4ff460
fix(daemon): retry failed ConPTY teardown
brennanb2025 Jul 13, 2026
20f470d
fix: avoid double-closing relay ConPTY
brennanb2025 Jul 14, 2026
04b3782
fix disconnected SSH forget-local cleanup
brennanb2025 Jul 14, 2026
c307382
fix(relay): skip redundant ConPTY close in the graceful SIGKILL fallback
brennanb2025 Jul 14, 2026
0bf7c87
test: expect isWsl registerPty argument from latest main
brennanb2025 Jul 14, 2026
106da2a
Merge origin/main into fix/windows-terminal-daemon-worktree-removal
brennanb2025 Jul 14, 2026
225cd11
fix(worktrees): make failed-teardown delete errors user-readable
brennanb2025 Jul 15, 2026
8a74573
fix(terminals): keep worktree teardown fail-closed
brennanb2025 Jul 15, 2026
45b4338
fix(relay): retain ConPTY liveness until native exit
brennanb2025 Jul 15, 2026
ba070c4
Merge remote-tracking branch 'origin/main' into pr8284-immediate-fixes
brennanb2025 Jul 15, 2026
e0185b1
fix(terminals): retain teardown ownership across graceful close
brennanb2025 Jul 15, 2026
ebe0b65
Merge origin/main: re-scope #8284 to the #8275 core; adopt main's #87…
brennanb2025 Jul 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 151 additions & 0 deletions config/reliability-gates.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,157 @@
],
"demotionRule": "Demote or quarantine if the gate flakes once without a product bug or harness bug filed to the owner."
},
{
"id": "terminal-session.worktree-removal-isolation",
"title": "Worktree removal verifies target PTYs without disturbing unrelated sessions",
"maturity": "experimental",
"protection": "partial",
"owner": "terminal-runtime",
"layer": "main-daemon-relay-contract",
"surfaces": [
"destructive worktree removal",
"terminal spawn admission",
"daemon and relay PTY teardown",
"local filesystem watcher ownership"
],
"platforms": ["macos", "linux", "windows"],
"providers": ["local", "daemon", "ssh", "wsl", "remote-runtime"],
"coveredPlatforms": ["macos"],
"coveredProviders": ["local", "daemon", "ssh"],
"coverageNotes": "Deterministic tests force Linux, Darwin, and Windows teardown semantics and cover local, current/legacy daemon, degraded-daemon, current/legacy SSH relay, disconnected SSH, runtime/headless, desktop IPC, duplicate removal, and watcher ordering. A local macOS live PTY harness verifies victim-tree death and unrelated-witness survival. Exact-head physical Windows, WSL, and live SSH runs remain explicit gaps.",
"motivatingLinks": [
"https://github.com/stablyai/orca/issues/8275",
"https://github.com/stablyai/orca/pull/8284"
],
"invariant": "Once worktree removal owns terminal-session.worktree-removal-isolation, no PTY for that host/worktree may enter or finish registration; every already-admitted target PTY must be stopped with full-session evidence before Git, recursive filesystem, registry, or metadata removal; failed or ambiguous teardown preserves the worktree, its watcher, and every unrelated PTY/daemon/relay session.",
"oracle": "Race renderer/runtime spawns, duplicate remove/forget calls, and bounded target shutdowns across local, daemon, and SSH ownership; require Git and metadata mutation to begin only after admission drains and every exact stop settles; inject legacy protocols, disconnects, malformed process inventories, PID/TTY ownership changes, ConPTY exit timeouts, graceful-to-immediate overlap, and retry; then assert deletion fails closed, the target owner remains retryable, no second ConPTY close occurs, and unrelated sessions are never signaled or retired.",
"commands": [
"pnpm exec vitest run --config config/vitest.config.ts src/relay/pty-session-kill.test.ts src/relay/pty-handler.test.ts src/main/runtime/worktree-pty-admission.test.ts src/main/runtime/pty-stop-concurrency.test.ts src/main/runtime/worktree-teardown.test.ts src/main/providers/local-pty-provider.test.ts src/main/providers/ssh-pty-liveness.test.ts src/main/providers/ssh-pty-provider.test.ts src/main/daemon/pty-subprocess.test.ts src/main/daemon/session.test.ts src/main/daemon/terminal-host.test.ts src/main/daemon/daemon-pty-router.test.ts src/main/daemon/degraded-daemon-pty-provider.test.ts src/main/ipc/pty.test.ts src/main/ipc/worktrees.test.ts"
],
"testFiles": [
"src/relay/pty-session-kill.test.ts",
"src/relay/pty-handler.test.ts",
"src/main/runtime/worktree-pty-admission.test.ts",
"src/main/runtime/pty-stop-concurrency.test.ts",
"src/main/runtime/worktree-teardown.test.ts",
"src/main/providers/local-pty-provider.test.ts",
"src/main/providers/ssh-pty-liveness.test.ts",
"src/main/providers/ssh-pty-provider.test.ts",
"src/main/daemon/pty-subprocess.test.ts",
"src/main/daemon/session.test.ts",
"src/main/daemon/terminal-host.test.ts",
"src/main/daemon/daemon-pty-router.test.ts",
"src/main/daemon/degraded-daemon-pty-provider.test.ts",
"src/main/ipc/pty.test.ts",
"src/main/ipc/worktrees.test.ts"
],
"assertionRefs": [
{
"file": "src/main/runtime/worktree-pty-admission.test.ts",
"assertions": [
"already-admitted spawns drain before teardown while later spawns reject until mutation settles",
"different worktrees remain independent, duplicate owners reject, and a timed-out drain reopens admission without leaking its waiter"
]
},
{
"file": "src/main/runtime/worktree-teardown.test.ts",
"assertions": [
"runtime, provider, and registry targets are deduplicated while unrelated and cross-host sessions survive",
"unverified local, legacy-daemon, disconnected-SSH, and provider-only sessions block destructive removal",
"50-session success and failure sweeps peak at eight workers and settle every snapshotted stop before returning"
]
},
{
"file": "src/relay/pty-session-kill.test.ts",
"assertions": [
"root PID, TTY, session, child-parent, and final-exit evidence use canonical bounded inventories and fail closed on malformed or changed ownership",
"Linux and Darwin descendant-first teardown captures reparented/session-escaped members without broad process scans",
"a stuck process tree performs at most two signal-0 probes per PID before bounded batched ps polling"
]
},
{
"file": "src/relay/pty-handler.test.ts",
"assertions": [
"Windows immediate teardown waits for native exit and never double-closes ConPTY after graceful or previous immediate shutdown",
"a graceful-fallback ConPTY without an exit event remains an unverified owner across repeated teardown timeouts until native exit arrives"
]
},
{
"file": "src/main/providers/local-pty-provider.test.ts",
"assertions": [
"a local Windows graceful close remains inventoried until native exit and an overlapping verified stop joins without issuing a second ConPTY close"
]
},
{
"file": "src/main/daemon/daemon-pty-router.test.ts",
"assertions": [
"an already-paid daemon inventory restores a missing legacy route before full-session capability checks without letting a stale legacy snapshot replace a newer current owner"
]
},
{
"file": "src/main/daemon/degraded-daemon-pty-provider.test.ts",
"assertions": [
"a terminating daemon session discovered by inventory cannot fall through to a successful local-fallback no-op"
]
},
{
"file": "src/main/ipc/worktrees.test.ts",
"assertions": [
"verified PTY shutdown precedes local watcher close, Git removal, and metadata cleanup",
"failed verification leaves Git, metadata, and the local watcher untouched",
"identical cross-surface removals join while conflicting remove/forget ownership cannot mutate twice"
]
}
],
"evidenceRuns": [
{
"date": "2026-07-14",
"runner": "local",
"platform": "macos",
"command": "pnpm exec vitest run --config config/vitest.config.ts src/relay/pty-session-kill.test.ts src/relay/pty-handler.test.ts src/main/runtime/worktree-pty-admission.test.ts src/main/runtime/pty-stop-concurrency.test.ts src/main/runtime/worktree-teardown.test.ts src/main/providers/local-pty-provider.test.ts src/main/providers/ssh-pty-liveness.test.ts src/main/providers/ssh-pty-provider.test.ts src/main/daemon/pty-subprocess.test.ts src/main/daemon/session.test.ts src/main/daemon/terminal-host.test.ts src/main/daemon/daemon-pty-router.test.ts src/main/daemon/degraded-daemon-pty-provider.test.ts src/main/ipc/pty.test.ts src/main/ipc/worktrees.test.ts",
"result": "passed",
"durationSeconds": 11.56,
"summary": "15 files and 966 tests passed locally, including canonical POSIX ownership parsing, bounded exit-probe counts, ConPTY graceful-fallback retry retention, watcher-after-verification ordering, eight-worker stop bounds, and local/daemon/SSH fail-closed isolation."
},
{
"date": "2026-07-14",
"runner": "local",
"platform": "macos",
"command": "pnpm exec vitest run --config config/vitest.config.ts src/relay/pty-session-kill.test.ts src/relay/pty-handler.test.ts src/main/runtime/worktree-pty-admission.test.ts src/main/runtime/pty-stop-concurrency.test.ts src/main/runtime/worktree-teardown.test.ts src/main/providers/local-pty-provider.test.ts src/main/providers/ssh-pty-liveness.test.ts src/main/providers/ssh-pty-provider.test.ts src/main/daemon/pty-subprocess.test.ts src/main/daemon/session.test.ts src/main/daemon/terminal-host.test.ts src/main/daemon/daemon-pty-router.test.ts src/main/daemon/degraded-daemon-pty-provider.test.ts src/main/ipc/pty.test.ts src/main/ipc/worktrees.test.ts",
"result": "passed",
"durationSeconds": 12.02,
"summary": "15 files and 974 tests passed after merging current main, including local graceful-to-verified ConPTY overlap and missing degraded/legacy route restoration without extra provider calls or stale-route replacement."
}
],
"runtimeBudget": {
"p95Seconds": 30,
"scope": "focused main/daemon/provider/relay worktree-removal contract tests"
},
"flakeHistory": {
"status": "unknown",
"evidence": "The focused slice passes locally but has not accumulated exact-head CI or soak history."
},
"redGreenEvidence": {
"status": "partial",
"evidence": "The new malformed-child-inventory assertions reject the prior silent token filtering, the graceful-fallback retry assertion rejects deletion of an unverified ConPTY owner, the local overlap test failed while graceful close erased its pre-exit owner, and the degraded/legacy inventory tests failed while missing routes fell through to the wrong owner. Saved CI red artifacts remain pending."
},
"performanceBudget": {
"required": true,
"evidence": "Destructive teardown adds no polling or scans to typing, output, focus, startup, or ordinary render paths. Fifty snapshotted PTYs use exactly fifty stops with an eight-worker ceiling; current SSH uses one targeted liveness request per PTY while a legacy relay shares one bounded inventory; missing daemon routes are restored with an O(returned sessions) pass over that existing inventory and no additional provider calls. POSIX discovery caps the tree at 1,024 PIDs under one 2.5-second deadline, final verification at 500 ms, PID batches at 64, and signal-0 fast probes at two per PID before batched ps polling. A final-source local victim-root-plus-child teardown completed in 99 ms against the 8,000 ms total budget while an unrelated witness PTY stayed alive."
},
"promotionCriteria": [
"Run the focused gate for at least 100 consecutive passes or 14 days across required CI platforms.",
"Attach exact-head physical Windows evidence for repeated target removals, ConPTY timeout/retry, stable daemon PID, and unrelated split-pane survival.",
"Attach live Linux SSH evidence for foreground/background target groups, disconnected fail-closed behavior, and unrelated relay witness survival.",
"Exercise native WSL worktree removal and retain explicit provider identity/path coverage."
],
"knownGaps": [
"Exact-head physical Windows and native WSL evidence is not available from this macOS review run.",
"SSH and remote-runtime behavior is deterministic contract coverage here; the retained live SSH evidence predates the final review fixes.",
"Daemon-crash restoration/reporting remains defense in depth outside this removal-path root-cause fix."
],
"demotionRule": "Keep experimental or demote if the gate flakes without a product/harness issue, if any destructive mutation precedes verified stop settlement, if an unverified owner disappears across retry, if stop concurrency exceeds eight, or if an unrelated PTY receives a shutdown or exit transition."
},
{
"id": "terminal-session.kill-all-surface-cleanup",
"title": "Kill all sessions removes only the confirmed terminal surfaces and current bindings",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { PREVIOUS_DAEMON_PROTOCOL_VERSIONS, PROTOCOL_VERSION } from './types'

describe('foreground-confirmation daemon protocol', () => {
it('rejects daemons from before the fresh-confirmation RPC', () => {
expect(PROTOCOL_VERSION).toBeGreaterThan(19)
expect(PROTOCOL_VERSION).toBe(22)
expect(PREVIOUS_DAEMON_PROTOCOL_VERSIONS).toContain(19)
// Why: v21 lacks full-session teardown, so the client must replace it
// instead of accepting a legacy daemon that can strand descendants.
expect(PREVIOUS_DAEMON_PROTOCOL_VERSIONS).toContain(21)
})
})
6 changes: 6 additions & 0 deletions src/main/daemon/daemon-protocol-capabilities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Why: protocol v22 is the first daemon that verifies the full POSIX process tree or Windows
// ConPTY exit before teardown completes; older daemons cannot make that safety guarantee.
export const VERIFIED_FULL_SESSION_TEARDOWN_PROTOCOL_VERSION = 22

// Why: protocol v22 is also the first daemon that applies the host-side Git credential guard.
export const GIT_CREDENTIAL_GUARD_HOST_PROTOCOL_VERSION = 22
12 changes: 12 additions & 0 deletions src/main/daemon/daemon-pty-adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,18 @@ describe('DaemonPtyAdapter (IPtyProvider)', () => {
})
})

describe('verified full-session teardown capability', () => {
it('accepts v22 and rejects a retained v21 daemon', () => {
const legacy = new DaemonPtyAdapter({ socketPath, tokenPath, protocolVersion: 21 })
try {
expect(adapter.canVerifyFullSessionTeardown('current-session')).toBe(true)
expect(legacy.canVerifyFullSessionTeardown('legacy-session')).toBe(false)
} finally {
legacy.dispose()
}
})
})

describe('producer flow control', () => {
it('routes pausePty/resumePty notifications to the daemon session subprocess', async () => {
const { id } = await adapter.spawn({ cols: 80, rows: 24 })
Expand Down
7 changes: 7 additions & 0 deletions src/main/daemon/daemon-pty-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import { supportsPtyStartupBarrier } from './shell-ready'
import { CODEX_SHELL_READY_TIMEOUT_MS } from './session'
import {
GIT_CREDENTIAL_GUARD_HOST_PROTOCOL_VERSION,
VERIFIED_FULL_SESSION_TEARDOWN_PROTOCOL_VERSION
} from './daemon-protocol-capabilities'
import {
PROTOCOL_VERSION,
type CreateOrAttachResult,
type DaemonEvent,
Expand Down Expand Up @@ -470,6 +473,10 @@ export class DaemonPtyAdapter implements IPtyProvider {
return this.activeSessionIds.has(id)
}

canVerifyFullSessionTeardown(_id: string): boolean {
return this.protocolVersion >= VERIFIED_FULL_SESSION_TEARDOWN_PROTOCOL_VERSION
}

write(id: string, data: string): void {
this.markSessionDirty(id)
this.client.notify('write', { sessionId: id, data })
Expand Down
Loading