Local Setup named only Node, pnpm, and Bun. A first pnpm install also
compiles node-pty and the native addons with node-gyp and loads them in
Electron, and *.electron.test.ts fall back to xvfb-run on headless Linux.
Document the per-platform build toolchain, the Linux Electron runtime
libraries plus xvfb, and the Corepack/pnpm version caveats.
Fixes#31589
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The free windows-11-arm and macos-15-intel lanes already set the matrix's wall
time; the paid 8-vCPU Blacksmith Windows lane finished minutes earlier for ~$48/day.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Both arch jobs overlapped ~2.5 min of setup with qualification, then polled
for the template for 400-560 s on the most expensive runner we use.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
The owner-loss smoke builds the Swift helper and forks the computer sidecar;
neither imports src/cli or src/main/daemon, yet those paths started the paid
macOS runner on every computer-e2e trigger. Move it to its own workflow with
the macOS-relevant subset of the old paths.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* feat(wsl): let an idle WSL distro stop once its orcad idle-exits
The WSL host's holder kept the distro (and the WSL VM) running after Orca quit, even with
nothing running in it. orcad now leaves a release request in ~/.orca when a managed idle stop
retired its terminal daemon; the holder waits for that orcad to exit, claims the request and
ends, so WSL can stop the distro. Live terminals, working agents, enabled automations or a
returning client keep the distro held as before. A host whose distro is stopped at startup
reads as stopped instead of unreachable, and the real-WSL lane gains a phase that proves the
release and records memory.
* fix(wsl): key the holder release on the WSL-host launch marker; name the lane phase
The real-WSL lane showed orcad idle-stopping with its daemon retired while the distro stayed
up: the setsid'd orcad's environment is not guaranteed to carry WSL_DISTRO_NAME, so the
release request was never written. Use ORCA_WSL_ORCAD_HOST, which only WSL-host launches set.
Phase 3 now carries its phase name and path filter, and records what kept a distro running.
* test(wsl): dispose the idle-release lane's guest transports
Each guest command left its exec loop running, a wsl.exe client that kept the distro up after
the holder had already let go (the receipt showed no holder, no orcad, no request left).
* test(wsl): record which wsl.exe clients keep the idle-release lane's distro up
* test(wsl): satisfy the flaky-test ratchet; feed the new idle port in the release test
* test(wsl): snapshot guest processes after launch to find what holds the session
* test(wsl): locate the step that leaves the zombie holding the idle distro
* test(wsl): log the idle-release lane's guest commands with times to place the zombie
* test(wsl): follow the remote runtime client's move
* test(wsl): record when each leftover WSL session init started
* fix(wsl): keep the distro while the user still runs anything, and never trip on the request file
Review of #33084:
- The holder now checks, before claiming a release, that no process of the user runs outside
the holder itself (session plumbing aside: systemd --user, (sd-pam), dbus-daemon). A detached
tmux, nohup job or dev server keeps the distro; it fails toward holding.
- A new holder drops any request left from before it started, so an older orcad's request can
never end a holder a client just asked for.
- orcad touches the request file only for WSL-host launches and never throws over it.
- The real-WSL lane proves a detached job keeps the holder and its request, and the distro
stops once the job ends.
* fix(wsl): a running container keeps the distro, whoever owns its shim
Docker's containerd-shim processes run as root, so the user-only scan missed them and a
release would have stopped running containers. Any containerd-shim process now keeps the hold.
* fix(wsl): read each process status only up to Uid; document what keeps the hold
Delta review of #33084: dash reads a status file a byte at a time, so stop at Uid. Document
that a root default user never releases, that per-user agents hold it, and the hidepid
exception. The lane's pkill no longer matches the shell running it.
* fix(browser): focus newly opened image tabs
Focus the window before the webview so Command+W closes the new
image tab instead of the original tab after a plain image click.
Add tests for focus order, image loading, and close-shortcut forwarding.
Fixes#34114
* test(browser): verify image tab focus with native keyboard input
* refactor(ci): keep browser E2E routes within registry size limit
* fix(ci): enable background launch for native browser regression
* test(ci): include browser routes in isolated detector fixture
---------
Co-authored-by: Neil <neil@stably.ai>
* fix(native-chat): a chat sends again once the terminal's agent exits on macOS
The terminal-writer check required the pane's root process to be a shell,
but macOS panes spawn under login(1), which stays as the shell's parent,
so the check refused every send until the terminal closed. Census the
shell login started instead.
* Reconcile current terminal writer evidence before continuation
* Share fresh foreground confirmation and prove hosted login census
* Restore shell process identity import in foreground tracker
* Correct process fixture identity and polling callback contracts
* Align decisive Windows inspection fixture expectations
* Complete foreground inspection Session fixture
lint:ast-grep and check-flaky-test-pattern-ratchet each parsed the whole tree.
ast-grep-gates.mjs scans once; the lint gate re-runs ast-grep only on files with
warning-or-error findings (ast-grep's own report and exit code), and the flaky
ratchet counts test-* matches from the shared scan.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
- Plan unit selection starts after setup (overlaps lint) instead of last.
- Localization extraction starts with the typecheck; test-file-runners is background.
- run-ratchets.mjs runs ratchets in a pool (19s serial -> 8s locally).
- pnpm lint uses CI's single-parse check-import-boundaries.mjs.
- ORCA_UNIT_FULL_SHARD_COUNT accepts 2-10 (default stays 5).
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* Unify terminal starts under one host operation
* Pin winning geometry across a late terminal join
* Use terminal creation options type in launch race test
* Check closed panes after acquiring the terminal spawn lease
* Preserve terminal start resource and join boundaries
* Keep terminal attach regression within project boundaries
* Capture terminal spawn authority and preserve image coverage
* Use checked fixtures for terminal image replay regression
* test: expose persisted pane to main layout in attach race
* test: contract terminal spawn scaling and retention
* test: correct spawn scaling fixture types and CI selection
* test(terminal): cover exited prompt claims and adopted team cleanup
* test(pty): consolidate mock registry imports
* fix(pty): retain untyped IPC resume inputs in common contract
* test: remove low-value inventories, passthroughs and redundant cases
* style: format test suites after pruning redundant cases
* test: remove obsolete workflow and flaky-test references
* test: prune deleted builder test from Windows shim allowlist
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Reverts #29133 except its daemon async-I/O stall telemetry, so we can measure how often stalls happen on official Node before committing to a patched runtime. The macOS terminal host goes back to the official Node 24.22.1 download and the daemon protocol back to v51; no release or hourly build published v52.
* fix(native-chat): one ownership index with link roles, and a chat refuses to start while a terminal writes its conversation
Each chat link now carries its role (head or earlier) and key; Session History names
the chat continuing a conversation first, duplicate adoption checks only head links in
both the pre-commit check and the store transaction, and terminal resumes stay refused
exactly as before. The terminal-writer check now also runs inside acquireOwner before
the spawn, refusing with the typed pre-spawn reason conversationOpenInTerminal.
* fix(native-chat): hold a terminal start Orca issues until its terminal reports it
An in-memory registry of managed terminal starts per conversation, registered in the
same step as the resume admission (terminal.send with a resume command, createTerminal
with a command), bound to the PTY execution that runs it, and ended only when that
terminal's hooks report the conversation or the execution is gone. A chat's agent
start refuses while an entry may hold its conversation.
* fix(terminal): an automatic pane restore never resumes a conversation an open chat continues
Resume recipes built from a pane's saved facts (cold restore, deferred and late reattach,
sleeping wake, host mobile materialization) carry resumeOrigin 'saved'. The execution host
decides before any command is issued: while an open chat continues the conversation, or a
chat may be writing it, the pane starts its plain shell with the started-fresh notice and
its saved claim is forgotten; with unreadable records only a saved restore is refused and
its record kept. An admitted resume registers its managed start in the same step, so the
chat's agent start refuses until the terminal reports it or exits. Clearing the last saved
record now reaches disk.
* Fix saved terminal resume ownership and effective launch results
* Preserve conversation case and clear refused cold restore identity
* fix: publish effective terminal launch results on attachment
* fix: preserve terminal launch callback and candidate ownership
* fix: keep existing terminal attachments observational
* fix: consolidate terminal launch imports
* test: document snapshot runtime fixture boundary
* fix: preserve terminal launch identity types
* fix: accept reattach payload policy sessions
* fix: preserve terminal result validation contracts
* refactor: separate terminal create contract
* test: document partial terminal runtime fixtures
* test: align sleeping resume store fixture
* Fix terminal create type contracts
* fix: return the terminal main started to its waiting view
* fix: register reattached terminals before publishing their handles
* Check unavailable-resume test fixture types
* Complete sidebar resume test store fixture
* test: bound saved resume ownership and refusal work
* Settle refused saved resume claims from actual launch results
* Fix saved resume test type contracts
* test(e2e): capture exact terminal parking diagnostics
* test: verify parked terminal with fresh host inventory
* Consolidate agent session imports in remote terminal transport
* Clarify terminal conversation refusal guidance
* Admit note-only saved conversations without hiding live writers
* Retain terminal writer proofs through final admission
Builds the macOS terminal host's Node 24.22.1 from pinned source with a libuv patch: idle threadpool workers recheck for queued work every 30 s instead of waiting forever, so a lost Darwin condition-variable wake-up (libuv #5280) no longer strands terminal I/O. Bumps the daemon protocol to v52 so new terminals start on the patched runtime right after an update; older daemons stay attachable. Adds stalled/recovered daemon async-I/O telemetry, and lets release workflows rebuild older refs that predate the source build.
Fixes STA-11709, #31926.
* perf(lint): keep JS plugins out of default oxlint and adopt ast-grep
Any jsPlugin makes Oxlint hand every file's AST to Node, so the seven custom
plugins cost the default `oxlint` run several times its native time.
- Move app-store-performance, quadratic-buffer-concat, renderer-scrollbar-style
and ime-enter-guard (scope- or dataflow-aware) to config/oxlint-js-plugins.json,
run by `pnpm lint:js-plugins`, CI, lint-staged and the changed-lines gate.
- Port sort-comparator-performance, mobile-pairing and native-chat-design-tokens
to ast-grep rules with rule tests, and delete the plugins.
- Add @ast-grep/cli, config/sgconfig.yml, `pnpm lint:ast-grep` / `test:ast-grep`,
a CI step, and an ast-grep pass in check-changed-code-quality.
- Add flaky-test rules (wall-clock reads, real-time waits, polling loops,
unquarantined skips, unexplained timeout bumps), baselined by a count ratchet.
* Enforce flaky-test ratchet on changed files; refresh baseline
* docs(agents): keep custom-rule details out of AGENTS.md
* Resolve ast-grep from the repo so gate scans of other roots work
Serves the renderer through Vite's experimental bundled dev mode when the
variable is set. Strict execution order keeps the React Refresh preamble ahead
of chunks shared by the four HTML entries. Default dev behavior is unchanged.
* feat(agents): TypeScript 7 code intelligence for coding agents, load-aware tsrs checkers
The stock agent TypeScript plugin needs a compiler build TS 7 no longer ships, so agents in
this repo got no diagnostics or ran some other TypeScript.
- config/scripts/agent-typescript-lsp.mjs starts the repo's own TS 7 language server
(tsrs on Apple Silicon and Linux, the typescript package's tsc elsewhere) and turns its
pull-only diagnostics into pushed publishDiagnostics for push-only clients.
- .claude/settings.json registers a repo-local plugin marketplace (config/agent-plugins)
and enables the orca-typescript LSP plugin; opencode.json runs the same server.
- Local tsrs typechecks now pass --checkers, splitting the cores the load average leaves
idle between concurrent projects (floor 4); ORCA_TYPECHECK_CHECKERS pins it.
* docs(agents): drop LSP and checker notes from AGENTS.md
- test:changed exits before starting Vitest when Git reports no changed files.
- Unrecorded tests beyond 100 in scope use Vitest's static import graph instead
of all running, so a fresh worktree's first changed run no longer runs the
whole suite (patched RecordedModulesResolver; CI does not record, unchanged).
- Bun's slow config load was the SQLite import scan statting every unit test;
filtered runs now classify only the tests their filters can select, and the
scan walks directories instead of fs.globSync.
- Silence the native config loader notice on every run.
- Zod's test bundle is content-addressed and reused, so a killed run leaks
nothing; leaked per-run directories older than 6h are swept.
- build:packages skips compiling when package inputs, compiler, and dist are
unchanged; test scripts no longer nest pnpm invocations.
- Bump mobile typescript to ^7.0.2 and vitest to 5.0.5; keep TypeScript 6 as typescript-api
for the test-support code that walks the AST, mirroring the root.
- Patch Expo CLI's tsconfig reader to fall back to typescript-api: TypeScript 7 has no
compiler API, and Metro otherwise crashes reading tsconfig paths.
- Register mobile/tsconfig.json and mobile/tsconfig.test.json with the root typecheck runner;
locally they use incremental tsrs (Bun elsewhere), CI uses Bun like root CI.
- Route the tests-typecheck ratchet through the same compiler selection.
- Enforce root/mobile parity for typescript, typescript-api and vitest.
Linked worktrees now get a per-worktree userData profile under
orca-dev-wt/<hash>, a deterministic collision-checked renderer port
(15200+) and CDP port (9333+), so parallel `pnpm dev` runs no longer hit
the single-instance lock. The main checkout keeps the shared orca-dev
profile; --shared-profile / --worktree-profile / ORCA_DEV_PROFILE override.
The runner prints `ORCA_DEV_INSTANCE {json}` on stdout and mirrors it to
out/dev-instance.json so agents can attach over CDP. orca-dev.mjs and the
terminal wrappers follow the profile the worktree's dev app used.
Adds profile seeding (pnpm dev:seed-profile, pnpm dev --seed-profile,
pnpm dev:fresh --seed-from): an allowlisted slim copy of projects,
workspaces, settings, SSH targets, and the account session, written as
orca-data.json for Orca's own import; the source is only read.
Storage reclaim removes per-worktree profiles once their worktree is gone
and no app, runtime, or daemon still uses them. dev-fresh-profile.sh is
replaced by a cross-platform dev-fresh-profile.mjs.
Add `pnpm native ensure <ios|android>`, which installs a cached dev-client
build keyed by the Expo fingerprint and only runs `expo run` on a miss.
The cache lives in the user cache dir so every worktree shares it, and
app.config.js registers it as Expo's buildCacheProvider so plain
`expo run` reuses hits too. A report-only PR job flags fingerprint
changes against the base in the step summary.
* ci: fail when a checked-in test file has no runner
Two node:test files under .github/actions lost their runner when #31792
trimmed cloud pretest, and the two monaco .bench.ts budgets were never run
by any CI job. Wire both back in and add a static check that every
test-like file is collected by a declared runner or imported by a
collected entry (the src .spec.* suite-module convention).
* ci: ignore type-only imports and quote the lease glob for cmd.exe
* ci: parse type-only imports, cover UNIT_EXCLUDE, route cloud runner edits
Type-only imports are now dropped with the TypeScript parser, so a type
alias before a runtime re-export no longer hides it. UNIT_EXCLUDE files
must be named by a pr.yml or unit-tests.yml step. A cloud-only diff that
adds a test or edits a runner config now runs the static job. The monaco
benchmarks stay manual (pnpm test:perf:benchmarks) until a CI runner
proves their wall-clock budgets.
* ci: require UNIT_INCLUDE names in dedicated jobs and erase typeof import()
* ci: resolve each runtime import to the one file Vite loads
The unit dependency graph keeps every plausible candidate so it can
invalidate conservatively, which cannot prove a suite runs: an import of
./case.spec counted both case.spec.ts and case.spec.tsx. Resolve with
Vite's extension order instead.
* ci: take runtime imports from transpiled output
Imports used only as types are erased without a type modifier, so
parsing for type-only syntax still followed them. Derive edges from
ts.transpileModule output, which erases what Vitest's transform erases,
and only transpile files that can reach an uncollected candidate.
* ci: fix the agent-status benchmark config and route all cloud code
The agent-status benchmark config inherited the unit projects, whose
includes override its own, so it ran the whole unit suite and never the
benchmark. Clear projects as the performance config does. Route every
cloud code file to the static job, since content-detected tests can have
any name.
* ci: move routine checks and unsigned builds to Blacksmith
* ci: retain GitHub Mac images for isolated SSH fixtures
* ci: keep shared terminal perf preparation on Blacksmith
* test: allow encrypted slow-link fixture to finish on ARM CI
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>