* feat(acp): ACP agents can declare a managed runtime installed on first chat start
An ACP launch spec may name a pinned runtime Orca installs under its locked
data folder. The stock lookup searches it last (Command, PATH, install
directories, managed folder); a start whose lookup missed installs it under
the acquisition's signal, then resolves the launch again. A Command setting,
restart-recovery reads and catalog probes never install.
The pinned-runtime materializer can now publish a verified multi-member
folder: extract once into staging, check each member's sha256, rename into
place, with a per-asset download size limit. Node's runtime path is unchanged.
* Fix ACP managed runtime lookup precedence
* feat(acp): ACP agents can run with a private per-process temp folder
* feat(acp): old managed runtime versions are removed at host start
Once per host start, the ACP registration removes every version beside an
agent's current managed runtime folder, and staging left by an earlier
process. One host owns the data root and ACP servers are never reattached
across host runs, so nothing runs from them. This process's own staging is
kept (a publish may be in flight). Best effort: a failure is logged and never
holds up a launch.
* feat(acp): a sign-in prompt on an ACP agent's stderr ends the start or turn signed out
An agent that waits for a browser sign-in prints a link on stderr and blocks for
minutes. A launch spec can now name that line (signInPrompt); the connection then
closes with a typed sign-in failure through the existing process owner, so a start
reads notSignedIn and a running turn fails signed out instead of settling unknown.
The matched line, which carries the sign-in URL, never enters the retained stderr
tail, errors or diagnostics.
* Prevent sign-in URLs leaking from long stderr lines
* feat(acp): an agent can enforce the permission mode itself through a session config option
An optional launch-spec field names the session config option and its ask/full values. Every new or
reopened session gets the value for the chat's full-access posture before it is exposed, and a refusal
fails the start. With the field present Orca no longer answers permission requests for the person.
No agent sets it yet.
* fix(acp): reject unconfirmed permission mode during acquisition
* feat(acp): an agent enforcing the permission mode itself runs the chat's own mode
The launch spec maps each chat permission mode to its session option value; a start sets the
value for the mode the host launches with (ask when it names none), replacing the global Agent
Permissions full-access read. A mid-chat change takes effect through the host's existing restart
of a child launched with another mode before the next send.
* Keep ACP session acquisition within the line limit
* feat(acp): open Antigravity in the Chat UI over its ACP server
With the experimental chat setting on, Antigravity chats run Google's
Antigravity ACP server, which Orca installs on the first chat start: a
pinned 1.3.0 release whose archive and both executables are verified by
hash (macOS arm64/x64, Linux x64/arm64).
Each host keeps one private sign-in folder for these chats under Orca's
data folder, made owner-only before every start; the server keeps its
sign-in there as a file. A chat signs in with the server's Google method
through the existing auth-required retry; with no saved sign-in the
server's sign-in prompt ends the chat signed out. The Agent Permissions
setting maps to the server's own mode, and a question the server asks
shows as a question that takes one of its choices, never auto-answered.
A chat reopens its session with session/resume.
* fix(acp): separate Antigravity chat command and require pinned host
* test(acp): assert Antigravity launch arguments across hosts
* feat(acp): Antigravity chats offer Ask, Accept edits and Full access per chat
Each mode maps to the server's own (default, auto_edit, yolo). No Approve for me:
the server has no automatic reviewer. Accept edits gets its own description,
since auto_edit approves file edit tools only.
* feat(native-chat): offer host-provided chats without their CLI detected
A host now marks each agentSession.agents row whose chat program Orca provides there
(an ACP managed runtime with a pinned build on this host). With Chat UI on, the tab-bar
picker, the empty-workspace default chat and the new-agent shortcut offer those agents
beside the detected ones when the launch opens as a chat. Terminal launches still follow
detection, and an older host that sends no mark keeps today's detection-only offer.
* Fix CLI-free chat fallback and picker updates
* Fix admission refusal reason test expectation
* feat(native-chat): the host health snapshot says whether each chat's program is on the machine
Antigravity's chat runs Orca's own download of Google's ACP program, not the agy CLI, so clients
could not tell from CLI detection whether it can open as a chat. Each snapshot row now carries an
optional chatProgram (available, installable, missing, unknown), found through the agent's own
launch resolver without spawning anything, and re-derived after a managed runtime install or
removal. Health state and reason are unchanged, so older clients decode the frame as before.
* feat(native-chat): one renderer reading of whether an agent can open as a chat on its host
Pickers asked CLI detection whether an agent can open as a chat, which is wrong for Antigravity,
whose chat runs a program Orca downloads. This module answers from the host's published chat
program fact, and for hosts that publish none, from the host's managed-runtime mark and install
capability or else CLI detection. Facts not yet learned read as unlearned, not as no.
* fix(native-chat): a declined chat opens its agent's terminal only where the CLI is installed
Once a chat can be offered without its CLI, a host refusal used to fall back to a terminal that
runs a CLI that is not there. The one admission boundary now reports the host's refusal instead
when the workspace host's detected agents lack the CLI, for new-tab, work-item and workspace
creation launches alike. A caller's own fallback, like the empty workspace's shell, still opens.
* feat(native-chat): the + menu and Settings offer a chat wherever its program runs
With Chat UI on and no agy CLI, Antigravity had no chat row in the + menu (or a disabled
'Not installed' one when switched on), because both read CLI detection. The menu now reads the
host's chat-program answer: a chat row where the program runs or installs from the chat, no
'(CLI)' row or terminal button without the CLI. A failed CLI check no longer keeps the menu from
learning that answer. Settings shows an unchosen provider on when its CLI or the program Orca
runs for it (downloaded or found outside Orca) is installed, and holds the switch until that is
known. A saved default that opens as a chat no longer says it is not detected.
* feat(native-chat): a default agent that opens as a chat starts without its CLI
An empty workspace and the new-agent shortcut skipped a default agent whose CLI is not detected,
even when it opens as a chat on that host (Antigravity without agy). Both now honor that default,
waiting briefly for the host's chat facts when they are not known yet, while the automatic pick
with no default still chooses only detected agents. Blank and the shell fallback are unchanged.
* feat(native-chat): new workspaces, work items, continuations and the dashboard offer chat-only agents
The new-workspace composer, work-item launches, session continuation and the dashboard launcher
listed only agents whose CLI is detected, so an agent that opens as a chat without its CLI could
not be picked, and an explicit pick of it was dropped. They now also offer agents that open as a
chat on the workspace's host, keep such an explicit choice, and the dashboard republishes when
the host's chat facts arrive. Automatic picks still choose only detected agents.
* feat(native-chat): Source Control and Fix Checks accept a chosen agent that opens as a chat
The Source Control action dialog, its saved auto-start, the checks panel, recovery launches and
Fix Checks (before and after its workspace exists) rejected a saved or default agent whose CLI
is not detected, even where it opens as a chat. They now accept it there and list it, through
the shared source-control pick, while the automatic pick still chooses only detected agents.
* fix(native-chat): an empty workspace does not wait on chat facts for a Claude or Codex default
Claude and Codex have no program Orca downloads, so without their CLI only the host's own answer
could offer them as a chat; holding the workspace empty for the host's agent list could not
change that.
* test(native-chat): pin the chat-program fact, cold explicit defaults and the CLI-less decline
Covers the host's program lookup and its re-read after an install, older and unknown snapshot
values falling back to the managed mark and install capability of the same host, an explicit
default opening as a chat with CLI detection failed while automatic and blank picks stay on
detected agents, kept composer and saved Source Control choices, and a declined chat reporting
the refusal instead of opening a terminal for a missing CLI on both launch paths.
* refactor(source-control): the action dialog's agent list lives in its own hook
The dialog hook was over the file-length limit after it learned which agents
open as a chat without their CLI; detection and that offer now live together
in use-source-control-launchable-agents.
* fix: typed test mocks and record the dialog's pre-existing reset effect
The Source Control dialog's open-cycle reset effect was already on main; the
changed-code gate reported it because the effect now also learns which agents
open as a chat.
* fix(native-chat): a declined chat refuses its terminal only when the CLI is known to be missing
When a host declines a chat, Orca falls back to that agent's terminal. A chat can now be offered
without its CLI, so the fallback must not open a terminal that cannot start. But a failed or
still-pending CLI check is not proof the CLI is missing: treating it that way blocked a working
terminal fallback (for example an OpenCode version that runs in a terminal but not as a chat).
Each detection producer (local, paired server, SSH) now records the outcome of its latest attempt
for the exact target, next to the IDs it already keeps. The decline check refuses the terminal
only when the same host's latest attempt succeeded and did not list the CLI; pending, failed or
unresolved outcomes keep the existing fallback, and a caller's own shell fallback is untouched.
* fix(native-chat): every launch surface learns whether a chat opens before it decides
A chat whose program runs on the host without its CLI (for example Antigravity) could be missing,
greyed out or swapped for another agent depending on which surface launched it: the composer
dropped an explicit pick while the host was still answering, Fix Checks and the Checks panel
refused a saved chat agent, an inactive paired host was never asked about its chats, the Providers
switch flashed off, and session continuation auto-picked an agent that was only offered.
One lease per host, runtime and pairing now acquires the host's agent list and health stream
for whoever needs it: the + menu, composers, Settings, the dashboard and its pop-out, Source
Control and Checks, Fix Checks, work items, shortcuts and continuation. Actions wait for those
facts within one bounded deadline (ordinary CLI detection keeps its own wait), cancel when the
workspace's host is genuinely replaced, and re-read settings before launching. A saved, default
or explicit choice may open as a chat without its CLI; automatic picks still come only from
detected agents. Pending or unknown facts never erase a choice or flash a switch off.
The dashboard's holds are reconciled by the publishers that read them, and a hold that notifies
while it is being created no longer leaks a second stream holder.
* fix: the new-workspace composer memoizes its launch workspace from its own fields
* fix: a declined chat reads the launch host's own agent inventory; test fixtures match their types
* fix: work-item launches detect before waiting on chat facts; the composer and Source Control list agents from one owner
The composer card no longer holds its own chat-facts lease beside its modal's, Source Control reads agent
inventory only while its dialog is open, and a work-item launch keeps the new workspace's own detection
when the store has not loaded that workspace yet.
* fix(native-chat): keep acquisition tied to the current host resource
* fix(native-chat): verify the current acquisition before returning choices
* fix(i18n): retain legacy terminal-list English entries in the runtime catalog
* fix(native-chat): the dashboard republishes launch options when the active workspace's host changes
Both dashboard publishers now watch the active workspace and its selected host, which own its launch
options. Two QuickLaunchButton tests assert the current "couldn't list terminal agents" wording.
Restores base forms and drops a duplicate workspace helper to stay inside the change budget.
* test: the dashboard publisher test reads its watched state from the function it tests
* fix(native-chat): dashboards watch one checked route-input view; saved Source Control discovery settles every outcome
Both dashboard publishers observe and derive launch options from one exhaustive, compiler-checked view of
the store slices the launch-route readers take, so a route input can no longer be missed by one list. A
saved Source Control action's discovery now ends as success, cancelled or failed; only success can auto-start,
and a cancelled or failed attempt reveals the editable dialog instead of launching on a replacement runtime.
Restores base forms and shares one worktree workspace helper to stay inside the change budget.
* style: format the runtime metadata resolver
* fix: merge duplicate imports the code-quality gate rejects
* test: the dashboard watch fixture seeds every route-input field
* test: the dashboard watch fixture is fully typed without a cast
* fix: an unknown agent scan stays unknown for Fix Checks and recovery, and a saved Checks agent keeps its message
* fix: the action-first chat path asks the same missing-CLI check before opening a declined terminal; compact to the size cap
* refactor: compact the chat-program changes to the size cap after merging main
* Pickers list an agent that can't open here as disabled with its reason instead of hiding it
The Create workspace and continuation pickers only searched the agents the caller could launch, so an enabled agent with no chat on this host (for example Antigravity when the host can't run it) lost its disabled row, while the + menu showed it.
* Continuation test accepts more than one disabled subfolder row now that disabled rows are listed
* Re-run CI: the code scan was cancelled across the repository
* test: cover chat selection without terminal CLI
* test: compact host chat program fixtures
* refactor(wsl): move the WSL guest bundle sources out of src/relay
The WSL hook relay and WSL browser network relay are built from src/relay but are
not part of the SSH relay. Move them, and the 46 relay modules the hook relay
bundles, to src/wsl-guest so the SSH relay can be deleted on its own. Pure
renames; the built bundles are byte-identical.
Adds a boundary test so nothing outside src/relay imports it except relay tests.
* fix(ci): exempt renamed files' existing lint debt in the changed-lines gate
* test(relay): keep the cast suppression on the wrapped line
* test(relay): only parse files that mention relay in the import boundary check
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Every changed-path gate asked git for `--merge-base "$BASE_SHA" "$HEAD_SHA"`,
which needs the event payload's base SHA to be in the local graph. That is the
only reason two jobs cloned all 8127 refs' history. On a pull_request checkout
HEAD is already the merge commit, so its first parent is the base side and no
merge base has to be computed. config/scripts/git-pull-request-diff-base.mjs
resolved that for the two Node gates; the workflow's inline gates now use the
same helper through a small CLI rather than open-coding it.
code_paths gates all 22 jobs, so its checkout is charged to the start of every
one of them: measured 20.7s to 1.6s, keeping blob:none because its sparse tree
is ~7 files and leaves no blobs to refetch. Static analysis drops the filter
instead, since populating all 30,226 files makes blob:none force a second
promisor fetch: 23s to ~11s.
Verified on a real merge ref. At depth 50 the old and new forms produce
identical changed-file sets. At depth 2 the new form still works and the old one
fails with `fatal: bad object`, which is the failure a stale base would have
caused once the checkout stopped being complete.
Also drops the dead resolveBase + merge-base prelude in the changed-code gate,
whose result resolvePullRequestDiffBase already discarded on every PR.
The rule rejects `vi.mock` / `vi.doMock` / `vi.unstable_mockModule` and the
`jest` equivalents, on the argument that a test which rewrites the module graph
asserts against a stand-in the production code never sees. It is already off for
`**/*.test.{ts,tsx}`, `**/*.spec.{ts,tsx}`, `tests/**` and `**/__mocks__/**` via
the existing override in config/oxlint-anti-slop.json; that override is
unchanged here. What the rule actually catches is module mocking that has drifted
out of a spec and into a first-party `.ts` support module, where nothing marks it
as test-only.
73 violations at baseline, all of them in test-support code. 9 were relocated
back into spec files the override already exempts; the remaining 64 sit in 10
files that are test-only but do not match the override globs, and carry a
file-level disable naming the rule and the reason.
Relocated:
- terminal-hydration-store-test-bootstrap.ts: the sonner / sync-runtime-graph /
pty-transport `vi.mock` calls moved into the two specs that import it
(terminals-hydration-canonical-rows, terminals-hydration-canonical-pty-overlap).
Vitest hoists `vi.mock` inside a test file, so registration is strictly earlier
than the previous module-eval-time call; the bootstrap keeps only the preload
API proxy. Both importers were updated.
- ipc-events-ssh-authority-test-fixtures.ts: the 6 direct-ssh `vi.doMock` calls
moved into useIpcEvents-agent-status-ssh-authority.test.ts as a local
`stubDirectSshModules()` helper, which also de-duplicates the three copies the
spec already had inline. The fixture now returns the store state and coordinator
doubles it builds, typed via the exported DirectSshReconnectCoordinatorDouble.
Suppressed, with justification (each is `/* oxlint-disable
anti-slop/no-module-mocking -- ... */`, rule named, no blanket disable):
- config/scripts/headless-serve-shutdown-matrix.test.mjs (1) - a genuine Vitest
spec that the override misses only because its globs say {ts,tsx}. The script
under test is a top-level CLI module; the alternative is spawning real docker.
- src/main/codex-accounts/runtime-home-service-test-harness.ts (1) - stubs one
probe predicate in ../pty/shell-startup-env, imported directly by several
main-process readers; 17 specs share it.
- src/main/computer/desktop-script-provider-test-harness.ts (2) - stubs
child_process/fs-promises for a provider that shells out; 8 specs share it.
- src/main/github/work-item-search-test-harness.ts (4) - one consumer lives in
tests/e2e, where the relative mock ids resolve differently, so moving the calls
into the specs would silently stop mocking there.
- src/renderer/src/components/automations/automations-page-test-harness.tsx (14)
- the mount rig for 10 AutomationsPage specs.
- src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-test-harness.ts
(1) - stubs refreshWebRuntimeSessionTabsSnapshot, imported directly by several
renderer runtime modules; 18 specs share it.
- src/renderer/src/hooks/ipc-events-agent-status-window-test-fixtures.ts (7) -
stubReactSyncEffect/stubAuxiliaryModules, shared by 11 specs.
- src/renderer/src/hooks/ipc-events-close-routing-test-harness.ts (11) - stubs
and hook invocation are one unit; 4 specs share it.
- src/renderer/src/hooks/ipc-events-terminal-create-test-harness.ts (13) - its
only spec is at 799 of an 800 max-lines budget.
- src/renderer/src/hooks/ipc-events-test-harness.ts (10) - shared by 8 specs.
No violation was converted to real dependency injection, and no max-lines disable
was added.
Verified: the audit command exits 0 with no output (and reports errors on a
planted probe, so the rule is live); node config/scripts/run-typecheck-projects-in-parallel.mjs
exits 0; 354 spec files / 2506 tests covering every importer of every touched
file pass. No mobile/ file was touched.
The changed-code quality gate's root Oxlint scan runs without --config so it never
loads the anti-slop JS plugin, which made all 10 of those file-level suppressions
read as "Unused oxlint-disable directive". check-changed-code-quality.mjs now
exempts directives naming an anti-slop rule from that unused-directive warning,
the same carve-out isCastingDirectiveUnusedWarning already makes for the casting
suppressions the casting config enforces. Such a directive can never suppress a
root-config rule, so nothing the root scan would otherwise report is hidden;
audit:anti-slop remains the scan that enforces the rule.
* feat(design-system): gate renderer UI with @shadcn/lint
Wires shadcn-ui/lint's Oxlint plugin into the two places this repo already
ratchets: the changed-lines PR gate for rules the renderer can't satisfy
today, and `pnpm lint` for the one that is already at zero.
- config/oxlint-design-system.json: no-restyle (layout allowed),
no-raw-colors, require-static-classes -- scoped to src/renderer/**/*.tsx,
run over added lines only. Measured at 10 findings across the last 60
commits (771 changed files), so it holds the line without a migration.
- config/oxlint-dead-classes.json: no-unknown-classes repo-wide, with the
renderer's plain-CSS hook namespaces allow-listed. Now at zero.
- no-inline-styles and no-arbitrary-values stay off; STYLEGUIDE says why.
Fixes the three live bugs the linter found:
- `--editor-surface` never reached `@theme inline`, so `bg-editor-surface`
generated no CSS -- 12 editor/artifact/notebook panes fell through to the
page background instead of #1e1e1e in dark mode.
- `scrollbar-none` is not a Tailwind utility and was declared nowhere, so
the remote file browser breadcrumbs showed the scrollbar they meant to
hide. Declared as a real `@utility`.
- Notebook markdown cells used `markdown-preview-body`, which no stylesheet
defines; the styled class is `markdown-body`. They rendered unstyled.
* ci: run the dead-class gate in PR CI
`pnpm lint` gained check:dead-classes, and pr-workflow-lint-parity requires
every `pnpm lint` step to have a matching step in pr.yml.
* fix(notebook): keep markdown theme selectors working
* Add casting code quality lint scan
Enforce type assertion style by adding a new oxlint scan with `typescript/consistent-type-assertions` rule. Requires using `as const`, type annotations, or `satisfies` instead of raw type casts, with documented `SAFETY:` exceptions for unavoidable cases.
* fix minor issue
Startup RPCs now fan out in parallel and the xterm engine pre-warms inside the
real terminal frame while they are in flight, so the first pane inherits a warm
WebView and an already-measured viewport instead of paying a round trip for it.
The pre-warm opens its engine before measuring: web-ready only reports that the
bundle loaded, and the WebView answers a measure with null until a terminal
exists. It also pre-warms at the user's saved text size, because cell size is
what the frame height gets divided by.
Host writes such as worktree.activate wait for an evaluated status.get reply.
Navigation still fails open when a host cannot answer one, but that fallback no
longer reads as a passing compatibility verdict.
Phase 6 of the relay split: the relay's deploy/operate surface moves under cloud/ with 24 cloud-* workflows gated on ORCA_CLOUD_OPERATIONS_ENABLED, the Cloud SQL rollout lease action, the relay Terraform root (dual-accept identities for both repositories), scripts, docs, CODEOWNERS, and a terraform validate job in Cloud Verify.
* fix(tooling): run oxlint gates without a Windows .cmd shim
`check:code-quality:changed` spawned `pnpm.cmd` without a shell, which Node
refuses under the CVE-2024-27980 mitigation, so the gate died with EINVAL
before linting anything. Resolve oxlint's own Node bin and run it under this
process's node instead — no shim, no shell, no quoting question — and add a
ratchet so the idiom cannot spread back into config/scripts.
* fix(tooling): validate the react-doctor diff base and widen the shim ratchet
`base` reaches cmd.exe unquoted on the shell fallback, so reject anything
outside a git revision before spawning. The ratchet matched only a handful of
runner names, which let `vitest.cmd` through even though config/scripts already
spawns vitest, playwright and electron-builder; match any batch-shim literal
instead, walk subdirectories, and cover tests/tools.
* docs(tooling): state what the shim ratchet and diff-base check miss
Both comments read as complete accounts of their guard's coverage. The revision
class rejects reflog syntax like HEAD@{1}, deliberately, since braces have no
business in a cmd.exe-bound argument; the ratchet misses a drive-lettered
literal because a colon is not in its class. Say so beside the template-literal
ceiling already noted.
---------
Co-authored-by: Orca Worker <orca-worker@localhost>
* Reorganize combined-diff components into feature-organized structure
Splits flat combined-diff files into feature-focused subdirectories
(browse-files, load-sections, resolve-changes, review-controls,
scroll-viewport) to improve code organization and reduce clutter in
the editor directory. Groups related logic by concern for easier
navigation and maintenance.
* Split up combined-diff viewer into feature-organized modules
Decompose the 221-line monolithic CombinedDiffViewer into smaller, focused modules organized by feature: entry resolution, section loading, view state memory, file tree navigation, review controls, and scroll viewport handling. Main component now composes these hooks to orchestrate the combined-diff view.
* fix(combined-diff): prevent replayed preference writes
Move preference write outside state updater callback since React may
replay state updaters, causing multiple writes. Add sideBySide to
dependency array.
* fix(combined-diff): re-resolve sections by key to handle list rebuilds
The section list can rebuild while a write is pending (due to rebase, file changes, etc.); re-resolve by key instead of stale index to apply updates to the correct section.
- Convert skipped conflicts message to structured i18n plural forms
- Add oldPath field to git status signature for rename tracking
* Suppress react-doctor diagnostics in combined-diff feature
Add suppressions for react-doctor diagnostics that are necessary patterns
for the combined-diff implementation, configured in both the quality check
script and package.json.
* Add skill deletion with cross-platform transaction safety
Implements end-to-end skill removal with placement enumeration, dependency guards, and transactional recovery. Covers native, WSL, and remote hosts; users can delete canonical directories and alias placements (symlinked directories or files) in a single atomic batch. Includes UI selection flow, preview, confirmation, and results band. Block reasons (bundled, plugin, unowned, stale) gate deletions that would fail or contradict user intent.
* Organize IPC handlers into module subdirectories
Move register-core-handlers and skill-delete-ipc-handlers into
dedicated subdirectories for improved code organization and to
reduce the flat structure in src/main/ipc/.
* Make skill deletion recovery transactions idempotent
Defer journal cleanup until both staging removal and receipt cleanup succeed, leaving the journal in place for startup to retry if either operation fails. This ensures the recovery process is safe to run multiple times without leaving partially-deleted skills.
* Consolidate skill-delete files into dedicated module
Reorganize skill deletion functionality into a modular structure under
`src/main/skills/skill-delete/` with simplified file names. Remove the
redundant `skill-delete-` prefix from file names since they now live in
the dedicated directory. Update all import paths throughout the codebase
to reflect the new structure, including imports from IPC handlers and
RPC methods.
* Fix broken import paths and add deletion robustness improvements
Import paths using `..//'` were invalid and broken. Replace with explicit
module names (`skill-discovery-sources`, `skill-install-filesystem`, etc.)
to clarify dependencies.
- Bind WSL filesystem methods to preserve `this` context
- Keep recovery journal when rollback rename fails, so startup can retry
- Skip symlink-based tests on Windows where they cannot run
- Only treat ENOENT/ENOTDIR as empty directories; propagate other errors
- Fix cross-platform path parent calculation to handle drive roots
- Replace shared constant with localized string for user-facing message
- Use `runProcess` for WSL integration test instead of bare `execFile`
* Add batch limit for skill deletion and improve host availability checkin
- Limit concurrent deletions to prevent remote host overload
- Add retry logic for capability probing to handle transient unavailability
- Add reprobe() method to recheck capability after errors or user refresh
- Fix status logic: receipt cleanup is best-effort, completion depends only on content removal
- Improve error message for unreachable hosts
A diagnostic's span often reaches past the block a split moved — most commonly
to a hook dependency array, which legitimately grows when closure variables
become props. Requiring every line of the span to match contiguously reported
the moved body as new.
The block must still start at the same line in the base and appear in order,
and >=90% of it must be present. Genuinely new code shares neither the anchor
nor the ordering.
A file-splitting refactor makes every line of the new module an added line, so
pre-existing lint debt in code that merely moved starts failing the gate. The
only way to satisfy it is to edit the moved code, which is what a
behavior-preserving refactor must not do. Exempt a diagnostic when its
highlighted lines already existed verbatim and contiguous in the base revision.