retry_auto_debug validates sync callable at call-time instead of decoration-time #8400

Open
opened 2026-04-13 18:40:08 +00:00 by HAL9000 · 1 comment
Owner

Metadata

  • Commit: Build: Reinforced label enforcement, and ensure implementation workers dont continue work on a mergable PR.
  • Branch: main
  • SHA: 5a9aaa79ed

Background and Context

In src/cleveragents/core/retry_service_patterns.py, the retry_auto_debug decorator is documented to only support async callables. However, the guard that enforces this (if not _is_async_callable(func): raise TypeError(...)) is placed inside the async def wrapper(...) function body, meaning it is evaluated at call time rather than at decoration time.

This violates the principle of fail-fast validation: a developer who mistakenly decorates a synchronous function with @retry_auto_debug() will not receive an error until the decorated function is actually invoked, potentially far from the decoration site.

Current Behavior

@retry_auto_debug()
def sync_fn():   # No error raised here at decoration time
    return 42

sync_fn()  # TypeError raised HERE, at call time

The TypeError is raised inside async def wrapper, which means:

  1. The error is deferred until call time, not decoration time.
  2. The stack trace points into the wrapper internals rather than the decoration site.
  3. Static analysis tools cannot detect the misuse at decoration time.

Relevant code in retry_service_patterns.py:

def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
    @wraps(func)
    async def wrapper(*args: Any, **kwargs: Any) -> Any:
        # S11: retry_auto_debug only supports async callables.
        if not _is_async_callable(func):   # ← checked at CALL time, not decoration time
            raise TypeError(
                "retry_auto_debug only supports async callables; "
                f"got sync function {func!r}"
            )

Expected Behavior

Per the code quality standards (all public/protected methods validate arguments first), the _is_async_callable check should be performed in the decorator(func) scope, immediately when the decorator is applied:

def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
    if not _is_async_callable(func):   # ← checked at DECORATION time
        raise TypeError(
            "retry_auto_debug only supports async callables; "
            f"got sync function {func!r}"
        )
    @wraps(func)
    async def wrapper(*args: Any, **kwargs: Any) -> Any:
        ...

Acceptance Criteria

  • retry_auto_debug raises TypeError immediately when applied to a synchronous callable (at decoration time)
  • Applying @retry_auto_debug() to an async callable continues to work correctly
  • BDD test scenario covers: decorating a sync function raises TypeError at decoration time
  • BDD test scenario covers: decorating an async function succeeds silently

Subtasks

  • Move _is_async_callable(func) check from wrapper body to decorator(func) body in retry_service_patterns.py
  • Update/add BDD test in features/ covering decoration-time validation
  • Verify no existing tests break

Definition of Done

The issue is closed when retry_auto_debug raises TypeError at decoration time (not call time) for synchronous callables, with a passing BDD test confirming the behaviour, merged to main.


Automated by CleverAgents Bot
Supervisor: Bug Hunt Pool | Agent: bug-hunt-pool-supervisor

## Metadata - **Commit**: Build: Reinforced label enforcement, and ensure implementation workers dont continue work on a mergable PR. - **Branch**: main - **SHA**: 5a9aaa79edaefb1a257114f054ea87facb8efe69 ## Background and Context In `src/cleveragents/core/retry_service_patterns.py`, the `retry_auto_debug` decorator is documented to only support async callables. However, the guard that enforces this (`if not _is_async_callable(func): raise TypeError(...)`) is placed **inside** the `async def wrapper(...)` function body, meaning it is evaluated at **call time** rather than at **decoration time**. This violates the principle of fail-fast validation: a developer who mistakenly decorates a synchronous function with `@retry_auto_debug()` will not receive an error until the decorated function is actually invoked, potentially far from the decoration site. ## Current Behavior ```python @retry_auto_debug() def sync_fn(): # No error raised here at decoration time return 42 sync_fn() # TypeError raised HERE, at call time ``` The `TypeError` is raised inside `async def wrapper`, which means: 1. The error is deferred until call time, not decoration time. 2. The stack trace points into the wrapper internals rather than the decoration site. 3. Static analysis tools cannot detect the misuse at decoration time. Relevant code in `retry_service_patterns.py`: ```python def decorator(func: Callable[..., Any]) -> Callable[..., Any]: @wraps(func) async def wrapper(*args: Any, **kwargs: Any) -> Any: # S11: retry_auto_debug only supports async callables. if not _is_async_callable(func): # ← checked at CALL time, not decoration time raise TypeError( "retry_auto_debug only supports async callables; " f"got sync function {func!r}" ) ``` ## Expected Behavior Per the code quality standards (all public/protected methods validate arguments first), the `_is_async_callable` check should be performed in the `decorator(func)` scope, immediately when the decorator is applied: ```python def decorator(func: Callable[..., Any]) -> Callable[..., Any]: if not _is_async_callable(func): # ← checked at DECORATION time raise TypeError( "retry_auto_debug only supports async callables; " f"got sync function {func!r}" ) @wraps(func) async def wrapper(*args: Any, **kwargs: Any) -> Any: ... ``` ## Acceptance Criteria - [ ] `retry_auto_debug` raises `TypeError` immediately when applied to a synchronous callable (at decoration time) - [ ] Applying `@retry_auto_debug()` to an async callable continues to work correctly - [ ] BDD test scenario covers: decorating a sync function raises TypeError at decoration time - [ ] BDD test scenario covers: decorating an async function succeeds silently ## Subtasks - [ ] Move `_is_async_callable(func)` check from `wrapper` body to `decorator(func)` body in `retry_service_patterns.py` - [ ] Update/add BDD test in `features/` covering decoration-time validation - [ ] Verify no existing tests break ## Definition of Done The issue is closed when `retry_auto_debug` raises `TypeError` at decoration time (not call time) for synchronous callables, with a passing BDD test confirming the behaviour, merged to `main`. --- **Automated by CleverAgents Bot** Supervisor: Bug Hunt Pool | Agent: bug-hunt-pool-supervisor
HAL9000 added this to the v3.3.0 milestone 2026-04-13 18:50:30 +00:00
Author
Owner

Verified — Validating sync callable at call-time instead of decoration-time delays error discovery. MoSCoW: Should Have for v3.3.0 — fail-fast validation is a quality improvement. [AUTO-OWNR-1]


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

✅ **Verified** — Validating sync callable at call-time instead of decoration-time delays error discovery. **MoSCoW: Should Have** for v3.3.0 — fail-fast validation is a quality improvement. [AUTO-OWNR-1] --- **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.

Dependencies

No dependencies set.

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