Files
cleveragents-core/features/plan_model.feature

268 lines
11 KiB
Gherkin

Feature: Plan Domain Model
As a developer
I want a plan domain model that supports the lifecycle
So that I can track plans through Strategize -> Execute -> Apply -> Applied phases
# NamespacedName Tests
Scenario: Parse simple name defaults to local namespace
When I parse the namespaced name "my-action"
Then the parsed namespace should be "local"
And the parsed item name should be "my-action"
And the parsed server should be empty
Scenario: Parse name with explicit local namespace
When I parse the namespaced name "local/my-action"
Then the parsed namespace should be "local"
And the parsed item name should be "my-action"
Scenario: Parse name with organization namespace
When I parse the namespaced name "myorg/my-action"
Then the parsed namespace should be "myorg"
And the parsed item name should be "my-action"
Scenario: Parse name with server qualifier
When I parse the namespaced name "prod:myorg/my-action"
Then the parsed server should be "prod"
And the parsed namespace should be "myorg"
And the parsed item name should be "my-action"
Scenario: NamespacedName string representation
Given I have a namespaced name with server "prod" namespace "myorg" and name "my-action"
Then the namespaced string representation should be "prod:myorg/my-action"
Scenario: NamespacedName without server
Given I have a namespaced name with namespace "local" and name "test-plan"
Then the namespaced string representation should be "local/test-plan"
Scenario: Invalid namespace with special characters fails
When I try to create a namespaced name with namespace "my@org" and name "test"
Then a validation error should be raised
Scenario: Empty namespace defaults to local
Given I create a namespaced name with an empty namespace and name "test-plan"
Then the parsed namespace should be "local"
# PlanPhase Tests
Scenario: Plan phases are in correct order
Then the plan phases should be in order "strategize, execute, apply, applied"
Scenario: STRATEGIZE phase is the starting phase
Given I create a new plan
Then the plan phase should be "strategize"
# PlanState Tests
Scenario: Strategize phase uses unified state
Given I create a new plan in action phase
Then the plan state should be "queued"
Scenario: Strategize phase defaults to queued state
Given I create a plan in strategize phase
Then the plan state should be "queued"
Scenario: Strategize phase defaults state and exposes it
Given I create a plan in action phase without action state
Then the plan state should be "queued"
Scenario: Strategize phase ignores legacy action state
Given I create a plan in strategize phase with action state set and no processing state
Then the plan state should be "queued"
# Plan Identity Tests
Scenario: Plan requires valid ULID
When I try to create a plan identity with invalid ULID "not-a-ulid"
Then a validation error should be raised
Scenario: Plan identity with valid ULID succeeds
When I create a plan identity with ULID "01ARZ3NDEKTSV4RRFFQ69G5FAV"
Then the plan identity should be valid
Scenario: Plan identity supports parent and root plan IDs
When I create a plan identity with parent "01ARZ3NDEKTSV4RRFFQ69G5FAV" and root "01ARZ3NDEKTSV4RRFFQ69G5FAW"
Then the parent plan ID should be "01ARZ3NDEKTSV4RRFFQ69G5FAV"
And the root plan ID should be "01ARZ3NDEKTSV4RRFFQ69G5FAW"
# Plan Lifecycle Tests
Scenario: New plan starts in STRATEGIZE phase with QUEUED state
Given I create a new plan with description "Test plan"
Then the plan phase should be "strategize"
And the plan state should be "queued"
And the plan should not be terminal
Scenario: Plan can check if it can transition to next phase
Given I create a plan in action phase with available state
Then the plan can transition to next phase
Scenario: Queued plan cannot transition to next phase
Given I create a new plan in action phase
Then the plan cannot transition to next phase
Scenario: Processing complete allows transition
Given I create a plan in execute phase with complete processing state
Then the plan can transition to next phase
Scenario: Get next phase from STRATEGIZE is EXECUTE
Given I create a new plan
Then the next phase should be "execute"
Scenario: Get next phase from APPLIED is None (terminal)
Given I create a plan in applied phase
Then the next phase should be empty
And the plan should be terminal
Scenario: Unknown phase returns no next phase
Given I create a plan with an unknown phase value
Then the next phase should be empty
# Phase Transition Validation Tests
Scenario: Valid transition from STRATEGIZE to EXECUTE
Then transition from "strategize" to "execute" should be valid
Scenario: Valid transition from EXECUTE to APPLY
Then transition from "execute" to "apply" should be valid
Scenario: Valid transition from APPLY to APPLIED
Then transition from "apply" to "applied" should be valid
Scenario: Invalid transition from STRATEGIZE to APPLY
Then transition from "strategize" to "apply" should be invalid
Scenario: No transition from APPLIED (terminal)
Then transition from "applied" to "strategize" should be invalid
# Error State Tests
Scenario: Plan in errored state is_errored returns True
Given I create a plan in strategize phase with errored state
Then the plan should be errored
Scenario: Plan in processing state is_errored returns False
Given I create a plan in strategize phase with processing state
Then the plan should not be errored
Scenario: Applied plan is terminal
Given I create a plan in action phase with archived state
Then the plan should be terminal
# Model Validation Tests
Scenario: Plan requires description
When I try to create a plan without description
Then a validation error should be raised
Scenario: Plan description cannot be empty
When I try to create a plan with empty description
Then a validation error should be raised
Scenario: Strategize phase accepts state
When I try to create a plan in action phase with processing state
Then the plan phase should be "strategize"
And the plan state should be "queued"
Scenario: Applied phase cannot transition to next phase
Given I create a plan in applied phase
Then the plan cannot transition to next phase
# Phase Transition Tests (spec-aligned: strategize -> execute -> apply -> applied)
Scenario: Full lifecycle transitions follow spec order
Then transition from "strategize" to "execute" should be valid
And transition from "execute" to "apply" should be valid
And transition from "apply" to "applied" should be valid
Scenario: Skipping phases is not allowed
Then transition from "strategize" to "apply" should be invalid
And transition from "strategize" to "applied" should be invalid
And transition from "execute" to "applied" should be invalid
Scenario: Backward transitions are not allowed
Then transition from "execute" to "strategize" should be invalid
And transition from "apply" to "execute" should be invalid
And transition from "applied" to "apply" should be invalid
# Automation Profile Provenance Tests
Scenario: Plan stores automation profile name at creation
Given I create a plan with automation profile "org/high-trust-profile"
Then the plan automation profile name should be "org/high-trust-profile"
And the plan model automation level should be "review_before_apply"
Scenario: Plan stores effective profile snapshot
Given I create a plan with automation profile and snapshot
Then the plan effective profile snapshot should contain "max_file_changes"
And the automation profile should be immutable after creation
Scenario: Plan as_cli_dict includes automation profile provenance
Given I create a plan with automation profile "org/high-trust-profile"
Then the cli dict should contain key "automation_profile"
And the cli dict "automation_profile" should be "org/high-trust-profile"
# Invariant Ordering Tests
Scenario: Invariants preserve insertion order
Given I create a plan with ordered invariants
Then the invariant at index 0 should have text "Global rule"
And the invariant at index 0 should have scope "global"
And the invariant at index 1 should have text "Project rule"
And the invariant at index 1 should have scope "project"
And the invariant at index 2 should have text "Plan rule"
And the invariant at index 2 should have scope "plan"
Scenario: Invariants appear in cli_dict with scope tags
Given I create a plan with ordered invariants
Then the cli dict should contain key "invariants"
And the cli dict invariants should have 3 entries
Scenario: Empty invariant text is rejected
When I try to create a plan invariant with empty text
Then a validation error should be raised
# Project Link Alias Validation Tests
Scenario: ProjectLink accepts valid alias
Given I create a project link with alias "my-project"
Then the project link alias should be "my-project"
Scenario: ProjectLink rejects alias with special characters
When I try to create a project link with alias "my@project"
Then a validation error should be raised
Scenario: ProjectLink rejects alias starting with hyphen
When I try to create a project link with alias "-invalid"
Then a validation error should be raised
Scenario: Plan rejects duplicate project link aliases
When I try to create a plan with duplicate project link aliases
Then a validation error should be raised
Scenario: ProjectLink alias is lowercased automatically
Given I create a project link with alias "MyProject"
Then the project link alias should be "myproject"
Scenario: Plan as_cli_dict includes action_name
Given I create a new plan
Then the cli dict should contain key "action_name"
And the cli dict "action_name" should be "local/test-action"
Scenario: Plan as_cli_dict renders all optional fields
Given I create a fully populated plan for cli dict testing
Then the cli dict should contain key "definition_of_done"
And the cli dict should contain key "strategy_actor"
And the cli dict should contain key "execution_actor"
And the cli dict should contain key "estimation_actor"
And the cli dict should contain key "invariant_actor"
And the cli dict should contain key "arguments"
And the cli dict should contain key "changeset_id"
And the cli dict should contain key "sandbox_refs"
And the cli dict should contain key "error_message"
And the cli dict should contain key "error_details"
And the cli dict should contain key "tags"
And the cli dict should contain key "parent_plan_id"
And the cli dict should contain key "root_plan_id"
And the cli dict should contain key "attempt"