Commit Graph
17134 Commits
Author SHA1 Message Date
Brennan Benson 8d908764dc Revert "Keep same-machine folders with matching Git remotes in separate projects (#32272)" (#34662)
* Revert "Keep same-machine folders with matching Git remotes in separate projects (#32272)"

This reverts commit 29a5f89b8a.

On a machine with several clones of one repo, #32272 moved the oldest clone
into the shared cross-host project. Shared Windows QA machines have several
orca clones, so everyone's sidebar "orca" project suddenly gained that
machine's main checkout and its worktrees. Revert until the intended
grouping is decided.

Conflicts resolved against later main: Settings files moved into
settings/repository (imports adjusted), and two test files later deleted on
main stay deleted.

* Use checked types in restored test fixtures

* Combine restored fixture type imports
2026-10-27 20:38:35 -07:00
Neil 44e26301e2 perf(editor): stop rank-only worktree patches and full-state copies from driving owner sweeps (#34705)
resolveEditorOwnerRoute spread the full app state on every owner lookup;
project only route inputs, cached per state. Compare worktreesByRepo by its
owner fields so sort-order/unread/activity patches skip editor ownership
invalidation.
2026-10-27 20:31:05 -07:00
Samuel VilaandNeil 66134086bd fix(cli): strip terminal control sequences from Linear text in CLI output (#31677)
Linear-provided titles, states, assignees, projects, labels, workspace
names and error messages were printed verbatim, so remote text could
emit OSC/CSI/C1 sequences or forge extra output lines. Move terminalSafe
into src/shared/terminal-safe-text.ts, add terminalSafeLine (collapses
CR/LF), and apply it to every Linear text field in the human-readable
formatters. --json output is unchanged.

Fixes #20478

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-10-27 20:30:08 -07:00
76d4b01969 chore(i18n): gate unreferenced catalog keys in the extraction check and retire 1,644 of them (#22056)
* chore(i18n): add a detector for catalog keys no source references

A key can leave the code without leaving the catalog: a feature is reverted, a
setting is rewritten in place, a component is deleted. Nothing fails, so the
entries stay. Every locale carries them, translators spend effort on strings no
screen renders, and coverage numbers understate how much is actually translated.

`verify:localization-extraction` already reports an unreferenced set, but it
cannot be deleted as-is: i18next-cli does not see a key held in a data structure
(`{ key: '…' }`) or passed as a variable, and 18 files do exactly that. So this
requires two independent conditions — extraction misses the key AND the exact
string appears in no source literal under src/ or mobile/.

Two properties matter and both were found the hard way:

- Plural variants are judged by their base key. i18next appends the CLDR
  category at runtime, so `…_one` never appears in a source file even though the
  base does. Without this the scan eats every plural in the catalog.
- The scan is iterative. Removing a base key can strand the variants that
  survived on its account, so it must be re-run until it reports none.

Registered as `verify:localization-orphans` but deliberately left out of the
lint chain: it reports 1,401 keys on current main, so gating now would fail CI.
The prune and the gate come together in a follow-up.

* test(i18n): cover the orphan detector's two non-obvious rules

Both rules decide whether a key is live, and both are easy to break without
noticing: the plural rule keeps `…count_one` when only `…count` appears in
source, and the literal scan is the sole evidence for keys extraction cannot
see — held in a data structure or passed as a variable.

Extracting them as pure functions changes no behaviour: the detector still
reports 14,459 keys, 12,887 referenced, 1,401 orphans, and --check still
exits 1.

* chore(i18n): rename the detector's key pattern constant

main gained the anti-slop rule no-shape-in-symbol-names after this branch
was cut, and KEY_SHAPED trips it once the branch is merged with main.
Renamed to CATALOG_KEY_PATTERN; no behaviour change.

* chore(i18n): name the orphan detector like the other localization verifiers

CODEOWNERS routes /config/scripts/*localization*.mjs to the localization owner, and find-orphaned-catalog-keys.mjs matched neither that pattern nor *locale*, so a change to it asked nobody for review. The file now carries the name of the npm script that runs it, verify:localization-orphans.

* fix(i18n): report stranded plural variants in the same pass as their base key

isKeyReferenced kept a plural variant alive whenever en.json still carried its base key, even when no source reached that base key. The base key was reported and the variants were not, so a prune needed a second run to find them. Only a base key that extraction or a source literal references keeps its variants now; the --check message drops the re-run advice and names the override tables, which sit outside the scan roots.

* fix(i18n): let the literal scan accept every key-shaped literal

The scan captured only literals of seven characters or more and then applied CATALOG_KEY_PATTERN, which has no such floor. A live key shorter than that, held in a data structure, would have been reported as an orphan. en.json has no key that short today, so the result is unchanged; the capture now leaves the shape decision to the one pattern.

* fix(i18n): stop the orphan scan when a source root cannot be read

collectSourceFiles swallowed every readdir error, so a directory it could not read contributed no literals and a key named only there would have been reported as safe to delete. Only a missing mobile/ root is skipped now, since a checkout without the mobile app is still a complete desktop scan; any other error stops the scan.

* style(i18n): wrap one line of the orphan detector to the formatter's width

* chore(i18n): fold the orphan-key detector into the extraction check and retire 1,644 unreferenced keys

verify-localization-extraction.mjs now filters its own unreferenced set by
source literals (src/ and mobile/), plural base keys and dynamic prefixes
composed inside translate calls, and fails when any key is named nowhere.
That replaces the standalone verify-localization-orphans.mjs script and its
second extraction pass.

The prune is recomputed on current main: 1,644 keys leave all seven locale
catalogs, 285 stale entries leave the key-override tables, and
en-runtime-required.json is regenerated.

Supersedes #19801.

Co-authored-by: Evgenii <413011+smwbev@users.noreply.github.com>

* chore(i18n): scope the unused-key scan to src/ and require a dotted dynamic prefix

mobile/ edits do not trigger the extraction check, so literals there must not
decide its verdict; a dotless runtime-built prefix would keep unrelated keys alive.

Co-authored-by: Evgenii <413011+smwbev@users.noreply.github.com>

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Evgenii <413011+smwbev@users.noreply.github.com>
2026-10-27 20:29:43 -07:00
Hyein ChoandNeil 48f7ce7fed feat(terminal): seed MacPorts /opt/local on PATH when port is installed (#20146)
Packaged macOS builds now append /opt/local/bin and /opt/local/sbin to the
seeded PATH when the default-prefix `port` binary exists, and the agent CLI
install-dir fallback also checks /opt/local/bin. Both are appended last so
existing Homebrew/system resolutions never change.

Co-authored-by: Neil <neil@stably.ai>
2026-10-27 20:24:47 -07:00
927a9573c6 docs: record which CI jobs stay on Blacksmith and why (#34685)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-27 20:20:12 -07:00
OrcaWinandOrcaWin 0323d28498 test: remove 49 low-value test cases (#34678)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 20:13:46 -07:00
Neil 68f0c7999f perf(editor): reuse model owner keys in closed-tab cleanup (#34691)
Each openFiles change (e.g. a dirty toggle) re-resolved every tab's owner
route twice. Cache owner keys per tab until an owner input changes, so a
single-tab update resolves only that tab.
2026-10-27 20:12:02 -07:00
Justas Brazauskas 280dee2477 fix(dsh): resume DSH sessions on the DSH backend (#27949)
dsh-tui 0.14+ boots the backend last picked in its launchpad
(~/.dsh-tui/kernel.json), so a plain `dsh-tui --resume <id>` could hand
a DSH session id to the Claude or Codex backend. Pin --backend=dsh in the
resume argv; dsh-tui <0.14 skips the joined form, but would read a
separate `dsh` token as the initial prompt.

Fixes #27944
2026-10-27 20:08:52 -07:00
8b64ec281a ci: restore main's Vitest transform cache in PR unit shards (#34512)
* ci: restore main's Vitest transform cache in PR unit shards

Main produces per-shard transform caches on a schedule; PR shards restore
the newest one for the same config/lockfile/runtime policy. A Vitest cache
key generator folds each literal specifier's resolution candidates into the
per-module key so newly added files that shadow a resolution invalidate the
importer, and bails on glob-expanded dynamic imports.

* ci: list the resolution key plugin in the node tsconfig project

* ci: accept Vitest's CACHEDIR.TAG when sealing the unit module cache

Vitest 5.0.5 tags its cache root, which made every seal fail with
'invalid cache entry name' so no producer cache was ever saved.

* ci: reseed the unit module cache when any cache-key input lands on main

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-27 20:04:56 -07:00
Neil 6bb0c14a98 refactor: group native chat text presentation (#34647) 2026-10-27 19:48:24 -07:00
OrcaWinandOrcaWin 5f4d9654f9 ci: run operation-count performance contracts on hosted runners (#34668)
The contracts count operations and retained objects; wall-clock budgets
stay in pnpm test:perf:benchmarks, which no CI job runs. They do not
need Blacksmith's consistent hardware, and the PR path filter runs them
~40 times a day ($7.90/day, $5.50 of it on the 6-vCPU Mac).

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 19:44:37 -07:00
OrcaWinandOrcaWin 1df26abf60 ci(adhoc): package Mac architectures on 6-vCPU runners (#34673)
Each 12-vCPU arch job spends ~3 of its ~7 minutes waiting on Apple's
notary service; packaging itself runs at the same speed as the hourly
build's 6-vCPU runner. Halve the per-minute rate.

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 19:44:18 -07:00
OrcaWinandOrcaWin 261eb660e3 ci(mac): fail the packaging step when overlapped notarization throws (#34658)
electron-builder's temp-file cleanup registers async-exit-hook, whose
beforeExit handler calls process.exit(0). The overlap script reported
failures through process.exitCode, so a thrown build exited 0: the
retry wrapper logged success and skipped its second attempt (hourly run
40608643418). Exit explicitly instead, as electron-builder's own CLI
guards against the same hook.

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 19:44:14 -07:00
OrcaWinandOrcaWin e0f32dcac1 ci: seed hosted Linux packaging and mobile bundle caches on main, and stop PR-local saves (#34672)
- Warm electron-builder-linux-<versions> and the hosted ubuntu-24.04 Electron native modules
- PRs restore hosted Linux Electron native modules without saving
- Replace mobile verify's bundler-cache with a restore of a warmup-saved bundle
- Drop the pnpm store cache where a direct install was faster (mobile main, glibc-floor containers)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 19:40:29 -07:00
OrcaWinandOrcaWin 4cd62c4946 ci: split the layout oracle window scenarios across two shards (#34671)
The single-file oracle cannot be split by whole-file balancing, so pass
Playwright test-level shards to the changed-spec lane. It was the last
required check in 40 of 42 measured runs.

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 19:25:17 -07:00
OrcaWinandOrcaWin e1838023cc test: remove 70 low-value test cases (#34665)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 19:25:02 -07:00
OrcaWinandm4air 6ee27ef60e test(wsl): prove Codex and Grok chat inside the distro in the real-WSL lane (#34620)
Scripted guest codex (app-server) and grok (ACP) fixtures, installed on the
guest login PATH beside claude. orcad streams a partial reply, asks for an
approval and takes Stop for each; the lane checks the guest wrapper ran the
fixture with the guest runtime, and that Windows-side codex/grok decoys on
WSL's appended PATH never ran.

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-27 19:22:01 -07:00
OrcaWinandOrcaWin 1f5939dffc ci: run relay integration on a free hosted runner (#34666)
It finishes in ~1.2 min beside ~7 min unit shards, so Blacksmith minutes buy no wall time.

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 19:21:13 -07:00
OrcaWinandOrcaWin e5f1eef466 test(sqlite): keep node-next-compat green on Node 26.12 (#34661)
Node 26.12 bundles SQLite 3.54, which rejects ALTER TABLE ADD COLUMN for
rowid aliases; rebuild the shadowing table with CREATE TABLE instead.

Node 26.10 binds explicit undefined as NULL. The deleted test only pinned
node:sqlite's own validation (a parity guard for the removed Bun adapter);
SqliteBindings already rejects undefined at compile time.

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 19:19:17 -07:00
Neil 6dab169f72 test(orcad): wait on the launcher stub's own markers instead of 5 s windows (#34656)
Each lifecycle step now resolves when the stub prints its marker (ready,
booting:, runtime-exit:N) or rejects with diagnostics when the launcher's
output closes first, so a slow macOS Intel runner only makes a step later.
2026-10-27 19:15:23 -07:00
Neil 2893d06533 test(native-chat): name the stalled step in the live-history watcher test (#34663)
* test(native-chat): name the stalled step in the live-history watcher test

A CI run hit the 30 s test timeout with no step named. Each wait now has its
own 8 s deadline that reports the stalled step with what the watcher had
delivered, and the file sets IS_REACT_ACT_ENVIRONMENT so act() flushes.

* test(native-chat): share one 27 s budget across the live-history waits

Per-step 8 s deadlines were stricter than the old 30 s test timeout; a shared
deadline keeps the total unchanged and only improves the failure message.

* test(native-chat): use an AbortSignal budget instead of the wall clock

Date.now trips the flaky-test wall-clock rule; AbortSignal.timeout gives the
same shared 27 s budget without reading the clock.
2026-10-27 19:13:52 -07:00
ef6e661449 fix(runtime): accept Muse's composer when the voice-input label is absent (#25695)
* fix(runtime): accept Muse's composer when the voice-input label is absent

Muse labels its composer's top rule "Voice input" only where voice is
available, so on Linux the readiness predicate never matched and
worker-start timed out at agent_readiness. Keep the voice-label match and
also accept the composer itself: a rule (two or more rule chars) directly
before the prompt glyph after the latest Muse banner, so a shell prompt like
'~/app ❯' or '╰─❯' under a printed banner is still not ready.

Fixes #25005

Co-authored-by: j33pia <242526320+j33pia@users.noreply.github.com>
Co-authored-by: Nawapat Buakoet <nawapat.b@covest.finance>

* fix(runtime): escape Muse composer regex so the orcad bundle stays ASCII

---------

Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: j33pia <242526320+j33pia@users.noreply.github.com>
Co-authored-by: Nawapat Buakoet <nawapat.b@covest.finance>
2026-10-27 19:12:13 -07:00
Neil 4fe3d7624b test(pty): scope the group-reap test's ps to its own PTY (#34669)
* test(pty): scope the group-reap test's ps to its own PTY

The helper listed every host process with full command lines, which
overflows execFileSync's 1MB buffer on busy machines (ENOBUFS). Discover
the tagged job on the test PTY's terminal and recheck survivors by their
captured pids, so the output stays small by construction and a job that
lost the hung-up terminal is still caught.

Refs #34410

* test(pty): clean up a job that discovery never captured

Refs #34410
2026-10-27 19:07:07 -07:00
Neil 61337dfb33 chore(ratchet): gate event-loop-blocking calls in the daemon, PTY and orcad code (#34614)
* fix(daemon): read the PTY process table without blocking on terminal kill and resize

The terminal daemon force-killed a session by running ps and reading
/proc/<pid>/stat synchronously, and checked the resize foreground group
with execFileSync('ps'). A slow or wedged ps froze every terminal the
daemon serves for up to the ps timeout on each call.

Both paths now use the async process-table read (runProcess with a
timeout that kills the child). SubprocessHandle.forceKill may return a
promise; the termination controller awaits it so a refused signal still
releases the claim for a retry. A force-kill or SIGWINCH whose root
exits while ps runs signals nothing, since the captured groups could
already be recycled. ConPTY force-kill stays synchronous so dispose()
still sees the node-pty kill it must not repeat. The in-process
provider's app-quit sweep keeps the blocking variant. The ps dialect
and table readers move to posix-pty-process-table.ts.

Refs #34410
Refs #33870

* fix(daemon): address review on async PTY process-table reads

Guard only the root-pid fallback on exit so proven groups are still swept
after dispose, wait out at most one ps dialect probe, move the sync-launch
baseline entry with the table reader, and keep tests cross-platform.

Refs #34410
Refs #33870

* fix(daemon): keep the PTY open until an in-flight force-kill sweep finishes

A sweep whose root exits mid-read must not signal: the freed TTY may
belong to another terminal. Dispose therefore defers destroy() until the
sweep settles, so its hangup cannot end the root first.

Refs #34410
Refs #33870

* test(daemon): build the destroy-tracking PTY mock without a type assertion

Refs #34410
Refs #33870

* fix(pty): make the local graceful-deadline force-kill async and stub ps in tests

The in-process provider's graceful-deadline timer now uses the async
group sweep; the app-quit sweep and the root-signal path stay
synchronous. Tests that drove force-kill through fixture pids now stub
the async process-table read instead of querying the host, and a new
test pins that waiters do not chain behind a failed dialect probe.
Session ids are only read for rows on the root's terminal.

Refs #34410
Refs #33870

* fix(pty): hold PTY teardown until an in-flight force-kill sweep settles

Orphan cleanup could destroy a local PTY while the graceful-deadline
sweep was still reading the process table, neutralizing its fallback
kill and hanging up the root first. Both the daemon handle and the
local provider now defer teardown through PendingPtyGroupSweep.

Concurrent force-kill callers in the daemon now await the shared
in-flight request, so a joiner's physical-exit budget starts after the
signals are sent and it sees their rejection.

Refs #34410
Refs #33870

* fix(pty): let disposal and orphan cleanup join the in-flight force-kill sweep

Session disposal now joins the controller's shared force-kill request
instead of starting a second sweep. Local orphan cleanup decides its
escalation only after a pending graceful-deadline sweep settles, so a
refused sweep is escalated before the handle is released rather than
leaving the retry with a neutralized kill.

Refs #34410
Refs #33870

* fix(daemon): SIGKILL live PTY groups before a shutdown deadline exit

The old synchronous sweep sent its signals before the shutdown deadline
timer could run; the async sweep can still be reading the process table
when it fires. The deadline now SIGKILLs each live session's process
group synchronously before exiting (node-pty roots lead their own
group), falling back to the root pid when the group kill is refused.
POSIX only.

Refs #34410
Refs #33870

* chore(ratchet): gate event-loop-blocking calls in the daemon, PTY and orcad code

The terminal daemon and orcad serve every pane from one event loop, so a
synchronous child launch or file write there freezes all of them. A new
count ratchet tracks execFileSync/spawnSync/execSync, runProcessSync,
appendFileSync and writeSync per file under src/main/daemon,
src/main/pty, src/main/runtime and src/main/orcad; counts may only go
down. It reuses the sync-spawn ratchet's import-aware call collector,
now extracted to gated-call-use-collector.mjs, so aliases, namespace
imports and re-exports count and same-named methods on other objects
do not.

Refs #34410
Refs #33870
2026-10-27 19:05:34 -07:00
Jinwoo Hong 3a4ac5445c fix(daemon): roll the daemon-side terminal fixes since v1.4.241 into a fresh v53 daemon (#34627) 2026-10-27 21:59:58 -04:00
Neil 3929371709 fix(wsl): background repo probes, PR refresh and worktree scans never start a stopped distro (E9) (#34632)
* fix(wsl): background repo probes, PR refresh and worktree scans never start a stopped distro (E9)

Any Windows touch of a \\wsl.localhost path, or Git/gh run for it, boots a
stopped distro. These background paths ran without asking the stopped-distro
gate: the worktree scan refresh (Git-admin fingerprint reads plus git worktree
list), the remote-identity and username enrichment sweeps, the startup
upstream backfill, the quiet-period ref maintenance, and queued PR refreshes.
Each now asks blockedWslDistroForPath first and skips, keeping known data,
until the distro runs; a host path never asks the gate.

* fix(wsl): a user act's wake allowance ends with the act

The allowance lived in an AsyncLocalStorage store, so any timer, listener or
queue created during a user act (opening a workspace, a wake read, the first
git status) kept it forever and could start the distro later, after a
terminate. The scope now carries an active flag cleared when the act settles.

* fix(wsl): skill discovery and automatic refreshPRNow never start a stopped distro (E9)

- Skill discovery (mounted by passive UI) leaves out roots on a stopped or
  unverifiable distro, and skips plugin discovery for such a cwd, instead of
  reading \\wsl.localhost; they are not reported missing, so the next scan
  picks them up.
- refreshPRNow skips automatic reasons (visible, active, post-push) on a
  blocked distro like the drainer does; only a manual refresh may start it.
- Ref maintenance's opt-out probe takes the same sync wslDistroForPath
  early-out as the other paths.
- Tests: stopped-with-no-cache worktree scans answer from stored rows, and
  'unverifiable' is treated as blocked.

* fix(wsl): skill discovery for a WSL target never starts a stopped distro (E9)

Passive UI discovers skills for projects whose agents run in WSL. The target
resolution probed the distro home (wsl.exe) and the scan ran in the distro,
so either booted a stopped one. Discovery now refuses a stopped or
unverifiable WSL target before the home probe and answers no skills and no
sources, so nothing reads as missing and the next scan retries. Delete and
share keep resolving without the gate.

* fix(wsl): a settled user act marks its distro woken; host skill scans never await the gate

- runAsWslUserAct (the gate's wrapper) notes the distro woken inside the act
  when it settles, success or throw, so a follow-up is never answered by a
  'stopped' read taken before the act.
- Skill discovery takes sync early-outs: a host-only root set and a host cwd
  never await the WSL gate.

* fix(wsl): Share discovers as a user act; a stopped distro's skills read as unknown, not absent

- skills:prepareShare (and the skills.share RPC) discover as a user act on the
  target's distro, so an explicit Share on a stopped distro finds its skills.
- A blocked WSL target answers an 'unavailable' source per requested kind, and
  roots (or a workspace plugin folder) on a stopped distro are reported
  'unavailable' instead of dropped, so the installed-skill verdict says the
  status may be incomplete rather than offering Install.

* test(wsl): use a fixed fetchedAt in the stopped-distro PR refresh test

* fix(wsl): Share scans stopped distros' skill folders, with or without a target

The desktop Share dialog sends no target, so the user-act scope had no distro
and the gated scan reported a stopped distro's repo folders unavailable; Share
then failed with skill-share-source-not-found. discoverSkills takes
gateStoppedWslDistros: false for both share paths (IPC and RPC), skipping the
stopped-root split and the plugin-cwd check; passive discovery keeps the gate.
Gated and ungated scans of one target no longer share a coalesced answer.
2026-10-27 18:56:02 -07:00
ansonlotiniatandOrcaWin 73f3fcd873 fix(i18n): standardize Simplified Chinese product terminology (#15464)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 18:52:13 -07:00
OrcaWinandOrcaWin 2e0ba4e4de fix(i18n): translate missing ko/ja/es/fr/pt UI strings (#34643)
* fix(i18n): translate missing ko/ja/es/fr/pt UI strings

Fill every en.json key missing from the ko, ja, es, fr and pt catalogs
(293 ko/es/fr, 244 ja, 316 pt leaves; purely additive). The provider
credit-balance status-bar keys stay sparse per
provider-credit-balance-locales.test.ts, and zh is left to #34478.

Also list Français and Português (Brasil) in the Settings docs.

Closes #34513

* fix(i18n): keep goal-change failure wording in English until reviewed

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 18:52:09 -07:00
Jinjing 023c8603e8 fix: dismiss chat naming error card before opening settings (#34655) 2026-10-27 18:49:07 -07:00
OrcaWinandOrcaWin 4f9b331570 test: remove 45 low-value cases and two suites (#34646)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 18:41:50 -07:00
Neil e0550733cd fix(wsl-host): chat and text generation never run a Windows-side agent CLI on a WSL host (#34590)
* fix(wsl-host): chat and text generation never run a Windows-side agent CLI on a WSL host

* fix(wsl-host): a Windows-only claude or codex reads as not installed to every CLI caller

* fix(wsl-host): say a Windows-only claude or codex is installed only on Windows at sign-in

* test(wsl): move the lane's chat envelope out of the contract to break an import cycle

* test(native-chat): inject guest mounts into the stock lookup the WSL guest-path tests exercise
2026-10-27 18:38:06 -07:00
Neil f4fb9cac56 fix(wsl): keep the revision conflict when its listener throws, and drop a stale backoff bypass (#34645)
A throwing onFailure on a terminal reopen failure would have replaced the
revision conflict the caller sees; it is now caught and logged like the
writer connection's own report. A reopen success also clears a pending
backoff bypass so it cannot carry into a later backoff.
2026-10-27 18:36:34 -07:00
Neil ca272a78c0 fix(daemon): read the PTY process table without blocking on terminal kill and resize (#34603)
* fix(daemon): read the PTY process table without blocking on terminal kill and resize

The terminal daemon force-killed a session by running ps and reading
/proc/<pid>/stat synchronously, and checked the resize foreground group
with execFileSync('ps'). A slow or wedged ps froze every terminal the
daemon serves for up to the ps timeout on each call.

Both paths now use the async process-table read (runProcess with a
timeout that kills the child). SubprocessHandle.forceKill may return a
promise; the termination controller awaits it so a refused signal still
releases the claim for a retry. A force-kill or SIGWINCH whose root
exits while ps runs signals nothing, since the captured groups could
already be recycled. ConPTY force-kill stays synchronous so dispose()
still sees the node-pty kill it must not repeat. The in-process
provider's app-quit sweep keeps the blocking variant. The ps dialect
and table readers move to posix-pty-process-table.ts.

Refs #34410
Refs #33870

* fix(daemon): address review on async PTY process-table reads

Guard only the root-pid fallback on exit so proven groups are still swept
after dispose, wait out at most one ps dialect probe, move the sync-launch
baseline entry with the table reader, and keep tests cross-platform.

Refs #34410
Refs #33870

* fix(daemon): keep the PTY open until an in-flight force-kill sweep finishes

A sweep whose root exits mid-read must not signal: the freed TTY may
belong to another terminal. Dispose therefore defers destroy() until the
sweep settles, so its hangup cannot end the root first.

Refs #34410
Refs #33870

* test(daemon): build the destroy-tracking PTY mock without a type assertion

Refs #34410
Refs #33870

* fix(pty): make the local graceful-deadline force-kill async and stub ps in tests

The in-process provider's graceful-deadline timer now uses the async
group sweep; the app-quit sweep and the root-signal path stay
synchronous. Tests that drove force-kill through fixture pids now stub
the async process-table read instead of querying the host, and a new
test pins that waiters do not chain behind a failed dialect probe.
Session ids are only read for rows on the root's terminal.

Refs #34410
Refs #33870

* fix(pty): hold PTY teardown until an in-flight force-kill sweep settles

Orphan cleanup could destroy a local PTY while the graceful-deadline
sweep was still reading the process table, neutralizing its fallback
kill and hanging up the root first. Both the daemon handle and the
local provider now defer teardown through PendingPtyGroupSweep.

Concurrent force-kill callers in the daemon now await the shared
in-flight request, so a joiner's physical-exit budget starts after the
signals are sent and it sees their rejection.

Refs #34410
Refs #33870

* fix(pty): let disposal and orphan cleanup join the in-flight force-kill sweep

Session disposal now joins the controller's shared force-kill request
instead of starting a second sweep. Local orphan cleanup decides its
escalation only after a pending graceful-deadline sweep settles, so a
refused sweep is escalated before the handle is released rather than
leaving the retry with a neutralized kill.

Refs #34410
Refs #33870

* fix(daemon): SIGKILL live PTY groups before a shutdown deadline exit

The old synchronous sweep sent its signals before the shutdown deadline
timer could run; the async sweep can still be reading the process table
when it fires. The deadline now SIGKILLs each live session's process
group synchronously before exiting (node-pty roots lead their own
group), falling back to the root pid when the group kill is refused.
POSIX only.

Refs #34410
Refs #33870
2026-10-27 18:36:30 -07:00
Younghwan ChaeandNeil e8d57136a2 fix(runtime): re-list a repo's worktrees when repo.update changes visibility (#32783)
* fix(runtime): re-list a repo's worktrees when repo.update changes visibility

`orca repo set --external-worktree-visibility show` (and any other repo.update
client) only emitted repos:changed, so the host renderer kept stale
`visible: false` worktree rows and the newly shown worktree vanished from the
sidebar. The settings controller now also emits worktrees:changed for the repo
when a visibility input changes, without invalidating cached Git scans.

Fixes #32764

Co-authored-by: Younghwan Chae <chyohw97@gmail.com>

* fix(runtime): declare notifyWorktreesChanged on RuntimeConstructionServices

The repository settings controller is built in OrcaRuntimeWithPreservedBranchCleanup,
but notifyWorktreesChanged is implemented further down the chain in
OrcaRuntimeWithGetStatus, so the base class needs the abstract member to typecheck.

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-27 18:30:12 -07:00
6631e5f6b6 fix(i18n): give each reused localization key its own sentence (#34648)
* fix(i18n): give each reused localization key its own sentence

Thirteen keys were shared by call sites that say different things. The
catalog holds one value per key, so the second site rendered the first
site's text: "Move X left" on the right-move button, "Use X repo color"
on the icon picker, "lines above" on the expand-below control, "Open X
issues" on the open-in-Linear action, and similar for status icons,
reclaimable workspaces, remote uploads, automation destination notices,
the add-project button and MiniMax credential errors.

Split every divergent site onto its own key with translations in all
locales, sync the runtime-required catalog, and make
verify-localization-catalog reject a key reused with different fallback
text so the collision cannot come back.

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

* fix(i18n): pluralize the remote upload toast with i18next count

The toast passed an English "s" as {{value1}}, so ja/ko/zh rendered
"ファイルs", "파일s" and "文件s". The catalog gate requires every locale to
keep the source placeholders, so dropping {{value1}} per locale is not an
option; use count plurals (_one/_other) like nativeDropTempCopyFailed.

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

* docs: add one-line docstrings to the functions this change touches

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

* fix(i18n): reject reused fallback keys before --fix writes en.json

A missing key used with two different fallback sentences had its first
sentence written to en.json before the reused-fallback check failed.

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

---------

Co-authored-by: Matheus Breguêz <matbrgz@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 18:28:23 -07:00
38a9e5c4b8 fix(i18n): translate the main-process startup and recovery dialogs (#34608)
* fix(i18n): translate the main-process startup and recovery dialogs

The GPU safe-graphics prompts, the profile-state recovery and write-failure
dialogs, the startup failure box and the local-build switch built their copy
from English literals, so they stayed English in every UI language. They now
read it through translateMain(), with entries in all six catalogs.

The GPU prompts built their options once at import; they now build them when
the dialog opens, so the language active at that moment applies.

Some of these dialogs can open before main i18n finishes initializing. Until
then translateMain() returned its fallback with {{placeholders}} unfilled; it
now fills them from the options, so the pre-init English copy stays correct.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(i18n): keep main-i18n out of the profile-state dialog's Bun test graph

The profile-state recovery dialogs imported translateMain directly. Their unit test runs in the Bun node-server suite, so that import pulled main-i18n, Electron's app and the renderer locale catalogs into the node-server build graph. config/scripts/node-server-change-scope.test.mjs forbids renderer files there, and the Bun build also failed on the duplicate "sessionNotRestored" key the es/fr/ja/ko/zh catalogs already carry.

The dialogs now take the translator as a dependency, like showMessageBox. src/main/index.ts passes translateMain; the dialog test passes an English translator that fills the {{time}} placeholder the way translateMain does before i18n loads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(i18n): read fallback placeholders by index instead of Reflect.get

anti-slop's no-reflect-get flags the placeholder lookup in interpolateFallback. i18next's TOptions already carries a string index signature, so a typed `options[name]` read into `unknown` replaces Reflect.get with the same behaviour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(i18n): finish localizing the main-process recovery dialogs

- Translate the profile repair dialog main added (chooseProfileStateRepair),
  including its section labels; CLI and log callers keep English labels.
- Use liveTerminals_one/_other plural keys for the local-build terminal count.
- Retranslate the recovery-not-applied title after main changed its copy.
- Add pt catalog entries for every new key.
- Keep only the pre-init interpolation test; drop the test that pinned
  Spanish catalog strings.

Co-authored-by: outof-place <contact@outofplace.space>

* fix(i18n): keep en-runtime-required.json to main plus the new plural keys

* fix(i18n): localize startup dialogs that fire before settings load

Profile-state recovery, preflight failure and recovery-not-applied dialogs
can show before the settings store sets the main UI language. Resolve the
system language first so they no longer fall back to English.

* fix(i18n): keep the uncached pre-ready language read inside the guarded lookup

* fix(i18n): keep the macOS/Windows preflight failure exit synchronous

---------

Co-authored-by: outof-place <contact@outofplace.space>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 18:28:17 -07:00
beb53a7f9e ci: keep hourly and daily macOS builds on Blacksmith (#34640)
Hosted macos-15 fails afterPack's packaged x64 terminal host boot check
(hourly run 40608643418: timed out waiting for the PTY), so no
latest-mac.yml is published. Windows, template qualification and routing
jobs stay on hosted runners.

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-27 18:18:20 -07:00
OrcaWinandOrcaWin 2cd3e6d6a3 ci: narrow churny cache keys and move PR-local cache saves to main (#34600)
* ci: narrow native, launcher and packaging cache keys and stop PR-local saves

* ci: hash Rust crate sources, not build outputs, in launcher and hook keys

* ci: fail closed on unlocked native packages and cover transitive key inputs

* ci: resolve the Windows Rust cache scope with a script, not a nested local action

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 18:14:29 -07:00
OrcaWinandOrcaWin 17667304b9 fix(i18n): render revised UI copy and fail on fallback drift (#34642)
* fix(i18n): render revised UI copy and fail on fallback drift

en.json kept stale values for 47 translate() keys whose source fallback was
rewritten, so the revised copy never rendered in any language.

- Adopt the newer source copy into en.json and retranslate zh/ko/ja/es/fr/pt.
- Move copy whose meaning changed to new keys (artifacts sign-in, remote host
  access link / name, ssh config picker, jump palette automation label, Claude
  subagent navigation hint) so stale translations cannot survive.
- Split five keys shared by different sentences at the sites the extractor
  flagged (activity group-by, automation destination notices, Linear open
  action, workspace options label).
- Fix catalog/source mistakes: onboarding "notify you know" typo, crash report
  double dot and missing space, mobile search keyword "app", remote server
  restart warning repeating "live tabs", and stale source fallbacks where the
  catalog was right (Ghostty import, Skills button, too-many-changes banner,
  card style and chat UI descriptions).
- verify-localization-extraction now fails on fallback drift and prints the
  en.json and source values with how to fix it.

Closes #25572

* fix(i18n): translate es live tab/pane counts used in server restart warning

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 18:09:28 -07:00
LANG LIUandNeil 5904b204b4 fix(sidebar): open retained agent rows whose exact pane still has a live PTY (#20804)
Retained sidebar rows were unconditionally inert, so an idle agent whose
live status was dropped could not be navigated to even though its terminal
was still running. Gate activation on the runtime PTY map for the exact
stable leaf (stablePaneHasLivePty) so sleeping panes, whose layout bindings
survive sleep, stay inert and are never resumed implicitly.

Fixes #20803

Co-authored-by: Neil <neil@stably.ai>
2026-10-27 18:08:37 -07:00
OrcaWinandOrcaWin 130781678d fix(i18n): localize editor context menu, diagnostics upload dialog and slash-command descriptions (#34641)
Refs #32207

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 18:00:26 -07:00
06008d20df fix(i18n): select whole count sentences at twelve more call sites (#27974)
* fix(i18n): select whole count sentences at twelve more call sites

Twelve strings were a noun plus a plural ending passed as an
interpolation value, so every non-English locale got a Latin "s" glued
to its own noun whenever the count was not one. Each call site now
chooses between two whole sentences with a single {{count}}, the same
Singular/Plural pattern #27907 uses for three Settings strings.

ja, ko and zh drop the stray ending. es and fr keep what they rendered
for one and get grammatical plural sentences. The "commits ahead"
tooltip in the compare summary takes the wording the catalogs already
have for the identical sentence, because three translations treated the
ending as an object. Legacy keys stay in the catalogs.

Fixes #27973

* fix(i18n): name commits in Korean in the compare-summary tooltip

The Korean pair for the "commits ahead" tooltip was copied from
compareBaseCommitsAheadOne/Other, which keep the English "commit" and
"commits". Both sentences now say 커밋 with the 개 counter, the form
#27907 uses for its Korean count sentences, so the tooltip no longer
carries an English plural.

The locale test also requires ja, ko and zh to use one sentence for
both counts. It fails on the previous Korean pair.

* fix(i18n): take the Korean count sentences from the audit review

Eleven of the twelve Korean pairs still put the number before the noun
with no counter ("3 라벨"), the order left over from dropping the
English plural ending. They now read noun, number, counter
("라벨 3개", "멤버 3명"), the wording abruption supplied in review of
#27974. The "commits ahead" pair already had that form and is unchanged.

Three sentences are also reworded to follow the English: a failed
discard no longer says the files could not be deleted, the group toast
says how many of the contained projects remained, and "restricted key"
is spelled out as an API key with restricted access.

The 22 values are recorded in locale-ko-key-overrides.json, next to the
other reviewed Korean strings.

* chore(i18n): credit the Korean count sentences to their reviewer

The Korean wording applied in f6bbcaf341 was supplied by abruption as a
patch in review of #27974 and went in byte for byte. That commit could
not carry a co-author trailer, so this empty one does.

Co-authored-by: YEONGUK LEE <contact@abruption.dev>

* fix(i18n): drop the twelve keys the count sentences replaced

No call site reads the twelve noun-plus-ending keys any more, so they
leave the six catalogs, as #28185 did for the keys it replaced. With
them go the three Korean override entries for those keys and the one
value ko-ui-semantic-mistranslations.test.ts pinned for the old
sshImportSynced text; the sentence it guarded is now the
sshImportSyncedSingular/Plural pair, covered by
count-sentence-locales.test.ts. en-runtime-required.json is
regenerated.

* fix(i18n): give remote drop uploads their own count keys; drop low-value tests

- Remote terminal drop uploads used the runtime keys with a different
  fallback ('to remote…'); split them into uploadingFileToRemote{Singular,Plural}
  with translations in all catalogs.
- Remove mock-translate tests and the catalog-pinning count-sentence test;
  verify:localization-* already enforces key and placeholder parity.
- Trim the 'why whole sentences' comments.
- Regenerate en-runtime-required.json.

* fix(i18n): natural zh/ja count strings and es host-import wording

---------

Co-authored-by: YEONGUK LEE <contact@abruption.dev>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 18:00:21 -07:00
OrcaWinandOrcaWin 3b4ae900d3 test: remove 19 low-value cases and three suites (#34628)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 17:57:15 -07:00
Neil 4d3451601a refactor: group native chat live presentation (#34638) 2026-10-27 17:56:38 -07:00
BoyceandOrcaWin 74556bcce4 fix(i18n): translate 229 missing Simplified Chinese UI strings (#34478)
* fix(i18n): translate 229 missing Simplified Chinese UI strings

Complete the zh-CN catalog for recently added surfaces: browser
history, agent permission settings, worktree/workspace management,
cookie import toasts, SSH host server dialogs and source-control
dropdown states. Terminology follows the existing catalog.

11 status-bar credit/balance keys stay untranslated on purpose:
provider-credit-balance-locales.test.ts keeps target catalogs sparse
and interpolates English copy at runtime.

Fixes #34439

* fix(i18n): translate remaining Simplified Chinese strings and unify workspace sleep wording

Add zh translations for the 83 English keys added since this branch was
cut (browser extensions, experimental pane, blocked-page load failure),
leaving the sparse status-bar balance keys untranslated as required.
Use 休眠 consistently for workspace sleep in the sidebar and terminal
close dialog.

* i18n(zh): translate WSL Windows-drive refusal strings

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 17:51:49 -07:00
OrcaWinandOrcaWin 8482a3fbb1 fix(i18n): localize source control count and force-push tooltips (#34639)
Shared remote dropdown builders take count/force-push sentence builders
through RemoteDropdownCopy; English defaults keep mobile and older callers
byte-identical. Desktop uses whole-sentence {{count}} plural keys, also for
the primary action tooltip. Refs #34144.

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 17:51:44 -07:00
YEONGUK LEEandOrcaWin 7efd2c4b7a fix(i18n): localize desktop Setup terminal titles (#28602)
* fix(i18n): localize desktop setup terminal titles

* test(runtime): declare orcad's English Setup terminal title in composition parity

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 17:51:39 -07:00
OrcaWinandOrcaWin 1c58ed636c fix(i18n): localize keyboard shortcut titles, groups, and filters (#34610)
* fix(i18n): localize keyboard shortcut titles, groups, and filters

Shortcut action titles and group names came straight from the English
keybinding registry, so the Shortcuts pane, settings search, conflict
warnings, and the terminal shortcut capture toast stayed English in
every locale. The Status filter labels were also hardcoded.

Add a renderer-side copy map with literal translate() keys that
`satisfies` every static action id, one {{agent}} template for agent
tab titles, and a group map. Localized definitions keep the English
title and group as search keywords so English queries still match.
Plugin titles pass through unchanged, and the plugin keybinding consent
preview keeps English registry titles so language packs cannot relabel
the actions a plugin shadows.

Translate the new strings for zh, ja, ko, es, fr, and pt.

* test(i18n): assert shortcut localization behavior without pinning catalog strings

* style: format keybinding title copy test

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 17:51:34 -07:00
Žan MlakarandNeil 7bcaee270d fix(integrations): stop treating any "already" failure as a successful issue/MR close or reopen (#28777)
GitHub issue close/reopen swallowed every gh failure mentioning "already"
(e.g. "API rate limit already exceeded"), and GitLab MR close/reopen did the
same for glab. gh exits 0 when the issue is already in the target state, so
every gh failure is now reported. For glab, only its fixed no-op refusals
("this merge request has been closed" / "this merge request is already open")
count as success; "has already been merged" and all other errors surface.

Fixes #28766

Co-authored-by: Neil <neil@stably.ai>
2026-10-27 17:42:24 -07:00