bc6677b3cd
Add comprehensive Subplan System specification defining module boundaries, data models (Subplan, SubplanResult, SubplanTree), PostgreSQL schema with indexes, the 8-step spawning algorithm, concurrency control via per-plan semaphores, and error handling. Implement invariant loading and enforcement in Strategize phase: - Add InvariantViolationError exception class - Add load_active_invariants() and check_invariants() methods to InvariantService - Add _is_violation heuristic for action/invariant matching - Add BDD tests with @load_invariants, @check_invariants, etc. tags ISSUES CLOSED: #8725
179 lines
8.7 KiB
Gherkin
179 lines
8.7 KiB
Gherkin
Feature: Invariant Enforcement in Strategize Phase
|
|
As a plan strategizer
|
|
I want invariants to be loaded and enforced during the Strategize phase
|
|
So that plan actions that violate constraints are rejected with clear error messages
|
|
|
|
Background:
|
|
Given a fresh InvariantService for enforcement
|
|
And a fresh PlanLifecycleService for enforcement
|
|
|
|
# === Invariant Loading ===
|
|
|
|
@load_invariants
|
|
Scenario: Load all active global invariants
|
|
Given a global invariant "Never delete production data" from source "system"
|
|
And a global invariant "All APIs must maintain backward compatibility" from source "system"
|
|
When I load active invariants for plan "01JQAAAAAAAAAAAAAAAAAAAA01"
|
|
Then 2 invariants should be loaded
|
|
And the loaded set should contain "Never delete production data"
|
|
And the loaded set should contain "All APIs must maintain backward compatibility"
|
|
|
|
@load_invariants
|
|
Scenario: Load invariants with project scope
|
|
Given a global invariant "Never delete production data" from source "system"
|
|
And a project invariant "Use ORM for all queries" from source "local/api-service" for project "local/api-service"
|
|
When I load active invariants for plan "01JQAAAAAAAAAAAAAAAAAAAA01" with project "local/api-service"
|
|
Then 2 invariants should be loaded
|
|
And the loaded set should contain "Never delete production data"
|
|
And the loaded set should contain "Use ORM for all queries"
|
|
|
|
@load_invariants
|
|
Scenario: Load invariants respecting plan > project > global precedence
|
|
Given a global invariant "Use REST for all APIs" from source "system"
|
|
And a project invariant "Use REST for all APIs" from source "local/api-service" for project "local/api-service"
|
|
And a plan invariant "Use REST for all APIs" from source "01JQAAAAAAAAAAAAAAAAAAAA01"
|
|
When I load active invariants for plan "01JQAAAAAAAAAAAAAAAAAAAA01" with project "local/api-service"
|
|
Then 1 invariant should be loaded
|
|
And the winning invariant for "use rest for all apis" should be from "plan" scope
|
|
|
|
@load_invariants
|
|
Scenario: Inactive invariants are not loaded
|
|
Given a global invariant "Never delete production data" from source "system"
|
|
And a global invariant "All APIs must maintain backward compatibility" from source "system"
|
|
When I deactivate the invariant "All APIs must maintain backward compatibility"
|
|
And I load active invariants for plan "01JQAAAAAAAAAAAAAAAAAAAA01"
|
|
Then 1 invariant should be loaded
|
|
And the loaded set should contain "Never delete production data"
|
|
And the loaded set should not contain "All APIs must maintain backward compatibility"
|
|
|
|
# === Invariant Checking ===
|
|
|
|
@check_invariants
|
|
Scenario: Action that violates "do not delete" invariant is rejected
|
|
Given a global invariant "Never delete production data" from source "system"
|
|
When I check action "Delete all production database records" against loaded invariants
|
|
Then an InvariantViolationError should be raised
|
|
And the error should include invariant ID
|
|
And the error should include the violated text "Never delete production data"
|
|
And the error should include the action text "Delete all production database records"
|
|
|
|
@check_invariants
|
|
Scenario: Action that violates "must not" invariant is rejected
|
|
Given a global invariant "Must not use hardcoded credentials" from source "system"
|
|
When I check action "Add hardcoded API key to config file" against loaded invariants
|
|
Then an InvariantViolationError should be raised
|
|
And the error should include the violated text "Must not use hardcoded credentials"
|
|
|
|
@check_invariants
|
|
Scenario: Action that complies with invariant is accepted
|
|
Given a global invariant "Never delete production data" from source "system"
|
|
When I check action "Create backup of production database" against loaded invariants
|
|
Then no inv error should be raised
|
|
And the action should be accepted
|
|
|
|
@check_invariants
|
|
Scenario: Multiple invariants are all checked
|
|
Given a global invariant "Never delete production data" from source "system"
|
|
And a global invariant "All APIs must maintain backward compatibility" from source "system"
|
|
When I check action "Create backup of production database" against loaded invariants
|
|
Then no inv error should be raised
|
|
And the action should be accepted
|
|
|
|
@check_invariants
|
|
Scenario: First violating invariant raises error
|
|
Given a global invariant "Never delete production data" from source "system"
|
|
And a global invariant "Must not use hardcoded credentials" from source "system"
|
|
When I check action "Delete production data with hardcoded key" against loaded invariants
|
|
Then an InvariantViolationError should be raised
|
|
And the error should include one of the violated invariants
|
|
|
|
@check_invariants
|
|
Scenario: Empty action text raises ValidationError
|
|
Given a global invariant "Never delete production data" from source "system"
|
|
When I check action "" against loaded invariants
|
|
Then an inv ValidationError should be raised
|
|
And the error message should contain "Action text must not be empty"
|
|
|
|
# === Integration with Strategize Phase ===
|
|
|
|
@strategize_integration
|
|
Scenario: Strategize phase loads invariants at startup
|
|
Given a plan "01JQAAAAAAAAAAAAAAAAAAAA01" in Strategize phase
|
|
And a global invariant "Never delete production data" from source "system"
|
|
When I start the Strategize phase for the plan
|
|
Then invariants should be loaded
|
|
And 1 invariant should be active for the plan
|
|
|
|
@strategize_integration
|
|
Scenario: Strategize phase rejects violating strategy decision
|
|
Given a plan "01JQAAAAAAAAAAAAAAAAAAAA01" in Strategize phase
|
|
And a global invariant "Never delete production data" from source "system"
|
|
When I attempt to create a strategy decision "Delete all production database records"
|
|
Then an InvariantViolationError should be raised
|
|
And the plan should remain in Strategize phase
|
|
And the decision should not be recorded
|
|
|
|
@strategize_integration
|
|
Scenario: Strategize phase accepts compliant strategy decision
|
|
Given a plan "01JQAAAAAAAAAAAAAAAAAAAA01" in Strategize phase
|
|
And a global invariant "Never delete production data" from source "system"
|
|
When I create a strategy decision "Create backup of production database"
|
|
Then the decision should be recorded
|
|
And the plan should progress normally
|
|
|
|
@strategize_integration
|
|
Scenario: Invariants survive plan restarts
|
|
Given a plan "01JQAAAAAAAAAAAAAAAAAAAA01" in Strategize phase
|
|
And a global invariant "Never delete production data" from source "system"
|
|
When I start the Strategize phase for the plan
|
|
And I restart the plan
|
|
And I start the Strategize phase again
|
|
Then invariants should be loaded fresh from database
|
|
And 1 invariant should be active for the plan
|
|
|
|
# === Error Messages ===
|
|
|
|
@error_messages
|
|
Scenario: Violation error includes all required information
|
|
Given a global invariant "Never delete production data" from source "system"
|
|
When I check action "Delete all production database records" against loaded invariants
|
|
Then an InvariantViolationError should be raised
|
|
And the error message should include the invariant ID
|
|
And the error message should include the invariant text
|
|
And the error message should include the action text
|
|
And the error should have scope information
|
|
And the error should have source_name information
|
|
|
|
@error_messages
|
|
Scenario: Clear error message identifies violated invariant
|
|
Given a global invariant "Never delete production data" from source "system"
|
|
When I check action "Delete all production database records" against loaded invariants
|
|
Then an InvariantViolationError should be raised
|
|
And the error message should clearly identify which invariant was violated
|
|
And the error message should explain why the action violates the invariant
|
|
|
|
# === Edge Cases ===
|
|
|
|
@edge_cases
|
|
Scenario: No invariants loaded for empty context
|
|
When I load active invariants for plan "01JQAAAAAAAAAAAAAAAAAAAA01"
|
|
Then 0 invariants should be loaded
|
|
|
|
@edge_cases
|
|
Scenario: Checking action with no invariants succeeds
|
|
When I check action "Delete all production database records" against empty invariants
|
|
Then no inv error should be raised
|
|
And the action should be accepted
|
|
|
|
@edge_cases
|
|
Scenario: Case-insensitive invariant checking
|
|
Given a global invariant "Never delete production data" from source "system"
|
|
When I check action "DELETE ALL PRODUCTION DATABASE RECORDS" against loaded invariants
|
|
Then an InvariantViolationError should be raised
|
|
|
|
@edge_cases
|
|
Scenario: Whitespace-only action text raises ValidationError
|
|
Given a global invariant "Never delete production data" from source "system"
|
|
When I check action " " against loaded invariants
|
|
Then an inv ValidationError should be raised
|