Skip to content

sync: upstream 5d32f372e7 into swaplabs/main - #50

Merged
kiwidream merged 139 commits into
swaplabs/mainfrom
sync/upstream-20261023
Oct 23, 2026
Merged

kiwidream merged 139 commits into
swaplabs/mainfrom
sync/upstream-20261023

Conversation

@kiwidream

@kiwidream kiwidream commented Oct 23, 2026 •

Copy link
Copy Markdown
Files Added Deleted Net
Test 979 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​41962 $\color{#cf222e}{\Huge{\mathbf{−}}}$​21647 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​20315
Prod 1400 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​57391 $\color{#cf222e}{\Huge{\mathbf{−}}}$​15586 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​41805

ELI5

Upstream Orca moved forward 138 commits since yesterday's sync (#47). This PR merges them into swaplabs/main. Three files conflicted with fork changes, one small hunk each; each keeps the fork's lines unchanged on top of upstream's new code. It supersedes #49 (head main), which could not merge.

What Changed

Upstream range a45bd06eac..5d32f372e7, 138 commits (compare). One signed merge commit, 5aafdd009f (Merge upstream main (5d32f372e7) into swaplabs/main), on sync/upstream-20261023, cut at the integration head 412ee3374a.

Conflicts, by fork PR (orca-fork-ops conflicts):

Fork PR File Upstream change Resolution
#10, #14 (update sources) src/preload/api/updater-api.ts upstream stablyai#32257 shortened the listBuilds doc comment upstream's comment, plus the fork's line about source; the signature and listSources are unchanged
#30 (host resume) src/shared/agent-hook-listener.ts upstream stablyai#31780 reads agentProcess right after the previous row, so the identity guard can tell a nested claude -p from its parent both: the fork's statusRowToComplete(...) on the previous row, then upstream's readHookAgentProcess line
#30, via the past sync resolution 1a247d1c5c src/main/runtime/session-lifecycle/projection/restored-pane-materialize.ts upstream stablyai#31718 adds a resumeClaim shared by pane resume and the new targeted resume both: upstream's resumeClaim line, then the fork's hostResume !== HOST_PANE_AGENT_REPORTED guard on the fresh-launch branch

No fork lines were moved or dropped. For every one of the 28 files both sides changed, the fork's added and removed lines against upstream are the same before and after the merge. The only exception is the comment in updater-api.ts above.

How the fork's hook-store resume (#30) sits next to upstream stablyai#31718: it runs only when resolvePaneOwnedResumeStartup returns undefined, which means the pane has no record. Upstream returns {}, not undefined, when another resumer holds the claim, so the fork's resume never runs while upstream holds a claim. The fork's resume does not take upstream's new in-memory claim itself. The only caller of upstream's targeted resume is the external-conversation reopen, which is off by default behind experimentalConnectors. This is noted on #41.

Some notable upstream changes in the range:

Why

Daily upstream sync.

Linked Issue

N/A: daily upstream sync. #41 notes the targeted-resume claim above.

Visual Proof

N/A: no UI change of the fork's. The conflicts are in a comment and in the main-process and shared code.

Testing

All checks ran on the merge result in the sync worktree, before the commit:

  • Typecheck: pnpm tc (all four projects, incremental tsrs) passes. A deliberate type error in a scratch file made it fail, which confirms the gate is live.
  • Fork tests: every unit test file the fork adds or changes against upstream (117 files), plus orca-runtime.test.ts, which runs the fork's worktree-ps-agent-row-dismissal.spec.ts. 117 of 118 files pass. The one failure is upstream's repository-project-operations-part-02.spec.ts: this host's signing wrapper refuses its fixture commit init. With fixture signing off (commit.gpgsign=false), orca-runtime.test.ts passes: 1,497 passed, 3 skipped.
  • Touched directories: src/main/runtime/session-lifecycle, src/shared/agent-hook*, src/shared/agent-status*, src/preload, src/main/agent-hooks, src/main/updater and src/renderer/src/components/settings: 559 files, 5,042 passed, 0 failed.
  • Changed-code gate: check-changed-code-quality.mjs with ORCA_SYNC_PR_DIFF_BASE=5d32f372e7: 0 findings across 331 fork files.
  • Lint and format of everything the merge stages (2,098 files outside cloud/): oxlint passes with both configs, and oxfmt --check reports every file formatted. The pre-commit hook then ran normally, and the tree before the commit (59707d1a11) equals HEAD^{tree} after it.
  • Catalogs: the 8 locale catalogs parse with no duplicate keys. verify:localization-runtime-catalog, -catalog, -catalogs, -extraction, -coverage, verify:rpc-params-catalog and check:max-lines-ratchet all pass.
  • Signature: 5aafdd009f is G from the sync key BF1DD75F888E96080C2453E2D9E158C5D6874235.

AI Disclosure

Prepared by the "Orca upstream sync" automation (Claude Code, Claude Opus 5.5).

Agent skill upstream boundary

  • Not applicable: this PR merges upstream's history; it adds no agent skill.

Notes

orca-fork-ops status for swaplabs/main (412ee3374a) against upstream 5d32f372e7, before this merge:

No upstream release tags were missing from the fork.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

Jinwoo-H and others added 30 commits October 22, 2026 14:01
…rames survives other changes to that workspace (switch-on builds only) (stablyai#31920)

* fix(L273-core): tabs this desktop created in a paired server's frame-drawn workspace survive the server's frames (switch-on)

A tab the server's layout never lists (a client-hosted browser tab today; any kind recorded the
same way) used to vanish on the server's next frame rebuild. This desktop now records such tabs by
server and workspace, in its localStorage only (never a session field nor sent), and the server's
frame builds keep them as drawn, inside the server's own groups:
- A held tab drawn in a group the server lacks moves to the nearest server group; it shows there
  only if the user was on it, and the user's group follows it. The group tree stays the server's.
- The record forgets an id once the server's frame lists it, on close, and on worktree removal,
  and prunes ids the restored session no longer has before the first frame after a reload.
- A held tab's drag moves it here only (no session.tabs.move); it offers no new split, and a
  server tab sharing its group counts only the server's tabs for split no-ops.
- Client-hosted staging records its tab (both ids) in layout-owner builds; restage takes the
  worktree from its caller instead of scanning the store.

* refactor(L273-core): the desktop-created record is inert with the switch off; clearer names

- With the switch off the record is never read, kept or written (a single gate in the record).
- The frame-drawn check is isDesktopCreatedTabInFrameDrawnServerWorkspace in
  frame-drawn-desktop-created-tabs.ts: "held" stays the frame hold's word.
- The held-tab placement drops its unread activeGroupId; the group follow derives it.

* fix(L282): a client-hosted browser tab this desktop creates in a frame-drawn server workspace completes once the server's snapshot lists it, instead of undoing itself after 8 s (switch-on)

* fix(L284): a server-hosted browser tab this desktop creates in a frame-drawn server workspace completes when the server's frames take it over, instead of reading as the user's close (switch-on)

* chore(L282/L284): adopt only staged client-hosted handles, and name the frame build's handover note (review nits)

* fix(L284): a staged tab the server's frames took over completes the create though its pane, unmounted, has no handle yet (layout-owner build only)

* fix(browser): a switch-on host answers a paired client's browser create with the new page's id, not its workspace's, before main's frame draws it

* fix(browser): the reply names the page id the create asked for first, then the new record's own page (L291)

* fix(L284): the frame build scans dropped pages for a handover only when a staged create's page is drawn, so a large close stays one pass

* fix(L285): a staged server-hosted browser tab stays through the server's frames until the server lists its page, then hands over to the server's own tab (layout-owner build only)

* fix(L285): release a held browser tab by the server listing its page, staged or adopted, in one scan per frame (review)
…e set of default tabs when the same path exists on another host (stablyai#32080)

* fix(worktree-create): a workspace created on a paired server seeds against that server, so a same-path twin on another host can't duplicate its default tabs (L277)

* fix(worktree-create): name the create's host once, keeping the flow under its line cap; type-only test import
…yai#29104)

* fix(runtime): release remote streams when the renderer reloads

* ci: re-run code scanning (no source change)
…lyai#28569)

* Offer Resolve with AI for paused merge, rebase, and cherry-pick

A paused operation can need recovery after every conflict is staged, but the Resolve with AI action only appeared when unmerged paths remained. The operation banner now offers it whenever AI actions are visible and an operation is in progress.

Starting an AI conflict-resolution agent is also blocked while a Git operation is running. The composer, the banner and card buttons, and the agent launch itself check the running-operation state, so a launch that began during agent detection cannot start while Git is still running.

* Block paused-operation recovery only on Orca's own Git work

The previous gate scanned the host's process table and refused "Resolve with AI" whenever it could not prove no Git process was running. That refused recovery on older paired hosts, on Windows whenever any Git process ran anywhere on the machine, and while a paged `git reflog` or `git grep` was open in a terminal.

- The host tracks Orca's own mutating Git commands per worktree (including ones still queued) and reports them through `git.mutationInFlight`. Reads and fetch/push/ls-remote are not tracked.
- Recovery is refused only when the host reports that work. Older hosts without the method keep today's behavior.
- The gate applies only to launches from the paused-operation banner and conflict card, not every resolveConflicts launch, so the Checks panel's PR-conflict flow is unchanged.
- The agent prompt now tells the agent to stop if index.lock exists or another git command is running, which covers Git started outside Orca on every host. With no unmerged files, the prompt asks the agent to find why Git paused instead of listing zero conflicts.

* Preserve Git mutation state until terminated commands exit

- Cancel stale recovery checks and agent launches when their dialog or scope closes
- Guide rebase recovery agents to amend fixes after failed exec steps

* Cover failed rebase exec recovery with real Git

* Recognize global Git options in worktree mutation tracking
…cides (stablyai#30910)

* fix(acp): OMP chats send images; an ACP agent's own startup answer decides

Images reached an ACP agent only when it advertised them and Orca also listed it, and OMP was never listed. Trust the advertised capability alone, offer attachments to every ACP agent until its live report narrows them, and send an image's @path reference to an agent that takes none instead of refusing the message.

Tracked in STA-11152.

* fix(acp): preserve image file references when native content cannot fit
…tablyai#31593)

* feat(resources): end local terminals with checked identity checks

- Add a PTYs view to the Resource Manager that lists local terminals and can end one. Before stopping, the host confirms the terminal's incarnation, agent ownership, and attachments. This prevents ending a same-ID replacement that started after the list was read.
- Bump the daemon protocol to v50 and gate the new fenced kill on it. Older daemons ignore the extra fields, so the client refuses End instead of sending an unchecked kill.
- Admit diagnostic reads (PTY listing and memory snapshot) only while the Resources view is visible, so a hidden window does no background scanning.
- Report incarnation and host on spawn and exit events so the renderer can match them to the terminal it showed.
- Replace the popover's terminal-session list with the new inventory and move memory polling into the Resources view.

* Show unrouted daemon PTYs in the Resource Manager

* Simplify PTY controls and clarify safe termination

* Format local build compatibility protocol lists

* Add PTY resource management localization strings

* Harden PTY resource manager test fixtures

* Use typed IPC event in PTY kill test fixture

* Refine local PTY resource view model typing

* Refine local PTY resource view model typing

* Include local host identity in PTY lifecycle event tests
…s twice (stablyai#31657)

* Install shared skills for 11 more agents

Add skill install provider entries for OpenClaude (own .openclaude folders,
OPENCLAUDE_CONFIG_DIR), and OpenCode 2, MiMo Code, OMP, Prime Agent, Qoder CN,
Codebuff, Freebuff, Ante, Jcode and DeepSeek Harness, which read the shared
.agents/skills folder at both scopes. A coverage test now requires every Orca
agent to have an entry or a recorded reason it has none.

* Use the shared skills folder for Qoder

Qoder's CLI loads .agents/skills at both scopes by default, so its own
.qoder/skills placement made every skill appear twice.

* Keep scanning Qoder's own skill folders

Qoder still reads .qoder/skills, so discovery keeps both folders as
discovery-only roots now that installs go to the canonical root.
* Stabilize remote host status bar layout during retries

* Add connecting status translation for remote hosts
* Set minimum width for native chat table cells

* Allow markdown table cells to wrap long content

* Set minimum width for native chat table cells
… server workspace from the server's layout (switch-on builds only) (stablyai#32088)

* wip(h5c-1): server workspace layout intents go to the server's main; positions per stream

* wip(h5c-1): e2e without a cast

* fix(h5c-1): a routed move never moves here; routed callers read the server's stream; a restarted main starts its positions over

* fix(h5c-1): layout-apply-send back under its line limit

* test(h5c-1): a routed answer reaches the handler with the server's stream key

* Editor tabs main-first: the window sends each editor action to main and draws editor tabs only from main's frames (dark, layout-owner build only)

With ORCA_LAYOUT_OWNER_IS_MAIN on, editor open (preview replace, open to the side), reopen at
its position, close, close-all, promote, the resolved conflict placeholder, a restored-editor
move and a disk rename are layout commands the window sends before it draws anything. Window
content (open file records, drafts, buffers, the autosave gate) is written at once for an open,
and rekeyed in the same store update as the frame for a move or rename. Main publishes each
non-edit tab's view, keeps views in memory across applies and partition changes, stamps the
tab's time, folds same-file duplicates, and republishes a windowless host's session.tabs after
its own file open. The switch-off path is unchanged.

* fix(terminal): a CLI or phone close through main keeps a pinned tab, as the window-era close did

* feat(terminal): main applies phone closes, renames, pane titles, sleeps and agent-launch tabs itself, with or without a window (layout-owner build)

* fix(terminal): a tab close through main also ends the tab's dormant SSH relay session; agent-launch tabs main writes keep the window's focus rule, prompt and outcome (layout-owner build)

* feat(terminal): main starts a new tab's first terminal under the window's ids, and the pane attaches to it (layout-owner build)

* test(terminal): tests that counted the window's create calls or publications see main's start of a new tab

* fix(terminal): an account-switch restart binds only into a pane main's layout holds, with no window tree (layout-owner build, L197)

* test(terminal): the new-tab start tests hold no type assertions; the start's ledger row is L252

* fix(terminal): a new tab's pane attaches to any terminal main binds it to, and a failed start keeps the tab's startup

* test(terminal): a Claude account-switch restart's tests draw the pane main holds

* test(d3a-2): the buffer view index follows a rename main answers

* fix(terminal): a launch pane main takes back still tells the window why, which drops its hold and prompt; a rename through main answers the name it stored (layout-owner build)

* fix(terminal): a new tab another owner's terminal won attaches to it, and a failed start's startup never runs twice

* fix(terminal): main's pane close reads its stop result as a type only, so no runtime module loads before its base class

* fix(terminal): a phone's close through main drops the closed rows from what phones list at once, not at the window's next publish (layout-owner build)

* chore(terminal): mark the phone-close row drop interim (ledger L260)

* test(worktree-create): a new local tab's startup counts as run when main starts it (layout-owner build)

* test(worktree-create): record main's starts without a type assertion

* fix(terminal): a tab closed before main declines its start gets no startup back (layout-owner build)

* feat(h5d): hand desktop-only editor tabs to a layout-intents server before its frames draw the workspace

A paired desktop dropped an editor tab it opened in a server workspace (with its open file and
unsaved draft) on the server's next records frame once that workspace became frame-drawn, and
editor opens made after gating hit the same loss.

- openEditorTab is now on SERVER_ROUTED_LAYOUT_COMMANDS: the proposed tab id and group are kept,
  the file id becomes the server's own, and the record loses the desktop's runtimeEnvironmentId.
- Before a workspace gates, server-layout-frames hands its desktop-only editor tabs to the
  server's main (sendToServerMain), recomputes them on each answer, and gates only when all
  answered ok or tab_exists. Any other refusal or a failed call keeps it on the mirror for that
  connection; the next one retries. No timer, no hold store.

* fix(h5d): only an applied open counts as handed over; tab_exists keeps the tab on the mirror

* fix(terminal): the window's new tab starts in main when its own window asks over IPC, which labels itself a client (layout-owner build)

* feat(h5c-2): a paired desktop's writes to a server workspace its frames draw land in that server's main (layout-owner build)

- Routes setTabProps, setChatPane and non-terminal renameTab to the server's main as layout.apply;
  closes, starts, sleeps and terminal names keep the RPCs that main now applies (session.tabs.close,
  terminal.close, terminal.split, session.tabs.createTerminal, terminal.rename). A window-style
  closeTabs is never routed: main takes it as already stopped and stops nothing.
- A close the server refuses (a pinned tab) redraws the workspace from the server's frames, so the
  window's optimistic drop never outlives the answer; same for a refused pane close.
- A frame-drawn workspace keeps none of the mirror's ledgers (L240): no tab-id mappings, close or
  focus intents; they stay only for servers without layout.intents.v1 until the Phase 2 cutoff.
  Its pane-geometry push is skipped (it goes as intents); pane renames still push.
- e2e: paired-desktop-server-layout-writes covers close (layout, session.tabs, terminal stop),
  rename, split and new tab on a windowless switch-on serve.

* fix(h5c-2): an undelivered close of a frame-drawn tab is held for replay; a launch-agent clear stays off the server route

* refactor(h5c-2): the undelivered-close test moves to its own module, keeping the lifecycle file under its line cap

* fix(h5d): a tab closed while its hand-over was in flight is closed on the server before the workspace gates

* fix(h5d): an editor tab closed in a workspace a server's frames draw closes on that server by session.tabs.close; a tab kept on the mirror closes here only

* fix(h5d): a handed-over tab closed here before the gate is closed on the server first; a server-opened tab's close in a frame-drawn workspace is covered

* chore(h5d): a closed stream also closes the hand-over its hand-back started

* refactor(h5d): move the frame stream's routing inputs out of server-layout-frames to keep it under the line cap

Main's stablyai#31869 grew server-layout-frames past 300 lines once merged with H5d; the two helpers move unchanged.
…tays where you opened it (switch-on builds only) (stablyai#32003)

* fix(L273-core): tabs this desktop created in a paired server's frame-drawn workspace survive the server's frames (switch-on)

A tab the server's layout never lists (a client-hosted browser tab today; any kind recorded the
same way) used to vanish on the server's next frame rebuild. This desktop now records such tabs by
server and workspace, in its localStorage only (never a session field nor sent), and the server's
frame builds keep them as drawn, inside the server's own groups:
- A held tab drawn in a group the server lacks moves to the nearest server group; it shows there
  only if the user was on it, and the user's group follows it. The group tree stays the server's.
- The record forgets an id once the server's frame lists it, on close, and on worktree removal,
  and prunes ids the restored session no longer has before the first frame after a reload.
- A held tab's drag moves it here only (no session.tabs.move); it offers no new split, and a
  server tab sharing its group counts only the server's tabs for split no-ops.
- Client-hosted staging records its tab (both ids) in layout-owner builds; restage takes the
  worktree from its caller instead of scanning the store.

* refactor(L273-core): the desktop-created record is inert with the switch off; clearer names

- With the switch off the record is never read, kept or written (a single gate in the record).
- The frame-drawn check is isDesktopCreatedTabInFrameDrawnServerWorkspace in
  frame-drawn-desktop-created-tabs.ts: "held" stays the frame hold's word.
- The held-tab placement drops its unread activeGroupId; the group follow derives it.

* wip(L270): a host advertising agentSession.create.tab-placement.v1 opens a created chat's tab where the creating client asked, in its own group and tab ids

* test(L270): a switch-off windowless host saves a chat opened with no client place at the end of the remembered active group, as before (test only)

* wip(L270): a desktop's chat in a frame-drawn server workspace stays put: the frames hold its provisional tab until the server lists it, and the create names its group and the server tab before it (layout-owner build only)

* test(L270): a switch-on serve saves a client's chat after the tab it named; the server-layout read moves to one e2e helper (test only)

* refactor(L270): the create's tab place keeps its own name, and the layout rule's comment names the client's place first

* refactor(L270): the create's tab fields live in their own schema file and the windowless save passes its chat through, so no file passes its line limit; the placement test types its mocks without assertions

* refactor(L270): the chat's place names server tabs through H5c's route, which also skips tabs the server's frames do not hold

* fix(L270): a chat opened while a reconnected server stream awaits its snapshot still names its group; the layout-owner e2e list runs the placement spec

* fix(L270): only a create in a frame-drawn server workspace awaits its tab place, so every other create keeps its timing (the eager History joiner after an early terminal close opens again)

* fix(native-chat): read a frame-drawn chat tab's place without a tick, so an eager History joiner's create is still sent

* test(native-chat): type the create's params so an async placement cannot compile; name the probe that fills the cache

* test: type the placement mock's original through a type import
…ment tail (stablyai#31934)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
…ai#31935)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
…ker (stablyai#31936)

* perf(chat): remove repeated scans and array copies from the skill picker

* perf(chat): keep picker grouping within the module line limit

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
…1950)

* perf(ai-vault): bound scope cache write batch enumeration

* test(ai-vault): type scope cache stamp fixture

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
…nce on the shared agent writer (stablyai#32092)

* feat(agent-write): opt-in launch cancel, approval wait and binding fence on the shared writer

executeAgentLaunch takes an optional signal; sendTerminalAgentPrompt (and
sendAgentTurn) take optional onPermission: 'wait' and expectedBinding. No
existing caller sets them, so every current path is unchanged.

* fix(agent-write): keep the default writer path main's; fence cancel and real submit boundary

Only opted-in writes take the submit layer, so default timing and the leaf
re-read are unchanged. Cancellation is rechecked before a structured-refusal
terminal fallback, and an opted-in launch learns Enter from an onSubmitWritten
callback at the write itself, OMP's combined frame included.

* fix(agent-launch): a cancel after a failed startup spawn recovers the created workspace
…lyai#31893)

* Revert "Preview HTML files by default" (stablyai#31575)

Restore source/diff opening for general HTML file-opening routes for the release.

This reverts commit 524fa00, retaining the
published files.readDocPreview capability for mixed-version clients and removing
an unnecessary restored test assertion to satisfy the current lint gate.

Existing explicit previews and terminal HTML links retain their previous behavior;
the underlying local-file preview exposure remains tracked in STA-11670.

* Preserve file ownership and symlink guards during HTML preview rollback

Keep the existing owner checks after file-drop uploads and tab-entry stats so
an ownership change cannot reopen a file through a stale host. Preserve local
preview source checks so symlinks outside the workspace keep user-file access.

The race and symlink tests reproduce failures on the mechanical revert before
these guards are retained. Cover HTML source opening for folder workspaces,
Explorer, and Source Control without restoring preview-by-default.

* fix: split tab entry browser requests to satisfy lint limit
…ds a terminal asks main's layout (stablyai#32002)

* refactor(runtime): every reader of a terminal's pane goes through paneOfPty (L80)

With the layout-owner switch on, main's layout answers which tab and pane hold a terminal, and
nothing reads or writes the PTY record's tabId/paneKey copies or their graph stamp. Switch-off
reads the copies exactly as before.

* refactor(runtime): read a terminal's pane through the runtime's paneOfPty method

* fix(runtime): main's pane index answers paneOfPty before any window graph

A headless host never installs a window graph, so the query asked a publisher that did not exist
and every terminal read as paneless. The index is now made on first query; it publishes only once a
graph is installed, as before.

* test(runtime): pin that a query with no window publishes nothing

* test(runtime): a terminal summary reads its pane once (L80)

* feat(runtime): pin bind-first at registration, and one own-handle rule with or without a window (switch-on only)

* test(runtime): census and tests that every create path binds its pane before it registers (L80)

* test(runtime): a bind-first fixture that stands in for the spawn commit (L80)

* test(runtime): fixtures bind a terminal's pane before registering it, as the spawn commit does (L80)

Also, with the switch on: skip the bind-first check for a paired server's workspace, release a
closed tab's terminal ownership once main has dropped the pane, and publish a pane's agent status
from the terminal main's layout binds it to.

* feat(runtime): an exit after main's close still finds the pane it retires (switch-on only, L80 Q2)

Main's layout write records every pane it drops whose terminal no pane holds any more, in a new
optional session field closedTerminalPanesByPaneKey (same TTL and cap as closed-tab records).
Exit cleanup reads main's layout first, then that record by terminal id and incarnation, and
removes the entry it used. Older builds ignore the field.

* refactor(layout): one dropped-leaf step every live layout edit runs (switch-on only, L80 Q2)

The step receives each dropped pane (host, worktree, tab, leaf, terminal, incarnation), computed
across the whole layout so a moved pane is not dropped, and runs only for a commit that declares a
live edit. Today it records the closed-pane entries; a drop is recorded once per terminal.

* fix(runtime): a phone or headless tab close still stops and releases its live terminals after main's layout drops the panes (switch-on only, L80)

The headless close reads each row's live terminal before main's close drops the panes it is found
by, and a renderer-retired tab releases the owned terminal main left in no pane, as the explicit
close already did. Close tests now commit through main's layout and bind panes first.

* fix(runtime): a local workspace's terminal tab left in a paired server's partition is main's pane (switch-on only, L257)

Older builds could save a local worktree's tab rows into a runtime partition (stablyai#18202); its terminals
are local, so main's index takes those tabs for workspaces the router homes locally. A tab or
terminal the local partition also holds is indexed once, from there; SSH and server-owned
workspaces are skipped as before.

* test(layout): the legacy-close comparison leaves out the closed-pane record only layout-owner builds write (L80)

* test(pty): a pane whose failed start withdrew its tab resolves to terminal_not_found in layout-owner builds (L80 #7)

Main's layout no longer holds the pane, so there is no pane to answer for; window-owned builds keep
the disconnected stub from the exited terminal's record.

* test(runtime): orca-runtime suites bind panes first, as the real store and spawn commit do (L80)

Fake PTY spawns commit the binding into the pane the runtime wrote, stores list their partitions,
and SSH sessions sit in their ssh:<conn> partition; terminals registered directly bind first.

* fix(runtime): main's layout names a pane's terminal only when the workspace and stamped incarnation match (switch-on only, L80)

A record made before its adoption registers (a host restart) or a local id reused after one
matched a saved binding by id alone, so a phone or paired desktop could be handed a second handle
for the pane before the terminal's own handle arrived and stay on it.

* test(runtime): a restored pane is only unnamed in the adoption gap, and no local record rescues a paired server's tab (L80)

* test(runtime): recovery, close and routing suites set up terminals as production does (L80)

A recoverable pane is one the host inventory dropped with no exit, live terminals register
rather than appear only in the window graph, and SSH stores route as the real Store does.

* test(runtime): suspects 3, 4 and 5 bind, register and apply as production does; PR-1's main-layout closes in the part-10 and part-11 close tests (L80)

* test(runtime): a fixture store applies main's layout commands as a profile does; PR-1's own closes in the part-05, part-08 and UUID-leaf close tests (L80)

* test(runtime): the profile-store stand-in moves to its own fixture, keeping bind-first under the line limit (L80)

* test(runtime): orphan adoption reads the pane from main's layout with the switch on, not the record copy (L80)

* test(runtime): a phone's pane close and a pinned tab close are main's layout closes with the switch on (stablyai#31571), in the no-widening and surface-close tables (L80)

* test(runtime): a paired phone close commits in main's layout with no relay ack when the switch is on (stablyai#31571) (L80)

* fix(terminal): a parked pane resumes its agent once main has closed the other pane that held the session (layout-owner build, L80 P1)

* test(runtime): the parked-pane suite's restart holder is a saved pane with no registered terminal, under the line limit (L80 P1)

* fix(terminal): a spawn commit that binds a reused local terminal id unbinds it from the saved pane of the older process (layout-owner build, L80 P2)

* test(runtime): the legacy OSC row test uses the UUID leaf main's layout holds and registers its live terminal with the switch on (L80 E5)

* test(runtime): the pty-exit echo close of a dead headless tab is expected to fail with the switch on until W4's close PR (L255)

* fix(terminal): a terminal record that arrives, changes incarnation or moves workspace after main composed its panes re-composes them, so a restored pane lists once (layout-owner build, L80)

* fix(terminal): a pane bound to a terminal a paired server runs keeps that binding and host, and is never indexed as a local spilled tab (layout-owner build, L80/L257)

* fix(terminal): a pane main owns no longer proves its terminal runs; the host's inventory and the terminal's record decide, so a dropped shell reads disconnected (layout-owner build, L80)

* fix(terminal): an SSH pane whose host says its shell ended keeps its leaf on a negative or failed code, as the window does, so recovery starts a shell in it; a stale handle learns the connected replacement (layout-owner build, L80)

* fix(agent-launch): a terminal main's layout still binds counts as running through a host listing gap, so a launch never starts a second shell in its pane (layout-owner build, L80)

* fix(terminal): a disconnected terminal main still binds stays a stop target, never reads stopped unproven, and keeps an exit wait waiting until a certified exit (layout-owner build, L80)

* test(terminal): the wait unit fixture answers the exit-settled dependency

* test(terminal): realistic fixtures for three phone-row tests: unique leaf ids per pane, and a window that draws main's own panes (layout-owner build, L80)

* fix(terminal): a stale handle learns a pane's replacement only when it was resolved for that pane, its own terminal ended, and the replacement is connected there now (layout-owner build, L80)

* test(session-tabs): switch-on variant for a window's chat placement (main's layout order rules); headless phone tab order is L283

* test(terminal-close): switch-on close reds name their ledger rows (L255, L256, L259a, L259b); a same-id respawn's commit stamps its incarnation; the crossing terminal binds first

* test(terminal): the crossing registration is refused by main's workspace fence (L256 switch-on variant); the placement test's tabs are main's saved layout

* refactor(terminal): the stale-handle guard reads an issue-time pane stamp on the handle record, not a second map; moved-pane, restart and single-reader tests (layout-owner build, L80)

* test(session-tabs): switch-on variants: the desktop's tab name is main's rename, and a mobile-created tab's live shell is the one its commit registers (layout-owner build, L80)

* fix(terminal): a re-issued handle keeps its issue-time pane while no pane holds its terminal (review)

* fix(terminal): stop targets and exit waits use main's holds check (workspace and incarnation), and any exit event settles a later wait (review)

* test(session-tabs): name the switch-on placement and split-group reds L288 and L289

* fix(terminal): an exit drops a closed pane's record in the same write that retires the pane, and an unbind folds into the binding write (layout-owner build, L80)

The exit used to remove main's closed-pane entry in a session write of its own, before the
retirement write. It now reads the entry, and the retirement write for that pane drops it,
re-checked by terminal id and incarnation. An exit that keeps the surface (relay contact lost,
intentional stop) keeps the entry until a confirmed exit or its TTL.

A reused terminal id's unbind is now a pure computation that applyPtyBinding applies through
its existing writes. Two split-pane tests still expected a -1 exit to close the pane; they now
follow the ruling that any negative code keeps it. Static-gate fixes: test fixtures and tests
use typed member access instead of Reflect, and no longer use type assertions.

* test(runtime): the shared fixture store announces its session writes and the L283 store lists its partitions, as a profile Store does; L289's test passes on that store (layout-owner build, L80)

The fixture store bindPaneFirst fills now tells write listeners of every session write, direct or
by a layout command, so main's post-commit merge runs as it does on a real Store. The notice is
not enumerable, so a test's own `{ ...store }` copy, which keeps its writes elsewhere, does not
inherit a subscription it would never fire. With it, the targeted split-group create (L289) passes
at this head, so its it.fails mark goes. L283's test runs on a store that names its partitions and
stays it.fails("L283").

* fix(terminal): recovery checks the caller's handle after the cheap guards again, so the switch-off build re-issues no handle on a refused recovery (layout-owner build, L80)

The stale-handle switch point had moved the handle lookup ahead of the pane, terminal and
workspace checks. That lookup can re-issue a handle, so a switch-off recovery refused on those
checks gained a side effect base never had. The lookup runs after them again, and the switch-on
replacement check lives in one method.

* test(runtime): the layout-owner start fixture's spawn commits the pane binding before main registers the terminal, as the host does (L80 switch-on variant)

The window-tab start test came in with the main merge after bind-first; its fake spawn returned
a terminal no pane was bound to, so registration refused it. The fixture's spawn now binds the
pane first, as every other bind-first fixture does.

* fix(terminal): a new process under a reused terminal id starts with no exit code, so an exit wait on it cannot settle on the old process's code (layout-owner build, L80)

The record of a reused id kept the earlier process's lastExitCode, and with the switch on an exit
wait settles on any recorded code once the terminal reads disconnected. A new incarnation now
clears the code and cause. Also drops the L283 test's partition-list option: the fixture store
already lists its partitions.

* test(runtime): switch-on variants for four tests the full local run never loaded (L80)

- A restored pane's host lists the terminal it started; a pane main owns vouches for none.
- The other pane holding a slept session is in main's layout, so an unverifiable holder blocks
  the resume as it does through a window.
- Main sleeps a workspace whose panes it lists itself, even with a renderer attached (stablyai#31571).
- A graph sync no longer reconnects a disconnected terminal of main's pane; the inventory decides.

* test(runtime): the phone Markdown route tests use stablyai#31667's realistic store, and the untitled cases wait on L268 with the switch on

The window's tab uses main's id (the file path), and the store lists its partitions and saves each
open editor's unified tab and group, as the window does (the fixture from stablyai#31667). With the
switch on, the three untitled cases fail on L268, main's row merge dropping the untitled flag,
which stablyai#31667 fixes; they are it.fails until A1b's history includes it.

* refactor(runtime): two merged files back under the line limit, by moving A1b's additions to their helpers (L80)

After the main merge, the holder-pane check and a closed tab's live-terminal lookup pushed two
files past the line limit. The holder check is now paneHoldsSession (main-terminal-surfaces.ts)
behind one method, and the closed tab's lookup is one parent-class method. Behaviour unchanged.

* test(runtime): the headless chat tests open the chat on the publish path, as every product path does; L283 stays it.fails for the main-owned reorder

openChat recorded the chat only in the runtime's snapshot, so main's layout never held it and the
phone's reorder was refused as an unknown tab. On the publish path main's layout holds it; the
reorder is accepted but does not survive the row rebuild after a close (L283, reopened).

* test(runtime): bind-first census follows the one spawn-commit step (stablyai#31596); the unbound-registration test keeps its spawn from the suites' stand-in commit

After the main merge both start lanes register in spawn-commit-assembly.ts, after
savePtySpawnPaneBinding (or, adopted, on the live owner's pane); the census lists that file. A
phone create now writes its pane before the spawn, so the suites' stand-in spawn commit bound it
and the test of a path that registers unbound passed for the wrong reason; that test's controller
now opts out of the stand-in commit.

* test(runtime): convert four switch-on tests the disguised full runs hid

Real switch-on runs showed four files whose fakes registered terminals unbound
or confirmed a stop without its exit. Bind first, stamp the source as main
does, and deliver the exit a confirmed stop delivers. Switch-off unchanged.

* test: type the two converted fakes instead of casting them

The PTY controller fake now takes the controller's optional pane fields, and the
switch-on store is a typed RuntimeStore, so neither needs an assertion.
nwparker and others added 21 commits October 23, 2026 02:53
…try can never re-run its command (stablyai#32278)

* fix(terminal): a timed-out CLI split names its pending pane and can be retried without splitting again

Fixes stablyai#32169

* fix(terminal): a blank retry id or a closed pane never re-runs a split's command

* refactor(terminal): a split retry only waits for its pane and never asks the window again

* fix(terminal): a split missing from the graph is reported pending, never closed
…32274)

Every place Orca reads an SSH key or identity file now goes through one
reader that refuses directories, FIFOs and devices (including through
symlinks) with a nonblocking open, and caps the read at 1 MB. An auth
failure now names any identity file that was skipped and why.

Fixes stablyai#31965. Reported by @Huge, who also proposed the first fix in stablyai#31966.

Co-authored-by: Martin Vlach <mr.huge@seznam.cz>
…unchanged (stablyai#32251)

* fix(mobile): keep a terminal tab's chat identity while its handle is unchanged

A status frame that omits agentType or providerSession flipped chat back to the
terminal for tabs with no launchAgent (stablyai#31977), and left a launched tab with no
session to read (stablyai#32134). Mobile now remembers the last resolved agent and
session per tab and handle, so a missing field is treated as missing evidence.

* fix(mobile): keep remembered chat read-only and drop it on relaunch

A chat resolved only from this phone's memory may sit on a bare shell, so its
writes stay locked until live status confirms the agent. A live agent frame
without a session no longer inherits the previous conversation, and the
latest remembered agent outranks an older launch hint.

* fix(mobile): gate native-chat image sends on a confirmed live agent

An image send from a chat only this phone remembers could paste into a bare
shell; it now shares the confirmed-agent gate with text sends.

* fix(mobile): type remembered chat identity structurally

Importing MobileSessionTab into the eligibility module pulled React Native
globals into the e2e program through nested-agent-idle-session-owner.spec.ts,
retyping setTimeout as number there.
…n reveal or recovery (stablyai#32281)

Settled reveal and every other atlas recovery forced the current xterm buffer
onto the screen even while a TUI held DEC 2026 synchronized output. Codex
erases rows inside each held redraw, so a reveal mid-redraw showed a blank
pane for a frame (stablyai#32198).

Viewport presents now have one mode: they keep the last coherent canvas and
let xterm present when the TUI closes the frame, bounded by xterm's 1s
synchronized-output safety timeout. Structural replay keeps its retained frame
up until an open synchronized frame closes, bounded by its existing hold cap.

Co-authored-by: YEONGUK LEE <contact@abruption.dev>
* Use incremental tsrs for local typechecks

* Update pinned Bun to 1.4.4

* fix(deps): bump dompurify to 3.4.17 and docs next to 16.4.1 for security advisories

dompurify 3.4.17 patches GHSA-cg2g-xh64-v4q7, GHSA-mrv2-3pqr-j5jr and
GHSA-c6gr-qvv4-5v3f in the root and mobile lockfiles. next 16.4.1 patches
GHSA-2464-fhr4-w9xw, GHSA-673q-mjm6-r5xr and GHSA-p3m5-59mp-jv5r in the
docs site.

* Prepare mobile bundle for smoke benchmarks

* Give native compiler benchmarks a larger ARM runner

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
)

* test: remove redundant cases and repair behavioral assertions

* docs: reconcile audit records with current main

* fix(deps): apply security updates from stablyai#32101

Apply the six-file DOMPurify 3.4.17 and Next.js 16.4.1 patch from PR stablyai#32101 to resolve the dependency security gate for this test audit. Original patch by Brennan Benson.

* test: satisfy changed-line quality gates

Add required braces and replace filter indexing with find. Rerun both CI quality gates and the 27 affected relay tests; refresh audit hashes and record the corrections.

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* test: replace redundant checks with observed behavior

* docs: reconcile completed audit batches and upstream review

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
…s itself back on after a cooldown (stablyai#32397)

* fix(watcher): back off on system event stalls and let the crash fuse recover

The watcher child's canary treated any missed delivery as a native deadlock
and exited. When the OS event source itself is stalled (an overloaded macOS
file-event daemon), each restart recreated every stream, adding load, and
after three restarts in two minutes the crash fuse disabled native watching
until the app restarted.

- Before exiting on a starved canary, probe the same dir with an independent
  fs.watch. If fs.watch delivers, it is a child deadlock: restart as before.
  If both stay silent (and fs.watch was proven to work on this host), it is a
  system stall: keep the child and its streams and back off the canary
  (30 s doubling to 5 min) until delivery resumes.
- The crash fuse now half-opens after an exponential cooldown (5 min doubling
  to 60 min) so native watching is retried; callers keep their polling
  fallback while it is open. A trial crash reopens it immediately; a trial
  that stays healthy for 10 min resets the escalation.
- Arm the canary while a native subscribe is pending: always on macOS (its
  subscribe does not crawl), and after a 10 min grace on crawling backends,
  so a subscribe stuck behind a deadlock no longer disables the check.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(watcher): bound stalls, keep callers fresh, and let the fuse reopen reach callers

Review follow-ups for the watcher stall/backoff change:

- fs.watch proof is no longer a cached startup probe. Any delivery in the
  process (including the canary's own fs.watch) proves it; a failed shallow
  probe is re-measured after 30 s instead of failing shallow subscribes forever.
- The canary keeps one long-lived fs.watch on its dir and checks whether it
  delivered since the probe started, instead of opening one per probe (which
  makes macOS rebuild the shared event stream).
- Any native callback in the child during the probe window counts as proof the
  child is not deadlocked. A stall that lasts 15 min forces a restart, still
  rate-limited by the crash fuse.
- The child reports delivery health (healthy/stalled) to the host. A stall that
  outlasts one backoff period makes the host send an overflow (rescan) to every
  subscription on that cadence, and once more on recovery.
- Roots that fail while the crash fuse is open are no longer cached as
  unwatchable until shutdown, and the git-common narrow watch's polling
  fallback retries native events every 5 min. Log wording no longer claims
  every caller polls.
- Linux/Windows keep the canary disarmed during a pending crawl (huge or
  network trees); macOS stays armed.
- Fuse escalation resets only after the trial child's canary has confirmed
  delivery for 10 min, not merely after it survived 10 min.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(watcher): back off stall rescans and end stalls when the canary disarms

- Host rescans during a stall now back off with the canary (30 s doubling to
  5 min) instead of every 30 s for the whole stall, and stop once no
  subscription is live.
- A canary that disarms after a stall (no live roots, or a crawl on
  Linux/Windows) reports 'unknown', which ends the host's stall mode with one
  rescan; the stall is re-detected once the canary re-arms. The crash fuse
  ignores 'unknown'.
- Health reports are ignored after supervisor disposal (they were already
  limited to the current child).

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
…non-UTF-8 files ask before converting (stablyai#32413)

* fix(editor): keep every view of a shared record on its text; ask before a lossy UTF-8 save

- A clean record's draft may reach a view's live model. Views of a record share one draft, so a
  commit that reads clean (a rich-mode undo that differs from disk only in trailing whitespace)
  is the record's text; refusing it left the source view showing undone text, and typing there
  resurrected it.
- A rich view applies another view's text (or a reload) without an undo step, so its Cmd+Z no
  longer reverts the source view's typing or re-applies text undone there.
- An outside change to a path (Source Control discard, watcher) now reaches views of the same
  record in other workspaces' panels, not only the named workspace's, so their cached copy of the
  discarded text is dropped and reloaded.
- Explicit saves send the lossless-UTF-8 precondition. A non-UTF-8 file asks for confirmation
  (Save as UTF-8, destructive) before replacing characters that aren't valid UTF-8; declining
  leaves the bytes alone. The autosave-paused notice no longer promises a lossless conversion.
- Dev builds resolve worker-thread entries from the main output folder when the caller was bundled
  into a shared chunk (packaged builds resolve from resources and are unchanged).

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(editor): say why save-and-close refused a non-UTF-8 file

---------

Co-authored-by: Claude <noreply@anthropic.com>
…ead of the whole folders (stablyai#32410)

* perf(watch): stop watching all of userData and .git for a handful of files

On macOS a directory fs.watch is a recursive FSEvents stream. Two watches used
that to notice a few files:

- The Active Server preference watched the whole userData directory for
  orca-environments.json, so Orca's own writes there (agent-hooks status,
  terminal history, browser caches; ~10 events/s measured) were routed back
  to it.
- The shallow git metadata watch ran fs.watch on each repo's whole common
  .git dir (and .git/logs), receiving objects/, refs/, lfs/ and every linked
  worktree's churn to see six files.

Both now watch the individual files (kqueue on macOS, no fseventsd) through a
new rebinding file watch: events and a 30s sweep re-stat the path, rebind when
a rename-replace or deletion moved it, and pick up files created later. Events
are confirmed by a stat (size/mtime/ctime/inode) because macOS raises an
attribute event for every mmap read of the index and packed-refs; the watch is
one-shot with a backing-off window so a file every git process maps costs about
one rebind a second instead of a wakeup per read. The registry path is
unchanged, so CLI/serve readers and older peers are unaffected. Linux and
Windows keep the non-recursive directory watch for shallow subscriptions.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(watch): don't hang unsubscribing an errored watcher; find late metadata files sooner

- Node closes an errored fs.watch handle without emitting 'close', and a later
  close() is a no-op, so awaiting 'close' could hang a watcher-child unsubscribe
  and every subscribe queued behind it. Errored watchers are now dropped without
  a close, and closeFileSystemWatcher also settles on 'error' or after 1s.
  Applies to both the macOS file watch and the Linux/Windows directory watch.
- Missing metadata files (packed-refs from pack-refs/gc) are re-statted every
  2s instead of waiting for the 30s rebind sweep.
- On macOS the delivery probe watches a file, matching the kqueue path the
  shallow subscriptions now use.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
* test: audit delayed replies and database boundaries

* docs: reconcile the fourth test audit batch

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
…i#32442)

* ci: run relay integration on the Blacksmith x86 pool

* ci: use the validated Blacksmith pool for Linux packaging

* test: allow shared Linux download caches across validated runner pools
* test: audit CLI output and deferred delivery boundaries

* docs: reconcile fifth test audit with main

* test: build process-host for Cloud-only relay validation

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Upstream range a45bd06..5d32f37, 138 commits. 3 conflicted
files, each one hunk; the fork's lines are kept unchanged on upstream's
new code.

- Update sources (fork #10 #14), src/preload/api/updater-api.ts:
  upstream's shortened listBuilds comment (upstream stablyai#32257) plus the
  fork's `source` line.
- Host resume (fork #30), src/shared/agent-hook-listener.ts: the
  fork's statusRowToComplete on the previous row, beside upstream's
  new agentProcess read (upstream stablyai#31780).
- Host resume (fork #30, past sync resolution 1a247d1),
  restored-pane-materialize.ts: upstream's resumeClaim (upstream
  stablyai#31718) beside the fork's HOST_PANE_AGENT_REPORTED guard on the
  fresh-launch branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-23T13:55:34.440363Z 5aafdd0 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

{showEmptyState ? <TabGroupEmptyState onNewTerminal={commands.newTerminalTab} /> : null}

P2 Badge Render only one empty-workspace action surface

When session hydration has completed with an explicit empty tabsByWorktree row and the group has no client-hosted rows, showEmptyState is true at the same time as the newly merged WorkspaceEmptyState condition above. This mounts the absolute TabGroupEmptyState over the richer workspace chooser, producing duplicate terminal actions and potentially intercepting its central controls. Reuse one empty-state implementation or make the predicates mutually exclusive rather than rendering both.

AGENTS.md reference: AGENTS.md:L24-L26

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@kiwidream
kiwidream merged commit 7d33e61 into swaplabs/main Oct 23, 2026
107 of 110 checks passed
@kiwidream

Copy link
Copy Markdown
Author

Follow-ups from this sync: #51 (the real WSL terminal check failed and could not be attributed from the host); the resume-claim note on #41 (#41 (comment)). Merged without waiting for reviewers, at the operator's request: Codex had completed on 5aafdd0 with no findings; Tenki had not reviewed.

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.

7 participants