* perf(windows): read the process table natively instead of forking PowerShell
Seven independent readers each forked powershell.exe to run
Get-CimInstance Win32_Process, with a wmic fallback that Windows 11 24H2
has removed. On a domain-joined host with PowerShell Transcription
enabled by policy, one of them running every ~2s recorded ~289GB across
1.4 million files (#15209). The same scan cost ~700ms and ran per pane
(#15036), and a Group Policy or AV block turned it into 'unavailable',
which callers read as 'no evidence' -- which is how a PTY tree survives
its own teardown (#9045, #10475).
A Toolhelp32 snapshot answers the same question with no child process.
Measured on Windows 11 with 1050 processes, p50/p95:
pid+ppid+name 15.9 / 17.5 ms
+memory +command line 30.6 / 33.7 ms
Get-CimInstance 706 / 723 ms
Two upstream defects needed patching, both found by running it on real
hardware. The binding requires Spectre-mitigated libraries our agents do
not carry (node-pty is patched the same way). And enumeration stopped
after 1024 processes: on a host with 1051 the module returned exactly
1024, and the querying process was itself among the 27 missing -- a
truncated snapshot silently hides the descendants teardown is looking
for, which is the failure this whole change exists to remove.
Migrated: the foreground/descendant reader (the #15209 scraper and the
teardown identity gate) and the port scanner's PID attribution. NOT
migrated: the memory collector and three identity probes, which need
Win32_Process.CreationDate and have no native equivalent. Start time is
a proxy for identity anyway; an inherited job handle is the real answer,
so those belong with the job-object work rather than here.
Packaging follows the windows-native-registry contract exactly:
optional, absent from onlyBuiltDependencies so macOS/Linux never run
node-gyp, win32-only in the packaged runtime. Asserted by the existing
contract test, which also stops pinning a whole source literal that only
tested its own formatting.
* chore(process): ratchet the child_process allowlist down
windows-foreground-process-rows.ts no longer spawns anything, so its
allowlist line is stale. The guard fails on a stale entry as well as a
new one, precisely so a migrated file cannot keep a slot open and hide
the next regression in the same path.
* fix(ports): import the process-table reader the scanner uses
Missing import: the migration replaced the PowerShell call but the new
symbol was never imported, so tsc failed. Vitest transpiles without
typechecking, which is why the port-scanner suite stayed green.
* fix(deps): sync this branch's lockfile with its patch set
Same class as the fix on the tip branch: pnpm records a hash per patched
dependency, and this branch introduces the windows-process-tree patch
without its lockfile entry matching. Every job here failed at install
with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
Verified with --frozen-lockfile, which is what CI runs and what my local
runs were not.
* test(relay): drive the relay's Windows fixtures from the native snapshot
Two relay cases fed a PowerShell CIM payload through a mocked execFile.
That reader is gone, so both failed -- deterministically, on every PR
run for this branch and the one above it.
I did not catch it because my own verification sweep was
'src/main src/shared config/scripts' and never included src/relay. The
relay is a first-class consumer of the process table; leaving it out of
the sweep is how a deterministic failure survived six review rounds.
* fix(terminal): preserve Option-composed ASCII input
* fix(terminal): preserve Option keyboard protocol semantics
* fix(terminal): complete Option keyboard event encoding
* fix(terminal): harden Option input encoding
* fix(terminal): close keyboard protocol fallback gaps
* test(terminal): prove Option-composed ASCII reaches the pty end to end
The Option-compose fix had unit coverage only. This drives a live Electron
pane whose kitty flags are armed by the application's own CSI > 1 u and
asserts the bytes at the pty boundary: composed `@` and Shift-layer `\`
arrive as text, configured Option-as-Alt still reports the layout-resolved
chord, and a non-ASCII glyph still reaches the app as its alt hotkey.
Restoring the pre-fix policy fails exactly the two composed-text scenarios.
Also records the ASCII rule's rationale where the rule lives, not only in a
test comment.
* refactor(terminal): drop the unread Option layers from the layout snapshot
The native helper computed an Option and Option+Shift character for every
key, shipped both over IPC, validated them in the parser and cached them in
the renderer — but no production caller ever asked for them. Only the base
and Shift layers are read, and Shift is the one the web layout map cannot
supply, which is why the helper exists at all.
Removing them halves the helper's UCKeyTranslate work per key and drops the
option parameter that six signatures were threading through for nobody.
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.
* fix(files): fsync read-only files on POSIX
* test(e2e): add golden E2E tests for POSIX profile index fsync
Validates that profile index files are properly persisted on POSIX systems, including with restrictive umask settings. These are release-blocking golden tests for Linux and macOS.
* test(terminal): wait for fish child ownership before stdin write
Fish 4.8 withdraws DECSET 2031 before spawning the child, so the
shell-contracts harness could send hello into an intermediate prompt
and hang waiting for CHILD-READ. Wait for the child's CHILD-READY
marker and answer split DA1/CPR/OSC queries across chunk boundaries.
* test(e2e): verify profile index persists to disk with restrictive umask
Strengthen the POSIX fsync test to verify the rebuilt index is actually
written to disk and has correct permissions under a restrictive umask,
not just cached in memory.
Windows terminal rendering golden is flaky on CI runners. Re-enable
Windows in the golden E2E gate with a scoped test for the fresh-profile
startup regression from #14130. Terminal rendering continues on Linux
and macOS; Windows runs fresh-startup only.
* feat(ai-vault): isolate scanning in service processes
* fix(ai-vault): retire idle service processes
* fix(ai-vault): discard unverified cache processes
* fix(ai-vault): clear relay sidecar cancel watchdog on acknowledgement
A cancelled relay call is settled before its 2s cancel watchdog is armed, so the acknowledgement path bailed out of settle() before clearing the timer. The watchdog then faulted a healthy sidecar two seconds after every aborted scan, killing whatever request had since become active.
* fix(ai-vault): clear the pending restart before scheduling another
recordFault overwrote this.timer, stranding a restart that dispose() could no longer cancel.
* refactor(ai-vault): drop the orphaned first-prompt IPC wrapper
session-first-user-prompt-handler.ts now owns this entry point and routes through the service; the copy left in the read module had no callers.
* fix(ai-vault): retry a faulted cold start before surfacing it
A slow first start surfaced a raw 'did not become ready' error to the caller even though the supervisor was already respawning. Requeue an unsent call once onto the scheduled respawn instead.
Also stop arming the cancellation watchdog for a call the child never received: no acknowledgement is coming, so it killed a healthy service and stalled the lane.
Invalidation bookkeeping and ready-waiter construction move to the state module to stay under the max-lines cap.
* fix(ai-vault): give relay title reads their own lane
Before this branch the relay read title files directly, concurrently with scans. Routing both through one sidecar lane put title resolution behind a list scan that may run up to 130s, so SSH tab titles could lag minutes behind.
Split cache and interactive lanes in both the relay client and the sidecar entry, mirroring the desktop service.
Also: clear the ready deadline on fault, so a sidecar that dies before ready cannot fault its healthy replacement five seconds later; retry an unsent call once across a respawn; and skip the cancellation watchdog for a call the sidecar never received.
Restart/circuit bookkeeping moves to its own module, mirroring the desktop policy, to stay under the max-lines cap.
* fix(ai-vault): degrade relay title resolution on sidecar failure
listSessions already returns a host issue when the sidecar is unavailable; titles propagated the raw RPC error instead. Return no titles so callers fall back to preview text, and keep cancellation propagating.
* fix(ai-vault): scrub the service child environment
The children are forked with a 384 MiB heap cap and no loader, but both
spawn sites handed them the full parent environment, so an exported
NODE_OPTIONS silently raised the cap or --require'd code into them.
Allowlist both, following the plugin worker. The desktop child keeps the
eleven agent-root overrides it resolves its own roots from; the relay
sidecar takes remoteHome and hostPlatform from its init message and so
needs none of them. Both children share one priority module while they
share this one.
* fix(ai-vault): soft-disable relay vault when the service is missing
A missing service threw out of the constructor, so a Vault wiring bug
would abort relay startup and take every PTY on the host with it. The
unsupported-platform branch three lines above already treats a Vault
failure as a soft disable; do the same here.
Threading the service through the two handlers instead of a field also
retires the definite-assignment assertion the throw was propping up.
* fix(ai-vault): drain consumed cache invalidations
invalidatedPaths was re-applied in every request's finally and never
drained, so once N paths had been invalidated every later request paid N
evictions for the life of the process; the 4096 cap only bounded how bad
that got.
The re-apply exists to cover a read that overlapped the invalidation, so
drain once nothing is executing. Clearing unconditionally would drop the
re-apply for a request still running on the other lane.
* fix(ai-vault): keep a busy child through slow invalidation acks
invalidate() reused the 5s ready budget as its acknowledgement deadline
and killed the child on expiry, so a delete issued during a large scan
could kill a healthy process mid-scan and burn a slot toward the restart
circuit.
Fault only when nothing is executing. Fork IPC ordering already puts the
invalidation ahead of any later request, so a busy child owes no ack
here, and the 130s/15s request deadlines still catch a wedged one.
The start-retry predicate moves to the state module to stay under the
line cap, matching the shape the relay client already uses.
* fix(ai-vault): report a failed local scan as a host issue
A local-scope scan let its error escape to the renderer, which paints it
over the session list. Service supervision now produces those errors, so
"AI Vault service restart circuit is open." replaced the list.
Route local scope through the degradation the all-hosts leg and every SSH
leg already use, so it lands as a retryable host issue row instead. Same
result shape either way, so no IPC or wire contract changes.
* test(ai-vault): cover the relay restart circuit transitions
The relay policy shipped without tests. Pin both circuit edges, the
aging-out case, the forced-refresh reopen the relay has and the desktop
does not, and the backoff schedule.
* fix(ai-vault): keep the OpenCode roots in the service child env
The scrubbed allowlist dropped XDG_DATA_HOME and OPENCODE_DB, which the child
reads to locate the OpenCode store and database. The pre-PR worker thread
inherited them, so a user who sets either lost every OpenCode session.
* test(ai-vault): anchor the service spawn env assertion
The Windows release job hard-failed in run 30125672117: every SignPath
module install attempt got 403 Forbidden from the gallery's OData API,
which is behind Azure Front Door and was also serving 502/504 at the
time. That step was the only hard-fail in an otherwise fail-open signing
chain, so a gallery incident blocked the whole release.
The gallery CDN that serves the nupkg is a separate origin and stayed
healthy throughout, so fall back to a pinned version fetched from it
after the normal install path is exhausted. The fallback verifies a
SHA-256 pin, since that route skips the gallery's own package
validation.
Extracted to a composite action so the release job and the signing
rehearsal cannot drift apart.
* fix(skills): advance the release ledger at the cut so shipped revisions freeze
#10340 made the released-skill registry a function of the committed ledger
instead of a git tag walk, and #10460 reverted the cut step that advances that
ledger because it violated the #9119 contract (a version-only cut must not
regenerate or stage the content-addressed skill artifacts). Both were right;
the result is a ledger that never advances.
generate-skill-bundle-manifest.mjs:390 derives releasedCount solely from
release-mapping.json and :461 assigns a changed skill releaseRevision =
releasedCount + 1, while :518 protects only committedReleasedCounts[name] —
so index releasedCount is unprotected. A tag ships that tail revision, nothing
records it, and the next skill change rebuilds the same revision number over
different bytes. Installs carrying the shipped digest then match no snapshot
and degrade to unrecognized, which cannot be updated.
Restore the advance in a form the #9119 contract can keep enforcing:
--release now verifies that current-manifest.json and snapshot-registry.json
already match the ref being tagged, appends the mapping row, and writes only
release-mapping.json. The cut stages just that file, so it still cannot move a
content-addressed artifact — the failure #9119 guarded against — and now fails
loudly instead of recording a revision the tag does not ship.
The contract test is narrowed to match: it asserts the cut runs --release
(never --write) and stages exactly package.json and release-mapping.json.
* test(release-cut): close the staging bypasses the narrowed gate left open
The narrowed contract test anchored its `git add` scan to line start and
only inspected staged paths, so three ways to reintroduce #9119 stayed
green: a `git add` chained after `&&`, a write that never calls `git add`
at all, and `pnpm run generate:skill-bundle-manifest` — the package.json
alias for `--write`, which the hyphenated ban never matched. That last one
also passed the pre-#10460 assertions, so it was never covered.
Drop the anchor, require every `resources/skills` mention in the step to
be exactly what is staged, and ban the alias and `commit -a`. Comments are
stripped first so prose cannot trip a ban. Verified each bypass fails and
the real workflow passes.
* fix(release-cut): make the new provenance failure actionable to an operator
Verifying the content-addressed artifacts is the only new way the cut can
block, and it fails inside a step named "Bump package.json and tag" with a
lint-shaped message. That names the files and the command but not the two
things the operator needs: the regeneration has to land on main, and the
cut is safe to re-run afterwards. Say so.
Also pin down why assertReleasedHistoryPreserved takes the pre-append
mapping. It pairs with artifacts.releasedSnapshotCounts, which seeding
fixed before the row existed; handing it the post-append mapping makes
every cut throw "Released snapshot history is incomplete", which points
at tag fetching rather than the real cause. Nothing enforces the pairing.
* test(release-cut): gate the whole cut job, not just the bump step
Round-2 review defeated the previous gate twice, both proved by running
the full contract file green with #9119 reintroduced.
Every step in the cut job shares one workspace and one index, but the
contract test only inspected `Bump package.json and tag`. A step inserted
earlier could run --write and `git add resources/skills`, and the bump
step's own commit swept it into the version commit and the tag. Assert
job-wide instead: only the bump step may name the directory, and no step
may regenerate under either the flag or its package.json alias. That lives
in the generator suite because the contract file is at its max-lines cap.
Two regexes were also evadable. The mention scan required a trailing
slash, so a path held in a variable was invisible; it now matches the
directory itself. The `commit -a` ban matched nothing at all — `commit\s`
ate the only separator, so `-a`, `-am`, and `--all` all survived while
only a trailing `-a` was caught. `--allow-empty` stays allowed.
* fix(release-cut): assert the index, not the workflow text, before committing
Round-3 review defeated the job-wide grep three ways, each proved by
running both test files green with #9119 reintroduced into the tagged
commit: an `env:` block holding `--write` and `resources/skills`, a
composite action whose steps the workflow never spells out, and plain
shell concatenation (`root=resources; leaf=skills`).
Grepping shell source for path literals is inherently evadable, and the
previous fix only relocated round-2's variable-indirection hole one step
over. Move the invariant to where it cannot be dodged: immediately before
committing, the cut diffs its own index and refuses anything that is not
package.json or the release-mapping row. That does not care which step
staged what, or how the path was spelled.
The workflow grep stays as a cheap tripwire for literal spellings, now
paired with a positive assertion that the index guard exists and precedes
the commit — indirection cannot hide a missing guard. Mention matching
dedupes and trims quotes, since the guard names the row a second time.
* fix(release-cut): match the staged-path allowlist literally
`grep -vx` treats its patterns as regexes, so the `.` in `package.json`
matched any character: a staged `packageXjson` or a
`resources/skills/release-mappingXjson` was silently accepted by the
index guard. Verified both slip through `-vx` and are caught by `-vxF`.
Exercised the guard against a legitimate cut, an empty index, a staged
content-addressed artifact, paths containing a space and a non-ASCII
character (git quotes the latter, so it fails closed), and a staged
deletion. Only the two allowed paths pass.
* test(release-cut): assert the index guard aborts, not just that it exists
The positive assertion pinned the guard's shape and its position before
the commit, but not its effect: replacing `exit 1` with `:` left both
test files green while the cut logged the error and shipped the artifact
anyway. That is the same failure this whole gate keeps having — asserting
the shape of a defense rather than what it does.
Pin the abort too. Verified the neutered guard now fails the suite.
* test(release-cut): scope the abort check and catch clustered commit flags
Two holes in the guards this PR added, both in the same shape-not-effect
class the previous commit was meant to close.
The abort assertion's lazy match was not scoped to the guard's own block,
so it could borrow an `exit 1` from any later `if ... fi` in the step.
Degrading the guard to a warning while adding a plausible HEAD
precondition left every test green. Stop the match at the guard's `fi`.
The `commit -a` ban only matched when `a` led the flag cluster, so `-vam`,
`-va`, `-qam` and `-sam` all survived. That matters more than it looks:
`commit -a` stages at commit time, after the index guard has already
inspected a clean index, so it is the one way to defeat that guard. Match
`a` anywhere in a short-flag cluster; `--allow-empty` and `--amend` stay
allowed. Verified both mutants now fail.
* fix(release-cut): validate the commit, not the index, before tagging
The index guard asserted the wrong thing. `git commit` has a family of
forms that commit the working tree rather than the index — `-a`, `-i`,
`--only`, and a bare pathspec — so a rogue earlier step could leave
regenerated artifacts unstaged and any of those forms would carry them
into the tagged commit while the guard saw a clean index and passed.
Reproduced end to end: `git commit -i resources` put current-manifest.json
and snapshot-registry.json in the tag with all gates green, and
`--only resources` additionally dropped package.json from the tag.
Banning those flags one by one is the same enumeration game the earlier
rounds kept losing. Assert the outcome instead: after committing and
before tagging, diff-tree HEAD and refuse anything that is not
package.json or the release-mapping row. That is indifferent to which
step staged what and to how the commit was spelled.
Verified the whole family is now blocked (-i, --only, -a, -am, -vam,
pathspec, and an alias expanding to `commit -i`), that a stock commit and
an --allow-empty re-cut still pass, and that deleting, neutering,
un-anchoring, or relocating the guard each fails the suite.
* fix(release-cut): make the commit guard fail closed on a merge commit
Plain `git diff-tree` prints nothing for a merge commit, so the guard
would have passed silently instead of failing closed — the one direction
that matters on a release path. `-m --first-parent` reports the diff
against the first parent; verified byte-identical output for an ordinary
commit and still empty for the `--allow-empty` re-cut, so nothing else
changes. Not reachable today (nothing in the cut job creates a merge, and
npm version has no lifecycle hooks defined), but the failure mode is a
guard that looks like it ran.
Pin the flags in the assertion too, so neither dropping -m nor slipping in
a `--diff-filter` can weaken it without failing the suite.
The current manifest stamped package.json's version into itself (9 lines),
so every RC/stable version bump made the committed artifact stale on every
open branch: lint failed until authors committed content-free regeneration
diffs, which also dragged the resources/skills-filtered update-roundtrip
matrix onto unrelated PRs. Cutting a release tag whose skills tree changed
had the same effect through release-mapping.json.
- current-manifest.json is now schema 2 and content-only; the generator no
longer reads package.json. Registry and mapping stay schema 1 so the
append-only released-history guard keeps its schema gate.
- The running build's version enters at the IPC boundary
(skills:freshnessInventory passes app.getVersion()) and threads through
the inventory to placement observation; current-revision placements are
labeled with it while historical revisions keep resolving through the
release mapping. The artifact loader and its cache stay content-only.
- verify tolerates a committed release mapping that is a byte-exact prefix
of the derived one when every missing trailing row's revisions equal the
current manifest (a just-cut tag over unchanged-since bytes); such rows
are provably redundant until the next real regeneration adds them.
Artifacts now change only when skills/ content changes.
* ci(release): regenerate skill manifest on version bump
The release-cut "Bump package.json and tag" step bumped package.json but
never regenerated resources/skills/current-manifest.json, so its appVersion
stayed at the prior release. That drift shipped in v1.4.144-rc.1, rc.2, and
rc.3 (all carried an rc.1 manifest) and turns verify:skill-bundle-manifest
red on every branch after a cut, since that check runs in `pnpm lint` and
the PR `verify` job.
Regenerate the manifest right after `npm version` and stage resources/skills
into the release commit so the bundled manifest always matches the shipped
version. The generator is dependency-free (node builtins + git), so it runs
without a pnpm install, and the step's fetch-depth:0 checkout supplies the
tag history it reads.
* test(release): guard skill manifest regeneration
* test(release): require full history for skill manifest
* fix(terminal): keep WebGL glyph atlas pages within the shader sampler budget
The fragment shader has sampler slots for maxAtlasPages (16 on most Macs)
and leaves outColor uninitialized for any higher page index, so glyphs
rasterized onto pages past the budget render as garbled pixels. Long
sessions grow past the budget via the merge fallback, and the previous
wipe fix re-activated those unbindable pages, so every atlas wipe
re-allocated glyphs onto them (post-wipe allocation prefers the last,
highest-index active page) and garbled whole panes mid-stream.
Fix, matching the direction xterm.js maintainers are pursuing upstream
(xtermjs/xterm.js#6043): a shared _evictAllPages resets the atlas to one
fresh page, called from clearTexture and from the two allocation paths
that could otherwise push a page past the budget (merge fallback and
oversized-glyph page creation), so the page count can never exceed the
renderer's texture capacity. Defensive backstops: a one-time warn plus
bind-loop clamp, and an else branch in the generated shader so an
unexpected overflow renders blank instead of undefined pixels.
* test(terminal): cover WebGL atlas sampler budget
* fix(terminal): align WebGL atlas invalidation source
* fix(ssh): isolate relay filesystem watchers
* Fix relay watcher fault-harness pid file and in-process fallback isolati
- Use exclusive ('wx') creation for the fault-harness pid file so a leaked
ORCA_WATCHER_CHILD_PID_FILE env var can't clobber an existing file, and
have the harness remove the file after reading a replacement pid.
- Force useInProcessVitestFallback to false in the relay watcher pool so a
leaked VITEST env var can never load the native watcher addon in-process
on the relay; fail closed instead when the isolated child is missing.
- Thread an injectable RelayWatcherProcessPool into FsHandler/
RelayFilesystemWatchRegistry for tests, and add coverage for both fixes.
* Fix crash-isolated file watcher process pool for orca-serve SIGSEGV afte
Replace the worker-thread runtime file watcher with a forked, crash-isolated
@parcel/watcher child process pool so a native FSEvents fault can no longer
take down the main/serve process, and add bounded event batching, delivery
backpressure, and quarantine-based recovery for faulty watch roots.
* Fix crash-isolated file watcher teardown and shutdown leaks
- Fault harness could throw before mkdtemp/realpath completed, skipping
cleanup; now tracks each temp path independently and races an async
watcher-callback error so it can't escape the try/finally unhandled.
- In-process fallback swallowed unsubscribe failures via a bare rejection
handler that could still throw; use .catch() instead.
- Watcher process entry's cancel-subscribe handler now reuses the async
unsubscribe path when a crawl already finished, releasing the native
handle instead of leaking it (blocks worktree unlock on Windows).
- Runtime watcher process pool exposed no real dispose(); shutdown now
kills pooled children so they don't outlive the main process.
* Fix disposeSlot double-iteration bug in file watcher pool teardown
Remove the unnecessary array snapshot in dispose(): disposeSlot mutates
allSlots by deleting the slot being visited, and deleting the
in-progress element during Set iteration is well-defined, so the spread
copy was dead weight left over from prior debugging.
* Fix pending file watcher installs not aborting on unsubscribe
- Local/WSL watcher installs and SSH fs.watch setup now honor the
in-flight AbortSignal, so the last unwatch cancels a slow native
subscribe or remote setup instead of waiting for it to finish.
- Thread signal through IFilesystemProvider.watch and SSH-backed
file explorer watches for the same early-cancel behavior.
* Fix crash-resubscribe hangs and SSH watch teardown races in file watcher
- Add a bounded deadline for post-crash resubscription crawls so one
stuck root quarantines instead of pinning its whole shard forever.
- Report FSEvents overflow as recoverable so delivery continues after
a dropped-events error instead of surfacing as terminal.
- Make WSL watcher abort errors real DOMException instances so
AbortSignal-based cancellation checks recognize them.
- Rework SSH watch registration so ownership of the shared setup
request (not just the first caller) decides teardown, preventing
one caller's abort from cancelling another's shared watch and
guaranteeing exactly one fs.unwatch per registration.
- Reformat reliability-gates.jsonc arrays and refresh WSL/SSH coverage
entries and evidence runs to match the above.
* Add CI gate to run the file-watcher SIGSEGV fault harness under Electron
- The reliability gate and release workflows (mac, Linux) previously only
exercised the crash-isolation harness under vanilla Node, which doesn't
catch runtime differences in the actual Electron binary that ships to
users.
- Adds an `ELECTRON_RUN_AS_NODE=1 pnpm exec electron ...` run of the same
harness alongside the existing Node run, so #8212's SIGSEGV-survival
contract is proven against both runtimes before packaging.
* Add CI gate blocking Linux/macOS release packaging on watcher fault reco
Adds a contract test asserting release-cut.yml and release-mac-build.yml
run the runtime-file-watcher-fault-harness after building and before
publishing artifacts, so a regression in watcher process fault recovery
fails release packaging instead of shipping silently.
* Fix use-after-clear crash in failAllWatcherSubscriptions
Snapshot the records map before iterating, since onTerminalError
hooks can dispose the supervisor and clear `records` mid-loop,
causing a crash. Also update the matching test to assert against
the shared buildParcelWatcherIgnoreOptions helper instead of a
loose arrayContaining match.
* Fix use-after-clear crash in failAllWatcherSubscriptions
Snapshot watcher records with Array.from instead of spread, since
spread syntax over an iterator that's mutated mid-loop by
onTerminalError hooks can produce inconsistent results.
* Revert "Preload the daemon windowsHide shim via --require; wrap promisify custom (#7499)"
This reverts commit 8f396badaf.
* Revert "Hide console windows for children of the node.exe-hosted daemon (#7486)"
This reverts commit f0fdd3a716.
* Revert "fix(daemon): relocate daemon host image out of the install-dir kill zone (#7473)"
This reverts commit f4faafa987.
* Revert "fix(pty): keep runtime dirs a surviving daemon still uses (#7463)"
This reverts commit 3cd23a13a1.
* Revert "Relocate node-pty ConPTY runtime outside the Windows install dir (fixes update-time terminal loss) (#7421)"
This reverts commit 509c41e2bf.
* Verify Windows app executable signing
* Isolate Windows signing verifier tests
* Handle direct Windows installer extraction
---------
Co-authored-by: Neil <neil@stably.ai>
Migrate fileURLToPath(import.meta.url) / dirname(...) boilerplate to the
native import.meta.dirname / import.meta.filename, then enable the rule
at error so new code stays on the native form.
The oxlint autofix rewrites the expression but leaves the now-unused
node:url / node:path imports behind (which the already-enabled
no-unused-vars=error would then flag), so this commit also removes those
34 orphaned imports — trimming the named import where other names are
still used, deleting the line where it was the sole import.
Scope is build scripts + Node-env tests only (config/scripts, tools/
benchmarks, *.test.{ts,mjs}, vitest configs); zero shipped runtime code.
The native properties are exact equivalents (Node >= 20.11; repo is on
24), so behavior is unchanged.
Verified: oxlint 0 errors tree-wide (root + mobile), oxfmt clean,
typecheck (node+cli+web) + mobile tsc pass, root vitest 22825 passed /
0 failed, mobile vitest 1018 passed. Exercised the rewritten scripts
directly: build:relay (6 targets), ensure-native-runtime,
verify-macos-entitlements all run correctly with import.meta.dirname.
- Remove Windows from the release evidence platform matrix check because Windows release evidence is temporarily paused due to CI runner PTY readiness.
- Add scenarioTitle as taskTitle and a display name to mock agent objects to satisfy updated runtime row shapes in mobile lag scripts.
* Keep mobile-driven browser panes paintable and park idle hidden panes
- Add useSyncExternalStore-based hooks to reactively track mobile-driven pages.
- Prevent unmounting or losing paintability for panes controlled by a mobile device.
- Park heavy browser pane subtrees in hidden worktrees to improve performance.
* Derive workflow platforms dynamically in runtime contract test
Avoid hardcoding Linux, macOS, and Windows platforms in the Electron
runtime package contract test. Instead, extract the platforms directly
from the golden E2E workflow matrix strategy and map them to their
corresponding run steps.