Repository navigation
Conversation
…ine module (stablyai#22430) stablyai#22349 added `src/shared/structured-agent-session-tool-call-block.ts`, which the projection and live turn the session route already reaches import. The PR was src/shared-only, so its CI never ran the closure suite; main's pin stayed at 4215 while the closure measures 4216. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…omoted (stablyai#22439) C30 was promoted to general on 2026-09-23 (selector generation 286). The same-cap wave now rolls it as a general cell instead of handing it back isolated, and the shadow gate reads its pool beside C27-C29. Follow-up to stablyai#22375. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…ds (stablyai#22443) The source cell runs the rehome commit. An Asia source pays a cross-ocean round trip per statement while holding relay_cells row locks every cell needs, which convoys the fleet database. Selection now drops source cells outside the director's region before building the decision window, so the incumbent_region filter shrinks while Asia cells stay valid targets. The preview counts the same hosts as source-outside-director-region and the poll summary reports skippedOffRegionSourceCells. Temporary stopgap. Claude-Session: ced32ebb-7155-4413-adad-1eccd14c2010
* feat(relay): alert on relay cell table lock convoys Adds a log-based metric and alert for cell-inventory lock holds of at least 1,000 ms, and a Cloud SQL log metric and alert for relay-only lock timeout cancels at 20 or more per minute. NOWAIT refusals are excluded: background sweeps produce about 160 per minute even with rehome paused. Replayed over 2026-09-20 14:00 to 2026-09-22 15:00 UTC: the hold filter matches all 93 asia-east2 rehome holds plus 9 director holds, and every one of the 88 cancel burst minutes overlaps an asia-east2 hold. Claude-Session: ced32ebb-7155-4413-adad-1eccd14c2010 * fix(relay): page only on cell lock holds; director holds stay visible Director holds of 1-2.5 s recur several times a day with rehoming paused, and pausing rehome does not stop them. The paging hold policy now selects role=cell samples only; a separate policy with no notification channel keeps director holds visible. The burst documentation no longer claims no burst happens while paused, and the runbook points a burst with no cell hold at the director policy. Replayed cell-only: 93 of 93 asia-east2 holds, 0 director holds over 2026-09-20 14:00 to 2026-09-22 15:00 UTC; 0 from then to 2026-09-23 07:30. Claude-Session: ced32ebb-7155-4413-adad-1eccd14c2010
…home commit (stablyai#22449) * fix(relay): lock only the target cell row, last and NOWAIT, in the rehome commit The idle-rehome commit runs on the source cell. From an Asia cell each statement is a cross-region round trip, and the transaction locked every relay_cells row plus every runtime, capability and safety row before about twenty more statements, so each Asia-source rehome held the whole fleet's cell rows for ~3.6 s and every reconnect, renewal and placement queued or timed out behind it. The commit now reads the cell inventory and the runtime, capability and safety tables unlocked, keeps the control and worker rows locked (now NOWAIT), and takes one cell lock: the target row, in a single statement that locks it NOWAIT, re-checks enabled, general admission and capacity, and reserves the units, issued as the last statement before COMMIT. A target that changed admission, filled up, or is locked by another writer rolls the whole commit back and answers deferred (candidate-ineligible). The hold is sampled under a site label, so cellInventoryHoldMsMax still sees rehome holds and rehomeTargetRowHoldMsMax reports them apart. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(relay): fail closed on the rehome target-row lock clause The target-row statement now carries FOR UPDATE ... NOWAIT unless the dialect is explicitly SQLite, so a wrapper that omits the optional dialect can no longer run the reservation unlocked. Test wrappers and the fault injection entry forward the dialect they wrap. The latency test also probes the admission and region tables at every round trip; only the target's admission row may be locked, and only before COMMIT. The runbook notes that an Asia-sourced commit holds the rehome control row for about 6.5 s, so a pause that fails once is retried. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…tablyai#14287) Fixes stablyai#22049 Co-authored-by: Nurdaulet Bolat <204565446+nvimq@users.noreply.github.com>
…tablyai#22457) * fix(cloud-verify): scan HEAD instead of all history - Gitleaks now verifies only the checked-out revision - Reduces scan scope and improves verification workflow performance * ci(cloud-verify): clarify that Gitleaks scans HEAD-reachable history
… creates (stablyai#22291) * feat(agent-launch): let a caller reserve the pane its terminal launch creates * fix(agent-launch): refuse a launch whose reserved pane is already live * fix(agent-launch): refuse a live reserved pane before it is revealed The live-pane refusal used to fire in the executor, after createTerminal had already issued a handle, published the mobile snapshot and revealed the tab. The reveal re-registered a fresh launch config over the running agent's. agent.launch now passes requireFreshPane with a reserved pane, and createTerminal throws AgentLaunchPaneAlreadyLiveError as soon as spawn reports it attached to a live pane. That is before any handle, snapshot or reveal. The spawn reattach itself is the one terminal.create already uses, so the live PTY is never killed, and the stable-pane create claim is still released in finally. The isReattach plumbing added to the launch factory for the old check is gone. A replay-safe launch refused this way on an existing workspace now records a failed ledger row, the same way a name collision does. Before, the row stayed claimed, so every retry got agent_session_operation_unknown. agent.launchReplay passes the code through. On create-worktree the workspace already exists when the terminal is refused, so the row stays unknown. The code is added to the runtime passthrough list so callers can branch on it. The pane key is now in the replay fingerprint, deliberately. It is not placement: group, anchor and focus still stay out of the request and out of the ledger. It is identity. It is written into the pane's PTY environment and names the tab the caller has placed. A retry that reserved a different pane is therefore a different request. Replaying the first answer would return a key the new reservation can never find. This matches terminal.createAgentSession, which also fingerprints its tab and leaf ids. The key is only folded in when present, so every existing digest is unchanged, and a test pins that. The wire schema now refuses a tab id the runtime would not adopt as sent: one with surrounding whitespace, which the runtime trims, and one longer than 512 characters, which the spawn reservation does not key on. It reuses the tab-id schema that Placement uses. * test(agent-launch): pin that a refused live pane issues no handle The refusal test named handle issuance but only asserted the reveal, so a throw moved to just before the reveal would still pass. Assert no terminal is registered, with the attach test as the positive control.
…et it from goal mode (stablyai#22377) * feat(native-chat): show a Codex chat's goal above the composer and set it from goal mode Structured Codex chat now treats the thread goal as session state: a banner above the composer shows the current goal (pursuing / paused) with clear, pause/resume and expand; /goal enters a goal mode whose send calls thread/goal/set; the objective is journaled as a user message marked as sent as a goal. The banner is derived from the journaled goal rows, which Codex's resume snapshot refreshes, so a reopened or adopted chat shows its goal. Fixes STA-8159 * fix(native-chat): replace a recorded goal by clearing first, and recover a lost goal-change response - A set while the journal records a goal (any status) clears it before setting, so the new goal starts with its own time and token counters instead of rewriting the old goal's objective in place. - The threadGoal plan answers an unknown outcome from the goal the journal records and reruns otherwise, so one request timeout no longer refuses every later Clear/Pause/Resume as unknown for the mounted session. - The goal-mode chip says "Exit goal mode"; "Clear goal" stays the banner's action on the provider goal. - A typed bare /goal on Enter enters goal mode, the same as picking it. - The renderer reads the goal off the tail of its ordered snapshot; the host's unordered map keeps the by-sequence reader. - Drop the composer's duplicate in-flight guard; the goal controller already serializes changes. - Pin that a counter-only revision reaches a subscriber's live page under its original sequence. * fix(native-chat): keep a bare /goal inside goal mode as the entrance, and pin goal delivery and serialization - A bare `/goal` submitted while already in goal mode re-enters the mode instead of setting a goal whose objective is the literal text "/goal". - The counter-only revision pin now drives the host's own event sink bound to a real journal, so it goes red when the publish after a lifecycle transition is dropped; the previous fake sink never published. - Pin that a set which threw after journaling its objective puts that objective back exactly once when the ledger reruns the same operation id. - Cover the goal controller hook: absent without host support, the loaded window wins over the host's answer, a second change while one is unsettled answers false without a request, and a refused change frees the next one. * fix(native-chat): resume a blocked or usage-limited goal, and keep goal-mode drafts honest - The goal bar offers Resume on a blocked or usage-limited goal, which the provider resumes exactly as it resumes a paused one; a goal whose token budget is spent still offers only Clear. The rule lives beside the other goal facts in shared code so every reader answers it the same way. - A `/goal <text>` typed inside goal mode sets the objective `<text>`, as it does outside goal mode, instead of a goal whose objective is the literal command. - Setting a goal is a host round trip; a draft edited while it was in flight is no longer wiped when the goal lands, matching every other host command. - Pin that a lost status-change response is read as applied only when the recorded goal is in that status, that a cleared row in the loaded window outranks the host's earlier answer, and that the PTY lane is untouched. * fix(native-chat): keep the load-older anchor on the loaded window when a live revision lands below it A live revision of a row keeps that row's original sequence. When the row is older than the client's loaded window, the shared reducer merged it in and it became the load-older anchor, so paging `before` it skipped every row between. A goal's counter-only revisions during a long goal turn reach any client that attached after the goal row left its window, so a reopened chat lost rows on scroll-back. The reducer now admits live rows only at or above the window's oldest row while older rows remain on the host; the journal keeps the revision and the page reader serves it once the window reaches the row. With nothing older on the host the window is the whole journal, so a row below the head is admitted as before. Also drain accepted provider events before a goal set reads the journal to decide whether it replaces a recorded goal.
…o the turn's end (stablyai#22432) * fix(native-chat): keep one live tool-run header from a call's start to the turn's end The collapsed tool run's header was two elements, one for "a call is running" and one for "nothing is", chosen call by call. Every call start and end remounted it, the count disappeared while a call ran and came back one higher, and a call that finished inside a frame still bought the whole swap. That is the 42→43 flicker in the report. The header is now one element whose live state belongs to the turn, not to any call: it stays live from the run's first call until the agent moves past it (prose, a further run, or the turn's end), and settles in place. While live the sentence speaks in the present tense and counts the call in flight ("Running 3 commands"), with the latest call's command beside it as a muted preview; once settled it reads as before ("Ran 3 commands ✓"). The category glyph is the run's in both states, and the completion mark only appears once settled, so nothing pops between calls. Which run is live is derived where the transcript is sliced into rows: the last row that speaks or acts is the trailing one. A reasoning aside after it leaves it live; an answer or a further run settles it. Present-tense forms for the ten sentence categories are added to the shared copy and the English catalog. The transcript-file lane, which renders with the structured activity UI off, is unchanged. * fix(native-chat): settle a run blocked on the reader, keep it live past an approval - A run whose question is awaiting the reader's answer no longer pulses "Reading 1 file" while the agent is blocked; it falls back to its calls. - An approval's receipt no longer moves past the run above it, so the call it just approved reads as running while it runs. - The header button is the live region, so the count is announced too. - Drop the unused live option and record from the shared English sentence; nothing renders it yet. * fix(native-chat): stop the settled run's check from fading in on every mount Windowing remounts settled rows as the reader scrolls, and a restored transcript mounts them all at once, so the fade replayed where nothing had changed. Also pin that the live header counts the next call on the same element.
* feat(sidebar): copy workspace name from context menu Add Copy Name directly below Copy Path in the workspace context menu. It copies the name through resolveWorktreeDisplayName, the renderer mirror of main's mergeWorktree fallback (custom name, then branch, then folder), so the copied text is what `name:` worktree selectors resolve against and a cleared custom name no longer copies `undefined`. The context-menu model now spreads the command handlers instead of listing each one twice, which keeps it under the file-length limit. Fixes stablyai#21980 Linear: STA-8068 * refactor(sidebar): rename Copy Name to Copy Worktree Name - Clarify that this copies the worktree display name, not the path - Update all locale strings and i18n keys - Rename test file to match * test(sidebar): cover folder workspace copy worktree name --------- Co-authored-by: Seongho Bae <me@seonghobae.me>
…perator (stablyai#22518) The selector membership check capped cell ids at c29, so enable failed closed with "selector membership is invalid" once c30 went general. Accept c1-c99 with no leading zero. Claude-Session: ced32ebb-7155-4413-adad-1eccd14c2010
…-edit cells (stablyai#22519) * feat(ipynb): parse ANSI SGR sequences in notebook output Tracebacks and stream output carry terminal colour codes that the notebook printed raw. Splits text into styled runs (16/256/truecolor, bold, italic, underline) and drops non-SGR escapes via the shared stripper. * feat(ipynb): render notebooks like a notebook, not a form - Prose inherits the app UI font instead of a bare terminal font name, which Chromium could not resolve and fell back to Times (removes the now-dead resolveEditorFontFamilyOrInherit). - Markdown cells render by default; double-click or Enter edits them in the same Monaco surface code cells use. Code cells activate on press so a collapsing neighbour cannot swallow the click, which also retires the root pointer-capture deactivation (Monaco blur already covers it). - Code sits on its own tinted surface, the active cell gets a ring, and the editor sizes to its content so activating a cell no longer jumps. - The always-on 8-button toolbar and native select become a hover/focus toolbar (move, delete, and a menu for insert and cell type); the Jupyter [n] prompt turns into the run button. - Outputs show only the richest MIME representation, HTML renders in a script-less, no-network sandboxed frame sized to its content, and ANSI colours use the default terminal palettes. Adopts the MarkdownPreviewBody reuse, richest-MIME selection, extra raster MIME ranks, and CSP-sandboxed auto-height HTML frame from stablyai#18542. Co-authored-by: maxidiazbattan <maxidiazbattan@gmail.com> * chore(ipynb): drop the nbformat label and BETA badge from the notebook header parseIpynb already rejects notebooks without a v4 cells array, so the label carried no actionable information; the parsed nbformat field goes with it. Removes those catalog keys and the stale lowercase code/markdown ones. * fix(ipynb): keep cells pixel-stable when they switch to editing The preview and the live editor disagreed on four things, measured over CDP: - Font: the excerpt painted the bare "SF Mono" name (or --font-mono via its row class), while Monaco appended its own fallbacks and landed on Menlo. Both now use resolveEditorFontStack, the editor font plus the terminal fallback chain. - Line height: 20px rows vs Monaco's 21px. Both read CODE_EXCERPT_LAYOUT. - Gutter: a 48px line-number column plus 12px inset vs Monaco's 25px gutter. Notebook cells drop line numbers (the Jupyter and VS Code notebook default) and Monaco's decorations lane is the same 12px inset. - Rows: colorized blank lines collapsed to 0px, and a trailing newline had no preview row. Rows are fixed-height and a trailing newline opens an empty last line, matching the Monaco model. The [n] prompt and run icon now share one grid cell, so the hover swap keeps the label's box and centre. The commented-line tint moves to a theme token. * feat(ipynb): VS Code-style run gutter above a fixed execution count Replaces the in-place [n]/play swap from 172b4f7. The [n] count is always shown and never moves; the run button sits above it in the same gutter column, revealed on hover or focus from an invisible slot that keeps its box. While a cell runs, the slot shows a spinner and the count reads [*]. * refactor(ipynb): give notebook code its own preview, keep MonacoCodeExcerpt unchanged 172b4f7 bent the shared MonacoCodeExcerpt (used by PR comment code context) to the notebook's layout, changing that surface's font, tracking and highlight tint. The excerpt's rendering is now byte-identical to main; only its colorizer is extracted as useMonacoColorizedLines. The notebook renders its own rows from CODE_LAYOUT, the same metrics its Monaco editor uses, so activation stays pixel-stable. * fix(ipynb): open the cell editor with the caret where the user pressed Monaco mounted with its caret at 1:1 and focus ran occurrence highlighting on that first token, so clicking any line lit up every 'import'. The press on the preview now maps to a model position (its rows mirror model lines) and Monaco opens there before focusing; keyboard and markdown activation open at the end. Occurrence highlighting stays on, as in VS Code notebooks. * feat(ipynb): put the execution count on top and the run button below it The [n] count now shares the first code line's box (the gutter mirrors the code surface's border and padding, and the count is one line tall); the run button slot sits beneath it and stays reserved while hidden. * fix(editor): re-colorize code excerpts when the theme changes Monaco's colorizer emits theme-specific token classes, so excerpts colorized under one theme kept that theme's token colours after a switch (dark-theme keywords rendered green on the light theme). Affects notebook previews and PR comment code context. * fix(ipynb): mount cell editors before paint so a click swaps in seamlessly @monaco-editor/react waits on an async loader, shows "Loading..." and builds the editor inside a display:none container. Every click on a cell therefore painted a placeholder, then an editor laid out at 0x0 (one uncoloured line), and the caret guess ran against the static preview, falling back to the end of the cell whenever the press missed a text row (padding, gaps). The cell editor is now created with monaco.editor.create in a layout effect: sized, tokenized (restoring its view state marks the visible lines stable) and placed before the first paint. Monaco hit-tests the press itself with getTargetAtClientPoint, so the caret lands on the nearest position for text, trailing whitespace, blank lines and padding; keyboard and markdown activation open at the start. Indent guides stay off to match the preview. Word and selection occurrence highlighting are off in cell editors, since clicking a word lit up every other occurrence like a search. Also drops wiring the rewrite made dead: the per-editor save shortcut (the notebook's capture handler already owns Cmd+S), the callback refs, the onSaveRequest prop, and the toolbar's positioning wrapper. * fix(editor): follow OS theme flips when colorizing code excerpts useMonacoColorizedLines read the theme once from settings, so a 'system' theme flipping with the OS never re-colorized. It now uses useDocumentDarkTheme, like the rest of the notebook; rendering is unchanged. * fix(ipynb): let Escape close an open widget before it leaves the cell The cell's Escape command had no context, so Escape with suggest, find or parameter hints open closed the whole cell editor. It now only fires when none of those widgets is visible. * fix(ipynb): keep HTML output frames at an opaque origin The output frame had allow-same-origin only so the viewer could measure its content height. Notebook HTML is untrusted, so the frame is now sandbox="" (opaque origin, no scripts) at a fixed height that fits a pandas head() or describe() table and scrolls anything larger. * refactor(fonts): move buildFontFamily into lib/monospace-font-family lib/editor-font-zoom imported it from a terminal-pane component module. The monospace fallback chain now lives in lib, and every importer, including the terminal, points there. Pure move; the tests move with it. * fix(editor): pass comma-separated editor font stacks through unchanged resolveEditorFontStack quoted a value like "JetBrains Mono, monospace" as one family name, which no font matches. A comma list is now used as-is; only a single name gets the quoted fallback chain. * chore(i18n): drop the Korean override for a removed notebook key --------- Co-authored-by: maxidiazbattan <maxidiazbattan@gmail.com>
Co-authored-by: m4air <m4air@m4airs-Air.localdomain> Co-authored-by: Andre Ambrósio <56239028+sirambrosio@users.noreply.github.com>
…yai#22370) * fix(native-chat): underline only file links a click can act on A chat message could underline a bare file name such as `deck.md` that resolved nowhere, and clicking it did nothing, so it read as a broken link. - Inline code and quoted text become file links only when they name a path (contain a `/` or `\`), matching plain prose; a bare file name stays plain code. - Every file link click now answers: it opens, or says the file was not found, that the host could not be checked, or that the path could not be resolved. - Explicit links like [x](README.md:5) route as files, and linked text keeps `#`, `?` and `%XX` literally instead of re-parsing them as URL syntax. * fix(native-chat): wrap the parsed file location so file URIs in chat text still open Linkified prose, quoted text and inline code wrapped their display text, which the literal wrapped-href route no longer URL-parses, so file:///... resolved as a relative path under the worktree. Wrap pathText[:line[:col]] from the parsed link instead.
…shape (stablyai#22312) * test(file-search): pin request-key listings against the real runtime shape * test(file-search): pin intermediate renders and late remote answers Strengthen the stale-answer guard to assert every intermediate render reads as loading (null), add a late-answer drop case, make the tab-entry loading pin non-vacuous, and correct the e2e comment for local listings. * test(file-search): keep only the non-duplicate runtime-listing pins Drop the remote projection cases already covered at the hook level, collapse the classifier integration to the loading pin, drop the local-only rapid-edit e2e, and fix the brittle README absent-file assertion that failed CI.
…rn (stablyai#22552) * fix: explain Xcode-blocked Git once in the sidebar and rescan on return * refactor: simplify Xcode toolchain banner to one classifier and a stateless rescan * refactor: fold banner copy into one table and cover SSH/runtime gating
…22548) stablyai#22399 admitted an OpenCode 2 form.created as a pane blocker only when metadata.kind === "question". On v2.0.15 that is an allow-list on a field with no contract: packages/schema/src/form.ts declares Metadata as an open Schema.Record and metadata itself as optional, and the public POST /api/session/:sessionID/form endpoint lets any client raise a real blocking form on a real session with no metadata. Orca dropped those, so the pane painted no blocker while OpenCode waited forever. Invert the default. Every form whose owner is a real session blocks; only a form owned by the "global" MCP-elicitation sentinel is dropped, because that owner is not a session and never goes idle, so its blocker could not be retired. That also restores websearch.provider as a blocker: it carries the real context.sessionID, session idle retires it, and while it is pending the agent is genuinely stalled on the user. Resolution is unchanged: clearAttentionForResolution keys on the exact form id plus source session, so a resolution for a dropped form matches nothing and cannot retire a live blocker.
…OTA phase C follow-up) (stablyai#22549) * fix(mobile-web): stop the page declaring viewport-fit=cover The shell already pads the WebView out of the status and navigation bars. With viewport-fit=cover, Android's edge-to-edge WebView still reports the window's bar insets through env(safe-area-inset-*), which react-native-safe-area-context on web reads, so every page-side SafeAreaView padded a full bar a second time. Without it env() reads 0 and the shell's pad is the only one. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): keep the shell's window insets out of the page WebView WebView M144+ forwards the window's systemBars and displayCutout insets to CSS env(safe-area-inset-*) for every WebView, and Chromium applies them regardless of viewport-fit. The shell already pads the WebView out of both bars, so every page-side SafeAreaView (expo-router's DefaultNavigator and the session header) padded a bar a second time. M139+ likewise resizes the visual viewport for ime(), which the shell has already done by shortening the WebView. The WebView now sees those three types zeroed, per Android's "zeroing" approach (not CONSUMED, so later changes still reach it). A listener replaces the WebView's own onApplyWindowInsets, so the zeroed set is passed back into it. iOS needs nothing: the WKWebView uses contentInsetAdjustmentBehavior = .never inside the padded shell and reports zero insets. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile-web): say why the page declares no viewport-fit The earlier comment claimed dropping viewport-fit=cover makes env() read 0 on Android; Chromium's WebView applies the safe area regardless of viewport-fit. The page simply never asks to extend under the bars, and the shell owns the safe area. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): keep the page inset zeroing private to the shell view The transformation has no honest JVM test (the builder runs as SDK 0 there and drops every inset type), so it moves into MobileWebShellView.kt as private members instead of standing alone. The listener comment now covers both the P-R listener and the S+ onApplyWindowInsets path it replaces, and the page document's comment says the env() zeroing is Android's; on iOS the padded WKWebView reports none. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
… chat (stablyai#22387) * feat(native-chat): one shell-environment setting for every structured chat Structured Codex chats started from the login-shell environment, while structured Claude chats started from Orca's own process environment, so a variable exported in .zshrc reached one and not the other. Both now start from the same base, chosen by a new setting: - on (default): the whole login-shell environment, as a terminal gets - off: Orca's environment plus PATH, locale, SSH_AUTH_SOCK, and the variable names the user lists The setting is re-read each time a chat starts or resumes. It is shown only when Chat UI, the Chat UI default view, and structured native chat are all on. Terminal-backed chat is unchanged. * fix(native-chat): normalize the shell-environment settings when a profile loads A hand-edited settings file could store the variable list as something other than an array, and the structured runtime called `.filter` on it per launch, so a malformed value failed every structured chat create and resume, and the settings pane render. Normalize both keys where the profile loads, the same way the other array settings are, through one shared normalizer the runtime policy also uses. Also pin that an uncommitted name draft survives an unrelated settings re-render. * fix(native-chat): keep the pinned account as the only source of a structured chat's Claude home The session record owns which Claude home a structured chat uses, and the acquisition pin (claudeConfigDirEnvPatch) is the only emitter of CLAUDE_CONFIG_DIR, compared against what the child would otherwise inherit. With the login-shell snapshot as the inherited base, a CLAUDE_CONFIG_DIR exported only in a shell rc flipped that comparison and produced an explicit pin to the CLI default home, which moves the CLI off its default Keychain item. Drop the inherited CLAUDE_CONFIG_DIR in the Claude launch resolver before the pin runs, as Codex already does for an inherited CODEX_HOME. A configured per-agent overlay still passes through, since the record already honors it. * fix(native-chat): drop Orca's own CLAUDE_CONFIG_DIR from a structured Claude child too The process spawner merges Orca's process env under the launch env, so a CLAUDE_CONFIG_DIR exported to Orca itself reached the child around the launch resolver's drop and unseen by the account pin. One helper now strips it from both inherited bases. Also declare the two shell-environment settings on the runtime store contract and add the six new strings to every locale catalog. * feat(native-chat): add shell variables one at a time with a removable list * fix(native-chat): return focus to the name input after removing a shell variable * fix(native-chat): use a neutral placeholder for the shell variable input The empty input showed a grey HTTPS_PROXY as its placeholder, which reads as a saved value, especially right after that exact entry is removed from the list. Use "Variable name" instead, in every locale catalog.
…rawn (stablyai#22528) * WIP * WIP2 * fix(browser): only pixel-capturing commands wait for the page to be drawn Every targeted browser command used to take an automation-visibility lease, which waits for two desktop-window animation frames (capped at 2 s). When the desktop window is minimized or throttled, that wait always hits the cap, so each phone tap or agent command stalled for up to 2 s (STA-8024). Input, page JavaScript, layout and accessibility snapshots all work on a hidden page; only pixel capture needs it drawn. The lease is now opt-in via `needsPaint` on the two commands that can capture pixels through this path (`exec` passthrough and `pdf`); `ensureVisible` is removed. Screenshots keep managing their own lease. (Commits 24821ae and 07b4a09 carry this change under WIP messages.) * refactor(browser): drop the session-recreate option only no-lease commands used Only exec and pdf take the automation-visibility lease now, and both create an agent-browser session, so the lease's re-registration path always recreates the session. Remove the `recreate` option from restartSessionForTarget and the options passthrough that fed it. Also fix the keypress test comment that still counted the lease's extra page lookup, and note that screenshots lease inside the screenshot lock. * fix(browser): resolve a command's page when it runs, not when it is queued Enqueue resolved the page's guest webContents and created its session up front. Before this PR every command re-resolved after its lease, which hid that; now a command queued behind exec/pdf, whose lease can re-register the page with a new guest webContents, would run against the old one. Enqueue now only picks the page (and so the queue). The webContents lookup and session setup move into the queued job and run when the command starts, after the lease for exec/pdf. The lease path's refresh collapses into that step. Resolving after the lease already sees a re-registration that happens during the lease, so the only thing left from the refresh is the case where the page's existing session is bound to an older guest: that session is restarted on the new one (keeping intercept routes) and the active-tab pointers move with it. That check now covers every session-backed command, not only leased ones. Admission is still checked at enqueue and again when the job starts. * refactor(browser): drop the session-restart reconcile no page can reach Every renderer re-registration of a page with a different guest goes through browser:registerGuest, which calls onProcessSwap in the same tick. That destroys the page's session and rejects its queued commands, so by the time a queued command runs, no session can still be bound to an older guest. The reconcile in the queue and restartSessionForTarget, its only caller, are removed along with the two tests that called it directly. A queued command now resolves the page's current guest, ensures its session, and runs. The re-registration tests now report the swap through onProcessSwap, as production does: commands queued behind the swapping lease are rejected with browser_tab_closed, the leased pdf runs on the new guest, and intercept routes survive the swap.
…e parent (stablyai#22563) Capped search rows are slices of the transcript. Own each row with the shared copier before buffering it, and keep the heap regression from stablyai#22378. The sidebar test harness now includes the worktree listing map the toolchain banner reads, so that rerender no longer crashes. Co-authored-by: fancivez <fancivez@gmail.com>
…r tests (stablyai#22567) The legacy worker terminal recovery retry re-arms itself on a 1s..30s backoff for as long as a dispatch stays deferred. Nothing in the runtime suite ever resolves one, so a single test armed a loop that kept re-running recovery -- and the worktree scans it issues -- through the shared `listWorktrees` stub for the rest of the file's run, landing inside whichever test was executing when the timer fired. That is what made `lineage-and-scan-cache-part-03`'s per-repo TTL test see 4 scans instead of 3 only under CI load. Give the controller a `cancelAllRetries`, track controllers while they have a timer armed, and cancel them from the shared runtime test lifecycle reset. Measured over the whole 1299-test file with an afterEach probe: 25 stray post-test `listWorktrees` calls before, 0 after.
…terminal launches with the session picks (stablyai#22523) * feat(agent-launch): let a caller reserve the chat session and carry session picks to a terminal launch * fix(agent-launch): keep a caller-minted session id named for its agent, and mint the fallback the same way * test(mobile): model the older host from the launch fields, not the refined schema * docs(agent-launch): describe the reserved session id as conversation identity, not placement The caller mints the session id so it knows which conversation it started; tab placement is not keyed on it. Also puts the terminal surface's doc comment back on createTerminalSurface. * docs(agent-launch): say a terminal launch reads the session picks on the wire contract The `sessionOptions` field doc still said a terminal launch ignores them, which this branch changed. * fix(agent-launch): check a reserved session id's token after the agent name, not the whole id A hyphenated agent name failed the one-token check, so any session id for such an agent was refused at the wire, while every other agent without a chat has its id ignored on the terminal.
…seam (OTA phase C follow-up) (stablyai#22556) * fix(mobile): route the bottom drawer's keyboard through the platform seam Fill-mode sheets called Keyboard.metrics() directly, which react-native-web does not implement, so opening one on the page threw and the shell re-downloaded the workspace. The drawer now reads useSoftKeyboard, whose native half seeds from metrics() and carries the event duration, and whose web half answers from the window (duration 0). The fill/content-sized seed rule and resolveBottomDrawerKeyboardInset are unchanged. A census keeps Keyboard.metrics/addListener inside the seam plus the tab-sheet hide wait. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(config): retire the drawer's exemption from the page keyboard census The bottom drawer now reads the keyboard seam, so no module in the source-control or review closures names react-native-web's Keyboard stub. The census also flags Keyboard.metrics, which the stub lacks. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): give the drawer an imperative keyboard pair from the seam The seam now exports subscribeSoftKeyboard and currentSoftKeyboardHeight beside its hooks. The drawer's effect is back to its original shape with only its Keyboard calls swapped for the pair, and useSoftKeyboard is back to {height, visible} with no metrics() seed. Seeding every consumer opened an iOS window between willHide and didHide where metrics() still reads open. The web pair answers from visualViewport, so it stays silent inside the shell and lifts sheets in a plain mobile browser. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): start the web keyboard subscription from the current strip A keyboard already covering the page when subscribeSoftKeyboard attached never produced onHide when it closed, so the occlusion hook and a seeded fill sheet stayed lifted. Outside the shell only. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…s draw one device pixel (OTA phase C follow-up) (stablyai#22569) * fix(mobile-web): drop the UA focus ring from page text inputs Chromium rings every focused text field (:focus-visible); no native TextInput paints one. A zero-specificity rule in its own inline block beside the Expo root reset removes it for every page input; buttons keep the browser's ring. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile-web): draw page hairlines one device pixel thick react-native-web pins StyleSheet.hairlineWidth to 1 CSS px, three device pixels on a 480 dpi phone; native draws one. A build shim replaces that one assignment with React Native's own formula (roundToNearestPixel(0.4), else 1/ratio), so every page hairline matches native without touching components. A rendered check at a real device scale (Playwright's emulated scale floors borders to CSS px, which no phone does) measures both parity fixes. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile-web): apply the hairline shim to react-native-web's CommonJS build The page's dependencies require react-native, so esbuild resolves every importer to react-native-web's dist/cjs build, which the previous filter did not match: the shipped bundle still assigned hairlineWidth=1. The filter now matches both builds, the rendered check requires the package the way the page does, and a builder test reads every hairlineWidth assignment in the bundle. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile-web): draw page hairlines at a width WebKit paints too 1/ratio is exactly one device pixel, and WebKit floors that to 0 and paints nothing (0.3333px at a scale of 3), so the iOS shell would have lost every hairline. The shim now uses native's device-pixel count plus half a pixel; both engines floor a border to whole device pixels, so each paints what React Native paints at ratios 1, 2, 3, 3.5 and 4, measured per engine. The rendered parity check now runs in WebKit at a device scale of 3 as well as Chromium. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile-web): check the parity style in the existing root-reset build Drops a second full build that read one HTML string, plus two assertions that tested the constant against itself. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile-web): round the page hairline up to the 1/64 px layout step Half a pixel over native's count kept borders at one device pixel but made the separators drawn as `height: StyleSheet.hairlineWidth` straddle two rows at about half of all offsets. Both engines lay out in 1/64 CSS px, and WebKit stores an exact 1/3 as 21/64 and paints nothing, so the width is now native's device-pixel count over the ratio, rounded up to the next 1/64 (22/64 at 3). The rendered check adds a separator at a 10.1 px offset in Chromium and WebKit. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(native-chat): dock the task strip on the goal tab The background-task strip was the full width of the message box, so it did not share an edge with the narrower goal tab underneath it. When a goal is showing, the strip now uses that tab's width and keeps a square bottom, and the goal tab's top stays square so the two sit on each other. * fix(native-chat): derive the task strip and goal tab seam from adjacency The strip and goal tab were each told by the chat session whether the other was showing, through two flags that had to match what actually rendered. The session also re-derived the goal tab's own visibility rule to compute one of them. Now each bar styles its side of the seam from the DOM: the strip takes the goal tab's width and drops its bottom corners and shadow when the goal tab is its next sibling, and the goal tab drops its top border and corners when the strip comes right before it. The flags and the duplicated goal visibility check are gone, so the seam cannot disagree with what renders, and anything placed between the two bars falls back to the separate look.
…phase C follow-up) (stablyai#22570) * feat(mobile): the page owns its safe area, like a native screen The shell reserved both system-bar strips outside the WebView and painted them bgBase, so every page screen showed a flat band above its header, sheet scrims stopped short of the status bar, and the dock floated above the gesture bar. For a page that declares `safe-area-insets` in `ready.accepts`, the shell now draws the WebView edge-to-edge and keeps only the keyboard strip off it. `init` carries the insets the view sits under (bottom 0 while the keyboard ends the view, top 0 under the update banner), and a move is re-sent over the existing route-update `init`. An older page keeps the reserved strips, since it has no reader for the insets. On the page, a root layout (`app/_layout.web.tsx`) feeds those insets to react-native-safe-area-context below ExpoRoot's env()-measuring provider. It also replaces expo-router's DefaultNavigator, an all-edges SafeAreaView that padded a second time. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile-web): re-measure the page pins for the root layout The page's route tree gained `./_layout.tsx` (its web sibling of the native root), so every pin that counts the tree moved: - Script sweep re-measured by building `routes.slice(0, n)` for each n. It reads 69 scripts at 16 routes, which matches the real build. The asset-ceiling crossing moves from 31 routes to 32. - Route closures now enter through both layouts. `entryNames` gains `[dir]` because `app/_layout` and `app/h/_layout` share a name. - Session closure pin 4216 -> 4219. The added modules are bridge-safe-area-insets, page-safe-area-provider and _layout.web. - The web-overrides allowlist names `app/_layout.web.tsx`. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): read safe-area ownership from the page-document state Review fixes on the page-owns-safe-area change. - Ownership is page-document state now. `page-ready` carries `accepts` beside `reports`, the patch sets `pageOwnsSafeArea` from `safe-area-insets`, and the session hook projects it like `backClaimed`. The screen's own per-session copy is gone. - Insets moves re-send `init` only to a page that declared `safe-area-insets`. A page that took route updates but not insets was sent a useless `init` on every keyboard show and hide. - The banner wrapper is gone. The root pads the status bar strip while the banner shows. - The shell session defaults the insets inline, with no predicate that mutated its argument. - The provider is folded into its single caller, `app/_layout.web.tsx`. The session closure pin reads 4218 (local 1032). - The screen tests share their module mocks, and the safe-area cases move to a suite of their own: owned page, banner, iOS and Android keyboard, and an older page that gets no re-init. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
v1.4.215 removed the agent-session PTY write gate (d443320), the hidden-resize output clear (8fa5883) and the mainWindow argument to the write-event check (9700030). The terminal-dock acknowledged write mirrors writePtyInput, so it takes the same shape upstream's writePtyInput now has; behavior is unchanged.
…oved d443320 deleted terminal-owned agent sessions and the agent-session PTY write gate with them, so 'rechecks the admitted session fence before the atomic write' asserts a check the runtime no longer performs and cannot pass without restoring upstream's removed subsystem. The OMP atomic-submit cases are unchanged.
v1.4.215 moved isTerminalWaitWhitespace's last upstream use into antigravity-terminal-readiness and stopped importing readFileSync in persistence-ui-state.test; the fork's Claude prompt detection and Ledger-tab persistence test still use both.
stablyai#22555 made callerSession a required key on run-scope and mailbox checks and replaced startLocalWorker's coordinatorPane with a coordinator identity. The watcher is a terminal-handle caller, so it passes no session and builds the identity with upstream's orchestrationCallerIdentity from the same handle/pane.
setActiveTabType now takes the worktree id, Antigravity joined the trust-preflight presets, and NativeChatLiveSession gained olderHistoryGeneration with a promise-returning loadEarlier.
…oad test filesystem-download-lifetime.test.ts is new in v1.4.215 and builds the handler context with four registries; the fork's pending-upstream diff cancellation adds a fifth. Declared as a passthrough seam, and write-input/persistence-ui-state budgets re-baselined for the two fixes above.
v1.4.215 restricts crypto.randomUUID in the renderer (no-restricted-properties), since it is missing over plain-HTTP Remote Web. Mirrors upstream's adoption in 7d2c399: createBrowserUuid calls randomUUID wherever it exists.
… tool name v1.4.215 renamed the tool row's hover group to group/tool-line, so the fork's uncategorized tool name kept a bare group-hover that no longer fires. Taken from upstream's own NativeChatToolLine change in the same release.
…ntract v1.4.215 folded root_directory_guard into code_paths; -X ours kept the fork's three-way condition naming it. The fork_ownership_guard exemption stays, and its seam line and budget move to the resolved condition.
|
…s budgets v1.4.215 added .mts/.cts and installed-oxlint cases pinned to upstream's 300/400/600/800 budgets; the fork's .oxlintrc.json already applies its 500/600/800/1000 budgets to those extensions, as the file's existing seam does.
…rness v1.4.215 made a writable Store require a profile-state authority; the fork's test now constructs through createSqliteTestStore and closes stores after each case, like upstream's own persistence tests.
…ders
Upstream's sender-scoped cancellation now subscribes to sender lifecycle events,
so a bare { id } sender throws; the fork cases use the EventEmitter senders
upstream's own cases in the same file already use.
Upstream's new middle-click repro test drives the mobile-actions seam with a plain-named leaf, and makePaneKey throws on non-UUID leaves. The seam now asks a leaf-keyed bridge helper that degrades to not-docked instead of throwing.
… cases v1.4.215 made targetCommitish required (stablyai#21835); the fork's three changelog-body cases predate it.
The ours.txt three-way merge carried upstream's new track-community-prs and package_windows cache assertions into two fork-owned contract tests; the fork deletes that workflow and has no Windows PR lane.
v1.4.215 stopped rendering a separate Result row; the coloring test keeps its one-glyph assertion and checks the result folds in.
…tream's caller census v1.4.215's census requires every launchAgentInNewTab call site to carry a profile the behaviour suites replay. The fork's hosted-review copy is profiled like upstream's fix-checks caller and spread into the table as a seam.
The store now persists to the profile-state database, so the fork case reads through upstream's readPersistedStateJson instead of the JSON file.
pr-ready-check-gate/reuse are new in v1.4.215 and assume upstream's job set; the fork's verify also requires fork_ownership_guard and has no package_windows or wsl_source_changed. Declared as seams.
The replayed stablyai#22541 contract makes loadEarlier return the page promise; awaiting it inside act() hung on reads the test resolves later, and the hang cascaded into 22 later cases. Uses upstream's own void-call form.
…ow imports v1.4.215's install-electron-package-binary re-exports from src/shared, which the image context and COPY list omitted, so every sandbox image build failed.
…alizer diffs Upstream's new transcript-replay test (stablyai#22586) replays every fixture in src/main/runtime/__fixtures__, including the fork's claude-code-ready-cold-start capture, and pins each one's I2 new-fail count to KNOWN_PREEXISTING_I2_FAILURES. Verified the table's own way: with ORCA_OLD_SERIALIZE_ADDON built from origin/main (v1.4.207), the fixture scores 88 checks, 8 both-fail, 29 fixed, 0 regression, 0 i1-bytes-differ. All 8 are pre-existing serializer limitations, not regressions.
…yai#23270) Playwright 1.63 (taken with v1.4.215) reads the forwarded `--` in `pnpm run test:e2e -- <spec>` as ending file selection, so the real-IME X11 step ran all 841 e2e tests and timed out at 25 minutes. Cherry-picks upstream's dffb349, which is on upstream main but in neither v1.4.215 nor v1.4.216. Declared as pending-upstream exceptions so the next sync does not reset these files to a tag that still has the bug; drop them once a stable tag carries it. Also declares the transcript-replay registration seam.
zpyoung
deleted the
zpyoung/auto-sync-fork-with-upstream-stablyai-orca-ru-20260928T1553
branch
September 28, 2026 17:57
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Absorbs upstream's latest stable release, v1.4.215 (
083f583a53e4c74a65acf420eee4ca2e0efa9df1), into the fork.mainis on v1.4.207. The earlier v1.4.209 attempt (#92) was closed without merging, so this one sync covers eight releases: 441 upstream commits and 5,923 changed files. This was an unattendedsync-upstreamrun.auto-ours+tree. The plain merge hit 42 conflicts, so it was retried with-X ours. Five tree conflicts remained. All five were "deleted by us, modified by them", and the fork's deletion stands (git rm):.github/workflows/track-community-prs.yamlconfig/scripts/electron-builder-mac-channel-config.test.mjsconfig/scripts/skill-sharing-release-workflow.test.mjsconfig/scripts/verify-dev-channel-packaging.test.mjssrc/renderer/src/components/native-chat/use-native-chat-live-session-visibility.test.ts--no-merges origin/main --not upstream/main v1.4.215range). All 474 are still reachable at their original SHAs. After this PR the range counts 484: the ownership commit plus nine fix commits.origin/backup/main-20260928-155359Zpoints at9710c6b44b(the oldorigin/main), confirmed withls-remote.main, so the merge base is correct.Ownership resolution (
config/scripts/sync-upstream-file-ownership.mjs)7,803 paths differed: checkout 5,583, remove 203, ours 187, merge-review 1,830.
src/shared/rpc-contract/rpc-params-catalog.generated.ts: rebuilt withgenerate-rpc-params-catalog.mjs.pnpm-lock.yaml:pnpm install --lockfile-onlychanges onlypackageManagerDependencies. That reflects the fork'spnpm@12.5.1pin from Add Heimdall watchers: PR sitter and objective runner #97; upstream is still on 12.0.0.release-mac-build.yml,reliability-gates.jsonc,ci-dependency-download-cache.test.mjs,create-draft-release.test.mjs,electron-builder-config.test.mjs,install-electron-package-binary.mjs,remote-wire-compatibility.md,mobile-browser-frameless-stream.test.tsx,filesystem-git-status-staging.test.ts, the fourorca-runtime-*OMP files,structured-agent-session-integration.test.ts,terminal-wait-detection.ts,use-native-chat-retained-session.test.ts, andremote-runtime-pty-transport.ts.en-runtime-required.jsonalso merged cleanly and was then regenerated, as its exception reason requires.pr.yml: took upstream's CI rework (perf(ci): reduce queue pressure without paid runners stablyai/orca#23053 and follow-ups): the ready-for-review readiness reuse, the masked outputs, and folding the root-directory and README-link checks intocode_paths. Kept the fork'sfork_ownership_guardjob, itsverifywiring, and thechanged_countoutput. The fork's removal ofpackage_windowsandwsl_source_changedstands.pr-code-change-scope.test.mjsandpr-workflow-parallelism.test.mjsfollow the same resolution.release-cut.yml: the fork keeps its removal of the skill-sharing release gates. Took upstream's-c.publish.releaseType=drafton both Linux commands.electron-builder.config.cjs: kept the fork'sowner: 'zpyoung'/repo: 'orca'. Took upstream'sreleaseType: 'draft'and the three newasarUnpackentries. The fork's mac build already publishes as a draft and asserts that it stays one, so this matches the fork's flow.create-draft-release.mjs: kept both imports.package.json: kept the fork version, test wiring andpnpm@12.5.1. Took upstream'stest:bun:profileand the new React Doctor rule levels.filesystem-handler-context.ts,orca-runtime-start-tui-idle-visible-read-probe.ts,runtime-terminal-wait.ts,main.css,NativeChatResolvedView.tsxandNativeChatComposer.tsx. Each is covered under the tier-4 review below.readme-downloads.svgandrelease-mapping.json.current-manifest.jsonandsnapshot-registry.jsonwere also kept and then regenerated withgenerate-skill-bundle-manifest.mjs --write, aftergenerate-bundled-skill-guides.mjs --writerefreshed the fork-onlyorca-askandorca-ledgerguides for upstream's new runtime-access-denied stub text.pnpm/action-setupor pin changes need replaying.--verify-residualsflagged 16 seam files. A clean three-way re-merge (no-X ours) of every seam file found conflicts in exactly those files, plus two whose budgets already matched (pr-e2e-gate-contract.test.mjsandNativeChatToolLine.tsx, fixed inc05eb6c011ande0a3c44cbe). Every conflict was resolved to keep both sides:ArtifactListRows.tsxwent from 196 to 58 lines (Virtualize artifacts list with reusable component stablyai/orca#22061). The barrel was restored from the tag, and theartifact-passwordsseam moved toArtifactListRow.tsxat the same +2/-3.visible-worktrees.tslostbuildVisibleWorktreeOptionsFromStateto the newvisible-worktree-options-from-state.ts. The workspace activity and review filter context moved there as a new seam (+4/-0), andvisible-worktrees.tsdrops to +14/-0.-X ourshunk damage, repaired to both sides:check-max-lines-ratchet.mjs(the fork's 1000 threshold plus upstream'smts|cts),cli/specs/index.ts,orca-runtime.ts(ledger mixin plus upstream's constructor invalidators),dispatcher.ts,pty-bridge-session-control.ts(the fork'swriteInputAcceptedwith upstream's simpleronWriteUnavailable),AppBackgroundServices.tsx,NativeChatToolRun.tsx(the fork's category dots with upstream's run sentence),use-native-chat-live-session-pending.test.ts,use-visible-workspace-kanban-worktree-ids.ts,use-terminal-pane-mobile-actions.ts(upstream's mouse-tracking guard plus the dock focus guard),ui-slice-hydration-actions.ts,notification-settings-types.ts,project-groups.ts, andloading-store/store.ts.orca-runtime.ts(+8/-3) anddispatcher.ts(+1/-0).agent-session-unproven-release-downgrade.unit.test.tspinsv1.4.211, which this remote does not have. No fork release contains v1.4.211 and nothing newer, so the constant is repointed tov1.4.208-rc.0.zy01(built on v1.4.207) and declared as a passthrough seam.agent-session-record-store.ts, the only module the test imports at that ref, is byte-identical across the two. Its import closure differs only additively (new thread-goal and turn-completion exports). This needs a reviewer's eye.Per-sync checklists
Tier-2 replay: 17
FORK-COPY-OFcopies. Seven had upstream deltas, and each was replayed by hand:use-agent-composer-keydown.ts: Escape after a layer consumed it.native-chat-pagination.ts:NativeChatOlderPageResult.AgentComposerActions.tsxandAgentComposerField.tsx: optional goal-chip and context-ring props and the goal placeholder.use-native-chat-live-session.ts:loadEarliernow returns a joined promise with a result code, plusolderHistoryGeneration(fix(native-chat): load older messages automatically before the reader reaches the top stablyai/orca#22541/fix(native-chat): show every user message on the message rail, not just loaded ones stablyai/orca#22558), kept on the fork's byte-offset paging.WorktreeGroupMenuItems.tsx: upstream's delta, a Copy Worktree Name item, is outside the copied region.fix-checks-agent-launch.ts: already carried thebeforeSurfaceOpen/result.surfaceshape.All 17
FORK-COPY-SHAheaders now point at083f583a53….Tier-4 pending-upstream: two items moved:
run-typecheck-projects-in-parallel.mjsretired. Upstream fixed the same OOM with memory-based admission (fix(build): admit typecheck projects by memory, not core count stablyai/orca#22074), so the file takes upstream's version and the exception and itsdocs/fork-upstreaming.mdsection are removed together.--status-warning,-backgroundand-border(with its own hues), so the fork now carries only--status-warning-foreground. The manifest reason and ledger entry are updated to match.The other pending items that upstream touched still carry their fork delta (checked by content), so they are unresolved.
Feature collision:
agent-composer: possible. Upstream's native-chat composer gained Codex goal mode (feat(native-chat): show a Codex chat's goal above the composer, and set it from goal mode stablyai/orca#22377) and a context-window usage ring (feat(native-chat): show context window usage in the composer stablyai/orca#22301). The fork'sNativeChatComposerhosts the fork agent composer, which does not wire either, though the tier-2 copies now accept the props.session-info: possible. Upstream now shows context usage in the composer, a different surface from the fork's sidebar.none. Upstream's refactor(native-chat): remove the unused terminal handoff stablyai/orca#22783 "remove the unused terminal handoff" is the agent-session owner handoff, not the fork'ssession-handoff.Fix commits
47f8521cc1Adapt the terminal-dock provably-live write to upstream's removal of the agent-session write gate, the hidden-resize clear, and themainWindowargument.1bf4b124d3Drop the OMP test case "rechecks the admitted session fence before the atomic write". refactor(native-chat): remove the unused terminal handoff stablyai/orca#22783 deleted terminal-owned agent sessions and the write gate, so there is no fence left to assert. The only test removed; flagged for review.f84ee51806Re-importisTerminalWaitWhitespaceandreadFileSync, which upstream stopped importing but fork lines still call.e975182c10Heimdall adapter: passcallerSession: undefinedand buildcoordinatorwith upstream'sorchestrationCallerIdentity, per refactor(orchestration): resolve every caller and target to one orchestration party, keyed by the Orca session id stablyai/orca#22555's unified caller.8d0913ca17Renderer callers:setActiveTabType(type, worktreeId), the Antigravity trust preset, and the live-session contract in a fork test.5668b56895Upstream's newfilesystem-download-lifetime.test.tspasses the fork's fifth (diff) cancellation registry. Declared as a seam.e0a3c44cbeThe fork's uncategorized tool name follows upstream'sgroup/tool-linehover-group rename, so its hover highlight still fires.c05eb6c011Drop the retiredroot_directory_guardfrom the e2e gate contract's ungated-job check. Thefork_ownership_guardexemption stays.74b89ed416Lint: route two forkcrypto.randomUUIDcalls throughcreateBrowserUuid, mirroring upstream's adoption in 7d2c399 for the newno-restricted-propertiesrule.Local verification gate
pnpm install --frozen-lockfile: pass--verify-seams: 1,050 lines across 496 files.--verify-residuals: all 451 seam files match..github/scripts/check-fork-ownership.mjs origin/main HEAD: pass.config/tsconfig.mobile-web.json, no longer exists; upstream retired that lane in feat(mobile): one build-time switch picks native or OTA, default native (OTA phase E1) stablyai/orca#22193.pnpm lint: passTests are left to this PR's own CI; vitest does not run on the sync host.
PR CI
The first run failed 15 test files across six shards. Every failure reproduced on the sandbox. All but one are fixed, each in its own commit:
e1a562cdf4: upstream's new max-lines.mts/.ctscases now use the fork's budgets.39c0a45105: the fork's project-group store test uses upstream's SQLite test harness. A writableStorenow requires a profile-state authority.c0cf3b60b9: the diff-cancellation tests useEventEmittersenders. Upstream's sender-scoped cancellation subscribes to sender events.2fafdb9864: the dock focus seam no longer throws on a leaf without a UUID. Upstream's new middle-click repro ([Bug]: Middle-click pastes the PRIMARY selection twice in mouse-tracking TUIs (Linux) — #8993's suppression is never armed when mouseTrackingMode !== 'none' stablyai/orca#21762) uses a plain-named leaf.936445c985: the fork's changelog draft-release cases passtargetCommitish, which is now required.0634795e84: dropped merged assertions ontrack-community-prs.yamlandpackage_windows. The fork removes both.0e40e4d515: the coloring test now expects the tool result folded into its call row. Upstream no longer renders a separate Result row.55fc06b6f3: the fork's hosted-review fix-checks launch is profiled in upstream's new agent-launch caller census. The census now counts 13 callers.df71798fa7: the Ledger-tab test reads persisted state throughreadPersistedStateJson, since state now lives in SQLite.971ab030b8: the Heimdall adapter test mocksorchestrationCallerIdentity.50390b8789: upstream's new readiness-reuse tests (pr-ready-check-gateandpr-ready-check-reuse) are adapted to the fork's PR job graph.234a108e82: the new feed-lifetime test uses the fork's release feed URL.9f56335436: the live-session test callsloadEarlierwithvoid, as upstream's own test does.await-ing the new promise insideact()hung the test, and the hang cascaded into 23 failures.1d60092a51: the test sandbox image now shipssrc/shared/zip-extractor-command.ts. Without it every sandbox image build failed at v1.4.215.The second run was green except for one failure, which needs a human decision.
Needs attention:
serialize-grid-transcript-replay.test.ts×claude-code-ready-cold-startv1.4.215 adds
src/main/daemon/serialize-grid-transcript-replay.test.ts(stablyai#22586). It replays every captured transcript insrc/main/runtime/__fixtures__through the serializer round trip. That directory also holds the fork's Claude cold-start readiness fixture (an exception-owned path). The fixture produces 8new-fail(I2) checks against an expected 0. The test pins a per-transcript table of known upstream limitations:KNOWN_PREEXISTING_I2_FAILURES = { less: 6, nano: 2, opencode: 5 }.This is deterministic. It failed in shard 5/8 on both CI runs and on the sandbox. Every other touched test passes (685/686).
It was left for a human because each available fix changes what the gate checks:
'claude-code-ready-cold-start': 8in upstream's known-failure table. The table's own contract requires verifying against the previous build's addon first, and this adds a fork entry to an upstream exact pin.Third CI round
The second run was green except two checks. Both are fixed:
f607ab15fe: transcript replay. Upstream's newserialize-grid-transcript-replay.test.tsreplays the fork'sclaude-code-ready-cold-startfixture, and 8 of that fixture's checkpoints fail I2. I checked them the way the table's own comment asks: I built the previous serializer fromorigin/mainwithbuild-serialize-addon-at-ref.mjsand re-ran withORCA_OLD_SERIALIZE_ADDON. The result was 88 checks: 8both-fail, 29fixed, 0 regressions, 0 I1 byte diffs. The previous build fails all 8 the same way, so they are pre-existing serializer limits. I added the fixture toKNOWN_PREEXISTING_I2_FAILURESas a one-line registration seam and left upstream's table line alone.67fb6a58a9: real IME / Linux X11. Playwright 1.63, which came in with this release, reads the--forwarded bypnpm run test:e2e -- <spec>as the end of file selection. The X11 step therefore ran all 841 e2e tests instead of 4 and hit the 25-minute timeout;main's scheduled run on 1.59 ran 4. Upstream fixed this onmainin fix(ci): preserve focused Playwright file selection stablyai/orca#23270 (dffb3498e2), but neither v1.4.215 nor v1.4.216 includes it. I cherry-picked that commit whole. Its five files are declared as pending-upstream exceptions (seedocs/fork-upstreaming.md#focused-playwright-file-selection), to be dropped once a stable tag carries the fix.Both passed on the sandbox, including upstream's new focused-selection test against the installed Playwright. The manifest checks and the ownership guard also pass.