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
134 lines
6.8 KiB
Gherkin
134 lines
6.8 KiB
Gherkin
@phase1 @infrastructure @sandbox @no_sandbox
|
|
Feature: Non-sandboxed resource behaviour
|
|
As a system that manages resources without isolation
|
|
I want changes to be applied immediately and invalid operations to be rejected
|
|
So that non-sandboxable resources behave predictably and safely
|
|
|
|
# --- Setup validation ---
|
|
|
|
Scenario: A non-sandboxed resource rejects an empty resource identifier during setup
|
|
When a non-sandboxed resource is prepared with an empty identifier
|
|
Then the non-sandboxed setup should fail with a resource identifier error
|
|
|
|
Scenario: A non-sandboxed resource rejects an empty path during setup
|
|
When a non-sandboxed resource is prepared with an empty path
|
|
Then the non-sandboxed setup should fail with a path error
|
|
|
|
# --- Context before creation ---
|
|
|
|
Scenario: A non-sandboxed resource has no context before it is created
|
|
Given a non-sandboxed resource for "my-api" at "/srv/api"
|
|
Then the non-sandboxed resource should have no context
|
|
|
|
# --- Creation validation ---
|
|
|
|
Scenario: Creating a non-sandboxed resource with an empty plan identifier is rejected
|
|
Given a non-sandboxed resource for "my-api" at "/srv/api"
|
|
When the non-sandboxed resource is created with an empty plan identifier
|
|
Then the non-sandboxed setup should fail with a plan identifier error
|
|
|
|
Scenario: Creating a non-sandboxed resource records the correct context
|
|
Given a non-sandboxed resource for "my-api" at "/srv/api"
|
|
When the non-sandboxed resource is created for plan "01JKXYZ1234567890ABCDEFGH"
|
|
Then the non-sandboxed context should reference plan "01JKXYZ1234567890ABCDEFGH"
|
|
And the non-sandboxed context should reference resource "my-api"
|
|
And the non-sandboxed context should use the original path as the sandbox path
|
|
And the non-sandboxed context should have strategy metadata "none"
|
|
|
|
# --- Path resolution ---
|
|
|
|
Scenario: Resolving a path before the resource is created is rejected
|
|
Given a non-sandboxed resource for "my-api" at "/srv/api"
|
|
When a file path is resolved against the non-sandboxed resource
|
|
Then the non-sandboxed path resolution should be rejected as premature
|
|
|
|
Scenario: Resolving a path after cleanup is rejected
|
|
Given a non-sandboxed resource for "my-api" at "/srv/api"
|
|
And the non-sandboxed resource has been created and committed and cleaned up
|
|
When a file path is resolved against the non-sandboxed resource
|
|
Then the non-sandboxed path resolution should be rejected as premature
|
|
|
|
Scenario: Resolving a path with directory traversal is rejected
|
|
Given a non-sandboxed resource for "my-api" at "/srv/api"
|
|
And the non-sandboxed resource has been created for plan "01JKXYZ1234567890ABCDEFGH"
|
|
When a file path containing directory traversal is resolved
|
|
Then the non-sandboxed path resolution should be rejected as unsafe
|
|
|
|
Scenario: The first path resolution activates a non-sandboxed resource
|
|
Given a non-sandboxed resource for "my-api" at "/srv/api"
|
|
And the non-sandboxed resource has been created for plan "01JKXYZ1234567890ABCDEFGH"
|
|
When the file "src/main.py" is resolved against the non-sandboxed resource
|
|
Then the non-sandboxed resource should be in the "active" state
|
|
And the resolved non-sandboxed path should combine the original path and file
|
|
|
|
Scenario: Subsequent path resolutions keep the resource active
|
|
Given a non-sandboxed resource for "my-api" at "/srv/api"
|
|
And the non-sandboxed resource has been created for plan "01JKXYZ1234567890ABCDEFGH"
|
|
And the file "first.py" has been resolved against the non-sandboxed resource
|
|
When the file "second.py" is resolved against the non-sandboxed resource
|
|
Then the non-sandboxed resource should be in the "active" state
|
|
|
|
# --- Commit ---
|
|
|
|
Scenario: Committing from a pending state is rejected
|
|
Given a non-sandboxed resource for "my-api" at "/srv/api"
|
|
When the non-sandboxed resource is committed
|
|
Then the non-sandboxed commit should be rejected as premature
|
|
|
|
Scenario: Committing after cleanup is rejected
|
|
Given a non-sandboxed resource for "my-api" at "/srv/api"
|
|
And the non-sandboxed resource has been created and committed and cleaned up
|
|
When the non-sandboxed resource is committed
|
|
Then the non-sandboxed commit should be rejected as premature
|
|
|
|
Scenario: A successful commit returns a positive result
|
|
Given a non-sandboxed resource for "my-api" at "/srv/api"
|
|
And the non-sandboxed resource has been created for plan "01JKXYZ1234567890ABCDEFGH"
|
|
When the non-sandboxed resource is committed
|
|
Then the non-sandboxed commit result should indicate success
|
|
And the non-sandboxed resource should be in the "committed" state
|
|
|
|
Scenario: A commit from the active state also succeeds
|
|
Given a non-sandboxed resource for "my-api" at "/srv/api"
|
|
And the non-sandboxed resource has been created for plan "01JKXYZ1234567890ABCDEFGH"
|
|
And the file "src/main.py" has been resolved against the non-sandboxed resource
|
|
When the non-sandboxed resource is committed
|
|
Then the non-sandboxed commit result should indicate success
|
|
|
|
# --- Rollback ---
|
|
|
|
Scenario: Rollback is always rejected for non-sandboxed resources
|
|
Given a non-sandboxed resource for "my-api" at "/srv/api"
|
|
And the non-sandboxed resource has been created for plan "01JKXYZ1234567890ABCDEFGH"
|
|
When the non-sandboxed resource is rolled back
|
|
Then the non-sandboxed rollback should be rejected as impossible
|
|
|
|
Scenario: Rollback is rejected even from the active state
|
|
Given a non-sandboxed resource for "my-api" at "/srv/api"
|
|
And the non-sandboxed resource has been created for plan "01JKXYZ1234567890ABCDEFGH"
|
|
And the file "src/main.py" has been resolved against the non-sandboxed resource
|
|
When the non-sandboxed resource is rolled back
|
|
Then the non-sandboxed rollback should be rejected as impossible
|
|
|
|
# --- Cleanup ---
|
|
|
|
Scenario: Cleaning up an already cleaned resource is harmless
|
|
Given a non-sandboxed resource for "my-api" at "/srv/api"
|
|
And the non-sandboxed resource has been created and committed and cleaned up
|
|
When the non-sandboxed resource is cleaned up again
|
|
Then the non-sandboxed resource should remain in the "cleaned_up" state
|
|
|
|
Scenario: Cleanup after commit transitions to cleaned up
|
|
Given a non-sandboxed resource for "my-api" at "/srv/api"
|
|
And the non-sandboxed resource has been created for plan "01JKXYZ1234567890ABCDEFGH"
|
|
And the non-sandboxed resource has been committed
|
|
When the non-sandboxed resource is cleaned up
|
|
Then the non-sandboxed resource should be in the "cleaned_up" state
|
|
|
|
# --- Identity ---
|
|
|
|
Scenario: Each non-sandboxed resource receives a unique identifier
|
|
Given a non-sandboxed resource for "api-1" at "/srv/api1"
|
|
And another non-sandboxed resource for "api-2" at "/srv/api2"
|
|
Then the two non-sandboxed resources should have different identifiers
|