Compare commits

...
Author SHA1 Message Date
adminandClaude Opus 4.5 cc92d4e7e8 Rewrite DEPROTO_PLAN.md to focus on remaining work
Removed historical sections about completed work. The doc now focuses on:
- The 75 remaining transitive proto deps through shardok_interface/
- Two files to migrate: EagleUnit.scala (5 proto deps) and
  ResolvedShardokPlayer.scala (1 proto dep)
- Concrete migration plan with phases
- Commands for checking progress

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 06:45:22 -08:00
+133 -272
View File
@@ -33,325 +33,186 @@
---
## Current State (January 2026)
## Current State
### Summary
**library/** has **0 direct proto imports** and **0 proto_converters dependencies** (enforced by linter).
| Area | Proto Imports | Status |
|------|---------------|--------|
| AI (`/ai/`) | **0** | ✅ **Complete** |
| Actions (`/library/actions/impl/action/`) | **0** | ✅ **Complete** |
| Commands (`/library/actions/impl/command/`) | **0** | ✅ **Complete** |
| Availability (`/library/actions/availability/`) | **0** | ✅ **Complete** (Hostility migrated) |
| Command Choice Helpers (`/library/util/command_choice_helpers/`) | **0** | ✅ **Complete** (Hostility migrated) |
| View Filters (`/library/util/view_filters/`) | **0** | ✅ **Complete** (BattleFilter already protoless) |
| Other Utilities (`/library/util/`) | **0** | ✅ **Complete** (Hostility migrated) |
| NewGameCreation (`/service/new_game_creation/`) | **0** | ✅ **Complete** (except input GameParameters proto) |
| LLM Prompt Generators (`/library/actions/llm_prompt_generators/`) | **0** | ✅ **Complete** (all 4 files migrated) |
| Root Library (`/library/`) | **0** | ✅ **Complete** |
However, library/ still has **75 transitive proto dependencies** through `shardok_interface/` types.
**Total: 0 files with proto imports in library/** - Shardok boundary methods moved to `FullGameHistory` in service/
### Enforcement
**Note:** The `bazel query` for proto dependencies returns many targets because it includes transitive dependencies. All non-boundary library code is now protoless.
### Completed Phases
| Phase | Status | Summary |
|-------|--------|---------|
| Phase 1: GameStateC | **Complete** | Scala `GameState` model with 22 fields |
| Phase 2: EngineImpl | **Complete** | Holds Scala `GameState` internally |
| Phase 3: GameHistory | **Complete** | `stateAfter` returns Scala GameState |
| Phase 4: ActionResultT | **Complete** | All 59 actions return `ActionResultT` |
| Phase 5: Action Base Classes | **Complete** | All actions converted to T-type base classes |
| Phase 5b-d: RoundPhaseAdvancer | **Complete** | Uses Scala GameState throughout |
| Phase 6: Core Engine | **Complete** | ActionResultApplier, RandomStateSequencer protoless |
| Phase 6b: CommandFactory | **Complete** | Uses Scala SelectedCommand and AvailableCommand |
| Phase 6c: AI Clients | **Complete** | AIClient, command choosers all protoless |
| Phase 6d: CommandSelection | **Complete** | Renamed ScalaCommandSelection → CommandSelection |
### Recent Completions (PRs #5326-#5390)
1. **CommandFactory protoless** - Now accepts/returns Scala `SelectedCommand` types
2. **AI clients protoless** - `AIClient`, `MidGameAIClient`, all command choosers use Scala types
3. **CommandSelection cleanup** - Deleted legacy proto-based `CommandSelection`, renamed `ScalaCommandSelection``CommandSelection`
4. **AvailableCommandsFactory protoless** - Returns Scala `OneProvinceAvailableCommands`
5. **All CommandChoiceHelpers protoless** - All selector/chooser files converted
6. **ChronicleEventGenerator protoless** (PR #5332) - Removed last proto enum import from Actions layer
7. **ProvinceUtils protoless** (PR #5333) - Converted to use Scala `BattalionType`
8. **FactionViewFilter protoless** (PR #5334) - Returns Scala `FactionView`, converts to proto at edge
9. **HeroViewFilter protoless** (PR #5336) - Returns Scala `HeroView`, converts to proto at edge
10. **ProvinceViewFilter protoless** (PR #5341) - `ProvinceView.knownEvents` uses Scala `ProvinceEvent`, converts at edge
11. **StatWithConditionUtils & ProvinceEventUtils protoless** (PR #5342) - Deleted unused proto overloads
12. **IncomingArmyUtils protoless** (PR #5348) - Deleted unused `stats()` method
13. **RansomOfferHelpers deleted** (PR #5351) - Deleted unused dead code file
14. **MapGenerator protoless** (PR #5373) - Returns Scala types instead of proto
15. **Remove proto ActionResult from Engine layer** (PR #5378) - Engine uses Scala `ActionResultT`
16. **NewGameCreation protoless** (PR #5381) - Uses Scala types throughout, no proto conversion
17. **PersistedHistory Scala API** (PR #5390) - New `apply` overload accepts `Vector[ActionResultT]` directly
18. **Remove proto types from LLM prompt generators** (PR #5444) - All 4 prompt generator files use Scala types
19. **Engine API protoless** - `ActionResultFilter`, `Engine`, `EngineImpl` return Scala `ActionResultView`
- `scripts/check_build_deps.sh` enforces that library/ cannot depend on `proto_converters`
- Run with `--ci` or `--strict` to fail on violations
---
## Remaining Work
## Remaining Work: shardok_interface Proto Dependencies
### Phase 7: View Filters ✅ Complete
### Source of Transitive Proto Dependencies
| File | Status | Migration Path |
|------|--------|----------------|
| `FactionViewFilter.scala` | ✅ **Complete** | Returns Scala `FactionView` (PR #5334) |
| `HeroViewFilter.scala` | ✅ **Complete** | Returns Scala `HeroView` (PR #5336) |
| `ProvinceViewFilter.scala` | ✅ **Complete** | Returns Scala `ProvinceView` with Scala events (PR #5341) |
| `GameStateViewFilter.scala` | ✅ **Complete** | Returns Scala `GameStateView`, converted at boundary |
| `GameStateViewDiffer.scala` | ✅ **Complete** | Uses Scala diff types internally, converted at boundary |
| `BattleFilter.scala` | Keep | Uses `ShardokBattleView` (boundary code for battles) |
library/ depends on 4 `shardok_interface/` targets that pull in proto dependencies:
**Pattern established**: Create Scala view type → Create converter → Update filter to return Scala type → Convert at edge.
| Target | Proto Dependencies | Used By |
|--------|-------------------|---------|
| `eagle_unit` | 5 proto deps | `resolved_eagle_unit` |
| `resolved_eagle_unit` | via `eagle_unit` | `battle_resolution`, library/actions |
| `resolved_shardok_player` | `victory_condition_scala_proto` | `battle_resolution`, library/actions |
| `battle_resolution` | via above | `resolve_battle_action` |
### Phase 8: LLM Prompt Generators ✅ Complete
### Files to Migrate
**Status**: All 4 files migrated to use Scala types
#### 1. EagleUnit.scala (5 proto deps)
| File | Migration | Notes |
|------|-----------|-------|
| `BattalionDescriptions.scala` | ✅ **Complete** | Removed proto `BattalionTypeId`, `Battalion`, `BattalionView`; now uses Scala `BattalionTypeId`, `BattalionT`, `BattalionView` |
| `ChronicleEventTextGenerator.scala` | ✅ **Complete** | Removed proto `EventForChronicle.*`; now uses Scala `ChronicleEvent` sealed trait |
| `GeneratorUtilities.scala` | ✅ **Complete** | Removed proto `Date`; now uses Scala `Date` |
| `QuestEndedGeneratorUtilities.scala` | ✅ **Complete** | Removed proto `BattalionTypeId`, `UnaffiliatedHeroQuest.*`; now uses Scala `QuestT`/`QuestC` types |
**Current imports:**
```scala
import net.eagle0.eagle.common.battalion_type.BattalionTypeId.LIGHT_INFANTRY
import net.eagle0.eagle.common.combat_unit.CombatUnit
import net.eagle0.eagle.internal.battalion.Battalion as EagleBattalion
import net.eagle0.eagle.internal.game_state.GameState
import net.eagle0.eagle.internal.hero.Hero as EagleHero
```
Also updated:
- `ChronicleUpdatePromptGenerator.scala` - Removed `ChronicleEventConverter` usage
- `HeroBackstoryUpdatePromptGenerator.scala` - Removed `BattalionViewConverter` and `QuestConverter` usage
- `QuestFailedPromptGenerator.scala` - Removed `QuestConverter` usage
- `QuestFulfilledPromptGenerator.scala` - Removed `QuestConverter` usage
**Migration:**
- Replace proto `Hero` with Scala `HeroT`
- Replace proto `Battalion` with Scala `BattalionT`
- Replace proto `GameState` with Scala `GameState`
- Replace proto `CombatUnit` with Scala equivalent (or delete if unused)
- Replace proto `BattalionTypeId` with Scala `BattalionTypeId`
### Phase 8b: Hostility Proto Migration ✅ Complete
**Complexity:** Medium - need to update `ExpandUnit` method and all callers
**Status**: All 4 files migrated to use Scala `Hostility` enum
#### 2. ResolvedShardokPlayer.scala (1 proto dep)
| File | Migration |
|------|-----------|
| `AvailableAttackDecisionCommandFactory.scala` | ✅ Uses Scala `Hostility` |
| `AvailableFreeForAllDecisionCommandFactory.scala` | ✅ Uses Scala `Hostility` |
| `AttackDecisionCommandChooser.scala` | ✅ Uses Scala `Hostility` |
| `FactionUtils.scala` | ✅ Uses Scala `Hostility` |
**Current imports:**
```scala
import net.eagle0.common.victory_condition.EndGameCondition
```
**Migration approach**: Created Scala `Hostility` sealed trait with `Allied`, `Neutral`, `Hostile`, `Self` cases.
**Migration:**
- Create Scala `EndGameCondition` sealed trait with cases for Victory, AllyVictory, Draw, Loss
- Update `ShardokInterfaceGrpcClient` to convert proto EndGameCondition to Scala at boundary
### Phase 8c: Engine API Protoless ✅ Complete
**Complexity:** Low-Medium - need to create Scala type and update conversion
**Status**: Engine API now returns Scala types; proto conversion moved to gRPC boundary
### Proto Dependencies (75 total)
| File | Migration |
|------|-----------|
| `ActionResultView.scala` | ✅ NEW - Scala case class for action result views |
| `ActionResultViewConverter.scala` | ✅ NEW - Converts Scala → Proto at boundary |
| `ActionResultFilter.scala` | ✅ Returns Scala `ActionResultView` |
| `Engine.scala` | ✅ Returns Scala `ActionResultView` |
| `EngineImpl.scala` | ✅ Returns Scala `ActionResultView` |
| `HumanPlayerClientConnectionState.scala` | ✅ Converts to proto at gRPC boundary |
These are the transitive proto dependencies that will be eliminated:
**Migration approach**: Created Scala `ActionResultView` case class, updated filter to return Scala type, moved proto conversion to `HumanPlayerClientConnectionState` which is the gRPC boundary.
**From victory_condition (2):**
- `//src/main/protobuf/net/eagle0/common:victory_condition_proto`
- `//src/main/protobuf/net/eagle0/common:victory_condition_scala_proto`
### Phase 9: Remaining Utilities ✅ Complete
**From eagle/common (25):**
- `battalion_type_proto`, `battalion_type_scala_proto`
- `combat_unit_proto`, `combat_unit_scala_proto`
- `province_event_proto`, `province_event_scala_proto`
- `action_result_notification_details_proto`
- `beast_info_proto`
- `chronicle_entry_proto`
- `command_type_proto`
- `date_proto`
- `diplomacy_offer_proto`, `diplomacy_offer_status_proto`
- `gender_proto`
- `hero_backstory_version_proto`
- `improvement_type_proto`
- `profession_proto`
- `province_order_type_proto`
- `recruitment_info_proto`
- `round_phase_proto`
- `tribute_amount_proto`
- `unaffiliated_hero_quest_proto`
- `unaffiliated_hero_type_proto`
| File | Proto Usage | Migration Path |
|------|-------------|----------------|
| `ProvinceEventUtils.scala` | ✅ **0** | Deleted unused proto overloads (PR #5342) |
| `StatWithConditionUtils.scala` | ✅ **0** | Deleted unused proto overloads (PR #5342) |
| `MapGenerator.scala` | ✅ **0** | Returns Scala types (PR #5373) |
| `IncomingArmyUtils.scala` | ✅ **0** | Deleted unused `stats()` method (PR #5348) |
| `GameStateViewDiffer.scala` | ✅ **0** | Uses Scala diff types, converts at boundary |
**From eagle/internal (19):**
- `army_proto`, `army_scala_proto`
- `battalion_proto`, `battalion_scala_proto`
- `battle_revelation_proto`
- `deferred_change_proto`
- `event_for_hero_backstory_proto`
- `faction_proto`, `faction_relationship_proto`
- `game_state_proto`, `game_state_scala_proto`
- `hero_proto`, `hero_scala_proto`
- `province_proto`
- `run_status_proto`
- `shardok_battle_proto`
- `supplies_proto`
- `unaffiliated_hero_proto`, `unaffiliated_hero_scala_proto`
### Phase 10: History APIs ✅ Complete
**From eagle/views (5):**
- `army_view_proto`
- `battalion_view_proto`
- `incoming_army_view_proto`
- `province_view_proto`
- `stat_with_condition_proto`
**Completed:**
- `ActionWithResultingState` now has `scalaActionResult` property (lazy converted from proto)
- `ActionResultFilter.includeForPlayer` uses Scala types (`ActionResultT`, `NotificationT`, `ActionResultType`)
- `GameHistory.withNewResultsScala` preserves both Scala `GameState` and `ActionResultT` to avoid re-conversion
- `PersistedHistory.apply` now accepts `Vector[ActionResultT]` directly (PR #5390)
- `NewGameCreation` is fully protoless (PR #5381)
**Note:** History internals still use proto for persistence (expected - proto is good for disk serialization).
**From shardok/* (24):**
- Various shardok protos (coords, hex_map, terrain, weather, etc.)
- These come transitively through EagleUnit's proto deps
---
## Architecture Summary
## Migration Plan
### Fully Protoless Areas ✅
### Phase 1: EndGameCondition (Low effort)
- **AI layer** (`/ai/`) - All AI clients and command choosers
- **Actions** (`/library/actions/impl/action/`) - All 52 action files
- **Commands** (`/library/actions/impl/command/`) - CommandFactory and all commands
- **Availability** (`/library/actions/availability/`) - AvailableCommandsFactory and all factories
- **Command helpers** (`/library/util/command_choice_helpers/`) - All selectors and choosers
- **Core types** - `CommandSelection`, `SelectedCommand`, `AvailableCommand`, `OneProvinceAvailableCommands`
- **View types** - `FactionView`, `HeroView`, `ProvinceView`, `ActionResultView` (return Scala, convert at edge)
- **Engine API** - `Engine`, `EngineImpl`, `ActionResultFilter` return Scala types
1. Create `model/state/EndGameCondition.scala` sealed trait:
```scala
sealed trait EndGameCondition {
def isVictory: Boolean
def isAllyVictory: Boolean
def isDraw: Boolean
}
case class Victory(condition: VictoryCondition) extends EndGameCondition
case class AllyVictory(condition: VictoryCondition) extends EndGameCondition
case class Draw(drawType: DrawType) extends EndGameCondition
case class Loss(condition: VictoryCondition) extends EndGameCondition
```
### Proto at Boundaries (Expected) ✅
2. Create `EndGameConditionConverter` in proto_converters
- `EagleServiceImpl.scala` - gRPC boundary
- `GameController.scala` - Client communication
- `HumanPlayerClientConnectionState.scala` - Converts Scala `ActionResultView` to proto for gRPC
- `GameStateViewFilter.scala` - Converts Scala views to proto for client
- `*Converter.scala` - Explicit conversion utilities
- `PersistedHistory.scala` - Disk persistence
3. Update `ResolvedShardokPlayer` to use Scala `EndGameCondition`
### Remaining Proto Usage
4. Update `ShardokInterfaceGrpcClient` to convert at boundary
- **None in library/** - Shardok boundary methods moved to `FullGameHistory` in service/
**Result:** Removes `victory_condition_scala_proto` from library/ transitive deps
---
### Phase 2: EagleUnit (Medium effort)
## Estimated Remaining Effort
1. Update `EagleUnit` to use Scala types:
- `HeroT` instead of proto `Hero`
- `BattalionT` instead of proto `Battalion`
- Scala `GameState` instead of proto `GameState`
- Scala `BattalionTypeId` instead of proto
| Component | Files | Status |
|-----------|-------|--------|
| View Filters | 0 | ✅ Complete |
| Utility files | 0 | ✅ Complete |
| History APIs | 0 | ✅ Complete |
| LLM Prompt Generators | 0 | ✅ Complete |
| LLM Request Generators | 0 | ✅ Complete |
| Hostility Migration | 0 | ✅ Complete |
| Engine API | 0 | ✅ Complete (Engine returns Scala types) |
| View Filters | 0 | ✅ Complete (BattleFilter already protoless) |
| Root Library | 0 | ✅ Complete (Shardok methods moved to FullGameHistory in service/) |
| **Total** | **0** | ✅ **All library code protoless!** |
2. Update `ExpandUnit` method signature and implementation
3. Update all callers in `ShardokInterfaceGrpcClient`
**Result:** Removes all 75 transitive proto deps from library/
---
## Success Criteria
### Code Quality
- [x] Zero proto imports in `/ai/`
- [x] Zero proto imports in `/library/actions/impl/command/`
- [x] Zero proto imports in `/library/actions/availability/` (Hostility migrated)
- [x] Zero proto imports in `/library/util/command_choice_helpers/` (Hostility migrated)
- [x] Zero proto imports in `/library/actions/impl/action/`
- [x] Zero proto imports in `/library/util/view_filters/` (BattleFilter already protoless)
- [x] Zero proto imports in `/library/util/` (FactionUtils uses Scala Hostility)
- [x] Zero proto imports in `/service/new_game_creation/` (except input GameParameters)
- [x] Zero proto imports in `/library/actions/llm_prompt_generators/`
- [x] Zero proto imports in `/library/actions/llm_request_generators/`
### Architecture
- [ ] Zero transitive proto dependencies in library/
- [x] Zero direct proto imports in library/
- [x] Zero proto_converters dependencies in library/ (enforced by linter)
- [x] Clear separation: Scala models (internal) vs Proto (boundaries)
- [x] Converters as the only bridge between domains
- [x] CommandFactory accepts/returns Scala types
- [x] AI layer fully protoless
- [x] FactionViewFilter returns Scala types
- [x] HeroViewFilter returns Scala types
- [x] ProvinceViewFilter returns Scala types
- [x] All view filters return Scala types
- [x] NewGameCreation uses Scala types
- [x] PersistedHistory accepts Scala types for new games
- [x] LLM layer uses Scala types
- [x] Hostility enum migrated to Scala
- [x] Engine API returns Scala types (ActionResultView)
- [x] No "proto creep" into business logic
- [x] **Linter enforces library/ cannot depend on proto_converters** (`scripts/check_build_deps.sh`)
---
## Open Questions
1. **LLM Prompt Generators**: Should these accept Scala types directly, or is it acceptable to have proto usage here since they're generating text (not core game logic)?
2. ~~**GameStateViewDiffer**: This works with view protos for client updates. Should it remain proto-based since it's generating client-facing data?~~ **Resolved**: GameStateViewDiffer now uses Scala types internally (`GameStateViewDiff`, `ProvinceViewDiff`, etc.) and converts to proto at the boundary in `ActionResultFilter`.
3. **History Serialization**: Keep proto for persistence (good for schema evolution) or consider alternatives?
---
## Proto Import Inventory (Detailed)
**Current: 0 files with direct proto imports in library/** (as of 2026-01-19)
The `bazel query` command reports many proto dependencies, but these are transitive dependencies through Scala model types. No library code directly imports proto types.
### Files with Proto Imports
## Commands
```bash
# Verified with:
grep -rl "import net.eagle0.eagle\.\(internal\|common\|api\|views\)\.\|import net.eagle0.shardok\.\|import net.eagle0.common.hostility" \
src/main/scala/net/eagle0/eagle/library/ --include="*.scala"
# Check current proto dependency count
./scripts/check_build_deps.sh --count
# Verify no proto_converters in library/ (enforced)
./scripts/check_build_deps.sh --strict
# List all proto deps
bazel query 'deps(//src/main/scala/net/eagle0/eagle/library/...) intersect //src/main/protobuf/...' 2>/dev/null | wc -l
# Find path from library/ to a specific proto
bazel query 'somepath(//src/main/scala/net/eagle0/eagle/library/..., //src/main/protobuf/net/eagle0/common:victory_condition_scala_proto)'
```
### Boundary Code (Keep Proto) - 0 files in library/
| File | Proto Imports | Notes |
|------|---------------|-------|
| ~~`GameHistory.scala`~~ | ~~Shardok protos~~ | ✅ Moved to `FullGameHistory` in service/ (PR #5459) |
### Already Protoless (was mislabeled in plan)
| File | Status |
|------|--------|
| `BattleFilter.scala` | ✅ Already uses Scala `ShardokBattleView` and `Hostility` enum |
### Library Files Made Protoless (Phase 8c)
| File | Previous Proto Usage | Now |
|------|---------------------|-----|
| `Engine.scala` | `ActionResultView` proto | ✅ Returns Scala `ActionResultView` |
| `EngineImpl.scala` | `ActionResultView` proto | ✅ Returns Scala `ActionResultView` |
| `ActionResultFilter.scala` | `ActionResultTypeProto`, `ActionResultView`, `GameStateViewDiffProto` | ✅ Returns Scala types, no proto imports |
### Hostility Proto - 4 files (Phase 8b) ✅ Complete
All files now use Scala `Hostility` enum (`net.eagle0.eagle.model.state.Hostility`).
| File | Status |
|------|--------|
| `AvailableAttackDecisionCommandFactory.scala` | ✅ Uses `FactionUtils` (no direct Hostility import) |
| `AvailableFreeForAllDecisionCommandFactory.scala` | ✅ Uses `FactionUtils` (no direct Hostility import) |
| `AttackDecisionCommandChooser.scala` | ✅ Uses Scala `Hostility` |
| `FactionUtils.scala` | ✅ Uses Scala `Hostility` |
### LLM Prompt Generators - 4 files (Phase 8) ✅ Complete
All files now use Scala types instead of proto.
| File | Migration |
|------|-----------|
| `BattalionDescriptions.scala` | ✅ Uses Scala `BattalionTypeId`, `BattalionT`, `BattalionView` |
| `ChronicleEventTextGenerator.scala` | ✅ Uses Scala `ChronicleEvent` sealed trait |
| `GeneratorUtilities.scala` | ✅ Uses Scala `Date` |
| `QuestEndedGeneratorUtilities.scala` | ✅ Uses Scala `QuestT`/`QuestC` types |
### Summary by Directory
| Directory | Files with Proto | Status |
|-----------|------------------|--------|
| `ai/` | 0 | ✅ Clean |
| `actions/availability/` | 0 | ✅ Clean (Hostility migrated) |
| `actions/impl/action/` | 0 | ✅ Clean |
| `actions/impl/command/` | 0 | ✅ Clean |
| `actions/llm_prompt_generators/` | 0 | ✅ Clean (all migrated) |
| `util/command_choice_helpers/` | 0 | ✅ Clean (Hostility migrated) |
| `util/view_filters/` | 0 | ✅ Clean (BattleFilter already protoless) |
| `util/faction_utils/` | 0 | ✅ Clean (Hostility migrated) |
| Root (`library/`) | 0 | ✅ Complete (Shardok methods moved to service/) |
| **Total** | **0** | ✅ **All library code protoless!** |
---
## Migration Pattern
The codebase follows a **Legacy* pattern** for separating proto-dependent and protoless code:
- **Protoless utilities**: `FactionUtils`, `HeroUtils`, `ProvinceUtils`, `ProvinceDistances`, etc.
- **Proto-dependent utilities**: `LegacyFactionUtils`, `LegacyHeroUtils`, `LegacyProvinceUtils`, etc.
When migrating a file:
1. Create a `Legacy*` version containing the proto-dependent methods
2. Keep the original file name for protoless methods
3. Update callers to use the appropriate version based on their context
4. Delete `Legacy*` files when no longer needed
### Deleted Legacy Files (no production callers)
- `LegacyProvinceDistances`
- `LegacyBattalionSuitability`
- `LegacyFoodConsumptionUtils`
- `LegacyHandleRiotUtils`