Feature: Decision type phase-gating at recording time As a developer I want the DecisionService to enforce phase-gating on decision types So that decisions recorded during Strategize or Execute are constrained to the types permitted in each phase Background: Given a phase-gated decision service # --- Valid types in Strategize phase --- Scenario Outline: Valid decision types succeed in Strategize phase When I record a "" decision in the "strategize" phase Then the phase-gated decision should be recorded successfully And the phase-gated decision type should be "" Examples: | decision_type | | prompt_definition | | invariant_enforced | | strategy_choice | | resource_selection | | subplan_spawn | | subplan_parallel_spawn | | user_intervention | # --- Valid types in Execute phase --- Scenario Outline: Valid decision types succeed in Execute phase When I record a "" decision in the "execute" phase Then the phase-gated decision should be recorded successfully And the phase-gated decision type should be "" Examples: | decision_type | | implementation_choice | | resource_selection | | tool_invocation | | error_recovery | | validation_response | | subplan_spawn | | subplan_parallel_spawn | | user_intervention | # --- Invalid types in Strategize phase --- Scenario Outline: Execute-only types are rejected in Strategize phase When I try to record a "" decision in the "strategize" phase Then a phase violation error should be raised And the phase violation error decision_type should be "" And the phase violation error plan_phase should be "strategize" And the phase violation error allowed_types should not contain "" Examples: | decision_type | | implementation_choice | | tool_invocation | | error_recovery | | validation_response | # --- Invalid types in Execute phase --- Scenario Outline: Strategize-only types are rejected in Execute phase When I try to record a "" decision in the "execute" phase Then a phase violation error should be raised And the phase violation error decision_type should be "" And the phase violation error plan_phase should be "execute" And the phase violation error allowed_types should not contain "" Examples: | decision_type | | prompt_definition | | invariant_enforced | | strategy_choice | # --- Phase-agnostic types work in both phases --- Scenario Outline: Phase-agnostic types succeed in both phases When I record a "" decision in the "strategize" phase Then the phase-gated decision should be recorded successfully When I record a "" decision in the "execute" phase Then the phase-gated decision should be recorded successfully Examples: | decision_type | | resource_selection | | subplan_spawn | | subplan_parallel_spawn | | user_intervention | # --- Error message quality --- Scenario: Phase violation error message includes type, phase, and allowed types When I try to record a "tool_invocation" decision in the "strategize" phase Then a phase violation error should be raised And the phase violation error message should contain "tool_invocation" And the phase violation error message should contain "strategize" And the phase violation error message should contain "Allowed types" # --- Phase-gating via database lookup --- Scenario: Phase resolved from database when plan_phase not explicitly provided Given a phase-gated decision service with a persisted strategize plan When I record a "strategy_choice" decision without explicit phase Then the phase-gated decision should be recorded successfully When I try to record a "tool_invocation" decision without explicit phase Then a phase violation error should be raised Scenario: Phase resolved from database for execute plan Given a phase-gated decision service with a persisted execute plan When I record a "tool_invocation" decision without explicit phase Then the phase-gated decision should be recorded successfully When I try to record a "strategy_choice" decision without explicit phase Then a phase violation error should be raised Scenario: Phase-gating skipped when plan not found in database Given a phase-gated decision service with an empty database When I record a "tool_invocation" decision without explicit phase Then the phase-gated decision should be recorded successfully # --- No phase-gating when phase is unknown --- Scenario: Phase-gating is skipped when phase cannot be resolved Given a phase-gated decision service without persistence When I record a "tool_invocation" decision without explicit phase Then the phase-gated decision should be recorded successfully # --- PHASE_ALLOWED_TYPES constant --- Scenario: PHASE_ALLOWED_TYPES maps strategize and execute correctly Then the PHASE_ALLOWED_TYPES should map "strategize" to STRATEGIZE_TYPES And the PHASE_ALLOWED_TYPES should map "execute" to EXECUTE_TYPES # --- DecisionPhaseViolationError attributes --- Scenario: DecisionPhaseViolationError exposes correct attributes Given a DecisionPhaseViolationError for type "tool_invocation" in phase "strategize" Then the phase violation error decision_type should be "tool_invocation" And the phase violation error plan_phase should be "strategize" And the phase violation error allowed_types should not be empty # --- _validate_phase_gating static method --- Scenario: _validate_phase_gating does not raise for ungated phases When I call _validate_phase_gating with type "tool_invocation" and phase "action" Then no phase violation error should be raised When I call _validate_phase_gating with type "tool_invocation" and phase "apply" Then no phase violation error should be raised # --- String coercion of plan_phase --- Scenario: plan_phase accepts a string value When I record a "strategy_choice" decision with string phase "strategize" Then the phase-gated decision should be recorded successfully Scenario: plan_phase accepts a PlanPhase enum directly When I record a "strategy_choice" decision with PlanPhase enum "strategize" Then the phase-gated decision should be recorded successfully # --- Invalid plan_phase string --- Scenario: Invalid plan_phase string raises ValidationError When I try to record a "strategy_choice" decision with invalid phase "not_a_real_phase" Then a validation error should be raised for invalid phase And the validation error message should contain "not_a_real_phase" # --- Database error resilience --- Scenario: resolve_plan_phase gracefully handles database errors Given a corrupted database unit of work for phase resolution When I call resolve_plan_phase with the corrupted unit of work Then resolve_plan_phase should return None