27bef69ddd
Add decision persistence layer with DecisionRepository, DecisionModel, and Alembic migration. Includes tree queries (BFS traversal via deque, path-to-root), superseded lookup, ordered decision path retrieval, concrete Decision type annotations (via TYPE_CHECKING), and comprehensive test coverage (Behave BDD, Robot Framework, ASV benchmarks). Updated database_schema.md, CHANGELOG.md, and CONTRIBUTORS.md. ISSUES CLOSED: #171
150 lines
6.3 KiB
Gherkin
150 lines
6.3 KiB
Gherkin
Feature: Decision persistence via DecisionRepository
|
|
As a developer
|
|
I want decisions to persist correctly through the DecisionRepository
|
|
So that the decision tree is durable across operations
|
|
|
|
Background:
|
|
Given a fresh in-memory decision persistence database
|
|
And a prerequisite action "local/decision-action" exists for decisions
|
|
And a prerequisite plan exists for decisions
|
|
|
|
# ------------------------------------------------------------------
|
|
# Create and retrieve
|
|
# ------------------------------------------------------------------
|
|
|
|
Scenario: Create a root decision and retrieve it by ID
|
|
Given a new root decision with sequence 0
|
|
When I persist the decision via the decision repository
|
|
Then I can retrieve the decision by its ID
|
|
And the persisted decision type should be "prompt_definition"
|
|
And the persisted decision should be a root
|
|
|
|
Scenario: Create a child decision and retrieve it
|
|
Given a persisted root decision
|
|
And a new child decision of type "strategy_choice" with sequence 1
|
|
When I persist the decision via the decision repository
|
|
Then I can retrieve the decision by its ID
|
|
And the persisted decision should not be a root
|
|
And the persisted decision parent should match the root
|
|
|
|
Scenario: Persisted decision preserves all scalar fields
|
|
Given a new root decision with all fields populated
|
|
When I persist the decision via the decision repository
|
|
Then I can retrieve the decision by its ID
|
|
And the persisted decision question should match
|
|
And the persisted decision chosen_option should match
|
|
And the persisted decision confidence_score should be 0.85
|
|
And the persisted decision rationale should match
|
|
|
|
# ------------------------------------------------------------------
|
|
# JSON round-trip
|
|
# ------------------------------------------------------------------
|
|
|
|
Scenario: Alternatives considered round-trip through JSON
|
|
Given a new decision with 3 alternatives
|
|
When I persist the decision via the decision repository
|
|
Then I can retrieve the decision by its ID
|
|
And the persisted decision should have 3 alternatives
|
|
|
|
Scenario: Context snapshot round-trip through JSON
|
|
Given a new decision with a populated context snapshot
|
|
When I persist the decision via the decision repository
|
|
Then I can retrieve the decision by its ID
|
|
And the persisted decision context hash should be "sha256:persist-test"
|
|
And the persisted decision context should have 2 resources
|
|
|
|
Scenario: Artifacts produced round-trip through JSON
|
|
Given a new decision with 2 artifacts
|
|
When I persist the decision via the decision repository
|
|
Then I can retrieve the decision by its ID
|
|
And the persisted decision should have 2 artifacts
|
|
|
|
Scenario: Downstream IDs round-trip through JSON
|
|
Given a new decision with downstream IDs
|
|
When I persist the decision via the decision repository
|
|
Then I can retrieve the decision by its ID
|
|
And the persisted decision should have downstream decision IDs
|
|
And the persisted decision should have downstream plan IDs
|
|
|
|
# ------------------------------------------------------------------
|
|
# Get by plan
|
|
# ------------------------------------------------------------------
|
|
|
|
Scenario: Get decisions by plan returns ordered results
|
|
Given 3 persisted decisions for the same plan
|
|
When I get decisions by plan ID
|
|
Then I should get 3 decisions in sequence order
|
|
|
|
# ------------------------------------------------------------------
|
|
# Get tree (BFS)
|
|
# ------------------------------------------------------------------
|
|
|
|
Scenario: Get tree returns root and descendants in BFS order
|
|
Given a persisted decision tree with 3 levels
|
|
When I get the decision tree from the root
|
|
Then the tree should have 4 decisions
|
|
And the first decision in the tree should be the root
|
|
|
|
# ------------------------------------------------------------------
|
|
# Get path to root
|
|
# ------------------------------------------------------------------
|
|
|
|
Scenario: Get path to root walks from leaf to root
|
|
Given a persisted decision tree with 3 levels
|
|
When I get the path from a leaf to root
|
|
Then the path should start with the leaf
|
|
And the path should end with the root
|
|
|
|
# ------------------------------------------------------------------
|
|
# Correction and superseded
|
|
# ------------------------------------------------------------------
|
|
|
|
Scenario: Create a correction decision
|
|
Given a persisted root decision
|
|
And a correction decision that corrects the root
|
|
When I persist the decision via the decision repository
|
|
Then I can retrieve the decision by its ID
|
|
And the persisted decision is_correction should be true
|
|
|
|
Scenario: Update superseded_by on an existing decision
|
|
Given a persisted root decision
|
|
When I update the root decision superseded_by to a new ULID
|
|
Then the root decision should be marked as superseded
|
|
|
|
Scenario: Get superseded decisions for a plan
|
|
Given a persisted root decision that is superseded
|
|
When I get superseded decisions for the plan
|
|
Then I should get 1 superseded decision
|
|
|
|
# ------------------------------------------------------------------
|
|
# List by type
|
|
# ------------------------------------------------------------------
|
|
|
|
Scenario: List decisions by type filters correctly
|
|
Given 2 persisted decisions of type "strategy_choice"
|
|
And 1 persisted decision of type "implementation_choice"
|
|
When I list decisions by type "strategy_choice"
|
|
Then I should get 2 decisions of that type
|
|
|
|
# ------------------------------------------------------------------
|
|
# Delete
|
|
# ------------------------------------------------------------------
|
|
|
|
Scenario: Delete a decision by ID
|
|
Given a persisted root decision
|
|
When I delete the root decision
|
|
Then the root decision should no longer exist
|
|
|
|
Scenario: Delete a non-existent decision returns false
|
|
When I try to delete a non-existent decision
|
|
Then the decision delete result should be false
|
|
|
|
# ------------------------------------------------------------------
|
|
# Duplicate detection
|
|
# ------------------------------------------------------------------
|
|
|
|
Scenario: Creating a duplicate decision raises DuplicateDecisionError
|
|
Given a persisted root decision
|
|
When I try to persist the same decision again
|
|
Then a DuplicateDecisionError should be raised
|