* fix(orchestration): scope @ group addresses to the sender's Run `@all`, `@idle`, and the agent-name groups (`@claude`, `@codex`, ...) resolved against every terminal on the host. A coordinator meaning "my three reviewers" reached 126 agents across every open project, twice in one day, and every unrelated agent burned a turn discarding mail that was never for it. Every group except `@worktree:<id>` now means the live Dispatches of the sender's own Run, each addressed as `dispatch:<id>` so delivery is durable even when the worker terminal is not attached yet. A sender bound to no Run is refused with `invalid_argument` naming `run:<id>` / `dispatch:<id>`; there is no host-wide fallback and the host's terminals are never enumerated for it. `@idle` and the agent-name groups filter within that set by the same terminal status and host-resolved identity as before. `ask --to @group` returns the same code and points at the owning Run mailbox. Federated Dispatches read relayed control mail rather than a local mailbox, so a Run-scoped fan-out skips them with a `recipient_unreachable` warning naming the direct `dispatch:<id>` address. Group addresses are resolved host-side, so no RPC or stream shape changes; an older CLI sending `@all` to a new host gets the Run-scoped meaning. Claude-Session: run-scoped-group-addresses * fix(orchestration): revalidate legacy takeover before the recipient verdict A legacy coordinator taken over while `listTerminals` was in flight reported `runtime_error` instead of `legacy_read_only`: Run scoping made "no live Dispatch in this Run" the first thing the group send could fail on, and that threw before the takeover check ran. Takeover is a precondition, not a commit-time detail — the sender must be told it is read-only whatever else is wrong with its recipient set. Revalidation moves to immediately after the only `await` in the path. Everything below it is synchronous, so the commit-time window it used to guard is unchanged; only the error paths now see it. The legacy partition test gave `term_current_worker` no Dispatch, so under Run scoping it is correctly not a recipient. It now holds a real current-contract Dispatch in the same adopted Run, which is what the test is named for: one `legacy_direct` and one `current_delivery` recipient in one fan-out. Claude-Session: run-scoped-group-addresses * fix(orchestration): address the Run a nested coordinator created, not its parent A nested coordinator is both a worker of its parent Run and the coordinator of the Run it created. `resolveMessageRun` answers with the parent, correctly, because that is where its own `worker_done` belongs — but audience is a different question. Scoping `@all` to that Run sent a nested coordinator's "shared context" to the siblings it was started beside instead of the workers it started, and reported success, so it never learned its sub-workers heard nothing. Before Run scoping the host-wide fan-out reached the sub-workers by accident; this turned an over-broad delivery into a wrong-audience one, the exact failure class the change exists to remove. Group audience now resolves off the Run the sender coordinates, falling back to its Dispatch's Run. A leaf worker coordinates nothing and is unaffected. This is a separate question from `routing.run`, not a second answer to the same one, so `resolveMessageRun` keeps its meaning for point-to-point mail. Also: when every live Dispatch in a Run is federated, the fan-out skipped them all and threw a bare `Error` that discarded the warnings naming those remote workers and how to address each one. The sender was told "no recipients" while three remote workers existed. That throw now carries a code and the skip explanations. Claude-Session: run-scoped-group-addresses * docs(orchestration): say that no group address reaches a coordinator A coordinator is not a Dispatch, so Run-scoped groups never include one. That follows from the rule, but nothing said it, and the old host-wide meaning did include the coordinator — a worker sending `@all` to raise a blocker would be heard by its siblings and by nobody who can act. The guide, the CLI note, and the docs page now say to use `run:<id>` for that, and that a worker which created its own Run addresses that Run's workers. Also restores the `@cursor` case dropped when the group tests moved: a Claude pane titled "Fix the text cursor blink" must not receive Cursor's mail. That hazard was recorded from real titles and `@droid` alone did not cover it. Claude-Session: run-scoped-group-addresses * fix(orchestration): preserve group audience and mailbox identity * fix(orchestration): validate group scope before dispatch routing * fix(orchestration): preserve pane identity and exclude coordinator dispatches
@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, 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
cd docs/site
pnpm --ignore-workspace install
pnpm --ignore-workspace dev
Open http://localhost:3004/docs.
Production build
cd docs/site
pnpm --ignore-workspace install
pnpm --ignore-workspace build
pnpm --ignore-workspace 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.jsonnavigationpublic/docs/— docs-only media, logo, and favicon (GIFs, posters, screenshots)src/app/docs/— fumadocs routes, OG imagessrc/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:
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)
- Create a Vercel project with Root Directory unset (
.). The workflow invokes Vercel fromdocs/site, so that directory is already the deployment root; setting it again would resolvedocs/site/docs/site. Leave automatic Git deployments disabled so this workflow remains the only deployment path. - Framework preset: Next.js. Use
pnpm --ignore-workspace install --frozen-lockfilefor install andpnpm --ignore-workspace buildfor build; this package has its own lockfile beside the root workspace. - Set GitHub Actions secrets (required by the production deploy job):
VERCEL_TOKENVERCEL_ORG_IDVERCEL_PROJECT_ID(docs project, not the marketing site)
- Protect the
docs-productionGitHub environment with required reviewers and custom deployment branch policies formainandv*tags. The release-cut dispatch runs frommain; the direct published-release fallback runs from a stable tag, while the workflow still authorizes only exact stable tags. - Prepare the three rewrites above in the
www.onorca.devmarketing project, but leave them disabled until the docs deployment is verified. A Vercel custom domain cannot delegate only/docsby itself; the default zone must proxy both page/API/media requests and/docs-staticassets. Keep the marketing project's old docs routes available for rollback during the transition; putting the proxy rules inbeforeFilesensures they win once enabled. - 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.yamlunderdocs/site/ - Not listed in the root
pnpm-workspace.yaml; install withpnpm --ignore-workspace - Engineering notes remain in repo-root
docs/— do not confuse with this package