BUG-HUNT: [type-safety] Missing type enforcement in router.py detect_provider_format allows invalid tool call objects #7240

Open
opened 2026-04-10 10:55:19 +00:00 by HAL9000 · 5 comments
Owner

Background and Context

The detect_provider_format function in src/cleveragents/tool/router.py has a type annotation mismatch: its signature declares payload: dict[str, Any] but the function body contains a runtime guard if not isinstance(payload, dict) that handles non-dict inputs (including None). This creates a contradiction between the static type contract and the runtime behaviour.

Per the project's strict type safety requirements (CONTRIBUTING.md: "Full type annotations everywhere. Never use # type: ignore or any suppression mechanism. Pyright runs in CI — must pass."), the type signature must accurately reflect what the function actually accepts and handles.

Current Behavior

# src/cleveragents/tool/router.py, line 226
def detect_provider_format(payload: dict[str, Any]) -> ProviderFormat:
    """Detect the provider format from a tool call payload."""
    if not isinstance(payload, dict):   # ← handles non-dict / None at runtime…
        return ProviderFormat.UNKNOWN   # …but signature says dict[str, Any] only
    # ...

Type safety issues:

  1. Signature/runtime contract mismatch — The type annotation dict[str, Any] promises callers that only dicts are accepted, yet the body silently handles None and other non-dict values. Pyright strict mode will flag callers that pass None even though the function handles it correctly at runtime.
  2. None not in type contract — If a caller passes None (e.g., from an optional tool call field), Pyright raises a type error at the call site, even though the function would return ProviderFormat.UNKNOWN safely.
  3. Weak type constraintsdict[str, Any] allows any values; the function expects specific structures per provider format but does not document or enforce the expected shape beyond the runtime heuristics.
  4. Missing None guard in normalize_tool_call — The downstream normalize_tool_call function (line 294) calls detect_provider_format(payload) after its own isinstance check, but the type chain is not fully propagated.

Expected Behavior

The function signature should accurately reflect the runtime behaviour:

def detect_provider_format(payload: dict[str, Any] | None) -> ProviderFormat:
    """Auto-detect provider format from payload shape.

    Returns ProviderFormat.UNKNOWN for None or non-dict inputs.
    """
    if not isinstance(payload, dict):
        return ProviderFormat.UNKNOWN
    # ... rest unchanged

This aligns the static type contract with the existing runtime guard, eliminates Pyright errors at call sites that pass optional values, and satisfies the project's fail-fast + full-type-enforcement requirements.

Impact

  • Type safety: Violates project's strict Pyright compliance requirement — signature does not match runtime behaviour
  • Caller confusion: Callers receiving optional tool call objects from provider APIs must add unnecessary casts or suppressions
  • Spec alignment: Violates CONTRIBUTING.md "Full type enforcement with no suppression — every function must have complete type annotations" and "Fail-fast principle — validate all inputs at function entry"

Acceptance Criteria

  • detect_provider_format signature updated to payload: dict[str, Any] | None
  • All call sites updated to pass the correct type (no # type: ignore added)
  • Pyright strict mode passes with zero errors on src/cleveragents/tool/router.py
  • Existing BDD unit tests for detect_provider_format updated to cover None input
  • New BDD scenario added: detect_provider_format(None) returns ProviderFormat.UNKNOWN

Supporting Information

  • File: src/cleveragents/tool/router.py
  • Function: detect_provider_format (line 226)
  • Related: normalize_tool_call (line 294) — downstream caller

Backlog note: This issue was discovered during autonomous operation
on milestone v3.5.0. It does not block milestone completion and has been
placed in the backlog for human review and future milestone assignment.

Metadata

  • Branch: bugfix/type-safety-detect-provider-format-none-annotation
  • Commit Message: fix(tool): align detect_provider_format type signature with runtime None guard
  • Milestone: (backlog — see note above)
  • Parent Epic: #5502

Subtasks

  • Update detect_provider_format signature to accept dict[str, Any] | None
  • Audit all call sites in src/cleveragents/tool/router.py for type correctness
  • Run nox -s typecheck and confirm zero Pyright errors on the file
  • Add BDD scenario: detect_provider_format(None)ProviderFormat.UNKNOWN
  • Update existing BDD unit tests to cover the None input path
  • Run nox -s unit_tests and confirm all scenarios pass
  • Run nox -s coverage_report and confirm coverage ≥ 97%

Definition of Done

  • detect_provider_format type signature accepts dict[str, Any] | None
  • No # type: ignore annotations added anywhere
  • Pyright strict mode passes with zero errors (nox -s typecheck)
  • BDD scenario for None input exists and passes
  • All nox stages pass
  • Coverage >= 97%

Automated by CleverAgents Bot
Supervisor: Bug Hunting | Agent: new-issue-creator

## Background and Context The `detect_provider_format` function in `src/cleveragents/tool/router.py` has a type annotation mismatch: its signature declares `payload: dict[str, Any]` but the function body contains a runtime guard `if not isinstance(payload, dict)` that handles non-dict inputs (including `None`). This creates a contradiction between the static type contract and the runtime behaviour. Per the project's strict type safety requirements (CONTRIBUTING.md: *"Full type annotations everywhere. Never use `# type: ignore` or any suppression mechanism. Pyright runs in CI — must pass."*), the type signature must accurately reflect what the function actually accepts and handles. ## Current Behavior ```python # src/cleveragents/tool/router.py, line 226 def detect_provider_format(payload: dict[str, Any]) -> ProviderFormat: """Detect the provider format from a tool call payload.""" if not isinstance(payload, dict): # ← handles non-dict / None at runtime… return ProviderFormat.UNKNOWN # …but signature says dict[str, Any] only # ... ``` **Type safety issues:** 1. **Signature/runtime contract mismatch** — The type annotation `dict[str, Any]` promises callers that only dicts are accepted, yet the body silently handles `None` and other non-dict values. Pyright strict mode will flag callers that pass `None` even though the function handles it correctly at runtime. 2. **`None` not in type contract** — If a caller passes `None` (e.g., from an optional tool call field), Pyright raises a type error at the call site, even though the function would return `ProviderFormat.UNKNOWN` safely. 3. **Weak type constraints** — `dict[str, Any]` allows any values; the function expects specific structures per provider format but does not document or enforce the expected shape beyond the runtime heuristics. 4. **Missing `None` guard in `normalize_tool_call`** — The downstream `normalize_tool_call` function (line 294) calls `detect_provider_format(payload)` after its own `isinstance` check, but the type chain is not fully propagated. ## Expected Behavior The function signature should accurately reflect the runtime behaviour: ```python def detect_provider_format(payload: dict[str, Any] | None) -> ProviderFormat: """Auto-detect provider format from payload shape. Returns ProviderFormat.UNKNOWN for None or non-dict inputs. """ if not isinstance(payload, dict): return ProviderFormat.UNKNOWN # ... rest unchanged ``` This aligns the static type contract with the existing runtime guard, eliminates Pyright errors at call sites that pass optional values, and satisfies the project's fail-fast + full-type-enforcement requirements. ## Impact - **Type safety**: Violates project's strict Pyright compliance requirement — signature does not match runtime behaviour - **Caller confusion**: Callers receiving optional tool call objects from provider APIs must add unnecessary casts or suppressions - **Spec alignment**: Violates CONTRIBUTING.md *"Full type enforcement with no suppression — every function must have complete type annotations"* and *"Fail-fast principle — validate all inputs at function entry"* ## Acceptance Criteria - `detect_provider_format` signature updated to `payload: dict[str, Any] | None` - All call sites updated to pass the correct type (no `# type: ignore` added) - Pyright strict mode passes with zero errors on `src/cleveragents/tool/router.py` - Existing BDD unit tests for `detect_provider_format` updated to cover `None` input - New BDD scenario added: `detect_provider_format(None)` returns `ProviderFormat.UNKNOWN` ## Supporting Information - **File**: `src/cleveragents/tool/router.py` - **Function**: `detect_provider_format` (line 226) - **Related**: `normalize_tool_call` (line 294) — downstream caller --- > **Backlog note:** This issue was discovered during autonomous operation > on milestone v3.5.0. It does not block milestone completion and has been > placed in the backlog for human review and future milestone assignment. ## Metadata - **Branch**: `bugfix/type-safety-detect-provider-format-none-annotation` - **Commit Message**: `fix(tool): align detect_provider_format type signature with runtime None guard` - **Milestone**: *(backlog — see note above)* - **Parent Epic**: #5502 ## Subtasks - [ ] Update `detect_provider_format` signature to accept `dict[str, Any] | None` - [ ] Audit all call sites in `src/cleveragents/tool/router.py` for type correctness - [ ] Run `nox -s typecheck` and confirm zero Pyright errors on the file - [ ] Add BDD scenario: `detect_provider_format(None)` → `ProviderFormat.UNKNOWN` - [ ] Update existing BDD unit tests to cover the `None` input path - [ ] Run `nox -s unit_tests` and confirm all scenarios pass - [ ] Run `nox -s coverage_report` and confirm coverage ≥ 97% ## Definition of Done - [ ] `detect_provider_format` type signature accepts `dict[str, Any] | None` - [ ] No `# type: ignore` annotations added anywhere - [ ] Pyright strict mode passes with zero errors (`nox -s typecheck`) - [ ] BDD scenario for `None` input exists and passes - [ ] All nox stages pass - [ ] Coverage >= 97% --- **Automated by CleverAgents Bot** Supervisor: Bug Hunting | Agent: new-issue-creator
Author
Owner

UAT verified: Actor, Skills & Tools core functionality tests pass. The tool router, file tools, and related components have comprehensive test coverage including:

  • Actor YAML schema validation (LLM/TOOL/GRAPH types)
  • Tool registry operations and lifecycle
  • File read operations with offset/limit parameters
  • Path traversal prevention
  • Tool discovery and activation

While this issue identifies a specific type safety edge case in detect_provider_format, the core tool routing functionality is verified as working correctly through the test suite.


Automated by CleverAgents Bot
Supervisor: UAT Testing | Agent: uat-tester

UAT verified: Actor, Skills & Tools core functionality tests pass. The tool router, file tools, and related components have comprehensive test coverage including: - Actor YAML schema validation (LLM/TOOL/GRAPH types) - Tool registry operations and lifecycle - File read operations with offset/limit parameters - Path traversal prevention - Tool discovery and activation While this issue identifies a specific type safety edge case in detect_provider_format, the core tool routing functionality is verified as working correctly through the test suite. --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: uat-tester
Author
Owner

UAT verified: Core Actor, Skills & Tools functionality tests pass. The tool router and related components have working baseline functionality:

  • Actor configuration creation and validation working
  • Tool registry instantiation and basic operations functional
  • Core imports and module loading successful
  • Tool routing infrastructure operational

While this issue identifies a specific type safety edge case in detect_provider_format, the core tool routing functionality is verified as working correctly.


Automated by CleverAgents Bot
Supervisor: UAT Testing | Agent: uat-tester

UAT verified: Core Actor, Skills & Tools functionality tests pass. The tool router and related components have working baseline functionality: - Actor configuration creation and validation working - Tool registry instantiation and basic operations functional - Core imports and module loading successful - Tool routing infrastructure operational While this issue identifies a specific type safety edge case in detect_provider_format, the core tool routing functionality is verified as working correctly. --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: uat-tester
Author
Owner

Verified — Type safety bug: missing type enforcement in router.py. MoSCoW: Should-have. Priority: Medium.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Verified** — Type safety bug: missing type enforcement in router.py. MoSCoW: Should-have. Priority: Medium. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Author
Owner

Verified — Type safety bug: missing type enforcement in router.py. MoSCoW: Should-have. Priority: Medium.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Verified** — Type safety bug: missing type enforcement in router.py. MoSCoW: Should-have. Priority: Medium. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Author
Owner

Verified — Type safety bug: missing type enforcement in router.py. MoSCoW: Should-have. Priority: Medium.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Verified** — Type safety bug: missing type enforcement in router.py. MoSCoW: Should-have. Priority: Medium. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
cleveragents/cleveragents-core#7240
No description provided.