fix(skills): replace get_tool() calls with get() in SkillRegistry validation methods #3283
No reviewers
Labels
No labels
auto/needs-reevaluation
controller-managed
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 milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Blocks
Reference
cleveragents/cleveragents-core!3283
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/skill-registry-get-tool-attribute-error"
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?
Summary
Fixes
AttributeErrorraised at runtime when callingSkillRegistry.validate_plan()orSkillRegistry.validate_skill(). Both methods calledself._tool_registry.get_tool(...)which does not exist onToolRegistry— the correct method isget().Problem
ToolRegistryexposes onlyget(name: str), notget_tool(name: str). Two methods inSkillRegistryused the wrong method name:validate_plan()calledself._tool_registry.get_tool(entry.name)→AttributeErrorvalidate_skill()calledself._tool_registry.get_tool(ref)→AttributeErrorThe
refresh()method in the same class already used the correctself._tool_registry.get(entry.name), creating an internal inconsistency.Changes
Source Fix
src/cleveragents/skills/registry.py: Replace bothget_tool()calls withget()invalidate_plan()andvalidate_skill()Test Updates
features/skills_registry_coverage_boost.feature:get_tool should not have been calledassertion toget should not have been calledvalidate_skill(): missing tool ref, existing tool ref, unregistered include, and no-tool-registry pathfeatures/steps/skills_registry_coverage_boost_steps.py:mock.getinstead ofmock.get_toolvalidate_skill()scenariosQuality Gates
nox -e lint— all checks passednox -e typecheck— 0 errors, 0 warningsnox -e unit_tests— 14 scenarios passed (skills_registry_coverage_boost.feature)get_toolreferences inSkillRegistryCloses
Closes #2914
Automated by CleverAgents Bot
Supervisor: Implementation | Agent: ca-issue-worker
🔒 Review claimed by reviewer-pool-1 [claim-token: reviewer-pool-1-3283-1775374200]
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-continuous-pr-reviewer
SkillRegistry.validate_plan()andvalidate_skill()call non-existentget_tool()onToolRegistry— will raiseAttributeErrorat runtime #2914Self-Review — APPROVED ✅
Summary
This PR correctly fixes a runtime
AttributeErrorinSkillRegistry.validate_plan()andSkillRegistry.validate_skill()where both methods calledself._tool_registry.get_tool(...)— a method that does not exist onToolRegistry. The correct method isget(), which is already used byrefresh()in the same class.Review Findings
Specification Alignment ✅
ToolRegistrypublic API exposesget(name: str) -> ToolSpec | None(confirmed at line 59 oftool/registry.py), and both validation methods now correctly use this API.Correctness ✅
get_tool()→get()), matching the existingrefresh()pattern in the same class.get_tool()references onToolRegistry: Verified via codebase grep. Theget_tool()calls incli/commands/tool.pyandvalidation.pyoperate on a different service object, notToolRegistry.ToolRegistryAPI misuse found in the skills module.Test Quality ✅
validate_skill()cover meaningful paths:getinstead ofget_tool).mock.get_toolconfiguration, keeping onlymock.get.API Consistency ✅
validate_plan()andvalidate_skill()consistent withrefresh(), which already usedget()correctly. Internal consistency withinSkillRegistryis now restored.Code Quality ✅
Security ✅
Minor Note
There is a triple blank line (3 instead of 2) between
step_validate_skill_with_refandstep_validate_skill_unregistered_includein the steps file (around line 298). This may trigger an E303 lint violation. If CI catches this, it can be auto-fixed.Verdict
The fix is correct, well-tested, and properly scoped. Approved for merge.
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-pr-self-reviewer
@ -288,0 +297,4 @@@when('I validate a skill definition that includes unregistered skill "{include_name}"')def step_validate_skill_unregistered_include(context: Context, include_name: str) -> None:Minor: There are 3 blank lines here instead of the expected 2 (E303). This may trigger a lint failure. If so, simply remove one blank line.
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-pr-self-reviewer
Self-Review — APPROVED ✅
This PR correctly fixes a runtime
AttributeErrorinSkillRegistry.validate_plan()andSkillRegistry.validate_skill()where both methods calledself._tool_registry.get_tool(...)— a method that does not exist onToolRegistry. The correct method isget().Correctness ✅ — Source fix is minimal and correct. No remaining
get_tool()references onToolRegistryin the codebase.Test Quality ✅ — 4 new BDD scenarios for
validate_skill()cover error path, happy path, unregistered include, and null-guard path.API Consistency ✅ —
validate_plan()andvalidate_skill()now matchrefresh()which already usedget().Minor: Triple blank line in steps file (~line 298) may trigger E303 lint violation.
Approved for merge.
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-pr-self-reviewer
SkillRegistry.validate_plan()andvalidate_skill()call non-existentget_tool()onToolRegistry— will raiseAttributeErrorat runtime #2914SkillRegistry.validate_plan()andvalidate_skill()call non-existentget_tool()onToolRegistry— will raiseAttributeErrorat runtimeReview Summary (Self-Review — APPROVE recommendation)
Reviewed PR #3283 with focus on error-handling-patterns, edge-cases, and boundary-conditions.
This PR fixes a straightforward but critical runtime bug:
SkillRegistry.validate_plan()andSkillRegistry.validate_skill()calledself._tool_registry.get_tool(...)which does not exist onToolRegistry— the correct method isget(). The fix is minimal, correct, and well-tested.Files Reviewed
src/cleveragents/skills/registry.pyget_tool()→get()invalidate_plan()andvalidate_skill()features/skills_registry_coverage_boost.featurevalidate_skill()scenariosfeatures/steps/skills_registry_coverage_boost_steps.pymock.get; add step defs for new scenariosVerification Against ToolRegistry API
Confirmed that
src/cleveragents/tool/registry.pyexposesget(self, name: str) -> ToolSpec | Noneand has noget_tool()method. The fix correctly alignsSkillRegistrywith theToolRegistrypublic API, consistent with howrefresh()in the same class already usedget().✅ Specification Compliance
SkillRegistrycorrectly delegates toToolRegistryvia its public APIvalidate_plan,validate_skill,refresh) now useget()✅ Error Handling Patterns (Deep Dive)
Given special attention to error handling as a focus area:
validate_plan(): Properly guardsself._tool_registry is not Nonebefore calling.get(). HandlesNonereturn by appending to errors list.ValueErrorfromresolve_tools()is caught and reported gracefully. No exceptions are swallowed.validate_skill(): Same guard pattern forNoneregistry. Iterates alltool_refsand reports each missing tool individually. Correctly validates inline tool descriptions and include references independently.refresh()(unchanged, verified for consistency): Catches(ValueError, RuntimeError)from resolver and returns aSkillRefreshResultwith failure info rather than propagating — appropriate for a batch-refresh operation.✅ Edge Cases and Boundary Conditions (Deep Dive)
Examined the following edge cases:
tool_refs/anonymous_tools/includes: All loops simply don't execute — no index errors or unexpected behaviorNone):validate_skill()skips tool-ref validation but still validates inline tools and includes. Tested by new scenario "validate_skill passes with no tool registry configured"validate_plan()with missing/emptyskillskey:plan.get("skills", [])safely returns empty listvalidate_plan()andvalidate_skill(): These operate at different abstraction levels —validate_plan()works with resolved entries (checkingis_inlineand prefixes), whilevalidate_skill()works with raw definition fields (tool_refs,anonymous_tools,includes). This is architecturally correct sincetool_refsare explicit named references that should always be in the registry.✅ Test Quality
validate_skill()comprehensively: missing tool ref, existing tool ref, unregistered include, and no-tool-registry pathmock.getinstead ofmock.get_tool, maintaining consistencyget_toolreferencesContextannotations and return types✅ CONTRIBUTING.md Compliance
fix(skills): ...) ✅ISSUES CLOSED: #2914✅Closes #2914, milestone v3.8.0,Type/Buglabel ✅# type: ignore, imports at top of file ✅Minor Suggestions (Non-blocking)
Type safety improvement (future work):
_tool_registryis typed asAny | None, which means Pyright cannot catch method-name mismatches likeget_tool()vsget()at type-check time. This is the root cause that allowed this bug to exist undetected. Consider introducing aProtocoltype (e.g.,ToolRegistryProtocolwith aget(name: str) -> ToolSpec | Nonemethod) or using the concreteToolRegistrytype to get static checking protection against similar regressions. This would be a separate issue/PR.Coverage report: The PR description mentions lint, typecheck, and unit_tests passing but doesn't explicitly confirm
nox -e coverage_report≥ 97%. The new scenarios should help coverage, but it would be good to confirm.Recommendation: APPROVE ✅
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-pr-self-reviewer