282 Commits
Author SHA1 Message Date
41732c8f98 Migrate deferred test files to Scala types, delete unused utilities (#5517)
* Migrate deferred test files to Scala types, delete unused utilities

- Migrate BattalionTypesTestData.scala to use Scala BattalionType
- Migrate NewRoundActionTest.scala to Scala types (FactionRelationship, MovingArmy, etc.)
- Migrate EndBattleAftermathPhaseActionTest.scala with complete rewrite
- Migrate ResolveBattleActionTest.scala (remove IDable dependency)
- Delete unused impl/package.scala and availability/package.scala
- Remove 48 dangling action_impl_pkg references from BUILD files
- Update DEPROTO_PLAN.md (24 migrated, 1 deferred, 3 deleted)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Delete unused IDable.scala and remove all :idable BUILD references

IDable.scala was listed as a BUILD dependency in 22 targets, but no code
actually imported or used it. The BUILD dependencies were stale.

This completes the library test deproto migration with zero deferred files.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 14:40:29 -08:00
c83ea25658 Fix DEPROTO_PLAN.md: Production library code IS protoless (#5513)
Corrects error from #5512 - the LLM prompt generators are actually
fully protoless. My grep was matching Scala utility imports like
`net.eagle0.common.JsonUtils`, not proto imports.

Verified with: grep -r "import net.eagle0.eagle.(internal|common|api|views)."
which returns zero matches in src/main/scala/net/eagle0/eagle/library/

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 12:20:16 -08:00
4d2e91a54d Update DEPROTO_PLAN.md: Hostility complete, LLM generators remain (#5512)
- Mark Hostility proto migration as complete (all 4 files now use Scala enum)
- Correct status: LLM prompt generators still have 4 files with proto imports
- List specific LLM files and their proto dependencies
- Move LLM generators from "Fully Protoless" to "Remaining Proto Usage"

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 12:09:51 -08:00
916c01978b Update DEPROTO_PLAN.md to reflect additional test migrations (#5511)
- EngineImplTest.scala migrated (#5488)
- GameStateViewFilterTest.scala migrated (#5510)
- Updated counts: 20 migrated, 6 deferred (was 18/8)
- Removed "API boundary tests" category - these test Scala APIs, not proto

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 11:00:46 -08:00
0f4d2ce2aa Update DEPROTO_PLAN.md with completed library test migration status (#5507)
Documents the completion of Phase 10 library test migration:
- 18 test files successfully migrated to Scala types
- 8 files deferred with documented rationale (shared utilities, API
  boundary tests, complex proto integration)
- 1 orphaned test file deleted
- Added migration patterns and key conversions reference

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 08:53:37 -08:00
6daa44cb13 Document test utilities with cascading proto dependencies (#5487)
BattalionTypesTestData, impl/package.scala, and IDable.scala are shared
test utilities that use proto types and have cascading dependencies.
These need to be migrated together with their dependent tests.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 06:25:17 -08:00
ad4b9e88f6 Update asset audit: clean up deleted items, document music licenses (#5481)
- Remove deleted stock images section (already removed files)
- Document Market Day (RandomMind) and Shopping List (Komiku) as free-to-use
- Flag Medieval: Victory Theme as needing verification
- Flag Dima Koltsov Audius tracks for eventual replacement (Open Music License, not CC)
- Update action items and recommendations

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 22:27:52 -08:00
bf7f736a4a Update DEPROTO_PLAN.md for library test migration (#5480)
- Mark production library/ code as complete
- Remove detailed history of completed phases
- Add Phase 10: Library Test Migration with 27 files to migrate
- Categorize files by proto import count (light/medium/heavy)
- Include key type conversions and recommended migration order

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 22:12:33 -08:00
658bdac97f Add alpha tester support, legal, and onboarding items to TODO (#5476)
- Alpha Tester Support: feedback channel, crash reporting, known issues
- Legal: privacy policy, ToS, data deletion
- Onboarding: narrative hook, first-session goals, early victories

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 17:08:51 -08:00
b7afd6d6ad Add Small Eagle TODO checklist for closed alpha (#5474)
Tracks remaining work for 10-50 user private alpha release:
- Gameplay productionization (Mac installer, client updates, etc.)
- User management (link accounts, MOTD)
- IP/Legal (licenses, asset audit)
- Basic gameplay (tutorials, goals/ending)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 17:02:12 -08:00
6e7611d547 Move ShardokBattle boundary conversion to ShardokInterfaceGrpcClient (#5471)
* Remove stale proto_converter deps from library/ BUILD files

Library code should not depend on proto_converters - those belong at
the service layer boundary. Removed 9 stale proto_converter deps that
were no longer used by any Scala code.

The only remaining proto_converter dep is shardok_battle_converter,
which is actually used by ResolveBattleAction.scala (to be fixed in
a follow-up PR).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Move ShardokBattle boundary conversion to ShardokInterfaceGrpcClient

Previously, BattleResolution contained a proto ShardokBattle, requiring
ResolveBattleAction in library/ to depend on ShardokBattleConverter.
This violated the boundary principle where proto conversions should
happen at the service layer, not in library code.

This change:
- Updates BattleResolution.battle to use Scala ShardokBattle
- Moves the proto-to-Scala conversion into ShardokInterfaceGrpcClient
- Removes ShardokBattleConverter dependency from library/ code
- Updates tests to use the scalaBattle() helper for conversion

This completely removes proto_converters dependencies from library/
(the last one was shardok_battle_converter).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add linter check for library/ proto_converters boundary

Adds Rule 3 to check_build_deps.sh that verifies library/ code does not
depend on proto_converters. Proto conversions should happen at service
boundaries (ShardokInterfaceGrpcClient, EagleServiceImpl, etc.), not in
library code.

Also updates baseline from 167 to 75 proto deps reflecting recent cleanup.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 06:27:57 -08:00
8975514e7c Add Sparkle delta updates implementation plan (#5469)
Documents the design for implementing delta patches in the Mac auto-update
system to reduce download sizes from ~200MB to ~10-30MB per update.

Covers:
- Appcast XML structure with sparkle:deltas
- S3 storage layout for app bundles and deltas
- BinaryDelta tool usage
- Migration strategy and error handling
- Storage impact analysis (~3.25GB total for 85% bandwidth savings)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 22:35:53 -08:00
af854ea2cc Remove stale proto deps from library/ BUILD.bazel files (#5461)
With all proto imports removed from library/ Scala code, these proto
dependencies in BUILD.bazel files are no longer needed. This completes
the deproto milestone for the library/ layer.

- Update DEPROTO_PLAN.md to reflect 0 proto files in library/
- Remove unused proto deps from library/BUILD.bazel, util/, actions/impl/

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 16:30:37 -08:00
9d07984b2e Make Engine API protoless by introducing Scala ActionResultView (#5447)
Created Scala ActionResultView type to replace proto ActionResultView
in the Engine's public API. Proto conversion now happens at the gRPC
boundary in HumanPlayerClientConnectionState.

Changes:
- Created model/view/action_result/ActionResultView.scala
- Created proto_converters/view/action_result/ActionResultViewConverter.scala
- Updated ActionResultFilter to return Scala ActionResultView
- Updated Engine and EngineImpl to use Scala type
- Updated HumanPlayerClientConnectionState to convert to proto at gRPC boundary
- Removed proto dependencies from library/

This follows the architecture principle that the Engine should vend a
pure Scala model API, with proto conversions happening at the outer
boundary layer (GameController/GamesManager).

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 13:48:13 -08:00
56312414ec Make Engine API protoless by introducing Scala ActionResultView (#5446)
Created Scala ActionResultView type to replace proto ActionResultView
in the Engine's public API. Proto conversion now happens at the gRPC
boundary in HumanPlayerClientConnectionState.

Changes:
- Created model/view/action_result/ActionResultView.scala
- Created proto_converters/view/action_result/ActionResultViewConverter.scala
- Updated ActionResultFilter to return Scala ActionResultView
- Updated Engine and EngineImpl to use Scala type
- Updated HumanPlayerClientConnectionState to convert to proto at gRPC boundary
- Removed proto dependencies from library/

This follows the architecture principle that the Engine should vend a
pure Scala model API, with proto conversions happening at the outer
boundary layer (GameController/GamesManager).

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 13:04:40 -08:00
9b2315aaff Remove proto types from LLM prompt generators (#5444)
* Remove proto Date from GeneratorUtilities

All callers use Scala Date, so the proto Date overload is unused.
- Remove import of net.eagle0.eagle.common.date.Date
- Remove the proto Date overload of dateString()
- Keep only the Scala Date version

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Remove proto types from LLM prompt generators

Migrated 8 files to use Scala types instead of proto:
- BattalionDescriptions: uses BattalionTypeId, BattalionT, BattalionView
- ChronicleEventTextGenerator: uses ChronicleEvent sealed trait
- ChronicleUpdatePromptGenerator: removed ChronicleEventConverter
- HeroBackstoryUpdatePromptGenerator: removed BattalionViewConverter, QuestConverter
- QuestEndedGeneratorUtilities: uses QuestT/QuestC types
- QuestFailedPromptGenerator: removed QuestConverter
- QuestFulfilledPromptGenerator: removed QuestConverter

Updated DEPROTO_PLAN.md to accurately reflect completion of Phase 8
(LLM Prompt Generators) and Phase 8b (Hostility Migration).

All non-boundary library code is now protoless.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 10:29:25 -08:00
7ab8fc796b Add BUILD.bazel dependency linting (#5437)
* Add BUILD.bazel dependency linting

Adds tooling to enforce architectural boundaries in the codebase:

1. Shell script `scripts/check_build_deps.sh` with multiple modes:
   - Default: runs all checks and shows dependency counts
   - --ci: fails only on hard violations (src/main depends on src/test)
   - --strict: also fails if proto deps in library/ increase from baseline
   - --count: just show current dependency counts
   - --update-baseline: update the baseline file for tracking

2. Bazel test `//:build_deps_test` for integration with test suite

3. Baseline file `scripts/build_deps_baseline.txt` tracking proto dep count

Current enforced rules:
- src/main must not depend on src/test (enforced now)
- library/ proto dependencies tracked (167 currently, deproto in progress)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update DEPROTO_PLAN.md with accurate proto import inventory

The previous status was inaccurate. This update:

- Corrects summary table: Availability, Command Choice Helpers,
  and other utilities still have Hostility proto imports
- Adds Phase 8b documenting Hostility proto migration (4 files)
- Updates detailed inventory showing actual 13 files with direct
  proto imports (vs 167 transitive deps from bazel query)
- Corrects success criteria checkboxes to reflect actual status
- Documents the grep command used to verify imports

The 167 proto deps from bazel query are transitive dependencies.
Only 13 files have direct proto imports:
- 5 boundary files (expected to keep proto)
- 4 files using Hostility proto (Phase 8b)
- 4 LLM prompt generator files (Phase 8)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 07:28:58 -08:00
8981298394 Update DEPROTO_PLAN.md - all business logic now protoless (#5432)
Mark Phase 8 (LLM Prompt Generators) as complete following PR #5429.
All 34 prompt generator files and all request generators now use Scala
LlmRequestT types instead of proto types.

Summary:
- Total proto imports: ~7 (down from 149)
- All remaining proto usage is at system boundaries (gRPC, persistence)
- All success criteria met
- All open questions resolved

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 21:45:01 -08:00
a37e0af028 Make DiplomacyResolutionLlmRequestGenerator protoless (#5396) (#5396)
* Update DEPROTO_PLAN.md with recent completions

- Mark Phase 9 (Utilities) as complete - MapGenerator now protoless
- Mark Phase 10 (History APIs) as complete - PersistedHistory accepts Scala types
- Add PRs #5373, #5378, #5381, #5390 to recent completions
- Update proto import inventory with current counts
- Update success criteria checklist

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Make DiplomacyResolutionLlmRequestGenerator protoless

Replace proto types with Scala equivalents:
- DiplomacyOfferStatus -> Status
- GeneratedTextRequest -> LlmRequestT
- Proto message types -> LlmRequestT enum cases

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 09:05:37 -08:00
c71bf10c0e Add scalaActionResult to ActionWithResultingState and use Scala types in ActionResultFilter (#5362)
- Add precomputedScalaActionResult parameter and lazy scalaActionResult property to
  ActionWithResultingState, following the same pattern as scalaGameState
- Update GameHistory.withNewResultsScala to preserve Scala ActionResultT to avoid
  re-conversion
- Convert ActionResultFilter.includeForPlayer to use Scala types (ActionResultT,
  NotificationT, ActionResultType) instead of proto types
- Add UNIVERSALLY_VISIBLE_TYPES_SCALA constant with Scala ActionResultType values

This continues Phase 10 of the deproto migration, moving internal logic to use
Scala types while keeping proto for boundaries.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 07:18:52 -08:00
46f3c52174 Make GameStateViewFilter and GameStateViewDiffer protoless (#5360)
This PR completes Phase 7 of the deproto migration by making the view
filter and differ components work with Scala types internally, converting
to proto only at boundaries.

## New Scala Types

- `GameStateView` - full game state view
- `GameStateViewDiff` - diff between two game state views
- `ProvinceViewDiff` - diff for province views
- `HeroViewDiff` - diff for hero views
- `FactionViewDiff` - diff for faction views
- `FullProvinceInfoDiff` - diff for full province info
- `ShardokBattleView` - battle view type
- `Hostility` - enum for hostility levels

## New Converters

- `GameStateViewConverter` - converts Scala GameStateView to proto
- `GameStateViewDiffConverter` - converts Scala GameStateViewDiff to proto
- `ProvinceViewDiffConverter` - converts Scala ProvinceViewDiff to proto
- `HeroViewDiffConverter` - converts Scala HeroViewDiff to proto
- `FactionViewDiffConverter` - converts Scala FactionViewDiff to proto
- `ShardokBattleViewConverter` - converts Scala ShardokBattleView to proto
- `HostilityConverter` - converts Scala Hostility to proto

## Updated Components

- `GameStateViewFilter` - now returns Scala `GameStateView`
- `GameStateViewDiffer` - now uses Scala diff types internally
- `ActionResultFilter` - converts Scala diff to proto at boundary
- `HumanPlayerClientConnectionState` - converts Scala GameStateView to proto

## Test Updates

- Updated tests to use Scala `ProvinceOrderType` instead of proto

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 21:39:37 -08:00
c6c7430dce Delete unused RansomOfferHelpers and its tests (#5351)
RansomOfferHelpers had no production callers - it was only tested.
The ransom offer logic in CommandChoiceHelpers already uses
Scala DiplomacyOptionType.Ransom and Scala RansomOfferDetails.

This removes the last 3 proto imports from command_choice_helpers/.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 19:46:20 -08:00
b48a44d910 Delete unused stats() method from IncomingArmyUtils (#5348)
The stats() method that returns proto ArmyStats has no callers.
Removing it eliminates both proto imports from IncomingArmyUtils.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 19:20:25 -08:00
84be65cfc3 Delete unused proto overloads from StatWithConditionUtils and ProvinceEventUtils (#5345)
- StatWithConditionUtils: Remove supportSc() and scWithRanges() proto versions
  (only Scala versions supportScala() and scWithRangesScala() were being used)
- ProvinceEventUtils: Remove all proto overloads (only Scala overloads used)
- Remove proto dependencies from BUILD.bazel files

Both files are now completely protoless.
Other Utilities: 13 → 9 proto imports (5 → 3 files)
Total: ~84 → ~80 proto imports remaining

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 19:09:09 -08:00
3834471840 Make ProvinceView use Scala ProvinceEvent instead of proto (#5341)
- Update ProvinceView.knownEvents from proto ProvinceEvent to Scala type
- Update ProvinceViewConverter to convert events with ProvinceEventConverter
- Update ProvinceViewFilter to work with Scala events internally:
  - Remove proto import
  - Use Scala overloads of ProvinceEventUtils checkers
  - Simplify event filtering logic
- Add visibility for event target to proto_converters/view and model/view

ProvinceViewFilter is now completely protoless - zero proto imports.
View filters directory reduced from 4 proto imports to 3 (in 2 files).

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 18:39:52 -08:00
cb0111a00f Make HeroViewFilter return Scala types instead of proto (#5336)
* Make HeroViewFilter return Scala types instead of proto

- Create HeroView.scala - Scala case class for the hero view type
- Create HeroViewConverter - converts Scala HeroView to proto
- Update HeroViewFilter to return Scala HeroView
- Update GameStateViewFilter to convert to proto at the edge
- Update AvailableCommandConverter to convert HeroView at the edge
- Update tests to use Scala types

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Consolidate DEPROTO_PLAN.md files and update with current stats

- Move detailed proto import inventory from root DEPROTO_PLAN.md to docs/
- Delete root level DEPROTO_PLAN.md (duplicate)
- Update all proto import counts based on current codebase state:
  - Total: ~85 imports remaining (down from 149)
  - Command choice helpers: 3 imports in 1 file (was 57 in 17)
  - View filters: 4 imports in 3 files
  - LLM generators: 56 imports in 34 files
- Add HeroViewFilter to recent completions (PR #5336)
- Update Phase 7 table with current view filter statuses

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 16:10:54 -08:00
de532a3448 Make FactionViewFilter return Scala types instead of proto (#5334)
* Make FactionViewFilter return Scala types instead of proto

- Create FactionView.scala - Scala case class for the view type
- Create FactionViewConverter - converts Scala FactionView to proto
- Create FactionRelationshipViewConverter - converts FactionRelationship to proto view
- Update FactionViewFilter to return Scala FactionView
- Update GameStateViewFilter to convert to proto at the edge
- Update tests to use Scala types

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update DEPROTO_PLAN.md with recent progress

- Actions layer now at 0 proto imports (PR #5332)
- ProvinceUtils converted to Scala BattalionType (PR #5333)
- FactionViewFilter returns Scala types (PR #5334)
- Updated summary table and success criteria
- Reorganized remaining work into clear phases

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 14:25:16 -08:00
6df165cc5a Tutorial bug fixes: prevent multiple popups and remove placeholder paths (#5300)
* Prevent multiple tutorials from showing simultaneously

- Don't trigger contextual tutorials while another is already active
- Fix HideOverlay to work even when parent container is inactive
  (was silently returning without hiding, causing UI pile-up)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Remove placeholder TargetGameObjectPath values from tutorials

The placeholder values like "ProvinceUI", "BattleButton", "EndTurnButton"
don't match actual GameObjects in the scene, causing warnings.

Overlays now show centered without targets. TODO comments mark where
to add proper targeting once the actual UI hierarchy is known.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add tutorial content documentation for editing

Creates docs/TUTORIAL_CONTENT.md with:
- Full onboarding sequence (13 steps) with titles, descriptions, triggers
- Strategic contextual tutorials (diplomacy, heroes, weather, prisoners)
- Tactical contextual tutorials (spells, terrain, abilities)
- Display mode reference
- Content guidelines

Edit this doc to refine content, then update TutorialContentDefinitions.cs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 16:19:43 -08:00
3f921c663f Update deproto plan: add DiplomacyOfferStatus and next candidates (#5097)
- Document completed DiplomacyOfferStatus enum migration (PR #5093)
- Add Enum Type Migrations section tracking proto enum conversions
- Add Next Candidates section with priority items for future work

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 06:31:05 -08:00
577c94f092 Remove unused images from Assets/Images, update references and asset audit (#5057)
- Delete all unused images from Assets/Images/ (keeping only startFire.png)
- Delete duplicate Assets/Images/bridge.png (identical to Shardok/commandImages/bridge.png)
- Update Gameplay.unity and Map Editor.unity to reference the Shardok bridge
- Update ASSET_AUDIT.md:
  - Mark previously deleted stock images as done
  - Add clip art section for bridge.png and startFire.png that need replacement
  - Update action items and recommendations

Deleted images (unused):
- Steel-Hauberk.png, bow00.png, brotherhood.png, burglar.png
- chicken-leg.png, clinking-beer-mugs.png, crystal-ball.png, faker.png
- flail.png, gift.png, jail.png, longbow.png, orders.png
- peace-dove.png, spearshield.png, supplies.png, travel.png, bridge.png

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 17:08:42 -08:00
66efefc8d6 Replace Hetzner plan doc with latency hiding strategies (#5053)
The Hetzner on-demand compute implementation is complete:
- ARM64 builds working in CI
- Shardok running on Hetzner CAX41 in Helsinki
- Production Eagle connected via TLS + token auth
- IPv6/NAT64 networking configured

Delete the completed planning doc and add a new doc covering
future latency optimization strategies:
1. Client-side animation masking (recommended first step)
2. Split Shardok architecture (future if needed)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 13:37:53 -08:00
80023d39a0 Update DEPROTO_PLAN.md: 100% action files now protoless (#5050)
- ResolveBattleAction migrated to Scala types (PR #5048)
- All 52 action files are now fully protoless
- CommandChoiceHelpers fully migrated to Scala types
- Update progress summary and validation checklist

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 13:20:46 -08:00
ba9c92222c Add asset licensing audit document (#5042)
Document all media assets in Unity project for licensing review before
potentially opening public access to game downloads. Identifies:
- Asset Store purchases (properly licensed)
- CC-licensed music with attribution
- Items needing verification (Shardok sounds, StrategyGameIcons, etc.)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 10:47:28 -08:00
e9dd53fdf8 Hetzner setup: docs + Step 7 implementation (#5018)
* Update Hetzner docs with actual deployment details

- Use shardok.prod.eagle0.net as the domain name
- Step 4: Specify GitHub Actions secrets location
- Step 5: Recommend Hillsboro, OR (hil) + IPv6 floating IP
- Update all code examples and cloud-init scripts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Wire SHARDOK_AUTH_TOKEN through Eagle's Shardok connection

Implements Step 7 of Hetzner setup guide:
- Main.scala reads SHARDOK_AUTH_TOKEN env var and creates ShardokSecurityConfig
- TLS is auto-enabled when Shardok address contains ".eagle0.net"
- Security config passed to both newGamesManager and newCustomBattleManager
- docker-compose.prod.yml passes SHARDOK_AUTH_TOKEN to Eagle container
- docker_build.yml passes secret during deployment

This is backward compatible: local Docker Shardok (shardok:40042) continues
to work without TLS/auth since the address doesn't contain ".eagle0.net".

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update ShardokInstanceManager for DigitalOcean registry and TLS

- Update cloud-init script to use DigitalOcean Container Registry instead of ghcr.io
- Add Let's Encrypt/Certbot setup for TLS certificates
- Configure automatic certificate renewal via cron
- Pass TLS cert paths and auth token file to Shardok container
- Default to shardok.prod.eagle0.net domain

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix cloud-init to use eagle0.conf instead of environment variables

The Shardok C++ server reads configuration from /usr/local/share/eagle0/eagle0.conf,
not environment variables. Updated cloud-init script to:
- Create eagle0.conf with TLS and auth paths
- Mount the config directory into the container
- Remove unused -e environment variable flags

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Simplify spin-up strategy: trigger on human turns with 10-min idle timeout

Instead of trying to predict battles, we now:
- Spin up Shardok when any human player takes a turn
- Shut down after 10 minutes of no human turns

This is simpler and more reliable. Battles happen regularly during active
play, so Shardok will be ready when needed. The 10-minute timeout is long
enough to cover thinking time but short enough to minimize idle costs.

Updated:
- SHARDOK_ON_DEMAND_COMPUTE.md with new strategy and state machine
- ShardokInstanceManager: renamed onPlayerActivity -> onHumanTurn,
  changed default idle timeout from 60 to 10 minutes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Document Hetzner testing progress and ARM64 blocker

Testing status:
- Hetzner CAX41 ARM64 server created in Helsinki
- Floating IPv6 configured with netplan persistence
- DNS and Let's Encrypt certificates working
- NAT64 configured for IPv6→IPv4 registry access

BLOCKER: ARM64 container crashes with runfiles error:
"cannot find runfiles (argv0="/app/shardok-server")"
Needs BUILD.bazel investigation for ARM64 image packaging.

Also documented known issues:
- IPv6-only servers need NAT64 DNS (nat64.net)
- Floating IP requires manual netplan config

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix ARM64 container crash: add missing environment variables

The ARM64 Shardok container was crashing with "cannot find runfiles"
because SHARDOK_RESOURCES_PATH and SHARDOK_MAPS_PATH weren't set.
Without these, the binary tries to use Bazel runfiles which don't
exist in the container.

Added the required environment variables to the docker run command
in the cloud-init script, matching docker-compose.prod.yml.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 17:07:10 -08:00
09041887c8 Fix ARM64 image push: use same repo with arm64- tag prefix (#5012)
DigitalOcean free tier limits to 5 repositories. Use the existing
shardok-server repository with arm64-prefixed tags instead of a
separate shardok-server-arm64 repository.

- shardok-server:latest (x86)
- shardok-server:arm64-latest (ARM64)

Also adds HETZNER_SETUP.md with infrastructure setup instructions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 08:27:02 -08:00
a92a878465 Switch ARM64 Shardok image to DigitalOcean Container Registry (#5009)
Use DigitalOcean registry for ARM64 images instead of GitHub Container
Registry for consistency with all other images (Eagle, Shardok x86,
admin, auth, jfr-sidecar).

- Update ci/BUILD.bazel: shardok_server_push_arm64 → registry.digitalocean.com
- Update shardok_arm64_build.yml: use DO_REGISTRY_TOKEN auth
- Update docs to reflect the registry change

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 22:25:17 -08:00
88273937da Add TLS and token authentication to Shardok server (#5001)
* Add TLS and token authentication support to Shardok server

Enables secure communication between Eagle and remote Shardok instances:

- TLS support: Read SSL cert/key from config paths, create SslServerCredentials
- Token auth: Validate "Authorization: Bearer <token>" header on all RPCs
- Config: Added authTokenPath to ServerConfiguration
- TokenValidator class reads token from file and validates requests
- Graceful fallback: If TLS not configured, uses insecure credentials

Configuration options (via eagle0.conf or environment):
- sslCertPath: Path to TLS certificate (e.g., Let's Encrypt fullchain.pem)
- sslPrivateKeyPath: Path to TLS private key
- authTokenPath: Path to file containing the auth token

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update implementation plan to mark completed tasks

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add TLS and token authentication support to Eagle's Shardok client

Add ShardokSecurityConfig to configure TLS and auth token for connecting
to remote Shardok instances. When useTls is enabled, uses system trust
store for Let's Encrypt certificates. When authToken is set, adds Bearer
token to all gRPC requests via BearerTokenInterceptor.

This enables Eagle to connect securely to Shardok instances running on
Hetzner Cloud with Let's Encrypt TLS and token-based authentication.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 22:12:31 -08:00
79aac7c9ce Add Hetzner Cloud API client for on-demand Shardok instances (#4996)
Implements a Scala client for the Hetzner Cloud API to manage
on-demand Shardok instances for tactical combat. Features:

- Create/delete servers with cloud-init user data
- Get server status and list servers by label
- Power on/off and graceful shutdown operations
- Async execution with Futures
- Proper error handling for API errors

Also updates the plan doc to track ARM64 build completion.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 16:01:58 -08:00
85eccb97f0 Add Shardok on-demand compute infrastructure documentation (#4989)
Documents the architecture for running Shardok on Hetzner ARM instances
with on-demand spin-up based on player activity:

- Hetzner CAX41 (16 ARM cores) in Ashburn at ~$0.04/hr
- Activity-based spin-up: start when players connect, stop after 1hr idle
- TLS + token authentication (Let's Encrypt for certs, auto-renewed)
- Estimated cost: $2-7/month vs $20-40/month for equivalent always-on

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 11:12:07 -08:00
23d2cb3968 Extract OAuth to Go service (Phase 1) (#4980)
* Extract OAuth to Go service (Phase 1)

Move OAuth authentication handling from Eagle Scala server into a separate Go
service running in its own container. This simplifies Eagle, enables independent
deployment, and sets up for future JWT validation extraction.

Architecture:
- Go auth service handles OAuth flows, JWT creation, state management
- Eagle proxies Auth gRPC calls to Go service when configured
- Go service calls Eagle's InternalUserService for user persistence
- Shared JWT keys via volume mount

New files:
- src/main/go/net/eagle0/authservice/ - Go auth service
- auth_internal.proto - Internal gRPC for Go→Eagle communication
- InternalUserServiceImpl.scala - User service wrapper for internal gRPC
- ExternalAuthClient.scala - Client for forwarding to Go service

Backward compatible: Eagle runs in standalone mode without --auth-service-url

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add unit tests for Go auth service

- jwt_test.go: Tests for JWT token creation, validation, and refresh
- oauth_test.go: Tests for OAuth state management and status checking

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 07:32:02 -08:00
13f79c37bc Add lobby status display and update OAuth documentation (#4976)
* Update OAUTH_NEXT_STEPS.md with current status and remaining work

- Mark completed items (headshots, logout, lobby display, etc.)
- Add new issues discovered (intermittent expired errors, token expiry bug)
- Reorganize implementation plan into Phase 2 (remaining) and Phase 3 (nice-to-haves)
- Update status of all known issues

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Wire up lobby UI elements in Unity scene

- Connect logout button
- Connect lobbyEnvironmentText and lobbyUserText fields

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add lobby environment and user display fields to ConnectionHandler

- Add lobbyEnvironmentText and lobbyUserText fields
- Add UpdateLobbyStatusDisplays() to populate them when entering lobby
- Shows OAuth DisplayName or classic login username

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 21:59:03 -08:00
3dd06c0aba Fix JWT auth to set legacy userName for backwards compatibility (#4964)
* Fix JWT auth to set legacy userName for backwards compatibility

When using JWT authentication, contextWithJwtClaims was not setting
the legacy userNameCtxKey, causing AuthorizationUtils.userName to
return null. This broke game management code that relies on userName
for mapping users to factions.

Fix by also setting userNameCtxKey to displayName when using JWT auth,
ensuring backwards compatibility with existing game management code.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add OAuth implementation next steps and design doc

Comprehensive design document covering:
- Known issues (identity fragility, headshots, logout, uniqueness)
- Proposed user identity model with userId as stable key
- Multi-provider account linking strategy
- Avatar/headshot strategy
- Phased implementation plan
- Technical debt and open questions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update OAuth design doc with headshot investigation

- Clarify that headshots are AI-generated character portraits, not user avatars
- Document headshot architecture: client → eagle0.net (home Mac) → S3 signed URL
- Explain why OAuth breaks headshots: eagle0.net nginx only validates Basic Auth
- Clarify PR #4964 is required now (fixes admin server NPE crash)
- Phase 2 migration to userId-based identity deferred

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 17:45:12 -08:00
72e2f016e2 Add game rewind feature to admin server (Phase 3) (#4902)
* Add game rewind feature to admin server (Phase 3)

Implement the ability to rewind a game to a previous action count via the
admin web UI. This is useful for debugging, testing "what if" scenarios,
and recovering from bugs.

Changes:
- Add RewindGame RPC to eagle.proto with request/response messages
- Add truncateTo() method to GameHistory trait and implementations
- Add rewindTo() method to Engine trait and EngineImpl
- Add rewindTo() method to GameController (disconnects clients, resets AI)
- Add rewindGame() to GamesManager with validation
- Implement rewindGame RPC in EagleServiceImpl
- Add /games/{id}/rewind POST handler to admin server
- Add rewind button to each history row with confirmation dialog
- Add success/error feedback UI with alert styling
- Add tests for InMemoryHistory.truncateTo, EngineImpl.rewindTo, and
  GameController.rewindTo

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Resync connected clients on rewind instead of disconnecting them

- Add resyncAfterRewind() to HumanPlayerClientConnectionState that sends
  a starting_state with the rewound GameStateView and new action count
- Update GameController.rewindTo() to keep clients and send resync messages
- Rename proto field disconnected_clients -> resynced_clients
- Update admin server messages to reflect new behavior
- Unity client's HandleStartingState() handles the resync automatically

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 17:16:02 -08:00
df711b26eb Add settings management to admin server (Phase 2) (#4891)
* Add settings management to admin server (Phase 2)

- Add GetSettings gRPC endpoint to eagle.proto
- Enhance SettingsLoader generator to include getAllSettings method
- Implement getSettings in EagleServiceImpl
- Add settings page with live search and inline editing
- Uses existing AddSettings endpoint for updates
- Modified settings are highlighted in the UI

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Make settings table rows more compact

- Reduce cell padding and font sizes
- Make input and button elements more compact
- Override Pico CSS defaults for better density

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Make settings input fields even more compact

- Use !important to override Pico CSS defaults
- Set fixed height of 22px for input and button
- Reduce padding to 2px

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 17:37:48 -08:00
29c7ad7c58 Complete Phase 1: reverse order history and clickable action details (#4890)
- Add GetActionDetail gRPC endpoint to fetch individual action data
- Display action history in reverse chronological order (most recent first)
- Make action rows clickable to expand and show JSON representation
- Add action_detail.html template for htmx-powered action expansion
- Update CSS for clickable rows and action detail styling
- Mark Phase 1 as complete in enhancement plan

New routes:
- GET /games/{id}/action/{index} - htmx partial for action detail

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 15:17:20 -08:00
9c7c929b0d Add Web UI to admin server with enhancement plan (#4888)
Phase 1 of admin server enhancements:
- Add Go templates with embed for layout, games list, game detail
- Add Pico CSS from CDN for styling, htmx for interactivity
- Implement htmx infinite scroll for action history
- Keep JSON API endpoints for backward compatibility

New routes:
- GET / - redirect to /games
- GET /games - HTML games list page
- GET /games/{id} - HTML game detail page
- GET /games/{id}/history - htmx partial for history rows

Also adds docs/ADMIN_SERVER_ENHANCEMENTS.md with full enhancement plan
including settings management and game rewind features.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 14:49:12 -08:00
fc1d5aa7ad Add OAuth implementation plan for Discord + Google authentication (#4824)
Documents the design for replacing HTTP Basic Auth with OAuth 2.0:
- System browser + deep link flow for secure authentication
- JWT tokens (RS256) for session management
- User-chosen display names
- 5-phase implementation covering proto definitions, server auth services,
  Unity client OAuth flow, platform configuration, and testing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 12:35:39 -08:00
c9d252093a Convert RansomOfferHelpers to use Scala GameState (#4821)
* Convert RansomOfferHelpers to use Scala GameState

- Change RansomOfferHelpers from proto GameState to Scala GameState
- Update imports from proto to Scala types (FactionT.OutgoingOfferRound, GameState)
- Change FactionUtils.trust to use Scala factions vector instead of proto GameState
- Change FactionUtils.isFactionLeader to use Scala factions vector
- Simplify OutgoingOfferRound pattern match (no unknownFieldSet in Scala version)
- Update BUILD.bazel: remove proto game_state dependency, add Scala model deps
- Add GameStateConverter.fromProto call in CommandChoiceHelpers.maybeRansomLeaderCommand
- Update RansomOfferHelpersTest to use Scala types (FactionC, FactionRelationship)
- Add currentPhase to test GameStates in CommandChoiceHelpersTest for conversion

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update DEPROTO_PLAN.md: mark RansomOfferHelpers as protoless

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 12:18:39 -08:00
355c06d342 Convert ImproveCommandSelector to use Scala types (#4811)
- Convert ImproveCommandSelector from proto to Scala types (GameState, ProvinceT, HeroT)
- Add protoless overload to HeroSelector.minimallyFatiguedHeroes, keep proto version as minimallyFatiguedHeroesProto
- Update all callers in CommandChoiceHelpers and MidGameAIClient to wrap with GameStateConverter.fromProto()
- Update ImproveCommandSelectorTest to use GameStateConverter.fromProto()
- Add exports to improve_command_selector for GameState visibility
- Update DEPROTO_PLAN.md to reflect progress

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 08:31:51 -08:00
dfd1da8b8a Convert ExpandCommandSelector to use Scala types (#4809)
- Convert ExpandCommandSelector from proto to Scala types (GameState, ProvinceT, FactionT)
- Add isAdjacentEnemy method to ProvinceUtils for protoless usage
- Add protoless overloads to ProvinceGoldSurplusCalculator
- Update ExpandCommandSelectorTest to use GameStateConverter.fromProto()
- Update DEPROTO_PLAN.md to reflect progress

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 07:58:21 -08:00