Repository navigation
feat(agents): pre-trust the folder wherever Orca starts an agent - #23744
Conversation
Claude Code asks "Do you trust this folder?" on first launch in any folder it has not seen, which blocks unattended launches in worktrees Orca itself made. Orca now records where a worktree's content came from when it creates it, and before each Claude launch writes Claude's own folder-trust entry for that worktree's root (never the main checkout) when the new setting is on and the content is the user's repository. Forks, bare commits, folder workspaces and external checkouts keep Claude's prompt. The write takes Claude's lock, never creates or breaks the file, runs on the SSH host itself, and is revoked when the worktree is removed or the setting is turned off. Launches that already pass --dangerously-skip-permissions also skip the trust prompt for that one process only, via CLAUDE_CODE_SANDBOXED=1 on the command.
A WSL worktree's Claude reads the guest's own config. Two paths still wrote its trust into the Windows host's ~/.claude.json instead: the Claude auth prep's fallback (runtime 'wsl' but the host config dir, when the WSL home cannot be resolved), which wrote a Linux-path key the removal revoke can never delete; and the Agent Teams leader, which passes no auth or distro and wrote a UNC key. Require the guest's own config dir, and treat any WSL worktree path as guest-only.
Pre-trust stays limited to worktrees Orca creates from the user's own repository. The per-launch CLAUDE_CODE_SANDBOXED prefix skipped Claude's trust question in every folder for launches carrying the skip-permissions flag (Orca's default Claude args), including the user's own folders and fork PR worktrees, and the setting could not turn it off. Remove the prefix, the inherited-variable strip that existed only for it, and the Agent Teams leader-to-teammate propagation; restore the tests that pinned the prefixed launch string.
At spawn the relay resolves Claude's config from the launch env, which carries a CLAUDE_CONFIG_DIR set in Orca's Claude default env. claudeTrust.converge had only the relay's own process env, so removing the worktree or turning the setting off revoked in the default file and the grant outlived the worktree. Send the config-file keys with the request, as the local revoke already uses.
The description said Claude skips its trust prompt in any worktree Orca created. Trust is written only when Orca itself starts Claude there, so a `claude` typed by hand in a fresh worktree still asks. Say that, and bring the es/fr/ja/ko/zh translations in line with the new text.
…tent A worktree based on a named ref was always stamped as the repository's own content, so picking the fork remote Orca adds for a pull request (or a local branch tracking it) as the base made a fork's code eligible for Claude trust. At create time, read the repo's `remote.<name>.orca-created` markers and each branch's tracked remote in one `git config` call; a base on such a remote is stamped as a fork's content, and a read failure is not vouched for. Remotes the user added, such as `upstream`, stay first-party.
…ktree Turning "Trust worktrees Orca creates for Claude" off read and parsed the whole Claude config once per Orca worktree on the main process. Group the revocations by config file locally and by SSH connection, and make claudeTrust.converge take a batch of requests.
…orktree-trust # Conflicts: # src/main/runtime/orca-runtime-resolve-worktree-removal-target.ts
Review status: ready for your review (still a draft, not merged)Head What changed during review
Architecture challengeOne issue class came up in every loop: "trust entries outlive their worktree". A separate review challenged the design against alternatives: revoking at every place Orca forgets a worktree, a config-wide sweep, and a durable record of what Orca wrote. Verdict: keep the design (decide again at every Orca launch, and revoke on delete and on setting-off). A sweep is unsafe, because the entry shape isn't unique to Orca and Claude's own hint text tells users to write it. The leftover entries are almost all for folders that no longer exist. The one change it recommended, batching the setting-off revocation, is in. Live QA (second Mac, throwaway Claude config, dummy key, no network)Round 1 at Not run live, covered by unit tests only: SSH, WSL, Windows, Agent Teams and a real fork pull request. Known gaps, all listed in the PR body
Found during review, not caused by this PR
|
|
| for (const key of args.folderKeys) { | ||
| const entry = projects[key] | ||
| projects[key] = isPlainObject(entry) | ||
| ? { ...entry, hasTrustDialogAccepted: true } | ||
| : { hasTrustDialogAccepted: true } | ||
| } |
There was a problem hiding this comment.
Granted trust cannot be revoked. If Claude already has a worktree entry with other fields and
hasTrustDialogAccepted: false, this code preserves those fields while granting trust. Revocation deletes only entries containing exactly the trust field, so turning the setting off or deleting the worktree leaves Orca’s grant active. Claude can later skip the trust question at that path despite the attempted withdrawal.
How this was verified: The grant merges into a multi-field entry, while every revoke path uses the one-field entry check.
| if (args.forkRemoteConfig === null) { | ||
| return 'unverified-commit' | ||
| } | ||
| return isBaseOnOrcaForkRemote(base, args.forkRemoteConfig) | ||
| ? 'cross-repo-review-head' | ||
| : 'repo-ref' |
There was a problem hiding this comment.
Short hashes bypass fork classification. A user can use an abbreviated hash of a fetched fork commit as the worktree base. This classifier recognizes only full-length hashes, so it treats the abbreviation as a named base and labels it
repo-ref when it matches no Orca-marked fork remote. Orca can then pre-trust third-party content, allowing its Claude hooks to run without the trust question.
How this was verified: The full-length-only hash check sends an abbreviated commit through the named-base branch, whose unmatched result is eligible for pre-trust.
| worktreeId: string | ||
| ): void { | ||
| // Why: cleanup must never gate the removal, so it runs detached and swallows failures. | ||
| void revokeWorktrees(store, [worktreeId]).catch(() => {}) |
There was a problem hiding this comment.
Late revocation removes fresh trust. Removal and setting-off revocations run in the background and identify entries only by worktree path. If the setting is turned back on, or a worktree is recreated at the same path, a new Claude launch can grant trust before the older revocation gets the config lock. The older revocation then deletes the fresh grant, leaving an unattended launch at Claude’s trust question.
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. 📝 WalkthroughWalkthroughThe change moves workspace trust writes from renderer and worktree preflight paths to fresh PTY launches and structured Codex session creation. It adds host-specific trust handling for local launches, Claude config files, WSL paths, and SSH relays. It also adds an enabled-by-default setting to control new trust writes, removes the former trust IPC bridge and remote trust writers, and updates tests, translations, and supporting documentation. Priority: ➖ Normal Merge Risk: 🟡 Moderate · up to A cancelled relay launch can leave a folder trusted even though no agent started. The setting description also overstates when the trust prompt is skipped. Fix the cancellation ordering and qualify the copy before merging. Security Architecture ReviewSecurity architecture risk: 🟡 Moderate · up to Starting a coding session now grants lasting folder trust by default, allowing project hooks and settings to run without their usual confirmation. An opt-out and broad-path checks limit exposure, but disabling the feature does not revoke existing trust. Retained concerns
Security review detailsSecurity Blast Radius
Security Findings and Attack Paths
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 39.53% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 129 functions across 78 files. (7 skipped: 7 unsupported.)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Advanced
Run ID: 5d2d4efc-bd1e-44bb-910b-0b147202a54b
📒 Files selected for processing (45)
src/main/claude/claude-folder-trust-file.test.tssrc/main/claude/claude-folder-trust-file.tssrc/main/claude/claude-worktree-trust-eligibility.tssrc/main/claude/claude-worktree-trust-host.tssrc/main/claude/claude-worktree-trust-lifecycle.test.tssrc/main/claude/claude-worktree-trust-lifecycle.tssrc/main/claude/claude-worktree-trust-spawn.test.tssrc/main/claude/claude-worktree-trust-spawn.tssrc/main/ipc/pty/ipc/spawn-options-claude-trust.test.tssrc/main/ipc/pty/ipc/spawn-options.tssrc/main/ipc/pty/runtime/spawn-options.tssrc/main/ipc/worktree-content-origin.test.tssrc/main/ipc/worktree-content-origin.tssrc/main/ipc/worktree-remote.tssrc/main/ipc/worktrees/removal/worktree-removal-ownership.tssrc/main/providers/pty-provider-contract.tssrc/main/providers/ssh-pty-spawn-request.tssrc/main/runtime/orca-runtime-resolve-terminal-split-source-authority.tssrc/main/runtime/orca-runtime-resolve-worktree-removal-target.tssrc/main/runtime/orca-runtime-tests/worktree-creation-content-origin.spec.tssrc/main/runtime/orca-runtime.test.tssrc/main/runtime/runtime-local-worktree-materialization.tssrc/main/startup/main-process-ready-foundation.tssrc/main/worktree-removal-safety.tssrc/relay/claude-folder-trust-spawn.test.tssrc/relay/claude-folder-trust-spawn.tssrc/relay/pty-handler.tssrc/relay/relay-runtime-services.tssrc/renderer/src/components/settings/AgentsPane.test.tsxsrc/renderer/src/components/settings/AgentsPane.tsxsrc/renderer/src/components/settings/agents-search.tssrc/renderer/src/components/settings/claude-worktree-trust-copy.tssrc/renderer/src/i18n/en-runtime-required.jsonsrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/i18n/locales/es.jsonsrc/renderer/src/i18n/locales/fr.jsonsrc/renderer/src/i18n/locales/ja.jsonsrc/renderer/src/i18n/locales/ko.jsonsrc/renderer/src/i18n/locales/zh.jsonsrc/shared/claude-folder-trust-spawn-request.tssrc/shared/default-global-settings.tssrc/shared/global-settings-types.tssrc/shared/worktree/content-origin.test.tssrc/shared/worktree/content-origin.tssrc/shared/worktree/meta-types.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review.
| null, | ||
| null |
There was a problem hiding this comment.
🔒 Security & Privacy | 🛡️ Detected with Advanced Tier | 🟠 Major | 🏗️ Heavy lift
Security Misconfiguration
Reachability: External
Exploitability: Difficult
CWE: CWE-863 — Incorrect Authorization
Resolve the WSL guest config for revocation.
For a WSL worktree, resolveLocalClaudeTrustRequest requires WSL authentication. Passing null makes it return null, so removal and setting disablement never enqueue a revocation. The previously accepted folder-trust entry remains in the guest config. A later Claude launch using that config can skip the trust prompt after the user has turned the setting off. Resolve the guest config before metadata removal and submit a trusted: false request. (code.claude.com)
| { | ||
| title: getClaudeWorktreeTrustTitle(), | ||
| description: getClaudeWorktreeTrustDescription(), | ||
| keywords: getClaudeWorktreeTrustSearchKeywords() | ||
| }, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Hide this search entry when the switch is unavailable.
In a paired web client window, AgentsPane omits ClaudeWorktreeTrustSetting, but getAgentsPaneSearchEntries still returns this entry. A search result can therefore lead to a pane with no matching control. Filter the entry for paired web client windows, as the catalog already does for other conditional settings.
There was a problem hiding this comment.
ℹ️ No critical issues — one rough edge inline and one narrow gap worth a look.
Reviewed changes
- Folder-trust writer — new
claude-folder-trust-file.tswrites/removesprojects[<worktree>].hasTrustDialogAcceptedin Claude's global config: atomic tmp+rename, mode + symlink preserved, never creates a missing file, refuses unparseable configs, and takes Claude's<file>.lockwithout ever breaking a held one. - Eligibility + origin gate —
claude-worktree-trust-eligibility.tstrusts only git worktrees with Orca creation provenance, decidingtrustedfrom settings and a neworcaCreationContentOriginstamped at creation inworktree-content-origin.ts(repo-ref/same-repo-review-head= first-party; fork / unverified = not). - Spawn integration — both PTY spawn paths and the Agent Teams leader converge trust just before spawn; SSH forwards a
claudeFolderTrustfield on the spawn request and the relay applies it on its own disk. - Revocation — removed worktrees and the setting being turned off withdraw Orca-shaped entries, locally and over a new
claudeTrust.convergerelay RPC (old relays reject it harmlessly). - UI — new default-on
claudeTrustOrcaWorktreessetting (Settings → Agents) with copy, search keywords, and translations.
I verified the two load-bearing invariants myself: fork push targets always carry remoteUrl while same-repo targets never do (getPullRequestPushTarget), and both spawn paths merge the Claude account envPatch into the env before the trust call, so the writer sees the account's CLAUDE_CONFIG_DIR. The lock/write path also releases on every return, including the uncancelled timeout, and cannot clobber a Claude-shaped entry.
ℹ️ Named-base fork detection leans on a marker some SSH relays never write
isBaseOnOrcaForkRemote decides "third-party content" solely from the remote.<name>.orca-created git config marker. On SSH that marker is written through the git.markRemoteOrcaCreated RPC, which an older relay rejects with a one-time warning and no retry — so the marker is permanently absent. If Orca added a fork remote while connected to such a relay and a later worktree is created from that fork branch by name as the base, no pushTarget is present, the marker lookup misses, and the worktree is stamped repo-ref and pre-trusted. The normal PR review-head flow always carries a fork pushTarget, so it is unaffected; reachability needs the old relay plus a by-name base selection. It is the one path I found where fork content can be stamped first-party, so it may be worth hardening (e.g. compare the named base's remote URL against the repo's own remotes, not just the marker).
Technical details
# Named-base fork classification only trusts Orca's `orca-created` marker
## Affected sites
- `src/shared/worktree/content-origin.ts:37-54` — `isBaseOnOrcaForkRemote` matches only `remote.<name>.orca-created` / `branch.<name>.remote`
- `src/main/ipc/worktree-content-origin.ts:17-22` — reads the marker only for a named base
- `src/main/ipc/worktree-remote.ts:1336-1345` — SSH marker write goes through `git.markRemoteOrcaCreated`
- `src/main/providers/ssh-git-worktree-provider.ts:158-172` — old relay: method-not-found → warning, marker never set
## Required outcome
- A fork remote Orca itself created must not let a by-name base be classified first-party, even when the marker RPC did not land.
## Open questions for the human
- Is the old-relay + by-name-base path acceptable as a known limitation, or should classification fall back to comparing the named remote's URL against the repository's own remotes?DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
| const legacyFile = join(configDir, '.config.json') | ||
| const configFile = existsSync(legacyFile) | ||
| ? legacyFile | ||
| : claudeAuth.envPatch.CLAUDE_CONFIG_DIR |
There was a problem hiding this comment.
WSL resolution ignores a launch-env CLAUDE_CONFIG_DIR override. This branch keys the config file only off claudeAuth.envPatch.CLAUDE_CONFIG_DIR and never consults env. When a WSL launch carries CLAUDE_CONFIG_DIR from the Claude agent env (visible to Orca and forwarded into the guest via WSLENV), the guest reads $CLAUDE_CONFIG_DIR/.claude.json while system WSL auth has envPatch: {} and this writes <guest-home>/.claude.json — the grant silently misses and Claude still asks. The non-WSL branch at lines 80-89 does read the same override out of env, so the two disagree.
Technical details
# WSL branch should resolve the config file the guest will read
## Affected sites
- `src/main/claude/claude-worktree-trust-spawn.ts:67-71` — config file chosen from `claudeAuth` only; `env` is unused
- `src/main/claude/claude-worktree-trust-spawn.ts:80-89` — host branch honors `env.CLAUDE_CONFIG_DIR`
- `src/main/claude-accounts/runtime-auth/runtime-auth-preparation.ts:44-55` — system WSL prep has `envPatch: {}`
- `src/main/providers/local-pty-windows-spawn-environment.ts:54-57` — forwards `CLAUDE_CONFIG_DIR` into the guest
## Required outcome
- For a WSL launch whose effective env carries a guest `CLAUDE_CONFIG_DIR`, resolve the UNC path for that dir (the `wslDistro` argument already names the distro) so grant/revoke act on the file the guest reads, instead of falling back to the guest home.
## Open questions for the human
- Is a user-set `CLAUDE_CONFIG_DIR` meant to be out of scope for WSL (like the documented shell-rc limitation), or should this branch honor the launch env the way the host branch already does?… > Agents Replace the Claude-only worktree trust toggle with a single setting, agentWorkspaceTrustEnabled (on by default; unreleased, so no migration). The row says what it does for every agent: agents Orca starts skip their "trust this folder?" prompt in that worktree or folder, turning it off stops new trust while existing trust stays, and while it is off unattended launches (orchestration workers, automations, the phone) stop at the agent's trust question until someone answers. Translations for es/fr/ja/ko/zh. Also restores the `awaitingUnnamed` chat catalog keys an earlier merge of main dropped from this branch.
…PTY spawn Every Orca-started agent PTY passes through one of the two spawn builders with its declared launchAgent, which survives setup-script wrapping. The builders now call one hook that, for a fresh launch (never a reattach or restored pane) with the setting on, applies the agent's trust preset to the worktree, folder workspace or main checkout it starts in. - One dispatcher, applyAgentWorkspaceTrust(preset, workspacePath, launch context), carries what a writer needs: the final spawn env, the Claude managed-account auth prep, the WSL distro and the SSH connection. - Claude joins the presets on both the claude and claude-agent-teams entries. Its writer stays grant-only in claude-folder-trust-file.ts: the file Claude reads (CLAUDE_CONFIG_DIR / custom-OAuth suffix / legacy .config.json / a WSL guest's own file), Claude's <file>.lock never broken and taken only when a write is due, atomic temp+rename keeping mode and symlinks, never creating the file, NFC + realpath keys. - SSH Claude launches forward the optional claudeFolderTrust spawn field so the relay grants with its own spawn env; old relays ignore it and Claude asks. Other presets keep the SFTP writer. A WSL launch never writes the Windows home: non-Claude presets skip it, Claude writes the guest's file or nothing. - Codex keeps the 20 s deadline its shared config lane needs; every other preset gets 1.5 s. A miss means the agent asks; trust bookkeeping never fails or blocks a launch. Removes the Claude-only machinery this replaces: the eligibility/host/ lifecycle/spawn modules, the persisted creation content-origin field and its classification, revoke-on-removal, the setting-off sweep, the claudeTrust.converge relay method and the Agent Teams leader special case (the leader pane now spawns through the hook with the claude preset). The agent config types move to tui-agent-config-types.ts so the config table stays under the line budget.
…ok replaces The spawn hook is now the only owner of agent folder trust, so remove every other writer: - the agentTrust:markTrusted IPC channel, its preload bridge and types, and all renderer callers (agent-trust-preflight and its callers in the background session, work-item direct launch, session continuation, worktree creation, folder workspace composer and session fork); - the main pre-spawn sites: the createdWithAgent preflight in worktree-remote.ts, markLocalWorktreeTrusted/markRemoteWorktreeTrusted and the runtime's markWorkspaceTrustedForAgent family with the markTrusted ports of the runtime create flows; - Codex's own launch-prep and resume-prep trust writes. Each of those launches reaches a spawn builder with launchAgent set, so the hook covers it. This also fixes a live gap: the worktree-remote.ts copy of the preset switch omitted Antigravity, so an agy agent started from a desktop worktree create still asked; the single dispatcher covers it. Trust is also written on the host the PTY actually spawns on, which removes the #11163 class of writing the wrong host's config.
…e spawn waits for trust The builder test passed untyped args (a string launchAgent) and cast its deps, which failed tc:node. It now builds both spawn states from a fully typed deps fixture and a typed restored pane, with no casts. Adds a case that holds the trust write pending and checks the builder does not finish until it settles, the ordering the deleted renderer and launch-prep tests used to cover.
The dispatcher gave every non-Codex preset a 1.5 s budget, including the SSH writers for Cursor, Copilot and Qoder, which make several round trips over the link. Before this PR those writes had 20 s (desktop) or no limit (runtime), so on a slow link an unattended SSH worker would now stop at the agent's trust question. SSH writes get the 20 s deadline back; local non-Codex writers keep the short budget, and Codex keeps 20 s. The relay's Claude grant keeps the short budget: it writes the relay host's own disk and does not cross the link.
A folder workspace can be the user's home folder or a disk root. Claude and Copilot let a trusted folder cover every folder under it, so pre-trusting one of those would silently trust everything on the machine for those agents. One check, isHomeOrFilesystemRoot, now refuses them for every preset: the dispatcher checks roots and this machine's homes (including the spawn env's HOME and a cached WSL guest home), the SSH writer checks the remote home it already resolves, and the relay checks its own home. The agent then asks, as it would without Orca.
…ed trust The spawn hook replaced the trust writes in Codex launch prep and resume prep, which left the fields that fed them unread: CodexHomeLaunchContext.workspacePath and .launchAgent, the resume prep's workspacePath, and the structured Codex launch input's workspacePath (plus the extra target lookup that produced it). Remove them and their plumbing; unavailableManagedHomePath stays. Also removes test stubs of runtime trust methods this PR deleted, whose not-called assertions could no longer fail, and two comments that still described the old trust preflight.
…t tests The agent-session trust gate still listed three test files this PR deleted (the renderer preflight, the Codex launch-prep deadline and the e2e trust completion suites), so check:reliability-gates, which runs in PR CI and in pnpm lint, failed on missing files. Its invariant also described the deleted IPC handler and pre-spawn writers. The gate now covers what replaced them: the spawn builders holding the spawn until trust settles, the fresh-launch and setting gates, the per-preset deadlines, and the home and root refusal.
On a Windows SSH host whose pane shell is wsl.exe, Claude runs inside the WSL guest and reads the guest's config. The relay still granted trust in the Windows host's own .claude.json, writing the Windows home for a WSL launch, which the local path never does. The relay now skips the grant there, so that Claude asks, as a local WSL launch does when Orca cannot reach the guest file.
Both spawn builders awaited the trust hook on every spawn, including plain shells, reattaches and agents without a preset. Awaiting even a resolved promise adds microtask ticks ahead of the pane-spawn reservation check, and this handler already keeps non-Codex spawns off an await because an extra tick reorders those reservation races. The hook now returns null when there is nothing to write, and the builders await only a real trust write.
| export function getLocalCodexTrustConfigFiles(): string[] { | ||
| return [ | ||
| join(getOrcaManagedCodexHomePath(), 'config.toml'), | ||
| join(getSystemCodexHomePath(), 'config.toml') | ||
| ] |
There was a problem hiding this comment.
Managed Codex home stays untrusted. When a managed account launches Codex from its own
CODEX_HOME, this list writes trust only to the shared Orca home and ~/.codex. Launch preparation has already copied the account’s config before this write, so the selected home does not receive the grant and an unattended launch can stop at Codex’s trust question.
There was a problem hiding this comment.
Actionable comments posted: 2
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Advanced
Run ID: 1c626dca-7f7f-480d-95ac-e7a71516b049
📒 Files selected for processing (127)
config/reliability-gates.jsoncdocs/reference/qoder-integration.mdsrc/main/agent-trust-presets.test.tssrc/main/agent-trust-presets.tssrc/main/agent-trust-write-deadline.tssrc/main/agent-workspace-trust-guard-failure.test.tssrc/main/agent-workspace-trust-spawn.test.tssrc/main/agent-workspace-trust-spawn.tssrc/main/agent-workspace-trust.test.tssrc/main/agent-workspace-trust.tssrc/main/claude/claude-folder-trust-file.test.tssrc/main/claude/claude-folder-trust-file.tssrc/main/codex-accounts/runtime-home-read-only-launch-resolution.test.tssrc/main/codex/codex-home-paths.tssrc/main/codex/codex-trust-config-concurrent-launch.test.tssrc/main/execution-host-workspace-trust.test.tssrc/main/execution-host-workspace-trust.tssrc/main/ipc/agent-trust.tssrc/main/ipc/pty-daemon-spawn-agent-home-env.test.tssrc/main/ipc/pty-ipc-daemon-provider-fixtures.tssrc/main/ipc/pty-ipc-spawn-drivers.tssrc/main/ipc/pty-spawn-env-agent-overlays.test.tssrc/main/ipc/pty/host-env/codex-resume.tssrc/main/ipc/pty/host-env/types.tssrc/main/ipc/pty/ipc/spawn-env-codex.tssrc/main/ipc/pty/ipc/spawn-options.tssrc/main/ipc/pty/ipc/spawn-types.tssrc/main/ipc/pty/provider/local-configure.tssrc/main/ipc/pty/runtime/controller-deps.tssrc/main/ipc/pty/runtime/spawn-options.tssrc/main/ipc/pty/runtime/spawn-preflight.tssrc/main/ipc/pty/spawn-options-agent-workspace-trust.test.tssrc/main/ipc/register-core-handlers/register-core-handlers.test.tssrc/main/ipc/register-core-handlers/register-core-handlers.tssrc/main/ipc/worktree-remote.tssrc/main/providers/pty-provider-contract.tssrc/main/providers/ssh-pty-spawn-request.tssrc/main/qoder/workspace-trust.tssrc/main/remote-agent-trust-presets.test.tssrc/main/remote-agent-trust-presets.tssrc/main/runtime/agent-terminal-launch-host.test.tssrc/main/runtime/agent-terminal-startup-prompt.test.tssrc/main/runtime/managed-worktree-create-execution-host.test.tssrc/main/runtime/orca-runtime-activate-managed-worktree.tssrc/main/runtime/orca-runtime-agent-session-operation.test.tssrc/main/runtime/orca-runtime-create-agent-session.tssrc/main/runtime/orca-runtime-create-managed-remote-worktree.tssrc/main/runtime/orca-runtime-create-managed-worktree.tssrc/main/runtime/orca-runtime-get-agent-session-execution-namespace.tssrc/main/runtime/orca-runtime-get-structured-agent-session-create-support.tssrc/main/runtime/orca-runtime-preserved-branch-cleanup.tssrc/main/runtime/orca-runtime-resolve-mobile-session-terminal-command.tssrc/main/runtime/orca-runtime-resolve-worktree-removal-target.tssrc/main/runtime/orca-runtime-state-fields.tssrc/main/runtime/orca-runtime-structured-agent-session-create-intent.test.tssrc/main/runtime/orca-runtime-terminal-create-deduplication.tssrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-02.spec.tssrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-03.spec.tssrc/main/runtime/orca-runtime-tests/worktree-setup-and-startup-part-05.spec.tssrc/main/runtime/runtime-folder-worktree-create.test.tssrc/main/runtime/runtime-folder-worktree-create.tssrc/main/runtime/runtime-local-worktree-terminal-startup.test.tssrc/main/runtime/runtime-local-worktree-terminal-startup.tssrc/main/runtime/runtime-remote-managed-worktree-create.test.tssrc/main/runtime/runtime-remote-managed-worktree-create.tssrc/main/runtime/runtime-worktree-agent-startup.test.tssrc/main/runtime/runtime-worktree-agent-startup.tssrc/main/runtime/structured-agent-account-home.tssrc/main/startup/codex-launch-preparation.tssrc/main/startup/codex-launch-trust-write-deadline.test.tssrc/main/startup/codex-session-resume-daemon-guard.test.tssrc/main/startup/codex-session-resume-launch.tssrc/main/startup/main-process-runtime-service.tssrc/main/workspace-trust-test-fixtures.tssrc/main/wsl-home-cache.tssrc/preload/api-types.tssrc/preload/api/agent-status-api.tssrc/preload/api/agent-trust-bridge.tssrc/preload/index.tssrc/relay/agent-workspace-trust-spawn-guard-failure.test.tssrc/relay/agent-workspace-trust-spawn.test.tssrc/relay/agent-workspace-trust-spawn.tssrc/relay/pty-handler-agent-workspace-trust.test.tssrc/relay/pty-handler.tssrc/renderer/src/components/agent-session-continuation/AgentSessionContinuationDialog.tsxsrc/renderer/src/components/settings/AgentsPane.test.tsxsrc/renderer/src/components/settings/AgentsPane.tsxsrc/renderer/src/components/settings/agent-workspace-trust-copy.tssrc/renderer/src/components/settings/agents-search.tssrc/renderer/src/components/sidebar/folder-workspace-composer-submit.test.tssrc/renderer/src/components/sidebar/folder-workspace-composer-submit.tssrc/renderer/src/components/terminal-pane/terminal-agent-session-fork.test.tssrc/renderer/src/components/terminal-pane/terminal-agent-session-fork.tssrc/renderer/src/hooks/settings-navigation-capability-sections.tssrc/renderer/src/i18n/en-runtime-required.jsonsrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/i18n/locales/es.jsonsrc/renderer/src/i18n/locales/fr.jsonsrc/renderer/src/i18n/locales/ja.jsonsrc/renderer/src/i18n/locales/ko.jsonsrc/renderer/src/i18n/locales/zh.jsonsrc/renderer/src/lib/agent-background-session-test-state.tssrc/renderer/src/lib/agent-trust-preflight.test.tssrc/renderer/src/lib/agent-trust-preflight.tssrc/renderer/src/lib/codex-session-restart.tssrc/renderer/src/lib/launch-agent-background-session-remote.test.tssrc/renderer/src/lib/launch-agent-background-session.test.tssrc/renderer/src/lib/launch-agent-background-session.tssrc/renderer/src/lib/launch-agent-session-continuation.test.tssrc/renderer/src/lib/launch-agent-session-continuation.tssrc/renderer/src/lib/launch-work-item-direct-agent-routing.test.tssrc/renderer/src/lib/launch-work-item-direct-agent-routing.tssrc/renderer/src/lib/launch-work-item-direct-route-preparation.tssrc/renderer/src/lib/launch-work-item-direct.test.tssrc/renderer/src/lib/worktree-creation-flow-execute.tssrc/renderer/src/lib/worktree-creation-flow.test.tssrc/shared/agent-workspace-trust-spawn-request.tssrc/shared/default-global-settings.tssrc/shared/global-settings-types.tssrc/shared/home-or-filesystem-root.test.tssrc/shared/home-or-filesystem-root.tssrc/shared/terminal-startup-cwd.tssrc/shared/tui-agent-config-types.tssrc/shared/tui-agent-config.tstests/e2e/agent-trust-completion.unit.test.tstests/e2e/helpers/electron-home-isolation.tstests/e2e/helpers/electron-home-isolation.unit.test.ts
💤 Files with no reviewable changes (41)
- src/renderer/src/lib/launch-agent-background-session.test.ts
- src/preload/index.ts
- src/main/ipc/register-core-handlers/register-core-handlers.ts
- src/main/runtime/orca-runtime-create-managed-remote-worktree.ts
- src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-03.spec.ts
- src/renderer/src/lib/worktree-creation-flow.test.ts
- src/main/ipc/register-core-handlers/register-core-handlers.test.ts
- src/renderer/src/lib/agent-trust-preflight.ts
- src/main/runtime/runtime-remote-managed-worktree-create.test.ts
- src/preload/api/agent-trust-bridge.ts
- src/main/runtime/orca-runtime-resolve-worktree-removal-target.ts
- src/renderer/src/components/sidebar/folder-workspace-composer-submit.ts
- src/main/runtime/orca-runtime-get-agent-session-execution-namespace.ts
- src/preload/api/agent-status-api.ts
- src/main/ipc/agent-trust.ts
- src/renderer/src/components/agent-session-continuation/AgentSessionContinuationDialog.tsx
- src/main/runtime/orca-runtime-resolve-mobile-session-terminal-command.ts
- src/renderer/src/lib/agent-trust-preflight.test.ts
- src/renderer/src/lib/launch-agent-background-session.ts
- src/main/startup/codex-launch-trust-write-deadline.test.ts
- src/renderer/src/lib/launch-work-item-direct-agent-routing.ts
- src/renderer/src/components/terminal-pane/terminal-agent-session-fork.ts
- tests/e2e/agent-trust-completion.unit.test.ts
- src/main/runtime/runtime-folder-worktree-create.test.ts
- src/renderer/src/lib/launch-agent-background-session-remote.test.ts
- src/main/runtime/runtime-local-worktree-terminal-startup.ts
- src/main/runtime/runtime-local-worktree-terminal-startup.test.ts
- src/main/ipc/pty/runtime/controller-deps.ts
- src/main/remote-agent-trust-presets.test.ts
- src/main/runtime/runtime-remote-managed-worktree-create.ts
- src/main/runtime/orca-runtime-create-agent-session.ts
- src/main/ipc/pty/host-env/types.ts
- src/main/runtime/runtime-folder-worktree-create.ts
- src/renderer/src/lib/launch-work-item-direct-route-preparation.ts
- src/main/ipc/pty/ipc/spawn-types.ts
- src/main/startup/codex-launch-preparation.ts
- src/renderer/src/lib/agent-background-session-test-state.ts
- src/main/remote-agent-trust-presets.ts
- src/renderer/src/lib/launch-agent-session-continuation.test.ts
- src/renderer/src/lib/launch-agent-session-continuation.ts
- src/main/runtime/orca-runtime-state-fields.ts
🚧 Files skipped from review as they are similar to previous changes (7)
- src/renderer/src/i18n/en-runtime-required.json
- src/renderer/src/i18n/locales/ja.json
- src/renderer/src/i18n/locales/en.json
- src/renderer/src/i18n/locales/zh.json
- src/renderer/src/i18n/locales/ko.json
- src/renderer/src/i18n/locales/fr.json
- src/renderer/src/i18n/locales/es.json
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
| /** | ||
| * Pre-trusts `workspacePath` for the agent Orca is about to start, on the host that runs | ||
| * it. Never throws and never waits past the preset's deadline: a miss means the agent asks. | ||
| */ | ||
| export async function applyAgentWorkspaceTrust( | ||
| preset: AgentTrustPreset, | ||
| workspacePath: string, | ||
| context: AgentTrustLaunchContext | ||
| ): Promise<AgentTrustSpawnFields> { | ||
| if (context.connectionId) { | ||
| // Why: the SSH host's relay writes on its own disk, under its own homes and the agent's final env. | ||
| return { agentWorkspaceTrust: { workspacePath } } | ||
| } | ||
| // Why: the other writers target this host's home, which a WSL guest agent never reads. | ||
| if (preset !== 'claude' && isWslLaunch(workspacePath, context)) { | ||
| return {} | ||
| } | ||
| await applyWorkspaceTrustOnThisHost(preset, workspacePath, () => ({ | ||
| homes: localHomePaths(workspacePath, context), | ||
| claudeConfig: () => | ||
| resolveLocalClaudeTrustConfig({ | ||
| workspacePath, | ||
| env: { ...process.env, ...context.env }, | ||
| claudeAuth: context.claudeAuth, | ||
| wslDistro: context.wslDistro | ||
| }), | ||
| codexConfigFiles: getLocalCodexTrustConfigFiles, | ||
| // Why: Codex queues behind a config lane it shares with Orca's hook installs. | ||
| deadlineMs: | ||
| preset === 'codex' ? AGENT_TRUST_WRITE_DEADLINE_MS : SHORT_AGENT_TRUST_WRITE_DEADLINE_MS | ||
| })) | ||
| return {} | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
sed -n '45,80p' src/main/agent-workspace-trust.ts
sed -n '40,65p' src/main/codex/codex-home-paths.ts
sed -n '110,145p' src/main/execution-host-workspace-trust.ts
rg -n 'CODEX_HOME|getLocalCodexTrustConfigFiles' src/main/ipc/pty/provider/local-configure.ts src/main/agent-workspace-trust.ts src/main/codexRepository: stablyai/orca
Length of output: 22192
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- trust module outline and callers ---'
ast-grep outline src/main/agent-workspace-trust.ts
rg -n -C 5 'applyAgentWorkspaceTrust|applyWorkspaceTrustOnThisHost|getLocalCodexTrustConfigFiles|localHomePaths' src/main src
printf '%s\n' '--- codex home paths and trust writer ---'
ast-grep outline src/main/codex/codex-home-paths.ts
rg -n -C 8 'markCodexProjectTrusted|codexConfigFiles|configFiles|config\.toml|CODEX_HOME' src/main/codex src/main/agent-workspace-trust.ts -g '*.ts'
printf '%s\n' '--- PTY/custom-home launch path ---'
rg -n -C 8 'applyAgentWorkspaceTrust|AgentTrustLaunchContext|CODEX_HOME|customCodexHome|codexHome' src/main/ipc src/main -g '*.ts' | head -n 500Repository: stablyai/orca
Length of output: 45667
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- agent workspace trust ---'
cat -n src/main/agent-workspace-trust.ts
printf '%s\n' '--- codex home paths ---'
cat -n src/main/codex/codex-home-paths.ts
printf '%s\n' '--- trust writer entrypoints ---'
rg -l 'function markCodexProjectTrusted|export.*markCodexProjectTrusted|codexConfigFiles' src/main -g '*.ts' | sort
printf '%s\n' '--- trust spawn adapter ---'
cat -n src/main/agent-workspace-trust-spawn.ts
printf '%s\n' '--- PTY production callers ---'
rg -l 'applyAgentWorkspaceTrust|registerPtyHandlers|pty:spawn' src/main/ipc -g '*.ts' | sortRepository: stablyai/orca
Length of output: 25391
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- trust dispatch call sites ---'
rg -n -C 12 'applyAgentWorkspaceTrustToSpawn|applyStructuredCodexWorkspaceTrust' src -g '*.ts'
printf '%s\n' '--- custom CODEX_HOME launch resolution ---'
rg -n -C 12 'getCustomCodexHomeOverrideForLaunch|environmentHomeOverride|custom-home|custom CODEX_HOME|CODEX_HOME.*env|env.*CODEX_HOME' src/main/ipc src/main/codex -g '*.ts' | head -n 700
printf '%s\n' '--- PTY spawn sequencing ---'
rg -n -C 20 'agentWorkspaceTrust|spawnOptionsAgent|applyAgentWorkspaceTrust' src/main/ipc/pty src/main/ipc/pty.ts src/main/ipc -g '*.ts' | head -n 700
printf '%s\n' '--- Codex trust consumer and config contract ---'
rg -n -C 12 'markCodexProjectTrusted|projects\\.|trust_level|config\.toml|runtime home|CODEX_HOME' src/main/agent-trust-presets.ts src/main/codex/codex-config-mirror.ts src/main/codex/codex-trust-config-concurrent-launch.test.ts src/main/execution-host-workspace-trust.test.ts -g '*.ts' | head -n 1000Repository: stablyai/orca
Length of output: 42877
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- non-test trust call sites ---'
rg -n -C 18 'applyAgentWorkspaceTrustToSpawn|applyStructuredCodexWorkspaceTrust' src -g '*.ts' -g '!*.test.ts'
printf '%s\n' '--- custom-home resolver definitions and callers ---'
rg -n -C 20 'getCustomCodexHomeOverrideForLaunch|environmentHomeOverride|homeRoute|CODEX_HOME' src/main/codex/codex-real-home-path.ts src/main/ipc/pty*.ts src/main/ipc/pty -g '*.ts' -g '!*.test.ts' | head -n 900
printf '%s\n' '--- workspace host and writer contracts ---'
cat -n src/main/execution-host-workspace-trust.ts | sed -n '1,180p'
rg -n -C 20 'markCodexProjectTrusted|function writePreset|codexConfigFiles' src/main/agent-trust-presets.ts src/main/execution-host-workspace-trust.tsRepository: stablyai/orca
Length of output: 42775
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- exact IPC spawn trust sequence ---'
cat -n src/main/ipc/pty/ipc/spawn-options.ts | sed -n '30,125p'
printf '%s\n' '--- runtime spawn trust sequence ---'
rg -n -C 20 'applyAgentWorkspaceTrustToSpawn' src/main/ipc/pty -g '*.ts' -g '!*.test.ts'
printf '%s\n' '--- final environment construction ---'
rg -n -C 18 'type PtyIpcSpawnState|interface PtyIpcSpawnState|ctx\.env|spawnEnv|buildPtyIpcSpawnOptions' src/main/ipc/pty/ipc src/main/ipc/pty/provider src/main/ipc/pty -g '*.ts' -g '!*.test.ts' | head -n 1000
printf '%s\n' '--- custom-home launch resolver exact source ---'
cat -n src/main/codex/codex-real-home-path.ts | sed -n '1,145p'Repository: stablyai/orca
Length of output: 42732
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- selected Codex home contract ---'
rg -n -C 20 'getSelectedCodexHomePath|selectedCodexHomePath|getCompatibleSelectedCodexHomePath|resolve.*Codex.*Home' src/main src/shared -g '*.ts' -g '!*.test.ts' | head -n 1200
printf '%s\n' '--- custom-home config references ---'
rg -n -C 12 'custom.*config\.toml|config\.toml.*custom|CODEX_HOME.*config\.toml|config\.toml.*CODEX_HOME|Every config\.toml|configFiles' src/main/codex src/main/agent-trust-presets.ts -g '*.ts' -g '!*.test.ts'
printf '%s\n' '--- changed hunk and related baseline ---'
git diff --unified=25 31012aeb09283dc901a2c7417747e5a081f33cbf -- src/main/agent-workspace-trust.ts src/main/codex/codex-home-paths.ts src/main/execution-host-workspace-trust.ts src/main/agent-trust-presets.tsRepository: stablyai/orca
Length of output: 42198
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- selected home override consumer ---'
rg -n -C 24 'codexHomePathOverride' src/main -g '*.ts' -g '!*.test.ts'
printf '%s\n' '--- local provider configuration ---'
cat -n src/main/ipc/pty/provider/local-configure.ts | sed -n '1,260p'
printf '%s\n' '--- spawn option/provider types ---'
rg -n -C 15 'codexHomePathOverride|PtySpawnOptions|spawnOptions' src/main/ipc/pty/provider src/main/ipc/pty -g '*.ts' -g '!*.test.ts' | head -n 900Repository: stablyai/orca
Length of output: 41965
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- selected-home definitions only ---'
rg -n -C 8 'getSelectedCodexHomePath|getCompatibleSelectedCodexHomePath' src/main --glob '*.ts' --glob '!*.test.ts' | grep -E '^[^:]+:[0-9]+|getSelectedCodexHomePath|getCompatibleSelectedCodexHomePath|return|CODEX_HOME|custom|managed|system' | head -n 500
printf '%s\n' '--- provider custom-home selection and final env ---'
cat -n src/main/ipc/pty/provider/local-configure.ts | sed -n '35,115p'
cat -n src/main/providers/local-pty-spawn-environment.ts | sed -n '1,80p'Repository: stablyai/orca
Length of output: 12875
Write trust to the selected Codex home.
When a fresh local Codex PTY uses a distinct CODEX_HOME, the trust dispatch writes only the managed and system config.toml files. Codex can therefore read the custom home without the new project entry and may show its trust prompt.
Pass the selected home to the trust dispatcher and include <CODEX_HOME>/config.toml in the write list.
Suggested fix
diff --git a/src/main/agent-workspace-trust.ts b/src/main/agent-workspace-trust.ts
@@
export type AgentTrustLaunchContext = {
/** The final spawn env, before the host's own process env. */
env: Record<string, string | undefined> | undefined
+ /** The home selected for this launch, when PTY preparation has resolved one. */
+ codexHomePath?: string | null
@@
- codexConfigFiles: getLocalCodexTrustConfigFiles,
+ codexConfigFiles: () =>
+ getLocalCodexTrustConfigFiles(context.codexHomePath ?? context.env?.CODEX_HOME),diff --git a/src/main/codex/codex-home-paths.ts b/src/main/codex/codex-home-paths.ts
@@
-export function getLocalCodexTrustConfigFiles(): string[] {
- return [
+export function getLocalCodexTrustConfigFiles(codexHomePath?: string): string[] {
+ const configFiles = [
join(getOrcaManagedCodexHomePath(), 'config.toml'),
join(getSystemCodexHomePath(), 'config.toml')
]
+ const customHome = codexHomePath?.trim()
+ if (customHome) {
+ const customConfig = join(customHome, 'config.toml')
+ if (!configFiles.includes(customConfig)) {
+ configFiles.unshift(customConfig)
+ }
+ }
+ return configFiles
}diff --git a/src/main/agent-workspace-trust-spawn.ts b/src/main/agent-workspace-trust-spawn.ts
@@
return applyAgentWorkspaceTrust(preset, workspacePath, {
env: args.env,
+ codexHomePath: args.codexHomePath,
claudeAuth: args.claudeAuth,diff --git a/src/main/ipc/pty/ipc/spawn-options.ts b/src/main/ipc/pty/ipc/spawn-options.ts
@@
settings: ctx.deps.getSettings?.(),
env: ctx.spawnEnv,
+ codexHomePath: ctx.selectedCodexHomePath,
claudeAuth: ctx.claudeAuth,diff --git a/src/main/ipc/pty/runtime/spawn-options.ts b/src/main/ipc/pty/runtime/spawn-options.ts
@@
settings: ctx.deps.getSettings?.(),
env: ctx.env,
+ codexHomePath: ctx.selectedCodexHomePath,
claudeAuth: ctx.claudeAuth,| export function getAgentWorkspaceTrustDescription(): string { | ||
| return translate( | ||
| 'auto.components.settings.agent-workspace-trust-copy.description', | ||
| 'Agents Orca starts skip their "trust this folder?" prompt in that worktree or folder, so the project\'s agent hooks and settings run without asking first. Turning this off stops new trust; folders already trusted stay trusted. While it is off, agents Orca starts unattended, such as orchestration workers, automations and agents started from your phone, stop at the trust question until someone answers it.' |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
sed -n '1,42p' src/renderer/src/components/settings/agent-workspace-trust-copy.ts
sed -n '44,55p' docs/reference/qoder-integration.md
sed -n '15,48p' src/relay/agent-workspace-trust-spawn.ts
sed -n '115,141p' src/main/execution-host-workspace-trust.tsRepository: stablyai/orca
Length of output: 4801
🏁 Script executed:
printf '%s\n' '--- relevant files ---'
git ls-files | rg '(^|/)(agent-workspace-trust-copy|qoder-integration|agent-workspace-trust|i18n|translation|localization)'
printf '%s\n' '--- trust-copy and Qoder text occurrences ---'
rg -n -i 'Trust the folder when Orca starts an agent|Every launch Orca starts|pre-trusts the workspace|trust this folder|qodercli|qoder' src docs --glob '!**/node_modules/**' | head -240
printf '%s\n' '--- relay trust bindings and preset definitions ---'
rg -n 'TUI_AGENT_CONFIG|preflightTrust|AGENT_TRUST_INHERITS_FROM_A_HOME|wouldTrustAHome|antigravity|wslShell' src/relay src/main src --glob '*.ts' | head -260
printf '%s\n' '--- changed files in PR ---'
git diff --stat 31012aeb09283dc901a2c7417747e5a081f33cbf e0323411778075e9238eaae448af559dd29bc564 -- src/renderer/src/components/settings/agent-workspace-trust-copy.ts docs/reference/qoder-integration.md src/relay/agent-workspace-trust-spawn.ts src/main/execution-host-workspace-trust.tsRepository: stablyai/orca
Length of output: 41575
🏁 Script executed:
printf '%s\n' '--- execution-host trust implementation ---'
cat -n src/main/execution-host-workspace-trust.ts | sed -n '1,175p'
printf '%s\n' '--- trust preset configuration and home policy ---'
cat -n src/main/agent-trust-presets.ts | sed -n '1,155p'
cat -n src/shared/tui-agent-config.ts | sed -n '1,90p'
printf '%s\n' '--- relevant trust tests ---'
cat -n src/main/execution-host-workspace-trust.test.ts | sed -n '1,145p'
cat -n src/relay/agent-workspace-trust-spawn.test.ts | sed -n '190,230p'
printf '%s\n' '--- documentation diff ---'
git diff --unified=12 31012aeb09283dc901a2c7417747e5a081f33cbf e0323411778075e9238eaae448af559dd29bc564 -- docs/reference/qoder-integration.md src/renderer/src/components/settings/agent-workspace-trust-copy.tsRepository: stablyai/orca
Length of output: 29999
Qualify the prompt-free trust guarantee.
The setting description omits reachable exceptions. Antigravity over SSH, non-Claude agents in WSL, and Qoder launches from a home folder or its ancestor can still show the trust prompt. Apply the same qualification to the English, Spanish, French, Japanese, Korean, and Chinese catalog entries.
Suggested copy update
- 'Agents Orca starts skip their "trust this folder?" prompt in that worktree or folder, so the project\'s agent hooks and settings run without asking first. Turning this off stops new trust; folders already trusted stay trusted. While it is off, agents Orca starts unattended, such as orchestration workers, automations and agents started from your phone, stop at the trust question until someone answers it.'
+ 'For supported launch paths, agents Orca starts skip their "trust this folder?" prompt in that worktree or folder, so the project\'s agent hooks and settings run without asking first. Antigravity over SSH, non-Claude agents started in WSL, and Qoder agents started in a home folder or an ancestor of a home folder can still show the trust prompt. Turning this off stops new trust; folders already trusted stay trusted. While it is off, agents Orca starts unattended, such as orchestration workers, automations and agents started from your phone, stop at the trust question until someone answers it.'-Every launch Orca starts (New Tab, workspace and draft launches, automations)
-pre-trusts the workspace at spawn while the agent-wide "Trust the folder when
-Orca starts an agent" setting is on. A hand-typed `qodercli` in a plain terminal
-can still show Qoder's trust dialog.
+Supported Orca launches (New Tab, workspace and draft launches, automations)
+pre-trust the workspace at spawn while the agent-wide "Trust the folder when
+Orca starts an agent" setting is on. Qoder does not pre-trust a workspace that
+is a home folder or an ancestor of a home folder. Those launches can still show
+Qoder's trust dialog. A hand-typed `qodercli` in a plain terminal can also show
+Qoder's trust dialog.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 'Agents Orca starts skip their "trust this folder?" prompt in that worktree or folder, so the project\'s agent hooks and settings run without asking first. Turning this off stops new trust; folders already trusted stay trusted. While it is off, agents Orca starts unattended, such as orchestration workers, automations and agents started from your phone, stop at the trust question until someone answers it.' | |
| 'For supported launch paths, agents Orca starts skip their "trust this folder?" prompt in that worktree or folder, so the project\'s agent hooks and settings run without asking first. Antigravity over SSH, non-Claude agents started in WSL, and Qoder agents started in a home folder or an ancestor of a home folder can still show the trust prompt. Turning this off stops new trust; folders already trusted stay trusted. While it is off, agents Orca starts unattended, such as orchestration workers, automations and agents started from your phone, stop at the trust question until someone answers it.' |
There was a problem hiding this comment.
ℹ️ No critical issues — the launch-path migration and the guard hold up under a full read. One narrow edge in the folder-trust guard and a comment nit are inline below.
Reviewed changes
- One spawn-time trust step:
applyAgentWorkspaceTrustToSpawnruns in both PTY spawn builders (ipc/pty/ipc/spawn-options.ts,ipc/pty/runtime/spawn-options.ts) and now also backs the structured-Codex path, replacing the deleted rendererpreflightAgentTrustand its seven callers, theagentTrust:markTrustedIPC + preload bridge,markLocal/RemoteWorktreeTrusted,markWorkspaceTrustedForAgentand the Codex launch-prep writes. I traced each deleted site to a spawn that declareslaunchAgentwith a defined command, so none lose coverage. - One dispatcher/host writer:
applyAgentWorkspaceTrustroutes SSH to the relay and everything else throughapplyWorkspaceTrustOnThisHost, which picks the stored path, applies the breadth guard for the inheriting agents (Claude, Copilot, Qoder) and calls the existing per-preset writers. The new Claude writer is lock-respecting, atomic and never creates a missing config;markCodexProjectTrustednow takes the config-file list. The guard's segment-boundary check means home / ancestor-of-home / root /../ symlink / drive-case / WSL-UNC-alias spellings are refused without false-positives. - SSH relay: a new optional
agentWorkspaceTrustfield rides the existingpty.spawnrequest and the relay writes on its own disk before the process starts, replacing the multi-round-trip SFTP writer. Old relays ignore the field. - Setting + UI:
agentWorkspaceTrustEnableddefaults on, sits in Settings → Agents, and is hidden from paired web clients and their settings search. - Testing: new unit suites for both builders, the dispatcher, the host writer, the Claude writer (mode, symlink, corrupt file, held lock, a 12-way burst), the relay handler and the structured-Codex intent; the old e2e unit file is deleted and the e2e harness now strips
CLAUDE_CONFIG_DIR.
ℹ️ Nitpicks
src/relay/agent-workspace-trust-spawn.ts:43says "each preset gets the local budget", butdeadlineMsis the 1.5s short budget for every preset including Codex. The PR body and the reliability gate both describe the 1.5s SSH budget as intended, so the comment is the misleading part; say "the short budget", or mirror the localpreset === 'codex' ? long : shortsplit if that was the actual intent.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
| const wslWorkspace = parseWslUncPath(workspacePath) | ||
| // Why: a WSL guest agent reads trust under the guest's home; an uncached one means write nothing. | ||
| return wslWorkspace | ||
| ? [getCachedWslHome(wslWorkspace.distro)] |
There was a problem hiding this comment.
The breadth guard compares the stored path against the homes in their host spelling, but for a WSL launch these two sides can land in different path spaces. getCachedWslHome returns whatever getWslHome cached, which is a Windows UNC path for a normal guest home but a drive path when the guest's $HOME is a drvfs mount (toWindowsWslPath prefers toWindowsWslDrivePath). Meanwhile the writer stores the Linux path via toClaudePath. When they differ, normalizeRuntimePathForComparison never bridges c:/users/alice with //wsl/<distro>/mnt/c/Users/alice, so a home or an ancestor can slip past the guard.
Technical details
# WSL home spelling diverges from the stored key in the breadth guard
## Affected sites
- `src/main/agent-workspace-trust.ts:41-45` — `localHomePaths` keys the guard home on `getCachedWslHome(distro)`, the host spelling.
- `src/main/claude/claude-folder-trust-file.ts:263-282` — the WSL writer maps the workspace through `toClaudePath` (`parseWslUncPath(...).linuxPath`) and stores that Linux key.
- `src/main/wsl.ts:274-297` — `getWslHome` caches `toWindowsWslPath(home, distro)`, which for `/mnt/<drive>/...` returns a drive path (`C:\Users\alice`) rather than the UNC alias.
## Preconditions (all required, so this is narrow)
1. The WSL guest's `$HOME` is under `/mnt/<drive>` (uncommon; default is `/home/<user>`).
2. A managed WSL Claude account is selected, so `ClaudeRuntimeAuthPreparation` returns `runtime: 'wsl'` with a `wslLinuxConfigDir` and a config dir the writer can target. The system-WSL branch bails when `parseWslUncPath(getWslHome(distro))` is null, so a non-managed WSL Claude launch writes nothing here.
3. The workspace is the drvfs home itself or a folder above it, spelled as a WSL UNC path.
## Required outcome
- The guard must refuse a drvfs guest home or an ancestor of it exactly as it refuses a `/home/...` guest home, i.e. both sides of `wouldTrustAHome` must be expressed in one path space.
## Suggested approach (optional)
- Add the workspace distro's home in the same space as the workspace to `localHomePaths` (for example the UNC alias of the cached home), or run the guard over the `toClaudePath`-mapped forms so it compares `//wsl/<distro>/...` to `//wsl/<distro>/...`.
## Open questions for the human (optional)
- Is a managed WSL Claude account with a `$HOME` on `/mnt/<drive>` a configuration Orca intends to support? If not, a note that the guard is best-effort for that layout may be enough.…orktree-trust # Conflicts: # src/main/startup/codex-launch-trust-write-deadline.test.ts # src/main/startup/codex-session-resume-launch.ts
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Advanced
Run ID: 3630002f-fdce-48b4-a527-7d107f16ad99
📒 Files selected for processing (17)
src/main/ipc/pty/host-env/types.tssrc/main/ipc/pty/ipc/spawn-env-codex.tssrc/main/ipc/pty/provider/local-configure.tssrc/main/ipc/pty/runtime/spawn-preflight.tssrc/main/startup/codex-launch-preparation.tssrc/main/startup/codex-launch-trust-write-deadline.test.tssrc/main/startup/codex-session-resume-launch.tssrc/relay/pty-handler.tssrc/renderer/src/components/settings/AgentsPane.test.tsxsrc/renderer/src/components/settings/agent-workspace-trust-copy.tssrc/renderer/src/i18n/en-runtime-required.jsonsrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/i18n/locales/es.jsonsrc/renderer/src/i18n/locales/fr.jsonsrc/renderer/src/i18n/locales/ja.jsonsrc/renderer/src/i18n/locales/ko.jsonsrc/renderer/src/i18n/locales/zh.json
💤 Files with no reviewable changes (4)
- src/main/startup/codex-launch-trust-write-deadline.test.ts
- src/main/ipc/pty/host-env/types.ts
- src/main/startup/codex-launch-preparation.ts
- src/main/startup/codex-session-resume-launch.ts
🚧 Files skipped from review as they are similar to previous changes (6)
- src/renderer/src/i18n/locales/ko.json
- src/renderer/src/i18n/locales/en.json
- src/renderer/src/i18n/locales/ja.json
- src/renderer/src/i18n/locales/fr.json
- src/renderer/src/i18n/locales/es.json
- src/renderer/src/i18n/locales/zh.json
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.
| await applyRelayAgentWorkspaceTrust(params.agentWorkspaceTrust, launchAgent, spawnEnv, { | ||
| wslShell: isRelayWslShell(shell) | ||
| }) |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Check cancellation before writing persistent trust.
If the client disconnects during loadPty() or buildSpawnEnv(), this call can still persist the folder’s trust record. The check at Line 1976 then rejects the launch with client_disconnected. No agent starts, but the folder remains trusted.
Check context.signal.aborted and context.isStale() before this call. Keep the existing check before native spawn.
…3789, #23812, #23852) into brennanb2025/codex-home-single-writer #23744 moves every agent's folder pre-trust into the PTY spawn hook (and the structured Codex chat create), removing Codex's launch-prep and resume-prep trust writes. markCodexProjectTrusted now takes the config files to write. Conflicts: - agent-trust-presets.ts: #23744's configFiles signature and nested lane acquisition, with this branch's lane rationale (hook installs read and rewrite across awaits); main's "capture->restore window" wording described the grant snapshot this branch removed. - agent-trust-write-deadline.ts: the Codex writer waits only on managed-home installs' inline grant; the real-home background approval holds no lane. #23744's SSH/SFTP writer is gone, so it is no longer named; its 1.5 s short deadline is kept. - codex-trust-config-concurrent-launch.test.ts: this branch's tests (a launch's write lands while a grant session runs; the renamed sibling-failure case) with #23744's new call signature. Outside the conflicts: codex-launch-during-real-home-grant.test.ts passed launchAgent/workspacePath into launch prep, which #23744 removed; its launch helper now runs launch prep and then applyAgentWorkspaceTrust, as the spawn builder does, so it still proves a launch and its project trust land while the real-home approval hangs.
Keeps #23744's exported resolveCodexProjectTrustRoot and its configFiles-based markCodexProjectTrusted, with this branch's Codex-mirroring root rule. Drops main's "trusts the main repository root for a linked worktree without reading commondir" test: its fixture has no commondir, a layout Codex's resolve_root_git_project_for_trust rejects (it keys trust on the worktree), so it encoded behaviour Codex does not have. The branch's real-git layout tests cover the case instead. linkGitWorktree now writes commondir as git does, so the execution-host trust tests describe a worktree Codex would recognise.
Main (#23744, #23937) moved every agent trust write into one dispatcher, applyWorkspaceTrustOnThisHost, run on the host that starts the agent: main for local and WSL launches, the SSH relay for remote ones. The desktop SFTP trust writer, the trust IPC and the per-call-site launch-prep writes are gone. Re-applied on top: - markCodexProjectTrusted attempts every config.toml it is given, so a file Orca refuses to edit never leaves another Codex home untrusted. - The dispatcher reports refused Codex config edits once per file and message. - smol-toml is loaded with a literal require so the relay's esbuild bundle inlines it; relay hosts have no node_modules, and createRequire made the relay fail to load. - Remote trust tests ported to the relay writer; hook-status log-once test moved out of the test file main deleted.
…ership #23930 merged current main, whose #23744/#23937 moved trust writing into applyWorkspaceTrustOnThisHost (local) and the SSH relay. Resolved by taking that design and dropping what it already covers: the per-home trust writes, the launch-prep trust writes and their shared budget, and the worker-start trust diagnosis (moved to a follow-up; #23847 is now referenced, not fixed). Kept on top of it: - Local Codex trust also reaches the per-account home the launch reads: the launch env's CODEX_HOME joins getLocalCodexTrustConfigFiles, as the relay already does. - An OS error that stops a trust write names config.toml, not the atomic writer's temp file; config refusals pass through unchanged. - The WSL hook-trust relocation now logs through warnCodexConfigOnce and writes through withConfigPath, the base's single refusal and write rules.
…er (#23929) * feat(settings): add a setting to run Codex terminals on Codex's shared server Off injects ORCA_CODEX_ISOLATE=0 into new terminals on every host (local, daemon, SSH relay, WSL), which the codex shell wrapper from #23900 reads to skip --no-daemon. On (the default) injects nothing. * feat(terminal): announce per-terminal Codex servers once Shows a one-time toast the first time a Codex terminal starts, with an Open Settings action that lands on the new Codex server setting. The seen flag persists in UI state and is set when the toast is shown. * fix(settings): drop the status warning from the Codex server setting * fix(terminal): simplify the Codex shared-server notice * fix(terminal): say agent status in the Codex notice * fix(settings): hide the Codex server setting from paired web search Gives its search entry an id and gates it with includeCodexTerminalServerIsolation, as the other host-only rows are, so a paired web client cannot find a row it never renders. Its search keywords now use catalogued agents-search keys instead of missing ones; CODEX_ISOLATE_ENV is no longer exported; the toast id comment names the case it guards. * test(settings): assert the Codex server search gate through the metadata builder Calling getAgentsPaneSearchEntries directly stayed green with the web gate deleted; the metadata builder test fails without it. Backport: #23744 (workspace trust setting) is not on this branch, so its adjacent setting, search entry and test are left out. (cherry picked from commit a0abcb6)
Upstream stablyai#23744 removed runtime.markWorkspaceTrustedForAgent, which the host resume called while preparing its launch. On the merged tree the call threw and every resume fell back to a plain shell. A resume re-opens a conversation that already ran in that folder, so it drops its own trust write, the input that carried it and the tests that pinned it. The spawn itself goes through upstream's spawn builder like upstream's own ensureAgentSession resume. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

What this fixes
When Orca starts a coding agent in a folder the agent has never seen, many agents stop at their own "Do you trust this folder?" question and wait for a key press. Claude Code always did this in Orca. Cursor, Copilot, Qoder and Antigravity did it on Orca's most common launch path (a new tab, quick launch, the dashboard, source control, fix-checks), because that path never ran Orca's trust step. An unattended launch (an orchestration worker, an automation, the phone,
orca worktree create --agent …) had nobody to press the key, so it sat there until Orca reported it as blocked (#22927). And the agents didn't behave the same: whether you got the question depended on which agent and which button you used.Scope: agent terminals that Orca itself starts, for agents with a trust preset (Claude Code, Claude Agent Teams, Codex, Cursor, Copilot, Qoder, Antigravity), in any worktree, main checkout or folder workspace, on macOS, Linux, Windows and SSH hosts, and for Claude also inside WSL. Not affected: an agent you type yourself in a plain terminal, and agents without a trust preset. Structured (native) chats never show the question; a structured Codex chat still gets Orca's Codex trust write before it starts, as on main.
ELI5
Some agents ask "do you trust this folder?" the first time they run somewhere. Orca now answers "yes" for them, ahead of time, whenever Orca is the one starting the agent in your workspace, the same way for every agent. One switch in Settings turns it off.
What Changed
After, with the new setting on (the default): when Orca starts an agent with a trust preset, Orca writes that agent's own "trusted" record for the workspace just before the agent's process starts, so the agent opens straight to its prompt. Per agent:
CODEX_HOME.CODEX_HOME. Inside WSL it still asks, as before.Setting: Settings → Agents → "Trust the folder when Orca starts an agent", on by default, translated for es, fr, ja, ko and zh. Its text: "Agents Orca starts skip their "Do you trust this folder?" prompt, so the project's agent hooks and settings run right away. Without this, each agent asks the first time it runs in a folder. Agents running without you watching, such as orchestration workers, automations and agents started from your phone, will wait at that question until you answer it in their terminal. Folders already trusted stay trusted." It is hidden in the paired web client, like the other host-machine settings.
What Orca never does:
/Users), or a disk root, however the path reaches it (a symlink or..). That is Claude Code (its trust check walks up parent folders; outside a git repository it goes all the way to the disk root), Copilot and Qoder (any trusted folder above the current one counts). The check runs on the exact path each agent would store, on the machine that runs the agent. Codex, Cursor and Antigravity are trusted there exactly as before this PR: Codex checks only the exact folder and then its repository root, Antigravity only the exact folder, and Cursor itself refuses to inherit trust from a home.Worth knowing: Claude Code and Codex treat every worktree of a repository as trusted once its main checkout is. So after Orca (or you) trusts a repository's main checkout, its worktrees don't ask either, even with this setting off, and that includes worktrees of pull requests from forks. That is how those agents work, and it is why this PR has no fork-specific exception: one could not hold for the two most-used agents.
Where an agent now asks that didn't on main: only Copilot and Qoder when the workspace is your home folder, a folder above a home, a disk root or a symlink to one of these (main pre-trusted those on some launch paths; since both agents extend a trusted folder to everything below it, that entry would have trusted every folder you own). With the setting turned off, every agent asks, by your choice. Everywhere else, every agent is trusted wherever it was on main, and on more launch paths.
Mechanism
applyWorkspaceTrustOnThisHostworks out the exact path the agent's existing writer will store, refuses it if it is too broad (above), then calls that writer with what it needs from the launch (the final environment, the managed Claude account, the WSL distro). The desktop uses it for local and WSL launches, and the SSH relay uses the same code on the remote machine. It replaces three copied switches; one of them had left Antigravity out.projects[<folder>].hasTrustDialogAcceptedin the config file that Claude process reads (CLAUDE_CONFIG_DIR, managed accounts, the legacy.config.jsonrule, a WSL guest's own file). The writer takes Claude's own<file>.lockand never breaks a held lock, re-reads under it, renames atomically, never creates a missing file, refuses a file it can't parse, keeps the file's mode and follows a symlinked config. Writes to the same file queue inside Orca, so a burst of parallel launches doesn't lose any to lock contention.agentWorkspaceTrustfield with the spawn request, and the relay (Orca's helper on the SSH host) writes the agent's trust on its own disk with the same module, for every agent except Antigravity (no verified remote writer yet). This replaces the desktop writing Codex, Cursor, Copilot and Qoder trust over SFTP, which was unlocked, not atomic and took several network round trips. The relay is always the same build as the desktop (its install folder and handshake are keyed to a hash of its own code), so the field is never sent to a relay that doesn't understand it.Why
The missing piece was a saved fact each agent looks for; writing it is each agent's own documented way to skip the question. Orca already had those writers for five agents. The problem was where they ran: seven renderer call sites and several main-process ones, each asking with a path it chose, and none on the most common launch path. Moving the decision to the one step every Orca-started agent passes through makes every agent behave the same, covers every launch path including ones added later, and removes the copies that had drifted apart.
Alternatives rejected:
Linked Issue
Refs #21867. Follows #22927.
Visual Proof
Live QA on a second Mac, driving a background dev build of this branch through the Chrome DevTools protocol, with a throwaway Claude config directory, a dummy API key, no network for Claude, and Claude Code 2.1.284. Antigravity ran as a stub program behind Orca's command override, so Orca still launched it as Antigravity and wrote its trust file, without starting (and self-updating) the real
agy. Every Claude launch used Orca's default arguments,claude '--dangerously-skip-permissions'. Round 3 (9/9 pass) ran atf3709c08b8e; round 4 (8/8 pass) ran at5a9865a6ae9, after the change that guards the stored path and moves SSH writes to the relay; round 5 (6/6 pass) ran ate0323411778, after the home guard became per agent, with Codex also as a stub. The only code changes after round 4 besides round 5's subject are one more guard form (a path climbing back through..), a relay ordering test, and the structured Codex trust write. The real Claude and Antigravity settings files on that machine were unchanged, and no QA path was added to its real~/.claude.json.A worktree Orca created, launched by Orca: Claude opens straight to its prompt, and its config gained
{"hasTrustDialogAccepted": true}for that worktree.A folder workspace: Claude now opens straight to its prompt there too (before this PR it asked).
Antigravity (stub) in a new worktree: Orca added the worktree to Antigravity's
trustedWorkspaces. The folder workspace was added the same way.The home folder as a folder workspace (and a symlink to it): Claude still asks, and nothing is written for it, because Claude's trust would then cover everything under your home.
Codex started from a floating terminal whose folder is the home (round 5): trusted exactly as on main; Codex checks only that exact folder, so this does not trust anything below it. Codex in a home-folder workspace and Antigravity in the home were trusted the same way, while Claude still asked there.
Setting off: Claude asks in a new worktree of a repository nobody has trusted, and Orca writes nothing. Entries written earlier stay.
claudetyped by hand in a plain terminal: Claude asks, as before.The setting (screenshot taken before the description was reworded to the text above):
Also checked live: a Claude tab reattached after restarting the app wrote no trust again, and the main checkout is trusted like any other workspace. Not run live, covered by unit tests only: SSH (the rig has no SSH host), WSL, Windows, the real Codex binary, Cursor, Copilot, Qoder, Agent Teams, and structured Codex chats.
Testing
New tests cover:
..back to home, and that Codex, Cursor and Antigravity are trusted there as before;pty.spawnwaits for trust before starting the agent;Each key mechanism was ablated and its test went red.
Local gates at the head:
tc:node,tc:webandtc:clipass; the changed-code quality gate (type-aware rules, React Doctor, design system, casting rationale),oxlintand the anti-slop rules pass on every changed file; the max-lines ratchet, reliability-gate check and localization catalog, extraction and coverage checks pass; the relay bundle builds for every platform with no Electron import. Tests, run with explicit files and a throwawayHOMEandCLAUDE_CONFIG_DIR: every test file in this PR plusorca-runtime.test.ts, 1854 passed, 1 skipped. Not run locally: e2e suites.Platforms: macOS (unit tests and gates on one machine, live dev-build QA on a second). Windows, WSL and SSH are covered by unit tests only.
Review
Reviewer notes:
CLAUDE_CONFIG_DIRexported only in a shell rc is invisible to Orca, so Claude asks..claude.jsonthrough\\wsl.localhostkeeps its 0600 mode.Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
Security: Orca only writes trust for a workspace it is starting an agent in; for agents whose trust covers subfolders (Claude, Copilot, Qoder) never for a home folder, a folder above one, or a disk root; and it never removes anything. Cross-platform: Windows keys use forward slashes as Claude does; WSL agents never get the Windows home. SSH: each remote host writes its own files through Orca's relay, which is always the same build as the desktop, so the new spawn field is never sent to a relay that doesn't understand it. Performance: a launch with nothing to write adds no wait; a local Claude launch reads and parses its config file once (about 11 ms for a 5.9 MB file).
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)