Commit Graph
8616 Commits
Author SHA1 Message Date
Jinwoo Hong 99d19d4635 feat(vm): add provisioned root recipe contract (#14352) 2026-08-13 16:23:21 -07:00
Jinwoo Hong a5a998ea77 fix(vm): make hidden SSH cleanup retryable (#14351) 2026-08-13 16:23:21 -07:00
Brennan Benson c3b8c145e2 fix(agent-status): preserve Codex escape interruption (#14372) 2026-08-13 16:19:26 -07:00
Neil a27d046e5e fix(terminal): reject cwd-resolving PATH entries and harden the legacy wrappers (#14370)
* fix(terminal): guard cmd wrapper PATH walks against an empty variable

An empty PATH or cleaned PATH left the substitution with an unbalanced quote, desynchronizing cmd parsing so the not-found branch emitted a parse error instead of its message. Reproduced and fixed on real Windows.

* fix(terminal): reject relative PATH entries and drop the dirname dependency

Adversarial review found the STA-4169 fix incomplete: it dropped empty PATH elements but kept relative ones, which resolve against the current directory identically. A repo-local git was still executed via PATH=. or node_modules/.bin, reproduced in all three wrappers.

Also removes the external dirname call in the POSIX wrapper (an unresolvable dirname silently made wrapper_dir the cwd, so the wrapper failed to exclude itself and reported git missing while git was on PATH), compares against the cached wrapper dir in the cmd subroutine where %~dp0 is rebound by CALL, and probes .cmd after .exe so a non-.exe git is still found.

* fix(terminal): test rooted paths in pure batch, not via an external tool

The rooted-path guard shelled out to findstr, which cmd resolves from the current directory first — so a repository-local findstr could run, and a malicious one could report success for every entry and defeat the guard entirely. Same hijack the guard exists to prevent. Now a pure-batch substring test with no external process.

Splits the Windows wrapper templates into their own module to stay under max-lines without a suppression.

* fix(terminal): reject drive-relative PATH entries and fix no-slash wrapper dir

Round-2 review: PowerShell IsPathRooted accepts drive-relative C:foo, which still resolves against the current directory on that drive. IsPathFullyQualified is absent on Windows PowerShell 5.1 (verified 5.1 on the test host), so match the same prefixes the cmd wrapper accepts.

The POSIX %/* strip yields the file name when the path has no slash, so wrapper_dir became the name, self-exclusion missed the shim dir, and the lookup resolved back to the wrapper (spurious 127). Handled with a case split.

Also probes .bat, since the replaced lookup honored PATHEXT.
2026-08-13 16:07:49 -07:00
m4air 2f85dda183 Narrow paired browser link fix to registration race 2026-08-13 15:55:23 -07:00
m4air 232e398c35 Fix paired remote terminal browser links
STA-4181
2026-08-13 15:55:23 -07:00
Jinwoo Hong ff8dda81e8 fix(serve): exit cleanly after headless Linux signals (#14334)
* fix(serve): keep owned Xvfb alive through Electron teardown

* test(serve): gate packaged signal shutdown

* test: harden headless shutdown lifecycle gate

* fix(serve): isolate Xvfb from foreground signals

* docs(serve): preserve Xvfb during systemd stop

* test(serve): pin shutdown policy to owned Xvfb unit

* test(serve): harden shutdown gate portability

* test(serve): bound systemd unit parsing
2026-08-13 15:45:00 -07:00
Brennan Benson 953cfab635 fix(terminal): make the bold font weight its own setting (#14368)
* fix(terminal): make the bold font weight its own setting

Deriving bold as max(700, regular + 200) silently destroyed bold. A family
exposes only a few real faces: the monospace the default chain resolves to on
macOS has exactly two, splitting at 600. Measured by rasterizing each weight to
a canvas — 100-500 are byte-identical (ink 3023) and 600-900 are byte-identical
(ink 3855), at every weight the same advance. So any base weight at or above 600
put both values in the same face and bold stopped existing, on 4 of the 9
positions the slider offers, with no error and nothing the user could do.

Arithmetic cannot fix it — on a two-face family there is no heavier face to
escape to. So bold is now user-owned: a new terminalFontWeightBold setting with
its own control, defaulting to 700. The default pair (500/700) straddles the
boundary, so existing profiles render exactly as before; a collision is now a
choice the user can see and undo.

The old test asserted 800 -> {800, 900} as 'keeps bold heavier', which is where
this hid: numerically heavier, identically rendered.

* fix(terminal): surface bold face collisions accurately
2026-08-13 15:32:01 -07:00
Dong dahaoandJinwoo-H d243137e35 fix(orchestration): resolve explicit worker worktrees directly (#14275)
* fix(orchestration): resolve explicit worker worktrees directly

* fix(orchestration): share worker workspace resolution

* fix(runtime): reject cross-host path ambiguity

* fix(orchestration): share federated workspace resolution

* refactor(runtime): share worktree host identity

* test(orchestration): align worker lifecycle fixtures

---------

Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
2026-08-13 15:26:58 -07:00
BingZ 202ce1ef0e fix(orchestration): make late task readiness atomic (#14163)
Derive initial dependency readiness inside the task INSERT so concurrent completion and creation cannot strand a task in pending.

Add deterministic state-machine coverage and a built-CLI RPC/runtime persistence E2E.

Fixes #14143
2026-08-13 15:25:25 -07:00
Jinwoo Hong e06cdbb4ee fix(orchestration): wait for Claude composer render (#14342) 2026-08-13 15:04:32 -07:00
Neil a12f8be144 fix(terminal): resolve legacy wrapper fallbacks from PATH only (STA-4169) (#14365)
The retained git/gh compatibility wrappers resolved their real binary with an unqualified lookup. On Windows that searches the current directory before PATH, so a repository-local git.exe/gh.exe ran with the user's arguments. The POSIX wrapper had the same exposure through empty PATH elements, which mean the current directory.

All three wrappers now resolve only against the cleaned PATH: cmd and PowerShell walk it explicitly instead of delegating to a lookup that includes the cwd, and the POSIX filter drops empty elements. Candidates inside the wrapper directory stay rejected.
2026-08-13 14:55:28 -07:00
Neil 11cd2b4310 revert(ssh): back out #13326 and #13928 — reconnect loses every tab (#14361)
* Revert "fix(daemon): stop killing live coding agents when the daemon can't report its sessions (#13928)"

This reverts commit 2e8cf589de.

Reverted together with #13326: the 1.4.182-daily.202608131439 build carrying
both loses every tab on an SSH disconnect/reconnect cycle. Reverting first so
main stays releasable and the P0 fixes in the wild remain cherry-pickable,
rather than fixing forward on a shipped regression.

* Revert "fix(ssh): stop SSH reconnect from multiplying terminals and resuming agents twice (STA-3077)" (#13326)

This reverts commit 3ab8b6a117.

Reported on 1.4.182-daily.202608131439: connect to an SSH worktree, disconnect
the host from the hosts popup, reconnect — every tab is gone. That is worse than
the behaviour this PR set out to fix, where most tabs were retained.

Reverting rather than fixing forward, so main stays releasable and the P0 fixes
already out in the wild stay cherry-pickable. STA-3077 stays open.
2026-08-13 14:54:29 -07:00
Brennan Benson 292012043f fix(daemon): isolate durable-history checkpoints per session (STA-4173) (#14346)
PR #14193 routed warm reattach and deep-buffer snapshots through
overlayDurableRestoreSnapshot, which joined a single process-wide
checkpoint tail with no deadline. One never-settling checkpoint blocked
every daemon-backed terminal from reattaching.

Checkpoint exclusivity only protects one session directory's
tmp-write/rename pair, so serialize per session instead. Reattach now
waits on a bounded deadline and degrades to the daemon's live window;
the abandoned compact keeps running and still commits, so a blown
deadline costs restore depth for one reattach, never durable history.
2026-08-13 14:51:59 -07:00
Brennan Benson e799259d3b fix(browser): fence the cookie-clear fallback to the pre-clear snapshot (STA-4170) (#14343)
* fix(browser): fence the cookie-clear fallback to the pre-clear snapshot (STA-4170)

The post-rejection fallback re-read the live cookie jar and removed everything
removable at that moment, while restore only ever covered the pre-clear
identity snapshot. A cookie that arrived mid-clear -- a login the user had just
completed -- was therefore deleted with no identity able to put it back, and a
later removal failure still reported "existing cookies were restored".

Fix the removal plan at the same point as the identity snapshot so the mutated
set can never exceed the restore set. Arrivals are not touched at all, which
matches what the successful bulk-clear path already does.

* test(browser): lock same-coordinate arrival rollback to the pre-clear value (STA-4170)
2026-08-13 14:51:27 -07:00
Neil 281cc77e79 fix(ai-vault): make the merged scan stamp independent of leg order (#14270)
* fix(ai-vault): make the merged scan stamp independent of leg order

The all-host merge picked its stamp with a strict `stampMs > latestMs` and
echoed the winning leg's verbatim string. Two legs reporting the same instant
in different legal ISO shapes ("...:05Z" vs "...:05.000Z") therefore resolved
by position in the results array, i.e. by host-enumeration order (local, then
SSH, then runtime). The prior lexicographic max was order-independent, so this
was a regression with no test covering it.

A merge has no single scan instant, so its stamp is derived data rather than
any one leg's string: return the canonical ISO form of the newest accepted
instant. That is order-independent and format-independent, and drops a
variable instead of adding a tie-break branch.

Also share one request resolver between main and the renderer so the
renderer's merged-scope predicate is equivalent to main's routing by
construction, rather than by a comment that overclaimed it.

* docs(ai-vault): scope the merged-predicate comment to the desktop IPC path

The replacement comment still asserted the result is always several hosts'
legs. The paired web transport drops executionHostScope and serves one host,
so 'all' there is a single scan. State that the predicate is deliberately
over-inclusive and why erring the other way would be unsafe.

* test(ai-vault): pin the merged-stamp Date range boundary

new Date(ms).toISOString() throws RangeError outside +/-8.64e15. That is
unreachable only because Date.parse applies TimeClip, so the NaN guard alone
constrains the argument. Nothing pinned that. Dropping the guard now fails
these two cases with the RangeError they exist to prevent.

* refactor(ai-vault): route session-title scope through the shared resolver

The last character-for-character copy of the request-scope default. Leaving
it would make the shared resolver the single source of truth for two of three
sites, which is the drift this change exists to remove. No behavior change.
2026-08-13 14:49:59 -07:00
Brennan Benson 3290ca0d9e feat(agent-map): flare fresh questions and finishes (#14349) 2026-08-13 14:31:13 -07:00
erishandJinwoo-H 1f4b731f7c fix(skills): use exported recipe id in environment guide (#14280)
* fix(skills): use exported recipe id in environment guide

* fix(skills): keep recipe-derived Vercel names valid

---------

Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
2026-08-13 14:18:57 -07:00
Brennan Benson e68c4566ff fix(native-chat): retain watcher identity during initial drain (#14344) 2026-08-13 14:05:55 -07:00
Brennan Benson 162001eef3 feat(sidebar): replace the external-worktrees inbox list with one card (#14341)
The Non-Orca worktrees modal already lists every hidden external worktree
with search, virtualization, and per-row Show. Its filter is a strict
superset of the sidebar inbox's, so the expanded sidebar list was a second,
worse copy that grew to ~900px at 24 worktrees.

The inbox is now a single clickable card stating the count, which opens that
modal. Drops the expand state, nested list, per-row Import, and the
Keep hidden / Import all footer.
2026-08-13 14:04:09 -07:00
Kevin Espiñeira 2f0c33757d fix(worker-start): match Codex effort ceilings (#14281)
Honor the advertised reasoning-effort ceilings for Codex models, preserve conservative unknown-model handling, and localize the new ultra effort label.
2026-08-13 13:58:41 -07:00
Brennan Benson 68a9dfa6e3 feat(dashboard): give the agent question state its own color and glyph (#14248)
* feat(dashboard): give the agent question state its own color and glyph

On the agent map, 'working' (yellow-500) and 'waiting' (amber-500) sat 16 hue
degrees apart and rendered as identical halo rings, so the only cue separating
"busy, leave it alone" from "it is asking you something" was a hue step most
people cannot resolve at map zoom. Every other surface distinguishes the two by
shape (spinner vs question glyph); the map had dropped that.

Move 'waiting' to orange-500 — midway between working-yellow and blocked-red —
and give the map node the same question glyph the sidebar and tabs already use,
so the state reads by shape when hue fails (low zoom, red-green CVD).

Both live behind one token, --agent-question, plus a shared AgentQuestionIcon,
so the sidebar, terminal tabs, kanban, toolbar and map cannot drift apart again.
The unread amber pip is deliberately left alone: "new output" and "needs an
answer" are different states and now read as different colors.

* test(dashboard): retarget the agent-row question assertion at the shared token

DashboardAgentRow renders the glyph through AgentStateDot, so the component
already moved with the token — only its assertion still pinned text-amber-500.
Missed locally because I ran components/dashboard-popout but not
components/dashboard.

* fix(dashboard): raise light question marker contrast

* perf(dashboard): keep question badges on the SVG paint path
2026-08-13 13:58:18 -07:00
Brennan Benson 820fe5b85c fix(sidebar): remove non-Orca source icons (#14345) 2026-08-13 13:57:07 -07:00
Donghoon KangandJinwoo-H 2f41c286e2 fix(docs): replace stale preload typecheck reference (#14298)
* docs: fix stale preload typecheck reference

Signed-off-by: HoonDongKang <d159123@naver.com>

* docs: keep .d.ts guidance canonical

---------

Signed-off-by: HoonDongKang <d159123@naver.com>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
2026-08-13 13:53:50 -07:00
Brennan Benson 8460a63c61 fix(orchestration): retry silent mail pointers (#14332)
* fix(orchestration): retry silent mail pointers

* fix(orchestration): bound mail pointer repair
2026-08-13 13:48:32 -07:00
Brennan Benson ee8dd4796e test(native-chat): deflake watcher rebind (#14335) 2026-08-13 13:03:44 -07:00
Jinwoo Hong f0c1503326 test(renderer): cover Agent Dashboard status replay fanout (#14036) 2026-08-13 12:48:47 -07:00
Jinjing ca9777093b test(e2e): stabilize release triage coverage (#14328)
* test(e2e): stabilize release triage coverage

* test(e2e): avoid post-selection remount race

* test(e2e): wait for New tab search focus

* test(e2e): focus New tab search without pointer input

* test(e2e): reopen voice settings after device change
2026-08-13 12:38:08 -07:00
Brennan Benson 0824ed39ea fix(terminal): clear the SGR pen on hidden-output restore and abandon (#14241)
* fix(terminal): clear the SGR pen on hidden-output restore and abandon

The hidden-delivery gate drops renderer-bound PTY bytes while a pane has no
visible view. The renderer's xterm is a separate emulator from the daemon
model, so when the dropped span contains the sequence closing an attribute run
(e.g. the ESC[22m ending a bold run) the renderer's pen stays latched while the
daemon model stays correct. Neither recovery path cleared it:

- buildMainModelSnapshotReplayWrites reset the pen on the two alt-screen
  branches but not on the normal-buffer branch, and replayed scrollbackAnsi
  ahead of the reset it did emit, so replayed content inherited the stale pen.
- abandonHiddenOutputRestoreAndDrainPendingForeground declares the dropped
  bytes unrecoverable (it writes a user-visible warning) and then drained the
  queued foreground chunks straight into xterm under that same unknown pen.

Add RESET_GRAPHIC_RENDITION and emit it ahead of replayed content in every
branch, and on both abandon exits. The existing profiles all clear DEC mode
bits and none touched SGR.

* fix(terminal): also restore charset designation after a dropped-byte gap

A gap can strand more than the pen: a dropped `ESC(B` leaves line-drawing
selected and ordinary text renders as box characters. Route both recovery
paths through one RESET_AFTER_BYTE_GAP profile covering SGR + charset.

Deliberately not a soft reset (DECSTR): xterm's DECSTR wipes kitty flags and
stacks (terminal-kitty-keyboard-mode-tracker applySoftReset), which would
silence Option chords for a live agent that negotiates them only at startup.
Reset what a gap strands and no running TUI re-asserts on its own; leave the
rest to its next repaint.

* fix(terminal): close the emulator state gap where the drop is announced

The restore-needed marker is the single point where "renderer-bound bytes
were dropped" is known. The handler already resets the transport's
cross-chunk parser state there for exactly this reason — a partial escape
spanning the gap would corrupt the next chunk. The emulator carries state
across chunks in the same way, so reset it in the same place.

That makes restore, abandon and overflow all start from a known pen by
construction, instead of each recovery path having to remember.

* fix(terminal): fully ground byte-gap recovery state

* fix(terminal): reset state when remote restore re-arms

* fix(terminal): keep the gap reset on the warning abandon path

The reset had been folded into an else of the unavailable-warning branch, so
the primary abandon path relied on the marker's earlier reset still standing.
It does not always: this function captures a replayingSnapshot, so it can run
after a partially-applied replay has already moved the pen, and the warning
itself is plain text carrying no SGR. Restore the unconditional write, guarded
only against the remote re-arm which writes its own.

* fix(terminal): scope the byte-gap reset to the pen and skip it under flood

Two regression risks in the widened recovery reset, both removed:

- The profile had grown to cancel partial escapes, close OSC 8 and re-designate
  all four ISO 2022 registers. Each changes what a live TUI sees on a path that
  runs in production, and none has a reported symptom behind it — a legitimately
  line-drawing TUI that does not re-designate after recovery would render box
  characters as ASCII. Scope back to SGR, which is what the field reports show.

- The marker-time reset ran before the flood-backpressure guard, so a flood
  wrote one reset per marker in exactly the case that guard exists to damp. Move
  it after; the flood path repaints through buildMainModelSnapshotReplayWrites,
  which grounds the pen itself, so no coverage is lost.

Coverage verified non-vacuous: blanking RESET_AFTER_BYTE_GAP fails 5 tests
across all four paths (replay branches, marker, abandon-with-warning, remote
re-arm).
2026-08-13 12:11:20 -07:00
Brennan Benson cbca291aa7 fix(orchestration): preserve direct user authority after worker_done (#14192)
* fix(orchestration): preserve direct user authority

* test(orchestration): assert settled dispatch boundaries
mobile-android-v0.0.43
2026-08-13 12:00:04 -07:00
Brennan Benson 096d1a46c4 fix(agent-map): hold orchestration chevrons at a fixed pitch (#14256)
* fix(dashboard): hold agent map orchestration chevrons at a fixed pitch

CHEVRON_SPACING only chose how many chevrons to draw; placement then divided
the edge evenly, so the pitch grew with the distance between agents and, past
the 32-chevron cap, grew without bound. Step at a literal 8px pitch instead,
centering the run so it never overhangs either node.

Fixed pitch makes length drive the chevron count, so cap coverage now decides
how far the run reaches: at 32 it spanned only 248px and every longer link
would have shown a chevron cluster stranded mid-edge. Raise it to 256 (~2048px,
past any real in-project link) and memoize the generated path per endpoint
coordinates, since the scene rebuilds every path string on each zoom frame and
on 4Hz snapshot refreshes while world positions stay put.

* test(agent-map): isolate lineage path cache coverage
2026-08-13 11:58:13 -07:00
Brennan Benson 99f9a3c43d fix(mobile): keep terminal identity scoped to leaves (#14247)
* fix(mobile): scope terminal identity to leaves

* fix(mobile): preserve leaf title projection
2026-08-13 11:44:25 -07:00
Brennan Benson e525f3fe15 fix(mobile): stop republishing stale launch agent identity (#14244) 2026-08-13 11:43:48 -07:00
Brennan Benson 76ecad1091 feat(agent-map): make an unread finish visible and let a glance demote it (#14197)
* feat(agent-map): make an unread finish visible and let a glance demote it

A finished agent was the quietest mark on the map. `status-glow` was defined
only for working/waiting/blocked, so a finish got a 1.7px emerald stroke on a
6px mark and no halo — invisible across a 200-agent fleet.

Worse, `dashboardCardDisplayState` folds `done && !unseen` into `idle`, so
opening an agent erased it: finished-but-unlanded work looked exactly like a
workspace that never ran. Looking at something is not the same as dealing
with it.

Split the two on the map only, via a local `AgentMapNodeStatus` at the single
`agentMapNodeStatus` seam. `DashboardCardDotState` crosses the pop-out bridge
and is unchanged, so there is no wire change and bucket counts are untouched.

- done + unseen: filled emerald core, emerald halo, one-shot 1.4s flare
- done + seen:   hollow emerald, no halo — still yours to land
- workspace ring turns green only once the whole workspace has settled

The flare is gated on a wall-clock recency window, not the map's `now` prop:
`now` ticks every 30s to refresh relative timestamps, so measuring a 1.4s
window against it fired at random moments instead of on the transition.

* fix(agent-map): bound completion paint work

* perf(agent-map): avoid fleet flare allocation

* fix(agent-map): localize seen completion label

* Remove unrelated merge formatting changes

* test(agent-map): guard completion burst paint budget
2026-08-13 11:34:17 -07:00
Brennan Benson d2850b9296 Fix mobile HTML report rendering (#14196)
* fix(mobile): render local HTML reports at phone width

* fix(mobile): satisfy browser URL lint
2026-08-13 11:33:26 -07:00
Jinjing d957e1c0f5 Move worktree labels to right badge rail with truncation tooltips (#14313)
* refactor(palette): move worktree labels to right badge rail with tooltip

Move worktree and branch names from the tab title line to a dedicated right-side badge rail, preventing long titles from being truncated. Add smart tooltips showing full names when truncated, and distinguish between workspace names and branch names based on whether they're auto-generated labels.

* fix(palette): disconnect worktree rail ResizeObserver on unmount

Move truncation observation into a layout effect so React Doctor sees a cleanup path and the subscription cannot leak after unmount.
2026-08-13 11:28:24 -07:00
Jinjing 70abf5cacc test: add golden e2e tests for agent TUI launch and shell recovery (#14258)
* test: add golden e2e tests for agent TUI launch and shell recovery

Add test fixtures and E2E tests to verify agent TUI functionality:
- Stub agent implementation supports cross-platform execution (Unix/Windows)
- Test verifies multiline composer with Shift+Enter support in agent TUI
- Test verifies clean shell resumes after agent exit without state leakage

* test: add golden e2e tests for agent TUI launch and shell recovery

Add agent TUI launch and shell-recovery tests to the golden (release-blocking)
E2E suite, covering agent initialization and shell availability after agent
exit. Improve escape sequence handling in the stub agent to prevent stray key
reports from contaminating test output. Add terminal input readiness checks to
ensure commands execute reliably before verification.

* test: coerce golden stub stdin chunks for type-aware lint

Node types the stdin data event as string | Buffer even after
setEncoding('utf8'), so restrict-plus-operands failed CI.

* test: fix golden stub agent Windows batch files and add Ctrl+C support

- Store batch files with CRLF to avoid Windows 512-byte parser boundary bug
- Handle Ctrl+C (0x03) in raw mode as alternative to Ctrl+D (0x04)
- Update release notes documenting golden test skip behavior on older tags

* Remove Windows batch file gitattributes workaround

The -text whitespace=cr-at-eol rule preventing CRLF conversion for
.cmd files is no longer needed. Allow batch files to use normalized
line endings.
2026-08-13 10:31:08 -07:00
Jinjing b8d6b21dfa test(e2e): add golden E2E tests for workspace session management (#14304)
* test(e2e): add golden E2E tests for workspace session management

- Restore exact file and terminal state after quit/relaunch
- Verify terminal file link activation and external edit detection
- Test worktree creation and switching with isolated terminals
- Isolate test repo paths between concurrent CI runs with UUIDs

* Add platform-aware marker echo command utility

- Create splitMarkerEchoCommand() to generate shell commands that
  safely echo test markers across Windows and Unix platforms
- Split markers into prefix/suffix fragments so output assertions
  prove execution, not just shell echo-back
- Consolidate SORTABLE_TAB export and improve tab bar locator logic
- Refactor terminal link helpers to extract client point calculation
2026-08-13 10:26:20 -07:00
Jinjing e84fb46eaf test: add golden E2E tests for source control workflows (#14260)
* test: add golden E2E tests for source control workflows

- Tests core source control interactions: file edit/save, commit staging, and diff viewing
- Integrated into CI/CD pipelines for Linux, macOS, and Windows
- Includes helper utilities for test setup and worktree management

* test(e2e): verify golden commit author and fix test flakiness

- Configure git author name/email at worktree level during setup
- Verify commits are made with correct author details in assertions
- Add explicit timeouts to file visibility waits and git status polling
- Fix test ordering to seed edits after source control is open
- Simplify git status refresh logic to rely on automatic updates

* Add rollback to createGoldenWorktree on setup failure

Cleanup callbacks only register after setup succeeds. When a config
command fails, the half-built worktree and branch leak into later
test runs, causing flakiness. Now we roll back immediately and
re-throw the setup error.

* test(e2e): match explorer rows after the git status badge appears

The golden file-save spec used an exact /^README.md$/ filter. After save,
the explorer row text becomes "README.md M", so reopen clicked nothing.

* test: strengthen golden worktree setup verification

- Track working directory in git call inspection to verify correct execution context
- Verify user.name/email config applies to worktree-specific settings, not repo
- Add exhaustive setup call sequence assertions to catch setup/rollback leaks
2026-08-13 09:56:30 -07:00
Jinjing e7b85266f5 Add golden E2E tests for fresh terminal and shell commands (#14302)
* test(e2e): add golden tests for fresh terminal and shell commands

Adds regression tests for terminal initialization in fresh profiles and shell command execution to the golden test suite, integrated across Linux, macOS, and Windows CI.

* test(e2e): bracket shell command output between markers

The echoed command can wrap or be clipped by the buffer tail. Bracket output between begin and end markers to reliably identify real output, and strip ANSI escape sequences that interfere with parsing.
2026-08-13 09:31:20 -07:00
Brennan Benson b908b55f6d feat(worktrees): add per-source visibility controls (#14189)
* feat(worktrees): add per-source visibility controls

* fix(worktrees): explain unsupported visibility hosts

* fix(worktrees): keep add location form inline

* fix(worktrees): align source visibility across runtimes

* test(worktrees): cover Windows drive-relative roots
2026-08-13 09:20:11 -07:00
Jinjing 1d2b8c7f42 test(e2e): stabilize triaged release failures (#14242)
* test(e2e): stabilize triaged release failures

* rm file
2026-08-13 08:58:04 -07:00
Brennan Benson 93334dc53f fix(browser): fail-closed cookie clear after a partial import wipe (STA-4090) (#14191)
* fix(browser): fail-closed cookie clear after a partial import wipe (STA-4090)

A rejected bulk clear plus a later per-cookie removal could permanently
delete cookies that were already gone. Snapshot partition-complete
identities before mutating, keep the bulk clear, and restore through CDP
so a failed import never silently drops existing cookies.

* fix(browser): preserve exact cookie clear identities

* fix(browser): abort unrepresentable cookie clears

* fix(browser): skip excluded-only cookie clears
2026-08-13 07:38:53 -07:00
Brennan Benson 9a81874bc1 fix(daemon): restore previously recoverable scrollback from durable history (#14193)
* fix(daemon): restore previously recoverable scrollback from durable history

Keep the 1000-row live daemon window so session count cannot grow the
grid without bound. Rebuild, remount, restart, and reattach now
reconstruct the desktop 5000-row depth from durable history instead of
compacting that live window.

STA-4091

* fix(daemon): bound durable snapshot replay

* fix daemon history reanchor after checkpoint failure

* fix daemon checkpoint continuity on reconnect

* test assert live scrollback write succeeds

* fix(terminal): preserve incremental restore continuity

* fix(terminal): retain live history after restore loss
2026-08-13 05:09:12 -07:00
Neil 4882eeb8ac rm git shim: neutralize stale wrappers without a host gate (#14255)
* Revert "fix terminal attribution shim removal edge cases (#14187)"

This reverts 585dd6d3a9. Re-landed in the next commit without the host capability gate. Nothing shipped with it, so no migration constraint.

* rm git shim: neutralize stale wrappers without a host gate

Re-lands the cleanup half of #14187: pass-through tombstones for retained wrapper paths, env/PATH scrubbing at every spawn owner, and the retired setting drop.

Only writes tombstones when the legacy directory already exists, so a clean install no longer has it created. Leaves out the terminal.attribution-removed.v1 capability gate: the tombstone neutralizes each host locally, so refusing terminal create/split against older hosts denied service without adding cleanup.

* rm git shim: surface neutralization failures and fix rollback marker

Readiness review follow-ups: warn on each failed attempt and on give-up (was silent and undiagnosable); write a VERSION marker distinct from the retired shim's '7' so a rolled-back build rewrites its own wrappers; clear a captured ORCA_REAL_* path that no longer exists so the cmd wrapper's where.exe fallback can run; stop a locked temp file masking the real error. Adds retry-exhaustion coverage.

* rm git shim: pin the cmd fallback order and correct the give-up count

Round-2 review follow-ups: string-pin that a stale ORCA_REAL_* is cleared before the where.exe fallback, and count the initial attempt in the give-up warning so it agrees with the per-attempt line.

* rm git shim: keep the split-failure toast

The revert took a toast that #14187 added alongside the gate but which stands on its own: without it a failed remote split only reaches the console and the pane silently never appears. Also pins attempt ordinals in the retry-exhaustion test.
2026-08-13 03:01:45 -07:00
NeilandOrca 4c5f818187 refactor(skills): remove the unreachable Skills page and the file count it rendered (#14259)
Co-authored-by: Orca <help@stably.ai>
2026-08-13 02:56:43 -07:00
Jinjing dd63d35d09 fix(ci): skip missing golden scripts on older release tags (#14267)
Cut Release is dispatched from main but checks out the tagged tree.
Cherry-pick tags such as v1.4.182-rc.1 do not define
test:e2e:windows-fresh-startup-golden, so the Windows golden job failed
with ERR_PNPM_NO_SCRIPT. Run tag-optional goldens with --if-present.
2026-08-13 02:45:31 -07:00
3984023375 feat(workspaces): make workspace board shortcut a toggle (#14240)
* feat(workspaces): make workspace board shortcut a toggle

The workspace.openBoard command only opened the board; pressing the
bound shortcut again was a no-op, so closing required Escape, the
toolbar button, or collapsing the sidebar. Bind the shortcut bridge
event to the existing toggleWorkspaceBoard so one shortcut both opens
and closes.

Rename the bridge event to TOGGLE_WORKSPACE_BOARD_EVENT and retitle the
command "Toggle Workspace Board". The action id stays workspace.openBoard
to preserve users' stored keybinding overrides.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(keybindings): assert new toggle/open/close search keywords

Cover the search-keyword additions from the toggle rename, per CodeRabbit review on #14240.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* style(keybindings): wrap workspace board search keywords for oxfmt

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-08-13 02:33:52 -07:00
Jinwoo Hong 9cfa00d665 Fix federation terminal settlement retries and legacy admission (#14105) 2026-08-13 02:31:56 -07:00
Neil 298c765a00 fix(ai-vault): stop the scan-stamp guard from deciding merged results (#14261)
Follow-up to #14245. That PR made `scannedAt` meaningful again by keeping
the newest leg stamp on an all-host merge instead of reminting it, and made
the renderer reconcile session rows structurally. Three loose ends:

- An 'all' result is a merge of legs on independent clocks stamped with the
  newest leg, so the renderer's `scannedAt` equality guard could hard-skip a
  real change: a paired host whose clock lags the desktop can return new
  sessions while the merged stamp repeats, hiding them for up to the local
  leg's 60s TTL. Restrict the guard to single-host scopes, where the stamp is
  minted by one scanner at scan completion and equality does imply equal
  content. Merged scopes now let the structural reconcile decide, which costs
  one deep compare per refocus and keeps the identity reuse intact.
  The scope test routes through `normalizeExecutionHostScope` so it matches
  what the main process merges on, including an empty or unrecognized scope.

- `latestAiVaultScannedAt` compared stamps lexicographically after only
  checking `Date.parse` validity. `scannedAt` is `z.string()` on the wire, so
  a legal ISO variant orders wrongly: '...:05Z' sorts after '...:05.500Z',
  and a '-05:00' offset five hours in the future sorts below the local stamp
  and would have been accepted. Compare parsed instants instead.

- `EMPTY_AI_VAULT_SESSIONS` is shared by every mounted hook; freeze it and
  return `readonly AiVaultSession[]` so no consumer can mutate the sentinel.

Each test fails against the pre-fix logic.
2026-08-13 02:26:06 -07:00