Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion scripts/langchain/issue_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ def _is_large_task(task: str) -> bool:
return True
if any(sep in lowered for sep in (" and ", " + ", " & ", " then ", "; ")):
return True
return bool(re.search(r"\s\+\s", lowered) or ", " in task or "/" in task)
return bool(re.search(r"\s\+\s", lowered) or ", " in task or " / " in task)

Copilot AI Jan 12, 2026

Copy link

Choose a reason for hiding this comment

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

The capability_check.py module contains a similar function _is_multi_action_task (line 180) that also checks for "/" in task. This will have the same issue as the functions fixed in this PR - it will incorrectly flag compound words like "additions/removals" and paths like "src/utils" as multi-action tasks. Consider updating that function to check for " / " in task instead for consistency with this fix.

Copilot uses AI. Check for mistakes.


def _detect_task_splitting(tasks: list[str], *, use_llm: bool = False) -> list[dict[str, Any]]:
Expand Down
6 changes: 4 additions & 2 deletions scripts/langchain/task_decomposer.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,10 @@ def _split_task_parts(task: str) -> list[str]:
parts = [part.strip() for part in task.split(";") if part.strip()]
elif ", " in task:
parts = [part.strip() for part in task.split(",") if part.strip()]
elif " / " in task or "/" in task:
parts = [part.strip() for part in re.split(r"\s*/\s*", task) if part.strip()]
elif " / " in task:
# Only split on spaced slashes to avoid splitting compound words
# like "additions/removals" or paths like "src/utils"
parts = [part.strip() for part in task.split(" / ") if part.strip()]
else:
parts = [task]
return [part for part in parts if part]
Expand Down
19 changes: 19 additions & 0 deletions tests/scripts/test_issue_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,3 +311,22 @@ def test_extract_json_payload_with_wrapped_text() -> None:
def test_formatted_output_validates_sections() -> None:
assert issue_optimizer._formatted_output_valid("## Tasks\n- x\n## Acceptance Criteria\n- y")
assert not issue_optimizer._formatted_output_valid("## Tasks only")


def test_is_large_task_ignores_compound_slashes() -> None:
"""_is_large_task should NOT flag compound words with unspaced slashes."""
# Compound words should NOT be flagged as large
assert not issue_optimizer._is_large_task("Color-coded additions/removals")
assert not issue_optimizer._is_large_task("Update src/utils module")

Copilot AI Jan 12, 2026

Copy link

Choose a reason for hiding this comment

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

This line has trailing whitespace. Consider removing it to maintain consistent code formatting.

Copilot uses AI. Check for mistakes.
# But spaced slashes still indicate alternatives (large task)
assert issue_optimizer._is_large_task("Option A / Option B")
assert issue_optimizer._is_large_task("Run lint / format / typecheck")


def test_is_large_task_detects_other_separators() -> None:
"""_is_large_task should still detect other multi-action patterns."""
assert issue_optimizer._is_large_task("Update docs and add tests")
assert issue_optimizer._is_large_task("Lint, format, typecheck")
assert issue_optimizer._is_large_task("Fix bug; write tests")
assert issue_optimizer._is_large_task("Run A + B")
22 changes: 19 additions & 3 deletions tests/scripts/test_task_decomposer.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,26 @@ def test_split_task_parts_with_with_list() -> None:
assert all(part.startswith("Build user dashboard with") for part in parts)


def test_split_task_parts_with_slash() -> None:
"""_split_task_parts splits on slashes."""
parts = task_decomposer._split_task_parts("config/settings")
def test_split_task_parts_with_spaced_slash() -> None:
"""_split_task_parts splits on spaced slashes ' / '."""
parts = task_decomposer._split_task_parts("option A / option B")
assert len(parts) == 2
assert "option A" in parts
assert "option B" in parts


def test_split_task_parts_preserves_compound_words() -> None:
"""_split_task_parts does NOT split compound words with unspaced slashes."""
# Compound words like "additions/removals" should NOT be split
parts = task_decomposer._split_task_parts("config/settings")
assert parts == ["config/settings"]

parts = task_decomposer._split_task_parts("additions/removals")
assert parts == ["additions/removals"]

# Paths like "src/utils/helpers" should NOT be split
parts = task_decomposer._split_task_parts("Update src/utils/helpers module")
assert parts == ["Update src/utils/helpers module"]


def test_split_task_parts_single_task() -> None:
Expand Down
Loading