diff --git a/.claude/HANDOFF_PROMPT_CLEANUP.md b/.claude/HANDOFF_PROMPT_CLEANUP.md new file mode 100644 index 0000000..e43bb96 --- /dev/null +++ b/.claude/HANDOFF_PROMPT_CLEANUP.md @@ -0,0 +1,227 @@ +# Context Window Handoff - Cleanup Work in Progress + +**Date**: 2025-01-14 +**Current Session**: Cleanup of proposed changes from demo review +**Status**: Session 1 Part 1 Complete (2/11 changes done) + +--- + +## Copy This Prompt to New Context Window + +``` +I'm continuing cleanup work on the Paper Dynasty / SBA web app. We're making changes based on a review of the frontend demo pages. + +Current status: Session 1 Part 1 complete (removed 4 unused config fields). Now need to continue with Session 1 Part 2 and beyond. + +Please read these files for full context: +- @.claude/PROPOSED_CHANGES_2025-01-14.md (master list of all 11 changes) +- @.claude/TODO_CLEANUP_COMPLETE.md (what we just finished) +- @backend/app/config/base_config.py (we just modified this) +- @backend/tests/unit/config/test_league_configs.py (we just modified this) + +Key decisions from Cal: +1. Remove supports_manual_result_selection() - DONE ✅ +2. Pitching change player ID - Will use league-independent polymorphic Player ID +3. Remove defensive alignment & offensive approach - DONE, remove completely +4. Offensive workflow refactor - YES, do before Phase F6 (last thing in cleanup) +5. Hit location fix - Only needed for: GROUNDOUT, FLYOUT, LINEOUT, SINGLE_UNCAPPED, DOUBLE_UNCAPPED, ERROR + +Current TODO list (in TodoWrite): +- [completed] Remove strikes_for_out and balls_for_walk fields +- [completed] Remove supports_manual_result_selection() method +- [pending] Fix hit location requirements +- [pending] Remove defensive alignment from DefensiveDecision model +- [pending] Remove offensive approach from OffensiveDecision model +- [pending] Add infield depth validation (infield_in, corners_in, normal) +- [pending] Add outfield depth validation (normal, shallow with walk-off rules) +- [pending] Refactor offensive decision workflow (swing_away, check_jump, hit_and_run, sac_bunt, squeeze_bunt) +- [pending] Add check jump validation (lead runner only) + +Workflow: +- Session 1: Quick wins (Changes #1-5) - IN PROGRESS +- Session 2: Standard changes (Changes #6-7) - NOT STARTED +- Session 3: Major refactor (Changes #8-11) - NOT STARTED + +Dev server is running at http://localhost:3005 with demo pages. + +Please continue from Session 1 Part 2: Fix hit location requirements. +``` + +--- + +## Files Modified So Far (Session 1 Part 1) + +### backend/app/config/base_config.py +**Changes**: Removed 3 items +- Line 27: `strikes_for_out` field +- Line 28: `balls_for_walk` field +- Lines 41-48: `supports_manual_result_selection()` method + +**Current state**: Clean, tests passing + +### backend/tests/unit/config/test_league_configs.py +**Changes**: Updated 2 test methods +- `test_sba_basic_rules`: Removed assertions for strikes/balls +- `test_sba_supports_auto_mode`: Renamed from manual_selection, tests auto mode +- `test_pd_basic_rules`: Removed assertions for strikes/balls +- `test_pd_supports_auto_mode`: Renamed from manual_selection, tests auto mode + +**Current state**: 28/28 tests passing + +--- + +## Next Steps (Session 1 Part 2) + +### Change #5: Fix Hit Location Requirements + +**Current Problem**: +`frontend-sba/components/Gameplay/ManualOutcomeEntry.vue:152` has: +```typescript +const outcomesNeedingHitLocation = [ + 'GROUNDOUT', + 'FLYOUT', + 'LINEOUT', + 'SINGLE_1', // ❌ Too broad + 'SINGLE_2', // ❌ Too broad + 'SINGLE_UNCAPPED', // ✅ Correct + 'DOUBLE_2', // ❌ Too broad + 'DOUBLE_3', // ❌ Too broad + 'DOUBLE_UNCAPPED', // ✅ Correct + 'TRIPLE', // ❌ Too broad + 'ERROR', // ✅ Correct +] +``` + +**Correct List** (per Cal): +- GROUNDOUT (all groundouts) +- FLYOUT (all flyouts) +- LINEOUT (all lineouts) +- SINGLE_UNCAPPED +- DOUBLE_UNCAPPED +- ERROR + +**Files to Update**: +1. `frontend-sba/components/Gameplay/ManualOutcomeEntry.vue:152` - Update array +2. Verify `PlayOutcome.requires_hit_location()` matches in backend (if it exists) + +**Expected outcome**: Only outcomes that affect defensive plays require hit location + +--- + +## Remaining Work After Session 1 + +### Session 2: Standard Changes (2-4 hours) + +**Change #6**: Remove defensive alignment +- File: `backend/app/models/game_models.py` - DefensiveDecision model +- File: `frontend-sba/components/Decisions/DefensiveSetup.vue` +- Action: Remove `alignment` field completely + +**Change #7**: Remove offensive approach +- File: `backend/app/models/game_models.py` - OffensiveDecision model +- File: `frontend-sba/components/Decisions/OffensiveApproach.vue` +- Action: Remove `approach` field completely (will be replaced in Change #10) + +**Change #8**: Add infield depth validation +- Valid values: "infield_in", "corners_in", "normal" +- Rule: infield_in and corners_in only legal with R3 +- Files: validators.py, DefensiveSetup.vue + +**Change #9**: Add outfield depth validation +- Valid values: "normal", "shallow" +- Rule: shallow only legal when walk-off possible (home team, bottom 9+, trailing/tied, runner on base) +- Files: validators.py, DefensiveSetup.vue + +### Session 3: Major Refactor (4+ hours) + +**Change #10**: Refactor offensive decision model +- Replace `approach` field with specific actions +- New actions: swing_away, check_jump, hit_and_run, sac_bunt, squeeze_bunt +- Validation rules: + - squeeze_bunt only with R3 and bases not loaded + - check_jump only with runner on base +- Files: OffensiveDecision model, OffensiveApproach.vue, tests +- **Impact**: Will break 213 Phase F3 tests - need to update + +**Change #11**: Check jump validation +- Rule: Only lead runner or both if 1st and 3rd +- No trail runners +- Bundle with Change #10 + +--- + +## Git Status + +**Branch**: `implement-phase-3` +**Last Commit**: `eab61ad` - "Phases 3.5, F1-F5 Complete" +**Working Tree**: Clean (all previous work committed) + +**Commits This Session**: +- Will need to commit cleanup work when done + +--- + +## Test Status Baseline + +**Before cleanup started**: +- Backend: 731/731 passing (100%) +- Frontend: 446/446 passing (100%) + +**After Session 1 Part 1**: +- Backend config tests: 28/28 passing (100%) +- Need to verify full suite still passing + +**Expected after Session 3**: +- Backend tests: Will need updates for offensive decision refactor +- Frontend tests: 213 F3 tests will need updates + +--- + +## Key Context Files + +**Master tracking**: +- `.claude/PROPOSED_CHANGES_2025-01-14.md` - All 11 changes with analysis +- `.claude/TODO_VERIFICATION_RESULTS.md` - What TODOs were already resolved +- `.claude/TODO_SUMMARY.md` - Quick reference + +**Implementation docs**: +- `backend/app/config/CLAUDE.md` - Config system docs +- `backend/app/models/CLAUDE.md` - Game models docs (will need for Changes #6-11) +- `frontend-sba/CLAUDE.md` - Frontend docs + +**Modified files so far**: +- `backend/app/config/base_config.py` +- `backend/tests/unit/config/test_league_configs.py` + +--- + +## Important Notes + +1. **Dev server running**: http://localhost:3005 for testing frontend changes +2. **Always run tests** after each change +3. **Commit frequently** - don't batch all changes into one commit +4. **Session 3 is complex** - offensive decision refactor affects many files +5. **Follow workflow** - Don't skip to Session 3, do Session 1 & 2 first + +--- + +## Questions Already Answered + +Q: Remove or keep supports_manual_result_selection()? +A: Remove completely ✅ + +Q: What identifier for pitching change? +A: League-independent polymorphic Player ID + +Q: Remove alignment and approach? +A: Yes, remove completely + +Q: When to do offensive refactor? +A: Before Phase F6, as last part of cleanup + +Q: Hit location requirements? +A: Only GROUNDOUT, FLYOUT, LINEOUT, SINGLE_UNCAPPED, DOUBLE_UNCAPPED, ERROR + +--- + +**Ready to continue!** Start with Session 1 Part 2: Fix hit location requirements. diff --git a/.claude/PROPOSED_CHANGES_2025-01-14.md b/.claude/PROPOSED_CHANGES_2025-01-14.md new file mode 100644 index 0000000..76266ce --- /dev/null +++ b/.claude/PROPOSED_CHANGES_2025-01-14.md @@ -0,0 +1,280 @@ +# Proposed Changes Analysis - 2025-01-14 + +**Date**: 2025-01-14 +**Source**: Cal's review of demo pages +**Total Changes**: 11 items + +--- + +## Quick Wins (Simple, Low Risk) - 3 items + +### ✅ Change #1: Remove `strikes_for_out` field +**Location**: `backend/app/config/base_config.py:27` +**Change**: Delete line 27 +**Impact**: 1 file +**Effort**: < 5 minutes +**Risk**: None (already marked as TODO to remove) +**Recommendation**: **PROCEED IMMEDIATELY** + +--- + +### ✅ Change #2: Remove `balls_for_walk` field +**Location**: `backend/app/config/base_config.py:28` +**Change**: Delete line 28 +**Impact**: 1 file +**Effort**: < 5 minutes +**Risk**: None (already marked as TODO to remove) +**Recommendation**: **PROCEED IMMEDIATELY** + +--- + +### 🤔 Change #3: Remove/refactor `supports_manual_result_selection()` +**Location**: `backend/app/config/base_config.py:41` +**Current**: Method on BaseGameConfig +**Question**: When would we ever use this function? + +**Current Usage**: +```python +def supports_manual_result_selection(self) -> bool: + # TODO: consider refactor: manually selecting results is default behavior + # with PD allowing auto-results as an option + return True +``` + +**Analysis**: +- SbaConfig: Always manual (no auto mode) +- PdConfig: Supports both manual and auto + +**Options**: +1. **Remove entirely** - If we never check this +2. **Rename to `supports_auto_mode()`** - Invert logic (clearer) +3. **Keep as-is** - If we do use it + +**Question for Cal**: Do we ever check if manual mode is supported? Or should this be `supports_auto_mode()` instead? + +**Recommendation**: **NEEDS DECISION** + +--- + +## Standard Changes (Need Review) - 4 items + +### 🔍 Change #4: Pitching change player identification +**Location**: `backend/app/websocket/handlers.py` (pitching_change event) +**Current**: Uses `player_in_card_id` +**Question**: Is card ID the best way to identify the new player from active roster? + +**Current Implementation**: +```python +@sio.event +async def request_pitching_change(sid, data): + player_in_card_id = data.get("player_in_card_id") # Card ID +``` + +**Analysis**: +- **PD League**: `card_id` makes sense (cards are the entity) +- **SBA League**: Uses `player_id` (different from card_id) +- **Roster**: RosterLink table has both card_id and player_id (polymorphic) + +**Options**: +1. **Keep card_id** - Works for PD, need to translate for SBA +2. **Use roster_link_id** - Unified identifier +3. **League-specific fields** - `card_id` for PD, `player_id` for SBA + +**Question for Cal**: What identifier does the frontend UI have access to when selecting a relief pitcher? + +**Recommendation**: **NEEDS DECISION** (depends on frontend data model) + +--- + +### 🔍 Change #5: Hit location enforcement +**Current**: Enforcing hit location on too many results +**Correct Behavior**: +- Use `PlayOutcome.requires_hit_location()` (already exists) +- Only required if runner on base who doesn't automatically score + +**Files to Check**: +- `frontend-sba/components/Gameplay/ManualOutcomeEntry.vue:152` (outcomesNeedingHitLocation array) +- `backend/app/core/play_resolver.py` (validation logic) + +**Analysis Needed**: +1. What outcomes currently require hit location? +2. What SHOULD require hit location per game rules? +3. Does `PlayOutcome.requires_hit_location()` match game rules? + +**Recommendation**: **NEEDS CODE REVIEW** - Let me check current implementation + +--- + +### 🔍 Change #6: Remove Defensive Alignment +**Current**: DefensiveDecision has `alignment` field +**Proposed**: Remove or mark unused (not active in game) + +**Impact**: +- `backend/app/models/game_models.py` - DefensiveDecision model +- `frontend-sba/components/Decisions/DefensiveSetup.vue` - UI component +- `backend/app/websocket/handlers.py` - Event handler + +**Options**: +1. **Remove completely** - Clean up unused code +2. **Keep but mark unused** - Future feature +3. **Hide from UI** - Keep model field, remove UI + +**Question for Cal**: Is this NEVER going to be used, or is it a future feature? + +**Recommendation**: **NEEDS DECISION** + +--- + +### 🔍 Change #7: Remove Offensive Approach +**Current**: OffensiveDecision has `approach` field +**Proposed**: Remove or mark unused (not active in game) + +**Similar to Change #6** - same questions apply + +**Recommendation**: **NEEDS DECISION** + +--- + +## Complex Changes (Workflow Changes) - 4 items + +### 💬 Change #8: Infield Depths - Simplify to 3 valid options +**Current**: May have more options +**Proposed Valid Values**: +- `"infield_in"` (only legal with runner on 3rd) +- `"corners_in"` (only legal with runner on 3rd) +- `"normal"` (default) + +**Impact**: +- `backend/app/models/game_models.py` - DefensiveDecision validation +- `backend/app/core/validators.py` - Add rule validation +- `frontend-sba/components/Decisions/DefensiveSetup.vue` - UI options +- Tests for validation + +**Effort**: 1-2 hours +**Risk**: Medium (validation logic) + +**Recommendation**: **NEEDS APPROVAL** - Good cleanup, requires validation updates + +--- + +### 💬 Change #9: Outfield Depths - Add legal constraints +**Current**: May allow any value +**Proposed Valid Values**: +- `"normal"` (default, 99.99% of time) +- `"shallow"` (only legal when batting team could walk-off win with runner on base) + +**Impact**: Same as Change #8 + +**Question**: How do we determine "could walk-off win"? +- Batter's team is home team +- Bottom of 9th or later +- Trailing or tied +- Runner on base + +**Effort**: 1-2 hours +**Risk**: Medium (walk-off logic) + +**Recommendation**: **NEEDS APPROVAL** - Adds game-accurate validation + +--- + +### 💬 Change #10: Replace offensive approach with specific actions +**Current**: OffensiveDecision has `approach` field +**Proposed**: Replace with specific action choices: +- `"swing_away"` (default) +- `"check_jump"` (for lead runner only) +- `"hit_and_run"` +- `"sac_bunt"` +- `"squeeze_bunt"` (only legal with R3 and bases not loaded) + +**Impact**: +- `backend/app/models/game_models.py` - Refactor OffensiveDecision +- `frontend-sba/components/Decisions/OffensiveApproach.vue` - Complete UI redesign +- `backend/app/websocket/handlers.py` - Update event handling +- Tests (213 tests in Phase F3 may need updates) + +**Effort**: 3-4 hours +**Risk**: High (affects decision workflow, breaks existing tests) + +**Recommendation**: **NEEDS DISCUSSION** - Major workflow change + +--- + +### 💬 Change #11: Check jump only with lead runner +**Current**: May allow checking jump with any runner +**Proposed Rule**: Players may only check jump with: +- Lead runner only +- OR both runners if 1st and 3rd +- NO checking jump with trail runners + +**Impact**: +- Validation logic for "check_jump" action (from Change #10) +- Frontend UI to disable invalid options + +**Effort**: 30 minutes (if Change #10 implemented) +**Risk**: Low (validation rule) + +**Recommendation**: **BUNDLE WITH CHANGE #10** + +--- + +## Summary by Category + +### Quick Wins (5 min each) +1. ✅ Remove strikes_for_out +2. ✅ Remove balls_for_walk +3. 🤔 Remove/refactor supports_manual_result_selection() - **NEEDS DECISION** + +### Standard Changes (1-2 hours each) +4. 🔍 Pitching change identifier - **NEEDS DECISION** +5. 🔍 Hit location enforcement - **NEEDS CODE REVIEW** +6. 🔍 Remove defensive alignment - **NEEDS DECISION** +7. 🔍 Remove offensive approach - **NEEDS DECISION** (conflicts with #10) + +### Complex Changes (3-4 hours each) +8. 💬 Infield depths validation - **NEEDS APPROVAL** +9. 💬 Outfield depths validation - **NEEDS APPROVAL** +10. 💬 Replace approach with actions - **NEEDS DISCUSSION** (breaks tests) +11. 💬 Check jump lead runner only - **BUNDLE WITH #10** + +--- + +## Recommended Approach + +### Phase 1: Quick Wins (15 min) +- Execute #1, #2 immediately +- Decide on #3 (remove vs refactor vs keep) + +### Phase 2: Decisions Needed (discussion) +- #4: What identifier does frontend use? +- #6, #7: Remove unused fields or keep for future? +- These inform #10 (if we're removing approach anyway, don't refactor it) + +### Phase 3: Code Review (30 min) +- #5: Review hit location logic, fix if needed + +### Phase 4: Validation Updates (2-4 hours) +- #8, #9: Add depth validation rules +- Only if approved + +### Phase 5: Workflow Refactor (4+ hours) +- #10, #11: Offensive action redesign +- Major change, do last after everything else settled +- Will break existing tests, need full re-test + +--- + +## Questions for Cal + +1. **Change #3**: Do we ever check `supports_manual_result_selection()`? Should we rename to `supports_auto_mode()` instead? + +2. **Change #4**: What identifier does the frontend have when selecting a relief pitcher from the bench? + +3. **Changes #6, #7**: Are alignment and approach NEVER going to be used, or future features we should keep? + +4. **Change #10**: This is a major workflow change - are you sure we want to do this now (before Phase F6 integration)? + +--- + +**Document Version**: 1.0 +**Status**: Awaiting decisions on questions above diff --git a/backend/app/config/base_config.py b/backend/app/config/base_config.py index a03cfe3..a303f07 100644 --- a/backend/app/config/base_config.py +++ b/backend/app/config/base_config.py @@ -24,8 +24,6 @@ class BaseGameConfig(BaseModel, ABC): # Basic baseball rules (same across leagues) innings: int = Field(default=9, description="Standard innings per game") outs_per_inning: int = Field(default=3, description="Outs required per half-inning") - strikes_for_out: int = Field(default=3, description="Strikes for strikeout") # TODO: remove - unneeded - balls_for_walk: int = Field(default=4, description="Balls for walk") # TODO: remove - unneeded @abstractmethod def get_result_chart_name(self) -> str: @@ -37,16 +35,6 @@ class BaseGameConfig(BaseModel, ABC): """ pass - @abstractmethod - def supports_manual_result_selection(self) -> bool: # TODO: consider refactor: manually selecting results is default behavior with PD allowing auto-results as an option - """ - Whether players manually select results after dice roll. - - Returns: - True if players pick from chart, False if auto-resolved - """ - pass - @abstractmethod def supports_auto_mode(self) -> bool: """ diff --git a/backend/app/core/ai_opponent.py b/backend/app/core/ai_opponent.py index 97b1089..9a23a4a 100644 --- a/backend/app/core/ai_opponent.py +++ b/backend/app/core/ai_opponent.py @@ -79,7 +79,7 @@ class AIOpponent: # if state.is_runner_on_third() and state.outs < 2: # decision.infield_depth = "in" - logger.info(f"AI defensive decision: {decision.alignment}, IF: {decision.infield_depth}") + logger.info(f"AI defensive decision: IF: {decision.infield_depth}, OF: {decision.outfield_depth}") return decision async def generate_offensive_decision( diff --git a/backend/app/models/game_models.py b/backend/app/models/game_models.py index 05887ef..5fc0163 100644 --- a/backend/app/models/game_models.py +++ b/backend/app/models/game_models.py @@ -151,20 +151,10 @@ class DefensiveDecision(BaseModel): These decisions affect play outcomes (e.g., infield depth affects double play chances). """ - alignment: str = "normal" # normal, shifted_left, shifted_right, extreme_shift infield_depth: str = "normal" # infield_in, normal, corners_in outfield_depth: str = "normal" # in, normal hold_runners: List[int] = Field(default_factory=list) # [1, 3] = hold 1st and 3rd - @field_validator('alignment') - @classmethod - def validate_alignment(cls, v: str) -> str: - """Validate alignment""" - valid = ['normal', 'shifted_left', 'shifted_right', 'extreme_shift'] - if v not in valid: - raise ValueError(f"alignment must be one of {valid}") - return v - @field_validator('infield_depth') @classmethod def validate_infield_depth(cls, v: str) -> str: diff --git a/backend/terminal_client/display.py b/backend/terminal_client/display.py index e38dd0a..87544e2 100644 --- a/backend/terminal_client/display.py +++ b/backend/terminal_client/display.py @@ -188,7 +188,6 @@ def display_decision(decision_type: str, decision: Optional[DefensiveDecision | decision_text = Text() if isinstance(decision, DefensiveDecision): - decision_text.append(f"Alignment: {decision.alignment}\n") decision_text.append(f"Infield Depth: {decision.infield_depth}\n") decision_text.append(f"Outfield Depth: {decision.outfield_depth}\n") if decision.hold_runners: diff --git a/backend/tests/unit/config/test_league_configs.py b/backend/tests/unit/config/test_league_configs.py index 3cd45e6..b366a6b 100644 --- a/backend/tests/unit/config/test_league_configs.py +++ b/backend/tests/unit/config/test_league_configs.py @@ -42,18 +42,16 @@ class TestSbaConfig: config = SbaConfig() assert config.innings == 9 assert config.outs_per_inning == 3 - assert config.strikes_for_out == 3 - assert config.balls_for_walk == 4 def test_sba_result_chart_name(self): """SBA uses sba_standard_v1 chart.""" config = SbaConfig() assert config.get_result_chart_name() == "sba_standard_v1" - def test_sba_supports_manual_selection(self): - """SBA supports manual result selection.""" + def test_sba_supports_auto_mode(self): + """SBA does not support auto mode (cards not digitized).""" config = SbaConfig() - assert config.supports_manual_result_selection() is True + assert config.supports_auto_mode() is False def test_sba_api_url(self): """SBA API URL is correct.""" @@ -86,18 +84,16 @@ class TestPdConfig: config = PdConfig() assert config.innings == 9 assert config.outs_per_inning == 3 - assert config.strikes_for_out == 3 - assert config.balls_for_walk == 4 def test_pd_result_chart_name(self): """PD uses pd_standard_v1 chart.""" config = PdConfig() assert config.get_result_chart_name() == "pd_standard_v1" - def test_pd_supports_manual_selection(self): - """PD supports manual result selection (though auto is also available).""" + def test_pd_supports_auto_mode(self): + """PD supports auto mode (digitized card data).""" config = PdConfig() - assert config.supports_manual_result_selection() is True + assert config.supports_auto_mode() is True def test_pd_api_url(self): """PD API URL is correct.""" diff --git a/backend/tests/unit/core/test_validators.py b/backend/tests/unit/core/test_validators.py index c5e77ed..7627ead 100644 --- a/backend/tests/unit/core/test_validators.py +++ b/backend/tests/unit/core/test_validators.py @@ -177,19 +177,6 @@ class TestDefensiveDecisionValidation: # Should not raise validator.validate_defensive_decision(decision, state) - def test_validate_defensive_decision_invalid_alignment(self): - """Test invalid alignment fails at Pydantic validation""" - from pydantic_core import ValidationError as PydanticValidationError - - # Pydantic catches invalid alignment at model creation - with pytest.raises(PydanticValidationError) as exc_info: - decision = DefensiveDecision( - alignment="invalid_alignment", - infield_depth="normal" - ) - - assert "alignment" in str(exc_info.value).lower() - def test_validate_defensive_decision_invalid_depth(self): """Test invalid infield depth fails at Pydantic validation""" from pydantic_core import ValidationError as PydanticValidationError diff --git a/backend/tests/unit/models/test_game_models.py b/backend/tests/unit/models/test_game_models.py index e17549f..31d478b 100644 --- a/backend/tests/unit/models/test_game_models.py +++ b/backend/tests/unit/models/test_game_models.py @@ -227,23 +227,10 @@ class TestDefensiveDecision: def test_create_defensive_decision_defaults(self): """Test creating defensive decision with defaults""" decision = DefensiveDecision() - assert decision.alignment == "normal" assert decision.infield_depth == "normal" assert decision.outfield_depth == "normal" assert decision.hold_runners == [] - def test_defensive_decision_valid_alignments(self): - """Test all valid alignments""" - valid = ['normal', 'shifted_left', 'shifted_right', 'extreme_shift'] - for alignment in valid: - decision = DefensiveDecision(alignment=alignment) - assert decision.alignment == alignment - - def test_defensive_decision_invalid_alignment(self): - """Test that invalid alignment raises error""" - with pytest.raises(ValidationError): - DefensiveDecision(alignment="invalid") - def test_defensive_decision_valid_infield_depths(self): """Test all valid infield depths""" valid = ['infield_in', 'normal', 'corners_in'] diff --git a/frontend-sba/components/Decisions/DefensiveSetup.vue b/frontend-sba/components/Decisions/DefensiveSetup.vue index 2a1a60d..984a581 100644 --- a/frontend-sba/components/Decisions/DefensiveSetup.vue +++ b/frontend-sba/components/Decisions/DefensiveSetup.vue @@ -16,20 +16,6 @@