Run Claude Code¶
Requires the Claude Code CLI on PATH and
either ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN in the environment, or a completed
claude auth login. Bedrock, Vertex and Foundry variables are forwarded too.
What the adapter runs¶
claude -p --output-format stream-json --verbose --max-turns 30
--permission-mode acceptEdits --permission-prompts none
--no-session-persistence --strict-mcp-config --session-id <uuid>
--disallowedTools WebFetch WebSearch --allowedTools …
with the task prompt on stdin. The parser normalizes text blocks, tool calls (Bash becomes a
command event with stdout and stderr, file tools become file_change events), tool results
paired by id, subagent activity (parent_call_id), per-message usage deduplicated by message id,
the final result record (cost, totals per model, turns, permission denials) and system events
(api_retry, compact_boundary, permission_denied). Thinking blocks become
reasoning_event {count: 1}; their text and signature are dropped before anything is persisted.
llm_calls counts distinct assistant messages, that is, API calls.
Guard rails the adapter enforces:
bypassPermissionsis refused unless the variant setsallow_dangerous_permissions: true.--include-partial-messages,--forward-subagent-textand--dangerously-skip-permissionsare never passed, even throughextra_args.- The environment is an allowlist:
PATH, locale, proxy and CA settings, provider credentials, plusDISABLE_AUTOUPDATER,DISABLE_TELEMETRY,DISABLE_ERROR_REPORTINGandCLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC. A Claude Code child never sees a parent session'sCLAUDECODEmarker.
Default shell allowlist¶
Commands outside the allowlist are denied, because nobody answers prompts in headless mode. The
denial count is stored per run, and a run that ends unsuccessfully after denials is marked
blocked rather than failed, so configuration problems stay distinguishable from task failures.
Read, Edit, Write, MultiEdit, Glob, Grep, LS,
Bash(python *), Bash(python3 *), Bash(pytest *), Bash(ls *), Bash(cat *),
Bash(git diff *), Bash(git status *), Bash(git log *)
Override it per variant with allowed_tools.
Variant options¶
| Option | Default | Effect |
|---|---|---|
model |
CLI default | --model |
max_turns |
30 |
--max-turns |
permission_mode |
acceptEdits |
default, acceptEdits, dontAsk, auto, plan, bypassPermissions (opt-in) |
allowed_tools |
the allowlist above | --allowedTools (permission rule syntax, e.g. Bash(python *)) |
disallowed_tools |
[WebFetch, WebSearch] |
--disallowedTools |
tools |
unset | --tools, restricts the built-in tool set |
max_budget_usd |
unset | --max-budget-usd |
bare |
false |
--bare: skips hooks, CLAUDE.md and plugins; needs an API key |
setting_sources |
unset | --setting-sources, e.g. project to ignore user settings |
append_system_prompt |
unset | text appended to the system prompt |
effort (alias reasoning_effort) |
unset | --effort low|medium|high|xhigh|max |
autocompact |
unset | --autocompact auto|<tokens>, e.g. 100k |
action_policy |
unset | batched, fine or free text appended to the system prompt |
harness |
unset | a harness bundle directory |
include_hook_events |
true |
--include-hook-events: every hook that runs becomes a hook event in the trace (see Safety); set false for CLI versions without the flag |
extra_args |
[] |
appended verbatim (forbidden flags rejected) |
env_passthrough |
[] |
extra environment variables forwarded to the CLI |
executable |
claude |
the binary to run |
With a harness bundle¶
When a variant carries harness:, the adapter writes the bundle's system_prompt.md (followed by
append_system_prompt and the action policy) to a file and passes --append-system-prompt-file,
and materializes skills/, hooks.json and agents/ as a Claude Code plugin loaded with
--plugin-dir. Both work under --bare. The launch event records the bundle hash and which
components were applied. See Grow the harness.
Hooks and the sentinel¶
Every run gets HARNESSLAB_PYTHON (the interpreter running Harness Lab),
HARNESSLAB_WORKTREE (the run's worktree), HARNESSLAB_SUITE_DIR (the task's directory) and
HARNESSLAB_SAFETY_LOG (a file in the run's artifacts) in its environment, which bundle hooks
such as the bundled sentinel use. Plugins installed in your own Claude
Code configuration are loaded too unless the variant sets bare: true or a narrower
setting_sources; their hooks appear in the trace as hook events.
Example variant¶
variants:
- id: claude-sonnet-tight
runner: claude
model: claude-sonnet-5
max_turns: 20
effort: high
autocompact: 100k
allowed_tools: [Read, Edit, "Bash(python *)"]
harness: ../harnesses/baseline
Real-CLI integration tests are opt-in: HARNESSLAB_INTEGRATION=1 uv run pytest tests/test_integration_real.py
from a clone.