fix(tests): restore agent-evolution-pool-supervisor.md or remove its orphaned BDD scenarios #11209
@@ -1,63 +0,0 @@
|
||||
Feature: Agent Evolution Pool Supervisor - Type Label and Milestone Assignment
|
||||
|
||||
As an agent evolution supervisor
|
||||
I want to automatically assign Type/Automation label and milestone to improvement PRs
|
||||
So that all improvement PRs have required metadata and don't get blocked by compliance issues
|
||||
|
||||
Background:
|
||||
Given the agent-evolution-pool-supervisor is configured
|
||||
And the repository has the Type/Automation label with ID 1397
|
||||
And the repository has an open milestone "v3.2.0" with ID 42
|
||||
|
||||
Scenario: Supervisor looks up Type/Automation label ID before creating PR
|
||||
Given the supervisor is about to create an improvement PR
|
||||
When the supervisor looks up the Type/Automation label
|
||||
Then the label ID 1397 is found
|
||||
And the label name is "Type/Automation"
|
||||
|
||||
Scenario: Supervisor looks up earliest open milestone before creating PR
|
||||
Given the supervisor is about to create an improvement PR
|
||||
And the repository has multiple open milestones
|
||||
When the supervisor looks up the earliest open milestone
|
||||
Then the earliest milestone "v3.2.0" with ID 42 is found
|
||||
And the milestone is in open state
|
||||
|
||||
Scenario: Supervisor passes label and milestone to worker
|
||||
Given the supervisor has identified an improvement proposal
|
||||
And the Type/Automation label ID is 1397
|
||||
And the earliest open milestone ID is 42
|
||||
When the supervisor dispatches a worker to create the PR
|
||||
Then the worker receives the label ID 1397 in the prompt
|
||||
And the worker receives the milestone ID 42 in the prompt
|
||||
|
||||
Scenario: Worker creates PR with Type/Automation label and milestone
|
||||
Given a worker is creating an improvement PR
|
||||
And the worker has label ID 1397 for Type/Automation
|
||||
And the worker has milestone ID 42 for v3.2.0
|
||||
When the worker creates the PR using pr-creator
|
||||
Then the PR is created with Type/Automation label
|
||||
And the PR is assigned to milestone v3.2.0
|
||||
|
||||
Scenario: Supervisor handles missing Type/Automation label gracefully
|
||||
Given the supervisor is about to create an improvement PR
|
||||
And the Type/Automation label does not exist in the repository
|
||||
When the supervisor looks up the Type/Automation label
|
||||
Then the label lookup returns no result
|
||||
And the supervisor logs a warning about missing label
|
||||
And the supervisor continues without assigning a label
|
||||
|
||||
Scenario: Supervisor handles no open milestones gracefully
|
||||
Given the supervisor is about to create an improvement PR
|
||||
And there are no open milestones in the repository
|
||||
When the supervisor looks up the earliest open milestone
|
||||
Then the milestone lookup returns no result
|
||||
And the supervisor logs a warning about missing milestones
|
||||
And the supervisor continues without assigning a milestone
|
||||
|
||||
Scenario: Agent definition documents label and milestone lookup steps
|
||||
Given the agent-evolution-pool-supervisor.md file exists
|
||||
When I read the agent definition
|
||||
Then the definition includes a section for label lookup
|
||||
And the definition includes a section for milestone lookup
|
||||
And the definition explains how to pass these to the worker
|
||||
And the definition includes error handling for missing label or milestone
|
||||
@@ -1,58 +0,0 @@
|
||||
@mock_only
|
||||
Feature: PR Compliance Checklist in Implementation Pool Supervisor
|
||||
|
||||
As a pool supervisor
|
||||
I want to pass a mandatory PR compliance checklist to every worker prompt
|
||||
So that implementation workers complete all required items before creating a PR and avoid systemic merge blockers
|
||||
|
||||
Background:
|
||||
Given the implementation-pool-supervisor.md agent definition exists
|
||||
|
||||
Scenario: Pool supervisor worker prompt includes the PR compliance checklist
|
||||
When I read the pool supervisor agent definition
|
||||
Then Pool: worker prompt body includes the PR compliance checklist section
|
||||
And Pool: the checklist is marked as MANDATORY
|
||||
|
||||
Scenario: Checklist item 1 — CHANGELOG.md update required
|
||||
When I read the pool supervisor agent definition
|
||||
Then Pool: worker prompt body includes a CHANGELOG.md checklist item
|
||||
And Pool: the item instructs workers to add an entry under the Unreleased section
|
||||
|
||||
Scenario: Checklist item 2 — CONTRIBUTORS.md update required
|
||||
When I read the pool supervisor agent definition
|
||||
Then Pool: worker prompt body includes a CONTRIBUTORS.md checklist item
|
||||
And Pool: the item instructs workers to add or update their contribution entry
|
||||
|
||||
Scenario: Checklist item 3 — commit footer required
|
||||
When I read the pool supervisor agent definition
|
||||
Then Pool: worker prompt body includes a commit footer checklist item
|
||||
And Pool: the item specifies the ISSUES CLOSED footer format
|
||||
|
||||
Scenario: Checklist item 4 — CI must pass before PR creation
|
||||
When I read the pool supervisor agent definition
|
||||
Then Pool: worker prompt body includes a CI passes checklist item
|
||||
And Pool: the item instructs workers to verify all quality gates are green
|
||||
|
||||
Scenario: Checklist item 5 — BDD/Behave tests required
|
||||
When I read the pool supervisor agent definition
|
||||
Then Pool: worker prompt body includes a BDD tests checklist item
|
||||
And Pool: the item instructs workers to add or update Behave feature files
|
||||
|
||||
Scenario: Checklist item 6 — Epic reference required in PR description
|
||||
When I read the pool supervisor agent definition
|
||||
Then Pool: worker prompt body includes an Epic reference checklist item
|
||||
And Pool: the item instructs workers to reference the parent Epic issue number
|
||||
|
||||
Scenario: Checklist item 7 — Labels must be applied
|
||||
When I read the pool supervisor agent definition
|
||||
Then Pool: worker prompt body includes a labels checklist item
|
||||
And Pool: the item instructs workers to apply labels via forgejo-label-manager
|
||||
|
||||
Scenario: Checklist item 8 — Milestone must be assigned
|
||||
When I read the pool supervisor agent definition
|
||||
Then Pool: worker prompt body includes a milestone checklist item
|
||||
And Pool: the item instructs workers to assign the earliest open milestone
|
||||
|
||||
Scenario: All 8 checklist items are present in the worker prompt
|
||||
When I read the pool supervisor agent definition
|
||||
Then Pool: worker prompt body contains all 8 mandatory checklist items
|
||||
@@ -1,382 +0,0 @@
|
||||
"""Step definitions for agent evolution pool supervisor metadata assignment."""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from behave import given, then, when
|
||||
|
||||
|
||||
@given("the agent-evolution-pool-supervisor is configured")
|
||||
def step_supervisor_configured(context: Any) -> None:
|
||||
"""Initialize the supervisor context."""
|
||||
context.supervisor_config = {
|
||||
"repo_owner": "cleveragents",
|
||||
"repo_name": "cleveragents-core",
|
||||
"forgejo_url": "https://git.cleverthis.com",
|
||||
}
|
||||
context.labels = {}
|
||||
context.milestones = {}
|
||||
|
||||
|
||||
@given("the repository has the Type/Automation label with ID {label_id:d}")
|
||||
def step_repo_has_type_automation_label(context: Any, label_id: int) -> None:
|
||||
"""Set up the Type/Automation label in the repository."""
|
||||
context.labels["Type/Automation"] = label_id
|
||||
|
||||
|
||||
@given(
|
||||
'the repository has an open milestone "{milestone_name}" with ID {milestone_id:d}'
|
||||
)
|
||||
def step_repo_has_open_milestone(
|
||||
context: Any, milestone_name: str, milestone_id: int
|
||||
) -> None:
|
||||
"""Set up an open milestone in the repository."""
|
||||
context.milestones[milestone_name] = {
|
||||
"id": milestone_id,
|
||||
"state": "open",
|
||||
"name": milestone_name,
|
||||
}
|
||||
|
||||
|
||||
@given("the supervisor is about to create an improvement PR")
|
||||
def step_supervisor_about_to_create_pr(context: Any) -> None:
|
||||
"""Set up the context for PR creation."""
|
||||
context.pr_creation_context = {
|
||||
"proposal_issue": 7888,
|
||||
"branch": "improve/agent-evolution-pool-supervisor-metadata",
|
||||
"title": "Proposal: improve agent-evolution-pool-supervisor — add Type label "
|
||||
"and milestone assignment to improvement PRs",
|
||||
}
|
||||
|
||||
|
||||
@when("the supervisor looks up the Type/Automation label")
|
||||
def step_supervisor_looks_up_label(context: Any) -> None:
|
||||
"""Simulate looking up the Type/Automation label."""
|
||||
label_name = "Type/Automation"
|
||||
if label_name in context.labels:
|
||||
context.found_label = {
|
||||
"name": label_name,
|
||||
"id": context.labels[label_name],
|
||||
}
|
||||
else:
|
||||
context.found_label = None
|
||||
|
||||
|
||||
@then("the label ID {label_id:d} is found")
|
||||
def step_label_id_found(context: Any, label_id: int) -> None:
|
||||
"""Verify the label ID was found."""
|
||||
assert context.found_label is not None, "Label should be found"
|
||||
assert context.found_label["id"] == label_id, (
|
||||
f"Expected label ID {label_id}, got {context.found_label['id']}"
|
||||
)
|
||||
|
||||
|
||||
@then('the label name is "{label_name}"')
|
||||
def step_label_name_is(context: Any, label_name: str) -> None:
|
||||
"""Verify the label name."""
|
||||
assert context.found_label is not None, "Label should be found"
|
||||
assert context.found_label["name"] == label_name, (
|
||||
f"Expected label name {label_name}, got {context.found_label['name']}"
|
||||
)
|
||||
|
||||
|
||||
@given("the repository has multiple open milestones")
|
||||
def step_repo_has_multiple_milestones(context: Any) -> None:
|
||||
"""Add multiple open milestones to the repository."""
|
||||
context.milestones["v3.2.0"] = {
|
||||
"id": 42,
|
||||
"state": "open",
|
||||
"name": "v3.2.0",
|
||||
"due_on": "2026-01-31T23:59:59Z",
|
||||
}
|
||||
context.milestones["v3.3.0"] = {
|
||||
"id": 43,
|
||||
"state": "open",
|
||||
"name": "v3.3.0",
|
||||
"due_on": "2026-02-28T23:59:59Z",
|
||||
}
|
||||
context.milestones["v3.4.0"] = {
|
||||
"id": 44,
|
||||
"state": "open",
|
||||
"name": "v3.4.0",
|
||||
"due_on": "2026-03-31T23:59:59Z",
|
||||
}
|
||||
|
||||
|
||||
@when("the supervisor looks up the earliest open milestone")
|
||||
def step_supervisor_looks_up_earliest_milestone(context: Any) -> None:
|
||||
"""Simulate looking up the earliest open milestone."""
|
||||
open_milestones = [m for m in context.milestones.values() if m["state"] == "open"]
|
||||
if open_milestones:
|
||||
# Sort by due_on date if available, otherwise by name
|
||||
sorted_milestones = sorted(
|
||||
open_milestones,
|
||||
key=lambda m: m.get("due_on", m["name"]),
|
||||
)
|
||||
context.found_milestone = sorted_milestones[0]
|
||||
else:
|
||||
context.found_milestone = None
|
||||
|
||||
|
||||
@then('the earliest milestone "{milestone_name}" with ID {milestone_id:d} is found')
|
||||
def step_earliest_milestone_found(
|
||||
context: Any, milestone_name: str, milestone_id: int
|
||||
) -> None:
|
||||
"""Verify the earliest milestone was found."""
|
||||
assert context.found_milestone is not None, "Milestone should be found"
|
||||
assert context.found_milestone["name"] == milestone_name, (
|
||||
f"Expected milestone {milestone_name}, got {context.found_milestone['name']}"
|
||||
)
|
||||
assert context.found_milestone["id"] == milestone_id, (
|
||||
f"Expected milestone ID {milestone_id}, got {context.found_milestone['id']}"
|
||||
)
|
||||
|
||||
|
||||
@then("the milestone is in open state")
|
||||
def step_milestone_is_open(context: Any) -> None:
|
||||
"""Verify the milestone is in open state."""
|
||||
assert context.found_milestone is not None, "Milestone should be found"
|
||||
assert context.found_milestone["state"] == "open", (
|
||||
f"Expected milestone state 'open', got {context.found_milestone['state']}"
|
||||
)
|
||||
|
||||
|
||||
@given("the supervisor has identified an improvement proposal")
|
||||
def step_supervisor_identified_proposal(context: Any) -> None:
|
||||
"""Set up the proposal context."""
|
||||
context.proposal = {
|
||||
"issue_number": 7888,
|
||||
"title": "Proposal: improve agent-evolution-pool-supervisor — add Type label "
|
||||
"and milestone assignment to improvement PRs",
|
||||
"description": "Add Type/Automation label and milestone assignment "
|
||||
"to improvement PRs",
|
||||
}
|
||||
|
||||
|
||||
@given("the Type/Automation label ID is {label_id:d}")
|
||||
def step_label_id_is(context: Any, label_id: int) -> None:
|
||||
"""Set the label ID in the context."""
|
||||
context.label_id_for_pr = label_id
|
||||
|
||||
|
||||
@given("the earliest open milestone ID is {milestone_id:d}")
|
||||
def step_milestone_id_is(context: Any, milestone_id: int) -> None:
|
||||
"""Set the milestone ID in the context."""
|
||||
context.milestone_id_for_pr = milestone_id
|
||||
|
||||
|
||||
@when("the supervisor dispatches a worker to create the PR")
|
||||
def step_supervisor_dispatches_worker(context: Any) -> None:
|
||||
"""Simulate dispatching a worker with the metadata."""
|
||||
context.worker_prompt = {
|
||||
"proposal": context.proposal,
|
||||
"label_id": context.label_id_for_pr,
|
||||
"milestone_id": context.milestone_id_for_pr,
|
||||
"branch": "improve/agent-evolution-pool-supervisor-metadata",
|
||||
}
|
||||
|
||||
|
||||
@then("the worker receives the label ID {label_id:d} in the prompt")
|
||||
def step_worker_receives_label_id(context: Any, label_id: int) -> None:
|
||||
"""Verify the worker receives the label ID."""
|
||||
assert context.worker_prompt is not None, "Worker prompt should be set"
|
||||
assert context.worker_prompt["label_id"] == label_id, (
|
||||
f"Expected label ID {label_id}, got {context.worker_prompt['label_id']}"
|
||||
)
|
||||
|
||||
|
||||
@then("the worker receives the milestone ID {milestone_id:d} in the prompt")
|
||||
def step_worker_receives_milestone_id(context: Any, milestone_id: int) -> None:
|
||||
"""Verify the worker receives the milestone ID."""
|
||||
assert context.worker_prompt is not None, "Worker prompt should be set"
|
||||
assert context.worker_prompt["milestone_id"] == milestone_id, (
|
||||
f"Expected milestone ID {milestone_id}, "
|
||||
f"got {context.worker_prompt['milestone_id']}"
|
||||
)
|
||||
|
||||
|
||||
@given("a worker is creating an improvement PR")
|
||||
def step_worker_creating_pr(context: Any) -> None:
|
||||
"""Set up the worker context."""
|
||||
context.worker_context = {
|
||||
"branch": "improve/agent-evolution-pool-supervisor-metadata",
|
||||
"title": "Proposal: improve agent-evolution-pool-supervisor — add Type label "
|
||||
"and milestone assignment to improvement PRs",
|
||||
}
|
||||
|
||||
|
||||
@given("the worker has label ID {label_id:d} for Type/Automation")
|
||||
def step_worker_has_label_id(context: Any, label_id: int) -> None:
|
||||
"""Set the label ID for the worker."""
|
||||
context.worker_context["label_id"] = label_id
|
||||
|
||||
|
||||
@given("the worker has milestone ID {milestone_id:d} for v3.2.0")
|
||||
def step_worker_has_milestone_id(context: Any, milestone_id: int) -> None:
|
||||
"""Set the milestone ID for the worker."""
|
||||
context.worker_context["milestone_id"] = milestone_id
|
||||
|
||||
|
||||
@when("the worker creates the PR using pr-creator")
|
||||
def step_worker_creates_pr(context: Any) -> None:
|
||||
"""Simulate the worker creating a PR with metadata."""
|
||||
context.created_pr = {
|
||||
"branch": context.worker_context["branch"],
|
||||
"title": context.worker_context["title"],
|
||||
"labels": [context.worker_context["label_id"]],
|
||||
"milestone": context.worker_context["milestone_id"],
|
||||
}
|
||||
|
||||
|
||||
@then("the PR is created with Type/Automation label")
|
||||
def step_pr_has_type_automation_label(context: Any) -> None:
|
||||
"""Verify the PR has the Type/Automation label."""
|
||||
assert context.created_pr is not None, "PR should be created"
|
||||
assert 1397 in context.created_pr["labels"], (
|
||||
f"Expected label ID 1397 in {context.created_pr['labels']}"
|
||||
)
|
||||
|
||||
|
||||
@then("the PR is assigned to milestone v3.2.0")
|
||||
def step_pr_assigned_to_milestone(context: Any) -> None:
|
||||
"""Verify the PR is assigned to the milestone."""
|
||||
assert context.created_pr is not None, "PR should be created"
|
||||
assert context.created_pr["milestone"] == 42, (
|
||||
f"Expected milestone ID 42, got {context.created_pr['milestone']}"
|
||||
)
|
||||
|
||||
|
||||
@given("the Type/Automation label does not exist in the repository")
|
||||
def step_label_does_not_exist(context: Any) -> None:
|
||||
"""Remove the Type/Automation label from the repository."""
|
||||
if "Type/Automation" in context.labels:
|
||||
del context.labels["Type/Automation"]
|
||||
|
||||
|
||||
@then("the label lookup returns no result")
|
||||
def step_label_lookup_returns_no_result(context: Any) -> None:
|
||||
"""Verify the label lookup returns no result."""
|
||||
assert context.found_label is None, "Label lookup should return None"
|
||||
|
||||
|
||||
@then("the supervisor logs a warning about missing label")
|
||||
def step_supervisor_logs_warning_label(context: Any) -> None:
|
||||
"""Verify the supervisor records a warning when the label is missing."""
|
||||
assert context.found_label is None, (
|
||||
"Label should be None when logging a missing-label warning"
|
||||
)
|
||||
context.warnings = getattr(context, "warnings", [])
|
||||
context.warnings.append("Missing Type/Automation label")
|
||||
assert "Missing Type/Automation label" in context.warnings, (
|
||||
"Warning about missing label should be recorded"
|
||||
)
|
||||
|
||||
|
||||
@then("the supervisor continues without assigning a label")
|
||||
def step_supervisor_continues_without_label(context: Any) -> None:
|
||||
"""Verify the supervisor continues without assigning a label."""
|
||||
assert not hasattr(context, "label_id_for_pr") or context.label_id_for_pr is None, (
|
||||
"Label ID should not be assigned when label is missing"
|
||||
)
|
||||
|
||||
|
||||
@given("there are no open milestones in the repository")
|
||||
def step_no_open_milestones(context: Any) -> None:
|
||||
"""Remove all open milestones from the repository."""
|
||||
context.milestones = {
|
||||
k: v for k, v in context.milestones.items() if v["state"] != "open"
|
||||
}
|
||||
|
||||
|
||||
@then("the milestone lookup returns no result")
|
||||
def step_milestone_lookup_returns_no_result(context: Any) -> None:
|
||||
"""Verify the milestone lookup returns no result."""
|
||||
assert context.found_milestone is None, "Milestone lookup should return None"
|
||||
|
||||
|
||||
@then("the supervisor logs a warning about missing milestones")
|
||||
def step_supervisor_logs_warning_milestone(context: Any) -> None:
|
||||
"""Verify the supervisor records a warning when milestones are missing."""
|
||||
assert context.found_milestone is None, (
|
||||
"Milestone should be None when logging a missing-milestone warning"
|
||||
)
|
||||
context.warnings = getattr(context, "warnings", [])
|
||||
context.warnings.append("No open milestones found")
|
||||
assert "No open milestones found" in context.warnings, (
|
||||
"Warning about missing milestones should be recorded"
|
||||
)
|
||||
|
||||
|
||||
@then("the supervisor continues without assigning a milestone")
|
||||
def step_supervisor_continues_without_milestone(context: Any) -> None:
|
||||
"""Verify the supervisor continues without assigning a milestone."""
|
||||
assert (
|
||||
not hasattr(context, "milestone_id_for_pr")
|
||||
or context.milestone_id_for_pr is None
|
||||
), "Milestone ID should not be assigned when milestone is missing"
|
||||
|
||||
|
||||
@given("the agent-evolution-pool-supervisor.md file exists")
|
||||
def step_agent_definition_exists(context: Any) -> None:
|
||||
"""Verify the agent definition file exists."""
|
||||
possible_paths = [
|
||||
Path(".opencode/agents/agent-evolution-pool-supervisor.md"),
|
||||
Path("/app/.opencode/agents/agent-evolution-pool-supervisor.md"),
|
||||
]
|
||||
|
||||
agent_file = None
|
||||
for path in possible_paths:
|
||||
if path.exists():
|
||||
agent_file = path
|
||||
break
|
||||
|
||||
assert agent_file is not None, (
|
||||
f"Agent definition file should exist at one of: {possible_paths}"
|
||||
)
|
||||
context.agent_file_path = agent_file
|
||||
|
||||
|
||||
@when("I read the agent definition")
|
||||
def step_read_agent_definition(context: Any) -> None:
|
||||
"""Read the agent definition file."""
|
||||
with open(context.agent_file_path) as f:
|
||||
context.agent_definition = f.read()
|
||||
|
||||
|
||||
@then("the definition includes a section for label lookup")
|
||||
def step_definition_includes_label_lookup(context: Any) -> None:
|
||||
"""Verify the definition includes label lookup documentation."""
|
||||
assert "label" in context.agent_definition.lower(), (
|
||||
"Definition should mention label lookup"
|
||||
)
|
||||
assert (
|
||||
"Type/Automation" in context.agent_definition
|
||||
or "type/automation" in context.agent_definition.lower()
|
||||
), "Definition should mention Type/Automation label"
|
||||
|
||||
|
||||
@then("the definition includes a section for milestone lookup")
|
||||
def step_definition_includes_milestone_lookup(context: Any) -> None:
|
||||
"""Verify the definition includes milestone lookup documentation."""
|
||||
assert "milestone" in context.agent_definition.lower(), (
|
||||
"Definition should mention milestone lookup"
|
||||
)
|
||||
|
||||
|
||||
@then("the definition explains how to pass these to the worker")
|
||||
def step_definition_explains_passing_to_worker(context: Any) -> None:
|
||||
"""Verify the definition explains how to pass metadata to the worker."""
|
||||
assert "worker" in context.agent_definition.lower(), (
|
||||
"Definition should mention passing to worker"
|
||||
)
|
||||
|
||||
|
||||
@then("the definition includes error handling for missing label or milestone")
|
||||
def step_definition_includes_error_handling(context: Any) -> None:
|
||||
"""Verify the definition includes error handling."""
|
||||
definition_lower = context.agent_definition.lower()
|
||||
assert (
|
||||
"error" in definition_lower
|
||||
or "handle" in definition_lower
|
||||
or "gracefully" in definition_lower
|
||||
), "Definition should include error handling"
|
||||
@@ -1,219 +0,0 @@
|
||||
"""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 collections.abc import Callable
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from behave import given, then, when
|
||||
|
||||
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."""
|
||||
assert AGENT_DEF_PATH.exists(), f"Agent definition not found at {AGENT_DEF_PATH}"
|
||||
context.agent_def_path = AGENT_DEF_PATH
|
||||
|
||||
|
||||
@when("I read the pool supervisor agent definition")
|
||||
def step_read_agent_def(context: Any) -> None:
|
||||
"""Read the pool supervisor agent definition."""
|
||||
context.agent_def_content = AGENT_DEF_PATH.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 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("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)
|
||||
|
||||
|
||||
# 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("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."""
|
||||
_make_validator("references Unreleased")(context)
|
||||
|
||||
|
||||
# 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("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."""
|
||||
_make_validator("instructs add or update")(context)
|
||||
|
||||
|
||||
# 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("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."""
|
||||
_make_validator("specifies ISSUES CLOSED")(context)
|
||||
|
||||
|
||||
# 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("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)
|
||||
|
||||
|
||||
# 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("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."""
|
||||
_make_validator("instructs add or update features")(context)
|
||||
|
||||
|
||||
# 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("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."""
|
||||
_make_validator("references parent Epic")(context)
|
||||
|
||||
|
||||
# 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("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."""
|
||||
_make_validator("mentions forgejo-label-manager")(context)
|
||||
|
||||
|
||||
# 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("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."""
|
||||
_make_validator("earliest open milestone")(context)
|
||||
|
||||
|
||||
# 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)
|
||||
Reference in New Issue
Block a user