Repository navigation
fix(chat-workflow): shrink tool_handler.py under its file-size ceiling (#14495) - #14497
Merged
Merged
Conversation
…ndler.py under its ceiling (#14495) Dev_new_gui was red on the code-quality audit-ceilings check because tool_handler.py grew to 4081 lines against its 4063 KNOWN_LARGE ceiling (from the squash-merge of #14470/#14420), failing every open PR's merge commit regardless of what it touches. Moves the six _dispatch_tool_call seam enforcers (_enforce_forbidden_work, _enforce_config_protection, _enforce_fact_forcing, _enforce_pre_action_verifier, _enforce_repetition, _enforce_work_item_approval) into a new chat_workflow/tool_dispatch_guards.py module as plain functions -- none of them touched self in their original form. ToolHandlerMixin keeps thin delegating methods of the same name so _dispatch_tool_call's call sites and existing test monkey-patches (mixin._enforce_forbidden_work = ...) are unaffected, and the deny-before-call ordering in _dispatch_tool_call is untouched. tool_handler.py: 4081 -> 3863 lines. Lowers KNOWN_LARGE for it from 4063 to 3863 in scripts/check_python_file_size.py, tightening the ratchet to the new measured size.
This was referenced Aug 17, 2026
Applied Black, isort, and autoflake to match code-quality checks. Triggered by workflow auto-fix-formatting.yml.
Contributor
✅ SSOT Configuration Compliance: Passing🎉 No hardcoded values detected that have SSOT config equivalents! |
This was referenced Aug 17, 2026
mrveiss
added a commit
that referenced
this pull request
Aug 18, 2026
…s additions (#14469) Moves the browser tool dispatcher, its param validator and the six result-formatting helpers out of tool_handler.py into a new browser_tool_handler.py, mirroring the shape #14497 used for the seam guards. None of the moved code touched self beyond calling one another, so ToolHandlerMixin keeps thin delegating methods (same names) for the existing test monkey-patches and _dispatch_tool_call call sites. Behavior, including the deny-before-call ordering in _handle_browser_tool, is unchanged.
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.
Closes #14495
This unblocks every open PR.
Dev_new_guiis currently red on thecode-qualityrequired check becauseautobot-backend/chat_workflow/tool_handler.py(4081 lines) exceeded its
KNOWN_LARGEceiling of 4063 inscripts/check_python_file_size.py. Since Actions checks out the PR mergecommit, every open PR against
Dev_new_guiinherits this failure regardlessof what it touches, until this lands.
Thinking Path
The squash-merge of #14470 (closing #14420) grew
tool_handler.pypast itsceiling without lowering it. The ratchet in
check_python_file_size.pyonlyever shrinks — raising
4063is exactly what it exists to refuse — so the fixhas to shrink the file.
A security review of this file had already flagged three oversized functions
(
_dispatch_tool_call~152 lines,_handle_browser_tool~105 lines,_process_single_command~84 lines) as extraction candidates. Rather thanpicking one to shave lines off arbitrarily, I looked for a cohesive unit
inside the biggest of the three,
_dispatch_tool_call: it opens with sixsequential seam-enforcer calls (
_enforce_forbidden_work,_enforce_config_protection,_enforce_fact_forcing,_enforce_pre_action_verifier,_enforce_repetition,_enforce_work_item_approval) — each records a block/hold inexecution_resultsand returns aWorkflowMessage, orNoneto fallthrough. None of the six touch
selfin their current form, which makesthem a clean, self-contained "dispatch guard" concern with zero coupling to
the rest of the mixin.
I moved all six, verbatim apart from mechanical changes, into a new module
chat_workflow/tool_dispatch_guards.pyas plain functions, and leftToolHandlerMixinwith six thin delegating methods of the same name soevery existing call site and every existing test that calls or monkey-patches
mixin._enforce_forbidden_work(...)etc. keeps working unchanged.What Changed
autobot-backend/chat_workflow/tool_dispatch_guards.py(338 lines)—
enforce_forbidden_work,enforce_config_protection,enforce_fact_forcing,enforce_pre_action_verifier,enforce_repetition,enforce_work_item_approval, moved out oftool_handler.py.autobot-backend/chat_workflow/tool_handler.py: the six_enforce_*methods are now one-line delegates to the module above; unused
SENSITIVE_TOOLSimport dropped (its only use moved withenforce_pre_action_verifier). 4081 → 3863 lines (218-line margin underthe old ceiling, and the new one).
scripts/check_python_file_size.py:KNOWN_LARGEfortool_handler.pylowered 4063 → 3863 — the ratchet only ever shrinks,and this tightens it to the size just achieved, not just far enough to pass.
Nothing was deleted as dead code — every line that left
tool_handler.pylanded in
tool_dispatch_guards.py; see the byte-diff evidence below.One symbol needed a deferred import that didn't exist before:
enforce_work_item_approvalcalls_approval_category_for, which stays intool_handler.py(it's also imported there bychat_workflow/graph.pyandtests/unit/chat_workflow/test_governed_execution.py, so moving it wouldwiden the blast radius for no size benefit).
tool_dispatch_guards.pyimports it back with a deferred, function-body import — the same pattern
graph.pyalready uses for this exact symbol — to avoid a load-time circularimport between the two modules.
Verification
Faithful-move evidence (diff-based, not asserted). I extracted the
original six function bodies from
origin/Dev_new_gui, mechanically dedentedthem one level and stripped the
self,parameter (the same transform Pythonrequires to turn a method into a module function), and diffed against the
new module. After normalizing the only three cosmetic differences PEP8/the
move require — the
ctx: "X" | None→ctx: "X | None"quoting style,blank-line count between top-level functions vs. methods, and the one
_enforce_x→enforce_xcross-reference inside a docstring — the diff isempty except for the one legitimately new deferred import
(
enforce_work_item_approval'sfrom chat_workflow.tool_handler import _approval_category_for, documented above and in a comment at the call site).That import is the only non-mechanical line added across all six functions.
Deny-before-call ordering — unaffected on all three RBAC surfaces. This
PR does not touch
_process_single_command,_handle_browser_tool, or_handle_web_search_toolat all (grep confirms zero diff lines in thosefunctions). It also does not touch
_dispatch_tool_callitself — only thesix methods it calls. Every one of the five
if <enforcer>_msg is not None: yield <msg>; returnguards in_dispatch_tool_callstill runs, unchanged,before the tool-specific branches (
compose,respond,delegate, LLCtools, and the uniform-builtin route that reaches
_process_single_command/_handle_browser_tool/_handle_web_search_tool).The
should_execute/_emit_before_tool_executeguard-before-call patterninside those three functions (lines ~2093, ~2435/2461ish, ~2668) is
byte-for-byte what it was in
origin/Dev_new_gui— confirmed bygit diffshowing no hunks in that range.
File size / ratchet:
KNOWN_LARGE["autobot-backend/chat_workflow/tool_handler.py"]lowered to3863, matching exactly —check_python_file_size.py's_grandfathered_verdictpasses a file that sits at its ceiling, andrepo_tests/python_file_size_ratchet_test.py::test_a_shrunk_file_must_lower_its_ceilingrequires exactly this.
Syntax:
ast.parse()over the resultingtool_handler.pysucceeds (nocodebase code executed — pure static parse, same technique the ratchet test
itself is not required to but the repo's own tooling relies on for
git ls-files+ AST checks elsewhere).Test-compatibility check (static, not executed — per policy, I do not run
code from this codebase): grepped every test file that calls or
monkey-patches these six methods
(
test_tool_handler_forbidden_work.py,test_tool_handler_config_protection.py,test_tool_handler_fact_forcing.py,test_tool_handler_pre_action_verifier.py,test_tool_handler_repetition_guard.py,test_governed_execution.py,session_role_test.py, plus four files that stub these viamixin._enforce_x = lambda *a, **k: None). All of them call throughmixin._enforce_x(...), which still resolves to the same-named method onToolHandlerMixin— now a delegate, but same signature, same return value.None of them
patch()an import path insidechat_workflow.tool_handlerfor the enforcers' own dependencies (
orchestration.agent_registry,autobot_shared.config_guard,autobot_shared.fact_forcing_guard,autobot_shared.pre_action_verifier_guard,autobot_shared.repetition_guard)— every patch in those tests targets the origin module directly (e.g.
patch("autobot_shared.pre_action_verifier_guard.PreActionVerifier.verify")),which resolves identically whether the deferred import executes inside
tool_handler.pyortool_dispatch_guards.py.Conflict check against PR #14492 (in flight, same file, closes #14469 /
threads RBAC through
_process_single_command,_handle_browser_tool,_handle_web_search_tool): ran a real local 3-way merge of this branchwith
origin/issue-14469in a scratch worktree (not #14492's own branch —read-only against the remote ref, discarded after). Result: clean,
zero-conflict automatic merge —
git merge --no-commit --no-ff origin/issue-14469reported "Automatic merge went well". The two PRs editdisjoint regions of the file (this PR: the six
_enforce_*bodies + oneimport block; #14492:
_process_single_command,_handle_browser_tool,_handle_web_search_tool,_builtin_route,_dispatch_execute_command, anda different two-line addition to the same import block). Merged result
parses clean at 3922 lines (3863 + #14492's own net +59).
Sequencing note for #14492 (not a blocker, but worth doing in order):
#14492 cannot merge before this PR regardless of order chosen, because the
base is already red for every open PR including #14492 — that's the bug
this PR fixes. Once this PR merges, #14492 will need to rebase onto the new
tip; at 3863 (this PR's ceiling) + #14492's own net +59 lines = ~3922, its
rebased branch will re-trip
audit-ceilings(3922 > 3863) purely from itsown diff — the ratchet is working as designed: any PR that grows a
grandfathered file has to account for it. The fix for #14492 is a one-line
addition to their own diff: set
KNOWN_LARGE["autobot-backend/chat_workflow/ tool_handler.py"]to their post-rebase actual line count once rebased (e.g.~3922) — that is still comfortably below the frozen ratchet baseline of
4063 in
repo_tests/python_file_size_ratchet_test.py::RATCHET_BASELINE, sotest_no_ceiling_may_be_raiseddoes not object; onlytest_recorded_ceilings_match_the_files_todayrequires it (ceiling mustequal the file's actual measured size, exactly, at all times). Recommended
order: merge this PR first, then rebase #14492 and have it adjust the
ceiling to its own new count as part of that rebase.
Model Used
Claude Opus 5 (1M context)