Commit Graph
1618 Commits
Author SHA1 Message Date
Neil c089ca5812 fix(ci): preserve rename boundaries in React Doctor gate (#32159)
* fix(ci): preserve rename boundaries in React Doctor gate

* fix(ci): retain coverage and reject inconsistent audit reports

* test(ci): update Git status fixture for rename-aware ranges
2026-10-22 20:40:05 -07:00
95323024a1 fix(packaging): exclude root development configs from desktop packages (#32021)
Exclude the seven repository-root development configs reported in #30993
from app.asar, using the exclusion glob suggested in the issue.

Co-authored-by: Marcin Nowicki <31430+pr0d1r2@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-22 20:30:34 -07:00
Jinwoo Hong 6a96733ba9 Install Orca's skills with one click on every setup screen (#31918)
* Add Orca's own bundled-skill planner and installer (not wired in yet)

One planner decides what installing Orca's bundled skills does to each
target folder; execute re-plans under per-path locks and runs only a plan
whose digest still matches; a shared summary derives badge, wording and
exit code. A copy counts as Orca's untouched one only when its SKILL.md
bytes match a version some build shipped (resources/skills/released-digests.json,
generated from git history and checked add-only).

Nothing calls this yet; the npx path is unchanged.

* Report left-alone folders as warnings and keep backups in the home

A leave is no longer a failure: the outcome line says what happened plus
"N left alone: <reason>", and the exit code is 1 only for a failed or
changed op. Backups for every scope go to <home>/.agents/.orca-skill-backups
on the executing host, so a project Replace never writes into the repo;
moving there falls back to copy, verify, then remove across filesystems.

* Update Orca's skills with Orca's own installer instead of npx skills

The app's Update skills dialog, badge, nudge and status pills now read the
bundled-skill plan and summary, and Update executes the reviewed plan's
digest. `orca skills install|update` runs the same planner in-process with
--dry-run, --force, --json and per-summary exit codes. Removes both npx
spawn sites, the freshness inventory modules and the npx round-trip CI.

* Update only where the skill already is; one provider-root rule for app and CLI

installedOnly now drops every location that holds nothing (keeping the shared slot
only when an existing agent link points at it), so Update never adds a skill for
another agent. The app and CLI resolve the Claude skills folder through the same
rule in localBundledSkillInput; a test asserts both plan identically.

* Compact Update skills dialog: one row per skill, locations named by agent

summarizeBundledSkillPlan now returns each skill's locations, labelled by
agent (or "Shared folder") with a ~-collapsed path; the dialog shows one row
per skill that needs something, collapses up-to-date skills into one line, and
lists every location behind "Show all locations". The result reads "Updated N
skills · M left alone". Under installedOnly a Replace with no shared copy
replaces in place instead of adding one.

* fix(skills): keep location paths readable by putting the state under the path

* fix(skills): hold the last plan in state, not a ref mutated during render

Split the update dialog's rows, headline and footer into components, move the Update label
next to the other plan copy, and use z.strictObject for the request schemas.

* Escape non-ASCII characters in bundled-skill regex and wording

The orcad bundle rejects a literal non-ASCII character in a regex.

* fix(cli): escape non-ASCII apostrophes in the skills install help text

* Plan and update Orca's skills on paired servers, SSH hosts and WSL

* Show the skills pill for paired hosts; real-WSL installer test

* test(orcad): check the bundled CLI plans a skill install without npx

Drop the deleted roundtrip workflow's group from the closed-PR cancellation fixture.

* Let the WSL installer create the backups folder itself

* Review fixes: follow the active workspace's host, keep hosts' plans and runs apart, never boot WSL for a background check

* test: execute requests carry the plan request; CLI pill follows the local target

* Write large WSL skill files in chunks that stay under the command-line limit

* Make bundled-skill test fixtures link folders the way the product does on Windows

Directory links in fixtures are junctions on Windows, which need no
Developer Mode; the one file-link fixture is skipped there.

* Let a client ask a host to install named Orca skills, not only update installed ones

* WIP surfaces

* fix casts

* Prune ratchet and shard entries for deleted files

* Review fixes: disable feature-wall Install without a host, name unknown-skill errors, drop leftovers

* Setup buttons follow the plan: Update only when it would change something, Replace for an edited copy; complete translations

* Plan an SSH workspace on the managed server that serves its host; ask to connect otherwise

* Translate the remote skill states into every locale

* Install on SSH workspaces through their managed server; ask to connect an unserved SSH host

* Resolve legacy skill agent keys canonically

* Reject stale skill replacements when auxiliary content changes

* Remove the Windows-hosted WSL bundled skill installer

* Wait for active workspace ownership before planning bundled skills

* Keep legacy WSL skill setup on its inline npx flow

* Use each setup surface's target for discovery and installation

* Describe skill setup actions and results from their install plans

* Keep ordinary bundled skill classification shallow

* Leave unreadable skill replacements alone without aborting plans

* Show blocked skill setup plans as needing attention

* Resolve skill setup discovery and installation from workspace ownership

* Keep legacy WSL setup scoped to the selected runtime

* Pass force-all through shallow planning and describe unreadable copies

* Keep skill setup previews shallow until Replace is clicked

* Stop Replace when its explicit plan cannot replace a selected copy

* Keep skill setup within the focus and workspace-record read ratchets

* Clarify skill update statuses and folder details

* Clarify skill setup results and blocking reasons

* Keep selected skill replacements in place with one status

* Use first-install wording when no agents have the skill

* Report completed skill replacements with their backup confirmation

* Preserve main’s skill selection for unavailable Computer Use

* Derive setup status and coverage from the install plan

* Use shared plan status labels on every setup panel

* Show blocking skill folders and keep incomplete setup steps pending

* Clarify available skill updates and skipped copies

* Pass setup execution outcomes to the skill result API

* Use update skipped wording in skill setup results

* Keep skill update results in preview order

* Show skipped skill copies before installing for more agents

* Show unsupported skill setup guidance before installation

* Use the host install plan for orchestration agent coverage

* Show each skill skip reason once

* Count skipped skills instead of skipped folders

* Make installer input and link tests work on Windows

* Show disconnected host guidance before offering skill installation

* Bound bundled skill classification reads and parse names linearly

* Keep workspace skill roots inside the resolved workspace

* Compare workspace skill roots with native path semantics

* Restrict bundled skill backups to their owner on POSIX

* Keep bundled skill updates on the proven local workspace host

* Keep skill details on the originating setup target

* Keep recovered skills needing attention until they are updated

* Expect recheck after a failed skill setup plan

* Count completed skill updates in the status tooltip

* Count only usable skills in orchestration agent coverage

* Sync the runtime localization catalog
2026-10-22 23:03:13 -04:00
Jinwoo Hong d3dbd9683f Update Orca's skills with Orca's own installer instead of npx skills (#31688)
* Add Orca's own bundled-skill planner and installer (not wired in yet)

One planner decides what installing Orca's bundled skills does to each
target folder; execute re-plans under per-path locks and runs only a plan
whose digest still matches; a shared summary derives badge, wording and
exit code. A copy counts as Orca's untouched one only when its SKILL.md
bytes match a version some build shipped (resources/skills/released-digests.json,
generated from git history and checked add-only).

Nothing calls this yet; the npx path is unchanged.

* Report left-alone folders as warnings and keep backups in the home

A leave is no longer a failure: the outcome line says what happened plus
"N left alone: <reason>", and the exit code is 1 only for a failed or
changed op. Backups for every scope go to <home>/.agents/.orca-skill-backups
on the executing host, so a project Replace never writes into the repo;
moving there falls back to copy, verify, then remove across filesystems.

* Update Orca's skills with Orca's own installer instead of npx skills

The app's Update skills dialog, badge, nudge and status pills now read the
bundled-skill plan and summary, and Update executes the reviewed plan's
digest. `orca skills install|update` runs the same planner in-process with
--dry-run, --force, --json and per-summary exit codes. Removes both npx
spawn sites, the freshness inventory modules and the npx round-trip CI.

* Update only where the skill already is; one provider-root rule for app and CLI

installedOnly now drops every location that holds nothing (keeping the shared slot
only when an existing agent link points at it), so Update never adds a skill for
another agent. The app and CLI resolve the Claude skills folder through the same
rule in localBundledSkillInput; a test asserts both plan identically.

* Compact Update skills dialog: one row per skill, locations named by agent

summarizeBundledSkillPlan now returns each skill's locations, labelled by
agent (or "Shared folder") with a ~-collapsed path; the dialog shows one row
per skill that needs something, collapses up-to-date skills into one line, and
lists every location behind "Show all locations". The result reads "Updated N
skills · M left alone". Under installedOnly a Replace with no shared copy
replaces in place instead of adding one.

* fix(skills): keep location paths readable by putting the state under the path

* fix(skills): hold the last plan in state, not a ref mutated during render

Split the update dialog's rows, headline and footer into components, move the Update label
next to the other plan copy, and use z.strictObject for the request schemas.

* Escape non-ASCII characters in bundled-skill regex and wording

The orcad bundle rejects a literal non-ASCII character in a regex.

* fix(cli): escape non-ASCII apostrophes in the skills install help text

* test(orcad): check the bundled CLI plans a skill install without npx

Drop the deleted roundtrip workflow's group from the closed-PR cancellation fixture.

* Make bundled-skill test fixtures link folders the way the product does on Windows

Directory links in fixtures are junctions on Windows, which need no
Developer Mode; the one file-link fixture is skipped there.

* Resolve legacy skill agent keys canonically

* Reject stale skill replacements when auxiliary content changes

* Keep ordinary bundled skill classification shallow

* Leave unreadable skill replacements alone without aborting plans

* Pass force-all through shallow planning and describe unreadable copies

* Clarify skill update statuses and folder details

* Keep selected skill replacements in place with one status

* Report completed skill replacements with their backup confirmation

* Clarify available skill updates and skipped copies

* Keep skill update results in preview order

* Show each skill skip reason once

* Count skipped skills instead of skipped folders

* Make installer input and link tests work on Windows

* Bound bundled skill classification reads and parse names linearly

* Keep workspace skill roots inside the resolved workspace

* Compare workspace skill roots with native path semantics

* Restrict bundled skill backups to their owner on POSIX

* Keep recovered skills needing attention until they are updated

* Count completed skill updates in the status tooltip

* Sync the runtime localization catalog
2026-10-22 22:35:14 -04:00
Jinwoo Hong 6768988ed6 Add Orca's own bundled-skill planner and installer (not wired in yet) (#31662)
* Add Orca's own bundled-skill planner and installer (not wired in yet)

One planner decides what installing Orca's bundled skills does to each
target folder; execute re-plans under per-path locks and runs only a plan
whose digest still matches; a shared summary derives badge, wording and
exit code. A copy counts as Orca's untouched one only when its SKILL.md
bytes match a version some build shipped (resources/skills/released-digests.json,
generated from git history and checked add-only).

Nothing calls this yet; the npx path is unchanged.

* Report left-alone folders as warnings and keep backups in the home

A leave is no longer a failure: the outcome line says what happened plus
"N left alone: <reason>", and the exit code is 1 only for a failed or
changed op. Backups for every scope go to <home>/.agents/.orca-skill-backups
on the executing host, so a project Replace never writes into the repo;
moving there falls back to copy, verify, then remove across filesystems.

* Escape non-ASCII characters in bundled-skill regex and wording

The orcad bundle rejects a literal non-ASCII character in a regex.

* Make bundled-skill test fixtures link folders the way the product does on Windows

Directory links in fixtures are junctions on Windows, which need no
Developer Mode; the one file-link fixture is skipped there.

* Reject stale skill replacements when auxiliary content changes

* Keep ordinary bundled skill classification shallow

* Leave unreadable skill replacements alone without aborting plans

* Bound bundled skill classification reads and parse names linearly

* Keep workspace skill roots inside the resolved workspace

* Compare workspace skill roots with native path semantics

* Restrict bundled skill backups to their owner on POSIX
2026-10-22 22:19:34 -04:00
Neil cae9a066b3 refactor: colocate Linear list-scoping owners (#32133) 2026-10-22 17:24:09 -07:00
Jinwoo HongandClaude 41661e3312 feat(sidebar): main runs workspace sleep; the window follows (P3b) (#31564)
* fix(sidebar): land flip-a's switch-on fixes on main, still dark (no flip)

The fixes found while the sidebar ran from main's feed, without turning
that on: ORCA_SIDEBAR_INPUTS_IS_MAIN stays false, and none of flip-a's
stop-gaps come along.

- Main stops counting a closed tab's terminal once it was asked to stop
  and no pane shows it; a failed or refused stop takes the request back,
  and a host out of contact stays unverifiable.
- Main fetches a paired server's rows with the transport's normal bound,
  so a server slower than 5 s still lists.
- A deferred unread raise flips the folder row it was decided for, and a
  read while the owner check is out cancels it.
- Phone: no worktree.ps flash before the feed; a host's last installed
  feed stands in while a fresh read is out; it highlights its own
  last-opened workspace and a fold never scrolls; projects file under
  their project group with the desktop's indent; the collapse spinner
  shows only after 500 ms; "Reopen to update" keeps the rows on screen.
- e2e helpers and journeys for the switch-on suite.

* fix(sidebar): keep an owed relay stop marked, clear a thrown stop, and prune pending unread raises

- A stop-and-wait that throws takes its stop request back, like one that fails.
- A final stop the legacy relay still owes stays marked, since the relay retries it.
- Pending unread raises are tracked only while one waits, so the map no longer grows.
- The paired-server fallback poll test gives its third fetch a reply and asserts live.

* feat(sidebar): main publishes cached snapshots and announces its own stores; detected, view and live-content domains (inert)

* fix(sidebar): keep links confirmed only by a new git run, no rescan on metadata writes, no phone wait on detected checkouts

* fix(sidebar): rescan the workspace rows when the Windows runtime default changes

* feat(sidebar): one lifecycle queue per workspace in main, with in-progress hints (switch-gated)

Create, delete, terminal.closeAll, terminal.sleep and worktree.sleep now run through one
first-in, first-out queue per host-qualified workspace: strict order, no merging, delete
cancels what has not started and waits for what has, an unconfirmed outcome pauses until
the host's own answer. Hints publish in a new inProgress feed domain (no reader yet); the
creations domain is its projection. A finished create or delete keeps its hint until a scan
started after it applies, in the same revision. Adds the paired-server metadata serializer
(saving domain, no caller yet). Replaces the creation registry.

* fix(sidebar): a create never queues behind the previous occupant's delete

* feat(sidebar): main runs sleep; the window follows (P3b)

* fix(sidebar): keep electron out of the headless server's sleep path

The desktop registers how sleep reaches paired servers; a headless serve has none.

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

* test(sidebar): type-check the sleep e2e specs; drop the removed VM suspend stub

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

* fix(sidebar): resume a paused workspace by re-observing, never replaying

- A command issued while paused waits behind the pause; when the owner answers again main
  re-reads terminal liveness and the listing, then carries on. A forced delete runs past it.
- Commands keep their arrival order across a workspace lookup; deletes by name key by the
  resolved id, so they wait for that workspace's running sleep.
- A second delete with different options is refused as already in progress.
- Close and delete read lost contact as sleep does.
- A create always frees its own queue entry, even after a retry took over its id.
- Drops the unused opId, and the paired-server write serializer and its feed section,
  which land with their first caller.

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

* fix(sidebar): main is the only writer of a sleep's wake records; sleep classifies only lost contact as unconfirmed

- The window no longer captures wake records for a sleep main runs; it takes main's records
  from main's answer, which its wake and tab-open resume then read.
- A workspace reopened during its sleep keeps its VM running (the window and a phone's
  activation tell main).
- Only lost contact pauses: a definite chat-stop failure or a server's still-live answer is a
  plain failure with today's toast.
- A paired server's chats are asked with the Sleep mark, and again without it when an older
  server refuses the field, instead of reading an unprobed status.
- A phone's worktree.sleep is answered on acceptance and its activation no longer waits on a
  queued wake, so neither can outlast the phone's request timeout. The web client passes
  markChatsAsleep and keeps the receipts.
- One desktop adapter, passed where the desktop builds the runtime, replaces the two module
  slots; a headless server gets none.

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

* chore(sidebar): share worktree.sleep's params with the RPC catalog; lint fixes

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

* test(sidebar): test the paired-server sleep where main runs it; drop the window's copies

The window no longer sleeps a paired server's terminals, so its convergence and order tests for
that path move to main (remote-workspace-terminal-sleep.test.ts) or follow main's answer.

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

* test(sidebar): retry the sleep spec's liveness read while terminals stop

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

* fix(sleep): main resumes every workspace-sleep wake; the window only attaches

- Main claims and starts each slept pane through its windowless resume path, with or
  without a window: the "window present" check is gone.
- A phone's open queues main's wake, which finishes only once the resumes have started,
  and always notes the reopen so a running sleep leaves the VM up.
- The window no longer relaunches a workspace sleep's records in fresh tabs; opening a
  slept pane asks main to resume it and the pane's spawn attaches to main's terminal.
- A window save cannot overwrite main's workspace-sleep records.
- A VM that would not suspend no longer reads as "kept open": the terminals slept, and
  the toast says the machine is still running.

* test(e2e): main resumes a slept pane once, in place, and keeps its record

* chore(i18n): add the VM-still-running sleep message

* fix(sleep): a phone's or the CLI's failed sleep shows the desktop toast again

* fix: restore main's final P2 test fixes a merge reverted

* fix(sleep): window saves never write or clear main's sleep records; headless wake report counts only real sleeps

* test: a same-id twin's grace follows the rows' change, not the removed catalog hook

* fix(sleep): drop the 1 s settle wait after an observed exit, and wake a slept workspace's mounted agents on phone open

A workspace sleep stopped each terminal through main's stop-and-verify, which polled for a
further second after the exit it had already seen. And a phone opening a slept workspace no
longer reached the window's mounted panes of agents that had finished before the sleep, so
they stayed asleep until the window opened their tab.

* test: give the workspace-sleep wake record its prompt field

* refactor: match slept-agent records to the workspace with worktreeIdsEqual

* chore(sleep): one import from sleep-record-pane-claim

* test(sleep): route the disconnected-sleep SSH spec, declare the desktop sleep adapter, and open the workspace after its sleep starts

* test(sleep): delete a workspace the spec creates, not the seeded one later specs share

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-22 19:39:07 -04:00
Neil ba0603c764 refactor: colocate repository AI persistence queue (#32122) 2026-10-22 16:29:51 -07:00
Neil ed5ad7f09b refactor: colocate repository worktree defaults (#32112) 2026-10-22 15:45:59 -07:00
Neil 799bb18aa5 refactor: colocate repository Jira project settings (#32107) 2026-10-22 15:15:31 -07:00
Neil 59497680d8 refactor: colocate Jira issue summary generation (#32099) 2026-10-22 13:42:03 -07:00
Neil 2e6963d707 refactor: colocate Jira query construction (#32089) 2026-10-22 12:28:18 -07:00
Neil 2366072dc8 refactor: colocate browser cookie validation (#32062) 2026-10-22 09:43:42 -07:00
Neil de2700d0fb refactor: colocate native-chat command envelopes (#32055) 2026-10-22 08:55:53 -07:00
Neil d053ff97ab refactor: colocate mobile web route and bundle tooling (#32008) 2026-10-22 06:04:04 -07:00
Neil c30fa6252a refactor(tooling): group mobile web preview and evidence tools (#31976) 2026-10-22 04:57:01 -07:00
Jinwoo Hong 15d663fc3f Test runner uses the repo's pinned Bun, and refuses an older one instead of silently skipping tests (#31981)
* chore(test): run-vitest launches the pinned Bun, and refuses a Bun older than config/.bun-version

An older Bun silently runs no tests in files that reach newer node builtins, so a local
comparison could skip them on both sides.

Claude-Session: W5b

* chore(test): an older Bun ends run-vitest by exit code, so its message is not cut off

Claude-Session: W5b
2026-10-22 07:17:35 -04:00
Neil 48660ae717 fix(packaging): keep ssh2's Windows-only Pageant files out of macOS and Linux packages (#31946)
* fix(packaging): keep ssh2's Windows-only Pageant files out of macOS and Linux packages

* test: explain the win32 ssh2 filter fixture and format

* test: read the excluded ssh2 file names from the exclude table
2026-10-22 03:58:38 -07:00
Neil 8e9690468a fix(orcad): ship computer use with the server and say when it is unavailable (#31958)
* fix(orcad): ship computer use with the server and say when it is unavailable

* test(computer): drop the type assertion in the sidecar app-environment mock

* fix(cli): carry the server reprovision hint into --json computer failures
2026-10-22 03:27:39 -07:00
Neil dc315a4e5a refactor(ssh): remove the dead SSH filesystem provider and desktop SSH file watcher (#31922)
* refactor(ssh): remove the dead SSH filesystem provider and desktop SSH file watcher

Nothing in production has registered an SSH filesystem provider since the
relay removal (#27214/#27215), so every getSshFilesystemProvider read
returned undefined. Delete the provider table and the remote watcher
subsystem, and collapse the call sites to that always-undefined behaviour:
fs:watchWorktree/fs:unwatchWorktree ignore SSH roots, the worktree base
watcher only resolves local repos, and worktree removal skips the desktop
watcher for SSH paths.

* test: drop the nested SSH watcher catalog case that can no longer occur

The base-directory watcher never carries a connection id now, so the
host-blind-event guard it exercised is gone. Also prune the deleted remote
watcher suites from the shard timings and the nested-SSH wording from the
worktree catalog reliability gate.
2026-10-22 01:54:29 -07:00
Neil a164f42b8a fix(ci): exclude inherited job results from runner demand (#31919) 2026-10-22 01:39:19 -07:00
Neil 4778133a1f ci: share the mobile bundle across SSH Windows host cells (#31883) 2026-10-22 01:01:05 -07:00
Neil e4959a6bed ci: canary a smaller WSL distro matrix for runtime changes (#31873) 2026-10-22 00:59:49 -07:00
Neil 2078db93ec ci: check dependency vulnerabilities on PRs and stable releases (#31858)
* ci: audit dependency security without gating releases

* ci: audit stable release tags instead of running daily

* ci: isolate release audit data and fix integration checks

* ci: require exact audit inputs and ignore candidate archive attributes
2026-10-22 00:43:07 -07:00
Neil 213bc4684d ci: distinguish indirect unit inputs from comments and data (#31876) 2026-10-22 00:42:21 -07:00
Neil 95f8f73b9a ci: include retry attempts in runner demand reports (#31865) 2026-10-22 00:05:32 -07:00
Jinwoo Hong 01db1b89af Main titles agent tabs and applies phone tab moves itself, and tab moves name the tab they follow (dark, layout-owner build only) (#31812)
* Layout groundwork (switched off): main derives agent tab titles from its own hook status

With the layout-owner switch on, main gives agent tabs their generated titles from
the hook-server status it owns, on the desktop, headless serve and orcad: one
renameTabs per status batch, so one write and one frame. It keeps the window's
rules (setting, custom and quick-command titles, first title wins, dispatch labels).
The window stops sending generated titles for tabs main holds; a paired server's
tab, or one main has no tab-bar entry for, is still titled by the window as today.

The title rules move to src/shared (agent-row-primary-text, and the new
agent-generated-tab-title-rules) so both sides share them; switch-off behaviour
is unchanged.

* feat(layout): main applies a phone's or paired client's tab move itself, and a tab move names the tab it follows so a stale index can't misplace it (layout-owner build, L193)

* fix(layout): a move in a group showing a tab main lacks goes the window's way, a twin's agent tab keeps its window title, and orcad subscribes titles through the store attach (layout-owner build)

* chore(layout): main's tab move reaches the old-client translation through its compat entry

* chore(layout): mark the window-only-row move fallback as bridge code (ledger L280)

* test(layout): attaching the hook store follows its rows for main's tab titles once

* chore(layout): the old-client move translation is a compat entry of its own, with no cyclic re-export

* test(config): the boundary message test names the old-client folder's configured entries
2026-10-22 02:24:37 -04:00
Neil 8e1ee111e4 ci: avoid global fan-out for routing test changes (#31856)
* ci: avoid global fan-out for routing test changes

* test(ci): isolate routing-test classification cases
2026-10-21 23:15:55 -07:00
Neil 42da9f430f refactor(mobile): group platform-seam analysis tools (#31845) 2026-10-21 23:15:21 -07:00
Neil ad4952e1da ci: retire completed relocation and pnpm cache experiments (#31843) 2026-10-21 22:37:06 -07:00
Neil 8b5c732e6e ci: skip relay integration for renderer-only PRs (#31842) 2026-10-21 22:31:54 -07:00
Neil 6c6ad7520f ci: vet adhoc source before build preparation (#31839) 2026-10-21 22:22:45 -07:00
eba0124de9 [mem] Backpressure worker subprocess output without dropping bytes (#30698)
* fix(memory): backpressure worker subprocess output without dropping bytes

* style(wsl): clear formatting gates without increasing line limits

* test: cover Codex deadlines and shutdown under worker backpressure

* test: synchronize backpressured Codex deadline fixture

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Neil <neil@stably.ai>
2026-10-21 21:52:41 -07:00
Neil 0a7d6bb2d5 refactor(main): group ephemeral VM lifecycle modules (#31771) 2026-10-21 21:31:21 -07:00
Neil 307f0cdaee Move Relay deployment configuration into private cloud operations (#31792)
* refactor(cloud): move deployment configuration into private operations repo

* docs(cloud): clarify private operator ownership

* fix(ci): install pnpm through npm-compatible bootstrap

* fix(ci): avoid native downloader metadata timeout

* test(push): move deployment preflight contract into private repo

* fix(ci): bootstrap Node before installing pnpm in minimal containers

* fix(ci): pass Windows boundary test paths through the Node runner

* fix(ci): run Windows boundary Vitest with installed Node
2026-10-21 21:01:25 -07:00
Neil 8ba1c9ba09 fix(ci): restore cloud and main checks during pnpm registry outages (#31793)
* fix(ci): recover verified pnpm setup from registry metadata outages

* fix(ci): prepare native pnpm shims after fallback verification

* fix(ci): provide Node for pnpm fallback in bare containers
2026-10-21 20:25:36 -07:00
Neil 95d9682b0c fix(ci): prepare historical VM rollback snapshots (#31775) 2026-10-21 19:40:42 -07:00
99d18f7a2f Browser, chat and simulator tabs main-first: the window asks main for each change and draws only main's update (dark, layout-owner build only) (#31516)
* The window shows each layout records frame record by record, without reading its session back (dark, layout-owner build only)

A records frame no longer re-serializes the window's whole session and runs the
Loader. Panes' terminal bindings and live titles apply alone, to the tabs the
frame names; any other record builds only its own workspace's slices, with the
view and facts read from the store. Only a workspace the store does not show
from frames yet (startup, a reopened stream, a first visit, one sent whole)
takes the full build.

Tests install an apply check that, after every flush, compares the store with
the full build for the same cache and pre-flush store; the shared layout-apply
transport now sends main's records frames and fails a flush that differs.

* A frame of only tab rows shows without reading the window's session back or running the Loader (test, layout-owner build only)

* The account-restart test's fake binding runtime carries per-pane binding generations, so it passes with the switch on (test only)

* The account-restart test types its store writes, so its five old e2e type errors are gone (test only)

* A workspace build reads the editor selection and groups through the save's own writers, as the full build does (dark, layout-owner build only)

* A tab released from the window's hold shows the binding main wrote meanwhile (test, layout-owner build only)

* The frame-apply check still checks a flush's other workspaces when it builds one in full, and the shared transport keeps a test's own check (test only)

* The frame-apply check is proven to catch a mismatch beside a full build, and the shared transport restores only the check it installed (test only)

* wip(d3a-2): a windowless host lists a file main opens on session.tabs at once

* wip(d3a-2): the record key's fields are declared where the key is made

* wip(d3a-2): main publishes an editor tab's view, and a window rebuilds the open file it lacks from it

Main's in-memory editor view now carries the file facts an open file needs; it rides the tab row
(PublishedTab.editorView) and a frame build rebuilds a diff, conflict, preview or check-details tab.
Inert with the switch off: views are set only by layout.apply (switch builds) and read only in frame-sync.

* wip(d3a-2): a switch-on window sends editor opens, closes and promotions to main and draws the tabs from its frames

Layout-owner build only; the switch-off path is unchanged. An open proposes the tab id and sends
openEditorTab with the edit file's record or the tab's view (a reopen also its index); the open
file is written at once and the tab is selected once its frame draws it. Callers that act on the
tab (focus request, link reveal, group focus) wait for that frame through layout-owner-pending-tabs.
A close sends closeTabs; the tab goes with the frame and the file's content after it, and a
refusal keeps both. A promotion sends promotePreviewTab and the frame clears the flag. Deleted:
the drawn editor senders, their before/after captures, the editor drawn-tab holds and the
window-record keep rule (a transient tab is kept by its frame view).

* wip(d3a-2): a switch-on close keeps its selection and a reopen, a link names its group, and main stamps an open's time

Layout-owner build only. After main's frame drops a closed editor tab, the window selects the
group's previous tab and its surface through activateTab and focusGroup, after closeFile's
content cleanup rather than before it; a reopen sent while the close was in flight keeps the
file. openFileForLink's proposed view names the group the open asked for. The window's
openEditorTab sends no createdAt: main stamps the tab's time (ledger L186). Preview promotion
writes no tab itself: the window ends a preview only where a paired server owns the layout.

* wip(d3a-2): editor tests assert switch-on tabs through main's command and frame (test only)

Each store and hook test that assumed an immediate editor-tab draw now runs with main's model
through the T1 transport in apply mode and its frames (tabs-drawn-from-main.test-fixture), and
asserts the tab, its selection and its content after main's frame. The fixture fills the frame
slices a partial editor-and-tabs store lacks.

* wip(d3a-2): main takes a restored move as a command, renames a view tab's path, and keeps one tab per file per group

Layout-owner build only (the switch gates every change; shipped builds unchanged). moveTabToWorkspace no
longer skips a tab "the window's save got there first" (L160): window saves no longer carry the move, so
a repeat is tab_not_found; tab_exists and invalid_params stay. A move or rename may carry a non-plain
tab's new view (its file under the new path or owner), which replaces editorViews[tab] (L180). Main
refuses a moveTab or moveTabToWorkspace that would put a second tab for a file into a group (tab_exists),
and its loader folds a saved session's duplicate into the first, as the window's restore does
(dedupeEditorTabsWithinGroups): the window's frame dedupe hid the second, which no close could reach.

* wip(d3a-2): a switch-on restored move and disk rename are main's commands, and the frame that shows them rekeys the content

Layout-owner build only; the switch-off path is unchanged. A restored editor tab re-owned by its
file's workspace (L157, L158) sends moveTabToWorkspace (a re-own in place sends renameEditorFiles)
under the ids and group the window proposes, with each view tab's new view; a disk rename (L180)
sends renameEditorFiles per workspace and draws nothing first. The window's content rekey (open
file records, drafts, buffers, the editorBuffers echo gate, pendingLiveDiskVerification and
pendingSelfMoveEcho) waits in layout-owner-content-rekeys and the frame flush that first shows main's
change applies it, in the same store update as the frame (a flush now writes one patch). A change no
tab shows is rekeyed on main's answer; a refusal drops it and changes nothing. The move coordinator
and the restored-owner loader wait for that outcome; a refused rename rolls the disk move back.
A rename picks a runtime-owned or floating file's owned id, as main's frames restore it.

Deleted: recordRestoredEditorMoveInLayoutOwner, recordRenamedEditorFilesInLayoutOwner, the
reparent's before/after capture and the drawn-tab holds for moves and renames. Tests that assumed
the window moves or renames first now run through main's T1 transport in apply mode and its frames.

* wip(d3a-2): the static gate's casts and layout-writer ratchet hold for the switch-on move, rename and gap fixes

Layout-owner build only. The content rekey's pure store change stays in rekey-open-files-action
(its writer count drops 4 -> 2, pruned); a twin tab main routes to a paired server ends its
preview through patchTab, the existing tab writer, instead of a new one; a frame-only editor tab
takes its label through the frame build's tab-label facts, read from its view or file record; the
moved file's activation names the moved tab, writing no surface field. Test casts are typed or carry
a SAFETY note.

* wip(d3a-2): the switch-on rename takes the window's pure rekey as an argument, so the two modules do not import each other

Layout-owner build only; no behaviour change. Clears the static gate's no-cycle finding.

* wip(d3b): a switch-on window sends browser, chat and simulator opens, closes and page changes to main and draws them from its frames

Main places the chats its host lists (twin h), a local cancel keeps no tombstone (L155), and the
listing no longer writes chat tab structure where frames draw it (L153).

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

* wip(d3b): a switch-on browser, chat or simulator close waits for main's frame, lands an emptied workspace as today, and its tests read main's frames

* wip(d3b): the stale-listing and cross-version chat tests check a switch-on local cancel through main, with a presence precondition and per-test resets

* wip(d3b): main places a tab whose group it lacks in the workspace's first group, not beside the window's anchor (L150)

* wip(d3b): tests for main's chat tab commands from a host's chat changes

* wip(d3b): main's windowless chat tab and chat fork tests apply a switch-on host's chat tab through main's layout

* wip(d3b): the switch-on close and open keep their layout writes in the files the writer ratchet counts them in, and a drawn browser close carries its position instead of a rebuilt layout

* Editor tabs main-first: the window sends each editor action to main and draws editor tabs only from main's frames (dark, layout-owner build only)

With ORCA_LAYOUT_OWNER_IS_MAIN on, editor open (preview replace, open to the side), reopen at
its position, close, close-all, promote, the resolved conflict placeholder, a restored-editor
move and a disk rename are layout commands the window sends before it draws anything. Window
content (open file records, drafts, buffers, the autosave gate) is written at once for an open,
and rekeyed in the same store update as the frame for a move or rename. Main publishes each
non-edit tab's view, keeps views in memory across applies and partition changes, stamps the
tab's time, folds same-file duplicates, and republishes a windowless host's session.tabs after
its own file open. The switch-off path is unchanged.

* wip(d3b): a switch-on close main answers workspace_not_found drops the tab's content as tab_not_found does, and placement reads a missing active-group map safely

* wip(d3b): switch-on browser and chat tests open and close through main's command and draw from its frame (test only)

* wip(d3b): a switch-on chat listing writes no tab structure of a workspace main's frames draw; a host chat title changes only the drawn tab's label

* wip(d3b): main's phone rows show a diff tab and a markdown preview as the window's do, with the preview's source named by the window's open-file id

* wip(d3b): the cleanup close's page-read guard counts its named linear passes, so a per-entry rescan still fails switch-on (test only)

* wip(d3b): the static gate's compat entry, focus-read ratchet and formatting hold for the switch-on test conversions and phone rows

* test(d3a-2): the buffer view index follows a rename main answers

* wip(d3b): placement reads the active-group map the real store always holds; the browser test store seeds it

* wip(d3b): a switch-on background browser open leaves the worktree's remembered browser tab unset, as with the switch off

* wip(d3b): a switch-on chat open selects the chat in its group without moving the focused group, as the window's own open does; its test counts the chats main draws

* wip(d3b): the converted simulator, launch-cancel, cleanup-close and chat-listing tests assert what the window's draws asserted, through main's command and frame (test only)

* wip(d3b): main fences a chat's create on its close even when the close fails for want of the chat host, so a cancelled launch never shows

* wip(d3b): a frame keeps the window's hidden chat launch slot after its neighbour, so a resumed chat keeps its saved place through main's frames

* wip(d3b): a declined chat's terminal is placed after the chat and pinned as it is in one main move, and shown before main closes the chat

* wip(d3b): a switch-on chat close the host refuses asks main to open the chat again, as the window's re-read shows it with the switch off

* wip(d3b): a paired server's workspace, its row naming the server, opens and closes browser and chat tabs without main's layout (test only)

* wip(d3b): a refused chat close comes back only while this machine's host still lists the chat, and a close main answers workspace_not_found removes the window-only tab

* wip(d3b): the twin chat close test names what each build stops: no host off, main's host on (test only)

* wip(d3b): the refused-close reopen takes only a chat agent it can launch

* wip(d3b): a frame's browser selection keeps an unchosen workspace's unset through the restore projection's own rule, not a second write

* wip(d3b): the simulator and paired-server tests type their mock stores without assertions (test only)

* wip(d3b): the switch-off declined-chat placement runs as before, with no wait; only the switch-on move waits for main

* chore(d3b): drop the frame-binding helper the main merge left without a caller

* chore(d3b): drop the import the deleted helper used

* wip(d3b): a chat this machine's window creates is placed only by the window's own open, so a reserved chat slot keeps its place (switch-on)

* wip(d3b): main reads a chat tab change against its layout when its queue reaches it, through the one rule the switch-off save uses, so a close right after an open closes it

* wip(d3b): a declined chat's terminal moves to its index among the tabs main draws, not past a hidden launch slot, so it still replaces the chat (switch-on)

* wip(d3b): main's phone-row comments say which rows the window's own row wins, and a test shows a window-listed diff keeps its dirty state (comments and test only)

* wip(d3b): a switch-on chat open returns only the tab's id and chat, the fields main's frame keeps; no caller reads more

* chore(d3b): the unit-routing census registers the browser suites' drawn-from-main store fixture as a support bridge (test only)

* chore(d3b): the chat tab rule's comment wraps and names its two callers (comment only)

* wip(d3b): main's chat tab queue asks a worktree's host only once it has a saved session, and one failed change no longer stops the ones after it (switch-on)

* wip(d3b): a chat tab batch whose changes cannot be read is logged, and the batches after it still land (switch-on)

* test(d3b): a CLI-chat resume's tab is opened once, by the window through main, after the tab before its hidden slot (test only)

* test(d3b): an SSH worktree has no host to run a chat on, so none of the chat open, close or fix paths reach it (test only)

* chore(d3b): the content tab placement reads folder workspaces through main's workspace-record selector, as the new ratchet requires

* fix(merge): put back main's f47f632 files that the merge took from a later main by mistake (no D3b change)

---------

Co-authored-by: E2E Test <e2e@test.local>
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-21 22:32:07 -04:00
Neil a8dff64cb8 chore: remove unused shadcn generator dependency (#31737) 2026-10-21 18:59:05 -07:00
Neil eb9d2f9a06 fix(security): update Node runtimes for October security releases (#31687)
* fix(security): update Node runtimes for October security releases

* fix(ci): compare normalized web file inventory without ordering artifacts
2026-10-21 17:06:28 -07:00
Jinwoo Hong f47f632bd7 feat(mobile): Home's New Workspace opens the desktop's create drawer over Home (native part ships with the next app build) (#31305)
* fix(mobile): the home screen's New Workspace opens the create sheet again

The native host-list route dropped the home screen's action=newWorktree when it built
the page's route, so the page opened as a plain list. The route now forwards it, and
the page names it back once applied so a reload does not reopen the sheet.

Reaches phones only with the next native build.

* test(mobile): drop the New Workspace tests that only re-check types and a null guard

* fix(mobile): Home's New Workspace opens the create sheet over Home

Replaces the action=newWorktree route hand-off. The quick action picks a
host (as Tasks does), then mounts the host's create sheet on Home; a
create opens the new workspace's session marked as just created.

The sheet controller becomes a plain visible/onClose component, and the
host screen's route-action state, the action route param and its builder
are deleted.

* fix(mobile): Home's create sheet seeds only proven worktree rows

Home's cold-start seed is a pathless resume row that the name suggester
cannot read, so the sheet now seeds from proven rows and is keyed by host.
The host screen gets one setter for the sheet's visibility and its ref.

* test(mobile): name the Home new-workspace test after the screen it renders

* proto(mobile): Home's New Workspace opens a native drawer hosting the page's create form

Home pushes /new-workspace/<host>, a react-native-screens formSheet over Home whose content is
MobileWebShellScreen for the page route /h/<host>/new-workspace. The page renders the shared
Create Workspace form with its drawers inline, so the form and its pickers fill the native
drawer. The shell's loading, download and failure states render inside the drawer. Prototype only.

* proto(mobile): the New Workspace drawer is the host screen's own sheet over a transparent modal

The native route is a transparentModal hosting the shell over Home; the page route renders the
host screen's NewWorktreeModal on a transparent page, so the drawer is the same component. While
the page loads, the shell shows a placeholder of that sheet (scrim, grabber, spinner or download
bar), and the page's sheet opens in place without its slide. A host that cannot serve the page
(old desktop, blocked, failed) opens the host list instead of a wall in a drawer.

* feat(mobile): Home's New Workspace opens the page's create drawer over a dimmed Home

A transparent native overlay route hosts the host picker and then the page's
/h/[hostId]/new-workspace route, which renders the same NewWorktreeModal as the
host screen's +. The native scrim matches the drawer backdrop and the page's
first drawer opens already dimmed, so Home stays dimmed throughout. Old desktops
without the route fall back to the host list.

* fix(mobile): one loading layer for Home's New Workspace overlay

The overlay route owns a single tappable loading layer (spinner, or the
download on a small card) that stays mounted from the pick until the page
paints, so the fade and spinner no longer restart per shell state. The shell
reports its load and shows the host list for anything it cannot open; the
picker hands the pick over at once and keeps sliding away. The page's first
drawer takes the dimmed backdrop once, and a gate's overlay pending view no
longer stacks a second scrim over a mounted stack. Native /h/[hostId]/new-workspace
redirects to the overlay. Drops the unused session-route params, the
clipboard grant, and pass-through tests.

* feat(mobile): New Workspace loads in a short drawer over an undimmed Home

While the page loads, the overlay shows one short native drawer in the shared
drawer chrome with a spinner or the download, and nothing dims Home: a tap
above it, a drag down or Back cancels. The page's real drawer then slides up
with its own backdrop exactly as on the host screen's +. Drops the native
scrim, the loading card and the already-dimmed backdrop handoff; the overlay's
WebView stays invisible until the page paints, since a fresh one draws opaque.

* feat(mobile): the New Workspace loading drawer dims Home like any drawer

The short loading drawer opens with the shared drawer backdrop and show
duration, and a tap on that backdrop, a drag down or Back cancels. It stays
in the screen's own window (a native Modal over the loading WebView stalls
the page) and goes in the frame the page paints: the shell reports its load
from a layout effect so the WebView shows and the drawer leaves together.

* fix(mobile): the page's first paint waits for a drawer opening with it

A drawer opening as its page screen mounts holds that screen's first-paint
report until its open animation ends, through a hold on the existing report
rather than a new bridge message. The New Workspace overlay's loading drawer
now stays until the create drawer is up, so a cold open no longer shows Home
undimmed between the two.

* refactor(mobile): the New Workspace overlay owns its exits; the loading drawer fades out

The shell no longer imports the feature: an overlay is a shell with
onOverlayLoad, which reports `unavailable` for what it cannot show, and the
overlay route opens the host list for every exit. HostProtocolGate takes one
transparentWhilePending flag. The loading drawer's drag handler follows a
stable cancel, and on paint the drawer fades out eased in, as the shell's page
cover does, because the WebView takes a frame or two to show the page after it
is revealed; removing it outright left Home undimmed for those frames.

* Revert "fix(mobile): the page's first paint waits for a drawer opening with it"

This reverts commit be0913b9d5.

* fix(mobile): Home's host picker leaves when there is no desktop to pick

An unreadable host catalog, or offered desktops that are all gone, left the
picker invisible inside the transparent overlay, holding a screen over Home
with nothing to dismiss it. The catalog read now resolves to no hosts on
failure, and an empty pick list goes back to Home.

* fix(mobile): Home's create drawer names SSH hosts as the host screen does

The page drawer opened from Home passed no host labels, so a project on an SSH
host showed its run target by raw host id. Reading the SSH target summaries
and display overrides moves into one function that both the host screen's
metadata fetch and the page drawer call.
2026-10-21 20:02:11 -04:00
Kelvin Amoaba 7e795b6be6 feat(browser): show every page load, link clicks included, as a line along the address bar on desktop and mobile (#31597) 2026-10-21 23:46:33 +00:00
Brennan Benson d085427628 Keep model choices coherent across Claude updates (#30107)
* Use one model catalog per execution scope

* Accept a missing session record in host scope

* Merge duplicate launch-resolution import

* Fix catalog typecheck errors

* Align catalog tests with the one-list design

* Type the integration fixture's send without a cast

* Give the discovery test complete runtime dependencies

* Fix model catalog completion and scoped configured display

* Preserve model catalog authority across released clients

* Fix mobile first-frame catalog fixture narrowing

* Keep startup catalog persistence with one completion owner

* fix: preserve chat selections across CLI model updates

A newer CLI can omit a model that an existing chat still runs. Keep that chat's selected model and effort instead of replacing them from the available list.

Discover one model list per host, account, launch command and folder, refreshing after two minutes. Keep current values and the running model's exact effort choices with its chat. Show only the known effort when Claude supplies no matching menu.

Preserve older paired-client delivery and configured launch arguments. Make Codex use independent discovery and retain the established ACP and Pi paths.

Tracked in STA-10947.

* fix: keep the Pi options read within its file limit after main's growth

Tracked in STA-10947.

* fix: import the option snapshot's shared module once

Tracked in STA-10947.

* fix: keep Grok and Pi model menus and fast controls across restarts

Restore the established ACP and Pi catalog route: newer listed models and qualified startup defaults stay visible, and genuine retained effort menus survive later reads. Keep exact-child Fast and Speed controls. Update the phone option contracts and the remaining stale expectations to the selection rules.

Tracked in STA-10947.

* fix: share each workspace's model list and keep live Grok and Pi menus

New chats read their workspace's own model list through the one shared request for that workspace, so a second chat in the same folder starts on the model and effort the agent actually runs there, and a slow first answer can no longer overwrite a newer list. A running Grok, OpenCode or Pi chat keeps its live model menu when an older shared list arrives later. Update the tests that still expected the replaced behavior.

Tracked in STA-10947.

* fix: keep separate agent and chat model lists for Grok, OpenCode and Pi

A brand-new Claude chat now offers the selected model's effort levels before the first message, as it did before. Grok, OpenCode, omp and Pi again keep the list read from the installed agent apart from the list a running chat reports, and each newer list replaces only its own source. So a chat shows the model the agent is actually running with that model's own effort menu, and an effort change can no longer act on an older model's menu. Saved model lists on WSL are readable again without starting the agent.

Tracked in STA-10947.

* fix: keep desktop catalog storage out of the phone's type graph and show Grok's late model answer

The phone app's type check no longer pulls in desktop-only catalog storage. The model-list code no longer reaches the saved-catalog file through a type import. When a Grok model change times out and Grok later confirms the model, the chat shows the model Grok actually runs.

Tracked in STA-10947.

* Remove unrelated merge formatter and package manager churn

* Clarify independent catalog ownership and refresh contracts

* Declare picker demand callback as an effect dependency

* Complete mobile catalog surface and catalog merge types

* Complete catalog contract fixtures for current host types

* fix: preserve catalog source contracts and account fallback

* test: inject independent Codex discovery transport

* test: remove accidental client fixture assertion

* test: qualify the saved account fallback read

* fix(chat): preserve catalog evidence and current child controls

* Fix catalog CI types and mobile option readiness

* Reconcile session controls after inventory changes

* Fix catalog import and deferred test resolver types

* Keep model capability observations separate from picker views

* Use installed model choices for chat options

* Fix model catalog test fixture contracts

* Restore model option compatibility and stable settlement

* Return a catalog reply in provisional launch fixture

* Compose unreported live model defaults in the host

* Preserve explicit replacement model on recovery send

* fix(chat): use stable scoped catalogs on local desktop

* test(chat): preserve generic catalog runner type

* fix: scope catalogs to configured environment and keep saved reads passive

* fix: satisfy catalog regression and controller CI checks
2026-10-21 16:42:27 -07:00
Neil 5f50b95c9c fix: remediate dependency security advisories across workspaces (#31681)
* fix: remediate dependency security advisories across workspaces

* test: verify security patches without ad hoc dependency installs
2026-10-21 16:31:17 -07:00
Neil 2e4af3f272 chore(ratchet): drop the stale owner-adapter focus seed and prune the baseline (#31625)
resolveOwnerWithLegacyFallback stopped reading the Active Server focus in #31058, so seeding it
counted 92 phantom focus reads across 55 files.
2026-10-21 12:42:43 -07:00
NeilandClaude 5dd5294294 perf(orcad): keep the server bundle ASCII so V8 stores it at half the size (#31495)
* perf(orcad): keep the server bundle ASCII so V8 stores it one byte per character

129 non-ASCII characters in regex literals (and three in a String.raw plugin
template) made V8 hold the whole ~11 MB orcad-server.js source as a two-byte
string. Write them as \uXXXX escapes and fail the build if any return.

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

* fix(orcad): keep license comments ASCII and name the module the guard trips on

esbuild gathers legal comments at the end of the bundle; escape that comment so
a dependency's license text cannot fail the ASCII guard, while licenses still
ship with the code. The guard now reports the offset and, from an unminified
rebuild, the module the character came from. A dark-switch build is covered.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-21 04:06:49 -07:00
Jinwoo Hong 2eae220b2e refactor(renderer): read workspace records through typed selectors (#31462)
* refactor(renderer): read workspace records through typed selectors

* refactor(renderer): keep only the selectors with readers; type the host owner lookup
2026-10-21 06:29:18 -04:00
Neil c149395c20 fix(remote-terminal): repaint an idle full-screen program when a reconnect can't image its pane (#31482)
* fix(remote-terminal): repaint an idle program when a reconnect can't image its pane

After an SSH reconnect the pane is repainted from the host's screen
snapshot. When the host cannot give one (too large for the replay limit,
no buffer, unanswered, overflowed), the pane only printed a loss banner and
an idle full-screen program that writes diffs kept its pre-disconnect frame
until the user resized.

The two places a remote pane declares it has no host image now ask the
program to repaint through one choke point: a one-row viewport claim and
back, which every TUI honours (Node's tty ignores a same-size SIGWINCH).
It respects a phone or another desktop holding the size, skips hidden
panes, and puts the row back even if the pane unmounts mid-nudge.

Fixes #26887

* test(e2e): idle full-screen program frame after a managed SSH outage

* test(e2e): wait for the pane before typing in the idle-frame spec

* fix(remote-terminal): pick the redraw height on the host's clamped grid

* fix(remote-terminal): wait for the host's grid before putting the redraw row back

* test: give transport mocks a connection status for the redraw gate

* test(e2e): keep the orcad idle-frame proxy typed for the e2e ratchet

* fix(remote-terminal): let a new PTY's redraw supersede a swapped-out nudge

* test: keep new transport-mock lines free of type assertions

* ci(e2e): route the managed-SSH idle-frame spec to its sources
2026-10-21 02:48:21 -07:00
Leo(오영택)andJaeyoung22 582efa7989 fix(i18n): correct Korean sleep with descendants wording (#31456)
* fix(i18n): correct Korean sleep with descendants wording

* fix(i18n): keep Korean descendant sleep label compact

---------

Co-authored-by: Jaeyoung22 <89302528+Jaeyoung22@users.noreply.github.com>
2026-10-21 01:38:06 -07:00
NeilandArthur Chang 0cba9f2e08 fix(mobile): render offline Nerd Font symbols on first terminal display (#31446)
Fixes #17168

Supersedes community PR #17220. Original contributor credit is preserved below.

Co-authored-by: Arthur Chang <arthurc0102@gmail.com>
2026-10-21 01:17:08 -07:00