* fix(i18n): restore identifiers and commands machine translation localized
39 values in ko, zh and es are code rather than copy — shell commands, CSS
class strings, git-style identifiers, sample filenames and hostnames — and had
been machine-translated. pnpm install read pnpm 설치, text-foreground read
文本前景, pr-view read PR视图, and localhost:3000 read 本地主机:3000.
pnpm install is the font-mono placeholder of the setup-script input, and
gh auth login / glab auth login are the commands the integration panes tell the
user to run, so the translated forms are shown to users as text to type.
Catalog-only. Running repair-locale-catalog.mjs over these locales fixes the
same values but rewrites several hundred unrelated ones, because the catalogs
are stale against the current policy.
* fix(i18n): restore the zh code strings found by call-site context
@smwbev scanned by where a translate() renders — inside <code> or a font-mono
element — rather than by value shape, and found nine more in zh: upstream read
上游 in the base-ref picker, nbformat read nb格式, orca.yaml read Orca.yaml,
LIN-329 read 林-329, GH #1799 lost its space, and orca · zsh read Orca·zsh.
The matching NEVER_TRANSLATE_VALUES entries landed with #12934.
* test(i18n): guard restored technical literals
---------
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* fix(settings): widen font size input so values are fully visible
The number input for terminal font size was too narrow (w-14) to display
two-digit values cleanly. Changed to w-24 to ensure 10-24px values fit.
* fix(settings): hide the native spin buttons clipping the font size value
The overlapping webkit spinner was what cut off the second digit, not the
box width. Adopt the number-input-clean idiom every other numeric settings
input already uses; the spinner also duplicated the -/+ steppers.
* fix(settings): keep font size stepper compact
---------
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* fix(sidebar): keep delayed workspace delete target stable
* fix(sidebar): report a stale workspace list instead of a silent delete no-op
runWorktreeDelete fails closed when the clicked row is no longer in the store
(concurrent delete, state reset, or a runtime re-pair that drops live rows).
The guard is right, but it returned with no feedback, so Delete looked broken.
Report the miss with the stale-list toast runWorktreeBatchDelete already uses,
extracted to a shared module so both paths share one description string.
* fix(sidebar): dispatch delete intent after menu close
* fix(sidebar): validate batch delete identities
* fix(sidebar): preserve delete identity through confirmation
* test(sidebar): follow delete status boundary extraction
* fix(sidebar): bound delete status hydration
* test(sidebar): register current parallel delete targets
---------
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* fix(pty): bound cooked reply queue
* fix(pty): bound cooked reply queue
Implement bounded storage for cooked-echo-safe replies: 64 pending
replies and 4096 UTF-16 code units. Shed oldest replies on overflow,
never ordinary input. Add drain failure containment with generation
fencing to prevent stale operations from clearing fresh input after
clear() reuse.
* fix(pty): report pty id in drain failures
When the async drain yields, the owner may rebind to a different PTY
before the failure surfaces. Pass the failing pty id so the transport can
ignore stale failures from a rebound owner. Also tighten the pending
reply queue size bound to prevent half-written entries.
* perf(native-chat): probe WSL transcript paths asynchronously
The WSL transcript resolvers probed `\wsl.localhost` UNC candidates with
existsSync. Those paths are served over 9P, so a stopped or unreachable distro
blocked the Electron main thread instead of falling through to the next
candidate.
- host-readable-transcript-path: the `pathExists` dep is now async, defaulting
to fs/promises.access. The per-distro loop stays sequential — the ranked order
exists so the owning distro wins, and probing every distro at once would fan
9P calls out to ones the user deliberately left stopped.
- session-file-resolver: dropped the existsSync guard outright rather than
converting it. walkSessionFiles already yields [] for a missing or unreadable
root, so the guard was redundant as well as blocking.
* perf(native-chat): stop the resolve poll mkdir-ing the Codex runtime home
codexSessionsDirs() only needs the managed home's path to scan it, but called
getOrcaManagedCodexHomePath(), which mkdirSyncs. That put a synchronous fs call
back on the 500ms-5s resolve poll the surrounding async probe work just cleared,
and materialized the runtime home as a side effect of a read-only lookup.
Use the resolve-only variant the module already exposes for this case. A missing
root walks to no matches, so behavior is unchanged.
* perf(native-chat): gate WSL transcript fs access behind a shared queue
Serializes and dedupes WSL 9P filesystem probes (access/readdir) so a
stopped or slow distro can't exhaust the shared libuv threadpool or
block unrelated local filesystem work. Routes Codex session-tree scans
and path-existence checks through the new wsl-transcript-fs-gate,
while sharing in-flight scans across concurrent callers.
* perf(native-chat): prioritize exact WSL transcript probes over scans
- Add cancellation (AbortSignal) throughout session resolution and
directory walking so an unsubscribed transcript watch stops
in-flight WSL filesystem work instead of leaking it.
- Split the WSL fs task gate into exact vs scan priority lanes with
per-route concurrency, so a live transcript access probe is never
queued behind a directory scan on another distro/provider.
- Extract Codex WSL session path scanning into its own module that
shares one root snapshot across concurrent session-id lookups and
refreshes on a shared miss to see post-start file creation.
- Skip the async WSL probe entirely for local paths via existsSync.
* fix(native-chat): harden WSL transcript cancellation
* fix(native-chat): finish transcript cancellation
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(gitlab): guard against non-array API responses in MR/issue listing
fetchIssuesAsWorkItems and listMergeRequests parsed glab's JSON output
and called .map straight on it. When the GitLab API returns a JSON
object instead of an array (error body, unexpected shape) on a
successful exit, this crashed with a bare TypeError that got
misclassified as "Failed to load issues: JSON.parse(...).map is not
a function" instead of a useful message.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(gitlab): cover listIssues and keep payloads out of error classification
The guard missed listIssues in issues.ts — the RPC-backed issue list that
produces the reported "Failed to load issues: JSON.parse(...).map is not a
function". Hoist the guard into glab-api-response.ts so both files share it.
The thrown message is fed to classifyGlabError, which substring-matches it.
A response payload is content, not a diagnostic: an MR titled "fix network
timeout" classified as network_error and the canned copy replaced the payload
the user needed. Report a GitLab error envelope by its own message, and mark
an opaque body so classification is skipped.
* test(gitlab): make the list-guard tests fail on the regressions they name
Two assertions were vacuous under mutation. The envelope test used a "403
Forbidden" message whose keyword matches earlier in the classifier chain than
its sibling payload, so leaking the payload into classification still passed;
it now uses a 404 envelope beside a "403 forbidden" sibling. No call-site test
carried a classifier keyword, so deleting the marker-error branch entirely
failed only one unit test; the MR API path now uses a keyword-bearing body.
Also give the non-list branch the same "Failed to load issues" prefix as every
other list error, cover the `{ error }` envelope field, and pin the thrown type.
* test(gitlab): pin the reported-payload bound
Removing the 300-char slice survived the whole suite, and the banner's
break-words now depends on it. Name the limit and assert both branches
truncate, plus the envelope falling through a blank message to `error`.
* test(gitlab): pin message-over-error envelope precedence
Swapping the lookup order passed the whole suite. Anchor the bound regex too
so it cannot match an incidental ": " near the end of a message.
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* fix(ai-vault): preserve agent metadata header on session row expansion
* chore(tests): remove redundant comment in AiVaultSessionRow test
* test(ai-vault): add happy-dom environment and window.api shim to AiVaultSessionRow tests
* test(ai-vault): assert the expanded session row via the rendered row
Replaces the parallel static-markup harness and second fixture with the
file's existing Testing Library row render, so both suites share one
session fixture and one prop list. Raw HTML substring matches are gone:
the identity assertions now run inside the metadata grid, because the
details-toggle button's aria-label repeats the agent name and made the
old check pass with the fix reverted.
Tags the grid with a data-testid like the row's other query anchors
rather than walking up from a text node, adds cleanup() — the suite has
no globals: true, so rows leaked across tests — and guards that the
worktree badge renders once when expanded. SessionWorktreeLine loses an
export the row no longer imports.
---------
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* fix(repo-icon): keep a renamed fork's own owner avatar
Fork repos always took the upstream owner's avatar, so a renamed fork
showed its parent project's logo. Same-name forks (personal copies)
still prefer the upstream owner; renamed forks now keep their origin
owner across auto-detect, the startup backfill, and the settings
avatar refresh.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(repo-icon): re-read repo state before backfill avatar write
The startup backfill computed icon updates from a pre-loop snapshot, so
an icon chosen in settings while the upstream/origin probes were pending
could be clobbered. Re-read the repo after the probes and only migrate
an icon that is still the auto-detected GitHub avatar.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(repo-icon): own the fork avatar rule in one shared selector
The renamed-fork rule was written out twice — once in the main-process
auto-detect and once in the renderer refresh — so the two copies could
drift. Move it next to `githubAvatarIcon` as `githubAvatarSlug`, which
collapses the renderer resolver to a single unbranched path.
Also stop swallowing a rejected origin probe: it cannot tell a renamed
fork from a same-name one, so degrading to the upstream owner would flip
a renamed fork's stored avatar back to the parent's. Letting it propagate
keeps the stored icon, matching how the non-fork path already behaved.
Adds coverage for the startup backfill, the third decision point the fix
claims, which had none.
* test(repo-icon): cover pending backfill icon change
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* fix(file-explorer): open symlink files when stat fails
* fix(file-explorer): grant symlink targets path access on activation
Following a symlink out of the workspace was denied by the main-process
path allow-list, so both the stat and the file read failed. Activating the
row is explicit intent, so authorize the target the way terminal links and
Quick Open already do.
---------
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* fix: wrap unbroken auto-rename failure output
* test(sidebar): cover unbroken auto-rename failure containment
happy-dom does no intrinsic sizing, so assert the two declarations that
keep an unbroken token from widening DialogContent's grid column. The
test fails when either min-w-0 or overflow-wrap:anywhere is reverted.
---------
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* refactor(runtime): extract pure path-candidate, review-branch, and folder-workspace helpers from orca-runtime.ts
Mechanical move of three closed, pure module-scope clusters out of
orca-runtime.ts (37,608 -> 37,207 lines) into domain-named siblings:
- terminal-output-path-candidates.ts: PTY output path harvesting and the
recent-candidate history bound (3 entry points + 15 private callees).
- selected-review-branch.ts: forge-agnostic selected-review predicates and
lookup hints (GitHub/GitLab/Bitbucket/Azure DevOps/Gitea).
- runtime-folder-workspace.ts: folder-workspace id math and the repo+meta
-> Worktree projection.
Bodies are token-identical to their previous form; the only production
changes are the moves, the new import statements, and `export` keywords.
The no-control-regex suppression travels with the path-candidate scanning
that needs it. No max-lines suppression was added and the ratchet is
unchanged.
Adds characterization tests for the two clusters that had no direct
coverage; the path-candidate cluster keeps its existing tests, repointed
at the new module.
* fix flaky timer on CI