* fix(mobile): restore lifecycle-safe terminal accessory repeat
The selection hook replaced the established repeat controller with unbounded timers, allowing held keys to cross terminal/client changes. Reintegrate the existing pinned sender, lifecycle cancellation and backpressure.
* test(mobile): stop held terminal input after refusal or disconnect
* test(mobile): give accessory-repeat harnesses the PTY modes and focus hook main now reads
* fix(mobile): stop a held key when its strip unmounts and repeat through the current live-input mode
* fix(mobile): type the accessory repeat sender's input
* refactor(mobile): read app state live in the accessory repeat gate
* test(mobile): pin the key bar's held press to the repeat controller's first send
---------
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
* fix(i18n): localize provider failures and remaining shared UI copy
Translate Linear/Jira failures at the renderer boundary while preserving
English errors and adding optional stable codes for existing clients.
Localize host status, source-control metadata, discard confirmations,
native dialogs, and catalog gaps across all six shipped locales.
Inventory and reproduction paths contributed in #32207.
Co-authored-by: Seiji Kohara <9543980+seijikohara@users.noreply.github.com>
Co-authored-by: Evgenii <413011+smwbev@users.noreply.github.com>
* test(i18n): assert stable task provider failure codes
* test(i18n): exercise goal notice fallback with an older language pack
* fix(i18n): address provider error and live title review findings
Handle missing remote error text, type provider codes and keep codes intact
through connection tests and optimistic edits. Fix the remaining Linear
load fallback and redundant translation calls, apply Japanese wording
corrections, and refresh native titles on language changes.
Co-authored-by: Seiji Kohara <9543980+seijikohara@users.noreply.github.com>
* fix(i18n): finish provider and shared-copy localization for all locales
- Translate the branch's new keys into de, ru, tr and zh-Hant (added to main
after this branch was cut), with Russian few/many forms.
- Use whole sentences for file deletion failures and the mixed batch notice
instead of inserting a 'Recycle Bin'/'Trash' word, and count keys for the
batch titles.
- Translate the workspace/activity options filter count (was a shared
'N filter(s)' literal) at the renderer call sites.
- Use _one/_other families for MCP server counts, untracked-file discard copy,
resume-chat counts, discard file counts and visible host counts.
- Localize PR/MR card status labels, the unsupported-agent hint, Linear title/
description update failures, the Linear 'showing first N' notice and the
Linear Connect button.
Co-authored-by: Evgenii <413011+smwbev@users.noreply.github.com>
---------
Co-authored-by: Seiji Kohara <9543980+seijikohara@users.noreply.github.com>
Co-authored-by: Evgenii <413011+smwbev@users.noreply.github.com>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(i18n): re-render memoized translated UI on language and catalog changes
Memoized components and useMemo/module caches that call translate() kept the
previous language after a switch or a plugin language-pack reload, because
react-i18next only re-renders components that subscribe themselves.
Add useUiLanguageRevision() (language + catalog revision via
useSyncExternalStore), call it in the 34 memoized translating components, key
translated useMemo results and module caches on it, and add a ratchet so new
memoized translating components must subscribe.
Fixes the composer pickers reported in #34783.
Co-authored-by: Vyacheslav Scherbinin <14369958+gentslava@users.noreply.github.com>
* fix(i18n): drive the language revision from the resource version
Tests that mock @/i18n/i18n have no i18n instance, so subscribe through
translation-resource-version (now also bumped on languageChanged) and give
useSyncExternalStore a server snapshot for renderToString tests.
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Vyacheslav Scherbinin <14369958+gentslava@users.noreply.github.com>
* Add repeatable Google cookie clearing for browser profiles
* Bind Google cookie clearing to its successful import target
* Repair cookie-clear contract test boundaries and positive flows
* Correct cookie-clear test fixture typing
* test: use checked runtime bindings in browser profile RPC fixture
* test: provide page registry to sidecar forwarding fixture
* fix: use selected browser profile owner for cookie clear menus
* test: provide confirmation context to link materialization fixture
* test: classify cookie clear session channels
* refactor: consolidate browser command property guards
* test: update cookie clear fixture for merged registry API
* feat: move Google sign-in recovery to browser settings
* Freeze Settings Google cookie clearing intent and refresh availability
* Restore baseline browser import disclosure coverage
* Pass the selected browser profile into cookie settings
* Localize Google cookie settings search keywords
* Update cookie restart fixture policy exports
* Keep localized browser search results visible
* fix: make Google cookie clear a red right-aligned setting that explains when to use it
* Explain why Google cookies are handled separately in the clear row caption
* Recover brief Linear read transport failures
* Preserve safe diagnostics across runtime Linear reads
* Match existing attachment metadata in read contract
* Use a checked runtime in Linear CLI integration tests
* Keep Linear CLI integration coverage in the backend test graph
* Use integration test ownership for Linear CLI recovery coverage
* Observe empty Linear recovery traces before the first sink write
* fix(browser): free the page queue when an eval's caller disconnects
A local orca eval whose caller gave up (CLI timeout, Ctrl-C) kept the
page's serial command queue until its script settled, so a never-settling
script blocked every later browser command on that page. The unix socket
now forwards its disconnect signal to browser.eval, and the eval stops
waiting on abort; the abandoned script keeps running.
* test(runtime): pin local disconnect forwarding for browser.eval
* refactor(runtime): name the methods that get the local disconnect signal
* fix(ssh): offer Remove from Orca for SSH workspaces this app cannot delete
Refs #34677
* fix(ssh): retire delete-remote copy for unserved SSH workspaces
Refs #34677
* fix(ssh): retire delete-remote copy in the German catalog
Refs #34677
* fix(ssh): drop a stale host-removal comment
Refs #34677
* test(ssh): pin that an unserved SSH twin is only forgotten
Refs #34677
* test(ssh): build the unserved-twin store without a cast
Refs #34677
* test(ssh): type the unserved-twin store state
Refs #34677
* feat(files): show images and PDFs from the byte-range preview path, local and remote
ImageViewer and PdfViewer read an orca-media:// URL instead of base64 when
the editor opens a file: images load by URL, and pdf.js reads PDFs by
range, so a large PDF no longer arrives whole through files.readPreview or
pages into memory first. The grant mints only for the editor's viewers;
diffs, git blobs, markdown images and the paired web client keep base64.
- Minting an image checks its header with the base64 path's rule, so an
image proven too large to decode safely never reaches the decoder.
- orca-media gains fetch and CORS privileges, and range responses expose
Accept-Ranges and Content-Range, so pdf.js can read by range.
- Grants report the file size the viewer used to estimate from base64.
* fix(files): read PDFs by real range, check served images, keep old hosts' previews
- pdf.js fetches ranges only over http(s), so an orca-media URL was read
whole; a small range transport now hands it bytes=a-b reads from the
grant, and a failed range fails the preview instead of hanging it.
- A file can change after mint, so the raster header is checked again on
the bytes each image request serves (the same handle locally).
- A paired host without files.readChunk falls back to files.readPreview
for images and PDFs, as before; only audio and video need the update.
* fix(files): mint no local document grant for a renderer gone mid-mint
The local image/PDF mint is async now (authorize, open, header check), so
a renderer that closed meanwhile could leave a grant no teardown drops. It
binds the renderer lifetime first and refuses to mint once it's gone, as
the remote mint does.
* fix(files): keep the 50 MB cap for URL images, and probe remote headers incrementally
An <img> decodes the whole file, so image grants keep the base64 path's
50 MB cap; PDFs, now read by range, don't need one. A remote header probe
reads only each wider step's new bytes instead of re-reading from the
start. A replaced image of a different size resets its zoom.
* fix(files): tear down a PDF load whose range failed, and read 256 KiB ranges
A failed range only showed the error and left the pdf.js task, its worker
and pages alive; it now destroys that load first. pdf.js asks for one
readChunk-sized range at a time instead of 64 KiB, and a Range preflight
gets its CORS answer instead of 405.
* fix(files): read PDF ranges over IPC so orca-media needs no fetch or CORS
orca-media had gained supportFetchAPI and corsEnabled, and its responses
sent Access-Control-Allow-Origin: *, so pdf.js could fetch ranges. That let
any default-session frame holding a token read the bytes. pdf.js now reads
a PDF grant's ranges through fs:readPreviewRange, which main answers only
for the renderer that minted the token. The scheme is back to element-load
privileges (standard, secure, stream), responses carry no CORS headers, and
preflights get 405.
Grant bookkeeping moves to preview-grant-registry.ts. An e2e spec proves an
orca-media URL can't be fetched from the app page or read from another
session.
* ci(e2e): route the byte-range preview sources to the viewer specs that exercise them
Changes to the orca-media grant path, the image/PDF viewers and the PDF
range transport now run the PDF reload, rebuild and find specs, the remote
large-file open, media playback and the partition-isolation spec.
* fix(files): stop PDF range reads on abort, abort remote reads with the window, fix the CI route
- A closed or reloaded viewer schedules no further pieces of a merged range,
and a remote range read stops when its window goes away.
- A URL without a size is an error rather than a fetch the scheme refuses.
- The PR guard job's sparse checkout and its admission test include the new
route module, and the route now also covers the mint handler, preload
bridge, editor surfaces, scheme registration and read client.
* fix(files): classify linked previews by their target, keep range reads off stopped WSL distros
A local image or PDF reached through a link is classified by the file it
resolves to, at mint and on every read, and a re-mint after the link is
retargeted gets a fresh grant of the new type. A paired host's document
whose first bytes are another kind defers to files.readPreview, which
classifies the target there. Lazy local reads go through the non-waking
WSL guard, as fs:readFile and fs:readFileChunk do.
When the pinned Bun binary is installed but its version probe times out,
required typecheck callers (repo typecheck, CLI build, E2E and mobile
typecheck) now report the timeout with the executable path and budget
instead of claiming Bun is missing. Each caller suggests its own compiler
fallback. Optional Bun consumers keep returning null and falling back.
Fixes#33707
Co-authored-by: Neil <neil@stably.ai>
Pass --literal-pathspecs to the per-file diff in collectAddedLineRanges so
filenames containing glob characters (e.g. `[ab].tsx`) match only
themselves instead of also pulling in other files' hunks (e.g. `a.tsx`).
Covers both edits and renames with real-Git regression tests.
Fixes#34784
Co-authored-by: Neil <neil@stably.ai>
Only srcset is a comma-separated candidate list; src and href hold one URL.
Splitting them invented missing targets for paths containing commas or
spaces and could pass a missing target whose fragments were tracked.
Fixes#34815
mobile/ is a separate pnpm project, so the root pnpm install leaves
mobile/node_modules empty and pnpm build failed in build:mobile-web with
hundreds of unexplained esbuild "Could not resolve" errors.
build-mobile-web-app-bundle now checks for mobile/node_modules first and
exits with the one command that fixes it. The probe path is shared with
the bundle tests' dependency check. CONTRIBUTING documents the install in
Local Setup and points "Before Opening a PR" at it.
Fixes#24324
Co-authored-by: Neil <neil@stably.ai>
* fix(wsl-host): Remove keeps a WSL host's server that another Orca on this computer shares
A WSL host's server lives in the Linux login's ~/.orca, so two Orca installs on
one Windows account (stable and dev), or a desktop reaching the distro over SSH,
share one server. Remove used to close every terminal there and decommission
it, ending the other desktop's sessions and agents.
The server now answers host.peerDesktops (capability orcad.peer-desktops.v1):
the other desktops paired with it, whether each is connected now, and when each
was last seen. Before tearing anything down, Remove asks. If another desktop is
connected or was seen in the last 30 days, it only closes this desktop's bridge
and reports the server kept. If the server can't answer, the teardown is
refused. An older server without the capability gets today's teardown, plus a
warning.
* fix(wsl-host): a shared Remove releases this Orca from the host, and says the host keeps running
- On a shared server, Remove now revokes this desktop's own device (and its
phones) on the host, through the new host.releaseDesktop, before letting go.
Otherwise its recent row would make a peer's later Remove keep the server
forever. Peers also skip rows of the caller's own installation (same caller
fingerprint), such as an enrollment from before a re-add.
- The renderer says when a removal kept the host for another Orca, and the
Remove confirmation no longer promises to end terminals unconditionally.
- Remove-path files now route CI to the fake transport e2e spec, which
exercises Remove.
* i18n(wsl-host): Russian and Traditional Chinese for the shared-host Remove copy
* fix(wsl-host): tell desktops apart by device key, and release this Orca on every Remove
- Peers are no longer hidden by caller fingerprint. Every device-key fork of
one pairing token row inherits that row's fingerprint, so two installations
that paired through one code hid each other, and one's Remove could tear the
server down under the other. Only a shared device public key now marks the
same installation, as with a re-add under a new token.
- This desktop's own device is released as the last call over the bridge on
every Remove that proceeds (no terminals left), not only a shared one. A
later Add then Remove never meets a stale row of ours.
- When the release fails on a shared Remove, the toast says the host may keep
running for a while. The Russian confirmation copy is corrected.
* fix(wsl-host): move undo refuses a removed host whose server another Orca kept
A Remove that kept a WSL host's server for another Orca (R39) now records
that. Before, undo of a move onto that host read the missing environment as
"its terminals and automations stopped with it", and would have restored the
project here while the host's copies kept running, duplicating its agents and
automations. Undo now reads such a host as unreachable and refuses, as it does
for any host it can't reach. A later Remove that proves the server stopped
clears the record. If the record can't be saved, the host stays listed as
pending instead.
* i18n(wsl-host): Turkish for the shared-host Remove copy
* fix(wsl-host): each WSL desktop splits off its own row from orcad's startup pairing
orcad publishes one startup pairing offer and never rotates it, so every
desktop that enrolled through it shared one token row and device id. Stable and
dev Orca on one distro were invisible to each other as peers, and releasing that
shared row broke the next re-add and pairing repair.
- New host.forkDesktop (same orcad.peer-desktops.v1 capability). A runtime
device on a shared desktop token mints its own token row, forked from it and
named by the caller. Forks share the device-key fork cap, and a released fork
frees its slot.
- The WSL desktop forks right after enrolling through the startup offer, or
after a repair lands back on it, and keeps the forked pairing (named after its
userData folder, e.g. Orca or Orca Dev). On an older orcad, or if the fork
fails, it keeps the shared pairing and warns.
- A token row that desktops forked from counts as a peer only while something
is connected on it. One that nobody forked from keeps the last-seen rule.
* fix(wsl-host): keep host.forkDesktop to the startup offer's holder, over E2EE, with a clean label
host.forkDesktop mints a credential, so:
- only the holder of the desktop pairing offer this server advertised last
(orcad's startup offer) may fork; an older offer, a fork, a phone or any
other row is refused;
- the fork takes exactly the caller's grants and reach; params are just a label
(strict, so a grant request is refused);
- it runs only on an authenticated E2EE socket; the token goes back only in
that reply and is never logged;
- the label is display-only: control, zero-width and bidi characters stripped,
whitespace collapsed, at most 64 characters; an empty result is refused.
* refactor(wsl-host): count only Orcas connected right now as WSL host peers; drop fork and release
Review found the token fork and self-release add more identity state than R39
warrants: orphan forks, a desktop left on the shared token releasing it, and a
changed caller fingerprint. So this drops host.forkDesktop, host.releaseDesktop
and the 30-day last-seen rule, and restores the restart test.
A peer is now another Orca installation with a control socket open on the
server right now. The desktop declares its installation (a hash of its userData
path) with host.declareClient on its shared-control socket after every ready,
including reconnects. The server tags that socket and drops the tag when it
closes. host.peerDesktops takes the caller's installation id, so its own other
sockets (multiplex, one-shot) never count, and two desktops sharing one startup
token stay distinct. Undeclared sockets, phones and browser sessions are
ignored. Peer names go through sanitizeDeviceLabel, and a startup-offer
"CLI <date>" name shows as "another Orca". An older orcad lacks the method:
nothing is declared, and Remove keeps today's teardown with a warning.
Limit: an Orca that's closed while its terminals keep running isn't protected.
* fix(wsl-host): keep a reconnecting Orca as a WSL host peer for 60 seconds
Lost contact isn't proof an Orca left. When an installation's last declared
control socket closes (a bridge exit, an orcad restart, resume from sleep,
reconnect backoff), the server keeps counting it as a peer for 60 seconds, or
until it re-declares. A Remove inside that gap no longer tears down its
terminals.
* fix(wsl-host): say an open Orca keeps the host, and name each peer once
- The Remove confirmation now reads "Unless another Orca that's open on this
computer is using it", since only a connected Orca keeps the server.
Updated in all 10 locales.
- The kept-host toast dedupes peer names, so several unnamed peers read
"another Orca" once.
* test(wsl-host): pin the reconnect grace to the last socket's close, and to the server's own forget
- The unit test advances the clock between the two closes and checks that the
peer still counts when the first close's grace would have run out.
- The integration test waits until the server has dropped the closed socket
before reading peers, so it fails without the grace (checked by setting the
grace to zero).
* fix(wsl-host): sweep expired reconnect graces on every close, on a monotonic clock
Expired graces are dropped at the start of forget() as well as peers(), so the
map stays bounded even when nobody asks for peers. The default clock is
performance.now(), so a wall-clock jump can't stretch or cut the grace.
* i18n(wsl-host): German for the shared-host Remove copy
* feat(i18n): add German UI language
Register German (de) as a built-in UI language alongside en, zh, zh-Hant,
ko, ja, es, fr, pt, ru and tr: language setting, system-locale detection
(de, de-DE, de-AT, de-CH, de_DE.UTF-8), renderer and main-process lazy
catalogs, Settings picker label in every catalog, the "Sprache" settings
search keyword, onboarding picker, docs and a German glossary and style
guide (config/i18n-german.md).
de.json covers every en.json key except the sparse-policy entries that must
fall back to English (status-bar credit balance copy and the incomplete goal
change notice). Tests that used de-DE as the unsupported system language now
use it-IT.
The catalog is machine-translated, reviewed by two independent passes with
arbitrated fixes, and swept for terminology consistency.
* feat(i18n): translate German strings added on main
Translate the 158 keys main added since the branch point (WSL project move,
diff size limits and others), reviewed by two passes like the rest of the
catalog, and drop the 5 keys main retired.
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(agents): open existing chat and retained pane destinations
* fix: keep agent navigation scoped to the current destination owner
* test: initialize dashboard tabs without a type assertion
* fix: complete navigation test and English catalog contracts
* fix(terminal): forward copies from Codex started in remote shells
* test(terminal): capture PowerShell copy probe exit status
* fix(terminal): preserve configured shell arguments for remote profiles
* test(terminal): isolate profile result from system startup output
* test(terminal): run native daemon shell probes with Node
* fix(terminal): keep an inherited remote-viewer marker out of local panes
An Orca started from a remote viewer's pane inherits ORCA_REMOTE_VIEWER_CLIPBOARD,
which would make every local Codex fake an SSH session. Treat the marker as
per-spawn identity so only explicit launch env carries it.
* feat(i18n): add Russian UI language
Register `ru` as a built-in UI language using the catalog from #33370,
reconciled against current en.json: keys pruned from English were dropped,
stale translations whose English source changed were removed, and the
catalog verifier now accepts CLDR few/many forms for an existing `_other`
family.
Co-authored-by: Vyacheslav Scherbinin <gentslava@mail.ru>
* fix(i18n): drop Russian _one entries superseded by count placeholders
Main now interpolates the count in English _one strings (#34693), so the
literal-1 Russian forms no longer match their source.
* feat(i18n): complete Russian catalog and add terminology glossary
Translate the 650 remaining English keys and the few/many plural forms for
every count family, and document the Russian glossary and style conventions.
* fix(i18n): apply reviewed Russian translation corrections
Fix mistranslations and grammar flagged in a full-catalog review, and align
terminology with the glossary: рабочее дерево for worktree, рабочее
пространство for workspace, пул-реквест and файлы cookie.
* fix(i18n): resolve Russian plural forms in main-process strings and align ru terminology
translateMain now resolves literal _one/_other keys through the CLDR plural
family like translate(), so native dialogs pick Russian few/many forms. The
plural helpers move to src/shared for reuse. apply-translation-delta accepts
locale-only plural categories sourced from English _other. ru.json follows the
glossary for account/session/prompt and uses a single ellipsis character.
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Vyacheslav Scherbinin <gentslava@mail.ru>
* fix: bound local iteration checks and show live progress
* fix: harden check lifecycles and cached test evidence
* fix: preserve process instrumentation and fixture lifecycles
* fix: give fixture unref spy a portable function type
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* feat(i18n): add Traditional Chinese (zh-Hant) UI locale
Adds 繁體中文 as a selectable UI language and derives its catalog from
zh.json rather than translating English afresh, so the reviewed Simplified
terminology and Orca glossary decisions carry over.
The conversion needed three corrections beyond OpenCC's `cn -> twp`:
- One pass only. `twp`'s phrase dictionary is not idempotent (it maps
進程->程序 and 程序->程式), and iterating to a fixed point makes output
worse: it rewrites the correct Taiwan 游標 to 遊標, and 程序 to 程式 where
the source meant an OS process.
- 16 terms where `twp` emits a form Taiwan does not use, each confirmed
against Microsoft's VS Code zh-Hant language pack (許可權 0 vs 權限 84,
賬戶 0 vs 帳戶 111, and so on).
- 進程 vs 程序 collapse in `twp` output. Taiwan calls an OS process 行程, so
the generator consults the Simplified source to disambiguate; daemon copy
reads 守護行程, not 守護程式.
Also pins `load: 'currentOnly'` on both i18next instances. i18next's default
expands `zh-Hant` into ['zh-Hant', 'zh', 'en'], so a key missing from the
Traditional catalog would silently render Simplified.
zh-Hant.json is generated and compared byte-for-byte, so the new check is
wired into verify:localization-catalogs to fail when a Simplified edit skips
regeneration.
Refs #10592
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(i18n): fail the zh-Hant check on an unreadable zh.json
The generator caught every error from reading and parsing zh.json, so a
malformed or unreadable Simplified catalog logged "absent here" and exited
0 without ever comparing the generated Traditional catalog. Only ENOENT
means there is nothing to derive, so every other failure now propagates.
Tests cover the three paths the catch was conflating: an absent zh.json
still passes for the combined verifier's synthetic fixtures, malformed
JSON throws, and a directory where the catalog belongs throws EISDIR
rather than reporting the file as missing.
Co-Authored-By: Claude Code <noreply@anthropic.com>
* feat(i18n): ship Traditional Chinese (Taiwan) as a maintained built-in catalog
Keeps the zh-Hant locale from this PR and makes it a hand-maintained
catalog like the other built-ins, without the OpenCC generator:
- Catalog reconciled with current en.json. Per key it prefers the
Taiwan-reviewed copy from #22402 (which carries #14223), falls back to
the zh-derived copy here, and drops entries whose English changed.
A review pass then applied Taiwan terms (代理程式, 工作階段, 存放庫,
分頁, 列/欄) and 「」 quotes. Glossary: config/i18n-traditional-chinese.md.
- Label 繁體中文(台灣)in every catalog, the picker, onboarding and search.
- Detection maps zh-TW/zh-HK/zh-MO/zh-Hant* to zh-Hant; zh-CN/zh-SG/zh-Hans
stay on zh. Both i18next instances use load: 'currentOnly' so missing
keys fall back to English, not Simplified.
- Drops opencc-js, the generator scripts and their verify hook.
Co-authored-by: hungchihsueh <84211143+hungchihsueh@users.noreply.github.com>
Co-authored-by: chilung-cgu <b0423031@gmail.com>
* feat(i18n): translate remaining Traditional Chinese (Taiwan) catalog keys
Fill the 2,043 keys added to en since the source PRs branched, following
the Taiwan glossary, and extend the glossary with skill, automation,
usage, review, token and related terms.
* fix(i18n): review Traditional Chinese (Taiwan) catalog wording
Apply 629 reviewed corrections across the full catalog: 30 meaning or
untranslated-text fixes and 599 glossary and phrasing fixes (終端機, 用量,
API 金鑰, clearer counts and actions). Each edit was cross-checked by an
independent review pass.
* fix(i18n): polish sampled zh-Hant strings and drop redundant locale test
Co-authored-by: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: hungchihsueh <84211143+hungchihsueh@users.noreply.github.com>
Co-authored-by: chilung-cgu <b0423031@gmail.com>
The layout oracle merge gate keeps Blacksmith via fast_runners: true; the
advisory e2e call, nightly schedule and layout-owner dispatch run on
ubuntu-24.04.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* 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
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>
* 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>
* 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>
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>
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>
- 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>
* 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
* 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>
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>