Feature Specification: Simple SDK Config Additions¶
Feature Branch: feature/007-claude-agent-features
Spec ID: 026-sdk-config-additions
Created: 2026-03-28
Status: Shipped (landed as feat(026))
Input: Expose four missing Claude Agent SDK configuration parameters in HoloDeck YAML: fallback_model, effort, disallowed_tools, and max_budget_usd.
User Scenarios & Testing (mandatory)¶
User Story 1 - Control Thinking Effort Level (Priority: P1)¶
A user configuring a Claude agent wants to control how deeply the agent reasons without manually tuning budget_tokens. They set effort: high in their agent YAML and the agent automatically uses deeper thinking for complex tasks.
Why this priority: Effort level is the simplest, most impactful thinking control -- it replaces manual budget_tokens tuning with a single intuitive setting that the SDK natively supports.
Independent Test: Set effort: high in agent.yaml under claude:, run a test case, and verify the SDK receives the effort parameter.
Acceptance Scenarios:
- Given an agent.yaml with
claude.effort: high, When the agent is initialized, Then the SDK options includeeffort="high". - Given an agent.yaml with
claude.effort: low, When the agent is initialized, Then the SDK options includeeffort="low". - Given an agent.yaml with
claude.effort: invalid, When the config is loaded, Then a validation error is raised listing valid values (low, medium, high, max). - Given an agent.yaml with no
effortfield, When the agent is initialized, Then the SDK receives no effort parameter (SDK default behavior).
User Story 2 - Cap Session Spending with Budget Controls (Priority: P1)¶
A team deploying agents in production wants to prevent runaway costs. They set max_budget_usd: 5.0 in their YAML and the agent stops execution when the token budget is exhausted.
Why this priority: Budget controls are essential for production deployments where cost overruns are a real risk. Without this, users have no spending guardrails.
Independent Test: Set max_budget_usd: 5.0 in agent.yaml under claude:, initialize the agent, and verify the SDK options include the budget limit.
Acceptance Scenarios:
- Given an agent.yaml with
claude.max_budget_usd: 5.0, When the agent is initialized, Then the SDK options includemax_budget_usd=5.0. - Given an agent.yaml with
claude.max_budget_usd: 0, When the config is loaded, Then a validation error is raised (budget must be positive). - Given an agent.yaml with no
max_budget_usdfield, When the agent is initialized, Then no budget limit is passed to the SDK (unlimited).
User Story 3 - Configure Fallback Model (Priority: P2)¶
A user wants their agent to degrade gracefully if the primary model is unavailable or rate-limited. They set fallback_model in YAML so the SDK automatically retries with the fallback.
Why this priority: Fallback is a resilience feature -- important for production but not required for basic agent functionality.
Independent Test: Set fallback_model: haiku in agent.yaml under claude:, initialize the agent, and verify the SDK options include the fallback model.
Acceptance Scenarios:
- Given an agent.yaml with
claude.fallback_model: haiku, When the agent is initialized, Then the SDK options includefallback_model="haiku". - Given an agent.yaml with no
fallback_model, When the agent is initialized, Then no fallback model is passed to the SDK.
User Story 4 - Block Specific Tools Unconditionally (Priority: P2)¶
A user wants to prevent the agent from ever using certain tools, regardless of other permission settings. They set disallowed_tools in YAML to create a deny list that takes precedence over allow lists.
Why this priority: Complements the existing allowed_tools allowlist. Deny lists are a safety net -- useful but secondary to the allowlist which is already implemented.
Independent Test: Set disallowed_tools: [Bash, Write] in agent.yaml under claude:, initialize the agent, and verify the SDK options include the disallowed tools list.
Acceptance Scenarios:
- Given an agent.yaml with
claude.disallowed_tools: [Bash, Write], When the agent is initialized, Then the SDK options includedisallowed_tools=["Bash", "Write"]. - Given both
allowed_toolsanddisallowed_toolsare set, When the config is loaded, Then validation succeeds (the SDK handles precedence -- disallowed wins). - Given an agent.yaml with no
disallowed_tools, When the agent is initialized, Then no disallowed tools list is passed to the SDK.
Edge Cases¶
- What happens when
effortis set alongsideextended_thinking.budget_tokens? The SDK exposeseffortandthinking(which carriesbudget_tokens) as independent parameters and does not publicly document precedence between them. To avoid ambiguity, HoloDeck MUST raise a validation warning when both are set on the same agent, instructing the user to pick one. (See FR-009.) - What happens when
max_budget_usdis set to a very small value (e.g., 0.01)? The agent may fail on the first turn. The system should surface the SDK's budget-exhausted error clearly. - What happens when
fallback_modelis set to the same value as the primary model? Passed through as-is; the SDK handles this case. - What happens when a tool appears in both
allowed_toolsanddisallowed_tools? Passed through as-is; the SDK's precedence rules apply (disallowed wins). - What happens when
disallowed_toolsis set to an empty list ([])? Equivalent to omitting the field — the SDK's default is an empty list, notNone.
Requirements (mandatory)¶
Functional Requirements¶
- FR-001: System MUST accept an
effortfield onClaudeConfigwith valid values:low,medium,high,max. - FR-002: System MUST accept a
max_budget_usdfield onClaudeConfigas a positive float. - FR-003: System MUST accept a
fallback_modelfield onClaudeConfigas a string. - FR-004: System MUST accept a
disallowed_toolsfield onClaudeConfigas a list of strings. - FR-005: System MUST pass all four fields through to the Claude SDK options when present.
- FR-006: System MUST NOT pass any of the four fields when they are not set (preserve SDK defaults). Note:
disallowed_tools's SDK default is an empty list ([]), so passing[]is equivalent to omitting it; the implementation MAY pass either form. - FR-007: System MUST reject invalid
effortvalues at config validation time with a clear error message. - FR-008: System MUST reject non-positive
max_budget_usdvalues at config validation time. - FR-009: System MUST raise a validation warning when both
effortandextended_thinkingare configured on the same agent, since the SDK does not publicly document precedence betweeneffortandthinking.budget_tokensand the combination is ambiguous.
Key Entities¶
- ClaudeConfig: Extended with four new optional fields (effort, max_budget_usd, fallback_model, disallowed_tools).
- EffortLevel: Enumeration of valid effort values (low, medium, high, max).
Success Criteria (mandatory)¶
Measurable Outcomes¶
- SC-001: All four new configuration fields are accepted in agent YAML without errors when valid values are provided.
- SC-002: Invalid values for any of the four fields produce clear validation errors at config load time, before agent execution begins.
- SC-003: All four fields are correctly passed to the Claude SDK when set, verified by unit tests asserting on the built options dictionary.
- SC-004: Omitting any of the four fields results in SDK default behavior (no field sent), verified by unit tests.
Assumptions¶
- The Claude Agent SDK's Python client (
ClaudeAgentOptions) already supportsfallback_model,effort,disallowed_tools, andmax_budget_usdas parameters (verified againstclaude_agent_sdk/types.py:max_budget_usd: float | None,disallowed_tools: list[str]defaulting to[],fallback_model: str | None,effort: Literal["low", "medium", "high", "max"] | None). - The
effortfield in the SDK accepts the four string literalslow | medium | high | max, not numeric values, and not arbitrary strings. - The
fallback_modelfield accepts any string (no SDK-side literal restriction), so values likehaikuor full model IDs likeclaude-haiku-4-5are both valid. HoloDeck SHOULD pass the value through unchanged. - The
disallowed_toolslist uses the same tool naming convention asallowed_tools(e.g.,Bash,Write,mcp__server__tool). - The SDK's
effortandthinkingparameters are independent — neither's docstring describes precedence over the other — so HoloDeck treats simultaneous use as a configuration ambiguity (see FR-009).