tool_max_rounds config silently ignored when falsy, and undocumented in the LLM agent specification #144
Labels
No labels
auto/blocked-by-deps
auto/ci-timeout
auto/claimed-implementer
auto/claimed-merge
auto/claimed-reviewer
auto/driver-down
auto/invariant-violation
auto/last-attempt-tier-0
auto/last-attempt-tier-1
auto/last-attempt-tier-2
auto/last-attempt-tier-min
Automation Tracking
auto/needs-conflict-resolution
auto/needs-implementer
auto/postmortem
auto/ready-to-merge
auto/restart-throttled
auto/revert
auto/sentinel
auto/stale-inactivity
auto/unstable
Blocked
Bounty
$100
Bounty
$1000
Bounty
$10000
Bounty
$20
Bounty
$2000
Bounty
$250
Bounty
$50
Bounty
$500
Bounty
$5000
Bounty
$750
MoSCoW
Could have
MoSCoW
Must have
MoSCoW
Should have
Needs Feedback
Points
1
Points
13
Points
2
Points
21
Points
3
Points
34
Points
5
Points
55
Points
8
Points
88
Priority
Backlog
Priority
CI Blocker
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Signed-off: Owner
Signed-off: Scrum Master
Signed-off: Tech Lead
Spike
State
Completed
State
Duplicate
State
In Progress
State
In Review
State
Paused
State
Unverified
State
Verified
State
Wont Do
Type
Automation
Type
Bug
Type
Discussion
Type
Documentation
Type
Epic
Type
Feature
Type
Legendary
Type
Refactor
Type
Support
Type
Task
Type
Testing
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Blocks
Depends on
#77 Epic: LLM Agent Runtime Stabilization — reliability, resource enforcement & correctness hardening
cleveragents/cleveractors-core
#145 TDD: tool_max_rounds config silently ignored when falsy, and undocumented in the LLM agent specification
cleveragents/cleveractors-core
Reference
cleveragents/cleveractors-core#144
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Metadata
Commit Message:
fix(agents): honor tool_max_rounds precedence and document itBranch:
bugfix/m1-tool-max-rounds-precedenceBackground and context
LLMAgent._execute_tool_loop()(cleveractors.agents.llm) bounds the multi-turn tool-call loop with a round limit resolved from either the per-agenttool_max_roundsconfig key or theTOOL_MAX_ROUNDSenvironment variable.CHANGELOG.md(issue #59 entry) documents the intended behavior as: "a configurable multi-turn loop (default 20 rounds, minimum 1, overridable viatool_max_roundsconfig orTOOL_MAX_ROUNDSenv var; values ≤ 0 are clamped to 1)".This feature predates the ADR-2030/2031 series and was never added to the normative specification:
docs/index.md§4.4 (LLM Agents configuration table) lists exactly 11 fields andtool_max_roundsis not among them, and no ADR documents it. ADR-2031 references "the multi-turn tool-call loop (implemented in #59/#60)" only as pre-existing background while formalizing unrelated token-budget/pruning fields.The implementation gap and the documentation gap are the same underlying problem: a working, tested feature that was shipped without being correctly specified, and one of its documented behaviors doesn't actually hold in code.
Current behavior
In
_execute_tool_loop():Because Python's
ortreats0(and"",False) as falsy, an agent that explicitly setstool_max_rounds: 0does not get the documented "clamped to 1" behavior — the falsy0is discarded and the code falls through toTOOL_MAX_ROUNDS/the"20"default instead. The clamp-to-1 behavior only reliably triggers via the environment-variable path, since a non-empty string like"0"is truthy and reachesmax(1, int("0")).Additionally:
tool_max_roundsreceives none of the init-time validation/storage that sibling fields (token_budget_percent,pruning_threshold,pruning_tool_filter,max_retries, etc.) get inLLMAgent.__init__()— it is parsed fresh, unvalidated, on every_execute_tool_loop()call.docs/index.md§4.4 does not listtool_max_roundsamong its documented LLM agent configuration fields.docs/guides/reasoning-aware-llm-agents.mdshowstool_max_roundsin example YAML, but this is non-normative guide content, not the specification, and doesn't describe the config/env precedence or the 0-clamp behavior.Expected behavior
tool_max_rounds(including0or other falsy-but-present values) is honored and clamped to 1, matching the documented behavior — it must not silently defer toTOOL_MAX_ROUNDS/the default.tool_max_rounds(any present value, including 0) >TOOL_MAX_ROUNDSenv var > default of 20.tool_max_roundsas a documented §4.4 configuration field.docs/index.md§4.4 configuration table includes atool_max_roundsrow, with a §21.1 Revision History entry attributing the addition to that ADR.Acceptance criteria
tool_max_rounds: 0in an LLM agent config results in exactly 1 permitted tool round, not 20 (or whateverTOOL_MAX_ROUNDSis set to)tool_max_roundsto a positive integer continues to take precedence overTOOL_MAX_ROUNDSTOOL_MAX_ROUNDSenv var is consulted only whentool_max_roundsis absent from config_execute_tool_loop()still raisesConfigurationErrorfor a non-integertool_max_roundsvaluedocs/index.md§4.4 table liststool_max_rounds, and §21.1 Revision History has a new row citing the governing ADRtool_max_roundsas a specification-level LLM agent field (revised in place, not a new ADR file)nox -s coverage_reportremains ≥ 97%Supporting information
Relevant code:
cleveractors.agents.llm.LLMAgent._execute_tool_loop(config parsing at the top of the method). Relevant docs:docs/index.md§4.4 (LLM Agents),CHANGELOG.md(issue #59 entry),docs/adr/ADR-2031-tool-loop-token-budget-and-pruning.md(background reference to the multi-turn loop),docs/guides/reasoning-aware-llm-agents.md(non-normative example usage).Subtasks
_execute_tool_loop()precedence logic so an explicitly-settool_max_rounds(including0) is honored and clamped to 1, distinguishing "absent from config" from "falsy"tool_max_roundsat__init__time, consistent with sibling config fieldstool_max_roundsas a §4.4 configuration fielddocs/index.md§4.4 configuration table with thetool_max_roundsrow and a §21.1 Revision History entry attributing the ADRCHANGELOG.mdwith a corrected entryfeatures/llm_agent_tool_loop.featurewith scenarios coveringtool_max_rounds: 0and absent-vs-falsy precedencenox -s coverage_reportnox(all default sessions), fix any errorsDefinition of Done
This issue is complete when: