# Technical Documentation: stablyai/orca

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/stablyai/orca)

### Overview (README.md)

<h1 align="center">
  <a href="https://onOrca.dev"><img src="resources/build/icon.png" alt="Orca" width="64" valign="middle" /></a> Orca
</h1>

<p align="center">
  <a href="https://github.com/stablyai/orca"><img src="https://img.shields.io/github/stars/stablyai/orca?style=flat&amp;label=%E2%98%85&amp;color=08C" alt="GitHub stars" /></a>
  <a href="https://github.com/stablyai/orca/releases"><img src="docs/assets/readme-downloads.svg" alt="Total downloads across all releases" /></a>
  <img src="https://img.shields.io/badge/license-MIT-08C?style=flat" alt="License: MIT" />
  <a href="https://discord.gg/fzjDKHxv8Q"><img src="https://img.shields.io/badge/Discord-5865F2?logo=discord&logoColor=white" alt="Join the Orca Discord" /></a>
  <a href="https://x.com/orca_build"><img src="https://img.shields.io/badge/X-000000?logo=x&logoColor=white" alt="Follow Orca on X" /></a>
  <img src="https://img.shields.io/badge/macOS%20%7C%20Windows%20%7C%20Linux-4493F8?style=flat-square" alt="Supported platforms: macOS, Windows, and Linux" />
</p>

<p align="center">
  <sub><a href="docs/readme/README.zh-CN.md">中文</a> · <a href="docs/readme/README.ja.md">日本語</a> · <a href="docs/readme/README.ko.md">한국어</a> · <a href="docs/readme/README.es.md">Español</a> · <a href="docs/readme/README.fr.md">Français</a> · <a href="docs/readme/README.pt.md">Português</a></sub>
</p>

<p align="center">
  <strong>The AI Orchestrator for 100x builders.</strong><br/>
  Run Codex, ClaudeCode, OpenCode or Pi side-by-side — each in its own worktree, tracked in one place.
</p>

<h3 align="center"><a href="https://onorca.dev/download"><ins>Download Orca</ins></a></h3>

<p align="center">
  <img src="docs/assets/readme-hero.jpg" alt="Orca desktop app running agents in parallel worktrees, with the Orca mobile companion app in the corner" width="960" />
</p>

## Features

<table>
<tr>
<td width="50%" valign="middle">

### Mobile Companion

Monitor and steer your agents from your phone — get notified when an agent finishes and send follow-ups from anywhere.

[iOS App Store](https://apps.apple.com/us/app/orca-ide/id6766130217) · [Android APK](https://www.onorca.dev/download/android) · [Docs →](https://www.onorca.dev/docs/mobile)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/mobile"><picture><source srcset="docs/assets/feature-wall/mobile-companion-app-showcase.gif" type="image/gif"><img src="docs/assets/feature-wall/mobile-companion-app-showcase.jpg" alt="Orca desktop with the mobile companion app" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Parallel Worktrees

Fan one prompt across five agents, each in its own isolated git worktree — compare the results and merge the winner.

[Docs →](https://www.onorca.dev/docs/model/worktrees)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/model/worktrees"><picture><source srcset="docs/site/public/docs/tab-split.gif" type="image/gif"><img src="docs/site/public/docs/posters/tab-split.jpg" alt="Parallel worktree orchestration" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Terminal Splits

Ghostty-class terminals with WebGL rendering, infinite splits, and scrollback that survives restarts.

[Docs →](https://www.onorca.dev/docs/terminal)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/terminal"><picture><source srcset="resources/onboarding/feature-wall/tile-02.webp" type="image/webp"><img src="resources/onboarding/feature-wall/tile-02.poster.jpg" alt="Terminal splits" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Design Mode

Click any UI element in a real Chromium window to send its HTML, CSS, and a cropped screenshot straight into your agent's prompt.

[Docs →](https://www.onorca.dev/docs/browser/design-mode)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/browser/design-mode"><picture><source srcset="docs/site/public/docs/orca-design-mode.gif" type="image/gif"><img src="resources/onboarding/feature-wall/tile-05.poster.jpg" alt="Embedded browser and Design Mode" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### GitHub &amp; Linear, Native

Browse PRs, issues, and project boards in-app — open a worktree from any task and review without a context switch.

[Docs →](https://www.onorca.dev/docs/review/linear)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/review/linear"><picture><source srcset="resources/onboarding/feature-wall/tile-03.webp" type="image/webp"><img src="resources/onboarding/feature-wall/tile-03.poster.jpg" alt="GitHub and Linear task workflows in Orca" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### SSH Worktrees

Run agents on a beefy remote box with full file editing, git, and terminals — auto-reconnect and port forwarding included.

[Docs →](https://www.onorca.dev/docs/ssh)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/ssh"><picture><source srcset="resources/onboarding/feature-wall/tile-06.webp" type="image/webp"><img src="resources/onboarding/feature-wall/tile-06.poster.jpg" alt="Remote worktrees over SSH" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Annotate AI Diffs

Drop comments on any diff line and ship them back to the agent — review, edit, and commit without leaving Orca.

[Docs →](https://www.onorca.dev/docs/review/annotate-ai-diff)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/review/annotate-ai-diff"><picture><source srcset="docs/site/public/docs/annotate-ai-diff.gif" type="image/gif"><img src="resources/onboarding/feature-wall/tile-08.poster.jpg" alt="Annotate AI-generated diffs" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Drag Files to Agents

VS Code's editor with autosave everywhere — drag files or images straight into an agent prompt.

[Docs →](https://www.onorca.dev/docs/editing/file-explorer)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/editing/file-explorer"><picture><source srcset="resources/onboarding/feature-wall/tile-07.gif" type="image/gif"><img src="resources/onboarding/feature-wall/tile-07.poster.jpg" alt="Drag files and images into an agent prompt" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Orca CLI

Agents drive Orca too — script every workflow with `orca worktree create`, `snapshot`, `click`, and `fill`.

[Docs →](https://www.onorca.dev/docs/cli/overview)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/cli/overview"><picture><source srcset="resources/onboarding/feature-wall/tile-09.webp" type="image/webp"><img src="resources/onboarding/feature-wall/tile-09.poster.jpg" alt="Script Orca from the CLI" width="100%" /></picture></a>
</td>
</tr>
</table>

**Also in the box:**

- **[Quick open](https://www.onorca.dev/docs/model/quick-open)** — Search across worktrees, files, agents, commands, and repo context without leaving your flow.
- **[Account switcher &amp; usage tracking](https://www.onorca.dev/docs/agents/usage-tracking)** — See Claude and Codex usage and rate-limit resets, and hot-swap accounts without re-logging in.
- **[Rich repo previews](https://www.onorca.dev/docs/editing/markdown)** — Preview Markdown, images, PDFs, and repo docs in the workspace.
- **[Computer Use](https://www.onorca.dev/docs/cli/computer-use)** — Let agents operate desktop apps and visible UI when a workflow needs real interaction.
- **[Notifications and unread state](https://www.onorca.dev/docs/notifications)** — Know when an agent finishes or needs attention, then mark threads unread to come back later.
- **And many, many more** — we ship daily, so this list is perpetually behind. The [changelog](https://github.com/stablyai/orca/releases) is the real feature list.

---

## Supported Agents

Works with **any CLI agent** — if it runs in a terminal, it runs in Orca.

<p>
  <a href="https://docs.anthropic.com/claude/docs/claude-code"><kbd><img src="docs/assets/claude-logo.svg" alt="Claude Code logo" width="16" valign="middle" /> Claude Code</kbd></a> &nbsp;
  <a href="https://github.com/openai/codex"><kbd><img src="https://www.google.com/s2/favicons?domain=openai.com&sz=64" alt="Codex logo" width="16" valign="middle" /> Codex</kbd></a> &nbsp;
  <a href="https://x.ai/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=x.ai&sz=64" alt="Grok logo" width="16" valign="middle" /> Grok</kbd></a> &nbsp;
  <a href="https://cursor.com/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=cursor.com&sz=64" alt="Cursor logo" width="16" valign="middle" /> Cursor</kbd></a> &nbsp;
  <a href="https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli"><kbd><img src="https://www.google.com/s2/favicons?domain=github.com&sz=64" alt="GitHub Copilot logo" width="16" valign="middle" /> GitHub Copilot</kbd></a> &nbsp;
  <a href="https://dev.meta.ai/docs/muse-code"><kbd><img src="src/shared/agent-icons/muse.png" alt="Muse logo" width="16" valign="middle" /> Muse</kbd></a> &nbsp;
  <a href="https://deepseek-harness.github.io/deepseek-harness/"><kbd><img src="src/shared/agent-icons/dsh.png" alt="DeepSeek Harness logo" width="16" valign="middle" /> DeepSeek Harness</kbd></a> &nbsp;
  <a href="https://zcode.z.ai/en/docs"><kbd><img src="src/shared/agent-icons/zcode.png" alt="ZCode logo" width="16" valign="middle" /> ZCode</kbd></a> &nbsp;
  <a href="https://opencode.ai/docs/cli/"><kbd><img src="https://www.google.com/s2/favicons?domain=opencode.ai&sz=64" alt="OpenCode logo" width="16" valign="middle" /> OpenCode</kbd></a> &nbsp;
  <a href="https://mimo.xiaomi.com/coder"><kbd><img src="https://www.google.com/s2/favicons?domain=mimo.xiaomi.com&sz=64" alt="MiMo Code logo" width="16" valign="middle" /> MiMo Code</kbd></a> &nbsp;
  <a href="https://ampcode.com/manual#install"><kbd><img src="https://www.google.com/s2/favicons?domain=ampcode.com&sz=64" alt="Amp logo" width="16" valign="middle" /> Amp</kbd></a> &nbsp;
  <a href="https://openclaude.gitlawb.com/"><kbd><img src="resources/openclaude-logo.png" alt="OpenClaude logo" width="16" valign="middle" /> OpenClaude</kbd></a> &nbsp;
  <a href="https://antigravity.google/docs/cli-overview"><kbd><img src="https://www.google.com/s2/favicons?domain=antigravity.google&sz=64" alt="Antigravity logo" width="16" valign="middle" /> Antigravity</kbd></a> &nbsp;
  <a href="https://pi.dev"><kbd><img src="https://pi.dev/favicon.svg" alt="Pi logo" width="16" valign="middle" /> Pi</kbd></a> &nbsp;
  <a href="https://omp.sh"><kbd><img src="https://omp.sh/favicon.svg" alt="oh-my-pi logo" width="16" valign="middle" /> oh-my-pi</kbd></a> &nbsp;
  <a href="https://hermes-agent.nousresearch.com/docs/"><kbd><img src="https://www.google.com/s2/favicons?domain=nousresearch.com&sz=64" alt="Hermes Agent logo" width="16" valign="middle" /> Hermes Agent</kbd></a> &nbsp;
  <a href="https://devin.ai/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=devin.ai&sz=64" alt="Devin logo" width="16" valign="middle" /> Devin</kbd></a> &nbsp;
  <a href="https://block.github.io/goose/docs/quickstart/"><kbd><img src="https://www.google.com/s2/favicons?domain=goose-docs.ai&sz=64" alt="Goose logo" width="16" valign="middle" /> Goose</kbd></a> &nbsp;
  <a href="https://docs.augmentcode.com/cli/overview"><kbd><img src="https://www.google.com/s2/favicons?domain=augmentcode.com&sz=64" alt="Auggie logo" width="16" valign="middle" /> Auggie</kbd></a> &nbsp;
  <a href="https://github.com/autohandai/code-cli"><kbd><img src="https://www.google.com/s2/favicons?domain=autohand.ai&sz=64" alt="Autohand Code logo" width="16" valign="middle" /> Autohand Code</kbd></a> &nbsp;
  <a href="https://github.com/charmbracelet/crush"><kbd><img src="https://www.google.com/s2/favicons?domain=charm.sh&sz=64" alt="Charm logo" width="16" valign="middle" /> Charm</kbd></a> &nbsp;
  <a href="https://docs.cline.bot/cline-cli/overview"><kbd><img src="https://www.google.com/s2/favicons?domain=cline.bot&sz=64" alt="Cline logo" width="16" valign="middle" /> Cline</kbd></a> &nbsp;
  <a href="https://www.codebuddy.ai/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=codebuddy.ai&sz=64" alt="CodeBuddy logo" width="16" valign="middle" /> CodeBuddy</kbd></a> &nbsp;
  <a href="https://www.codebuff.com/docs/help/quick-start"><kbd><img src="https://www.google.com/s2/favicons?domain=codebuff.com&sz=64" alt="Codebuff logo" width="16" valign="middle" /> Codebuff</kbd></a> &nbsp;
  <a href="https://freebuff.com"><kbd><img src="src/shared/agent-icons/freebuff.png" alt="Freebuff logo" width="16" valign="middle" /> Freebuff</kbd></a> &nbsp;
  <a href="https://commandcode.ai/docs/quickstart"><kbd><img src="https://www.google.com/s2/favicons?domain=commandcode.ai&sz=64" alt="Command Code logo" width="16" valign="middle" /> Command Code</kbd></a> &nbsp;
  <a href="https://docs.continue.dev/guides/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=continue.dev&sz=64" alt="Continue logo" width="16" valign="middle" /> Continue</kbd></a> &nbsp;
  <a href="https://docs.factory.ai/cli/getting-started/quickstart"><kbd><img src="docs/assets/droid-logo.svg" alt="Droid logo" width="16" valign="middle" /> Droid</kbd></a> &nbsp;
  <a href="https://kilo.ai/docs/cli"><kbd><img src="https://raw.githubusercontent.com/Kilo-Org/kilocode/main/packages/kilo-vscode/assets/icons/kilo-light.svg" alt="Kilocode logo" width="16" valign="middle" /> Kilocode</kbd></a> &nbsp;
  <a href="https://www.kimi.com/code/docs/en/kimi-code-cli/getting-started.html"><kbd><img src="https://www.google.com/s2/favicons?domain=moonshot.cn&sz=64" alt="Kimi logo" width="16" valign="middle" /> Kimi</kbd></a> &nbsp;
  <a href="https://kiro.dev/docs/cli/"><kbd><img src="https://www.google.com/s2/favicons?domain=kiro.dev&sz=64" alt="Kiro logo" width="16" valign="middle" /> Kiro</kbd></a> &nbsp;
  <a href="https://github.com/mistralai/mistral-vibe"><kbd><img src="https://www.google.com/s2/favicons?domain=mistral.ai&sz=64" alt="Mistral Vibe logo" width="16" valign="middle" /> Mistral Vibe</kbd></a> &nbsp;
  <a href="https://github.com/QwenLM/qwen-code"><kbd><img src="https://www.google.com/s2/favicons?domain=qwenlm.github.io&sz=64" alt="Qwen Code logo" width="16" valign="middle" /> Qwen Code</kbd></a> &nbsp;
  <a href="https://support.atlassian.com/rovo/docs/install-and-run-rovo-dev-cli-on-your-device/"><kbd><img src="https://www.google.com/s2/favicons?domain=atlassian.com&sz=64" alt="Rovo Dev logo" width="16" valign="middle" /> Rovo Dev</kbd></a> &nbsp;
  <kbd>+ any CLI agent</kbd>
</p>

---

## Install

### Desktop — macOS, Windows, Linux

- **[Download from onOrca.dev](https://onorca.dev/download)**
- Or grab a build directly: [macOS Apple Silicon](https://github.com/stablyai/orca/releases/latest/download/orca-macos-arm64.dmg) · [macOS Intel](https://github.com/stablyai/orca/releases/latest/download/orca-macos-x64.dmg) · [Windows (.exe)](https://github.com/stablyai/orca/releases/latest/download/orca-windows-setup.exe) · [Linux AppImage](https://github.com/stablyai/orca/releases/latest/download/orca-linux.AppImage) · [All builds](https://github.com/stablyai/orca/releases/latest)

_Or via a package manager:_

```bash
# macOS (Homebrew)
brew install --cask stablyai/orca/orca

# Arch Linux (AUR) — or stably-orca-git to build from source
yay -S stably-orca-bin
```

### Mobile Companion — iOS, Android

Pair with your desktop app to monitor and steer your agents from your phone.

- **iOS:** [Download on the App Store](https://apps.apple.com/us/app/orca-ide/id6766130217)
- **Android:** [Download APK](https://www.onorca.dev/download/android) · [Install guide](https://www.onorca.dev/docs/android-apk)

---

## Community &amp; Support

- **Discord:** Join the community on **[Discord](https://discord.gg/fzjDKHxv8Q)**.
- **Twitter / X:** Follow **[@orca_build](https://x.com/orca_build)** for updates and announcements.
- **WeChat:** Group 12 is full. Scan to join the Orca community WeChat group 13.

  <img src="docs/assets/wechat-qr-group13.jpg" alt="WeChat group 13 QR code for the Orca community" width="160" />

- **Feedback &amp; Ideas:** We ship fast. Missing something? [Request a new feature](https://github.com/stablyai/orca/issues).
- **Privacy:** See the [privacy &amp; telemetry docs](https://www.onorca.dev/docs/telemetry) for what anonymous usage data Orca collects and how to opt out.
- **Show Support:** [Star](https://github.com/stablyai/orca) this repo to follow along with our daily ships.

---

## Developing

Want to contribute or run locally? See our [CONTRIBUTING.md](.github/CONTRIBUTING.md) guide.

The relay that pairs the mobile app with a desktop host is also in this repository under
[`cloud/`](cloud/README.md), with a separate pnpm workspace and setup guide.

<a href="https://github.com/stablyai/orca/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=stablyai/orca" alt="Orca contributors" />
</a>

<p align="center">
  <img src="docs/assets/star-history.png" alt="GitHub star history chart for stablyai/orca" width="880" />
</p>

## Signed Builds

Windows code signing sponored/provided by [SignPath.io](https://signpath.io), certificate by [SignPath Foundation](https://signpath.org).

## License

Orca is free and open source under the [MIT License](LICENSE).

---

### Site README (`docs/site/README.md`)

# @orca/docs

This package contains the product documentation and public media intended to
ship alongside Orca's source code.

Open-source product documentation for [Orca](https://www.onorca.dev), served at `/docs` (same URL shape as `https://www.onorca.dev/docs`).

This package is a **self-contained Next.js app**. It is intentionally **not** a root monorepo workspace member, so installing Electron app dependencies does not pull Next/fumadocs.

## Local development

Use Node.js 24 and the pnpm version pinned in `package.json`. CI reads the
Node.js version from this package's `engines.node` field.

```bash
cd docs/site
pnpm install
pnpm dev
```

Open [http://localhost:3004/docs](http://localhost:3004/docs).

## Linting

Run `pnpm lint` inside this directory. The isolated Oxlint
configuration keeps the native equivalents of the previous Next.js, React,
React hooks and accessibility checks at their existing severities, including
alt text on Next.js `Image` components. Generated Next.js and MDX output is
excluded. Oxlint also reports its default correctness warnings.

The migration does not provide equivalents for React's deprecated-API check,
React Compiler configuration/gating diagnostics, or Next.js's relative
`location.assign` advisory. JSX variable-use bookkeeping is handled by Oxlint's
parser. Keep TypeScript and the production Next.js build in the validation
workflow; this is not a claim of complete ESLint rule parity.

## Production build

```bash
cd docs/site
pnpm install
pnpm build
pnpm start
```

`pnpm start` serves the production build on port 3004. Paths:

| Path               | Purpose                              |
| ------------------ | ------------------------------------ |
| `/`                | Redirects to `/docs`                 |
| `/docs`            | Docs index                           |
| `/docs/...`        | Nested doc pages from `content/docs` |
| `/docs/api/search` | Fumadocs search index                |
| `/docs/og/...`     | Per-page Open Graph image route      |

## Layout

- `content/docs/` — MDX pages + `meta.json` navigation
- `public/docs/` — docs-only media, logo, and favicon (GIFs, posters, screenshots)
- `src/app/docs/` — fumadocs routes, OG images
- `src/components/` — docs-scoped chrome (header/footer/search), not marketing site

## Updating content

Treat the documentation tree as a deliberate publication boundary. Before
importing source material, review the diff for internal references, credentials,
third-party media rights, and feature/version claims, then copy only approved
pages and assets. Keep the app shell and deployment configuration in this
repository so a docs-only pull request can be reviewed and built independently.

## Same-domain routing

The docs app is a separate Vercel project (the **docs zone**) and keeps the
public `/docs` URL namespace. The marketing site remains the default zone for
`www.onorca.dev`; configure its Next/Vercel proxy with these `beforeFiles`
rewrites, replacing `DOCS_ORIGIN` with the docs project's production URL:

```js
return {
  beforeFiles: [
    {
      source: '/docs',
      destination: `${DOCS_ORIGIN}/docs`
    },
    {
      source: '/docs/:path*',
      destination: `${DOCS_ORIGIN}/docs/:path*`
    },
    {
      source: '/docs-static/:path*',
      destination: `${DOCS_ORIGIN}/docs-static/:path*`
    }
  ]
}
```

`/docs-static` is the docs zone's `assetPrefix`; it prevents `_next` asset
collisions with the marketing zone. Keep the rewrites in the default zone and
use ordinary `<a>` links when navigating between zones. Do not add
`basePath: '/docs'` to this app: its route tree and Fumadocs `baseUrl` already
include `/docs`, so doing so would publish `/docs/docs/...` URLs. If a future
deployment needs `basePath`, first move the route tree to an unprefixed
`src/app/[[...slug]]` shape and update every generated/link URL together.

## Deploy (Vercel)

1. Create a Vercel project with **Root Directory** unset (`.`). The workflow
   invokes Vercel from `docs/site`, so that directory is already the deployment
   root; setting it again would resolve `docs/site/docs/site`. Leave automatic
   Git deployments disabled so this workflow remains the only deployment path.
2. Framework preset: Next.js. This package's `engines.node` selects Node.js 24 for
   Vercel builds and functions. `vercel.json` pins install (`pnpm install --frozen-lockfile`) and build (`pnpm build`) over the dashboard settings; this package has its own lockfile beside the root workspace.
3. Set GitHub Actions secrets (required by the production deploy job):
   - `VERCEL_TOKEN`
   - `VERCEL_ORG_ID`
   - `VERCEL_PROJECT_ID` (docs project, not the marketing site)
4. Protect the `docs-production` GitHub environment with required reviewers and
   custom deployment branch policies for `main` and `v*` tags. The release-cut
   dispatch runs from `main`; the direct published-release fallback runs from a
   stable tag, while the workflow still authorizes only exact stable tags.
5. Prepare the three rewrites above in the `www.onorca.dev` marketing project,
   but leave them disabled until the docs deployment is verified. A Vercel
   custom domain cannot delegate only `/docs` by itself; the default zone must
   proxy both page/API/media requests and `/docs-static` assets. Keep the
   marketing project's old docs routes available for rollback during the
   transition; putting the proxy rules in `beforeFiles` ensures they win once
   enabled.
6. Deploy a stable desktop tag that contains `docs/site`, verify the docs
   origin, then enable the marketing rewrites. Tags cut before this package was
   added cannot bootstrap the docs project because production intentionally
   checks out the tag's exact commit. Remove the old marketing docs routes in a
   follow-up after the proxy is stable.

`.github/workflows/docs.yml` runs credential-free checks for every pull request.
It intentionally does not deploy PR previews: a PR-controlled build must not
receive Vercel credentials. A maintainer can add a separate trusted preview
workflow later. Production deploys only from an authorized stable desktop release: an exact
`vX.Y.Z` tag, a published non-prerelease release, and the
`github-actions[bot]` release author. Manual dispatch must run from the default
branch and name an existing release that meets the same checks. Mobile,
prerelease, draft, and human-authored releases are skipped. Fork pull requests
remain build-only because GitHub does not expose deployment secrets to fork
jobs.

## Versioning

`versioning.config.ts` keeps the current unversioned `/docs` URLs stable while
reserving `content/versions/<id>` for future snapshots. Versioned routes are not
live yet; when they are needed, add the corresponding loader/routes and a
version entry. The release workflow can then deploy them without a trigger
change.

## Isolation from the desktop app

- Own `package.json` + `pnpm-lock.yaml` under `docs/site/`
- Not listed in the root `pnpm-workspace.yaml`; its own `pnpm-workspace.yaml` makes it a separate workspace root and holds its pnpm settings, which pnpm 12 ignores under `--ignore-workspace`
- Engineering notes remain in repo-root `docs/` — do not confuse with this package

---

### Audits Acknowledged Tab Retirement README (`docs/audits/acknowledged-tab-retirement/README.md`)

# Acknowledged terminal-tab retirement

A host-requested whole-tab close can return `closed: true` while its row remains in `orca-data.json`. Once a repository's host topology revision is positive, the Store preserves host membership against a renderer save that merely omits a tab. The renderer acknowledges after its session flush; its graph removal can arrive later. The existing runtime fallback committed retirement only after that graph stopped owning the parent.

This fixes that acknowledgement ordering path relevant to [#17344](https://github.com/stablyai/orca/issues/17344). It does not establish that every tab in that report followed this path or explain large RSS from the small persisted row alone.

## Run

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/acknowledged-tab-retirement/reproduce.mjs
```

The runner uses temporary Vite configurations and the repository's cross-platform process runner. It reverses only `fix.patch` in memory for the baseline; the checkout remains unchanged. Tests use temporary Store files and inert desktop/provider ports. No application window, shell, SSH connection, or external host is started.

The 20-case artifact fixture drives the actual renderer close and session builders, Store persistence/reload, and runtime close/graph methods. Eleven source regressions exercise actual Store/runtime boundaries, including provider exit and spawn. The baseline has **28 failing fixed-behavior assertions and 3 passing controls**; the fix passes **31 cases**. Results include source hashes, exact failed cases, process exit codes, and timeout status.

## Ownership checks

The acknowledged-close path captures the original execution-host partition, persisted tab creation/generation, leaf/PTY bindings, and current process incarnations. It rechecks these after the renderer acknowledgement before committing the existing host retirement transaction. Original leaves may disappear during physical exit, and the tab's aggregate PTY or remote-session ID may promote an original surviving sibling. A new leaf, binding, incarnation, tab generation, or host is refused. Current pins are checked by the existing commit primitive; explicit force retains its existing meaning.

The process guard covers original persisted row, remote-session and leaf IDs, plus snapshot leaf/layout IDs. Thus a partial mobile snapshot cannot hide an original sibling's replacement. Missing runtime records alone are not treated as evidence of process death. The existing headless fallback still collects persisted SSH kill IDs before it commits; the added renderer-owned path issues no provider kill.

Controls include local and SSH-backed repositories and folder workspaces, unrelated host partitions, same-ID replacements, physical split exits, stale renderer saves/graphs, current pins, force, renderer refusal, and disk failure. The renderer-owned live snapshot can remain until its ordinary graph removal arrives; an older queued graph cannot restore it afterward.

## Scope limits

- A direct renderer-only close of a never-bound local tab still sends no explicit host retirement request. That separate gap remains reproduced by a negative control.
- The new durable identity capture requires the legacy terminal row in `tabsByWorktree`. Unified-only terminal metadata is outside this fix.
- The existing retirement primitive propagates a failed disk flush, but its attempted rollback can be rebased against the staged newer topology revision. The failure control verifies rejection and the original disk row; it does not claim successful in-memory rollback.
- No wire fields, process-liveness verdicts, or membership-fence rules change. SSH persistence stays in its existing host partition; this does not reconcile the separate historical renderer/host partition divergence.
- The related [original audit](../local-tab-close-rebase/README.md) traced the branch in reported **v1.4.192**. Executable comparisons use current source with this patch reversed, not the historical packaged application.

---

### Audits Auth Filesystem Wait Retention README (`docs/audits/auth-filesystem-wait-retention/README.md`)

# Release aborted shared auth filesystem waits

When a filesystem operation remains pending, later Codex/Kimi quota polls reuse it and wait with new deadlines. The old waiter uses `Promise.race` for every poll. On the installed Electron runtime, each abandoned race retains its rejection reason until the raw operation settles, despite removing its abort listener. The fix uses the existing `PromiseSettlementWaiters` registry, which attaches one raw-result reaction and removes expired waiters.

The original ownership symbols, last-waiter cancellation finalizer, one-raw-operation behavior, live callers, and future reads of a late result are preserved. Auth opts into deferred abort settlement so an already-queued raw result keeps its `Promise.race` priority; 24 success/failure/abort schedules compare equal before and after. Existing registry consumers keep their immediate-abort behavior. The abort factory type accepts `unknown` so false, zero, strings, and objects retain the existing auth rejection semantics. No admission or timeout limit changes.

## Reproduce

```sh
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=128 docs/audits/auth-filesystem-wait-retention/reproduce.cjs
```

For Electron, run the installed Electron executable with `ELECTRON_RUN_AS_NODE=1` and the same arguments/environment. This launches no window. The proof reconstructs the original sources by reversing `fix.patch`; expected baseline hashes in `source-versions.json` make source drift fail. Both versions run the actual production scheduler/waiter code, with only the raw filesystem operation replaced by one manually settled promise. A 15-second deadline fails stalled proof execution.

| Runtime / source                         | Plain aborted Errors alive while raw result pending | Amplified payload objects alive | After raw result settles | After owner drops |
| ---------------------------------------- | --------------------------------------------------- | ------------------------------- | ------------------------ | ----------------- |
| Node 26.6.0 / original                   | 1 of 128                                            | 1 of 128                        | 1                        | 0                 |
| Node 26.6.0 / fixed                      | 1 of 128                                            | 1 of 128                        | 1                        | 0                 |
| Electron 43.7.0, Node 24.21.0 / original | 128 of 128                                          | 128 of 128                      | 1                        | 0                 |
| Electron 43.7.0, Node 24.21.0 / fixed    | 1 of 128                                            | 1 of 128                        | 1                        | 0                 |

The one remaining reason belongs to the existing cancellation controller's first abort. Dropping the shared operation releases it. AbortController objects and abort listeners are released by both versions. The amplified arm attaches a **synthetic 64 KiB Uint8Array** to each Error, at most 8 MiB per case. Ordinary timeout errors are much smaller; the separate plain-Error arm verifies that artificial bytes are not needed to reproduce retention. Runtime differences are measured, without attributing them to a particular V8 change.

Controls check an already-aborted first caller never starting raw work, raw rejection identity, aborted caller identity, future callers receiving late and already-settled results, a live sibling surviving cancellation, arbitrary abort reasons, and removed listeners. The unit regression additionally checks that repeated expired waits add no raw-result reactions and that their plain Error objects are collectible while a live caller still needs the operation.

Validation: 50 auth/registry tests pass, including 18 new cases; the reverse-patch configuration produces two expected failures and 48 passes. Six existing watcher-consumer suites pass another 39 tests. Node, Web, and CLI typechecks, focused ordinary/type-aware lint, formatting, and the changed-code quality gate pass. `validation.json` records the test paths and scope. To run the prior implementation against the current tests, use `--config docs/audits/auth-filesystem-wait-retention/before.config.mjs` with those six auth/registry test paths.

## Scope and limits

The three production consumers are `codex-auth-presence.ts`, `codex-backend-auth.ts`, and `kimi-fetcher.ts`. Each intentionally retains the shared operation until actual filesystem settlement to avoid stacking native requests when UNC/WSL reads stall. This audit does not reproduce a real filesystem stall, historical Electron binary behavior, or an affected-host workload.

The auth source matches `v1.4.198` and the audited main revision; the existing registry also matches main. The earlier broad accumulator PR #10179, reverted by #10255, added path/waiter/admission limits to this module. This change instead removes abandoned wait reactions and introduces no such limits. Nothing here attributes #19831 or #19768 to this mechanism or claims an incident-scale memory slope.

---

### Audits Browser Closed Result Retention README (`docs/audits/browser-closed-result-retention/README.md`)

# Closed browser dispatcher retains completed results behind pending native work

Before the fix, completed command results stayed in a closed browser dispatcher until its final handler settled. The fix releases settled cache records at close and drops newly settled records while closed. Pending native handlers, page authority, and executor teardown keep their existing lifetime.

## Actual paths and bounds

Source references in this section describe the hash-fenced baseline. `BrowserClientHostCommandDispatcher.dispatch` refuses every command once closed, before authority or duplicate lookup (`browser-client-host-command-dispatcher.ts:77–79`). `close` aborts active work and cancels queued work, but retains its pages and cached completed records when the join returns false (`:156–179`). `finishHandler` clears those owners only after the last native handler settles (`:268–272`). A newly settled cancellation record is also cached while a sibling remains active (`:297–302`).

`BrowserClientHostCommandResultCache.clear` drops only its record-to-page index. PageState.records and PageState.sequencesByCommandId also own the records; clearing just that index does not release result graphs. Existing `releasePage` uses exact settled-record eviction to remove both indexes (`browser-client-host-command-result-cache.ts:27–55`).

Defaults (`browser-client-host-command-state.ts:7–13`) are 256 pages, 256 active commands, 8 concurrent handlers, 32 queued/page, 64 cached results/page, 1,024 cached results total, and a 5,000 ms close/retirement join. Automation result schema allows at most 768 KiB of JSON-serialized value (`browser-client-automation-protocol.ts:5,89–99,116–131`). These are count/serialized-value limits, not a guaranteed heap/RSS size. The audit uses 32 tiny results, one native wait, and one canceled tiny queued input; it does not allocate near those maxima.

Production composition calls dispatcher close via `PairedRuntimeBrowserClientHost.closeHost` (`paired-runtime-browser-client-host.ts:165–180`). If close times out, actual `closeBrowserClientHostComposition` defers executor close behind `whenHandlersSettled` (`paired-runtime-browser-client-host-teardown.ts:37–56`). Keeping handler/page/native authority alive is intentional. Completed results cannot serve new or duplicate closed requests and need not share that lifetime.

## Ordinary handler time boundaries

- The navigation command checks cancellation before starting, then calls `routeWebContents.navigateGuest` (`browser-client-page-command-execution.ts:20–40`). The actual registry delegates to `navigateBrowserRouteGuest`, which awaits native `guest.loadURL` (`browser-route-guest-lifecycle.ts:99–123`) without adding a JS deadline or taking the AbortSignal. Native completion/rejection remains its settlement owner.
- Automation checks cancellation before execution, registers the exact guest, and forwards the signal into RPC (`browser-client-page-automation-runtime.ts:42–57`; startup `main-process-ready-runtime.ts:61–77`). Core handlers such as browser.snapshot destructure runtime and call its method without observing that signal (`runtime/rpc/methods/browser-core.ts:39–43`).
- The ordinary agent-browser helper execution has a 90-second default subprocess timeout (`agent-browser-bridge-types.ts:6`; `agent-browser-bridge-raw-process.ts:20–36`), with overrides for some operations. The agent-bridge embedded goto wrapper separately has a 30-second navigation timeout. Those deadlines are not a universal bound on all handler phases, and the direct route navigate path above does not use that wrapper.

The condition is a handler that outlives the dispatcher's five-second join. No affected-host occurrence, natural indefinite stall, or incident attribution has been established.

## Bounded actual-source proof

`scenario.test.mjs` uses the actual dispatcher, page executor, automation runtime, browser.snapshot RPC descriptor, native-navigation wrapper, and composition teardown function. Existing page-executor harness supplies renderer/session/route ports. The runtime's browserSnapshot/native loadURL are small controlled ports; no Electron window, OS child, real web request, or network is used. Logger/mock result arrays do not own the produced payloads: the automation output and dispatcher handler are plain functions, and each completed value is observed only through WeakRef after the helper returns.

Both Node 26.6 and Electron 43.7 / Node 24.21 pass all 4 cases before and after the two-file fix. A 15 ms join override keeps the proof bounded; the native port is explicitly resolved in finally and all native custody eventually settles.

| Observation                                                            | Before | Fixed |
| ---------------------------------------------------------------------- | -----: | ----: |
| Completed small payload objects alive after timed-out close            |     32 |     0 |
| Cached records after close (32 results + create + queued cancellation) |     34 |     0 |
| Canceled queued input still reachable                                  |    yes |    no |
| Running native handlers after close                                    |      1 |     1 |
| Page/executor and route/session custody retained                       |    yes |   yes |
| Close repeated before native settlement                                |  false | false |
| whenClosed pending before native settlement                            |    yes |   yes |
| Payload objects alive after explicit native settlement                 |      0 |     0 |
| First late cancellation cached while sibling remains pending           |      1 |     0 |

Open request replay preserves the exact same Promise and runs once. Closed duplicates fail with dispatcher_closed. Normal native resolution and rejection both complete the existing settlement path. Executor close and route/session release happen only after native settlement in both variants.

The initial candidate compatibility run passed 78 tests in 5 files, including its three lifecycle cases and existing dispatcher, page executor, paired-runtime composition, and paired-runtime host tests. The permanent retention suite adds two object-lifetime regressions: releasing completed results while native navigation stays pending, and releasing one late closed record while a sibling handler remains active. Final validation passed 77 tests across 5 production suites, Node typechecking, and all five explicit quality scans over product and artifact code. Reversing the patch produces the two expected lifetime failures while all 16 existing dispatcher tests pass. Commands and outcomes are recorded in `validation.json`.

## Fix scope

`fix.patch` changes only:

- `src/main/browser/browser-client-host-command-dispatcher.ts`: release each page's already-settled cache during close, after cancellation; discard newly settled records instead of caching them when closed.
- `src/main/browser/browser-client-host-command-result-cache.ts`: accept an optional `retain` flag in `record`, defaulting to the existing caching behavior. When false, existing identity-checked eviction drops the settled record before any cache admission.

Active/cancelling records, pages, native promises, abort behavior, join timing, FIFO, generation/authority checks, and the closed-settlement promise remain owned exactly as before. No row/byte cap changes. The host and executor continue waiting for their existing settlement owners.

## retirePage is separate

`selectCommandPage` rejects retiring and retired generations before `findExistingCommand` (`browser-client-host-command-page.ts:34–43`). Thus retired duplicate replay is already unavailable, even though the cache remains until forget/replacement. The control confirms that behavior and verifies explicit forget releases the cache and preserves the stale-generation floor. This fix leaves that existing retirement policy unchanged; freeing results on page retirement is a separate possible follow-up, especially while executor cleanup is still pending. Do not assume a live duplicate replay contract where the actual admission path rejects first.

## Reproduction and source fences

```sh
ORCA_BACKGROUND_LAUNCH=1 ORCA_BROWSER_CACHE_VARIANT=before pnpm exec vitest run --config docs/audits/browser-closed-result-retention/vitest.config.mjs docs/audits/browser-closed-result-retention/scenario.test.mjs
ORCA_BACKGROUND_LAUNCH=1 ORCA_BROWSER_CACHE_VARIANT=fixed pnpm exec vitest run --config docs/audits/browser-closed-result-retention/vitest.config.mjs docs/audits/browser-closed-result-retention/scenario.test.mjs
```

For Electron run the installed binary with `ELECTRON_RUN_AS_NODE=1` and `ORCA_BACKGROUND_LAUNCH=1`, passing node_modules/vitest/vitest.mjs and the same arguments. Reports are separate per runtime/variant; set `ORCA_BROWSER_CACHE_OUTPUT` to another file path to preserve captured reports. `sources.cjs` reverses `fix.patch` in memory and checks exact baseline/fixed hashes plus 21 caller/dependency hashes. The config loads those sources at their real production module IDs without changing checkout files. A synthetic CRLF control checks all 24 source/patch reads against canonical LF hashes. Both variants use the same controlled producer and lifecycle ports.

`source-versions.json` records 23 canonical-LF source/caller hashes. All 23 match named main checkpoint 291b4ddd6f1c1af480169885e0fda7f9c78ff053; 21 match v1.4.198. Both fixed source baselines match both named versions. The proof executes current source/dependencies, not a historical application binary.

## Permanent regression validation

```sh
ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/main/browser/browser-client-host-command-retention.test.ts src/main/browser/browser-client-host-command-dispatcher.test.ts src/main/browser/browser-client-page-command-executor.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts
ORCA_BACKGROUND_LAUNCH=1 ORCA_BROWSER_CACHE_VARIANT=before pnpm exec vitest run --config docs/audits/browser-closed-result-retention/vitest.config.mjs src/main/browser/browser-client-host-command-retention.test.ts src/main/browser/browser-client-host-command-dispatcher.test.ts
ORCA_BACKGROUND_LAUNCH=1 pnpm tc:node
```

The baseline regression command intentionally fails the two new lifetime assertions. The source and object counts prove a code mechanism, not incident-specific browser use, native stall duration, aggregate app RSS, or attribution to #19831.

---

### Audits Browser Destroyed Guest Retention README (`docs/audits/browser-destroyed-guest-retention/README.md`)

# Destroyed browser guests retain main-process callbacks

An embedded browser guest's `destroyed` event called `cleanupGuestPolicyAttachment`. That removed its primary page-to-WebContents lookup but left four per-page cleanup callbacks that capture the destroyed WebContents wrapper, plus renderer/workspace/worktree/profile metadata. `unregisterAll` subsequently iterated only the now-empty primary lookup: three callback maps and the renderer/workspace metadata survived that cleanup too.

Renderer reload can destroy guests without each page sending explicit unregister IPC. A later close of a restored, unmounted page does not send that IPC either: `destroyPersistentWebview` returns early when its renderer registry has no guest. Explicit unregister correctly releases these resources; same-page re-registration also replaces its callbacks. The defect affects destroyed owners that do not take either path.

The fix routes destruction through the existing `unregisterGuest` with a guest-retirement reason only when that exact guest still owns the primary page ID. Already bound downloads retain their existing renderer routing until they settle; explicit page close still cancels them. Unregistered guests and popups retain policy-only cleanup. A stale callback cannot unregister a replacement. Normal renderer-process loss keeps its live WebContents and metadata for reload recovery; a fresh guest registration supplies its ownership metadata again. Shared browser sessions and sibling pages are untouched.

## Download lifetime correction

Review found that the initial fix treated guest destruction as logical page closure and canceled bound downloads. An exact-source before/after control confirmed that difference with an EventEmitter guest and controlled DownloadItem. Guest retirement now releases guest-owned callbacks while preserving ongoing page downloads, their destinations and cancel authorization. A retained numeric renderer route drains after the last download settles, provided no replacement guest, other download or newer routing owner needs it.

Nine additional controls cover progress and completion/error delivery, explicit close after guest destruction, multiple downloads, replacement guests/routing, repeated guest destruction and renderer loss. Together with four existing browser suites, 55 tests pass; Node typecheck and ordinary/anti-slop lint pass. These controls do not establish native Chromium download survival after destruction on each operating system. No download capacity or wire format changes.

## Reproduce

With dependencies already installed, run from the repository root:

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/browser-destroyed-guest-retention/reproduce.mjs
```

The script runs the actual manager and guest callback installers with EventEmitter WebContents fixtures. It removes only the destruction guard in memory for the baseline, then runs the same nine tests on the fixed source. A temporary test observer records actual map sizes before each assertion. It launches no Orca window or browser process, limits each worker to 512 MiB and each run to 60 seconds, uses the shared process launcher, and removes temporary files. Results include source hashes and runtime provenance.

| After 1,000 distinct guest destructions                                    | Before | Fixed |
| -------------------------------------------------------------------------- | -----: | ----: |
| Primary guest lookup                                                       |      0 |     0 |
| Each context-menu / grab-shortcut / app-shortcut / wheel cleanup map       |  1,000 |     0 |
| Each renderer / workspace / worktree / profile map                         |  1,000 |     0 |
| Policy cleanup map                                                         |      0 |     0 |
| Each context-menu / grab-shortcut / app-shortcut map after `unregisterAll` |  1,000 |     0 |
| Renderer and workspace maps after `unregisterAll`                          |  1,000 |     0 |

Baseline: six tests pass, three fail. Fixed: all nine pass. Controls cover explicit unregister, same-ID replacement with a captured old callback, popup and pre-registration destruction, renderer-process recovery, fresh guest registration, and two pages sharing one browser session profile. The selected existing browser-manager and offscreen lifecycle suites also passed: 64 tests across seven files including the new suite.

## Version and limits

Targeted source reads of `v1.4.198` confirm the same destroyed-event policy-only cleanup, map ownership, and `unregisterAll` omission. The executable comparison uses current production source; it does not launch the historical app. This is a retaining path present in the version reported by #19831/#19768. It does not establish that either incident followed this destruction sequence, or measure native memory retained by a destroyed WebContents. The 1,000 iterations measure retained callbacks and metadata, not 1,000 surviving Chromium processes or a gigabyte allocation.

Adjacent audit negatives: explicit page close removes the renderer guest registry and main registration; worktree switching deliberately parks guests under the existing hidden-worktree retention policy; offscreen creation is synchronously indexed with shutdown admission and exact-window teardown; client-hosted async page creation checks availability after acquisitions and cleans canceled owners. PDF capture rejects late disconnected-client completion, its stream buffers have a five-minute TTL, and existing screenshot commands have deadlines. No additional native screenshot hang or unbounded native-page acquisition was reproduced. The separate late renderer registration reply can restore small page-ID metadata after close; it is outside this main-process fix.

---

### Audits Browser Registration Reply Retention README (`docs/audits/browser-registration-reply-retention/README.md`)

# Late browser registration replies restore retired renderer state

`createBrowserPageWebviewGuestSession` awaited `registerGuest` IPC and then wrote the returned guest ID into the renderer's persistent `registeredWebContentsIds` map. An explicit close could remove the webview and map entry before that reply arrived; a delayed success restored the retired entry. An older reply could also overwrite the ID of a replacement guest. Its follow-on callbacks could synchronize an obsolete annotation bridge or mutate recovery state after the listener session was disposed. Separately, recovery validation could issue repair IPC after its initial registration query outlived that owner.

The fix checks the existing recovery disposal state, current listener ref, persistent registry identity, and captured WebContents ID before accepting a reply or running those continuations. It makes no new registry and sends no late unregister IPC. A current hidden guest still accepts successful registration. When a persistent guest remounts, the new session's existing `validateAfterResume` path retries registration if the old reply was ignored. Current unsuccessful replies and current repair retain their prior behavior.

## Reproduce

With dependencies already installed:

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/browser-registration-reply-retention/reproduce.mjs
```

This runs the actual renderer session, recovery controller, and persistent guest registry against headless DOM fixtures and deferred IPC replies. The baseline reverses only the included production patch in memory. A temporary observer records map/callback counts after all replies settle. The script uses the shared process launcher, 512 MiB workers, a 60-second deadline, and temporary files removed in `finally`. No Orca window, native guest, or remote host is launched.

| After 1,000 explicit guest closes and delayed successful replies | Before | Fixed |
| ---------------------------------------------------------------- | -----: | ----: |
| Live webviews                                                    |      0 |     0 |
| Retained registration entries                                    |  1,000 |     0 |
| Late annotation synchronizations                                 |  1,000 |     0 |
| Unregister calls                                                 |  1,000 | 1,000 |

The baseline fails ten tests and passes six controls; fixed source passes all 16. Cases cover distinct closed IDs, replacement elements, a changed guest ID on the same element, a remount reusing the same element/ref, disposed and moved refs, registry removal before listener disposal, a throwing identity getter, hidden current guests, successful/inconclusive replies, and late versus current repair. The repair-completion case verifies that an old success cannot clear a newer guest's recovery error. All host-guest suites also pass: 196 tests across 23 files, including recovery, viewport, registry, worktree retention, and paintability. The web typecheck passes.

## Version and limits

Targeted reads of `v1.4.198` confirm the same unconditional registration setter, post-reply callbacks, post-query repair, and close-time map deletion. This establishes a renderer retaining path in the reported version, not that #19831 or #19768 exercised it. Each retained entry is a page ID and numeric guest ID. This proof does not show a surviving native browser process or explain gigabyte-scale memory growth. The independent main-process destroyed-guest callback retention has its own fix and proof.

The registration reply is the only production setter of `registeredWebContentsIds`; explicit close and replacement remove its key. Following callers found no second setter that could recreate this same metadata after removal. The annotation callback uses current page routing, which is why skipping a stale callback is necessary without issuing cleanup against a replacement.

---

### Audits Browser Viewport Owner Retention README (`docs/audits/browser-viewport-owner-retention/README.md`)

# Retired browser viewport operation ownership

The viewport operation captures a guest ID, then awaits CDP commands. Closing a tab deletes its viewport state, but the old continuation can subsequently recreate the UA-intent entry. A failed UA clear can also restore the old value over a replacement guest's completed desktop preset, or a late clear can delete the replacement's mobile intent.

The correction reuses that captured guest ID at three mutation boundaries: before publishing an applied preset's UA intent, before reading/deleting a cleared preset's intent, and before failed-clear rollback. Same-owner rollback, native UA profiles, navigation behavior, and the per-tab promise chain are preserved.

## Evidence

The regression fixture calls the actual manager, registration, unregistration, and viewport implementation. Electron WebContents and pending debugger replies are controlled ports; no native browser or window is launched.

- Baseline: **7 failing ownership regressions, 5 passing controls**.
- Fixed: **12/12 ownership cases**, plus **30 existing viewport, navigation, partial-failure, and UA cases**.
- Sixteen pending UA-clear rejections after `unregisterAll` leave **16 retired UA entries before, zero after**. Registration, preset, and promise maps remain empty.
- Other regressions cover closed-tab late success, failed-clear rollback, mobile/desktop replacement, and native-to-default profile replacement.
- Controls preserve ordinary serialized mobile/desktop/null operations, both native-profile presets, same-owner rollback, and the replacement promise tail while old queued operations settle.
- An independent reviewer ran all 12 candidate cases and reviewed the three mutation guards before promotion.

The retained entries are tab ID strings and booleans. This does **not** demonstrate retained native WebContents, a process RSS slope, or gigabyte-scale memory growth. In-flight CDP work still owns its continuation until it settles. Positive and negative post-close command replies are injected schedules, not an affected-host trace.

## Ordinary callers and compatibility

The renderer requests overrides when the user selects a viewport preset and on guest `dom-ready`, including null presets. The trusted IPC handler validates dimensions before calling this manager. Navigation later reads the UA-intent map, so stale replacement values can alter the standing mobile/desktop identity. The fixture does not execute the renderer or IPC producer.

Both local webview and host-side offscreen registrations use these maps. The correction changes no wire fields, protocol, execution-host ownership, native process lifecycle, folder/worktree handling, or UI layout. It only prevents an operation for a different guest from mutating the current registration's state.

`source-versions.json` records 11 paths at audit checkpoint `4a09b1d1`, independent main `291b4ddd`, and reported v1.4.198 `e0826956`. The viewport implementation, registration, registry declarations, IPC handler, and toolbar producer match all three. Ten sources match independent main and eight match v1.4.198. The guest-session producer contains an earlier audit fix; historical navigation and fixture sources differ. This is a current-dependency replay with the exact historical viewport source, not a historical app-binary replay.

The browsing activity in #19831 makes this path applicable in principle. The report does not establish the required overlap or tab count, and this small metadata mechanism does not account for its reported memory totals.

## Reproduction

From the worktree, run the fixed regression suite:

```sh
ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config docs/audits/browser-viewport-owner-retention/vitest.config.mjs
```

Run the same tests with the exact baseline viewport implementation; exit status 1 and seven failed cases are expected:

```sh
ORCA_BACKGROUND_LAUNCH=1 ORCA_VIEWPORT_BASELINE=1 node node_modules/vitest/vitest.mjs run --config docs/audits/browser-viewport-owner-retention/vitest.config.mjs
```

The import overlay never rewrites product files. `baseline-source.txt` contains only the original viewport module; current support modules remain in use. `baseline-results.json`, `fixed-results.json`, and `validation.json` record the measured results and their scope.

---

### Audits Cdp Stream Retention README (`docs/audits/cdp-stream-retention/README.md`)

# CDP outbound retention reproduction

From the worktree root:

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/cdp-stream-retention/reproduce.mjs
```

This bundles the current response writer and connects real loopback WebSockets. The reader pauses while the producer sends at most 128 MiB. The script starts no Electron application or PTY and closes all sockets. `before.json` records the same experiment against the unbounded writer; `after.json` records the fixed writer.

Before the fix, 128 MiB of generated payload left 133,177,280 bytes in the WebSocket buffer, with roughly linear growth at every sample. The fixed writer uses the existing outbound queue: an 8 MiB socket soft threshold, 64 MiB held-queue cap and 4,096 queued-frame cap. The stalled connection terminates on overflow and releases its queue. The socket threshold can overshoot by one frame; a single large reply on a clear connection remains permitted, preserving large PDF/screenshot responses. This bounds accumulated backlog, not the allocation needed to construct an individual response or the aggregate across arbitrarily many browser pages.

Unit tests also cover a reader that drains a complete burst in order, request/session correlation, queue disposal on close/replacement, and a 65 MiB healthy reply. Debugger events and command responses share this writer.

This establishes an Electron-main retention mechanism when a CDP automation client stops reading. Neither #19831 nor #19768 establishes that precondition, so it is not a confirmed cause of either incident. RSS reflects GC/allocator timing; queued bytes are the direct measurement.

---

### Audits Claude Branch Proof Streaming README (`docs/audits/claude-branch-proof-streaming/README.md`)

# Claude transcript branch-proof allocation

Orca verifies the complete conversation ancestry before preserving or resuming a Claude session. This change reads the transcript incrementally instead of keeping the whole file and its split lines in memory. It still checks every record in the observed byte range, including old ancestors and disconnected conflicts. It does not just parse the first line.

## Reproduce

After the repository's normal dependency installation:

```sh
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc docs/audits/claude-branch-proof-streaming/reproduce.cjs /tmp/claude-branch-streaming-results.json
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/claude-branch-proof-streaming/regressions.mjs /tmp/claude-branch-streaming-regressions.json
```

`diff` is a pinned direct development dependency. The harness reverses `fix.patch` against the checked-out reader in memory and verifies the original reader's SHA-256 before building the comparison. It does not modify production files. esbuild writes temporary modules, which are removed along with their require-cache entries on exit. No window or network access is needed.

The variants in the JSON are:

- `baseline`: the original full-file reader, reconstructed and hash-checked.
- `candidate`: an open-ended streaming experiment retained as a control. It can admit newly appended records and is not the implementation being merged.
- `windowCandidate`: the exact checked-out production reader, including its one internal refresh after descriptor growth.

`results.json` and `electron-results.json` were regenerated against production revision `2e623a227474101160de4c977bfae69da4b052ee`. They record the runtime, tool versions, harness inputs, actual bundled dependencies, production source variants, and bundle hashes. Allocation and duration values vary between runs. `provenance.json` now describes the shipped source; reproduction fails if its source hashes or quoted call-site lines drift.

The obsolete initial-window and open-ended result snapshots were removed because they described a different splitter and reader. Their experimental results remain available in git history. `baseline-initial-results.json` is explicitly historical: its original runtime and complete dependency graph were not captured, so it is not current reproducibility evidence.

## Production behavior

The reader opens the file once, stats that descriptor, and streams a finite range through the existing line splitter. Each message body can be released after parsing; the UUID ancestry graph remains until validation finishes. The existing 10,000-ancestor limit stays unchanged. No transcript quota, record-size cap, eviction, or tail-only proof is introduced.

A valid first range returns immediately, even if a newer marker appears later. When the first range is missing a marker/cursor or ends in an incomplete record, the reader checks the same descriptor's size. If the descriptor size increased, it retries the entire proof once over the enlarged finite range. Already-completed repairs therefore succeed without a caller retry. An unfinished repair can still require the existing caller retry. Conflicting ancestry, invalid sessions, malformed middle records, and append-order errors remain errors.

An increased size does not establish append-only writes or prove that the original prefix stayed unchanged. This is not an atomic filesystem snapshot: in-place overwrites and truncation can affect bytes inside the range, as with the old reader. Failed restat, shrink, and pathname replacement do not prove growth. The original opened descriptor is always closed before the production reader returns or throws.

## Reachability

`provenance.json` records exact source hashes and call sites for the current implementation and reported release v1.4.198:

- The local structured Claude runtime adapter resolves the account's transcript and delegates to the full branch proof through `session-file-resolver.ts`.
- Closing a structured session reads the transcript after connection close, before persisting the handle. A proof failure preserves the previously observed leaf. Unexpected-exit persistence uses the same reader.
- The resumed-PTY verifier calls the full reader and retries incomplete-tail errors on its existing 100 ms cadence, within a 15-second deadline. Static absence and invalid history remain fatal there.

These paths already used unrestricted full-file reads in v1.4.198. Rewind handling is newer than that release. The audit establishes source reachability and synthetic allocation reduction; it does not establish a user's transcript size, the number of concurrent readers, or the cause of a reported production OOM.

## Evidence and limits

Both recorded runtime audits pass 51 static real-file cases across all file/string APIs, 24 growth and cleanup controls, and 10 actual-verifier cases. Growth controls inject append, replace, delete, shrink, stat-error, and read-error events against real files. The three completed-repair verifier cases now succeed on the first caller attempt. Static absence, conflicts, session mismatches, and append-order failures retain their error behavior.

The harness checks the original `FileHandle` for closure. Historical readers without an exposed handle also check descriptor identity, avoiding false leak reports when the OS reuses a closed descriptor number. The fixture uses a private Node filesystem binding only for deterministic timing; production uses public APIs.

`regression-results.json` records 68 resolver, rewind, recovery, and history-window tests passing on each of the three variants (204 tests). The focused production run also passed all 26 streaming tests: 94 tests across the five relevant files in total. Tests were run on macOS arm64; this does not claim a Windows/Linux or live SSH integration run.

The 32 MiB allocation fixture uses 64 message bodies of 512 KiB with realistic identifiers. The current Electron audit measures about **34.6 MB** additional sampled live heap for the original reader and **1.1 MB** for the production reader. Both release almost all of that allocation after returning. Sampling forces GC at actual `JSON.parse` boundaries; it is not RSS, a natural peak, or evidence of a retained leak. GC noise can make retained deltas negative.

A single 8 MiB record still needs about **16.8 MB** under every variant. The identifier graph still grows with record count. This removes whole-transcript payload retention; it is not a total memory cap.

The separate duration control reads one stable 2.79 MB file with 8,193 small records, using two warm rounds and five measured rounds in alternating order without forced GC during timing. Electron medians were **4.9 ms before** and **7.37 ms after**. These are synthetic per-run observations, not a throughput guarantee or a timing gate.

`electron-results.json` uses installed Electron 43.7.0 / Node 24.21.0 in Node-only mode, with no app/window launch:

```sh
ELECTRON_RUN_AS_NODE=1 ORCA_BACKGROUND_LAUNCH=1 node_modules/electron/dist/Electron.app/Contents/MacOS/Electron --expose-gc docs/audits/claude-branch-proof-streaming/reproduce.cjs /tmp/claude-branch-streaming-electron.json
```

Other platforms use their installed Electron executable with the same environment and arguments. This is compatibility evidence for the installed runtime, not the historical Electron binary.

---

### Audits Claude History Window Budget README (`docs/audits/claude-history-window-budget/README.md`)

# Claude history-window read quota

Restart reconciliation's Claude history reader declares a 16 MiB source quota, but the original `stat` followed by unrestricted `readFile` admits later growth. Reuse `readNodeFileWithinLimit` to enforce the same quota while reading from one descriptor. Overflow preserves the existing inconsistent-history result; it cannot establish that a submitted message was absent.

## Reproduce

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/claude-history-window-budget/reproduce.mjs
```

The seven-case fixture calls the actual history reader, branch proof and bounded reader against real temporary files. It injects an append immediately after the relevant size snapshot. The baseline reverses only `fix.patch` in a temporary Vite transform, leaving the checkout unchanged. Each child has a 45-second timeout and a 512 MiB old-space ceiling. No application window or provider process starts.

| Case                                                      | Before                                         | After                                                                   |
| --------------------------------------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------- |
| 305-byte valid source grows to 17 MiB after size snapshot | Reads 17,825,792 bytes and accepts the history | Stops after 16,777,217 bytes, rejects history and closes its descriptor |
| Stable valid source                                       | Accepted                                       | Accepted                                                                |
| Exactly 16 MiB                                            | Accepted                                       | Accepted                                                                |
| Initially 16 MiB plus one byte                            | Rejected before content read                   | Same                                                                    |
| Growth within quota                                       | Reads the additional bytes and accepts         | Same                                                                    |
| Read error                                                | Inconsistent history                           | Same; descriptor closed                                                 |
| Missing anchor                                            | No source read                                 | Same                                                                    |

Before: one failing fixed-behavior assertion and six passing controls. After: seven passing cases. `results.json` records observed read bytes, size snapshots, descriptor counts, source hashes, exit codes and timeout status. These are read-budget measurements, not RSS or retained-heap measurements; the byte quota does not describe all parser allocations.

This reader is **absent from v1.4.198**, so the finding cannot explain #19768 or #19831. It fixes a current-source quota race. The separately unrestricted `proveClaudeTranscriptBranch` file reader has no declared byte quota and is outside this change. The related legacy-import quota race is already addressed by #20976.

---

### Audits Claude Task Retention README (`docs/audits/claude-task-retention/README.md`)

# Retained Claude background-task text

The actual Claude task tracker retained oversized input strings through its
512-character description/name slices. Its live tasks, settled tasks, and
recently removed tasks can each retain those slices. The fix uses the existing
`ownRetainedString` at the shared text boundary; normalization, UTF-16 clipping,
task identity, publication, and lifecycle behavior stay the same.

This extends [ML-018 / #20960](https://github.com/stablyai/orca/pull/20960).

## Reproduce

```sh
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc docs/audits/claude-task-retention/reproduce.cjs
```

The script bundles the actual tracker and its retention classes. Its baseline
removes only the new copy call in memory. It exercises flat strings, concatenated
strings, and JSON-parsed SDK-style frames; each input has a distinct task owner.
It measures after GC, then clears the tracker and yields before measuring cleanup.
[Results and bundle hashes](./results.json) preserve the complete run.

| JSON-parsed case          |   Input per task | Tasks |      Visible text | Heap before | Heap after |
| ------------------------- | ---------------: | ----: | ----------------: | ----------: | ---------: |
| Live                      | 64 Ki characters |    32 | 16,384 characters |   2,125,672 |     43,536 |
| Settled                   | 64 Ki characters |    32 | 16,384 characters |   2,127,072 |     44,296 |
| Removed, awaiting outcome | 64 Ki characters |    32 |      0 characters |   2,108,136 |     25,360 |
| Live                      |  4 Mi characters |     8 |  4,096 characters |  33,562,624 |     11,048 |
| Settled                   |  4 Mi characters |     8 |  4,096 characters |  33,563,960 |     11,656 |
| Removed, awaiting outcome |  4 Mi characters |     8 |      0 characters |  33,558,584 |      7,008 |

Captured with Node v26.6.0 on macOS. Cleanup returned near the initial heap for
every case. Six regression tests retain the actual tracker through these three
lifetimes for both descriptions and names. Text behavior tests preserve whitespace
normalization, fallback names, and a clipped surrogate pair.

## Reachability and limits

`claude-stream-json-connection.ts` forwards SDK messages to the structured adapter,
whose `emit` calls `backgroundTasks.observe`. Installed SDK 0.3.251 uses Node
`readline` to assemble stdout records, parses each record with `JSON.parse`, then
yields it. The inspected path imposes no record or description length limit;
native read-chunk size does not cap an assembled JSON field. Descriptions are
declared as plain strings in `SDKTaskStartedMessage`.

The description slice and this SDK version also exist in `v1.4.198`; that tag
keeps the reader inline in `claude-background-task-tracker.ts`. The separate
settled/recently-removed retention and name-reader paths describe current code.

The current maps are count-bounded: at most 256 live, 256 settled, and 256 recently
removed entries per tracker. Settled context clears when no visible work remains;
recently removed context awaits an outcome, eviction, or explicit clearing.
Session end/close clears the tracker. Copy work is at most 512 UTF-16 code units
per retained field, and it does not reduce temporary parsing allocation.

These are synthetic oversized task fields, not evidence that an affected user
received such fields. The path concerns structured Claude sessions, not ordinary
terminal output or stderr. Neither #19831 nor #19768 establishes this trigger.

The separate digest-bounded subagent ID was also checked at actual consumers.
The mobile response sanitizer can temporarily retain the original until JSON
serialization flattens its concatenated ID. Worker transcript bounding already
serializes for its byte budget and released that parent in the probe. No durable
ID-owner leak was established, so that helper is unchanged.

---

### Audits Closed Editor Model Lifetime README (`docs/audits/closed-editor-model-lifetime/README.md`)

# Closed editor model lifetime

Closing the final editor, or closing a file while every editor panel is hidden, can leave its Monaco text model registered for the renderer's remaining lifetime. The old cleanup hook lived in the conditionally mounted `EditorPanel`: it disappeared before observing the final close and missed closes while absent. `MonacoEditor` uses `keepCurrentModel`, so unmounting the widget does not provide the missing cleanup.

The fix subscribes from the app shell and lazily receives the model registry from the existing `monaco-setup` load. It retires only closed tab owners. It preserves live same-URI editors, replacement model objects, generated diff namespaces, attached models until detachment, and caches written by a reopened owner during a disposal callback. Existing scroll, selection, preview, and PDF caches retain their separate 20-entry LRU lifetime. No open tab, dirty draft, or live editor history is pruned. Closed-file undo follows Monaco's existing bounded policy; it is not unlimited (see below).

## Measured result

| Actual source control                        | Before                                                                 | Fixed                                       |
| -------------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------- |
| Eight distinct last-file closes              | 8 registered closed models; 2,097,208 logical characters; 0 open files | 0 registered models; 0 open files           |
| Close with another editor still mounted      | Closed model disposed; sibling preserved                               | Same                                        |
| Close while hidden, then open another editor | Old closed model remains; 2 models for 1 open file                     | Old model disposed; 1 model for 1 open file |
| Normal terminal/editor switch                | Original open model preserved                                          | Same                                        |

All four controls pass with their stated before/fixed expectations on both graphs and both runtimes: **32 comparative cases**, Node 24.20.0 and Electron 43.7.0 / Node 24.21.0. Reports are `worktree-*-results.json` and `main-*-results.json`. Contents use eight distinct fixture paths and 256 KiB of logical characters per model; this is a controlled amplification, not a field file-size measurement or heap-byte estimate.

This explains a concrete renderer retention mechanism relevant to [#12845](https://github.com/stablyai/orca/issues/12845). The same conditional panel, hook placement, and `keepCurrentModel` chain exists in reported v1.4.170; `source-versions.json` records four historical source hashes and matching line numbers. That comparison is static, not an execution of the historical application. It does not establish which allocations caused #12845's reported heap, or the process and cause of [#19831](https://github.com/stablyai/orca/issues/19831).

## Ownership and regression coverage

The **27 permanent cases across five suites** in `closed-editor-model-{lifetime,reentrancy,registry,shell}.test.*` and `closed-editor-view-state-retention.test.ts` use actual installed Monaco models and store close actions. They cover final/hidden panel closure, live siblings and shared URIs, replacement model identities, attachment/detachment, reopening, registry removal/replacement, reentrant registry changes, diff prefixes, bounded caches, and ordinary edit closes without a global registry scan. Together with the three existing disposal/cache suites, **47 tests pass**. The fixture exercises Monaco's real attachment ports without creating a native editor widget.

The startup routing suite now also guards the production app-shell hook placement: deleting that call fails the test. All **75 selected tests across nine suites pass**. Full Web typechecking currently reports one unrelated existing unused `NativeChatMessage` import in `NativeChatMessageList.windowing.test.tsx`; it reports no errors in the changed files.

Registry notifications cancel obsolete queued callbacks but preserve captured model identities and attached-model listeners. A temporary missing registry defers cleanup until registration returns; an interrupted disposal batch is retried. The identity fence prevents that retry from disposing a successor at the same URI. Controller teardown still cancels that controller's ownership and listeners; this audit does not claim to reconcile all preexisting models after an app-shell HMR remount.

### Closed-file undo decision

Keep Monaco's bounded closed-file undo store rather than retaining every edited model indefinitely, following the same design used by [VS Code's model service](https://github.com/microsoft/vscode/blob/main/src/vs/editor/common/services/modelService.ts). Installed Monaco keeps about 20 MiB of closed-file undo data. Tests confirm ordinary POSIX paths and `file:///C:/...` paths restore undo when reopened with matching contents. Large files (over 10 MiB of text in the fixture) and raw Windows `C:/...` paths do not. Those cases lose undo after close/reopen, including final/hidden tabs whose models previously leaked. Existing view-state caches remain intact within their 20-entry budget. This is a deliberate bounded-memory tradeoff, not zero user-visible risk.

## Source fences and independent main publication

`source-versions.json` fences **1,021 repository paths**: the complete loaded repository graph of the comparative proof plus selected static wiring/caller files. Vite rejects any additional repository module under `src/` or `config/`. Package code is not part of that repository-graph claim: twelve separate dependency hashes pin the installed package metadata and relevant Monaco model/registry/event and Zustand store implementations.

- Worktree baseline: `f142544a8653eedab2eec0237a9005001497b2f5`.
- Publication main: `291b4ddd6f1c1af480169885e0fda7f9c78ff053`.
- `fix.patch` contains only the eight product changes, including two new modules. The loader reverses it from the checked-out fixed sources to reconstruct baseline behavior.
- `main-context.patch` reconstructs 22 unrelated context differences, including two paths absent on main. It is proof scaffolding; do not apply it as a product patch.
- Main retains its unrelated `registerShellMarkdownAliases` import/call in `monaco-setup.ts`. The standalone product patch preserves both lines.
- The loader checks each before/fixed hash, refuses source/dependency drift, normalizes CRLF to LF, and supports both the working checkout and an independently published main checkout. `loader-results.json` records synthetic CRLF and simulated-main-checkout controls for all four graph/variant combinations.

The comparative test executes the actual legacy surface, old/new cleanup hook, disposal functions, store close action and model registry. It supplies controlled Panel/Shell ports and a fixture store to the hook. Actual app-shell placement, first lazy registration, conditional panel callers and `keepCurrentModel` wiring are source-fenced static checks; it does not render the entire application or exercise HMR through Vite itself. It opens no Electron window, starts no native editor widget, and measures no RSS, heap bytes, or incident allocation rate.

## Review follow-up

The registry-transition regression is covered by four new cases. The uninformative cold-registry fixture assertion was removed, and this audit's product patch, source hashes, loader controls, and all eight comparative reports were regenerated from the current sources. The audit directory is now explicitly allowed in `.gitignore`.

## Reproduce

From the repository root with its installed dependencies:

```sh
ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config docs/audits/closed-editor-model-lifetime/vitest.config.mjs
node docs/audits/closed-editor-model-lifetime/loader-controls.cjs
```

The default is `ORCA_CLOSED_MODEL_GRAPH=worktree` and `ORCA_CLOSED_MODEL_VARIANT=fixed`. Repeat with each graph (`worktree`, `main`) and variant (`before`, `fixed`). Set these environment variables using the syntax of your shell; the runner and path handling are platform-neutral. Set `ORCA_CLOSED_MODEL_OUTPUT` or `ORCA_CLOSED_MODEL_LOADER_OUTPUT` to an output path to avoid replacing checked-in reports.

For Electron, run the installed Electron binary with `ELECTRON_RUN_AS_NODE=1`, `ORCA_BACKGROUND_LAUNCH=1`, `--no-experimental-webstorage --expose-gc`, followed by `node_modules/vitest/vitest.mjs run --config docs/audits/closed-editor-model-lifetime/vitest.config.mjs` and the same graph/variant environment. This uses Electron's Node runtime without creating a window.

---

### Audits Codex Prompt Claim Retention README (`docs/audits/codex-prompt-claim-retention/README.md`)

# Codex prompt claims retained after turn completion

Confirmed cancellation keeps a prompt claim until its turn completes. If the prompt's lookup entries are evicted or replaced first, the old `clearTurn()` cannot find it. The separate claims map then retains the prompt until the whole session is cleared.

The fix includes claimed prompts in the existing exact-thread/turn cleanup. Existing turn matching and `forget()` object-identity checks preserve a replacement prompt's authority. Registry limits and cancellation timing are unchanged.

## Source ownership and reachability

1. `codex-structured-provider-events.ts:57` registers incoming prompt requests and publishes them through the translator. `codex-structured-session-acquire.ts:95` binds the translator's turn cleanup to the session registry.
2. `codex-structured-prompt-ownership.ts:33` acquires the claim. Confirmed cancellation deliberately leaves it owned; unsuccessful/unconfirmed cancellation releases it. The actual `CodexStructuredTurnCancellation` invokes the confirmation callback after the injected interrupt transport acknowledges success.
3. `codex-prompt-registry.ts:258` trims the address and journal-binding maps independently. Neither trim removes claims. Replacing the same journal address can similarly leave the old claim without a lookup entry.
4. A later `turn/completed` goes through `translateCodexNotification`, the journal translator and `settleCodexJournalTurn`. Accepted lifecycle settlement invokes `clearPromptTurn` at `codex-structured-journal-settlement.ts:170`.
5. The old cleanup enumerates only address/binding values. The fix also enumerates `claims.keys()`, still filtering by the exact thread/turn. `forget()` deletes replacement lookup entries only when they contain that same prompt object.

The safely expired owner is the claim for the terminal turn whose cleanup has been admitted. Live claims survive unrelated turn cleanup and registry eviction. A refused lifecycle settlement does not clear them.

## Reproduce

From the worktree root, using installed dependencies:

```sh
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/codex-prompt-claim-retention/reproduce.cjs
```

For Electron, use its installed executable with `ELECTRON_RUN_AS_NODE=1`, the same flags and script. The final optional argument selects the report path; the default is `node-results.json` beside the script. No Electron window is created.

`sources.cjs` reverses `fix.patch` against current source and rejects a baseline hash mismatch. It neither reads a previous commit to reconstruct the implementation nor changes product files. The proof bundles actual source in memory. Each report records effective source and bundle hashes, dependency hashes and runtime versions. Only the requested report is written.

The fixture uses the actual registry, server-request delivery, cancellation ownership function, cancellation class, journal translator and delayed notification delivery. It injects an accepting journal sink, interrupt transport and primary-turn lookup. Prompts belong to child threads, so the production child-turn cancellation branch does not enumerate or terminate processes. Every injected process helper throws if unexpectedly called.

The sequence creates 32 ordinary small prompt objects, confirms their cancellations, admits 256 unrelated prompts to evict lookup entries, then completes the original exact turns. WeakRefs count prompt liveness after forced collections. No large payload is attached. The 20-second deadline and 192 MiB heap limit bound the proof.

## Results

Both [Node 26.6.0](./node-results.json) and [Electron 43.7.0 / Node 24.21.0](./electron-results.json) produced:

| Observation                                                                | Before | After |
| -------------------------------------------------------------------------- | -----: | ----: |
| Retained cancelled prompts after lookup eviction, before completion        |     32 |    32 |
| Retained after exact turn completion                                       |     32 |     0 |
| Retained after all lookup maps become empty                                |     32 |     0 |
| Retained after session clear                                               |      0 |     0 |
| Old prompt retained after same-address replacement and old-turn completion |      1 |     0 |

Ordinary completion releases its prompt on both versions. Wrong-thread, wrong-turn and refused-completion controls preserve claims. The replacement prompt and its active claim remain valid after old-turn cleanup on both versions.

The four regression tests cover 32 evicted claims, replacement authority, a compatibility turn digest and session cleanup. Applying the reversed source produces three expected failures; the session-clear control passes. Existing prompt ownership/reply tests also pass on the reversed source. Current source passes 71 tests across six files plus Node, CLI and Web typechecks; [validation.json](./validation.json) records commands and other checks.

## Limits

This is a code-level lifetime defect. The request/completion ordering is deliberately injected; this is not a capture of Codex emitting that sequence or an affected host. Counts do not measure ordinary prompt bytes or establish a growth rate. It does not identify the cause of #19831 or any other incident.

[source-versions.json](./source-versions.json) records matching baseline source at the named main revision. No historical application runtime was reproduced. The fix uses existing turn ownership and identity checks; it adds no arbitrary eviction policy.

---

### Audits Crashpad Read Limit README (`docs/audits/crashpad-read-limit/README.md`)

# Crashpad bounded reads

After a crash, Orca extracts a short diagnostic signature from a potentially large dump.
The old reader loaded the complete dump. The current parser reads bounded ranges through
four retained 64 KiB pages and scans embedded diagnostic text in roughly 1 MiB windows.
It can capture a report that grows beyond the existing 64 MiB directory-discovery limit
without keeping that report in one large buffer. Reports already beyond that limit when
discovered retain the existing exclusion policy.

## Reproduction

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/crashpad-read-limit/reproduce.mjs
```

Use the repository's installed dependencies and supported Node version. The runner checks
committed SHA-256 values in `source-hashes.json` before executing any tests. This includes
the capture/parser, test fixtures, runner and its local imports, plus package and lockfile
versions. It refuses changed sources instead of silently recording new hashes. Updating
the manifest requires reviewing the changed evidence and rerunning the proof.

The runner executes the actual capture and file-source tests with synthetic files, then
repeats them with only the zero-size extent deadline disabled through an in-memory Vite
transform. The mutation changes no files. This is a regression control for the deadline,
not a simulation of every behavior in the original implementation.

| Phase                  | Pass | Fail | Skip | Exit |
| ---------------------- | ---: | ---: | ---: | ---: |
| Deadline removed       |   24 |    2 |    0 |    1 |
| Current implementation |   26 |    0 |    0 |    0 |

The two expected failures verify that a file reported as size zero stops being read at
the deadline, after either one or two pages, even though more bytes remain available.
The controls fail both on the observed extent and the number of actual file reads.
The runner checks their exact names and exits unsuccessfully for unexpected results.
`results.json` records the verified hashes, counts, failed cases, exit codes and timeouts.

Other controls cover 80 MiB sparse dumps, metadata beyond 64 MiB, marker/check-message
boundaries, growth and replacement during capture, zero-size growth, truncation, descriptor
cleanup and partial-header retry behavior. A symlink swap, deleted candidate, or directory replacement now skips only that candidate; another valid dump can still supply the signature. The symlink-swap test is skipped on platforms without `O_NOFOLLOW`. The largest requested parser read is 1 MiB +
4,096 bytes, plus four retained 64 KiB metadata pages. A 1,042-module fixture requires no
more than eight reads, preventing repeated reads between module and name pages.

## Parser compatibility

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/crashpad-read-limit/stream-signature-parity.cjs
```

The separate parity script compares 47 fixtures with the published parser at
`09dbe227547fadaec8d9163f35fd127b0dc1c3ed`, using both in-memory buffers and real file handles.
It covers annotations, modules, exceptions, chunk boundaries and marker exhaustion. It
also prints five warm timings for 8/64 MiB sparse dumps; these local synthetic timings
are not a platform-wide performance guarantee. This older parity script does not enforce
the source manifest; use the manifest-checking regression runner first.

## Limits and user impact

This removes a potentially large transient allocation after a crash; it does not establish
the cause of normal-session OOMs or an aggregate process-memory cap. Independent captures
can overlap. No evidence ties this file-growth race to #19831 or #19768.

The opened file supplies the parsed ranges. In-place rewrites are not an atomic snapshot.
Nonempty files retain their opened extent. A file opened at size zero receives at least one
read, then observes growth only until the capture deadline; later bytes may therefore be
omitted from that diagnostic signature. This keeps a continuously growing report from
holding capture open indefinitely. Dump files themselves are not truncated by this reader.

Crashpad normally writes an invalid header first and promotes a completed file later:
[database implementation](https://chromium.googlesource.com/crashpad/crashpad/+/refs/heads/main/client/crash_report_database_mac.mm)
and [minidump writer](https://chromium.googlesource.com/crashpad/crashpad/+/HEAD/minidump/minidump_file_writer.cc).
The fixtures represent that ordering; they do not reproduce a field incident or a native
Crashpad process. All checks run in background Node processes without app windows.

---

### Audits Daemon Late Exit README (`docs/audits/daemon-late-exit/README.md`)

# Delayed daemon output after a synthetic exit

A daemon stop response can arrive on its control socket before its final DATA and
EXIT events arrive on the separate stream socket. Main then emits a synthetic exit.
The delayed DATA recreates a headless model and marks the runtime PTY connected;
the old duplicate-exit check suppresses the physical EXIT before runtime cleanup.
The host has no live session, but main retains the connected record, title tracker
and headless terminal.

## Reproduce

From the checkout, using its installed dependencies:

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/daemon-late-exit/reproduce.mjs /tmp/daemon-late-exit-results.json
ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/ipc/pty/daemon-late-exit.test.ts
```

The script uses the real daemon server, client, provider, socket pair, kill IPC
handler, listener binding, and runtime. The native subprocess boundary is a fixture
whose force-kill callback reports exit. Pausing only the stream reader makes the
independent control/stream ordering deterministic. No renderer or visible app is
launched. Temporary Vitest files are removed afterward.

The before case moves duplicate suppression back ahead of runtime cleanup in the
loaded module only. It retains the current incarnation-aware marker representation,
which does not affect the same-incarnation race. The on-disk source stays unchanged.
The script verifies its transform boundary and records the current source hash.

## Results

[results.json](./results.json) contains four before/after controls:

| Scenario                                                 | Before                                                 | After                                  |
| -------------------------------------------------------- | ------------------------------------------------------ | -------------------------------------- |
| Kill reply overtakes queued DATA and EXIT                | Connected; headless model and title tracker retained   | Disconnected; both released            |
| Host inventory verifies exit before queued DATA and EXIT | Connected despite an `exited` verdict; models retained | Disconnected; models released          |
| Kill reply overtakes EXIT with no queued DATA            | Disconnected; cause remains `stop_unverified`          | Disconnected; confirmed requested stop |
| Natural DATA then EXIT                                   | Disconnected; models released                          | Same                                   |

All eight runs receive one physical provider exit, deliver all final output to the
renderer admission boundary, send one renderer exit, and call the runtime exit
listener once. Fresh daemon inventory is empty in all cases. Additional regression
tests cover same-ID replacement, stale provider callbacks, legacy unstamped exits,
and one-time dispatch settlement with the real SQLite orchestration database.

The fix always processes the current incarnation's provider exit in main. Duplicate
suppression applies only to the renderer notification. When the provider supplies an
incarnation, its marker names that stopped incarnation and cannot suppress a
differently stamped replacement's exit. Legacy unstamped events retain their
existing matching behavior. A matching marker restores the original stop intent
while normal exit-cause resolution still handles negative,
unconfirmed exits. No output is dropped and no wire fields or opcodes change.

## Report correlation and limits

The early-return listener and synthetic renderer-kill exit are present in both
`v1.4.197` (#19018) and `v1.4.192` (#17344). The reproduced `connected: true` plus
`stop_unverified` state matches #19018's reported contradiction and provides a
concrete main-process retaining path relevant to #19831. This does not prove which
ordering occurred in either user's session, explain #19018's failed subsequent
inventory/close reconciliation, or by itself prove persisted tab resurrection in
#17344. A missing `diagnostics.memory` row is not process-exit evidence; this proof
uses the owning daemon's physical exit and fresh session inventory.

A second runtime cleanup may advance an already-retired surface's topology revision
once more. It does not republish a removed surface. Existing exit listeners and
waiters remove themselves on settlement; completed dispatches are no longer active.
The existing marker timeout remains 30 seconds. The separate asynchronous shutdown
call's ownership across its await is outside this change.

---

### Audits Daemon Shared Owner Incarnation Retention README (`docs/audits/daemon-shared-owner-incarnation-retention/README.md`)

# Shared daemon owner incarnation retention

A degraded daemon provider creates two owner resolvers with one shared route map. On an authenticated daemon identity change, the attach resolver removes that daemon’s routes first. The liveness resolver then sees no corresponding routes and previously left its private session-to-incarnation entries behind. Repeating replacements with newly discovered session IDs grows that private map for the lifetime of the degraded provider.

The fix removes private incarnation entries whose shared route is absent after provider invalidation. It preserves every remaining route, including another provider’s live session and a same-ID successor. It does not change process liveness, stop remote work, change the wire protocol, or depend on a git workspace.

## Actual ownership and trigger

- `src/main/daemon/daemon-provider-init.ts:123` selects `DegradedDaemonPtyProvider` for `degraded-new-pty-fallback`; startup discovery runs at line 139.
- `src/main/daemon/degraded-daemon-owner-recovery.ts:15` constructs both resolvers with the same map; public discovery and liveness probes populate their private indexes. Startup reconciliation can also record both routes.
- `src/main/daemon/degraded-daemon-owner-recovery.ts:70` subscribes to each daemon’s identity publication and invalidates the attach resolver before the liveness resolver.
- `src/main/daemon/daemon-pty-connection-lifecycle.ts:41` publishes only after a different authenticated identity replaces a previous identity. Repeated observation of the same identity does not retire anything.
- `src/main/daemon/daemon-pty-daemon-recovery.ts:268` can replace the daemon while retaining its adapter and the degraded provider.
- `src/main/daemon/daemon-session-owner-resolution.ts:44` performs the invalidation and the new private-metadata prune.

This is a local desktop main-process degraded-provider path. Loss of SSH contact is not its retirement trigger. Entry counts below do not establish retained bytes, RSS, an OOM, or causation for #19831.

## Bounded actual-source proof

The fixture uses the actual degraded provider, recovery controller, resolvers, daemon adapter inventory, authenticated identity publication, and direct attach implementation. Only finite authenticated transport replies and the empty fallback provider are inert; it starts no native PTY, socket, network connection, or application window. It does not depend on garbage-collection timing or a never-settling promise.

Each of 32 cycles discovers a new current-daemon session, populates both resolvers through public calls, observes an unchanged identity, then publishes a replacement identity. An unrelated legacy-daemon session remains live throughout. Finally, an ordinary legacy exit removes its route from both resolvers.

| After 32 replacements and the legacy exit | Baseline | Fixed |
| ----------------------------------------- | -------: | ----: |
| Shared routes                             |        0 |     0 |
| Attach resolver incarnation entries       |        0 |     0 |
| Liveness resolver incarnation entries     |       32 |     0 |

Additional assertions preserve a same-ID successor on another provider, an unchanged authenticated identity, direct attach with a matching authoritative incarnation without inventory, refusal of a mismatched authoritative incarnation, and ordinary exit cleanup. The permanent tests include the repetition regression and three compatibility controls; the portable fixture additionally exercises the actual adapter attach transport path.

## Reproduce

Run from the repository root with its dependencies installed:

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/daemon-shared-owner-incarnation-retention/reproduce.cjs
ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/main/daemon/daemon-shared-owner-incarnation-retention.test.ts src/main/daemon/daemon-session-owner-resolution.test.ts src/main/daemon/degraded-daemon-pty-provider.test.ts
```

The runner accepts an optional output filename as its first argument. On macOS, the Electron runtime control is:

```sh
ORCA_BACKGROUND_LAUNCH=1 ELECTRON_RUN_AS_NODE=1 node_modules/electron/dist/Electron.app/Contents/MacOS/Electron docs/audits/daemon-shared-owner-incarnation-retention/reproduce.cjs
```

On Linux or Windows, use the corresponding installed Electron binary with the same environment variables. It runs as Node and never displays a window.

The baseline test overlay reverses only the fenced product patch in memory:

```sh
ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config docs/audits/daemon-shared-owner-incarnation-retention/before.config.mjs src/main/daemon/daemon-shared-owner-incarnation-retention.test.ts src/main/daemon/daemon-session-owner-resolution.test.ts src/main/daemon/degraded-daemon-pty-provider.test.ts
```

Expected: exactly the new repeated-retirement assertion fails before the fix; the other 53 tests pass. All 54 pass with the fix.

## Source identities and publication independence

`sources.cjs` checks the exact fixed source hash, reverses `fix.patch`, checks the baseline hash, and fences every evaluated TypeScript dependency. It records actual evaluated and input hashes and a bundle hash in each report. A CRLF control checks source and patch normalization. The default runner needs neither Git history nor ignored audit notes.

`source-versions.json` records the audited source graph (276 modules) and the independent main graph at `291b4ddd6f1c1af480169885e0fda7f9c78ff053` (274 modules). Both graphs are accepted explicitly; ten surrounding modules differ because of unrelated audit fixes. The proof therefore does not require those fixes to be stacked. The publication reports were produced through the exported `run({ readSource, output, sourceLabel })` API, reading each non-target source from that named main revision and applying only this product change. The default command also runs directly on that publication tree with the fix and artifact installed.

Node 26.6.0 and Electron 43.7.0 / Node 24.21.0 both produced the table above against both source graphs. All four executions used the working installation’s external packages. These are source overlays, not historical application or dependency installations.

At reported v1.4.198 (`e0826956fcfc532f5a1e55b5e081f2e57e553c43`), the resolver, shared recovery controller, and authenticated identity publication match the recorded baseline exactly. The surrounding degraded provider differs, as recorded in `historicalCore`; no whole-v1.4.198 execution or incident attribution is claimed.

`validation.json` records tests, typecheck, full-file artifact quality, and limits. The four result files contain measured entry counts and exact source/artifact identities.

---

### Audits Daemon Stream Retention README (`docs/audits/daemon-stream-retention/README.md`)

# Daemon stream retention reproduction

Run from the worktree root after installing dependencies:

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/daemon-stream-retention/reproduce.mjs
```

The script bundles the current `DaemonStreamDataBatcher` with esbuild, then uses real loopback TCP sockets. It pauses the reader, feeds at most 8 MiB, honors the producer-pause callback, resumes the reader, and closes every socket in `finally`. It starts no PTY, agent, or application window. Output includes the bundle hash, queued payload sizes, RSS, and drain outcome.

Visible 64 KiB and 1 KiB producers should pause within the backlog budget and resume after both queues drain. A hidden 1 KiB producer should keep running while the existing keep-tail policy bounds its held output. RSS includes allocator/GC timing; queue measurements are the direct evidence.

`after.json` records a historical fixed run under Node v26.6.0: the visible bulk producer paused after 2,686,976 characters with 131,300 socket bytes and 2,031,616 held characters; the visible small-write producer paused with 3,401,200 socket bytes. The hidden producer processed all 8 MiB with 131,300 socket bytes and 667,648 held characters. Every case resumed and drained both queues to zero.

## Preserved measurements before the fix

`before-1mib-chunks.json` and `before-64kib-chunks.json` were captured before adding producer backpressure, with a real paused reader and 96 MiB of input. The latter includes the original compiled bundle hash and a hidden-session comparison.

| Input  | Socket buffered bytes | Held characters |
| ------ | --------------------: | --------------: |
| 32 MiB |               131,321 |      32,636,928 |
| 64 MiB |            32,809,583 |      33,554,432 |
| 96 MiB |            66,406,511 |      33,554,432 |

The last 32 MiB retained another 33,596,928 queued payload bytes. The held queue plateaued at 32 MiB while the socket queue grew at 1.00127 bytes per produced ASCII byte. Resuming the reader drained both queues in 74 ms. Sampled RSS reached 1.38 GB with 64 KiB chunks; this includes string coalescing/slicing and GC effects.

The original measurement used Node v26.6.0 on macOS; the current reproduction records its runtime and should be run on the supported Node 24 toolchain. The historical build command was:

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/daemon-stream-retention/reproduce.mjs
```

This proves a current stalled-consumer retaining path. It does not attribute issue #19831's whole-system memory total to this path; that report lacks per-process measurements.

---

### Audits Empty Streamed Delta Retention README (`docs/audits/empty-streamed-delta-retention/README.md`)

# Empty streamed deltas retain array entries

The text coalescer charged streamed text by UTF-8 bytes but appended an array entry for every empty delta. A live stream receiving repeated empty updates could retain an increasing number of entries while both byte counters stayed zero. Flushing published a joined string and kept the entries. The actual Codex notification path accepts `delta: ''`; this diagnostic exercises its stream handler and coalescer.

The fix skips only the empty `chunks.push` operation. Empty-stream creation, snapshots, dirty state, scheduled publication, callback receiver, backpressure and eviction remain unchanged.

## Reproduce

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/empty-streamed-delta-retention/reproduce.cjs
```

The runner reverses hash-checked patches in memory and checks every bundled source dependency. It changes no product files and starts no native process or UI. A bounded CRLF control checks source and patch loading. Reports were recorded on Node 26.6.0 and Electron 43.7.0's Node 24.21.0.

| Control                                                              | Before                         | Fixed                     |
| -------------------------------------------------------------------- | ------------------------------ | ------------------------- |
| Four batches of 16,384 empty Codex deltas, flushing each batch       | 16,384 → 65,536 retained slots | 0 slots after every batch |
| Logical stream count                                                 | 1                              | 1                         |
| Accounted / observed text bytes                                      | 0 / 0                          | 0 / 0                     |
| Scheduled callbacks / published rows in the complete caller scenario | 6 / 5                          | 6 / 5                     |
| Append `hé` after empty updates                                      | Same 3-byte text               | Same 3-byte text          |
| Forget and disposal                                                  | Clear retained state           | Clear retained state      |

The runner compares the entire recorded publication and scheduling behavior before/after. Controls also cover first-empty snapshots, failed publication and retry, rejection of a new empty key while the previous stream is backpressured, accepted eviction, callback receiver, UTF-8 truncation and an empty update after truncation. The two runtimes each execute four source phases: current/main before and fixed, plus the v1.4.198 coalescer before and with the same narrow guard.

The permanent regression invokes the actual Codex stream caller. A temporary `Array.prototype.join` observer measures the matching chunk array only during synchronous snapshot creation, then restores the method. The baseline fails with 65,537 slots versus the expected single nonempty prefix; the other 14 coalescer controls pass. All 64 focused compatibility tests pass with the fix. See [validation.json](./validation.json).

## Source and incident scope

The current baseline is byte-identical to the coalescer at named main commit `291b4ddd6f1c1af480169885e0fda7f9c78ff053`. The exact v1.4.198 coalescer contains the same unconditional empty append; its surrounding implementation differs. Historical phases replace only that module and use the recorded current Codex caller/dependencies. This is a source overlay, not a packaged historical application replay. [source-versions.json](./source-versions.json) records these distinctions and named caller hashes.

Claude's generic checkpoint API also uses the coalescer, but its ordinary provider path rejects empty text in `claude-streamed-block-identity.ts` before calling it. This artifact demonstrates the Codex path and preserves Claude compatibility; it does not claim an ordinary Claude trigger.

Measurement instrumentation reads private map and array cardinalities without changing their contents. These are retained-entry counts, not heap, RSS or byte measurements. The fixture keeps the live stream owned until forget/disposal; it does not establish retention after all owners collect. Nonempty one-byte deltas can still have substantial array overhead within the text-byte allowance, and overflow concatenation has its own transient cost.

No affected-host data establishes how often Codex emitted empty updates in #19831 or another incident. The finding is a reproducible code-level growth mechanism present in the reported release. It does not attribute an app-scope OOM total to this mechanism or establish its incident magnitude. No remote protocol, process liveness, process termination or terminal ownership behavior changes.

---

### Audits Gitlab Known Host Retirement README (`docs/audits/gitlab-known-host-retirement/README.md`)

# Retire obsolete GitLab known-host generations

Each successful `getGlabKnownHosts` probe previously stored a host-list array under a connection ID plus its SSH provider generation. Reconnecting under the same ID created a new entry while every earlier successful generation remained cached until an explicit preflight reset. The cache now keeps one successful generation per observed execution identity.

Async publication also uses the existing coalescer's `ownsKey()` and checks the current SSH generation. A result completing after reconnect, explicit reset, or replacement by a newer probe cannot recreate retired cache state. Original callers can still receive their own completed result. Explicitly remembered hosts, native/WSL separation, command routing and existing probe timeouts are preserved.

## Evidence

The runner bundles the actual cache, coalescer and parser. Only command-result and SSH-generation ports are controlled; it opens no SSH connection and runs no GitLab command. It reverses `fix.patch` in memory, verifies the original source hash, and compares that baseline against the unmodified current product source. Reports include product, dependency, regression-test and fixture hashes.

| Control | Original | Fixed |
| --- | --- | --- |
| Successful result arrays retained after 128 generations | 128 | 1 current array |
| Remembered result arrays retained after 16 generations | 16 | 1 current array |
| Delayed old-generation result after a successor answers | Still retained | Collectable; successor preserved |
| Explicit reset followed by old completion | Old result repopulates cache | Next lookup executes a fresh probe |
| Abandoned probe finishes after its replacement | Old host added to replacement cache | Replacement remains unchanged |
| Explicit reset after retention exercise | 0 original arrays retained | 0 original arrays retained |

Both phases preserve remembered-host updates while probes succeed or fail and isolate native, Ubuntu WSL, Debian WSL and two connection IDs. `results.json` records Node26.6; `electron-results.json` records installed Electron43.7 / Node24.21 running without an app window. Both runs pass all controls. This is compatibility evidence, not a historical packaged-binary reproduction.

```sh
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc docs/audits/gitlab-known-host-retirement/reproduce.cjs
ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/gitlab/gitlab-known-host-retirement.test.ts src/main/gitlab/gitlab-known-host-probe.test.ts src/main/gitlab/gitlab-known-host-probe-wsl-fallback.test.ts src/main/git/coalesced-probe.test.ts src/main/gitlab/client-mr-auth-rate-limit.test.ts
```

For the installed macOS Electron binary:

```sh
ELECTRON_RUN_AS_NODE=1 ORCA_BACKGROUND_LAUNCH=1 node_modules/electron/dist/Electron.app/Contents/MacOS/Electron --expose-gc docs/audits/gitlab-known-host-retirement/reproduce.cjs docs/audits/gitlab-known-host-retirement/electron-results.json
```

The runner is portable; that executable path is macOS-specific. Thirty-two focused tests pass, including eight new retention/lifecycle/scope controls. Running those eight against the original source produces six failures and two passing controls. Node typecheck, focused lint (including artifact type-aware/casting scans), and the changed-code quality gate pass. The original product module and reused coalescer match named main `291b4ddd6f1c1af480169885e0fda7f9c78ff053`.

## Limits and incident mapping

This removes small metadata retained across SSH generations. Distinct historical execution identities may still keep one entry each until reset; this change does not impose a new cache cap or alter connection/provider lifetime. One generation's host list remains input-sized.

The demonstrated accumulation requires changing SSH provider generations, so it cannot explain [#19831](https://github.com/stablyai/orca/issues/19831)'s reported all-local session. No affected-host observation ties it to another OOM report. The proof measures reachable result arrays, not RSS or gigabytes of incident memory.

---

### Audits Headless Hydration Retention README (`docs/audits/headless-hydration-retention/README.md`)

# Late headless snapshot ownership

## Reproduced defect

The runtime can retire a PTY or replace its headless model while a renderer/provider snapshot or emulator seed write is pending. The old completion still updates maps keyed only by PTY ID. After exit cleanup, a successful renderer reply recreates CWD, recent-output and title state; even an empty or rejected reply recreates the hydration `done` entry. A replaced model can also lose its provider preference or pending hydration status to the old completion.

Provider snapshot validation has two related races. Its late generation check calls an allocating getter, recreating an entry that exit just deleted. Its cleanup can delete a newer capture's live-mode scanner Set after the old Set becomes empty. Provider tail parsing has the same allocating late check after an awaited parse/write.

These paths exist in `v1.4.198`. They explain a concrete main/runtime retention mechanism under PTY churn, but the frequency and retained size in #19831/#19768 remain unproven. The renderer request already has a 750 ms timeout (`src/main/ipc/pty/ipc/serialize-buffer.ts`); this fix addresses callbacks writing after their owner retires, not an indefinite renderer wait.

## Fix and ownership

The three model-seeding paths compare `headlessTerminals.get(ptyId)` with the captured state before starting work and after asynchronous boundaries. Completion bookkeeping runs only for that state. Provider capture/tail completion compares the existing generation without allocating, and capture cleanup removes the map entry only while it still owns the same Set.

Admission generation allocation and normal queued live writes keep their existing behavior. Disposal still drains live writes queued before retirement. Current hydration, query replay suppression, CWD/kitty metadata and replacement capture mode tracking remain covered. These are local runtime ownership checks for both local and SSH-backed terminals; they add no process-death inference, remote cancellation or wire change. They do not depend on git worktrees.

## Reproduce

The original before/after reproduction belongs to commit `9ed2f743a4dd7aab1d35906c049c11d143a92aaf`. Its patch targets the runtime layout at that commit; use that checkout to reproduce the historical result:

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/headless-hydration-retention/reproduce.mjs
```

The script runs the actual `OrcaRuntimeService` regression fixtures twice. For the before case, it reverses only the included four-file `fix.patch` in a temporary Vite transform. It neither rewrites source files nor needs an unpublished commit. The after case uses the checked-out source. Source hashes and individual failing cases are recorded in `results.json`.

- Before: **16 failed, 6 passed**.
- After: **22 passed**.

The tests control pending promises to cover retirement before callback admission, during renderer/provider replies, during seed writes and during kitty metadata application. They cover success, null, rejection, same-ID replacement, current-state success, generation preservation and live-mode scanner ownership. Provider-tail checks exercise both normal and visible-screen-only parsing.

Current ownership regression coverage lives together in `src/main/runtime/headless-hydration-ownership.test.ts`; all 22 original cases remain. Current validation:

```sh
ORCA_BACKGROUND_LAUNCH=1 node node_modules/typescript/bin/tsc --noEmit -p config/tsconfig.node.json
ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/runtime/orca-runtime.test.ts src/main/runtime/terminal-query-responder.test.ts src/main/runtime/headless-hydration-ownership.test.ts --testNamePattern 'headless|hydrat|WSL|provider cwd|live WSL cwd|query|seed|retire|replacement|capture|renderer'
```

Historical validation at #20996: Node typecheck passed. The selected existing runtime/query checks plus the new cases passed **306 tests**, with 1087 unrelated cases skipped by the name filter. All runs were headless and used `ORCA_BACKGROUND_LAUNCH=1`.

---

### Audits Legacy Import Source Budget README (`docs/audits/legacy-import-source-budget/README.md`)

# Legacy import source byte budget

The production importer accepted a transcript larger than its existing 16 MiB
quota when the file grew between `stat()` and reading it. The fix applies that
same quota to consumed raw bytes and rejects the entire import before replacing
the journal. It does not import a truncated prefix.

## Reproduce

From the repository root, with dependencies installed:

```sh
ORCA_BACKGROUND_LAUNCH=1 node --max-old-space-size=256 docs/audits/legacy-import-source-budget/reproduce.mjs
```

The script bundles the actual production importer and decoder twice. The baseline
restores the importer's UTF-8 stream and omits the new consumed-byte quota in
memory; the second run uses the current source. All other dependencies are
identical. Explicit file paths bypass discovery; a stub fails if discovery is
unexpectedly called. No historical checkout or local-only commit is required.

Each run creates one real temporary file containing a valid Claude message.
The controlled `stat` seam captures its original size, appends spaces until the
file is exactly 17 MiB, then returns the original stat. The production importer
reads the grown file. A journal spy records replacement attempts and its epoch;
no SQLite database or user transcript is touched. Temporary files are removed.

`results.json` records source hashes, actual consumed bytes, stream closure,
and replacement attempts. Before the fix the importer consumes all 17 MiB,
returns success, and replaces the journal with the decoded prefix. Afterward it
refuses on the first chunk crossing 16 MiB, destroys the stream, and leaves the
prior epoch untouched. Read buffering permits one chunk of overshoot.

## Process and version attribution

This importer runs in the host runtime through structured-session adoption,
handoff, transcript catch-up, and journal recovery. On desktop that runtime is
Electron main; a remote runtime imports its own host-local source. The stat-only
quota and unrestricted stream already existed in `v1.4.198`, release commit
`e0826956fcfc532f5a1e55b5e081f2e57e553c43`.

The reproduction establishes a quota bypass, not the cause of #19768 or #19831.
It requires an import and source growth/replacement after the size check;
neither incident provides that evidence. This fix is independent of #20963's
resumable JSONL record limit.

## Other whole-document audit results

- `ai-vault/session-transcript-reader.ts:152` routes rewritten JSON documents
  through whole-file `readFile` and `JSON.parse`. Gemini JSON, Hermes, Devin,
  Cline, Grok metadata, Kimi state, Rovo, and OpenCode file readers lack a source
  byte quota. These are allocation peaks; the parse cache retains summaries,
  not the full documents.
- The production default, including v1.4.198, places those scans in the forked
  AI Vault service with a 384 MiB V8 old-space limit. That is not a total RSS
  limit. `ORCA_AI_VAULT_SERVICE_PROCESS=0` instead puts scans in a worker thread
  sharing main's PID; its first-prompt fallback runs directly in main.
- Existing remote whole-document streaming parsers offer a reuse path, but
  local conversion must preserve message-sink delivery for search indexing.
  Streaming also needs an explicit policy for an individually oversized JSON
  value; arbitrary history truncation was not introduced.
- The older `native-chat/transcript-reader.ts` and `transcript-read-cache.ts`
  have whole-history behavior, including a newest-entry cache exemption, but
  the current production source has no external call sites for those exports.

Validation: 50 targeted tests passed, including a regression that failed before
the fix, UTF-8 and raw-byte limits, exact-boundary acceptance, and stream cleanup.
Node typecheck, targeted lint, formatting, and diff checks passed.

---

### Audits Local Log Tail Lifetime README (`docs/audits/local-log-tail-lifetime/README.md`)

# Local log-tail watchers outliving their renderer

Main can receive a log-tail subscription, await path authorization, and finish installing its native watcher after the requesting renderer has gone away. Previously, the destroyed listener was registered only after authorization. Installed watchers also survived a renderer crash or a new document loaded into the same WebContents. The map retained each watcher and its sender callback; callbacks suppressed notifications to destroyed senders without releasing resources. This handler is present in `v1.4.198`.

The fix gives each sender one owner using the existing `abortWhenRendererGone` policy: destruction, renderer process loss, or committed document navigation closes its live watches and invalidates pending authorization. Same-document and canceled navigation preserve the owner. For a reused subscription ID, the latest pending request wins. Each pending subscription has an identity token; old completions and old watcher errors cannot replace or close newer subscriptions. Failed authorization preserves an existing installed watch. The last pending/live release removes all owner listeners.

This is a reproduced native-handle and small metadata leak. Watchers do not retain file-content chunks. It does not establish the input frequency or memory scale in [#19768](https://github.com/stablyai/orca/issues/19768) or [#19831](https://github.com/stablyai/orca/issues/19831).

## Reproduce

From the repository root with existing dependencies:

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/local-log-tail-lifetime/reproduce.mjs
```

The script runs the actual IPC handlers against temporary files, real `fs.watch` handles, controlled authorization promises, and EventEmitter senders. The existing IPC tests use watcher doubles to deliver an error from a retired watcher. No Electron window, real user log, process inventory, or network request is used. Test cleanup releases all watchers.

The baseline reverses only `fix.patch` in a temporary Vite transform. Working sources remain unchanged; source hashes and exact failed cases are recorded in `results.json`. Child test runners use the shared cross-platform process runner.

| Version             | Passed | Failed |
| ------------------- | -----: | -----: |
| Before lifetime fix |      9 |     10 |
| With lifetime fix   |     19 |      0 |

The twenty-owner case retained twenty native watcher owners before the fix and zero afterward. The broader cases cover destruction during authorization, active-plus-pending replacement, process loss/navigation, failed replacement, explicit stop, idle listener disposal, superseded success/error, and failed native installation. Ordinary tab cancellation already waited for start before stop; that behavior remains covered by the renderer hook tests.

Additional validation: Node typecheck, direct lint, and the existing renderer-lifetime and local-log-tail hook suites. This endpoint only watches renderer-authorized local logs. SSH/paired-runtime execution ownership and wire schemas do not change; the local editor eligibility check already excludes runtime-environment files. Folder workspaces follow the existing path authorization policy.

---

### Audits Module Organization README (`docs/audits/module-organization/README.md`)

# Orca file and folder organization assessment

**Yes: clean up the busiest directories in small, domain-focused steps.** The repeated prefixes often describe folders that should exist, or existing folders whose organization is incomplete. Keep kebab-case for ordinary filenames. The problem is how many unrelated responsibilities sit beside each other and how hard it is to find their owners.

This is a navigation and maintainability investment. Moving files alone does not make the application faster or establish better architecture. It becomes valuable when a contributor can find a feature, understand its entry points, and make a change without searching several enormous directories.

Start with the seven Linear IPC files and ten Jira IPC files, then Jira attachments, Linear issue context, repository settings and ephemeral VM ownership. Follow the quieter platform and tooling work before the busier Task Page and filesystem folders. Schedule the shared contracts, terminal lifecycle and session runtime work with their owners; those areas have broader compatibility and integration obligations. The [execution task tracker](TASKS.md) defines the full sequence.

This assessment proposes moves; it applies none. The accompanying [codebase walkthrough](CODEBASE-MAP.md) explains the system, and [codebase-map.json](codebase-map.json) contains the measured directory inventory, dependency summaries and exact proposed paths. [audit-plan.json](audit-plan.json) holds the editable architectural labels and proposals.

The [execution task tracker](TASKS.md) now provides the authoritative PR sequence, starting with quieter areas based on recent development activity. It refines the original phase suggestions below into smaller batches and explicitly removes existing forwarding modules. [execution-tracker.json](execution-tracker.json) records file actions, checks and merge progress; no barrel exports or old-path shims are allowed.

## What the filenames are telling us

Your example is a sound instinct. A prefix can act as a folder name written repeatedly:

```text
Current: src/main/ipc/
  linear.ts
  linear-issue-handlers.ts
  linear-project-handlers.ts
  linear-team-handlers.ts
  linear-custom-view-handlers.ts
  linear-ipc-args.ts
  linear.test.ts

Proposed: src/main/ipc/linear/
  register-handlers.ts
  issue-handlers.ts
  project-handlers.ts
  team-handlers.ts
  custom-view-handlers.ts
  ipc-args.ts
  register-handlers.test.ts
```

The proposed registration file still exports the same registration function. The `linear:*` channel names stay the same. A source-path cleanup should not change the external contract.

However, the backend already has [`src/main/jira`](../../../src/main/jira) and [`src/main/linear`](../../../src/main/linear). There is no reason to combine everything mentioning Jira into one repository-wide folder. Jira UI, authenticated backend access, IPC handlers and pure contracts have different jobs and run in different environments. Group by domain **within the appropriate layer**:

```text
src/main/jira/                     backend client and domain operations
src/main/ipc/jira/                 desktop request handlers, proposed
src/main/runtime/rpc/methods/      remote request methods
src/shared/jira/                   pure contracts and rules, proposed
src/renderer/src/components/
  task-page/jira/                  existing provider UI
```

These are related locations, not necessarily duplicate implementations. The [feature trails](CODEBASE-MAP.md#follow-a-feature-through-the-codebase) connect them.

## Where the problem is substantial

The following counts are JS/TS files directly in a directory, including its tests and support files. They do not include children. A directory with many colocated tests is less concerning than the same count of production modules, so the JSON also separates those categories.

| Directory                                   | Direct files | Assessment                                                                                            |
| ------------------------------------------- | -----------: | ----------------------------------------------------------------------------------------------------- |
| `src/shared`                                |        2,905 | Largest discovery problem; partition contracts and pure rules by domain.                              |
| `src/renderer/src/lib`                      |        1,292 | Too many unrelated rules and adapters behind one role-based name; introduce concrete domain children. |
| `src/main/runtime`                          |        1,251 | Keep runtime composition visible; group owned session, workspace and transport responsibilities.      |
| `src/renderer/src/components/terminal-pane` |        1,070 | Already a feature folder; needs smaller responsibilities inside it, not another `terminal/` wrapper.  |
| `config/scripts`                            |          987 | Organize tool families by purpose; grouping everything named `check-*` would mix unrelated owners.    |
| `src/renderer/src/components/settings`      |          838 | Repository settings are a good first section to group.                                                |
| `src/renderer/src/components/native-chat`   |          813 | Review composer, journal presentation and interaction boundaries separately.                          |
| `src/renderer/src/components/sidebar`       |          807 | Review concrete sidebar responsibilities; keep host facts separate from view state.                   |
| `src/main/ipc`                              |          797 | Provider families and incomplete filesystem/PTY consolidation are clear candidates.                   |
| `src/renderer/src/components/editor`        |          776 | Extend the existing responsibility-based organization; do not move every editor file at once.         |
| `mobile/src/session`                        |          722 | Separate mobile chat, terminal and session-navigation responsibilities.                               |
| `src/renderer/src/components`               |          619 | Finish the existing Task Page subtree before inventing a new feature hierarchy.                       |

The complete directory inventory covers the whole tracked repository, including cloud, mobile, native code, tooling and documentation. The proposed work is a selected backlog, not an assertion that every other directory is already well organized.

## The strongest findings

### Complete the organization that is already there

Task Page already has `jira/`, `linear/`, `github/` and `gitlab/` children, yet many `task-page-*` and `use-task-page-*` files remain in the parent components directory. The [Task Page entry](../../../src/renderer/src/components/task-page/TaskPage.tsx) reaches out to those hooks through parent-relative imports. This is direct evidence of one feature spread across two discovery locations. Route provider-specific files into the existing provider children and keep cross-provider composition at the feature root.

Filesystem and PTY code have the same pattern: existing [`ipc/filesystem`](../../../src/main/ipc/filesystem) and [`ipc/pty`](../../../src/main/ipc/pty) trees coexist with flat `filesystem-*`, `parcel-watcher-*` and `pty-*` families. Consolidate by the responsibilities already established inside those trees. Filesystem watchers include a separately built process entry, so this is more involved than the provider IPC pilot.

[`src/shared/linear`](../../../src/shared/linear) already groups issue types, workspace types, links, media and agent-access contracts. There are no direct `linear-*` JS/TS files left in `src/shared` in this snapshot. Jira's equivalent flat family is a concrete place to follow that precedent.

### A feature folder can itself be too flat

`browser/`, `claude/`, `codex/`, `terminal-pane/` and mobile `session/` already have meaningful names. Wrapping each in another folder does not solve their problem. Useful next boundaries are responsibilities such as browser cookies, structured Claude sessions, the Codex app-server adapter and the mobile chat composer.

There is an existing local example in [`editor/combined-diff`](../../../src/renderer/src/components/editor/combined-diff): children include `browse-files`, `load-sections`, `resolve-changes`, `review-controls`, `scroll-viewport` and `remember-view`. Those names help someone find an operation. It is a useful organization model, though the folder layout alone does not prove that its implementation is otherwise ideal.

### Keep composition visible and scrutinize excessive fragmentation separately

The [root store](../../../src/renderer/src/store/index.ts) explicitly composes named Zustand slices, and [OrcaRuntime](../../../src/main/runtime/orca-runtime.ts) composes host responsibilities. These entry points should remain easy to find. Moving their related files into domain folders does not require hiding the composition behind `index.ts` re-export files.

The repository also enforces file-size limits: generally 300 counted lines for TS, 400 for TSX, 600 for MJS and 800 for tests, with existing exceptions tracked by a ratchet. That policy encourages splitting large files. It does not tell contributors where the resulting modules belong. Add folder guidance alongside it. The audit does not establish that this rule caused the current directory sizes.

The Task Page entry also chains many staged hooks. Folders will improve discoverability, but whether that composition has too many stages is a separate design review. Do not use a file-move PR to silently rewrite state ownership or behavior.

## Naming rules I would adopt

- Create a folder when files describe one feature or responsibility with an identifiable owner. Three or more production modules is a useful review prompt, not an automatic rule.
- Prefer completing an existing folder to creating a parallel one. Keep tests and their fixtures with the code they exercise, preserving special shared fixture locations where needed.
- Remove a prefix when the enclosing path already supplies that exact context and the shorter name stays informative: `jira/attachments/image-cache.ts` is clearer than repeating `jira-attachment-` everywhere.
- Retain meaningful context when ambiguity would hurt search or review. `agent-session/journal-position.ts` is more useful than `agent-session/position.ts`. A descriptive kebab-case filename is healthy.
- Use concrete entries such as `register-handlers.ts`, `read-context.ts` or a named slice file. Avoid new `utils`, `helpers`, `common`, or catch-all folders. Avoid a forest of one-file folders and generic `manager.ts` files.
- Keep a small root for composition and entries. Add another level when distinct responsibilities emerge; do not mechanically convert every hyphen into a path separator.
- Keep imports explicit. A new folder does not require an `index.ts` that re-exports every internal file. A folder provides navigation; an enforced boundary or a real package provides stronger isolation.

The first small proposals shorten redundant prefixes. Most larger proposals retain basenames initially so a reviewer can verify the relocation easily. Shortening those names can follow after the boundary is settled. No global case-style migration is needed.

## How to pursue the cleanup

Phase 1 contains independently selectable pilots. Start with **one provider IPC family**. Linear and Jira are lower-risk choices because the selected files are desktop transport handlers around existing backend domains. Lower risk does not mean no validation is needed. Issue-context, attachments and VM code have more lifecycle or permission concerns and should follow after the pilot establishes the process.

Phase 2 completes existing trees and groups clearer subdomains. Phase 3 handles broadly shared contracts and lifecycle code after ownership review. Priority and risk are independent: shared contracts have high navigation value but also high migration cost.

The dependency evidence makes that sequencing concrete. Each provider IPC proposal has one outside production-classified caller. The shared agent-session scope has 1,085, while Jira's shared scope has 176. The flat persistence scope contains only one production-classified module among 85 files, yet 209 outside production-classified files import that scope. Moving a central entry can create substantial churn even when its neighbors are mostly tests. These counts include type imports and reflect direct callers in this snapshot, not runtime coupling or transitive impact.

The exact scope, file counts, outside callers and destinations for all proposals are in [the backlog table](CODEBASE-MAP.md#cleanup-backlog) and `codebase-map.json.proposals`. Broad proposals must be split into smaller changes; the JSON is an inventory of candidate ownership, not an executable bulk-move plan.

For each implementation PR:

1. Select a concrete responsibility and inspect its incoming callers and outgoing dependencies. Agree its public entry and identify any generated inputs, runtime entries, fixtures or compatibility fences.
2. Move production files with their relevant tests. Update imports, source-reading tests, aliases, package exports, build entry tables, scripts and CI references. Search for both full paths and entry basenames; the import graph cannot find every path string.
3. Keep exported APIs, channel names, wire formats, stored formats, registration order and lifecycle behavior stable. Keep semantic redesign in a separate review.
4. Run the affected tests and typechecks. Use desktop checks for desktop moves, mobile's independent checks for mobile/shared consumers and cloud's checks for cloud work. Run applicable boundary and ratchet checks; path moves must not add baseline exceptions to make CI pass.
5. Verify packaged worker/process entries when affected, including Windows/WSL and macOS/Linux behavior. Shared-contract changes need mixed-version client/server coverage; workspace changes need both folder workspaces and git worktrees.

For the first Linear IPC PR, the concrete change is seven file moves plus caller updates. Check its registration composition, the Linear IPC tests and desktop typechecking. The [main build-entry table](../../../config/build-plugins/main-build-entry-points.ts) becomes particularly relevant for later watcher and worker-related moves. Existing [compatibility import fences](../../../config/scripts/check-compat-boundary-imports.mjs) and the [process-host import boundary](../../../config/scripts/check-process-host-imports.mjs) should remain intact.

Judge success by whether a contributor can locate and change one responsibility in one obvious subtree, with explicit composition and a reviewable diff. Raw filename length, folder count and a lower sibling count are supporting signals, not success on their own. There is no defensible runtime-speed or engineering-hours savings estimate from this static audit.

## Keeping the evidence useful

Run from the repository root with its existing Node and pnpm dependencies installed:

```sh
node docs/audits/module-organization/generate-map.mjs
node docs/audits/module-organization/generate-map.mjs --check
```

The first command refreshes only `codebase-map.json` and records the checkout commit as snapshot provenance. The second preserves that recorded commit while recomputing and strictly comparing every measured field, including the source-content hash. Committing the audit or other changes outside the measured inventory therefore does not make an otherwise identical snapshot stale. The recorded commit must have a valid full Git object ID; checking does not require its historical object to be available in a shallow checkout. Both commands read Git-tracked paths with current working-tree contents and exclude this audit's own artifacts. Newly added source files must be tracked before they enter the inventory. The hash covers JS/TS paths and contents, not every repository asset.

The generator reuses the repository's tracked-file inventory, AST module-specifier parser, test classifier and root workspace source resolver. Human judgments live in `audit-plan.json`; changing those judgments requires review. After moves land, remove or update completed selectors before regeneration: an empty selector deliberately fails rather than silently retaining an obsolete proposal. Update the prose tables when refreshing the data; the generator does not rewrite the report.

The import graph includes type-only imports and recognized literal loaders. It counts direct file dependencies, not calls or runtime loading cost. Computed paths, `new URL`, filesystem reads, spawn/worker strings, package scripts and CI path references require separate inspection. Unresolved specifiers and parse failures are recorded explicitly. Folder size and repeated-prefix families identify places to inspect; the families overlap and must not be summed.

---

### Audits Native Pty Spawn Env Retention README (`docs/audits/native-pty-spawn-env-retention/README.md`)

# Native PTY spawn environment lifetime

The native PTY handle's exit callback captured its complete creation arguments solely to read `reportsChildExitStatus`. Those arguments include the merged spawn environment. Copying that boolean before registering the callback releases the arguments and environment while the PTY remains live.

This is per-handle retention: the original objects also collect after the handle and native event owner become unreachable. It does not establish retention after every terminal closes, native PTY memory usage, an RSS slope, or the cause of #19831.

## Ownership and compatibility

- `src/main/daemon/pty-subprocess.ts:72–113` creates the environment, completes preflight and native spawn, then passes a fresh object literal to `createDaemonPtySubprocessHandle`. This is the sole production call site; the caller never stores or mutates that object afterward.
- `src/main/daemon/pty-subprocess/native-pty-spawn.ts:29–74` computes `reportsChildExitStatus` synchronously from the selected native launch command. Every successful return copies the boolean into its result. It is an immutable spawn fact in this call chain.
- `src/main/daemon/pty-subprocess/subprocess-handle.ts:26–69` needs the process, projected foreground metadata, scalar exit-status fact and PATH. Its long-lived exit callback previously retained the whole argument object. The fix changes only that capture. Native spawning, native signal ownership, physical-exit ordering, disposal and output buffering are unchanged.
- The environment is a fresh merged object, but many of its string values may already be shared with `process.env`. Releasing its reachability does not imply an equivalent reduction in resident bytes. Required PATH remains reachable through `shellPathEnv`.

`source-versions.json` records exact hashes. Main commit `291b4ddd6f1c1af480169885e0fda7f9c78ff053` exactly matches the audited wrapper baseline. Release `v1.4.198` (`e0826956fcfc532f5a1e55b5e081f2e57e553c43`) has the same environment capture and callers, but predates unrelated I/O-failure and exit-listener ordering changes. The two-line patch applies to both named sources. This is a historical source comparison, not a historical packaged-runtime reproduction.

The change stays inside the daemon's execution-host wrapper. It adds no remote wire data or client-side process verdict, and depends on neither a git worktree nor a folder workspace.

## Bounded before/after proof

`sources.cjs` reverses `fix.patch` in memory and checks the exact baseline and fixed SHA-256 values before bundling either version. It imports the actual foreground tracker and pre-listener queue, and records all effective source dependency hashes and the generated bundle hash. No git refs, copied production implementation, build outputs, credentials or ignored notes are required to rerun it.

Source and patch reads normalize CRLF to LF before reversal and hashing; recorded named-source, dependency and event-emitter hashes use canonical LF. The proof also feeds synthetic CRLF source and patch text into the loader in memory and verifies identical before/after source and hashes, without writing product files. This checks the checkout line-ending case, not a Windows runtime.

The fixture uses the installed `node-pty` JavaScript event emitter and an inert process port. Native termination imports and `process.kill` are guarded; no native PTY, subprocess scan, OS signal, socket or window is created. WeakRefs measure one small argument object and one small environment object, with no payload amplification. The deadline is 15 seconds and the heap limit in these commands is 128 MiB.

| Runtime                        | Live handle before: args / env | Live handle after: args / env | After owner drop, both versions |
| ------------------------------ | ------------------------------ | ----------------------------- | ------------------------------- |
| Node 26.6.0                    | 1 / 1                          | 0 / 0                         | 0 / 0                           |
| Electron 43.7.0 / Node 24.21.0 | 1 / 1                          | 0 / 0                         | 0 / 0                           |

Both versions preserve PATH, startup-delivery metadata, raw foreground lookup, pre-listener output and exit replay, normal exit codes, signal causes, unavailable wrapper status, dead-handle signal guards and idempotent disposal. `node-results.json` and `electron-results.json` contain the measured results.

From the repository root, run the Node proof:

```sh
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=128 docs/audits/native-pty-spawn-env-retention/reproduce.cjs
```

For Electron, run the installed Electron executable with `--expose-gc --max-old-space-size=128 docs/audits/native-pty-spawn-env-retention/reproduce.cjs`. Set `ELECTRON_RUN_AS_NODE=1` and `ORCA_BACKGROUND_LAUNCH=1` in its environment. This keeps Electron in Node mode; it creates no windows.

## Regression checks

The new lifetime regression measures collection before native exit, then confirms that the live handle still delivers data and exit. Two more cases preserve both exit-status interpretations after collection. Existing lifecycle, foreground identity/cadence, environment inheritance and I/O-failure cleanup suites cover neighboring contracts.

The fixed six-file run passed 114 tests with four existing platform skips. The reversible baseline overlay ran the new and existing lifecycle suites: one expected lifetime failure, 29 passing controls. To reproduce the overlay without editing product files:

```sh
ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config docs/audits/native-pty-spawn-env-retention/before.config.mjs src/main/daemon/pty-subprocess-env-retention.test.ts src/main/daemon/pty-subprocess-handle-lifecycle.test.ts
```

`validation.json` records the verification commands and outcomes. The pending-creation cancellation audit is separate and is not changed here.

---

### Audits Nested Repo Processed Queue README (`docs/audits/nested-repo-processed-queue/README.md`)

# Release completed nested-repository scan records

`scanNestedRepos` kept every consumed `TraversalFolder` in its breadth-first queue until the scan finished. Those records retained path segments and inherited parsed ignore rules after their directories had been processed. Releasing each consumed slot and occasionally compacting the empty prefix removes that temporary retention while preserving traversal order.

## Run

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/nested-repo-processed-queue/reproduce.cjs
```

The runner uses the repository's process launcher to start a Node child with forced GC, a 256 MiB old-space limit and a 15-second timeout. It bundles the actual scan and ignore-rule parser. An observational hook records weak references and scalar queue counts. A finite injected filesystem pauses one directory read; no app window, PTY, SSH connection or native watcher starts. The unused local Git detector is a throwing stub, ensuring the injected filesystem owns every probe.

The fixture has 96 branches, each with 64 distinct ignore rules and one child directory. It pauses the penultimate child read, leaving one pending directory. Four event-loop-separated GC rounds precede each observation.

| Observation                                |  Before | Slot-release control |  Fixed |
| ------------------------------------------ | ------: | -------------------: | -----: |
| Completed child records surviving GC       | 94 / 94 |               0 / 94 | 0 / 94 |
| Their inherited rule arrays surviving GC   | 94 / 94 |               0 / 94 | 0 / 94 |
| Observed records surviving scan completion |       0 |                    0 |      0 |
| Total directories visited                  |     193 |                  193 |    193 |

All variants visit the same directories in exactly the same order and return the same empty result. The baseline reverses only `fix.patch` in memory. The diagnostic control adds only consumed-slot clearing to that baseline, without compaction; it isolates the retaining path. The fixed variant executes the current queue implementation. `results.json` includes source hashes, exact queue counts, runtime provenance, process exit and timeout status.

The narrow source regression suite exercises a wider traversal across repeated compaction, including Windows and SSH POSIX path forms, inherited ignore rules, breadth-first result order, maximum depth, repository caps, cancellation and optional timeout behavior. All 37 discovery, queue and scan-rule tests passed, along with Node typechecking and focused lint checks. Existing discovery tests cover local filesystem and symlink behavior.

## Reuse and scope

The change follows the consumed-slot release pattern in `ws-outbound-backpressure-queue.ts` and the amortized prefix compaction pattern in `runtime-rpc-call-queue.ts`. It introduces no new queue abstraction, traversal policy, RPC field or host boundary.

The baseline source matches `v1.4.198`; the runner verifies this named-tag comparison after normalizing CRLF line endings to LF for Windows checkout portability. This is not an execution of the historical packaged application.

The IPC route uses this scanner for local and SSH-backed folder selection, with filesystem operations delegated to the selected host. Runtime scan/import routes request a 15-second timeout; IPC forwards options, whose timeout defaults to null. Existing time checks happen between awaited operations and do not cancel a pending read.

This fix releases completed work. It does not cap the active frontier, directory entry arrays, `.gitignore` size or directory breadth. The original implementation releases its records on scan completion. No heap-byte savings or field-incident attribution is claimed; no affected-host data was used.

---

### Audits Osc133 Carry Retention README (`docs/audits/osc133-carry-retention/README.md`)

# Retained OSC 133 incomplete carry

The shared command-lifecycle scanner keeps an incomplete OSC 133 suffix of at
most 4,096 UTF-16 code units. A V8 sliced string can keep the entire preceding
PTY input alive through that small suffix. The correction copies only the final
incomplete carry through existing `ownRetainedString`; short prefixes, content,
parsing, callbacks, authority and reset behavior are preserved.

This adds the fifteenth retained-text boundary to
[#20960](https://github.com/stablyai/orca/pull/20960), following the
[kitty/mouse tails](../terminal-mode-tail-retention/README.md) and other
[retained text slices](../retained-text-slices/README.md). It introduces no wire
change and applies equally to local and SSH/remote terminal bytes reaching the
shared scanner.

## Reproduce

```sh
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/osc133-carry-retention/reproduce.cjs
```

Run the same script with the installed Electron executable, setting
`ELECTRON_RUN_AS_NODE=1` and `ORCA_BACKGROUND_LAUNCH=1`, with the same Node flags.
No application window, native PTY, or network is created. The runner has a
60-second deadline and accepts an optional output-report path as its first
argument; otherwise it writes [Node](./node-results.json) or
[Electron](./electron-results.json) results here.

The portable loader validates all 28 bundled source modules and seven additional
caller/fixture files. Non-evaluated provenance callers accept only the recorded
audited or named-main bytes, and reports identify which was present; evaluated
modules each require one exact fixed hash. It reverses only the new import/copy call in memory through
a zero-context [patch](./fix.patch), then validates the baseline hash. All
evaluated-source and artifact hashes are recorded. It needs no Git history,
ignored notes, or absolute developer paths. Source/patch reads normalize CRLF;
an in-memory CRLF control checks equivalent before/after strings.

The scanner baseline exactly matches named main
`291b4ddd6f1c1af480169885e0fda7f9c78ff053` and `v1.4.198`
(`e0826956fcfc532f5a1e55b5e081f2e57e553c43`). The copier did not exist in
`v1.4.198`; current helpers and callers are used for both sides of this
experiment. [Source provenance](./source-versions.json) records each named
identity/absence separately. This is not a replay of a complete historical app.

## Result and controls

Both Node 26.6 and Electron 43.7 / Node 24.21 pass **117 cases**. Each runtime
compares the baseline, fixed Buffer copier and fixed Bufferless copier through
the actual scanner, shared title tracker, and daemon background transient-fact
relay. Thirty-two 64 Ki-character inputs retain roughly 2 MiB before the copy;
eight 1 Mi-character inputs retain roughly 8 MiB. Fixed deltas are below the
asserted 1 MiB tolerance, including owner overhead. Completion and reset/exit
release the old parents. Exact GC-sensitive measurements are in the reports;
they are heap deltas, not RSS or exact allocation attribution.

The ordinary sequence bytes come from the fish 4.7.1 capture documented in
`src/shared/terminal-mode-2031-final-state.test.ts`: `A;click_events=1` and
`C;cmdline_url=npx`. That capture contains complete OSC sequences. **The large
plain-output prefix and cut before the terminator are synthetic.** This does
not claim the original capture had those sizes or boundaries.

Controls preserve BEL/ST completion, split prefixes, C/D callback values,
background disable/re-enable, reset, and every split of a Unicode/NUL/lone-
surrogate fixture. The Bufferless copier is selected while Buffer is absent,
then Buffer is restored before measurement; this exercises the renderer's
actual fallback without launching a renderer. Short ordinary `D;0` prefixes,
complete sequences and plain input are negative retention controls. Oversized
unterminated input is separately labelled malformed-protocol stress. V8's
independent last successful RegExp input is reset before both measurements.

Eight permanent tests cover both copier paths, long captured-fish suffixes,
completion, reset and short `D;0`. With the in-memory baseline overlay, exactly
four long-suffix regressions fail at 33,550,680–33,565,360 retained bytes against
a 2 MiB allowance; the other 41 tests in the four-suite run pass. The fixed run
passes all 45. Wider proof/quality validation is recorded in
[validation.json](./validation.json).

## Owners and ordinary input bounds

Main creates a per-PTY tracker with `onCommandFinished` in
`orca-runtime-get-unpersisted-tracked-title-for-pty.ts`; scanner enablement still
respects transient-fact consumer/authority state. Ordinary daemon output frames
delivered to main are sliced to 64 Ki characters in
`daemon-stream-data-batcher.ts`, and ordinary relay output to 16 Ki characters
in `src/relay/pty-handler.ts`. The 64 Ki cases therefore demonstrate retention
without requiring a multi-megabyte main input; the 1 Mi cases amplify the
mechanism. Replay and transformed output have their own existing limits.

The daemon's `BackgroundTransientFactRelay` owns one tracker per background
session. `daemon-terminal-admission.ts` feeds it before output batching, so the
batcher's later slicing is not an input cap on this daemon scanner. Native data
passes through `pty-subprocess/subprocess-handle.ts`, the session's shell
readiness/startup/recovery path, and its stream client. The inspected local
intake does not impose an independent string-length limit; platform/native
library chunk sizes were not measured here.

Completion/replacement of the incomplete escape, scanner reset, session exit,
background retirement, tracker disposal or owner release drops the old parent.
This is at most the last incomplete-parent cost per live scanner, not a list of
every historical chunk. Multiple readers may share the same input backing
storage; do not add their isolated measurements as independent process totals.

This is a reproduced code-level retention mechanism. It does not establish a
native output pause, normal-session frequency/duration, the trigger in
#19831/#19768, a reported sustained growth rate, or a multi-gigabyte incident's
cause. The change does not reduce original input allocation.

---

### Audits Pending Runtime Pane Close README (`docs/audits/pending-runtime-pane-close/README.md`)

# Closing an unbound paired-runtime pane with a captured handle

A restored pane can already hold a scoped `remote:<environment>@@<handle>` layout binding while `remote.attach()` waits for `terminal.resolvePane`. The transport's `getPtyId()` is still null. An explicit split close therefore passed null to `closeWebRuntimeTerminal`, removed the layout binding, and destroyed only the viewer. The host terminal stayed connected. This attachment/teardown behavior exists in `v1.4.198`.

This is a specific retained host-terminal mechanism. The change is stacked on the local/direct-SSH pending-close fix in [#21001](https://github.com/stablyai/orca/pull/21001) and reuses its current-owner query. It does not prove the incident frequency in [#15210](https://github.com/stablyai/orca/issues/15210), Linux Electron-main growth, or [#19831](https://github.com/stablyai/orca/issues/19831)'s memory slope.

## Scope and authority

Only an exact scoped handle whose environment matches the owning workspace's runtime authorizes this fix. Existing retirement planning and the shared current-owner query protect other tabs, sibling aliases, and bound transports. The provider helper captures the pairing revision, performs its existing compatibility check, then checks pairing and current ownership again immediately before dispatch. The second call skips only the check that just completed. It sends the existing `terminal.close` request for the captured handle.

The actual host fixture verifies that re-registering the same PTY ID with a new incarnation allocates a new handle. A close addressed to the old handle rejects and never invokes the controller's kill operation. Client same-leaf adoption, a replaced transport map, and changed worktree/pairing ownership also suppress the queued request. Ordinary detach remains viewer-only.

Native host PTY hints, legacy handles without an explicit environment, and returned different handles are outside this fix. Current client snapshot registries retain freshness/frame identity rather than a live terminal-row incarnation. Inferring destructive authority from a late native-hint resolution could stop a replacement. The separate read-only native-hint and pending web-activation reproduction remains in `notes/paired-pending-split-close`; it establishes omitted requests, with no claim that these excluded cases are fixed. No parent-tab close, local fallback, new wire field, or capability is introduced. A request is not confirmation of process death; provider failures retain their existing handling.

## Close-confirmation review correction

The public split-close callback now probes the captured scoped handle before authorizing retirement, including while `terminal.resolvePane` remains pending. Live or unverified pending work opens the existing confirmation dialog. Cancel keeps the host terminal; Confirm rechecks the captured tab, pane, transport, handle, host, and pairing revision. A replacement or a split that became the only pane invalidates the old decision. The host's existing handle-incarnation fence and the compatibility-dispatch checks remain in force.

`pending-pane-close-confirmation.test.ts` adds public-callback controls for both local/direct-SSH and paired pending panes. The comparative counts below remain the original proof snapshot, which called the post-confirmation `executeClosePane` callback directly.

## Reproduce

Run in the repository root with existing dependencies:

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/pending-runtime-pane-close/reproduce.mjs
```

The script runs 13 tests using the actual split-close hook and remote transport, plus two tests delivering the close RPC into an actual `OrcaRuntimeService` with a fake PTY controller. React registration and unrelated presentation callbacks are mocked. No Electron window, host process inventory, or real PTY child is used.

The temporary Vite transform reverses only `fix.patch`; the baseline includes the IPC fix from #21001. Working sources remain untouched. The script uses the shared cross-platform process runner and records source hashes and exact cases in `results.json`.

| Version                  | Passed | Failed |
| ------------------------ | -----: | -----: |
| Before scoped-handle fix |      5 |     10 |
| With scoped-handle fix   |     15 |      0 |

The baseline failure count includes new eager-request/compatibility assertions, not ten independent leaks. Additional validation: 255 tests in 21 selected renderer suites, full renderer typecheck, direct lint, and the changed-code quality gate pass. The original 24-case IPC proof still runs after the shared ownership extraction; its committed results remain a snapshot of the published IPC source.

---

### Audits Pending Split Close README (`docs/audits/pending-split-close/README.md`)

# Pending split close can omit shell retirement

A restored split pane can be explicitly closed while its IPC spawn/reattach reply is pending. Its transport has no bound PTY ID yet. The old close path deleted the durable leaf binding and destroyed the unbound transport without requesting retirement. The late-result cleanup deliberately preserved reattach and cold-restore replies for remounts, so an existing shell or newly cold-restored shell could remain live with no pane and no kill request.

This is a concrete mechanism matching the **missing kill requests** in [#15210](https://github.com/stablyai/orca/issues/15210). The relevant close, late-result exclusion, and daemon cold-restore behavior exists in both `v1.4.184` and `v1.4.198`. The report does not establish that this sequence produced its 51 shells. It also does not establish a retained Electron-main heap slope or independently explain [#19831](https://github.com/stablyai/orca/issues/19831).

## Ownership and fix

The explicit split-close hook captures its durable requested PTY before removing the leaf binding. Existing tab-retirement planning resolves the local/direct-SSH execution owner. It requests retirement immediately and retains an explicit-close callback for the late same-ID result. The late callback rechecks current store ownership and the current transport map, including a replacement at the same leaf. It makes a second request only when no owner remains.

An eager request alone is insufficient: Electron spawn preflight can still be waiting before the adapter/history lock exists. A kill can return `SessionNotFoundError`, then the pending spawn creates a new cold-restored shell. The late-result request closes that window. If spawn already owns the adapter's history lock, the ordinary known-ID shutdown waits behind it; the control case preserves that behavior.

Generic detach/destroy keeps its existing behavior. Repeated destroy retains explicit intent, while a different returned reattach identity remains protected. The tab aggregate/row ID is not counted as a separate same-tab owner; it can be the closing leaf's own stale index. Other live tabs use all existing retirement ownership sources; remote alias matching reuses the existing normalized identity.

Paired-runtime handles, runtime-owned native hints, and unresolved owners never fall through to local kill. They are outside this IPC fix. A provider failure is logged by the existing retirement helper; requesting retirement is not confirmation of process death. No host inventory sweep, wire change, global tombstone, or remount-driven shutdown is introduced. A shared owner already present at close keeps its established retirement responsibility; this does not change all pending-fresh/shared-owner races.

## Close-confirmation review correction

The original tests called `executeClosePane` after a close decision. A separate review found that the public `handleRequestClosePane` callback skipped the running-work check while the transport was still unbound. Retirement now obtains the pending local/direct-SSH identity from the existing retirement plan and runs the existing confirmation flow first. An unverified pending probe asks for confirmation; Cancel preserves the process. Before a delayed decision or confirmation acts, the captured tab generation, pane, transport, binding, and execution owner must still match. A split that became the final pane cannot turn an old confirmation into a whole-tab close.

`pending-pane-close-confirmation.test.ts` exercises the public callbacks, including live and unverified work, Cancel, confirmed close, completed attachment, ownership replacement, and direct SSH. These are separate regression controls added after the original comparative proof below; they do not change its historical case counts.

## Reproduce

From the repository root with the existing dependencies installed:

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/pending-split-close/reproduce.mjs
```

The script runs the actual close hook, layout binding, pane-close handler, IPC transport, daemon server, and daemon adapter. React registration and unrelated presentation/status callbacks are mocked. Daemon cases use temporary sockets, synthetic history, and the existing fake subprocess fixture; no real shell or Orca window is launched. It cleans its temporary configuration and invokes Vitest through the repository's cross-platform `runProcess`.

`fix.patch` is reversed only inside a temporary Vite source transform for the baseline. Working files remain unchanged. New helper/test sources remain present, but the baseline close hook cannot call the helper. Source hashes and exact cases are recorded in `results.json`:

| Version | Passed | Failed |
| --- | ---: | ---: |
| Before fix | 10 | 14 |
| With fix | 24 | 0 |

The 24 cases retain the original nine reproduction/control scenarios and add same-leaf/new-map/different-tab adoption, sibling ownership, repeated destroy, provider failure/retry, spawn rejection, returned-ID mismatch, direct SSH, unresolved/paired-runtime routing, and folder workspace coverage. Eight separate ownership-query tests cover legacy/scoped aliases and the existing tab ownership sources. The baseline failures include assertions about the new eager request; they are not 14 independent leaks.

Historical entry points: `v1.4.184` `TerminalPane.tsx:1163`, `use-terminal-pane-lifecycle.ts:1368`, `pty-transport.ts:859`, and `src/main/daemon/daemon-pty-adapter.ts:750`. The current equivalents are `use-terminal-pane-close-actions.ts`, `terminal-pane-pane-closed.ts`, `ipc-pty-connect.ts`, and `daemon-pty-spawn-result.ts`.

---

### Audits Plugin Uninstall Log Retirement README (`docs/audits/plugin-uninstall-log-retirement/README.md`)

# Plugin logs after successful uninstall

Successful uninstall previously removed the plugin and its worker while retaining up to 200 log rows under its key. Repeating this across distinct keys retained every old ring. Old worker callbacks could also append into a same-key reinstall; a late stdout tail could write after worker exit and uninstall.

The fix clears a key only after worker deactivation and filesystem removal both succeed. Worker, panel and event producers capture the current log-entry token before asynchronous work. Clearing the entry invalidates those callbacks without retaining the old row array or adding permanent key tombstones. Installed stopped/crashed history and failed-removal history keep their existing policy.

## Removal authority

Removal joins the existing refresh chain and resolves the installed object after earlier refreshes finish. A normal refresh replaces discovery objects, so capturing one before joining that queue would incorrectly reject an authorized uninstall. Fresh command, panel and event work for the selected object is blocked while removal owns it; failure restores admission.

The service rechecks bundled ownership before deactivation. The filesystem remover independently rechecks it inside the existing per-directory mutation queue, before deleting files or lock ownership. This protects a newly bundled revision published while the old worker is stopping. The fixture publishes genuinely different bytes: reinstalling identical bytes preserves the previous immutable provenance and does not establish a bundled successor.

A successful removal retires the exact discovered object before a later refresh can publish a successor. This changes no wire format or execution-host policy. There is no process-death inference, extra queue, broad history cap, or kill policy change.

## Evidence

The four-control portable comparison uses actual service, IPC handler, installer, discovery, worker runtime, IPC parsing and stdout framing. Worker fork/exit transport, Store settings access and imported plugin code are controlled ports; real temporary files exercise installation and removal. No native worker process or app window is created.

| Control                                                 | Before                                     | Fixed                 |
| ------------------------------------------------------- | ------------------------------------------ | --------------------- |
| Eight distinct successful uninstalls, 205 SDK rows each | 8 keys / 1,600 rows / 8 sampled rows alive | 0 / 0 / 0             |
| Old callbacks held after identical-key reinstall        | Old log accepted                           | Successor log remains |
| Worker exit precedes final stdout end                   | Retired tail retained                      | No retired key        |
| Installed SDK / malformed raw IPC bounds                | 8,192-code-unit SDK limit; 200 rows        | Same                  |

Old fake worker objects remain rooted during the collection control. WeakRefs measure reachability of sampled row objects, not RSS or byte savings. Bounded synthetic callback ordering is not a claim that a native worker produced that ordering in an affected incident.

All 89 tests pass across the 17 permanent regressions and 72 existing compatibility tests. The 17 permanent regression controls additionally cover partial filesystem failure/retry, late panel and event diagnostics, refresh-before-remove, changed revisions, both bundled checks, queued identical reinstall, admission restoration, service disposal and installed crash history. Existing compatibility suites cover the host runtime, controller, panel, installer, integrity/reconciliation and IPC behavior.

## Source scope and reproduction

`source-versions.json` records exact source hashes. The original eight changed product baselines match named main `291b4ddd6f1c1af480169885e0fda7f9c78ff053` and reported v1.4.198 `e0826956fcfc532f5a1e55b5e081f2e57e553c43`. The comparison uses explicitly fenced current dependencies; it is not a replay of either whole historical checkout. The small `dependency-context.patch` reconstructs four current dependency differences when running from published main. Tests and fixture modules travel with this change. The new ninth product module, `plugin-installation-state.ts`, contains the extracted installation/log state and removal transaction. It is absent from both named baselines; the service remains the sole refresh-queue owner. The source inventory supplies 249 before / 250 fixed repository paths (including the optional baseline test), with six package manifests. Actual comparative evaluation loads 246 before / 247 fixed repository paths.

The loader reverses the exact zero-context fix for the before run, verifies every supplied repository source and package-manifest hash, and rejects unfenced runtime repository imports. The loader controls verify CRLF normalization, source drift rejection and a simulated publication checkout. Installed third-party runtime code is represented by the recorded package manifests, not claimed byte-identical to historical packages.

From the repository root:

```sh
ORCA_BACKGROUND_LAUNCH=1 ORCA_PLUGIN_LOG_VARIANT=before pnpm exec vitest run --config docs/audits/plugin-uninstall-log-retirement/phase.config.mjs
ORCA_BACKGROUND_LAUNCH=1 ORCA_PLUGIN_LOG_VARIANT=fixed pnpm exec vitest run --config docs/audits/plugin-uninstall-log-retirement/phase.config.mjs
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/plugin-uninstall-log-retirement/loader-controls.cjs
```

For Electron's installed Node runtime, use the installed Electron executable with `ELECTRON_RUN_AS_NODE=1 ORCA_BACKGROUND_LAUNCH=1` and `node_modules/vitest/vitest.mjs` followed by the same `run --config` arguments. `ORCA_PLUGIN_LOG_OUTPUT` and `ORCA_PLUGIN_LOG_LOADER_OUTPUT` select alternate report paths. `before.config.mjs` runs the six permanent retention cases against baseline and is expected to fail those retention assertions.

## Review correction, 2026-09-17

The first published head failed the child-process import boundary because the mock support filename did not identify it as a test fixture. Renaming it to `__mocks__/plugin-uninstall-log-test-fixture.ts` uses the guard's existing test-fixture exemption. The native fork mock and real host/SDK IPC lifecycle remain unchanged; no production importer, allowlist or count limit changed.

After the rename, 23 checks passed across the three uninstall suites, the child-process boundary suite and the two native worker supervision cases on macOS. Node typecheck, ordinary lint and anti-slop passed. All 16 before/fixed comparative controls passed again across Node and Electron's Node runtime, and loader controls passed with the renamed source fences. The prior 89-test batch above remains historical evidence for the unchanged product code.

Successful uninstall now discards the previous installation's 200-row in-memory history. The settings UI already removes the deleted plugin's log row, but reinstalling the same key no longer resurrects that old history. This is an explicit lifetime policy change, not a claim of identical observable output in every uninstall/reinstall sequence.

## Review correction, 2026-09-18

Activation rechecks approval after waiting for an old revision to stop. A concurrent successful uninstall therefore cannot be followed by a stale activation capturing a new log ring or starting another worker. The controller regression holds the old worker's disposal, completes deactivation and log retirement, then releases disposal and verifies that neither a worker nor a log capture follows. All 35 tests across the manager, controller and three uninstall suites passed; ordinary lint and Node typecheck passed. The before/fixed comparisons and loader controls were rerun with refreshed source hashes. The loader control now accepts both documented publication layouts: dependencies already carrying the fenced context and dependencies still at the named main source.

## Remaining limits

This explains a code-level main-process owner that survives successful uninstalls. It does not establish plugin usage, uninstall rate, field allocation size, or attribution for #19831 or any other incident. Installed, disabled, crashed, development and externally deleted plugin histories are not comprehensively retired by this change. Sequential filesystem deletion is not a rollback transaction; failure retains discovery/history for retry. The in-process mutation queues do not make external or cross-process lockfile edits atomic. If a bundled successor appears during shutdown, the old worker may already have stopped before removal is refused; the successor remains installed and can activate afterward.

---

### Audits Plugin Worker Output Retention README (`docs/audits/plugin-worker-output-retention/README.md`)

# Plugin worker output retention

The worker output parser capped a line at 8,192 code units, but retained slices could keep a much larger decoded input chunk alive. This artifact reproduces two ownership paths using the actual parser and actual `PluginLogBuffer`:

1. An unfinished line stays in the stream listener's buffer.
2. A completed short or truncated line stays in the service's 200-entry log ring.

The fix uses the existing `ownRetainedString` copier for incomplete segments retained across callbacks and for the bounded string passed to the log sink. Line contents, truncation, callback invocation, ring capacity, and worker lifecycle are unchanged. Strings shorter than 13 code units keep the helper's existing fast path.

## Production reachability and lifetime

- `src/main/plugins/plugin-host-process.ts` installs the parser on child stdout and stderr at lines 101–102, with UTF-8 decoding in the parser. The production sink passes through `plugin-worker-manager.ts:148` and `plugin-service.ts:94` to `plugin-log-buffer.ts:14`, which stores the original string without copying it.
- The parser retains at most one incomplete line per stream. The default five active workers allow ten live stdout/stderr buffers. Worker slots are acquired before startup. Idle workers are reaped after five minutes, checked every minute; stream end clears parser buffering.
- The log ring belongs to the long-lived `PluginService`, not the worker. Worker exit and stream end preserve its last 200 entries per plugin. Ring eviction releases the entries. Several lines can share one parent; the backing allocation must be counted once.
- This is a main-process plugin path. The plugin-system setting gates activation (`src/main/startup/main-process-plugins.ts:59–62`). It is not a terminal daemon or renderer retention path. The plugin `orca.log` IPC message is a separate producer.
- `PluginService.getLogs` and its IPC handler expose the existing ring. Reading or serializing a concatenated string can flatten it and shorten its parent retention, but does not remove the service's ring entries.

## Reproduce

From the repository root with the project's dependencies installed:

```sh
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/plugin-worker-output-retention/reproduce.cjs
```

For Electron, run the installed Electron executable with `ELECTRON_RUN_AS_NODE=1`, `ORCA_BACKGROUND_LAUNCH=1`, and the same arguments. This uses Node mode without opening an app window. For example, on macOS:

```sh
ORCA_BACKGROUND_LAUNCH=1 ELECTRON_RUN_AS_NODE=1 node_modules/electron/dist/Electron.app/Contents/MacOS/Electron --expose-gc --max-old-space-size=192 docs/audits/plugin-worker-output-retention/reproduce.cjs
```

The runner writes `node-results.json` or `electron-results.json` beside itself. Pass `--output <path>` to preserve the captured reports. It uses inert PassThrough streams, no OS child process or network, a 192 MiB heap limit, and a 30-second deadline.

`sources.cjs` reverses `fix.patch` in memory and checks exact baseline/fixed parser hashes. It also checks eight dependency/caller hashes and records actual evaluated source and bundle hashes. No source files are overwritten. A synthetic CRLF read control checks all ten source/patch reads.

`source-versions.json` records identical parser, sink, caller, and helper hashes at main checkpoint `291b4ddd6f1c1af480169885e0fda7f9c78ff053`, main `f78483ec29891ab11f49bb25e6cd628837b1242e`, and the #20960 topic `np-oom-scan-retained-text-slices` at `0d2efbc0d3b4f902b9bc02f5451c6ff4291405cf`. The parser, sink and caller modules also match v1.4.198 (`e0826956fcfc532f5a1e55b5e081f2e57e553c43`), which lacks the newer `own-retained-string.ts` wrapper. The baseline bundle uses only the unchanged parser and ring; fixed variants use the recorded publication helper. These are source controls with current build dependencies, not a historical app binary.

## Controls and results

Both Node 26.6 and Electron 43.7 / Node 24.21 pass 24 cases: baseline, diagnostic tail-only copy, fixed Buffer copy, and fixed code-unit-copy fallback, each with two input sizes and three ownership cases. The fallback is a shared-helper compatibility control; production main normally has Buffer.

| Retained owner                                  | Baseline heap delta | Fixed heap delta |
| ----------------------------------------------- | ------------------: | ---------------: |
| Ten unfinished tails, 64 KiB input each         |        0.72–0.73 MB |          9–24 KB |
| Eight unfinished tails, 4 MiB input each        |      33.56–33.57 MB |          6–11 KB |
| 200 short log rows from 205 × 64 KiB inputs     |      13.14–13.16 MB |         27–45 KB |
| 200 truncated log rows from 205 × 64 KiB inputs |      13.14–13.15 MB |     3.29–3.31 MB |
| Eight short log rows, 4 MiB input each          |            33.56 MB |       about 1 KB |
| Eight truncated log rows, 4 MiB input each      |            33.56 MB |       128–132 KB |

Heap deltas include GC noise. Truncated strings legitimately retain 8,192 code units, including the non-ASCII truncation suffix. Tail-only copying fixes unfinished buffers but leaves both log-ring paths. Stream end clears no-op-sink tails while the actual ring remains live; replacing all 200 entries releases the original parents.

64 KiB is an ordinary-scale stdio input control. The 4 MiB input is amplified stress, not a claim about normal OS pipe reads. PassThrough delivers the selected chunk intact; real child-pipe chunk sizes depend on runtime and OS. Retention is bounded by owner count, ring capacity and backing input size; this is not an unbounded line queue.

Behavior comparisons cover blank and split lines, null/empty streams, CRLF, end flushing, discard/resume after overflow, log level, exact ring content, NUL, lone surrogates, emoji, and the code-unit limit. Value comparisons run separately from heap controls because comparing concatenated strings can flatten them and change retention.

## Validation

```sh
ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/main/plugins/plugin-worker-output-retention.test.ts src/main/plugins/plugin-worker-output-buffer.test.ts src/main/plugins/plugin-host-process.test.ts src/shared/own-retained-string.test.ts
ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config docs/audits/plugin-worker-output-retention/before.config.mjs src/main/plugins/plugin-worker-output-retention.test.ts src/main/plugins/plugin-worker-output-buffer.test.ts
ORCA_BACKGROUND_LAUNCH=1 pnpm tc:node
```

The fixed source passes 20 tests. The baseline overlay intentionally fails all three new heap regressions: approximately 33.6 MB for unfinished tails and 13.2 MB for each ring case, against 2 MiB and 5 MiB ceilings; its original behavior test passes. Node typecheck passes. All five changed-quality scan configurations pass over all five product/test/artifact code files with `--no-ignore --deny-warnings`, including the ordinary and type-aware lint rules.

This proves a reachable code mechanism and its repair. It does not establish affected-host plugin use, output cadence, aggregate app RSS, or attribution to #19831 or another incident.

---

### Audits Pty Detector Retention README (`docs/audits/pty-detector-retention/README.md`)

# Retained PTY detector input

The advertised-URL watcher keeps a 4,096-character carry for each bound PTY and
16,384 characters for each of at most 32 unbound PTYs. The Command Code status
detector keeps 300 characters before its agent-specific prefilter, including for
ordinary shell, Claude, and Codex output. Each could keep the whole original
input alive through a V8 sliced string.

The fix uses the existing `ownRetainedString` copier when dropping oversized
input. It preserves URL reconstruction, status detection, UTF-16 code units,
binding/unbinding, cache limits, and remote/local authority. These are three
additional boundaries in [#20960](https://github.com/stablyai/orca/pull/20960).

## Reproduce

```sh
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc docs/audits/pty-detector-retention/reproduce.mjs
```

The script bundles the actual detector and watcher. The baseline removes only
the three copy calls in memory. Each input has its own live owner; URL cases
use separate watcher instances to isolate each carry. Heap is measured after
GC, before completing the partial URLs and verifying cleanup. Results include
owner overhead, not just text. [Bundle hashes and measurements](./results.json).

| Case                |  Input per owner | Owners | Heap before | Heap after |
| ------------------- | ---------------: | -----: | ----------: | ---------: |
| Status detector     | 64 Ki characters |     32 |   2,112,688 |     41,400 |
| Bound URL carry     | 64 Ki characters |     32 |   2,173,848 |    204,112 |
| URL pending binding | 64 Ki characters |     32 |   2,148,824 |    575,512 |
| Status detector     |  4 Mi characters |      8 |  33,557,144 |      6,504 |
| Bound URL carry     |  4 Mi characters |      8 |  33,569,168 |     47,112 |
| URL pending binding |  4 Mi characters |      8 |  33,568,936 |    144,504 |

Captured with Node v26.6.0 on macOS. Three GC regression tests failed before the
fix, retaining about 32 MiB each, and pass afterward. The five-suite run passes
164 tests including existing URL/status behavior and copier Unicode/fallback
tests.

## Scope and limits

Main feeds both observers before renderer batching. Default daemon bulk output
frames are at most 64 Ki UTF-16 characters; main's later 16 Ki-character batching
does not bound these readers. The 4 Mi-character cases demonstrate the retaining
mechanism under larger inputs, not normal daemon frame size. Both implementations
also exist in `v1.4.198`.

Transformed frames bypass ordinary chunk slicing but still face the daemon's
16 MiB encoded-line limit. Native fallback output has no application chunk cap;
this audit does not establish multi-MiB native reads. Ordinary relay chunks are
16 Ki characters. The actual main feed is `orca-runtime-on-pty-data.ts` and the
ordinary daemon bound is in `daemon-stream-data-batcher.ts`.

These are per-owner last-input costs, not unbounded growth for a fixed set of
PTYs and fixed-size frames. Owners consuming the same input can share the same
backing string, so the three measurements must not be added as independent
process costs. Unbind removes URL buffers and pending entries. This improves
memory proportional to active owners; it does not establish the cause or growth
rate of #19831 or #19768. Copy work is bounded by the small retained tails.

---

### Audits Queued Terminal Graph Exit README (`docs/audits/queued-terminal-graph-exit/README.md`)

# Queued renderer graph restores an exited pane owner

This is a separate source-level explanation for part of [#19018](https://github.com/stablyai/orca/issues/19018). It reproduces an execution host certifying exit, followed by a queued renderer graph restoring that PTY's runtime `connected` flag and making the actual stable-pane resolver throw `terminal_pane_owner_conflict` against the successor's durable binding.

## Run

From the checkout, with installed dependencies:

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/queued-terminal-graph-exit/reproduce.mjs /tmp/queued-terminal-graph-exit.json
```

The script uses the real renderer graph publisher, main `Store.persistPtyBinding`, runtime, daemon server, adapter, and local sockets. Only the subprocess and the IPC dispatch boundary are controlled. It creates temporary data/socket paths, runs hidden Node tests, and removes its scratch files. It does not launch an Electron window or install dependencies. The JSON records source hashes and excludes randomly allocated terminal handles/incarnations.

## Ordering

1. Publish the mounted predecessor's graph normally.
2. Capture its next unchanged publication at the IPC dispatch boundary. The real publisher sends the mounted leaf, `mobileSessionTabs: []`, and `unchangedMobileSessionWorktrees`.
3. While that publication is queued, spawn a successor and durably bind it to the same tab and leaf.
4. Deliver the predecessor's physical daemon EXIT. The runtime becomes disconnected with an `exited` verdict.
5. Deliver the already-captured graph, without reordering renderer publications.
6. Resolve the pane, query the owning daemon's fresh inventory, and publish once more. A second variant unmounts the renderer terminal before that inventory and remounts it afterward through the real registration/publisher API.

The replacement-binding commit and the daemon EXIT can run while a renderer invocation is queued. The production spawn commit persists the binding before returning its reply (`ipc/pty/ipc/spawn-commit-persist.ts`); the graph publisher reads the mounted pane's transport independently. The fixture controls that ordering; it does not prove its frequency on the reporting machine.

Retirement correctly refuses to delete the successor's durable binding. Before the fix, the retained surface coordinates then allow the old graph leaf to set the predecessor connected again. A healthy inventory contains only the successor, but the runtime sweep skips records that still have a graph leaf. The list's presentation can label that leaf disconnected while the underlying pane resolver still sees it connected.

## Results

| Variant         | First queued graph restores predecessor | After unmount, inventory, and remount | Pane conflict     |
| --------------- | --------------------------------------- | ------------------------------------- | ----------------- |
| Before          | yes                                     | yes                                   | yes               |
| Exit check only | no                                      | yes                                   | no at first check |
| Complete fix    | no                                      | no                                    | no                |

All three variants also run an ordinary-exit control without a replacement; it remains retired throughout. The middle variant isolates why a weak inventory absence during a renderer mount gap must preserve an already-earned exit certificate. Without that mount gap, the retained disconnected leaf keeps the inventory sweep from forgetting the verdict.

The fix reuses the existing liveness verdict registry to keep an exited graph leaf disconnected and nonwritable, and skips recreating its PTY/URL-watcher ownership. It preserves surface membership: a separate actual `stopExactTerminalsForWorktree({ keepHistory: true })` control passes a changed renderer-built mobile snapshot while physical exit has completed but the stop reply is pending. The history surface remains present before and after the renderer clears its PTY binding. All phases check this control; the fixed phase also checks that mobile projection never combines one PTY's ID with another PTY's handle.

Fresh spawn/registration clears the prior verdict; an owning inventory can establish `live`. A physical exit still records its certificate if the bounded PTY archive was already pruned. Host-only tests cover same-ID replacements, stale predecessor EXIT, fresh renderer-only panes, physical negative exit codes, local unverified stops, SSH disconnects, and retained history. The portable proof runs 15 actual-runtime cases across its three source variants.

## Limits and version evidence

The relevant graph admission, unconditional graph-connected write, durable retirement refusal, and inventory leaf exception are present in the reported **v1.4.197**. That tag already passes `providerExitObserved` from local and daemon physical exit callbacks and computes `processDeathCertified`; this proof's natural-exit path does not depend on #21000's synthetic-notification correction. Executable before/after runs use the current checkout with narrowly asserted source transforms, not the complete historical binary.

This proves stale runtime/pane ownership, not a measured native-process or heap leak. The graph alone does not recreate a headless terminal model. It does not establish that every missing diagnostics row denotes an exited process, nor explain all handle-count growth in the issue.

The existing register retains at most 256 unowned verdicts; PTY/handle/leaf owners keep their verdicts until their own lifecycle ends. The disconnected PTY archive is capped at 128. After both a record and its bounded verdict are evicted, the graph has no remaining per-ID certificate; this change does not add permanent tombstones. Later loss-of-contact writes can still replace an exit verdict with `unverifiable`; a stale positive inventory can separately write a connected record. Those paths are not exercised or fixed by this local queued-publication proof.

A separate audit found that an unreachable unrelated legacy daemon can make aggregate exact-stop verification fail despite absence on the target's own daemon. That is excluded from this fix and from the proof's healthy target-inventory assertion.

## Recorded run provenance

`results.json` records the graph fix before the separate provider-inventory lifecycle fence in #21014. Its source hashes identify that earlier run; they are not a claim that every later audit commit has the same bytes. The reproduction can be rerun against the combined worktree.

## Pull request dependency

The graph PR is stacked on #21000, reusing its daemon socket fixture and physical-exit delivery contract. The graph mechanism is separate; the stack makes the executable proof dependencies explicit.

---

### Audits Retained Text Slices README (`docs/audits/retained-text-slices/README.md`)

# Retained CI and terminal text tails

Capped V8 string slices can keep their entire original input alive. The affected
CI excerpt cache accepts 128 entries of 16 KiB text, from downloads up to 64 MiB.
GitLab's raw-trace clamp reaches the same shared excerpt function. Terminal
session/eager/shutdown buffers, deferred reattach queues, recent-output buffers,
and error surfaces also retained oversized parents despite their logical caps.

The fix reuses the existing shared `ownRetainedString` copier for CI, persisted
session tails, and main/relay recent output. Renderer queues and errors reuse
their existing `flattenRetainedSlice` helper. Content, Unicode, earlier-error
selection, cache counts, and transport payloads stay identical. Ordinary
untruncated terminal chunks keep their existing path. Main/relay recent output
preserves chunk boundaries for path-candidate backfill.

Local persisted scrollback is already pruned; the session-buffer fix primarily
covers remote or not-yet-classified owners. Queue and error fixes cover local
and remote output. The main/relay recent-output buffer has a configurable cap,
64 Ki characters by default.

## Reproduce

```sh
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc docs/audits/retained-text-slices/reproduce.mjs
```

The script bundles actual production functions. Its baseline removes only the
six new copy boundaries in memory; production files are not changed. Each case
retains eight distinct inputs: 2 Mi characters per CI log and 4 Mi characters
per terminal input. It measures heap after GC and clears V8's independent legacy
RegExp input reference. Bundle hashes and measurements are in
[results.json](./results.json).

| Case                             | Returned bytes, all eight | Retained heap before |     After |
| -------------------------------- | ------------------------: | -------------------: | --------: |
| GitHub long line                 |                   131,072 |           16,787,512 |   145,224 |
| GitHub earlier Unicode error     |                   131,064 |           33,577,840 |   112,744 |
| GitLab long line                 |                   131,072 |           16,793,640 |   147,312 |
| Persisted terminal buffers       |                 4,194,304 |           33,555,624 | 4,195,032 |
| Eager/pre-handler/shutdown tails |                 4,194,304 |           33,556,040 | 4,202,608 |
| Main/relay recent output         |                   524,288 |           33,558,752 |   527,384 |
| Terminal error surfaces          |                    32,000 |           33,555,864 |    33,336 |
| Deferred reattach tails          |                 4,194,304 |           33,565,384 | 4,198,352 |

Captured on macOS with Node v26.6.0. Heap samples include allocator/GC variation;
the large separation is the relevant result. Regression tests also retain the
actual error state and shutdown/reattach/recent-output queue objects.

Validation passed: 41 tests across six CI/provider/helper suites; 69 tests across
six terminal storage/ownership/UTF-8 suites; 28 tests across three error/reattach
suites; and a final 63 tests across seven recent-output/CI/terminal/copier suites.
These are per-run counts and overlap. Full typecheck and changed-code quality pass.

All six cap/slice paths exist in `v1.4.198`. Neither #19831 nor #19768 establishes
the CI-log viewing or oversized terminal inputs required for incident attribution.
Copying costs scale with retained caps: 16 KiB per CI excerpt, 4,000 characters
per error, 512 KiB for the largest byte-capped buffer, and 512 Ki characters for
deferred reattach. The change does not reduce temporary original-input allocation.

The follow-up [PTY detector reproduction](../pty-detector-retention/README.md)
adds three boundaries in the same PR: advertised-URL carries, output waiting for
workspace binding, and Command Code status carries used by ordinary PTYs too.
Thirty-two production-sized 64 Ki-character inputs retain about 2.1 MB in each
isolated baseline. Owned carries reduce that to about 41 KB, 204 KB, or 575 KB,
including the different owner objects. These per-owner costs are not an
unbounded growth curve, and readers of the same input can share its parent.
The follow-up adds 164 passing tests across five detector/URL/copier suites.

The [Claude task metadata reproduction](../claude-task-retention/README.md) adds
the shared 512-character description/name boundary. JSON-parsed task frames
retained their parents in the actual live, settled, and recently removed tracker
entries. Eight 4 Mi-character inputs retained about 32 MiB before the copy and
7–12 KB afterward; 32 smaller 64 Ki-character inputs retained about 2.1 MB before
and 25–45 KB afterward. These synthetic fields establish a retaining mechanism,
not the trigger of a reported incident.

---

### Audits Runtime Rpc Consumed Queue README (`docs/audits/runtime-rpc-consumed-queue/README.md`)

# Completed RPC queue records remain reachable

Status: reproduced before and after the product fix on Node 26 and installed Electron 43.7.0 in Node mode.

`RuntimeRpcCallQueuePool` advances each lane's head without clearing the consumed array element. While any call keeps that selector active, completed records keep their `run`, `resolve`, `reject`, and signal fields reachable until that same lane compacts. Compaction requires more than 32 consumed entries and at least half the array consumed. Selector deletion also releases the arrays when every active and queued call finishes.

The fix assigns `undefined` to the consumed slot in `takeForeground` and `takeBackground` and permits undefined in the two array element types. An active call remains owned by its promise cleanup closure. Queued entries, both lane heads, counts, admission thresholds, batching, foreground preference, and cancellation behavior stay intact.

## Production reachability

- Desktop `src/main/ipc/runtime-environment-call-queue.ts` owns a module singleton. `runtime-environment-transport-routing.ts::callRuntimeEnvironment` passes a closure capturing request params, environment, and optional orchestration envelope. Its normal timeout is 15 seconds; `status.get` bypasses this queue.
- Paired web `src/renderer/src/web/preload-api/web-runtime-session.ts` owns another module singleton. `web-runtime-calls.ts` and `web-filesystem-api.ts::captureWebFileMutationSession` pass closures capturing params, environment, and sometimes an explicit client. Web request timeout defaults to 30 seconds after connection readiness.
- Current production callers pass/default `retainedBytes` to zero. The nonzero byte values in the proof exercise the queue's accounting; they are not evidence that deployed callers account their object graphs here.
- These are remote/paired execution paths. The finding does not explain local-only #19831 from code reachability alone.

Finite individual call duration does not guarantee that an old lane's consumed records disappear: overlapping calls in the other lane can keep the selector active. The extended proof completes 70 successive background calls while eight completed foreground payloads stay reachable before the fix; each background call finishes, and releasing the final call makes the selector idle and permits collection.

This is retention beyond useful lifetime, bounded in record count by existing compaction/admission behavior. It is not proof of unlimited growth for one selector or of any reported incident's magnitude. The isolated payloads are bounded dummy arrays rather than a real network workload.

## Proofs

`reproduce.cjs` bundles the actual queue module with its actual imports. `queue-source.cjs` reconstructs the baseline in memory by reversing `fix.patch`; both baseline and current source hashes must match `source-versions.json`. Eight completed calls capture eight 1 MiB typed-array payloads, with one other call holding the selector active. Weak references remain live before and all clear after the fix. Foreground and background lanes, eventual compaction, and eventual idle cleanup are covered. Queue byte credit and queued-call count already equal zero during stale retention. At most eight payload MiB are intentionally live in each fixture; the process has a 128 MiB old-space limit and a ten-second deadline.

`extended-controls.cjs` checks active payloads are retained until their call settles, cancellation releases queued payloads without waiting for unrelated active calls, synchronous failure releases only after compaction/idle before the fix, rolling cross-lane traffic, and a 140-call mixed burst with six cancellations. Both variants execute the same 134 remaining calls in FIFO lane order with foreground priority, then delete the idle selector.

`resolver-controls.cjs` isolates the runtime's settled-promise resolver behavior without using the queue. Keeping native resolve functions can also keep settled results reachable in some runtime versions; this must be reported separately from input closures.

Node and Electron results are stored separately. Node 26.6.0/V8 14.6 collects fresh response payloads even with the stale queue records. Installed Electron 43.7.0/Node 24.21.0/V8 15.0 retains all eight fresh response payloads before and releases them after the fix. The standalone resolver control reproduces the same difference: saving native resolve functions retains eight of eight payloads in Electron and zero in Node; releasing those functions permits collection in both. This control isolates runtime promise behavior without claiming all Electron versions or browser renderer modes behave identically.

The original Node-only negative-control expectation for response retention failed under Electron. The baseline now records that result rather than assuming every V8 version releases settled results identically. The fixed variant must release responses in both environments. This proof does not measure the exact Electron 43.4.1 historical binary.

Run from the worktree:

```sh
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=128 docs/audits/runtime-rpc-consumed-queue/reproduce.cjs
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=128 docs/audits/runtime-rpc-consumed-queue/extended-controls.cjs
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=128 docs/audits/runtime-rpc-consumed-queue/resolver-controls.cjs
ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/shared/runtime-rpc-call-queue.test.ts src/shared/runtime-rpc-call-queue-retention.test.ts
```

The installed Electron binary can run the same scripts with `ELECTRON_RUN_AS_NODE=1`, `ORCA_BACKGROUND_LAUNCH=1`, and the same Node flags. No Electron app or window is created. No network, microphone, or affected-host data is used. No heap-snapshot tools are exposed in this session; the proof measures WeakRef reachability and bounded process counters instead of reading raw heap snapshots.

The existing eight queue tests and six added retention/lifecycle tests pass with the fix. To reproduce the three retention failures against the reconstructed baseline, use `ORCA_BACKGROUND_LAUNCH=1 node --expose-gc node_modules/vitest/vitest.mjs run --config docs/audits/runtime-rpc-consumed-queue/baseline.config.mjs src/shared/runtime-rpc-call-queue.test.ts src/shared/runtime-rpc-call-queue-retention.test.ts`; the expected outcome is 11 passing tests and three failures, with exit code 1. Node and Web project typechecks and the changed-code quality gate passed during promotion. Explicit basic/type-aware lint also covers these otherwise ignored audit scripts.

## Source identity

The queue source before the fix, fetched `origin/main`, and `v1.4.198` all had SHA-256 `45921658a10ffeefe0247673227aab643d1b5251d24aa460244fadd5480fc349` at review. `source-versions.json` records exact baseline/current hashes and compared commit IDs. Availability in that release supports reachability analysis; it does not attribute an incident.

---

### Audits Scanner Late Cancel README (`docs/audits/scanner-late-cancel/README.md`)

# AI Vault scanner late cancellation

The scanner child kept cancellation IDs after their requests had already settled.
Its response can still be in transit when the parent sends a cancellation, so this
does not require an invalid caller. The completed request has already run its
cleanup; nothing remains to delete the newly inserted ID.

The fix admits cancellation only while the existing `pending` set owns the request.
That set includes both queued and running requests. Their cancellation and cleanup
remain unchanged. No protocol or history-retention policy changes.

## Proof

Run from the repository root with dependencies installed:

```sh
ORCA_BACKGROUND_LAUNCH=1 node --max-old-space-size=128 docs/audits/scanner-late-cancel/reproduce.mjs
```

The script loads the checked-out production entry and derives the before version by
removing only the three-line `pending` membership guard in memory. It checks that
the guard occurs exactly once; no historical commit or Git access is needed.
Esbuild strips TypeScript before both versions run in separate VM contexts. Only
imported collaborators are stubbed: the production message handler, request lanes,
sets, and cleanup run. After each synthetic first-prompt request completes, its
matching cancel arrives.

The script asserts the counts and emits JSON with both source SHA-256 hashes and
Node/platform/heap-limit provenance. [results.json](./results.json) records a run on
Node v26.6.0 with a 128 MiB old-space limit. This measures retained entries, not RSS.

| Source | Requests/responses | Pending | Controllers | Retained cancel IDs |
| ------ | -----------------: | ------: | ----------: | ------------------: |
| Before |      1,000 / 1,000 |       0 |           0 |               1,000 |
| After  |      1,000 / 1,000 |       0 |           0 |                   0 |

The regression test imports the production entry and directly observes its existing
cancellation set through an admitted cancellation. It repeats late cancels after
both successful and failed requests, verifies queued/running cancellation, and
checks shutdown cleanup. No production diagnostics or test-only exports were added.

```sh
ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/ai-vault/session-scanner-service-cancellation.test.ts src/main/ai-vault/session-scanner-service-entry.test.ts src/main/ai-vault/session-scanner-service-client.test.ts
```

## Incident scope

The same unconditional insertion exists in release `v1.4.198`. This retains numeric
IDs in the scanner child, not transcript contents in Electron main. It is a concrete
small leak; it does not explain the reported roughly 26 MB/s main-process growth in
#19768 or establish the cause of #19831's scope-level peak memory measurements.

---

### Audits Session Search Write Fences README (`docs/audits/session-search-write-fences/README.md`)

# Session-search write fence retention

The search writer remembered every removed path for its lifetime. Those counters
fenced a read whose source disappeared before its first commit: both the original
and deleted database cursors are absent, so comparing cursors alone cannot detect
the removal. Counters for paths with no remaining reads were never released.

The fix tracks only active reads. Removal marks their captured lifetime as removed
and releases the path from the map immediately. New reads get a fresh lifetime;
cleanup from an older read cannot delete it. Final commit and explicit discard
release ownership, while intermediate chunk commits keep it. Consumer errors,
incomplete reads, throwing error reporters, and store close release their fences.

## Reproduce

From the repository root with dependencies installed and a Node version providing
`node:sqlite`:

```sh
ORCA_BACKGROUND_LAUNCH=1 node --max-old-space-size=128 docs/audits/session-search-write-fences/reproduce.mjs
```

The script bundles the actual writer twice, using the production SQLite schema and
adapter. The baseline is published commit
`243f4431557471daa05636aed1a30be790485eda` (#20551), whose writer matches the pre-fix
source. The after version is the working tree. Only this named Git object is read;
the script fetches nothing. Results include both source hashes and runtime details.

After 1,000 complete index/retire cycles:

| Source | Remaining file rows | Retained path entries | Never-indexed stale commit accepted |
| ------ | ------------------: | --------------------: | ----------------------------------- |
| Before |                   0 |                 1,000 | No                                  |
| After  |                   0 |                     0 | No                                  |

The regression suite additionally drives the actual consumer/channel path, checks
intermediate flush ownership, failed/incomplete reads, throwing error reporters,
new-generation protection, idempotent discard, and close:

```sh
ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/ai-vault-search/session-search-write-lifetime.test.ts src/main/ai-vault-search/session-search-file-write.test.ts src/main/ai-vault-search/session-search-index-writer.test.ts src/main/ai-vault-search/session-search-index-consumer.test.ts
```

Existing retry bookkeeping can recreate a failed `files` metadata row when a
removed read finishes. Its session/messages stay absent and it cannot restore
searchable content. This patch preserves that status policy.

This is current-code path metadata in the scanner child. The search writer did not
exist in `v1.4.198`; this finding does not explain the reported #19831/#19768 build.

---

### Audits Ssh File Metadata Retention README (`docs/audits/ssh-file-metadata-retention/README.md`)

# SSH file readers retain unrelated streams before metadata

The file reader queued every file-stream notification while awaiting its own metadata. A delayed read therefore retained payloads from other reads that had already completed. The fix installs metadata through the mux's existing synchronous `beforeResolve` callback and ignores notifications until the read has a stream identity. Listeners still register before the request, and own frames adjacent to the response are processed correctly.

This is conditional transient retention during a pending metadata request. The proof establishes a source mechanism and its correction; it does not identify an affected host, measure a natural native I/O stall, or attribute #19831 to SSH.

## Actual producer and ownership chain

1. Desktop `filesystem-read-handlers.ts` calls the selected `SshFilesystemProvider.readFile` for `fs:readFile`; this route does not serialize reads. Runtime previews also use the provider with caller-specific caps. An AI-vault scan has an eight-operation gate, which still permits repeated completions in other slots while one operation waits.
2. `readFileViaStream` subscribes to chunk/end/error notifications before sending `fs.readFileStream`. Previously it appended all such notifications until the metadata promise's `.then` callback ran, even when they belonged to other streams.
3. Relay `FilesystemHandler` forwards the path and request context to `readRelayFileStreamMetadata`. The producer awaits `stat` before acquiring a stream slot. For unknown MIME types, its prefix probe also precedes registration. After opening/registering the file, it schedules its pump with `setImmediate` and returns metadata.
4. `RelayDispatcher` publishes the small metadata response in its control lane; the writer prioritizes control before bulk. The saturated-writer control verifies metadata precedes chunks after drain.
5. The mux runs `beforeResolve` synchronously during response dispatch, before resolving the request promise. Its decoder can dispatch adjacent notifications before any `.then` callback runs. The fix installs the stream ID and buffer at that synchronous boundary, eliminating the need to save foreign frames.

The producer, mux, dispatcher, decoder, writer, file I/O, and stream registry are actual source in the portable proof. The fixture connects both ends through an in-memory duplex transport, uses real temporary files, and supplies the filesystem handler's small path/client/pacing adapter. It does not launch an SSH process, Electron window, native PTY, or network server.

## Bounds and payload sharing

- The relay allows **16 concurrent registered streams**, with a **four-chunk ACK window** per paced stream. Chunks are 256 KiB. A metadata operation waiting before registration occupies no stream slot. Other transfers can complete and reuse slots repeatedly.
- Reader size caps are **10 MiB text / 50 MiB binary**, optionally tightened by the caller. They apply after that reader's metadata and do not charge foreign history accumulated before it.
- The metadata request has a **30,000 ms deadline**. After metadata, the reader uses a **60,000 ms inactivity deadline**, reset by its own chunks and integrated with suspend/resume. Connection disposal also releases subscriptions. These timers and transport throughput bound ordinary retention duration; suspension/event-loop stalls can delay timers. No indefinite native stall was established.
- The decoder limits each turn to 64 frames / 4 ms and bounds retained framing bytes. Those limits do not bound arrays owned by subscribers after frames are parsed.
- The mux passes the **same parsed params object** to all subscribers. Four waiting readers add four wrappers per frame, but share its payload. The result is not four copied payloads or quadratic payload-byte growth.

## Comparative results

All **80 portable cases pass**: ten controls × baseline/fixed × audited-worktree/named-main graph × Node/Electron. Node is 26.6; Electron 43.7 uses Node 24.21. Reports record exact versions, all 59 selected source hashes, the observed reader hash, and proof artifact hashes.

| Observation                                                             |                            Before |    Fixed |
| ----------------------------------------------------------------------- | --------------------------------: | -------: |
| Four waiting readers; 16 completed 2 MiB transfers                      |                      576 wrappers |        0 |
| Unique shared params objects retained                                   |                               144 |        0 |
| Logical base64 bytes, counted once per unique params object             |                        44,739,584 |        0 |
| Peak registered streams in that workload                                |                                 1 |        1 |
| ACKs processed                                                          |                               128 |      128 |
| Reader history after metadata completion, handled disposal, or deadline |                          released | released |
| Actual pump with ACK delivery withheld                                  |              stops after 4 chunks |     same |
| Sixteen active streams, then a seventeenth request                      | refused; later admission succeeds |     same |
| Saturated writer, then drain                                            |        metadata before own chunks |     same |
| Response plus own chunk/end in one decoder turn                         |                    correct result |     same |
| Unpaced producer / ordinary completion                                  |                    correct result |     same |
| Canonical LF vs synthetic CRLF source/patch reads                       |                    66 reads agree |     same |

The primary portable workload deliberately gates four request handlers **immediately before invoking the actual relay file producer**. The request remains pending while other real transfers complete. This controlled adapter delay is distinct from a native `stat` already in progress; production source establishes that awaiting `stat` occurs at the same pre-registration phase. It does not measure how often or how long native metadata I/O delays occur on a user's machine.

The baseline observation adds only `WeakRef(pending)` to expose the closed-over array. It does not add a strong owner. Shared params identity is checked across all waiting readers. Heap deltas support the object/byte accounting but are neither exact object sizes nor RSS. After disposal, the test consumes lazy `Error.stack` and retains only error codes: externally retained unmaterialized V8 error stacks can themselves retain callback context, so the release claim is after normal error handling.

Every successful transfer checks payload length and SHA-256. Stream capacity, pacing, cancellation, and output assertions run identically for both variants. The controlled producer ignores ACK pacing in one case; this tests existing unpaced behavior, not every historical relay binary.

## Source graphs and publication

`source-versions.json` records the full 59-module import graph and five additional actual caller hashes. Both graphs select the same file-reader source variant. The audited worktree and named main `291b4ddd6f1c1af480169885e0fda7f9c78ff053` otherwise differ only in the previously published SSH writer consumed-prefix correction.

`main-context.patch` reconstructs that single context difference in memory. The loader accepts either of its two exact recorded checkout hashes and reconstructs the selected graph. This lets the same artifact run on this worktree or the independent main publication without depending on another memory PR. `fix.patch` is the separate, single-product-file change under review. Every other graph/caller source is hash-fenced; unknown production imports fail. Dedicated portable tests omit unrelated global Vitest setup files.

The current reader baseline and relay file producer are also byte-identical to `v1.4.198` (`e0826956fcfc532f5a1e55b5e081f2e57e553c43`). That named version has synchronous `beforeResolve` and control-first writer scheduling. Its comparison is limited to the recorded paths; the proof does not execute a whole historical application.

No wire field, opcode, host execution verdict, stream cap, timeout, fallback, or native process lifetime changes. Existing MethodNotFound fallback and malformed-metadata / tighter-cap / empty-image / adjacent-error handling are covered through the actual mux by the permanent regression suite.

## Reproduce

Choose either graph (`worktree` or `main`) and variant (`before` or `fixed`):

```sh
ORCA_BACKGROUND_LAUNCH=1 ORCA_SSH_READER_GRAPH=main ORCA_SSH_READER_VARIANT=fixed pnpm exec vitest run --config docs/audits/ssh-file-metadata-retention/vitest.config.mjs
```

For Electron, invoke the installed Electron binary with `ELECTRON_RUN_AS_NODE=1` and `ORCA_BACKGROUND_LAUNCH=1`, passing `node_modules/vitest/vitest.mjs` and the same arguments. Reports are separate for every graph/variant/runtime. Set `ORCA_SSH_READER_OUTPUT` to an alternative file path to preserve captured reports.

Permanent tests and the intentional baseline failure:

```sh
ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/main/ssh/ssh-filesystem-stream-retention.test.ts src/main/providers/ssh-filesystem-provider-stream.test.ts src/main/providers/ssh-filesystem-provider.test.ts src/main/ssh/ssh-channel-multiplexer.test.ts src/relay/fs-handler-stream.test.ts
ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config docs/audits/ssh-file-metadata-retention/before.config.mjs src/main/ssh/ssh-filesystem-stream-retention.test.ts src/main/providers/ssh-filesystem-provider-stream.test.ts
```

The baseline keeps all 64 observed foreign frame objects while metadata remains pending, causing exactly the new lifetime assertion to fail; the other 22 tests pass. The initial four-suite run passed 70 tests. Detailed quality/typecheck results are in `validation.json`. Full-file casting diagnostics are the same 15 inherited assertions in the original reader and provider test, verified by exact diagnostic/source-span comparison; the changed-code gate reports no new findings. No lint rule was suppressed and no unrelated wire validation behavior was changed to satisfy that baseline cleanup.

The expanded five-suite run passes **124 tests**, including the general provider suite. The empty-file control uses the existing streaming fixture, which invokes the mux's `beforeResolve` callback before resolving metadata, and verifies all stream listeners are released. The older generic fixture omitted that callback and reproduced the CI timeout; actual-mux empty metadata controls already passed. This correction changes test setup only.

---

### Audits Ssh Writer Consumed Prefix README (`docs/audits/ssh-writer-consumed-prefix/README.md`)

# Completed SSH writes retained behind a rolling backlog

The SSH multiplexer lane scheduler advanced its read index without clearing consumed entries. A lane that stayed nonempty retained every completed `WriterEntry`, its encoded buffer and its settlement callback. The writer had already released those entries from its byte and frame counters, so admission limits did not bound this consumed prefix. Fully draining the lane or disposing the multiplexer released it.

The fix clears the selected slot and compacts a consumed prefix after at least 1,024 selections when it occupies at least half the array. This follows the existing `RelayFrameBuffer` pattern. Lane ordering, fairness, admission limits, transport settlement and in-flight ownership are unchanged. `clear()` returns only remaining live entries.

## Actual caller and ownership

The ordinary-lane proof executes `writeToSshPtyWithSettlement` → `SshChannelMultiplexer.notifyWithSettlement` → `SshMultiplexerTransportWriter` → `SshMultiplexerWriterLaneScheduler`. `SshPtyProvider` exposes the same helper through its RPC operations. The control-lane proof uses the `git.responseAck` notification shape emitted by `requestGitStreamable`.

`ssh-relay-deploy-helpers.ts` connects transport writes and settlement callbacks to `channel.stdin.write`, and registers its `drain` event. A producer that keeps at least one queued entry behind repeated backpressure/drain cycles reaches the retained-prefix state. The proof exercises both a controlled callback/drain port and a real Node `Writable` with a deferred write callback and a synthetic 16 KiB high-water mark. No SSH connection, app, window or remote process is launched.

Selecting an entry transfers scheduler custody to the writer's in-flight set. Clearing its consumed queue slot does not complete the write. A separate control disposes with an in-flight and a queued write: their existing results remain `unverifiable` and `refused`, respectively. The native callback can still retain the in-flight buffer until that callback reference is released. Late and duplicate callbacks do not settle it twice.

## Results

Both captured runtimes produce the same counts: Node 26.6 and Electron 43.7 / Node 24.21. Each runs six scenarios before and after the fix.

| Scenario at the controlled pause                                        |   Original |   Fixed |
| ----------------------------------------------------------------------- | ---------: | ------: |
| Ordinary lane: completed buffers and settlement callbacks retained      | 2,048 each |       0 |
| Control lane: completed buffers and settlement callbacks retained       | 2,048 each |       0 |
| Physical queue slots after those selections                             |      2,050 |       2 |
| Logical queued ordinary frames / bytes                                  |    2 / 706 | 2 / 706 |
| Logical queued control frames / bytes                                   |    2 / 184 | 2 / 184 |
| Real writable: retained written buffers, including one in flight        |        128 |       1 |
| In-flight buffer retained after disposal while native callback is owned |          1 |       1 |
| Written buffers retained after complete drain or final callback release |          0 |       0 |

The real-writable scenario completes 128 writes, starts the 129th and keeps two further writes queued. The first write preceded the rolling backlog and is collectible in both variants; the original therefore retains 127 completed buffers plus the in-flight one. Its logical budget is three frames / 49,440 bytes in both variants. Empty slots below the compaction threshold are expected and do not retain those buffers.

The scenarios assert FIFO order, isolation of ordinary/control counters, full-drain and disposal cleanup. Five permanent lifetime regressions plus 26 existing tests pass. The original-source overlay fails the four rolling-backlog regressions and passes the other 27 tests. Existing tests cover control priority and starvation prevention, liveness bypass, synchronous drain, callback errors and duplicates, overflow, disposal, timeouts and slow-but-live transport handling. See `validation.json` for commands and full-publication quality checks.

## Reproduce

From the repository root after dependency installation:

```sh
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/ssh-writer-consumed-prefix/reproduce.cjs
```

For Electron, run the installed Electron executable with `ELECTRON_RUN_AS_NODE=1` and `ORCA_BACKGROUND_LAUNCH=1`, passing the same flags and script path. This is a Node-mode process with no UI. The runner has a 20-second deadline.

An optional final argument selects the report destination, for example `notes/ssh-writer-consumed-prefix/reviewer-node.json`. Without it, the runner refreshes the corresponding artifact `node-results.json` or `electron-results.json`.

`sources.cjs` reverses `fix.patch` in memory and verifies both original and fixed SHA-256 hashes. It also verifies all nine bundled source dependencies against the recorded hashes. No source file is rewritten. A synthetic CRLF read of the patch and source must reproduce identical canonical LF sources and hashes. `before.config.mjs` uses the same source loader for the original-source test overlay.

## Source compatibility and scope

`source-versions.json` records the exact scheduler baseline at the pre-fix audit commit, independent main `291b4ddd6f1c1af480169885e0fda7f9c78ff053`, and v1.4.198 `e0826956fcfc532f5a1e55b5e081f2e57e553c43`. These scheduler sources are byte-identical after LF normalization, so the same patch yields the same fixed hash. All nine bundled sources and three additional caller sources match independent main. The projected main change has no dependency on the other memory-audit fixes.

The v1.4.198 scheduler is identical, and its writer contains the same enqueue/select/release path. Seven of the twelve dependency/caller files differ from current source; this artifact does not claim to execute the packaged historical release.

Inputs and timing are controlled fixtures. Reachability counts establish the code-level retention mechanism; they do not measure affected-host RSS, model a reported growth rate, or establish that an incident had a continuously nonempty SSH write lane. Active pending writes, transport-owned callbacks and retained primitive sequence timestamps remain governed by their existing limits and lifecycles.

---

### Audits Stale Pty Inventory README (`docs/audits/stale-pty-inventory/README.md`)

# Stale provider inventory overwrites newer PTY ownership

A provider inventory started before a process exit can finish afterward and reconnect the exited runtime record. If a replacement was admitted under the same ID, the old response can also overwrite its incarnation, invalidate its terminal handle, and clear its pane binding.

This is a separate source-level explanation for stale terminal ownership such as [#19018](https://github.com/stablyai/orca/issues/19018). The proof does not establish the cause of [#19768](https://github.com/stablyai/orca/issues/19768) or [#19831](https://github.com/stablyai/orca/issues/19831), or measure their reported memory growth.

## Reproduce

From a checkout with dependencies installed:

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/stale-pty-inventory/reproduce.mjs
```

The script runs the actual runtime inventory, registration, spawn, and exit methods with deferred provider responses. It uses temporary Vitest configuration to reverse only `fix.patch` for the baseline, leaves checkout files untouched, and removes its temporary files. It launches no app, enumerates no real processes, and installs nothing. Node subprocesses use the repository's portable `runProcess` implementation.

`results.json` records source hashes and the before/after result: **16 failing / 6 passing tests before; all 22 passing after**.

## Retaining and ownership path

1. `refreshPtyWorktreeRecordsWithControllerInventory` starts a provider request and captures its generation and liveness observation sequence.
2. While the response is pending, `onPtyExit` records a physical exit. A subsequent `registerPty` or `onPtySpawned` may admit a successor, including one absent from the runtime when the request began.
3. The original inventory returns its older row. Previously, the verdict setter's observation check did not protect the rest of the row processing: handle adoption ran, and `recordPtyWorktree` wrote `connected: true` and the old incarnation.

For an exit without replacement, the record could therefore be connected while its verdict still said `exited`. A later empty listing skips the disconnected-record sweep while a graph leaf remains. In the successor case, `adoptControllerTerminalHandle` can invalidate the new handle before the old incarnation is written. Fresh positive inventory can restore the successor handle and incarnation; it cannot reconstruct cleared pane coordinates without binding evidence.

The proof exercises these mutations directly. It does not recreate a native process or a headless terminal model. Consequently, it demonstrates incorrect ownership and potential retained records, rather than a quantified heap or native-process leak.

## Fix and controls

Accepted spawn, registration, and exit events now invalidate pending inventory for their provider through the existing provider generation registry. The existing stale-response check runs before handle adoption, record mutation, or construction of an authoritative live-ID result.

The response is rejected as a whole, so filtered rows cannot become false evidence of absence. A targeted worktree read retries at most once using its original deadline; another invalidation yields an unknown result. A targeted different-host query remains valid. Aggregate requests conservatively reject when a provider changes during the request. This can reject a response that happened to include the newly admitted process; a subsequent fresh request remains authoritative.

The tests cover stale positive rows, stale absence, unknown-at-request-start spawns, registrations with no incarnation, ignored predecessor EXIT, exported handle preservation, fresh discovery and replacement, local/SSH routing (including SSH aliases with spaces, `@`, and `:`), concurrent provider generations, and bounded retries. Existing partial-relay and liveness tests verify that failed contact is not promoted to process death. The change adds no permanent per-PTY registry and changes no wire fields or liveness vocabulary.

## Version and scope

The relevant await, guarded verdict update, unconditional handle/record updates, and spawn/registration verdict deletion exist in **v1.4.198**. That version forgot the prior verdict on a positive inventory row; current code records `live`. Both versions leave subsequent mutation outside that observation fence. The executable baseline is current source with the narrow fix reversed, not a historical application binary.

The graph-publication fence is a separate fix. Later loss-of-contact writes and independent paths that query provider inventory directly remain outside this patch. Incident frequency and the reporting machines' actual ordering are unproven.

---

### Audits Structured Hold Retention README (`docs/audits/structured-hold-retention/README.md`)

# Structured session hold lost during resume

Run from the repository root:

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/structured-hold-retention/reproduce.mjs
```

The script bundles the actual `StructuredAgentSessionHolds` implementation into temporary CommonJS
modules, loads them normally, and removes their files and module-cache entries. It runs the code
once without the post-resume holder check and once with the current source. It uses a deferred
provider acquisition, an isolated fake child, and a 5 ms release grace. It launches no application,
provider, or terminal and reads no user profile.

The last surface releases its hold while acquisition is pending. At that point the session has no
provider child, so `release()` cannot arm the release clock. Before the fix, acquisition completes
with a child, zero holders, and no scheduled eviction. With the fix, successful acquisition checks
for surviving holders and schedules the existing release clock. The recorded child is released once.

The RPC path registers connection cleanup before awaiting `host.hold()` in
`src/main/runtime/rpc/methods/structured-agent-session-hold.ts`. Runtime socket close calls
`cleanupSubscriptionsForConnection()` in `runtime-rpc/runtime-rpc-lifecycle.ts`. That supplies the
production release-during-acquisition ordering reproduced here.

## Ownership limits

- This proves a lifecycle race, not that it caused any particular OOM report. No process RSS was
  measured. It applies to structured sessions acquiring a provider child, not ordinary PTY tabs.
- The release clock preserves its 15-second production grace, waits while a turn is active, and
  cancels when a new holder arrives. Acquisition failures retain their existing handling.
- Disposal prevents late acquisition or release callbacks from restarting the clock. Host teardown
  owns cleanup after disposal. The host's broader pre-attach shutdown admission is outside this fix.
- A restored childless journal is not necessarily abandoned. Startup selects persisted visible
  tabs; `host.sessions` supplies `listSessionTabs()`, and childless sessions can retain live TUI
  owners. `host.close()` closes that TUI owner before removing the journal. This fix neither evicts
  childless history nor infers process exit from transport loss.
- Execution remains on the owning runtime, with no wire or SSH routing changes.

Targeted regressions live in
`src/main/native-chat/agent-session-wire/structured-agent-session-hold-resume-race.test.ts`.
They cover last-holder loss, active turns, new holders, reconnection, failed acquisition, explicit
close, and disposal.

Same-ID replacement is fenced at both ownership layers. Holder entries receive a new incarnation
after release and re-add, so an old failed acquisition cannot remove a replacement. The RPC uses
the subscription registry's `releaseIfCurrent()` cleanup, so its failure cannot unregister the
replacement's connection cleanup. Duplicate adds remain one holder. Class tests and real host/RPC
tests cover the old failure arriving before and after replacement success; disconnect still releases
the replacement normally. They also cover the reverse outcome: an old acquisition succeeds and the
replacement refuses a stale fence. Its last-holder rollback starts the same turn-aware release clock
for the acquired child. No RPC fields or published frame shapes change.

---

### Audits Terminal Close Observed Exit README (`docs/audits/terminal-close-observed-exit/README.md`)

# Preserve an observed exit during explicit terminal close

An explicit close can receive the target daemon's physical EXIT, then fail its aggregate verification because another preserved daemon is unavailable. The close method used to invoke the fallback kill even though the runtime already held an `exited` verdict. That redundant request emitted a synthetic `-1`, replacing `operator_close` with `unknown/stop_unverified` and sending a second renderer exit notification.

The fix captures the stamped PTY incarnation before awaiting the stop. A false stop result is accepted only when the same incarnation remains current and the runtime already has an `exited` verdict. It does not create an exit certificate from an empty inventory or transport failure.

## Reproduce

From the checkout, with dependencies already installed:

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/terminal-close-observed-exit/reproduce.mjs /tmp/terminal-close-observed-exit.json
ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/runtime/terminal-close-observed-exit.test.ts
```

The script runs eight scenarios before and after the change, reversing only the new capture and guard for the before variant. It uses the actual runtime close method, runtime controller, daemon router, and two real daemon socket endpoints. The subprocess itself is controlled by the existing test harness. The script uses temporary configuration files, checks the expected outcomes, records source hashes, and removes its temporary directory. It does not install dependencies, launch a UI, or alter the checkout. `results.json` preserves the recorded result; use a separate output path when rerunning.

| Scenario                                                    | Before                                                                                      | After                                                                                             |
| ----------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| Target physical EXIT received; unrelated daemon unavailable | Close returns false; one redundant kill; cause overwritten; two renderer exit notifications | Close returns true; no redundant kill; `operator_close` preserved; one renderer exit notification |
| Healthy aggregate inventory, delayed physical EXIT          | Close succeeds                                                                              | Unchanged                                                                                         |
| Target socket paused; unrelated daemon unavailable          | Close remains unverifiable despite target's empty inventory                                 | Unchanged                                                                                         |
| Same stamped incarnation already exited                     | Redundant fallback kill                                                                     | Existing exit accepted                                                                            |
| Same raw ID registered with a newer incarnation             | Old certificate rejected                                                                    | Unchanged                                                                                         |
| Synthetic negative exit, no host exit certificate           | Close remains unverifiable                                                                  | Unchanged                                                                                         |
| Unstamped legacy session                                    | Certificate not reused                                                                      | Unchanged                                                                                         |
| Stop throws after exit                                      | Catch records unverifiable                                                                  | Unchanged                                                                                         |

In all socket scenarios, the physical provider event and runtime exit listener settle once. The fixed observed-exit case has no headless model or title tracker retained. This proof measures lifecycle behavior, not retained heap bytes.

## Dependency and incident limits

This change is stacked on [#21000](https://github.com/stablyai/orca/pull/21000), branch `np-oom-scan-daemon-late-exit`, and reuses its actual daemon socket fixture and late physical-exit reconciliation. #21000 fixes final DATA arriving after a synthetic exit. This change prevents a redundant synthetic exit after a physical exit has already been accepted. The before variant is the current checkout with this narrow guard reversed, not a pristine historical build.

The unconditional fallback and exit-cause assignment are present in the reported `v1.4.197` source (`orca-runtime-stop-explicitly-closed-tab-ptys.ts`, `orca-runtime-on-pty-exit.ts`). They explain a concrete way to get a failed close and `stop_unverified` despite a confirmed local exit. [#19018](https://github.com/stablyai/orca/issues/19018) does not establish that an unrelated preserved daemon was unavailable; this is a conditional explanation, not proof of the reporter's exact ordering.

Generic inventory remains fail-closed. Exact-owner verification across daemon generations is separate work. This change does not solve a thrown stop, SSH loss of contact, unstamped identities, or all same-ID shutdown races. A missing diagnostics row remains insufficient evidence of process death.

---

### Audits Terminal Completed Spawn Inputs README (`docs/audits/terminal-completed-spawn-inputs/README.md`)

# Completed terminal spawns retain consumed inputs

Status: reproduced against actual `TerminalHost`, `Session`, output pipeline, and daemon admission code on Node 26.6.0 and installed Electron 43.7.0 / Node 24.21.0. The fix releases completed request objects and consumed history seed arrays while the terminal remains alive.

## Retaining paths and fix

Three long-lived callbacks kept spawn-only input objects reachable:

1. `terminal-host-session-create.ts::spawnAndPublishSession` gave `Session` an exit callback capturing the complete request and dependencies. A small factory now captures only the exit callback, session ID, and agent-session generation.
2. `TerminalHost.createOrAttach` constructed that exit callback beside the cancellation check that captures the request. Their shared lexical context kept the request reachable even after the first capture was projected. The unchanged exit/reap body now lives in a method bound to its host.
3. `session-output-pipeline.ts` captured pipeline options in its foreground-confirmation callback. Those options include history chunks that `SessionOutputPlane` has already consumed synchronously. The callback now captures the subprocess object; the liveness callback is also extracted before constructing the pipeline.

The subprocess remains the receiver of `subprocess.confirmShellForeground?.()`. The only production provider of the exit callback is `TerminalHost`; its bound method preserves the host receiver. Exit codes, incarnation tombstones, claimed-generation release, reaping, cancellation, and process ownership follow the same paths. A constructor-only `maxTombstones` field was removed to keep `TerminalHost` within the existing line limit; the registry receives the same configured/default value directly.

## Production reachability and limits

- `daemon-provider-init.ts::initDaemonPtyProvider` installs the local daemon adapter. The cold-restore path in `daemon-pty-spawn-result.ts` supplies recovered history to terminal creation. `daemon-server.ts` owns the host and admission objects; `daemon-request-router.ts:59` routes `createOrAttach` to admission.
- `daemon-terminal-admission.ts:90` obtains inline history or takes completed transfer chunks, then passes the chunks, environment, and cancellation inputs into the host at line 96. `session-output-plane.ts:63` consumes all seed chunks into the emulator and retains the success flag.
- `terminal-history-seed-transfer-registry.ts:97` removes a completed transfer from its map and byte accounting when handing its chunks to creation. Its pending-transfer limits therefore do not bound the aggregate of already-consumed seeds retained by live sessions. The configured checkpoint maximum is 200,000,000 bytes, but these proofs use tiny seeds and do **not** measure a 200 MB allocation or incident-sized RSS.
- Retention lasts for the live session. Disposal permits collection even before the fix. This is avoidable retention per live terminal, not proof of unlimited growth after successful teardown.
- Real admission stream callbacks still keep preparation/signal metadata while attached: the routed-session getter shares the admission context with its cancellation callback (`daemon-terminal-admission.ts:117–122`). The admission control confirms those objects collect after public `host.detach` with the fix. This patch does not change that attached-stream lifetime.
- Native `pty-subprocess/subprocess-handle.ts:48–60` still captures its spawn arguments through the exit-status callback, including its merged environment object. Collection of the original request environment object does not prove all copied environment strings disappear from a real native process owner. The proof injects an inert subprocess and does not measure native allocations.
- The daemon path can run locally and on execution hosts used remotely. No wire fields or messages change, and folder workspaces require no special behavior. The finding is compatible with a local application memory report such as #19831, but no affected-host process/heap evidence establishes that the incident used this restore path or that it explains the reported magnitude.

## Reproduction and controls

`spawn-source.cjs` bundles actual source and reconstructs the baseline in memory by reversing `fix.patch`. SHA-256 checks fence both versions of all three changed modules using `source-versions.json`. The loader accepts the exact audit-branch pair and the exact independent-main publication pair; all other source hashes fail. Reports contain hashes of the source actually evaluated. Dependencies remain actual worktree code. Only the OS descendant-kill port is replaced with a throwing guard; subprocess handles are small injected objects, with no real shell, socket, process signal, or network activity.

`reproduce.cjs` measures weak references to request, environment, history array, and cancellation signal objects. It also tests pending ownership, actual exit/reap and claimed-generation replacement, retired-incarnation exit evidence, and foreground confirmation with the correct subprocess receiver and queued prompt delivery.

| Check                                                     | Baseline                        | Fixed                |
| --------------------------------------------------------- | ------------------------------- | -------------------- |
| Three completed requests while three sessions remain live | 3 of each input object retained | 0 of each retained   |
| One request during unresolved spawn                       | All four input objects retained | All four retained    |
| That request after publication                            | All four retained               | All four collectible |
| Inputs after disposal                                     | All collectible                 | All collectible      |
| Exit/reap, new incarnation/generation, shell confirmation | Pass                            | Pass                 |

`admission-control.cjs` exercises actual daemon admission and preparations above the actual host. A forwarding observer stores only weak references. Transport, attachment bookkeeping, and native subprocess ports are inert. Both runtimes reproduce the following:

| Admission phase                             | Original options/env/history | Preparation/signal | Request/payload |
| ------------------------------------------- | ---------------------------- | ------------------ | --------------- |
| Baseline, attached or detached live session | Retained                     | Retained           | Collectible     |
| Fixed, attached live session                | Collectible                  | Retained           | Collectible     |
| Fixed, detached live session                | Collectible                  | Collectible        | Collectible     |
| Either version after disposal               | Collectible                  | Collectible        | Collectible     |

The seeded snapshot remains readable after collection. These object reachability checks establish specific removed retaining paths; they do not establish total memory released. No heap-snapshot tool was exposed in this session. The historical Electron 43.4.1 binary was not tested. Each process uses a 192 MiB old-space limit and a 15-second deadline.

Run from the worktree:

```sh
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/terminal-completed-spawn-inputs/reproduce.cjs --baseline
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/terminal-completed-spawn-inputs/reproduce.cjs
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/terminal-completed-spawn-inputs/admission-control.cjs
ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/daemon/terminal-host-spawn-input-retention.test.ts
```

For Electron, run the same proof scripts with the binary returned by `require('electron')`, the same Node flags, `ELECTRON_RUN_AS_NODE=1`, and `ORCA_BACKGROUND_LAUNCH=1`. This starts no application or window. Node and Electron reports are stored separately in this directory.

The four permanent regressions pass with the fix. The reconstructed baseline deliberately fails the two retention regressions and passes both lifecycle controls: `ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config docs/audits/terminal-completed-spawn-inputs/baseline.config.mjs` exits 1. Existing host, concurrent create, teardown/recreate, reaping, agent ownership, preflight replacement, and history restore tests also pass: 67 tests across nine files. Node typecheck and the changed-code quality gate passed; explicit basic/type-aware lint includes the audit scripts.

## Source identity and compatibility

`source-versions.json` records the exact audited branch baseline, fixed hashes, previously reviewed main commit `77cd61df396f25ec91ee2d5ddcbd1f55aa94f818`, release `v1.4.198` commit `e0826956fcfc532f5a1e55b5e081f2e57e553c43`, and independent publication main commit `291b4ddd6f1c1af480169885e0fda7f9c78ff053`. The create and pipeline files exactly match these historical baselines. Historical `TerminalHost` differs only in the unrelated producer pause/resume source parameter from #20947 on the audit branch. This fix applies independently and does not require #20947.

The supported `TerminalHost` SHA-256 pairs are audit baseline `8cd6804b249ffb0d82da7f5a7ec8faa66514b0d6e3e36472cd81e6409c3edbc4` → fixed `23cfd9c6317db30edd48a0c8bd7c54658206654703461cf12295274b23fb8844`, and independent-main baseline `5216562b3c10c5fce4238614dc3a81887c970359a60e7e23179c6dfa15ccbcca` → fixed `f22856504559a6bb78498c6d5aae07cbbd80a21019723278a560628e0142236f`. Each selected fixed source is reverse-patched and checked against its own paired baseline hash.

The four permanent tests pass when the three patched main modules are overlaid on current dependencies. The six `mapped-*.json` reports repeat both runtime proofs and admission controls using the exact patched publication-main modules. They report the main hashes actually evaluated. This is a narrow compatibility check with working-tree dependencies, not a full historical application build. An optional `ORCA_SPAWN_INPUT_PROOF_SOURCE_MAP` points to a JSON object from these three relative source paths to exact reviewed fixed-source strings; unknown or incomplete mappings fail the same hash checks. With no mapping, the loader checks the published checkout directly. Mapped runs write separate reports prefixed `mapped-`.

Cancellation wait/listener findings from the preceding audit remain diagnostic and are outside this patch. The independent admission review narrowed the signal/environment claims before publication.

---

### Audits Terminal Mode Tail Retention README (`docs/audits/terminal-mode-tail-retention/README.md`)

# Retained terminal mode scan tails

The kitty keyboard tracker and daemon mouse-mode mirror retain an incomplete
escape-sequence tail of at most 4,096 UTF-16 code units. A V8 sliced string can
keep the entire consumed input alive through that small tail. An ordinary
split grouped mode sequence, `ESC[?1049;2004;1000;`, is enough: its 18-character
tail retains each input backing string while its parser stays idle.

The correction copies only accepted incomplete tails through the existing
`ownRetainedString` helper. Empty/rejected tails and short ESC/CSI prefixes keep
their existing behavior; the helper leaves strings shorter than 13 code units
alone. Parser state, live/replay semantics, stack caps, mode flags, and wire
content are unchanged. These are additional boundaries in
[#20960](https://github.com/stablyai/orca/pull/20960), alongside the
[PTY detector carries](../pty-detector-retention/README.md).

## Reproduce

```sh
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/terminal-mode-tail-retention/reproduce.cjs
```

Run the same script with the installed Electron executable, setting
`ELECTRON_RUN_AS_NODE=1` and `ORCA_BACKGROUND_LAUNCH=1`, and passing the same
Node flags. This launches no app window or native PTY. Each run has a 30-second
deadline and writes either [Node results](./node-results.json) or
[Electron results](./electron-results.json).

The loader reads the actual five source modules, verifies their fixed hashes,
reverses only the three copy calls/imports in memory for the baseline, and
verifies the resulting baseline hashes. All evaluated module and bundle hashes
are recorded. It needs no Git history, absolute development paths, or ignored
notes. CRLF source text is normalized before hashing. The parsers and flag
parser match `v1.4.198`; all five modules match the pre-extension topic
`8d599520e44654a5c28e9930e3070c00d6499931`, except for these copy calls. This
tests current dependencies and the selected source modules, not a historical
application binary. See [source versions](./source-versions.json).

Each runtime checks 42 bounded heap cases: baseline, fixed Buffer copier, and
fixed Bufferless copier; kitty live/replay, mouse live; 32 distinct 64-Ki-character
inputs and eight 4-Mi-character inputs; short, complete, oversized, and C1-CSI
tail controls. Completion must reconstruct the correct modes and release the
large backing strings. Additional controls preserve replay push idempotence,
the 16-frame live stack cap, alternate-screen state, snapshot unknownness,
mouse encodings, and RIS with a trailing partial sequence.

Both runtimes pass all 42 cases. Representative live-path heap deltas in bytes:

| Runtime               | Parser | Input × owners |   Baseline | Buffer copy | Bufferless copy |
| --------------------- | ------ | -------------- | ---------: | ----------: | --------------: |
| Node 26               | Kitty  | 64 Ki × 32     |  2,120,952 |      18,376 |           9,480 |
| Node 26               | Mouse  | 64 Ki × 32     |  2,111,984 |      15,112 |           9,336 |
| Node 26               | Kitty  | 4 Mi × 8       | 33,557,944 |       3,448 |           3,448 |
| Node 26               | Mouse  | 4 Mi × 8       | 33,557,072 |       1,312 |           1,312 |
| Electron 43 / Node 24 | Kitty  | 64 Ki × 32     |  2,111,864 |      12,244 |           5,192 |
| Electron 43 / Node 24 | Mouse  | 64 Ki × 32     |  2,103,444 |      14,432 |           8,004 |
| Electron 43 / Node 24 | Kitty  | 4 Mi × 8       | 33,556,316 |       1,884 |           2,604 |
| Electron 43 / Node 24 | Mouse  | 4 Mi × 8       | 33,556,172 |         776 |             752 |

Heap readings include owner overhead and follow forced GC. The harness clears
V8's last successful regexp input identically in baseline and fixed cases to
isolate per-owner storage. That independent process-wide regexp reference can
keep a most-recent input alive until another successful match; this change does
not eliminate it. The Bufferless selection is memoized while Buffer is absent,
then Buffer is restored before measuring; it exercises the actual renderer
fallback without running a browser renderer.

Two permanent kitty heap regressions fail before the correction at 33,560,832
and 33,575,040 retained bytes against a 2-MiB limit. They also verify that the
retained prefix completes correctly and that replay/pop/snapshot state remains
valid. Existing parser and copier tests provide the wider protocol controls.
The two mouse regressions likewise fail before the correction at 33,559,240 and
33,573,200 bytes, and pass afterward with both CSI encodings. The five-suite
run passes 98 tests, including actual headless-emulator mode snapshots; Node
and renderer TypeScript checks pass.

## Callers and lifetime

- Kitty renderer panes create or reuse one tracker per pane in
  `connect-pane-pty.ts:160`. `write-pty-output-to-xterm.ts:23` feeds application
  output; `apply-reattach-payload.ts` and `hidden-output-seq-and-skip.ts` feed
  replay. Fresh spawn and exit reset it, and
  `terminal-pane-pane-closed.ts:69` deletes the map entry. Dashboard previews
  own another tracker per effect (`AgentTerminalPreview.tsx:116`); cleanup
  removes its listeners and disposes its terminal.
- Main's `orca-runtime-capture-provider-terminal-buffer.ts:23` registers
  temporary live scanners during provider snapshot acquisition and removes
  them in `finally`. It creates a persistent tracker only after observing an
  alternate-screen transition (`:48–57`). `orca-runtime-on-pty-data.ts:30`
  feeds those trackers before later output processing. Exit, floating PTY
  liveness cleanup, and provider generation reset delete the persistent entry.
- The daemon does not directly instantiate the kitty tracker, despite its
  old class comment: its kitty flags come from xterm. No mobile bundle imports
  this class. Mobile can exercise main-side snapshot acquisition; SSH output
  can reach main and renderer trackers through the existing provider routes.
- Mouse mirrors are owned by `HeadlessEmulator` (`headless-emulator.ts:59`).
  Async writes scan after xterm parses the data (`:190`); synchronous live and
  cold-restore writes scan at `:224`. Both daemon sessions and main's headless
  projections use this emulator. It therefore also covers local/remote host
  emulators serving mobile clients. Emulator disposal stops future writes;
  eventual owner release removes the mirror. Completing/replacing its tail
  also releases the old backing string. No ownership or shutdown rule changes.

## Scope and limits

This is a per-owner last-input cost. It does not grow indefinitely with a fixed
set of parsers and bounded input chunks, and further output often completes or
replaces the tail. Multiple readers of the same input can share its backing
storage; do not sum their measurements as independent process memory.

Ordinary daemon bulk frames delivered to main are at most 64 Ki characters
(`daemon-stream-data-batcher.ts:35`), and ordinary relay output slices are
16 Ki characters (`relay/pty-handler.ts:343`). Mouse scanning inside the daemon
happens before outgoing stream framing. The 64-Ki cases demonstrate the issue
at a normal main-input bound; the 4-Mi cases amplify the mechanism, not a claim
that ordinary native reads or daemon frames have that size. Replay inputs and
transformed streams follow their own existing limits. No network, application
renderer, operating-system PTY, or incident heap was used in this proof.

This reduces retained output in local and SSH paths without changing published
terminal content. It neither establishes the trigger in #19831/#19768 nor
explains a reported sustained growth rate or multi-gigabyte incident by itself.

---

### Audits Terminal Wait Leading Blank README (`docs/audits/terminal-wait-leading-blank/README.md`)

# Terminal wait tail-window termination

`startOfLastNonBlankLines` loops indefinitely if its input begins with a newline and contains fewer nonblank rows than requested. Once the backward cursor reaches zero, JavaScript `lastIndexOf` clamps its negative start position to zero and rediscovers the same first newline. The cursor stops advancing.

The fix ends the scan when the cursor reaches zero and returns the existing short-tail offset, zero. It changes no prompt patterns or readiness rules. The ordinary finite-window selection cases retain their previous offsets.

## Reproduce

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/terminal-wait-leading-blank/reproduce.mjs > /tmp/orca-terminal-wait-leading-blank.json
```

The script bundles actual source and reverses only the two-line termination change for the baseline. Each case runs in an isolated child with a two-second deadline and 128 MiB heap limit. The parent confirms child termination; no app windows open. Five direct helper/detector cases time out before and return after. A sufficient-row control and actual headless terminal projection controls pass in both variants. Results include source hashes, platform, timing, and an explicit v1.4.198 helper comparison.

The regression suite uses a separate child for inputs that could hang the worker. It also tests exact row offsets with intervening whitespace, trailing blanks and tails shorter than the requested window. Fifty helper/detector tests pass.

## Production and incident limits

The helper is byte-identical in v1.4.198. However, all inspected main/provider/renderer visible-screen projection routes pass through `visibleNonBlankTerminalLines`, and ordinary retained-tail construction removes blank rows too. The real headless producer control confirms this filtering. Calling the public detector directly with a leading newline is therefore insufficient evidence that those production routes trigger the defect.

A clipped 300-character preview can start at a newline. The preview fallback also preserves it when passed empty retained rows; the proof records both facts. It does not establish an actual application lifecycle that combines that fallback with a live detector call. That remains unproven.

This is a defensive termination fix found during the memory audit. The loop itself does not allocate a growing collection. No memory magnitude was measured, and it is not an attribution of #19768's main-process growth or #19831's application-scope OOM.

---

### Audits Transcript Record Retention README (`docs/audits/transcript-record-retention/README.md`)

# AI Vault oversized transcript records

The local/WSL incremental JSONL reader accumulated every chunk of a newline-free
record, then concatenated and decoded the whole record at newline or EOF. Its
piece list avoided quadratic copying but did not bound memory. Both the reader
and the scanner child's 384 MiB V8 old-space setting exist in `v1.4.198`; that
heap setting does not impose a 384 MiB process RSS cap on external buffers.

The fix shares the existing streamed remote reader's 10 MiB record limit and
checks local bytes before retaining, concatenating, or decoding an oversized
record. It rejects that session read through the existing scan-issue path; it
does not silently discard records or advance the persisted resume point after
failure. A legitimate record larger than 10 MiB is therefore unavailable in that
scan. Large files containing many smaller records continue to work. This covers
the resumable JSONL route, not every whole-document/import reader.

## Reproduce

```sh
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc docs/audits/transcript-record-retention/reproduce.mjs
```

The script writes a 64 MiB synthetic record in a temporary local file and bundles
the actual reader. Only the filesystem adapter is replaced with the equivalent
native `createReadStream` route plus a byte counter. The baseline removes the
three new budget checks in memory. Each case runs in its own Node process with
`--max-old-space-size=384`; both terminated and unterminated records are tested.
Temporary files are removed. [Results and bundle hashes](./results.json) were
captured on macOS with Node v26.6.0.

| Case                 | Bytes read | External allocation delta after read |   Peak RSS |
| -------------------- | ---------: | -----------------------------------: | ---------: |
| Before, unterminated | 67,108,864 |                          201,303,426 | 248.25 MiB |
| After, unterminated  | 10,551,296 |                           10,528,130 |  62.13 MiB |
| Before, terminated   | 67,108,865 |                          201,368,963 | 248.22 MiB |
| After, terminated    | 10,551,296 |                           10,528,130 |  62.16 MiB |

RSS includes runtime overhead; allocator behavior varies. The mechanism is the
unbounded record assembly and its copies, not the precise sample. Ordinary
terminated records can be reclaimed afterward; this is a peak-allocation/OOM
risk, not evidence of a permanent leak after each read.

Validation: 71 tests across five reader/remote/cache/WSL suites passed, then ten
tests across the reader and cache-recovery suites passed (nine overlap). The
regressions cover all three assembly paths, exact byte boundaries, Unicode,
large multi-record files, iterator cleanup, and unchanged cached resume state
followed by successful repair. Full typecheck, final node typecheck, lint, and
changed-code quality passed. Tests used `ORCA_BACKGROUND_LAUNCH=1`.

This is another reachable allocation mechanism within the app process group.
#19831 does not establish an oversized transcript record. In the reported build
normal scanning runs in a child, so this route does not explain #19768's isolated
main-PID measurement. No affected-host attribution is claimed.

---

### Audits Tui Transcript Acquisition README (`docs/audits/tui-transcript-acquisition/README.md`)

# Transcript catchup can outlive host teardown

Host teardown stops TUI transcript catchup before draining in-flight handoffs. Previously, catchup setup registered its state only after asynchronous path resolution and stored its unsubscribe function only after asynchronous subscription acquisition. Teardown could miss either resource. A handoff that had not entered preparation yet could also start a watcher after `stopAll`. Actual-host tests reproduced a surviving watcher after the host session was removed. Stopping an already acquired watcher before its first snapshot instead left preparation waiting indefinitely for that snapshot.

## Ownership fix

Catchup now registers its state before its first await and owns an abort controller throughout setup. It passes the existing resolver/subscriber cancellation signal, releases late subscriptions, settles the initial-ready wait on stop, and preserves a newer same-session acquisition. `stopAll` permanently closes this host's admission; ordinary per-session `stop` still permits a replacement.

Preparation returns its signal internally so the handoff checks cancellation immediately before and after launching a TUI. A dedicated internal cancellation error, while no TUI owner or process identity has been committed, releases the unused reservation through the existing fenced `abandonStoredAgentSessionHandoffAttempt` transition. If launch returns after cancellation with an owner, the existing proven cleanup path is invoked; if cleanup is unavailable or fails, ownership is retained and manual recovery remains required. It leaves a recoverable native lease without acquiring a replacement. This distinction matters: ordinary preparation failure invokes native recovery, and a delayed replacement acquisition can finish after the five-second teardown drain. Ordinary read failures retain that recovery behavior. Canceled recovery of a live TUI stops without retrying or relabeling its live lease, and settles any original durable operation that was still pending.

These are internal lifecycle changes. They add no wire type and infer no remote process death. A launch that remains in flight beyond the bounded teardown drain, and boundary/import I/O already admitted before cancellation, remain outside this change's cancellation guarantee.

## Reproduce

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/tui-transcript-acquisition/reproduce.mjs
```

The script runs seven tests through the actual host, handoff coordinator, durable record store, journal, and transcript watcher. Real file resolution, watcher installation, and initial read are paused at explicit asynchronous boundaries; provider processes use the existing fake adapter/transport. No real shell or app window launches.

`fix.patch` is reversed inside a temporary Vite transform for the baseline. The new internal error declaration remains available to the same assertions; it does not change baseline control flow. Source hashes and exact failing cases are recorded in `results.json`. Each runner uses a 512 MiB old-space limit, a 90-second deadline, and the repository's cross-platform `runProcess`. The proof requires the fixed runner to exit successfully in addition to matching its seven-pass/zero-fail report. Temporary runner/configuration files and acquired watchers are cleaned up.

| Version    | Passed | Failed |
| ---------- | -----: | -----: |
| Before fix |      1 |      6 |
| With fix   |      7 |      0 |

The five preparation cases cover resolution, subscription return, initial snapshot, admission after teardown, and a completed preparation whose caller has not resumed. The recovery case preserves the live TUI lease. The control delays native acquisition after an ordinary resolver error and verifies the original error and recovery behavior. Six additional ownership tests cover overlapping prepare/recover replacements, per-session restart, repeated shutdown, and the signal returned when no supported record is available. Existing catchup tests preserve live appends and restart gap replay.

Two additional handoff regressions cover a TUI launch returning after cancellation and cancellation during recovery of a pending durable operation. Both fail against the original PR head and pass with the review fix. A late owner is stopped through the existing proven-cleanup contract, then the reservation is abandoned without acquiring a replacement native owner. If cleanup is unavailable or cannot prove the owner stopped, the existing manual-recovery path retains ownership instead. Recovery cancellation marks the original operation failed without relabeling the live TUI lease.

## Version and attribution

Named-path reads confirm the same setup gaps, unguarded forward launch, and stop-before-drain ordering in `v1.4.198`. In that tag the teardown phases are inline in `structured-agent-session-host.ts:254`; current source extracts them into `structured-agent-session-host-teardown.ts`. The executable proof compares current source before/after this fix. It establishes an execution-host watcher retaining path present in the reported version, without proving that #19831 or #19768 exercised this teardown race or explaining either report's memory magnitude.

---

### Audits Tui Transcript Close README (`docs/audits/tui-transcript-close/README.md`)

# Closed TUI sessions retain transcript watchers

After a structured session hands off to a terminal, `StructuredTuiTranscriptCatchup` tails the provider transcript. Successful `StructuredAgentSessionHost.close` stopped the TUI owner, durably released its lease, and removed the host session, but did not stop its transcript catchup. The live watcher and catchup state, including the previously seen message IDs, remained reachable. Repeated closes of distinct sessions could accumulate these resources until host teardown.

The fix calls the existing `stopTuiHistoryCatchup` callback after the verified terminal close and durable lease transition succeed. It removes the catchup state and unsubscribes the watcher before later journal eviction. An unverified stop or failed lease write preserves the watcher for retry. A later journal-close failure cannot undo completed watcher cleanup. The execution host retains authority; no client-side inference of remote process death or wire change is involved.

## Reproduce

From the repository root with existing dependencies installed:

```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/tui-transcript-close/reproduce.mjs
```

The proof uses the actual structured host, record store, journal, handoff coordinator, and transcript watcher against a temporary synthetic Codex transcript. Only provider process acquisition/stop is a test transport; no real shell or Orca window launches. It removes the single cleanup call in a temporary Vite transform for the baseline, then runs the same four tests against the fixed source. It uses the repository's `runProcess` and cleans temporary configurations/module files. Source hashes are recorded in `results.json`.

| Version    | Passed | Failed |
| ---------- | -----: | -----: |
| Before fix |      0 |      4 |
| With fix   |      4 |      0 |

The successful-close case observes one added watcher, proves live TUI text reaches the journal, closes the session, verifies the lease is released and session removed, and expects the watcher count to return to its original value. Before the fix it stays one higher. The remaining cases exercise unverified terminal stop/retry, failed durable transition/retry, and failed journal eviction/retry. These are four checks of one cleanup omission.

Existing catchup tests also verify that live appends and recovery of writes made while the host was down remain intact. All seven targeted tests passed, as did the Node typecheck and direct lint.

## Version and limits

Named-path reads of `v1.4.198` confirm that its host close calls the same owner-close helper, that helper omits catchup cleanup, and its catchup owns the same state/watcher lifetime. The executable comparison uses current production source. This establishes a retaining path present in the reported build; it does not establish that either #19831 or #19768 exercised this handoff-and-close sequence, or measure either report's memory growth.

The separate asynchronous acquisition race remains open: teardown calls `stopAll` before draining handoffs, while catchup setup can still be awaiting path resolution or subscription acquisition. Merely rejecting a canceled preparation is insufficient as a full teardown fix: the forward handoff's existing failure recovery may acquire a native replacement, and the handoff drain has a five-second limit. That race needs its own owner-cancellation policy and regression proof; this change covers successful close of an acquired TUI owner.

---

### Audits Working Directory Wait Retention README (`docs/audits/working-directory-wait-retention/README.md`)

# Canceled cwd validation waiter lifetime

Canceled working-directory checks retained their AbortSignals while the shared native filesystem check remained pending. The change releases those caller references immediately while preserving the underlying native operation, raw-promise identity and callback ordering.

**A small promise reaction and empty holder still remain per canceled wait until native settlement.** JavaScript promise reactions cannot be removed. This correction releases the signal, listener and caller resolvers; it does not establish a total bound on waiting metadata or explain an incident's memory magnitude.

## Ownership and callers

`src/main/providers/working-directory-validation.ts` keeps one pending validation per exact cwd. The raw `fs.stat` cannot be aborted, so the map entry and any UNC semaphore slot must survive caller cancellation until real settlement. Retiring them early would permit duplicate native work on the same stalled path.

Previously each caller registered a `finally` callback that captured its signal. The fix keeps each caller's raw promise reaction in its original position, but that reaction now references a small holder. Abort or settlement removes the abort listener and clears the holder. A separate waiter factory prevents the first signal from sharing the map's cleanup closure. The redundant per-call rejection observer is removed; the existing map-level `then(forget, forget)` still handles native failure when every caller has left.

The sole production importer is `pty-subprocess/spawn-preflight.ts:127–136`, through `local-pty-utils.ts`. `daemon-terminal-admission.ts` supplies a preparation signal, and `pty-subprocess.ts` forwards it to preflight. Ordinary daemon requests use a 30-second client timeout and a 5-second cancellation guard. A caller can therefore finish while the native filesystem operation remains pending across later requests. Those request timers do not bound the raw stat duration.

No-signal callers still receive the exact original promise. Native map deletion, UNC lane ownership, WSL checks, creation reservations, shutdown and process authority are unchanged. The change stays on the execution host and applies to folder workspaces and git worktrees without a wire change.

## Why each raw reaction remains

Existing wait utilities were checked. A shared settlement observer changes this API's callback order: a raw-promise observer registered before a signal waiter can abort it before its raw result arrives. Moving every waiter behind an earlier shared observer would fulfill that waiter instead. The per-call holder preserves that order and synchronous cancellation. Six permanent regressions cover an external aborting observer before, between and after signal waiters, for native success and failure.

## Before/after evidence

The standalone proof bundles the actual validation module, UNC path parser and semaphore. Its native async stat is a deferred fixture; WSL subprocess operations throw if unexpectedly reached. It performs no actual cwd probe, remote filesystem access, native subprocess launch or app launch. The fixture contains 32 small canceled callers per outcome; no large payload is attached.

| Before raw native settlement    | Original Node 26.6.0 | Original Electron 43.7.0 / Node 24.21.0 | Fixed, both |
| ------------------------------- | -------------------- | --------------------------------------- | ----------- |
| Signals reachable               | 32                   | 32                                      | 0           |
| Cancellation errors reachable   | 0                    | 32                                      | 0           |
| Caller option objects reachable | 0                    | 0                                       | 0           |
| Native stat calls               | 1                    | 1                                       | 1           |

All measured caller objects collect after native settlement in both versions. Both native success and failure have the same lifetime result. Other controls pass on both runtimes:

- Later live waiters receive the original operation's success or actionable error, and their listeners are removed.
- An already-aborted first caller still leaves the raw operation owned; no-signal callers share the same raw promise.
- Three canceled callers on one UNC host leave two native slots occupied. The third native operation starts only after one real completion.
- Forty-eight actual-module settlement/abort schedules and six raw-observer ordering cases match the original.
- Native rejection after all callers cancel produces no unhandled rejection.
- Synthetic CRLF source and patch reads produce identical reversed/fixed source and hashes without product writes.

`sources.cjs` reverses `fix.patch` in memory and checks exact baseline and fixed SHA-256 values. Source hashes use canonical LF; reports include effective dependency and bundle hashes. No git history, ignored notes or copied production implementation is needed to rerun the proof. Each run has a 20-second deadline; the commands below set a 192 MiB heap limit.

```sh
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/working-directory-wait-retention/reproduce.cjs
```

For Electron, run its installed executable with the same flags and script path, setting `ELECTRON_RUN_AS_NODE=1` and `ORCA_BACKGROUND_LAUNCH=1`. It runs in Node mode and creates no windows.

## Source compatibility and validation

The audited baseline module is byte-identical to main commit `291b4ddd6f1c1af480169885e0fda7f9c78ff053` and release `v1.4.198` (`e0826956fcfc532f5a1e55b5e081f2e57e553c43`). All nine recorded caller/dependency sources also match that main commit. This one-product-file change does not depend on the shared waiter helper or its auth-wait changes. `source-versions.json` records the exact comparisons; historical source equality is not a historical packaged-runtime reproduction.

The fixed three-file regression run passed 31 tests. Reversing only this fix gives one expected first-caller retention failure and 30 passing controls, including the six raw-observer cases:

```sh
ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config docs/audits/working-directory-wait-retention/before.config.mjs src/main/providers/working-directory-validation-retention.test.ts src/main/providers/working-directory-validation.test.ts src/main/daemon/pty-subprocess-cwd-cancel-identity.test.ts
```

`validation.json` records verification results. The measured retention requires a still-pending native operation; no affected-host capture, byte slope or attribution to #19831 is claimed.

---

### Bug Reproductions Native Chat Tool Pairing README (`docs/bug-reproductions/native-chat-tool-pairing/README.md`)

# Native chat tool pairing

Latest follow-up: [mobile row identity](./mobile-row-identity.md) keeps an opened detail with its command after earlier rows disappear or move.

Long tool runs repeatedly scanned all unanswered calls to find each named result.
Calls that emitted no output stayed at the front of that scan. Positional results
also shifted the remaining pending-call array after every answer.

The shared matcher now uses a linked FIFO and builds an ID index only when a
named answer needs to match beyond its head. That index stays valid until the
pending queue empties. Named and unnamed answers remove a pending call without
array movement; each call enters the index at most once. Duplicate IDs retain
FIFO order. Existing pair limits and output references are preserved.
The implementation remains shared by desktop, mobile, and host readers.

The rendered check exposed related correctness defects: Claude and Codex
transcript decoders discarded result IDs, and folding used only call counts to
assign results. Decoders now retain the optional provider ID; folding uses
the canonical matcher and occurrence indices, so an unrelated result cannot
consume another call's answer. Unmatched named results stay visible separately
and cannot acquire a guessed owner during later folds. Results without IDs
remain positional; already imported calls missing IDs cannot own named results.

Legacy journal import also preserves IDs on singleton calls and keeps singleton
results as result messages. The focused importer/projector regression test
covers reverse batched results, mixed record shapes, duplicate IDs, anonymous
FIFO, and old persisted calls whose IDs were already lost. That integration
coverage is separate from the rendered transcript fixture below.

## Reproduction and measurements

Baseline: `51e7181850b`. At 2,048 calls, the regression tests measured 2,098,176
ID reads for reverse completions, 4,194,304 reads for silent-call backlogs and
unmatched results, and 2,096,128 moved array slots for FIFO completions. The
fixed implementation passes linear operation budgets at 64, 512, and 2,048
calls. Semantic differential tests preserve call/result references across
mixed IDs, interleavings, immutable inputs, and unusual limits.

Run the production benchmark with the original module on stdin:

```sh
git show 51e7181850b:src/shared/native-chat-tool-fold.ts |
  ORCA_BACKGROUND_LAUNCH=1 node config/scripts/native-chat-tool-pairing-benchmark.mjs
```

[benchmark.json](./benchmark.json) records eight counterbalanced sample pairs
per workload, source bundle hashes, raw timings, and correctness checks. It
includes immediate pairs, reverse completion, positional FIFO, duplicate IDs,
silent-call backlogs, unmatched results, and finite limits at seven sizes.
At 2,048 calls, reverse completion improved from 7.46 ms to 0.163 ms, a
silent-call backlog from 13.02 ms to 0.244 ms, and immediate ordered pairs from
92.0 microseconds to 47.3 microseconds. Single-call backlog/missing workloads
remain within 11 ns of the baseline. These timings cover the shared pairing
function, not React, DOM, transport, or the full transcript projection. These
measurements identify the earlier matcher revision in the artifact. Later orphan
projection, journal import, and discarded-wrapper allocation changes are outside
this benchmark's scope.

The deterministic contract runs in `pnpm test:perf:contracts`. Broader coverage
uses the shared/native-chat and renderer/native-chat suites, provider transcript
reader suites, and `tests/e2e/native-chat-tool-pairing.spec.ts` for hidden
Electron rendering and output ownership.

## Full folding, allocation, and mobile disclosure

The later allocation fix reuses the same matcher for folding attribution without
collecting pair wrappers that folding immediately discarded. Consumer pairing
still returns fresh wrappers with the original call/result references. No cache,
parallel matcher, or ownership fallback was added.

Run the three-version production-module benchmark and its retained-allocation
control with:

```sh
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc config/scripts/native-chat-fold-benchmark.mjs --memory-control
ORCA_BACKGROUND_LAUNCH=1 node --expose-gc config/scripts/native-chat-fold-benchmark.mjs
```

[full-fold-benchmark.json](./full-fold-benchmark.json) compares original
`51e7181850b`, published `b1e44269d655`, and the measured worktree. It records
54 cases across three sizes, six workloads, and full folding, settled desktop
projection plus all-row render-data pairing, and repeated folding. Each
comparison uses six counterbalanced sample pairs; the table uses raw-sample
medians because the artifact's rounded summary loses microsecond precision.
The two comparisons have separate current samples.

| All-row projection and pairing                     | Original → current               | Published → current |
| -------------------------------------------------- | -------------------------------- | ------------------- |
| 8,192 reverse completions                          | 320.4727 → 3.7358 ms, about 86×  | 3.5954 → 3.6126 ms  |
| 8,192 silent calls plus 8,192 later answered calls | 558.9641 → 5.5075 ms, about 101× | 5.5551 → 5.2629 ms  |
| 8,192 immediate ordered pairs                      | 2.4124 → 2.5636 ms               | 2.6577 → 2.3606 ms  |

These are large adversarial-run gains, not a general frame-time improvement.
At 32 immediate pairs, the original/current pipeline medians were 11.38 and
16.74 microseconds: roughly 5.4 microseconds of extra work. The separate
published/current comparison measured 15.60 and 13.10 microseconds. Original
folding and refolding alone were faster than the ownership-preserving fold:
for the large silent backlog, folding rose from 1.1191 to 2.6317 ms and refolding
from 0.3171 to 1.4090 ms. The expensive original consumer scan dominates the
large named-result cases. Full folding still visits the collection on updates;
settled row reuse does not make the fold incremental.

Against published C, the large fold medians fell approximately 3–9%; whole
pipeline changes varied, including the small reverse-case increase above.
The deterministic allocation gain is clearer: large silent-backlog folding
collects zero discarded pair wrappers instead of 16,384, while consumer pairing
retains the required 16,384. Its median pre-GC heap delta fell from 8.23 to
6.84 MB; retained output stayed near 0.23 MB. Named orphan conservation,
original block references, unique row IDs, and repeated-fold identity are
checked for published/current outputs, which are deeply equal for every
workload. Original output counts expose historical drops or guessed ownership;
those cases do not provide equivalent visible-output timing comparisons.

Preserving separately sourced orphan evidence still costs rows and memory.
With 8,192 singleton orphan outputs, current folding retains roughly 2.61 MB
versus the original's 0.15 MB; fresh all-open projection and pairing retain
about 9.23 MB versus 0.33 MB. Published/current retention is essentially the
same. The original grouped these outputs with unrelated calls. Desktop
windowing normally mounts only viewport rows, while this consumer benchmark
processes every run as opened. It excludes React commits, DOM layout, mobile
runtime, and transport latency.

[full-fold-memory-control.json](./full-fold-memory-control.json) confirms that
the sampling method retains a live million-slot array at about 8 MB. Each
memory sample releases its preceding output, holds the current output through
forced GC, and excludes shared source inputs. Projection memory uses a fresh
projector/cache; timing reuses a settled projector. Pre-GC deltas indicate
allocation pressure, not peak memory or total allocated bytes. Negative
retained deltas are noise, not savings or per-update growth.
[full-fold-benchmark-receipt.json](./full-fold-benchmark-receipt.json) records
commands, source/tool hashes, and the post-main-merge graph review. Only three
account-startup enum/map modules changed after measurement; those branches are
absent from the benchmark fixtures, so no new timing claim is made for them.

Mobile previously exposed only six tool rows with no way to reach later
outputs. The existing disclosure now reveals six more rows per request and can
collapse back to six. A closed run performs no pairing; an opened run collects
only the requested prefix plus one sentinel to decide whether Show more is
available. This limits wrapper and mounted-row retention, while matching may
still scan a pending suffix to find the requested owners. Long runs require
multiple reveals, and expanding them mounts more rows at the user's request.
Result-only summaries use the existing Result label without inventing a call
count. Local mobile stream and SSH-authoritative handler tests cover late named
completion, orphan reachability, cursor paging, and older-host no-cursor
replacement. They establish ownership and paging behavior, not network latency.

## Rendered verification

The rebuilt Electron app passed the dedicated E2E test through the real Claude
JSONL reader: three reverse completions behind 32 silent calls, an unrelated
result, and successful/failed task updates. The [inspected screenshot](https://github.com/user-attachments/assets/79f41dc6-8ec8-4f12-8e89-e4d7ec4b5daa)
shows all three outputs attached to their commands, the accepted pending task,
and the unrelated output under its own Result summary and row.
[rendered-validation.json](./rendered-validation.json) records the
commands, test source hash, exact worktree identity, hidden/unfocused native
window assertions, and process cleanup. The fixture uses synthetic Claude
JSONL through the production reader and real UI clicks. This is desktop
correctness evidence; it does not exercise an actual provider CLI or adoption
launch, measure mobile rendering, or measure SSH transport latency.

The mobile component check uses the actual React Native Web primitives and
production message/fold modules under the shipped browser build options and
shell content-security policy. The [before capture](https://github.com/user-attachments/assets/6b6b30f1-58f0-4bc7-8149-6f2169a368b2)
shows six orphan results with no way to reveal later rows. The [after capture](https://github.com/user-attachments/assets/81241691-0dfe-4a88-b3d1-bdf7e8d02adc)
shows all eight Result rows and the seventh output's full body. Real clicks on
Show more and Show less reveal and hide those rows. Further captures show
[reversed named outputs and a separate orphan](https://github.com/user-attachments/assets/26e0559f-3f0c-482e-9ef2-f32903466c82)
and [a named completion after seventeen silent calls](https://github.com/user-attachments/assets/69b1e49b-0627-40b3-977d-052b885beef6).
[mobile-rendered-validation.json](./mobile-rendered-validation.json) records
row-specific full-body assertions, source and bundle hashes, the one-component
historical overlay, and exact headless-browser cleanup. This proves mobile web
component disclosure and ownership; it does not exercise a native phone, the
full session route, provider decoding, adoption, or transport.

The [before screenshot](https://github.com/user-attachments/assets/b9f84d6e-a786-42d5-b895-8a4319f972cd) shows the unrelated result and named outputs attached to silent calls, and the failed task replacing the accepted task. The [before validation record](./before-validation.json) identifies the three modules restored from `51e7181850b`: `native-chat-tool-fold.ts`, `transcript-record-blocks.ts`, and `transcript-line-decoders-codex.ts`. Other app code, including the hidden launch policy, stayed current. This proves the original folding/decoder defect, rather than a full historical app build. Each of the three overlaid source files was restored byte for byte and the owned app process exited. Images are uploaded attachments, not committed binaries.

---

### Bug Reproductions Opencode2 Form Created Kinds README (`docs/bug-reproductions/opencode2-form-created-kinds/README.md`)

# OpenCode 2 `form.created` is not always a question (#22371)

OpenCode 2 has one form primitive and several producers. Orca's setup bridge mapped every
`form.created` to `question.asked`, which is Orca's un-evictable "the pane owner must answer
this" blocker — including forms owned by a sentinel that is not a session.

Captured against the shipped `opencode v2.0.12` binary on macOS, driving the real TUI in a PTY
against a real `opencode serve` instance and reading the server's `/api/event` SSE stream.

## The discriminator: `form.sessionID`, not `form.metadata.kind`

`Form.Info` is `{ id, sessionID, title, metadata?, fields }`. Every `Form.ask` call site in the
v2.0.12 bundle (still exactly five on `v2.0.15`, in `tool/plugin/question.ts`,
`tool/plugin/websearch.ts` and `mcp/index.ts`):

| `metadata.kind`      | Form title                                    | `sessionID` | Blocks the pane owner     |
| -------------------- | --------------------------------------------- | ----------- | ------------------------- |
| `question`           | `Questions`                                   | the session | yes — the `question` tool |
| `websearch.provider` | `Web Search` / `Choose a web search provider` | the session | yes — the turn is stalled |
| `mcp-elicitation`    | `<server> is requesting input`                | `"global"`  | no — see below            |

`metadata.kind` looks like the discriminator but cannot be one. In `packages/schema/src/form.ts`
on `v2.0.15`, `Metadata` is `Schema.Record(Schema.String, Schema.Unknown)` and line 130 declares
`metadata: Metadata.pipe(optional)` — so `metadata` may be absent entirely and `kind` is a
convention no producer is obliged to stamp. The public `POST /api/session/:sessionID/form`
endpoint (`packages/protocol/src/groups/session.ts:809`, payload at ~147) lets any client raise a
genuinely blocking form on a real session with no metadata at all. Keying on
`metadata.kind === "question"` therefore drops real blockers silently.

What actually differs is the owner. `mcp-elicitation` passes `GLOBAL_ELICITATION_SESSION_ID`
(`"global"`, `packages/core/src/mcp/index.ts:82`), which is not a session, so a blocker minted for
it can never be retired by that session going idle — only by an exact `form.replied` /
`form.cancelled`. `websearch.provider` passes the real `context.sessionID`, so session idle retires
it normally, and while it is pending the agent genuinely is waiting on the user.

So Orca blocks on every session-owned form and drops only the non-session sentinel. Upstream notes
in `form.ts:122-129` that `"global"` is temporary and elicitations will get real session ids; when
that lands the exclusion stops matching and Orca starts blocking on them correctly.

`form-created-question.json` and `form-replied-question.json` are the live capture of the
`question` tool's form being raised and answered. Note `metadata.tool` is `{ messageID, id }`,
not the `{ messageID, callID }` that Orca's `clearQuestionForToolPart` matches on, and OpenCode 2
never emits `message.part.updated` at all — so that retirement path is dead for OpenCode 2 and
`form.replied` / `form.cancelled` is the only reply-side retirement it has.

## The reported menu does not raise a form

`subagent-panel-screen.txt` is the rendered PTY screen from the reported surface — the
`Subagents / Shell / Terminals` activity dock, opened over a session with three subagents.
The whole time that dock was opened, paged and dismissed, the server's `/api/event` stream
carried nothing but `server.connected`, heartbeats, and unrelated `skill.updated` filewatcher
noise from another checkout. Same result for the `shift+tab` agent picker and the `ctrl+p`
command palette. The TUI's pickers are local Solid components; they never call `Form.ask`, so
they produce no server event of any kind and cannot be the thing Orca saw.

So this capture proves the mapping was wrong and which field fixes it; it does not reproduce
the exact frame in the issue screenshot.

## Reproduce

```sh
opencode serve --hostname 127.0.0.1 --port 47391     # OPENCODE_SERVER_PASSWORD=<pw>
curl -s -u opencode:<pw> -N http://127.0.0.1:47391/api/event   # tee this
opencode --server http://127.0.0.1:47391 --session <sid>       # drive in a PTY
```

Ask the agent to call its `question` tool for the `question` shape. For the picker and MCP
shapes, `POST /api/session/<sid>/form` with the `metadata.kind` from the table — that is the
same publish path the internal producers use, and it is what the plugin-level check was
verified against.

---

### Bug Reproductions Opencode2 Session Lineage README (`docs/bug-reproductions/opencode2-session-lineage/README.md`)

# Orca could not resolve OpenCode 2 session lineage (#22371 follow-up)

Orca's OpenCode plugin resolves a session's ancestry so a subagent's work rolls up to the pane
without taking it over. Against OpenCode 2 that resolution returned `null` for every session, so
every child session was published as if it were a root — and a subagent's question minted an
un-evictable "needs input" blocker the lead agent had never asked for.

Captured against the shipped `opencode v2.0.12` binary on macOS: Orca's real generated plugin in
`~/.config/opencode/plugins/orca-opencode2-status.js`, real `ORCA_PANE_KEY` / hook-token env, a
logging hook server on a loopback port, and `opencode2 --standalone` driven in a real PTY.

## The SDK shape Orca was given

`src/main/opencode2/status-plugin-setup-source.ts` handed the shared lineage walk a client built
from the plugin `setup()` context. Probing that context live inside the running plugin:

| call                                         | result                                              |
| -------------------------------------------- | --------------------------------------------------- |
| `ctx.session.get({ sessionID }, { signal })` | the session record **directly** — no `data` wrapper |
| `ctx.session.get({ path: { id } })`          | throws `Missing key at ["sessionID"]`               |
| `ctx.session.list`                           | `undefined`                                         |
| `ctx.session.get.length`                     | `1`                                                 |

`lookupSessionList` only accepts a result when `result?.data?.id === sessionID`, so every lookup
was discarded, and the `session.list` fallback it then tried does not exist. `resolveRootSessionID`
therefore returned `null` for every session and `childState` stayed permanently `null`.

## The capture

One prompt, run twice, telling the agent to delegate `echo hello-from-subagent` to exactly one
subagent. `hook-posts-before.jsonl` and `hook-posts-after.jsonl` are the verbatim hook POSTs.

Before — the subagent publishes as a root:

```
{"hook_event_name": "SessionStart", "sessionID": "ses_f33016571ffenJDTLmPcLATavv"}
{"hook_event_name": "SessionBusy",  "sessionID": "ses_f33016571ffenJDTLmPcLATavv"}
{"hook_event_name": "SessionBusy",  "sessionID": "ses_f330152d1ffer9a1SCLERqCLv8"}   <- the child
{"hook_event_name": "SessionBusy",  "sessionID": "ses_f33016571ffenJDTLmPcLATavv"}
```

`ses_f330152d1ffer9a1SCLERqCLv8` never received a `SessionStart` because the plugin skips
`session.created` events that carry a `parentID`. OpenCode's own `session_v2` row confirms it:
`parent_id = ses_f33016571ffenJDTLmPcLATavv`.

After — the child is recognised and rolls up; only the root's id is ever published:

```
{"hook_event_name": "SessionStart", "sessionID": "ses_f32f26424ffe3xdiePHBUnsdAY"}
{"hook_event_name": "SessionBusy",  "sessionID": "ses_f32f26424ffe3xdiePHBUnsdAY"}
{"hook_event_name": "SessionIdle",  "sessionID": "ses_f32f26424ffe3xdiePHBUnsdAY"}
```

The subagent still ran in the "after" capture (`ses_f32f2519dffeNSA1jG2Z8JVJyv`,
`parent_id = ses_f32f26424ffe3xdiePHBUnsdAY`); its id simply never appears in a POST.

## Reproduce

```sh
# Log POSTs on a free loopback port, then:
ORCA_PANE_KEY=tab:leaf ORCA_OPENCODE_AGENT=opencode2 \
ORCA_AGENT_HOOK_PORT=<port> ORCA_AGENT_HOOK_TOKEN=<token> \
  opencode2 --standalone --prompt '<delegate something to one subagent>'
```

Unset `ORCA_AGENT_HOOK_ENDPOINT` so the plugin reads the port/token from env instead of a live
Orca's endpoint file.

One trap: OpenCode 2 loads both `orca-opencode-status.js` and `orca-opencode2-status.js` from
`~/.config/opencode/plugins/`, and both declare the same plugin `id` (`orca-opencode-status`).
Only one survives, and if it is the v1 file its agent gate silently returns no hooks, so nothing
posts at all. Move the v1 file aside while reproducing.

---

### Bug Reproductions Usage Scan Append README (`docs/bug-reproductions/usage-scan-append/README.md`)

# Usage scans after a transcript append

Latest follow-up: [lossless source compression](./source-compression.md) removes the disk increase in three measured histories, with CPU costs reported. Earlier tables below describe the uncompressed implementation.

Claude usage refresh previously cached a transcript only while its modification time and size were unchanged. Appending one usage row invalidated that cache and parsed every earlier user, tool and assistant row again. The production route is `ClaudeUsageStore` → `UsageProviderStoreLifecycle.runScan` → the usage worker → `scanClaudeUsageFiles`. Long tool output therefore made a small usage refresh expensive even when the number of usage records stayed fixed.

The regression is deterministic: the append test measures bytes returned by the real filesystem reader. Before the fix, a 424-byte append to a 1,083,929-byte transcript read 1,084,353 bytes. The new scan reads a verified suffix and a fixed amount of prefix evidence. It retains all usage history and places no new cap on transcripts, rows or tokens.

## Reproduce

From the repository root:

```sh
ORCA_BACKGROUND_LAUNCH=1 pnpm test src/main/claude-usage src/main/usage/jsonl-line-offsets.test.ts
ORCA_BACKGROUND_LAUNCH=1 pnpm test src/main/codex-usage
ORCA_BACKGROUND_LAUNCH=1 ORCA_CLAUDE_USAGE_APPEND_BENCH_ROUNDS=8 ORCA_CLAUDE_USAGE_APPEND_BENCH_BASELINE=51e7181850b022bae2d41091fe1229accef4bd50 node config/scripts/claude-usage-append-benchmark.mjs --compare --verify
```

Run the timing benchmark while builds, test suites and other benchmarks are idle. It uses temporary transcript directories and loads the actual scanner through an esbuild bundle. Account/profile discovery is isolated; transcript reads remain real. The I/O wrapper records stream bytes and descriptor window reads without counting descriptor stream internals twice. Its accounting smoke check verifies one complete file read plus an eleven-byte descriptor read. Unaccounted `readFile` and `readv` APIs fail the benchmark.

The current comparison loader pins every project dependency to its named baseline commit and freezes the current source graph. The historical comparison below overlaid named Claude modules; the original scanner's five-module graph came entirely from the original commit. Fixtures are identical across arms. Arm order alternates across rounds; each round records scanner-cache-cold, unchanged, append and fresh-after-append phases. “Cold” describes the scanner cache: newly written fixtures can already be in the operating system page cache. Raw samples include read counts, bytes, scanner duration, cache serialization duration, serialized state bytes, source hashes and environment. Separate scanner and serialization values prevent a cursor from merely moving work into persistence.

The fixture set includes one thousand small transcripts; two and thirty-two MiB of UTF-8 user/tool output with the same 256 usage keys; and thirty-two MiB with 4,096 and 16,384 usage keys. Every appended result must match a fresh scan of the same bytes. Across baseline/current arms, additive checkpoint and generation metadata are excluded from behavioral comparisons; sessions, daily aggregates, per-file aggregates, line counts and owned keys are compared.

## Initial controlled results

This comparison records the original append fix before the later protected source-sidecar and column-storage refinement. Its source hashes and raw samples remain unchanged; the later persistence measurements below use their own complete source graphs.

The final [comparison.jsonl](./comparison.jsonl) contains eight counterbalanced rounds per scenario on Node v24.20.0, macOS arm64, Apple M4 Max. Baseline is commit `51e7181850b022bae2d41091fe1229accef4bd50`. Each arm ran first four times. No builds, tests, app or competing benchmark ran during the measurement. All eleven recorded current runtime-source hashes and four tool hashes were checked against the frozen worktree after the run. Exact within-arm cold parity, cross-arm behavioral parity and byte budgets passed.

The table shows medians. Time is the scanner plus compact cache serialization; the raw artifact records these separately and retains every unrounded sample.

| Tool payload / owned usage keys | Appended bytes | Before bytes read | After bytes read | Before time | After time | Before serialized cache | After serialized cache |
| ------------------------------- | -------------: | ----------------: | ---------------: | ----------: | ---------: | ----------------------: | ---------------------: |
| 2 MiB / 256                     |          4,730 |         2,288,956 |           29,306 |    5.943 ms |   0.616 ms |                 8,196 B |               13,689 B |
| 32 MiB / 256                    |          4,730 |        34,534,456 |           29,306 |   81.513 ms |   0.741 ms |                 8,197 B |               13,691 B |
| 32 MiB / 4,096                  |          4,748 |        36,560,492 |           29,324 |   92.323 ms |   3.093 ms |               114,262 B |              188,877 B |
| 32 MiB / 16,384                 |          4,766 |        43,062,968 |           29,342 |  124.310 ms |  10.651 ms |               471,124 B |              766,924 B |

For the 256-key large transcript, scanner time alone fell from 81.494 ms to 0.724 ms, and append bytes read fell by 99.915%. Increasing transcript payload by 15.117× left appended reads unchanged. Increasing owned keys still increased scanner and serialization work, as expected. The cache is larger than the original aggregate-only cache because five old maxima must remain available to update a repeated historical key correctly.

The cold full-scan path also improved on these large fixtures: the 32-MiB/256-key scan took 82.270 ms before and 35.014 ms after, with sixteen KiB of extra evidence reads. The new raw-buffer line reader builds each complete line once, while retaining exact byte offsets. This timing includes the projection and checkpoint cost; it is not a separate parser microbenchmark.

The small-file tradeoff is visible. A thousand initial transcripts contain 459,780 bytes in total; appending a repeated maximum to each raises their total to 920,560 bytes. Full snapshot verification requires one additional read of each small file.

| Thousand-file phase | Before scan | After scan | Before bytes read | After bytes read |
| ------------------- | ----------: | ---------: | ----------------: | ---------------: |
| Cold                |   73.233 ms |  94.872 ms |           459,780 |          919,560 |
| Unchanged           |    9.176 ms |   9.400 ms |                 0 |                0 |
| Changed             |   71.938 ms |  94.933 ms |           920,560 |        1,841,120 |

Small-file cold scans cost about 21.6 ms more across this thousand-file fixture, a 29.5% increase. Unchanged refreshes retain zero transcript reads. This verification cost prevents a short, rotated or rewritten snapshot from being published under complete-file metadata; it is not hidden by excluding tiny files or adding a history cap. Scanner execution remains on the existing usage worker.

At 16,384 keys, the normalized cache is 766,924 bytes, compared with 6,203,025 bytes in the retained pre-normalization artifact: 87.6% fewer serialized bytes. The older run is evidence for the removed duplication, not a controlled timing comparison. Final scanner-plus-serialization time remains 10.651 ms at that key count, so the result does not claim constant total cost.

## Shared reader before provider-specific projection

The durable byte reader and checkpoint evidence were extracted from Codex into `src/main/usage/jsonl-file-checkpoint.ts` and reused by both providers. The persisted Codex checkpoint fields remain compatible. The extraction also repairs a demonstrated race: path verification followed by a new path open could merge an old prefix with a replacement file, then publish the replacement's metadata over those old totals and reuse the result indefinitely.

Verification, initial stat, parsing and checkpoint creation now use one pinned descriptor. Parsing ends at the initial file size, so an append after the scan starts is picked up by the next refresh. Before publishing, the reader proves the complete requested byte range was consumed, checks the current path still names the opened generation, validates its original byte evidence and rejects truncation or changed snapshot evidence. Failed generations discard speculative projection and ownership before a cold retry. A short stream, digest window or legacy-prefix read on an unchanged snapshot propagates an error instead of publishing partial totals or retrying forever. Reopening requires positive generation or metadata change and closes each failed attempt once. Normal completion closes the owning handle; borrowed descriptor streams are not destroyed independently because that closes the descriptor on Node as well.

The inherited 12-KiB resumable-prefix crossover avoids paying more evidence work than a full read on tiny files. Tiny files hash the bytes actually parsed and verify them with one post-read descriptor read, including deliberately skipped legacy prefixes. They therefore read twice on a cold or changed scan, while unchanged files read zero bytes. Large cold scans add four 4-KiB evidence pages; a normal large append adds six pages. These costs are measured explicitly.

## Claude projection and cache compatibility

Claude assistant rows are deduplicated by their existing request/message key. A repeated key keeps its first session, timestamp, model and location attribution, while each of the five token counters takes its independent maximum. The incremental projection applies only the token difference to the original session, day, model and location. It does not create another turn. A zero-cache-read turn is decremented once when that key's cache-read maximum changes from zero to positive. An invalid first timestamp remains invalid even when a later repeated row has a valid timestamp.

The persisted encounter order preserves cold-scan sorting when a token update creates a tie between locations, or a new row creates a timestamp tie between sessions. Keyless rows remain ordinary counted turns. Completed-line count is stored separately from the exposed line count: an unterminated fragment is counted once and read again after completion. A parseable unterminated usage row, including a duplicate that only raises maxima, suppresses resume until its provisional contribution can be parsed safely.

Cached cross-file ownership is retained while a changed owner is being read. Once that read validates, actual deleted or dropped keys are released and deferred forks reclaim them. A small owner's ordinary append therefore does not force every unchanged deferred fork to reread its history. Deletion, truncation and replacement that actually lose an owned key still recover that history from a surviving fork.

Generation metadata is stored independently of a resumable checkpoint, including for small files and parseable partial tails. Same-size replacement with a restored modification time is invalidated by physical identity; same-size in-place change with a restored modification time is invalidated by change time where available. Old cache entries lacking this metadata are cold-scanned once, then use unchanged zero-read reuse. Missing or unrecognized incremental state falls back to a full parse without discarding tracking preferences.

Resume state is validated at the boundary. It checks key coverage, compact tuple shape, finite token values, projection indexes and routing, and full session/location encounter-order coverage. Malformed state falls back to a cold scan instead of losing rows, double counting repeated keys or throwing during a correction.

## Retained state and remaining scaling

The first implementation repeated each key and its full routing metadata in an owned-turn map. The isolated pre-normalization run measured 6,203,025 serialized bytes at 16,384 usage keys, with 4.306 ms spent serializing one appended scan. That exposed a second cost before finalizing the fix.

The normalized state reuses the existing ordered `ownedDedupeKeys` array. Parallel tuples store five maxima and a routing index; session/day/model/location routes are interned once. Existing-key updates retain their positions; new keys append. Tests include reversed suffix key order and JSON persistence between updates. Appends now use the tuples directly, replacing only corrected rows and sharing unchanged rows without mutating prior state. This removes the historical owned-turn object hydration and full tuple re-encoding pass.

Transcript-payload growth no longer determines append reads. Hydration, key validation, aggregate cloning and serialization still scale with the number of owned usage keys and rollups. The benchmark reports these costs rather than claiming constant total time or constant retained state. It also reports the small-file verification overhead rather than hiding it in a large-file average.

`pre-normalization.jsonl` retains the earlier same-source measurement that motivated the compact codec. It is not the final baseline/current timing comparison; source and method changes prevent treating those separate runs as controlled timing evidence.

## Protected worker cache and visible report

The final implementation reuses upstream's worker-owned source sidecar and `whenLoaded` barrier. Per-file ownership, token maxima and checkpoint routes stay on the usage worker; main retains the session/day report. The disk codec packs each of six token columns as one scalar when all values match, or an exact dense column otherwise. It changes no worker or public usage-report shape, keeps every key and maximum, and serializes one file's columns at a time.

Unchanged refreshes retain the sidecar only after verified schema 7, clean normalization and identical file count, order and references. Any change, repair or legacy upgrade writes a new source generation. Report completion and report persistence still run.

Schema 7 source files and reports have separate SHA-256 guards over their exact JSON bytes. The source guard includes small files that have no resumable checkpoint. The worker verifies its source bytes before granting a request-local reuse proof; direct scanner callers and genuine older signed files still pass the coupled key/maxima/route/rollup validation. A malformed owner also invalidates deferred forks so the next scan can reclaim copied history. The checksum detects accidental drift; it does not authenticate a cache deliberately changed and re-sealed by another writer.

The report is authoritative for startup independently of the sidecar. A failed source write may leave an older sidecar, and a crash before report commit may leave a newer sidecar. Neither case replaces displayed report totals with another generation's totals. Legacy inline migration verifies protected files and reconstructs its report before splitting; invalid projections clear all visible totals and completion rather than presenting a partial history. An existing compatible newer sidecar survives a repeated migration after a failed report write.

Genuine unsigned schema 6 reports remain readable with their existing cache-trust policy until a successful source scan creates schema 7. Older signed tuple checkpoints migrate through their original coupled guard. Schema 7 requires valid integrity framing, including when a digest is removed. Downgrading to a schema 6 app causes a one-time cold rebuild while preserving tracking preferences.

Small reports keep upstream's synchronous load. Reports above its eight-MiB threshold use the existing worker operation; the verification fact is attached to the exact returned report text, so main does not hash those bytes again. Main still parses and serializes its visible rollups. If the worker is unavailable, `whenLoaded` covers the exceptional inline validation on main, including disabled cached history. Preference writes preserve that original inline source generation until a successful scan, and shutdown still drains writes after a failed preference write. This fallback can be slower, but does not retain the historical source graph in main.

Source-cache parsing, column decoding, key indexing, rollup cloning and durable sidecar writes still scale with cached history. The refinement moves this work off main and removes redundant representations; it does not claim constant total refresh time, bounded history, or universal detection of transcript rewrites.

## Final Claude persistence measurements

The [final evidence receipt](./claude-resource-evidence-receipt.json) verifies every captured production dependency, tool and artifact. [Detailed costs and limits](./hardening-summary.md) distinguish the original commit, published PR, merged upstream main and final worktree. The final timing drivers use six counterbalanced rounds with complete pinned source graphs and actual source-cache read/verification/decoding, scanning and durable source/report writes. Worker startup, IPC transport/queueing, telemetry, worktree discovery and rendering are excluded.

| Owned keys / phase          | Published PR | Upstream main |      Final |
| --------------------------- | -----------: | ------------: | ---------: |
| 16,384 repeated / cold      |    72.455 ms |     61.117 ms |  83.160 ms |
| 16,384 repeated / unchanged |    13.106 ms |     21.844 ms |  12.290 ms |
| 16,384 repeated / append    |    28.000 ms |     61.131 ms |  28.876 ms |
| 100,000 unique / cold       |   385.203 ms |    265.264 ms | 390.682 ms |
| 100,000 unique / unchanged  |    34.915 ms |     36.950 ms |  30.662 ms |
| 100,000 unique / append     |   128.631 ms |    267.204 ms |  84.152 ms |

The [16k](./claude-persistence-cost-final-16k.json) and [100k](./claude-persistence-cost-final-100k.json) measurements retain every sample. Each unchanged final scan writes zero source-cache bytes, while still reading 471,838 or 6,126,038 source bytes and persisting a small report. At 100k, appended transcript reads fall from 47,323,259 to 25,030 bytes; including the source sidecar, total logical payload reads fall from approximately 50,303,147 to 6,151,068 bytes. These are measured operation costs, rather than whole-app latency or physical device I/O.

The [four-arm byte check](./claude-persisted-bytes-final.json) uses actual production layouts and indentation. At 16k repeated maxima, final report plus sources take 538,876 bytes: 29.8% below the published PR, 14.1% above main and 13.2% below original source. At 100k unique maxima, final state takes 6,128,046 bytes: 3.2% below the published PR, 105.5% above main and 57.8% above original source. One ownership-key list and the six dense token/routing columns dominate that adverse case; no duplicate key list remains. Dense cold rebuilding also remains slower than main, and the 16k complete append is slightly slower than the published PR despite a faster scanner.

The [readiness diagnostic](./claude-readiness-cost-final.json) retains disabled history with zero per-source records on main. Upstream's report-only startup already removes the large source graph from main, so that improvement is inherited. First legacy migration becomes ready in 94.434 ms versus main's 63.060 ms. Exceptional validation yields 293 times, but its initial legacy parse still occupies main. A separate 13.87 MB report still requires a 16.512 ms observed main parse segment, and final serialization/checksum costs 20.628 ms versus main's 16.183 ms. These observations do not establish universal latency bounds.

Reproduce final evidence while other builds, tests, apps and benchmarks are idle:

```sh
ORCA_BACKGROUND_LAUNCH=1 node config/scripts/claude-usage-persistence-cost-benchmark.mjs --usage-keys 16384 --maxima-pattern repeated --rounds 6
ORCA_BACKGROUND_LAUNCH=1 node config/scripts/claude-usage-persistence-cost-benchmark.mjs --usage-keys 100000 --maxima-pattern unique --rounds 6
ORCA_BACKGROUND_LAUNCH=1 node config/scripts/claude-usage-readiness-cost-benchmark.mjs --usage-keys 100000 --large-report-mib 13 --rounds 6
ORCA_BACKGROUND_LAUNCH=1 node config/scripts/claude-usage-persisted-bytes-check.mjs
```

Each tool accepts `--output`; the timing tools also accept `--published-ref` and `--main-ref`. Default immutable refs and complete provenance appear in the artifacts. Equivalent environment-variable launchers work on Windows. These macOS measurements make no Windows or SSH latency claim.

## Codex parser retained memory

Codex folds each owned event into the existing session/day accumulator while reading. It keeps ownership keys and rollups, instead of an attributed-event array for the whole transcript. Claims are published only after descriptor validation; rejected reads discard their speculative rollups. Deferred records still advance cumulative token context.

Run the separate memory comparison while other benchmarks and builds are idle:

```sh
ORCA_BACKGROUND_LAUNCH=1 node config/scripts/codex-usage-parser-memory-benchmark.mjs --baseline-ref 51e7181850b022bae2d41091fe1229accef4bd50 --events 100000 --pairs 2 --sample-ms 35 --output docs/bug-reproductions/usage-scan-append/codex-parser-memory.json
```

The baseline bundles every project source dependency of the actual parser from the named commit; the current arm bundles and hashes the worktree dependency graph before starting workers. Each arm, scenario and round runs in a fresh Node process. Two rounds alternate baseline/current and current/baseline. The 100,000-record fixture has distinct timestamps and fixed token values. One scenario owns every record; the other defers 90% to a simulated prior owner. Both arms must preserve identical sessions, daily aggregates, owned keys and deferred state, with exact event and token totals.

Workers force two full garbage collections before parsing, every 35 ms during parsing, and after parsing with the returned result retained. Raw snapshots include heap, RSS, external buffers and elapsed time. The reported peak is the largest **sampled retained heap** increase; it does not measure absolute peak allocation or total process memory. Forced GC affects elapsed times, so this is a memory comparison rather than a speed benchmark. The artifact records runtime/platform details, fixture hash, all bundled source hashes, bundle hashes, instrumentation hashes, arm order and correctness counts.

The final [`codex-parser-memory.json`](./codex-parser-memory.json) comparison used Node 24.20.0 on darwin/arm64, an Apple M4 Max, and the 20,200,079-byte fixture. All eight fresh workers collected samples during parsing (3–12 per worker). The two counterbalanced runs measured these retained-heap increases:

| Owned records             | Baseline sampled peak (MiB) | Current sampled peak (MiB) |
| ------------------------- | --------------------------: | -------------------------: |
| All 100,000               |              44.681, 44.953 |               9.071, 7.964 |
| 10,000, with 90% deferred |                3.692, 3.746 |               1.233, 1.251 |

Every arm retained exactly 100,000 or 10,000 keys and counted events, with identical full usage-projection hashes and exact token totals. Final returned-result retention stayed approximately 6.49 → 6.51 MiB with all keys owned and 0.97 → 1.00 MiB with 90% deferred: the improvement removes intermediate event retention while preserving the result. Raw artifacts include all 16 baseline and 18 current source-module hashes; each current source and instrumentation hash matched the frozen worktree after the run. Snapshot records themselves add small duration-dependent observer overhead. A separate 1,000-record smoke passed parity but correctly flagged every measurement as final-only, so it is not used as peak-memory evidence.

`codex-memory-exploration.json` preserves the earlier actual-parser exploratory measurements: approximately 45.5 → 7.6 MiB sampled retained heap with all 100,000 keys owned, and 3.7 → 0.9 MiB with 10,000 keys owned. That earlier run overlaid only the baseline parser onto current dependencies and did not save source hashes or individual snapshots. Its limitations are explicit; the pinned full-graph comparison provides reproducible final evidence.

## Correctness coverage and limits

The final [validation record](./validation.json) captures the frozen source hashes,
8,278 passing integrated tests across 839 files, 118 performance contracts,
full typechecking, changed-code quality, formatting and artifact hash checks.
The contract workflow is configured for Linux, macOS and Windows; this local run
used macOS. [Rendered Electron evidence](https://github.com/stablyai/orca/blob/nwparker/perf-native-chat-tool-pairing/docs/bug-reproductions/native-chat-tool-pairing/rendered-validation.json)
records the rebuilt hidden app, output/task ownership and process cleanup.

Focused suites cover UTF-8 byte offsets, CRLF and tiny chunks; duplicate maxima and first metadata; keyless rows; malformed and parseable partial tails; restart after JSON persistence; location and session ties; worktree/folder attribution; owner deletion and key loss; truncation while opening; replacement and rewrite races; appends after the pinned stat; old-cache upgrade; malformed resume state; and stable short reads with single-handle cleanup and no partial ownership publication. An independent actual-source differential check ran 24 seeded append phases with 512 assistant rows, five sessions, three locations, three models, duplicate maxima, shifting metadata, invalid first dates and a JSON round trip before each resume. Sessions, daily aggregates and line counts matched cold scans after every phase.

Head and committed-boundary digests are evidence for the append-only contract, not a universal proof of every byte. Arbitrary middle-only rewrites that also grow a file while preserving the sampled windows can evade bounded evidence. Same-length dirty files take the full-parse path; changed same-length metadata during a read rejects the snapshot, and small-file validation hashes the full bytes. No new arbitrary history bound is used to conceal this limitation.

---

### Site Content Docs Index (`docs/site/content/docs/index.mdx`)

---
title: What is Orca?
description: 'A 60-second pitch: who Orca is for and when to reach for it.'
---

import { Callout } from '@/components/docs/prose'

Orca is a desktop IDE for running multiple AI coding agents side by side. Every task gets its own git worktree, its own agent terminal, and its own browser tab — so you can fan out work across Claude Code, Codex, Cursor CLI, and friends without stashing, branch-juggling, or losing flow.

## When to use Orca

- You want three agents trying the same bug in parallel and to pick the winner.
- You want to review AI-generated diffs seriously before you ship them.
- You already pay for Claude Code, Codex, or Cursor CLI and want one place to orchestrate them.
- You want agents to run remotely — over SSH, on a self-hosted Orca server, or in an on-demand VM — without giving up your IDE.

## Who it's for

Orca is designed for people who already write code for a living and want to use AI as leverage — not as a replacement. It assumes you read diffs, care about commits, and keep a worktree tidy. If you're looking for a no-code tool, Orca is not that.

## What Orca is not

- **Not a model.** Orca runs agents you already use — bring your own Claude, Codex, or OpenCode subscription.
- **Not a git replacement.** Every worktree is a real git worktree. You can `cd` in and use plain git whenever you want.
- **Not a hosted VPS product.** Orca runs on your desktop by default. Remote compute uses machines and cloud accounts you control — [SSH targets](/docs/ssh), [self-hosted Orca servers](/docs/remote-servers), or [Cloud VMs / per-workspace environments](/docs/ways-to-run#4-cloud-vms-per-workspace-environments).

<Callout title="Next steps">
  Head to [Install](/docs/install), then walk through [Your first 3-agent
  session](/docs/first-session) — the single most important page in these docs. When you're ready to
  move agents off the laptop, start with [Ways to run Orca](/docs/ways-to-run).
</Callout>

---

### Site Content Docs Browser Overview (`docs/site/content/docs/browser/overview.mdx`)

---
title: Per-worktree browser
---

import { ImagePlaceholder } from '@/components/docs/prose'

Every Orca worktree has its own browser. It's a real Chromium window — address bar, history, devtools — embedded in a pane. Tabs are scoped to the worktree, so the app you're building against stays out of the way of your other work.

<ImagePlaceholder
  src="/docs/orca-design-mode.gif"
  caption="Per-worktree browser pane with address bar and tab strip"
/>

## Controls

- Address bar with history and fuzzy URL completion. Non-URL text searches with your [Default Search Engine](/docs/settings) — the same engine the [new-tab omnibox](/docs/model/quick-open#new-tab-omnibox) uses. Prefix a query with `?` in the **+** field to force search.
- Back / forward / reload / stop. Hover the reload control for the normal-reload shortcut; right-click (or long-press) for **Reload** and **Hard Reload** with their shortcuts — hard reload bypasses the cache when you're iterating on local frontend assets.
- `Cmd-F` — find in page.
- `Cmd-T` — new tab, scoped to this worktree.
- `Cmd-Shift-T` — reopen last closed tab.
- Page links that use `target=_blank` and ordinary unnamed popups open in new Orca tabs, using the opener tab's browser profile. Named or OAuth-style popups can still open in a separate window.

## Worktree scoping

Tabs are filtered per worktree. Switching worktrees restores that worktree's browser tabs and scroll positions.

## Remote workspaces

For a workspace on a paired [Remote Orca Server](/docs/remote-servers), new browser pages render on this desktop by default while HTTP(S), WebSocket, DNS, and loopback traffic still go through the remote host. Input, selection, and popups stay native to this device without changing the page's network identity. The host indicator in the browser toolbar shows where traffic is going.

Under [Settings → Browser → Remote server workspaces](/docs/settings), choose **This device** or **Server (streamed)**. The choice applies to new pages only; it does not move a page that is already open. If a locally rendered remote page cannot start, **Reopen on server** opens a new server-hosted page at its last address (signed-in or form state may differ).

For SSH workspaces, **Browse through SSH workspace hosts** controls whether browser traffic and DNS go through the workspace's SSH host or this device. Downloads from a client-hosted remote page stay on the remote workspace host, and uploads are staged there before the page receives them.

## Link routing

Under [Settings → Browser → Link Routing](/docs/settings), choose whether http(s) links from the terminal, markdown, and editor open in Orca's per-worktree browser or the system browser.

A nested **Hold Shift…** toggle inverts that default for one click with the platform modifier (`⇧⌘-click` on macOS, `Shift+Ctrl+click` on Windows / Linux):

- When links open in **Orca**, the modifier sends one link to the system browser.
- When links open in the **system browser**, turn the toggle on so the modifier opens one link in Orca's built-in browser instead (plain click stays system).

A capability-verified paired runtime can offer the same Orca/system-browser choice for its terminal links. SSH-owned terminal links and links from older runtimes remain system-browser only. See [Terminal → Link actions](/docs/terminal#link-actions).

## Downloads

Browser downloads appear in a shelf under the toolbar while they are active or recently completed, with actions to cancel an in-progress download, open a finished file, show it in its folder, or dismiss the row.

## Share as artifact

Local HTML files opened in the worktree browser can use **Share as artifact** in the toolbar to mint a public view link through your signed-in Orca account (same opt-in gate and management surface as Markdown). Relative HTML assets are not uploaded — share a self-contained file or use absolute asset URLs. See [Settings → Artifacts](/docs/settings#artifacts) and [CLI reference → Artifacts](/docs/cli/reference#artifacts).

## Viewport-size emulation

Set a custom viewport size on a browser tab to test responsive layouts without resizing the whole pane. Orca uses Chrome DevTools Protocol device emulation under the hood, so the page sees the emulated dimensions in `window.innerWidth` and media queries.

## Automation

The browser is also scriptable by agents via the [Orca CLI](/docs/cli/overview) — `orca snapshot`, `orca click`, `orca fill`, and so on. Same browser you interact with, same tabs.

## Next steps

- [Design Mode](/docs/browser/design-mode) — turn the browser into a pointer-to-code feedback loop.
- [Browser-use profiles](/docs/browser/profiles) — run the browser with a specific login or cookie jar.

---

### Site Content Docs Cli Overview (`docs/site/content/docs/cli/overview.mdx`)

---
title: Orca CLI overview
description: Use the Orca CLI to script Orca from a terminal, manage worktrees, control agent terminals, automate the built-in browser, and install agent skills.
keywords:
  - orca cli
  - Orca CLI
  - orca command line
  - Orca skills
  - npx skills add
  - agent CLI
---

import { Callout } from '@/components/docs/prose'

The Orca CLI is the `orca` command-line interface for scripting a running Orca editor from any shell. Use it to create and inspect worktrees, drive agent terminals, open files and diffs, automate the built-in browser, run scheduled automations, share HTML/Markdown artifacts, and control Orca-native tools from scripts or AI agents.

It ships with the desktop app; register it under [Settings → General → Orca CLI](/docs/settings). On Linux the command is `orca-ide`, because GNOME Orca's screen reader already owns `/usr/bin/orca` — see [Install → Linux](/docs/install#linux).

Agents can install the matching Orca CLI skill with:

```bash
npx skills add https://github.com/stablyai/orca --skill orca-cli
# headless / no Settings UI:
orca skills install --skill orca-cli
```

See [Skills registry & MCP](/docs/cli/skills) for every installable Orca skill, including `orca skills install` / `orca skills update`.

## Install & verify

```
command -v orca
orca status --json
```

## Worktree commands

```
orca worktree ps --json
orca worktree create --repo id:<repoId> --name my-task --issue 123 --json
orca worktree create --repo id:<repoId> --name review-pr-123 --pr 123 --json
orca worktree current --json
orca worktree set --worktree active --comment "reproduced bug" --json
orca worktree set --worktree active --gitlab-issue 42 --gitlab-mr 77 --json
orca worktree set --worktree active --pr null --json
orca worktree rm --worktree id:<id> --force --json
```

For selectors, setup flags, parent/child worktrees, and the broader command map, see [Orca CLI reference](/docs/cli/reference).

## Terminal commands

```
orca terminal list --json
orca terminal read --json
orca terminal send --text "continue" --enter --json
orca terminal wait --for tui-idle --timeout-ms 30000 --json
orca terminal create --worktree path:/projects/app --command "npm test" --json
orca terminal split --direction vertical --command "npm run dev" --json
```

For tracked multi-agent work, use [Orchestration](/docs/cli/orchestration) instead of plain terminal prompts.

## File commands

Open files and diffs as tabs in their Orca worktree from a shell. They open without changing what you are looking at; add `--focus` to be brought to the file:

```
orca file open src/App.tsx
orca file diff src/App.tsx --staged
orca file open-changed --mode both
```

Use `--worktree <selector>` when the shell's current directory is not inside the target worktree.

## Browser profiles

Browser profiles isolate tab session state, so a script or agent can test with different cookies, local storage, and logged-in identities. The CLI exposes profile commands under `orca tab profile`; start with `orca tab profile list --json`, then use `create`, `set`, `clone`, or `use-default` as needed.

## Scheduled automations

Use `orca automations` to create, inspect, run, and remove scheduled Orca tasks from a shell. Start with [Scheduled automations](/docs/cli/automations) when you want a recurring prompt to run against a repo or an existing worktree.

## Artifacts

Share HTML or Markdown as public view links through the signed-in Orca account (`orca artifacts share|update|list|delete`). Publishing is **opt-in** under Settings → Artifacts. Command details: [CLI reference → Artifacts](/docs/cli/reference#artifacts).

## Browser automation

The CLI also drives the built-in browser with a snapshot-interact-re-snapshot loop:

```
orca goto --url https://example.com --json
orca snapshot --json     # returns refs like @e1, @e3
orca click --element @e3 --json
orca fill --element @e1 --value "user@example.com" --json
orca screenshot --json
```

For responsive browser checks, switch the active tab to a named device profile:

```
orca set device --name "iPhone 12" --json
orca screenshot --json
```

## Mobile emulator

The CLI can also drive an iOS Simulator through Orca's mobile emulator bridge. It is scoped to the active worktree, so agents and scripts can attach a simulator from `orca emulator list`, tap normalized coordinates, type text, send gestures, rotate the device, and shut it down without leaving Orca.

```
orca emulator list --json
orca emulator attach "<device-name-or-udid>" --json
orca emulator tap 0.5 0.7 --json
orca emulator type "hello" --json
orca emulator gesture '[{"type":"begin","x":0.5,"y":0.8},{"type":"move","x":0.5,"y":0.4},{"type":"end","x":0.5,"y":0.2}]' --json
orca emulator rotate landscape_left --json
orca emulator kill --json
```

Use `--worktree <selector>`, `--device <udid-or-name>`, or `--emulator <id>` when a script needs an explicit target.

<Callout>
  For the full command surface including tabs, waits, cookies, and frames, see [Orca CLI
  reference](/docs/cli/reference), then install the Orca CLI skill (see [Skills
  registry](/docs/cli/skills)) and point your agent at it.
</Callout>

---

### STYLEGUIDE (`docs/STYLEGUIDE.md`)

# Orca UI Style Guide

This is the **UI/visual design** doc for Orca — color tokens, typography, component selection, and UX rules. It is _not_ an architecture doc; for system-level design see code and inline comments. Token values live in `src/renderer/src/assets/main.css` (canonical); this file documents the _roles and rules_ for using them.

## Overview

Orca is an Electron desktop app for orchestrating coding agents across git worktrees. The visual identity is **monochrome and quiet** — neutral grays carry the chrome, color is reserved for state (selection ring, destructive, git decorations). The product spends most of its time hosting other people's tools (Monaco, xterm, Markdown previews), so Orca's own UI should recede and frame.

When in doubt:

- Reach for **muted/accent/border** before reaching for color.
- Reach for **CSS variables** before hardcoding hex.
- Match the nearest **shadcn primitive** before writing custom CSS.

## Source of truth

| Concern                                       | Canonical location                                    |
| --------------------------------------------- | ----------------------------------------------------- |
| Color tokens                                  | `src/renderer/src/assets/main.css` (`:root`, `.dark`) |
| Tailwind theme bindings                       | Same file, `@theme inline { … }` block                |
| Component primitives                          | `src/renderer/src/components/ui/` (shadcn-style)      |
| App typography / scrollbars / titlebar chrome | Same `main.css`                                       |

Never hardcode a hex value in component code if a variable already covers it. If a new token is needed, add it to `main.css` (both `:root` and `.dark`), expose it in the `@theme inline` block, then use it.

## Color roles

Tokens come in pairs: a **surface** and a **foreground** that meets contrast on it. Always use them together.

| Role                                     | Use it for                                                  | Don't use it for                                    |
| ---------------------------------------- | ----------------------------------------------------------- | --------------------------------------------------- |
| `background` / `foreground`              | App canvas, default text                                    | Cards, popovers, sidebar (have their own)           |
| `card` / `card-foreground`               | Panels lifted off the canvas                                | The canvas itself                                   |
| `popover` / `popover-foreground`         | Floating menus, dropdowns, hovercards                       | Inline UI                                           |
| `primary` / `primary-foreground`         | The single affirmative action in a flow (Save, Confirm)     | Decorative accents; hover states; secondary actions |
| `secondary` / `secondary-foreground`     | Lower-emphasis actions next to a primary                    | The affirmative action                              |
| `muted` / `muted-foreground`             | De-emphasized text, captions, placeholders, disabled chrome | Body copy; primary actions                          |
| `accent` / `accent-foreground`           | Hover/active backgrounds for ghost buttons and list rows    | Solid filled buttons (use `secondary` instead)      |
| `destructive` / `destructive-foreground` | Delete, discard, irreversible-action buttons; error states  | Cancel buttons (Cancel is not destructive)          |
| `border`                                 | All hairlines: dividers, input outlines, card edges         | Heavy emphasis; that's `ring`                       |
| `input`                                  | Form field background only                                  | Anywhere outside form fields                        |
| `ring`                                   | Focus-visible outlines, active selection halos              | Persistent decoration                               |
| `sidebar` (+ variants)                   | The worktree sidebar and its children                       | Other panels                                        |
| `editor-surface`                         | Background of Monaco / markdown editor panes                | App chrome                                          |

The `sidebar` family expands into `--sidebar`, `--sidebar-foreground`, `--sidebar-primary`, `--sidebar-primary-foreground`, `--sidebar-accent`, `--sidebar-accent-foreground`, `--sidebar-border`, and `--sidebar-ring` — use them inside the worktree sidebar so its hover/selected/focus states stay consistent and don't bleed into other panels. `editor-surface` is its own token (not just `background`) because Monaco and the markdown editor have a slightly darker surface in dark mode to match VS Code conventions; reach for it whenever you're rendering an editor pane.

### Git decoration colors

For diff status, file-tree decorations, and the changes view, use the git decoration tokens (mirroring VS Code's palette so users transferring from VS Code aren't surprised):

| Token                        | State          |
| ---------------------------- | -------------- |
| `--git-decoration-added`     | Added / new    |
| `--git-decoration-modified`  | Modified       |
| `--git-decoration-deleted`   | Deleted        |
| `--git-decoration-renamed`   | Renamed        |
| `--git-decoration-untracked` | Untracked      |
| `--git-decoration-copied`    | Copied         |
| `--git-decoration-ignored`   | Ignored by git |

Use these _only_ for git status. Don't reuse them for unrelated state colors — that breaks the convention.

### List rows: hover, selected, current

A common point of drift. Use these conventions for any list-style row (worktrees, command palette items, settings nav):

- **Idle:** transparent background.
- **Hover:** `bg-accent` (in the worktree sidebar, `bg-sidebar-accent`).
- **Keyboard-selected (cmdk highlight):** do **not** rely on flat `bg-accent` alone on light popover/dialog surfaces — `--accent` (#f5f5f5) is nearly identical to `--background` (#fff), so the cursor vanishes. Use the jump-palette recipe in `main.css` (`.jump-palette-item[data-selected='true']`): `color-mix` foreground into background (~12%) plus an inset ring. Expose the mix as `--jump-palette-selection-surface` when nested cutouts (status pips) must match. The `data-selected` attribute is set by `cmdk` automatically.
- **Persistent "current" / "active" row** (e.g. the worktree the user is viewing): also `bg-accent`, _plus_ a `data-current="true"` attribute so CSS or future styling can distinguish it from the cmdk highlight.
- **Don't:** hardcode `bg-[#ededed]` / `bg-[#333333]` or invent a "selected" color. Mix from existing tokens (`foreground`/`background`/`accent`) so light/dark stay aligned.

### Color mixing

When you need a tint (e.g. a 12% primary wash on hover), use `color-mix` against the existing token, not a new hex:

```css
background: color-mix(in srgb, var(--primary) 12%, var(--background));
```

This keeps light/dark parity automatic.

## Typography

- **Family:** `Geist` is loaded as a single variable woff2 (weight range 100–900). Always reach for `Geist` for sans, never `Inter` or system sans.
- **Mono:** `var(--font-mono)` — used for paths, terminal-adjacent UI, code, and anywhere monospace conveys "this is literal."
- **Body letter-spacing:** `0.01em` (set globally on `body`). Don't override per component.
- **Sizes:** Tailwind's default scale. Common sizes in this repo:
  - 11px (uppercase meta, sidebar headers, captions) — pair with `font-weight: 600` and `text-transform: uppercase` and `letter-spacing: 0.05em` for category labels.
  - 12px (sub-text, paths, secondary content)
  - 13px (sidebar items, dense list rows)
  - 14px (default body, button text in `default` size)
- **Named small steps:** `text-2xs` (11px) and `text-3xs` (10px) are theme steps in `main.css`. Use them instead of `text-[11px]` / `text-[10px]`.

### Chat type scale

The native chat uses four steps and no others. Never write an arbitrary size (`text-[13px]`) inside `components/native-chat/`. `pnpm lint` enforces this, along with the softened-foreground and focus-ring rules below (`config/oxlint-plugins/native-chat-design-tokens.mjs`).

| Step             | Default | Use it for                                                                                                 |
| ---------------- | ------- | ---------------------------------------------------------------------------------------------------------- |
| `text-sm`        | 14px    | Anything the user reads: message prose, tool and agent activity rows, card titles and bodies, the composer |
| `text-xs`        | 12px    | Labels, timestamps, counters, secondary lines under a row                                                  |
| `text-2xs`       | 11px    | Captions, attribution, uppercase category labels                                                           |
| `text-3xs`       | 10px    | Badges and mono pills                                                                                      |
| `text-chat-code` | 12px    | Code blocks, diffs, commands and tool output                                                               |

The chat root (`.native-chat-appearance`) redefines these steps as ratios of the Text size setting, so every tier scales together; `text-chat-code` follows the Code text size setting. The chat root also ties the spacing unit to the text size (4px at the default 14px), so icons, gaps, padding and row heights grow with the text. Use spacing-scale utilities (`size-3.5`, `min-h-6.5`, `pl-3.25`), not pixel lengths (`min-h-[26px]`), or the element stays fixed while the rest scales. Size an element with one of these utilities only: a CSS rule that sets `font-size` outside a layer silently overrides them.

### Chat text colour, hover and focus

- **Primary text on the chat canvas** uses `text-chat-foreground`, and `text-chat-foreground-strong` for titles and emphasis; both follow the Contrast setting. Don't soften `text-foreground` by hand (`text-foreground/85`). `text-foreground` is for text on a lifted surface inside the chat.
- **Secondary text on the chat canvas** (transcript rows, notices, labels between messages) uses `text-chat-foreground-faint`. `text-muted-foreground` is for text on a lifted surface inside the chat: cards, menus, popovers and the docks above the composer.
- **Row hover** in the transcript is `hover:bg-accent/20`. Buttons, menu items and card controls use `hover:bg-accent`.
- **Focus rings:** full-width transcript rows use the inset ring (`focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring`) so it isn't clipped by the scroll container; standalone controls use `focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring`. Inside the chat `--ring` is `--chat-focus-ring`, which clears 3:1 on the canvas; don't lower its opacity.
- **State colours in the chat:** warnings use `text-chat-warning`; diff lines use `text-chat-diff-added` / `text-chat-diff-removed`; what the reader has not seen yet uses `text-chat-unread`. They are mixed toward the foreground so they pass text contrast in light and dark. Don't use raw palette colours (`text-yellow-500`) or the git decoration tokens for text on a tinted row.

## Radius

`--radius: 0.625rem` (10px) is the base; the rest are computed (`--radius-sm` = 0.6×, `--radius-md` = 0.8×, `--radius-lg` = 1×, `--radius-xl` = 1.4×, etc.). Buttons and inputs use `rounded-md`; the `Card` primitive uses `rounded-xl`; badges use `rounded-full`. Match the existing primitive's radius rather than introducing a new one.

## Elevation & shadows

Orca uses shadows sparingly. Three levels in practice:

1. **Inset hairline** — `border` + `border` token. The default. Almost everything sits at this level.
2. **Subtle lift** — `shadow-xs` + a single-token border. Outline buttons, embedded cards.
3. **Floating** — `shadow-floating` (`0 10px 24px rgba(0, 0, 0, 0.18)`). Popovers, popups that escape the editor surface. Reserved.

Don't add a fourth level. If something needs more emphasis than "floating," you're probably reaching for the focus `ring` instead.

## Components

Use the shadcn primitives in `src/renderer/src/components/ui/` before writing anything custom. The shadcn-style wrappers in this folder follow a consistent pattern:

- Most carry a `data-slot="<name>"` attribute on their root for CSS targeting — do not strip it. (The non-shadcn helpers in this folder — `sonner` and `repo-multi-combobox` — don't follow this pattern and shouldn't be modeled when adding new primitives that should.)
- Use `cn()` for class merging. Pass user `className` last so callers can override.
- Use `class-variance-authority` (CVA) for variants when there are multiple.

### Buttons (`button.tsx`)

Variants in priority order:

| Variant       | Use case                                                           |
| ------------- | ------------------------------------------------------------------ |
| `default`     | The single affirmative action in a flow.                           |
| `secondary`   | Lower-emphasis sibling next to a `default`.                        |
| `outline`     | Toolbar / standalone actions where a filled button feels heavy.    |
| `ghost`       | Icon buttons, list-row triggers, anywhere chrome should disappear. |
| `link`        | Inline text actions inside paragraphs.                             |
| `destructive` | Delete, discard, irreversible. Never for Cancel.                   |

Sizes: `default` (36px), `sm` (32px), `xs` (24px), `lg` (40px), plus `icon`, `icon-xs`, `icon-sm`, `icon-lg`. Match the size to the surrounding row height — don't drop a `default` button into a 28px toolbar.

### Other primitives in this repo

Browse `src/renderer/src/components/ui/` for the full list. Most wrap a Radix UI primitive — exceptions are `command` (wraps `cmdk`), `sonner` (wraps `sonner`), and the visual-only wrappers (`badge`, `button-group`, `card`, `input`) which apply tokens and Tailwind utilities directly. Never reimplement headless behavior; extend the existing wrapper.

Use `DialogContent surface="security"` for every dialog that asks the user to trust, approve, run or remove code, including plugin provenance and approval, so plugin themes cannot change the trust surface's tokens. Use its `scrollable` prop when the whole dialog scrolls. Wrap a trust decision outside a dialog, such as an agent approval card or a certificate override, in `SecuritySurface`.

### Picking the right primitive

When a control has multiple plausible primitives, use this fork:

| You want…                                                    | Reach for                                  | Don't use                             |
| ------------------------------------------------------------ | ------------------------------------------ | ------------------------------------- |
| Hover-only label on an icon-only button                      | `Tooltip`                                  | `HoverCard` (too heavy), title attr   |
| Hover preview of richer content (avatar + summary)           | `HoverCard`                                | `Tooltip` (no rich content)           |
| Click-revealed menu with actions                             | `DropdownMenu`                             | `Popover` with hand-rolled list       |
| Right-click contextual actions                               | `ContextMenu`                              | `DropdownMenu` (different invocation) |
| Click-revealed surface with arbitrary content (form, picker) | `Popover`                                  | `Dialog` (it traps focus and dims)    |
| Modal that demands a decision before you continue            | `Dialog`                                   | `Popover`, inline overlay             |
| Drawer / panel sliding in from an edge                       | `Sheet`                                    | `Dialog` centered                     |
| Single choice from a known list                              | `Select`                                   | Custom listbox                        |
| Single choice with search / fuzzy filtering                  | `Command` inside `Popover`                 | `Select` (no search)                  |
| Multi-select with search                                     | `repo-multi-combobox` (mirror its pattern) | Roll a new one                        |
| Transient confirmation ("Saved", "Copied")                   | `sonner` toast                             | `Dialog`, inline banner               |
| Persistent inline status ("3 errors")                        | inline text + `Badge`                      | toast (toasts disappear)              |

If you find yourself styling around a primitive (`<Popover>` to act like a `<Dialog>`, or vice versa), stop and reconsider — the focus-management semantics differ and a future contributor will be misled by the mismatch.

### Tooltips

Tooltips exist to _name_ a control whose meaning isn't obvious from its appearance. They are not the place to teach, persuade, or warn — anything users need to read while acting belongs in the visible UI.

- **Use a tooltip when:** an icon-only button or compact chip needs a label. Toolbar icons, badges with abbreviations, truncated paths.
- **Don't use a tooltip when:** the control already has a visible label, the content is interactive (links, buttons), or the message is critical (errors, blocking warnings — those go inline).
- **Mounting:** the global `<TooltipProvider delayDuration={400}>` lives at the App root. Don't nest a second `TooltipProvider` unless you need a different delay for a tightly-scoped surface.
- **Trigger pattern:** wrap the trigger element with `<TooltipTrigger asChild>` so the tooltip's accessibility props attach to the button (not a wrapper span). This is required for keyboard focus to surface the tooltip.
- **Placement:** default `side="top" sideOffset={4}` — match the toolbar pattern in `sidebar/SidebarToolbar.tsx`. Pick a different side only when the default would clip against the viewport.
- **Shortcut chips inside tooltips:** if the action has a keyboard shortcut, append `<ShortcutKeyCombo />` rather than baking the keys into the label string. The chips render correctly per platform; baked-in strings drift.

```tsx
<Tooltip>
  <TooltipTrigger asChild>
    <Button variant="ghost" size="icon-sm" onClick={openSettings}>
      <Settings />
    </Button>
  </TooltipTrigger>
  <TooltipContent side="top" sideOffset={4}>
    Settings
  </TooltipContent>
</Tooltip>
```

### Icons

Icons come from **`lucide-react`**. Don't import a second icon library.

- **Default size:** `size-4` (16px). `Button` auto-applies this to any `<svg>` it contains via `[&_svg:not([class*='size-'])]:size-4`, so most call sites don't need to set a size on the icon.
- **`size-3` / `size-3.5`:** for metadata, captions, and dense list rows where 16px is too loud.
- **`size-7`+:** for featured/empty-state hero icons only.
- **Stroke width:** lucide's default 2px. Don't override per-icon.
- **Color:** inherit from surrounding text — `text-muted-foreground` for secondary, `text-destructive` for destructive, etc. Don't apply a token to the SVG directly when the parent already carries the right color.
- **Spinner:** the canonical loading icon is `<Loader2 className="size-4 animate-spin" />`. For 3s+ multi-step work, prefer a label that names the stage ("Cloning…" → "Installing…") over an unlabeled spinner. See _UX rule 1_.

### Keyboard shortcut chips

Use **`<ShortcutKeyCombo />`** from `src/renderer/src/components/ShortcutKeyCombo.tsx`. It renders a consistent key-cap style and inserts a `+` separator on Windows/Linux (Mac shows adjacent glyphs, no separator). It does **not** transform key strings — the _caller_ picks the platform-appropriate labels and passes them in:

```tsx
const isMac = navigator.userAgent.includes('Mac')
const mod = isMac ? '⌘' : 'Ctrl'
const shift = isMac ? '⇧' : 'Shift'
<ShortcutKeyCombo keys={[mod, shift, 'N']} />
```

See `Landing.tsx` for the canonical pattern. Don't roll a one-off `<kbd>` — kbd chips drift in shape and color across the app fast if everyone styles their own.

**Where shortcuts surface in the UI:**

- **Tooltips on icon buttons** — append the chip after the label, trailing.
- **Dropdown / context-menu items** — use `<DropdownMenuShortcut>` (or its context-menu equivalent) for the right-aligned chip; don't position one yourself.
- **Never on Cancel, Dismiss, or `link`-variant inline actions** — see _UX rule 3_.

**The label MUST match the actual binding for the platform.** If the keyboard handler reads `metaKey` on Mac and `ctrlKey` elsewhere, the chip must show `⌘` on Mac and `Ctrl` elsewhere. Mismatched chips are worse than no chip.

### Form anatomy

The pattern in `src/renderer/src/components/settings/SettingsFormControls.tsx` is the house style for any label + control + helper text. Match it for new forms:

- **Outer stack:** `space-y-3` for full-section forms (`ThemePicker`); `space-y-2` for compact single-control fields (`ColorField`, `NumberField`). Pick by density, not preference.
- **Label group:** `space-y-1` containing `<Label>` and a description in `text-xs text-muted-foreground`.
- **Control:** the shadcn primitive (`<Input>`, `<Select>`, etc.). Errors surface via `aria-invalid`; the input primitive already maps that to a destructive ring — don't paint your own.
- **Trailing metadata:** `text-[11px] text-muted-foreground` below the control (e.g., "Current: 14px · Default: 13px"), not next to the label.

### Scrollbars

Three scrollbar classes are defined globally in `main.css`:

- **`.scrollbar-sleek`** — the default thin, neutral scrollbar for sidebars, lists, popovers. Pair with `.scrollbar-sleek-parent` on a hover-target ancestor if you want the thumb to fade in only on parent hover.
- **`.scrollbar-editor`** — slightly heavier, used inside Monaco-adjacent surfaces.
- **`.worktree-sidebar-scrollbar`** — no reserved gutter: paired with `overflow-y-auto`, the scrollbar (and its width) exists only while content actually overflows, so a short list stays flush with the fixed header controls and classic-scrollbar Windows shows no arrow buttons on empty lists. The thumb stays invisible until the parent (`.scrollbar-sleek-parent`) is hovered. Used only in the worktree sidebar.

Apply one of these to overflow containers; don't write a fourth style.

## UX rules

These are the rules a contributor will most often get wrong if they're working in isolation. They apply to every UI change.

**UI copy must not overclaim.** Never imply the app has taken an action, made a decision, or observed a fact unless the code has real state or result data to support it. Use neutral process language while work is pending, and reserve result verbs like "skipped", "protected", "found", "verified", or "deleted" for actual results.

### Screen UX review rubric

Use this rubric when reviewing any Orca IDE screen, screenshot, or prototype. A good review should name the highest-impact friction first, then give concrete changes the implementer can make.

#### Review output format

1. **Top fixes:** the 3 changes that would most improve the screen.
2. **Friction notes:** specific clutter, alignment, copy, focus, or flow issues, with the affected UI element named.
3. **Suggested changes:** exact changes to layout, hierarchy, controls, copy, empty/error states, and disclosure.
4. **Keyboard and speed check:** whether the primary workflow can be completed in 1-2 actions where appropriate, with good default focus and Enter/Esc behavior.
5. **Follow-up links or states:** missing external links, acquisition actions, or persistent errors the user needs to recover.

#### What to judge

- **Progressive disclosure:** keep high-frequency actions visible and prominent. Move low-frequency actions out of the common pointer path into menus, overflow controls, detail drawers, or advanced sections. Do not make menus so long that the user has to scan unrelated actions; group or split them when they grow.
- **Action hierarchy:** the primary action must be obvious through placement, size, and `default` button styling. Put high-frequency actions at the top of menus and in the most reachable toolbar positions. Secondary and rare actions should not compete with the primary action.
- **Click count:** remove unnecessary intermediate steps. Common workflows should complete in 1-2 actions when the app already has enough information to proceed.
- **Default focus:** dialogs, popovers, and command surfaces should focus the field or primary action the user is most likely to use. If Enter submits, focus must land where Enter triggers the intended primary action. Esc should back out without adding visual noise to Cancel/Dismiss.
- **Keyboard navigation:** prefer searchable command surfaces for long option lists. Add search fields when users need to find repositories, branches, worktrees, agents, commands, settings, files, or providers from a list.
- **Shortcut labels:** show shortcut chips only for shortcuts that are actually implemented and useful at that location. Labels must match the platform binding. If a shortcut strategy is undecided, do not expose a placeholder label in product UI.
- **Alignment:** rows and columns must line up to a visible grid. Left-align text and labels for scanability; right-align numbers, counts, shortcuts, and trailing metadata when comparison matters; center-align only compact icon controls, empty states, and table cells where symmetry is the clearest read.
- **Copy quality:** displayed text must be typo-free, concise, and specific. Prefer direct verbs and concrete nouns. Remove filler like "please", "simply", "just", "you can", and generic success language that is not backed by state.
- **Dialogs and overlays:** choose a dialog size that matches the amount of input. Short confirmations stay compact; forms with multi-line text, path pickers, provider setup, or review content need a larger dialog or sheet. Floating surfaces must use the documented shadow/elevation and background treatment so they read as above the page.
- **Empty and error states:** when data is missing, show a direct action to acquire or configure that data. Use toasts for transient failures or confirmations; persist errors inline when the user needs to read, retry, copy, or act on the message.
- **External links:** add direct links when the user may need provider docs, token settings, billing/setup pages, Git provider resources, or troubleshooting context. Put links near the relevant empty state, error, helper text, or setup step instead of burying them in a generic menu.
- **Affordance:** users should be able to discover available features without intrusive education. Use familiar icons, visible hover/focus states, clear labels where needed, and tooltips for icon-only controls. Prefer simple lucide icons already used nearby over obscure alternatives.
- **Layout density:** avoid jamming controls together. Preserve breathing room around the primary workflow, reduce competing buttons, and keep toolbar groups visually distinct. Dense screens are acceptable only when grouping, alignment, and hierarchy make scanning faster.
- **Cards and containers:** cards must be visually distinct from their parent surface through the existing `card`/`border` treatment. Avoid nesting cards inside cards. If a section is not a repeated item, modal, or framed tool, consider an unframed layout or full-width band instead.
- **Side-by-side layouts:** default to row-by-row layouts for complex workflows because they are easier to align and scan. Use side-by-side layouts only when space is constrained or comparison is the point, then polish column widths, baselines, and wrapping states carefully.
- **Animation:** use subtle animation to soften expanding/collapsing content and prevent jumpy layout changes. Animation should clarify continuity, not decorate. Respect reduced motion element by element: turn movement off with `motion-reduce:` (slides, rotations, size changes); give spinners, pulses and progress indicators a still state that still says "working" (`LoadingSpinner` does this for spinners) instead of freezing them; short opacity fades can stay. Scripted scrolls ignore CSS, so pass them through `motionSafeScrollBehavior()`.
- **SSH and latency:** assume actions may run remotely. Disable submit controls immediately, delay visible loading feedback when appropriate, and keep focus stable while remote data arrives.

### 1. Match in-flight feedback to perceived duration

The right question isn't _"should this control change while it's working?"_ — it's _"how long does the action take, and what does the user need to know during that time?"_

| Duration           | Feedback                                      |
| ------------------ | --------------------------------------------- |
| 0–100 ms           | None. Anything visible reads as a glitch.     |
| 100 ms–1 s         | Disabled state only.                          |
| 1 s–3 s            | Disabled + spinner or label swap.             |
| 3 s+ or multi-step | Stage labels, progress, optional reassurance. |

Two corollaries:

- **Pre-reserve any space you'll later occupy.** If a control may swap to a longer label or grow an icon, fix its footprint up front (use `width`, not `min-width`). A control that resizes mid-action looks broken even when the action succeeded.
- **Don't pick worst-case feedback for everyone.** If the action is fast locally and slow remotely (SSH), defer the visible loading state by ~200ms. Local users see nothing; remote users get appropriate feedback. Bind the _disabled_ state immediately (so double-clicks don't double-submit) and the _visible_ state on a timer.

### 2. Look for sibling components before designing in isolation

If your component has a sibling — same domain, overlapping behavior, often visible at adjacent moments in the same flow — the two should read as one design. Same icons, same shortcut conventions, same submit semantics. A user moving between them shouldn't perceive a seam.

This is _not_ "match every existing pattern." Some repo patterns are debt and copying them spreads the debt. The narrower claim is about _adjacent_ components. Diverging from a sibling needs a reason: either the sibling is wrong (fix both) or the new component has a real difference in role (commit to it).

When there's no sibling, match the surrounding chrome — button sizes, icon weights, copy tone — and don't manufacture a sibling from a screen the user will never correlate with this one.

### 3. Don't overload the back-out path

`destructive` is for actions that lose data or can't be undone. **Cancel, Dismiss, Close, and Discard are not destructive** — they back the user out of an in-progress action and should stay quiet (default ghost button, no color, no keyboard chip, no animated affordance). Save the visual weight for the affirmative action so the two don't compete. Keyboard handlers can still honor Esc; the visible decoration is what stays minimal.

## Cross-platform

Orca runs on macOS, Linux, and Windows. Every UI change must hold up on all three, in both light and dark mode.

- **Modifier keys:** Never hardcode `e.metaKey`. Use `navigator.userAgent.includes('Mac')` to choose `metaKey` on Mac and `ctrlKey` on Linux/Windows. Electron menu accelerators should use `CmdOrCtrl`.
- **Shortcut labels:** Display `⌘` / `⇧` on Mac; display `Ctrl+` / `Shift+` on other platforms. The label must reflect the actual binding for that platform.
- **Window chrome:** macOS shows traffic lights; the titlebar reserves an 80px gutter (`titlebar-traffic-light-pad`) so they don't overlap content. Don't put hit targets in that band on Mac.
- **SSH:** Many users run Orca on a remote machine. Loading states, focus management, and animations must hold up under 50–200 ms of extra latency. Test under simulated latency (or actual SSH) — local-only verification isn't enough. See _UX rules → 1_.

## When this guide is silent

If you have a UI question this doc doesn't answer:

1. Look at adjacent code in `src/renderer/src/components/` for the closest sibling, and follow its lead.
2. Check `src/renderer/src/components/ui/` for a primitive that already encodes the pattern.
3. If it's a token question, `main.css` is canonical — use what's there, or add a new one in both light and dark.
4. If none of those resolve it, ask the user before inventing.

---

### Agent Skill Sharing Implementation Checklist (`docs/agent-skill-sharing-implementation-checklist.md`)

# Agent skill sharing implementation checklist

Status: implementation and validation in progress.

Last updated: 2026-08-12.

Implementation baselines captured by this checklist update:

- Orca implementation: `skills-share` through `607e31dd04`; no PR.
- Orca Cloud: bundle smoke PR `#329` merged as `eddb144afe`; generation-aware recovery PR `#330`
  merged as `8045c85dad`; encrypted physical-host credential PR `#336` merged as `8fce3298ef`;
  kill-switch discovery PR `#342` merged as `c2bef2ff20fb`.
  Windows device-name validation PR `#343` merged as `dbb14a658cbc`; explicit disabled production
  bootstrap PR `#352` merged as `2a23f6ace5`; publisher-identity omission PR `#355` merged as
  `b3213bd34d1b224d8a3b11527eceaac883965400`; production enablement PR `#357`, bounded OIDC smoke
  PR `#358`, monitor IAM PR `#359`, and monitor release-ownership PRs `#360`/`#361` are merged
  through `d8605edde72229c5d00d22c8bd6853a13c1a4aaa`.

Validated so far:

- Current release hardening cancels bundle extraction after partial bytes exist and removes the
  extraction directory, durable journal, and managed-install state. Four repeated focused runs and
  the real SIGKILL recovery test passed. Signed GCS uploads use the earlier of signed-policy expiry
  and a 15-minute cap, including their archive read stream, so slow valid uploads can finish while
  stalled uploads remain bounded.
- Local and headless managed-skill operations now wait for the one startup transaction-recovery
  promise before reading or changing install state. Codex uses its documented global
  `~/.agents/skills` root, and provider removal derives allowed roots from the registry so aliases
  remain removable after an agent is no longer detected. The combined focused suite passed 74
  tests across 12 files, including fake-timer slow-upload and controlled-recovery regressions.
- The reported desktop install failure was a strict main-process IPC schema rejecting the
  renderer's additive `providers` selection before bundle verification. All four install schemas
  now accept the bounded field, and focused schema/provider tests pass. The original production
  link resolves as a valid one-skill bundle and returns a fresh same-version local download grant;
  the signed URL and private metadata were not logged or printed during verification.
- The final reliability hardening at `607e31dd04` adds a 60-second Cloud request deadline;
  cancellable, journaled bundle extraction; bounded package creation, verification, and preview
  fanout; atomic share-preparation admission and abandoned-archive cleanup; process-owned download
  staging; SSH startup recovery; and additive paired-client cancellation-result negotiation. The
  focused suite passed 78 ordinary tests plus a real process-death recovery test. Full Node, CLI,
  and web typechecks, repository lint and reliability gates, changed-file formatting, and diff
  checks passed. Mobile-facing wire changes remain additive, and no mobile app surface changed.
- A final performance and React lifecycle review found one stale-response race in managed-install
  inventory: a slower previous machine could overwrite the newly selected machine's list. The
  generation fence and regression test now preserve destination ownership; bundle busy state also
  reaches the parent close guard in the initiating event instead of a mirror effect. The complete
  renderer skill/settings slice passed 122 tests across 18 files, all Node/CLI/web typechecks,
  focused Oxlint, changed-code quality, max-lines, diff, and localization gates.
- The final local readiness pass at `9fcd72e327` passed 489 skill-domain tests across 73 files
  with 11 intentional platform skips, all Node/CLI/web typechecks, localization catalog,
  extraction and coverage, max-lines, changed-code quality, React Doctor, formatting, and diff
  checks. No new P0, P1, or P2 finding was proven by the crash/retry, security, performance,
  functional, mobile-backcompat, or Windows/Linux review.
- Multi-select sharing now preserves hidden selections, reconciles changed discovery scans,
  visibly explains ineligible skills, and blocks duplicate names before review. Exact complete
  managed bundles publish a new immutable version to their existing package; ambiguous or partial
  selections safely create a new package. Managed bundles with an active link can reopen the
  destination-aware installer for another machine; the final renderer slice passed 114 tests.
- The mobile/backcompat review found no persisted mobile keys, routes, deep links, or required
  legacy fields changed. Skill RPC methods and capabilities are additive and capability-gated;
  the cross-platform review found production paths use host path APIs and platform checks, with
  POSIX-only literals confined to tests or WSL guest execution.
- Final bearer-link changes pass 143 Orca Cloud API tests with one opt-in skip; 58 focused Orca
  tests; four mixed-version wire tests; desktop/node/web typecheck; localization catalog,
  extraction, and coverage; changed-code quality; max-lines; and diff checks.
- The final Cloud change passed PR run `31564069382` and merged-`main` run `31564235724`, including
  the full monorepo build, lint, typecheck, tests, Terraform format, and Terraform validation.
- The opt-in desktop staging harness now publishes a bearer share without retired audience fields;
  Node typecheck and Playwright test discovery pass without starting Orca or a browser login flow.
- Local Node and web typechecks, changed-code quality gates, 94 skill-domain files with 770 tests
  passed and 3 skipped, 134 Orca Cloud API tests with one opt-in integration skip, the full Cloud
  monorepo test/typecheck/lint/build gates, and isolated Terraform formatting and validation.
- Final Orca validation passed repository-wide lint, all Node/CLI/web typechecks, the production
  desktop/native build, the Node 18 Relay bundle contract, 78 focused skill files with 557 tests
  passed and 10 platform skips, and the mixed-version wire suite. The full repository run passed
  49,977 tests with 112 skips; its three environment/timeout failures passed as a 124-test rerun
  after removing Orca-injected Git config variables and rerunning the slow macOS PTY case.
- Skill sharing and installation renderer copy now passes the localization catalog, extraction,
  and coverage gates without a max-lines exception.
- Remote upload sessions actively expire abandoned bytes after their bounded idle lifetime, and
  begin/chunk/commit retries resume from the host-acknowledged offset under one stable transfer ID.
- Paired skill install and upload cancellation now reaches queued calls, capability probes,
  one-shot sockets, cached request sockets, and shared-control requests instead of waiting for the
  five-minute transfer timeout. Focused coverage proves queued work never starts, pending request
  admission is released, late responses are ignored, one-shot sockets close, and unrelated cached
  or shared-control requests survive; 84 transport/service tests and Node typecheck passed.
- Standalone and bundled package names reject Windows reserved device names in both Orca and Cloud,
  preventing a package published on macOS/Linux from failing only when installed on Windows.
- Identical archives are deduplicated only within an owner tenant. PostgreSQL object identity and
  tenant-hashed GCS keys prevent cross-tenant existence or finalization-timing disclosure.
- Native Windows package/install/recovery/copy-fallback tests and Node typecheck on `windows 2`;
  the current slice passed 284 tests with 21 intentional platform skips across 43 files.
- The final combined physical `windows 2` run at `44d1266641` enabled native-Windows, real-process,
  and Ubuntu 24.04 WSL coverage together: 67 files passed, 449 tests passed, and 17 intentional
  platform skips remained. Commit `97b831dd17` made Windows recovery assertions path-semantic and
  time-deterministic. Commit `44d1266641` fixed WSL alias reconciliation after proving native UNC
  `lstat` can miss an existing distro symlink; WSL now inspects the alias in-distro and creates new
  aliases with `ln -sT`. The checkout remained clean and no `orca-skill-*` directory remained in
  WSL `/tmp`.
- Native Windows transaction validation passed all 21 cases at `6d3ce582aa`, including a real
  canonical install beyond `MAX_PATH` while the host's `LongPathsEnabled` policy remained disabled.
- Real Ubuntu 24.04 WSL global, guest-workspace, and `/mnt/c` workspace transactions, including
  alias placement, interrupted update recovery, conflict preservation, update, and removal.
- Real Ubuntu 24.04 WSL provider detection and POSIX semantics, including case-sensitive names,
  `0600` regular files, and `0700` executable files.
- Exact portable package digest and archive SHA-256 goldens passed on macOS and native Windows
  despite different bundled zlib versions (`1.2.12` and `1.3.1`).
- The focused deterministic package suite passed 26/26 tests in Debian Bullseye/glibc 2.31 on
  native Linux ARM64 and emulated Linux x64, plus the real Ubuntu 24.04 WSL distro on `windows 2`;
  every host produced the same package digest and archive SHA-256 golden.
- The same 26/26 portable package suite passed at `109272dab1` on a disposable native x86_64
  Ubuntu 22.04 GCE host using Linux 6.8 and Node 24.18. The VM had no service account or scopes;
  its boot disk, uploaded Git bundle, and temporary project SSH metadata were removed afterward.
- The exact x64 Electron directory package at `f1dccb4f42` passed its after-pack ABI scan for all
  18 bundled native binaries, then loaded packaged `node-pty` and spawned `/bin/sh` inside an
  Ubuntu 20.04/glibc 2.31 container. The physical run corrected the gate to use Linux's actual
  `orca-ide` executable and `resources/node_modules` layout. A native ARM64 package built from
  exact commit `abe92d565b` on an isolated `aarch64` GCE host then passed the same 18-binary
  Ubuntu 20.04/glibc 2.31 ABI gate and packaged `node-pty` load/spawn smoke. The ARM64 VM had no
  service account or scopes; its auto-delete boot disk was verified absent, and its local Git
  bundle was moved to Trash after the clean-checkout proof.
- Real native Windows global, linked Git-worktree, and plain folder installs with spaces and
  non-ASCII paths, plus privacy-safe install diagnostics and owner-private staging tests.
- Docker-backed SSH relay installation from the real Electron client passed global, remote Git
  worktree, and remote plain-folder scopes, including client-mediated upload, unchanged preview,
  managed-install listing, safe removal, and remote-target Cloud authorization.
- Real paired-runtime installation passed from an isolated Electron client to both a headed
  desktop host and headless `orca serve` host. The host owned global and Git-worktree installs;
  the headed host also owned a plain-folder install. The client's home remained untouched.
- A fresh post-resilience E2E run passed all three headed paired-runtime, headless `orca serve`,
  and Docker SSH journeys. Focused failure injection also proves invalid gzip classification,
  old-version preservation after `EACCES` and `ENOSPC`, bounded recovery from a lost final install
  response, and validated structured SSH failure data without arbitrary error-data disclosure.
- Isolated staging bucket, IAM, `orca_skills` database and principal, enabled secret version, 11
  log metrics, four alerts, and one dashboard in `onorca-cloud-staging`. The complete targeted
  skill-infrastructure plan is zero-diff at Cloud `41ef335`.
- PostgreSQL migration startup is serialized by a transaction-scoped advisory lock. Transactional
  DDL rollback and eight concurrent startup callers passed against ephemeral PostgreSQL 16 and 17,
  with exactly one recorded schema version.
- Cloud PR `#349` merged as `6a812c5e11` after proving an older API migration runner preserves a
  newer additive column/table and continues using its existing SQL. Build, lint, typecheck, tests,
  Terraform initialization, and validation passed; no service or infrastructure was deployed.
- Cloud PR `#350` merged as `c22007384a` and added a required disposable PostgreSQL 16/17 drill.
  Real old and future-additive Node processes coexist and write against one database, the database
  is dumped and restored, and both processes write again after restoration. PR run `31624484826`
  and merged-main run `31624717796` passed every code, Terraform, and database job; no staging or
  production resource was accessed or deployed.
- The immutable API deploy path stamps every artifact and skill literal variable plus exactly one
  approved skill-database secret, and rejects secret, Cloud SQL, service-account, scaling, CPU,
  memory, volume, mount, probe, or unexpected-environment drift before traffic promotion.
  Staging intentionally carries all four skill controls as `true`; production remains `false`.
- The route-disabled baseline, API revision `orca-cloud-api-staging-00039-rek`, first proved the
  database-ready startup and anonymous `401`/`404` boundaries with zero revision errors.
- Cloud PR `#313` passed run `31533988351`, merged as `d0bf926`, and passed `main` run
  `31534229437`. Guarded wake run `31534564230` restored exactly the two configured Relay cells.
- Auth deploy run `31535179937` promoted revision `orca-cloud-auth-staging-00017-dug` at 100%
  traffic. Health, JWKS, exact GitHub OIDC claim constraints, and zero deployment-window errors
  passed.
- The historical ACL-era API deploy run `31535438327` promoted revision
  `orca-cloud-api-staging-00042-hef` at 100%
  traffic with `authenticatedSmoke: true` and `skillSmoke: true`. The smoke covered artifact
  lifecycle; skill upload/finalize/download; two immutable versions; recipient and outsider
  authorization; local and remote grants; rollback selection; expiry; revocation; package
  deletion; and signed-object cleanup.
- Cloud PR `#317` passed both required checks and merged as `be00db10`. Its historical ACL-era
  deploy run `31546194596`
  promoted revision `orca-cloud-api-staging-00048-xom` at 100% traffic with
  `authenticatedSmoke: true` and `skillSmoke: true`; canonical health and the immutable image
  digest independently matched after promotion.
- The signed-in macOS desktop staging journey reached upload, finalize, durable share creation,
  and global install after replacing an invalid colon in the audience-derived idempotency key.
  The retry test now requires a stable path-safe key, and the live test records the package ID
  before publication so a failed share can still clean up its finalized package. Its update phase
  uncovered same-name discovery ambiguity; the harness now selects the exact workspace source
  directory instead of the first same-named global skill. A final full live rerun remains open.
- The serving API has all four controls `true`, zero revision errors, and privacy-safe structured
  request logs. The log field inventory contains no principals, credentials, signed URLs, object
  paths, or private contents; a sensitive-value scan returned zero matches.
- Cloud PR `#314` removed deploy-owned Auth release metadata from Terraform ownership, passed run
  `31536141357`, merged as `4e91cf9`, and passed `main` run `31536329459`. The targeted Auth/API
  plan then reported zero intended changes. Known SQL and artifact-bucket drift was excluded and
  never applied.
- Cloud PR `#320` passed both required checks and merged as `0579cc1a71`. Targeted Terraform added
  only `google_logging_project_exclusion.skill_share_bearer_request_urls` in staging: one addition,
  zero changes, and zero deletions. This prevents per-link Cloud Run request URLs from entering
  log storage while retaining route-template application telemetry.
- Guarded wake run `31564370807` prepared the reviewed staging topology. Deploy run `31564803943`
  promoted `orca-cloud-api-staging-00051-yoq` at 100% traffic with immutable image digest
  `sha256:511c0196511d2079bd9138092ad3cf065304b46a089b02d8df9318e0ae2e656a` and reported
  `authenticatedSmoke: true` and `skillSmoke: true`.
- Candidate and canonical smoke produced 60 privacy-safe route-template events covering owner
  upload/finalize/share creation/inventory/revocation and anonymous pinned/latest preview,
  local/remote download grants, version selection, expiry, uniform unavailable responses,
  package deletion, and object cleanup. Anonymous requests with no Authorization header and an
  invalid header both returned the same `404 skill_share_not_found` response with `no-store`.
  The serving revision had zero error logs and Cloud Logging retained zero per-link platform
  request logs.
- Guarded sleep run `31546908228` stopped all three Relay cells after the physical staging work.
  Independent GCP reads verified Cloud SQL policy `NEVER`, every MIG target at zero, and both
  managed Cloud Run minimums at zero.
- Final guarded sleep run `31565009141` restored the same low-cost state after the bearer rollout;
  independent reads found Cloud SQL `NEVER`/`STOPPED` and C1, C2, and C3 at target size zero.
- Cloud PR `#329` passed required checks, merged as `eddb144afe`, and passed merged-main run
  `31569732482`. Browser-free desktop run `31569902499` then passed bundle publish v1, install,
  local modification preservation, v2 update, rollback, revoke, installed-copy preservation, local
  removal, and Cloud package deletion without opening a login page.
- Cloud PR `#330` passed required run `31578720429`, merged as `8045c85dad`, and passed merged-main
  run `31578942857`. Guarded wake `31579195133` brought SQL and all three Terraform-owned cells up;
  deploy `31579844413` promoted `orca-cloud-api-staging-00057-kat` at 100% traffic with immutable
  digest `sha256:f61745b21d00b111087620ab1108c9a96e9863ec9e902a424aaad01e9e945605`.
- Recovery smoke `31580071168` published an isolated bundle, soft-deleted the exact published GCS
  generation, restored it with `ifGenerationMatch=0`, verified immutable metadata, transactionally
  repointed every matching database reference, verified the unlisted bearer download, and deleted
  the probe. No ephemeral recovery job or live probe object remained.
- Guarded sleep `31580339694` returned staging to low cost. Independent reads verified Cloud SQL
  `NEVER`/`STOPPED`, C1/C2/C3 target size zero and stable, and API/Auth min scale zero. Production
  was not touched.
- Cloud PRs `#332` through `#335` added the staging-only 12-by-30 finalization load gate, narrowly
  authorized its OIDC identity, fixed deterministic fixture ordering, and made retry settlement
  cleanup-safe. Their merge commits were `ac85d0690e`, `53559bd644`, `7e1d50a84b`, and
  `bb8bf8b9ac`; merged-main verification passed through run `31586525326`.
- Auth deploy `31584318896` promoted `orca-cloud-auth-staging-00021-tuq` with digest
  `sha256:e17075d69dca36df427f02fa515481f359c8f67397c5ab16de9759f7f949c6be`; candidate and
  canonical smoke passed with one 100%-traffic revision and no remaining candidate tag.
- Load run `31585710645` exercised 12 concurrent bundles with 30 skills and 3,949,317 extracted
  bytes each. Two finalized immediately and ten returned explicit saturation; client p95 was
  1,514 ms. Aggregate utilization stayed bounded: request p95 1,401.06 ms, API CPU 8.95%, API
  memory 26.95%, one API instance, database CPU 10.89%, database memory 25.59%, and seven
  connections. A saved, exact one-add Terraform plan restored the declared
  `roles/monitoring.viewer` binding for the staging deploy identity before those aggregate metrics
  were read; it changed no service, traffic, SQL, storage, Relay, or production resource.
- Cleanup run `31586684354` removed the exact failed-wave package set through normal package DELETE
  routes. The two remaining quarantine archives were manifest-proven as 30-skill fixtures for
  suffix `013756ec4740` and deleted with exact GCS generation preconditions; no live object from
  that run window remained. GCS soft delete keeps the operation recoverable.
- Guarded sleep `31587083752` returned staging to low cost. Independent reads verified Cloud SQL
  `NEVER`/`STOPPED`, C1/C2/C3 target size zero and stable with no current actions, and API, Auth,
  and Relay active-revision minimums at zero. Production was not touched.
- Cloud PR `#336` added a browser-free physical-host credential handoff restricted to the exact
  `main`-branch staging workflow identity. It encrypts only the ten-minute owner token to a
  one-time RSA-3072 key, grants no GCP permissions, retains ciphertext for at most one day, and
  passed PR run `31588982191` plus merged-main run `31589194501` as merge `8fce3298ef`.
- Guarded wake `31589384191` restored SQL and the two configured Relay cells without waking C3.
  Auth deploy `31589963244` promoted `orca-cloud-auth-staging-00025-zuz` at 100% traffic with
  immutable digest `sha256:2b5cb04060a871f372298786dbed681054a076f5cf75966ea5e2130403db7254`.
- The physical `windows 2` staging journey passed in 27.6 seconds using credential run
  `31591275227`. It installed v1 into the Windows-owned global path with the published digest and
  no macOS fallback, updated only that host to v2, preserved independent local version selection,
  rolled Windows back to v1, proved revocation left its installed copy intact, removed both host
  and local installs, and deleted the Cloud package. The encrypted artifact and local one-time key
  material were deleted immediately after use.
- The staging skill bucket independently reports the exact age-one-day Delete lifecycle restricted
  to `uploads/`, seven-day soft deletion, uniform bucket access, and public-access prevention. Its
  oldest live quarantine object was created at `2026-08-11T22:49:34Z`, so lifecycle execution—not
  configuration—is still time-gated.
- Guarded sleep `31592709817` completed at Cloud `8fce3298ef`. Independent reads verified SQL
  `NEVER`/`STOPPED`, all three Relay MIGs stable at target zero with no active actions, and API,
  Auth, and Relay active-service minimums at zero.
- Real process-death recovery now kills the transaction process after partial extraction and
  immediately before and after every durable install/removal journal transition. All 17 macOS
  cases passed, including extraction cleanup, dead-lock reclamation, receipt/filesystem agreement,
  and absence of transaction debris; the release workflow requires the same suite on macOS,
  native Windows, and Ubuntu 20.04/glibc 2.31.
- Guarded wake `31603249983` restored only the two configured staging cells. A disposable
  no-service-account Ubuntu 20.04/glibc 2.31 VM then passed the browser-free SSH staging lifecycle
  in 31.3 seconds: publish v1, host-owned install with exact digest, v2 update, managed-install
  verification, rollback, revocation with installed-copy preservation, local removal, and Cloud
  package deletion. The remote install, Orca SSH target, encrypted credential artifact, one-time
  keys, and VM were removed. Guarded sleep `31604391897` passed; independent reads verified SQL
  `NEVER`/`STOPPED` and C1/C2/C3 target zero, stable, and reached. Production was untouched.
- Guarded wake `31634214856` restored the two configured cells at Cloud `dfb8359ff5`. An isolated
  native `Darwin arm64` OpenSSH target with a separate temporary home then passed the full staging
  lifecycle in 20.1 seconds: publish v1, SSH-owned install, v2 update, managed-version checks,
  rollback, revocation with installed-copy preservation, removal, and Cloud package deletion.
  This corrected the staging oracle to accept absolute POSIX homes outside `/home`. The GitHub
  ciphertext artifact was deleted immediately; the SSH service, relay, install, temporary home,
  and one-time keys were removed. Guarded sleep `31635145830` passed; independent reads verified
  SQL `NEVER`/`STOPPED`, all three MIGs stable and reached at zero with no active actions, and
  API/Auth/Relay minimum scale zero. Production was untouched.
- Guarded wake `31605729090` restored the same configured topology. An isolated headless Orca host
  with a separate home/profile then passed the full paired-runtime staging lifecycle in 12.5
  seconds, proving host-owned install, update, managed-install state, rollback, revocation
  preservation, removal, and Cloud deletion without local fallback. The paired environment,
  install, package, encrypted credential artifact, and one-time keys were removed. Guarded sleep
  `31606600532` passed; independent reads again verified SQL `NEVER`/`STOPPED` and all MIGs at
  stable, reached target zero.
- Guarded wake retry `31630802215` succeeded at Cloud `dfb8359ff5`; the additional physical WSL
  Cloud journey was then waived as duplicate release evidence. The first guarded sleep attempt
  `31631288043` failed closed during Terraform provider initialization on a transient upstream
  `503`, before the mutation step. Retry `31631379891` succeeded. Independent reads verified SQL
  `NEVER`/`STOPPED`, all three MIGs stable and reached at target zero with no active actions, and
  the API, Auth, and Relay active-revision minimums at zero. Production was untouched.
- Cloud PR `#355` removed internal publisher and organization identifiers from anonymous skill
  responses, passed every code, Terraform, and PostgreSQL 16/17 check, and merged as
  `b3213bd34d1b224d8a3b11527eceaac883965400`. Guarded wake `31639920301` prepared staging. Deploy
  `31640677572` promoted `orca-cloud-api-staging-00060-qay` at 100% traffic with immutable digest
  `sha256:31cb0a91a7abf1f82e4de08bd31e98fbd71519adc731a005fc95f60480658f73`; authenticated and skill
  candidate/canonical smoke passed. The unrelated post-promotion storage-monitor image update
  lacked `iam.serviceAccounts.actAs`; the unchanged prior monitor image remains serving, and the
  API promotion and verification completed successfully. Guarded sleep `31640935616` passed in
  8m51s. Independent reads verified SQL `NEVER`/`STOPPED`, C1/C2/C3 stable and reached at target
  zero, and the API at minimum scale zero with revision `00060-qay` retaining 100% traffic.
  Production was untouched.

Rollout gate: staging infrastructure, OIDC owner identity exchange, anonymous bearer lifecycle,
owner management, browser-free desktop bundle lifecycle, privacy-safe logging, published-object
recovery, bounded finalization load, and guarded rollback-capable deployment passed. Production
infrastructure and the unlisted-link API are live with all four controls enabled after successful
authenticated candidate and canonical lifecycle smoke. Native Windows, real Ubuntu 24.04 WSL
filesystems, physical Ubuntu 20.04 and
macOS ARM64 SSH, and paired non-Windows staging passed. On 2026-08-12, the owner accepted the
remaining live WSL-to-staging journey as duplicate evidence after the combined 449-test physical
Windows/WSL run and native-Windows staging lifecycle; this does not treat WSL semantics as
identical to macOS. The user-driven signed-in desktop and real-host production journey, supported
Windows SSH, and the quarantine lifecycle deletion remain. The shared staging data plane is asleep.

This checklist turns the architecture plan into ordered implementation and release work. A checked
item means evidence exists in code, tests, reviewed infrastructure, or release documentation; it
does not mean the surrounding phase is complete.

## Definition of done

- [x] A user can select one or many private local skills, publish one immutable Skill Bundle, and
      receive one durable Orca share URL.
- [x] Anyone with an active unlisted link can inspect a bundle without signing in, choose all or a
      subset of its skills, and install them globally or into a Git worktree or plain folder
      workspace.
- [x] Installation works on local macOS, Linux, native Windows, WSL, paired Orca runtimes, and
      supported SSH targets.
- [x] The portable archive root conforms to Agent Plugins 1.0.0 for skills-only packages and keeps
      Orca integrity metadata in `dev.orca.skill-sharing/manifest.json`.
- [x] One canonical `.agents/skills/<skill-name>` copy is installed for each selected skill;
      provider-specific placements are reconciled for explicitly selected agents, defaulting to
      detected agents when no selection is supplied.
- [x] Install, update, rollback, and removal preserve modified or unowned local content unless the
      user explicitly approves replacement.
- [x] Package ingestion rejects unsafe archives before any destination mutation.
- [x] Interrupted operations recover to a complete previous or requested version.
- [x] Private package bytes remain access-controlled, published versions persist until deletion,
      and incomplete uploads expire automatically.
- [x] Mixed-version clients and remote hosts fail safely through capability negotiation.
- [x] Cloud resources are Terraform-owned, monitored, recoverable, and protected by independent
      upload, download, and remote-install kill switches. Targeted Terraform is zero-diff; staged
      generation recovery passed; dashboards, alerts, and runbooks cover the data plane; and Cloud
      PR `#342` proves all three mutation controls can be disabled independently while unlisted
      preview remains available.
- [x] The existing Skills page supports multi-select sharing, selective installation, per-skill
      conflicts/results, and installed-bundle management; Settings → Share Skills provides the
      authenticated owner inventory for copying and revoking active links.

## 0. Confirm scope and ownership

### Product and legal decisions

- [x] Record `vercel-labs/skills` as a behavioral reference only.
- [x] Record upstream baseline commit `c6f69c631292444cc541ac6d91e2226b0ff247da`.
- [x] Decide not to copy upstream source, tests, fixtures, registry data, or path tables.
- [x] Decide Orca will not depend on the upstream CLI or an unsupported programmatic API.
- [x] Decide V1 accepts Orca package sources only; Git, npm, and community registries remain with
      existing tools.
- [x] Decide every Skill Bundle share is unlisted and reachable only through its durable link; do
      not add search, browsing, organization-library, marketplace, or contained-skill indexes.
- [x] Record Agent Plugins 1.0.0 commit `bd383552095128f6effe895b9257cfd580a6d179`
      as the portable-format reference.
- [x] Decide to implement from the specification without copying its CC BY prose or vendoring its
      Apache-licensed schemas.
- [x] Treat the archive as a Skill Bundle, not an Orca plugin product or executable plugin runtime.
- [x] Limit V1 bundles to skills; exclude MCP servers, hooks, processes, connectors, and
      permissions.
- [ ] Review the architecture decision record covering the reference-only boundary with
      engineering and legal owners.
- [x] Add a review check that flags any proposed copied or mechanically translated upstream
      material so attribution and licensing can be reconsidered before merge.
- [ ] Assign desktop, runtime, Cloud API, Terraform, security, design, and release owners.
- [ ] Confirm the first release ends after WSL and SSH support; keep team reconciliation as a later
      product milestone.

### Product semantics

- [x] Confirm private means unlisted and protected by an unpredictable revocable bearer link, not
      end-to-end encryption from Orca Cloud operators.
- [x] Confirm a published immutable version persists until package or version deletion; it has no
      age-based object expiry.
- [x] Confirm an unfinished upload grant expires after 15 minutes and its quarantine object is
      deleted after one day.
- [x] Confirm a durable share URL contains a bearer credential for a revocable authorization
      record, not a GCS object or permanent blob grant.
- [x] Confirm a signed download grant lasts five minutes and is the maximum share-revocation lag.
- [x] Confirm updates create immutable versions and never mutate existing package objects.
- [x] Confirm rollback installs a selected prior immutable version.
- [x] Confirm revoking or deleting a Cloud share does not silently remove existing local installs.
- [x] Confirm sharing and updating use an artifact-like flow—preview, upload, unlisted durable
      link, update, revoke/delete—while retaining skill-specific version, trust, and install
      semantics.
- [x] Confirm recipients do not sign in; publishing and managing owned links still require sign-in.
- [x] Confirm revocation blocks future resolution/download grants but does not remove installed
      copies.
- [x] Add a Settings section named **Share Skills**, parallel to **Artifacts**, without adding any
      index or discoverability surface.
- [x] Treat the authenticated owner-only active-link inventory as management, not recipient
      discoverability; never expose it anonymously or through browse/search APIs.
- [x] Confirm V1 has no checked-in workspace desired-state lockfile and no automatic fleet-wide
      installation.
- [x] Confirm one skill is a one-item bundle and a bundle may contain large selections such as 30
      skills behind one durable link.
- [x] Keep bundle identity, access, versions, transport, update/rollback, revocation, and deletion
      at bundle scope.
- [x] Keep installation selection, conflicts, provenance, modification protection, and outcomes at
      skill scope.
- [x] Keep loose-skill installation as the universal/default path and treat provider-native plugin
      adapters as optional compatibility outputs.

### Existing-code and provider research

- [x] Inventory current skill discovery, `observeSkillPackage`, package identity, topology, and
      freshness code; record reusable contracts and missing behavior.
- [x] Inventory plugin staging, provenance, lock serialization, and Windows rename retry designs;
      extract only primitives that both domains can name and test accurately.
- [x] Inventory current desktop IPC and remote skill RPC registration.
- [x] Inventory existing SSH execution and file-transfer providers and identify the host-side
      installer entry point.
- [x] Research official documentation for every initially supported agent's global and project
      skill directories.
- [ ] Verify each provider path with a real installation on every supported platform.
- [x] Record whether each provider reads `.agents/skills` directly, how it is detected, and which
      alias mechanisms it supports.
- [x] Add a normal reviewed maintenance process for provider registry changes; do not create an
      upstream synchronization job.

## 1. Define package and install contracts

### Bundle contract replacement

- [x] Replace the unpublished single-skill envelope with root `plugin.json`, `skills/<name>/`, and
      `dev.orca.skill-sharing/manifest.json`; do not add an outer `bundle/` directory.
- [x] Define `SkillBundleManifestV1` with stable package/version identity, bundle metadata, ordered
      skill entries, per-skill identity and file lists, globally unique archive paths, and a bundle
      digest.
- [x] Validate Agent Plugins 1.0.0 `plugin.json` fields from independently authored local rules;
      never fetch a schema during loading.
- [x] Create fresh staging roots and reject unknown, malformed, or conflicting imported
      `dev.orca.skill-sharing` namespaces without overwriting them. The extractor now exclusively
      creates its staging root, preserves a pre-existing namespace on conflict, rejects unknown
      top-level extension entries, and removes only staging it created.
- [x] Keep the detailed manifest inside the archive for export, remote/SSH transport, and offline
      validation; keep GCS metadata compact and PostgreSQL limited to exact link/ownership lookup.
- [x] Add selected skill IDs and per-skill conflict choices to preview/install requests.
- [x] Return aggregate status plus ordered installed, unchanged, kept-local, and failed per-skill
      outcomes; support retrying failed items only.
- [x] Add `skills.install.bundle.v1` and additive bundle RPC methods so mixed-version peers fail
      before transfer.
- [x] Decide to dispose of the unpublished staging-only single-skill records before changing the
      Cloud schema; there
      is no production compatibility commitment to the unpublished single-skill format.

### Shared package manifest

- [x] Add `src/shared/skill-package-manifest.ts`.
- [x] Define the original single-skill `SkillPackageManifestV1` prototype with schema version,
      stable package ID, immutable version ID, name, description, creation time, file identities,
      and package digest.
- [x] Normalize manifest paths to `/` while keeping filesystem conversion host-owned.
- [x] Define the canonical digest algorithm over normalized paths, file identity, executable state,
      and classification.
- [x] Require `skill/SKILL.md`, valid frontmatter, and agreement between skill and package names.
- [x] Specify deterministic serialization so identical inputs produce identical identities across
      macOS, Linux, Windows, and WSL.
- [x] Version the schema and require additive changes or a new schema version.
- [x] Reject unknown schema versions with a stable error category.

### Package limits and file policy

- [x] Enforce maximum path depth 16.
- [x] Enforce maximum 2,048 archive entries.
- [x] Enforce maximum 512 regular files.
- [x] Enforce maximum 4 MiB per file.
- [x] Enforce maximum 32 MiB total extracted bytes.
- [x] Enforce maximum 40 MiB compressed bytes.
- [x] Reject absolute, parent-traversal, NUL-containing, and Windows drive-prefixed paths.
- [x] Reject duplicate normalized paths and Unicode or case-fold collisions.
- [x] Reject symlinks, hardlinks, devices, FIFOs, sockets, encrypted entries, and other special
      files in V1.
- [x] Preserve executable modes represented by the package manifest.
- [x] Document that install never executes package scripts.

### Install request, preview, and result

- [x] Add `src/shared/skill-install-contract.ts`.
- [x] Define requests with operation ID, immutable package identity, ingress kind, destination, and
      optional conflict resolution.
- [x] Support download-grant, staged-upload, and trusted in-process local-file ingress.
- [x] Ensure arbitrary remote RPC callers cannot supply a local filesystem path.
- [x] Model global, Git worktree, and folder-workspace destinations without assuming Git exists.
- [x] Define preview output for destination, current state, provider coverage, conflicts, and trust
      metadata.
- [x] Define structured installed, updated, unchanged, conflict, partial, and failed results.
- [x] Define stable error categories for admission, transport, archive, filesystem, conflict,
      recovery, provider placement, and compatibility failures.
- [x] Ensure responses never include grants, credentials, ACL membership, or private package
      contents.

### Capability and wire compatibility

- [x] Add `src/shared/skill-install-capability.ts` with `skills.install.v1` and an update-required
      compatibility message.
- [x] Read and apply `docs/reference/remote-wire-compatibility.md` before changing RPC contracts.
- [x] Add new methods without changing existing discovery behavior.
- [x] Keep new request and response fields optional until all supported peers understand them.
- [x] Do not add a terminal stream opcode for skill installation.
- [x] Define behavior for new client/old host, old client/new host, and capability loss during an
      operation.

### Phase 1 contract gate

- [x] Review package, ingress, conflict, result, and capability contracts. Shared schema, runtime
      method, Relay handler, ingress, planner, and stable-failure suites now form the release-gated
      contract evidence; no existing discovery method or stream opcode changed.
- [x] Freeze stable V1 error categories used by desktop, runtime, SSH, and Cloud tests.

## 2. Build and validate packages

### Package creation

- [x] Add `src/main/skills/skill-package-creation.ts`.
- [x] Accept a specific skill directory rather than an arbitrary parent tree.
- [x] Observe and validate the source before staging.
- [x] Copy the source into an owner-private staging directory.
- [x] Observe the staged copy again and fail if its identity differs from the source snapshot.
- [x] Generate a deterministic tar archive with `manifest.json` and a `skill/` envelope.
- [x] Include only `skill/` contents in the eventual installed directory.
- [x] Bind the user-visible share preview to the final staged digest.
- [x] Clean staging files on success, cancellation, source drift, and error.
- [x] Accept a non-empty ordered selection of skill directories and support at least 30 within the
      global entry/file/byte limits. Focused local creation/extraction now covers one 30-skill
      archive; staging load run `31585710645` finalized the same maximum selection through Cloud.
- [x] Reject duplicate normalized skill names and preserve selection across search/filter changes
      in the caller. The picker keeps hidden selections, unions explicit filtered selection, and
      disables a second same-name source before review.
- [x] Generate deterministic `plugin.json`, `skills/`, and Orca extension metadata.
- [x] Bind the share review to every staged skill digest and the final bundle digest.

### Bounded extraction

- [x] Add `src/main/skills/skill-package-extraction.ts`.
- [x] Stream archive inspection and extraction without buffering the entire package.
- [x] Validate `manifest.json` before trusting archive file metadata.
- [x] Enforce compressed, extracted, entry, file, depth, and per-file limits during extraction,
      not only after it.
- [x] Create extraction staging on the same filesystem as the canonical destination.
- [x] Convert manifest paths with the destination runtime's path APIs.
- [x] Re-observe extracted `skill/` and compare every file identity and package digest.
- [x] Delete partial extraction bytes on cancellation or failure.
- [x] Validate the portable root, Orca extension namespace, bundle manifest, and every selected
      `skills/<name>` subtree before destination mutation.
- [x] Extract selected skill subtrees without requiring installation of unselected skills.

### Package tests

- [x] Test deterministic manifests, archives, and digests on macOS and native Windows. The exact
      golden passed on both hosts at `8be7275d0d` after replacing zlib-dependent streaming output
      with bounded deterministic stored DEFLATE blocks.
- [x] Repeat the portable package golden on native Linux and inside WSL.
      The focused golden passed in Debian Bullseye/glibc 2.31 containers on native ARM64 and
      emulated x64, and inside the real Ubuntu 24.04 WSL distro, at `92c8c6a4c6`. The same 26/26
      suite passed at `109272dab1` on a disposable native x86_64 Ubuntu 22.04 GCE host; the VM,
      boot disk, uploaded bundle, and temporary SSH metadata were removed after validation.
- [x] Test source changes during packaging.
- [x] Test CRLF/LF behavior explicitly and document whether byte identity changes.
- [x] Test executable-mode preservation and Windows's mode limitations.
- [x] Test missing, malformed, and identity-mismatched `SKILL.md` files.
- [x] Test every size, count, and depth boundary at limit, one below, and one above.
- [x] Test traversal, absolute paths, drive paths, Unicode/case collisions, duplicate paths, all
      rejected link and special-file types, and encrypted entries.
- [x] Test truncated archives and invalid tar and content checksums.
- [x] Test invalid gzip bytes and map zlib-specific errors to the stable non-retryable archive
      category.
- [x] Fuzz archive path normalization and envelope parsing with bounded resources.

### Phase 2 package gate

- [x] Prove invalid archive classes fail before destination mutation.
- [x] Prove the same source bytes produce the same digest on macOS, Linux, native Windows, and WSL.
- [x] Prove package creation and extraction require no `npx`, external Node installation, or
      upstream CLI runtime.

## 3. Implement the local installer transaction

### Destination resolution

- [x] Add `src/main/skills/skill-install-destinations.ts`.
- [x] Resolve global canonical roots as `<host-home>/.agents/skills/<skill-name>` on the executing
      host.
- [x] Resolve workspace canonical roots from runtime-owned worktree or folder-workspace identity.
- [x] Reject client-supplied remote paths that do not resolve to the selected workspace identity.
- [x] Resolve WSL home, paths, and target distro inside the selected distro.
- [x] Keep all path joins platform-native and reject destination escapes after realpath-aware
      containment checks.
- [x] Support long Windows paths and case-insensitive destination collision checks.

### Admission, locking, and ingress

- [x] Add `src/main/skills/skill-package-download.ts`.
- [x] Add `src/main/skills/skill-install-service.ts`.
- [x] Validate request shape, schema, capability, package identity, scope, destination, and policy
      before download or mutation.
- [x] Recover prior journals for the same destination before planning a new operation.
- [x] Acquire a filesystem-backed cross-process lock keyed by canonical destination.
- [x] Bound lock wait time and return a retryable busy result.
- [x] Stream ingress to an owner-private bounded temporary file and hash it while downloading.
- [x] Require HTTPS whenever the configured Cloud endpoint uses HTTPS.
- [x] Allow only configured Orca skill-bucket origins and reject credential-bearing cross-host
      redirects.
- [x] Check expected compressed bytes, archive SHA-256, package digest, cancellation, grant expiry,
      and disconnects.
- [x] Delete partial ingress bytes on every incomplete path.

### Planning and conflict handling

- [x] Add `src/main/skills/skill-install-planner.ts`.
- [x] Classify missing canonical destinations as installable.
- [x] Classify an identical requested digest as unchanged and continue placement repair.
- [x] Permit clean updates only when installed bytes still match Orca provenance.
- [x] Return a modified conflict when installed bytes drift from their receipt.
- [x] Return an unowned conflict when a destination exists without Orca provenance.
- [x] Return topology conflicts for files, external links, broken links, and name collisions.
- [x] Classify every requested provider placement independently.
- [x] Repeat current-state inspection immediately before commit and invalidate stale previews.
- [x] Require explicit confirmation before discarding local modifications or unowned content.

### Durable commit and recovery

- [x] Add `src/main/skills/skill-install-transaction.ts`.
- [x] Copy extracted bytes into a hidden sibling staging directory and verify the digest again.
- [x] Preserve executable modes where the host supports them.
- [x] Keep staging, replacement, and backup on the destination filesystem.
- [x] Write and durably flush a versioned transaction journal before moving the current
      destination.
- [x] Move an approved existing destination to a journal-owned backup.
- [x] Rename verified staging into the canonical path.
- [x] Add bounded retries for Windows `EPERM`, `EACCES`, and `EBUSY` rename races caused by
      antivirus or indexing.
- [x] Observe the committed destination and require the requested digest.
- [x] Restore the backup after failed commit and retain sufficient journal state after a crash.
- [x] Never remove a path based only on a filename pattern; require journal ownership,
      containment, and expected identities.
- [x] Run bounded recovery at startup and before later operations for the destination.
      Startup scans at most 64 install and 64 removal journals, bounds each read to 4 MiB,
      validates the journal filename against the canonical-path state key, and recovers removal
      before install under the destination lock. Dead-PID locks are reclaimed immediately after a
      killed runtime; malformed locks retain the bounded stale-age safeguard. Corrupt or conflicting
      journals are preserved and reported by path-free failure code for manual recovery.

### Provenance

- [x] Add `src/main/skills/skill-install-provenance.ts`.
- [x] Store versioned receipts outside installed skill directories.
- [x] Record package/version IDs, digest, scope, destination identity, canonical identity,
      placements, previous version, timestamp, and runtime identity.
- [x] Exclude credentials, grants, share values, manifests, and package contents.
- [x] Serialize receipt updates across processes.
- [x] Write receipt updates through durable temporary-file replacement.
- [x] Support aggregate-index reconstruction from bounded per-install receipts.
- [x] Mark the journal complete only after receipt publication, then remove transaction backups and
      staging.

### Verification and discovery

- [x] Re-run skill discovery on the destination after commit.
- [x] Verify the canonical skill and successful provider placements are observable.
- [x] Determine success from installed bytes and discovery, not process exit alone.
- [x] Invalidate renderer skill caches and refresh the Skills page.
- [x] Return canonical success with `partial` when an optional provider placement fails.

## 4. Implement provider placements

### Provider registry

- [x] Add `src/main/skills/skill-provider-destinations.ts`.
- [x] Add data-only records for Orca agent ID, canonical-root support, global/project resolvers,
      detection evidence, and platform alias support.
- [x] Populate only provider paths independently verified from official documentation and real
      installations.
- [x] Detect an agent before creating its provider-specific configuration root.
- [x] Avoid modifying roots for agents that consume `.agents/skills` directly.
- [x] Keep loose-skill placement as the universal/default installation path.
- [x] Do not add Cursor-specific placement in V1; keep the Agent Plugins-compatible root as the
      portable archive shape while installing contained skills through canonical loose-skill paths.
- [x] Do not generate `.claude-plugin/plugin.json` or `.codex-plugin/plugin.json` adapters in V1;
      native plugin installation remains a separately reviewed follow-up if it becomes useful.
- [x] Use loose skills for plugin clients, including the current Codex IDE extension, and retain
      bundle/version provenance for every installed skill.

### Placement reconciliation

- [x] Add `src/main/skills/skill-placement-reconciliation.ts`.
- [x] Create relative directory aliases from real parent directories on POSIX.
- [x] Create directory junctions with absolute canonical targets on Windows.
- [x] Detect provider parents already linked to canonical storage.
- [x] Fall back to an independently copied and verified directory when aliases are unavailable or
      denied and policy allows it.
- [x] Record each canonical copy, provider alias, junction, or independent-copy topology.
- [x] Reconcile placements idempotently after install and update.
- [x] Repair broken Orca-owned aliases.
- [x] Leave unowned or modified provider placements untouched unless explicitly replaced.
- [x] Preserve canonical success when a provider placement fails and make coverage retryable.

### Provider validation

- [ ] Confirm every initial provider discovers a global canonical or reconciled install.
- [ ] Confirm every initial provider discovers a workspace install only in that workspace.
- [x] Test symlink, junction, and copy fallback behavior after provider and canonical parent paths
      are moved or linked.
- [ ] Test provider release upgrades against the registry through the normal review process.

## 5. Implement update, rollback, and removal

### Update and rollback

- [x] Resolve the latest accessible immutable Cloud version without mutating local state.
- [x] Compare the requested digest with provenance and observe current bytes before offering an
      update.
- [x] Route clean updates through the same install transaction.
- [x] Offer keep-local, authorized publish-as-new-version, and explicit discard-and-replace choices
      for modified installs.
- [x] Reconcile recorded aliases, junctions, and independent copies during updates.
- [x] Implement rollback as installation of a selected retained immutable version.
- [x] Never invoke `npx skills update` or write ownership metadata for the community CLI.

### Local removal

- [x] Observe the canonical destination and every receipt-owned placement before mutation.
- [x] Remove only aliases or junctions that still target the recorded canonical path.
- [x] Remove independent copies only when they match their receipt unless discard is explicit.
- [x] Remove the canonical copy only when Orca owns it, it is unmodified, and no retained receipt
      depends on it.
- [x] Publish provenance changes durably before completing removal.
- [x] Leave changed or unowned paths intact and report exactly what remains.

### Recovery failure injection

- [x] Inject failure before and after every journal transition.
- [x] Verify partial downloads and extraction staging are deleted.
- [x] Verify a moved destination is restored from backup.
- [x] Verify a placed destination without a receipt is completed or restored according to journal
      state.
- [x] Verify a published receipt with an incomplete journal is finalized safely.
- [x] Verify a restarted process discovers orphaned install and removal journals without a later
      user operation. Deterministic startup tests prove a committed update publishes provenance and
      cleans its dead lock, while an interrupted removal restores both bytes and receipt.
- [x] Verify interrupted placement reconciliation preserves the canonical install and retries.
- [x] Test cancellation during download, extraction, staging copy, commit, provenance, and
      placement reconciliation.

## 6. Provision GCP infrastructure

### Environment and Terraform prerequisites

- [x] Inspect the active `onorca-cloud` project read-only and record the existing Cloud Run, GCS,
      Cloud SQL, IAM, and enabled-service baseline.
- [x] Locate the authoritative Orca Cloud Terraform worktree, state, modules, and deployment
      pipeline.
- [x] Confirm or create a separate staging GCP project before exercising new lifecycle and IAM
      behavior.
- [x] Confirm production names and quotas, including availability of
      `onorca-cloud-skill-packages`. A read-only 2026-08-12 check returned `404` for the global
      bucket name, found no production `orca_skills` database or skill-database secret collision,
      and confirmed the intended existing API service and runtime identity. Cloud Run exposes a
      1,000-job regional quota and 180 job runs per minute, while V1 reuses the existing API at a
      20-instance ceiling and existing Cloud SQL instance; no quota increase is required.
- [x] Confirm `US` storage satisfies initial residency requirements.
- [x] Confirm Cloud SQL connection and PostgreSQL user provisioning conventions.
- [x] Keep Firestore, Cloud Tasks, and new Pub/Sub dependencies out of V1.
- [x] Declare every durable resource and IAM binding in Terraform; do not provision them manually
      with `gcloud`.

### Dedicated GCS bucket

- [x] Declare private bucket `onorca-cloud-skill-packages`, subject to Terraform validation.
- [x] Set location to `US` for parity with current artifact storage.
- [x] Enforce uniform bucket-level access and public-access prevention.
- [x] Configure seven-day soft delete.
- [x] Keep object versioning disabled and record object generations in PostgreSQL.
- [x] Add a one-day deletion lifecycle for `uploads/` quarantine objects only.
- [x] Add exact approved production and development Orca origins to CORS; never use `*`.
- [x] Allow only required signed POST upload and GET/HEAD download behavior and response headers.
- [x] Keep the bucket off public custom domains.
- [x] Define immutable final keys as
      `packages/v1/tenants/<tenant-hash>/sha256/<prefix>/<archive-sha256>/package.tar.gz`, with the
      logical package digest verified in object metadata and PostgreSQL.
- [x] Define tenant-bound random quarantine keys as `uploads/<upload-id>/package.tar.gz`.

### Database and secret

- [x] Declare database `orca_skills` on existing regional PostgreSQL 17 instance
      `orca-cloud-auth-db`.
- [x] Declare dedicated principal `orca_skills_app` with access only to `orca_skills`.
- [x] Store its connection URL in Secret Manager as `orca-cloud-skills-database-url`.
- [x] Attach the existing Cloud SQL instance to `orca-cloud-api` without replacing the service.
- [x] Inject only the skill database secret into the API service.
- [x] Verify backups and point-in-time recovery cover the new database. `orca_skills` shares the
      Terraform-managed Cloud SQL instance whose live staging and production settings both have
      backups, replication-log archiving, seven retained backups, and seven-day PITR enabled.

### IAM

- [x] Grant `orca-cloud-api@onorca-cloud.iam.gserviceaccount.com` bucket-scoped
      `roles/storage.objectUser`.
- [x] Grant the API service account `roles/cloudsql.client` for the existing instance.
- [x] Grant service-account-scoped IAM Credentials `signBlob` for self-signing V4 policies and
      URLs.
- [x] Grant Secret Manager accessor only for skill-specific secrets.
- [x] Verify bucket IAM contains neither `allUsers` nor `allAuthenticatedUsers`.
- [x] Do not grant desktop, remote runtime, or end-user identities direct bucket IAM.
- [x] Do not create or distribute long-lived GCP service-account keys.
- [x] Keep deployment-account permissions unchanged except for reviewed Terraform management
      requirements. Skill sharing adds no deployment-account IAM binding: immutable API and
      monitor-image updates use its existing `roles/run.developer`, while storage inventory and
      scheduling use separate least-privilege service accounts.

### Cloud Run configuration

- [x] Extend `orca-cloud-api` in `us-central1`; do not create a separate V1 worker service.
- [x] Configure bucket, 40 MiB compressed limit, 15-minute upload TTL, five-minute download TTL,
      fixed finalize concurrency, and skill database URL.
- [x] Reuse existing auth base URL and application CORS configuration.
- [x] Stream validation with fixed buffers under the existing 512 MiB memory limit.
- [x] Add a small per-instance finalization semaphore and retryable `429` or `503` with
      `Retry-After` when saturated.
- [x] Preserve current service scaling initially and tune only from measured CPU, latency,
      database, and error data.
- [x] Define criteria for splitting finalization into a worker service if it harms existing API
      traffic.
- [x] Make immutable API candidates atomically replace the complete Terraform-owned literal
      environment and the exact `ORCA_SKILLS_DATABASE_URL` Secret Manager reference.
- [x] Reject serving or candidate drift in the runtime service account, Cloud SQL attachment,
      scaling, CPU, memory, volumes, mounts, ports, and probes before moving traffic.
- [x] Keep all four skill controls explicit in both deploy workflows. Staging changed to `true`
      only with the authenticated OIDC smoke gate; production remains `false`.

### Terraform review and apply

- [x] Format and validate Terraform.
- [x] Produce and review a staging plan.
- [x] Verify the plan does not replace the existing artifact bucket, Cloud SQL instance, Cloud Run
      services, or unrelated IAM.
- [x] Apply to staging and capture resource and IAM verification evidence.
- [ ] Produce and review the production plan after staging gates pass.
- [ ] Apply the approved production plan only during the rollout phase.
- [ ] Verify production with read-only bucket, Cloud Run, database, IAM, lifecycle, and CORS
      commands without printing secret payloads or signed values.

## 7. Implement Cloud metadata and APIs

### PostgreSQL migrations

- [x] Serialize concurrent Cloud Run startup migrations with a transaction-scoped PostgreSQL
      advisory lock before migration-table inspection.
- [x] Prove failed DDL records no schema version and eight concurrent callers converge on one
      version against real PostgreSQL 16 and 17.
- [x] Add `skill_packages` with owner organization, slug/name, creator, timestamps, and deletion
      state.
- [x] Add immutable `skill_package_versions` with package and archive identities, GCS key and
      generation, sizes, manifest, release notes, creator, and publication state.
- [x] Add tenant/user-bound `skill_package_uploads` with quarantine identity, expiry,
      finalization state, and failure category.
- [x] Add `skill_package_acl` for organization and user principals and permissions.
- [x] Add unpredictable, revocable, optionally version-pinned `skill_share_records`.
- [x] Add bounded `skill_package_audit_events` without contents, filenames, or signed values.
- [x] Add unique package slug per organization, immutable version and digest constraints, and
      tenant-scoped archive and final GCS key/generation constraints.
- [x] Add active-share, ACL-principal, and pending-upload expiration indexes. The ACL index remains
      legacy storage and is not used for bearer resolution.
- [x] Add foreign keys preventing blob-reference deletion while a published version uses it.
- [x] Accept and stream-validate both the pre-release single-skill envelope and the new Agent
      Plugins skills-only bundle envelope during the desktop migration.
- [x] Store the detailed bundle manifest in the existing immutable version record; expose it only
      after exact-link bearer authorization or to owners.
- [x] Keep contained skills out of separate database rows and search indexes. Selective inspection
      reads the validated manifest attached to the exact immutable version.
- [x] Keep detailed file identity in the in-archive manifest rather than duplicating it into GCS
      custom metadata. Published-object metadata contains only aggregate archive/package digests
      and immutable generation identity; per-file paths, sizes, modes, and hashes remain inside the
      authorized archive manifest and immutable PostgreSQL version record.
- [x] Test forward migration, rollback strategy, backup restoration, and migration compatibility
      during mixed API versions. Unit coverage proves atomic forward/idempotent migration, ignores
      an additive schema version written by a newer API, and leaves no recorded or partial schema
      after failure. PostgreSQL 16/17 integration coverage proves transactional DDL rollback and
      eight concurrent startup callers converge on one version. Cloud PR `#349` additionally proves
      the older runner and its existing SQL preserve and operate beside a simulated newer additive
      column/table. Cloud PR `#350` requires real old/new Node-process coexistence, writes before
      and after a `pg_dump`/`pg_restore`, and full cleanup on disposable PostgreSQL 16 and 17; both
      PR and merged-main runs passed.

### Authorization and lifecycle model

- [x] Allow share resolution and share-scoped download grants without an authenticated principal.
- [x] Treat an unpredictable active, unexpired share ID as the bearer authorization; do not apply
      legacy package ACLs to recipients.
- [x] Return the same non-disclosing `404` for missing, expired, revoked, or deleted shares.
- [x] Keep authenticated owner checks on upload, finalize, package/version management, share
      creation, revocation, and deletion.
- [x] Apply distributed per-IP abuse limits to anonymous resolution and grant requests without
      persisting raw requester addresses.
- [ ] Apply organization retention and legal deletion rules over product rollback retention.
- [x] Make package deletion revoke shares before dereferencing immutable objects.
- [x] Delete an object only after a transaction proves no retained version references it.
- [x] Design metadata/object reconciliation for partial publication and deletion failures.

### Upload and finalization APIs

- [x] Implement `POST /v1/skill-packages/uploads` with existing authentication, organization
      membership, quota, rate, and concurrency checks.
- [x] Insert a single-use pending upload before issuing a grant.
- [x] Create a 15-minute V4 signed POST policy bound to exact key, content type, upload ID, expected
      archive SHA-256 metadata, and 40 MiB length range.
- [x] Upload directly from the client to GCS without routing package bytes through Cloud Run.
- [x] Implement idempotent finalize by upload ID and manifest identity.
- [x] Validate GCS key, size, type, metadata, tenant, generation, and expiry before streaming.
- [x] Stream once to calculate archive SHA-256 and validate envelope, manifest, paths, limits, and
      package digest.
- [x] Promote with an if-absent generation precondition to the tenant-scoped,
      content-addressed final key.
- [x] If the tenant's final key exists, verify recorded identity without exposing cross-tenant
      package existence.
- [x] Publish the version and complete the upload in a PostgreSQL transaction.
- [x] Delete quarantine bytes after success and rely on lifecycle cleanup for abandonment.
- [x] Make all mutating endpoints accept idempotency keys.

### Package, version, and share APIs

- [x] Implement package creation and version publication under a stable package identity.
- [x] Implement package details and paginated version history.
- [x] Stop accepting organization/selected-user audience fields for new bearer shares; retain
      legacy ACL storage only as migration-compatible data.
- [x] Implement durable share creation with optional pinned version and expiry.
- [x] Implement anonymous bearer-link metadata preview.
- [x] Implement immediate share revocation for new grant requests.
- [x] Implement Cloud package/version deletion with retention and reference checks.
- [x] Implement version update lookup and rollback selection.

### Download grants

- [x] Implement anonymous `POST /v1/skill-shares/<share-id>/download-grants` after fresh active-link
      evaluation.
- [x] Generate a five-minute V4 signed GET URL for the exact immutable key and stored generation.
- [x] Set only response content type and safe attachment filename overrides.
- [x] Grant no list or write capability.
- [x] Return expected archive identity and byte count beside the grant for runtime verification.
- [x] Test revocation immediately blocks new grants while already issued URLs expire within five
      minutes.

### Cloud tests

- [x] Test anonymous latest/pinned preview and download grants without an Authorization header.
- [x] Test missing, expired, revoked, and deleted shares all fail without disclosure.
- [x] Test authenticated owner management remains protected.
- [x] Test anonymous per-IP rate limiting without recording raw IP addresses.
- [x] Test the authenticated owner-only active-link inventory and ensure responses are not cached.
- [x] Test expired, reused, wrong-tenant, wrong-key, wrong-size, and wrong-hash uploads.
- [x] Test malformed, oversized, and resource-exhausting archives during finalization.
- [x] Test finalization semaphore saturation and retry behavior.
- [x] Test concurrent idempotent mutations and partial database/GCS failures.
- [x] Test tenant-scoped deduplication without cross-tenant timing or response disclosure. Orca
      Cloud `ddbd4e2` proves identical archives use distinct tenant-hashed GCS keys and independent
      PostgreSQL object identities.
- [x] Test quota and rate limits.
- [x] Test durable shares resolving intended latest and pinned immutable versions.
- [x] Test update, rollback, revocation, deletion, soft-delete recovery, and orphan reconciliation.

## 8. Implement desktop Cloud client and UX

### Cloud client

- [x] Make share resolution and share-scoped download-grant calls anonymous while keeping upload,
      package/version management, share creation, revocation, and deletion authenticated.
- [x] Keep desktop contracts storage-provider-neutral.
- [x] Implement bounded progress, cancellation, retry, and idempotency behavior.
- [x] Redact signed policies, URLs, credentials, private paths, and package contents at creation.

### Share experience

- [x] Read and apply `docs/STYLEGUIDE.md`; use canonical CSS tokens and shadcn primitives.
- [x] Add **Share skill** to eligible installed and workspace skills.
- [x] Show name, description, author, file count, total size, scripts, executable files, and the
      unlisted-link warning before upload.
- [x] Remove organization and selected-person audience controls from bearer-link publishing.
- [x] Accept optional release label and notes.
- [x] Package the exact previewed bytes and invalidate preview after source drift.
- [x] Show bounded upload and finalization progress with cancellation.
- [x] Return a copyable durable Orca URL after publication.
- [x] Remove access editing from the bearer-share workflow; keep version publishing, revoke, and
      package deletion actions.
- [x] Make clear that unsharing blocks future installs but does not remove installed copies.
- [x] Add **Share skills**, **Install from link**, and installed-package **Manage** to the existing
      Skills page header; expose active link copy/revocation in Settings → **Share Skills** so links
      remain manageable even when the source was never a managed install.
- [x] Add selection mode whose selection survives search/filter changes and an explicit **Select
      all results** action; never select all implicitly.
- [x] Keep the per-card share action as a one-skill bundle shortcut.
- [x] Disable ineligible skills with visible, screen-reader-linked reasons and block duplicate
      skill names before review.
- [x] Review bundle name, skill/file/byte counts, script/executable warnings, expandable per-skill
      details, unlisted-link behavior, and release notes.
- [x] Add Settings → **Share Skills** with artifact-like explanation and a route to the Skills page.
- [x] Show named preparation, upload, verification, and link-publication progress and return one
      durable link for the bundle. Preparation uses **Preparing preview…**; publication advances
      through **Uploading…**, **Verifying package…**, and **Publishing link…** before rendering the
      single durable share URL. Cancellation reaches both upload and link publication.

### Install experience

- [x] Show description, version, digest, file summary, scripts, and executable files without
      exposing internal publisher or organization identifiers.
- [x] Treat the package as code from its author and require an explicit install action.
- [x] Show current-machine global installation as the default.
- [x] Allow selection of connected machine, Git worktree, or plain folder workspace.
- [x] Show detected-agent coverage and canonical/provider topology before commit.
- [x] Show installed-state conflicts and explicit resolution choices.
- [x] Show phase progress without exposing grants or local private paths.
- [x] Render installed, unchanged, updated, partial, conflict, unsupported, cancelled, and failed
      results with actionable recovery.
- [x] Add incomplete-coverage retry.
- [x] Open deep links in inspection mode without starting installation.
- [x] Show immutable version, skill count, scripts/executables, and release notes before selection;
      V1 omits publisher identity from the public response and recipient UI.
- [x] Let the recipient select all or a subset, then choose local, paired runtime, WSL, or SSH and
      global or workspace scope.
- [x] Preview new, unchanged, update, and conflict counts and resolve all per-skill conflicts in one
      surface with **Keep local** as the default.
- [x] Label the primary action **Install N skills** and show aggregate progress plus current skill.
      The destination-owned installer emits the current name and one-based bundle position. Local
      installs forward it directly; paired and SSH hosts expose a capability-gated read-only
      progress method, with phase-only fallback for mixed-version older hosts.
- [x] Group results into installed, unchanged, kept local, and failed; retry failed items only.

### Skills page lifecycle

- [x] Show installed package/version identity and update availability.
- [x] Offer clean update, modified-copy choices, prior-version rollback, and safe local removal.
- [x] Show whether a package came from Orca Cloud and its accessible version history.
- [x] Refresh discovery and installation state after local or remote actions.
- [x] Keep Cloud deletion, share revocation, and local removal clearly separate.
- [x] Add installed-bundle update, rollback, install-on-another-machine, inspect, and remove actions.
      An active unlisted link opens the existing destination-aware installer for another machine.
- [x] Add shared-bundle copy-link, access, revoke, publish-version, and Cloud-delete actions. Exact
      full-bundle version publishing reuses its package; ambiguous or partial managed-bundle
      selections create a new package rather than mutating the wrong history.
- [x] Protect locally modified skills during bundle update and removal, including when only part of
      a bundle remains managed. Keep-local is the default and destructive replacement is explicit.

### UX validation

- [x] Test keyboard shortcuts and labels with platform-aware macOS versus Windows/Linux behavior.
      Skill sharing adds no modifier shortcut or shortcut label. Renderer coverage proves its
      platform-neutral Escape navigation closes the page, leaves editable controls alone, and
      remains owned by open dialogs; the compatibility scan found no hardcoded Meta-only binding,
      macOS glyph, or platform-specific accelerator in the feature.
- [x] Test loading, error, cancellation, partial, conflict, and stale-preview states. Renderer
      coverage includes scan loading, unavailable links, upload/install cancellation, partial
      provider results, keep-local conflicts, capability loss after preview, and selection
      reconciliation after a changed discovery scan; package tests cover source drift.
- [x] Test screen-reader names, focus order, progress announcements, and destructive confirmations.
      The link field receives initial focus, Tab reaches the enabled primary action, Escape closes
      the dialog, and machine, destination, WSL, and workspace selectors have programmatic names.
      Upload progress exposes its phase and numeric value from 0 through publication; dialog-level
      install tests announce authorization, installation, and current-skill position. Link revoke,
      Cloud deletion, and local removal retain focus while switching to exact confirmation names.
- [x] Test long names, release notes, paths, organization names, and localized copy. Renderer
      coverage exercises the 64-character bundle/skill-name boundary, a 10,000-character multiline
      release note, long Unicode descriptions and organization IDs, long Windows-style skill paths,
      and long workspace labels. Review copy wraps without losing full values, selectors constrain
      labels to dialog width, and localization catalog, extraction, and coverage gates pass.
- [ ] Review trust and privacy wording with security and design.

## 9. Implement paired runtime installation

### Runtime and IPC surface

- [x] Register preview, install, update, and remove through runtime RPC and desktop IPC.
- [x] Add bounded `beginUpload`, `uploadChunk`, `commitUpload`, and `cancelUpload` methods.
- [x] Route every request to the runtime that owns the selected host and workspace.
- [x] Keep transfer separate from installation so grants, relayed chunks, and local files converge
      at the validated ingress boundary.
- [x] Make upload sessions bounded by count, total bytes, idle lifetime, and chunk size.
- [x] Require monotonic offsets and idempotent acknowledged-chunk retry or full restart. A stable
      transfer ID and host-acknowledged offset resume a lost begin response or interrupted upload.
- [x] Release staging bytes after cancellation, disconnect, timeout, and runtime restart. Active
      idle timers remove abandoned archives even when no later upload arrives.

### Direct and client-mediated transfer

- [x] Prefer destination-runtime direct download when it can reach approved Orca Cloud storage.
- [x] Verify origin, redirects, generation, byte count, archive hash, and package digest on the
      destination runtime.
- [x] Fall back to authenticated client-mediated chunk transfer when the host cannot reach GCS.
- [x] Never pass the desktop's local path as a remote package source.
- [x] Preserve the same inspection, transaction, provenance, and result behavior for both
      transports.
- [x] Retry an idempotent install after a recoverable lost final response; rebuild a staged
      transfer before retrying so a consumed upload is never reused.
- [x] Run the real Electron client against a paired desktop host for global, Git-worktree, and
      plain-folder install, unchanged preview, inventory, and removal.
- [x] Run the same contract against a paired headless `orca serve` host for global and
      Git-worktree install, unchanged preview, inventory, and removal.
- [x] Verify paired installation creates files only in host-owned roots and never falls back to the
      client's isolated home.
- [ ] Test connection loss and resumption or restart behavior at every transfer boundary. Lost
      final install-response convergence is covered for direct and staged paired-runtime paths;
      deterministic cancellation now settles in-flight and queued paired calls across every
      transport without leaking request admission or disrupting peers. Client-mediated transfer
      also cancels and cleans a fully uploaded session when cancellation interrupts the commit RPC.
      Restart and the remaining physical disconnect boundaries are still open.

### Mixed versions

- [x] Advertise additive bundle installation separately on paired and SSH hosts; never send the
      bundle method to an older peer that lacks `skills.install.bundle.v1`.
- [x] Hide or disable remote install when `skills.install.v1` is absent.
- [x] Show a specific host-update-required message instead of attempting the RPC.
- [x] Test new client/old server and old client/new server.
- [x] Test optional-field omission in both directions.
- [x] Test a capability changing after preview but before execution.
- [x] Confirm older servers retain skill discovery and ignore no unknown stream opcode because none
      is introduced.

## 10. Validate native Windows and WSL on `windows 2`

The connected Orca environment `windows 2` was reachable on 2026-08-11 and ran Orca `1.4.180` with
runtime ID `68b5e70d-baaf-40a5-b384-be09cc088880`. Validation used the isolated checkout
`C:\Users\neil\orca\skills-share-validation` and WSL distro `Ubuntu-24.04`; no production skill
directory was used for destructive failure injection.

The final combined run used workspace `skills-share-staging-validation` at
`C:\Users\neil\orca\workspaces\orca\skills-share-staging-validation` on branch
`OrcaWin/skills-share-staging-validation`. At `44d1266641`, native-Windows, real-process, and real
WSL coverage ran together: 67 files passed, 449 tests passed, and 17 intentional platform skips
remained. The Windows checkout was clean afterward, and WSL `/tmp` contained no remaining
`orca-skill-*` directory.

The 2026-08-11 host inventory recorded Windows 11 Pro `10.0.26200` build `26200`, x64, healthy
NTFS, Windows Defender, `LongPathsEnabled=0`, no Developer Mode registry grant, and an expected
`UnauthorizedAccessException` for an unprivileged directory-symlink probe. The current user home
is `C:\Users\neil` and the temp root is under that profile. WSL `2.7.11.0` uses kernel
`6.18.33.2-microsoft-standard-WSL2`; its only installed distro is the running default
`Ubuntu-24.04` WSL2 instance, with default user `neil`, home `/home/neil`, x86_64, and an ext-family
distro filesystem. No standalone Orca CLI is installed inside that distro; WSL skill operations
are owned by the connected Windows Orca runtime. A reproducible second-distro setup is
`wsl --install -d Debian --no-launch`, followed by first launch and an isolated Orca test user/home
record before the multi-distro gate.

At commit `1e2d2e806f`, the opt-in real-Windows workspace harness created an actual Git linked
worktree and a plain non-Git folder under owner-controlled temporary roots containing spaces and
non-ASCII characters. It installed globally and into both workspace kinds through host-owned IDs,
then removed the complete fixture tree. The full native `src/main/skills` slice passed 284 tests
with 21 intentional platform skips across 43 files, and the Node typecheck passed. Two discovery
fixtures uncovered by that run now use Windows junctions instead of privileged directory symlinks,
matching Orca's supported Windows placement topology.

At commit `cdafe43542`, the opt-in WSL semantics harness used real provider detection and installed
through the selected distro after converting UNC verification paths to distro-native paths. The
run passed both cases: Linux case sensitivity was preserved, regular files were `0600`, executable
files were `0700`, and the packaged script executed successfully. Existing real transaction
coverage also verified POSIX provider alias creation inside the distro.

At commit `92c8c6a4c6`, the exact portable package golden passed 26/26 focused tests from a native
WSL filesystem using an isolated user-owned Node 24 toolchain. The package digest and archive
SHA-256 matched macOS, native Windows, and Linux container results; the temporary WSL toolchain,
source snapshot, dependencies, and package-manager store were removed after the run.

Commit `97b831dd17` stabilized the physical Windows recovery harness by asserting parsed JSON paths
instead of shell-escaped text and by using a fake clock for upload ownership expiry. The combined
run then exposed a real WSL placement bug: native UNC `lstat` could miss an existing distro symlink,
so GNU `ln -s` followed it and created a self-link inside the canonical skill. Commit `44d1266641`
now asks the WSL filesystem whether the alias already targets the canonical path before native
existence checks and uses `ln -sT` for safe creation.

Only `Ubuntu-24.04` is installed on this host. A second-distro permutation is a post-launch
resilience follow-up, not a first-release gate. On 2026-08-12, the owner accepted the additional
live WSL-to-staging journey as duplicate evidence after the physical Windows/WSL matrix and native
Windows Cloud lifecycle passed; the remaining disconnect/cancellation boundaries stay recorded as
residual follow-up work.

### Host preparation

- [x] Confirm `windows 2` is saved and reachable through `orca environment list` and
      `orca status --environment "windows 2" --json`.
- [x] Confirm the host provides both a native Git checkout and a native plain folder workspace.
- [x] Record Windows edition/build, architecture, filesystem type, long-path policy, current user
      home, temp root, antivirus status, and developer-mode/symlink policy without collecting
      secrets.
- [x] Discover installed WSL version and distros, each distro's running state, default user, home,
      filesystem, and Orca host support.
- [x] Ensure at least two WSL distros are available or record a reproducible second-distro setup
      for the multi-distro test.
- [x] Create isolated test skills and destination roots under explicit test workspaces; never use
      production user skill directories for destructive failure injection.
- [x] Record exact Orca client/server versions and capabilities with each test run.

### Native Windows package and destination tests

- [x] Package on macOS and install on native Windows; compare manifest and digest. The physical
      staging journey installed the macOS-published archive with the exact published digest, and
      the portable golden independently matched manifest, package digest, and archive SHA-256.
- [ ] Package on native Windows and install on macOS/Linux; compare identity and executable-mode
      policy.
- [x] Test global home resolution without constructing the path on the macOS client.
- [x] Test a host-owned real Git worktree and plain folder workspace independently.
- [ ] Test path joining, drive prefixes, UNC rejection/handling policy, reserved names, trailing
      dots/spaces, long paths, Unicode normalization, and case collisions.
- [x] Test source and destination paths containing spaces and non-ASCII characters.
- [x] Test junction creation succeeds for a detected provider.
- [x] Force junction denial and verify an independently copied, digest-verified fallback.
- [x] Test an unowned junction, external link, broken junction, and provider parent that is already
      linked. All four real NTFS junction cases passed on `windows 2` at `c7c8a2e32d`; Orca repairs
      only its receipt-owned broken junction and preserves unowned links.
- [x] Test copy-fallback drift during update and removal. Real Windows coverage at `b4f5b6c119`
      proves a locally modified independent copy is skipped during reconciliation and refused
      during removal without changing its bytes.
- [x] Hold destination files open to simulate antivirus/indexer contention and verify bounded
      `EPERM`, `EACCES`, and `EBUSY` rename retries. A real `FileShare.None` lock on `windows 2`
      proved transient recovery and bounded exhaustion at `18fe31f107`.
- [x] Verify retry exhaustion restores the old version and yields an actionable result. The
      transaction preserves the old bytes and returns retryable
      `skill-install-filesystem-failed`; the real Windows source remains intact for retry.
- [x] Terminate the runtime before and after each journal boundary and verify startup recovery.
      The combined physical Windows run passed the real-process extraction, install, removal, and
      upload restart suites, including begun, partial, fully uploaded, and committed upload states.
- [ ] Test permission-denied, read-only, disk-full, cancellation, runtime disconnect, and partial
      provider-coverage paths. Deterministic `EACCES` and `ENOSPC` transaction injection now proves
      the prior installed version remains intact; physical read-only/disk-full and disconnect
      journeys remain open.

### WSL tests

- [x] Execute package ingress, extraction, home resolution, installation, provenance, and discovery
      inside the selected distro.
- [x] Prove the Windows client never constructs a Linux home or mutates the distro through a
      translated Windows path.
- [ ] Post-launch: test global installs for a second distro with a different default user/home.
- [x] Test Linux case sensitivity and executable modes inside the installed distro. Real
      `Ubuntu-24.04` coverage passed at `cdafe43542` with `0600` regular files and `0700`
      executable files.
- [x] Install a multi-skill bundle selectively into a distro-owned home and prove unselected skills
      are absent. The first `windows 2` run found missing filesystem preparation; the fix at
      `70b847c38e` passed all 3 real `Ubuntu-24.04` tests and the temporary worktree was removed.
- [ ] Post-launch: repeat Linux case-sensitivity and executable-mode coverage in a second distro.
- [ ] Test a distro-owned Git worktree and plain folder workspace.
- [x] Test a workspace on the distro filesystem and document behavior for `/mnt/c` separately.
- [x] Test provider detection and POSIX alias creation inside the distro. Real `Ubuntu-24.04`
      provider detection passed at `cdafe43542`; the transaction harness also verified the Claude
      placement as a distro-owned POSIX symlink.
- [ ] Test concurrent native Windows and WSL installs of the same package without provenance or
      lock collision across host identities.
- [ ] Test distro stopped, distro removed, default user changed, home moved, and runtime restarted
      between preview and install.
- [ ] Block outbound GCS access inside WSL and verify client-mediated chunk transfer.
- [ ] Disconnect the macOS client during transfer and commit; verify bounded cleanup and recovery.

### Windows/WSL mixed-version tests

- [ ] Use `windows 2` as old host with a newer client and verify capability-gated UI.
- [ ] Upgrade `windows 2` only through the normal supported update path, then test an older client
      against the newer host.
- [ ] Verify folder-workspace IDs, runtime-owned paths, and results survive client/host version
      skew.
- [x] Capture structured test evidence without signed URLs, tokens, skill contents, or private
      absolute paths beyond approved test fixtures.

### Windows/WSL release gate

- [x] Real native Windows passes junction success, junction denial, verified copy fallback,
      antivirus contention, long paths, crash recovery, Git worktree, and folder-workspace tests.
      The final combined run passed 449 tests across 67 files at `44d1266641`.
- [ ] Real WSL passes distro-owned paths, Linux semantics, provider placement, cancellation, and
      crash recovery tests. Distro-owned paths, semantics, provider placement, and interrupted
      update recovery passed in the combined physical matrix. The owner accepted the additional
      live WSL-to-staging journey as duplicate release evidence on 2026-08-12; physical
      disconnect/cancellation injection remains open, and a second distro is post-launch.
- [x] Temporary local test installations, workspaces, and packages are removed through verified,
      recoverable cleanup. The final checkout was clean and WSL `/tmp` had no `orca-skill-*` debris.

## 11. Implement and validate SSH targets

The 2026-08-11 live Orca inventory contained only the connected `windows 2` environment and no
non-local worktree or repository. Later staging journeys registered disposable Ubuntu 20.04 and
native macOS ARM64 hosts directly through Orca's SSH provider and removed them afterward. A
supported Windows SSH target remains required for its platform-specific gate.

The Docker-backed Linux SSH harness exercises the production Electron → SSH provider → relay →
remote installer path. Its loopback package origin is unreachable from the container, forcing
client-mediated chunk upload. The test independently verifies Cloud request receipts and remote
files for global, Git-worktree, and plain-folder installs, unchanged preview, listing, and removal.
Physical staging journeys additionally passed the supported Ubuntu 20.04/glibc 2.31 floor and
native macOS ARM64; supported Windows remains open.

- [x] Define a host-side installer command that invokes the same installer core and structured
      result contract.
- [x] Transfer immutable packages through the existing SSH/SFTP provider into bounded private
      staging.
- [x] Invoke installation on the SSH host so it owns home, path, provider, and filesystem
      resolution.
- [x] Add capability/version detection for the required host component.
- [x] If unsupported, offer a package download or explicit command; never install into the
      desktop's home as fallback.
- [x] Propagate cancellation and clean partial SSH transfers and staging.
- [x] Route selective bundle installs through an additive SSH method with direct download,
      client-mediated fallback, per-skill results, and old-host capability fencing.
- [ ] Test SSH-only macOS, Linux at the supported floor, and Windows where the existing provider
      supports it. Ubuntu 20.04/glibc 2.31 and native macOS ARM64 passed the full live staging
      lifecycle; supported Windows remains open.
- [x] Test Git worktree and folder-workspace scope over SSH through the disposable Docker Linux
      target and verify the host-owned paths independently.
- [ ] Test connection loss during upload, extraction, commit, provenance, and result return. Lost
      result-return recovery is covered for direct and staged SSH installs; the other physical
      boundaries remain open.
- [x] Prove SSH and paired runtimes return the same result and error-category contracts. The full
      nine-category matrix now passes through both transports with exact code and retryability
      preservation. This exposed and fixed SSH's numeric JSON-RPC code taking precedence over its
      validated structured skill failure; arbitrary error data remains rejected by the strict
      schema.

## 12. Add observability, operations, and security controls

### Metrics and dashboards

- [x] Record bounded package byte/file counts and package, upload, finalization, download, transfer,
      install, placement, and recovery durations.
- [x] Record outcomes by error category, OS, destination kind, transport, conflict type, and
      placement topology.
- [x] Record junction, alias, copy-fallback, rollback, recovery, capability-absence, and orphan
      reconciliation counts.
- [x] Add dashboards for grant/finalize/share rates, authorization and rate limits, finalization
      saturation, archive rejection, and digest mismatch.
- [x] Add Cloud Run 5xx, CPU, memory, instance, and skill-route latency panels and alerts.
- [x] Add GCS quarantine/published bytes, object count, and lifecycle failure panels and alerts.
- [x] Add PostgreSQL connection, storage, query latency, migration, and transaction panels and
      alerts.
- [x] Add signed-policy/URL generation and IAM Credentials failure alerts. Grant-signing failures
      are mapped to a stable application category so the alert does not depend on project-wide
      Data Access audit logging; evidence is in Orca Cloud `69b388e`.
- [ ] Add budget alerts for GCS storage/egress, Cloud Run growth, and Cloud SQL storage.

Dashboard evidence in Orca Cloud `41ef335` and `f67ee2c`: upload-grant, finalize, share, and
download-grant success; bounded security failures; Cloud Run CPU, memory, instances, and latency;
GCS bytes and object count; and Cloud SQL connections, disk use, p99 skill-principal query latency,
and skill-database transaction rate. Cloud PR `#344` passed both required checks and merged as
`fcf8655a`; it adds privacy-bounded migration-ready/failure metrics, a migration lifecycle panel,
and a zero-tolerance migration-failure alert. Its targeted staging plan creates the two metrics and
alert, updates only the dashboard, reruns descriptor propagation, and destroys no infrastructure.
Cloud PR `#345` passed both required checks and merged as `06a5c729`; it normalizes malformed
compression failures to the bounded `skill_package_archive_invalid` category, adds explicit
finalization-saturation and archive-rejection metrics, and displays successful and failed API
operations as rates. The broader unchecked items still require the listed split metrics, lifecycle
visibility, budget coverage, and reviewed alert thresholds. Cloud PR `#346` passed both required
checks and merged as `8199c048`; it adds privacy-bounded per-route latency distributions, sustained
80% CPU/memory and near-instance-ceiling alerts, separate five-second interactive and 30-second
finalization p99 latency alerts, and a route-template p99 dashboard without bearer identifiers.
Cloud PR `#347` passed both required checks and merged as `65320475`; it adds a six-hour
aggregate-only storage inventory job, fixed quarantine/published bytes and object-count metrics,
an overdue-quarantine metric, two storage panels, and an overdue-or-job-failure alert. The monitor
identity has only `storage.objects.list`, while the scheduler can invoke only that job. The reviewed
targeted staging apply added 18 observability resources, updated the existing dashboard, and
replaced only the inert descriptor-propagation timer. A manual run emitted only aggregate fixed-
namespace measurements with zero overdue objects; the post-apply targeted plan is zero-diff, SQL
remained `NEVER`/`STOPPED`, all Relay MIG targets remained zero, and production remained disabled.

Desktop operations now emit local diagnostic spans for package creation, direct upload,
finalization, grant download, client-mediated runtime/SSH transfer, single and bundle install,
provider placement, transaction rollback settlement, and startup recovery. Package bytes/files,
OS, destination kind, transport, conflicts, placement topology/mechanism, copy fallback,
capability absence, recovered transactions, stale extraction/lock cleanup, and failure categories
use fixed or bounded values; bundle error-category cardinality is capped at 32. The implementation
adds no product telemetry event, RPC field, persisted schema, or remote opcode. The skill-sharing
release suite passes 427 tests with 29 expected platform skips, the focused affected matrix passes
102 tests with one expected skip, typecheck and the full lint pipeline pass, and the real-process
macOS recovery matrix passes all 17 crash boundaries. The final combined physical Windows/WSL run
passes 449 tests across 67 files with 17 intentional platform skips; live WSL staging and physical
disconnect boundaries remain separate gates below.

### Logging and privacy

- [x] Log package/version IDs, phases, destination labels, placement outcomes, and bounded error
      categories only.
- [x] Exclude package contents, filenames, manifests, raw local paths, ACL membership, durable share
      URLs, upload policies, download grants, and credentials.
- [x] Redact sensitive network values before logger invocation, not after ingestion.
- [x] Audit authorization and lifecycle events without instruction or script contents.
- [x] Verify diagnostics and support bundles preserve the same exclusions. Desktop install spans
      map requests and results to bounded labels before the tracer boundary; regression coverage
      proves raw paths, filenames, manifests, ACL values, durable share URLs, upload policies,
      download grants, credentials, and raw error text never enter the collected bundle.

### Security review

- [x] Threat-model package creation, archive ingestion, manifest trust, path containment, local
      conflicts, grants, redirects/SSRF, authorization, tenant isolation, and instruction/script
      trust in `docs/reference/agent-skill-sharing-threat-model.md`; keep approval and residual-risk
      acceptance as separate human gates.
- [x] Verify owner-private staging permissions on supported hosts. POSIX download, package,
      extraction, upload-session, provenance, and lock tests require `0700` directories and `0600`
      files; real WSL verifies owner-private executable modes; and the native-Windows privacy suite
      verifies staging remains under the owning profile without broad ACL disclosure.
- [x] Rate-limit uploads, finalization, downloads, share resolution, and remote transfer sessions.
- [x] Test malicious redirects, DNS/host confusion, expired grants, mismatched generations, and
      oversized streaming bodies. Exact HTTPS origins, manual same-origin redirects, URL
      credentials, suffix/port confusion, streamed byte bounds, expiry-before-fetch, and immutable
      GCS generation pinning are covered in Orca `534abb8908` and Orca Cloud `81a581d`.
- [x] Test archive and filesystem race conditions, including source drift and destination changes
      after preview. Package creation re-observes staged bytes, and install commit re-observes the
      locked canonical destination before its first destructive rename.
- [ ] Review organization departure, deletion, retention, soft-delete, and operator recovery.
- [ ] Complete privacy and security sign-off before external rollout.

### Runbooks and kill switches

- [x] Add independent server-side flags for upload grants, download grants, and remote
      installation.
- [x] Verify disabling Cloud grants does not break skill discovery or already installed skills.
- [x] Document finalization saturation, GCS/IAM signing failure, database outage, corrupt package,
      orphan reconciliation, and rollback response.
- [x] Document coordinated PostgreSQL point-in-time and GCS generation/soft-delete restoration.
- [x] Document package/share deletion, organization departure, legal retention, and audit handling.
- [x] Document how and when to split finalization into its own service.

## 13. Complete automated and end-to-end validation

### Transaction matrix

- [x] Test fresh install, identical reinstall, clean update, rollback, explicit replacement, and
      safe removal.
- [x] Test modified and unowned canonical and provider conflicts.
- [ ] Test canonical/provider paths as regular files, directories, aliases, junctions, external
      links, and broken links. Deterministic native coverage now preserves canonical regular-file
      name collisions, unowned provider files/directories, external and broken unowned links,
      Orca-owned broken-link repair, aliases, and verified copy fallback. Physical Windows
      junction coverage remains open.
- [ ] Test concurrent desktop, headless runtime, CLI, SSH, and recovery attempts.
      The shared transaction core now serializes two simultaneous installs into one installed and
      one unchanged result, one receipt, and no staging residue. The host request service also
      serializes simultaneous download-grant and trusted-local ingress requests with the same
      guarantees. A real multi-process harness proves an on-disk destination lock returns a
      retryable busy result without residue, then converges to unchanged after the owning process
      commits and releases; the release gate runs it on macOS, native Windows, and Ubuntu 20.04.
      Named desktop/headless/CLI/SSH/recovery process combinations remain open.
- [ ] Test permission and read-only failures, disk exhaustion, cancellation, process termination,
      and host disconnect. Simulated `EACCES` and `ENOSPC` preservation plus lost-response retry are
      covered; physical failure and termination journeys remain open.
- [x] Test independent-copy drift and incomplete provider coverage. Deterministic transaction
      coverage preserves modified independent copies, retains canonical success when an unowned,
      failed, or cancelled provider placement cannot be reconciled, reports the exact partial
      placement outcome, and repairs interrupted coverage on retry.

### Platform and target matrix

- [ ] Run macOS ARM64 and x64 behavior where supported.
- [x] Run Linux against Ubuntu 20.04/glibc 2.31 and verify bundled native binaries respect the
      floor. The exact x64 directory package passed its 18-binary ABI scan and packaged
      `node-pty` load/spawn smoke inside Ubuntu 20.04 at `f1dccb4f42`. The exact ARM64 directory
      package at `abe92d565b` passed the same 18-binary floor scan and emitted
      `orca-node-pty-floor-ok` from an ARM64 Ubuntu 20.04 container on native `aarch64` hardware.
- [x] Run native Windows and WSL scenarios on `windows 2`.
- [x] Run local and remote Git worktrees and plain folder workspaces. Native Windows covered a
      linked worktree and folder; paired desktop and Docker SSH covered host-owned worktrees and
      folders; physical Ubuntu SSH covered the global lifecycle.
- [x] Run same-build paired desktop and headless runtimes through client-mediated transfer; headed
      coverage includes global, Git-worktree, and plain-folder scopes, while headless coverage
      includes global and Git-worktree scopes.
- [ ] Run paired runtimes with both client-newer and server-newer combinations.
- [ ] Run supported SSH-only macOS, Linux, and Windows targets. Native macOS ARM64 and Ubuntu
      20.04/glibc 2.31 passed full staging lifecycles; supported Windows remains open.
- [x] Run a remote target without outbound Cloud connectivity through chunk transfer. The Docker
      SSH target could not reach the loopback package origin and completed global, worktree, and
      folder installation through client-mediated upload.

### Required journeys

- [ ] Share on machine A, install globally on machine B, launch a detected agent, and discover the
      skill.
- [ ] Share on machine A, install into a folder workspace on a connected runtime, and discover it
      only in that workspace.
- [x] Modify installed bytes, publish an update, and prove Orca refuses silent replacement. The
      browser-free macOS staging lifecycle preserved the modified v1 copy before applying v2 only
      after the explicit test decision.
- [ ] Disconnect during commit, reconnect, recover, and prove either the old or new version is
      complete.
- [x] Revoke a share, prove a new install fails, and prove the existing local install remains.
      Bearer-route tests and staging smoke reject revoked resolution/grants; macOS, native Windows,
      paired, and SSH lifecycles independently preserve already installed bytes.
- [x] Publish a new immutable version, update one machine, leave another pinned, then rollback the
      updated machine. The physical Windows staging journey updated only the remote host to v2,
      retained the local machine's independent version, and rolled Windows back to v1.
- [x] Remove local installation without deleting its Cloud package, then delete/revoke Cloud
      access without mutating another local installation. Staging lifecycles separate host/local
      removal from package deletion, and revocation never mutates either installed copy.

### CI and test evidence

- [x] Add deterministic fixtures authored for Orca without copying upstream fixtures.
- [x] Add failure-injection hooks available only to tests/development harnesses.
- [x] Add a CLI-only developer harness for local and remote integration tests.
- [x] Keep CI commands compatible with macOS, Linux, Windows PowerShell/cmd, and WSL.
- [x] Archive bounded test results and security evidence without secrets or private contents. The
      required release jobs archive bounded machine-readable results; privacy-safe Cloud field
      inventories, load aggregates, and recovery evidence contain no grants or package contents.
- [x] Make package safety, transaction recovery, platform, and mixed-version suites required release
      checks. Release publication now depends on focused macOS, real native-Windows, and Ubuntu
      20.04/glibc 2.31 jobs that archive bounded machine-readable results.

## 14. Stage and release

### Staging

- [x] Apply reviewed Terraform and migrations to staging.
- [x] Deploy `orca-cloud-api` skill routes disabled by server-side flags.
- [x] Enable all four staging controls and pass the GitHub OIDC owner plus anonymous bearer-link
      lifecycle smoke, including owner management, immutable versions, local/remote grants,
      rollback selection, expiry, revocation, deletion, and object cleanup.
- [x] Run staging upload/finalize/download smoke for anonymous denial, oversize, corruption,
      expiry, revocation, deletion, and cleanup; retain tenant-deduplication coverage in the API
      integration suite.
- [x] Run desktop-to-local, paired-runtime, `windows 2`, WSL-equivalent host ingress, and SSH
      journeys in staging.
      Browser-free macOS run `31569902499` passed publish, install, conflict preservation, update,
      rollback, revocation, installed-copy preservation, removal, and Cloud deletion. Physical
      native Windows passed the same lifecycle through run `31591275227`, including remote-owned
      path and digest verification with no macOS fallback. A disposable Ubuntu 20.04/glibc 2.31
      host passed the same SSH lifecycle after guarded wake `31603249983`; guarded sleep
      `31604391897` restored the low-cost state. An isolated headless host passed the paired
      non-Windows lifecycle after guarded wake `31605729090`; guarded sleep `31606600532` restored
      the low-cost state. The physical Windows/WSL matrix separately passed 449 tests on real NTFS
      and Ubuntu 24.04 filesystems. On 2026-08-12, the owner accepted another WSL Cloud lifecycle as
      duplicate evidence rather than a launch gate.
- [x] Give the live staging E2E an owner-private persisted test profile or a short-lived
      non-interactive test credential so reruns do not open PKCE login tabs or copy a user's
      primary Orca session.
- [x] Verify staging application logs contain only route templates, method, status, duration, and
      standard process metadata, with zero credential or private-content matches. Exclude per-link
      Cloud Run platform request logs through Terraform and verify zero are retained.
- [x] Verify logs, metrics, traces, diagnostics, and support bundles contain no grants or private
      package data. The physical SSH window emitted 27 structured route-template events whose
      complete application field inventory was only duration, event, hostname, level, method,
      process ID, route template, status, and time; it emitted zero API error logs. Install traces
      retain only bounded IDs, counts, labels, and enums. The support-bundle suite injects private
      paths, filenames, contents, share URLs, signed grants, policies, ACLs, and credentials and
      proves none survive collection. No skill-sharing product telemetry event is registered.
- [x] Load-test finalization and choose the fixed semaphore from observed memory, CPU, request
      latency, and database usage. Run `31585710645` kept the existing semaphore: its 12 concurrent
      30-skill bundles produced two immediate successes and ten explicit saturation responses,
      1,514 ms client p95, 8.95% API CPU, 26.95% API memory, one API instance, 10.89% database CPU,
      25.59% database memory, and seven database connections.
- [ ] Exercise at least one quarantine lifecycle deletion and published-object soft-delete
      recovery. Published-object recovery passed run `31580071168`; the one-day quarantine
      lifecycle deletion remains time-gated.

### Production infrastructure and launch

- [x] Apply the approved production Terraform plan and verify no unrelated replacement. The saved
      skill-only plan added 40 resources with zero updates, replacements, or deletions; the
      storage-monitor follow-up added one narrow IAM binding with zero changes or deletions. The
      final hardening used two reviewed targeted phases: 13 additions plus one WIF update, followed
      by exactly five broad-IAM deletions. A separate targeted apply made nine in-place
      observability updates. No full plan or unrelated Relay drift was applied.
- [x] Run production database migrations before routing skill traffic. Disabled-route bootstrap
      run `31648015091` completed the skill schema migration before any skill route was enabled.
- [x] Deploy the API with all skill flags disabled. Bootstrap revision `orca-cloud-api-00016-yem`
      received 100% traffic and returned `404` for the disabled skill route while Artifact and Auth
      health remained green.
- [x] After every first-release gate passes, enable the feature for all accounts in one launch.
      Production run `31650178315` initially enabled upload, download, remote install, and sharing
      together. Hardening run `31661421728` then passed candidate and canonical authenticated
      artifact plus skill smoke and promoted `orca-cloud-api-00025-qup` at 100% traffic with digest
      `sha256:e4f044105ff2345574bdceb36eec1629761428cc2edf9d743939390b857d61e0`.
- [ ] Verify one complete share, local install, remote install, update, rollback, revoke, local
      removal, Cloud deletion, upload expiry, and soft-delete recovery journey. Production CI has
      verified the server-side share/version/grant/revoke/delete lifecycle; the signed-in desktop
      and real-host production journey remains manual.
- [ ] Verify upload, download, and remote-install kill switches independently.
- [ ] Review error budgets, cost, authorization denials, saturation, orphan counts, and support
      signals. The hardened production revision has zero post-deploy error logs. All eight skill
      alerts route to the enabled dedicated `Orca skill sharing alerts` channel, and aggregate
      monitor execution `orca-cloud-skill-storage-monitor-lfd26` passed on the serving API digest
      with zero overdue objects. Sustained-usage review remains post-launch.

### Launch operations

- [x] Keep upload grants, download grants, and remote installation independently controllable.
      Separate Terraform-owned Cloud Run variables gate upload/finalize/share publication,
      download grants, and remote-target grants. Route tests prove each narrow control while local
      grants remain available when only remote installation is disabled; staging advertises all
      three explicit controls.
- [ ] Disable the affected operation on unexplained digest mismatch, archive containment failure, data leak,
      unrecoverable local mutation, or cross-tenant authorization defect.
- [x] Publish user documentation for sharing, access, install destinations, updates, rollback,
      removal, retention, and trust in
      [`docs/reference/sharing-agent-skills.md`](./reference/sharing-agent-skills.md).
- [x] Publish admin documentation for organization access, user departure, deletion, and retention
      in [`docs/reference/admin-agent-skill-sharing.md`](./reference/admin-agent-skill-sharing.md).
- [ ] Remove rollout flags only after sustained healthy usage and a reviewed decision.

### First-release gate

- [ ] Threat model and privacy review are approved.
- [x] Cross-platform package and transaction suites are required and green. Release publication
      requires focused macOS, native-Windows, and Ubuntu 20.04/glibc 2.31 jobs with bounded archived
      results; the latest recorded release and platform passes are green.
- [ ] `windows 2` native Windows and WSL release gates pass on real filesystems. The combined run
      passed 449 tests across 67 files, and the owner accepted the additional live WSL Cloud
      journey as duplicate evidence on 2026-08-12. Physical WSL disconnect/cancellation injection
      remains open.
- [x] Real SSH paths and host-owned resolution pass. Native macOS ARM64 and Ubuntu 20.04/glibc
      2.31 independently passed SSH-owned global install, update, rollback, managed inventory,
      revocation preservation, removal, and Cloud cleanup without local fallback.
- [x] Mixed-version tests pass in both directions. Bundle/install capabilities are additive, the
      cross-version wire suite passes client-newer and host-newer, and capability-loss tests prove
      execution is blocked after a stale preview.
- [ ] Cancellation and crash recovery pass during transfer and every commit boundary.
- [x] Durable-share authorization, revocation lag, retention, deletion, and recovery are documented
      and tested. The user/admin guides document the lifecycle; Cloud route, integration, restore,
      and cleanup suites cover the implemented contract. Automatic one-day quarantine deletion
      remains a separate time-gated infrastructure observation.
- [x] The UI identifies scripts and executable content before install without exposing internal
      publisher or organization identifiers. Renderer coverage asserts script and executable
      summaries, digest, release notes, and long-content behavior before the primary install
      action.
- [x] Telemetry, logs, and diagnostic bundles contain no credentials or private contents. Cloud
      field-inventory and sensitive-value scans, bounded install-span attributes, and adversarial
      support-bundle collection tests cover the complete first-release data path.
- [x] Kill switches are tested without affecting discovery or existing installations. Cloud PR
      `#342` proves unlisted-link preview remains available with upload, download, and remote
      installation controls disabled; existing route coverage proves remote-off retains local
      grants. Existing installations and local discovery use only host-owned files and receipts.

## 15. Post-release multi-machine management and reconciliation

- [ ] Measure sharing, installation, update, conflict, fallback, failure, and multi-machine demand
      without collecting private contents.
- [ ] Add **Install on another machine** and bounded multi-machine progress.
- [ ] Add optional desired-version policy for selected personal or organization machines.
- [ ] Add opt-in drift and missing-install reconciliation that never overwrites modifications.
- [ ] Evaluate an explicit, reviewable project desired-state manifest.
- [ ] Evaluate direct machine-to-machine transfer if Cloud persistence is not appropriate.
- [ ] Define offline convergence without durable shared credentials.
- [x] Document organization removal, user departure, package retention, and reconciliation
      semantics before enabling policy-driven installs. The admin guide explicitly leaves legal
      retention and ownership-transfer approval as human release gates.

## 16. Agent and Orca CLI publishing

- [x] Add a separate `agentSkillSharingEnabled` capability that defaults off and accepts only an
      exact persisted `true` value. Manual desktop publishing remains independent.
- [x] Let only the local desktop Settings → Share Skills switch grant the capability. Runtime
      `settings.get` exposes a fail-closed read-only projection; `settings.update`, paired web,
      mobile, agents, and the CLI cannot enable it.
- [x] Add `orca skills installed --json` with discovery IDs and names but no local filesystem paths.
- [x] Add `orca skills share` with repeated explicit `--skill` selectors, a required bundle name,
      one unlisted link for multi-skill bundles, and no `--all` or arbitrary-path input.
- [x] Resolve exact discovery IDs before unambiguous names; fail missing, ambiguous, and duplicate
      bundle-folder names with actionable errors.
- [x] Enforce the capability in the executing runtime before discovery, before package reads, and
      again before publishing. CLI preflight is an optimization, not the authority.
- [x] Reject forwarded WSL, SSH, and paired-runtime CLI contexts before discovery so native file
      APIs cannot read a path belonging to another machine. Native runtimes remain supported on
      macOS, Linux, and Windows.
- [x] Reuse the reviewed bundle preparation and Cloud publisher, propagate RPC cancellation into
      upload, and remove per-operation preparation files after success, denial, cancellation,
      authentication failure, or Cloud failure.
- [x] Admit one agent bundle preparation/publish at a time per host so concurrent agents cannot
      multiply the bounded per-bundle disk and CPU budget.
- [x] Keep CLI JSON limited to the unlisted URL, public share/package/version IDs, bundle name, and
      selected skill summaries; never return auth tokens or source paths.
- [x] Add gate, persistence, RPC grant-denial, selector, archive-content, cancellation, cleanup,
      web mirror, Windows-path contract, CLI parser/help, and Settings UI tests.
- [ ] Complete a live signed-in production publish through the dev CLI, resolve the returned link,
      revoke it, and confirm a later resolution fails while the local source skills remain intact.
- [ ] Complete independent OpenCode and release-readiness review, full repository validation, PR
      CI, and an ad hoc build from the final commit.

---

### Automated Setup Tab Astra Review (`docs/automated-setup-tab-astra-review.md`)

# Independent review: automated Setup tab

## Current review verdict

Reviewed product source head `d96ae2ca6afdca919b4d7f8d85308363abe8b28b`, based on the merged PR 1 commit `7f9b51cf5cc1de8769baa646b40c0c93af76be81`, then the documentation-only head and CI corrections described in Rounds 6–8. **P0: none found. P1: none unresolved. Design and actual source diff have converged, with concrete existing paths and no known smaller adequate implementation.** Review only; this report is my only edited file. The numbered rounds below are historical checkpoints, including questions and pending checks that were subsequently resolved.

The implementation retains the existing Setup-tab creation paths and removes only automated setup split placement and its unreachable state. It retires the setting through the existing settings stripper, accepts old-host layouts, preserves manual/issue splits and generic lifecycle behavior, and leaves PR 1's native-chat provisioning change intact. No publication, sibling reattachment, generalized retry, or ownership repair was added.

Product-behavior local validation is complete. Lead's consolidated post-rebase run reports 34 Node suites / 1816 passing tests, two retained expected-failure cases, and two skips on the reviewed source; node/web typechecks and the changed-code gate passed. Rebuilt hidden paired probes passed setup output, independent Setup tab placement, background selection, manual splitting, actual native-chat surface creation, fixture terminal-agent output, and hidden/unfocused host/client windows. I inspected the proof assertions and before/after/native-chat screenshots; I did not independently rerun tests under the lead's exclusive runtime lease. The E2E typecheck comparison reports 261 baseline and 261 final errors, with no new or removed file/error-code pairs; that project remains non-clean. CI subsequently found a fixture-naming ratchet failure; its reviewed correction changes no product behavior, and lead reports both affected ratchets, 23 focused consumer suites (187 passing tests plus two retained expected failures), repeated node/web typechecks and the changed-code gate all passed.

Accepted **P2** limitations: automatic side-by-side setup placement is removed by design; old hosts can still return splits; the conditional ownership fixture does not prove the incident root cause or a publication/reattachment fix; native-chat proof submits no prompt, the terminal agent is a fixture CLI, and platform construction tests do not prove Windows/WSL/SSH execution. The generated English catalog conservatively retains 20 removed-callsite strings (1,136 uncompressed bytes), without restoring settings/search UI. Its remaining nonnative preflight checks passed per the Round 7 receipt. No unresolved source-review question or local correction-validation item remains. The correction commit, exact-head full PR workflow aggregate, and parent merge decision remain separate gates; this report is not merge authorization. Worker settlement awaits the lead's explicit request.

The latest CI correction changes only a stale counterfactual listener-count expectation: the actual 15-listener budget already passed and remains unchanged. The lead reproduced the failing arithmetic under the CI Bun test path, then all four tests and the changed-code gate passed after correction. Round 8 records the independent code/history evidence; no product behavior or performance limit was changed.

## Round 1 — historical design and control-flow review

Reviewed 2026-10-16 in this worktree at `54bb885bbd391642adecdaef262e87c3424f46e6`. Review only; this report is my only owned file. Implementation/final-diff approval is outstanding. Tests and native builds were not run: the coordinator explicitly reserves the shared native-runtime lease for PR 1.

**P0: none found. P1: no demonstrated unresolved design defect in the scoped tab-only approach.** This is not approval of an implementation or proof of the incident's cause. The changes below need concrete checks after PR 1 is merged and this branch is rebased onto that baseline.

### Evidence and scope

- `src/shared/worktree-first-surface.ts:11–17` explicitly seeds a primary shell for either split setting, even with `callerProvidesSurface`. Removing these two conditions removes a real split-anchor dependency without changing startup or issue-command requirements.
- Existing tab implementations are sufficient: renderer `worktree-setup-issue-command-queue.ts:27–45`, host `runtime-worktree-terminal-provisioning.ts:155–197`, and IPC `worktree-remote.ts:368–411`. Keep these command/env/wrapper/handle paths; do not invent a coordinator, ownership store, or retry mechanism.
- The IPC function is actually `spawnLocalStartupAndSetupTerminals` (`worktree-remote.ts:298`), not `spawnCreatedWorktreeStartup`. It is invoked in the local worktree flow at line 1697 despite the filename. Its no-startup/default-tabs early return at lines 308–310 matters when asserting reachability. Label this surface accurately in the design.
- Host success suppresses fallback: `runtime-local-worktree-terminal-startup.ts:203–214` omits returned setup after provisioning, while `worktree-remote.ts:1741–1749` returns only unconsumed setup. Preserving this control flow is smaller and safer than command deduplication. A failed wrapped setup retains `command` at `worktree-remote.ts:421–428`; the renderer prioritizes it at `worktree-setup-issue-command-queue.ts:28–32`.
- `setup-split-activation-single-owner.test.ts:13–18` describes a conditional reconstruction, and its host fixture deliberately returns no session tabs at lines 103–114 while terminal census returns two orphan records at 117–123. That establishes behavior under those premises, not the original missing-binding cause. Keep manual/saved or old-host split coverage without advertising this policy change as a binding/publication repair.

### Native-chat reachability: do not inflate the first-terminal concern

The desktop structured route sets `backendStartup` to undefined (`worktree-creation-flow-execute.ts:63–68`), routes explicit setup through the background seeding branch (`244–269`), and only then launches structured chat (`289–302`). IPC's early return above therefore returns setup for renderer execution. On the ordinary local route, `resolveWorkspaceTerminalHostAuthority` returns `none` (`workspace-terminal-host-authority.ts:124–127`), so it does **not** enter the first-terminal wait at `worktree-initial-terminal-seeding.ts:136–169`. It can use the existing null-primary Setup-tab branch at lines 172–196.

The host-authority branch really does couple returned setup to a first terminal. However, the host native-chat route normally consumes setup (`runtime-local-worktree-terminal-startup.ts:174–187,203–214`), and the paired `agent.launch` test already exercises a real request (`worktree-agent-first-surface.test.ts:155–180`). A P1 claim needs a request that actually returns fallback setup while host authority is live and no terminal will arrive. A constructed `ensureWorktreeHasInitialTerminal` call alone is insufficient. Keep generic delayed delivery unchanged in PR 2 unless PR 1's actual merged route demonstrates that necessity; ask the parent to resolve any overlap first.

### Settings compatibility

Delete the live setting and type; add only a retired-key entry/destructure to `stripRetiredGlobalSettings` (`terminal-settings-migrations.ts:59–91`). The existing function covers loading (`normalize-loaded-global-settings.ts:68`), updates (`settings-update.ts:62`), and serialization (`state-serialization-secret-handling.ts:262`). A separate settings migration/normalization layer has no demonstrated need.

Current paired read/write types omit this key (`runtime-client-settings-shape.ts:7–40,52–75`), and strict RPC `SettingsUpdate` omits it (`client-settings-params.ts:87–131`). Independent bounded named-path history found:

1. `b1973657ea6` introduces `SettingsUpdate` in `src/main/runtime/rpc/methods/client-ui.ts` on 2026-05-21. Its explicit strict allowlist and `getClientSettings`/`updateClientSettings` projection in `orca-runtime.ts:1142–1193` exclude setup location; the same revision uses setup location privately in host provisioning at lines 7006–7009.
2. `c84007c541d^` predates the generated RPC parameter move. `rpc/methods/client-settings-schemas.ts:81–129` remains a strict allowlist without the key, and the then-inline types/projection in `runtime-client-settings.ts` omit it.
3. Targeted `git log -SsetupScriptLaunchMode` over the current/previous schema and client-settings paths found no introduction of the key into those schemas. Historical matches in `orca-runtime.ts` are host provisioning uses, not paired settings fields.

This supports removal without an ignored paired field or wire-schema relaxation. Old hosts still own their placement: accept their returned split layouts without replay, flattening, migration, or interpreting a missing new preference as proof of unexecuted setup.

### Explicit P2 items and acceptance details

- **P2 product tradeoff:** automatic side-by-side placement disappears. Manual and issue splits remain. Users of old hosts can still receive split layouts until that host updates.
- **P2 design precision:** fix the IPC function name and distinguish local IPC from actual remote-host ownership. Replace the missing Electron-skill claim with the coordinator's discovered `agent-browser skills get core` / `electron` capability and pending hidden-CDP validation.
- **P2 coverage retention:** do not delete generic remount/restore/purge/PTY ownership assertions just because their fixtures mention setup. Convert them to manual/issue/old-host splits. Preserve startup identity consumption. Remove only setup-specific geometry gating (`terminal-pane-mount-preparation.ts:222–225`, `pty-resize-geometry.ts:219–226,242–244`); the visible startup-grid settle at lines 211–218 and 228 onward is broader than setup.
- **P2 selection precision:** renderer Setup creation restores the primary only when activation is allowed (`worktree-setup-issue-command-queue.ts:35–43`). Host `createTerminal` does not default to focused: `orca-runtime-core.ts:240–251` and `terminal/launch/terminal-create.ts:252–279` distinguish focus from selection-if-empty. Missing `activate:false` in the host's existing tab branch is not itself proof of focus theft. Assert primary/background selection through the real route instead of changing generic focus machinery.
- **P2 setup-only/templates:** simplify the mode predicate in both `worktree-creation-flow-execute.ts:173–177` and `runtime-worktree-terminal-provisioning.ts:129–138`, while preserving explicit `setupOnly` intent and no-setup fallback. Do not suppress templates for ordinary blank/native-chat creation. The existing failure fallback at provisioning lines 204–221 remains necessary when Setup was the only owed surface.

### Smaller alternatives and convergence

A default-only change is already inadequate: the default is already `new-tab`, while persisted split values still enter three separate branches and create an anchor shell. Ignoring the setting but retaining queues and geometry leaves dead lifecycle state and fails the requested removal. Replacing the three setup placement branches with their existing tab arm, removing only now-unreachable setup plumbing, and retiring the setting through the existing stripper is the smallest adequate design found.

Before declaring design convergence, lead should confirm the corrected route labels, the historical-settings evidence above, and that no first-terminal or publication/reattach work will slip into PR 2 without actual post-PR-1 reachability evidence. No unresolved P0/P1 is currently demonstrated. Final convergence remains conditional on reviewing actual code and meaningful tests, including old-host no-replay, remount one-shot execution, setup-only/defaults, native-chat/terminal/background routes, sequencing failure fallback, and preserved manual/issue splits. Windows/WSL/SSH command tests establish construction behavior only; do not claim those platforms were executed unless they were.

## Round 2 — design convergence

Re-read the revised design after durable message `msg_bf91c03835ff`. The lead incorporated the correct local-IPC function/route, historical paired-schema evidence, first-terminal reachability qualification, and the PR 1/publication/reattachment boundaries. The design retains generic startup-grid settling and requires retargeting generic split fixtures. The Electron capability statement now identifies the installed specialization and keeps validation pending.

**Design converged: no unresolved P0/P1, concrete implementation paths identified, and no known smaller adequate design.** The P2 product tradeoff and old-host behavior are explicit and accepted within scope. This permits scoped source implementation; it does not approve unreviewed code, tests, or merging. Re-evaluate against PR 1's exact merged baseline before final review.

## Implementation review

At this historical checkpoint, lead revision, PR 1 baseline, actual final diff and test results were pending; their resolution appears in the later rounds and current verdict above. Do not interpret this report as a merge authorization.

### Interim fixture review

At the lead's request (`msg_76639d99e47e`), inspected the retargeted delayed-primary/sibling-fallback test in `pty-connection-split-spawn.spec.ts:235–338`. Its CWD promise is a real generic delay (`run-deferred-connect.ts:74–103`), but then it waits for visible startup-grid settling; 20 shared animation callbacks are not necessarily 12 primary settle ticks when the sibling has its own resize callbacks. Recommended making both explicit-startup panes hidden, retaining the CWD gate and shared transport registry, then asserting sibling-first ownership before resolving the primary CWD. Existing `pty-connection-deliberate-sleep-guard.spec.ts:183–213` demonstrates this hidden CWD gate. This preserves the ownership regression purpose without changing production reattachment or restoring removed setup geometry. Lead must verify the proposed fixture correction; reviewer did not run tests or edit source.

### Round 3 — implementation review before PR 1 rebase

Reviewed the restored working diff after `msg_065efb1f75ff` (base remains `54bb885bbd391642adecdaef262e87c3424f46e6`). **No production P0/P1 found.** A final post-PR-1-baseline verdict remains outstanding.

- All three placement changes retain the existing tab arm. Runtime retains completion-token registration, default-tab dressing, explicit setup-only suppression and failed-Setup fallback. IPC retains its wrapper, platform/shell builder, env, inactive/background options, and consumed-result behavior. No setup execution authority or retry mechanism was introduced.
- The removed state is setup-specific. Issue-command queues/consumption remain; `terminal-pane-mount-bootstrap.test.ts` checks the surviving primary-anchor split, env, focus policy and one-shot command clearing. `terminal-pane-pane-created.ts` still passes the captured raw startup identity to `createQueuedStartupConsumer` after removal of the setup-wait copy.
- `terminal-startup-grid-settle.ts` removes only the now-unused readiness predicate and its frame counter. With no predicate, old `readyFrame` and `frame` advanced together, and `usesReadinessGate` was false; the remaining algorithm therefore preserves the former generic branch. Grid stability/cap/cancellation tests and hidden-window start tests remain.
- Compared old and new PTY test-case names: only the obsolete setup-geometry case was deleted; delayed-primary was retargeted, and remount, resize/mobile locks, hidden startup, and replay cases remain. The final hidden-CWD delayed-primary fixture explicitly proves sibling ownership before release and then asserts a distinct primary PTY; lead reports 78/78 passing.
- Existing old-host first-activation reconstruction retains its layout/ownership assertions and now asserts an empty pending-startup map. Its expected-failure branch remains an acknowledged existing limitation, not a claimed fix. Purge/sleep tests retain issue-queue and generic startup coverage while removing only the deleted map's assertions.
- Stale settings are removed on load, update and save/reload by meaningful persistence tests; remaining setup-location references are deliberate stale-profile test input or the stripper. No paired RPC schema change or saved-layout migration was introduced.
- Inspected `setup-before.png` and `setup-after.png` directly in `.bench-fixtures/setup-split-removal-proof`: before has a split inside one terminal tab, after has Terminal 1 and Setup tabs with one visible Setup pane; both display the setup completion marker. Screenshots alone do not prove hidden/focus state; the proof's explicit BrowserWindow assertions and lead's execution receipt cover that separately.

**P2 correction requested (`msg_ce3248db323b`):** `host-split-and-sleep-journeys.ts:56` calls public `terminal.split`, whose RPC handler (`terminal-lifecycle-methods.ts:96–101`) does not forward inactive options; `terminal-split-surface.ts:129,169` defaults activation to true. Thus the renamed `host-split-first-activation` journey can activate during fixture creation and overstates its coverage. Preserve inactive-first-open through an existing internal test seam, or explicitly navigate away and label this a reactivation fixture, retaining genuine old-host first-open coverage in the unit test. Do not broaden production RPC for the fixture. Also asked the lead to format/check the new proof spec and replace its unchecked result cast.

Validation reported by lead: 34 Node files / 1799 tests with two existing expected-fail cases and two skipped; a separate 78-test PTY fixture run; node/web typechecks; changed-file quality gate. Reviewer has not independently executed these under the lead's exclusive native lease. Final logs/results, E2E typecheck baseline comparison, corrected P2 fixture, PR 1 merged-base adaptation, and exact-head workflow gates remain to be assessed.

Round 3 follow-up: verified the P2 fixture correction after `msg_b7784f689bca`: `host-split-and-sleep-journeys.ts:57` returns to the seeded workspace and the oracle scenario is now `host-split-reactivation`. The old-host first-activation unit assertions remain. This resolves the misleading-coverage finding without broadening production scope. The lead will keep the one-off before/after probe under ignored proof artifacts, not in the PR. Lead also reports both hidden before/after probes passed output, tab/pane count, retained manual split, background-selection and hidden/unfocused-window assertions. No unresolved P0/P1 in the currently reviewed PR 2 diff; post-PR-1 review is still required.

### Round 4 — exact PR 1 merged baseline

Verified locally that `merge-base(64fbb53d8a4c92e17c5aa19a8aa86cd1869979bc, 7f9b51cf5cc1de8769baa646b40c0c93af76be81)` is the exact PR 1 merge `7f9b51cf5cc1de8769baa646b40c0c93af76be81`. The PR 2 diff does not modify PR 1's production `runtime-local-worktree-terminal-startup.ts`: its shared `callerProvidesInitialSurface` predicate still supplies the host's background agent ownership decision, and host success still suppresses returned setup at lines 202–209.

Reviewed the only uncommitted overlap adaptation, `src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts`. It retains renderer payload construction → strict RPC parse → runtime argument builder → actual managed-worktree create (lines 72–91), and expands the Claude/Codex setup-only-host assertion across both stale split values, new-tab, and absent settings (95–113). It checks command/env, running setup receipt, consumed setup and absence of an invented startup terminal. Former split-anchor controls now request a real startup and verify distinct startup/Setup tab identities (132–143). Blank, activating attribution, run-hooks, skipped setup and default-command controls remain. The stale field is fixture-only and no deleted live type remains.

**Post-rebase semantic review: no unresolved P0/P1, no duplicated PR 1 production change, and no smaller adequate alternative identified.** The accepted P2 product/old-host limitations remain as documented. Final post-rebase checks/proof and the eventual exact-head CI/merge authorization are still separate gates; reviewer is awaiting their receipts and the lead's explicit settlement request.

### Round 5 — committed-head confirmation

Verified the clean tracked worktree at `d96ae2ca6afdca919b4d7f8d85308363abe8b28b`. Its only change from the Round 4 checkpoint is the already-reviewed native-chat setup test adaptation above; no additional production changes were introduced. **P0: none found. P1: none unresolved. The actual PR 2 diff has converged, with no known smaller adequate implementation.**

Post-rebase validation receipt from lead (`msg_87e2f64711ef`): 1443 focused/runtime tests passed with one existing skip; node/web typechecks and the changed-code gate passed; the rebuilt paired hidden placement proof passed two tabs/one Setup pane, actual setup output, retained background selection, manual splitting, and hidden/unfocused BrowserWindow assertions. These are lead-executed results under the exclusive runtime lease, not independently rerun tests. The extra native-chat `agent.launch` probe and final E2E typecheck comparison remain pending and must not be described as passed yet.

Accepted P2 limitations remain: automatic side-by-side setup placement is intentionally removed; old hosts may still publish split layouts; the preserved conditional ownership fixture is not incident root-cause proof or a publication/reattachment fix; Windows/WSL/SSH construction tests do not establish execution on those platforms. The E2E typecheck has a reported baseline of 261 pre-existing file/code failures, with the final comparison still outstanding. This confirms source review on the named head, not exact-head CI or merge authorization. Worker settlement remains pending the lead's explicit request.

Round 5 validation follow-up (`msg_b865b628c0e7`): lead reports the additional final-baseline hidden probe passed. Independently read its actual paired `agent.launch` calls and assertions: background Claude creates a structured chat, the rendered workspace contains exactly one agent-session and one terminal, and the terminal-agent route creates two terminal tabs and displays the fixture CLI marker. Directly inspected `native-chat-after.png`, which shows Setup and Claude Chat with an empty composer. This proves native-chat surface creation, not a model turn; no prompt was submitted. The terminal-agent check uses a fixture CLI, not a real Codex session. The probe asserts every host/client BrowserWindow is hidden and unfocused.

Read `e2e-typecheck-comparison.json`: baseline and final each have 261 errors, with empty new/removed file-and-TypeScript-code lists. Thus no new errors were found by that comparison, while the E2E project itself is not typecheck-clean. These receipts resolve the two pending validation items above. **Final source-review verdict on `d96ae2ca6afdca919b4d7f8d85308363abe8b28b`: no unresolved P0/P1; accepted P2 limitations are explicit; no known smaller adequate design.** Awaiting explicit settlement or any CI-driven source revision.

Final consolidated receipt (`msg_6734645deb90`): the lead reran all 34 selected Node suites after rebasing, reporting 1816 passing tests, two retained expected failures and two skips on the same source head. This supersedes the earlier focused-test counts as the final consolidated local result. All requested local validation items are resolved with the limitations stated in the current verdict; exact-head PR CI and parent merge approval remain outstanding.

### Round 6 — CI fixture classification correction

Verified documentation-only head `c6cdb67aaaf1cc7e0a2b5befa7e6e8b7cbb8cab2` preserved the reviewed product source. CI then rejected two fixture calls to `getDefaultSettings` under the focus-read ratchet. These calls replaced incomplete asserted settings during the setup preference removal; they did not add production execution-host routing.

Independently inspected both fixtures and all repository references before and after the staged correction. Both modules import Vitest, and every discovered consumer is a `.test.ts` file. `check-focus-read-ratchet.mjs:357–359` already excludes `.test-fixture.ts`, with direct coverage at `check-focus-read-ratchet.test.mjs:244`. Renaming to `worktree-activation-created-agent.test-fixture.ts` and `worktree-activation.test-fixture.ts` therefore applies the existing test/product distinction. The staged renames are 100% content-identical; every import edit is test-only. The existing layout detector also excludes fixtures (`check-layout-writer-ratchets.mjs:161–165`), so the correction deletes the one obsolete 21-writer baseline row for the old filename. Neither detector nor any allowance was expanded.

**No new P0/P1/P2 finding in this correction; prior accepted P2 limitations remain.** Conventional fixture naming is smaller and more accurate than a detector change, a suppression, duplicating defaults, restoring unchecked settings casts, or adding baseline allowances. Final correction receipt (`msg_b98097cf4ff3`): both ratchets passed, 23 focused consumer suites passed 187 tests with two retained expected failures, node/web typechecks passed, and the changed-code gate against the exact PR 1 baseline passed. Existing product-behavior and rendered proof receipts remain applicable; a hidden-app rebuild is unnecessary for content-identical fixture renames and test import changes. No local correction-validation item remains pending. Fresh exact-head full PR CI and parent merge approval are still required.

### Round 7 — generated English catalog correction

Verified clean committed fixture-correction head `fe855b8c97ddf90c0d2ad273f1355cf711ee5d8e` exactly matched Round 6. CI then failed catalog verification: removing the Setup UI/search callsites leaves English catalog entries without literal defaults. The existing `collectRuntimeRequiredKeys` contract (`generate-runtime-required-english-catalog.mjs:45–82`) conservatively retains such entries because they may be dynamically referenced; it does not attempt dead-key reachability analysis. The verifier prescribes the existing `sync:localization-runtime-catalog` generator.

Independently compared flattened JSON before/after generation and the deleted callsites: exactly 20 added entries, zero removed or changed entries, all 20 keys present in the removed setup UI/search code. The uncompressed generated file grows by 1,136 bytes. There is no unrelated generator churn and no restored UI or search registration. Regenerating this single derived catalog is the smallest conservative correction; pruning locale translations requires a broader dynamic-key/locale audit, and changing the verifier would weaken an unrelated contract.

**No new P0/P1; accepted P2:** some obsolete English text remains in the boot catalog under its existing conservative policy. This does not restore setup split behavior. Lead's final receipt (`msg_83f4e9157231`) reports the remaining nonnative gates passed: localization catalogs/extraction/coverage using the CI Bun commands, feature-wall budget, macOS entitlements, and preload/shared declaration guard. The unit-plan script completed its local full-reference fallback, which is not itself proof of the CI plan; lead separately reports the actual CI plan and all static steps preceding catalog verification, including Typecheck, had passed. No local correction-validation item remains pending. Fresh exact-head full PR CI and parent merge approval remain required after the correction commit.

### Round 8 — stale listener-savings arithmetic

Verified committed catalog head `35f132ba2547422cb6e11e00d1b631528fdab424` matched Round 7. A required unit shard then failed `terminal-pane-store-subscription-budget.test.tsx`. Independently traced the exact assertions: the live per-pane budget remains 15 and its assertion passes; the failure is the later equation `45 - 15 = actionKeys.length + 6`, where the action list is now 23, making the right side 29 rather than 30. It is not a measured increase to 30 live subscriptions.

PR 2 removed `consumeTabSetupSplit` from the stable `useMemo`/`getState` action binding and removed a `useState` initializer that reads `pendingSetupSplitByTabId` through `getState`. Neither opened a subscription. The `PRE_FOLD_LISTENERS_PER_PANE` comment defines a counterfactual current mount with one listener per bound action, not an immutable historical observation. Bounded history corroborates its intended updates: `3e25fe70f6b` lowered 46 to 45 for removal of `readTerminalPaneOnInput`; `93baa828be9` lowered 50 to 46 for four removed actions. Both retained the actual live budget.

The reviewed correction changes only `PRE_FOLD_LISTENERS_PER_PANE` from 45 to 44 and makes the stale action-count comment count-neutral. The exact 15-listener assertion, linear scaling, zero-listener stable-action binding and action identity assertions remain unchanged. This is smaller than introducing a retired-action offset or changing the test model, and does not loosen any live performance limit. **No new P0/P1/P2; prior accepted P2 limitations remain.**

Lead's receipt (`msg_278eaae4e38e`): the exact Bun path reproduced one savings-equation failure with three tests passing before the change; all four passed afterward, and the changed-code gate against the exact PR 1 baseline passed. No product source changed. Existing product validation remains applicable; fresh exact-head full CI and parent merge approval remain separate pending gates.

---

### Automated Setup Tab Design (`docs/automated-setup-tab-design.md`)

# Automated workspace setup: dedicated Setup tab

Status: design converged; implementation, local validation, and independent source review complete. Exact-head CI and merge authorization are tracked by [PR #29819](https://github.com/stablyai/orca/pull/29819).

## Decision and boundary

Run terminal-backed automated workspace setup in its own **Setup** tab using the existing new-tab path. Remove automated setup split placement, its preference/control, and its one-shot split/geometry state. Keep manual splits, issue-command splits, generic startup consumption, saved layouts, default-tab policy, and setup-to-agent sequencing.

This is PR 2. [PR 1 (#29810)](https://github.com/stablyai/orca/pull/29810) separately fixed background native-chat setup provisioning and merged first as `7f9b51cf5cc1de8769baa646b40c0c93af76be81`. This branch was rebased onto that exact merge before final review and relevant checks. Parent chat-publication and sibling-reattachment fixes, broad terminal ownership, corrupt saved-binding repair, and retries are outside this PR.

## Evidence and severity

* Confirmed pre-change mechanism: `shouldSeedWorktreePrimaryTerminal` treated split setup as a reason to create a shell even when the caller supplies native chat. Renderer `queueSetupAndIssueCommands`, runtime `provisionWorktreeTerminals`, and `spawnLocalStartupAndSetupTerminals` in `worktree-remote.ts` each branched on setup location. This PR reuses their existing dedicated-tab arms.
* The supplied incident investigation observed two leaves bound to one PTY and an independent equal-count chat-publication failure. This worktree does not establish the original missing-binding cause or two setup executions. Removing placement complexity is the user-requested product policy; it is not proof of fixing every frozen terminal.
* No demonstrated P0. P1 acceptance risks are replaying setup after host provisioning, losing requested startup/setup sequencing, or changing primary selection/background workspace selection. The validation below exercises actual creation routes and the preserved one-shot startup mechanisms.
* P2 tradeoff: automatic side-by-side setup placement disappears; manual splitting remains. Older execution hosts can still return existing split layouts, which updated clients must accept without moving/replaying setup.
* The first-terminal delayed delivery coupling exists in `ensureWorktreeHasInitialTerminal` for runtime-owned fallback commands. Ordinary desktop structured creation passes no backend startup and returns setup through local IPC, then uses host authority `none`, so its null-primary Setup path is already independent. The host native-chat route normally consumes setup before returning. Keep delayed delivery unchanged: no actual request proves the alleged native-chat P1. The post-PR-1 semantic review found no reason to expand this scope. The conditional STA-9417 expected-failure fixture does not establish the original incident's cause.

## Surfaces and selection

| Intent | Surfaces owed by this create with setup |
| --- | --- |
| Native chat | Requested chat plus Setup, without a split-anchor shell. |
| Terminal agent | Requested agent terminal plus Setup. |
| Plain terminal | Normal primary terminal plus Setup. |
| Setup only | Setup alone; preserve current suppression of default tabs. |
| Explicit default tabs | Existing template allocation/commands/title/color policy plus Setup. |
| No setup | Existing behavior. |

Issue automation retains its independent split from the normal primary pane, command/env, one-shot behavior, and focus policy. Setup stays unselected when another primary surface exists. Setup-only can select Setup when creation is still in view. Background creates/navigation away preserve workspace/tab selection and never reveal/focus a native window.

## Small existing mechanisms

1. Renderer: always create Setup with `pendingStartup`, title it through `setTabCustomTitle`, and preserve active primary/background options. Remove the location input from primary-shell and setup-only decisions. Retain host-spawn-result handling: `runtime-local-worktree-terminal-startup.ts` and local IPC omit consumed setup from fallback results, and remount restores its existing surface.
2. Runtime host: always `createTerminal` titled Setup; retain command construction, env, completion token/returned handle, fallback surface, `surfaceOwner`, and default-tab dressing. Remove setup's use of `splitTerminal` without changing generic split APIs.
3. Local IPC creation (`spawnLocalStartupAndSetupTerminals` in `worktree-remote.ts`): always use the existing new-tab branch; preserve platform runner/shell selection, sequencing wrapper, `activate: false`, and host authority. Accept old-host startup/layout evidence as authoritative.
4. Delete `pendingSetupSplitByTabId`, queue/consume actions, setup props/captured refs/bootstrap branch, setup-only geometry waits. Preserve generic geometry/resize functions used elsewhere and startup identity consumption. Do not remove regression purpose when updating fixtures: use manual/saved or issue-command splits for generic restore, purge, remount, and PTY ownership coverage.
5. Remove the Setup Script Location section and search registration. No new style/token/component is needed; follow `docs/STYLEGUIDE.md`.

## Settings and mixed versions

The implementation deletes `setupScriptLaunchMode` from live `GlobalSettings`, defaults, and launch types, and adds it to existing `stripRetiredGlobalSettings` so stale load/update/save keys are dropped. Remaining setting inputs are deliberate legacy-profile regression fixtures. There is no dead normalization or shared type retained for an unproven peer requirement.

Current `SettingsUpdate` is a strict explicit client-settings allowlist without this key; `RuntimeClientSettings` and `RuntimeClientSettingsUpdate` also omit it. Independent bounded history confirms the initial paired schema/projection at `b1973657ea6` (2026-05-21) and the pre-generated schema/types at `c84007c541d^` omit it; targeted key-history finds no paired-field introduction (see the Astra report for paths). Retire through the existing stripper, which already covers load, update and serialization; do not add a new normalization layer. Hosts own placement: an updated host ignores retired settings, and an old host may still produce a split. Updated clients accept returned layouts without replay or migration. Removing a client setting cannot force old hosts to adopt the new policy.

No protocol field/opcode, live-layout migration, scheduler, ownership store, command-string deduplication, or retry machinery is proposed. Runner arguments/env/cwd, Windows batch/shebang, WSL/SSH paths, and setup completion remain with existing functions.

## Alternatives

Changing only the default retains legacy split behavior and lifecycle complexity. Keeping the setting while ignoring it retains an unused contract unless actual old-peer evidence requires it. Moving all setup provisioning into a new service duplicates existing working paths and crosses PR 1. General binding enforcement and saved-layout repair solve broader problems and need separate evidence/design. Independent design and actual-diff review found no known smaller adequate approach; this is a scoped engineering judgment, not a mathematical optimality claim.

## Validation and review gates

Before implementation: Astra round 1 found no demonstrated unresolved P0/P1 in this scoped design, with concrete route/settings evidence and no known smaller adequate approach. Round 2 confirms the corrected local-IPC label, historical settings evidence, unchanged first-terminal delivery and strict PR 1/publication/reattachment boundary. The independent report is `docs/automated-setup-tab-astra-review.md`; implementation review remains separate.

Meaningful automated coverage: renderer/native-chat/terminal/setup-only/default-tab/background actual requests, host/remote no-split command/env preservation, sequencing/failure fallback, old-host returned layout/no replay, remount one-shot startup, issue-command/manual split/restore/purge regression intent. Run relevant tests with `ORCA_BACKGROUND_LAUNCH=1`, node/web typechecks, changed-file code-quality and format checks. Preserve platform-specific runner tests; report platforms actually exercised.

Rendered checks require rebuilt hidden Electron plus Playwright CDP; inspect tab count/selection/setup output and retained manual splits, with before/after evidence. The installed agent-browser CLI provides the version-matched Electron skill (`agent-browser skills get core`, then `agent-browser skills get electron`), now read; use it alongside Playwright CDP. The coordinator serialized the shared native-runtime lease after an initial local rebuild failure; the supported rebuilt hidden-app checks subsequently passed.

After implementation: Astra reviews actual final diff/tests and semantic overlap after PR 1 rebase. No merge until PR 1 confirmation, exact-head full `.github/workflows/pr.yml` successful aggregate `verify` / PR checks passed and all required checks, and parent approval through the mandatory blocking merge-gate ask. Use `--match-head-commit` and confirm the merge.


## Implemented validation record

Reviewed source head: `d96ae2ca6afdca919b4d7f8d85308363abe8b28b`, based on the exact PR 1 merge above. The follow-up documentation commit changes no source behavior. The independent review records no unresolved P0/P1 and explicit P2 tradeoffs.

- **Node tests:** final consolidated post-rebase selection passed 34 suites and 1,816 tests; two existing conditional expected-failure cases and two skips remain visible. The selection includes composed native-chat create payload/RPC/host routes for Claude and Codex, retired locations, setup-only/default tabs, wrapper/env/shell construction, failure fallback, one-shot startup, old-host layouts, manual/issue splits, remount, restore, sleep and purge behavior.
- **Types and static checks:** node/web typechecks, changed-file code-quality gate and staged Oxlint/format checks passed. The extra e2e project still reports 261 baseline diagnostics; baseline/final comparison by file and TypeScript error code found none added. This is not a clean e2e-project typecheck. A Bun `node:sqlite` suite-loader failure was avoided by executing the complete selected suites with the supported `test:node` command; no test-routing refactor is included.
- **Real rendered before/after:** rebuilt e2e-mode Electron host and paired client, both hidden, legacy vertical-split preference. Before: one terminal tab and two panes. After: Terminal 1 and Setup tabs, one Setup pane. Both displayed the real setup completion marker, retained background tasks selection, and supported another manual split. Post-rebase after-proof passed again. Screenshots are attached to the PR outside the source diff.
- **Rendered agent surfaces:** actual paired `agent.launch` created Claude native chat plus Setup with no shell anchor; the empty chat composer rendered and no prompt/model turn was submitted. A terminal-agent fixture CLI launched separately beside Setup and displayed its marker. Every native window was asserted hidden and unfocused.
- **Coverage retention:** the public `terminal.split` API activates by design, so its host layout fixture explicitly navigates away and tests reactivation. Genuine conditional old-host first-activation ownership/no-replay assertions remain in the unit fixture. Its expected-failure reconstruction is not presented as incident-cause proof.
- **Platforms actually executed:** macOS local Electron host plus paired desktop client. Windows/WSL command construction is tested, while live Linux, Windows, WSL and SSH execution were not performed. Existing saved layouts remain intact, and old execution hosts can still publish split layouts until upgraded.

Design/Astra artifacts are intentionally preserved in this PR. One-off probe sources and raw local logs live under ignored `.bench-fixtures/setup-split-removal-proof`; no images, profiles, auth data, or temporary test spec are committed. Merge still requires the exact current head's full PR workflow aggregate and all required checks, then the parent's mandatory blocking merge-gate approval.

---

### Native Chat Setup Provisioning Design (`docs/native-chat-setup-provisioning-design.md`)

# Native-chat background setup provisioning

## Problem and evidence

A structured native-chat creation deliberately omits terminal startup in `worktree-creation-flow-execute.ts`, while passing the selected agent as `createdWithAgent`. `buildRuntimeWorktreeCreateParams` and the RPC `buildManagedWorktreeCreateArgs` preserve that agent and leave activation false when startup is absent. The execution host therefore handles this as background creation, even when the renderer later opens the chat.

The host already treats a submitted agent as a caller-provided surface for background creation without setup. With setup/default tabs, `provisionArgs` instead recognizes only the internal `promisedAgentChat` flag. In new-tab setup mode this inconsistency creates an idle shell before Setup. Renderer-only tests returning unprovisioned setup do not exercise that host decision.

## Proposed policy

Use one host-local caller-surface predicate for terminal provisioning and fallback shell seeding: explicit `promisedAgentChat` always counts; submitted `createdWithAgent` counts only when neither `activate` nor `runHooks` is true. These are transient inputs to this create, not persisted workspace metadata. Apply the same predicate whether or not setup/default tabs exist.

Continue using `shouldSeedWorktreePrimaryTerminal`: a real startup, issue work, existing default terminal, setup-only choice, and split setup anchors retain their existing precedence. Activated attribution-only requests still owe a primary terminal; attribution must not become an activation promise. Preserve every setup split mode and current setup sequencing, surfacing, completion, and failure behavior.

## Scope and compatibility

Change only the host startup policy and regression coverage. No new RPC field, surface store, ownership/retry redesign, split removal, process command change, or UI styling. Renderer and RPC already transmit enough information for the existing background policy; a new wire intent would duplicate that policy and leave older clients unfixed. Old clients benefit when connected to the updated execution host; updated clients connected to old hosts retain the old behavior. Nothing changes execution-host ownership or introduces client-local SSH fallback.

Folder creation has its own matching no-setup background policy and no automated setup provisioning path; retain it and cover its existing contract. The shared host policy is platform independent and leaves macOS/Linux/Windows/WSL runner construction unchanged. Manual splits, explicit commands, issue automation, default tabs, and startup failures remain owned by their existing mechanisms.

Background attribution-only callers intentionally inherit their established no-setup policy. The pre-existing conditional issue-only/first-terminal coupling and asynchronous setup failure reporting are P2 follow-ups outside this change; successful setup/default provisioning still supplies the terminal used for queued issue work. Independent Astra design review accepted this scope with no P0/P1 before implementation.

## Verification

Add coverage that feeds actual structured renderer creation arguments through the production payload builder, RPC schema/argument mapping, host startup, and real terminal provisioner, recording created terminals. New-tab setup should create Setup alone for background native chat, while blank/background creation still receives its shell. Cover both agents, activated attribution-only creation, run-hooks, promised chat, explicit startup, default tabs, split anchors, and setup-only/failure behavior using existing tests where adequate.

Demonstrate the regression failing against the original policy and passing after the change. Run relevant renderer/runtime/RPC/provisioning/folder tests, node and web typechecks, and changed-file format/lint checks with `ORCA_BACKGROUND_LAUNCH=1`. No platform or rendered UI result is claimed without an actual run. Independent Astra review must converge before implementation and review the final diff with no unresolved P0/P1; record P2 dispositions in the review report.

## Results

The original policy failed both agent regressions by spawning an undefined-command shell before Setup; nine controls passed. With the fix, seven selected suites passed 1,451 tests with one existing skip; 15 focused route/promised-chat controls and four first-surface tests also passed. Node/web typechecks, changed-file lint, formatting, and the Electron e2e build passed on macOS ARM64.

A temporary Playwright probe used isolated hidden host/client apps, the production renderer create action with `createdWithAgent: codex` and no startup, real RPC/PTY provisioning, host terminal inventory, and DOM tab-strip assertions. The baseline host rendered two terminal tabs; the fixed host rendered Setup alone. Both runs passed hidden/unfocused-window assertions and captured CDP screenshots. This verifies the terminal provisioning behavior, without claiming a real structured-agent launch or live SSH/Windows/Linux/WSL run. Probe source, logs, and screenshots are retained locally under `.bench-fixtures/native-chat-setup-proof/`; screenshots are attached to the PR, not committed. Final source was restored and rebuilt after the baseline capture.

---

### Native Chat Setup Provisioning Review (`docs/native-chat-setup-provisioning-review.md`)

# Independent native-chat setup provisioning review

## Design verdict

Accepted before implementation and after final diff/test review: no unresolved P0/P1 findings. All P2 concerns have explicit dispositions below.

The reviewed design is `docs/native-chat-setup-provisioning-design.md`. Review is confined to this worktree; this report is the reviewer's only edited file.

## Root cause and simplest mechanism

The structured route in `worktree-creation-flow-execute.ts` deliberately omits terminal startup and startup draft, but supplies the selected agent. `worktree-create-payload.ts`, the RPC schema, and `worktree-create-args.ts` preserve that attribution. No startup means no activation request from this payload builder.

`runtime-local-worktree-terminal-startup.ts` already suppresses a fallback shell for a background create with either `promisedAgentChat` or `createdWithAgent`. Its setup/default provisioning branch supplies only `promisedAgentChat` to `callerProvidesSurface`. The extra shell is therefore a disagreement between two branches of the same host policy.

Use the existing background policy consistently. Keep explicit `promisedAgentChat` unconditional, and count submitted agent attribution only when neither `activate` nor `runHooks` is true. A new RPC flag is unnecessary: it duplicates existing intent and would leave older clients' requests unfixed. Do not infer this from persisted worktree metadata or introduce a surface store.

## Challenges and dispositions

| Concern                                                                                       | Severity     | Disposition                                                                                                                                                                   |
| --------------------------------------------------------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Attribution is weaker than an explicit chat promise.                                          | P2           | Accepted existing background policy, not a new universal interpretation. Activated/run-hooks attribution-only creates must retain a primary surface.                          |
| A new unconditional chat promise could strand renderer issue commands waiting for a terminal. | P1 candidate | Withdrawn for the chosen design: no-setup policy is unchanged, while successful setup/default provisioning supplies a mirrored terminal. No new wire promise is being added.  |
| Renderer-only setup tests cannot catch the execution host's extra shell.                      | P2           | Require a regression through production payload/schema/mapper and host startup/provisioner, with real creation argument evidence.                                             |
| Older clients and hosts may differ.                                                           | P2           | No wire change: new hosts apply the corrected policy to existing requests; old hosts retain the known extra shell. No capability negotiation or client-local fallback needed. |
| Setup spawn failure and renderer issue-only delivery have existing asynchronous limitations.  | P2           | Keep out of this policy correction; do not claim these are repaired. Preserve current setup receipt/return behavior and failure controls.                                     |

## Invariants checked during design review

- Execution host retains terminal provisioning and setup ownership; navigation does not choose another execution host.
- The shared first-surface predicate still requires anchors for both setup split orientations and real terminal startup.
- Configured default tabs remain explicit work; they are not discarded to make the terminal count smaller.
- Setup runner construction, quoting, environment, sequencing, completion observation, and shell selection remain untouched across macOS/Linux/Windows/WSL.
- Native IPC returns setup to the renderer; paired/runtime creation reaches the host policy under review. No direct SSH fallback is introduced.
- Folder workspace creation has a separate background no-setup policy and no corresponding setup-provisioning branch; preserve it.
- Manual splits and issue command queuing remain in their existing paths. No claim is made that a setup terminal is itself a structured agent surface.

## Final verification

Final review requested and performed on the production predicate, route regression, renderer assertions, explicit promised-chat controls, and node typecheck inclusion. No unresolved P0/P1 found. Final `git diff --check` passed.

The sole production change replaces both branch-specific decisions with `callerProvidesInitialSurface`. The original no-setup branch can execute only when activation/run-hooks are false, so adding those guards to the common predicate does not change its behavior. Setup/default provisioning is the only newly affected branch. Explicit startup still supplies `startupNeedsTerminal`; successful startup/default terminals still count as existing primary surfaces; split modes still override suppression.

The route regression invokes the production renderer payload builder, RPC schema/mapper, runtime creation, and terminal provisioner. The renderer test separately checks the actual execute call's selected agent, absent startup, and absent startup draft. This is composed route evidence, not a rendered end-to-end UI test. The runtime fixture waits for provisioning to observe terminal calls deterministically.

Reviewed the lead's pre-fix log `/tmp/orca-native-chat-before.log`: both agent cases failed on the unexpected `undefined` command preceding Setup, while nine controls passed. Reviewed `/tmp/orca-native-chat-tests.log`: seven files passed, 1,451 tests passed and one skipped. Supplemental validation is attributed below.

P2 test hardening resolved during final review: the split control now asserts that `ORCA_TAB_ID` is a nonempty string before comparing the two panes' values.

Independent baseline run: `ORCA_BACKGROUND_LAUNCH=1 pnpm test` for `runtime-local-worktree-terminal-startup.test.ts`, `runtime-worktree-terminal-provisioning.test.ts`, `runtime-folder-worktree-create.test.ts`, and `rpc/methods/worktree-create-args.test.ts` passed all 37 tests in four files. These existing tests establish controls; they do not demonstrate the new regression fix.

The added node typecheck include names only the pure, type-only-importing renderer payload builder used by the regression; it follows existing cross-layer fixture includes and introduces no runtime renderer dependency.

Lead-reported supplemental validation: 15 focused promised-chat/route controls and four `worktree-agent-first-surface` controls passed; node/web typechecks exited zero and changed-file lint passed. The reviewed final source contains the corrected predicate and the strengthened split assertion.

The coordinator explicitly closed the independent code review after these checks. Any hidden-renderer before/after proof is lead-owned and is not an independently verified claim in this report. The reviewer launched no application or visible window. No live SSH, Windows, or WSL validation is claimed; compatibility conclusions concern the unchanged routing, wire contract, and runner code.

---

### Cloud Docs Relay Security 2026 10 21 (`cloud/docs/relay-security-2026-10-21.md`)

# Relay security review — 2026-10-21

This review covers the checked-in Cloud workspace, its production and development
dependencies, relay/push/fence-broker container images, the Cloud SQL Auth Proxy,
Terraform provider binaries, and the configured COS host release. No deployment,
Terraform apply, or live resource mutation was performed.

The relay and push runtime images have zero reported vulnerabilities. The broker
has one advisory without an upstream fix. **The complete stack is not free of
known findings:** the latest official proxy and Terraform providers still embed
vulnerable dependencies. See the complete dated
[finding inventory](../security/findings-2026-10-21.json).

## Scan results

Counts are package/advisory instances, not unique CVEs or confirmed reachable
exploits. Container and provider scans target Linux amd64, matching the current
Cloud deployment. Trivy's advisory database was updated at
`2026-10-21T19:18:22Z`.

| Surface | Scanner | Before | After |
| --- | --- | --- | --- |
| Cloud production and development dependency graph | pnpm audit | 0 | 0 |
| Dependency graph, independent advisory source | OSV Scanner 2.7.0 | 0 | 0 (174 packages) |
| Relay runtime image | Trivy 0.76.1 | 12 | 0 |
| Push runtime image | Trivy 0.76.1 | Not captured | 0 |
| Fence broker runtime image | Trivy 0.76.1 | Vulnerable Debian, npm, Python and Terraform dependencies | 1 UNKNOWN, no fix |
| Cloud SQL Auth Proxy | Trivy 0.76.1 | 109 | 3: 1 HIGH, 2 UNKNOWN |
| Installed Terraform providers | Trivy 0.76.1 rootfs | 241 | 49: 12 HIGH, 32 MEDIUM, 3 LOW, 2 UNKNOWN |
| Cloud source snapshot secrets | Gitleaks 8.30.1 | 0 | 0 |
| Cloud source static security analysis | Semgrep 1.181.0 | 0 | 0 |

The final Semgrep scan ran `p/security-audit`, `p/javascript`, and
`p/typescript`: 101 applicable rules, 668 files, approximately 99.9% parsing.
A retry of 26 files resolved rule timeouts with a two-worker, 60-second
per-rule limit. Two partial-parser warnings affected tests (`apns-session-replacement.test.ts`
and `host-whereabouts-app.test.ts`). This is automated source analysis, not a
claim that every execution path was manually audited.

Trivy's source/configuration scan reported four LOW configuration findings:
three Dockerfiles without Docker HEALTHCHECK instructions, and a GCS bucket
without customer-managed encryption keys. GCP supplies the runtime health checks;
changing the bucket's encryption policy is an infrastructure policy decision.
Terraform scans with default inputs do not establish the live IAM/network policy.

## Updates

- Digest-pin Node 24.22.1 Alpine runtime/build images. Remove global npm,
  Corepack, Yarn and pnpm plus their caches after installing production modules.
  Exclude nested Terraform caches/state, node_modules, dist and environment files
  from Docker contexts so a local build cannot embed stale providers or secrets.
  Relay's twelve original image findings were entirely in the bundled npm tree.
- Upgrade Cloud workspace libraries and tooling to the current releases:
  Hono/node-server 2.1.5, Hono 4.13.13, pg 8.23.1,
  pg-connection-string 2.14.1, google-auth-library 11.3.1, Zod 4.6.7,
  TypeScript 7.0.2 and Vitest 5.0.5, with current compatible type packages.
  Keep Node typings on 24 and the compiler API test alias on TypeScript 6.0.3;
  TypeScript 7 no longer provides that API. Adapt Zod record/introspection calls
  and explicitly include Node types. Wire contract fields and versions stay the same.
- Pin the broker to Google Cloud CLI 590.0.0 Alpine and Terraform 1.17.0.
  Upgrade the SDK's actual vendored certifi/urllib3 using hash-locked requirements.
  Remove unused bq/gsutil, pip/wheel, and the vulnerable compiled CRC helper.
  Use gcloud's Python checksum implementation instead. Install libstdc++ for the
  Alpine Node runtime. The broker continues to run as uid 10001.
- Pin the official Cloud SQL Auth Proxy v2.26.2 by digest, replacing the old
  image. Its existing Unix socket, private IP and Cloud Run options are retained.
- Upgrade all Cloud workflow Terraform installations to 1.17.0. Lock Google
  provider 8.8.0, external 2.4.2 and random 3.9.1, including Linux/macOS/Windows
  checksums. Review the Google provider
  [v7](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/version_7_upgrade)
  and [v8](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/version_8_upgrade)
  migration notes before rollout. Preserve the prior instance-template disk
  defaults explicitly; load balancer schemes were already explicit.
- Update staging, production, and newly generated Asia cell inputs to
  `cos-121-18867-638-13`, the October 19 security release on the existing COS 121
  LTS milestone. Accept versioned LTS image names as well as legacy `cos-stable`
  names in validation. COS 121 remains supported through March 2027. This is
  release-note verification, not a scan of a running host or boot disk.

## Remaining upstream findings

- `GO-2026-5932`: the Go OpenPGP package is unmaintained and unsafe by design.
  Trivy reports the containing `golang.org/x/crypto` module in Terraform,
  the proxy, and two providers. No fixed module version is published; module
  presence alone does not establish that the affected OpenPGP functions execute.
- Proxy `CVE-2026-111943` (HIGH): official v2.26.2 embeds Go 1.27.2; the
  advisory requires Go 1.27.3 or 1.26.10. Proxy `DSA-6572-1` (UNKNOWN): the
  official distroless image predates the Debian ca-certificates rebuild.
  No newer official proxy release was available at review time.
- The latest external/random provider binaries embed Go 1.26.8; Google 8.8.0
  embeds Go 1.26.9. Additional findings affect their embedded x/net, x/crypto,
  and OpenTelemetry SDK versions. Updating a Terraform lockfile cannot patch
  dependencies inside signed upstream executables. External has 19 instances,
  Google 10, and random 20; all are itemized in the inventory.

Govulncheck 1.9.0 additionally analyzed the exact Linux amd64 Terraform, proxy,
and provider executables in binary symbol mode. The inventory records its
strongest evidence for each matching Go finding. OpenPGP is module-only in the
proxy, while its affected symbols are linked into Terraform, Google and random.
The proxy's affected `crypto/x509.Certificate.Verify` symbol is linked. These
checks establish linked code, not runtime exploitability or a production call
trace; the original Trivy findings remain visible.

| Binary | Unique Go advisories at module level | With affected linked symbols |
| --- | --- | --- |
| Terraform 1.17.0 | 1 | 1 |
| Cloud SQL Proxy 2.26.2 | 2 | 1 |
| Google provider 8.8.0 | 10 | 9 |
| External provider 2.4.2 | 13 | 13 |
| Random provider 3.9.1 | 14 | 14 |

CI retains unfiltered JSON and CycloneDX inventories for every runtime image and
installed Terraform providers, including these findings. Runtime image checks
reject advisories with available fixes at every severity. Only the two specific
proxy rebuild findings have a documented exception, expiring **2026-11-04** in
[the proxy exception file](../security/cloud-sql-proxy.trivyignore.yaml).
Unfixed runtime findings remain in the full reports. Provider scanning is
report-only while upstream binaries retain the findings above; it is not a
provider security approval gate. No global severity exclusions were added.

## Reproduce

Run from a fresh checkout; this is an independent pnpm workspace.

```sh
pnpm --dir cloud install --frozen-lockfile
pnpm --dir cloud audit
pnpm --dir cloud audit --prod
osv-scanner scan --lockfile cloud/pnpm-lock.yaml
gitleaks dir cloud --config cloud/.gitleaks.toml --redact
semgrep scan --config p/security-audit --config p/javascript --config p/typescript --metrics off --jobs 2 --timeout 60 --timeout-threshold 0 cloud
docker build --platform linux/amd64 -f cloud/apps/relay/Dockerfile -t orca-relay-security cloud
trivy image --scanners vuln --ignorefile '' orca-relay-security
```

Cloud Verify additionally builds push and broker (supplying the exact checkout
SHA as `ORCA_RELAY_FENCE_IMAGE_COMMIT`), resolves the proxy from Terraform, tests
nonroot runtime imports and broker tools, and scans installed provider binaries
after backend-free initialization. It needs no GCP credentials and publishes no
images. The existing full-history Gitleaks and snapshot TruffleHog checks remain.

## Validation and rollout

Cloud build and typecheck passed. Relay unit tests passed 1,249 tests with one
existing skip; all 217 PostgreSQL tests passed on a fresh, dedicated database
in an isolated run. Other Cloud packages passed 561 tests, Cloud pretests/runner
passed 233 tests, deployment scripts passed 889 tests, and the root secret-history
workflow tests passed 2 tests. Earlier concurrent runs encountered load-sensitive
contention assertions; reusing their database also exposed retained test fixtures.
No test assertions or timeouts were weakened. The isolated database suite passed
all 44 files. YAML/shell linting of the new workflow steps and the changed-code
quality check passed; Cloud source was additionally checked with Oxlint. The
installed actionlint predates the existing background/wait workflow syntax, so
that unchanged syntax was removed only from a temporary lint input.
Broker checks include Node module loading, gcloud startup, a known CRC32C value
through the Python fallback, Terraform version, backend-free initialization, and
configuration validation. Provider initialization validates signed checksums for
Linux amd64/arm64, macOS amd64/arm64, and Windows amd64; execution was tested on
Linux amd64. No native-focus or desktop UI testing is needed for these changes.

The local GCP session requires reauthentication. Live image inventory, live COS
versions, Cloud SQL maintenance state, deployed IAM/network configuration and
state-backed Terraform plans were therefore **not verified**. No assertion is
made that the deployed fleet already contains these updates.

Before deploying, recheck proxy/provider releases and the remaining advisories.
Build and publish the updated relay, push and broker images through the existing
release process and bind their immutable digests to the rollout inputs. Review
fresh staging and production Terraform plans with valid credentials, paying
particular attention to provider schema changes, the proxy digest, and expected
COS instance-template replacements. Canary staging before rolling production
cells through the existing drain/fencing flow. Preserve prior immutable images
and the versioned Terraform state for recovery; a provider downgrade after
state migration requires its own review. This PR does not perform those steps.

Sources: [Node releases](https://nodejs.org/dist/index.json),
[Cloud SQL Proxy releases](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/releases),
[COS 121 release notes](https://docs.cloud.google.com/container-optimized-os/docs/release-notes/m121),
[COS support policy](https://docs.cloud.google.com/container-optimized-os/docs/concepts/versioning),
[OpenPGP advisory](https://vuln.go.dev/ID/GO-2026-5932.json).

---

### Bug Reproductions Daemon Async Io Stall (`docs/bug-reproductions/daemon-async-io-stall.md`)

# Terminal daemon asynchronous I/O stall

Observed on macOS arm64 on 2026-10-15. The installed app was
`1.4.227-hourly.202610150643`; the preserved v45 daemon was
`1.4.225-adhoc.20261012205540`, running Node 24.21.0 and libuv 1.52.1.

## What was proved

- The affected daemon answered authenticated control RPCs and its existing
  `ptySpawnHealth` probe. It owned 213 running sessions.
- Input sent through the CLI and directly through the daemon's `write` RPC did
  not appear in a selected terminal. Its native `CustomWriteStream` retained
  141 queued chunks / 1,250 bytes, with no retry timer. The shell-ready gate
  was open, and the native handle was neither dead nor marked I/O-failed.
- A synchronous write to that same nonblocking master fd immediately appeared
  in the agent's input. The diagnostic text was removed without submitting it.
  Resizing also reached the real terminal, and output was still received.
- Independent asynchronous filesystem stat and crypto requests did not complete
  while timers and socket RPCs continued working. Process samples showed all
  four libuv workers idle. This is evidence of stalled asynchronous work or
  completion delivery, **not proof of thread-pool saturation**.
- The production `DaemonAsyncIoProbe`, evaluated in that affected daemon,
  returned `stalled` at 5,000.8 ms. The debugger was closed afterwards.

## Cause and remaining investigation

The immediate failure is below the renderer: native asynchronous writes stop
completing, and later keystrokes accumulate behind the first pending write.
New terminals can also block on asynchronous working-directory checks.

The initial runtime/native trigger is **not established**. No evidence yet ties
it to a specific patch, an update, sleep/resume, workload size, or a protocol
version mismatch. Increasing the thread pool, replaying queued input, replacing
the writer with synchronous calls, or restarting healthy daemons is not a
validated prevention fix.

An older build remained alive intentionally: bundle freshness must not terminate
existing terminal processes. This explains why an app update did not replace
this daemon; it does not explain what first stopped its asynchronous work.

## Isolated symptom reproduction

On macOS/Linux, with dependencies prepared for the Node runtime:

```sh
ORCA_BACKGROUND_LAUNCH=1 node config/scripts/ensure-native-runtime.mjs --runtime=node
ORCA_BACKGROUND_LAUNCH=1 UV_THREADPOOL_SIZE=1 node config/scripts/repro-daemon-async-io-stall.mjs
```

The script owns an isolated real PTY and blocks its one filesystem worker with a
FIFO open. It proves that native spawn/exit still passes while asynchronous input
and a working-directory stat stop completing, synchronous input still arrives,
and the production detector reports `stalled`. Releasing the FIFO delivers queued
input and restores `healthy`. It has a 15-second watchdog and never connects to
the user's daemon or sessions. This passed on macOS arm64 (Node 24.20.0), with
detection at 5,000.8 ms.

This reproduces the symptoms by fault injection, **not the original trigger**:
the incident's workers were idle, whereas this reproduction deliberately blocks
a worker. The asynchronous completion boundary is the shared failure surface.

## Detection and recovery contract

`terminal-daemon.async-io-progress`: a responding daemon whose asynchronous work
does not complete must not be represented as fully healthy solely because it
can start and exit a native shell. Detection must never infer process exit,
replay user input, or restart a daemon automatically.

The daemon probes an asynchronous stat of its own executable. Completion,
including completion with an errno, proves progress. A five-second deadline
reports a stall; the original operation stays owned until it actually completes.
Concurrent requests share it, so polling a permanently stalled daemon never
adds another filesystem request. Successful checks are cached for 30 seconds.
The daemon logs transitions as `async-io-stalled` and `async-io-recovered`.

The desktop checks every 30 seconds while the sidebar is mounted and the document
is visible, plus on focus/visibility changes. Checks coalesce and never enumerate
sessions or start terminals. A notice directs the user to the existing Manage
Sessions restart review, which requires confirmation before ending processes.

The new unary RPC is optional: old daemons return `unsupported`; lost contact or
an invalid response is `unverifiable`. Neither proves a stall. An affected old
daemon needs a deliberate restart onto a build with the probe before automatic
detection is available. Updating only the client cannot add instrumentation to
its already running daemon.

## Coverage and gaps

The experimental gate covers stalled/completing work, recovery, deduplication,
bounded timers/requests, old-peer compatibility, disconnected peers, notice
behavior, visibility gating, and unmount cleanup. The production probe also
identified this incident in the affected live daemon.

Local/daemon macOS evidence is covered. The probe is platform-independent, but
physical Linux, Windows, WSL and SSH deployments remain gaps. The notice describes
only this computer's current daemon. Paired web clients explicitly decline local
daemon inspection; remote/legacy daemon notices and hidden-sidebar surfacing are
not covered. Initial-trigger reproduction and a prevention fix remain open.

---

### Bug Reproductions Delayed Agent Launch (`docs/bug-reproductions/delayed-agent-launch.md`)

# Delayed terminal agent launches

## Reproduction (2026-10-12, macOS)

An isolated Orca dev instance ran off screen with `ORCA_BACKGROUND_LAUNCH=1`.
In a folder workspace, `terminal create --command 'sleep 30 && claude'` launched
real Claude Code 2.1.296 after the delay. SessionStart, working and done hooks
arrived, and a prompt requesting `DELAYED_LAUNCH_OK` completed. A blanket Claude
hook failure was **not** reproduced on this machine.

The same delayed command was captured through
`config/scripts/capture-agent-pty-transcript.mjs`. Its startup emits
`OSC 0;✳ Claude Code`, terminal capability queries, and the normal composer.
The capture was scanned for identifiers and credentials; raw output is kept
outside the repository.

Deterministic reproductions found these failures:

- All 45 configured harnesses lost hidden startup query eligibility behind
  `sleep 30 &&`. The renderer query-delivery test also reproduced the drop.
- Delayed OpenCode 2 selected and installed OpenCode v1 hooks. Disabling the
  requested version could install the other version instead.
- Delayed Pi, OMP and Prime launches lost explicit preparation of their own
  integration. Managed OpenCode/Devin accounts and MiMo overlays also relied on
  first-executable classification.
- Once foreground discovery exhausted its initial read window, a captured late
  Claude startup title did not restart confirmation. The connection test left
  process routing unset at 30 seconds.

After the fix, a fresh `sleep 30 && claude` launch gained process-confirmed
routing. Real turns running `sleep 8` and `sleep 20` completed with the requested
replies. CDP screenshots showed the working indicator during a turn and the done
indicator with the completed reply. Blocked, waiting and interrupted turns were
not exercised.

## Change and invariant

`agent-session.delayed-terminal-launch` in `config/reliability-gates.jsonc`
tracks this contract: a delayed launch retains harness preparation and hidden
terminal replies, while only the execution host can confirm a live process.

A shared, bounded scanner removes literal numeric `sleep … &&` prefixes **only
from preparation hints**. The shell receives the original command, including
the delay, arguments and conditional operator. Existing setup-sequence hints,
explicit harness choices, disabled integrations and account ownership retain
precedence. Process-table command recognition still treats the waiting shell
as a shell.

Callers use shared launch-token and launch-recognition APIs. Prefix handling is
contained in `src/shared/agent-command-line-intent.ts`; the setup-sequence resolver
continues to return its supplied command. Launch recognition reuses the existing
agent registry and headless filters without changing strict process recognition.

Fresh agent startup titles request one additional bounded confirmation sequence
when initial discovery has finished. A title never supplies process identity or
input-routing authority. An active read is preserved; title storms, cancellation
and close are covered by deterministic read-count tests. No recurring timer or
sleep-length polling was added.

## Coverage and remaining gaps

| Surface                      | Evidence                                                                                     |
| ---------------------------- | -------------------------------------------------------------------------------------------- |
| Local and daemon preparation | Plugin files, account environment and host spawn tests                                       |
| Renderer replies and routing | Hidden query-delivery and captured late-title tests                                          |
| macOS real Claude            | Hidden Electron/CDP launch and completed prompt                                              |
| SSH/remote-runtime           | Shared classification; existing fenced process reads retained; no physical delayed launch    |
| WSL                          | Shared Pi/OMP and OpenCode preparation; physical guest launch unproved                       |
| Linux/Windows                | Platform-neutral classification and existing mock coverage; physical delayed launch unproved |

The scanner intentionally does not parse arbitrary shell programs, variables,
pipelines or background jobs. A late agent emitting neither a recognizable title
nor hooks still needs a later input or visibility observation. No RPC field or
stream opcode changed; mixed-version wire behavior is unchanged.

---

### Readme README.Es (`docs/readme/README.es.md`)

<h1 align="center">
  <a href="https://onOrca.dev"><img src="../../resources/build/icon.png" alt="Orca" width="64" valign="middle" /></a> Orca
</h1>

<p align="center">
  <a href="https://github.com/stablyai/orca"><img src="https://img.shields.io/github/stars/stablyai/orca?style=flat&amp;label=%E2%98%85&amp;color=08C" alt="Estrellas en GitHub" /></a>
  <a href="https://github.com/stablyai/orca/releases"><img src="../assets/readme-downloads.svg" alt="Descargas totales en todas las versiones" /></a>
  <img src="https://img.shields.io/badge/license-MIT-08C?style=flat" alt="Licencia: MIT" />
  <a href="https://discord.gg/fzjDKHxv8Q"><img src="https://img.shields.io/badge/Discord-5865F2?logo=discord&logoColor=white" alt="Únete al Discord de Orca" /></a>
  <a href="https://x.com/orca_build"><img src="https://img.shields.io/badge/X-000000?logo=x&logoColor=white" alt="Sigue a Orca en X" /></a>
  <img src="https://img.shields.io/badge/macOS%20%7C%20Windows%20%7C%20Linux-4493F8?style=flat-square" alt="Plataformas compatibles: macOS, Windows y Linux" />
</p>

<p align="center">
  <sub><a href="../../README.md">English</a> · <a href="README.zh-CN.md">中文</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.fr.md">Français</a> · <a href="README.pt.md">Português</a></sub>
</p>

<p align="center">
  <strong>El orquestador de IA para desarrolladores 100x.</strong><br/>
  Ejecuta Codex, Claude Code, OpenCode u Pi en paralelo — cada uno en su propio worktree, supervisados desde un solo lugar.
</p>

<h3 align="center"><a href="https://onorca.dev/download"><ins>Descargar Orca</ins></a></h3>

<p align="center">
  <img src="../assets/readme-hero.jpg" alt="La app de escritorio de Orca ejecutando agentes en worktrees paralelos, con la app companion móvil de Orca en la esquina" width="960" />
</p>

## Características

<table>
<tr>
<td width="50%" valign="middle">

### App companion móvil

Supervisa y dirige a tus agentes desde el teléfono — recibe una notificación cuando un agente termine y envía instrucciones de seguimiento desde cualquier lugar.

[App Store de iOS](https://apps.apple.com/us/app/orca-ide/id6766130217) · [APK para Android](https://www.onorca.dev/download/android) · [Docs →](https://www.onorca.dev/docs/mobile)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/mobile"><picture><source srcset="../assets/feature-wall/mobile-companion-app-showcase.gif" type="image/gif"><img src="../assets/feature-wall/mobile-companion-app-showcase.jpg" alt="Orca de escritorio con la app companion móvil" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Worktrees en paralelo

Lanza un mismo prompt a cinco agentes, cada uno en su propio worktree de git aislado — compara los resultados y haz merge del ganador.

[Docs →](https://www.onorca.dev/docs/model/worktrees)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/model/worktrees"><picture><source srcset="../site/public/docs/tab-split.gif" type="image/gif"><img src="../site/public/docs/posters/tab-split.jpg" alt="Orquestación de worktrees en paralelo" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Terminales divididas

Terminales de nivel Ghostty con renderizado WebGL, divisiones infinitas y un scrollback que sobrevive a los reinicios.

[Docs →](https://www.onorca.dev/docs/terminal)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/terminal"><picture><source srcset="../../resources/onboarding/feature-wall/tile-02.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-02.poster.jpg" alt="Terminales divididas" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Modo diseño

Haz clic en cualquier elemento de UI en una ventana real de Chromium para enviar su HTML, su CSS y una captura recortada directo al prompt de tu agente.

[Docs →](https://www.onorca.dev/docs/browser/design-mode)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/browser/design-mode"><picture><source srcset="../site/public/docs/orca-design-mode.gif" type="image/gif"><img src="../../resources/onboarding/feature-wall/tile-05.poster.jpg" alt="Navegador integrado y modo diseño" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### GitHub y Linear, nativos

Explora PRs, issues y tableros de proyecto dentro de la app — abre un worktree desde cualquier tarea y revisa sin cambiar de contexto.

[Docs →](https://www.onorca.dev/docs/review/linear)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/review/linear"><picture><source srcset="../../resources/onboarding/feature-wall/tile-03.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-03.poster.jpg" alt="Flujos de trabajo de GitHub y Linear en Orca" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Worktrees por SSH

Ejecuta agentes en una máquina remota potente con edición completa de archivos, git y terminales — con reconexión automática y reenvío de puertos incluidos.

[Docs →](https://www.onorca.dev/docs/ssh)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/ssh"><picture><source srcset="../../resources/onboarding/feature-wall/tile-06.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-06.poster.jpg" alt="Worktrees remotos por SSH" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Anotar diffs de IA

Deja comentarios en cualquier línea de un diff y envíalos de vuelta al agente — revisa, edita y haz commit sin salir de Orca.

[Docs →](https://www.onorca.dev/docs/review/annotate-ai-diff)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/review/annotate-ai-diff"><picture><source srcset="../site/public/docs/annotate-ai-diff.gif" type="image/gif"><img src="../../resources/onboarding/feature-wall/tile-08.poster.jpg" alt="Anotar diffs generados por IA" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Arrastra archivos a los agentes

El editor de VS Code con autoguardado en todas partes — arrastra archivos o imágenes directo al prompt de un agente.

[Docs →](https://www.onorca.dev/docs/editing/file-explorer)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/editing/file-explorer"><picture><source srcset="../../resources/onboarding/feature-wall/tile-07.gif" type="image/gif"><img src="../../resources/onboarding/feature-wall/tile-07.poster.jpg" alt="Arrastra archivos e imágenes al prompt de un agente" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Orca CLI

Los agentes también manejan Orca — automatiza cualquier flujo de trabajo con `orca worktree create`, `snapshot`, `click` y `fill`.

[Docs →](https://www.onorca.dev/docs/cli/overview)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/cli/overview"><picture><source srcset="../../resources/onboarding/feature-wall/tile-09.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-09.poster.jpg" alt="Automatiza Orca desde la CLI" width="100%" /></picture></a>
</td>
</tr>
</table>

**También incluye:**

- **[Apertura rápida](https://www.onorca.dev/docs/model/quick-open)** — Busca entre worktrees, archivos, agentes, comandos y contexto del repo sin salir de tu flujo.
- **[Cambio de cuenta y seguimiento de uso](https://www.onorca.dev/docs/agents/usage-tracking)** — Consulta el uso de Claude y Codex y los reinicios de límites de uso, y cambia de cuenta al instante sin volver a iniciar sesión.
- **[Previews ricos del repo](https://www.onorca.dev/docs/editing/markdown)** — Previsualiza Markdown, imágenes, PDFs y documentos del repo en el workspace.
- **[Computer Use](https://www.onorca.dev/docs/cli/computer-use)** — Deja que los agentes manejen apps de escritorio y UI visible cuando un flujo de trabajo necesita interacción real.
- **[Notificaciones y estado de no leído](https://www.onorca.dev/docs/notifications)** — Entérate cuando un agente termine o necesite tu atención, y marca hilos como no leídos para retomarlos después.
- **Y muchas, muchas más** — lanzamos a diario, así que esta lista siempre va atrasada. El [changelog](https://github.com/stablyai/orca/releases) es la verdadera lista de funciones.

---

## Agentes compatibles

Funciona con **cualquier agente CLI** — si corre en una terminal, corre en Orca.

<p>
  <a href="https://docs.anthropic.com/claude/docs/claude-code"><kbd><img src="../assets/claude-logo.svg" alt="Claude Code logo" width="16" valign="middle" /> Claude Code</kbd></a> &nbsp;
  <a href="https://github.com/openai/codex"><kbd><img src="https://www.google.com/s2/favicons?domain=openai.com&sz=64" alt="Codex logo" width="16" valign="middle" /> Codex</kbd></a> &nbsp;
  <a href="https://x.ai/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=x.ai&sz=64" alt="Grok logo" width="16" valign="middle" /> Grok</kbd></a> &nbsp;
  <a href="https://cursor.com/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=cursor.com&sz=64" alt="Cursor logo" width="16" valign="middle" /> Cursor</kbd></a> &nbsp;
  <a href="https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli"><kbd><img src="https://www.google.com/s2/favicons?domain=github.com&sz=64" alt="GitHub Copilot logo" width="16" valign="middle" /> GitHub Copilot</kbd></a> &nbsp;
  <a href="https://dev.meta.ai/docs/muse-code"><kbd><img src="../../src/shared/agent-icons/muse.png" alt="Muse logo" width="16" valign="middle" /> Muse</kbd></a> &nbsp;
  <a href="https://deepseek-harness.github.io/deepseek-harness/"><kbd><img src="../../src/shared/agent-icons/dsh.png" alt="DeepSeek Harness logo" width="16" valign="middle" /> DeepSeek Harness</kbd></a> &nbsp;
  <a href="https://zcode.z.ai/en/docs"><kbd><img src="../../src/shared/agent-icons/zcode.png" alt="ZCode logo" width="16" valign="middle" /> ZCode</kbd></a> &nbsp;
  <a href="https://opencode.ai/docs/cli/"><kbd><img src="https://www.google.com/s2/favicons?domain=opencode.ai&sz=64" alt="OpenCode logo" width="16" valign="middle" /> OpenCode</kbd></a> &nbsp;
  <a href="https://ampcode.com/manual#install"><kbd><img src="https://www.google.com/s2/favicons?domain=ampcode.com&sz=64" alt="Amp logo" width="16" valign="middle" /> Amp</kbd></a> &nbsp;
  <a href="https://openclaude.gitlawb.com/"><kbd><img src="../../resources/openclaude-logo.png" alt="OpenClaude logo" width="16" valign="middle" /> OpenClaude</kbd></a> &nbsp;
  <a href="https://antigravity.google/docs/cli-overview"><kbd><img src="https://www.google.com/s2/favicons?domain=antigravity.google&sz=64" alt="Antigravity logo" width="16" valign="middle" /> Antigravity</kbd></a> &nbsp;
  <a href="https://pi.dev"><kbd><img src="https://pi.dev/favicon.svg" alt="Pi logo" width="16" valign="middle" /> Pi</kbd></a> &nbsp;
  <a href="https://omp.sh"><kbd><img src="https://omp.sh/favicon.svg" alt="oh-my-pi logo" width="16" valign="middle" /> oh-my-pi</kbd></a> &nbsp;
  <a href="https://hermes-agent.nousresearch.com/docs/"><kbd><img src="https://www.google.com/s2/favicons?domain=nousresearch.com&sz=64" alt="Hermes Agent logo" width="16" valign="middle" /> Hermes Agent</kbd></a> &nbsp;
  <a href="https://block.github.io/goose/docs/quickstart/"><kbd><img src="https://www.google.com/s2/favicons?domain=goose-docs.ai&sz=64" alt="Goose logo" width="16" valign="middle" /> Goose</kbd></a> &nbsp;
  <a href="https://docs.augmentcode.com/cli/overview"><kbd><img src="https://www.google.com/s2/favicons?domain=augmentcode.com&sz=64" alt="Auggie logo" width="16" valign="middle" /> Auggie</kbd></a> &nbsp;
  <a href="https://github.com/autohandai/code-cli"><kbd><img src="https://www.google.com/s2/favicons?domain=autohand.ai&sz=64" alt="Autohand Code logo" width="16" valign="middle" /> Autohand Code</kbd></a> &nbsp;
  <a href="https://github.com/charmbracelet/crush"><kbd><img src="https://www.google.com/s2/favicons?domain=charm.sh&sz=64" alt="Charm logo" width="16" valign="middle" /> Charm</kbd></a> &nbsp;
  <a href="https://docs.cline.bot/cline-cli/overview"><kbd><img src="https://www.google.com/s2/favicons?domain=cline.bot&sz=64" alt="Cline logo" width="16" valign="middle" /> Cline</kbd></a> &nbsp;
  <a href="https://www.codebuff.com/docs/help/quick-start"><kbd><img src="https://www.google.com/s2/favicons?domain=codebuff.com&sz=64" alt="Codebuff logo" width="16" valign="middle" /> Codebuff</kbd></a> &nbsp;
  <a href="https://freebuff.com/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=freebuff.com&sz=64" alt="Freebuff logo" width="16" valign="middle" /> Freebuff</kbd></a> &nbsp;
  <a href="https://commandcode.ai/docs/quickstart"><kbd><img src="https://www.google.com/s2/favicons?domain=commandcode.ai&sz=64" alt="Command Code logo" width="16" valign="middle" /> Command Code</kbd></a> &nbsp;
  <a href="https://docs.continue.dev/guides/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=continue.dev&sz=64" alt="Continue logo" width="16" valign="middle" /> Continue</kbd></a> &nbsp;
  <a href="https://docs.factory.ai/cli/getting-started/quickstart"><kbd><img src="../assets/droid-logo.svg" alt="Droid logo" width="16" valign="middle" /> Droid</kbd></a> &nbsp;
  <a href="https://kilo.ai/docs/cli"><kbd><img src="https://raw.githubusercontent.com/Kilo-Org/kilocode/main/packages/kilo-vscode/assets/icons/kilo-light.svg" alt="Kilocode logo" width="16" valign="middle" /> Kilocode</kbd></a> &nbsp;
  <a href="https://www.kimi.com/code/docs/en/kimi-code-cli/getting-started.html"><kbd><img src="https://www.google.com/s2/favicons?domain=moonshot.cn&sz=64" alt="Kimi logo" width="16" valign="middle" /> Kimi</kbd></a> &nbsp;
  <a href="https://kiro.dev/docs/cli/"><kbd><img src="https://www.google.com/s2/favicons?domain=kiro.dev&sz=64" alt="Kiro logo" width="16" valign="middle" /> Kiro</kbd></a> &nbsp;
  <a href="https://github.com/mistralai/mistral-vibe"><kbd><img src="https://www.google.com/s2/favicons?domain=mistral.ai&sz=64" alt="Mistral Vibe logo" width="16" valign="middle" /> Mistral Vibe</kbd></a> &nbsp;
  <a href="https://github.com/QwenLM/qwen-code"><kbd><img src="https://www.google.com/s2/favicons?domain=qwenlm.github.io&sz=64" alt="Qwen Code logo" width="16" valign="middle" /> Qwen Code</kbd></a> &nbsp;
  <a href="https://support.atlassian.com/rovo/docs/install-and-run-rovo-dev-cli-on-your-device/"><kbd><img src="https://www.google.com/s2/favicons?domain=atlassian.com&sz=64" alt="Rovo Dev logo" width="16" valign="middle" /> Rovo Dev</kbd></a> &nbsp;
  <kbd>+ any CLI agent</kbd>
</p>

---

## Instalación

### Escritorio — macOS, Windows, Linux

- **[Descarga desde onOrca.dev](https://onorca.dev/download)**
- O descarga un build directamente: [macOS Apple Silicon](https://github.com/stablyai/orca/releases/latest/download/orca-macos-arm64.dmg) · [macOS Intel](https://github.com/stablyai/orca/releases/latest/download/orca-macos-x64.dmg) · [Windows (.exe)](https://github.com/stablyai/orca/releases/latest/download/orca-windows-setup.exe) · [Linux AppImage](https://github.com/stablyai/orca/releases/latest/download/orca-linux.AppImage) · [Todos los builds](https://github.com/stablyai/orca/releases/latest)

_O mediante un gestor de paquetes:_

```bash
# macOS (Homebrew)
brew install --cask stablyai/orca/orca

# Arch Linux (AUR) — or stably-orca-git to build from source
yay -S stably-orca-bin
```

### App companion móvil — iOS, Android

Vincúlala con tu app de escritorio para supervisar y dirigir a tus agentes desde el teléfono.

- **iOS:** [Descargar desde App Store](https://apps.apple.com/us/app/orca-ide/id6766130217)
- **Android:** [Descargar el APK](https://www.onorca.dev/download/android)

---

## Comunidad y soporte

- **Discord:** Únete a la comunidad en **[Discord](https://discord.gg/fzjDKHxv8Q)**.
- **Twitter / X:** Sigue a **[@orca_build](https://x.com/orca_build)** para novedades y anuncios.
- **Feedback e ideas:** Lanzamos rápido. ¿Te falta algo? [Pide una nueva feature](https://github.com/stablyai/orca/issues).
- **Privacidad:** Consulta la [documentación de privacidad y telemetría](https://www.onorca.dev/docs/telemetry) para saber qué datos anónimos de uso recopila Orca y cómo desactivar su envío.
- **Muéstranos tu apoyo:** Dale una [estrella](https://github.com/stablyai/orca) a este repo para seguir nuestros lanzamientos diarios.

---

## Desarrollo

¿Quieres contribuir o ejecutar Orca localmente? Consulta nuestra guía [CONTRIBUTING.md](../../.github/CONTRIBUTING.md).

<a href="https://github.com/stablyai/orca/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=stablyai/orca" alt="Contribuidores de Orca" />
</a>

## Licencia

Orca es libre y de código abierto bajo la [Licencia MIT](../../LICENSE).

---

### Readme README.Fr (`docs/readme/README.fr.md`)

<h1 align="center">
  <a href="https://onOrca.dev"><img src="../../resources/build/icon.png" alt="Orca" width="64" valign="middle" /></a> Orca
</h1>

<p align="center">
  <a href="https://github.com/stablyai/orca"><img src="https://badgen.net/github/stars/stablyai/orca?label=%E2%98%85" alt="Étoiles GitHub" /></a>
  <a href="https://github.com/stablyai/orca/releases"><img src="../assets/readme-downloads.svg" alt="Téléchargements totaux sur toutes les versions" /></a>
  <img src="https://badgen.net/github/license/stablyai/orca" alt="Licence" />
  <a href="https://discord.gg/fzjDKHxv8Q"><img src="https://img.shields.io/badge/Discord-5865F2?logo=discord&logoColor=white" alt="Rejoindre le Discord Orca" /></a>
  <a href="https://x.com/orca_build"><img src="https://img.shields.io/badge/X-000000?logo=x&logoColor=white" alt="Suivre Orca sur X" /></a>
  <img src="https://img.shields.io/badge/macOS%20%7C%20Windows%20%7C%20Linux-4493F8?style=flat-square" alt="Plateformes prises en charge : macOS, Windows et Linux" />
</p>

<p align="center">
  <sub><a href="../../README.md">English</a> · <a href="README.zh-CN.md">中文</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.es.md">Español</a> · <a href="README.pt.md">Português</a></sub>
</p>

<p align="center">
  <strong>L'orchestrateur d'IA pour les builders 100x.</strong><br/>
  Lancez Codex, Claude Code, OpenCode ou Pi côte à côte — chacun dans son propre worktree, le tout suivi au même endroit.
</p>

<h3 align="center"><a href="https://onorca.dev/download"><ins>Télécharger Orca</ins></a></h3>

<p align="center">
  <sub>Sous Windows ? Prenez la <a href="https://github.com/stablyai/orca/releases#release-v1.4.147-rc.3">dernière RC</a> — elle inclut des correctifs Windows.</sub>
</p>

<p align="center">
  <img src="../assets/readme-hero.jpg" alt="Application de bureau Orca exécutant des agents dans des worktrees parallèles, avec l'app companion mobile Orca dans le coin" width="960" />
</p>

## Fonctionnalités

<table>
<tr>
<td width="50%" valign="middle">

### Companion mobile

Surveillez et pilotez vos agents depuis votre téléphone — soyez notifié quand un agent termine, et envoyez des instructions de suivi où que vous soyez.

[App Store iOS](https://apps.apple.com/us/app/orca-ide/id6766130217) · [APK Android](https://www.onorca.dev/download/android) · [Docs →](https://www.onorca.dev/docs/mobile)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/mobile"><picture><source srcset="../assets/feature-wall/mobile-companion-app-showcase.gif" type="image/gif"><img src="../assets/feature-wall/mobile-companion-app-showcase.jpg" alt="Orca desktop avec l'app companion mobile" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Worktrees parallèles

Lancez un même prompt sur cinq agents, chacun dans son propre worktree git isolé — comparez les résultats et mergez le gagnant.

[Docs →](https://www.onorca.dev/docs/model/worktrees)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/model/worktrees"><picture><source srcset="../site/public/docs/tab-split.gif" type="image/gif"><img src="../site/public/docs/posters/tab-split.jpg" alt="Orchestration de worktrees parallèles" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Splits de terminal

Terminaux de niveau Ghostty avec rendu WebGL, splits infinis et un scrollback qui survit aux redémarrages.

[Docs →](https://www.onorca.dev/docs/terminal)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/terminal"><picture><source srcset="../../resources/onboarding/feature-wall/tile-02.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-02.poster.jpg" alt="Splits de terminal" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Mode Design

Cliquez sur n'importe quel élément d'UI dans une vraie fenêtre Chromium pour envoyer son HTML, son CSS et une capture recadrée directement dans le prompt de votre agent.

[Docs →](https://www.onorca.dev/docs/browser/design-mode)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/browser/design-mode"><picture><source srcset="../site/public/docs/orca-design-mode.gif" type="image/gif"><img src="../../resources/onboarding/feature-wall/tile-05.poster.jpg" alt="Navigateur intégré et Mode Design" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### GitHub &amp; Linear, natifs

Parcourez PRs, issues et boards de projet dans l'app — ouvrez un worktree depuis n'importe quelle tâche et reviewz sans changer de contexte.

[Docs →](https://www.onorca.dev/docs/review/linear)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/review/linear"><picture><source srcset="../../resources/onboarding/feature-wall/tile-03.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-03.poster.jpg" alt="Workflows GitHub et Linear dans Orca" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Worktrees SSH

Faites tourner des agents sur une machine distante costaude, avec édition de fichiers, git et terminaux complets — reconnexion auto et port forwarding inclus.

[Docs →](https://www.onorca.dev/docs/ssh)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/ssh"><picture><source srcset="../../resources/onboarding/feature-wall/tile-06.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-06.poster.jpg" alt="Worktrees distants via SSH" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Annoter les diffs IA

Posez des commentaires sur n'importe quelle ligne de diff et renvoyez-les à l'agent — review, édition et commit sans quitter Orca.

[Docs →](https://www.onorca.dev/docs/review/annotate-ai-diff)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/review/annotate-ai-diff"><picture><source srcset="../site/public/docs/annotate-ai-diff.gif" type="image/gif"><img src="../../resources/onboarding/feature-wall/tile-08.poster.jpg" alt="Annoter les diffs générés par l'IA" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Glisser-déposer vers les agents

L'éditeur VS Code avec autosave partout — glissez fichiers ou images directement dans le prompt d'un agent.

[Docs →](https://www.onorca.dev/docs/editing/file-explorer)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/editing/file-explorer"><picture><source srcset="../../resources/onboarding/feature-wall/tile-07.gif" type="image/gif"><img src="../../resources/onboarding/feature-wall/tile-07.poster.jpg" alt="Glisser des fichiers et images dans le prompt d'un agent" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Orca CLI

Les agents pilotent aussi Orca — scriptez n'importe quel workflow avec `orca worktree create`, `snapshot`, `click` et `fill`.

[Docs →](https://www.onorca.dev/docs/cli/overview)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/cli/overview"><picture><source srcset="../../resources/onboarding/feature-wall/tile-09.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-09.poster.jpg" alt="Scripter Orca depuis la CLI" width="100%" /></picture></a>
</td>
</tr>
</table>

**Aussi dans la boîte :**

- **[Quick open](https://www.onorca.dev/docs/model/quick-open)** — Cherchez parmi worktrees, fichiers, agents, commandes et contexte du repo sans quitter votre flow.
- **[Sélecteur de comptes &amp; suivi d'usage](https://www.onorca.dev/docs/agents/usage-tracking)** — Suivez l'usage Claude et Codex et les resets de rate limit, et basculez de compte à chaud sans vous reconnecter.
- **[Aperçus riches du repo](https://www.onorca.dev/docs/editing/markdown)** — Prévisualisez Markdown, images, PDF et docs du repo dans le workspace.
- **[Computer Use](https://www.onorca.dev/docs/cli/computer-use)** — Laissez les agents piloter des apps desktop et l'UI visible quand un workflow demande une vraie interaction.
- **[Notifications et non-lus](https://www.onorca.dev/docs/notifications)** — Sachez quand un agent termine ou a besoin d'attention, puis marquez des fils comme non lus pour y revenir plus tard.
- **Et bien plus encore** — on ship tous les jours, donc cette liste est toujours en retard. Le [changelog](https://github.com/stablyai/orca/releases) est la vraie liste des features.

---

## Agents pris en charge

Fonctionne avec **n'importe quel agent CLI** — s'il tourne dans un terminal, il tourne dans Orca.

<p>
  <a href="https://docs.anthropic.com/claude/docs/claude-code"><kbd><img src="../assets/claude-logo.svg" alt="Logo Claude Code" width="16" valign="middle" /> Claude Code</kbd></a> &nbsp;
  <a href="https://github.com/openai/codex"><kbd><img src="https://www.google.com/s2/favicons?domain=openai.com&sz=64" alt="Logo Codex" width="16" valign="middle" /> Codex</kbd></a> &nbsp;
  <a href="https://x.ai/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=x.ai&sz=64" alt="Logo Grok" width="16" valign="middle" /> Grok</kbd></a> &nbsp;
  <a href="https://cursor.com/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=cursor.com&sz=64" alt="Logo Cursor" width="16" valign="middle" /> Cursor</kbd></a> &nbsp;
  <a href="https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli"><kbd><img src="https://www.google.com/s2/favicons?domain=github.com&sz=64" alt="Logo GitHub Copilot" width="16" valign="middle" /> GitHub Copilot</kbd></a> &nbsp;
  <a href="https://dev.meta.ai/docs/muse-code"><kbd><img src="../../src/shared/agent-icons/muse.png" alt="Logo Muse" width="16" valign="middle" /> Muse</kbd></a> &nbsp;
  <a href="https://deepseek-harness.github.io/deepseek-harness/"><kbd><img src="../../src/shared/agent-icons/dsh.png" alt="DeepSeek Harness logo" width="16" valign="middle" /> DeepSeek Harness</kbd></a> &nbsp;
  <a href="https://zcode.z.ai/en/docs"><kbd><img src="../../src/shared/agent-icons/zcode.png" alt="Logo ZCode" width="16" valign="middle" /> ZCode</kbd></a> &nbsp;
  <a href="https://opencode.ai/docs/cli/"><kbd><img src="https://www.google.com/s2/favicons?domain=opencode.ai&sz=64" alt="Logo OpenCode" width="16" valign="middle" /> OpenCode</kbd></a> &nbsp;
  <a href="https://mimo.xiaomi.com/coder"><kbd><img src="https://www.google.com/s2/favicons?domain=mimo.xiaomi.com&sz=64" alt="Logo MiMo Code" width="16" valign="middle" /> MiMo Code</kbd></a> &nbsp;
  <a href="https://ampcode.com/manual#install"><kbd><img src="https://www.google.com/s2/favicons?domain=ampcode.com&sz=64" alt="Logo Amp" width="16" valign="middle" /> Amp</kbd></a> &nbsp;
  <a href="https://openclaude.gitlawb.com/"><kbd><img src="../../resources/openclaude-logo.png" alt="Logo OpenClaude" width="16" valign="middle" /> OpenClaude</kbd></a> &nbsp;
  <a href="https://antigravity.google/docs/cli-overview"><kbd><img src="https://www.google.com/s2/favicons?domain=antigravity.google&sz=64" alt="Logo Antigravity" width="16" valign="middle" /> Antigravity</kbd></a> &nbsp;
  <a href="https://pi.dev"><kbd><img src="https://pi.dev/favicon.svg" alt="Logo Pi" width="16" valign="middle" /> Pi</kbd></a> &nbsp;
  <a href="https://omp.sh"><kbd><img src="https://omp.sh/favicon.svg" alt="Logo oh-my-pi" width="16" valign="middle" /> oh-my-pi</kbd></a> &nbsp;
  <a href="https://hermes-agent.nousresearch.com/docs/"><kbd><img src="https://www.google.com/s2/favicons?domain=nousresearch.com&sz=64" alt="Logo Hermes Agent" width="16" valign="middle" /> Hermes Agent</kbd></a> &nbsp;
  <a href="https://devin.ai/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=devin.ai&sz=64" alt="Logo Devin" width="16" valign="middle" /> Devin</kbd></a> &nbsp;
  <a href="https://block.github.io/goose/docs/quickstart/"><kbd><img src="https://www.google.com/s2/favicons?domain=goose-docs.ai&sz=64" alt="Logo Goose" width="16" valign="middle" /> Goose</kbd></a> &nbsp;
  <a href="https://docs.augmentcode.com/cli/overview"><kbd><img src="https://www.google.com/s2/favicons?domain=augmentcode.com&sz=64" alt="Logo Auggie" width="16" valign="middle" /> Auggie</kbd></a> &nbsp;
  <a href="https://github.com/autohandai/code-cli"><kbd><img src="https://www.google.com/s2/favicons?domain=autohand.ai&sz=64" alt="Logo Autohand Code" width="16" valign="middle" /> Autohand Code</kbd></a> &nbsp;
  <a href="https://github.com/charmbracelet/crush"><kbd><img src="https://www.google.com/s2/favicons?domain=charm.sh&sz=64" alt="Logo Charm" width="16" valign="middle" /> Charm</kbd></a> &nbsp;
  <a href="https://docs.cline.bot/cline-cli/overview"><kbd><img src="https://www.google.com/s2/favicons?domain=cline.bot&sz=64" alt="Logo Cline" width="16" valign="middle" /> Cline</kbd></a> &nbsp;
  <a href="https://www.codebuff.com/docs/help/quick-start"><kbd><img src="https://www.google.com/s2/favicons?domain=codebuff.com&sz=64" alt="Logo Codebuff" width="16" valign="middle" /> Codebuff</kbd></a> &nbsp;
  <a href="https://freebuff.com/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=freebuff.com&sz=64" alt="Logo Freebuff" width="16" valign="middle" /> Freebuff</kbd></a> &nbsp;
  <a href="https://commandcode.ai/docs/quickstart"><kbd><img src="https://www.google.com/s2/favicons?domain=commandcode.ai&sz=64" alt="Logo Command Code" width="16" valign="middle" /> Command Code</kbd></a> &nbsp;
  <a href="https://docs.continue.dev/guides/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=continue.dev&sz=64" alt="Logo Continue" width="16" valign="middle" /> Continue</kbd></a> &nbsp;
  <a href="https://docs.factory.ai/cli/getting-started/quickstart"><kbd><img src="../assets/droid-logo.svg" alt="Logo Droid" width="16" valign="middle" /> Droid</kbd></a> &nbsp;
  <a href="https://kilo.ai/docs/cli"><kbd><img src="https://raw.githubusercontent.com/Kilo-Org/kilocode/main/packages/kilo-vscode/assets/icons/kilo-light.svg" alt="Logo Kilocode" width="16" valign="middle" /> Kilocode</kbd></a> &nbsp;
  <a href="https://www.kimi.com/code/docs/en/kimi-code-cli/getting-started.html"><kbd><img src="https://www.google.com/s2/favicons?domain=moonshot.cn&sz=64" alt="Logo Kimi" width="16" valign="middle" /> Kimi</kbd></a> &nbsp;
  <a href="https://kiro.dev/docs/cli/"><kbd><img src="https://www.google.com/s2/favicons?domain=kiro.dev&sz=64" alt="Logo Kiro" width="16" valign="middle" /> Kiro</kbd></a> &nbsp;
  <a href="https://github.com/mistralai/mistral-vibe"><kbd><img src="https://www.google.com/s2/favicons?domain=mistral.ai&sz=64" alt="Logo Mistral Vibe" width="16" valign="middle" /> Mistral Vibe</kbd></a> &nbsp;
  <a href="https://github.com/QwenLM/qwen-code"><kbd><img src="https://www.google.com/s2/favicons?domain=qwenlm.github.io&sz=64" alt="Logo Qwen Code" width="16" valign="middle" /> Qwen Code</kbd></a> &nbsp;
  <a href="https://support.atlassian.com/rovo/docs/install-and-run-rovo-dev-cli-on-your-device/"><kbd><img src="https://www.google.com/s2/favicons?domain=atlassian.com&sz=64" alt="Logo Rovo Dev" width="16" valign="middle" /> Rovo Dev</kbd></a> &nbsp;
  <kbd>+ n'importe quel agent CLI</kbd>
</p>

---

## Installation

### Desktop — macOS, Windows, Linux

- **[Télécharger depuis onOrca.dev](https://onorca.dev/download)**
- Ou récupérez un build directement : [macOS Apple Silicon](https://github.com/stablyai/orca/releases/latest/download/orca-macos-arm64.dmg) · [macOS Intel](https://github.com/stablyai/orca/releases/latest/download/orca-macos-x64.dmg) · [Windows (.exe)](https://github.com/stablyai/orca/releases/download/v1.4.147-rc.3/orca-windows-setup.exe) · [Linux AppImage](https://github.com/stablyai/orca/releases/latest/download/orca-linux.AppImage) · [Tous les builds](https://github.com/stablyai/orca/releases/latest)
- **Sous Windows :** utilisez la [dernière RC (`v1.4.147-rc.3`)](https://github.com/stablyai/orca/releases#release-v1.4.147-rc.3) — elle inclut des correctifs Windows absents de la stable.

_Ou via un gestionnaire de paquets :_

```bash
# macOS (Homebrew)
brew install --cask stablyai/orca/orca

# Arch Linux (AUR) — ou stably-orca-git pour compiler depuis les sources
yay -S stably-orca-bin
```

### Companion mobile — iOS, Android

Associez-la à l'app de bureau pour surveiller et piloter vos agents depuis votre téléphone.

- **iOS :** [Télécharger sur l'App Store](https://apps.apple.com/us/app/orca-ide/id6766130217)
- **Android :** [Télécharger l'APK](https://www.onorca.dev/download/android)

---

## Communauté &amp; support

- **Discord :** Rejoignez la communauté sur **[Discord](https://discord.gg/fzjDKHxv8Q)**.
- **Twitter / X :** Suivez **[@orca_build](https://x.com/orca_build)** pour les news et annonces.
- **WeChat :** Le groupe 11 est complet. Scannez pour rejoindre le groupe WeChat 12 de la communauté Orca.

  <img src="../assets/wechat-qr-group12.jpg" alt="QR code WeChat groupe 12 de la communauté Orca" width="160" />

- **Feedback &amp; idées :** On ship vite. Il manque quelque chose ? [Demandez une feature](https://github.com/stablyai/orca/issues).
- **Confidentialité :** Voir la [doc confidentialité &amp; télémétrie](https://www.onorca.dev/docs/telemetry) pour ce qu'Orca collecte en anonyme et comment désactiver la télémétrie.
- **Soutenez-nous :** [Mettez une star](https://github.com/stablyai/orca) sur ce repo pour suivre nos ships quotidiens.

---

## Développement

Envie de contribuer ou de lancer le projet en local ? Consultez notre guide [CONTRIBUTING.md](../../.github/CONTRIBUTING.md).

<a href="https://github.com/stablyai/orca/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=stablyai/orca" alt="Contributeurs Orca" />
</a>

<p align="center">
  <img src="../assets/star-history.png" alt="Graphique d'historique des étoiles GitHub pour stablyai/orca" width="880" />
</p>

## Builds signés

Signature de code Windows sponsorisée / fournie par [SignPath.io](https://signpath.io), certificat par [SignPath Foundation](https://signpath.org).

## Licence

Orca est libre et open source sous la [licence MIT](../../LICENSE).

---

### Readme README.Ja (`docs/readme/README.ja.md`)

<h1 align="center">
  <a href="https://onOrca.dev"><img src="../../resources/build/icon.png" alt="Orca" width="64" valign="middle" /></a> Orca
</h1>

<p align="center">
  <a href="https://github.com/stablyai/orca"><img src="https://img.shields.io/github/stars/stablyai/orca?style=flat&amp;label=%E2%98%85&amp;color=08C" alt="GitHub スター数" /></a>
  <a href="https://github.com/stablyai/orca/releases"><img src="../assets/readme-downloads.svg" alt="全リリースの合計ダウンロード数" /></a>
  <img src="https://img.shields.io/badge/license-MIT-08C?style=flat" alt="ライセンス: MIT" />
  <a href="https://discord.gg/fzjDKHxv8Q"><img src="https://img.shields.io/badge/Discord-5865F2?logo=discord&logoColor=white" alt="Orca の Discord に参加" /></a>
  <a href="https://x.com/orca_build"><img src="https://img.shields.io/badge/X-000000?logo=x&logoColor=white" alt="X で Orca をフォロー" /></a>
  <img src="https://img.shields.io/badge/macOS%20%7C%20Windows%20%7C%20Linux-4493F8?style=flat-square" alt="対応プラットフォーム: macOS、Windows、Linux" />
</p>

<p align="center">
  <sub><a href="../../README.md">English</a> · <a href="README.zh-CN.md">中文</a> · <a href="README.ko.md">한국어</a> · <a href="README.es.md">Español</a> · <a href="README.fr.md">Français</a> · <a href="README.pt.md">Português</a></sub>
</p>

<p align="center">
  <strong>100x ビルダーのための AI オーケストレーター。</strong><br/>
  Codex、Claude Code、OpenCode、Pi を並べて実行 — それぞれを専用のワークツリーで動かし、1 か所で追跡できます。
</p>

<h3 align="center"><a href="https://onorca.dev/download"><ins>Orca をダウンロード</ins></a></h3>

<p align="center">
  <img src="../assets/readme-hero.jpg" alt="並列ワークツリーでエージェントを実行する Orca デスクトップアプリと、隅に表示された Orca モバイル companion アプリ" width="960" />
</p>

## 機能

<table>
<tr>
<td width="50%" valign="middle">

### モバイル Companion

スマートフォンからエージェントを監視・操作 — エージェントの完了を通知で受け取り、どこからでもフォローアップを送信できます。

[iOS App Store](https://apps.apple.com/us/app/orca-ide/id6766130217) · [Android APK](https://www.onorca.dev/download/android) · [ドキュメント →](https://www.onorca.dev/docs/mobile)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/mobile"><picture><source srcset="../assets/feature-wall/mobile-companion-app-showcase.gif" type="image/gif"><img src="../assets/feature-wall/mobile-companion-app-showcase.jpg" alt="Orca デスクトップとモバイル companion アプリ" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### 並列ワークツリー

1 つのプロンプトを 5 つのエージェントに展開し、それぞれを独立した git ワークツリーで実行 — 結果を比較して、最良のものをマージできます。

[ドキュメント →](https://www.onorca.dev/docs/model/worktrees)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/model/worktrees"><picture><source srcset="../site/public/docs/tab-split.gif" type="image/gif"><img src="../site/public/docs/posters/tab-split.jpg" alt="並列ワークツリーのオーケストレーション" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### ターミナル分割

WebGL レンダリング、無制限の分割、再起動後も残るスクロールバックを備えた Ghostty クラスのターミナル。

[ドキュメント →](https://www.onorca.dev/docs/terminal)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/terminal"><picture><source srcset="../../resources/onboarding/feature-wall/tile-02.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-02.poster.jpg" alt="ターミナル分割" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### デザインモード

実際の Chromium ウィンドウで任意の UI 要素をクリックすると、その HTML、CSS、切り抜いたスクリーンショットがそのままエージェントのプロンプトに送られます。

[ドキュメント →](https://www.onorca.dev/docs/browser/design-mode)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/browser/design-mode"><picture><source srcset="../site/public/docs/orca-design-mode.gif" type="image/gif"><img src="../../resources/onboarding/feature-wall/tile-05.poster.jpg" alt="組み込みブラウザとデザインモード" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### GitHub &amp; Linear をネイティブに

PR、Issue、プロジェクトボードをアプリ内で閲覧 — 任意のタスクからワークツリーを開き、コンテキストスイッチなしでレビューできます。

[ドキュメント →](https://www.onorca.dev/docs/review/linear)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/review/linear"><picture><source srcset="../../resources/onboarding/feature-wall/tile-03.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-03.poster.jpg" alt="Orca の GitHub と Linear タスクワークフロー" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### SSH ワークツリー

強力なリモートマシン上でエージェントを実行 — ファイル編集、git、ターミナルをフルに使え、自動再接続とポートフォワーディングも付属します。

[ドキュメント →](https://www.onorca.dev/docs/ssh)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/ssh"><picture><source srcset="../../resources/onboarding/feature-wall/tile-06.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-06.poster.jpg" alt="SSH 経由のリモートワークツリー" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### AI Diff に注釈

任意の Diff 行にコメントを付けてエージェントへ送り返せます — Orca から離れずにレビュー、編集、コミットまで完結します。

[ドキュメント →](https://www.onorca.dev/docs/review/annotate-ai-diff)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/review/annotate-ai-diff"><picture><source srcset="../site/public/docs/annotate-ai-diff.gif" type="image/gif"><img src="../../resources/onboarding/feature-wall/tile-08.poster.jpg" alt="AI が生成した Diff への注釈" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### ファイルをエージェントへドラッグ

オートセーブが全面的に効く VS Code のエディタ — ファイルや画像をそのままエージェントのプロンプトへドラッグできます。

[ドキュメント →](https://www.onorca.dev/docs/editing/file-explorer)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/editing/file-explorer"><picture><source srcset="../../resources/onboarding/feature-wall/tile-07.gif" type="image/gif"><img src="../../resources/onboarding/feature-wall/tile-07.poster.jpg" alt="ファイルや画像をエージェントのプロンプトへドラッグ" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Orca CLI

エージェント自身も Orca を操作できます — `orca worktree create`、`snapshot`、`click`、`fill` であらゆるワークフローをスクリプト化できます。

[ドキュメント →](https://www.onorca.dev/docs/cli/overview)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/cli/overview"><picture><source srcset="../../resources/onboarding/feature-wall/tile-09.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-09.poster.jpg" alt="CLI から Orca をスクリプト操作" width="100%" /></picture></a>
</td>
</tr>
</table>

**さらに同梱:**

- **[クイックオープン](https://www.onorca.dev/docs/model/quick-open)** — フローを離れずに、ワークツリー、ファイル、エージェント、コマンド、リポジトリコンテキストを横断検索できます。
- **[アカウント切り替えと使用量トラッキング](https://www.onorca.dev/docs/agents/usage-tracking)** — Claude と Codex の使用量やレート制限のリセットを確認し、再ログインなしでアカウントを切り替えられます。
- **[リッチなリポジトリプレビュー](https://www.onorca.dev/docs/editing/markdown)** — Markdown、画像、PDF、リポジトリ文書をワークスペース内でプレビューできます。
- **[Computer Use](https://www.onorca.dev/docs/cli/computer-use)** — 実際の操作が必要なワークフローでは、エージェントにデスクトップアプリや画面上の UI を操作させられます。
- **[通知と未読ステータス](https://www.onorca.dev/docs/notifications)** — エージェントの完了や要対応をすぐに把握し、スレッドを未読に戻して後で確認できます。
- **その他、まだまだたくさん** — 毎日リリースしているので、このリストは常に追いついていません。本当の機能一覧は[チェンジログ](https://github.com/stablyai/orca/releases)です。

---

## 対応するエージェント

**あらゆる CLI エージェント**で動作します — ターミナルで動くものなら、Orca でも動きます。

<p>
  <a href="https://docs.anthropic.com/claude/docs/claude-code"><kbd><img src="../assets/claude-logo.svg" alt="Claude Code logo" width="16" valign="middle" /> Claude Code</kbd></a> &nbsp;
  <a href="https://github.com/openai/codex"><kbd><img src="https://www.google.com/s2/favicons?domain=openai.com&sz=64" alt="Codex logo" width="16" valign="middle" /> Codex</kbd></a> &nbsp;
  <a href="https://x.ai/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=x.ai&sz=64" alt="Grok logo" width="16" valign="middle" /> Grok</kbd></a> &nbsp;
  <a href="https://cursor.com/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=cursor.com&sz=64" alt="Cursor logo" width="16" valign="middle" /> Cursor</kbd></a> &nbsp;
  <a href="https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli"><kbd><img src="https://www.google.com/s2/favicons?domain=github.com&sz=64" alt="GitHub Copilot logo" width="16" valign="middle" /> GitHub Copilot</kbd></a> &nbsp;
  <a href="https://dev.meta.ai/docs/muse-code"><kbd><img src="../../src/shared/agent-icons/muse.png" alt="Muse logo" width="16" valign="middle" /> Muse</kbd></a> &nbsp;
  <a href="https://deepseek-harness.github.io/deepseek-harness/"><kbd><img src="../../src/shared/agent-icons/dsh.png" alt="DeepSeek Harness logo" width="16" valign="middle" /> DeepSeek Harness</kbd></a> &nbsp;
  <a href="https://zcode.z.ai/en/docs"><kbd><img src="../../src/shared/agent-icons/zcode.png" alt="ZCode logo" width="16" valign="middle" /> ZCode</kbd></a> &nbsp;
  <a href="https://opencode.ai/docs/cli/"><kbd><img src="https://www.google.com/s2/favicons?domain=opencode.ai&sz=64" alt="OpenCode logo" width="16" valign="middle" /> OpenCode</kbd></a> &nbsp;
  <a href="https://ampcode.com/manual#install"><kbd><img src="https://www.google.com/s2/favicons?domain=ampcode.com&sz=64" alt="Amp logo" width="16" valign="middle" /> Amp</kbd></a> &nbsp;
  <a href="https://openclaude.gitlawb.com/"><kbd><img src="../../resources/openclaude-logo.png" alt="OpenClaude logo" width="16" valign="middle" /> OpenClaude</kbd></a> &nbsp;
  <a href="https://antigravity.google/docs/cli-overview"><kbd><img src="https://www.google.com/s2/favicons?domain=antigravity.google&sz=64" alt="Antigravity logo" width="16" valign="middle" /> Antigravity</kbd></a> &nbsp;
  <a href="https://pi.dev"><kbd><img src="https://pi.dev/favicon.svg" alt="Pi logo" width="16" valign="middle" /> Pi</kbd></a> &nbsp;
  <a href="https://omp.sh"><kbd><img src="https://omp.sh/favicon.svg" alt="oh-my-pi logo" width="16" valign="middle" /> oh-my-pi</kbd></a> &nbsp;
  <a href="https://hermes-agent.nousresearch.com/docs/"><kbd><img src="https://www.google.com/s2/favicons?domain=nousresearch.com&sz=64" alt="Hermes Agent logo" width="16" valign="middle" /> Hermes Agent</kbd></a> &nbsp;
  <a href="https://block.github.io/goose/docs/quickstart/"><kbd><img src="https://www.google.com/s2/favicons?domain=goose-docs.ai&sz=64" alt="Goose logo" width="16" valign="middle" /> Goose</kbd></a> &nbsp;
  <a href="https://docs.augmentcode.com/cli/overview"><kbd><img src="https://www.google.com/s2/favicons?domain=augmentcode.com&sz=64" alt="Auggie logo" width="16" valign="middle" /> Auggie</kbd></a> &nbsp;
  <a href="https://github.com/autohandai/code-cli"><kbd><img src="https://www.google.com/s2/favicons?domain=autohand.ai&sz=64" alt="Autohand Code logo" width="16" valign="middle" /> Autohand Code</kbd></a> &nbsp;
  <a href="https://github.com/charmbracelet/crush"><kbd><img src="https://www.google.com/s2/favicons?domain=charm.sh&sz=64" alt="Charm logo" width="16" valign="middle" /> Charm</kbd></a> &nbsp;
  <a href="https://docs.cline.bot/cline-cli/overview"><kbd><img src="https://www.google.com/s2/favicons?domain=cline.bot&sz=64" alt="Cline logo" width="16" valign="middle" /> Cline</kbd></a> &nbsp;
  <a href="https://www.codebuff.com/docs/help/quick-start"><kbd><img src="https://www.google.com/s2/favicons?domain=codebuff.com&sz=64" alt="Codebuff logo" width="16" valign="middle" /> Codebuff</kbd></a> &nbsp;
  <a href="https://freebuff.com/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=freebuff.com&sz=64" alt="Freebuff logo" width="16" valign="middle" /> Freebuff</kbd></a> &nbsp;
  <a href="https://commandcode.ai/docs/quickstart"><kbd><img src="https://www.google.com/s2/favicons?domain=commandcode.ai&sz=64" alt="Command Code logo" width="16" valign="middle" /> Command Code</kbd></a> &nbsp;
  <a href="https://docs.continue.dev/guides/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=continue.dev&sz=64" alt="Continue logo" width="16" valign="middle" /> Continue</kbd></a> &nbsp;
  <a href="https://docs.factory.ai/cli/getting-started/quickstart"><kbd><img src="../assets/droid-logo.svg" alt="Droid logo" width="16" valign="middle" /> Droid</kbd></a> &nbsp;
  <a href="https://kilo.ai/docs/cli"><kbd><img src="https://raw.githubusercontent.com/Kilo-Org/kilocode/main/packages/kilo-vscode/assets/icons/kilo-light.svg" alt="Kilocode logo" width="16" valign="middle" /> Kilocode</kbd></a> &nbsp;
  <a href="https://www.kimi.com/code/docs/en/kimi-code-cli/getting-started.html"><kbd><img src="https://www.google.com/s2/favicons?domain=moonshot.cn&sz=64" alt="Kimi logo" width="16" valign="middle" /> Kimi</kbd></a> &nbsp;
  <a href="https://kiro.dev/docs/cli/"><kbd><img src="https://www.google.com/s2/favicons?domain=kiro.dev&sz=64" alt="Kiro logo" width="16" valign="middle" /> Kiro</kbd></a> &nbsp;
  <a href="https://github.com/mistralai/mistral-vibe"><kbd><img src="https://www.google.com/s2/favicons?domain=mistral.ai&sz=64" alt="Mistral Vibe logo" width="16" valign="middle" /> Mistral Vibe</kbd></a> &nbsp;
  <a href="https://github.com/QwenLM/qwen-code"><kbd><img src="https://www.google.com/s2/favicons?domain=qwenlm.github.io&sz=64" alt="Qwen Code logo" width="16" valign="middle" /> Qwen Code</kbd></a> &nbsp;
  <a href="https://support.atlassian.com/rovo/docs/install-and-run-rovo-dev-cli-on-your-device/"><kbd><img src="https://www.google.com/s2/favicons?domain=atlassian.com&sz=64" alt="Rovo Dev logo" width="16" valign="middle" /> Rovo Dev</kbd></a> &nbsp;
  <kbd>+ any CLI agent</kbd>
</p>

---

## インストール

### デスクトップ — macOS, Windows, Linux

- **[onOrca.dev からダウンロード](https://onorca.dev/download)**
- またはビルドを直接入手: [macOS Apple Silicon](https://github.com/stablyai/orca/releases/latest/download/orca-macos-arm64.dmg) · [macOS Intel](https://github.com/stablyai/orca/releases/latest/download/orca-macos-x64.dmg) · [Windows (.exe)](https://github.com/stablyai/orca/releases/latest/download/orca-windows-setup.exe) · [Linux AppImage](https://github.com/stablyai/orca/releases/latest/download/orca-linux.AppImage) · [すべてのビルド](https://github.com/stablyai/orca/releases/latest)

_パッケージマネージャーからもインストールできます:_

```bash
# macOS (Homebrew)
brew install --cask stablyai/orca/orca

# Arch Linux (AUR) — or stably-orca-git to build from source
yay -S stably-orca-bin
```

### モバイル Companion — iOS, Android

デスクトップアプリとペアリングして、スマートフォンからエージェントを監視・操作できます。

- **iOS:** [App Store からダウンロード](https://apps.apple.com/us/app/orca-ide/id6766130217)
- **Android:** [APK をダウンロード](https://www.onorca.dev/download/android)

---

## コミュニティとサポート

- **Discord:** **[Discord](https://discord.gg/fzjDKHxv8Q)** のコミュニティに参加してください。
- **Twitter / X:** アップデートやお知らせは **[@orca_build](https://x.com/orca_build)** をフォローしてください。
- **フィードバックとアイデア:** 私たちは高速にリリースしています。足りない機能がありますか？[機能リクエストを送信](https://github.com/stablyai/orca/issues)してください。
- **プライバシー:** Orca が収集する匿名の利用データとオプトアウトの方法については、[プライバシーとテレメトリーのドキュメント](https://www.onorca.dev/docs/telemetry)をご覧ください。
- **応援する:** 毎日のリリースを追うために、このリポジトリに[スター](https://github.com/stablyai/orca)を付けてください。

---

## 開発について

貢献したい、またはローカルで実行したいですか？ [CONTRIBUTING.md](../../.github/CONTRIBUTING.md) ガイドをご覧ください。

<a href="https://github.com/stablyai/orca/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=stablyai/orca" alt="Orca のコントリビューター" />
</a>

## ライセンス

Orca は [MIT License](../../LICENSE) の下で無料かつオープンソースです。

---

### Readme README.Ko (`docs/readme/README.ko.md`)

<h1 align="center">
  <a href="https://onOrca.dev"><img src="../../resources/build/icon.png" alt="Orca" width="64" valign="middle" /></a> Orca
</h1>

<p align="center">
  <a href="https://github.com/stablyai/orca"><img src="https://img.shields.io/github/stars/stablyai/orca?style=flat&amp;label=%E2%98%85&amp;color=08C" alt="GitHub 스타" /></a>
  <a href="https://github.com/stablyai/orca/releases"><img src="../assets/readme-downloads.svg" alt="전체 릴리스 누적 다운로드 수" /></a>
  <img src="https://img.shields.io/badge/license-MIT-08C?style=flat" alt="라이선스: MIT" />
  <a href="https://discord.gg/fzjDKHxv8Q"><img src="https://img.shields.io/badge/Discord-5865F2?logo=discord&logoColor=white" alt="Orca Discord 참여" /></a>
  <a href="https://x.com/orca_build"><img src="https://img.shields.io/badge/X-000000?logo=x&logoColor=white" alt="X에서 Orca 팔로우" /></a>
  <img src="https://img.shields.io/badge/macOS%20%7C%20Windows%20%7C%20Linux-4493F8?style=flat-square" alt="지원 플랫폼: macOS, Windows, Linux" />
</p>

<p align="center">
  <sub><a href="../../README.md">English</a> · <a href="README.zh-CN.md">中文</a> · <a href="README.ja.md">日本語</a> · <a href="README.es.md">Español</a> · <a href="README.fr.md">Français</a> · <a href="README.pt.md">Português</a></sub>
</p>

<p align="center">
  <strong>100x 빌더를 위한 AI 오케스트레이터.</strong><br/>
  Codex, Claude Code, OpenCode, Pi를 나란히 실행하세요. — 각 에이전트는 자체 worktree에서 실행되고 한곳에서 추적됩니다.
</p>

<h3 align="center"><a href="https://onorca.dev/download"><ins>Orca 다운로드</ins></a></h3>

<p align="center">
  <img src="../assets/readme-hero.jpg" alt="병렬 worktree에서 에이전트를 실행 중인 Orca 데스크톱 앱과 한쪽 모서리에 보이는 Orca 모바일 companion 앱" width="960" />
</p>

## 기능

<table>
<tr>
<td width="50%" valign="middle">

### 모바일 Companion

휴대폰에서 에이전트를 모니터링하고 조종하세요 — 에이전트가 완료되면 알림을 받고 어디서든 후속 지시를 보낼 수 있습니다.

[iOS App Store](https://apps.apple.com/us/app/orca-ide/id6766130217) · [Android APK](https://www.onorca.dev/download/android) · [문서 →](https://www.onorca.dev/docs/mobile)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/mobile"><picture><source srcset="../assets/feature-wall/mobile-companion-app-showcase.gif" type="image/gif"><img src="../assets/feature-wall/mobile-companion-app-showcase.jpg" alt="Orca 데스크톱과 모바일 companion 앱" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### 병렬 Worktree

하나의 프롬프트를 다섯 에이전트에 동시에 보내세요. 각 에이전트는 격리된 자체 git worktree에서 실행됩니다 — 결과를 비교하고 가장 좋은 것을 머지하세요.

[문서 →](https://www.onorca.dev/docs/model/worktrees)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/model/worktrees"><picture><source srcset="../site/public/docs/tab-split.gif" type="image/gif"><img src="../site/public/docs/posters/tab-split.jpg" alt="병렬 worktree 오케스트레이션" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### 터미널 분할

WebGL 렌더링, 무한 분할, 재시작 후에도 유지되는 스크롤백을 갖춘 Ghostty급 터미널.

[문서 →](https://www.onorca.dev/docs/terminal)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/terminal"><picture><source srcset="../../resources/onboarding/feature-wall/tile-02.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-02.poster.jpg" alt="터미널 분할" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### 디자인 모드

실제 Chromium 창에서 UI 요소를 클릭하면 해당 HTML, CSS, 잘라낸 스크린샷이 에이전트 프롬프트로 바로 전송됩니다.

[문서 →](https://www.onorca.dev/docs/browser/design-mode)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/browser/design-mode"><picture><source srcset="../site/public/docs/orca-design-mode.gif" type="image/gif"><img src="../../resources/onboarding/feature-wall/tile-05.poster.jpg" alt="내장 브라우저와 디자인 모드" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### GitHub &amp; Linear 네이티브

PR, issue, 프로젝트 보드를 앱 안에서 탐색하세요 — 어떤 작업에서든 worktree를 열고 컨텍스트 전환 없이 리뷰할 수 있습니다.

[문서 →](https://www.onorca.dev/docs/review/linear)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/review/linear"><picture><source srcset="../../resources/onboarding/feature-wall/tile-03.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-03.poster.jpg" alt="Orca의 GitHub 및 Linear 작업 워크플로" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### SSH Worktree

강력한 원격 머신에서 에이전트를 실행하세요. 파일 편집, git, 터미널을 모두 지원하며 자동 재연결과 포트 포워딩도 포함됩니다.

[문서 →](https://www.onorca.dev/docs/ssh)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/ssh"><picture><source srcset="../../resources/onboarding/feature-wall/tile-06.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-06.poster.jpg" alt="SSH를 통한 원격 worktree" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### AI Diff 주석

diff의 어느 줄에든 코멘트를 남기고 에이전트에게 바로 보내세요 — Orca를 떠나지 않고 리뷰하고 수정하고 커밋할 수 있습니다.

[문서 →](https://www.onorca.dev/docs/review/annotate-ai-diff)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/review/annotate-ai-diff"><picture><source srcset="../site/public/docs/annotate-ai-diff.gif" type="image/gif"><img src="../../resources/onboarding/feature-wall/tile-08.poster.jpg" alt="AI가 생성한 diff에 주석 달기" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### 에이전트로 파일 드래그

어디서나 자동 저장되는 VS Code 에디터 — 파일이나 이미지를 에이전트 프롬프트로 바로 드래그하세요.

[문서 →](https://www.onorca.dev/docs/editing/file-explorer)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/editing/file-explorer"><picture><source srcset="../../resources/onboarding/feature-wall/tile-07.gif" type="image/gif"><img src="../../resources/onboarding/feature-wall/tile-07.poster.jpg" alt="파일과 이미지를 에이전트 프롬프트로 드래그" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Orca CLI

에이전트도 Orca를 조작할 수 있습니다 — `orca worktree create`, `snapshot`, `click`, `fill`로 모든 워크플로를 스크립팅하세요.

[문서 →](https://www.onorca.dev/docs/cli/overview)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/cli/overview"><picture><source srcset="../../resources/onboarding/feature-wall/tile-09.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-09.poster.jpg" alt="CLI에서 Orca 스크립팅" width="100%" /></picture></a>
</td>
</tr>
</table>

**그 밖에 기본으로 제공되는 기능:**

- **[빠른 열기](https://www.onorca.dev/docs/model/quick-open)** — 작업 흐름을 벗어나지 않고 worktree, 파일, 에이전트, 커맨드, 리포지토리 컨텍스트를 검색하세요.
- **[계정 전환 및 사용량 추적](https://www.onorca.dev/docs/agents/usage-tracking)** — Claude와 Codex의 사용량과 rate limit 초기화 시점을 확인하고, 다시 로그인하지 않고 계정을 바로 전환하세요.
- **[풍부한 리포지토리 미리보기](https://www.onorca.dev/docs/editing/markdown)** — Markdown, 이미지, PDF, 리포지토리 문서를 워크스페이스에서 미리 볼 수 있습니다.
- **[Computer Use](https://www.onorca.dev/docs/cli/computer-use)** — 워크플로에 실제 상호작용이 필요할 때 에이전트가 데스크톱 앱과 화면에 보이는 UI를 직접 조작하게 하세요.
- **[알림과 읽지 않음 상태](https://www.onorca.dev/docs/notifications)** — 에이전트가 완료되거나 주의가 필요할 때 알림을 받고, 스레드를 읽지 않음으로 표시해 나중에 다시 확인하세요.
- **그리고 훨씬 더 많은 기능** — 새로운 기능이 매일 출시되므로 이 목록은 늘 한 발 늦습니다. 진짜 기능은 [체인지로그](https://github.com/stablyai/orca/releases)에서 확인하세요.

---

## 지원 에이전트

**모든 CLI 에이전트**와 함께 작동합니다 — 터미널에서 실행되는 에이전트라면 Orca에서도 실행됩니다.

<p>
  <a href="https://docs.anthropic.com/claude/docs/claude-code"><kbd><img src="../assets/claude-logo.svg" alt="Claude Code logo" width="16" valign="middle" /> Claude Code</kbd></a> &nbsp;
  <a href="https://github.com/openai/codex"><kbd><img src="https://www.google.com/s2/favicons?domain=openai.com&sz=64" alt="Codex logo" width="16" valign="middle" /> Codex</kbd></a> &nbsp;
  <a href="https://x.ai/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=x.ai&sz=64" alt="Grok logo" width="16" valign="middle" /> Grok</kbd></a> &nbsp;
  <a href="https://cursor.com/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=cursor.com&sz=64" alt="Cursor logo" width="16" valign="middle" /> Cursor</kbd></a> &nbsp;
  <a href="https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli"><kbd><img src="https://www.google.com/s2/favicons?domain=github.com&sz=64" alt="GitHub Copilot logo" width="16" valign="middle" /> GitHub Copilot</kbd></a> &nbsp;
  <a href="https://dev.meta.ai/docs/muse-code"><kbd><img src="../../src/shared/agent-icons/muse.png" alt="Muse logo" width="16" valign="middle" /> Muse</kbd></a> &nbsp;
  <a href="https://deepseek-harness.github.io/deepseek-harness/"><kbd><img src="../../src/shared/agent-icons/dsh.png" alt="DeepSeek Harness logo" width="16" valign="middle" /> DeepSeek Harness</kbd></a> &nbsp;
  <a href="https://zcode.z.ai/en/docs"><kbd><img src="../../src/shared/agent-icons/zcode.png" alt="ZCode logo" width="16" valign="middle" /> ZCode</kbd></a> &nbsp;
  <a href="https://opencode.ai/docs/cli/"><kbd><img src="https://www.google.com/s2/favicons?domain=opencode.ai&sz=64" alt="OpenCode logo" width="16" valign="middle" /> OpenCode</kbd></a> &nbsp;
  <a href="https://mimo.xiaomi.com/coder"><kbd><img src="https://www.google.com/s2/favicons?domain=mimo.xiaomi.com&sz=64" alt="MiMo Code logo" width="16" valign="middle" /> MiMo Code</kbd></a> &nbsp;
  <a href="https://ampcode.com/manual#install"><kbd><img src="https://www.google.com/s2/favicons?domain=ampcode.com&sz=64" alt="Amp logo" width="16" valign="middle" /> Amp</kbd></a> &nbsp;
  <a href="https://openclaude.gitlawb.com/"><kbd><img src="../../resources/openclaude-logo.png" alt="OpenClaude logo" width="16" valign="middle" /> OpenClaude</kbd></a> &nbsp;
  <a href="https://antigravity.google/docs/cli-overview"><kbd><img src="https://www.google.com/s2/favicons?domain=antigravity.google&sz=64" alt="Antigravity logo" width="16" valign="middle" /> Antigravity</kbd></a> &nbsp;
  <a href="https://pi.dev"><kbd><img src="https://pi.dev/favicon.svg" alt="Pi logo" width="16" valign="middle" /> Pi</kbd></a> &nbsp;
  <a href="https://omp.sh"><kbd><img src="https://omp.sh/favicon.svg" alt="oh-my-pi logo" width="16" valign="middle" /> oh-my-pi</kbd></a> &nbsp;
  <a href="https://hermes-agent.nousresearch.com/docs/"><kbd><img src="https://www.google.com/s2/favicons?domain=nousresearch.com&sz=64" alt="Hermes Agent logo" width="16" valign="middle" /> Hermes Agent</kbd></a> &nbsp;
  <a href="https://devin.ai/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=devin.ai&sz=64" alt="Devin logo" width="16" valign="middle" /> Devin</kbd></a> &nbsp;
  <a href="https://block.github.io/goose/docs/quickstart/"><kbd><img src="https://www.google.com/s2/favicons?domain=goose-docs.ai&sz=64" alt="Goose logo" width="16" valign="middle" /> Goose</kbd></a> &nbsp;
  <a href="https://docs.augmentcode.com/cli/overview"><kbd><img src="https://www.google.com/s2/favicons?domain=augmentcode.com&sz=64" alt="Auggie logo" width="16" valign="middle" /> Auggie</kbd></a> &nbsp;
  <a href="https://github.com/autohandai/code-cli"><kbd><img src="https://www.google.com/s2/favicons?domain=autohand.ai&sz=64" alt="Autohand Code logo" width="16" valign="middle" /> Autohand Code</kbd></a> &nbsp;
  <a href="https://github.com/charmbracelet/crush"><kbd><img src="https://www.google.com/s2/favicons?domain=charm.sh&sz=64" alt="Charm logo" width="16" valign="middle" /> Charm</kbd></a> &nbsp;
  <a href="https://docs.cline.bot/cline-cli/overview"><kbd><img src="https://www.google.com/s2/favicons?domain=cline.bot&sz=64" alt="Cline logo" width="16" valign="middle" /> Cline</kbd></a> &nbsp;
  <a href="https://www.codebuff.com/docs/help/quick-start"><kbd><img src="https://www.google.com/s2/favicons?domain=codebuff.com&sz=64" alt="Codebuff logo" width="16" valign="middle" /> Codebuff</kbd></a> &nbsp;
  <a href="https://freebuff.com/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=freebuff.com&sz=64" alt="Freebuff logo" width="16" valign="middle" /> Freebuff</kbd></a> &nbsp;
  <a href="https://commandcode.ai/docs/quickstart"><kbd><img src="https://www.google.com/s2/favicons?domain=commandcode.ai&sz=64" alt="Command Code logo" width="16" valign="middle" /> Command Code</kbd></a> &nbsp;
  <a href="https://docs.continue.dev/guides/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=continue.dev&sz=64" alt="Continue logo" width="16" valign="middle" /> Continue</kbd></a> &nbsp;
  <a href="https://docs.factory.ai/cli/getting-started/quickstart"><kbd><img src="../assets/droid-logo.svg" alt="Droid logo" width="16" valign="middle" /> Droid</kbd></a> &nbsp;
  <a href="https://kilo.ai/docs/cli"><kbd><img src="https://raw.githubusercontent.com/Kilo-Org/kilocode/main/packages/kilo-vscode/assets/icons/kilo-light.svg" alt="Kilocode logo" width="16" valign="middle" /> Kilocode</kbd></a> &nbsp;
  <a href="https://www.kimi.com/code/docs/en/kimi-code-cli/getting-started.html"><kbd><img src="https://www.google.com/s2/favicons?domain=moonshot.cn&sz=64" alt="Kimi logo" width="16" valign="middle" /> Kimi</kbd></a> &nbsp;
  <a href="https://kiro.dev/docs/cli/"><kbd><img src="https://www.google.com/s2/favicons?domain=kiro.dev&sz=64" alt="Kiro logo" width="16" valign="middle" /> Kiro</kbd></a> &nbsp;
  <a href="https://github.com/mistralai/mistral-vibe"><kbd><img src="https://www.google.com/s2/favicons?domain=mistral.ai&sz=64" alt="Mistral Vibe logo" width="16" valign="middle" /> Mistral Vibe</kbd></a> &nbsp;
  <a href="https://github.com/QwenLM/qwen-code"><kbd><img src="https://www.google.com/s2/favicons?domain=qwenlm.github.io&sz=64" alt="Qwen Code logo" width="16" valign="middle" /> Qwen Code</kbd></a> &nbsp;
  <a href="https://support.atlassian.com/rovo/docs/install-and-run-rovo-dev-cli-on-your-device/"><kbd><img src="https://www.google.com/s2/favicons?domain=atlassian.com&sz=64" alt="Rovo Dev logo" width="16" valign="middle" /> Rovo Dev</kbd></a> &nbsp;
  <kbd>+ any CLI agent</kbd>
</p>

---

## 설치

### 데스크톱 — macOS, Windows, Linux

- **[onOrca.dev에서 다운로드](https://onorca.dev/download)**
- 또는 빌드를 직접 받기: [macOS Apple Silicon](https://github.com/stablyai/orca/releases/latest/download/orca-macos-arm64.dmg) · [macOS Intel](https://github.com/stablyai/orca/releases/latest/download/orca-macos-x64.dmg) · [Windows (.exe)](https://github.com/stablyai/orca/releases/latest/download/orca-windows-setup.exe) · [Linux AppImage](https://github.com/stablyai/orca/releases/latest/download/orca-linux.AppImage) · [전체 빌드](https://github.com/stablyai/orca/releases/latest)

_또는 패키지 매니저로 설치:_

```bash
# macOS (Homebrew)
brew install --cask stablyai/orca/orca

# Arch Linux (AUR) — or stably-orca-git to build from source
yay -S stably-orca-bin
```

### 모바일 Companion — iOS, Android

데스크톱 앱과 페어링해 휴대폰에서 에이전트를 모니터링하고 조종하세요.

- **iOS:** [App Store에서 다운로드](https://apps.apple.com/us/app/orca-ide/id6766130217)
- **Android:** [APK 다운로드](https://www.onorca.dev/download/android) · [설치 가이드](https://www.onorca.dev/docs/android-apk)

---

## 커뮤니티와 지원

- **Discord:** **[Discord](https://discord.gg/fzjDKHxv8Q)** 커뮤니티에 참여하세요.
- **Twitter / X:** 업데이트와 공지는 **[@orca_build](https://x.com/orca_build)** 를 팔로우하세요.
- **WeChat:** 그룹 11은 정원이 찼습니다. QR 코드를 스캔해 Orca 커뮤니티 WeChat 그룹 12에 참여하세요.

  <img src="../assets/wechat-qr-group12.jpg" alt="Orca 커뮤니티 WeChat 그룹 12 QR 코드" width="160" />

- **피드백과 아이디어:** 우리는 빠르게 출시합니다. 필요한 기능이 있나요? [새 기능을 요청](https://github.com/stablyai/orca/issues)하세요.
- **개인정보 보호:** Orca가 수집하는 익명 사용 데이터와 수집 거부 방법은 [개인정보 및 텔레메트리 문서](https://www.onorca.dev/docs/telemetry)를 참고하세요.
- **응원하기:** 이 리포지토리에 [Star](https://github.com/stablyai/orca)를 눌러 매일 공개되는 릴리스 소식을 확인해 주세요.

---

## 개발

기여하거나 로컬에서 실행하고 싶으신가요? [CONTRIBUTING.md](../../.github/CONTRIBUTING.md) 가이드를 확인하세요.

<a href="https://github.com/stablyai/orca/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=stablyai/orca" alt="Orca 기여자" />
</a>

<p align="center">
  <img src="../assets/star-history.png" alt="stablyai/orca GitHub Star 히스토리 차트" width="880" />
</p>

## 서명된 빌드

Windows 코드 서명은 [SignPath.io](https://signpath.io)가 후원·제공하며, 인증서는 [SignPath Foundation](https://signpath.org)에서 제공합니다.

## 라이선스

Orca는 [MIT 라이선스](../../LICENSE)에 따라 자유롭게 사용할 수 있는 오픈 소스입니다.

---

### Readme README.Pt (`docs/readme/README.pt.md`)

<h1 align="center">
  <a href="https://onOrca.dev"><img src="../../resources/build/icon.png" alt="Orca" width="64" valign="middle" /></a> Orca
</h1>

<p align="center">
  <a href="https://github.com/stablyai/orca"><img src="https://img.shields.io/github/stars/stablyai/orca?style=flat&amp;label=%E2%98%85&amp;color=08C" alt="Estrelas no GitHub" /></a>
  <a href="https://github.com/stablyai/orca/releases"><img src="../assets/readme-downloads.svg" alt="Total de downloads em todas as versões" /></a>
  <img src="https://img.shields.io/badge/license-MIT-08C?style=flat" alt="Licença: MIT" />
  <a href="https://discord.gg/fzjDKHxv8Q"><img src="https://img.shields.io/badge/Discord-5865F2?logo=discord&logoColor=white" alt="Entre no Discord do Orca" /></a>
  <a href="https://x.com/orca_build"><img src="https://img.shields.io/badge/X-000000?logo=x&logoColor=white" alt="Siga o Orca no X" /></a>
  <img src="https://img.shields.io/badge/macOS%20%7C%20Windows%20%7C%20Linux-4493F8?style=flat-square" alt="Plataformas compatíveis: macOS, Windows e Linux" />
</p>

<p align="center">
  <sub><a href="../../README.md">English</a> · <a href="README.zh-CN.md">中文</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.es.md">Español</a> · <a href="README.fr.md">Français</a></sub>
</p>

<p align="center">
  <strong>O orquestrador de IA para builders 100x.</strong><br/>
  Rode Codex, ClaudeCode, OpenCode ou Pi lado a lado — cada um em seu próprio worktree, acompanhado em um só lugar.
</p>

<h3 align="center"><a href="https://onorca.dev/download"><ins>Baixar o Orca</ins></a></h3>

<p align="center">
  <img src="../assets/readme-hero.jpg" alt="App desktop do Orca executando agentes em worktrees paralelos, com o app companion móvel do Orca no canto" width="960" />
</p>

## Recursos

<table>
<tr>
<td width="50%" valign="middle">

### Companion móvel

Monitore e conduza seus agentes pelo celular — receba uma notificação quando um agente terminar e envie instruções de acompanhamento de qualquer lugar.

[App Store para iOS](https://apps.apple.com/us/app/orca-ide/id6766130217) · [APK Android](https://www.onorca.dev/download/android) · [Docs →](https://www.onorca.dev/docs/mobile)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/mobile"><picture><source srcset="../assets/feature-wall/mobile-companion-app-showcase.gif" type="image/gif"><img src="../assets/feature-wall/mobile-companion-app-showcase.jpg" alt="Orca desktop com o app companion móvel" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Worktrees paralelos

Envie um mesmo prompt para cinco agentes, cada um em seu próprio worktree git isolado — compare os resultados e faça merge do vencedor.

[Docs →](https://www.onorca.dev/docs/model/worktrees)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/model/worktrees"><picture><source srcset="../site/public/docs/tab-split.gif" type="image/gif"><img src="../site/public/docs/posters/tab-split.jpg" alt="Orquestração de worktrees paralelos" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Terminais divididos

Terminais no nível do Ghostty com renderização WebGL, divisões infinitas e scrollback que sobrevive a reinicializações.

[Docs →](https://www.onorca.dev/docs/terminal)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/terminal"><picture><source srcset="../../resources/onboarding/feature-wall/tile-02.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-02.poster.jpg" alt="Terminais divididos" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Modo Design

Clique em qualquer elemento de UI em uma janela real do Chromium para enviar HTML, CSS e uma captura recortada direto para o prompt do seu agente.

[Docs →](https://www.onorca.dev/docs/browser/design-mode)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/browser/design-mode"><picture><source srcset="../site/public/docs/orca-design-mode.gif" type="image/gif"><img src="../../resources/onboarding/feature-wall/tile-05.poster.jpg" alt="Navegador integrado e Modo Design" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### GitHub e Linear nativos

Navegue por PRs, issues e quadros de projeto dentro do app — abra um worktree a partir de qualquer tarefa e revise sem trocar de contexto.

[Docs →](https://www.onorca.dev/docs/review/linear)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/review/linear"><picture><source srcset="../../resources/onboarding/feature-wall/tile-03.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-03.poster.jpg" alt="Fluxos de trabalho de tarefas do GitHub e Linear no Orca" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Worktrees por SSH

Execute agentes em uma máquina remota potente com edição completa de arquivos, git e terminais — com reconexão automática e encaminhamento de portas incluídos.

[Docs →](https://www.onorca.dev/docs/ssh)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/ssh"><picture><source srcset="../../resources/onboarding/feature-wall/tile-06.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-06.poster.jpg" alt="Worktrees remotos por SSH" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Anotar diffs de IA

Deixe comentários em qualquer linha de diff e envie-os de volta ao agente — revise, edite e faça commit sem sair do Orca.

[Docs →](https://www.onorca.dev/docs/review/annotate-ai-diff)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/review/annotate-ai-diff"><picture><source srcset="../site/public/docs/annotate-ai-diff.gif" type="image/gif"><img src="../../resources/onboarding/feature-wall/tile-08.poster.jpg" alt="Anotar diffs gerados por IA" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Arraste arquivos para agentes

O editor do VS Code com salvamento automático em todos os lugares — arraste arquivos ou imagens direto para o prompt de um agente.

[Docs →](https://www.onorca.dev/docs/editing/file-explorer)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/editing/file-explorer"><picture><source srcset="../../resources/onboarding/feature-wall/tile-07.gif" type="image/gif"><img src="../../resources/onboarding/feature-wall/tile-07.poster.jpg" alt="Arraste arquivos e imagens para o prompt de um agente" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Orca CLI

Agentes também controlam o Orca — automatize qualquer fluxo de trabalho com `orca worktree create`, `snapshot`, `click` e `fill`.

[Docs →](https://www.onorca.dev/docs/cli/overview)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/cli/overview"><picture><source srcset="../../resources/onboarding/feature-wall/tile-09.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-09.poster.jpg" alt="Automatize o Orca pela CLI" width="100%" /></picture></a>
</td>
</tr>
</table>

**Também incluído:**

- **[Abertura rápida](https://www.onorca.dev/docs/model/quick-open)** — Pesquise entre worktrees, arquivos, agentes, comandos e contexto do repositório sem sair do seu fluxo.
- **[Troca de contas e acompanhamento de uso](https://www.onorca.dev/docs/agents/usage-tracking)** — Veja o uso de Claude e Codex, os reinícios de limites e troque contas instantaneamente sem fazer login de novo.
- **[Prévias ricas do repositório](https://www.onorca.dev/docs/editing/markdown)** — Pré-visualize Markdown, imagens, PDFs e documentos do repositório no workspace.
- **[Computer Use](https://www.onorca.dev/docs/cli/computer-use)** — Deixe agentes operarem apps de desktop e UI visível quando um fluxo de trabalho precisa de interação real.
- **[Notificações e estado de não lido](https://www.onorca.dev/docs/notifications)** — Saiba quando um agente termina ou precisa de atenção, depois marque conversas como não lidas para voltar depois.
- **E muito, muito mais** — lançamos novidades todos os dias, então esta lista vive atrasada. O [changelog](https://github.com/stablyai/orca/releases) é a lista real de recursos.

---

## Agentes compatíveis

Funciona com **qualquer agente CLI** — se roda em um terminal, roda no Orca.

<p>
  <a href="https://docs.anthropic.com/claude/docs/claude-code"><kbd><img src="../assets/claude-logo.svg" alt="Logotipo do Claude Code" width="16" valign="middle" /> Claude Code</kbd></a> &nbsp;
  <a href="https://github.com/openai/codex"><kbd><img src="https://www.google.com/s2/favicons?domain=openai.com&sz=64" alt="Logotipo do Codex" width="16" valign="middle" /> Codex</kbd></a> &nbsp;
  <a href="https://x.ai/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=x.ai&sz=64" alt="Logotipo do Grok" width="16" valign="middle" /> Grok</kbd></a> &nbsp;
  <a href="https://cursor.com/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=cursor.com&sz=64" alt="Logotipo do Cursor" width="16" valign="middle" /> Cursor</kbd></a> &nbsp;
  <a href="https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli"><kbd><img src="https://www.google.com/s2/favicons?domain=github.com&sz=64" alt="Logotipo do GitHub Copilot" width="16" valign="middle" /> GitHub Copilot</kbd></a> &nbsp;
  <a href="https://dev.meta.ai/docs/muse-code"><kbd><img src="../../src/shared/agent-icons/muse.png" alt="Logotipo do Muse" width="16" valign="middle" /> Muse</kbd></a> &nbsp;
  <a href="https://deepseek-harness.github.io/deepseek-harness/"><kbd><img src="../../src/shared/agent-icons/dsh.png" alt="DeepSeek Harness logo" width="16" valign="middle" /> DeepSeek Harness</kbd></a> &nbsp;
  <a href="https://zcode.z.ai/en/docs"><kbd><img src="../../src/shared/agent-icons/zcode.png" alt="Logotipo do ZCode" width="16" valign="middle" /> ZCode</kbd></a> &nbsp;
  <a href="https://opencode.ai/docs/cli/"><kbd><img src="https://www.google.com/s2/favicons?domain=opencode.ai&sz=64" alt="Logotipo do OpenCode" width="16" valign="middle" /> OpenCode</kbd></a> &nbsp;
  <a href="https://mimo.xiaomi.com/coder"><kbd><img src="https://www.google.com/s2/favicons?domain=mimo.xiaomi.com&sz=64" alt="Logotipo do MiMo Code" width="16" valign="middle" /> MiMo Code</kbd></a> &nbsp;
  <a href="https://ampcode.com/manual#install"><kbd><img src="https://www.google.com/s2/favicons?domain=ampcode.com&sz=64" alt="Logotipo do Amp" width="16" valign="middle" /> Amp</kbd></a> &nbsp;
  <a href="https://openclaude.gitlawb.com/"><kbd><img src="../../resources/openclaude-logo.png" alt="Logotipo do OpenClaude" width="16" valign="middle" /> OpenClaude</kbd></a> &nbsp;
  <a href="https://antigravity.google/docs/cli-overview"><kbd><img src="https://www.google.com/s2/favicons?domain=antigravity.google&sz=64" alt="Logotipo do Antigravity" width="16" valign="middle" /> Antigravity</kbd></a> &nbsp;
  <a href="https://pi.dev"><kbd><img src="https://pi.dev/favicon.svg" alt="Logotipo do Pi" width="16" valign="middle" /> Pi</kbd></a> &nbsp;
  <a href="https://omp.sh"><kbd><img src="https://omp.sh/favicon.svg" alt="Logotipo do oh-my-pi" width="16" valign="middle" /> oh-my-pi</kbd></a> &nbsp;
  <a href="https://hermes-agent.nousresearch.com/docs/"><kbd><img src="https://www.google.com/s2/favicons?domain=nousresearch.com&sz=64" alt="Logotipo do Hermes Agent" width="16" valign="middle" /> Hermes Agent</kbd></a> &nbsp;
  <a href="https://devin.ai/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=devin.ai&sz=64" alt="Logotipo do Devin" width="16" valign="middle" /> Devin</kbd></a> &nbsp;
  <a href="https://block.github.io/goose/docs/quickstart/"><kbd><img src="https://www.google.com/s2/favicons?domain=goose-docs.ai&sz=64" alt="Logotipo do Goose" width="16" valign="middle" /> Goose</kbd></a> &nbsp;
  <a href="https://docs.augmentcode.com/cli/overview"><kbd><img src="https://www.google.com/s2/favicons?domain=augmentcode.com&sz=64" alt="Logotipo do Auggie" width="16" valign="middle" /> Auggie</kbd></a> &nbsp;
  <a href="https://github.com/autohandai/code-cli"><kbd><img src="https://www.google.com/s2/favicons?domain=autohand.ai&sz=64" alt="Logotipo do Autohand Code" width="16" valign="middle" /> Autohand Code</kbd></a> &nbsp;
  <a href="https://github.com/charmbracelet/crush"><kbd><img src="https://www.google.com/s2/favicons?domain=charm.sh&sz=64" alt="Logotipo do Charm" width="16" valign="middle" /> Charm</kbd></a> &nbsp;
  <a href="https://docs.cline.bot/cline-cli/overview"><kbd><img src="https://www.google.com/s2/favicons?domain=cline.bot&sz=64" alt="Logotipo do Cline" width="16" valign="middle" /> Cline</kbd></a> &nbsp;
  <a href="https://www.codebuff.com/docs/help/quick-start"><kbd><img src="https://www.google.com/s2/favicons?domain=codebuff.com&sz=64" alt="Logotipo do Codebuff" width="16" valign="middle" /> Codebuff</kbd></a> &nbsp;
  <a href="https://freebuff.com/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=freebuff.com&sz=64" alt="Logotipo do Freebuff" width="16" valign="middle" /> Freebuff</kbd></a> &nbsp;
  <a href="https://commandcode.ai/docs/quickstart"><kbd><img src="https://www.google.com/s2/favicons?domain=commandcode.ai&sz=64" alt="Logotipo do Command Code" width="16" valign="middle" /> Command Code</kbd></a> &nbsp;
  <a href="https://docs.continue.dev/guides/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=continue.dev&sz=64" alt="Logotipo do Continue" width="16" valign="middle" /> Continue</kbd></a> &nbsp;
  <a href="https://docs.factory.ai/cli/getting-started/quickstart"><kbd><img src="../assets/droid-logo.svg" alt="Logotipo do Droid" width="16" valign="middle" /> Droid</kbd></a> &nbsp;
  <a href="https://kilo.ai/docs/cli"><kbd><img src="https://raw.githubusercontent.com/Kilo-Org/kilocode/main/packages/kilo-vscode/assets/icons/kilo-light.svg" alt="Logotipo do Kilocode" width="16" valign="middle" /> Kilocode</kbd></a> &nbsp;
  <a href="https://www.kimi.com/code/docs/en/kimi-code-cli/getting-started.html"><kbd><img src="https://www.google.com/s2/favicons?domain=moonshot.cn&sz=64" alt="Logotipo do Kimi" width="16" valign="middle" /> Kimi</kbd></a> &nbsp;
  <a href="https://kiro.dev/docs/cli/"><kbd><img src="https://www.google.com/s2/favicons?domain=kiro.dev&sz=64" alt="Logotipo do Kiro" width="16" valign="middle" /> Kiro</kbd></a> &nbsp;
  <a href="https://github.com/mistralai/mistral-vibe"><kbd><img src="https://www.google.com/s2/favicons?domain=mistral.ai&sz=64" alt="Logotipo do Mistral Vibe" width="16" valign="middle" /> Mistral Vibe</kbd></a> &nbsp;
  <a href="https://github.com/QwenLM/qwen-code"><kbd><img src="https://www.google.com/s2/favicons?domain=qwenlm.github.io&sz=64" alt="Logotipo do Qwen Code" width="16" valign="middle" /> Qwen Code</kbd></a> &nbsp;
  <a href="https://support.atlassian.com/rovo/docs/install-and-run-rovo-dev-cli-on-your-device/"><kbd><img src="https://www.google.com/s2/favicons?domain=atlassian.com&sz=64" alt="Logotipo do Rovo Dev" width="16" valign="middle" /> Rovo Dev</kbd></a> &nbsp;
  <kbd>+ qualquer agente CLI</kbd>
</p>

---

## Instalação

### Desktop — macOS, Windows, Linux

- **[Baixe em onOrca.dev](https://onorca.dev/download)**
- Ou baixe um build diretamente: [macOS Apple Silicon](https://github.com/stablyai/orca/releases/latest/download/orca-macos-arm64.dmg) · [macOS Intel](https://github.com/stablyai/orca/releases/latest/download/orca-macos-x64.dmg) · [Windows (.exe)](https://github.com/stablyai/orca/releases/latest/download/orca-windows-setup.exe) · [Linux AppImage](https://github.com/stablyai/orca/releases/latest/download/orca-linux.AppImage) · [Todos os builds](https://github.com/stablyai/orca/releases/latest)

_Ou por um gerenciador de pacotes:_

```bash
# macOS (Homebrew)
brew install --cask stablyai/orca/orca

# Arch Linux (AUR) — ou stably-orca-git para compilar a partir do código-fonte
yay -S stably-orca-bin
```

### Companion móvel — iOS, Android

Conecte ao app desktop para monitorar e conduzir seus agentes pelo celular.

- **iOS:** [Baixar na App Store](https://apps.apple.com/us/app/orca-ide/id6766130217)
- **Android:** [Baixar APK](https://www.onorca.dev/download/android)

---

## Comunidade e suporte

- **Discord:** Entre na comunidade no **[Discord](https://discord.gg/fzjDKHxv8Q)**.
- **Twitter / X:** Siga **[@orca_build](https://x.com/orca_build)** para atualizações e anúncios.
- **Feedback e ideias:** Lançamos rápido. Sentiu falta de algo? [Peça um novo recurso](https://github.com/stablyai/orca/issues).
- **Privacidade:** Veja a [documentação de privacidade e telemetria](https://www.onorca.dev/docs/telemetry) para saber quais dados anônimos de uso o Orca coleta e como desativar.
- **Mostre apoio:** Dê uma [estrela](https://github.com/stablyai/orca) neste repositório para acompanhar nossos lançamentos diários.

---

## Desenvolvimento

Quer contribuir ou rodar localmente? Veja nosso guia [CONTRIBUTING.md](../../.github/CONTRIBUTING.md).

<a href="https://github.com/stablyai/orca/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=stablyai/orca" alt="Contribuidores do Orca" />
</a>

## Licença

Orca é livre e de código aberto sob a [Licença MIT](../../LICENSE).

---

### Readme README.Zh CN (`docs/readme/README.zh-CN.md`)

<h1 align="center">
  <a href="https://onOrca.dev"><img src="../../resources/build/icon.png" alt="Orca" width="64" valign="middle" /></a> Orca
</h1>

<p align="center">
  <a href="https://github.com/stablyai/orca"><img src="https://img.shields.io/github/stars/stablyai/orca?style=flat&amp;label=%E2%98%85&amp;color=08C" alt="GitHub Star 数" /></a>
  <a href="https://github.com/stablyai/orca/releases"><img src="../assets/readme-downloads.svg" alt="所有版本的总下载量" /></a>
  <img src="https://img.shields.io/badge/license-MIT-08C?style=flat" alt="许可证: MIT" />
  <a href="https://discord.gg/fzjDKHxv8Q"><img src="https://img.shields.io/badge/Discord-5865F2?logo=discord&logoColor=white" alt="加入 Orca Discord" /></a>
  <a href="https://x.com/orca_build"><img src="https://img.shields.io/badge/X-000000?logo=x&logoColor=white" alt="在 X 上关注 Orca" /></a>
  <img src="https://img.shields.io/badge/macOS%20%7C%20Windows%20%7C%20Linux-4493F8?style=flat-square" alt="支持的平台：macOS、Windows 和 Linux" />
</p>

<p align="center">
  <sub><a href="../../README.md">English</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.es.md">Español</a> · <a href="README.fr.md">Français</a> · <a href="README.pt.md">Português</a></sub>
</p>

<p align="center">
  <strong>面向 100x 构建者的 AI 编排器。</strong><br/>
  并排运行 Codex、Claude Code、OpenCode 或 Pi — 每个都在自己的 worktree 中运行，并在一个地方统一跟踪。
</p>

<h3 align="center"><a href="https://onorca.dev/download"><ins>下载 Orca</ins></a></h3>

<p align="center">
  <img src="../assets/readme-hero.jpg" alt="Orca 桌面应用在并行 worktree 中运行智能体，角落里是 Orca 移动 companion 应用" width="960" />
</p>

## 特性

<table>
<tr>
<td width="50%" valign="middle">

### 移动 Companion 应用

用手机监控并指挥你的智能体 — 智能体完成时收到通知，随时随地发送后续指令。

[iOS App Store](https://apps.apple.com/us/app/orca-ide/id6766130217) · [Android APK](https://www.onorca.dev/download/android) · [文档 →](https://www.onorca.dev/docs/mobile)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/mobile"><picture><source srcset="../assets/feature-wall/mobile-companion-app-showcase.gif" type="image/gif"><img src="../assets/feature-wall/mobile-companion-app-showcase.jpg" alt="Orca 桌面端与移动 companion 应用" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### 并行 Worktree

把一个提示同时分发给五个智能体，每个都在自己隔离的 git worktree 中运行 — 比较结果，合并最佳方案。

[文档 →](https://www.onorca.dev/docs/model/worktrees)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/model/worktrees"><picture><source srcset="../site/public/docs/tab-split.gif" type="image/gif"><img src="../site/public/docs/posters/tab-split.jpg" alt="并行 worktree 编排" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### 终端分屏

Ghostty 级终端，支持 WebGL 渲染、无限分屏，以及重启后依然保留的滚动历史。

[文档 →](https://www.onorca.dev/docs/terminal)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/terminal"><picture><source srcset="../../resources/onboarding/feature-wall/tile-02.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-02.poster.jpg" alt="终端分屏" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### 设计模式

在真实的 Chromium 窗口中点击任意 UI 元素，把它的 HTML、CSS 和裁剪好的截图直接发送到智能体的提示中。

[文档 →](https://www.onorca.dev/docs/browser/design-mode)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/browser/design-mode"><picture><source srcset="../site/public/docs/orca-design-mode.gif" type="image/gif"><img src="../../resources/onboarding/feature-wall/tile-05.poster.jpg" alt="内置浏览器与设计模式" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### GitHub &amp; Linear 原生集成

在应用内浏览 PR、issue 和项目看板 — 从任意任务打开 worktree，无需切换上下文即可完成评审。

[文档 →](https://www.onorca.dev/docs/review/linear)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/review/linear"><picture><source srcset="../../resources/onboarding/feature-wall/tile-03.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-03.poster.jpg" alt="Orca 中的 GitHub 与 Linear 任务工作流" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### SSH Worktree

在高性能远程机器上运行智能体，完整支持文件编辑、git 和终端 — 自动重连与端口转发一应俱全。

[文档 →](https://www.onorca.dev/docs/ssh)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/ssh"><picture><source srcset="../../resources/onboarding/feature-wall/tile-06.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-06.poster.jpg" alt="通过 SSH 使用远程 worktree" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### 标注 AI Diff

在任意 diff 行上添加评论并发回给智能体 — 评审、编辑、提交，全程无需离开 Orca。

[文档 →](https://www.onorca.dev/docs/review/annotate-ai-diff)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/review/annotate-ai-diff"><picture><source srcset="../site/public/docs/annotate-ai-diff.gif" type="image/gif"><img src="../../resources/onboarding/feature-wall/tile-08.poster.jpg" alt="标注 AI 生成的 diff" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### 拖文件给智能体

VS Code 的编辑器，处处自动保存 — 把文件或图片直接拖入智能体提示。

[文档 →](https://www.onorca.dev/docs/editing/file-explorer)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/editing/file-explorer"><picture><source srcset="../../resources/onboarding/feature-wall/tile-07.gif" type="image/gif"><img src="../../resources/onboarding/feature-wall/tile-07.poster.jpg" alt="将文件和图片拖入智能体提示" width="100%" /></picture></a>
</td>
</tr>
<tr>
<td width="50%" valign="middle">

### Orca CLI

智能体也能驱动 Orca — 用 `orca worktree create`、`snapshot`、`click` 和 `fill` 把每个工作流脚本化。

[文档 →](https://www.onorca.dev/docs/cli/overview)

</td>
<td width="50%">
  <a href="https://www.onorca.dev/docs/cli/overview"><picture><source srcset="../../resources/onboarding/feature-wall/tile-09.webp" type="image/webp"><img src="../../resources/onboarding/feature-wall/tile-09.poster.jpg" alt="从 CLI 脚本化 Orca" width="100%" /></picture></a>
</td>
</tr>
</table>

**开箱即用的还有：**

- **[快速打开](https://www.onorca.dev/docs/model/quick-open)** — 在 worktree、文件、智能体、命令和仓库上下文之间搜索，不打断你的心流。
- **[账号切换与用量追踪](https://www.onorca.dev/docs/agents/usage-tracking)** — 查看 Claude 和 Codex 的用量与限额重置时间，并且无需重新登录即可热切换账号。
- **[丰富仓库预览](https://www.onorca.dev/docs/editing/markdown)** — 在工作区中预览 Markdown、图片、PDF 和仓库文档。
- **[Computer Use](https://www.onorca.dev/docs/cli/computer-use)** — 当工作流需要真实交互时，让智能体操作桌面应用和可见 UI。
- **[通知与未读状态](https://www.onorca.dev/docs/notifications)** — 第一时间知道智能体何时完成或需要关注，并可将会话标记为未读，稍后再回来处理。
- **还有很多很多** — 我们每天发布新功能，这个列表永远跟不上。[更新日志](https://github.com/stablyai/orca/releases)才是真正的功能列表。

---

## 支持的智能体

适配**任何 CLI 智能体** — 只要能在终端里运行，就能在 Orca 里运行。

<p>
  <a href="https://docs.anthropic.com/claude/docs/claude-code"><kbd><img src="../assets/claude-logo.svg" alt="Claude Code logo" width="16" valign="middle" /> Claude Code</kbd></a> &nbsp;
  <a href="https://github.com/openai/codex"><kbd><img src="https://www.google.com/s2/favicons?domain=openai.com&sz=64" alt="Codex logo" width="16" valign="middle" /> Codex</kbd></a> &nbsp;
  <a href="https://x.ai/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=x.ai&sz=64" alt="Grok logo" width="16" valign="middle" /> Grok</kbd></a> &nbsp;
  <a href="https://cursor.com/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=cursor.com&sz=64" alt="Cursor logo" width="16" valign="middle" /> Cursor</kbd></a> &nbsp;
  <a href="https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli"><kbd><img src="https://www.google.com/s2/favicons?domain=github.com&sz=64" alt="GitHub Copilot logo" width="16" valign="middle" /> GitHub Copilot</kbd></a> &nbsp;
  <a href="https://dev.meta.ai/docs/muse-code"><kbd><img src="../../src/shared/agent-icons/muse.png" alt="Muse logo" width="16" valign="middle" /> Muse</kbd></a> &nbsp;
  <a href="https://deepseek-harness.github.io/deepseek-harness/"><kbd><img src="../../src/shared/agent-icons/dsh.png" alt="DeepSeek Harness logo" width="16" valign="middle" /> DeepSeek Harness</kbd></a> &nbsp;
  <a href="https://zcode.z.ai/en/docs"><kbd><img src="../../src/shared/agent-icons/zcode.png" alt="ZCode logo" width="16" valign="middle" /> ZCode</kbd></a> &nbsp;
  <a href="https://opencode.ai/docs/cli/"><kbd><img src="https://www.google.com/s2/favicons?domain=opencode.ai&sz=64" alt="OpenCode logo" width="16" valign="middle" /> OpenCode</kbd></a> &nbsp;
  <a href="https://ampcode.com/manual#install"><kbd><img src="https://www.google.com/s2/favicons?domain=ampcode.com&sz=64" alt="Amp logo" width="16" valign="middle" /> Amp</kbd></a> &nbsp;
  <a href="https://openclaude.gitlawb.com/"><kbd><img src="../../resources/openclaude-logo.png" alt="OpenClaude logo" width="16" valign="middle" /> OpenClaude</kbd></a> &nbsp;
  <a href="https://antigravity.google/docs/cli-overview"><kbd><img src="https://www.google.com/s2/favicons?domain=antigravity.google&sz=64" alt="Antigravity logo" width="16" valign="middle" /> Antigravity</kbd></a> &nbsp;
  <a href="https://pi.dev"><kbd><img src="https://pi.dev/favicon.svg" alt="Pi logo" width="16" valign="middle" /> Pi</kbd></a> &nbsp;
  <a href="https://omp.sh"><kbd><img src="https://omp.sh/favicon.svg" alt="oh-my-pi logo" width="16" valign="middle" /> oh-my-pi</kbd></a> &nbsp;
  <a href="https://hermes-agent.nousresearch.com/docs/"><kbd><img src="https://www.google.com/s2/favicons?domain=nousresearch.com&sz=64" alt="Hermes Agent logo" width="16" valign="middle" /> Hermes Agent</kbd></a> &nbsp;
  <a href="https://block.github.io/goose/docs/quickstart/"><kbd><img src="https://www.google.com/s2/favicons?domain=goose-docs.ai&sz=64" alt="Goose logo" width="16" valign="middle" /> Goose</kbd></a> &nbsp;
  <a href="https://docs.augmentcode.com/cli/overview"><kbd><img src="https://www.google.com/s2/favicons?domain=augmentcode.com&sz=64" alt="Auggie logo" width="16" valign="middle" /> Auggie</kbd></a> &nbsp;
  <a href="https://github.com/autohandai/code-cli"><kbd><img src="https://www.google.com/s2/favicons?domain=autohand.ai&sz=64" alt="Autohand Code logo" width="16" valign="middle" /> Autohand Code</kbd></a> &nbsp;
  <a href="https://github.com/charmbracelet/crush"><kbd><img src="https://www.google.com/s2/favicons?domain=charm.sh&sz=64" alt="Charm logo" width="16" valign="middle" /> Charm</kbd></a> &nbsp;
  <a href="https://docs.cline.bot/cline-cli/overview"><kbd><img src="https://www.google.com/s2/favicons?domain=cline.bot&sz=64" alt="Cline logo" width="16" valign="middle" /> Cline</kbd></a> &nbsp;
  <a href="https://www.codebuff.com/docs/help/quick-start"><kbd><img src="https://www.google.com/s2/favicons?domain=codebuff.com&sz=64" alt="Codebuff logo" width="16" valign="middle" /> Codebuff</kbd></a> &nbsp;
  <a href="https://freebuff.com/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=freebuff.com&sz=64" alt="Freebuff logo" width="16" valign="middle" /> Freebuff</kbd></a> &nbsp;
  <a href="https://commandcode.ai/docs/quickstart"><kbd><img src="https://www.google.com/s2/favicons?domain=commandcode.ai&sz=64" alt="Command Code logo" width="16" valign="middle" /> Command Code</kbd></a> &nbsp;
  <a href="https://docs.continue.dev/guides/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=continue.dev&sz=64" alt="Continue logo" width="16" valign="middle" /> Continue</kbd></a> &nbsp;
  <a href="https://docs.factory.ai/cli/getting-started/quickstart"><kbd><img src="../assets/droid-logo.svg" alt="Droid logo" width="16" valign="middle" /> Droid</kbd></a> &nbsp;
  <a href="https://kilo.ai/docs/cli"><kbd><img src="https://raw.githubusercontent.com/Kilo-Org/kilocode/main/packages/kilo-vscode/assets/icons/kilo-light.svg" alt="Kilocode logo" width="16" valign="middle" /> Kilocode</kbd></a> &nbsp;
  <a href="https://www.kimi.com/code/docs/en/kimi-code-cli/getting-started.html"><kbd><img src="https://www.google.com/s2/favicons?domain=moonshot.cn&sz=64" alt="Kimi logo" width="16" valign="middle" /> Kimi</kbd></a> &nbsp;
  <a href="https://kiro.dev/docs/cli/"><kbd><img src="https://www.google.com/s2/favicons?domain=kiro.dev&sz=64" alt="Kiro logo" width="16" valign="middle" /> Kiro</kbd></a> &nbsp;
  <a href="https://github.com/mistralai/mistral-vibe"><kbd><img src="https://www.google.com/s2/favicons?domain=mistral.ai&sz=64" alt="Mistral Vibe logo" width="16" valign="middle" /> Mistral Vibe</kbd></a> &nbsp;
  <a href="https://github.com/QwenLM/qwen-code"><kbd><img src="https://www.google.com/s2/favicons?domain=qwenlm.github.io&sz=64" alt="Qwen Code logo" width="16" valign="middle" /> Qwen Code</kbd></a> &nbsp;
  <a href="https://support.atlassian.com/rovo/docs/install-and-run-rovo-dev-cli-on-your-device/"><kbd><img src="https://www.google.com/s2/favicons?domain=atlassian.com&sz=64" alt="Rovo Dev logo" width="16" valign="middle" /> Rovo Dev</kbd></a> &nbsp;
  <kbd>+ 任何 CLI 智能体</kbd>
</p>

---

## 安装

### 桌面端 — macOS、Windows、Linux

- **[从 onOrca.dev 下载](https://onorca.dev/download)**
- 或直接获取安装包：[macOS Apple Silicon](https://github.com/stablyai/orca/releases/latest/download/orca-macos-arm64.dmg) · [macOS Intel](https://github.com/stablyai/orca/releases/latest/download/orca-macos-x64.dmg) · [Windows (.exe)](https://github.com/stablyai/orca/releases/latest/download/orca-windows-setup.exe) · [Linux AppImage](https://github.com/stablyai/orca/releases/latest/download/orca-linux.AppImage) · [全部构建](https://github.com/stablyai/orca/releases/latest)

_也可以通过包管理器安装：_

```bash
# macOS (Homebrew)
brew install --cask stablyai/orca/orca

# Arch Linux (AUR) — or stably-orca-git to build from source
yay -S stably-orca-bin
```

### 移动 Companion 应用 — iOS、Android

与桌面应用配对，用手机监控并指挥你的智能体。

- **iOS:** [从 App Store 下载](https://apps.apple.com/us/app/orca-ide/id6766130217)
- **Android:** [下载 APK](https://www.onorca.dev/download/android)

---

## 社区与支持

- **Discord:** 加入 **[Discord](https://discord.gg/fzjDKHxv8Q)** 社区。
- **Twitter / X:** 关注 **[@orca_build](https://x.com/orca_build)** 获取更新和公告。
- **微信:** 第 11 群已满。扫码加入 Orca 社区微信第 12 群。

  <img src="../assets/wechat-qr-group12.jpg" alt="Orca 社区微信第 12 群二维码" width="160" />

- **反馈与想法:** 我们发布很快。缺少什么功能？[提交功能请求](https://github.com/stablyai/orca/issues)。
- **隐私:** 查看[隐私与遥测文档](https://www.onorca.dev/docs/telemetry)，了解 Orca 收集哪些匿名使用数据以及如何退出。
- **支持我们:** 给这个仓库点 [Star](https://github.com/stablyai/orca)，关注我们的日常发布。

---

## 开发

想要贡献代码或在本地运行？请参阅我们的 [CONTRIBUTING.md](../../.github/CONTRIBUTING.md) 指南。

<a href="https://github.com/stablyai/orca/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=stablyai/orca" alt="Orca 贡献者" />
</a>

## 许可证

Orca 是自由且开源的软件，遵循 [MIT 许可证](../../LICENSE)。

---

### Readme Release Source Sharing (`docs/readme/release-source-sharing.md`)

# Shared historical source for cross-version tests (P2)

Cross-version tests extract historical releases under each worktree's
`tests/e2e/.cross-version-checkouts`. Previously, branches testing the same release each
retained a full copy. Local tests now prepare one read-only source seed and share its file
data with each worktree's independent import directory.

## How it works

Seeds live under `<git-common-dir>/orca-cache/release-source/format-8/<commit>`.
The resolved commit and extraction format identify the source, including its rewritten imports.
The common Git directory lets sibling worktrees reuse the same seed without scanning them.
Existing local checkouts remain in place; the new format creates fresh import directories.

Missing-package stand-ins depend on the current branch's dependencies, so each worktree
creates those locally. Mutable copies of current-branch modules also remain local.
The existing lock, staging directory, and rename transaction publishes complete seeds and
import directories. Invalid stamps, missing protocol source, or invalid package metadata
cause rebuilding or ordinary local extraction.

macOS uses APFS clones. Linux tries reflinks, then read-only hardlinks. Copies preserve the
seed's read-only permissions; these historical files are never edited by the harness.
Windows uses independent copies because removing read-only hardlinks can change permissions
on surviving links. Unsupported sharing falls back to a private copy. If the optional shared
cache fails, the harness extracts locally and continues the test.

The current format retains at most eight seeds, ordered by recent use. A shared lock covers
publication, copying, and eviction, so cleanup cannot delete a source while another test
copies it. Copying and permission changes are asynchronous so the lock heartbeat keeps running.
Interrupted staging directories are removed on the next use. Retention only touches the
current format; older implementations may still be using other formats. Eviction leaves
already-created import directories usable.

## Controls and scope

Sharing is enabled for ordinary local cross-version tests. Set
`ORCA_DISABLE_SHARED_RELEASE_CACHE=1` to keep ordinary local extraction. CI and explicit
`cacheRoot` options remain isolated unless a caller explicitly supplies `sharedCacheRoot`.

This only changes test tooling. It adds no development-server task or background worktree
scan. Sharing does not migrate or delete existing local caches. Tests running on a remote host use that
host's repository and filesystem; no client filesystem or remote protocol is involved.
Folder contexts without a Git repository cannot provide a historical release checkout.

## Cleaning up older historical caches

First stop cross-version tests in all this repository's worktrees, then preview historical-cache
cleanup across the registered local worktrees and shared source cache:

```sh
pnpm reclaim-space --tests-stopped
```

The default selects stamped release checkouts older than 30 days, including previous extraction
formats. Answer `Y` after the preview to apply it, or run the same selection directly:

```sh
pnpm reclaim-space --apply --tests-stopped
```

Use `--release-cache-min-age-days 7` for a shorter retention window, or `--release-cache-min-age-days 0` to select all
recognised completed release checkouts. `--repo DIRECTORY` selects another local repository.
Each run rescans and rechecks eligibility; it does not apply a saved preview.

This is manual because older harnesses do not hold a lease while importing source. Locks prevent
deletion during publication or shared-source copying, but do not prove that a completed checkout
is unused. Local checkout age uses directory modification time, which ordinary reuse does not
refresh; shared seed timestamps reflect recent copying. `--tests-stopped` acknowledges that the
caller has stopped consumers; it does not detect them.

The script checks matching commit/format stamps, skips symlinked cache paths and future formats,
and leaves mutable `working-tree` copies, source, dependencies, and worktree registrations alone.
It reports logical bytes rather than promised disk savings: another clone may retain the same
blocks. Deleted historical source rebuilds on the next test run. Run the command on the execution
host for SSH repositories. The same manual command also performs P1's Electron maintenance;
automatic dev-bundle sweeps exclude historical test cache cleanup.

## Measurement and validation

A disposable macOS/APFS run against `v1.4.190` compared ordinary extraction with two separate
local caches using one shared seed. Each import tree contained 8,175 files. APFS private file
allocation was measured after all three views existed, without following symlinks.

| Method                           | Elapsed time | Private file data per import directory |
| -------------------------------- | ------------ | -------------------------------------- |
| Ordinary extraction              | 4.64 s       | 68.79 MiB                              |
| Shared source, creating the seed | 22.06 s      | 44 KiB                                 |
| Shared source, reusing the seed  | 1.33 s       | 44 KiB                                 |

Shared blocks still occupy approximately one source copy. Private bytes exclude those blocks
and filesystem metadata, so they are not the total footprint. The cold seed costs extra work;
one timing sample on a busy machine does not establish a speed guarantee. Deleting the seed
left the other import directory readable. No existing worktrees were cleaned up.

Tests cover concurrent publication, lock heartbeats and eviction, independent branch stand-ins,
corrupt seeds, fallback, the opt-out, and preserved read-only files. Run them with:

```sh
ORCA_BACKGROUND_LAUNCH=1 pnpm test tests/e2e/cross-version-wire/release-source-cache.unit.test.ts tests/e2e/cross-version-wire/release-source-retention.unit.test.ts tests/e2e/cross-version-wire/release-checkout.unit.test.ts tests/e2e/cross-version-wire/release-checkout-tree.unit.test.ts tests/e2e/cross-version-wire/release-missing-packages.unit.test.ts config/scripts/space-sharing-copy.test.ts
```

## P3 remains an experiment

P3 shares the tracked source of new Orca worktrees. It requires independent writable files,
Git indexes, and matching checkout settings, and has more compatibility risk than P2's
immutable test source. The APFS experiment demonstrated substantial storage savings but
variable creation time and stays out of this change. Its code, measurements, and design notes
are preserved on `nwparker/tracked-source-sharing-experiment`; [the draft PR is closed](https://github.com/stablyai/orca/pull/27618).

---

### Readme Worktree Storage Maintenance (`docs/readme/worktree-storage-maintenance.md`)

# Worktree storage maintenance

Run one command from the repository to preview maintenance across its locally registered Git worktrees:

```sh
pnpm reclaim-space
```

In an interactive terminal, the preview offers `Apply this cleanup? [y/N]`; enter `Y` to apply it.
Redirected input or output remains preview-only. To apply without a prompt, use `pnpm reclaim-space --apply`.

Historical test caches are retained by default. After stopping cross-version tests in all this
repository's worktrees, preview the same selection with:

```sh
pnpm reclaim-space --tests-stopped
```

Answer `Y` to apply that preview, or use `pnpm reclaim-space --apply --tests-stopped` to apply directly.

The command reuses the historical test cache, Electron distribution, dev bundle, and Vite cache cleanup scripts.
It shares complete Electron distributions through the repository cache and removes idle
`out/electron-dev` bundles, which development rebuilds on demand. It also removes tracked
`.cache/vite` dependency caches unused for 30 days. With `--tests-stopped`, it also
removes stamped historical test checkouts and shared seeds older than 30 days; tests regenerate them when needed. It leaves source files,
dependency installs, worktree registrations, and other build output alone. Reported sizes are
logical file data; APFS clones and hardlinks mean actual disk savings can be smaller.

Use `--release-cache-min-age-days 7` to shorten historical-cache retention, or `0` to select all
recognised completed historical checkouts. This age option applies only to test caches. Local
checkout age uses directory modification time, which ordinary reuse does not refresh. The
`--tests-stopped` flag acknowledges stopped tests; older harnesses cannot prove a completed
checkout is unused. Without that flag, historical caches are excluded from both preview and apply.
Cache cleanup skips symlinks, malformed stamps, future formats, and publication/copy locks.
See [release source sharing](./release-source-sharing.md) for the cache and locking details.

Vite dev servers record cache use at startup and shutdown. Cleanup retains recent caches,
caches owned by a live process, and caches whose ownership cannot be verified, including caches
created before tracking was added. A maintenance lock prevents cleanup racing with server startup.
Desktop and web caches are reclaimed together; the next dev launch regenerates them as needed.

Use `--repo DIRECTORY` to choose another local repository. Git's worktree list determines scope,
including registered worktrees outside Orca's workspace directory. Folder workspaces without Git
have no repository-wide sweep. These commands run on the local host: they do not inspect or delete
anything on SSH hosts. Run maintenance on the execution host when maintaining remote repositories.

## Keeping installs shared

Healthy Electron installs join the shared distribution cache during native dependency setup,
without waiting for an Electron upgrade. The manual command migrates older installs immediately.
Read-only Electron package directories are retained and reported as skipped. Cache publication
or copying failures are also reported as skipped rather than silently omitted.

`pnpm dev` and `pnpm dev-stable-name` start an automatic repository-wide Electron and Vite cleanup worker
in the background after the dev process starts. It never prompts or waits before launching the app.
The worktree being launched is excluded from Electron sharing, dev bundle removal, and Vite cleanup; active
sibling apps and recent unfinished builds remain protected by the existing bundle checks.
Background sweep output is suppressed; worker start or exit failures are reported without stopping dev. Set `ORCA_SKIP_DEV_STORAGE_RECLAIM=1` to opt out.
The worker finishes independently if the dev app exits, and each cleanup operation has a ten-minute limit.

Historical test cache cleanup runs only in the explicit manual command with `--tests-stopped`.
Automatic invocations exclude it because tests may still be importing source.
The repository lock prevents competing cleanup commands. Dev bundle removal checks active
processes and recent unfinished builds, then rechecks eligibility immediately before deletion.
An unreadable process table prevents removal.

To retain older bundles during a manual sweep, run the underlying command with an age limit:

```sh
node config/scripts/reclaim-dev-electron-bundles.mjs --min-age-days 7 --apply
```

That limit uses the bundle directory's modification time. The underlying command also accepts
`--exclude-worktree DIRECTORY` to protect an entire worktree. A missing exclusion path is an error;
filesystem aliases resolve to the same worktree. Neither option schedules cleanup.

---

### Reference Agent Browser Focus Isolation (`docs/reference/agent-browser-focus-isolation.md`)

# Agent browser input and the user's keyboard focus

Read this before changing how agent browser commands deliver mouse, key or text input to a
browser page, or before adding a `webContents.focus()` call on a page an agent drives. Background:
issue [#20595](https://github.com/stablyai/orca/issues/20595), with #16674, #12912, #16410, #19297,
#20533, #26338 and #8139.

## The rule

Agent input reaches the page it targets and nothing else. It never moves the user's keyboard focus,
and it never lands in whatever the user is typing into. Only an explicit user-facing focus request
(`tab switch --focus`) may reveal a page, and even that does not hand it OS focus.

## Why `<webview>` makes this hard (measured, Electron 43)

A `<webview>` guest is an inner WebContents in the window's frame tree. Chromium routes input by
that tree's focus, not by which debugger the command arrived on. A hidden-window probe (host
`<textarea>` focused, guest page logging its own events) measured:

| Input                                                                         | Where it lands             | Host focus                                            |
| ----------------------------------------------------------------------------- | -------------------------- | ----------------------------------------------------- |
| CDP `Input.dispatchKeyEvent` / `Input.insertText` on the guest's main session | **host textarea**          | kept                                                  |
| same, with `Emulation.setFocusEmulationEnabled`                               | **host textarea**          | kept                                                  |
| `webContents.sendInputEvent` key / `char` on the guest                        | guest                      | kept                                                  |
| CDP key / `insertText` on a cross-site iframe's own (auto-attached) session   | that iframe                | kept                                                  |
| `webContents.insertText` with focus in a cross-site iframe                    | **guest renderer crashed** | —                                                     |
| `sendInputEvent` key with focus in a same-site iframe                         | that iframe                | kept                                                  |
| any mouse press (CDP mouse, touch, tap gesture, `sendInputEvent`)             | guest                      | **moved to the webview**                              |
| `inert` / `tabindex=-1` on the `<webview>` during the press                   | guest                      | **moved**                                             |
| press + synchronous host restore on the `<webview>` `focus` event             | guest                      | restored, next user key lands in the host             |
| the restore above with focus emulation enabled first                          | guest                      | **not restored** (the host `focus` event never fires) |

With focus emulation on, the press still moves focus and the restore on the `focus` event never
runs, but restoring when the press ends works and the page sees no blur, so a menu or picker the
agent just opened stays open.

In a visible window (Xvfb with a window manager, keys typed with `xdotool`), the user's real
keystrokes follow the host page's DOM focus: after an agent press and the restore below they reach
the host's focused element. `webContents.getFocusedWebContents()` reports the guest whenever a
`<webview>` exists, even before any input, so it cannot tell where typing goes.

So:

- Keys and text must be sent to the guest directly, not over the main-session CDP `Input` domain,
  and never with `webContents.insertText`. Cross-site iframe sessions already route correctly.
- A mouse press always moves focus in Chromium. The host renderer has to put it back when the
  press ends, and only for input an agent started.
- Focus emulation keeps the page from seeing that hand-back as a blur.
- `webContents.focus()` on a guest is only acceptable while the pane is holding the user's focus
  to give back.

Offscreen rendering (pages painted into a canvas) avoids all of this, but rebuilds every native
popup and copies every frame. It stays an experiment, not the fix.

## Design

1. **No focus side effects from CDP.** The proxy answers `Page.bringToFront` and
   `Target.activateTarget` without focusing anything.
2. **Keys and text go straight to the guest.** For an embedded guest, main-session
   `Input.dispatchKeyEvent` / `Input.insertText` (from the proxy) and Orca's own `keypress` become
   `sendInputEvent` key / `char` events with `skipIfUnhandled`, followed by a page round trip so
   the reply comes after the page handled them. When the page's focus sits in a cross-site
   frame, keys and text go to that frame's own CDP target session instead: main follows
   `document.activeElement` through frame elements (`DOM.describeNode` gives the frame id, which
   is the frame's target id), attaches with `Target.attachToTarget {flatten:true}`, enables focus
   emulation there and sends the input on that session. A same-site frame shares the guest's
   renderer, so widget events already reach it. The proxy hides these Orca-owned sessions' events
   from the agent's client. Offscreen and headless pages have no host frame tree and keep CDP.
3. **Agent input that needs focus borrows it.** Before a press, or before input only CDP can
   express (a frame that cannot be reached, text with control characters, unmapped keys,
   root-session IME composition), main enables focus emulation and asks the hosting pane to note the user's
   focused element, waiting for its answer. When the input ends the pane gives focus back, and
   checks again 50 ms later in case Chromium's focus move arrived after the end message. Nothing is
   restored when the user already had focus in that page.
   If the pane does not answer, the input is refused with `browser_focus_guard_unavailable`
   rather than sent. Agent Tab keys take the same guard, because Tab from the page's last field
   moves focus out of the guest into Orca's window.
   While the user's IME composition is open the pane answers `composing` and main refuses the
   input, because the blur would commit the unfinished text (into a terminal, that sends it to
   the shell). It is refused rather than held, so a press cannot outlive its agent or land in the
   user's next composition. Direct commands report `browser_user_composing`; commands run through
   the agent-browser helper, like `click`, report `browser_error` with the same retry message.
4. **Agent keys are marked as the agent's.** While agent input is injected, the guest's Orca
   shortcut and grab handlers ignore keys and `setIgnoreMenuShortcuts` keeps unhandled keys
   from Orca's app menu, so an agent's Cmd/Ctrl+T, W or Ctrl+Tab stays in the page. Measured:
   synthetic input never fired the guest's `before-input-event`; the mark is the guarantee.

Known gaps: the user's element is blurred for the length of one agent press, so keys typed in that
window reach the page, and a composition started between the pane's answer and the press is still
committed. Edit commands (`selectAll`, `paste`, ...) run even when the page cancels the key. Focus
emulation stays on while the debugger is attached, so a page the user really leaves sees no blur,
and it covers the main frame only, so a menu inside a cross-site frame can still close. A pane
that misses the 150 ms ready window refuses that input. Only offscreen rendering removes the first
two gaps.

## Tracker

| Step                                                                                              | PR             | State     |
| ------------------------------------------------------------------------------------------------- | -------------- | --------- |
| CDP focus side effects removed (`bringToFront`, `activateTarget`) + this doc                      | stack 1/3      | in review |
| Keys and text delivered to the guest directly                                                     | stack 2/3      | in review |
| Presses and CDP-only input borrow focus and give it back; focus emulation                         | stack 3/3      | in review |
| Regression spec: user typing in a terminal while an agent clicks, types and presses keys          | stack 2/3, 3/3 | in review |
| Visible-window spec (`@headful`, isolated display only): native focus stays on Orca's window      | stack 3/3      | in review |
| Main-session keys and text for a focused cross-site iframe go to its own session, no focus borrow | follow-up PR   | in review |
| Agent presses are refused while the user's IME composition is open (#28700)                       | follow-up PR   | in review |

---

### Reference Appimage Extraction Cache (`docs/reference/appimage-extraction-cache.md`)

# AppImage extraction cache

Linux AppImage builds extract their payload once into `$XDG_CACHE_HOME/orca/appimage` (default
`~/.cache/orca/appimage`) so the CLI, `orca serve` and anything they start can run from a copy that
does not vanish when the AppImage's FUSE mount does.

## Layout

```
<cache>/launcher/orca-ide            stable wrapper; execs launcher/installed
<cache>/launcher/installed           symlink to <ns>/<generation>/resources/bin/orca-ide
<cache>/<ns>/<generation>/           one extracted payload ("generation")
<cache>/leases/<ns>.<gen>.<pid>.<starttime>.json
```

- `<generation>` is keyed by the AppImage file's identity (device, inode, size, mtime).
- `<ns>` is keyed by the AppImage path **and a layout version**. Builds before generation leases used
  `sha256(path)` alone. Those builds prune every generation in that directory except their own, so new
  builds extract into a directory they never scan. New builds still reclaim idle generations left in the
  old directory for the same path.

## When a generation may be deleted

A pruner deletes a generation only when all of these hold. Every pruner runs under the registration
lock (`withAppImageRegistrationLock`).

1. It is not the generation the pruner keeps, and `installed` does not point at it.
2. No process of this user has its executable in it, according to `/proc/<pid>/exe`.
3. No lease names it and a live process. A lease records the pid, the Linux start ticks and the boot
   id. A different start time means a recycled pid, and a different boot id means a reboot. Both make
   the lease stale, and the pruner deletes it.

If `/proc` or a lease holder cannot be read, the generation is kept. A desktop, `serve` or daemon
process that runs from a generation writes its own lease at startup.

## Remaining window: rollback

Running a pre-lease build again at the same AppImage path brings back its pruner. It scans only the
old directory, so new generations are safe from it. Generations in the old directory, which only
pre-lease builds extract, stay as exposed as they were before this change.

One gap is accepted. A process that `exec`s from a generation just as `installed` is republished and a
pruner runs may lose its copy before `/proc` shows it. `installed` and the pruner's own generation are
never deleted, so only a launch in that instant is exposed.

## The desktop terminal daemon

AppImage builds carry the pinned Node at `resources/node-runtime/bin/node`. deb and rpm drop that file
at packaging time. When a window starts a terminal daemon, it tries these in order:

1. **The extracted copy.** The bundled Node in the generation for the file now at `$APPIMAGE` runs that
   generation's `daemon-entry.js`. It is used only when the generation is complete, has the Node and the
   entry, and its stamped version equals the running app's. Nothing it runs from is on a FUSE mount. Its
   executable keeps the generation `live` for pruners, and it writes a lease at startup.
2. **The AppImage's own mount.** `$APPIMAGE -e` starts the daemon on Electron (#18200).
3. **The window's mount.** This is the original launch.

The desktop never extracts a generation itself; CLI registration and `orca serve` do. Each launch that
cannot use rung 1 logs `[daemon] AppImage daemon host fallback reason=<reason>`, where the reason is
`no-extracted-copy`, `no-bundled-node`, `no-daemon-entry` or `version-mismatch`. A rung that fails to
start logs before the next one runs. Rungs 1 and 2 record the same identity, `$APPIMAGE/<entry>`. That
means a running daemon is adopted whichever rung started it: as a normal target at the same version,
and preserved while busy across an upgrade, as for any other daemon.

---

### Reference Cross Platform Support (`docs/reference/cross-platform-support.md`)

# Cross-Platform Support

Orca targets macOS, Linux, and Windows. Keep all platform-dependent behavior behind runtime checks:

- **Keyboard shortcuts**: Never hardcode `e.metaKey`. Use a platform check (`navigator.userAgent.includes('Mac')`) to pick `metaKey` on Mac and `ctrlKey` on Linux/Windows. Electron menu accelerators should use `CmdOrCtrl`.
- **Shortcut labels in UI**: Display `⌘` / `⇧` on Mac and `Ctrl+` / `Shift+` on other platforms.
- **File paths**: Use `path.join` or Electron/Node path utilities — never assume `/` or `\`.
- **Windows terminal shells**: `--shell` picks the shell a terminal _is_; `--command` is typed into whatever shell the host spawned, so a shell choice routed through `command` silently becomes a child process.
- **Windows setup scripts**: the setup/issue-command runner is a `.cmd` batch file unless the script starts with a `#!` line — never derive that from the user's terminal-shell preference, and never launch a `.cmd` runner with a bare `cmd.exe /c` from a Git Bash pane (MSYS rewrites the `/c`).
- **Windows child processes**: start them through `runProcess`/`spawnProcess` from `@orca/process-host` — never `child_process` directly. It pins `windowsHide`, refuses `shell: true`, and encodes `.cmd`/`.bat` arguments so neither `CommandLineToArgvW` nor `cmd.exe` mangles them. Recognised npm/pnpm `.cmd` shims are resolved to their real target so the spawn skips `cmd.exe` entirely.
- **Ripgrep**: Orca bundles `rg` for every platform, WSL, and SSH remotes. Spawn it through `spawnBundledRipgrep` (main) or `resolveRelayRipgrepCommand` (relay), never a bare `'rg'` — Windows resolves a bare name in the spawn cwd before PATH. Don't add git/readdir fallbacks locally; the relay's chain exists only for hosts an upload never reached.
- **Windows process enumeration**: read the table through `src/main/windows/windows-process-table.ts`, never by forking `powershell.exe`.
- **Windows MSYS/Git Bash panes**: their children break away from the per-PTY job unless it is created without `JOB_OBJECT_LIMIT_BREAKAWAY_OK`, and a `conpty.node` built before that fix passes every existing gate.
- **Windows daemon-host relocation**: the terminal daemon runs from a copy of the app runtime under `%LOCALAPPDATA%`, which is what survives an auto-update.
- **macOS daemon launch**: packaged macOS runs the terminal daemon from a copy of the signed terminal host helper through launchd, so an update cannot sever its privacy grants. Before touching that copy, its cleanup, or the fork fallback, read [`macos-daemon-tcc-attribution.md`](./macos-daemon-tcc-attribution.md).
- **Windows EDR signal**: don't add `-ExecutionPolicy Bypass`, `-EncodedCommand`, `cmd.exe /c` with escaped free text, per-operation interpreter spawning, or runtime `Add-Type` compilation without reviewing the EDR impact first — behavioural EDR scores each of those, and being signed does not clear them.
- **WSL commands**: build argv with `buildWslExecArgs` (always `--exec` — under `--`, `wsl.exe` expands `$name` in every argument and silently rewrites the script), and fence anything whose stdout you parse with `buildWslCapturedLoginShellCommand`, because the interactive login shell prints the distro banner to stdout.
- **Linux native modules**: keep the glibc floor at Ubuntu 20.04 / glibc 2.31. A module compiled from source on a newer runner can reference symbol versions absent on the floor and crash the app on startup. Packaging fails if a bundled native binary needs newer glibc.

---

### Reference Git Workflow (`docs/reference/git-workflow.md`)

# Git and Review Work

## Git Binary Compatibility

Orca runs the user's Git binary on native, WSL, and SSH hosts, which may all have different versions. Treat Git 2.25 as the core-workflow baseline.

## Git Scan Safety

- Never enumerate every ref and then run `git ls-tree -r` or `git show` once per ref. That ref × tree fan-out can retain gigabytes of output before a downstream `sort -u` or search can make progress.
- Prefer `rg` over the checked-out files for source searches. For history or refs, use a named ref, an explicit namespace/path, `--max-count`, and a bounded output; do not use an unqualified `--all` scan as a first diagnostic.
- Keep repository-wide commands targeted to the current repository and worktree. If an unbounded scan is genuinely required, measure the ref count first, explain the cost, and get confirmation before running it.

## Git Provider Compatibility

Source-control and review changes must consider GitLab and other supported git providers, not only GitHub. Keep provider-specific behavior behind explicit checks, and avoid GitHub-only naming for generic review concepts.

## GitHub CLI Usage

Be mindful of the user's `gh` CLI API rate limit — batch requests where possible and avoid unnecessary calls. All code, commands, and scripts must be compatible with macOS, Linux, and Windows.

## Merging Pull Requests

Before merging, verify that the required checks and aggregate gate in the full PR workflow have pass.

## Retrying GitHub Actions

Inspect a failed run with `gh run view <run-id> --log-failed --repo stablyai/orca`, then retry failed jobs with `gh run rerun <run-id> --failed --repo stablyai/orca`. GitHub also reruns jobs that depend on the failed jobs. In GitHub's UI, choose **Re-run failed jobs**; Orca offers **Rerun failed checks**.

Keep successful jobs unless there is a concrete reason to revalidate them, such as investigating runner-dependent flakiness. A full rerun restarts passing unit shards, packages, and platform checks too. If fixing the failure changes code, push the fix and validate the new commit; rerunning an older run still tests its original commit.

On a partial retry, the unit-selection plan or full set of shard artifacts may be absent for that attempt. Unit execution falls back to the full test inventory; incomplete selection evidence is advisory and cannot justify enabling selection. Inspect artifact requirements before retrying release or publishing jobs.

---

### Reference Headless Secret Storage (`docs/reference/headless-secret-storage.md`)

# How the headless server stores credentials

The desktop app seals saved credentials (Linear, Jira, Bitbucket, API keys) with Electron's
`safeStorage`, which uses the OS keychain. The headless server (`orcad`, the managed SSH server)
has no Electron, so it seals them itself, on the machine it runs on
(`src/main/orcad/orcad-secret-store.ts`).

## Where the key lives

Each credential is encrypted with AES-256-GCM. The master key is created on first start and kept
in `~/.orca/orcad-secret-store-<machine>.json`, mode `0600` (an owner-only ACL on Windows), mixed
with the machine ID (`/etc/machine-id`, macOS hardware UUID, Windows `MachineGuid`). That is the
directory the credentials live in, whatever `ORCA_USER_DATA` says, so the key lasts exactly as
long as the credentials it seals. There is one record per machine, so hosts that share a home
each keep their own key.

This is the same on every host: macOS, Linux, Windows and WSL. The server does not use the macOS
Keychain or a Secret Service keyring. A Mac reached only over SSH keeps its login Keychain locked
after a reboot, which made credentials unreadable until someone signed in at the screen. A later
design is expected to keep secrets on the client, as VS Code and Zed do.

A server started with `ORCA_USER_DATA` under a temporary directory (tests, smoke launchers)
keeps the old behavior. It stores credentials unencrypted and never creates a key in the real
`~/.orca`, or re-seals what is there.

Windows does not use DPAPI. Reaching it from Node takes PowerShell or a native addon, and running
PowerShell for each operation is an EDR signal.

On startup the server also deletes key-record staging files (`*.staging`) that a crashed write
left in the credential directory. It deletes only this machine's record's staging files owned by
this user, and only when the process that wrote them has exited.

## Hosts set up by an hourly build that used the keyring

Some hourly builds kept the master key in the macOS login Keychain or the Secret Service keyring,
and wrote a record with `"backend": "os-keyring"` instead of the key. On startup, a server that
finds such a record reads the key once (`security find-generic-password`, or `secret-tool
lookup` when a D-Bus session bus is set), with a 5-second timeout. In a session that may not show
UI, a locked Keychain fails right away (exit 36) instead of prompting.

- If the key reads back and matches the record, the server replaces the record with a key-file
  record holding the same key, not bound to the machine ID. Keyring keys were never machine-bound,
  so every sealed credential still opens and nothing is re-sealed.
- Otherwise (locked, timed out, missing, or a different key), it logs one `[secrets]` line and
  starts a new key file. Credentials sealed under the old key then show the usual "Could not
  decrypt saved Linear credential… reconnect Linear" error, and connecting the integration again
  fixes it. The server does not keep failing closed waiting for the keyring.

Either way the replacement holds a lock file beside the record (`<record>.lock`, so two starts
agree on one key), uses the same atomic write as other key-record changes, happens once,
and leaves the keyring item where it is. This migration is meant to be removed a few releases
after it ships (`src/main/orcad/orcad-legacy-keyring-key.ts`).

## Limitations

- **The key file is weaker than a keychain.** The key sits next to the credentials, and the
  machine ID it is mixed with is not secret. Profile-state records include it, for example. So the
  key file mainly stops a single credential file from opening on another machine. Anyone who can
  read this account's files, or a backup of `~/.orca`, can unseal the credentials. Treat the key
  file like the credentials themselves. The server logs this gap once with the `[secrets]` prefix.
- **The key belongs to the host.** If the record is unreadable or bound to a different machine
  ID, the server does not fall back to plaintext. It leaves the saved credentials
  alone and refuses new saves until the key loads again. If the key is gone for good, deleting the
  record starts over, and every integration has to be connected again. A new machine ID counts as
  a new machine, so it gets a new key, and credentials sealed under the old one have to be
  connected again.
- **Shared home directories.** Integration credentials live in `~/.orca` under the login home.
  Hosts that share that home (over NFS, for example), or a desktop app on the same machine, each
  seal with their own key, so a credential one of them saves can't be read by the others. To keep
  the old shared plaintext behavior on such hosts, set `ORCA_HEADLESS_SECRET_STORE=plaintext`.
- **Rolling back to an older server.** A server from before this change can't read credentials a
  newer one sealed. An SSH or WSL rollback restores profile state, not `~/.orca`, so after a
  rollback the old server shows "Could not decrypt saved Linear credential… reconnect Linear".
  Connect each integration again on that server. Plaintext copies are deliberately not kept for
  rollback.
- **GitHub and GitLab** tokens are not stored by Orca at all. `gh` and `glab` keep them, under
  their own storage settings.

## Migration

On startup, once the key loads, the server seals every credential that an older version stored in
plaintext. Each file is rewritten only after the new value has been decrypted and checked, and
only if no other process changed the file in the meantime. A file that can't be sealed is left as
it was. Readers still accept the legacy plaintext form, so no credential is lost.

---

### Reference Macos Daemon Tcc Attribution (`docs/reference/macos-daemon-tcc-attribution.md`)

# macOS terminal permissions across updates

Read this before changing how the packaged macOS terminal daemon is launched, copied or cleaned
up, or before treating a missing executable as a permission denial. Background: issue
[#13921](https://github.com/stablyai/orca/issues/13921); the signed native evidence is in PR #25848.

## Why a signed copy started by launchd

A daemon forked from the app runs as `Orca Helper` and macOS charges its privacy access to the app
at its original path. While an update replaces that path, a daemon that first touches a protected
file is denied, and the denial persists after the new bundle arrives even though System Settings
still shows the grant. A missing executable alone does not cause it: a daemon that already read
protected files keeps working after its old executable is deleted.

So a packaged macOS GUI app starts its daemon from a private copy that the updater never touches:

- Each packaged macOS app ships a signed helper, `Contents/Helpers/Orca Terminal Host.app` (about
  124 MiB): the pinned Node from `src/shared/node-runtime-pin.ts` as its main executable, the
  daemon's `out/main` JS and that slice's node-pty. `config/scripts/macos-terminal-host-bundle.cjs`
  builds and signs it in `afterPack`, boots it through one PTY round trip, and fails the build on any
  layout, Info.plist, OS floor, architecture or signature drift.
- `macos-daemon-bundle.ts` keeps one copy per helper build, named by the helper's CDHash, at
  `userData/daemon-host/macos/terminal-host-<cdhash>/app.noindex/Orca Terminal Host.app`. The helper
  is read from the bundle the running main process executes from, never the whole app. A present
  copy is reused as is. Otherwise the helper is cloned (APFS clone, regular copy otherwise) into a
  `.staging-<pid>-*` sibling, verified once with
  `codesign --verify --strict -R "=<app designated requirement>"`, and published with one `rename`;
  losing that rename to another publisher is fine. An app without the helper falls back to the
  fork.
- `macos-daemon-launchd.ts` runs the copy's Node on the copy's `daemon-entry.js` as a
  non-persistent launchd job, so the daemon has Orca's own identity rather than the UI process's
  replaceable path. The label is fixed per profile and protocol
  (`com.stablyai.orca.terminal.<profile hash>.v<protocol>`). Before bootstrap a stopped job with
  that label is booted out. A running job gets a bounded wait for shutdown and launchd retirement;
  a job still running afterward gets the retired-service recovery below for this protocol's label
  only, and stays fatal unless that recovery releases it; an unreadable job is fatal. `--entry-path` stays the installed
  app's entry, which the replacement preflight compares. The job file is 0600 and deleted right
  after bootstrap, because the environment may hold credentials. It sets
  `AssociatedBundleIdentifiers` to `com.stablyai.orca`, which is how a launchd job not installed
  through SMAppService names the app it belongs to for Local Network access. Readiness is the
  normal authenticated handshake, fenced by the launch nonce.

Node servers, SSH hosts, unpackaged builds, Linux and Windows keep the fork launcher. Adopting an
existing daemon never copies. Telemetry reports a daemon started from a copy as
`spawner_path_class=stable-copy`.

Only Developer ID builds exercise the helper path in practice. A local ad hoc build's designated
requirement is a cdhash, which the helper (a different binary) cannot match, so it falls back to the
fork.

## Why the helper is `com.stablyai.orca`

TCC records grants against the designated requirement, which for Orca is the identifier plus team.
Signing the helper with the app's own identifier lets it inherit every existing grant, including Full
Disk Access, which a user can only add by hand. A distinct identifier would be a new client: new
folder prompts naming it, and a new Full Disk Access row for every user.

## macOS floor

The helper's Node declares `LC_BUILD_VERSION minos 13.5`, which is also the helper's
`LSMinimumSystemVersion` and `NODE_RUNTIME_DARWIN_MINIMUM_OS`. Packaging fails if a Node pin bump
changes it. Below 13.5 the launcher returns before copying and the fork runs, so those Macs keep the
pre-#25848 behaviour; Orca itself still supports macOS 12.0.

## First launch

macOS takes about 1 s the first time a binary runs from a new path, so the first daemon of each
build starts that much slower; later daemons reuse the copy and pay nothing. During that first exec
the daemon's `/usr/bin/login` preflight can time out, and that one shell is spawned directly.

## Security

The helper is a signed "run any JS as Orca" binary: it honours `NODE_OPTIONS` and any script path,
and Electron's fuses do not apply to it. Orca's own binary already allows the same, because its
RunAsNode fuse is enabled, so the helper adds no new exposure.

## Fallback states

`launchMacDaemonFromStableBundle` returns null, and the launcher forks, only when no job from the
attempt can still claim the endpoint. It logs one `[daemon] macOS terminal host fallback reason=<…>`
line with `MacStableLaunchFallbackReason`: `prepare-failed` (helper, copy, signature or job file),
`deadline`, `bootstrap-refused` (launchd refused the job and it is absent), `exited-before-ready`
(the job stopped before answering), or `endpoint-taken` (another daemon owns the endpoint, which
the fork path then adopts normally). After `bootstrap-refused` or `exited-before-ready` the copy is
deleted if unused, so the next launch re-copies it. A timed-out bootstrap, or a job still running or
unreadable, throws: forking beside it could split the endpoint.

The stable attempt must hand off by `MAC_STABLE_LAUNCH_HANDOFF_MS` after the launch starts, so the
fork's own readiness wait and adapter install still fit the 60 s startup PTY gate. Preparation
shares one abort signal that ends early enough for bootstrap and the job's readiness wait to fit
too. A fallback after the handoff deadline throws instead of forking a daemon that would arrive
after the app has moved to local PTYs.

## Cleanup rule

After publishing or reusing the current copy, each launch starts one background collection (one at
a time per process). It deletes every other entry under `daemon-host/macos/`, older copies and
crashed staging dirs, once `lsof +D` over it reports no process. `lsof +D` exits 1 even when it
lists users, so only empty stdout counts as unused; any timeout, stderr, truncated output or other
exit code keeps the copy. Nothing else can run a non-current copy: only this app launches daemons,
and only from the current copy. Staging dirs named with this process's pid are skipped, since they
may still be copying.

## LaunchServices and Spotlight

The helper declares no links, documents or types, has a lower `CFBundleVersion` than the app and
`LSUIElement`, and was not registered in the signed spike, nested or copied. Copies sit in a
`.noindex` folder, which Spotlight skips, and Orca never registers them. Starting the daemon through
launchd leaves no record, but another process running the copy's executable directly can make
macOS register it, so retirement runs `lsregister -u` on the bundle before deleting it.

The helper's main-executable UUID is Node's, shared by every official Node 24.21.0 binary of that
architecture, so it is not unique to Orca. Local Network access still passed in the signed spike:
macOS matched the client by its code signature, and the job's `AssociatedBundleIdentifiers` ties it
to Orca. Do not register copies explicitly (`lsregister -f` or `LSRegisterURL`).

## Rollback

To turn this off, make `launchMacDaemonFromStableBundle` (`macos-daemon-launchd.ts`) return `null`
right after its platform checks: every launch then takes the fork path. Copy-launched daemons that
are still running are adopted as usual. Copies are only deleted by the collection, so keep it running
there (`retireOtherMacDaemonCopies(root, '')`) until no copy-launched daemon can remain; each copy
keeps about 124 MiB.

## Existing sessions and status

RPC and signal shutdown share a five-second process-exit deadline, armed before terminal teardown.
A stalled teardown must not leave the endpoint retired while the launchd job stays alive forever.

An explicit, untargeted Restart also recovers signed-helper jobs left by older builds after they
retired their PID record and endpoint but failed to exit. Recovery uses exact launchd labels and
arguments for this profile, ignores newer protocols, and preserves live or unreadable endpoints
and services still publishing a PID record. It verifies descendant exits before killing the old
root and releasing its label. A failed recovery is logged without blocking the current daemon's
restart. A launch runs the same recovery only for the current protocol's label, and only once that
label still blocks bootstrap after the retirement wait (#29361); otherwise the label refused every
new terminal until a manual Restart. Node/SSH hosts do not perform GUI-service recovery.

Running old daemons keep their terminals; settings reports app and terminal-host Full Disk Access
separately, and a denied terminal is recovered by closing it or restarting the daemon. The packaging
hook also gives the main bundle and every Helper the Desktop, Documents and Downloads descriptions,
because the fork fallback still hosts terminals in `Orca Helper`.

---

### Reference Remote Snapshot Intent (`docs/reference/remote-snapshot-intent.md`)

# Snapshots never carry intent

A client and a host exchange two kinds of payload: a **snapshot** (the whole state as the sender
sees it now, such as a pane layout) and a **delta** (what the user just did). They update
independently, so a snapshot may be resent, replayed or reordered by either side.

The rule: a snapshot never carries intent. Anything the receiver should treat as a user's action
travels as an explicit, optional delta field that names exactly what changed.

- A tag on the whole snapshot ("this push was a gesture") can't say which part of it the user
  changed, so the receiver either ignores it or applies it to fields the user never touched.
- An optional delta field is safe for older peers: one that doesn't know it drops it and keeps
  today's snapshot behaviour.

Example: `session.tabs.updatePaneLayout` sends the full layout and `titlesByLeafId` as the snapshot,
and `renamedLeafIds` as the delta (the panes the user renamed in this edit). The retired
`intent: 'gesture'` tag is stripped unread by the params schema, so older clients that still send it
keep working.

---

### Reference Test Antipatterns (`docs/reference/test-antipatterns.md`)

- Tests that reimplement production logic instead of importing and exercising the real code.
- Tests with descriptive names but no meaningful assertions, including `expect(true).toBe(true)`.
- Tests that only assert a mock was called, or assert the exact hardcoded value returned by their own mock.
- Tests that mock internal database, ORM, builder, or service chains so wrong tables, fields, joins, filters, or arguments cannot fail.
- Tests whose fixtures, field names, thresholds, or expected statuses encode behavior different from real producers or the source contract.
- Tests whose names promise inputs or behavior that their setup never exercises.
- Tests that duplicate stronger coverage and add no unique coverage, faster feedback, or better failure diagnosis.
- Tests that copy inventories, manifests, export lists, source imports, or literal strings instead of proving behavior.
- Tests of private predicates, call shapes, or provider-local replays that duplicate shared helper coverage.
- Test-only exports, globals, wrappers, or production paths that exist solely to make tests possible.
- Mocks that implement the behavior being asserted or pretend distinct APIs are equivalent.
- Fixtures that provide receipts, admission, callback ordering, or other outputs the owning production code should generate.
- Persistence tests that assert against a store path the exercised code never writes.
- Tests that assert capability flags instead of actual delivery or acknowledgement.
- Negative controls that pass because an unrelated guard fires or the rejection path is unreachable.
- Tests that sleep or wait on real time, retries, polling, or networks instead of using fake clocks and deterministic boundaries.

---

### Relay Region Correction RELAY REGION CORRECTION ACCEPTANCE (`docs/relay-region-correction/RELAY-REGION-CORRECTION-ACCEPTANCE.md`)

# Idle regional correction acceptance

Updated 2026-09-11. Scope: [idle-cutover plan](RELAY-REGION-CORRECTION-IDLE-CUTOVER-PLAN.md).
Local implementation is based on main `74cc9b50390b481009b34823a35eee01a5b90e40`,
with uncommitted changes atop `08c0802089a9186cf48ce0d168cd87565bd92d65`.

**Local validation is green; the published PRs are not updated or merge-ready.**
Cloud draft20037 still points to `0462a87011d8783f91f9a9e977ea7a68437a0d5b`;
desktop draft20031 still points to `f9e5b0193448e6c524603836b9278878b96cf51c`.
Their older CI is not evidence for this implementation. No commit, push, merge,
deployment, workflow dispatch or production mutation occurred in this continuation.

## What the implementation proves

The source permits optimization only when actual client sockets, splices, pending
connections and in-flight admission/control work are absent. It installs the local
gate before awaiting a constrained assignment transaction. Duplicate requests bind
exact authority; ambiguous database outcomes keep the source fenced until locked
reconciliation establishes the result. After commit, it releases the empty control
and uses ordinary reconnect and migration recovery. Emergency drains retain their
existing behavior. No retained-source protocol or restoration loop remains.

Three real TCP WebSocket scenarios join authenticated local HTTP dispatch, two
cells, the actual desktop origin pool, SQLite and an independent execution child:

1. Either connected device prevents movement; after both leave, target reconnect
   succeeds and the durable append-once mutation oracle shows no replay.
2. A racing arrival receives4409; a definite failed commit restores source admission.
3. The target control connection is actually attempted and fails; source activity
   is released and ordinary expiry recovery allows source epoch3 reconnect.

These use synthetic time and token verification. They do not prove physical mobile
background scheduling, production authentication/network behavior or user latency.

## Commands and results

Every test/app command uses `ORCA_BACKGROUND_LAUNCH=1`. Cloud commands run from
`cloud/apps/relay`; root commands run from this worktree. All logs below are under
`.tmp/idle-cutover-review/`.

| Scope | Command | Result / log |
| --- | --- | --- |
| Cloud | `ORCA_RELAY_TEST_POSTGRES_URL='postgresql://postgres@127.0.0.1:55440/postgres?options=-csearch_path%3Didle_full_root_20260911' ORCA_IDLE_REHOME_POSTGRES_URL='postgresql://postgres@127.0.0.1:55440/postgres' ORCA_REGION_CORRECTION_POSTGRES=1 pnpm exec vitest run --no-file-parallelism` |77 files /682 passed /zero skips; `cloud-full-postgres-idle.log` |
| Cloud | `pnpm run typecheck` | Passed; `cloud-typecheck-after-preview.log` |
| Cloud | `pnpm build` | Passed; `cloud-release-build-idle.log` |
| Root | `pnpm test src/main/runtime/relay` |20 files /177 passed; `desktop-relay-full-idle.log` |
| Root | `pnpm test tests/e2e/relay-region-correction.unit.test.ts tests/e2e/relay-region-compatibility.unit.test.ts` |16 passed; `transport-contract-cleanup.log` |
| Root | `pnpm test src/main/global-fetch-call-site-audit.test.ts tests/e2e/relay-region-correction.unit.test.ts` |4 passed after CI fixes; `ci-gaps-green.log` |
| Root | `pnpm tc:node` | Passed; `node-final-idle.log` |
| Root | `pnpm exec oxlint src/main/runtime/relay tests/e2e/relay-region-correction.unit.test.ts tests/e2e/relay-region-compatibility.unit.test.ts` | Passed; `desktop-lint-idle.log` |
| Root | `pnpm run check:code-quality:changed` | Passed,0 new findings across30 changed files; `code-quality-changed-idle.log` |
| Root | `pnpm run check:reliability-gates` |121 manifest gates passed; `reliability-idle.log` |
| Root | `ORCA_E2E_SSH_DOCKER=1 pnpm exec playwright test tests/e2e/ssh-docker-transport-drop-recovery.spec.ts tests/e2e/paired-remote-terminal-serve-restart-binding.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1` |7 passed after fresh build; `ssh-folder-idle.log` |
| Root | `node --test cloud/dev/scripts/deploy-relay-blue-green.test.mjs cloud/dev/scripts/read-relay-serving-regional-placement-version.test.mjs cloud/dev/scripts/relay-regional-rehome-workflow.test.mjs` |47 passed; `deployment-guards-idle.log` |

PostgreSQL16.15 reused the existing `orca-region-release-pg16` container on55440.
The suite used an isolated schema, dropped afterward (`cloud-full-postgres-cleanup.log`);
new concurrency tests create and clean independent schemas. No other PostgreSQL
port was used. Root oxlint ignores cloud; the configured cloud lint is TypeScript.

The seven background Electron checks cover paired folder-capable binding across
serve restart and six real Docker SSH recovery journeys: live pane, output bounds,
host-proven exit, repeated restarts, frozen-host silence and resumed input. They do
not exercise a physical phone-to-SSH regional cutover or packaged upgrade.

## Regression and review evidence

- Reconciliation: four cases fail against constant not-committed, then pass with
  locked authority checks (`reconciliation-{red,green}.log`).
- Registry: disabling pre-await admission accounting makes the arrival race fail;
  restoring it passes. Five conflicting operation-ID authority tuples fail before
  the identity fix, then all48 registry tests pass (`operation-tuple-{red,green}.log`).
- SQLite startup capability upgrade: red before upgrade logic;8 database tests pass
  afterward. Legacy controls default to not idle-capable.
- The commit placeholder negative control was run after implementation; it is
  counterfactual evidence, not a claim of chronological test-first development.
- Full PostgreSQL verification supersedes intermediate preview/legacy-test failures.
  An agent's earlier PostgreSQL safety-latch discrepancy was disproven in an
  isolated schema and explicitly withdrawn.
- Fifth GPT-6-astra low audit: **APPROVE within implementation scope**, no new blocker.
  Reviewed source barriers, exact duplicates, locked ambiguity and worker progress.
  Reviewer had migrated PostgreSQL tests, but did not author the core implementation.
  Ledger: `.tmp/idle-cutover-review/review-ledger.md`. No sixth cycle started.

## CI preparation and review artifacts

Old desktop CI failed on a stale global-fetch inventory count and missing `pg` for
the transport test. The count reproduces locally; the downstream catalog/probe
consumers already consume/cancel bodies, so the audited count is corrected.
The unit workflow installs locked cloud relay dependencies and builds their contracts.
From `cloud/`, both commands pass (`ci-relay-dependencies.log`):

- `npx --yes pnpm@10.24.0 --filter '@orca-cloud/relay...' install --frozen-lockfile --ignore-scripts`
- `npx --yes pnpm@10.24.0 --filter '@orca-cloud/relay^...' build`

Local split patches and draft bodies are in `.tmp/idle-cutover-review/`:
`cloud-idle.patch`, `desktop-idle.patch`, `cloud-pr-body.md`, `desktop-pr-body.md`.
`prepare-split.py` verifies ordered application against the stated main baseline;
`split-manifest.json` identifies the combined tree. The actual index/branches remain
unchanged. The HTML explainer is `.tmp/relay-region-explainer.html`; four stages,
failure toggle, light/dark mobile/desktop layout and browser-error checks pass.

## Remaining acceptance gaps

- Update the two draft PRs and verify fresh CI for their exact heads. The original
  handoff explicitly prohibited pushes; publication needs authorization.
- Packaged mixed-version desktop/mobile, physical-device lifecycle and platform
  transport remain unverified. Pinned wire tests are narrower evidence.
- CI soak and production RTT/interaction benefit remain unmeasured. Correction
  defaults off; deployment/enablement require the reviewed rollout procedure.
- Archive of intermediate/superseded evidence:
  `.tmp/idle-cutover-review/acceptance-history-before-final.md` and pre-rescope branch
  `relay-region-before-idle-implementation`. Earlier retention results do not prove
  the idle design or repair the superseded live-retention transport case.

---

### Relay Region Correction RELAY REGION CORRECTION API (`docs/relay-region-correction/RELAY-REGION-CORRECTION-API.md`)

# Idle regional correction contracts

Updated 2026-09-11. The [idle-cutover plan](RELAY-REGION-CORRECTION-IDLE-CUTOVER-PLAN.md)
is authoritative. This replaces the former retention/restoration protocol.

## Desktop measurement and capability

The optional assignment `regionCorrection` namespace supports `issue-window` and
`report`. The server issues a generation, fixed expiry, assignment epoch, incumbent
region and policy version1. A report supplies measurements for both regions or an
inconclusive reason. The first accepted report remains immutable; stale or changed
assignment evidence cannot authorize a move. Legacy placement hints remain separate.

Desktop advertises `idle-regional-rehome-v1` in its control capability header.
There is no new mobile message, retained-control lease, or restoration notification.
The cell persists the exact activity, generation, assignment and incarnation with
`idle_regional_rehome`. The obsolete `finish_existing` database column is always
written0 for schema compatibility and is not an eligibility signal.

## Director to source cell

`POST /v1/admin/host-idle-rehome` requires the configured director identity and
matching source cell/incarnation. The strict request contains:

- `v:1`, stable UUID `attemptId`, `userId`, `relayHostId`;
- `sourceCellId`, `sourceCellIncarnation`, `sourceAssignmentEpoch`, `sourceGeneration`;
- `targetCellId`, authenticated `cohortPercent`, and fresh `directorSafety`.

The strict response is `{v:1,outcome}` with `busy`, `committed`, `deferred`, or
`stale`. A lost HTTP reply is ambiguous and does not consume a dispatch-failure
budget. Repeat delivery retains the same operation identity. Cell status advertises
regional protocol3; new selection requires both cells at protocol3 or newer.

## Store and source ownership

`selectIdleRegionalRehomeCandidates` is read-only. It checks fresh evidence,
capability, policy, cooldown, telemetry and target capacity; bounded rotating pages
allow progress past busy hosts. Selection does not prove physical idleness.

`HostSessionRegistry.idleRehome` accounts for accepts, attaches, control commands,
and activation before their first await. Only an idle source installs its admission
gate. New arrivals receive normal retryable routing failure. Conflicting reuse of
an operation ID cannot share another authority tuple's result.

`commitIdleRegionalRehome` rechecks the exact request under existing locks, including
policy, cohort, capacity, global rate and concurrency. It atomically reserves the
target, advances assignment and records the attempt/source generation. It does not
choose a different host or destination. Completion is recorded at the source;
ordinary migration refresh, completion and expiry recovery remain responsible for
the target-registration lifecycle.

`reconcileIdleRegionalRehome` locks the assignment before reading the operation.
It distinguishes committed, not-committed with unchanged live source authority,
and stale authority. Missing data after an unlocked read is never rollback proof.
A database error leaves admissions fenced while reconciliation retries. Successful
cutover closes/releases the empty source control; the desktop resolves its normal
assignment and reconnects. Definite rollback reopens only the same source authority.

Outcome reporting aggregates actual attempts and migrations by cell/state, without
host identities or a retained-source table. A completed idle move does not authorize
closing future clients attached to the target.

---

### Relay Region Correction RELAY REGION CORRECTION CHECKLIST (`docs/relay-region-correction/RELAY-REGION-CORRECTION-CHECKLIST.md`)

# Relay region correction — implementation checklist

> **Scope: idle-only correction.** Superseded retention history is preserved in
> `.tmp/idle-cutover-review/checklist-history-before-final.md` and the backup branch.
> Follow [idle-cutover plan](RELAY-REGION-CORRECTION-IDLE-CUTOVER-PLAN.md).

## Idle-only implementation tracker

- [x] Resolve plan review findings and obtain Astra approval (revision 2).
- [x] Preserve pre-rescope revision on `relay-region-before-idle-implementation`.
- [x] Endpoint authentication and incarnation check: deterministic red/green.
- [x] Worker selects before cutover; busy deferral and lost reply tests: red/green.
- [x] Source barrier covers accepts, attaches, commands and control replacement (48 registry tests, including conflicting operation-ID authority tuples; red/green evidence in acceptance).
- [x] Constrained assignment commit and locked ambiguous-outcome reconciliation (11 PostgreSQL 16 tests on 55440, including both replacement orders and a lost commit reply).
- [x] Desktop removes live-retention handling, retains fresh decisions and fallback (53 focused desktop/compatibility tests; node typecheck passes).
- [x] Remove superseded cloud retention protocol/store/cleanup and obsolete tests. The legacy database capability column remains written as0 for existing schema compatibility; it enables no behavior.
- [x] Real two-cell transport: two clients, quiet connection, idle move, arrival race, and observed target-registration failure with ordinary recovery (3 tests pass).
- [x] Focused PostgreSQL transaction/concurrency checks on 55440 (11 passed).
- [x] Full local relevant cloud/desktop suites: 682 cloud tests with PostgreSQL,177 desktop relay tests,16 transport/compatibility tests.
- [x] Local pinned-wire compatibility, Docker SSH/folder continuity (7), types, lint and reliability manifest. Packaged/device/platform gates remain open.
- [x] Fifth Astra low implementation audit: APPROVE within the documented scope.
- [ ] Rewrite and validate cloud/desktop PRs, CI and final acceptance evidence.

These are merge-readiness tasks. Device/package/platform and production rollout
requirements remain explicit gaps until independently evidenced.



Current transport disposition: **the replacement idle-only transport suite is green
(3 tests), but the PRs are not ready**. Local cloud/desktop verification, final implementation audit, SSH/folder evidence
and reliability docs are complete. PR updates, fresh CI and release evidence remain.
The full cloud suite passes682 tests with PostgreSQL16 and no skips; cloud typecheck passes. Final audit and remaining end-to-end/PR tasks are still open. Exact commands/results are at the top of the acceptance document. The original
live-retention rollback assertion is superseded by the approved product rescope;
these results do not claim that old design was repaired.

## Publication and release gates

- [x] Prepare separate cloud/desktop patches and concrete PR descriptions locally.
- [x] Reproduce and address old CI failures: fetch audit count and cloud test dependencies.
- [ ] Obtain authorization to publish under the original no-push handoff constraint.
- [ ] Update cloud draft20037 and desktop draft20031; verify exact-head CI.
- [ ] Packaged mixed-version desktop/mobile and physical-device lifecycle.
- [ ] Linux/Windows transport evidence, CI soak, bounded rollout and measured benefit.

No production mutation or deployment occurred. Local passing tests and audit approval
are not a claim that the current published PRs are ready or the feature is deployed.
The acceptance document lists commands, evidence scope and remaining gaps.

---

### Relay Region Correction RELAY REGION CORRECTION IDLE CUTOVER PLAN (`docs/relay-region-correction/RELAY-REGION-CORRECTION-IDLE-CUTOVER-PLAN.md`)

# Relay region correction — idle cutover plan

Status: **REVISION 2 — DESIGN APPROVED; implementation and verification pending.**

Independent GPT-6-astra low review found no remaining design blocker. Approval is
conditional on the implementation gates below: pre-await ownership accounting,
shared assignment-row serialization, locked ambiguous-outcome reconciliation,
operation identity deduplication and late-callback fencing must be proven in tests.
Four independent idle-scope audits have been counted conservatively (scope, plan v1,
simplification, revision 2). If another plan revision fails review and a sixth cycle
would be needed, stop for user re-evaluation. Approval does not mean the current
live-retention PRs implement this design or are ready to merge.

## Requirement and deliberate tradeoffs

Automatically improve the host's relay region when no relay clients are using it.
A phone and iPad both disconnecting can create an opportunity. Continuous clients
can postpone optimization indefinitely. Returning during a move may incur ordinary
reconnect delay; zero-delay reconnect is NOT a requirement. Do not deliberately
close an established client connection to optimize latency. Direct LAN sessions
and running terminal processes do not themselves count as relay clients.

The mobile 30-second background grace is not a reliable scheduling event: the OS
can delay it. Use actual source-cell connection state. Desktop control remains
open without phones; do not wait for it to disappear naturally.

Keep fresh desktop measurements, incumbent-relative 25ms AND 20% improvement,
cohort/enable controls, capacity checks and cooldown. Probe improvement is not
proof of improved application latency. Cloud selects authoritative assignments;
mobile reconnect reads an assignment, it does not choose a faster region.

## Simplicity constraints

Reuse the regional worker, assignment transactions, reservations, request identity,
cell admission registry and normal desktop reconnect. No live-source retention,
splice transfer, recurring retained-control lease, new mobile protocol, global idle
poller, or desktop background-disconnect timer. Existing data sockets are never
copied or transferred (the former design did not transfer them either).

Try an idle opportunity, immediately defer if busy; do not keep a gate waiting for
users to leave. The gate exists only for a short attempted cutover. A retryable
arrival may lose this race and reconnect normally. Do not count that as evidence
of lost execution or replay a previously sent mutation.

Source cells already share the assignment database. Use one source-owned local
barrier around a constrained assignment transaction; do NOT introduce a distributed
prepare/commit protocol, a durable preparation table, or a second admission service.
The director selects candidates; the source's transaction rechecks director policy.

## What is idle?

The source owns one per-host/generation admission barrier covering every accept,
attach and control replacement path. Inventory these paths before changing them.
The quiescence predicate must require:

- `activeConnIds.size === 0` (includes attaches while persistence awaits).
- `activeSplices.size === 0` and `pendingConns.size === 0`.
- Zero accepts in flight before insertion in those maps. Track ownership before
  the first asynchronous operation that can admit this host; every exit releases it.
- Zero in-flight control operations that install credentials, mutate connection
  basis or create admissions. Track actual server handlers; do not invent an
  approximate count from RPC traffic or old database leases.

An authenticated open desktop control socket, its ping/pong and activity renewal
are NOT client work and NOT a reason to defer. They can remain until cutover closes
the empty session. A desktop request arriving after the barrier gets a normal
retryable transport failure; finish previously accepted state mutations before
claiming idle. Reconnection must preserve pairing and not blindly replay mutations.

Missing/expired database activity leases are never proof of idle. Outstanding
reservation cleanup remains owned and must be completed or safely fenced, but
waiting for every lease to expire would wrongly wait on the healthy control lease.

## Source-owned try-cutover

1. The director selects candidates read-only, using existing eligibility and pacing.
   It sends an authenticated idle-cutover request with stable operation ID, expected
   source assignment epoch/incarnation/generation and candidate target. Selection
   does not reserve capacity or change assignment. Repeat delivery uses the same ID.
2. Source validates the request and current session. In one synchronous segment,
   check all counters and install a per-host barrier. If busy, return `busy` without
   closing sockets or holding a barrier. Busy is a deferral, not a dispatch failure.
   The next worker pass must progress past busy candidates rather than starve others.
3. While barred, reject new clients and any new source control activation/rebind.
   Late asynchronous continuations must recheck barrier/session after awaits and
   release abandoned reservations. Install this fence before the first await of
   the cutover. No barrier timer may reopen admissions on its own.
4. Source calls a constrained version of the existing assignment transaction. Reuse
   its lock order, global rate/concurrency controls, cooldown, capacity reservation,
   freshness and safety checks. Recheck exact source epoch/incarnation/generation,
   target and operation identity. Reserve target, update assignment/epoch and record
   the existing durable migration/attempt atomically. Do not call today's unrestricted
   `claimRegionalRehome` and let it choose a different host. No network calls inside
   DB locks. Zero sockets is established locally, not inferred from activity leases.
5. If committed, retire the still-empty source session/control via the existing
   resolve-director closure path and release its activity. Desktop uses normal
   reconnect and registers on target. Reply with the durable operation outcome.
6. If definitively not committed and source authority remains unchanged, remove the
   barrier and continue on the original control. No target reservation survives a
   rolled-back transaction. If authority changed, retire the obsolete source instead.
   A timeout or transport error is NOT definitive rollback.

## Ambiguous transactions, restarts and failures

The request operation ID is known BEFORE the transaction and recorded as the
existing attempt ID on commit. Duplicate calls return its outcome and never start
another migration. A concurrent retry, cancellation or definitive-abort check must
serialize under the same host lock as commit; an unlocked absent-row lookup is
insufficient because the original transaction could still commit later.

Keep a barrier until the database transaction is known terminal and a locked
reconciliation establishes the outcome. If the driver result is ambiguous, retry
status through a per-attempt backoff callback, using the same identity. If durable
access is unavailable, remain fenced; bounded availability cannot be promised
while the assignment's authority is unknown. No timeout-only reopen. Use the
existing DB transaction timeout and request timeout, not a new renewable gate lease.

A lost director HTTP reply does not interrupt source-owned completion: source
finishes its transaction, reads durable outcome and closes/reopens locally. A
retry from any director sees the same operation. Director crashes do not strand
preparations because no separate preparation exists.

A source restart/replacement control is fenced by existing authoritative registration
and activity validation. It must serialize against the cutover transaction under
host locks, validate the current assignment and reject old source ownership if the
commit won. If replacement won, the old transaction must fail its generation/
incarnation recheck. This requires tracing current registration persistence and
proving the shared serialization point, not relying solely on the in-memory fence.
Late callbacks from a closed session cannot reopen admissions for its replacement.
Emergency drain invalidates local authority and participates in this serialization;
a cutover already committed follows its outcome, never reopens the emergency source.

After commit, target registration failure uses ordinary bounded migration recovery.
The old empty control must be released even if outcome delivery failed; otherwise
current rollback refuses while source activity remains (`assignment-store.ts:6923`).
Source process death is handled by normal activity expiry and cell incarnation
fencing. No live clients were discarded, but a returning client may wait for recovery.
Once clients attach at target, preserve them under ordinary assignment rules:
initial source idleness never authorizes closing future target clients.

## Compatibility and authority

Mobile already re-resolves on `WRONG_CELL` (4409) through
`dialRelayThroughDirectorFallback`; use that existing close code for arrivals at a
gated source. Before commit, resolution can still return the old address: existing
backoff must prevent tight retry loops. After commit it returns the target. Pin
old parser/client fixtures and test the actual codes; do not assume every error is
retryable. Do not introduce a new mobile close code or protocol message.

Empty host control closure uses the existing `DRAINING` / resolve-director path;
verify its reconnect behavior against the baseline desktop implementation.
Negotiate a distinct idle-cutover capability for participating cells and updated
desktops; do not reuse finish-existing capability to imply this new behavior.
Unsupported participants skip optional correction. Preserve old-server HTTP-400
fallback for measurement fields. Emergency drain/auth enforcement can invalidate
any in-flight cutover; it must fence late commit and preserve existing hard deadlines.
Disabling correction stops new attempts; existing ones still reconcile.

## Review and implementation gates

Review must verify the shared-store serialization and identify every admission and
control mutation path before approving implementation. Minimum tests (red before
green for new guarantees):

1. Phone remains while iPad disconnects: no move. Both disconnect: move possible.
   A quiet established socket or expired DB splice lease still prevents a move.
2. Accept before/after barrier, accept awaiting activity persistence, attach awaiting
   basis persistence, and credential mutation crossing the barrier. No late attach,
   leaked reservation or interrupted established client.
3. Busy attempt leaves source admissions usable; repeated busy hosts do not starve
   idle candidates or consume dispatch-failure budget.
4. Commit/replacement race; lost database/HTTP replies; timeout during transaction;
   duplicate workers; director crash; source restart; replacement control; stale
   epoch/incarnation; database outage and recovery. No timeout-only reopening.
5. Target failure before registration and after new client attachment; source-control
   release; ordinary recovery completes without retained-source restoration.
6. Current/old mobile reconnect before and after commit, same-address retry pacing,
   pairing preservation and no mutation replay. Old/new desktop/cloud combinations.
7. Emergency drain/auth denial during the cutover; no altered hard-drain behavior.
8. Real TCP WebSockets for two clients, admission race and failed cutover, independent
   execution-process identity and append-once mutation evidence. Docker SSH and
   folder workspace continuity. Tests use `ORCA_BACKGROUND_LAUNCH=1`.

Keep tests proportional: deterministic component races first, then real transport,
relevant cloud/desktop suites, types/lint and PR CI. PostgreSQL only on 55440 when
validating authoritative transactions. Do not replace a failing oracle with a weaker
assertion or accumulate tests mirroring implementation.

After clean review, replace superseded feature code/tests/docs on the two draft PRs,
preserving an immutable backup. Freshness and appropriate compatibility tests stay;
retention-only mechanisms and release requirements must be removed if irrelevant.
Do not claim readiness from tests of the superseded design.

Release separately from merge readiness: packaged mixed versions, physical device
background timing, Linux/Windows, bounded rollout and measured user benefit remain
explicit evidence requirements. No deployment or enable is authorized by this plan.

## Implementation notes — 2026-09-11

The authenticated director command carries its configured cohort percentage and
fresh process safety snapshot. A cell cannot use its own default-zero director
cohort setting to authorize or reject a selected host; it validates the authenticated
command, combines director/source safety, and rechecks durable policy, the host's
cohort bucket, and fleet/target safety inside the existing transaction. These fields
are on the internal admin endpoint, not the mobile or desktop protocol.

Candidate selection is read-only and uses a rotating page offset to progress past
busy hosts. A deterministic UUIDv5 derived from the exact source authority and target
keeps operation identity stable across director retries/restarts. Both details still
need final implementation audit and an explicit page-boundary fairness test.

Current focused and real-transport results are recorded at the top of the acceptance
document. They do not complete the remaining compatibility, cleanup and PR gates.

---

### Relay Region Correction RELAY REGION CORRECTION PLAN (`docs/relay-region-correction/RELAY-REGION-CORRECTION-PLAN.md`)

# Relay automatic region correction — implementation plan v2

Status: **IMPLEMENTED AND LOCALLY VALIDATED; RELEASE GATES REMAIN**. Section 5a is normative for first-grant adoption, retained-source rollback, renewal and cleanup. This replaces the idle-only proposal. Historical design reviews are preserved in the combined backup revision; current verification and remaining gaps are recorded in the acceptance document.

Implementation tracking: [live checklist](RELAY-REGION-CORRECTION-CHECKLIST.md). Completed work and validation evidence are recorded there; this document defines the behavior.

## Outcome and precise terms

An updated desktop obtains a reliable region preference. If its assigned region is materially worse, the existing director worker makes the better region the destination for new connections. Existing physical relay data connections continue through the old cell until they close or fail naturally. The desktop then releases the old origin and the durable migration completes.

“Existing connection” means a physical client-to-desktop relay data connection, represented by a cell splice and desktop connection ownership. It can carry multiple commands and subscriptions; it is not a terminal process, agent run, saved pairing, or recent typing. Tracked pending attachments and basis-bound control requests also protect source retirement. Quiet live connections are retained. No fixed maximum connection duration has been established or proposed for optional optimization.

For mobile: active use remains on the old cell; after the app actually suspends/closes that connection, the next successful connection resolves to the target. Current mobile schedules background suspension after 30 seconds, and checks an overdue deadline on foreground if the timer did not run. Putting the phone down while the app remains foreground is not a disconnect. No re-pairing is required solely for relocation.

Promise: optional region optimization does not deliberately terminate pre-existing connections merely because its grace timer expired. This is not a guarantee against network failure, process exit, revoked/expired auth, or emergency maintenance. New connection attempts during target startup may need normal recovery; do not promise zero delay on those attempts.

## Evidence and reuse

Source experiments used `origin/main` at `721a2692893ab29f8daee3149965bf5e9adf99a0`. Review must fetch current main and record its SHA, distinguishing changed source from these dated results.

- Bidirectional worker already exists in #19241 and predates deployed #19915. Do not implement a second placement/migration engine.
- Desktop `relay-origin-pool.ts` already opens a target, retains source connection ownership, and closes the source after final release. Auth refresh already visits every origin.
- Cell `host-session-registry.ts` has drain-only state, live/pending connection maps, and existing control renewal. Director store already retains migration state until source activity releases.
- Two unconditional deadline sites force interruption: desktop origin-pool and cell regional host drain. Removing those scheduling sites in a diagnostic snapshot made both preservation tests pass; restoring source made the identical tests fail again.
- A SQLite store test sustained a registered migration for a simulated hour with both controls renewed, then completed on source release.
- Mobile harness restored credentials/assignment/subscriptions after simulated drain. Sent mutations can become delivery-unknown and are not blindly replayed. The 251ms fake-clock recovery result is not measured real-world downtime.

Full evidence: [interruption findings](RELAY-INTERRUPTION-FINDINGS.md), [harness and patches](https://github.com/stablyai/orca/blob/0db9fdc486366f7451289f0c0599eed9ae1d94be/tests/tools/relay-rehome-interruption/README.md). The counterfactual patch is NOT production code: it has no negotiation, incorrectly changes normal deadline semantics, and does not validate failure/replay paths.

## 1. Ordered, expiring region decisions

Reuse desktop sampling and the existing assignment exchange. Separate legacy placement hints from eligibility to move an existing assignment.

Preserve cold-start placement: run the existing pre-placement probe and send its placement hint with the first assignment request. Do not create a default-region assignment merely to obtain a measurement window. Obtain the epoch-bound window with or after that first assignment; only a subsequent post-window measurement can certify migration eligibility. Test first placement with and without conclusive probes, plus old-server fallback, so the new migration protocol does not regress initial placement.

Proposed concrete protocol: an opt-in server-issued measurement window. A supporting desktop requests a window; director returns a per-host monotonic generation, fixed server expiry, and incumbent assignment epoch/region. Issuing a successor invalidates the predecessor for migration. The desktop probes after receiving the window, then reports a conclusive or inconclusive decision for that generation. Repeated delivery cannot extend expiry. An inconclusive outcome is retained as a tombstone; delayed older conclusive reports cannot resurrect eligibility. A restart that cannot reuse a valid cached decision obtains a successor window. Server time is authoritative for expiry.

Persist window generation/expiry, incumbent basis, supported probe-policy version, and outcome in the existing preference state. Keep the latest supported evidence only; no history of every probe. Duplicate same-generation decisions are idempotent; conflicting same-generation outcomes must not upgrade inconclusive to conclusive. Serialize window issuance/reporting per broker and reject stale assignment basis under claim lock. The exact schema and transaction ordering must be reviewed before implementation.

Legacy enum-only requests retain placement/reconnect behavior but cannot inherit or overwrite verified migration eligibility. Explicit new inconclusive decisions and missing legacy fields are different operations. Diagnostic region overrides are not measured proof. New request and response fields require explicit opt-in because both schemas are strict. Deploy server support before clients; implement old-server 400 fallback for both request shape and response negotiation without changing a healthy assignment.

## 2. Compare with the actual assigned region

Keep the current warm-up, sample count, spread rejection, and requirement that both regions be measurable. Initial placement may pick a best measured region. Moving an existing assignment additionally requires target latency at least **25ms lower AND 20% lower** than the measured incumbent region. These are the existing hysteresis thresholds applied to the correct basis, not a production-validated optimum.

Report compact comparison evidence (incumbent/target region timing, policy and reason), tied to the window and assignment epoch. Director validates bounds, eligibility and margin. A missing incumbent, incomplete catalog, rejected measurement, nearly tied regions, unsupported policy or stale assignment basis means no move. Clear legacy probe caches on upgrade without treating an unopposed or 1ms winner as migration evidence.

This optimizes the desktop-to-region path. Sample actual assigned-cell and mobile application behavior during rollout before claiming end-to-end user benefit.

## 3. Refresh ownership and cadence

The broker owns one decision deadline and one in-flight refresh/report task. Cancel them on broker close. Reuse the successful 24-hour cache interval and one-hour inconclusive retry interval, with jitter/backoff; eligibility expiry is distinct from retry scheduling. Do not wake offline/sleeping desktops for probes. On resume, check deadlines before using expired eligibility. Debounce network-change refresh only where the existing lifecycle provides a reliable signal.

Probe/report failure must not fail successful auth renewal or intentionally reconnect healthy controls. Serialize assignment response application with drain/recovery: same cell/epoch updates metadata; a newer assignment follows the existing target activation flow; stale responses are discarded. Report retries reuse the same decision/window rather than re-probing or extending expiry. During an open migration, do not claim another move or replace the retained source; refresh decisions may be stored for later reevaluation only.

## 4. Extend graceful migration to finish existing connections

Introduce an explicit opt-in drain mode for optional regional optimization. Persist it on the attempt and propagate it through the director-to-cell host-drain command and cell-to-desktop drain message. The source must learn mode from durable protocol state, not infer it from grace=0, a hostname or a retry count. Maintenance/emergency drains keep their current hard deadlines.

Eligibility requires supporting desktop and cell capabilities, fresh decision and matching source epoch/incarnation. New protocol/capability values and strict-parser fallbacks need cross-version tests. An unsupported participant defers optional correction rather than falling back to forced close. The reviewer should challenge how host capability remains bound to the currently active source generation, not merely a stale version-bearing report.

Nominal transition:

1. Existing claim reserves target capacity and commits target assignment plus migration/attempt state.
2. Source receives the mode-bearing drain and establishes the first valid authorized grant specified in section 5a before acknowledgment/cutover; desktop resolves/registers the target through existing code.
3. Target becomes the desktop's active origin. Existing source connections retain their source ownership; new connections resolve the current assignment. Already-admitted pending source connections may finish attaching and remain there.
4. Optional regional mode installs no forced old-origin/session close deadline. Existing event callbacks retire the source after its last owned connection and pending control operation end. Retain normal auth enforcement and operational emergency-close behavior.
5. Source control release/orphan cleanup removes its remaining activity. Existing completion logic finalizes the migration after source activity is gone and target is live.

Do not use DB splice lease absence to infer idle: those leases can expire with a live socket. This design instead allows source work to exist. Do not build the proposed pre-move globally atomic idle gate.

Required integration checks: pending control-RPC completion must trigger retirement when it was the final outstanding item; attach timeout/rejection and late async admission must not leak an origin or attach after retirement. Preserve existing source/target identity and request ownership. Cover two simultaneous mobile clients and a quiet connection; terminal counts and workspace type do not decide transport lifetime.

## 5. Failure, replay and bounded resource use

This section is a required implementation contract, not evidence that the current implementation already satisfies it.

| Condition | Required behavior |
| --- | --- |
| Target registration fails | Retain still-live source work; retry or reconcile using existing migration recovery. Restore source new-admission authority only through section 5a's retained-generation rollback; do not discard a migration that still owns connections. |
| Lost receipt / duplicate drain / zero-grace re-drain | Read durable mode and preserve existing work. Replay must never turn optional mode into forced closure. |
| Desktop restart / source generation replacement | Old process connections may already be gone; reconcile exact generations before retaining or clearing state. Unsupported replacement must not silently hard-drain existing work. |
| Source network/cell failure | Use existing failure recovery; connectivity loss is not proof remote execution exited. This is outside the no-deliberate-interruption promise. |
| Target fails after becoming active | Keep source connections that remain live; reconcile assignment/new connections through existing recovery without starting a third overlapping rehome. |
| Auth expiry/revocation or emergency cell drain | Preserve existing enforcement; optimization does not exempt sessions from security/maintenance lifecycle. |
| Last source data connection closes while control RPC pending | Wait for bounded RPC completion/timeout, then run cleanup without a polling loop. |
| Source remains busy for hours | Keep the migration open while healthy. Long duration alone does not force-close users or spend dispatch-failure budget. |

Bound **concurrent open migrations** in addition to starts/minute. Count pre-existing attempts; one migration per host remains enforced. Retain both controls' auth/lease renewals and account for source use plus target reservations. Data is not duplicated; extra controls/reservations still consume capacity.

Ensure fair progress in the existing 100-row lease-refresh and 10-row candidate/sweep pages; waiting old migrations must not starve registration, renewal or cleanup for newer ones. Initially enforce a conservative concurrency bound below the smallest relevant page capacity, counting existing open work, until fair traversal is verified. Filters for cohort/policy/capability belong before LIMIT and are rechecked under locks.

No user-visible maximum drain duration is claimed. If operations later require one for optimization, forcing closure would change the product promise and needs an explicit decision; a larger timer is not equivalent to finish-existing behavior.

## 5a. Review corrections: retained-source authority and lifetime

The independent review found three required contracts. This section supersedes any suggestion above that unchanged auth renewal or generic rollback suffices. Current source-control lifetime is six hours with thirty-minute jitter; auth-refresh does not extend it, and ordinary rotation only renews the active target. Current durable rehome refresh also has a 24-hour age ceiling. The one-hour SQLite experiment proved neither of those paths safe for indefinite live retention.

### Reuse the cell's existing activity renewal (supersedes the extra wire exchange)

Follow-up investigation found a smaller mechanism: a successfully validated `renewControlActivity` result can extend the same retained control's in-memory lease to `max(existingExpiry, requestedActivityExpiry)`. The cell already runs this renewal; no new desktop renewal timer, old-source rebind or recurring WebSocket exchange is needed. See [prototype evidence](https://github.com/stablyai/orca/blob/0db9fdc486366f7451289f0c0599eed9ae1d94be/tests/tools/relay-rehome-interruption/RETAINED-CONTROL-LEASE.md).

Before acknowledging the optional drain and telling desktop to cut over, establish the first short authorized renewal for the exact mode/attempt/source generation/incarnation. Subsequent grants reuse normal heartbeat renewal. Failure or stale state during adoption does not authorize retaining the source; reconcile the provisional target through the rollback contract below. A mode flag, pending database request or activity reacquisition alone is not a grant.

Narrow the existing atomic database renewal predicate with the retained attempt/mode/source basis, using immutable fields and preserving existing lock order; avoid a new precheck/query that can race mutation. The success callback must still match captured attempt, live session/socket/generation and mode; retirement/rollback/replacement/emergency drain invalidates it. Use the deadline sent at request start (currently 105s ahead), not response time, and do not add six hours on every heartbeat. Keep normal JWT/silence/watchdog enforcement and ordinary control rotation unchanged. Normal mode retains its existing lease; only extra retention needs these short successful grants.

Prototype evidence: 43 cell-registry tests and package typecheck pass, including >12h simulated retention without extra recurring renewal calls; 6 real Postgres renewal tests execute with zero skips on local 55440. Baseline/revert fails the three extension oracles. The prototype injects the future mode marker and is NOT production-ready: initial adoption ordering, durable mode predicate, wire negotiation and rollback remain integration work. This revision addresses source renewal only, not the whole retention feature.

### Final renewal-review correction: fence failures as well as success

Fresh GPT-6-astra / low review: [retained-control review](https://github.com/stablyai/orca/blob/0db9fdc486366f7451289f0c0599eed9ae1d94be/docs/relay-region-correction/RELAY-RETAINED-CONTROL-REVIEW.md), **REVISE one completion-fencing detail; heartbeat reuse supported**. The following correction is incorporated after review, not independently approved or implemented.

Every renewal completion and awaited recovery continuation must validate its captured socket/session, activity ID, current authority/mode transition and applicable ordering before altering scheduling, extending expiry, closing a socket, or reacquiring activity. In particular, a denial from an aborted retained attempt arriving after same-generation rollback must not close the restored source. A current applicable denial must still enforce closure. An obsolete missing-activity result must not initiate recovery; after awaited recovery, recheck authority and clean up abandoned acquisition as required. Do not use a blanket success-only fence or suppress all failures.

Add controlled-promise tests for late denial after rollback, after a newer valid authority transition, obsolete missing-activity recovery, and applicable denial. Verify both preserved socket/splice identity and correct rejection, not just expiry values. First-grant adoption must also reject a success whose requested expiry is already past.

In retained-mode SQL, the ordinary current-assignment authorization alternative must not bypass an aborted attempt check after rollback. If locking attempt rows, preserve assignment -> attempt -> migration -> activity dependency order used by existing rehome operations, rather than appending a late attempt lock. Use the prescribed PostgreSQL 16 environment for implementation concurrency validation on 55440; the earlier six-test PostgreSQL 17 run remains accurately labeled as narrower evidence.

### Retained-generation rollback

Generic source reassignment alone is insufficient: the source cell otherwise remains drain-only, and a fresh generation closes old splices. Add a durable, idempotent rollback transition for the exact optional attempt. In one authoritative store transaction, assign a newer source epoch and record that attempt's rollback outcome and retained source generation. Reconcile the source cell to that state: clear only that attempt's optional drain, preserve its socket/splices, update assignment metadata, and restore new admission only after validating current authority. Keep an aborted-attempt tombstone so a late drain cannot reverse rollback.

Desktop recovery must find/reuse the retained source origin and update its assignment metadata without replacing its control generation or transports. This needs an explicit supported transition, not the current rebind-failed -> fresh-generation fallback. Late target registration cannot override the newer source epoch. Release target reservations when reconciled; keep one open migration per host until cleanup completes. If the source process/generation is gone, use ordinary failure recovery and report that preservation is unavailable; do not infer remote execution exited.

### Transition table

| State/event | Authority and action | Existing source work |
| --- | --- | --- |
| Claim optional move | Durable attempt binds mode, source generation/incarnation, target, epoch and supported participants | Retained |
| Target registering | Source receives optional drain; existing target retry/reconciliation proceeds | Retained; do not convert age into forced closure |
| Target registered | Director target assignment is authoritative; desktop activates target | Existing source connections keep their origin |
| Retained source needs renewal | Existing cell activity renewal + exact optional migration authorize a short same-generation lease extension | Retained, source remains drain-only |
| Target failure / rollback | New durable source epoch plus rollback tombstone; source cell and desktop reuse exact retained generation | Retained if that generation still exists |
| Final source work ends | Connection and pending-work callbacks retire source; cancel renewals; release activity and complete | No source work left to preserve |
| Delayed drain/renew/register | Compare durable attempt outcome and epochs; ignore/reject obsolete transition | Must not resurrect draining or replace generation |
| Source failed / emergency drain | Existing authenticated operational/failure semantics apply | Preservation not promised under those failures |

### Mode-specific durable lifetime and compatibility floor

For healthy registered optional retained-source attempts, remove the current 24-hour age-only lease-refresh ceiling and exclude them from the age-only zero-grace re-drain lane. Keep bounded target-registration failure/reconciliation; do not extend an unreachable unregistered target forever. Duration alone is not dispatch failure, and active source data must not be dropped to reclaim an optimization slot. Current generic and regional cleanup paths must both understand the optional mode.

Before enabling optional retention, deploy a director/worker compatibility floor that understands all durable mode and rollback states even when new claims are disabled. Operational rollback must not go below that floor while such attempts exist. A disabled enable flag does not stop older cleanup/redrain code from misinterpreting new rows. Prove safe restart/rollback with existing open attempts and multi-day retention. The minimum revision will be recorded only after the compatible implementation is merged and validated.

Additional accepted obligations: notify retirement on every final pending-control transition (response, rejection, timeout, close); perform a final local session/generation check after awaited admission work and release abandoned reservations; bind negotiated support to current authenticated source generation rather than a stale measurement report; enforce the concurrent-migration cap in locked shared state, including pre-existing work. None of these requires rebuilding a global idle detector.

## 6. Rollout and observability

Keep rehome disabled while implementing/testing. Deploy compatible director/database support, then supporting cells and desktops with feature gated off. Verify readiness and actual capabilities before enabling an authorized bounded cohort. Do not dispatch workflows as part of this review.

Reuse current rate, cooldown, safety, capacity, durable attempts, and failure-budget controls. Preview must be read-only and share eligibility predicates, report full aggregate counts rather than a capped candidate page, and never claim attempts or consume budget.

Track eligibility/exclusions by direction, target registration, migration completion/abort, number/age of retained sources, concurrent reservations, deliberate forced-close count by drain mode, and reconnect/error rates. Retain compact sampled comparisons and matched before/after assigned-cell/application latency where available; a registered target alone is not evidence of user benefit. Use an unchanged cohort to detect unrelated network variation. Never log credentials, pairing data, or raw host IDs.

Acceptance: supported eligible hosts move new connections to their chosen target; old data connections survive optional-drain deadlines and retire on actual release; no forced source close solely due to optimization age; resources clean up; failures remain recoverable; sampled latency/reliability shows benefit without material regression. Specify sample sizes and numerical regression limits before production enable, using available traffic rather than inventing measured thresholds here.

## 7. Required validation and implementation order

1. Land freshness/ordering and incumbent-relative eligibility support under disabled control, with strict request/response compatibility tests. Cases: first-ever placement with correction disabled, placement-hint/actual-assignment mismatch, cold-start inconclusive probes and old-server fallback; delayed old reports, duplicates, inconclusive tombstones, clock changes, restarts, legacy writes, overrides, policy upgrades, stale epochs and cache clearing.
2. Implement broker refresh and event-driven origin retirement, test no auth coupling, no reconnect on unchanged assignment, pending-operation completion and sleep/resume.
3. Implement negotiated optional drain mode end-to-end in existing worker/cell/desktop paths, including normal emergency deadlines and replay. Extend the diagnostic oracles into real feature tests; do not merge the timer-removal experiment.
4. Validate real WebSocket traffic across source/target while sending unique stream markers and a mutation with delayed acknowledgment; check no duplicate/replayed mutation and independent host-side execution/output. Then validate mobile background/foreground reconnection and pairing preservation. Mock tests are not an end-to-end substitute.
5. Run actual Postgres integration/concurrency tests on **55440 only**. Require configured database, executed test counts and no conditional skip. Cover registration failure, target failure, concurrent admissions, cleanup, pagination and capacity accounting with long-lived sources.
6. Validate supported/unsupported desktop and cell combinations and old/new director rollback. SSH-hosted execution and folder workspaces remain governed by transport/owning host, not local process assumptions. No visible app tests on the user's desktop; background launch and isolated profiles are required.
7. Run a fresh operational safety gate and capability check only when a reviewed rollout is authorized. Enable a bounded cohort, observe retained-source/resource/benefit evidence, then expand. Disable stops new optional moves while safely reconciling existing ones.

### Implementation correction: restoration confirmation can retry

A rollback response can reach the cell while desktop director corroboration fails.
The cell therefore retains an exact pending-restoration authority (aborted attempt,
newer source epoch, original generation/incarnation/activity) until ordinary
same-generation rebind confirms restoration. Each successful existing heartbeat
renews only its short request-start deadline and replays `region-restored`.
Retained and restored authority are mutually exclusive; the old retained authority
remains rejected after rollback. Rebind, replacement, emergency drain, and applicable
denial fence outstanding callbacks. No extra timer or six-hour heartbeat grant is
introduced. This avoids losing long-lived source connections merely because the
first director confirmation failed.

---

### Relay Region Correction RELAY REGION CORRECTION ROLLOUT (`docs/relay-region-correction/RELAY-REGION-CORRECTION-ROLLOUT.md`)

# Remaining release work and proposed rollout

Status: implementation and local validation; no deployment authorization used.

## Ordered release actions

1. Review the local changes/PR and CI results. Record the immutable merged commit
   and image for deployment and rollback. All directors must run the reviewed idle
   worker before enabling correction; verify mixed-version deployment behavior
   while correction remains disabled.
2. Publish the relay image and deploy the director with correction cohort0 and
   durable rehome disabled. Verify image, health, preview route, migration schema,
   pool pressure and cleanup. This requires the authorized deployment workflow.
3. Roll supporting cell images using their existing cell workflow; verify protocol3,
   correct incarnation and telemetry. Release the updated desktop normally.
   The phone protocol is unchanged; no mobile update is required for correction.
4. Validate a packaged desktop with a physical phone (foreground, actual background
   suspension, resume, quiet connection, and target failure), and existing clients
   against new cells. Run the same transport gate on Linux/Windows in CI; validate
   an actual SSH-owned terminal survives client disconnection and normal reconnect.
5. Read the authenticated aggregate preview. Estimate eligible population by
   direction; use a matching unchanged comparison cohort. Choose the initial
   cohort and record approvals before changing settings or enabling.
6. Configure the approved cohort through the existing director workflow input,
   keeping the durable control disabled during deployment. Verify the serving revision and tagged rollback
   revision, then enable through the existing regional-rehome control workflow.
7. Stop new claims on a regression while ordinary migration cleanup and recovery continue.
   Investigate existing work rather than forcing a timer-based source closure.

## Proposed numerical acceptance criteria (must be approved before enable)

These are rollout proposals, not measurements of production baseline or authorization.

- First phase:1% deterministic host cohort; global cap remains8 open migrations.
  Observe at least24h and30 completed moves. If traffic cannot supply30, extend
  observation; do not treat a small sample as success.
- Immediate stop: any optimization-induced close of an established client, admission
  reopening on ambiguous source authority, duplicated mutation, authorization bypass, or more than8
  optimization migrations admitted (pre-existing work also consumes cap).
- Reliability stop: compared with an unchanged cohort over matching15-minute
  windows, assignment/connect failure rate rises by>=1 percentage point or2x
  (require>=100 attempts in each comparison group); investigate lower-count failures
  individually. Existing production incident limits always take precedence.
- Performance acceptance: matched post-move assigned-cell control RTT improves by
  >=25ms AND>=20% median per host for at least80% of evaluable moved hosts; require
  two independent samples before and after. Identify samples using
  assignment epoch/cell identity. Log sample insufficiency as unevaluable.
- Client connection setup p95 must not regress by>10% versus its matched baseline
  after accounting for the unchanged cohort. Setup is not application command
  latency; separately record physical-phone interaction timings on validation runs.
- Expansion requires healthy registration/completion, stable reservation usage, no growing stuck-recovery backlog, and numerical criteria
  above. Continuously connected clients may postpone optimization indefinitely.

## Evidence boundaries

Local tests use real socket traffic and independent host execution, but synthetic
clock/authentication. Neither the build nor schema fixtures prove distribution,
production latency, a physical phone or a signed desktop upgrade. Separate Docker SSH tests validate
SSH-provider recovery; they are not physical mobile-to-SSH cutover evidence.
The checklist leaves these release gates open deliberately.

---

### Site THIRD PARTY NOTICES (`docs/site/THIRD_PARTY_NOTICES.md`)

# Third-party notices

## Geist

The Geist font files in `src/assets/fonts/` are Copyright (c) 2023 Vercel, in
collaboration with basement.studio. They are distributed under the SIL Open
Font License, Version 1.1. Source: <https://github.com/vercel/geist-font>.

The full license text follows.

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Audits Browser Viewport Owner Retention Baseline Source (`docs/audits/browser-viewport-owner-retention/baseline-source.txt`)

import { webContents } from 'electron'
import {
  BROWSER_ANNOTATION_VIEWPORT_BRIDGE_WORLD_ID,
  buildBrowserAnnotationViewportBridgeScript,
  type BrowserAnnotationViewportBridgeOptions
} from '../../shared/browser-annotation-viewport-bridge'
import type { BrowserViewportOverride } from '../../shared/browser-workspace-types'
import { googleAuthUserAgent, isGoogleAuthUrl } from './browser-google-auth-ua'
import { BrowserManagerDownloadLifecycle } from './browser-manager-download-lifecycle'

export abstract class BrowserManagerViewport extends BrowserManagerDownloadLifecycle {
  // Why: guests are isolated from Orca's preload bridge, so main owns the devtools escape hatch after a tab→guest lookup.
  async openDevTools(browserTabId: string): Promise<boolean> {
    const webContentsId = this.webContentsIdByTabId.get(browserTabId)
    if (!webContentsId) {
      return false
    }
    const guest = webContents.fromId(webContentsId)
    if (!guest || guest.isDestroyed()) {
      // Why: a stale guest must clear every per-tab registry entry, not just the WebContents maps.
      this.unregisterGuest(browserTabId)
      return false
    }
    // Offscreen guests have no visible window on this desktop; detaching DevTools would open it
    // on the host display with no route back to the remote client.
    if (this.offscreenGuestIds.has(webContentsId)) {
      return false
    }
    guest.openDevTools({ mode: 'detach' })
    return true
  }

  // Why: emulate viewport via CDP; never detach the debugger here or the agent bridge's per-guest state is cleared.
  async setViewportOverride(
    browserTabId: string,
    override: BrowserViewportOverride | null
  ): Promise<boolean> {
    // Why: chain per-tab so rapid toggles don't interleave CDP commands and the last-requested override wins.
    const expectedWebContentsId = this.webContentsIdByTabId.get(browserTabId)
    if (expectedWebContentsId !== undefined) {
      // Keep host panning available while CDP applies the requested dimensions. The guest id fence
      // prevents this intent from leaking to a replacement guest; clearing the preset removes it.
      this.viewportPresetActiveByTabId.set(browserTabId, {
        guestWebContentsId: expectedWebContentsId,
        active: override !== null
      })
    }
    // The renderer resizes the host before CDP completes; discard the old geometry until it
    // reports the new pane bounds so a pending preset cannot route wheel input using stale limits.
    this.viewportScrollStateByTabId.delete(browserTabId)
    const prev = this.viewportOpsByTabId.get(browserTabId) ?? Promise.resolve()
    const next = prev
      .catch(() => {})
      .then(() => this.doSetViewportOverrideImpl(browserTabId, override, expectedWebContentsId))
    this.viewportOpsByTabId.set(browserTabId, next)
    try {
      return await next
    } finally {
      // Why: only clear if we're still the tail; a later call may have replaced the entry, and deleting would break serialization.
      if (this.viewportOpsByTabId.get(browserTabId) === next) {
        this.viewportOpsByTabId.delete(browserTabId)
      }
    }
  }

  async setAnnotationViewportBridge(
    browserTabId: string,
    options: BrowserAnnotationViewportBridgeOptions,
    resolveGuest: () => Electron.WebContents | null
  ): Promise<boolean> {
    const prev = this.annotationViewportBridgeOpsByTabId.get(browserTabId) ?? Promise.resolve()
    const next = prev
      .catch(() => {})
      .then(() => this.doSetAnnotationViewportBridgeImpl(options, resolveGuest))
    this.annotationViewportBridgeOpsByTabId.set(browserTabId, next)
    try {
      return await next
    } finally {
      if (this.annotationViewportBridgeOpsByTabId.get(browserTabId) === next) {
        this.annotationViewportBridgeOpsByTabId.delete(browserTabId)
      }
    }
  }

  // Why the caller resolves the guest: the same bridge serves browsing pages and workspace
  // documents, which live in different halves of the page registry.
  // Why a resolver and not the guest itself: this op may have waited behind another one, and a
  // cross-process navigation meanwhile swaps the tab's contents without destroying the old one —
  // injecting into the guest the request named would bridge a page nobody is looking at.
  // Why no tab id: with teardown gone this reaches only the guest the resolver hands back, and
  // taking an id it cannot act on would invite the next reader to act on it.
  protected async doSetAnnotationViewportBridgeImpl(
    options: BrowserAnnotationViewportBridgeOptions,
    resolveGuest: () => Electron.WebContents | null
  ): Promise<boolean> {
    // Why no teardown here: the resolver already unregisters a page whose guest died, and the only
    // case it uniquely leaves is an ownership mismatch on a healthy page — where tearing down would
    // cancel that page's in-flight downloads and grabs over a request that was merely misaddressed.
    const guest = resolveGuest()
    if (!guest || guest.isDestroyed()) {
      return false
    }

    try {
      // Why: run the scroll bridge in an isolated world so page scripts can't read the per-tab token or tamper with it.
      await guest.executeJavaScriptInIsolatedWorld(
        BROWSER_ANNOTATION_VIEWPORT_BRIDGE_WORLD_ID,
        [{ code: buildBrowserAnnotationViewportBridgeScript(options) }],
        false
      )
      return true
    } catch {
      return false
    }
  }

  protected async doSetViewportOverrideImpl(
    browserTabId: string,
    override: BrowserViewportOverride | null,
    expectedWebContentsId: number | undefined
  ): Promise<boolean> {
    const webContentsId = this.webContentsIdByTabId.get(browserTabId)
    if (!webContentsId || webContentsId !== expectedWebContentsId) {
      return false
    }
    const guest = webContents.fromId(webContentsId)
    if (!guest || guest.isDestroyed()) {
      // Why: a stale guest must clear every per-tab registry entry, not just the WebContents maps.
      this.unregisterGuest(browserTabId)
      return false
    }

    try {
      if (!guest.debugger.isAttached()) {
        guest.debugger.attach('1.3')
      }
    } catch (err) {
      // Why: attach throws if DevTools is open on the guest; log context so this failure mode is diagnosable.
      console.warn('[browser-manager] setViewportOverride: failed to attach debugger', {
        browserTabId,
        webContentsId,
        error: err instanceof Error ? err.message : String(err)
      })
      return false
    }

    const dbg = guest.debugger
    try {
      if (override) {
        await dbg.sendCommand('Emulation.setDeviceMetricsOverride', {
          width: override.width,
          height: override.height,
          deviceScaleFactor: override.deviceScaleFactor,
          mobile: override.mobile
        })
        if (this.webContentsIdByTabId.get(browserTabId) === webContentsId) {
          this.viewportPresetActiveByTabId.set(browserTabId, {
            guestWebContentsId: webContentsId,
            active: true
          })
        }
        await dbg.sendCommand('Emulation.setTouchEmulationEnabled', {
          enabled: override.mobile,
          maxTouchPoints: override.mobile ? 5 : 0
        })
        // Why: viewport sizing must not override a profile's explicit native-UA identity.
        if (this.userAgentModeByPageId.get(browserTabId) !== 'native') {
          // Navigation must see the preset intent while the final CDP command is in flight.
          this.viewportUaOverrideMobileByTabId.set(browserTabId, override.mobile)
          // Why: same sender as the navigation path, so both resolve the tab's host identically.
          await this.sendViewportUserAgentOverride(guest, override.mobile)
        }
      } else {
        await dbg.sendCommand('Emulation.clearDeviceMetricsOverride', {})
        if (this.webContentsIdByTabId.get(browserTabId) === webContentsId) {
          this.viewportPresetActiveByTabId.set(browserTabId, {
            guestWebContentsId: webContentsId,
            active: false
          })
        }
        await dbg.sendCommand('Emulation.setTouchEmulationEnabled', {
          enabled: false,
          maxTouchPoints: 0
        })
        const trackedMobile = this.viewportUaOverrideMobileByTabId.get(browserTabId)
        // A navigation after this point must not re-install the override behind the clear.
        this.viewportUaOverrideMobileByTabId.delete(browserTabId)
        try {
          if (this.authUserAgentOverrideStateByGuestId.has(guest.id)) {
            const url = this.resolveTabNavigationUrl(guest)
            const restored = await this.applyAuthUserAgentOverrideOverCdp(
              guest,
              false,
              url,
              isGoogleAuthUrl(url) ? googleAuthUserAgent() : guest.session.getUserAgent()
            )
            if (!restored) {
              throw new Error('Failed to preserve auth user agent')
            }
          } else {
            // Why: passing an empty string restores the session default UA.
            await dbg.sendCommand('Emulation.setUserAgentOverride', { userAgent: '' })
          }
        } catch (error) {
          if (trackedMobile !== undefined) {
            this.viewportUaOverrideMobileByTabId.set(browserTabId, trackedMobile)
          }
          throw error
        }
      }
      if (this.webContentsIdByTabId.get(browserTabId) !== webContentsId) {
        return false
      }
      return true
    } catch {
      return false
    }
  }
}

---

### Audits Module Organization CODEBASE MAP (`docs/audits/module-organization/CODEBASE-MAP.md`)

# Orca codebase map and cleanup backlog

Use this map to locate a responsibility before choosing a cleanup. Read the [assessment](README.md) for the recommended naming policy and migration process. [codebase-map.json](codebase-map.json) backs the tables with a complete tracked-directory inventory, repeated-prefix signals and proposed file paths.

Use the [task tracker](TASKS.md) and [execution-tracker.json](execution-tracker.json) for the current PR order and progress. Their activity-informed sequence supersedes the preliminary phases below. The original file paths here remain the audit baseline; execution tasks refine destinations and replace four forwarding-file moves with removal and direct-owner imports.

## How the product fits together

The desktop interface runs in a renderer. Electron's preload bridge exposes selected host operations, and IPC handlers connect those requests to backend domains. The host runtime also serves remote clients, owns sessions and workspace state, and coordinates execution. A long-lived daemon owns terminal processes. Shared modules supply contracts and pure rules to several surfaces.

The mobile app is a separate application. Its connection reaches the desktop host through cloud relay services; the cloud also has a separate push gateway. The relay's director and cells coordinate and carry connections. It is not the place where desktop application commands are implemented.

```mermaid
flowchart LR
  Desktop[Desktop interface] --> Preload[Electron preload bridge]
  Preload --> IPC[Desktop IPC handlers]
  IPC --> Backend[Backend domain services]
  IPC --> Runtime[Host runtime and RPC]
  Web[Web interface] --> Runtime
  CLI[Orca CLI] --> Runtime
  Phone[Mobile app] --> Relay[Cloud relay]
  Relay --> Runtime
  Runtime --> Backend
  Runtime --> Daemon[Terminal daemon]
  Backend --> Platform[Process and native adapters]
  Contracts[Shared contracts and pure rules] -.-> Desktop
  Contracts -.-> Phone
  Contracts -.-> Runtime
```

Arrows describe principal responsibilities and communication routes; this is not an exhaustive deployment or security diagram. The machine-readable `productionAreaImportEdges` is a separate static import summary: an import does not establish the direction of a runtime request.

## Where to look

| Area               | Repository path    | Tracked files | JS/TS files | Responsibility                                                                                    |
| ------------------ | ------------------ | ------------: | ----------: | ------------------------------------------------------------------------------------------------- |
| desktop-shell      | `src/main`         |         8,728 |       8,645 | Desktop application lifecycle, integrations, operating system services and execution adapters     |
| runtime            | `src/main/runtime` |         3,290 |       3,024 | Host-owned sessions, workspace state, command execution, RPC and orchestration                    |
| ipc                | `src/main/ipc`     |         1,208 |       1,208 | Electron renderer-to-host request handlers                                                        |
| terminal-daemon    | `src/main/daemon`  |           587 |         574 | Long-lived terminal processes, endpoint ownership and terminal history                            |
| preload            | `src/preload`      |           181 |         181 | Typed Electron bridge exposed to renderer windows                                                 |
| renderer           | `src/renderer`     |        12,906 |      12,842 | Desktop and web React interface, local state and runtime clients                                  |
| contracts          | `src/shared`       |         3,647 |       3,592 | Cross-surface contracts, schemas and domain rules; a physical folder, not one cohesive domain     |
| cli                | `src/cli`          |           428 |         428 | Orca command-line client and command presentation                                                 |
| process-host       | `src/packages`     |            41 |          36 | Internal packages including the controlled subprocess API                                         |
| wsl-guest          | `src/wsl-guest`    |            94 |          94 | Windows Subsystem for Linux guest dispatcher and protocol                                         |
| types              | `src/types`        |             1 |           1 | Ambient TypeScript declarations and build constants                                               |
| mobile             | `mobile`           |         3,901 |       2,995 | Expo phone application, transport, embedded web engines and mobile tooling; independent workspace |
| cloud              | `cloud`            |           662 |         578 | Relay, push services, wire packages, operations and infrastructure; independent workspace         |
| native             | `native`           |            74 |           4 | Platform-specific operating system integrations and native packages                               |
| tooling            | `config`           |         1,138 |       1,035 | Build configuration, generators, audits, development scripts and CI checks                        |
| integration-tests  | `tests`            |         1,166 |       1,106 | End-to-end, cross-version and test-tool coverage; unit tests also live beside source              |
| documentation-site | `docs/site`        |           125 |          28 | Next.js documentation website; independent package                                                |
| documentation      | `docs`             |           409 |         108 | Engineering references, investigations, audits and documentation assets                           |
| automation         | `.github`          |           142 |          14 | CI, release and cloud-operation workflows and actions                                             |
| repository-support | `.`                |           163 |           3 | Repository root, resources, bundled skills, examples and remaining support files                  |

Counts above assign each tracked file to its most specific area. For example, `runtime`, `ipc` and `terminal-daemon` are removed from the desktop-shell count, and the documentation website is removed from the documentation count. They therefore add up without counting those children twice. The JSON additionally provides recursive directory counts when you want a whole subtree.

Most unit tests live beside implementation files. The top-level `tests/` directory is not the full test suite. Root, mobile, cloud and the documentation website have separate package configurations; consult the package in the affected workspace when checking a move.

## Follow a feature through the codebase

| Feature         | Backend and runtime                                                                                                                         | Contracts and interface                                                                                                                                |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Jira            | `src/main/jira/`; `src/main/ipc/jira*.ts`; `src/main/runtime/rpc/methods/jira*.ts`                                                          | Flat `src/shared/jira*.ts`; Jira RPC schemas under `src/shared/rpc-contract/`; `components/task-page/jira/`; Jira state under renderer `store/slices/` |
| Linear          | `src/main/linear/`; `src/main/ipc/linear*.ts`; `src/main/runtime/runtime-linear*.ts`; Linear RPC methods                                    | `src/shared/linear/`; `components/task-page/linear/`; Linear renderer state                                                                            |
| Terminals       | `src/main/runtime/terminal/`; `src/main/ipc/pty/` and flat PTY files; `src/main/daemon/`; `src/packages/process-host/`                      | Renderer `components/terminal-pane/`; phone transport in `mobile/src/transport/`                                                                       |
| Agent chat      | Provider adapters such as `src/main/claude/` and `src/main/codex/`; `src/main/native-chat/`; host session modules under `src/main/runtime/` | Agent-session and native-chat families in `src/shared/`; renderer `components/native-chat/`; `mobile/src/session/`                                     |
| Workspace state | `src/main/runtime/sidebar-inputs/`; `src/main/persistence/`; `src/main/ipc/worktrees/`                                                      | `src/shared/workspace-layout/`; renderer state under `store/slices/`                                                                                   |

Renderer component paths in this table are relative to `src/renderer/src/`. Patterns identify current filenames, not proposed directories. The JSON's `featureTrails` contains the corresponding repository paths and their roles.

Use each trail to retain layer boundaries during cleanup. Moving a Jira IPC handler into `src/main/ipc/jira/` is appropriate; putting the authenticated Jira client into a shared contract folder would cross an execution and dependency boundary.

## Cleanup backlog

Phase 1 means small candidate pilots; start with either IPC provider family. Phase 2 means the next domain-focused work. Phase 3 needs broader ownership and compatibility review. A row is an inventory scope, which can require several PRs.

| Phase | Scope ID                 | Files P/T | Outside prod callers | Risk     | Proposed destination                               |
| ----: | ------------------------ | --------: | -------------------: | -------- | -------------------------------------------------- |
|     1 | `ipc-linear`             |       6/1 |                    1 | lower    | `src/main/ipc/linear/`                             |
|     1 | `ipc-jira`               |       6/4 |                    1 | lower    | `src/main/ipc/jira/`                               |
|     1 | `linear-issue-context`   |      10/7 |                   10 | moderate | `src/main/linear/issue-context/`                   |
|     1 | `jira-attachments`       |       9/6 |                    6 | moderate | `src/main/jira/attachments/`                       |
|     1 | `ephemeral-vm`           |      11/6 |                    2 | moderate | `src/main/ephemeral-vm/`                           |
|     2 | `task-page-leftovers`    |    125/68 |                   61 | moderate | `src/renderer/src/components/task-page/`           |
|     2 | `settings-repository`    |     40/20 |                    9 | moderate | `src/renderer/src/components/settings/repository/` |
|     2 | `store-agent-status`     |     31/24 |                   26 | moderate | `src/renderer/src/store/slices/agent-status/`      |
|     2 | `renderer-session-rules` |     38/21 |                   52 | moderate | `src/renderer/src/lib/agent-session/`              |
|     2 | `ipc-filesystem`         |     68/73 |                   58 | higher   | `src/main/ipc/filesystem/`                         |
|     3 | `ipc-pty`                |     12/78 |                   31 | higher   | `src/main/ipc/pty/`                                |
|     2 | `shared-jira`            |     21/10 |                  176 | higher   | `src/shared/jira/`                                 |
|     2 | `browser-cookie`         |     25/32 |                    4 | higher   | `src/main/browser/cookies/`                        |
|     2 | `cloud-relay-assignment` |      8/20 |                   11 | higher   | `cloud/apps/relay/src/assignment/`                 |
|     2 | `tooling-mobile-web`     |     24/51 |                    2 | moderate | `config/scripts/mobile-web-app/`                   |
|     3 | `shared-session`         |    171/86 |                1,085 | higher   | `src/shared/agent-session/`                        |
|     3 | `shared-native-chat`     |     98/73 |                  420 | higher   | `src/shared/native-chat/`                          |
|     3 | `shared-remote-runtime`  |     52/29 |                  175 | higher   | `src/shared/remote-runtime/`                       |
|     3 | `runtime-session`        |     59/70 |                   76 | higher   | `src/main/runtime/agent-session/`                  |
|     3 | `mobile-native-chat`     |   165/121 |                   27 | higher   | `mobile/src/session/native-chat/`                  |
|     3 | `claude-structured`      |     46/73 |                   33 | higher   | `src/main/claude/structured/`                      |
|     3 | `codex-app-server`       |     14/11 |                   40 | higher   | `src/main/codex/app-server/`                       |
|     3 | `persistence-leftovers`  |      1/84 |                  209 | higher   | `src/main/persistence/`                            |

`P/T` separates production-classified JS/TS from test/support files using the repository's existing classifier. Production-classified includes tools and declarations and is not a count of shipped application modules. Outside callers count distinct production-classified files with a direct import into that scope, including type imports. They measure required import-review work, not runtime cost. Paths are suggestions, not moves already applied.

The Task Page scope routes Jira, Linear, GitHub and GitLab files into their existing child folders; cross-provider files go to `task-page/`. Other broad scopes generally retain basenames in the initial move. Inspect each proposal's `review` field before implementing it.

Directory-size signals also identify work beyond these selected scopes, including terminal-pane, sidebar, editor, right-sidebar and renderer runtime. The `prefixFamilies` collection supplies candidate families throughout the repository. Those signals need semantic review before becoming additional proposals.

## What the data proves

Snapshot commit: `0b82f88e9f2b6904f8ecf296b11d0581de4cfbb9`.

- **38,891 tracked files** across **901 directories**, including **36,496 JS/TS files** and **135 native/shell source files**. Other tracked formats remain in the directory inventory.
- JS/TS classification: **19,666 production-classified**, **16,828 test/support**, and **2 identified generated files**.
- **66 directories** have more than 100 direct JS/TS files; **34** have more than 100 direct production-classified JS/TS files. These are triage signals, not proposed CI limits.
- **1,457 overlapping prefix families** and **23 reviewed candidate scopes** covering **2,008 distinct files**.
- **197,356 distinct-per-file module specifiers** examined, of which **150,800 resolve to tracked JS/TS inputs** and **236 to other tracked inputs**. Multiple specifiers may resolve to one file; graph edges deduplicate those file pairs.
- **0 parser failures**, **0 missing tracked paths**, and **27 unresolved local/source-package specifiers**. The unresolved list includes generated or built outputs and investigation fixture references; it is not a list of product defects. Bare external or unmodeled packages are counted separately.
- **0 existing-target collisions**, with proposed target comparison also checking case-insensitive names.

The data identifies the checkout commit and a SHA-256 over JS/TS paths and current contents. `directories` inventories every existing tracked directory in scope, with direct and recursive counts. `prefixFamilies` normalizes kebab-case, PascalCase and camelCase and removes the `use-` hook prefix to find domain signals; the first one to three tokens produce overlapping families. A family appears when it has at least eight files and three production-classified files. This deliberately does not prescribe a folder for every matching prefix.

For each proposal, `moves` provides exact `from` and `to` paths. `dependencyBoundary` supplies all outside importer paths, a production-classified subset, outgoing dependency-directory counts and direct internal/inbound/outbound edge counts. Internal edges show relationships inside the proposed group; outbound edges show that moving a group does not make it independent. `collisionCount` checks proposed targets against existing tracked paths without case sensitivity; duplicate targets and overlapping source selections are rejected by the generator.

The import analyzer reuses the existing AST module-specifier collector. It recognizes relative imports, supported renderer/mobile/documentation aliases, root workspace source exports and JS-to-TS source counterparts. Bare third-party packages and other unmodeled package paths are not included in local dependency counts. Computed runtime paths, worker URLs, fixture copying, package scripts and workflow strings need a separate search during implementation. Native and shell files have inventory counts but no import graph.

## Working with the JSON

Change the human-maintained `audit-plan.json` when an architectural label or proposed scope changes. Regenerate and verify with:

```sh
node docs/audits/module-organization/generate-map.mjs
node docs/audits/module-organization/generate-map.mjs --check
```

These commands read tracked paths and working-tree contents; they do not move source files. Add new source files to Git before expecting them in the map. Keep this walkthrough's tables and the assessment in sync when refreshing the snapshot.

Examples with `jq`:

```sh
# Largest directories by production-classified sibling count
jq '.directories | sort_by(-.directProductionJsTs) | .[:20]
  | .[] | {path, directProductionJsTs, directTestSupportJsTs}' \
  docs/audits/module-organization/codebase-map.json

# Exact pilot paths and every outside caller
jq '.proposals[] | select(.id == "ipc-linear")
  | {moves, callers: .dependencyBoundary.externalImporterFiles}' \
  docs/audits/module-organization/codebase-map.json

# Domain discovery signals in the flat shared root
jq '.prefixFamilies[] | select(.directory == "src/shared")' \
  docs/audits/module-organization/codebase-map.json
```

| Field                       | How to use it                                                                     |
| --------------------------- | --------------------------------------------------------------------------------- |
| `snapshot`                  | Check commit, corpus hash, coverage and missing tracked paths.                    |
| `methodology`               | Read count definitions and analysis limits before interpreting a metric.          |
| `areas`                     | Locate system responsibilities; counts are mutually exclusive.                    |
| `featureTrails`             | Trace one product feature across execution layers.                                |
| `directories`               | Find oversized sibling lists and inspect production/test breakdowns.              |
| `prefixFamilies`            | Discover repeated domain names; overlapping and not additive.                     |
| `productionAreaImportEdges` | See observed direct dependencies between architectural areas.                     |
| `importAnalysis`            | Inspect unresolved inputs and any parser failures.                                |
| `proposals`                 | Review priority, risk, current target contents, callers and exact proposed moves. |

---

### Audits Module Organization TASKS (`docs/audits/module-organization/TASKS.md`)

# Orca module organization task tracker

Start with the small, quieter provider and backend areas, merge each PR, and build the next change on the merged result. Keep the session runtime, broadly shared contracts and terminal lifecycle work later in the queue. This tracker is the execution order; it supersedes the preliminary phase ordering in the [assessment](README.md) and [codebase map](CODEBASE-MAP.md).

The full plan accounts for all **2,008 files in the original 23 scopes**: **2,004 proposed relocations and four forwarding-module removals**. It breaks them into **88 provisional code PR batches**, with seven checkpoints and nine discovery tasks covering all 66 directories with more than 100 direct JS/TS files. These are review-sized planning units, not a PR quota or a promise to finish everything in a few days. Caller fanout and live development may require further splits; adjacent tasks can combine when they form one responsibility and remain easy to review. Record that adjustment before implementation and preserve file coverage.

Implementation has not started; G00 documentation publication is in progress. Every completion checkbox below is open. [execution-tracker.json](execution-tracker.json) is the authoritative structured tracker: it includes each batch's file actions, activity evidence, validation profile, export review, workflow predecessor, status, owner, PR URL, merge SHA and check records. Keep this checklist and the JSON in sync in each PR. Stable task IDs are local planning IDs, not GitHub PR numbers; never reuse an ID for unrelated work.

## Start here

- [ ] **G00 — Save and merge the documentation PR.** Include the original report, data, generator, this tracker and the narrow `.gitignore` allowlist. This makes the multi-day plan available on main.
- [ ] **PR00 — Linear IPC, seven files.** Move the six implementation modules and their existing test into `src/main/ipc/linear/`; import `linear/register-handlers` directly from registration composition. No directory-index resolution and no forwarding file at the old path.
- [ ] **PR01 — Jira IPC, ten files.** Apply the same pattern to Jira, preserving cancellation, site scope, channel names and handler registration order.
- [ ] Continue through the quiet-area queue, then complete **G01** before taking on broader changes.

Open one code-move PR at a time by default. Do not spend several days preparing a stack that will conflict with the rest of the team. Each merged PR should stand on its own, pass required checks and leave direct imports in their final form.

## How quieter work was selected

The bounded sample is the latest **1,000 first-parent commits at the audited checkout**, spanning **October 16–21, 2026**. It measures development edits, which predict merge conflict exposure; it does not measure product usage. It does not include unmerged work or other branches. Zero edits in this sample is a prioritization clue, not proof of an inactive feature.

| Scope                    | Commits editing scope | Commits editing outside callers |
| ------------------------ | --------------------: | ------------------------------: |
| `ipc-linear`             |                     1 |                               5 |
| `ipc-jira`               |                     1 |                               5 |
| `jira-attachments`       |                     0 |                               4 |
| `linear-issue-context`   |                     1 |                               2 |
| `settings-repository`    |                     1 |                               2 |
| `ephemeral-vm`           |                     3 |                               1 |
| `browser-cookie`         |                     0 |                               3 |
| `tooling-mobile-web`     |                     5 |                               6 |
| `cloud-relay-assignment` |                     3 |                              12 |
| `task-page-leftovers`    |                    12 |                               7 |
| `ipc-filesystem`         |                    14 |                              38 |
| `store-agent-status`     |                     9 |                              20 |
| `renderer-session-rules` |                    14 |                              59 |
| `codex-app-server`       |                     8 |                              29 |
| `claude-structured`      |                    19 |                              36 |
| `shared-jira`            |                     2 |                              30 |
| `shared-remote-runtime`  |                     8 |                             160 |
| `shared-native-chat`     |                    23 |                             119 |
| `shared-session`         |                    48 |                             267 |
| `mobile-native-chat`     |                    32 |                              27 |
| `runtime-session`        |                    32 |                              91 |
| `ipc-pty`                |                    20 |                              15 |
| `persistence-leftovers`  |                    31 |                             118 |

Caller activity uses the audit's entire outside-caller set for the scope, including tests; it is deliberately broader than the files any one sub-batch will edit. Production caller counts in the JSON are also scope-wide, not per-batch estimates. Refresh actual callers and their activity before implementation.

IPC goes first because its scopes are small and each has one outside production-classified caller. Jira attachments have no sampled edits, but image authorization deserves a focused review. Browser cookies are also quiet, yet touch credential and platform behavior, so they follow the first pilots. High-fanout shared contracts and busy session code come later. The sequence balances edit traffic, caller fanout and operational risk; sorting by a single edit count would miss those differences.

Before every new PR, inspect current main and available active-work information. If another change is touching this scope or its required callers, select the next quieter eligible task and record the revised order. `sequenceAfter` encodes the default workflow, not a proven code dependency. Resolve actual technical dependencies during preflight and record them in `technicalDependencies` before resequencing.

## Rules for every PR

**No barrels, including temporary ones.** No new `index.ts` aggregation, `export *` convenience surfaces, named re-export aggregators, old-path forwarding stubs, or folder imports that rely on an index. A named entry such as `register-handlers.ts` should implement registration; other callers import the concrete module that owns the behavior or type.

The source export review identified **52 production-classified modules with local forwarding exports**, including exports of imported bindings. Four are pure forwarding modules. Their tasks explicitly remove them and change consumers to the real owners:

| Forwarding module to remove                                         | Import the concrete owner                                                                                                               |
| ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `src/main/ephemeral-vm-recipe-runner.ts`                            | `src/shared/ephemeral-vm-recipe-runner.ts`                                                                                              |
| `src/renderer/src/components/task-page-github-task-kind.ts`         | `src/shared/task-github-search.ts`                                                                                                      |
| `mobile/src/session/mobile-native-chat-image-transcript-markers.ts` | `src/shared/native-chat-image-transcript-markers.ts`                                                                                    |
| `src/main/persistence.ts`                                           | The existing `persistence/loading-store/` and `persistence/applying-settings/` implementation files appropriate to each imported symbol |

For the other flagged modules, retain their implementation and remove convenience forwarding exports as their consumers switch to direct owners. Each task lists the relevant existing exports in `existingLocalReexports`. Review actual bindings, mock behavior and cycles; a path-only search cannot distinguish a type's owner from a forwarding API. Required external package export contracts remain explicit package boundaries, not a reason to create internal barrels.

Each PR follows this checklist:

1. **Refresh the scope.** Set owner and status; verify the source file list, existing target contents, test ownership, recent activity and current outside callers. Read applicable platform, Git and subtree instructions.
2. **Settle the destination.** Confirm concrete responsibility folders and final descriptive basenames. The JSON gives path proposals; its larger batches need semantic review. Avoid a second broad rename pass just to shorten every name.
3. **Bound the review.** Aim for 5–20 production modules. More than 25 production modules, 60 moved files or 100 caller/path-reference edits triggers a split review. Keep production/test pairs and platform twins together; assign cross-cutting integration tests by what they exercise rather than their prefix alone. Batch file counts do not include caller edits.
4. **Make the atomic change.** Move code and relevant tests, remove designated forwarding modules, and update all imports, mocks, runtime path strings, fixture-copying code, build entries, scripts and CI references in the same PR. Never bridge two batches with re-export shims.
5. **Preserve behavior.** Keep wire methods, IPC channels, stored formats, process output names, lifecycle order and lazy-loading boundaries stable. Keep API redesign, performance changes and unrelated formatting out of the move.
6. **Verify and review.** Run the affected existing tests, typechecks, formatting, changed-code quality and applicable CI gates. Apply the [refactor-audit skill](../../../.agents/skills/refactor-audit/SKILL.md): compare against the merge base, check moved bodies, literals, callers and wire names, and obtain an independent sub-agent review before merging. Inspect changed imports/exports for accidental aggregation, cycles and stale path strings. Preserve upstream fixes when rebasing and run the skill's merge-validation gate. Record commands, review findings and results.
7. **Merge and reconcile.** Record PR URL, merge SHA and actual file actions. Set status to `merged`, then `verified` after checking merged main. Update the map selectors, inventory, report and tracker at scope checkpoints so completed work does not remain a pending proposal. Start the next PR from updated main.

Do not mark a task complete when its PR opens or local tests pass. Preserve its full scope when splitting it; create child tasks with stable IDs and exact file ownership. If a file should stay in place, record a reviewed reason rather than silently dropping it. A failed move can be reverted as an atomic PR; restoring a barrel is not the recovery plan.

## Sequential PR checklist

`Files P/T` means production-classified files / test-support files in this batch. Production-classified includes tooling and declarations; the four removal tasks count their forwarding files in that column. `Edits` counts sample commits touching the batch's original paths. It excludes edits to its outside callers. PR scope also includes necessary caller and path-reference updates, which must be measured at preflight.

### Wave 1 Quiet provider backend and settings work

First merge milestone: PR00 and PR01. Continue one at a time through the remaining quiet scopes; G01 checks what the pilot taught us.

| Done | Task     | Proposed PR or checkpoint                                       | Files P/T | Edits |
| ---- | -------- | --------------------------------------------------------------- | --------: | ----: |
| [ ]  | **PR00** | Group Linear IPC handlers with direct registration imports      |       6/1 |     1 |
| [ ]  | **PR01** | Group Jira IPC handlers with direct registration imports        |       6/4 |     1 |
| [ ]  | **PR02** | Group Jira attachment discovery, images and cache ownership     |       9/6 |     0 |
| [ ]  | **PR03** | Group Linear issue-context reads and replace forwarding imports |      10/7 |     1 |
| [ ]  | **PR04** | Group repository identity and icon settings                     |       8/5 |     0 |
| [ ]  | **PR05** | Group repository hooks and host setup settings                  |      15/7 |     1 |
| [ ]  | **PR06** | Group source-control AI repository settings                     |       9/3 |     0 |
| [ ]  | **PR07** | Move repository settings composition and remaining fields       |       8/5 |     0 |
| [ ]  | **PR08** | Group ephemeral VM provisioning and cleanup                     |      11/6 |     3 |
| [ ]  | **G01**  | Review the first merged moves before expanding scope            |         — |     — |

### Wave 2 Quiet platform and tool subsystems

Cookie files had no edits in the sample, but their platform and credential ownership needs review. Tooling and cloud work follow; neither entails a deployment.

| Done | Task     | Proposed PR or checkpoint                                  | Files P/T | Edits |
| ---- | -------- | ---------------------------------------------------------- | --------: | ----: |
| [ ]  | **PR09** | Group Chromium cookie scanning and import steps            |       5/0 |     0 |
| [ ]  | **PR10** | Group cookie source detection, parsers and decryption      |      10/4 |     0 |
| [ ]  | **PR11** | Group cookie import orchestration, state and cleanup       |     10/28 |     0 |
| [ ]  | **PR12** | Group mobile web input and platform seam tools             |       5/3 |     0 |
| [ ]  | **PR13** | Group mobile web preview and evidence tools                |      12/3 |     1 |
| [ ]  | **PR14** | Group mobile web route and bundle analysis tools           |      7/45 |     5 |
| [ ]  | **PR15** | Group relay assignment modules and existing tests          |      8/20 |     3 |
| [ ]  | **G02**  | Reconcile quiet-area PRs and approve the busier-area queue |         — |     — |

### Wave 3 Desktop features and provider lifecycle

These are busier scopes. Keep provider and subsystem batches separate and refresh caller lists before every PR.

| Done | Task     | Proposed PR or checkpoint                                             | Files P/T | Edits |
| ---- | -------- | --------------------------------------------------------------------- | --------: | ----: |
| [ ]  | **PR16** | Finish the existing GitLab Task Page folder                           |       1/2 |     2 |
| [ ]  | **PR17** | Finish the existing Linear Task Page folder                           |      22/5 |     3 |
| [ ]  | **PR18** | Group Jira Task Page board behavior                                   |       6/5 |     0 |
| [ ]  | **PR19** | Group Jira Task Page creation and summary flows                       |       6/2 |     0 |
| [ ]  | **PR20** | Move Jira Task Page filters, issue lists and creation flows           |     24/26 |     0 |
| [ ]  | **PR21** | Group GitHub Task Page mutation ownership                             |      19/7 |     0 |
| [ ]  | **PR22** | Move GitHub Task Page reads, refresh and composition                  |      19/5 |     3 |
| [ ]  | **PR23** | Move cross-provider Task Page hooks and composition                   |      13/1 |     4 |
| [ ]  | **PR24** | Move cross-provider Task Page composition and pure view rules         |     15/15 |     5 |
| [ ]  | **PR25** | Consolidate local filesystem guards, import and search                |     11/13 |     2 |
| [ ]  | **PR26** | Group Parcel watcher child acquisition and termination                |      12/4 |     0 |
| [ ]  | **PR27** | Group the Parcel watcher process and supervisor                       |     19/16 |     2 |
| [ ]  | **PR28** | Group remote and WSL filesystem watchers                              |       9/5 |     5 |
| [ ]  | **PR29** | Consolidate local watcher lifecycle and events                        |     15/17 |     7 |
| [ ]  | **PR30** | Move filesystem request registration and integration suites           |      2/18 |     5 |
| [ ]  | **PR31** | Group agent-status records, contracts and reducers                    |      13/2 |     5 |
| [ ]  | **PR32** | Move agent-status actions, scheduling and slice composition           |     18/22 |     8 |
| [ ]  | **PR33** | Group renderer session launch records and publication                 |      12/4 |     2 |
| [ ]  | **PR34** | Group renderer session launch admission and requests                  |     24/15 |    12 |
| [ ]  | **PR35** | Group renderer session tab activation and cancellation                |       2/2 |     5 |
| [ ]  | **PR36** | Group Codex app-server protocol and handshake leaves                  |       9/2 |     3 |
| [ ]  | **PR37** | Move Codex app-server connection, session and teardown                |       5/9 |     8 |
| [ ]  | **PR38** | Group structured Claude launch options and model selection            |     16/15 |     9 |
| [ ]  | **PR39** | Group structured Claude journal translation and prompts               |      8/11 |     5 |
| [ ]  | **PR40** | Group structured Claude acquisition and session startup               |       9/3 |    10 |
| [ ]  | **PR41** | Move structured Claude session and dispatch lifecycle                 |     13/44 |    15 |
| [ ]  | **G03**  | Review desktop feature and process boundaries before shared contracts |         — |     — |

### Wave 4 Shared contracts and all consumers

Move each leaf group with all consumer updates atomically. Split further by concrete owner if caller churn exceeds the review-size prompt. Preserve mixed-version compatibility.

| Done | Task     | Proposed PR or checkpoint                                       | Files P/T | Edits |
| ---- | -------- | --------------------------------------------------------------- | --------: | ----: |
| [ ]  | **PR42** | Group shared Jira URL, query and status rules                   |       9/5 |     0 |
| [ ]  | **PR43** | Group shared Jira attachment and stream contracts               |       3/0 |     0 |
| [ ]  | **PR44** | Move shared Jira types, capabilities and saved-filter contracts |       9/5 |     2 |
| [ ]  | **PR45** | Group remote runtime requests and subscriptions                 |     18/12 |     4 |
| [ ]  | **PR46** | Group remote runtime shared-control lifecycle                   |      21/7 |     4 |
| [ ]  | **PR47** | Move remote runtime connection, handshake and socket rules      |     13/10 |     5 |
| [ ]  | **PR48** | Group shared native-chat tool, edit and diff rules              |     23/19 |     1 |
| [ ]  | **PR49** | Group shared native-chat prompts and session options            |     19/12 |     9 |
| [ ]  | **PR50** | Group shared native-chat transcript and row rules               |     24/19 |     9 |
| [ ]  | **PR51** | Group shared native-chat command, stream and provider rules     |      10/7 |     3 |
| [ ]  | **PR52** | Move shared native-chat command, stream and root contracts      |     22/16 |    13 |
| [ ]  | **PR53** | Group shared agent-session options and catalogs                 |     25/11 |     7 |
| [ ]  | **PR54** | Group shared agent-session journal contracts                    |       9/4 |     3 |
| [ ]  | **PR55** | Group shared agent-session requests, queue and send contracts   |      16/8 |    11 |
| [ ]  | **PR56** | Group shared agent-session failure and refusal rules            |      23/9 |    10 |
| [ ]  | **PR57** | Group shared agent-session records and stored forms             |       4/4 |     3 |
| [ ]  | **PR58** | Group shared agent-session lifecycle and provider identity      |     23/13 |    10 |
| [ ]  | **PR59** | Group shared agent-session projection and turn rules            |     24/15 |     8 |
| [ ]  | **PR60** | Group shared agent-session capabilities and availability        |      12/1 |    10 |
| [ ]  | **PR61** | Group shared agent-session context, approvals and questions     |       9/9 |     4 |
| [ ]  | **PR62** | Group shared agent-session conversation and message identity    |       7/3 |     3 |
| [ ]  | **PR63** | Move remaining agent-session capability and root contracts      |      19/9 |    15 |
| [ ]  | **G04**  | Verify shared consumers and mixed-version compatibility         |         — |     — |

### Wave 5 Mobile runtime terminal and persistence

These areas have substantial lifecycle or active-work risk. Revisit the order using current main. The persistence forwarding entry is removed, not moved.

| Done | Task     | Proposed PR or checkpoint                                        | Files P/T | Edits |
| ---- | -------- | ---------------------------------------------------------------- | --------: | ----: |
| [ ]  | **PR64** | Group mobile native-chat visual documents and media rendering    |      11/5 |     5 |
| [ ]  | **PR65** | Group mobile native-chat attachment and image ownership          |      10/5 |     2 |
| [ ]  | **PR66** | Group mobile native-chat composer, attachments and drafts        |     19/12 |     9 |
| [ ]  | **PR67** | Group mobile native-chat session options and queued editing      |      12/7 |     9 |
| [ ]  | **PR68** | Group mobile native-chat asks, permissions and prompts           |     20/15 |     3 |
| [ ]  | **PR69** | Group mobile native-chat tool, task and subagent rows            |       6/4 |     4 |
| [ ]  | **PR70** | Group mobile native-chat page and scroll ownership               |      17/6 |     3 |
| [ ]  | **PR71** | Group mobile native-chat message text and prose presentation     |     12/11 |     8 |
| [ ]  | **PR72** | Group mobile native-chat row and turn presentation               |     17/11 |    11 |
| [ ]  | **PR73** | Group mobile native-chat send state and terminal-stream coupling |     16/15 |     4 |
| [ ]  | **PR74** | Move mobile native-chat controller, send and session composition |     25/30 |    20 |
| [ ]  | **PR75** | Group runtime session records and durable store                  |     17/11 |     9 |
| [ ]  | **PR76** | Group runtime session claims, leases and process identity        |     15/15 |     7 |
| [ ]  | **PR77** | Group runtime session teardown and tab ownership                 |       8/5 |     2 |
| [ ]  | **PR78** | Move runtime session dispatch, teardown and tab lifecycle        |     19/39 |    27 |
| [ ]  | **PR79** | Move PTY IPC test harness and provider fixtures                  |      4/10 |     9 |
| [ ]  | **PR80** | Group PTY delivery, backpressure and drain behavior              |      4/15 |     1 |
| [ ]  | **PR81** | Consolidate PTY ownership, spawning and management suites        |      4/53 |    18 |
| [ ]  | **PR82** | Remove the persistence forwarding entry and import real owners   |       1/0 |     0 |
| [ ]  | **PR83** | Colocate persistence orcad migration tests and fixtures          |      0/10 |     8 |
| [ ]  | **PR84** | Colocate persistence terminal topology and lease tests           |      0/22 |    10 |
| [ ]  | **PR85** | Colocate persistence workspace and repository tests              |      0/20 |    14 |
| [ ]  | **PR86** | Colocate persistence settings and automation tests               |      0/15 |     7 |
| [ ]  | **PR87** | Colocate remaining persistence loading and security tests        |      0/17 |     7 |
| [ ]  | **G05**  | Verify lifecycle moves before the remaining-directory review     |         — |     — |

### Wave 6 Remaining directory dispositions

These are discovery tasks, not bulk move PRs. Each assigned directory needs an explicit keep, organize or deeper-design-review decision. Add exact implementation tasks for organize decisions.

| Done | Task    | Work                                                               | Coverage       |
| ---- | ------- | ------------------------------------------------------------------ | -------------- |
| [ ]  | **D00** | Review remaining terminal UI and daemon organization               | 3 directories  |
| [ ]  | **D01** | Review remaining desktop native-chat and provider organization     | 10 directories |
| [ ]  | **D02** | Review sidebar, tab bar and remaining workspace UI organization    | 5 directories  |
| [ ]  | **D03** | Review editor and remaining settings organization                  | 2 directories  |
| [ ]  | **D04** | Review remaining renderer libraries, runtime, state and components | 7 directories  |
| [ ]  | **D05** | Review mobile transport, tasks, components and session leftovers   | 7 directories  |
| [ ]  | **D06** | Review remaining host services, IPC and runtime organization       | 21 directories |
| [ ]  | **D07** | Review remaining shared contracts and root composition             | 6 directories  |
| [ ]  | **D08** | Review remaining tooling, cloud and integration-test organization  | 5 directories  |

### Final verification

Completion includes discovery-created work; it does not stop at the initial 23 scopes.

| Done | Task    | Work                                                         | Coverage                                     |
| ---- | ------- | ------------------------------------------------------------ | -------------------------------------------- |
| [ ]  | **G06** | Close the organization program with a refreshed codebase map | All completed scopes and directory decisions |

The file groups above are planning partitions, not proof of architectural independence. The JSON has exact source actions and proposed targets for every original file. Later tasks require boundary and caller review before execution; the currently small source counts of a shared-contract task can still conceal hundreds of consumer edits.

## Validation and merge evidence

Set `ORCA_BACKGROUND_LAUNCH=1` for all tests and agent-launched applications. UI checks, when needed, use the repository's Electron skill and hidden Playwright CDP; platform-native checks run in an isolated environment or CI. File moves alone do not require inventing new behavioral tests.

| Change                    | Required local validation to select from current callers                                                                                                              |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Desktop backend or IPC    | `pnpm tc:node`; existing moved tests and affected consumer/registration suites with `pnpm test`                                                                       |
| Renderer                  | `pnpm tc:web`; affected existing component, hook and store tests; node typecheck if its config explicitly includes a moved path                                       |
| Shared contracts          | `pnpm tc`; mobile typecheck and test typecheck; affected shared, desktop, CLI and mobile suites; cloud checks if actual cloud consumers are affected                  |
| Mobile                    | `pnpm --dir mobile typecheck`; `pnpm --dir mobile typecheck:tests`; affected suites through mobile's own test command                                                 |
| Filesystem workers or PTY | Desktop typecheck and lifecycle suites; rebuild affected entry sources and verify launch paths and stable output names; applicable Windows/WSL and macOS/Linux checks |
| Tooling                   | Existing tool/fixture/CI contract tests; affected generator verification; script-relative paths and generated output checks                                           |
| Cloud assignment          | Cloud workspace typecheck and tests; no deploy is part of this organization task                                                                                      |

Every PR also runs changed-code quality, changed-file formatting, `git diff --check` and its required CI checks. Applicable import-boundary and ratchet gates must pass without adding baseline exemptions. Preserve remote/local execution, mobile consumers, mixed-version compatibility and folder workspaces as well as git worktrees.

Fill in commands from the task's `validationProfile`; angle-bracket arguments in JSON are templates, not runnable literal paths. Attach check evidence like:

```json
{
  "status": "verified",
  "prUrl": "actual merged PR URL",
  "mergeCommit": "actual merge SHA",
  "checks": [
    { "command": "actual command with exact paths", "result": "passed", "commit": "checked SHA" }
  ],
  "notes": "Actual moves, direct-owner import changes and any approved scope refinement"
}
```

Keep the JSON as a durable record; do not regenerate it from the original snapshot and overwrite progress. The original `generate-map.mjs` updates `codebase-map.json` only. Retire or update completed selectors in `audit-plan.json` before rerunning it, because an empty selector deliberately fails. If early path moves change later callers, update later file actions and checks against current main without changing the baseline coverage record.

## Completion means a reconciled codebase

The program closes after the original scopes and any discovery-created implementation tasks are verified, every large directory has a recorded disposition, obsolete source paths and convenience forwarding exports are removed from the changed scopes, and the refreshed map reflects merged main. A remaining design-review decision is open work, not a completed cleanup.

Publish the merged PR list and before/after directory counts. Evaluate whether a contributor can find a responsibility and its concrete owners; file moves alone do not establish performance gains or prove every module is well designed.

---

### Bug Reproductions Native Chat Tool Pairing Mobile Row Identity (`docs/bug-reproductions/native-chat-tool-pairing/mobile-row-identity.md`)

# Keep mobile tool details with their row

Opening a tool result, then removing an earlier command, could move the expanded detail to a different command. Mobile used the row position as its React identity.

Mobile now shares desktop's provider-ID and duplicate-occurrence identity. Named standalone results use a separate result-ID namespace, preserving their detail across copied history refreshes without transferring it to a command. Anonymous rows use a weak object identity, avoiding large input serialization and resetting detail when a replacement cannot be identified safely. Desktop identities and matching rules are preserved.

Four mobile component regressions cover prefix removal, reordering, changed input/results, duplicate IDs, orphan replacement and copied named-result refreshes. Two shared identity tests cover named duplicates and avoiding serialization of anonymous inputs. The current-main integration check also passed all 13 tests across the identity, result-availability and paging suites, including both host-scope variants of copied journal refreshes. The earlier rendered receipt below captures the command-row fix before this additional result-row guard.

The rendered browser check uses production React Native Web components, shipped bundle options and the shell CSP. Both unique-ID and duplicate-ID scenarios reproduce the incorrect expansion before the fix and retain the correct detail after removal and reordering. It records source and output hashes in [the receipt](./mobile-row-identity-validation.json). This is mobile component proof, not a native-phone or SSH latency test.

| Before                                                                                                                | After                                                                                                               |
| --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| [Different command becomes expanded](https://github.com/user-attachments/assets/966c9d20-650e-4651-96da-1e51f121b95c) | [Selected result remains expanded](https://github.com/user-attachments/assets/358669cd-7a49-4750-a76b-9897d3eb6323) |

Missing historical IDs cannot be recovered. Removing or reordering earlier occurrences with the same provider ID remains ambiguous. Replacement anonymous rows close their detail; results stay visible. Weak identities do not retain removed blocks. No public wire fields or styling changed.

---

### Bug Reproductions Opencode2 Form Created Kinds Subagent Panel Screen (`docs/bug-reproductions/opencode2-form-created-kinds/subagent-panel-screen.txt`)

• Affirmative response          +
  ┃  Use the task tool three times in one go to launch three subagents in parallel. Give each one this exact
  ┃  instruction: run the bash command `sleep 240` and then report done. Do not wait for them to finish before          Affirmative response
  ┃  replying to me.
  ┃                                                                                                                     Context
                                                                                                                        8,907 tokens
     ✓ General Subagent — Sleep 240 seconds  Background                                                                 1% used
                                                                                                                        $0.04 spent
     ✓ General Subagent — Sleep 240 seconds  Background

     ✓ General Subagent — Sleep 240 seconds  Background

     I have launched three subagents, each running sleep 240 in the background. You will be notified when they
     complete.

     Build · Gemini 2.5 Flash · 2.2s · 117.5 tok/s

     ↳ General finished · Sleep 240 seconds

     One of the subagents has reported "Done." I'm still waiting for the other two subagents to complete their sleep
     240 command.

     Build · Gemini 2.5 Flash · 1.1s · 58.7 tok/s

     ↳ General finished · Sleep 240 seconds

     Another subagent has finished. One subagent is still running its sleep 240 command.

     Build · Gemini 2.5 Flash · 1.9s · 59.3 tok/s

     ↳ General finished · Sleep 240 seconds

     All three subagents have now completed their tasks.

     Build · Gemini 2.5 Flash · 478ms · 21.2 tok/s

  ┃
  ┃  Subagents  Shell  Terminals                                                                               esc
  ┃
  ┃  No active subagents
  ┃
  ┃
  ┃
  ┃
  ┃
  ┃  show inactive ctrl+a  tabs ←/→
  ┃                                                                                                                     /private/tmp/oc2-evidence

---

### Bug Reproductions Usage Scan Append Codex Reader (`docs/bug-reproductions/usage-scan-append/codex-reader.md`)

# Verified transcript reads and Codex usage memory

Codex could verify one file generation, reopen a replacement, and publish old
totals under the replacement's metadata. A short parse could also publish a
partial result as a complete file. Unchanged refreshes then reused those totals.

The shared JSONL reader now pins verification, parsing and checkpoint creation
to one descriptor and the initial file size. It proves complete range consumption
and validates generation and byte evidence before publishing. Stable short reads
propagate errors; only evidence that the file changed permits a retry. Each failed
attempt closes its handle. A successful attempt closes before ownership publication
and aggregate work. Metadata-less caches reparse once.

Codex folds owned records into the existing session/day accumulator while reading.
It commits ownership after validation, and lets unchanged forks reclaim keys that
an owner actually loses. Deferred records still advance cumulative token context.
This removes intermediate event arrays while preserving all history and results.

## Reproduce and verify

```sh
ORCA_BACKGROUND_LAUNCH=1 pnpm test src/main/codex-usage src/main/usage
ORCA_BACKGROUND_LAUNCH=1 node config/scripts/codex-usage-parser-memory-benchmark.mjs --baseline-ref 51e7181850b022bae2d41091fe1229accef4bd50 --events 100000 --pairs 2 --sample-ms 35 --output docs/bug-reproductions/usage-scan-append/codex-parser-memory.json
```

The original [memory comparison](./codex-parser-memory.json) bundles the full parser
dependency graph from the named baseline and current worktree. Two counterbalanced
rounds use fresh Node workers and record source/tool hashes and raw forced-GC
snapshots. Every arm preserves identical projections, owned keys and token totals.

| Owned records             | Baseline sampled retained heap | Current sampled retained heap |
| ------------------------- | -----------------------------: | ----------------------------: |
| 100,000                   |                  44.7–45.0 MiB |                   8.0–9.1 MiB |
| 10,000, with 90% deferred |                  3.69–3.75 MiB |                 1.23–1.25 MiB |

Final result retention stays approximately 6.49 → 6.51 MiB for all owned records:
the result still contains the full history. Timer samples miss synchronous peaks,
and forced GC affects timing; these figures measure sampled retained heap rather
than total process memory or speed.

Real-file regressions cover replacement, truncation, rewrite, suffix resume,
partial tails, stable short reads, failed ownership publication, old-cache upgrade
and handle cleanup. Local checks used macOS; CI covers Linux and Windows.
The shared aggregation compatibility suites also passed for Muse and OpenCode.

Head and committed-boundary digests support the append-only contract. They cannot
prove arbitrary middle-only rewrites during growth. Small changed files receive
a full verification reread; unchanged files read no transcript bytes.

The companion Claude PR adopts this reader. The original integrated validation
and benchmarks were recorded from a combined worktree; production source remains
identical when split into PRs.

## Adversarial hardening

Tiny files now use bounded descriptor reads and exact byte comparison for both
passes. This avoids creating a stream without reducing verification. Exact bigint
device/inode values prevent large Windows file IDs from colliding after numeric
rounding; unsafe persisted offsets fall back to a complete parse.

Discovery distinguishes a directory that disappeared from one that cannot be
read. Missing children leave readable siblings available. Permission and I/O
errors abort the refresh, retaining the previous verified totals and showing the
existing error state rather than publishing incomplete totals.

A [counterbalanced ten-round comparison](./shared-reader-small-files.json) of the published reader and hardened
reader over 1,000 tiny files measured median scanner cold time 85.024 → 80.529 ms
and changed time 91.512 → 84.092 ms. Both arms read the same bytes twice; streams
fell from 1,000 to zero. Unchanged scans read zero bytes in both arms and measured
7.808 → 8.241 ms. These page-cache-warm macOS timings are observations, not CI
thresholds or a claim about the original pre-verification reader.

Regression contracts exercise short reads, exact comparison, unsafe offsets,
large file identities, fractional timestamps, and discovery failures. The
original retained-heap artifact above predates this hardening; its source hashes
describe that earlier measurement, rather than the final reader.

## Further risk reduction

The [refreshed memory comparison](./codex-parser-memory-final.json) captures the
final parser and reader source. Its two counterbalanced rounds use eight fresh
Node workers and preserve exact output parity with the original baseline.

| Owned records             | Original sampled retained heap | Final sampled retained heap |
| ------------------------- | -----------------------------: | --------------------------: |
| 100,000                   |                43.87–44.54 MiB |               8.07–8.30 MiB |
| 10,000, with 90% deferred |                  4.53–4.58 MiB |             1.231–1.235 MiB |

The median reductions are approximately 81.5% and 72.9%. Final output retention
stays similar or slightly higher: 6.49 → 6.53–6.55 MiB for owned events, and
0.975 → 1.005–1.014 MiB for deferred events. These are forced-GC samples, not
absolute peaks, full-app memory, or speed measurements.

Real-file lifetime contracts cover both tiny and streamed transcripts. They
prove the descriptor is closed before an ownership callback replaces the path,
the verified old projection keeps its captured metadata, and the next parse
observes the replacement. Failure cases check cleanup when validation fails or
the first close attempt rejects before reaching the native close.

This shortens Windows replacement contention to reading and validation; parsing
can still include asynchronous attribution while the descriptor is pinned.
Ordinary append writes remain compatible. Tiny verification still reads the full
file twice, and bounded append evidence retains its append-only assumption.

---

### Bug Reproductions Usage Scan Append Hardening Summary (`docs/bug-reproductions/usage-scan-append/hardening-summary.md`)

# Usage scan hardening and remaining costs

Claude keeps every historical ownership key and its five independent token maxima so an appended correction can update the original attribution. The final refinement shares unchanged tuple rows during an append, stores scalar or dense columns on disk, and validates source checkpoints on the usage worker. Main retains a separately protected aggregate report. The source/report split and `whenLoaded()` lifecycle came from upstream main; this PR adds the protected codec, source/report authority checks and guarded unchanged-source write reuse to that architecture.

An unchanged source sidecar is retained only after schema 7 and its raw JSON checksum validate, normalization preserves every source, and the scanner returns identical file references in the same order and count. Changes, repairs and legacy upgrades write a new source generation. Report completion and report persistence still run. Raw checksums cover tiny unsigned source records as well as resumable checkpoints; source and report use different checksum domains. Corrupt sources force a cold scan while an independently valid report remains readable. A corrupt report clears its totals and explains the required rebuild. Tracking preferences survive both cases.

Directory discovery distinguishes absent history from unreadable history. Permission and I/O failures reject the provider scan, preserving the previous completed totals and exposing an error. A deleted or genuinely absent directory remains valid absence. The [permission reproduction](./claude-discovery-coverage.json) demonstrates the earlier silent loss of a readable sibling session; deterministic error contracts cover environments where that POSIX permission experiment cannot run.

## Final source and measurement scope

The final artifacts compare published Claude PR commit `8467877ed92aba2ef59737aebf461e9cab52c273`, merged upstream main `b44a5796c1154de84efa25cc09f3dae2b61eb4b5`, and the frozen worktree. The [four-arm byte check](./claude-persisted-bytes-final.json) also includes original commit `51e7181850b022bae2d41091fe1229accef4bd50`. Every bundled project dependency comes from its named commit or the captured worktree snapshot. The [evidence receipt](./claude-resource-evidence-receipt.json) records tool/artifact hashes and verifies the complete source graphs after measurement.

Six permutations balance the three timing arms across six rounds. The fixtures are scanner-cache cold and operating-system-page-cache warm. Production source-cache read/verification/decoding, scanning, source packing/checksumming/durable writes and report serialization/durable writes are measured in their production order. Durable writers include file sync and replacement. Timings exclude worker startup, IPC transport/queueing, telemetry, worktree discovery and rendering. They measure these operations, not whole-app speed. Every appended projection matches a fresh scan and the other arms' session/daily projections.

## Actual persistence and refresh costs

The [byte check](./claude-persisted-bytes-final.json) uses each arm's actual production layout and indentation. Totals include the report plus source sidecar where present.

| Owned keys / maxima | Original full state | Published PR full state | Upstream main report + sources | Final report + sources |
| ------------------- | ------------------: | ----------------------: | -----------------------------: | ---------------------: |
| 16,384 / repeated   |           620,849 B |               768,008 B |                      472,318 B |              538,876 B |
| 100,000 / unique    |         3,883,175 B |             6,327,802 B |                    2,981,812 B |            6,128,046 B |

The final representation is 29.8% smaller than the published PR at 16,384 repeated maxima and 3.2% smaller at 100,000 unique maxima. It is 14.1% and 105.5% larger than aggregate-only upstream main. The unique fixture has two attribution days, so all six columns are dense: one ownership-key list takes 2,977,781 bytes and the five maxima plus routing column take 3,145,041 bytes. There is no second key list to remove. At 16,384 repeated maxima, one correction expands one scalar column to a dense column; its exact values remain retained. No cap, dropped history or assumed correlation between counters hides this cost.

The [16k](./claude-persistence-cost-final-16k.json) and [100k](./claude-persistence-cost-final-100k.json) artifacts include actual source and report persistence. Times below are complete measured-operation medians; individual phase medians need not add to their total median.

| Owned keys / phase          | Published PR | Upstream main |      Final |
| --------------------------- | -----------: | ------------: | ---------: |
| 16,384 repeated / cold      |    72.455 ms |     61.117 ms |  83.160 ms |
| 16,384 repeated / unchanged |    13.106 ms |     21.844 ms |  12.290 ms |
| 16,384 repeated / append    |    28.000 ms |     61.131 ms |  28.876 ms |
| 100,000 unique / cold       |   385.203 ms |    265.264 ms | 390.682 ms |
| 100,000 unique / unchanged  |    34.915 ms |     36.950 ms |  30.662 ms |
| 100,000 unique / append     |   128.631 ms |    267.204 ms |  84.152 ms |

The guarded unchanged path writes zero source bytes in every recorded round. It still reads and validates 471,838 bytes or 6,126,038 bytes of source cache, and writes a 1,515-byte or 2,023-byte report. Dense cold scans remain slower than aggregate-only main. At 16k, the additional durable source/report commit offsets the faster append scanner relative to the published PR: 28.000 to 28.876 ms. At 100k, the final appended scanner takes 36.869 ms versus 105.960 ms in the published PR, and the complete measured append falls to 84.152 ms. These are visible tradeoffs, not universal speed claims.

Transcript I/O and all cache I/O are different quantities. The 100k append reads 25,030 transcript bytes versus main's 47,323,259 bytes, but it also reads the 6,126,038-byte source sidecar. Total logical payload reads are therefore 6,151,068 bytes versus approximately 50,303,147 bytes, not a 99.9% reduction in all refresh traffic. Logical payload counts are not physical device I/O.

## Readiness and remaining main-thread work

The [readiness measurement](./claude-readiness-cost-final.json) uses the actual store lifecycle and prestarted Node workers calling the production splitter. Inactive telemetry/analytics/worktree paths throw if reached. Disabled history remains accessible after `whenLoaded()`, and main retains zero per-source records.

For the one-session, 100k-key fixture, published synchronous loading takes 36.034 ms; upstream main's report-only load takes 0.092 ms and the final protected report takes 0.116 ms. Moving the source graph away from main is inherited upstream behavior, rather than a gain attributable solely to this PR. The final first inline migration takes 94.434 ms to become ready, versus 63.060 ms on upstream main; validation and source publication add worker work. Its initial legacy JSON read/parse still occupies main for an observed 8.752 ms.

Worker unavailability preserves disabled history through a cooperative main fallback. The 100k legacy fixture yields 293 times, with a median maximum observed validation segment of 0.493 ms, but its initial legacy parse still occupies main for 8.574 ms. Those observations are not universal bounds: large metadata records, aggregate merging and sorting can remain synchronous.

A separate 13,869,588-byte report with 13,222 distinct rollups exposes the remaining aggregate-report limit. Trusting validation of the exact worker-returned report text removes a repeated main checksum pass: the observed main segment falls from 21.393 to 16.512 ms. Main still parses the entire report; upstream main shows 16.690 ms for that parse path. Final readiness is 44.394 ms versus upstream's 39.592 ms, and main serialization plus checksum takes a median 20.628 ms versus upstream serialization's 16.183 ms. If the worker fails, report parse plus verification occupies main for an observed 22.326 ms. This diagnostic preserves an independently valid report with absent source cache and does not claim a generated transcript-corpus parity result.

## Earlier measurements and persistent limits

[comparison.jsonl](./comparison.jsonl), [hardening-comparison.jsonl](./hardening-comparison.jsonl), [claude-hardening-cost-16k.json](./claude-hardening-cost-16k.json), [claude-hardening-cost-100k.json](./claude-hardening-cost-100k.json) and [claude-cache-representation-final.json](./claude-cache-representation-final.json) retain earlier source/layout measurements. Their inline-cache loading costs predate the worker-owned protected sidecar. Captured hashes remain unchanged even where later scripts differ. Final costs come from the separate final artifacts above.

The earlier canonical reader comparison documents the verification tradeoff: one thousand tiny changed files are read twice, while unchanged transcripts read zero bytes. Cold scanner time rose from 68.095 to 84.020 ms in that earlier run. Bounded head/boundary evidence remains an append-only check, not proof of an arbitrary unsampled middle rewrite that also grows the file. Same-length changes, replacement, truncation and invalid checkpoints fall back to full parsing. No automatic whole-corpus audit or new history-rebuild UI was added.

Schema 7 fences older builds from interpreting protected columns as legacy inline state. Schema 6 remains an explicit migration path. Downgrading to an older build requires a cold rebuild before cached totals reappear; tracking preference remains preserved. Source/report write failures can cause a later colder scan, and one unreadable transcript still prevents a complete provider refresh. Checksum validation detects accidental inconsistency; it is not a signature against deliberate resealing.

All per-source work belongs to the execution host. Public snapshots carry aggregate reports, rather than checkpoint arrays. The measurements do not establish Windows, SSH or whole-app latency improvements, and there are no machine-dependent timing gates.

## Reproduce

Run timing tools while builds, tests, apps and other benchmarks are idle:

```sh
ORCA_BACKGROUND_LAUNCH=1 node config/scripts/claude-usage-persistence-cost-benchmark.mjs --usage-keys 16384 --maxima-pattern repeated --rounds 6
ORCA_BACKGROUND_LAUNCH=1 node config/scripts/claude-usage-persistence-cost-benchmark.mjs --usage-keys 100000 --maxima-pattern unique --rounds 6
ORCA_BACKGROUND_LAUNCH=1 node config/scripts/claude-usage-readiness-cost-benchmark.mjs --usage-keys 100000 --large-report-mib 13 --rounds 6
ORCA_BACKGROUND_LAUNCH=1 node config/scripts/claude-usage-persisted-bytes-check.mjs
```

Each accepts `--output`. Timing drivers accept `--published-ref` and `--main-ref`; defaults are the named immutable commits above. Equivalent environment-variable launchers work on Windows. The byte check uses fixed immutable refs and makes no timing claim.

---

### Bug Reproductions Usage Scan Append Source Compression (`docs/bug-reproductions/usage-scan-append/source-compression.md`)

# Smaller saved Claude usage data

The previous PR used more disk to remember previously counted usage. This follow-up losslessly compresses only the worker-owned source cache with asynchronous gzip level 1. The displayed report, every historical total, integrity checks and atomic file-write sequence stay intact.

| Test history                      | Main disk | Previous PR disk | Compressed disk | Main update | Previous PR update | Compressed update |
| --------------------------------- | --------: | ---------------: | --------------: | ----------: | -----------------: | ----------------: |
| 16,384 repeated entries           |    472 KB |           539 KB |           84 KB |       60 ms |              28 ms |             30 ms |
| 100,000 distinct entries          |   2.98 MB |          6.13 MB |         1.60 MB |      260 ms |              83 ms |             97 ms |
| 100,000 less-compressible entries |   6.80 MB |         10.46 MB |         5.39 MB |      276 ms |             103 ms |            224 ms |

Decimal sizes include the saved source cache and report. Each row uses six counterbalanced rounds of the complete measured processing/persistence path, checked against a full recount. Baselines are main `b44a5796` and published PR `4102ea91`. These are fixture measurements, not whole-app latency or a universal disk-size guarantee.

Compression trades CPU for space. Full rebuilds measured 82 vs main's 62 ms, 410 vs 261 ms, and 533 vs 281 ms in these three fixtures. Unchanged refreshes measured 13 vs 22 ms, 37 vs 38 ms, and 60 vs 47 ms. Less-compressible data can therefore slow unchanged refreshes despite saving space. Unchanged source caches still incur zero writes.

Small caches stay plain. Compression is kept only when smaller than this PR's plain representation. Sources exceeding 128 MiB stay plain, preserving history and existing cache reuse. Compressed reads validate the decoded size, bound expansion, and then apply the same independent source integrity checks. Truncation, corruption, inconsistent concatenated output and forged sizes are rejected. Existing plain caches remain readable. Older Orca versions skip the optional compressed source cache and can rebuild it; their independently valid displayed report remains available. Writes capture their destination and workspace binding before awaiting compression. A test changes the caller's binding mid-write and verifies that only the original destination is written. No public wire format changes.

[Evidence receipt](./claude-source-compression-receipt.json) links raw samples and source hashes. Earlier measurement files describe the previous uncompressed implementation and remain historical evidence. Integration with current main updates benchmark subprocess imports to the existing `@orca/process-host` package; captured source and tooling hashes retain their original measurement provenance.

Reproduce:

```bash
ORCA_BACKGROUND_LAUNCH=1 node config/scripts/claude-usage-source-compression-benchmark.mjs --usage-keys 16384 --maxima-pattern repeated --rounds 6
ORCA_BACKGROUND_LAUNCH=1 node config/scripts/claude-usage-source-compression-benchmark.mjs --usage-keys 100000 --maxima-pattern unique --rounds 6
ORCA_BACKGROUND_LAUNCH=1 node config/scripts/claude-usage-source-compression-benchmark.mjs --usage-keys 100000 --maxima-pattern varied --rounds 6
```

---

### Site Content Docs Activity (`docs/site/content/docs/activity.mdx`)

---
title: Agents feed
description: Threaded feed of agent completions, blocking states, response previews, and worktree creation events across every worktree.
---

The **Agents** entry in the sidebar opens a threaded feed of agent events across every worktree — completions, blocking questions, unread state, and worktree creation. It complements the per-worktree [notifications](/docs/notifications) view by giving you one chronological surface to triage from.

The entry is on by default. It shows an unread badge when new events have arrived since you last visited; clicking an entry jumps you to the matching worktree and pane — the same handoff Orca does from a system notification.

Use `Cmd+F` on macOS or `Ctrl+F` on Windows and Linux to focus the feed filter. When the embedded terminal has focus, the same shortcut stays with the terminal instead.

## What shows up

Each entry in the feed is one of:

- An agent finishing a turn, either idle or blocked on a question.
- A new worktree being created.
- An agent that's been waiting on input long enough to surface as blocking.
- A short preview of the agent's most recent response, so you can skim the feed without opening every thread.

Threads are grouped by status — running agents stay pinned at the top so you can spot the ones still working at a glance.

## Interplay with notifications

The Agents feed doesn't replace the [header bell](/docs/notifications) or system notifications — those still fire on agent-finished pings. The feed is the catch-up surface when you've been away from Orca: instead of scrolling each worktree to find what changed, scan it.

## Next steps

- [Notifications & Inbox](/docs/notifications) — system pings, the header bell, and per-category tuning.
- [Agents & sessions](/docs/model/agents-sessions) — how Orca tracks the working/idle/blocked transitions this feed surfaces.

---

### Site Content Docs Android Apk (`docs/site/content/docs/android-apk.mdx`)

---
title: Install Orca Mobile on Android
description: Download and install Orca Mobile on Android.
---

## Install Orca Mobile

1. Open the [Orca download page](https://www.onorca.dev/download) in Chrome or another full browser and select **Android**.
1. When the download finishes, open your browser's **Downloads** or your device's **Files → Downloads**, then tap `app-release.apk`.
1. Follow Android's installation prompt. If asked, allow only the browser or Files app to install unknown apps, then turn that permission off after Orca is installed.

## Troubleshooting

### The download never starts or never finishes

1. If the link opened inside Discord, GitHub, or another app, choose **Open in browser** / **Open in Chrome**, or copy it into a full browser.
1. Start the [Android download](https://www.onorca.dev/download/started?platform=android) again and wait for it to finish.

### Samsung Galaxy: Auto Blocker

If your Samsung Galaxy says **Auto Blocker** prevented the install, open **Settings → Security and privacy → Auto Blocker**, temporarily turn it off, install Orca, then turn it back on. Do not disable Play Protect.

---

### Site Content Docs First Session (`docs/site/content/docs/first-session.mdx`)

---
title: Your first 3-agent session
description: From empty app to three agents running in parallel in under five minutes.
---

import { Callout } from '@/components/docs/prose'

This is the single most important page in the docs. By the end you'll have three agents running in parallel on three different approaches to the same task, with one PR shipped.

## 1. Add a repository

Click **Add Repo** on the sidebar and point Orca at a local checkout. Orca reads the repo's git state and picks up your default branch as its **base ref** — the ref every new worktree branches from.

You can change the base ref later under the repo's settings.

## 2. Create a worktree

Click the **+** next to the repo name. Type a task name ("fix-login-race" is fine — if you leave it blank, Orca names it after a marine creature).

The launcher opens with your **default agent** preselected (configurable under [Settings → Agents](/docs/settings)) or an empty terminal if you'd rather start cold. Pinned worktrees stand out in the sidebar so the ones you actually live in stay visible.

Pick a **start-from** ref. Most of the time this is your base ref (`origin/main`), but you can start from any branch or commit.

Orca creates a real git worktree under its managed directory, checks out the branch, and opens it.

## 3. Pick an agent

In the new worktree, a terminal opens with an **agent combobox**. Pick Claude Code, Codex, Cursor CLI, or any of the [supported agents](/docs/agents/supported). Orca will launch the agent's CLI with the correct working directory and forward your subscription credentials.

## 4. Race three agents on the same task

Now repeat steps 2–3 twice more. You now have three worktrees:

- `fix-login-race` → Claude Code
- `fix-login-race-2` → Codex
- `fix-login-race-3` → Cursor CLI

Paste the same prompt into each. Three branches. Three diffs. Same prompt. Let them work.

## 5. Split panes so you can watch them all

Drag a worktree's tab to the right or bottom edge of a pane to split. You can watch all three agents at once. See [Tabs, panes & split layouts](/docs/model/tabs-panes-splits).

## 6. Pick a winner, review the diff, ship it

Once agents settle, open each worktree's diff view. Use [Annotate AI Diff](/docs/review/annotate-ai-diff) to leave inline comments and send them back to whichever agent got closest.

Commit and push directly from Orca — see [Commit & push from Orca](/docs/review/commit-push). The other two worktrees can be deleted with one click; their branches go with them.

<Callout title="That's it">
  This flow — add → worktree → agent → split → diff → ship — is the whole of Orca. Every other page
  in these docs is a deeper look at one of those steps.
</Callout>

---

### Site Content Docs Github Errors (`docs/site/content/docs/github-errors.mdx`)

---
title: Troubleshooting GitHub errors
description: Diagnose rate limits, auth failures, and other GitHub CLI errors in Orca PR checks, Tasks, and Source Control.
---

Orca talks to GitHub through the **GitHub CLI (`gh`)** on your machine (or remote Orca host). When PR status, checks, issues, or Tasks fail to refresh, the cause is almost always GitHub auth, permissions, or API rate limits — not a broken PR panel by itself.

This page covers the errors you’ll see most often and how to fix them.

## Quick triage

| What you see                                                      | Likely cause                                               | First thing to try                                                                                               |
| ----------------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| “GitHub is rate-limiting requests” / “rate limit exceeded (core)” | GitHub REST (core) quota exhausted for your user           | Wait for reset; stop extra `gh` / agent / Orca usage; check [Settings → Git → GitHub API Budget](/docs/settings) |
| “GitHub authentication is unavailable” / `gh auth` prompts        | `gh` not logged in, expired token, or bad `GITHUB_TOKEN`   | `gh auth status`, then `gh auth login`                                                                           |
| “GitHub did not allow access” / HTTP 403 (not rate limit)         | Missing scopes or no access to the repo                    | Re-auth with `repo` (and needed org SSO); confirm you can open the PR in the browser                             |
| “repository is unavailable” / HTTP 404                            | Wrong remote, private repo without access, or renamed repo | Check `git remote -v` and browser access                                                                         |
| “GitHub is unreachable” / timeouts                                | Network, proxy, VPN, or GitHub outage                      | Check [githubstatus.com](https://www.githubstatus.com/); retry off VPN                                           |
| “GitHub CLI is unavailable”                                       | `gh` missing from PATH Orca uses                           | Install `gh` and restart Orca                                                                                    |

## Rate limits (most common)

GitHub gives each **authenticated user** a shared hourly budget. **Every tool on that account shares it**: Orca, `gh` in terminals, Claude/Codex/Grok agents that call `gh`, CI scripts, browser extensions, and other apps.

### Buckets Orca cares about

| Bucket          | What it covers                                                               | Typical limit (authenticated) |
| --------------- | ---------------------------------------------------------------------------- | ----------------------------- |
| **REST (core)** | Most PR/issue/API calls (`gh pr view`, checks metadata, many REST endpoints) | 5,000 / hour                  |
| **GraphQL**     | Project/Tasks and some richer PR queries                                     | 5,000 points / hour           |
| **Search**      | Search-driven lists                                                          | 30 / minute                   |

When a primary bucket is exhausted, GitHub returns HTTP **403** with a message like `API rate limit exceeded`. Orca classifies that as rate-limited, keeps the last known PR status when it can, and **stops spawning more `gh` calls** for a short window so a single limit doesn’t turn into a storm of failures.

### Why Settings can look “OK” while the PR panel is blocked

**Settings → Git → GitHub API Budget** reads GitHub’s special `rate_limit` endpoint. That probe is **exempt** from rate-limit accounting and sometimes still reports remaining quota while real REST calls for the same user already return `remaining: 0`.

Trust this order when they disagree:

1. The **error on the PR / Checks panel** (live request failed)
2. A real CLI check (below)
3. The Settings budget numbers (useful, but probe-only)

Also don’t confuse **GitHub API Budget** with **Claude / Codex / Grok usage** under Accounts — those are AI provider limits, not GitHub’s REST quota.

### Confirm a rate limit from a terminal

```bash
# Probe (does not consume quota; can look healthier than reality)
gh api rate_limit --jq '.resources | {core, graphql, search}'

# Real REST call — this is what PR refresh depends on
gh api user -i 2>&1 | head -40
```

If `gh api user` returns **403** with `API rate limit exceeded` and `X-Ratelimit-Remaining: 0`, the account is blocked for REST until `X-Ratelimit-Reset` (Unix epoch seconds).

### What usually burns the quota

- Many Orca windows or electron-dev builds open at once (each may refresh PRs / Tasks)
- Agents automating `gh` (assign PRs/issues, poll checks, bulk GraphQL)
- Heavy Tasks / multi-repo fan-out while also refreshing PR panels
- Other apps using the same GitHub user token

### What to do

1. **Wait** for the hourly reset shown in the error or in `X-Ratelimit-Reset`.
2. **Reduce concurrent GitHub clients** — quit extra Orca instances and pause bulk `gh` automations.
3. Avoid hammering refresh on the PR panel while limited; Orca is already backing off.
4. Prefer batching GraphQL in scripts; don’t loop REST one-PR-at-a-time across large sets on a personal account.
5. After reset, if PR refresh still fails, re-check auth (next section).

## Authentication problems

Orca inherits whatever `gh` uses on that host.

### Check status

```bash
gh auth status -h github.com
gh api user --jq '{login, id}'
```

Healthy output: logged in, token valid, `gh api user` returns your login.

### Common footguns

**`GITHUB_TOKEN` / `GH_TOKEN` in your shell profile**<br />
If these are exported (for example `export GITHUB_TOKEN=$(gh auth token)` in `~/.bash_profile` or `~/.zshrc`), `gh` prefers them over the keyring. A stale or wrong env token produces confusing auth or rate-limit behavior. Unset them and re-login:

```bash
unset GITHUB_TOKEN GH_TOKEN
gh auth logout -h github.com
gh auth login -h github.com
```

**Expired or revoked token**<br />
`gh auth status` may show the token as invalid. Run `gh auth login` (or `gh auth refresh`) and restart Orca so it picks up the new credentials.

**Org SAML SSO**<br />
Private org repos may need SSO authorization for the token. Open the org’s SSO authorize link from the GitHub token settings, then retry.

**Remote / SSH worktrees**<br />
GitHub auth is **per host**. Logging in on your laptop does not log in `gh` on a remote machine. SSH into the host and run `gh auth login` there, or use Orca’s remote-server GitHub budget view for that environment.

## Permission and repository errors

| Symptom                                        | Meaning                                                     |
| ---------------------------------------------- | ----------------------------------------------------------- |
| HTTP 403 without “rate limit”                  | Token lacks scope or you’re not allowed to see the resource |
| HTTP 404 / “could not resolve to a Repository” | Repo missing, renamed, or invisible to this token           |
| “resource not accessible by integration”       | App/token type can’t perform that action                    |

Fixes:

- Confirm the PR/repo opens in the browser while logged in as the same user as `gh api user`
- Re-auth with classic scopes that include `repo` (and `read:org` / `project` if you use those features)
- For GitHub Enterprise, ensure `gh` is authenticated to that hostname (`gh auth login --hostname …`)

## Network and GitHub outages

Messages about timeouts, “could not resolve host”, or “GitHub is unreachable” are connectivity issues:

- Check [GitHub Status](https://www.githubstatus.com/)
- Try without VPN / corporate proxy
- On remote hosts, confirm outbound HTTPS to `api.github.com` works

## GitHub CLI missing

If Orca reports the GitHub CLI is unavailable:

1. Install [`gh`](https://cli.github.com/)
2. Confirm `which gh` works in a normal terminal
3. Fully quit and reopen Orca (so PATH matches)
4. On Windows, install for the same environment Orca launches (WSL vs native)

## How Orca behaves when GitHub fails

- **Rate limit / outage**: PR and Checks panels prefer **last known status** plus a short banner instead of wiping the UI.
- **Hard auth / permission failures**: You’ll get clear empty-state or banner copy telling you to fix login or access.
- **Circuit breaker**: After a primary rate-limit 403, Orca briefly refuses new `gh` spawns for that bucket (`core`, `search`, or `graphql`) so the app stays responsive and doesn’t dig a deeper hole.

## Check GitHub API Budget in Orca

Open **[Settings → Git](/docs/settings)** and find **GitHub API Budget**:

- **REST / Search / GraphQL** remaining counts from GitHub’s probe
- Refresh after waiting out a limit
- On remote Orca servers, use the remote advanced budget view for the **server-owned** `gh` identity (local Settings only shows the desktop client)

## Still stuck?

1. Reproduce once in a terminal with `gh pr view` or `gh api user` from the same machine/user Orca uses.
2. Collect logs: **Help → Open Logs**.
3. File an issue with the classified error text (not secrets), `gh auth status` redacted output, and whether terminal `gh` fails the same way.

- [GitHub Issues](https://github.com/stablyai/orca/issues)
- [Discord](https://discord.gg/fzjDKHxv8Q)

## Related

- [Hosted reviews, issues & Actions](/docs/review/github) — PR and Checks features that depend on GitHub
- [Settings reference](/docs/settings) — Integrations and Git panes
- [Usage & rate-limit tracking](/docs/agents/usage-tracking) — AI provider limits (Claude/Codex), not GitHub API
- [Troubleshooting & FAQ](/docs/troubleshooting) — general Orca issues

---

### Site Content Docs Install (`docs/site/content/docs/install.mdx`)

---
title: Install
description: Download Orca for macOS, Windows, or Linux, and opt into RC builds.
---

import { Callout } from '@/components/docs/prose'

## Download

<div className="my-4 rounded-md border border-border bg-card p-4 md:hidden">
  <p className="mb-3 text-sm text-muted-foreground">
    Orca is a desktop app. Download the latest build for your platform from GitHub:
  </p>
  <a
    href="https://github.com/stablyai/orca/releases/latest"
    className="inline-flex min-h-10 items-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
  >
    Download latest release
  </a>
</div>

<ul className="hidden md:block">
  <li>
    **macOS:** [Apple
    Silicon](https://github.com/stablyai/orca/releases/latest/download/orca-macos-arm64.dmg) ·
    [Intel](https://github.com/stablyai/orca/releases/latest/download/orca-macos-x64.dmg)
  </li>
  <li>
    **Windows:**
    [installer](https://github.com/stablyai/orca/releases/latest/download/orca-windows-setup.exe)
  </li>
  <li>
    **Linux:** AppImage
    [x64](https://github.com/stablyai/orca/releases/latest/download/orca-linux.AppImage) ·
    [arm64](https://github.com/stablyai/orca/releases/latest/download/orca-linux-arm64.AppImage) ·
    [.deb](https://github.com/stablyai/orca/releases) ·
    [.rpm](https://github.com/stablyai/orca/releases) — see [Linux](#linux) for which to pick
  </li>
  <li>Older versions: [GitHub Releases](https://github.com/stablyai/orca/releases).</li>
</ul>

### Homebrew (macOS)

Orca is also published as a Homebrew cask, auto-bumped on every stable release:

```
brew install --cask stablyai/orca/orca
```

`brew upgrade --cask orca` picks up new stable builds. The cask tracks the stable channel — for RC builds, use the GitHub Releases links above or the in-app **Check for Updates** flow described under [Updates](#updates).

## First launch

On first launch Orca will:

- Ask for access to your home directory so it can add repos.
- Offer to import `~/.claude`, `~/.codex`, and Ghostty terminal settings if present.
- Drop you on an empty landing screen where you add your first repo.

## Updates

Orca auto-updates by default, tracking the **stable** channel. Stable releases are vetted; **RC (release candidate)** builds ship new features first, often daily.

On Linux, whether Orca can apply an update itself depends on which package you installed. See [Linux](#linux) before you pick one.

There is no permanent in-app opt-in for the RC channel. Modifier clicks on **Check for Updates** ([Settings → General → Updates](/docs/settings), or the app / Help menu):

| Modifier                                               | Effect                                                                         |
| ------------------------------------------------------ | ------------------------------------------------------------------------------ |
| **Shift+click**                                        | Include the latest **RC** prerelease                                           |
| **Cmd+click** (macOS) / **Ctrl+click** (Windows/Linux) | Latest **perf**-tagged prerelease                                              |
| **Option+click** (macOS only)                          | Pick a **validated local macOS build** that passes Orca’s compatibility checks |

You can still download any build directly from the [GitHub Releases page](https://github.com/stablyai/orca/releases).

<Callout title="Don't like the current update">
  Older versions are always available on the [GitHub Releases
  page](https://github.com/stablyai/orca/releases). Orca will not force-downgrade your worktree data
  if you go back.
</Callout>

## Platform notes

### macOS

Signed and notarized. On first launch, macOS may still ask you to confirm — that's normal for Electron-based apps.

### Windows

The default shell can be set to PowerShell or CMD under [Settings → Terminal](/docs/settings). Most users want PowerShell.

### Linux

Each published release ships three Linux packages — an **AppImage**, a **`.deb`**, and an **`.rpm`** — for both x64 and arm64. They contain the same app. What differs is how updates reach you, so pick on that.

| Package      | Pick it when                                              | Updates                                                           |
| ------------ | --------------------------------------------------------- | ----------------------------------------------------------------- |
| **AppImage** | You want Orca to update itself, like on macOS and Windows | Orca downloads and applies the update in place                    |
| **`.deb`**   | You manage software with `apt` on Debian or Ubuntu        | Orca tells you a version is out and hands you the install command |
| **`.rpm`**   | You manage software with `dnf`, `yum`, or `zypper`        | Same as `.deb`                                                    |

The AppImage has a stable download link per architecture — [`orca-linux.AppImage`](https://github.com/stablyai/orca/releases/latest/download/orca-linux.AppImage) for x64 and [`orca-linux-arm64.AppImage`](https://github.com/stablyai/orca/releases/latest/download/orca-linux-arm64.AppImage) for arm64 — and needs `chmod +x` before its first run, because GitHub release assets carry no permission bits. The `.deb` and `.rpm` filenames carry the version and architecture, and the two formats spell architecture differently (`orca-ide_<version>_amd64.deb` or `_arm64.deb`; `orca-ide-<version>.x86_64.rpm` or `.aarch64.rpm`), so take those from the [Releases page](https://github.com/stablyai/orca/releases) rather than a fixed URL.

#### How updating works

**The AppImage self-updates.** Choose it if you want automatic updates. Orca checks for a new release, you click **Update**, and it replaces the AppImage in place — the same flow as macOS and Windows.

**The `.deb` and `.rpm` do not self-update.** Orca still notices the new version and downloads the package, then gives you a **Copy Install Command** button. Copy it rather than retyping it: Orca resolves every program to an absolute path in a trusted system directory and single-quotes the package path, so what you paste looks like this:

```
/usr/bin/sudo /usr/bin/apt install -- '/home/you/.cache/orca-updater/pending/orca-ide_1.4.194_amd64.deb'
```

Which package manager appears depends on what your system actually has: `apt`, else `dpkg -i`, for a `.deb`; `zypper`, `dnf`, `yum`, then `rpm -Uvh` for an `.rpm`. The download directory follows `XDG_CACHE_HOME` when that is set and falls back to `~/.cache` when it is not.

**Quit Orca before you run the command**, then reopen it once the install finishes. You are replacing the files of a running application, and the package manager cannot swap them safely underneath a live process. Orca deliberately never escalates privileges to do this for you: installing a system package needs root, `orca serve` runs as an unprivileged user, and a headless machine has no authentication agent to prompt. VS Code and Signal make the same call on `.deb`.

**A distro-managed build is left alone.** If you are running a repackaged Orca — an AUR build, a Nix derivation — Orca sees that no package manager it can drive owns this install and stops offering a download it could never apply. It still reports that a new version exists, so you can update the way you normally would.

<Callout title="Planned: a signed apt/yum repository">
  [#18086](https://github.com/stablyai/orca/issues/18086) tracks publishing a signed repository so
  your OS package manager owns Orca updates the way it owns everything else. It does not exist yet —
  today, `.deb` and `.rpm` updates are the manual step described above.
</Callout>

#### The CLI command is `orca-ide`

On Linux the [Orca CLI](/docs/cli/reference) installs as **`orca-ide`**, not `orca`. GNOME Orca — the screen reader that ships by default on Ubuntu and other GNOME desktops — already owns `/usr/bin/orca`, and Orca will not shadow it. The `.deb` and `.rpm` packages are named `orca-ide` for the same reason.

- The `.deb` and `.rpm` put `orca-ide` on your `PATH` at install time, as `/usr/bin/orca-ide`.
- With the AppImage, register the CLI from [Settings → General → Orca CLI](/docs/settings). That installs `~/.local/bin/orca-ide`.
- Inside Orca's own terminals, bare `orca` works. Orca puts a shim on the `PATH` of the terminals it manages, so agents and scripts running there use the same command as on macOS and Windows.
- On a headless host, a packaged `orca serve` writes a bare `orca` into `~/.local/bin` as it starts, unless a file it does not own already holds that name. It writes that _during_ startup, so it is never what starts the server — the first launch is always [`orca-ide serve`](/docs/remote-servers).

Do not verify with `command -v orca`: on a GNOME desktop that succeeds and resolves to the screen reader. Use `orca-ide` in your own shell and `orca` inside Orca. If you want the short name everywhere and you do not use the screen reader, link it yourself:

```
ln -s "$(command -v orca-ide)" ~/.local/bin/orca
```

---

--- METRICS ---
- Files Extracted: 101
- Estimated Token Budget: ~224161 tokens
- Canonical Reference: https://codewiki.google/github.com/stablyai/orca
