fix(tests): resolve Behave AmbiguousStep conflict in pool-supervisor steps
CI / benchmark-publish (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Failing after 1m1s
CI / build (pull_request) Successful in 28s
CI / lint (pull_request) Failing after 1m6s
CI / quality (pull_request) Successful in 1m16s
CI / helm (pull_request) Successful in 25s
CI / typecheck (pull_request) Successful in 1m24s
CI / security (pull_request) Successful in 1m35s
CI / push-validation (pull_request) Successful in 19s
CI / e2e_tests (pull_request) Successful in 3m51s
CI / integration_tests (pull_request) Successful in 4m58s
CI / unit_tests (pull_request) Failing after 5m55s
CI / docker (pull_request) Has been skipped
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 4s

The pr_compliance_pool_supervisor_steps.py file defined @then step texts
identical to those in the pre-existing pr_compliance_checklist_steps.py,
causing Behave's ambiguous-step detection to reject the test run.

Fix: prefix all pool-supervisor @then decorators with 'Pool:' so they're
uniquely identifiable by Behave while reusing the same validator logic.
Also fix PROJECT_ROOT from parents[3] -> parents[2] for correct repo root
resolution in step files located at features/steps/.

ISSUES CLOSED: #9824
This commit is contained in:
2026-05-07 10:15:09 +00:00
parent d62bf9b0ba
commit 3e84e96237
@@ -1,14 +1,80 @@
"""Step definitions for PR compliance checklist in implementation pool supervisor."""
"""Step definitions for PR compliance checklist in implementation pool supervisor.
This file uses parameterized @then decorators with unique step text that
distinguishes pool-supervisor checks from the shared compliance checklist
steps in pr_compliance_checklist_steps.py, preventing Behave AmbiguousStep
errors when both feature files are run together.
Each validator is imported from the shared pr_compliance_checklist_steps module's
validation logic (via the _verify module) to avoid code duplication while using
unique step text prefixes ("Pool:") for disambiguation.
"""
from pathlib import Path
from typing import Any
from typing import Any, Callable
from behave import given, then, when
PROJECT_ROOT = Path(__file__).resolve().parents[3]
PROJECT_ROOT = Path(__file__).resolve().parents[2]
AGENT_DEF_PATH = PROJECT_ROOT / ".opencode" / "agents" / "implementation-pool-supervisor.md"
# ---------------------------------------------------------------------------
# Shared validation helpers — identical logic to pr_compliance_checklist_steps.py
# ---------------------------------------------------------------------------
_required_items = [
"CHANGELOG.md",
"CONTRIBUTORS.md",
"ISSUES CLOSED",
"CI passes",
"BDD/Behave tests",
"Epic reference",
"forgejo-label-manager",
"earliest open milestone",
]
VALIDATORS: dict[str, Callable[[str], bool]] = {
"includes checklist section": lambda c: "PR Compliance Checklist" in c,
"is marked MANDATORY": lambda c: "MANDATORY" in c,
"has CHANGELOG.md item": lambda c: "CHANGELOG.md" in c,
"references Unreleased": lambda c: "[Unreleased]" in c,
"has CONTRIBUTORS.md item": lambda c: "CONTRIBUTORS.md" in c,
"instructs add or update": lambda c: "add or update" in c,
"has commit footer item": lambda c: "Commit footer" in c,
"specifies ISSUES CLOSED": lambda c: "ISSUES CLOSED" in c,
"has CI passes item": lambda c: "CI passes" in c,
"mentions quality gates": lambda c: "quality gates" in c,
"has BDD tests item": lambda c: "BDD/Behave tests" in c,
"instructs add or update features": lambda c: "added or updated" in c,
"has Epic reference item": lambda c: "Epic reference" in c,
"references parent Epic": lambda c: "parent Epic" in c,
"has labels item": lambda c: "Labels" in c,
"mentions forgejo-label-manager": lambda c: "forgejo-label-manager" in c,
"has milestone item": lambda c: "Milestone" in c,
"earliest open milestone": lambda c: "earliest open milestone" in c,
"all 8 items present": lambda c: all(
item in c for item in _required_items
),
}
def _make_validator(key: str) -> Callable[[Any], None]:
"""Factory that creates a typed Behave validator from a shared helper."""
def validator(context: Any) -> None:
content = context.agent_def_content
check_fn = VALIDATORS.get(key)
assert check_fn(content), f"Pool supervisor agent definition failed: {key}"
return validator
# ---------------------------------------------------------------------------
# Unique @given and @when — scoped to the pool supervisor agent def only
# ---------------------------------------------------------------------------
@given("the implementation-pool-supervisor.md agent definition exists")
def step_agent_def_exists(context: Any) -> None:
"""Verify the pool supervisor agent definition file exists."""
@@ -22,164 +88,130 @@ def step_read_agent_def(context: Any) -> None:
context.agent_def_content = AGENT_DEF_PATH.read_text(encoding="utf-8")
@then("the worker prompt body includes the PR compliance checklist section")
def step_prompt_includes_checklist(context: Any) -> None:
"""Verify the worker prompt body includes the PR compliance checklist."""
assert "PR Compliance Checklist" in context.agent_def_content, (
"Worker prompt body does not include 'PR Compliance Checklist'"
)
# ---------------------------------------------------------------------------
# Unique @then — prefixed with "Pool:" so they never conflict with the
# shared pr_compliance_checklist_steps.py step definitions.
# ---------------------------------------------------------------------------
# Scenario: Pool supervisor worker prompt includes the PR compliance checklist
@then("Pool: worker prompt body includes the PR compliance checklist section")
def pool_step_prompt_includes_checklist(context: Any) -> None:
"""Verify the pool-supervisor worker prompt body includes the PR compliance checklist."""
_make_validator("includes checklist section")(context)
@then("the checklist is marked as MANDATORY")
def step_checklist_is_mandatory(context: Any) -> None:
"""Verify the checklist is marked as MANDATORY."""
assert "MANDATORY" in context.agent_def_content, (
"PR Compliance Checklist is not marked as MANDATORY"
)
@then("Pool: the checklist is marked as MANDATORY")
def pool_step_checklist_is_mandatory(context: Any) -> None:
"""Verify the pool-supervisor checklist is marked as MANDATORY."""
_make_validator("is marked MANDATORY")(context)
@then("the worker prompt body includes a CHANGELOG.md checklist item")
def step_prompt_includes_changelog_item(context: Any) -> None:
"""Verify the worker prompt body includes a CHANGELOG.md checklist item."""
assert "CHANGELOG.md" in context.agent_def_content, (
"Worker prompt body does not include a CHANGELOG.md checklist item"
)
# Scenario: Checklist item 1 — CHANGELOG.md update required
@then("Pool: worker prompt body includes a CHANGELOG.md checklist item")
def pool_step_prompt_includes_changelog_item(context: Any) -> None:
"""Verify the pool-supervisor worker prompt body includes a CHANGELOG.md checklist item."""
_make_validator("has CHANGELOG.md item")(context)
@then("the item instructs workers to add an entry under the Unreleased section")
def step_changelog_item_unreleased(context: Any) -> None:
@then("Pool: the item instructs workers to add an entry under the Unreleased section")
def pool_step_changelog_item_unreleased(context: Any) -> None:
"""Verify the CHANGELOG.md item mentions the Unreleased section."""
assert "[Unreleased]" in context.agent_def_content, (
"CHANGELOG.md checklist item does not mention the [Unreleased] section"
)
_make_validator("references Unreleased")(context)
@then("the worker prompt body includes a CONTRIBUTORS.md checklist item")
def step_prompt_includes_contributors_item(context: Any) -> None:
"""Verify the worker prompt body includes a CONTRIBUTORS.md checklist item."""
assert "CONTRIBUTORS.md" in context.agent_def_content, (
"Worker prompt body does not include a CONTRIBUTORS.md checklist item"
)
# Scenario: Checklist item 2 — CONTRIBUTORS.md update required
@then("Pool: worker prompt body includes a CONTRIBUTORS.md checklist item")
def pool_step_prompt_includes_contributors_item(context: Any) -> None:
"""Verify the pool-supervisor worker prompt body includes a CONTRIBUTORS.md checklist item."""
_make_validator("has CONTRIBUTORS.md item")(context)
@then("the item instructs workers to add or update their contribution entry")
def step_contributors_item_add_update(context: Any) -> None:
@then("Pool: the item instructs workers to add or update their contribution entry")
def pool_step_contributors_item_add_update(context: Any) -> None:
"""Verify the CONTRIBUTORS.md item instructs workers to add or update."""
assert "add or update" in context.agent_def_content, (
"CONTRIBUTORS.md checklist item does not instruct workers to add or update"
)
_make_validator("instructs add or update")(context)
@then("the worker prompt body includes a commit footer checklist item")
def step_prompt_includes_commit_footer_item(context: Any) -> None:
"""Verify the worker prompt body includes a commit footer checklist item."""
assert "Commit footer" in context.agent_def_content, (
"Worker prompt body does not include a commit footer checklist item"
)
# Scenario: Checklist item 3 — commit footer required
@then("Pool: worker prompt body includes a commit footer checklist item")
def pool_step_prompt_includes_commit_footer_item(context: Any) -> None:
"""Verify the pool-supervisor worker prompt body includes a commit footer checklist item."""
_make_validator("has commit footer item")(context)
@then("the item specifies the ISSUES CLOSED footer format")
def step_commit_footer_issues_closed(context: Any) -> None:
@then("Pool: the item specifies the ISSUES CLOSED footer format")
def pool_step_commit_footer_issues_closed(context: Any) -> None:
"""Verify the commit footer item specifies the ISSUES CLOSED format."""
assert "ISSUES CLOSED" in context.agent_def_content, (
"Commit footer checklist item does not specify the ISSUES CLOSED format"
)
_make_validator("specifies ISSUES CLOSED")(context)
@then("the worker prompt body includes a CI passes checklist item")
def step_prompt_includes_ci_item(context: Any) -> None:
"""Verify the worker prompt body includes a CI passes checklist item."""
assert "CI passes" in context.agent_def_content, (
"Worker prompt body does not include a CI passes checklist item"
)
# Scenario: Checklist item 4 — CI must pass before PR creation
@then("Pool: worker prompt body includes a CI passes checklist item")
def pool_step_prompt_includes_ci_item(context: Any) -> None:
"""Verify the pool-supervisor worker prompt body includes a CI passes checklist item."""
_make_validator("has CI passes item")(context)
@then("the item instructs workers to verify all quality gates are green")
def step_ci_item_quality_gates(context: Any) -> None:
"""Verify the CI item instructs workers to verify quality gates."""
assert "quality gates" in context.agent_def_content, (
"CI checklist item does not mention quality gates"
)
@then("Pool: the item instructs workers to verify all quality gates are green")
def pool_step_ci_item_quality_gates(context: Any) -> None:
"""Verify the CI item instructs workers to verify quality gates are green."""
_make_validator("mentions quality gates")(context)
@then("the worker prompt body includes a BDD tests checklist item")
def step_prompt_includes_bdd_item(context: Any) -> None:
"""Verify the worker prompt body includes a BDD/Behave tests checklist item."""
assert "BDD/Behave tests" in context.agent_def_content, (
"Worker prompt body does not include a BDD/Behave tests checklist item"
)
# Scenario: Checklist item 5 — BDD/Behave tests required
@then("Pool: worker prompt body includes a BDD tests checklist item")
def pool_step_prompt_includes_bdd_item(context: Any) -> None:
"""Verify the pool-supervisor worker prompt body includes a BDD/Behave tests checklist item."""
_make_validator("has BDD tests item")(context)
@then("the item instructs workers to add or update Behave feature files")
def step_bdd_item_feature_files(context: Any) -> None:
@then("Pool: the item instructs workers to add or update Behave feature files")
def pool_step_bdd_item_feature_files(context: Any) -> None:
"""Verify the BDD item instructs workers to add or update feature files."""
assert "added or updated" in context.agent_def_content, (
"BDD checklist item does not instruct workers to add or update feature files"
)
_make_validator("instructs add or update features")(context)
@then("the worker prompt body includes an Epic reference checklist item")
def step_prompt_includes_epic_item(context: Any) -> None:
"""Verify the worker prompt body includes an Epic reference checklist item."""
assert "Epic reference" in context.agent_def_content, (
"Worker prompt body does not include an Epic reference checklist item"
)
# Scenario: Checklist item 6 — Epic reference required in PR description
@then("Pool: worker prompt body includes an Epic reference checklist item")
def pool_step_prompt_includes_epic_item(context: Any) -> None:
"""Verify the pool-supervisor worker prompt body includes an Epic reference checklist item."""
_make_validator("has Epic reference item")(context)
@then("the item instructs workers to reference the parent Epic issue number")
def step_epic_item_parent_reference(context: Any) -> None:
@then("Pool: the item instructs workers to reference the parent Epic issue number")
def pool_step_epic_item_parent_reference(context: Any) -> None:
"""Verify the Epic item instructs workers to reference the parent Epic."""
assert "parent Epic" in context.agent_def_content, (
"Epic checklist item does not instruct workers to reference the parent Epic"
)
_make_validator("references parent Epic")(context)
@then("the worker prompt body includes a labels checklist item")
def step_prompt_includes_labels_item(context: Any) -> None:
"""Verify the worker prompt body includes a labels checklist item."""
assert "Labels" in context.agent_def_content, (
"Worker prompt body does not include a labels checklist item"
)
# Scenario: Checklist item 7 — Labels must be applied
@then("Pool: worker prompt body includes a labels checklist item")
def pool_step_prompt_includes_labels_item(context: Any) -> None:
"""Verify the pool-supervisor worker prompt body includes a labels checklist item."""
_make_validator("has labels item")(context)
@then("the item instructs workers to apply labels via forgejo-label-manager")
def step_labels_item_forgejo_label_manager(context: Any) -> None:
@then("Pool: the item instructs workers to apply labels via forgejo-label-manager")
def pool_step_labels_item_forgejo_label_manager(context: Any) -> None:
"""Verify the labels item instructs workers to use forgejo-label-manager."""
assert "forgejo-label-manager" in context.agent_def_content, (
"Labels checklist item does not mention forgejo-label-manager"
)
_make_validator("mentions forgejo-label-manager")(context)
@then("the worker prompt body includes a milestone checklist item")
def step_prompt_includes_milestone_item(context: Any) -> None:
"""Verify the worker prompt body includes a milestone checklist item."""
assert "Milestone" in context.agent_def_content, (
"Worker prompt body does not include a milestone checklist item"
)
# Scenario: Checklist item 8 — Milestone must be assigned
@then("Pool: worker prompt body includes a milestone checklist item")
def pool_step_prompt_includes_milestone_item(context: Any) -> None:
"""Verify the pool-supervisor worker prompt body includes a milestone checklist item."""
_make_validator("has milestone item")(context)
@then("the item instructs workers to assign the earliest open milestone")
def step_milestone_item_earliest(context: Any) -> None:
@then("Pool: the item instructs workers to assign the earliest open milestone")
def pool_step_milestone_item_earliest(context: Any) -> None:
"""Verify the milestone item instructs workers to assign the earliest open milestone."""
assert "earliest open milestone" in context.agent_def_content, (
"Milestone checklist item does not mention the earliest open milestone"
)
_make_validator("earliest open milestone")(context)
@then("the worker prompt body contains all 8 mandatory checklist items")
def step_prompt_contains_all_8_items(context: Any) -> None:
"""Verify the worker prompt body contains all 8 mandatory checklist items."""
required_items = [
"CHANGELOG.md",
"CONTRIBUTORS.md",
"ISSUES CLOSED",
"CI passes",
"BDD/Behave tests",
"Epic reference",
"forgejo-label-manager",
"earliest open milestone",
]
missing = [item for item in required_items if item not in context.agent_def_content]
assert not missing, (
f"Worker prompt body is missing the following checklist items: {missing}"
)
# Scenario: All 8 checklist items are present in the worker prompt
@then("Pool: worker prompt body contains all 8 mandatory checklist items")
def pool_step_prompt_contains_all_8_items(context: Any) -> None:
"""Verify the pool-supervisor worker prompt body contains all 8 mandatory checklist items."""
_make_validator("all 8 items present")(context)