f2f7aa5dc9
Implement multiple Stage A/B/E/SEC milestones for the v3 lifecycle system: - Stage A5.3+A5.4: Add LifecycleActionModel and LifecyclePlanModel SQLAlchemy models with to_domain()/from_domain() conversion methods - Stage A5.6: Implement ActionRepository with full CRUD, namespace/state queries, referential integrity checks, and retry decorator - Stage E1: Add subplan domain models (ExecutionMode, SubplanMergeStrategy, SubplanConfig, SubplanStatus, SubplanAttempt, SubplanFailureHandler) with computed properties on Plan (is_subplan, is_root_plan, depth, has_subplans) - Stage A6: Add AutomationLevel enum (MANUAL, REVIEW_BEFORE_APPLY, FULL_AUTOMATION), settings integration, PlanLifecycleService auto-progression, pause/resume, and CLI commands (--automation-level, set-automation-level) - Stage SEC1: Remove eval()/exec() from stream_router.py, replace with named operation and transform registries; code blocks and unregistered transforms now raise StreamRoutingError - Add langchain-anthropic dependency - Update BDD tests for security changes and relax ADR directory requirement
177 lines
8.8 KiB
Gherkin
177 lines
8.8 KiB
Gherkin
@phase1 @database @models @lifecycle
|
|
Feature: Lifecycle Data Persistence and Retrieval
|
|
As a system operator
|
|
I want lifecycle actions and plans to survive database round-trips faithfully
|
|
So that no data is lost or corrupted when storing and loading lifecycle records
|
|
|
|
Background:
|
|
Given the lifecycle database is ready
|
|
And a lifecycle database session is open
|
|
|
|
@lifecycle_action @to_domain
|
|
Scenario: A stored action retains its identity and naming when loaded
|
|
Given an action record exists with valid attributes and tags
|
|
When the action record is loaded as a domain object
|
|
Then the loaded action should preserve its original identifier
|
|
And the loaded action should preserve its namespace and short name
|
|
And the loaded action should preserve its description fields
|
|
And the loaded action should preserve its actor assignments
|
|
And the loaded action should preserve its state and flags
|
|
And the loaded action should preserve its timestamps
|
|
And the loaded action should preserve its tags
|
|
|
|
@lifecycle_action @to_domain
|
|
Scenario: A stored action with input arguments loads them correctly
|
|
Given an action record exists with a structured arguments schema
|
|
When the action record is loaded as a domain object
|
|
Then the loaded action should contain the expected argument entries
|
|
And each argument entry should have the correct name and type
|
|
|
|
@lifecycle_action @to_domain
|
|
Scenario: A stored action with no inputs or tags loads empty collections
|
|
Given an action record exists with no inputs and no tags
|
|
When the action record is loaded as a domain object
|
|
Then the loaded action should have an empty arguments collection
|
|
And the loaded action should have an empty tags collection
|
|
|
|
@lifecycle_action @from_domain
|
|
Scenario: A fully populated action domain object is stored correctly
|
|
Given a complete action domain object is prepared
|
|
When the action domain object is stored as a database record
|
|
Then the stored record should preserve the action identifier
|
|
And the stored record should preserve the name components
|
|
And the stored record should preserve the description fields
|
|
And the stored record should preserve the actor assignments
|
|
And the stored record should serialize the arguments as JSON
|
|
And the stored record should serialize the tags as JSON
|
|
And the stored record should preserve the state value
|
|
And the stored record should format timestamps as ISO strings
|
|
|
|
@lifecycle_action @from_domain
|
|
Scenario: An action with an enumerated state stores the enum value
|
|
Given an action domain object is prepared with an enumerated state
|
|
When the action domain object is stored as a database record
|
|
Then the stored record state should equal the enum value
|
|
|
|
@lifecycle_action @from_domain
|
|
Scenario: An action with a custom string state stores it verbatim
|
|
Given an action-like object is prepared with a custom string state
|
|
When the custom-state action is stored as a database record
|
|
Then the stored record state should equal the custom string
|
|
|
|
@lifecycle_plan @parse_iso
|
|
Scenario: A valid ISO-8601 timestamp string is parsed to a datetime
|
|
When a valid ISO-8601 string is parsed as a timestamp
|
|
Then the parsed timestamp should be the expected datetime value
|
|
|
|
@lifecycle_plan @parse_iso
|
|
Scenario: A missing timestamp value parses to nothing
|
|
When an absent value is parsed as a timestamp
|
|
Then the parsed timestamp should be absent
|
|
|
|
@lifecycle_plan @to_iso
|
|
Scenario: A datetime is formatted as an ISO-8601 string
|
|
When a datetime value is formatted as an ISO string
|
|
Then the formatted string should match ISO-8601 format
|
|
|
|
@lifecycle_plan @to_iso
|
|
Scenario: An absent datetime formats to nothing
|
|
When an absent datetime is formatted as an ISO string
|
|
Then the formatted value should be absent
|
|
|
|
@lifecycle_plan @to_domain
|
|
Scenario: A plan in the action phase loads with its action state
|
|
Given a plan record exists in the action phase with draft state
|
|
When the plan record is loaded as a domain object
|
|
Then the loaded plan should preserve its identity
|
|
And the loaded plan should be in the action phase
|
|
And the loaded plan action state should be draft
|
|
And the loaded plan processing state should be absent
|
|
And the loaded plan should preserve its description
|
|
And the loaded plan should preserve its timestamps
|
|
And the loaded plan should preserve its metadata
|
|
|
|
@lifecycle_plan @to_domain
|
|
Scenario: A plan in the strategize phase loads with its processing state
|
|
Given a plan record exists in the strategize phase with processing state
|
|
When the plan record is loaded as a domain object
|
|
Then the loaded plan should be in the strategize phase
|
|
And the loaded plan processing state should be processing
|
|
And the loaded plan action state should be absent
|
|
|
|
@lifecycle_plan @to_domain
|
|
Scenario: A completed plan loads all phase timestamps correctly
|
|
Given a plan record exists with all phase timestamps populated
|
|
When the plan record is loaded as a domain object
|
|
Then the loaded plan timestamps should include the strategize window
|
|
And the loaded plan timestamps should include the execute window
|
|
And the loaded plan timestamps should include the apply window
|
|
|
|
@lifecycle_plan @to_domain
|
|
Scenario: A plan with associated projects and tags loads them as lists
|
|
Given a plan record exists with associated project identifiers and tags
|
|
When the plan record is loaded as a domain object
|
|
Then the loaded plan should contain the expected project identifiers
|
|
And the loaded plan should contain the expected tags
|
|
|
|
@lifecycle_plan @from_domain
|
|
Scenario: A plan with an action state stores the state correctly
|
|
Given a plan domain object is prepared in the action phase with an available state
|
|
When the plan domain object is stored as a database record
|
|
Then the stored plan record should have state "available"
|
|
And the stored plan record should preserve the plan identifier
|
|
And the stored plan record should preserve the phase
|
|
And the stored plan record should serialize the project identifiers as JSON
|
|
And the stored plan record should serialize the plan tags as JSON
|
|
And the stored plan record should format plan timestamps as ISO strings
|
|
And the stored plan record automation level should be "manual"
|
|
|
|
@lifecycle_plan @from_domain
|
|
Scenario: A plan with a processing state stores the state correctly
|
|
Given a plan domain object is prepared in the strategize phase with a queued state
|
|
When the plan domain object is stored as a database record
|
|
Then the stored plan record should have state "queued"
|
|
And the stored plan record phase should be "strategize"
|
|
|
|
@lifecycle_plan @from_domain
|
|
Scenario: A plan with no explicit state defaults to queued
|
|
Given a plan domain object is prepared with neither action nor processing state
|
|
When the stateless plan domain object is stored as a database record
|
|
Then the stored plan record should have state "queued"
|
|
|
|
@lifecycle_plan @from_domain
|
|
Scenario: A plan with all phase timestamps stores them as ISO strings
|
|
Given a plan domain object is prepared with all phase timestamps
|
|
When the plan domain object is stored as a database record
|
|
Then the stored plan record should have all phase timestamps as ISO strings
|
|
|
|
@lifecycle_plan @from_domain
|
|
Scenario: A plan with non-default automation level stores the real value
|
|
Given a plan domain object is prepared with automation level "full_automation"
|
|
When the plan domain object is stored as a database record
|
|
Then the stored plan record automation level should be "full_automation"
|
|
|
|
@lifecycle_plan @from_domain
|
|
Scenario: A plan with review-before-apply automation level stores correctly
|
|
Given a plan domain object is prepared with automation level "review_before_apply"
|
|
When the plan domain object is stored as a database record
|
|
Then the stored plan record automation level should be "review_before_apply"
|
|
|
|
@lifecycle_plan @from_domain
|
|
Scenario: A plan stored with an explicit action_id preserves it
|
|
Given a plan domain object is prepared in the action phase with an available state
|
|
When the plan domain object is stored with an explicit action identifier
|
|
Then the stored plan record should preserve the supplied action identifier
|
|
|
|
@lifecycle_action @round_trip
|
|
Scenario: An action survives a full save-and-reload cycle unchanged
|
|
Given a complete action domain object is prepared
|
|
When the action is saved to the database and reloaded as a domain object
|
|
Then the reloaded action should match the original action
|
|
|
|
@lifecycle_plan @round_trip
|
|
Scenario: A plan survives a full save-and-reload cycle unchanged
|
|
Given a plan record exists in the action phase with draft state
|
|
When the plan record is saved and then reloaded as a domain object
|
|
Then the reloaded plan should preserve its identity and phase
|