fix(cli): pass granular Claude SDK runtime options#94
Draft
rawwerks wants to merge 2 commits into
Draft
Conversation
Mirror the codex env-var pattern (PROSE_CODEX_SANDBOX_MODE, PROSE_CODEX_APPROVAL_POLICY, ...) on the claude-sdk side so non- interactive harness runs can widen the SDK's default permission gate. Background. PR #70 set settingSources: ["user", "project"] so the SDK reads ~/.claude and <repo>/.claude, but permissionMode is a query-level option that settings.json does not flow into. With the wrapper never passing permissionMode, the SDK falls back to "default" and prompts for explicit Write approval on every tool use -- making claude-sdk unrunnable in CI, conformance benchmarks, or any other context that cannot answer prompts. codex-sdk got env-var passthrough for the equivalent knobs; claude-sdk did not. Adds tools/cli/src/harnesses/claude-options.ts with claudeRuntimeOptions(env) reading PROSE_CLAUDE_PERMISSION_MODE and validating against {default, acceptEdits, bypassPermissions, plan}. claude-sdk.ts spreads the result into the query() options. Tests (vitest): - forwards PROSE_CLAUDE_PERMISSION_MODE to the SDK as permissionMode - omits permissionMode when the env var is unset - rejects invalid values with the same error shape as codex-options README documents the new env var alongside the codex section.
Three should-fix items surfaced by an independent audit against
CONTRIBUTING.md:
- CHANGELOG: add [Unreleased] entry naming PROSE_CLAUDE_PERMISSION_MODE
so the env var surfaces in release notes (codex env vars missed this
in their original PR; not repeating the omission here).
- claude-options.ts: tighten return type to
Pick<ClaudeQueryOptions, "permissionMode"> against the Claude SDK's
own query() options type. If the SDK ever changes its permissionMode
union (adds/removes a mode), this surfaces at compile time instead of
silently drifting. Mirrors codex-options.ts's
Pick<CodexThreadOptions, ...> shape.
- harnesses.test.ts: pass explicit `env: {}` in the "omit when unset"
test so it stops depending on the host shell's environment. Without
this, running the test suite with PROSE_CLAUDE_PERMISSION_MODE
exported (as during the E2E spike that motivated this PR) made the
test flake.
Re-verified: 16/16 vitest tests pass; E2E validator still throws
expected error on PROSE_CLAUDE_PERMISSION_MODE=yolo after rebuild.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR Body: fix(cli) — pass granular Claude SDK runtime options
Ready-to-paste body for
gh pr create --body-file notes/003-pr-body.mdagainst branch
feat/cli-claude-permission-mode→mainonopenprose/prose. Commits:d4a3e3e(patch) andd312c7f(auditfix-ups).
Summary
PROSE_CLAUDE_PERMISSION_MODEenv-var passthrough to theclaude-sdkharness, mirroring the existingPROSE_CODEX_*patternadded in fix(cli): pass granular Codex runtime options #77/fix(cli): pass granular Codex runtime options #78.
{default, acceptEdits, bypassPermissions, plan}and forward thevalidated value to
@anthropic-ai/claude-agent-sdk'squery()aspermissionMode.prose run --harness claude-sdkin CI,conformance benchmarks, and scheduled jobs that previously stalled
because
permissionModeis a query-level SDK option thatsettingSourcesdoes not flow into.block alongside the codex env-var section; one
[Unreleased]CHANGELOG entry.
Use Case / Run Evidence
Discovered while building a
telltail-backed prose conformancebenchmark (private workbench: rawwerks/telltail-prose-lab). The
benchmark drives the upstream
tests/open-prose/smoke/suite across(harness, model)cells and scores each via a deterministicsentinel-substring check on
runs/{id}/bindings/**.Friction: under default config,
prose run --harness claude-sdkcouldnot complete a single smoke fixture in non-interactive mode because
the SDK fell back to
permissionMode: "default"and prompted forexplicit Write approval on every binding/run-receipt write. Three
independent reproductions (manual probe, manual probe with
project-level
.claude/settings.jsonbypassPermissions, orchestratorattempt) all hit the same gate.
codex-sdkhad no equivalent frictionbecause
PROSE_CODEX_SANDBOX_MODE/PROSE_CODEX_APPROVAL_POLICYalready wired through.
Concrete prose runs (in the workbench, gitignored):
claude-sdkcell inoutcomes/v1.jsonlrecordsfailure_mode: "permission_gate_blocked_writes"(3 fixtures × 3reproductions).
runs/20260522-195404-016210/withPROSE_CLAUDE_PERMISSION_MODE=bypassPermissions— a realclaude-sdkrun that wroteroot.prose.md,vm.log.md, sourcesnapshots, workspace output, and the declared
messagebindingcontaining the fixture's sentinel string. Scorer returns
score=1.0with
sentinels_found={"single-service-smoke-pass": true},failure_mode: null.Design Boundary
The change is entirely in
tools/cli/src/harnesses/. It does not moveany semantics into the CLI: prose-cli still does no permission decision
of its own — it reads a single env var, validates its value, and
forwards the SDK option. Per CONTRIBUTING.md's "Where Changes Belong,"
this is the row for "Shell entrypoint, harness forwarding."
No skill files, no spec docs, no
packages/std/, nopackages/co/,no examples touched. No language/framework surface modified.
Naming, validation shape, error-message format, and return-type
discipline all mirror
codex-options.tsso a maintainer reviewing bothfiles sees a uniform pattern.
Examples
Invalid values throw with the same error shape as codex:
Testing
The three new cases:
forwards PROSE_CLAUDE_PERMISSION_MODE to the SDK as permissionMode— given
env: { PROSE_CLAUDE_PERMISSION_MODE: "bypassPermissions" },the query options receive
permissionMode: "bypassPermissions".omits permissionMode when PROSE_CLAUDE_PERMISSION_MODE is unset—given
env: {}, the query options do not have apermissionModekey.
rejects invalid PROSE_CLAUDE_PERMISSION_MODE— givenenv: { PROSE_CLAUDE_PERMISSION_MODE: "yolo" }, the harness throwsthe expected
must be one oferror before calling the SDK.End-to-end verification beyond unit tests:
pnpm --filter @openprose/prose-cli build).01-single-service.prose.mdsmoke fixture withPROSE_CLAUDE_PERMISSION_MODE=bypassPermissions. The SDK fired(claude-agent-sdk@0.2.141 resolved via pnpm), produced
runs/20260522-195404-016210/with the full filesystem run envelope,and the binding contained the fixture's sentinel.
PROSE_CLAUDE_PERMISSION_MODE=yolosurfaced the validator's exact error string through prose-cli's
standard error prefix — proving the patched dist (not some other
path) executed.
Residual Risk / Follow-ups
pnpm run typecheckreports pre-existing errors insrc/prose/repository-serve-reactor-adapters.ts,src/prose/repository-status.ts,src/prose/responsibility-reactor.ts, and several files referencingzod 4 / bun-types. These are identical on
origin/mainand notintroduced by this PR. The narrow
vitest run tests/harnesses/harnesses.test.tsis clean and is the test theCONTRIBUTING.md "CLI or harness behavior" row points at.
fully independent Anthropic API run (different account, different
model preset) wasn't tested. The wrapper code is harness-shape, not
model-shape, so this remains the correct surface area; flagging for
transparency.
permissionModeprecedence betweenrunOptions.envandprocess.envfollows the sameenv?.[name] ?? process.env[name]pattern as
codex-options.ts(claude-options.ts:21). Notseparately tested — neither is the codex case — but the precedence
matches the codex precedent so consumers can rely on the same mental
model across both harnesses.
tools/cli/AGENTS.mddoes not exist; this PRdoes not create one. If a future PR adds an agents file for the CLI,
the env vars belong there.
🤖 Generated with Claude Code