Commit Graph
7672 Commits
Author SHA1 Message Date
Merge Sim 3d9adf7bbd test(native-chat): drop orphaned Claude handoff-auth test left by the main merge
'pins Claude handoff auth through the terminal provider boundary' is absent from main and its
production counterpart preserveClaudeAuthEnv no longer exists outside this test - orphaned residue
of the terminal/native handoff work this PR excludes by scope.

Removed rather than repaired: the failure was a renamed field (providerHome -> providerRoot), and
renaming it would have carried out-of-scope handoff code into the merge. Body preserved as evidence
and logged in CLAUDE-STRUCTURED-DISPOSITION-TABLE.md.
2026-08-30 12:51:44 -07:00
Merge Sim c0ab558bd0 Merge origin/main (c3aceacc7b) into brennanb2025/claude-structured-mobile
Reconciles this lane onto the landed Codex structured native-chat substrate (#16729).

This branch was a divergent SIBLING of that substrate, not a descendant: merge-base 2b1e69d4b0
(2026-08-11), 1125 commits behind, carrying its own pre-split copy of the journal, session record,
lease, host RPC, reducer, outbox, tab model, restart reconciliation, capability gating, and
process-ownership mechanisms.

Resolution (216 conflicts: 151 both-added, 61 both-modified, 4 delete-vs-modify):
- Shared substrate resolves to main wholesale. Main's copy is authoritative; this branch's is a
  stale sibling. 91 take-main paths verified byte-identical to the pin.
- Main's deletions are honoured (4 paths). Branch-only content was extracted to evidence first;
  src/shared/types.ts needs zero re-expression (main's tab-types.ts already carries all of it).
- The lane's parallel handoff state machine and generic substrate copies are dropped (42 paths);
  main owns those mechanisms and terminal/native switching is out of scope for this PR.
- The remaining 79 resolve to main's architecture per the hand-resolution analysis; the named
  branch provider deltas are re-expressed at main's current seams in the implementation phase
  rather than merged as parallel code.

This merge establishes the reconciled base. It does NOT yet deliver working Claude structured
chat: the surviving Claude adapter is written against the old substrate and the Claude provider
behaviour is re-added on top from the preferred source in the implementation phase.

79 branch-only test names are absent from main's entire tree and are tracked for explicit
disposition in CLAUDE-STRUCTURED-DISPOSITION-TABLE.md; none are dropped silently.
2026-08-30 12:44:58 -07:00
Brennan BensonandMerge Sim c3aceacc7b Fix PR unlink for auto-detected reviews (#16898)
* fix: make PR unlink hide auto-detected reviews

* Type the empty-content test double against the real model

The literal narrowed suppressedGitHubPR to number and typed the callback
as Mock, so neither direction was comparable and tsconfig.tc.web.json
failed on TS2352. Keeping the 'as' cast preserves checking of the fields
the double does supply.

* Add localization keys for the unlinked checks-panel state

The unlinked title, relink action, and the remote-runtime upgrade notice
introduced untranslated keys that static analysis requires in en.json.

* Advertise PR suppression capability in the transport test

The client capability list is pinned by websocket-transport.test.ts, and
adding WORKTREE_GITHUB_PR_SUPPRESSION left the expected list stale.

* Fix stale PR suppression in Checks

* fix: harden PR unlink suppression state

* refactor: extract PR unlink state handling

* fix: show PR relink recovery in source control

* fix: add unlinked PR localization

* Clarify workspace-scoped PR unlinking

---------

Co-authored-by: Merge Sim <sim@local>
2026-08-30 12:24:51 -07:00
Brennan BensonandMerge Sim c539b38856 Fix select all in native chat composer (#17294)
Co-authored-by: Merge Sim <sim@local>
2026-08-30 12:20:51 -07:00
Neil 3ab9766e38 perf(worktree): prepare checkouts while the composer is open
Squashed merge of PR #17290.
2026-08-30 12:12:04 -07:00
Neil a8183884bd perf(wsl): place worktrees inside the distro when the project runs in WSL
Fix-forward for readiness review: align retirement placement with WSL mirrors and preserve Windows-side git-common watchers.
2026-08-30 12:11:21 -07:00
Neil b81e578cff fix(updater): accept GitHub release asset redirects on Windows
Accept manual GitHub release-asset redirects on Windows, preserve non-Windows probing, and cover redirect/error/timeout paths.
2026-08-30 12:10:11 -07:00
Neil 16e6b103d6 fix(session): deduplicate editor records during restore (#17370)
Deduplicate persisted editor records and repair tab-group references during session hydration. Closes #17185.
2026-08-30 12:07:41 -07:00
Neil ac02232015 perf: overlap independent worktree create preflight (#17386)
Readiness checklist passed; required CI and review checks are green.
2026-08-30 11:50:29 -07:00
Neil fd52e942bd fix(tasks): keep the remembered GitHub scroll offset instead of clobbering it (STA-5949) (#17433) 2026-08-30 11:49:12 -07:00
Neil 5e19c35dc5 fix(automations): stop vetoing Escape for overlays the page does not own (STA-5207) (#17431) 2026-08-30 11:49:00 -07:00
Neil 6677ae4e5e test: correct 8 stale specs surfaced by the test-detected-bugs sweep (#17434) 2026-08-30 11:45:21 -07:00
Neil d607a63670 fix(native-chat): replay a draft clear dropped mid-composition (#17392)
A structured send clears the draft asynchronously, on RPC acceptance. If the
user opens the next IME composition first, the clear lands while the browser
owns the field, the DOM sync skips it, and settlement adopts element.value —
which still holds the message that was already sent.

The field now records a clear dropped mid-composition and applies it at
settlement, keeping only what the IME composed on top of the value the field
held when the composition started. Browser ownership is unchanged for every
other programmatic draft; the clear stays on the acceptance path, so a
rejected send still keeps its draft.

Also advances a frame before the attachment-flush focus assertions, which
were vacuous because the focus they forbid is scheduled in rAF.

Fixes #17359
2026-08-30 03:55:37 -07:00
Neil 93a080112f fix(linear): stop the filter coverage warning firing on complete selections (#17376)
* fix(linear): stop the filter coverage warning firing on complete selections

#17342 inferred transport-cap truncation from the bounded filter after the
fact, with `atLimit = selectedIds.length >= max`. A row the cap could not fit
leaves no trace in the surviving ids, so at exactly the cap a complete
selection and a trimmed one are indistinguishable from the value alone, and
the inference biased toward always warning. A workspace with 20 teams x 5
status names expands to exactly 100 ids: picking all 5 rows is provably
untruncated, yet the menu read "100 selected · partial".

Record the trim where it happens instead. `applyPickedFilter` holds both the
pre-cap expansion and the bounded result, so it stores the surviving ids as a
truncation record; the notice, section-menu summary, and pill consume that
flag. Keying the record on the ids it describes is what keeps it fresh — the
moment the facet carries anything else (row toggle, pill clear, Clear all, the
prune effect, a workspace switch) the record no longer matches and the warning
goes away, which matters because the prune effect only ever removes ids.
`intended > applied` stays as the fallback for restored filters that carry no
record, and `boundLinearIssueAttributeFilter` is still the last word on the
cap.

Also moves the section-menu partial marker out of the `max-w-[120px] truncate`
summary span, where "100 selected · partial" could clip.

Refs STA-5996

* test(linear): cover the untouched-facet truncation guard

The guard that keeps a recorded trim alive across an unrelated facet click was
untested — the first attempt sat where intended > applied, so the value-derived
shortfall answered for it and removing the guard left every test green. Move the
scenario onto the cap, where only the record can speak.

Also stop an empty record matching an empty facet: a filter carrying nothing is
never truncated.

* test(linear): pin set equality, not subset, on a truncation record

A facet that grew past its record has refetched underneath it; matching by
subset would keep warning about a trim that no longer describes the filter.
Found by mutation: the subset mutant survived the whole suite.

* test(linear): fuzz that the coverage pill and the section notice agree

They are the same claim rendered twice; a pill reading partial over a silent
section is a lie either way round. 20k random topologies, zero divergence.
2026-08-30 03:30:41 -07:00
Neil 70df6f0224 fix(terminal): mask the agent composer's dim placeholder during a preedit (#17377)
Split out of #17170, which now carries only the xterm composition-overlay work.

Codex and Claude draw an all-dim, full-row ghost placeholder. The opaque preedit
overlay reproduces the committed row tail it covers, so without this the ghost is
repeated to the right of the composing syllable instead of staying masked. The
binding keys off the `.xterm-composition-remainder` class that #17170 adds and
hides it through CSS while a composition owns a structurally verified placeholder
row — bold prompt glyph plus a dimmed model footer below a blank gap for Codex, a
frame line above the prompt for Claude. Arbitrary dim output, shell lookalikes,
and any row carrying typed text keep their tail visible.

readTerminalCursorLineContext moves from src/main/daemon to src/shared because the
renderer now needs the same reader the daemon uses; the move is import-only.

Depends on #17170.
2026-08-30 03:12:22 -07:00
Neil 7f822a73e3 fix(terminal): render the IME caret and give the candidate anchor one owner (#17170)
* fix(terminal): render IME caret without placeholder overlap

* fix(terminal): preserve dim mid-line composition tails

* fix(terminal): keep IME caret visible at row edge

* fix(terminal): harden IME overlay lifecycle and layout

* test(terminal): type final-cell layout mock

* fix(terminal): keep final-cell IME anchor on-screen

* fix(terminal): bind IME masking to composer ownership

* fix(terminal): bound IME placeholder session ownership

* fix(terminal): track latest IME placeholder session

* test(terminal): share IME session event fixture

* fix(terminal): keep both writers of the IME candidate anchor in agreement

`textarea.style.left` has two writers: xterm's patched CompositionHelper and
Orca's terminal-ime-candidate-anchor.ts. The anchor module listens on
terminal.element, so within a composition event it writes after xterm's textarea
listener and reverted the final-column clamp the patch had just applied.

Moving the clamp into the anchor module and dropping the patch hunk does not fix
it, and the rendered e2e caught that: CoreBrowserTerminal.ts:444 drives
updateCompositionElements from onRender as well, so xterm re-asserts the textarea
position on every repaint, with no composition event for that module to hear. The
anchor survived only when no render happened to follow — measured as a flake at the
final column, 1561.28px against a 1557px screen edge, the fully unclamped value.

So both writers now compute the same clamp. The patch keeps it, because it is the
writer on the render path and already holds cursorLeft, maxWidth and the preedit
bounds. The anchor module applies the same one, so its composition-event write no
longer reverts the correction in the window before the next render. Both halves are
individually necessary and both are mutation-tested.

Also restores _getRowRemainderText's expression from main: translateToString(true,
x, line.length) and translateToString(false, x, getTrimmedLength()) are the same
call, since upstream does endCol = min(endCol, getTrimmedLength()) under trimRight.

Adds the two missing tests — one installing both anchor writers in a single rig, one
driving a render under an open composition — plus disposal cleanup and clamp-bound
coverage, and moves the Codex/Claude placeholder mask to a follow-up PR.
2026-08-30 02:23:04 -07:00
Neil fa230cee42 refactor(preflight): rename execLocalPreflightCommand to ...OrThrow (#17380)
Sibling of execCommandInWslOrThrow (#17375) with the identical throwing
contract, sitting in the same `try { ... } catch { return false }`
blocks. After that rename the pair read inconsistently — one announced
that it throws, the other did not, while both collapse to a silent
false at the call site.

Also states the contract in a doc comment: it rejects rather than
reporting "absent", so a caller that swallows the rejection makes "not
installed" and "could not run it" the same answer.
2026-08-30 02:09:05 -07:00
Jinwoo Hong d64097d109 Open linked reviews in Orca browser (#17360)
* feat(sidebar): open linked reviews in Orca browser

* test(e2e): match paired window reveal assertion

* feat(sidebar): focus linked browser tabs

* ci: retry checks after cancelled rerun
2026-08-30 04:58:56 -04:00
Neil 1215cc98f2 refactor(preflight): rename execCommandInWsl to execCommandInWslOrThrow (#17375)
Pure rename, no behavior change. Callers like isCommandAvailable and
isCommandOnPath wrap this in try { ... return true } catch { return false },
collapsing "distro unreachable" and "command absent" into the same value —
a recurring bug class in this subsystem (see
docs/reference/wsl-probe-failure-semantics.md). The OrThrow suffix makes
that swallow visible at the call site instead of implicit in the function
name, so a reviewer notices when a new caller does the same collapse.
2026-08-30 01:53:47 -07:00
Brennan BensonandJinwoo-H d9870c6c75 fix(browser): apply the app-wide HTTP proxy to embedded browser sessions (#15536)
* fix(browser): apply the app-wide HTTP proxy to embedded browser sessions

The proxy setting was only ever written to `session.defaultSession`, but browser
guests run on their own `persist:orca-*` partitions. Any host reachable only via
the configured proxy failed to load in an embedded tab, landing on
`chrome-error://chromewebdata/`, while the same setting worked everywhere else.

Adds a per-session applier alongside the existing defaultSession path, keyed by a
WeakMap so one session's applied config can't suppress another's, and applies it
to every browser partition through the single installer they all pass through.
Startup awaits an explicit sweep so the first guest navigation can't race the
installer's fire-and-forget write, and a settings change re-sweeps so toggling
the proxy takes effect without a restart.

Env-var fallback and the system-proxy probe mirror the defaultSession behaviour,
so a browser partition resolves the proxy the same way the rest of the app does.

Fixes STA-4779

* fix(browser): await per-session proxy readiness

* fix(proxy): preserve loopback and authenticate

* fix(proxy): settle browser partition update races

* fix(proxy): close partition policy races

* fix(proxy): order settings and release removed sessions

* test(browser): await partition proxy readiness

* fix(proxy): cancel removed partition retries

* refactor(proxy): keep OpenCode rate limits out of scope

* fix(proxy): preserve sessionless host policy

* fix(proxy): gate requests on policy readiness

* fix(proxy): retire deleted browser sessions

* fix(proxy): close retired browser guests

* fix(proxy): retain retired session guards

* fix(proxy): retain retired partition policies

* fix(browser): retry transient proxy application failures

* fix(browser): release deleted partition installer state

* fix(proxy): retry delayed transient failures

* fix(proxy): preserve route session authority after rebase

* fix(proxy): clear retired session credentials

* fix(proxy): retire failed browser profiles

* fix(proxy): harden failed session cleanup

---------

Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
2026-08-30 04:53:20 -04:00
e741ff1318 fix(wsl): scan agent sessions only in running distros (#17072)
* fix(wsl): scan sessions only in running distros

* test(ai-vault): pin WSL discovery platform

* fix(wsl): suspend transcript watchers for stopped distros

* test(wsl): pin transcript scan gate platform

* fix(wsl): settle stopped transcript loading

* fix(wsl): add last-known-good fallback and backoff to running-distro discovery

listRunningWslDistrosAsync failed closed on any probe error (timeout, ENOENT,
wsl.exe hiccup), indistinguishable from "no distros running". A 2s poll
(wsl-transcript-running-observer.ts) calls it indefinitely while any WSL
transcript tab is open, so a persistently broken wsl.exe silently made every
WSL session vanish app-wide with no way to tell "discovery broken" from
"distro stopped", and re-spawned wsl.exe every 2s forever.

Extract a dedicated cache/backoff module (wsl-running-distro-cache.ts,
mirroring the sibling machinery already in wsl.ts for the full distro list)
so a probe failure falls back to the last-known-good running-distro list and
backs off further probes, while a genuine empty result (no distros running)
stays authoritative. Add a consumer-level test simulating a sustained wsl.exe
outage across a live transcript-watcher polling session, asserting the
observer keeps reporting "running" and that real wsl.exe spawns stay bounded.

* fix(build): list the new WSL cache module in the web typecheck project

config/tsconfig.tc.web.json enumerates its files explicitly, so a new
module imported by wsl.ts fails the full typecheck with TS6307 until it
is listed. pnpm tc:node passes without it, which is how this got missed.

  src/main/wsl.ts(13,8): error TS6307: File 'src/main/wsl-running-distro-cache.ts'
  is not listed within the file list of project 'config/tsconfig.tc.web.json'.

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-08-30 01:49:05 -07:00
Neil 58a52a8ce3 test(wsl): guard probes that report failure as a negative answer (#17352)
* test(wsl): guard probes that report failure as a negative answer

A WSL probe that cannot reach its distro returns the same value as one
that asked and got "no". Downstream nothing can tell them apart, so a
distro that was busy for a second reports no git, or no agent sessions,
until relaunch — sticky, silent, and identical to the real thing.

That has shipped three times: preflight CLI probes, the glab auth
fallback (#8941), and listRunningWslDistrosAsync failing closed with no
last-known-good while polled every 2s (PR #17072).

Scan the WSL and preflight probe modules for the shape and hold the
current set in an allowlist that only shrinks. Scoped deliberately: the
same shape appears ~850 times across src/ and is usually correct,
because for most callers a failure really does mean absent. It is only
dangerous where the answer describes a distro.

The guard cannot see the dangerous part — whether the value is later
cached or gates discovery is dataflow, not syntax. It stops a new
swallow site appearing here without someone saying why it is safe to
pin, which is the review that was missing all three times.

* test(wsl): make ratchet failures actionable

A red build must say what to do. Name the offending files, say the
allowlist is where a safe case goes, and — for a stale entry — say the
change is fine and the list just needs to shrink.

* docs(wsl): track the probe failure-semantics reference

docs/** is gitignored with an explicit allowlist, so the reference the
ratchet points contributors to was silently left out of the branch. A
guard whose error message cites a doc that is not in the repo is worse
than no doc.

* test(wsl): catch a swallow whose reason trails the return

The guard only tolerated comments before `return`, so
`return false // ...` slipped past — including the exact snippet the
doc and the test's own docstring use as the canonical example. The doc
asks authors to write down why a swallow is safe, and the natural place
for that sentence is trailing the return, so following the guidance
defeated the guard.

Verified against both shapes: trailing comment and comment on the line
after.
2026-08-30 01:30:25 -07:00
Neil d9cb020178 fix(diagnostics): count case-only worktree nesting on macOS (#17364) 2026-08-30 01:23:43 -07:00
Neil 3af2c665c0 fix(cli): name PowerShell when it strips quotes from JSON flags (#17351)
* fix(cli): name PowerShell when it strips quotes from JSON flags

Windows PowerShell 5.1 does not escape inner quotes when building a native
command line, so `--options '["a","b"]'` reaches orca.exe as `--options [a,b]`.
The value is correct when printed and damaged by the time argv is parsed, so the
resulting "invalid JSON" error blamed the user's input rather than the shell.

#16743 recovered this for `--deps`, which is safe only because generated task IDs
have a fixed 12-hex grammar. The same mangling hits `--options`, `--payload` and
`--result`, and those are NOT safely recoverable: `["1","2"]` and `[1,2]` arrive
at argv identically, so a general repair would silently turn strings into numbers.

Detect instead. `getOptionalJsonFlag` rejects the damaged shape up front with an
error that names the shell and shows the workaround. It fires only when the value
is bracketed, quote-free, fails JSON.parse, AND consists entirely of bare tokens
that quoting would rescue, so valid JSON is untouched.

Also share the generated-id contract: `task-deps-flag` hardcoded
/^task_[0-9a-f]{12}$/i, which silently diverges if `generateId`'s byte count
changes. It now calls `isGeneratedId`, with a test pinning the two together.

Verified on a Windows host. Measured argv, which the new test pins as a fixture:
  PS_VALUE=["task_b2a580db74d8","task_c3b691ec85e9"]
  ARGV=["--deps","[task_b2a580db74d8,task_c3b691ec85e9]"]

Before: Invalid --options: must be a JSON array of strings
After:  --options arrived as [a,b], which is not valid JSON.
        Windows PowerShell 5.1 strips the inner quotes ...

* fix(cli): scope JSON-flag detection to genuinely JSON flags

Review found the detector wired to two flags that are not JSON:

- `orchestration ask --options` is documented `<csv>` and the runtime splits it
  on commas, so `--options [a,b]` was a legitimate value being rejected.
- `task-update --result` is stored verbatim and reused as dispatch failure text;
  existing tests pass free text, so a bracketed `[ok]` was being rejected.

Both revert to `getOptionalStringFlag`. Only `gate-create --options`
(`<json_array>`) and `send --payload` (`<json>`) are JSON-parsed and keep it.

Three further review fixes:

- Objects now require a `key:value` pair per entry. `{a,b}` and `{a:b,c}` were
  reported as quote-stripped although quoting them cannot produce valid JSON.
- The raw value is no longer echoed. A `--payload` can carry secrets and this
  message reaches `--json` output; the flag name and guidance are enough.
- The message hedges the shell attribution. Detection inspects only the value's
  shape, so it also fires when a macOS/Linux user forgets to quote, where
  PowerShell is not involved.

Verified against a Windows host, all six cases: both JSON flags fire on the
mangled shape and pass valid JSON through to the runtime; both non-JSON flags
now reach the runtime again; and the secret in `{token:hunter2}` appears zero
times in the error output.
2026-08-30 01:23:27 -07:00
Neil 4bc2085271 Revert "perf(rpc): compile Zod request schemas lazily" (#17368) 2026-08-30 01:21:55 -07:00
Neil 316cb1ca4b fix(linear): surface truncation when a deduplicated status exceeds the state-id cap (#17342)
* fix(linear): flag partially applied status and label filters (STA-5983)

Since #16879 one status/label row expands to an id per team, and the renderer
bounds that list to the 100-id transport cap. Any surviving id kept the row
fully checked, so the picker claimed coverage the filter never had; show how
many of the row's per-team ids are actually applied.

* fix(linear): keep every picked status row inside the transport cap

The cap sliced a lexicographically sorted id list, so a whole picked row could lose
every id — reverting to unchecked with no notice, and dropping out of the coverage
denominator that was supposed to explain it. Spread the cap across the picked rows,
and carry the partial-coverage signal to the section menu and the pill, which are
what the user reads once the detail panel is closed.

* fix(linear): stop the status filter claiming coverage it cannot apply

More picked rows than the transport id cap cannot all be represented, and
MultiSelectList.toggle appends the clicked key last — so the starved row was
always the row the user had just clicked: it stayed unchecked, no notice fired,
and coverage still reported a full 100 of 100. Coverage now takes the cap and
reports a spent id budget as its own shortfall, so the picker says how much it
is really carrying instead of claiming teams it never covered.

Capping also bucketed by the click order the picker hands it, so the same
visible selection could resolve to different ids between renders; it now buckets
in metadata order, with ids from unloaded teams sorted after. boundLinear-
IssueAttributeFilter stays the last word on the transport bound.

The pill's `partial` marker moves from a bare title attribute to the Tooltip
primitive, which keyboard and screen-reader users can actually reach.

Pill labels and facet clearing move to their own module so sections stays under
the max-lines cap.

* test(linear): assert coverage at the cap it actually caps to

The exactly-on-the-cap test capped at max=4 but asserted non-partial at
max=5, so it never covered its own subject. Pin both: at the cap coverage
warns (a starved row leaves no trace in the ids), below it stays quiet.
2026-08-30 01:19:34 -07:00
Neil 8b5aaae5dc Consolidate the renderer now-clock and drop the epoch setState round-trip (#17358)
* Consolidate the renderer now-clock and drop the epoch setState round-trip

Follow-up cleanup to #17337.

`src/renderer/src/hooks/use-now.ts` duplicated the shared clock that already
lived at `components/dashboard/useNow.ts`, with a per-instance `setInterval`
and no visibility gating — the exact pattern that file's own comment warns
against. Keep the shared `useSyncExternalStore` implementation, move it to the
`hooks/` home the duplicate had taken, and give it the `enabled` flag that was
the duplicate's only real addition. A disabled caller no longer holds the
shared interval open or re-renders on its ticks.

Gate the two 1 Hz callers on the state that can actually consume them: the
checks-panel empty content only reads the clock for a GitHub auto-retry or
retry-disabled window, and the diff notes menu only for an open request already
addressed to its worktree. Both previously ticked for their whole lifetime —
the empty content re-rendered the create composer once a second while the user
typed in it.

Replace the `setState`-in-`useEffect` epoch clocks with a sample keyed on
`agentStatusEpoch`. The effect ran a render late, so the freshness-scheduler
bump — whose whole purpose is to expire an entry on the stale boundary — first
painted a frame that still read the pre-expiry timestamp, then corrected it.
Sampling during render keeps the value deterministic per epoch and every
consumer of one epoch agreeing on the boundary.

Also: `isPanelVisible` never gates the checks-panel clock (ChecksPanel is
unmounted, not hidden), `setPrRefreshStateNow` was returned but never read,
`panelContextKey` was an unused dep on the expiry effect already keyed by
`prCacheKey`, `useResetCountdownClock` kept a dead alias, and `ProviderPanel`
derived its window sections twice per render.

* Fix the open-request TTL and the epoch clock's captured Date.now

Review findings on the previous commit.

The diff notes menu's 5s TTL stopped working. The shared clock's snapshot is
frozen while nobody at that cadence is subscribed, and `useSyncExternalStore`
subscribes in a passive effect — which flushes child-first, so NotesSendMenu's
open effect ran before the clock could catch up. With both 1 Hz callers now
narrowly gated, nothing holds that cadence, so an open request that was never
consumed could reopen the menu arbitrarily later.

The TTL is a deadline, not a drifting label, so enforce it on the commit that
acts on the request: DiffNotesSendMenu passes `openRequestExpiresAt` and
NotesSendMenu checks it against `Date.now()` in the effect that opens the menu.
Exact, and it removes the 1 Hz clock from that path entirely.

`createAgentStatusEpochClock`'s `readNow = Date.now` default bound the native
function when the module-load singleton was created, so a suite's fake timers
never applied to it. Call through instead. Also add a reset seam: store resets
rewind `agentStatusEpoch` to 0, and without rewinding the sample the next render
at epoch 0 reuses the previous test's timestamp.

Both regressions have tests that fail without the fix. Also corrects the
disabled-caller contract on `useNow` — the snapshot is frozen, not merely
bounded by an enabled caller — and notes on the three memos that they stay keyed
on the epoch because two bumps in one millisecond share a sample.
2026-08-30 01:17:28 -07:00
Neil 1bdb878ac2 perf(preflight): cache WSL CLI probes per distro (#17350)
* perf(preflight): cache WSL CLI probes per distro

A preflight check against a WSL target skipped the cache entirely
(`cacheable = !wslTarget`), so every caller re-spawned up to five
`wsl.exe` probes — git/gh/glab detection plus gh/glab auth, two of them
through login shells — and woke an idle distro each time. Repeated
Landing mounts, pane switches and per-worktree restore each paid the
full set.

Cache per distro so one distro's toolchain never answers for another,
and join concurrent callers onto one probe set instead of letting each
run its own.

The entry expires rather than living for the session like the local
cache does: `isCommandAvailable` collapses every failure into
`installed: false`, so an unreachable distro is indistinguishable from
one with no tooling. Pinning that would report "git not installed"
until relaunch, where the uncached code self-healed. Expiring keeps the
burst collapsed and still lets a transient failure recover.

Propagating unreachable-vs-absent out of the probe layer would allow a
longer-lived entry, but that reaches well past WSL and belongs in its
own change.

* fix(preflight): stop a superseded probe caching its stale result

Review caught two races the first version had.

A forced refresh runs alongside a slower probe already in flight. Both
wrote the cache unconditionally on settle, so the older one landing last
replaced the newer answer — a Re-check could silently return the status
it was asked to replace, for the whole TTL.

The same write also repopulated a cache that `_resetPreflightCache` had
just cleared, which jira/linear call on credential changes: the probe
already out would settle afterwards and restore what was invalidated.

Tag each run and only let it write while it is still the newest for its
key, with an epoch doing the same across a reset. A superseded run still
returns its own answer to its own caller; it just stops becoming the
cached one.

Both tests fail without the guard.
2026-08-30 01:12:22 -07:00
Neil 7b86833120 perf(rpc): compile Zod request schemas lazily (#17353)
* perf(rpc): compile Zod request schemas lazily

* test: align window reveal assertion
2026-08-30 01:03:54 -07:00
Neil 85de42cdce fix(native-chat): preserve IME composition across async updates (#17169)
* fix(native-chat): preserve IME composition during streaming

* fix(native-chat): remount composer when draft owner changes

* fix(native-chat): reset composition across draft owners

* fix(native-chat): key pane-owned composer state

* fix(native-chat): preserve resolved attachments through IME

* fix(native-chat): bound deferred attachment paths

* fix(native-chat): add pending attachment overflow translation

* test(native-chat): publish attachment probe after render

* fix(native-chat): repair localization file newline

* fix(native-chat): use effect for attachment probe publication
2026-08-30 00:39:30 -07:00
Neil 8ffd067c3d fix(diagnostics): measure reconciled IME typing latency (#17171)
* fix(diagnostics): measure terminal IME input

* fix(diagnostics): settle prevented IME inputs

* fix(typing-diagnostic): preserve sample attribution invariants

* fix(typing-diagnostic): bound echo settlement state
2026-08-30 00:31:27 -07:00
Jinwoo Hong 252dbd60ea fix(terminal): restore lossy initial remote snapshots (#17113)
* fix(terminal): restore lossy initial remote snapshots

* test(terminal): strengthen lossy snapshot causal oracle
2026-08-30 03:11:55 -04:00
Jinwoo Hong ae0f3675a1 fix(remote): focus host-delegated split panes (#16886)
* fix(remote): focus host-delegated split panes

Return the authoritative leaf identity from terminal.split, record viewer-local focus intent behind the captured pairing revision, and replay the mirrored layout before focusing the exact pane. Preserve old-host fallback and prevent delayed split responses from stealing focus after the viewer moves away.

Add deterministic runtime, renderer, concurrency, compatibility, and headed paired-Electron coverage for Cmd+D, header splits, and immediate PTY input routing.

Fixes #16510

* fix(remote): preserve split focus across tab groups

Resolve the initiating source tab and leaf from the remote PTY, while keeping the viewer's current focus as a separate anti-steal baseline. This lets context-menu/header splits from non-focused group tabs focus their result without allowing delayed responses to override a later navigation.

* test(remote): drive split focus with key events

* test(remote): use the platform split shortcut

* fix(remote): fence concurrent split focus intent

* fix(remote): harden split focus ordering

* fix(remote): preserve split focus after runtime refactor

* fix(remote): fence stale split focus gestures

* test(remote): keep split focus regression within line budget
2026-08-30 03:07:20 -04:00
Neil 5ea9daba97 fix(window): keep automated Electron launches out of the foreground (#17347) 2026-08-29 23:55:00 -07:00
Neil 2daea491b9 Enable React static component and purity lints (#17337)
* Enable React purity and static component lints

* fix lint follow-up clock activation and eligibility expiry

* test render status bar provider panel with hooks

* fix checks clock activation before paint

* fix React type import in git history files
2026-08-29 23:46:35 -07:00
Jinwoo Hong 59278570db STA-5496 offer Orca Browser for SSH terminal links (#16503)
Reuse direct SSH browser-route eligibility and route owner-pinned terminal links through the workspace createBrowserTab path. Keep the popover and modifier hints aligned with the eligible destination.

Preserve printed 0.0.0.0 and localhost URLs; the existing SSH SOCKS boundary normalizes wildcard listeners to remote loopback immediately before connect.
2026-08-30 02:37:40 -04:00
Chen 07df4bf0be fix(orchestration): recover stripped task deps 2026-08-29 23:07:36 -07:00
Jinwoo Hong f572ba34bc feat(browser): address-bar convergence — previews and browser tabs convert in place (STA-5681) (#16998) 2026-08-29 22:38:59 -07:00
Neil 3031c2330c refactor(native-chat): isolate resolved bridge view
Keep NativeChatView focused on mode routing and session-gate ownership by moving the resolved bridge UI into its own component. This behavior-preserving split clears the pre-existing max-lines baseline without disabling the lint rule.
2026-08-29 22:05:07 -07:00
Neil 47827b7539 fix(add-project): use the entered group name when opening a folder (#16881) 2026-08-29 21:48:35 -07:00
Neil 558f4e0b8d fix(pi): stop the titlebar spinner on settlement and idle compaction (#16880) 2026-08-29 21:19:56 -07:00
Neil d138a44b88 fix(linear): collapse duplicate per-team status rows in the Tasks filter (#16879) 2026-08-29 21:19:01 -07:00
Neil 19fcaf7204 fix(sidebar): keep project header labels from switching to the repo name (#16877) 2026-08-29 21:18:30 -07:00
Neil 668c1bc0c0 fix(native-chat): keep a tab launch draft out of split sibling composers (#16883) 2026-08-29 21:16:35 -07:00
Neil 7e7f241ec6 chore: enable React correctness lints (#17285)
* chore: enable React correctness lints

* chore: enable additional safe lint rules
2026-08-29 21:02:11 -07:00
Neil 4bb9dd5b89 chore(deps): bump electron 43.4.1 and other meaningful runtime deps (#17330)
Take the high-value desktop and mobile upgrades that fix crashes, jank,
or security holes. Leave Electron 44, Lucide 1, Reanimated 4.6, Expo
56/57, and xterm betas for later.

Desktop: electron 43.4.1, @tanstack/react-virtual 3.14.10, mermaid
11.17.2, ws 8.21.3, react 19.2.8, pdfjs-dist 6.3.289, vitest 4.1.11,
happy-dom 20.11.8.

Mobile: Expo SDK 55 patch train, react-native 0.83.10 (IME patch
ported), reanimated 4.3.4, webview 13.16.2 (thread-safe decision
manager; restore WebView generic default so TS 6 does not collapse
props to never).

Electron 43.4 dropped marginType from PrintToPDFMargins; CDP print
mapping now supplies the four sides only.
2026-08-29 20:44:43 -07:00
Neil 14d39bd0f0 Split accounts pane responsibilities (#17286)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Split ports panel layers

* Split runtime environments pane layers

* Split local PTY provider responsibilities

* Split CDP bridge responsibilities

* Split relay Git handler responsibilities

* Track moved relay Git fetch audit

* Split Linear item drawer responsibilities

* Split telemetry event schema responsibilities

* Split resource usage status responsibilities

* Split remote terminal multiplexer responsibilities

* Split Git worktree responsibilities

* Split Codex hook service responsibilities

* Keep mirrored hook trust type private

* Split web runtime session responsibilities

* Split GitHub project view read path

* Split Claude runtime auth responsibilities

* Split runtime RPC server responsibilities

* Split Settings page responsibilities

* Split filesystem watcher responsibilities

* refactor codex account service modules

* fix duplicate managed home path import

* Split activity page responsibilities

* Split accounts pane responsibilities

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161

* Fix F7F8-codex for #17277
2026-08-29 20:25:07 -07:00
Neil d903d07115 Split Activity page responsibilities (#17283)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Split ports panel layers

* Split runtime environments pane layers

* Split local PTY provider responsibilities

* Split CDP bridge responsibilities

* Split relay Git handler responsibilities

* Track moved relay Git fetch audit

* Split Linear item drawer responsibilities

* Split telemetry event schema responsibilities

* Split resource usage status responsibilities

* Split remote terminal multiplexer responsibilities

* Split Git worktree responsibilities

* Split Codex hook service responsibilities

* Keep mirrored hook trust type private

* Split web runtime session responsibilities

* Split GitHub project view read path

* Split Claude runtime auth responsibilities

* Split runtime RPC server responsibilities

* Split Settings page responsibilities

* Split filesystem watcher responsibilities

* refactor codex account service modules

* fix duplicate managed home path import

* Split activity page responsibilities

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161

* Fix F7F8-codex for #17277
2026-08-29 20:24:46 -07:00
Neil 42857f73bb refactor: split Codex account service modules (#17277)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Split ports panel layers

* Split runtime environments pane layers

* Split local PTY provider responsibilities

* Split CDP bridge responsibilities

* Split relay Git handler responsibilities

* Track moved relay Git fetch audit

* Split Linear item drawer responsibilities

* Split telemetry event schema responsibilities

* Split resource usage status responsibilities

* Split remote terminal multiplexer responsibilities

* Split Git worktree responsibilities

* Split Codex hook service responsibilities

* Keep mirrored hook trust type private

* Split web runtime session responsibilities

* Split GitHub project view read path

* Split Claude runtime auth responsibilities

* Split runtime RPC server responsibilities

* Split Settings page responsibilities

* Split filesystem watcher responsibilities

* refactor codex account service modules

* fix duplicate managed home path import

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161

* Fix F7F8-codex for #17277
2026-08-29 20:24:24 -07:00
Neil d140e561a6 Split filesystem watcher responsibilities (#17274)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Split ports panel layers

* Split runtime environments pane layers

* Split local PTY provider responsibilities

* Split CDP bridge responsibilities

* Split relay Git handler responsibilities

* Track moved relay Git fetch audit

* Split Linear item drawer responsibilities

* Split telemetry event schema responsibilities

* Split resource usage status responsibilities

* Split remote terminal multiplexer responsibilities

* Split Git worktree responsibilities

* Split Codex hook service responsibilities

* Keep mirrored hook trust type private

* Split web runtime session responsibilities

* Split GitHub project view read path

* Split Claude runtime auth responsibilities

* Split runtime RPC server responsibilities

* Split Settings page responsibilities

* Split filesystem watcher responsibilities

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:24:02 -07:00