Skip to content

fix(reasoning): detect a bare READY marker in text plans#6488

Open
HumphreySun98 wants to merge 1 commit into
crewAIInc:mainfrom
HumphreySun98:fix/reasoning-ready-detection
Open

fix(reasoning): detect a bare READY marker in text plans#6488
HumphreySun98 wants to merge 1 commit into
crewAIInc:mainfrom
HumphreySun98:fix/reasoning-ready-detection

Conversation

@HumphreySun98

Copy link
Copy Markdown

Problem

With reasoning enabled, the executor asks the model to conclude its plan with READY or NOT READY and loops "refine plan" until ready. But there's a mismatch between what the prompts request and what the parser accepts:

  • refine_plan_prompt (and one of the create_plan_prompt variants) say: "Conclude with READY or NOT READY" → models emit a bare READY.
  • The text parser only accepted the exact sentence:
    ready = "READY: I am ready to execute the task." in response

So a bare READY is never detected as ready — the agent stays stuck refining until max attempts. This bites text-parsing models (the reporter used Ollama GLM), where the trace clearly shows READY but it's read as not-ready.

Fix

Add a _detect_plan_ready helper and use it at all three text-detection sites:

_READY_MARKER = re.compile(r"NOT\s+READY|READY", re.IGNORECASE)

def _detect_plan_ready(text: str) -> bool:
    markers = _READY_MARKER.findall(text)
    if not markers:
        return False
    return "NOT" not in markers[-1].upper()
  • Matches the last marker, so a "not ready" mentioned mid-plan doesn't override a final READY.
  • NOT READY wins over the bare READY it contains.
  • The old full sentence ("READY: I am ready to execute the task.") still counts as ready — backward compatible.

Only the text-parsing path is affected; the structured/function-calling path already uses a boolean ready field.

Behavior (old → new)

response ends with old new
READY ❌ not ready ✅ ready
NOT READY: … not ready not ready
READY: I am ready to execute the task. ready ready

Tests

Added TestDetectPlanReady (bare/full/none/mid-text/case-insensitive + _parse_planning_response integration). Full test_structured_planning.py (33) green; ruff + mypy clean. Branched off latest main (v1.15.2).

Fixes #6204


This PR was authored with Claude Code. Per CONTRIBUTING.md, AI-generated contributions require the llm-generated label — I don't have triage permission to set it, so could a maintainer please add it? 🤖 Generated with Claude Code

The refine prompt (and one of the create prompts) tells the model to
"Conclude with READY or NOT READY", so models — especially on the
text-parsing path — emit a bare `READY`. But the parser only accepted the
exact sentence "READY: I am ready to execute the task.", so `READY` was never
detected as ready and the agent stayed stuck refining until max attempts.

Add a _detect_plan_ready helper that matches the last READY / NOT READY marker
(NOT READY wins over the bare READY it contains; the full sentence still
counts as ready) and use it at all three text-detection sites.

Fixes crewAIInc#6204

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Plan readiness detection in reasoning_handler.py was changed from an exact substring match to a regex-based parser (_READY_MARKER, _detect_plan_ready) that identifies the last READY/NOT READY marker in model output. This detection is applied across function-calling, fallback, and parsing paths, with new unit tests added.

Changes

Plan readiness detection fix

Layer / File(s) Summary
Readiness marker regex and detection helper
lib/crewai/src/crewai/utilities/reasoning_handler.py
Adds import re, a compiled _READY_MARKER regex pattern, and _detect_plan_ready(text) which scans for readiness markers and returns readiness based on the last one found, treating NOT READY as not ready.
Wire detection into planning response paths
lib/crewai/src/crewai/utilities/reasoning_handler.py
Replaces prior exact-substring readiness checks in the function-calling success path, fallback text-parsing path, and _parse_planning_response with calls to _detect_plan_ready.
Unit tests for readiness detection
lib/crewai/tests/utilities/test_structured_planning.py
Adds TestDetectPlanReady covering bare READY, full sentence markers, NOT READY, last-marker precedence, case-insensitivity, no-marker default, and integration with _parse_planning_response.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: detecting a bare READY marker in reasoning plans.
Description check ✅ Passed The description is clearly related to the changeset and explains the readiness-detection fix.
Linked Issues check ✅ Passed The PR satisfies #6204 by recognizing READY in text plan parsing and adding coverage for the expected behavior.
Out of Scope Changes check ✅ Passed The added NOT READY, last-marker, and case-insensitive handling are all consistent with the readiness parsing fix and tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
lib/crewai/tests/utilities/test_structured_planning.py (1)

28-52: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a false-positive guard test.

The suite covers positive/negative/precedence/case scenarios well. Consider adding a test to verify that common English words containing "ready" (e.g., "already", "readiness") do not trigger a false READY — this directly guards the regex word-boundary concern in the source.

✨ Suggested test
+    def test_word_containing_ready_is_not_a_marker(self):
+        assert _detect_plan_ready("I am already prepared to proceed.") is False
+        assert _detect_plan_ready("The readiness check is complete.") is False
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/tests/utilities/test_structured_planning.py` around lines 28 - 52,
Add a false-positive guard test in test_structured_planning for
_detect_plan_ready so that ordinary words containing “ready” like “already” or
“readiness” do not match as READY. Update the test suite near the existing
_detect_plan_ready and AgentReasoning._parse_planning_response cases to assert
these inputs return False, protecting the word-boundary behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/crewai/src/crewai/utilities/reasoning_handler.py`:
- Around line 31-33: The _READY_MARKER regex in reasoning_handler is too broad
because READY matches inside other words like already or readiness. Update the
pattern used by _READY_MARKER to require word boundaries around the READY token
(while keeping the NOT\s+READY alternative) so _detect_plan_ready only returns
true for an actual standalone readiness signal and not embedded substrings.

---

Nitpick comments:
In `@lib/crewai/tests/utilities/test_structured_planning.py`:
- Around line 28-52: Add a false-positive guard test in test_structured_planning
for _detect_plan_ready so that ordinary words containing “ready” like “already”
or “readiness” do not match as READY. Update the test suite near the existing
_detect_plan_ready and AgentReasoning._parse_planning_response cases to assert
these inputs return False, protecting the word-boundary behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 50296fc4-8581-4111-9b4d-106cd2b7863f

📥 Commits

Reviewing files that changed from the base of the PR and between 289686a and 4b99651.

📒 Files selected for processing (2)
  • lib/crewai/src/crewai/utilities/reasoning_handler.py
  • lib/crewai/tests/utilities/test_structured_planning.py

Comment on lines +31 to +33
_READY_MARKER: Final[re.Pattern[str]] = re.compile(
r"NOT\s+READY|READY", re.IGNORECASE
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Regex lacks word boundaries — "READY" matches inside words like "already".

The pattern READY without \b will match the "ready" substring in common words such as "already", "readiness", or "readied", producing a false-positive READY marker. This could cause the executor to skip plan refinement when the model never actually signaled readiness.

🐛 Proposed fix: add word boundaries
 _READY_MARKER: Final[re.Pattern[str]] = re.compile(
-    r"NOT\s+READY|READY", re.IGNORECASE
+    r"\bNOT\s+READY\b|\bREADY\b", re.IGNORECASE
 )

A test case like _detect_plan_ready("I am already prepared") is False would guard against regression.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_READY_MARKER: Final[re.Pattern[str]] = re.compile(
r"NOT\s+READY|READY", re.IGNORECASE
)
_READY_MARKER: Final[re.Pattern[str]] = re.compile(
r"\bNOT\s+READY\b|\bREADY\b", re.IGNORECASE
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/src/crewai/utilities/reasoning_handler.py` around lines 31 - 33,
The _READY_MARKER regex in reasoning_handler is too broad because READY matches
inside other words like already or readiness. Update the pattern used by
_READY_MARKER to require word boundaries around the READY token (while keeping
the NOT\s+READY alternative) so _detect_plan_ready only returns true for an
actual standalone readiness signal and not embedded substrings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Reasoning plan always detects "NOT READY" even though the model indicates "READY"

1 participant