Commit Graph
690 Commits
Author SHA1 Message Date
Neil d371e2d8b6 ci: reduce runner occupancy in release and contract jobs (#28930) 2026-10-14 19:53:57 -07:00
Neil 8df0f67c82 ci: skip redundant PR platform warmers (#28917) 2026-10-14 18:48:13 -07:00
Neil f23fa8ca76 Prepare Windows natives with scoped compiler settings and measure servicing (#28881)
* Scope Windows native compiler setup and measure ARM inbox preparation

* Add a controlled Windows native preparation comparison

* Test native preparation through an injected process runner

* Guard reviewed Windows native batch runners
2026-10-14 18:18:24 -07:00
Jinwoo Hong 6e46d5ef33 feat(relay): watch a reserve cell and flip it back on the first step 5 stop rule (#28698)
* feat(relay): watch a reserve cell and flip it back on the first step 5 stop rule

A manual, inert-until-dispatched workflow samples one reserve cell every minute for up to
300 minutes against the step 5 §9 stop rules, logs one JSON line per sample, and on the
first trip (or N unreadable samples in a row) runs the flag tool's own admitMode=db write
under the flag workflow's concurrency group. dry-run logs the flag tool's plan instead.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): keep the reserve watch's flip back out of the flag group, and baseline 503s before the flip

- The flip back holds no concurrency group: GitHub keeps one pending run per group and a
  newer one cancels it. The flag tool's CAS orders it instead; a generation conflict is
  re-read and retried (5 tries, backoff) while the switch file still says reserve.
- The 503 background comes from the 10 minutes before a required flipped-at, or an explicit
  value; neither is refused.
- A failed or cancelled write-mode watch flips back too. An unreachable cell fails with the
  exact break-glass inputs.
- Missing 503 data (empty series, unpublished minute, unparseable time) is unverifiable.
- Seat-behind-row key adds the director instance and cell; six segments share one command.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): read the reserve watch's 503 publishing front from every response code

A quiet fleet has no 503 series, so the watch read it as unverifiable and flipped a healthy
cell back within minutes. The 503 read now groups request_count by response code in one
query; the newest minute any code has a point for is how far the series is published, so a
503 minute at or before it with no point is zero and minutes past it are left out.

Also: the flip-back job's steps run on always(), flipped-at ages against the watch's start
in later segments, a pending pre-flip background no longer adds to the unverifiable streak,
the flip back retries runtime-status before calling for break glass, and the concurrency
rationale lives once in the docs.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* docs(relay): state what a cancel during the flip-back lease wait can leave, and the 503 publish-skew assumption

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): fail a segment closed on a corrupt watch state file, and keep no checkout token on disk

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): alarm on a failed flip back, refuse a watch under a rollout lease, wait for reserve to apply, and control the duplicate read

- A flip back whose write or plan never finished (no ID token, a failed login, a silent cell)
  raises an ::error:: annotation and a FLIP BACK FAILED summary, as the clock-skew monitor does.
- The first segment reads the Cloud SQL rollout lease through the lease action's own reader and
  refuses to start while any run holds it, or when it cannot be read.
- A cell not yet reporting reserve is waited on for up to 10 minutes, judging nothing, before the
  watch ends refused without a write.
- The duplicate-seat read takes the per-minute ledger summary in the same query and window; with
  no fresh summary in it the sample is unverifiable, not zero duplicates.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): page on a refused reserve watch or a failed flip back

A refused watch can leave a cell in reserve and unwatched, the same as a flip back that did not
land. Both now raise an escaped ::error:: annotation through one `alarm` command, and in write
mode write orca_relay_reserve_watch_alarm with gcloud logging write. A new log-based metric and
alert policy page on any such line, per cell, through relay_alert_notification_channels, and the
deploy identity gets roles/logging.logWriter in production to write it.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): name the log-writer risk, check the alarm's project, and say a failed login cannot page

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* docs(relay): note a force-cancel can skip the reserve-watch flip-back write
2026-10-14 20:42:27 -04:00
Neil 39815a086f Reduce CI import, frame-capture, teardown, and shard costs (#28879)
* Avoid unused module imports and trim cosmetic test coverage

* Capture hidden popup frames and attach CDP before guest creation

* Overlap relay test projects within the existing worker budget

* Consolidate native preparation and add an opt-in transform cache comparison

* Release inherited debugger sockets during final E2E teardown

* Refresh unit scheduling weights from all five qualified ARM shards

* Avoid retaining unasserted RPC call history in the delivery model

* Use the supported unconditional background wait syntax

* Overlap unconditional output upload with native preparation

* Verify relay integration stays enabled for ordinary unit callers
2026-10-14 17:10:55 -07:00
Neil 6607c54984 perf(ci): cut redundant tests and reuse expensive setup (#28669)
* perf(ci): reduce test overhead and reuse expensive build work

* test(ci): keep worker startup outside failure-case deadlines

* test: isolate push fixture advisory locks

* test(ci): preserve fixed drain retry allowance

* Align CI contracts with current triggers and guide content

* Stabilize consolidated reveal scenarios and retain behavioral CI contracts

* Fix upstream lint blockers while retaining behavior and assertions

* Classify global Jira preferences in the WSL migration inventory

* Align Jira test fixtures with startup and build safety checks

* Include package-test routing dependency in CI checkouts and fixtures

* Keep native stream classes intact in transcript dependency tests
2026-10-14 16:14:27 -07:00
Jinwoo Hong d09605ee8e ci: run the Electron browser probes serially, out of the unit shards (#28799)
The three popup compatibility probes ran in the parallel unit shards and
timed out at their 14s in-process deadline when other files starved the
runner. Move them into the package job's serial Electron lifecycle step,
and stop the six probes already in that step from also running in the
shards.

Claude-Session: p2-headless
2026-10-14 15:35:39 -04:00
Jinwoo Hong 68b186e2aa fix(relay): end the restart-safe quiet at the drain's pace window, not a window after it (#28677)
* fix(relay): end the restart-safe quiet at the drain's pace window, not a window after it

A paced drain tells its last host when the window ends, so the restart-safe
wait has to see an empty runtime reaching past that point. It counted a full
window of quiet samples from the first empty one instead, and the runtime only
empties as the window ends, so every same-cap cell waited the window twice:
about 25 minutes per Asia cell at 1200 s on 2026-10-13, and 5 minutes per
300 s cell.

The window is now measured from the verifier's own start, which runs after the
cell accepted the drain and so ends no earlier than the cell's own window. A
pass still needs two consecutive restart-safe samples, the last taken at or
after that end, and any non-zero runtime counter or open migration still
restarts the count. Progress lines report paceWindowRemainingMs in place of the
old sample target.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* fix(relay): drop the same-cap drain timeout's double-wait allowance and stale quiet wording

The drain step's timeout added the window's excess over 300000 only because
restart-safe used to wait a second window after the cell emptied, so it is
now the 15-minute migration lease plus the window for every pace (35 minutes
at 1200000, unchanged at 300000 and below). Docs, workflow comments and the
shadow gate's settledAfterSeconds note now describe the window-end rule. The
always-quiet test starts its clock late, so a window measured from zero
fails it, and the overlapping late-control test is folded away.

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* docs(relay): state the restart-safe window-end rule exactly

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f

* docs(relay): the slowest restart-safe wait is now 35 minutes

Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f
2026-10-14 15:35:31 -04:00
Neil 9c0420fa9a fix(browser): agent clicks keep the user's keyboard focus (#28454)
Any press on a <webview> guest moves Chromium focus into it, so an agent click
took the user's terminal focus (#20595). Main now asks the hosting pane to guard
focus for each agent press (Orca's click/mouse down and the CDP proxy) and the
pane puts it back inside the webview's focus event, and again when the press
ends. Offscreen pages keep their native focus.
2026-10-14 02:40:07 -07:00
Neil c1597c3168 ci: remove recurring homepage repository checks (#28682) 2026-10-14 02:08:46 -07:00
Neil 092d6bdc76 ci: check README links and workflow configuration on main (#28678) 2026-10-14 01:52:33 -07:00
Neil f31aada5b4 Require a unique full PR gate before merging (#28632)
* Require exact-commit PR checks before agent merges

* Give full PR checks a unique required status context

* Validate required context ownership on every pull request
2026-10-14 01:25:28 -07:00
Neil f894e257e9 ci: stop daily and post-merge headless-server checks (#28664) 2026-10-14 01:13:59 -07:00
Neil b83a6f74be Finish agent-selection cleanup and restore merge validation (#28607)
* Retire newly translated None option labels

* Reserve required verify check for the complete PR gate

* Preserve headless default tab styling and stabilize import ordering test

* Restore the option mutation clock after crossing fixes

* Check both supported workflow extensions
2026-10-13 23:57:33 -07:00
Jinjing 126009d4c5 Skip Windows security addon steps when the builder is missing (#28546)
Older requested refs predate the security addon builder, so the desktop
template CI job failed trying to run a script that isn't there. Gate the
build, keep, and collect steps on that script existing in the checkout.
Add a test that pins the gating for both cases.

Also drop an unused getAppEnvironment import in orcad-entry.ts.
2026-10-13 21:48:47 -07:00
Neil 13a75d3939 [CI] Reduce redundant test work and import existing helpers directly (#28365)
* [CI] Reduce redundant test work and import existing helpers directly

* [CI] Preserve provider guards and pin the mobile browser test core

* [CI] Fix CI test setup and verify retained contracts

* [CI] Compare native media chunks without boxed byte arrays

* [CI] Detect shifted native media reads with position-sensitive bytes
2026-10-13 20:25:00 -07:00
Neil abe2e8118d perf(ci): reuse import analysis and run localization coverage with Bun (#28463)
* perf(ci): share import analysis and run localization coverage with Bun

* test(ci): recognize the shared import guard in parallelism contracts
2026-10-13 20:17:23 -07:00
Neil 3f81541188 Support restarting Windows Orca as administrator (#28073)
* feat(windows): add administrator PowerShell terminal setting

* fix(settings): localize administrator terminal search keywords

* fix(windows): restrict elevated terminal host to Orca's runtime and requesting account

* fix(windows): harden administrator terminal routing, inventory and restore

* fix(build): build the Windows agent hook when electron-builder packages Windows

* fix(build): build the packaged Windows agent hook for the target architecture

* fix(windows): accept the development terminal runtime and test real elevation in CI

* feat(windows): show administrator status and restart instructions

* fix(settings): localize administrator mode search keywords

* test(settings): isolate the desktop permission section in shell picker tests

* Add Windows administrator restart and simplify Terminal settings

* Keep administrator restart silent and settings actions within narrow panes

* Keep Windows shell choices and permission guides usable at minimum width

* Prevent later restarts from replaying administrator terminal reset

* Fix CI route resolution and transient Windows fixture cleanup
2026-10-13 18:06:05 -07:00
Neil c827ed5324 ci: run full native mobile typechecks without compiler caches (#28409) 2026-10-13 14:27:14 -07:00
Jinwoo Hong 6cfe678dbe ci(relay): use the runner's preinstalled gcloud instead of downloading the SDK (#28392)
* ci(relay): use the runner's preinstalled gcloud instead of downloading the SDK

dl.google.com downloads of google-cloud-sdk time out on the runners, failing every relay
operation at setup-gcloud. skip_install keeps the WIF cred-file login.

* ci: drop the temporary gcloud skip_install proof workflow
2026-10-13 16:24:53 -04:00
Neil b3ad227650 ci: use Bun 1.4.3 across all workflows (#28384)
* ci: share Bun 1.4.3 across current toolchain workflows

* ci: use shared Bun pin for server migration tests too
2026-10-13 13:03:19 -07:00
Neil fc7dff8ce1 ci(wsl): real WSL2 lane for WSL via orcad (C10) (#27921)
* ci(wsl): real WSL2 lane for orcad deploy, bridge, liveness and holder (WSL-via-orcad C10)

* ci(wsl): upload the template's dot-directories; save the rootfs cache on failure

* ci(wsl): update the runner's WSL to 3.0.1+ before asserting WSLInterop survives

* test(wsl): let a released holder settle before asserting it exited

* ci(wsl): install a pinned, cached WSL 3.0.1 MSI instead of wsl --update

* test(wsl): keep the lane's survival seconds typed in its assertion message
2026-10-13 07:09:02 -07:00
OrcaWinandm4air 38a4958cad feat(linux): run the AppImage terminal daemon off the mount, on the extracted copy's bundled Node (#28214)
On a validated AppImage the desktop's daemon launch first runs <generation>/resources/node-runtime/bin/node
on the generation's daemon-entry.js when the extracted copy for the file at $APPIMAGE is complete, carries
the bundled Node and the entry, and is the running release. It then falls back to the AppImage's own mount
and the current mount, logging every fallback. The copy rung records $APPIMAGE/<entry> like the own-mount
rung, so either daemon is adopted. Generation leases recognize the bundled Node. macOS, Windows, deb and
rpm never reach it.

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-13 04:34:25 -07:00
Neil fba7b753ec Cache mobile typechecks and overlap CI setup (#28192)
* ci: run mobile runtime test groups on separate runners

* ci: use a lightweight runner for the mobile result gate

* ci: distribute Bun tests across both mobile runners

* ci: reuse mobile compiler state without adding runner queues

* test: verify eviction of source variants in one cache bucket

* test: baseline existing serializer limits in the capacity capture
2026-10-13 03:35:51 -07:00
Neil 3a7ec7dff1 perf: reuse verified Node and run the layout check with Bun (#28218)
* perf: overlap preflight daemon smoke and reuse verified Node executable

* Keep measured Node reuse and Bun layout wins; drop smoke scheduling experiments
2026-10-13 02:58:56 -07:00
OrcaWinandOrca Worker 4559c250b3 fix(windows): restrict secure files in-process instead of spawning icacls (#28135)
* fix(windows): restrict secure files in-process instead of spawning icacls

Every secure write on Windows spawned icacls.exe five times on the calling
thread (the Electron main thread for most stores): verify, /reset,
/inheritance:r /grant:r, verify, verify. Process creation under endpoint
security costs ~0.6 s each, so each write froze the UI for seconds (#26027).
The /reset + /inheritance:r pair is also what behavioural EDR scores as
ransomware tradecraft (#25583).

Add native/windows-security (@orca/windows-security): a small N-API addon
over advapi32 that reads and writes DACLs in-process. It exposes the token
user SID, a DACL read as SDDL, a DACL write (sync and on the thread pool),
a create-with-DACL file write, and an additive grant. Policy and
verification stay in TypeScript.

- writeSecureFile creates each temp file already carrying the protected
  user/SYSTEM/Administrators DACL (CreateFileW + SECURITY_ATTRIBUTES), reads
  it back once, and renames it. No ACL is modified on the write path and no
  process starts. Before: 5-7 spawns, 94-138 ms per write. After: 0 spawns,
  ~0.5 ms.
- A secure directory is verified once per process. It is rewritten only
  when verification fails, on the thread pool, because the write walks
  every descendant (~2 s on a 29k-entry userData).
- The verifier follows OpenSSH's rule: only the user, SYSTEM and
  Administrators may be granted anything, and the path must be protected
  (or, for a file, inherit from a secure parent). DACLs written by the
  icacls-era hardening pass unchanged, so there is no migration.
- If the addon cannot load, the write still happens and reports
  unrestricted, logged once. There is no icacls or PowerShell fallback.
- grantDirAcl, the startup userData grant, and the install-dir probe and
  package repair use the same addon, so whoami.exe and icacls.exe are no
  longer spawned anywhere at runtime.
- orcad's Windows slots ship the addon beside their bundles (built per
  arch, PE-machine checked). Its data-root privacy check fails closed, so
  without the addon a Windows orcad would not start.

Fixes #26027
Fixes #25583

* refactor(secure-file): harden existing Windows files through the sync lane

The read path called restrictWindowsPathSync directly and recorded success
itself; the existing sync lane already does both. One call site fewer.

* refactor(windows-security): one load, one rule, no fallthrough (review #28135)

- The descriptor parser is anchored, so a conditional ACE (its body holds
  parentheses) or anything else it cannot read fails closed instead of
  verifying the ACEs before it.
- findSecureDaclViolation is one flat rule: protected, allow rules only for
  the user, SYSTEM and Administrators (OpenSSH's ACE rule), and full
  control for the user. The inherits-from-a-secure-parent exception is gone:
  it accepted exactly what loosening the parent reaches.
- The addon and the user SID load once, behind one wrapper that adds the
  extended-length prefix to every path and runs async DACL writes to one
  path in order, so a startup grant cannot land after a re-lock and restore
  the broader DACL.
- With the addon loaded, a failed born-restricted create throws with the
  matching Node error code instead of falling back to a plain write.
- A grant leaves a NULL DACL untouched instead of turning 'everyone' into
  'only this SID'. WriteFile runs once and fails on a short write.
- The artifact create-intent store writes through writeSecureFile and drops
  its own temp writer, directory LRU and synchronous directory re-lock.
- Test seams the global fake makes redundant are gone.

* fix(packaging): fail a Windows build whose workspace addon binary is missing or foreign-arch

The @orca/windows-* workspace links exist on every host, so their presence
proved nothing: a missing or wrong-architecture .node only surfaced at
runtime, as unrestricted secure files or an orcad that refuses to start.
beforePack now reads each addon's PE machine against the target arch.

* refactor(windows-security): prune what the native ACL left dead (review #28135)

- Sync-spawn ratchet: hardenSecurePath and the sync-flag exemption it alone
  used are gone, and the advice and baseline header no longer say secure
  writers run icacls.
- The install-dir probe reads ACEs through parseSddlDacl, so an ACE it
  cannot parse fails the whole reading closed instead of being skipped; AC
  is a resolved alias there now.
- The packaging check reads the addon binary through node_modules, the copy
  that ships.
- No winCode-to-errno table: nothing reads .code off a failed secure write.
- The load memo is not mutated; secure-path keeps its own log-once flag.
- grantInstallDirPackageAcl is a plain function, and secure-file restricts
  a Windows directory and a POSIX path directly instead of through one
  two-platform helper.

* fix(windows-security): resolve LA in real-ACL tests; tear a file whose close fails

- The GitHub Windows runner is the built-in Administrator (RID 500), whose
  SID reads back as LA. Production already resolves it through
  localDomainSidOf; the win32 test helpers parsed without it. One shared
  readDaclOf resolves aliases for every real-ACL suite, and the verifier's
  unit test covers an LA-aliased directory and LA as a stranger.
- createFileWithDacl treats a failed CloseHandle like a failed write (lost
  data on a network volume) and deletes the torn file.
- Drop a duplicate test reset and a typo.

* fix(windows-security): load only from inside the app; read DACLs as structure (review #28135)

- Loader: orcad's staged binary first, then the workspace package only if
  its real path is inside the app (process.resourcesPath, or this
  checkout). A bare specifier otherwise walks every parent up to
  C:\node_modules, which any local user may create, and orcad holds
  credentials.
- The addon reads a DACL as { protected, aces: [{ type, flags, mask, sid }] }
  via GetAce, with a SID only for plain allow/deny ACEs and null for a NULL
  DACL. findSecureDaclViolation and the install-dir probe check that
  structure, so conditional, callback and object ACEs fail closed. The SDDL
  parser, its alias table, LA/localDomainSid resolution and getDaclSddl are
  gone; SDDL is only the write format.
- Install-dir tree pass: files flagless as before, but a directory only when
  its own DACL is protected (inheritably). Re-granting inheriting
  directories re-propagated each subtree (3.2 s for 1,093 directories). The
  root is no longer granted, or counted, twice. The two passes are plain
  functions behind a { root, tree } seam.
- One namespacing (path.win32.toNamespacedPath) at the addon boundary, also
  the per-path ordering key; the comment states the real sync-grant
  invariant.
- restrictWindowsDirectory(dir): Promise<boolean> replaces the callback
  lane; the reporter is guarded once, so the 'settle' stage is gone.
- Sync-spawn ratchet: the secure writers no longer spawn or block, so they
  leave the gate (109 -> 61 uses).
- Errors name the Win32 call and code only; a failed
  GetSecurityDescriptorControl fails the write instead of defaulting to
  unprotected.

* test(secure-file): assert the durable POSIX write's outcome, not the old chmod order

writeSecureFile now chmods the temp file to 0o600 before fsyncing it, so
the fsync also covers the mode change and the file is owner-writable when
opened. Assert what matters under umask 0o200: the write succeeds, the
temp file is fsynced read-only, and the published file is exactly 0o600.

* fix(windows-security): namespace paths with the host's toNamespacedPath

path.win32.toNamespacedPath rewrote POSIX temp paths (/tmp/x -> \tmp\x) in the
Linux runs of the Windows-logic unit tests, so the fake addon wrote to the wrong
place. The addon only runs on Windows, where the host function is the win32 one;
elsewhere it is the identity. The long-path assertion runs on Windows only.

* test(windows-security): assert the install-dir long-path prefix on Windows only

The prefix comes from the host's toNamespacedPath, which is the identity off
Windows; the Linux run of this suite expected the win32 escape.

* fix(windows-user-data-acl): grant the root even when its children cannot be listed (review #28135)

A readdir failure on userData aborted the whole grant, so the root grant
that could repair a DACL refusing the listing was never attempted. The
listing failure is now recorded, the root is still granted, and the
marker stays withheld so the next launch retries.

* test(windows-security): wait for relock propagation, accept Entra ID SIDs (review #28135)

- The large-relock test waited only for the parent to turn protected,
  which happens before propagation reaches the children, then read a child.
  It now waits on the parent and both ends of the child range.
- The token-user test rejected S-1-12-1 (Entra ID) accounts, failing the
  suite on Entra-joined dev machines.

---------

Co-authored-by: Orca Worker <orca-worker@localhost>
2026-10-13 01:56:55 -07:00
OrcaWinandm4air 49a1129aa6 build(linux): carry the pinned Node inside the AppImage so serve's slot works offline (#28204)
* build(linux): carry the pinned Node inside the AppImage so orca serve's slot works offline

Linux packaging places the pinned glibc Node at resources/node-runtime/bin/node before the
glibc-floor gate, proves it serves one PTY with the packaged daemon entry, and deb/rpm exclude
it through fpm. orca serve's orcad slot copies that Node (hash-checked) instead of downloading.

* test(serve): type the bundled Node fixture

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-13 00:39:39 -07:00
Neil b5c901747b [CI] Reuse test compilation and reduce avoidable test waits (#28169)
* [CI] Reuse test compilation and reduce avoidable test waits

* [CI] Keep startup observation polls from advancing deadlines
2026-10-12 22:19:00 -07:00
Jinwoo Hong 5a77f068b6 Link the Android APK through a stable URL and bump it on release (#28143)
* Link the Android APK through a stable URL and bump it on release

READMEs, the docs site, and the desktop app now link
https://www.onorca.dev/download/android, which the marketing site
redirects to the current APK, so none of them need a per-release edit.
A published Android release opens a PR on the marketing site that bumps
its one ANDROID_RELEASE_VERSION constant; a failed bump is reported in
the job summary and does not fail the release.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* Skip the APK download bump for OTA-shell test builds

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* Bump the APK download for every published Android release

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-10-12 23:00:27 -04:00
Neil c56e6718b4 ci(windows): build the agent hook before packaging in installer e2e workflows (#28137) 2026-10-12 19:04:02 -07:00
Neil ebfae6a5e9 Speed up Mobile Checks by overlapping typechecks and tests (#28127)
Overlap mobile typechecks with tests and avoid repeated filesystem probes and unused device substitutes in recording replays. Keep state isolated between loaders, give exhaustive snapshot cases an explicit execution deadline, and retire obsolete locale keys that blocked repository validation.
2026-10-12 18:11:57 -07:00
Neil c8996d76a5 Speed up daemon smoke checks and keep compiler slots busy (#28106)
* Remove repeated daemon smoke builds and idle compiler slots

* Test compiler scheduling through its process interface

* Keep preview component below the existing line limit with a type-only split
2026-10-12 17:20:19 -07:00
Neil 845b723258 [CI] Warm only Linux for compiler recipe changes (#28081)
* [CI] Warm only Linux for compiler recipe changes

* [CI] Follow cache warmer callers in policy contracts
2026-10-12 16:34:58 -07:00
OrcaWinandOrca Worker 7909732283 perf(claude): run the Windows Claude hook as a signed native claude-hook.exe (#27719)
* perf(claude): run the Windows Claude hook as a native orca-hook.exe

Each Claude hook event on Windows started cmd.exe (plus the user's cmd
AutoRun) and then curl.exe on top of the agent's own shell. A small signed
Rust binary now does the same synchronous POST in one process, under Git
Bash, PowerShell and direct CreateProcess alike.

- native/windows-agent-hook: std-only crate, same contract as the .cmd hook
  (prints {}, env guards before stdin, endpoint re-read, 0.5s/1.5s caps,
  waits for the listener reply, always exit 0). Its installed file name
  selects the agent profile, so the registration stays a bare path.
- Packaged as resources/bin/orca-hook.exe (SignPath signs it with the other
  inner PEs); afterPack fails on a missing or wrong-machine binary.
- Claude install copies it verbatim to ~/.orca/agent-hooks/claude-hook.exe
  (temp + rename, running copy moved aside) and registers it; it falls back
  to the .cmd hook when the bundle is missing, unsigned or cannot be copied.
  Only Claude opts in, via windowsNativeHookExecutable.
- The managed-command matcher sweeps .exe entries alongside .cmd/.ps1/.sh.
- The CLI launcher and hook builds share windows-rust-executable-build.mjs,
  which now also checks the PE machine.

Refs #20913

* fix(build): publish native Windows binaries atomically so parallel builds cannot collide

The Windows CI lane runs every orca-hook.exe suite in one vitest
invocation, each building through build-windows-agent-hook.mjs. The build
copied straight onto native/windows-agent-hook/.build/orca-hook.exe, and
CopyFile opens its destination unshared, so a second build (or a scanner
still holding the fresh image) failed with EBUSY.

- windows-rust-executable-build.mjs skips the publish when the output
  already has the built bytes, otherwise stages a per-process temp file and
  renames it over the output, retrying briefly while another handle holds it.
- The test fixture hands each suite a private copy of the build output, so
  no suite reads or starts the shared file while another build replaces it.

Refs #20913

* refactor(claude): simplify the native Windows hook to one Claude binary

Review follow-up on the native hook (#20913): the same behaviour with less
machinery.

- The binary is built, shipped and installed as claude-hook.exe, so the
  installed name matches its PE OriginalFilename. It is Claude-only, so the
  file-name profile table and the per-agent opt-in flag are gone; the
  registration is gated by the existing Claude-only usesWindowsEntry check.
- Install and refresh reuse writeManagedScript / refreshManagedScriptIfPresent
  (now accepting bytes): temp file, rename, skip when identical. A copy whose
  hook is still running cannot be replaced; install then registers the .cmd
  hook and refresh keeps the old build until a later start, instead of the
  rename-aside and *.retired sweep.
- The install-time Authenticode-presence check is dropped. Release CI now
  requires resources\bin\claude-hook.exe to be signed (release-cut and the
  signing rehearsal), next to resources\bin\orca.exe.
- getStatus counts either Windows form through findManagedEventHookForms in
  hook-settings.ts, replacing managed-event-hook-status.ts.
- Rust: plain Claude constants, one endpoint-file lookup (last assignment
  wins, as under `call`), one request write and a deadline-bounded drain.
  Guard order and form encoding are unchanged.
- Build: PR CI builds the hook once before the Windows tests, so the suites
  only read it; the launcher publish goes back to copyFileSync and the retry
  loop and its test are gone. The PE-machine check lives only in afterPack.
  Cargo.lock joins both crates' build fingerprints, and the launcher's alias
  exports are removed.

Refs #20913

* test(ci): run the managed hook stdin-lifecycle suite in the Windows lane

Its claude-hook.exe case (and the other Windows-only cases) are gated per
test, so the lane-registration guard did not require the file and it ran
only on Linux, where those cases skip. Register it in both Windows lists.

Refs #20913

* feat(agent-hooks): ship the native hook as orca-agent-hook.exe <agent>

The native Windows hook (#20913) becomes one general binary that stays
Claude-only in behaviour.

- Named orca-agent-hook.exe everywhere: build output, PE OriginalFilename
  and InternalName, resources\bin, the afterPack check, the installed copy
  in ~/.orca/agent-hooks, and the release signed-target lists.
- It takes the agent as its first argument and Claude registers
  `<path> claude`. The CLAUDE_JOB_DIR exit and the Grok/Devin drain belong
  to that agent; an unknown or missing agent answers {} without reading
  stdin. The managed-command matcher owns `orca-agent-hook.exe claude` and
  still owns the old claude-hook.exe entry, so installs migrate; install
  deletes a leftover claude-hook.exe when nothing runs it.
- A copy still running from the previous build no longer forces the .cmd
  fallback. Identical bytes are not rewritten; otherwise the temp file is
  renamed over the target, and when Windows refuses because the image is
  mapped, the running copy is renamed to orca-agent-hook.exe.old first.
  Install deletes a stale .old; only when that copy is also still running
  does the install fall back to .cmd. writeManagedScript and
  refreshManagedScriptIfPresent take the aside path, sharing one helper.

Refs #20913

* fix(agent-hooks): roll back a failed aside rename and keep an installed hook exe

Design-review follow-up on the native hook (#20913).

- If the running copy was moved to orca-agent-hook.exe.old but the new
  file then could not take its name, the copy is renamed back before the
  error is rethrown, so settings.json never points at a missing exe. The
  rename helper now owns the `.old` name, next to the comment that
  explains it; a mocked-fs unit test covers both the sync and async paths.
- A failed write, or a build with no bundled exe, keeps registering a copy
  that is already installed (every build knows `claude`), so a dev checkout
  and an installed build no longer flip settings.json between forms.
- The claude-hook.exe migration is gone: no release ever shipped that
  name, so the legacy sweep, the `.exe` matcher needle and their test are
  removed.

Refs #20913

* fix(agent-hooks): derive the native hook's agent with win32 path rules

getWindowsNativeHookAgent took the host's basename of the managed script
path, so on a POSIX host (Linux CI) a Windows path was not split and the
registered command became `orca-agent-hook.exe C:\...\claude`. It now takes
the path and applies path.win32.basename itself. The install destination
and the `.old` aside stay on host paths: they name files the host opens.

Refs #20913

---------

Co-authored-by: Orca Worker <orca-worker@localhost>
2026-10-12 16:09:30 -07:00
OrcaWinandOrca Worker 424437a206 fix(claude): register the Windows statusLine so usage updates without Git Bash (#27710)
* fix(claude): register a statusLine Windows PowerShell can run

Claude runs the statusLine under Git Bash when present and otherwise under
PowerShell, which cannot parse the POSIX runtime-home command Orca registered.
On Windows without Git Bash the statusline never posted rate_limits, so the
live Claude usage feed went dark. Register the same shell-neutral .cmd
invocation the lifecycle hooks use; the old form is still recognised as
Orca-owned and replaced, and a user's own statusLine is never touched.

* fix(claude): keep the statusLine free of a per-tick PowerShell launch

A profile path the bare form cannot carry (a space, non-ASCII, quotes) fell
back to the encoded PowerShell launcher, which would start a hidden PowerShell
on every statusline tick and print "{}" if the script went missing. Register
the bare .cmd path only when it is cmd-safe and keep the runtime-home form
otherwise, so Git Bash users on such paths still run the .cmd directly.
Fold the registration tests into claude-status-line-install.test.ts and add a
real Git Bash run for a profile path with a space.

* test(claude): pair the stubbed win32 platform with win32 basename

The Windows statusLine case fed a Windows path to getManagedCommand on a
Linux runner, whose posix basename does not split backslashes. Production
only takes that branch on real Windows, where node:path is path.win32.

---------

Co-authored-by: Orca Worker <orca-worker@localhost>
2026-10-12 14:51:28 -07:00
OrcaWinandOrca Worker 1df7d7dd7a fix(daemon): verify daemon PID identity from the native Windows process table, not PowerShell (#27703)
* fix(daemon): verify daemon PID identity from the native Windows process table

The Windows daemon PID check forked powershell.exe Get-CimInstance through
node:child_process with no windowsHide, flashing a console and costing
~850ms per check. Read the command line and creation time from the shared
windows-process-table reader instead, and route the POSIX ps queries
through @orca/process-host.

Refs #27390

* refactor(daemon): drop the ps migration and trim the PID identity tests

Keep #27703 to the PowerShell removal: restore the macOS/Linux ps paths,
their tests and the process-host baseline entry. Collapse the mocked
Windows identity cases into one table, keep only the real-addon native
case, and move the creation-time fallback beside queryWindowsProcess.

---------

Co-authored-by: Orca Worker <orca-worker@localhost>
2026-10-12 14:49:08 -07:00
Neil be25938415 [CI] Reduce repeated test imports, waits and unrelated jobs (#28069)
Preserve all 52 Markdown cases while consolidating one file and importing existing direct owners. Advance editor debounce windows through public selective timers, exclude unrelated lint tests from Performance PR triggers, and remove a pane assertion case entailed by retained coverage.
2026-10-12 14:22:19 -07:00
Neil e02e2bb1d9 [CI] Advance transcript clocks and skip test-only computer checks (#28060) 2026-10-12 13:46:53 -07:00
Brennan Benson fa4ef2b961 ci: stop new main-process code from freezing Orca with synchronous launches (#27756)
* ci: ratchet synchronous child-process launches in main and shared code

The Electron main process freezes every window while a synchronous child
process runs, and on Windows each launch is slow because antivirus scans
it. Nothing stopped new synchronous launches from landing in main-process
code, and the spawn observer behind ORCA_MAIN_THREAD_DIAGNOSTICS only saw
async spawns, so the worst blocks were invisible to it.

Ratchet: check:main-sync-spawn-ratchet parses production src/main and
src/shared files (test-only paths excluded) and fails when a file not in
config/main-sync-spawn-baseline.txt reaches execFileSync, spawnSync or
execSync from child_process, runProcessSync from @orca/process-host, or
one of the synchronous secure-file writers (they run icacls on Windows).
Named, aliased, namespace, default, require() and import() forms count.
A baseline entry whose file no longer launches synchronously fails as
stale, so the list can only shrink. It runs in lint and in the PR
static-analysis job next to the process-host import check.

Observer: runProcessSync now reports to the spawn observer, timing the
whole call (that is how long the thread was stuck) and reporting from a
finally so failing, throwing and timed-out children are counted too.

Runtime behavior is unchanged except that the diagnostics observer now
sees synchronous launches; with no observer registered the sync path
pays one extra branch.

* ci: count sync launches per file and gate the repo's sync wrappers

The sync-spawn ratchet only tracked which files launched synchronously, so a
file already on the list (wsl.ts, win32-utils.ts, git-exec-file.ts) could add
more launches unnoticed, and new callers of the repo's own synchronous
wrappers passed CI.

The baseline now stores a per-file count of uses, in the same format as the
focus-read ratchet: a rise or a new file fails, a lower or zero count fails
until `pnpm check:main-sync-spawn-ratchet --prune` lowers it, and --prune
never raises an entry. Every value reference to a gated function counts
(call, hand-off, alias and its uses, re-export); imports alone count nothing.

Gated wrappers: gitExecFileSync (and the git/runner re-export),
restrictWindowsPathSync, hardenSecurePath when called with sync: true,
grantDirAcl, getWslHome, listWslDistros, wslUncDirectoryExists,
isWslAvailable (and the wsl.ts re-export), isPwshAvailable. Their 18
current caller files join the baseline.

Failure messages now give advice per family: the async process-host API for
child-process launches, the async twin for wrappers, and for the secure
writers a note that no async secure writer exists yet, so the sync one stays
and must never become a plain write (that drops the Windows ACL hardening).

Also refresh two stale blockMs descriptions and drop a no-op line from the
spawn-observer "stays silent" test.

* ci: count hardenSecurePath unless visibly async, and tighten ratchet tests

hardenSecurePath was counted only when `{ sync: true }` was written inline,
so moving the options into a variable or a spread dropped the count and the
check then asked for a prune. It now counts every use except a call with no
options or a spread-free object literal whose `sync` is absent or false.

Also: resolve backslash paths in the importing file as well as the
specifier; still visit heritage and instantiation expressions
(`extends mixin(fn)`, `fn<T>`); state the reference-only counting limit;
and point the secure-writer advice at raising this file's baseline count.

Tests cover the hardenSecurePath shapes, the alias chains that need the
fixpoint loop, and a prune that must not raise a grown row.

* test(ci): pin the main sync-spawn ratchet background step

* fix: count secure hardening calls with spread arguments
2026-10-12 11:28:25 -07:00
OrcaWinandm4air 4ad0129984 refactor(orcad): orcad artifacts live under orcad/ and node-runtime/ (#27963)
Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-12 04:16:28 -07:00
OrcaWinandm4air 7c13365f03 refactor(build): relay survivors named for what they are (#27962)
Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-12 04:09:18 -07:00
OrcaWinandm4air 822bd9f972 fix(ssh): keep 1.4.224's running terminals working after the upgrade (legacy relay bridge) (#27787)
* wip: legacy relay bridge client

* Revert "fix(ssh): end an older Orca's relay terminals from the connect refusal (STA-10166) (#27748)"

This reverts commit d4a55f583e.

* feat(ssh): bridge terminals an older relay still runs into their old tabs

* test(ssh): cover the legacy relay bridge client, target bridge, transport and gate

* test(ssh): type the bridge test fakes without assertions

* test(e2e): a 1.4.224 relay's live terminal survives the upgrade on a Docker SSH host

* ci(e2e): route legacy relay bridge changes to its Docker spec

* fix(ssh): bridge delta moves too, and never read a non-exhaustive relay listing as exit

* fix(ssh): keep retrying a bridge for panes that mount before connect; end panes proven gone

* fix(ssh): bridge 1.4.224's detached leases through export preflight; publish every leased pane before the first look

* test(e2e): build the released relay out of process so Playwright never loads ESM build tooling

* fix(renderer): drop the recovery hook main removed from PtyTransport

* test(e2e): show the converted worktree before reading the bridged tab

* test(e2e): reconnect and wait for the converted host before opening the bridged tab

* fix(renderer): match bridged leases to tabs mirrored from the managed server

* test(e2e): find the mirrored relay-era tab under any worktree key

* test(e2e): open the converted worktree before looking for its mirrored tab

* test(e2e): a retired lease is terminated or pruned

* fix(ssh): back off attaches a listed terminal refuses; one pane per bridged shell

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-12 02:24:12 -07:00
Neil 1d571e67ec Speed up import checks, localization extraction, and CI typechecking (#27727)
* Skip unnecessary compatibility-boundary parsing and benchmark it in CI

* Use pinned preflight Bun for compatibility checks and localization extraction

* Measure two concurrent Bun projects in full preflight and update its scope test
2026-10-11 20:43:34 -07:00
983532a5cb fix(bench): make bench:idle-cpu build, sample CPU and parse the process table on Windows (#27622)
* fix(bench): make the idle-CPU benchmark run and report CPU on Windows

Three independent Windows defects in the idle-CPU benchmark (its report is
what Windows performance work would cite):

- `buildAppIfNeeded` spawned `npx`, which is `npx.cmd` on Windows, so
  execFileSync failed with ENOENT before any sampling. Build through
  `config/scripts/run-electron-vite-build.mjs` instead — the repo's own
  Windows-safe wrapper, which also runs the boot-graph verification.
- `readWindowsProcesses` returned `cpuTimeSeconds: null`, so the sampler's
  per-process CPU delta (idle-cpu-process-sampling.mjs:198) fell back to
  the hardcoded `percentCpu: 0` and every CPU KPI in the report was zero.
  Read `UserModeTime`/`KernelModeTime` from `Win32_Process` (100 ns units)
  and keep `null` when a host does not return them.

Measured on Windows, same options before and after the CPU fix:

| class | mean CPU before | mean CPU after |
| --- | ---: | ---: |
| renderer | 0 | 2.20 |
| gpu | 0 | 2.08 |
| utility | 0 | 0.11 |
| agent-or-node | 0 | 0.06 |
| total | 0 | 4.51 (p95 8.40) |

RSS is unchanged (934 MiB -> 937 MiB), so only the CPU reporting changed.

Refs #25235

* fix(bench): reject a missing Win32_Process time instead of reading it as zero

Review follow-up (CodeRabbit). `Number(null)` is `0`, which is also a real
counter value, so an absent `UserModeTime`/`KernelModeTime` passed the
finite check and the sampler would report a kernel-only delta instead of
taking the fallback. Require an actual number from the CIM payload.

Verified on Windows that the real payload still reports CPU once the check
is tight: total mean 4.40%, p95 14.48%, renderer 3.51%.

* test(bench): cover the Windows CIM CPU time mapping

The mapping was only reachable through Get-CimInstance, so no test ran it.
Lift it into cimCpuSeconds and assert the 100ns-to-seconds sum plus the
missing / string / non-finite cases that must stay null.

* fix(bench): read the Windows process table as UTF-8 so JSON.parse survives

PowerShell writes to a redirected stdout in the OEM code page (437 here),
which best-fits characters such as U+2022 and U+2192 in any running
process's command line to raw 0x07 / 0x1A bytes. ConvertTo-Json had left
them unescaped because they are not control characters in UTF-16, so the
sampler's JSON.parse failed with "Bad control character in string literal"
whenever such a process happened to be alive -- hence intermittent.

Set [Console]::OutputEncoding to UTF-8, as the app's own CIM scan does,
and cover it with a Windows-only test that fails without the fix.

Fixes #25236

* fix(bench): pass the local runtime to fetchRepos in the idle-CPU fixture

fetchRepos now requires a runtimeEnvironmentId, so the fixture's bare call
threw "Cannot read properties of undefined (reading 'runtimeEnvironmentId')"
before sampling on every platform. null selects this app's own catalog, as
the e2e helpers already do.

* ci(bench): run the Windows process-table test in the Windows lane

The UTF-8 regression test self-skips off win32, so it must be registered in
both Windows lists or it runs on no machine.

---------

Co-authored-by: Paolo <paovalletta@hotmail.it>
Co-authored-by: Orca Worker <orca-worker@localhost>
2026-10-11 20:27:15 -07:00
Jinwoo Hong b9d1b958ee feat(relay): promotion path for Asia spare cell c35 (#27733)
Mirrors #25757 (c34): adds the c35 promotion wave, workflow promote and
canary case entries, the c35 canary evidence entry, tests, and docs.
2026-10-11 23:26:19 -04:00
Jinwoo Hong a4da555b4f feat(relay): declare Asia spare cell c35 as migration-only (#27695)
* feat(relay): declare Asia spare cell c35 as migration-only

Adds a seventh asia-east2 cell at the C34 shape (cap 3000, 6000 request
units, pool 16, e2-standard-4) in asia-east2-a, pinned to image F. It gets
its own topology and registration wave but no promotion wave, so the
admission script and workflow refuse to promote it; it stays a
migration-only spare and out of the fleet pool list. Mirrors #25336.

* test(relay): accept the c35 spare under the same-cap capacity allowlist
2026-10-11 22:29:58 -04:00
OrcaWinandOrca Worker 83056e5edb fix(skills): judge official skill updates by ownership, not snapshot matching (#27603)
* fix(skills): judge official skill updates by ownership, not snapshot matching

Orca decided whether an installed official skill was pristine by matching its
bytes against a shipped list of past snapshots, falling back to the skills CLI's
lock hash. Copies installed between releases, CRLF copies, and installs whose lock
recorded a sha256 hash all read "doesn't match the official version" and could not
be updated (#26841), and older content could be labelled newer and never offered.

Now an official skill is Orca's when the skills.sh global lock says it came from
stablyai/orca, and it is current only when its listed files equal the bundled
manifest (line endings and sidecars ignored); anything else is outdated. Install
and update are one pinned `skills add .../tree/v<appVersion>` command, used by the
in-app runner, `orca skills update`, and the Settings rails. The update dialog
preselects Orca-owned rows and lists same-named skills from elsewhere unchecked.

Removes the snapshot registry, release mapping, git-tree identity, lock trust and
convergence logic, and the release-cut provenance step.

* ci(skills): run the pinned-add round trip on a skills CLI that can fetch a commit

* fix(skills): let orca skills update target the shared folder on a bare host

Update became the same pinned add as install, so it inherited install's
refusal when no agent is detected. That broke the bare-host update the old
command guaranteed, and the built-CLI check (update --dry-run, no --agent) on
CI runners. The in-app runner already fell back to universal; both now share
one detectSkillsCliUpdateAgentKeys rule.

Also brings the CLI skills docs off `npx skills update`.

* refactor(skills): derive the update selection from the grouped rows

The selection hook took the live and the retained inventory, re-derived Orca
ownership from installations beside the grouping's own orcaOwned, and kept a
separate display count. Groups already carry both eligibility and ownership,
so the hook reads them once; the button's existing live-scan gate is the only
authorization check. The unselected line is inlined into the headline.

* refactor(skills): drop helpers the ownership design made redundant

- isOwnerManagedSkillScope only negated into the participation check once
  plugin caches stopped participating; fold it in.
- Eligibility re-checked resolvedPath/physicalIdentity, which observation
  already turns into 'inaccessible'.
- The manifest verifier probed access() before a readFile that throws anyway.
- orchestration-install-command.ts only forwarded one constant from the lib
  module its three importers already import.

* i18n(skills): stop naming npx skills update in four skipped-copy strings

The es/ja/ko/zh cantUpdateReason and tipDuplicate strings still named
`npx skills update`, which Orca no longer runs for its skills. Say 'the update
command' instead.

* refactor(skills): inline the single-use pinned source URL helper

orcaSkillsSourceUrl was exported but only called by the builder beside it.

* fix(skills): let an update choice lapse when the install source changes

The selection froze every checked name, Orca's preselection included, the
moment the user ticked anything. A re-check that then attributed a name to
another source kept it checked, replacing it without consent. Store only the
user's explicit choices, keyed by name and lock source, and default to
ownership otherwise.

* fix(skills): label the empty update action 'Update skills', not 'Update 0 skills'

The button stays whenever a row is updatable, so unticking every row (or a
dialog with only non-Orca copies outdated) doesn't reflow the footer. At zero
selected it stays disabled and reads 'Update skills' instead of a zero count.
The label lives beside the dialog's other count copy.

* fix(skills): let keyboard focus open the Not from Orca tooltip

The source of a non-Orca copy is only in the tooltip, and its trigger was a
plain span. Make it focusable like Orca's other tooltip-on-text triggers
(tabIndex 0, cursor-help, the row primitive's focus ring), as in the agreed
mock.

* test(skills): hold the round-trip add equal to the shared install builder

The CI round trip hardcoded its skills add argv, so it could drift from what
Orca runs. A .mjs script can't import the TS builder (extensionless imports),
so the argv moves into an exported roundTripAddArgs and a unit test asserts it
is the builder's unattended add with the tag swapped for the PR commit. The
script runs under the same main guard as the manifest generator.

* i18n(skills): translate the skill row's location count and duplicate reason

es, ko and zh still carried English for SkillUpdateRow.oneLocation and
manyLocations, and es and ko for SkillFreshnessRow.skippedReasonDuplicate.

* fix(skills): pin official skill installs to the build commit and skills CLI

`skills add .../tree/v<version>` only works for stable and RC builds: hourly,
daily and adhoc builds publish elsewhere, so stablyai/orca has no tag for their
version and both install and update failed there. Every build is made from a
commit that exists in stablyai/orca, so the build now stamps `git rev-parse HEAD`
and the one shared builder pins to `tree/<commit>`, which equals the bundled
skills by construction. Main, renderer, web and vitest get it as
ORCA_BUILD_COMMIT (replacing ORCA_APP_VERSION); the tsc-built CLI reads it from
the compiled out/package.json boundary. A missing or malformed commit fails the
build or the command with a clear error instead of building a bad URL.

The builder also pins the skills CLI (`npx skills@1.7.2`): 1.5.x cannot fetch a
commit, and a bare `npx skills` can reuse a stale cache or resolve to another
binary (#18687). The CI round-trip script reuses the same constant.

* ci(skills): run the round trip on the shared pinned skills CLI version

* docs(skills): document the commit-pinned skills add

* refactor(skills): keep the pinned skills CLI version in the install builder

skills-cli-version.ts existed only so the plain-Node round-trip script could
strip-load a .ts file, which needed --disable-warning in CI. The version now
lives beside the builder that uses it; the script keeps a literal, and the
existing round-trip test already asserts its argv, skills@<version> included,
equals the builder's.

* refactor(skills): validate the build commit once, where it is resolved

resolveBuildSourceCommit already refuses anything but a full hash, and both the
define and the CLI's stamped package.json come from it. The builder's second
check could only fire as a module-load throw in the renderer's command
constants. Also drops the resolver's unused cwd parameter.

* test: stamp a fixed build commit instead of running git in the vitest config

Tests only interpolate ORCA_BUILD_COMMIT, so they need not read HEAD (or fail
outside a git checkout) on every run.

* refactor(settings): match only skills add in the Windows npx preflight

Every official skill command is now the builder's skills@<version> add; no
rail emits skills update any more.

* fix(skills): fail an update that leaves a broken skill link behind

Eligibility already skips names with a broken link before the run, so one
afterwards is the run's own damage; report it instead of claiming success.

* ci(release): refuse to tag when the skill manifest is stale

The removed --release step implicitly checked the committed manifest against
skills/. Keep that guarantee with a verify-only run; it writes nothing.

---------

Co-authored-by: Orca Worker <orca-worker@localhost>
2026-10-11 18:30:03 -07:00
OrcaWinandm4air e2b31cbc59 ci(e2e): one managed-SSH lane and routing regex; no relay build in e2e setup (#27640)
Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 18:18:59 -07:00
Jinwoo Hong 8dfa2b1875 test(e2e): mixed-version, two-client and phone layout baselines recorded on main (#27688)
* test(e2e): record mixed-version, two-client and phone layout baselines on main (P3)

* test(e2e): P3 review fixes: reconnect and I/O proof, restart order bounds, Linux-only baselines

* test(e2e): re-record Linux phone and serve-restart baselines

* test(e2e): P3: echo drawn in the desktop pane; bound the group selection after restart

* test(e2e): re-record serve-restart group selection

* test(e2e): name P3 baseline helpers for what they record
2026-10-11 21:18:16 -04:00
Brennan Benson c9097ac991 test: run a structured chat against the packaged headless server (#26527)
* fix: preserve module URLs in the headless server bundle

* test: exercise structured chat on the packaged Node server

* test(orcad): run the packaged chat check from the desktop's server template

The chat check loaded the server bundle directly, so a desktop build missing its
server template still passed. Materialize the slot from the built template the way
the desktop does, and replace the fixed streaming pause with a test-owned
acknowledgement so the partial-reply assertion can't race.

* Connect the packaged chat test through a paired connection

Main now sends remote runtime requests over a paired connection rather than a
pairing offer, so the test converts the server's offer once when it is ready.
2026-10-11 17:46:15 -07:00