Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
cac68e4
fix(orchestration): fence worker release on mobile keystrokes
Jinwoo-H Sep 7, 2026
624228a
fix(orchestration): ask the database, do not remember, whether a pane…
Jinwoo-H Sep 7, 2026
702f2e3
refactor(orchestration): let the database dedupe the takeover, drop t…
Jinwoo-H Sep 7, 2026
afccb38
refactor(terminal): name the provenance rule the takeover fence hangs…
Jinwoo-H Sep 7, 2026
4fcee9a
test(orchestration): prove the unary lane classifies a metadata-less …
Jinwoo-H Sep 7, 2026
c4e9f7e
fix(terminal): let provenance alone decide the takeover, on every acc…
Jinwoo-H Sep 7, 2026
ce297b1
fix(runtime): resolve retained handles before mobile input provenance
Jinwoo-H Sep 7, 2026
87add4f
test(rpc): require unconditional terminal afterWrite callbacks
Jinwoo-H Sep 7, 2026
6c665f8
fix(mobile): report worker takeover outside terminal byte delivery
Jinwoo-H Sep 8, 2026
23f48ca
fix(runtime): allow the takeover report from mobile-scoped tokens
Jinwoo-H Sep 8, 2026
a8264bd
fix(mobile): a no-op takeover report does not arm the gate; Stop repo…
Jinwoo-H Sep 8, 2026
8d467b9
fix(mobile): takeover gate ignores the host answer, like desktop
Jinwoo-H Sep 8, 2026
e667017
docs(runtime): say why resolveLiveLeafForHandle re-links before lookup
Jinwoo-H Sep 8, 2026
9078dae
Merge remote-tracking branch 'origin/main' into fix-mobile-takeover-r3
Jinwoo-H Sep 8, 2026
deb5961
chore(i18n): regenerate the runtime-required catalog for the contrast…
Jinwoo-H Sep 8, 2026
dd86b50
Merge remote-tracking branch 'origin/main' into fix-mobile-takeover-r3
Jinwoo-H Sep 8, 2026
83cf5ec
test(orchestration): give the stopping-worker guard fixtures a Run
Jinwoo-H Sep 8, 2026
593f22c
Merge remote-tracking branch 'origin/main' into fix-mobile-takeover-r3
Jinwoo-H Sep 8, 2026
0bdbc43
test(orchestration): drop fence-sweep assertions retired by the settl…
Jinwoo-H Sep 8, 2026
de97bb6
Merge remote-tracking branch 'origin/main' into fix-mobile-takeover-r3
Jinwoo-H Sep 8, 2026
433fbb1
test(orchestration): pin the mid-boot phone takeover that #19608 make…
Jinwoo-H Sep 8, 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
21 changes: 20 additions & 1 deletion config/reliability-gates.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -14202,7 +14202,7 @@
"providers": ["local", "daemon", "ssh", "wsl", "remote-runtime"],
"coveredPlatforms": ["macos"],
"coveredProviders": ["local", "ssh"],
"coverageNotes": "Deterministic service tests cover release-versus-reuse ordering, transactional retain and takeover cancellation, exact host/pane/process identity, dead external/user-owned/transferred/stopped/abandoned reconciliation, host-partition persistence and legacy retirement replay with an absent web-terminal layout map, conservative unknown provider and legacy metadata handling, immutable transcript and bounded terminal archives, mutation restart, reset cleanup, replay idempotency, and 50-resource accounting. A macOS Electron journey invokes the freshly compiled worker-release CLI after the worker process disappears, then independently checks released SQLite state and coordinator liveness. Injected inventories cover local and SSH provider routing; live SSH, WSL, Windows, paired-runtime, and provider-close lost-ack journeys remain explicit gaps.",
"coverageNotes": "New phones explicitly report terminal takeover on real user sends, throttled per owning client and handle. Host byte lanes perform zero orchestration SQL work; local and injected SSH report tests fence release. Phones predating this build do not fence release. Deterministic service tests cover release-versus-reuse ordering, transactional retain and takeover cancellation, exact host/pane/process identity, dead external/user-owned/transferred/stopped/abandoned reconciliation, host-partition persistence and legacy retirement replay with an absent web-terminal layout map, conservative unknown provider and legacy metadata handling, immutable transcript and bounded terminal archives, mutation restart, reset cleanup, replay idempotency, and 50-resource accounting. A macOS Electron journey invokes the freshly compiled worker-release CLI after the worker process disappears, then independently checks released SQLite state and coordinator liveness. Injected inventories cover local and SSH provider routing; live SSH, WSL, Windows, paired-runtime, and provider-close lost-ack journeys remain explicit gaps.",
"motivatingLinks": [
"https://github.com/stablyai/orca/pull/12355",
"https://github.com/stablyai/orca/issues/13860",
Expand All @@ -14213,6 +14213,8 @@
"invariant": "A settled Dispatch may close only its one coordinator-created terminal lease. Explicit reuse, real user input, retain, identity or host change, ambiguity, and another resource for the same exact host/pane/process must fence closure. Once the authoritative owning provider positively excludes the resource's exact immutable process incarnation, even an external, user-owned, or transferred dead resource must converge to released without any process close. Unknown host scope, missing incarnation metadata, or unavailable inventory must remain retained. Exact terminal-close persistence must settle when a host partition omits renderer-owned layout state. Output preservation and the requested-to-releasing transition are atomic, archives remain readable without the provider file, retries resume idempotently, and orchestration reset removes archive and authority state.",
"oracle": "Record release intent for a settled owner, attempt exact reuse before close, and require worker-start to fail with terminal_release_in_progress while the terminal stays open; then release the original owner exactly once. Race retain and real user input against a controlled archive promise and require no committed archive or close. Rebase a closed web-terminal host partition without terminalLayoutsByTabId and require the persistence write to complete while preserving host-authoritative membership; replay a valid legacy retirement under the same omission and require exact membership removal plus revision advancement. For retained external, user-owned, transferred, stopped, and abandoned resources, run one fresh inventory against the exact local/WSL or SSH provider: an exact live incarnation and every unknown inventory shape stay retained, while positive absence atomically sets ownership_state and release_state to released with processAction none and zero closeTerminal calls. Change host or process identity and inject duplicate resource evidence to require retention. Freeze a structured transcript, delete its source file, and require archived worker-read to return the same bounded redacted messages. Restart a pending mutation, reset orchestration state, and create 50 resources while asserting replay convergence, zero orphan rows, two-query worker listing, and no unrelated close.",
"commands": [
"ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/rpc/methods/orchestration/worker/worker-release-mobile-report.test.ts src/main/runtime/orca-runtime-terminal-handle-incarnation.test.ts src/renderer/src/lib/worker-terminal-takeover-report.test.ts",
"ORCA_BACKGROUND_LAUNCH=1 mobile/node_modules/.bin/vitest run --config mobile/vitest.config.ts mobile/src/session/mobile-worker-takeover-send-sites.test.ts mobile/src/terminal/worker-terminal-takeover-report.test.ts",
"pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/pty-inventory-liveness-verdict.test.ts src/main/runtime/orca-runtime-process-incarnation-liveness.test.ts src/main/runtime/mobile-session-terminal-persistence-retirement.test.ts src/main/runtime/rpc/methods/orchestration/worker/worker-release.test.ts src/main/runtime/rpc/methods/orchestration/worker/worker-release-recovery.test.ts src/main/runtime/rpc/orchestration-mutation-ledger.test.ts src/main/runtime/orchestration/worker-transcript-read.test.ts src/renderer/src/lib/worker-terminal-takeover-report.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/orca-runtime-process-incarnation-liveness.test.ts src/main/runtime/mobile-session-terminal-persistence-retirement.test.ts src/main/runtime/rpc/methods/orchestration/worker/worker-release.test.ts src/main/runtime/rpc/methods/orchestration/worker/worker-release-recovery.test.ts src/main/runtime/rpc/orchestration-mutation-ledger.test.ts src/main/runtime/orchestration/worker-transcript-read.test.ts src/renderer/src/lib/worker-terminal-takeover-report.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/orca-runtime-process-incarnation-liveness.test.ts src/main/runtime/rpc/methods/orchestration/worker/worker-release.test.ts src/main/runtime/rpc/methods/orchestration/worker/worker-release-recovery.test.ts src/main/runtime/rpc/orchestration-mutation-ledger.test.ts src/main/runtime/orchestration/worker-transcript-read.test.ts src/renderer/src/lib/worker-terminal-takeover-report.test.ts --reporter=dot",
Expand All @@ -14221,6 +14223,9 @@
"pnpm run build:cli && SKIP_BUILD=1 pnpm exec playwright test tests/e2e/orchestration-worker-settlement-release-cli.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1"
],
"testFiles": [
"src/main/runtime/rpc/methods/orchestration/worker/worker-release-mobile-report.test.ts",
"mobile/src/session/mobile-worker-takeover-send-sites.test.ts",
"mobile/src/terminal/worker-terminal-takeover-report.test.ts",
"src/main/runtime/pty-inventory-liveness-verdict.test.ts",
"src/main/runtime/orca-runtime-process-incarnation-liveness.test.ts",
"src/main/runtime/mobile-session-terminal-persistence-retirement.test.ts",
Expand All @@ -14233,6 +14238,20 @@
"tests/e2e/orchestration-worker-settlement-release-cli.spec.ts"
],
"assertionRefs": [
{
"file": "src/main/runtime/rpc/methods/orchestration/worker/worker-release-mobile-report.test.ts",
"assertions": [
"a handle-addressed phone report fences %s worker release",
"mobile %s bytes do no orchestration database work"
]
},
{
"file": "mobile/src/session/mobile-worker-takeover-send-sites.test.ts",
"assertions": [
"%s reports on its send target once per handle per 30 seconds",
"%s never reports takeover"
]
},
{
"file": "src/main/runtime/pty-inventory-liveness-verdict.test.ts",
"assertions": [
Expand Down
5 changes: 5 additions & 0 deletions mobile/src/session/mobile-native-chat-permission-send.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// Takeover RPCs have their own send-site integration tests; these fixtures script PTY acknowledgements.
vi.mock('../terminal/worker-terminal-takeover-report', () => ({
reportWorkerTerminalUserInput: vi.fn()
}))

import { createElement } from 'react'
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
Expand Down
18 changes: 13 additions & 5 deletions mobile/src/session/mobile-native-chat-send.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,10 +309,13 @@ describe('typeMobileNativeChatCommandWithOutcome', () => {

await expect(result).resolves.toBe('accepted')
expect(
vi.mocked(client.sendRequest).mock.calls.map((call) => {
const params = call[1] as { text: string; enter: boolean }
return { text: params.text, enter: params.enter }
})
vi
.mocked(client.sendRequest)
.mock.calls.filter(([method]) => method === 'terminal.send')
.map((call) => {
const params = call[1] as { text: string; enter: boolean }
return { text: params.text, enter: params.enter }
})
).toEqual(
['\x15', '/', 'm', 'o', 'd', 'e', 'l', '\r'].map((text) => ({
text,
Expand All @@ -338,7 +341,12 @@ describe('typeMobileNativeChatCommandWithOutcome', () => {
await vi.runAllTimersAsync()
await result

const params = vi.mocked(client.sendRequest).mock.calls.map((call) => call[1]) as Array<{
// Why the filter: an accepted send also fires the unawaited takeover report, which is not a
// terminal.send and carries no draft.
const params = vi
.mocked(client.sendRequest)
.mock.calls.filter((call) => call[0] === 'terminal.send')
.map((call) => call[1]) as Array<{
text: string
resolvedLaunchDraft?: { text: string; createdAt: number }
}>
Expand Down
7 changes: 6 additions & 1 deletion mobile/src/session/mobile-native-chat-send.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { reportWorkerTerminalUserInput } from '../terminal/worker-terminal-takeover-report'
import type { RpcClient } from '../transport/rpc-client'
import { isRpcDeliveryUnknown } from '../transport/rpc-delivery-ambiguity'
import { isLogicalClientCutoverError } from '../transport/stable-logical-rpc-client'
Expand Down Expand Up @@ -64,7 +65,11 @@ export async function sendMobileNativeChatMessageWithOutcome(
// pins the composer for twice as long.
{ timeoutMs, budgetSpansConnect: true }
)
return isTerminalSendRpcAccepted(response) ? 'accepted' : 'rejected'
if (!isTerminalSendRpcAccepted(response)) {
return 'rejected'
}
reportWorkerTerminalUserInput(args.client, args.terminal)
return 'accepted'
} catch (error) {
// Why: a logical relay↔direct cutover rejects the in-flight send without
// knowing whether its frame reached the wire (the desktop may have delivered
Expand Down
4 changes: 2 additions & 2 deletions mobile/src/session/mobile-session-route-parity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ const HEAD_MAIN_HOOK_SHA256 = '10071240ef9edafc2b9c8bed73be83dceaf7828e3b29f17da
const HEAD_HOOK_BINDING_SHA256 = '1dadb8c3dc0573ea20659ce7251629669e618dd0effaeac3a4536b29c2e865a1'
const HEAD_CALLBACK_IDENTITY_SHA256 =
'2a9e4825df007f6ef53b81aa5004991d6318eee7507b44d625c07e630be432eb'
const HEAD_CALLBACK_BODY_SHA256 = '22103ba85a86e3a3fcb80a7509c7a455d79863010cde3af02db6565b55e3ebe9'
const HEAD_CALLBACK_BODY_SHA256 = 'af7f3c62954250d4be7ee432ecd10dc2689792aad8230fed2d1d68bbc892d776'
const HEAD_EFFECT_SHA256 = 'd9ebfaabc1e79773cdada7ab370b20459ed972f1f8edce1652199f4d0391cd13'
const HEAD_CONTENT_HOOK_SHA256 = '9c3b612fef3f370d66873aefdbe1d701f20cb64ded31fef5cc45fde6f8189581'
const HEAD_NESTED_FUNCTION_SHA256 =
'536c72b233c813bb0cea164b090bdce5406ceb965bbc5b83c1f89b89b46f3821'
'fde6679349ab2b8c30c7e627841ff99bd1dd24441ee95323d0aa70230422ae24'
const HEAD_NATIVE_REGISTRATION_SHA256 =
'cab85e4e4a3f43289ba93ddea9ccce57aea83e0bf14fd1620a965aad0c1cb49e'
const HEAD_NATIVE_REMOVAL_SHA256 =
Expand Down
Loading
Loading