Optional ReadonlyallowedOptional ReadonlyfilesFixture files to write in a fresh temp working dir (path → contents).
ReadonlymodelThe scripted model turns the agent will take.
Optional ReadonlyoutputPath to an OUTPUT STYLE file to switch on for this run. The style is written where the harness keeps styles, and selected by the name the harness reads from the file — never by a name you type, because a setting that misses the name loads no style and raises no error.
The run is then checked: if no model request carried the style,
runHarnessTest THROWS instead of returning a trace, so a style that never
loaded cannot pass a test. Refused up front on a harness without output
styles, or when the fixture's files or settings already decide.
What it proves is delivery. A scripted model does not read the style, so nothing in this tier shows that a model FOLLOWS it.
Optional ReadonlypluginPath to a real plugin/repo whose harness (hooks + CLAUDE.md + skills) is
loaded into the sandbox, so you test the assembled machine, not a retyped
subset. Inline settings/files layer on top. See src/plugin-loader.ts.
Optional ReadonlypluginPath to a plugin dir to install NATIVELY via claude --plugin-dir, so its
skills / commands / agents / hooks register and ACTIVATE the real way — a
scripted Skill tool_use resolves, and the real model can trigger them.
Unlike plugin (which materializes a file subset that does NOT register
skills for the Skill tool), this is the real install path, so point it at a
COMPLETE plugin (internal references resolve). Inline settings/files and
plugin still layer on top. Resolved to an absolute path.
Optional ReadonlypromptThe user prompt. Default: "go".
Optional ReadonlysandboxConfinement policy for the code this run executes (src/sandbox.ts).
Default "auto" is safe-by-default: an inline-only spec (you authored it)
runs directly, but an external plugin / pluginDir brings in untrusted
third-party hooks and is run under bubblewrap — or, if no sandbox is
available, the run REFUSES rather than executing unconfined. Pass false to
opt out and run unconfined (you audited the code, or trust the outer
container); "strict" to force confinement even for trusted code.
NOTE: confined execution is Linux only (bubblewrap is a Linux tool). On
macOS / Windows no sandbox is available, so an untrusted run will REFUSE
under "auto"/"strict" — use sandbox: false there if you trust the code.
Optional Readonlysettings.claude/settings.json contents — the hooks/permissions under test.
Optional ReadonlytimeoutPer-run wall-clock timeout in ms. Default 60000.
Optional ReadonlytoolsWhich tools EXIST in the session (claude --tools, Claude Code 2.0.31+). A
tool left out is not offered at all, so a call to it is "No such tool
available". Omitted, every tool exists. This is the field that withholds a
tool. Names only: a permission rule such as Bash(git *) is refused (put the
rule in allowedTools). [] is refused (an agent with no tools is never
served a scripted turn), and a scripted call to a tool the list leaves out
throws before the run starts. An MCP tool is not a built-in, so --tools
does not withhold it. Codex has no equivalent, so a run there refuses it.
| field | Claude Code flag | meaning |
|---|---|---|
tools |
--tools |
which tools exist |
allowedTools |
--allowedTools |
which are pre-approved |
Optional ReadonlytranscriptCapture the full event transcript (--output-format stream-json) into
stdout, instead of just the final result object, so you can assert on what
the agent's tools returned — e.g. the body a Skill tool_use resolved. With
this on, stdout is newline-delimited JSON events, not a single object.
Which tools are PRE-APPROVED (
claude --allowedTools), so they do not stop on a permission prompt. Takes permission rules (Bash(git *)). Omitted: Read, Edit, Write and Bash are approved. An explicit list REPLACES that default, sotools: ["Read", "Write"]withallowedTools: ["Skill"]leaves Write unapproved.[]: nothing is approved.It never removes a tool, and leaving a tool out is not a fence: in headless mode Claude Code refuses only the calls it would have asked about. For Bash that is a command that creates, changes or removes a file (
touch,>), inside or outside the working directory; a command with no file effect (ls,printf,id) runs unapproved. So a refusedtouchproves the file-change guard is live, not that Bash is withheld. To withhold a tool usetools; to forbid its use put a deny rule in the fixture'ssettings.permissions.deny.