Commit Graph
10243 Commits
Author SHA1 Message Date
Neil 41454cfec3 test(electron): reap isolated macOS crash reporters on teardown 2026-09-05 07:59:20 -07:00
Neil 5da57004e9 Merge branch 'nwparker/worktree-create-technical-v2' into nwparker/instant-retained-workspace 2026-09-05 07:45:30 -07:00
Neil 80c9377154 test(ssh): verify recovered shell execution and lease ownership 2026-09-05 07:45:14 -07:00
Neil 4ebfcf6c01 test(ssh): verify recovered shell execution and lease ownership 2026-09-05 07:44:24 -07:00
Neil 27e4727d00 Merge branch 'nwparker/worktree-create-technical-v2' into nwparker/instant-retained-workspace 2026-09-05 07:28:46 -07:00
Neil 148ee486d1 test(ssh): wait for replacement PTY before lease recovery input 2026-09-05 07:28:12 -07:00
Neil 2aa499b941 Merge branch 'nwparker/worktree-create-technical-v2' into nwparker/instant-retained-workspace 2026-09-05 06:59:27 -07:00
Neil a9f00a034d fix(build): retain Codex preflight entry for packaged CLI 2026-09-05 06:59:11 -07:00
Neil c6f3776834 Merge branch 'nwparker/worktree-create-technical-v2' into nwparker/instant-retained-workspace 2026-09-05 06:51:16 -07:00
Neil 9a84a57882 perf(cli): avoid loading other agent hooks for Codex preflight 2026-09-05 06:51:01 -07:00
Neil 78e56ed5e1 perf(cli): avoid loading other agent hooks for Codex preflight 2026-09-05 06:50:42 -07:00
Neil a7d1aef437 Merge branch 'nwparker/worktree-create-technical-v2' into nwparker/instant-retained-workspace 2026-09-05 06:13:51 -07:00
Neil 5e2e2e1951 perf(git): skip malformed remote base probes 2026-09-05 06:13:12 -07:00
Neil 14a6530189 fix(worktree): align packaged compatibility and handler contracts 2026-09-05 05:22:08 -07:00
Neil 3d43b43114 merge updated technical base into composer preparation stack 2026-09-05 05:10:19 -07:00
Neil 9d88718170 perf: preserve user Git checkout worker settings 2026-09-05 05:07:40 -07:00
Neil 08ae314cee merge main into worktree technical improvements 2026-09-05 05:06:49 -07:00
Neil 39a02f1dc9 merge main into retained composer integration 2026-09-05 05:06:06 -07:00
Neil 17c888fda8 fix(daemon): require restart-aware deferred startup owners 2026-09-05 04:59:21 -07:00
Neil f03dc12b0d feat(worktree): prepare agent shells in retained composer drafts 2026-09-05 04:45:29 -07:00
Neil b268adef18 feat(runtime): prepare owner-fenced deferred terminals 2026-09-05 04:01:17 -07:00
Neil af82126058 fix(native-chat): give the Claude exit barrier a handle on unpublished exits (#18826)
A first-hand Claude exit is not published where it is observed. `handleExit`
re-enters the close ladder and persists the transcript cursor before it emits
`ended`, and only that emission reaches the runtime's recovery chain. So the
runtime's `waitForRecovery` — whose whole job is to drain an in-flight recovery
before teardown stops children — returns immediately for an exit that is still
climbing the ladder, and nothing outside the adapter can tell an observed exit
from a published one.

The integration test for fenced host reconciliation had no handle on that
barrier, so it bounded-polled the lease for 100ms instead. Measured under 16x
local concurrency, publication alone takes 77-204ms: 19/24 runs failed.

Retain the ladder-then-settle tail on the exit record and expose
`drainObservedExits`, fold it into `waitForRecovery`, and export the barrier so
a caller that needs the settled lease can await it. Codex publishes inside its
own exit callback and needs nothing. The test now awaits the barrier: 0/24
under the same load, and it fails on an idle machine without the drain.
2026-09-05 03:50:19 -07:00
Neil f0110601bd feat(daemon): negotiate deferred startup preparation and release 2026-09-05 03:49:32 -07:00
Neil f75781902d feat(daemon): hold composer startup until an owner-fenced release 2026-09-05 03:31:37 -07:00
Neil 265871c53d fix(native-chat): stop a settling handoff throwing an unhandled rejection at teardown (#18824)
* fix: stop a handoff flow from outliving the host that owns its session

A structured handoff runs on the session's serialized chain and nothing in
production awaited it. When the client-side deadline for the switch expired
first, teardown dropped the session map out from under a live flow, and the
flow's own failure notification then threw `agent_session_ownership_unknown`
out of a status publish — an unhandled rejection, plus journal rows written
into a directory that was already being removed.

Three fixes, each with a regression test that fails without it:

- The status publish is a notification, not a mutation: it now reads the fence
  without requiring an attached session, so an evicted or torn-down session
  makes it a no-op instead of a throw.
- `track` used `.finally`, which forwards a rejection onto a promise nobody
  awaits. `drain` settles flows through `allSettled`, so the bookkeeping chain
  is now settle-only and cannot resurface one.
- Host teardown drains in-flight handoffs before dropping the session map.
  `drain` existed for exactly this and was never wired up.

The integration test's `vi.waitFor` is dropped rather than widened: the request
enqueues the flow on the session's serialized chain before it returns, so the
status read is already ordered behind it. The poll only added a wall-clock
deadline that a loaded runner missed.

* fix: bound the handoff drain so a wedged flow cannot hold the quit open
2026-09-05 03:15:17 -07:00
Neil eae9f17236 refactor(pty): separate startup identity from eager shell argv 2026-09-05 03:12:06 -07:00
Neil b7375e090b docs(worktree): clarify successful preparation refill ownership 2026-09-05 03:02:52 -07:00
Neil a823f97d63 perf(worktree): skip remote probes with no possible result (#18821) 2026-09-05 03:01:12 -07:00
Neil 267bd8f094 fix(worktree): replenish active standby after successful creation 2026-09-05 02:56:12 -07:00
Neil 50071fc362 perf(worktree): retain an idle preparation for the active composer target 2026-09-05 02:42:50 -07:00
Jinwoo Hong 9f2a9a248e fix(cloud): validate protocol-0 same-cap cell plans without rehome trust lines (#18818) 2026-09-05 05:42:37 -04:00
Soshiro Narita 1a76a11e39 feat(i18n): localize onboarding flow to Japanese (#18787) 2026-09-05 02:37:06 -07:00
Neil 27bbd61b96 feat(worktree): expose checkout-only standby preparation 2026-09-05 02:30:33 -07:00
Neil d3ebcd4535 fix(worktree): preserve retained creation progress on submit 2026-09-05 02:15:08 -07:00
Neil 0bbf86bb7a fix(renderer): stop a Node-only process-table module blanking the app at boot (#18814)
Every Electron E2E spec that boots the app has been failing on
`workspaceSessionReady did not become true`, and the app itself has been
launching to a blank white window: the renderer threw
`ReferenceError: process is not defined` while evaluating a shared chunk,
so React never mounted and no startup step ever ran.

`agent-completion-poll-interval.ts` (renderer) imported one constant,
`PROCESS_TABLE_SNAPSHOT_MAX_STALENESS_MS`, out of
`shared/process-table-snapshot-reader.ts` — a `node:child_process` /
`node:fs/promises` module whose dependency evaluates `process.platform` at
module scope to pick `ps` columns. The renderer runs sandboxed with
contextIsolation, where `process` is undefined, so that module-scope read
threw and took the whole chunk with it. Introduced by #18742; #18780 added a
second module-scope read next to the first.

The constant now lives in `shared/process-table-snapshot.ts`, the
environment-neutral half of the pair, and the reader re-exports it so host
callers are unchanged. The two `ps` column sets read the platform behind a
`typeof process` guard, which defuses the same landmine for any future
renderer import of that module — only hosts ever run the argv.

The regression test walks the renderer import graph (lazy routes included)
from all three entries and refuses any module that reaches a `node:` builtin.
It fails on the pre-fix import with the full 10-hop chain from `main.tsx`.
2026-09-05 02:07:37 -07:00
Neil 504f2a5d0a fix(worktree): preserve background startup across runtime requests 2026-09-05 01:55:32 -07:00
Jinwoo Hong 12e05203a4 fix(cloud): let the same-cap roll isolate Asia cells (#18811)
The same-cap wave validator approves 19 cells (c7-c26 plus the Asia cells
c27-c29), but the canary script it drives hard-rejected anything outside the
16 US capacity cells, so the first Asia same-cap canary failed closed at
isolate. Give the canary an explicit --approved-cells switch that selects the
same-cap allowlist, and pass it from the four same-cap job invocations. With no
switch the behaviour is unchanged, so the US-only capacity workflow keeps its
scope.
2026-09-05 01:51:06 -07:00
Neil 06ca54ae7c fix(test): stop detached git maintenance racing the divergence fixture teardown (#18810)
`worktree-base-divergence-real-git.test.ts` builds cap-sized histories (100 and
101 commits). Every `git commit` detaches `git maintenance run --auto`, whose
commit-graph task arms at 100 new commits, so the fixture reliably spawns a
background `git commit-graph write --split` that keeps creating
`.git/objects/info/commit-graphs` entries after the synchronous exec returns.
The `afterEach` recursive remove is then deleting `.git/objects` underneath a
live writer and dies with ENOTEMPTY — which is how "counts drift in both
directions" failed on main.

Reuse the existing `GIT_FETCH_SKIP_AUTO_MAINTENANCE_CONFIG_ARGS` (it already
covers modern maintenance and legacy auto-gc, so it holds at the Git 2.25
baseline) in the fixture's git helper. Traced spawns of
`git commit-graph write` over a full run of this file: 4 before, 0 after.

The production path under test only runs `rev-list` and `merge-base`, neither of
which triggers auto-maintenance, so there is nothing to fix outside the fixture.
2026-09-05 01:49:48 -07:00
Neil b58849c71a fix(worktree): await queued Codex trust before agent startup 2026-09-05 01:47:50 -07:00
Brennan BensonandMerge Sim cec26336e5 fix(native-chat): say when a structured launch fell back to a terminal (#18762)
* fix(native-chat): say when a structured launch fell back to a terminal

A definitive refusal already opened a terminal instead of the requested
structured chat, but said nothing — indistinguishable from the bug where the
wrong surface opens. Notify at message severity, since nothing failed.

Also stop putting the raw error in the failure toast's description: it carried
errnos and absolute paths straight into the UI. The detail moves to a warn log
and the toast gets catalog copy, matching how the coded refusals already read.

* fix(native-chat): avoid overstating terminal fallback

---------

Co-authored-by: Merge Sim <sim@local>
2026-09-05 01:47:43 -07:00
Neil da23e30e90 perf(worktree): prepare agent checkouts before composer submission 2026-09-05 01:41:49 -07:00
Neil 9927edd631 fix(ssh): let the host say whether it armed the ready marker (#18802)
#18796 made every SSH Codex background launch wait for the shell-ready marker,
but the client cannot see the remote shell. On a host that never publishes one --
fish, sh, Windows, or a relay predating #18796 -- no marker arrives and delivery
falls back at 1.5s where it used to write at 50ms.

The relay already computes whether it armed the marker; publish that as an
optional `shellReadyArmed` on the spawn reply and let the client skip a wait it
now knows is pointless. Absent stays UNKNOWN and keeps the client's own guess, so
an older host behaves exactly as before; false is only ever an answer a host gave.
It rides every reply, false included, or absent would stop meaning "old host".

A host that did not arm the marker did not arm bracketed paste either, so the
released path still submits raw.
2026-09-05 01:34:38 -07:00
Neil 1f7392d2d7 perf(worktree): retain remote composer checkouts before terminal activation 2026-09-05 01:29:10 -07:00
Neil 14b360733b fix(worktree): preserve selection during background runtime startup 2026-09-05 01:28:40 -07:00
Neil 602bef4b6f perf(worktree): keep successful preparation off stale cleanup waits 2026-09-05 01:17:14 -07:00
Neil 766e30ca46 perf(worktree): activate completed composer workspaces without async gaps 2026-09-05 01:09:27 -07:00
Neil e95d247be1 perf(terminal): cheap-tier process inspection for anchored local agent panes (#18780)
* perf(terminal): cheap-tier process inspection for anchored local agent panes

Every idle local pane's completion cadence ran a full whole-host `ps` (with
`tty=` and `command=`, 0.34-0.50s on a 1,900-process Mac, 1.15s on Linux)
purely to build `foregroundProcessEvidence` that the renderer then discards
for local ids. Add a cheap tier (same job-control columns, no tty/command,
0.03s) gated so that it introduces no user-facing trade-off:

- Only a pane whose last FULL capture proved a recognized agent may take the
  cheap tier. Panes with no anchor always take the full capture, so start
  discovery keeps today's exact behaviour.
- The cheap tick compares a per-pane fingerprint (root shell pid+start, tpgid,
  every descendant's pid+start+pgid+job-control state). Any change, a changed
  node-pty foreground name, an unreadable capture, or an incarnation mismatch
  escalates to the full capture. A recognized agent's exit is always a pid
  vanishing, which the fingerprint always sees.
- A cheap answer OMITS evidence rather than fabricating a tty-less fence.
  Remote/restore consumers never send `steadyState`, so they keep the full
  capture unchanged.
- `steadyState` is a new optional request field; an old daemon ignores it and
  answers with the full capture.

Measured (8 idle panes, 60s, idle cadence, forks counted by column set):
30 full -> 1 full + 29 cheap.

* fix(terminal): route the cheap ps capture through runProcess

The cheap-tier reader imported node:child_process directly, which the
child-process import-boundary and windowsHide ratchet tests reject (CI shards
1/8 and 3/8). Use Orca's single spawn entry point instead; it pins windowsHide
and encodes argv. Map its result onto the capture-error vocabulary:
outputTruncated -> capture_truncated, timedOut -> capture_timeout, non-zero
exit -> ps_exit_<code>. Tests mock at the runProcess seam.

* fix(perf): refuse a pane fingerprint when any descendant start marker is missing

`buildPaneProcessFingerprint` rejected only a missing root start marker; a missing descendant
marker was stamped as `?`. Two captures that both failed to read the same descendant therefore
compared equal, which removes the pid-reuse protection the fingerprint exists to provide: a
recycled pid could make a vanished agent look unchanged, and the cheap tier would keep serving
its name instead of escalating.

Reachable on Linux, where `readLinuxProcStartTime` legitimately returns null when a process
exits between the `ps` capture and the `/proc/<pid>/stat` read.

Every subtree member now needs a start marker or the fingerprint is refused, which sends the
caller to the full capture — the same conservative default every other uncertain path takes.

Reported by CodeRabbit on #18780. The two new tests fail against the previous code with
`expected '4242@2400#4300:|4300@?:4300:+' to be null`.
2026-09-05 00:57:02 -07:00
Neil ccf3e27800 perf(relay): bound the symlink directory probes a remote readDir fans out (#18752)
`readRelayDir` issued one `stat` per symlinked entry and awaited them all in a single
`Promise.all`. A pnpm `node_modules` is hundreds-to-thousands of package symlinks in one
directory, so expanding it over SSH put that many stats in flight at once, saturating libuv's
four-thread pool and delaying every other relay filesystem operation — including the
interactive reads `fs-list-files-scan-coordinator` exists to protect.

The probes now run through `forEachWithConcurrency` at 8, the cap every other bounded probe in
this codebase already uses (`GIT_COMMON_SNAPSHOT_CONCURRENCY`,
`PRUNABLE_EXISTENCE_PROBE_CONCURRENCY`, `SPARSE_CHECKOUT_DETECTION_CONCURRENCY`).

Results and ordering are unchanged: every symlink still resolves to its target's kind, and
`sortDirEntries` still runs afterwards. The new test builds a 60-symlink directory and asserts
the same 60 stats happen with exactly 8 in flight at peak — the probes overlap, and never past the cap.
2026-09-05 00:56:50 -07:00
Neil 3ff0d0e4c3 feat: retain background composer workspaces for blank terminal creation 2026-09-05 00:55:27 -07:00
Neil 89cf4433ac perf: preserve user Git checkout worker settings 2026-09-05 00:53:41 -07:00