Skip to content

Python: Honor a zero handoff autonomous turn limit instead of defaulting to 50 - #8965

Open
Grapette.L (Lesereingrape) wants to merge 1 commit into
microsoft:mainfrom
Lesereingrape:fix/handoff-autonomous-turn-limit-zero
Open

Grapette.L (Lesereingrape) wants to merge 1 commit into
microsoft:mainfrom
Lesereingrape:fix/handoff-autonomous-turn-limit-zero

Conversation

@Lesereingrape

Copy link
Copy Markdown
Contributor

Motivation & Context

HandoffAgentExecutor.__init__ folded the configured autonomous turn limit through truthiness, so an explicit 0 became _DEFAULT_AUTONOMOUS_TURN_LIMIT (50). That value reaches the executor unchanged from the public builder path — HandoffBuilder.with_autonomous_mode(turn_limits={agent_id: 0}) → self._autonomous_mode_turn_limits.get(id, None) (_handoff.py:1210) — and the consumer compares a counter against it (_handoff.py:480), so a caller who asked an agent never to continue on its own instead got up to 50 injected autonomous prompts and model calls before control returned to the user. The docstrings describe the default as belonging to the unset case, which is None. Reported and reproducible in #8964.

Description & Review Guide

  • What are the major changes? One expression in python/packages/orchestrations/agent_framework_orchestrations/_handoff.py:282: the or fallback becomes _DEFAULT_AUTONOMOUS_TURN_LIMIT if autonomous_mode_turn_limit is None else autonomous_mode_turn_limit, plus a docstring line in HandoffAgentExecutor.__init__ and in HandoffBuilder.with_autonomous_mode stating that 0 means no autonomous continuation and the default applies only when the limit is left unset. One regression test, test_autonomous_mode_zero_turn_limit_returns_control_immediately, added to python/packages/orchestrations/tests/test_handoff.py next to the existing turn-limit test.
  • What is the impact of these changes? 0 is now honored: the executor takes the request_info branch on the agent's first non-handoff reply instead of running 50 more turns. None still yields 50, and any positive limit behaves exactly as before, so the change is confined to the value that previously read as "unset". Nothing else in the executor changes; the two adjacent autonomous tests (test_autonomous_mode_yields_output_without_user_request, test_autonomous_mode_resumes_user_input_on_turn_limit) still pass untouched.
  • What do you want reviewers to focus on? Whether 0 should be honored as "no autonomous continuation" (this PR) or rejected at build time with a ValueError. I chose to honor it because the existing counter comparison already expresses that meaning correctly once 0 survives, and because with_autonomous_mode documents per-agent limits as caller-chosen. If validation is preferred, the same test can assert the raised error instead.

Local run of the package's full gate (uv run python scripts/workspace_poe_tasks.py check --package orchestrations): fmt and lint pass, all five typing checkers (pyright, mypy, ty, pyrefly, zuban) report 0 errors, and 236 tests pass with 86% coverage on _handoff.py. Before the source change, the new test fails on main (a91fe668) with assert 51 == 1 — 1 required reply plus the 50 autonomous turns the configured 0 was meant to forbid — and passes after it.

Related Issue

Fixes #8964

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • This is not a breaking change. No breaking change label needed: only the value that previously fell through to the default changes behavior, and None keeps the documented default of 50.

Disclosure: this change was produced by an AI coding assistant working from the issue repro, then run through the package's own gate locally as described above. The diff is small and self-contained on purpose so it is quick to audit.

HandoffAgentExecutor folded autonomous_mode_turn_limit through `or`, so an
explicit 0 became the default 50 and the agent kept running on injected
prompts instead of returning control to the user.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@agent-framework-automation agent-framework-automation Bot added the python Usage: [Issues, PRs], Target: Python label Oct 2, 2026
@eavanvalkenburg

Copy link
Copy Markdown
Member

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAF Automated Review — Iteration 1

Result: No findings
Scope: full PR (1 commit(s)): dcf2f073a31f
Model: gpt-5.6-sol

Overview

The change narrowly distinguishes an explicit zero autonomous-turn limit from the unset None case while preserving the existing default and positive-limit behavior. The builder path preserves zero, handoff and termination checks still take precedence, and the regression test covers the complete public path through the immediate user-input request. No publishable Critical, High, or Medium issue was established.

Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
No publishable findings remained after source verification for this scope.

This branch was successfully deployed

1 active deployment
github-app-auth — dcf2f073 Deployed Oct 2, 2026 by Lesereingrape via team_check #5810
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Bug]: A handoff autonomous turn limit of 0 silently becomes the default 50 turns

3 participants