forked from HAL9000/cleveragents-core
007af498b8
Renamed all 11 task-type confidence threshold fields in AutomationProfile from phase-transition semantics to spec-defined task-type semantics. Updated all 8 built-in profiles, CLI formatting, YAML schema, services, and all Behave/Robot tests referencing the old field names. Post-review fixes: - Fixed 24 stale old field names in M6 fixture files (automation_profiles.json, autonomy_guardrails.json) - Added model_validator(mode='before') to detect legacy field names and raise actionable ValueError with rename mapping - Added semantic bridge comments in PlanLifecycleService mapping task-type thresholds to phase-transition gates - Added threshold_field to structured log messages for observability - Restored categorised CLI automation-profile show output to match spec (Phase Transitions / Decision Automation / Self-Repair / Execution Controls) instead of flat list - Added missing access_network field to spec show output examples (Rich, Plain, JSON, YAML variants) - Aligned ADR-017 profile fields table to all 11 fields with descriptions matching spec Automatable Tasks table - Aligned automation_profiles.md threshold descriptions with spec - Added spec section references in phase_reversion.md, error_recovery.md, and plan_execute.md for field naming context - Extended repository roundtrip test to assert all 11 threshold fields - Fixed benchmark _make_profile() passing safety fields as top-level kwargs instead of via SafetyProfile sub-model (incompatible with extra="forbid") - Aligned CLI JSON/YAML output structure for automation-profile show with the specification grouped format (phase_transitions, decision_automation, self_repair, execution_controls) - Moved safety boolean fields into the Execution Controls section of Rich output per spec examples - Reverted auto profile description to "Fully automatic except apply" per specification (line 16703, line 28406) - Improved bridge comments in test steps with semantic context for threshold-to-gate mappings ISSUES CLOSED: #902
209 lines
7.8 KiB
Python
209 lines
7.8 KiB
Python
"""Step definitions for plan_lifecycle_transitions_r2.feature.
|
|
|
|
Targets lifecycle transition branches in ``plan_lifecycle_service.py``:
|
|
|
|
* Non-reusable ``use_action`` archiving (line 576-577).
|
|
* ``execute_plan`` persisted mode (line 216 True via ``_commit_plan``).
|
|
* ``apply_plan`` persisted mode (line 216 True via ``_commit_plan``).
|
|
* ``cancel_plan`` persisted mode (line 216 True via ``_commit_plan``).
|
|
* ``pause_plan`` / ``resume_plan`` persisted mode.
|
|
|
|
All step text uses the ``r2plc-`` prefix to avoid collisions with
|
|
existing step definitions.
|
|
|
|
Shared steps (Background, common Given/Then) live in
|
|
``plan_lifecycle_error_r2_steps.py`` and are discovered globally by
|
|
Behave from the ``steps/`` directory.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from behave import given, then, when
|
|
from behave.runner import Context
|
|
|
|
from cleveragents.domain.models.core.action import ActionState
|
|
from cleveragents.domain.models.core.plan import (
|
|
PlanPhase,
|
|
ProjectLink,
|
|
)
|
|
|
|
# -------------------------------------------------------------------
|
|
# Helpers
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
def _create_action(context: Context, name: str, **kwargs: Any) -> Any:
|
|
"""Create an action through the service with sensible defaults."""
|
|
defaults: dict[str, Any] = {
|
|
"name": name,
|
|
"description": f"R2 test action {name}",
|
|
"definition_of_done": "Tests pass",
|
|
"strategy_actor": "openai/gpt-4",
|
|
"execution_actor": "openai/gpt-4",
|
|
}
|
|
defaults.update(kwargs)
|
|
return context.r2_service.create_action(**defaults)
|
|
|
|
|
|
# ===================================================================
|
|
# use_action with non-reusable action (line 576-577)
|
|
# ===================================================================
|
|
|
|
|
|
@given('r2plc-a non-reusable action "{name}" exists')
|
|
def step_r2_non_reusable_action(context: Context, name: str) -> None:
|
|
context.r2_ctx.reset_mock()
|
|
_create_action(context, name, reusable=False)
|
|
context.r2_ctx.reset_mock()
|
|
|
|
|
|
@when("r2plc-I use the non-reusable action to create a plan")
|
|
def step_r2_use_non_reusable(context: Context) -> None:
|
|
context.r2_plan = context.r2_service.use_action(
|
|
action_name="local/r2-oneshot",
|
|
project_links=[ProjectLink(project_name="proj-r2-oneshot")],
|
|
)
|
|
|
|
|
|
@then('r2plc-the action "{name}" should be archived')
|
|
def step_r2_check_non_reusable_archived(context: Context, name: str) -> None:
|
|
action = context.r2_service.get_action(name)
|
|
assert action.state == ActionState.ARCHIVED, (
|
|
f"Expected action '{name}' to be ARCHIVED, got {action.state}"
|
|
)
|
|
|
|
|
|
@then("r2plc-a plan should have been created from the action")
|
|
def step_r2_check_plan_created(context: Context) -> None:
|
|
assert context.r2_plan is not None, "Expected a plan to be created"
|
|
assert context.r2_plan.phase == PlanPhase.STRATEGIZE
|
|
|
|
|
|
# ===================================================================
|
|
# execute_plan persisted mode (line 216 True via _commit_plan)
|
|
# ===================================================================
|
|
|
|
|
|
@given("r2plc-a plan in STRATEGIZE COMPLETE state")
|
|
def step_r2_plan_strategize_complete(context: Context) -> None:
|
|
"""Create a plan and advance to STRATEGIZE/COMPLETE."""
|
|
context.r2_ctx.reset_mock()
|
|
action = _create_action(context, f"local/r2-exec-{id(context)}")
|
|
plan = context.r2_service.use_action(
|
|
action_name=str(action.namespaced_name),
|
|
project_links=[ProjectLink(project_name="proj-r2-exec")],
|
|
)
|
|
context.r2_service.start_strategize(plan.identity.plan_id)
|
|
context.r2_service.complete_strategize(plan.identity.plan_id)
|
|
# complete_strategize calls auto_progress which may call execute_plan
|
|
# Re-fetch the plan to see its actual state
|
|
context.r2_plan = context.r2_service.get_plan(plan.identity.plan_id)
|
|
# If auto_progress already executed, the plan may be in EXECUTE.
|
|
# For testing execute_plan explicitly, we need the plan in STRATEGIZE/COMPLETE.
|
|
# The manual profile (default) has create_tool=1.0 so auto_progress won't fire.
|
|
context.r2_ctx.reset_mock()
|
|
|
|
|
|
@when("r2plc-I call execute_plan in persisted mode")
|
|
def step_r2_execute_plan_persisted(context: Context) -> None:
|
|
context.r2_plan = context.r2_service.execute_plan(context.r2_plan.identity.plan_id)
|
|
|
|
|
|
@then("r2plc-the plan should be in EXECUTE phase")
|
|
def step_r2_check_execute_phase(context: Context) -> None:
|
|
assert context.r2_plan.phase == PlanPhase.EXECUTE, (
|
|
f"Expected EXECUTE, got {context.r2_plan.phase}"
|
|
)
|
|
|
|
|
|
# ===================================================================
|
|
# apply_plan persisted mode (line 216 True via _commit_plan)
|
|
# ===================================================================
|
|
|
|
|
|
@given("r2plc-a plan in EXECUTE COMPLETE state")
|
|
def step_r2_plan_execute_complete(context: Context) -> None:
|
|
"""Create a plan and advance to EXECUTE/COMPLETE."""
|
|
context.r2_ctx.reset_mock()
|
|
action = _create_action(context, f"local/r2-apply-{id(context)}")
|
|
plan = context.r2_service.use_action(
|
|
action_name=str(action.namespaced_name),
|
|
project_links=[ProjectLink(project_name="proj-r2-apply")],
|
|
)
|
|
pid = plan.identity.plan_id
|
|
context.r2_service.start_strategize(pid)
|
|
context.r2_service.complete_strategize(pid)
|
|
context.r2_service.execute_plan(pid)
|
|
context.r2_service.start_execute(pid)
|
|
context.r2_service.complete_execute(pid)
|
|
context.r2_plan = context.r2_service.get_plan(pid)
|
|
context.r2_ctx.reset_mock()
|
|
|
|
|
|
@when("r2plc-I call apply_plan in persisted mode")
|
|
def step_r2_apply_plan_persisted(context: Context) -> None:
|
|
context.r2_plan = context.r2_service.apply_plan(context.r2_plan.identity.plan_id)
|
|
|
|
|
|
@then("r2plc-the plan should be in APPLY phase")
|
|
def step_r2_check_apply_phase(context: Context) -> None:
|
|
assert context.r2_plan.phase == PlanPhase.APPLY, (
|
|
f"Expected APPLY, got {context.r2_plan.phase}"
|
|
)
|
|
|
|
|
|
# ===================================================================
|
|
# cancel_plan persisted mode (line 216 True via _commit_plan)
|
|
# ===================================================================
|
|
|
|
|
|
@when('r2plc-I cancel the plan with reason "{reason}"')
|
|
def step_r2_cancel_plan(context: Context, reason: str) -> None:
|
|
context.r2_ctx.reset_mock()
|
|
context.r2_plan = context.r2_service.cancel_plan(
|
|
context.r2_plan.identity.plan_id, reason=reason
|
|
)
|
|
|
|
|
|
# ===================================================================
|
|
# pause_plan persisted mode
|
|
# ===================================================================
|
|
|
|
|
|
@when("r2plc-I pause the plan")
|
|
def step_r2_pause_plan(context: Context) -> None:
|
|
context.r2_ctx.reset_mock()
|
|
context.r2_plan = context.r2_service.pause_plan(context.r2_plan.identity.plan_id)
|
|
|
|
|
|
@then("r2plc-the plan automation profile should be manual")
|
|
def step_r2_check_manual_profile(context: Context) -> None:
|
|
assert context.r2_plan.automation_profile is not None
|
|
assert context.r2_plan.automation_profile.profile_name == "manual", (
|
|
f"Expected 'manual', got '{context.r2_plan.automation_profile.profile_name}'"
|
|
)
|
|
|
|
|
|
# ===================================================================
|
|
# resume_plan persisted mode
|
|
# ===================================================================
|
|
|
|
|
|
@when('r2plc-I resume the plan with profile "{profile}"')
|
|
def step_r2_resume_plan(context: Context, profile: str) -> None:
|
|
context.r2_ctx.reset_mock()
|
|
context.r2_plan = context.r2_service.resume_plan(
|
|
context.r2_plan.identity.plan_id,
|
|
automation_profile=profile,
|
|
)
|
|
|
|
|
|
@then('r2plc-the plan automation profile should be "{profile}"')
|
|
def step_r2_check_profile(context: Context, profile: str) -> None:
|
|
assert context.r2_plan.automation_profile is not None
|
|
assert context.r2_plan.automation_profile.profile_name == profile, (
|
|
f"Expected '{profile}', got '{context.r2_plan.automation_profile.profile_name}'"
|
|
)
|