Migrate ResolvedShardokPlayer to use native Scala EndGameCondition
instead of the proto type. This removes 1 transitive proto dependency
from library/ (75 → 74).
Changes:
- Add Scala EndGameCondition sealed trait with Victory, AllyVictory,
Draw, and Loss cases
- Add DrawType enum for draw conditions
- Add EndGameConditionConverter in ShardokBattleConverter
- Update ResolvedShardokPlayer to use Scala EndGameCondition
- Move proto conversion to ShardokInterfaceGrpcClient boundary
- Update ResolveBattleAction to use Scala EndGameCondition API
- Update ResolveBattleActionTest to construct Scala EndGameCondition
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* 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>
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>
Use run-specific artifact names (signed-mac-app-{run_id}, notarized-mac-app-{run_id})
to prevent race conditions where a previous run's cleanup job might delete
artifacts from a concurrent run.
Root cause: When PR #5465 merged, the previous run (which used the old workflow
that deleted ALL artifacts by name) ran its cleanup job between when the new
run uploaded its artifact (06:13:20) and when it tried to download it (06:18:27).
Fix: Include the GitHub run ID in artifact names so each run's artifacts are
uniquely named and cleanup jobs can only delete their own artifacts.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
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>
* Add Eagle server build to CI
Add eagle_build.yml workflow that builds the Eagle server when Scala
or Eagle proto files change. This ensures build failures in the Eagle
server are caught during PR review, similar to how shardok_build.yml
works for the C++ Shardok server.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix unused dependencies and simplify GameAdminServiceImpl
- Remove unused json4s and settings_loader deps from :service target
- Simplify GameAdminServiceImpl since GamesManager now returns
game_admin proto types directly (no conversion needed)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Read version from the built app's Info.plist instead of calculating
from git commit count at deploy time. This ensures the appcast version
always matches what's actually in the app bundle.
Previously, the deploy step used `git rev-list --count HEAD` which could
differ from the version baked into the Unity build if commits were added
between the build and deploy steps.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add first-encounter profession tutorials
Shows tutorial popup explaining each profession's abilities when player
first encounters a hero with that profession. Covers both strategic
(Eagle) and tactical (Shardok) abilities in natural language.
Triggers when viewing provinces with employed or free heroes.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update mage and paladin profession tutorial text
- Mage: Can create *or end* blizzards and droughts
- Paladin: Giving alms provides much larger support boost
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Now that the admin console uses the GameAdmin service (PR #5460),
remove the duplicate admin endpoints from eagle.proto and their
implementations from EagleServiceImpl.
Changes:
- Remove 14 admin RPC endpoints from Eagle service in eagle.proto
- Remove corresponding message types (AddSettings*, Get*, Convert*,
Reassign*, Rewind*, Import*, Delete*, CheckGame*, DownloadGame*)
- Remove admin method implementations from EagleServiceImpl.scala
- Update GamesManager to import admin response types from game_admin
proto instead of eagle proto
This completes the admin service separation:
- PR #5458: Create GameAdmin service
- PR #5460: Migrate admin console to GameAdmin
- This PR: Remove admin endpoints from Eagle
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
After the GameHistory split, EagleServiceImpl needs to access
shardokPlayerCount from controller.fullHistory instead of
controller.engine.history. Also export full_game_history from
game_controller so downstream targets can see the FullGameHistory type.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
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>
Update admin_server.go to call the new GameAdmin gRPC service instead
of Eagle for all game management operations. This is step 2 of the
admin service separation:
- Replace grpcClient (EagleClient) with gameAdminClient (GameAdminClient)
- Update all game admin RPC calls to use gameadminpb types
- Add game_admin_go_proto dependency to BUILD.bazel
- Fix game_admin BUILD.bazel to use go_grpc instead of go_grpc_v2
(v2 only generates gRPC code, not message types)
Auth operations still use authpb (via Eagle's Auth service).
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Create a separate GameAdmin service to handle admin-only game management
operations (RewindGame, DeleteGame, GetRunningGames, etc.) that were
previously exposed through the Eagle service alongside client endpoints.
This is the first step in separating admin and client APIs:
- Creates game_admin.proto with all 13 admin game endpoints
- Implements GameAdminServiceImpl calling the existing GamesManager
- Registers the new service in Main.scala on the same port as Eagle
The admin console will be migrated to use this service in a follow-up PR,
after which the admin endpoints can be removed from eagle.proto.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Go's XML unmarshaler doesn't preserve xmlns attributes correctly.
When the appcast was fetched and re-marshaled, the Sparkle namespace
was being set to empty string, which prevented Sparkle from parsing
version numbers and detecting updates.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Change from com.Shardok-Games.eagle0 to net.eagle0.eagle0 to match
the project's domain (net.eagle0).
Updated in:
- Unity ProjectSettings (applicationIdentifier for Standalone)
- inject_sparkle.sh (CFBundleURLName for URL scheme)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Sparkle's default check interval is 24 hours. If a user launches the app
shortly after a new version is deployed but before their scheduled check,
they won't see the update prompt.
This adds an explicit background check on every startup to ensure users
get updates promptly when a new version is available.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Make Engine API protoless by introducing Scala ActionResultView
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>
* Split GameHistory into protoless GameHistory and FullGameHistory
- Create FullGameHistory trait in service/ that extends GameHistory
and adds Shardok-specific methods (shardokPlayerResultsSince,
shardokPlayerAvailableCommands)
- Remove receiveBattleUpdate from Engine trait (moved to GamesManager)
- Update GameController to hold fullHistory: FullGameHistory field
- Update PersistedHistory and InMemoryHistory to extend FullGameHistory
- Add extractFullHistory helper methods for safe type extraction
- Update tests to use FullGameHistory mocks
This continues the deproto effort by isolating proto-dependent
Shardok methods from the library layer's GameHistory trait.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Regenerate ICO file with proper format:
- Use PNG compression for 256x256 size (Windows Vista+ requirement)
- Use BMP format for smaller sizes (16, 24, 32, 48)
- Proper BITMAPINFOHEADER structure for BMP entries
The previous ICO may not have displayed correctly due to format issues.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Reduce build size: optimize Twitch button, remove unused profession icons
1. glitch_flat_purple.png (Twitch OAuth button):
- Reduce maxTextureSize from 2048 to 512
- Enable crunched compression
- Expected savings: ~8 MB
2. Delete Resources/Professions folder:
- These icons were unused (profession textures are assigned via
EagleCommonTextures component, not loaded from Resources)
- Expected savings: ~7 MB (5.3 MB paladin + others)
Total expected savings: ~15 MB
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Move actually-used icons from Unused to InUse folder
22 icons from 4000_Fantasy_Icons/Unused were actually referenced by
Gameplay.unity (for flame effects, UI elements, etc). Moving them
to an InUse folder makes the naming accurate.
Unity tracks assets by GUID, so moving files with their .meta files
preserves all scene/prefab references.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Uses the same eagle head icon that was added to the Unity/macOS app.
Converted from PNG to ICO format with multiple sizes (16-256px).
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Unity creates .meta files for the native plugins which should also
be ignored since the plugins are built at CI time.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
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>
* Add styled DMG with arrow pointing to Applications
- Add create-dmg as a bazel dependency for creating styled DMG installers
- Add background image with arrow pointing from app to Applications
- Update mac_build_handler to use create-dmg for styled DMG creation
- Update CI workflow to use the new DMG creation process
The DMG now shows a visual arrow guiding users to drag the app
to the Applications folder.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add --skip-jenkins flag for headless CI environment
The AppleScript that positions icons times out in CI environments
without a GUI session. Using --skip-jenkins skips the Finder styling
while still including the background image and Applications link.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Switch from create-dmg to dmgbuild for CI compatibility
dmgbuild generates .DS_Store files programmatically without needing
AppleScript or Finder access, making it work in headless CI environments.
- Remove create-dmg bazel dependency (relied on AppleScript)
- Use dmgbuild Python tool instead (pip install dmgbuild)
- Generates proper icon positions and background without GUI
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix DMG styling: lighter background, adjust icon positions
- Use light gray background so file/folder names are readable
- Move arrow up to align with icon centers
- Move Applications link further right (500 -> 520) to clear the arrow
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix arrow: move left, connect head to shaft properly
- Arrow now starts at x=200 (closer to app icon)
- Arrow ends at x=460 (further from Applications at x=520)
- Head and shaft now overlap for proper connection
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
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>
* 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>
The IsRunningFromReadOnlyVolume and ShowDMGWarning functions were
not being exported from the native plugin, causing the DMG launch
check to fail silently.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Runs every 6 hours to check total artifact storage. Fails if storage
exceeds 500 MB and lists the largest artifacts for debugging.
Steady-state should be ~50 MB (logs and test results). This acts as
a safety net in case cleanup jobs fail to run properly.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The cleanup step was deleting ALL signed-mac-app artifacts across the
entire repo, not just the current run's. When two main builds ran
concurrently, one build's cleanup would delete the other's artifacts.
Fix: Change cleanup to only delete the current run's artifacts using
the runs/$run_id/artifacts API endpoint instead of deleting all
artifacts matching the name.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Adds a step to the bazel_test workflow that runs check_build_deps.sh
in strict mode. This enforces architectural boundaries:
- src/main should not depend on src/test
- library/ proto dependencies should not exceed baseline
Runs before tests so failures are caught early.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The test tutorials ("You've issued your first command!") were
accidentally left enabled in the Gameplay scene.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Replace Hostility proto with Scala Hostility enum
This removes direct proto imports from library/ for the Hostility type:
- FactionUtils.hostilityStatus now returns Scala Hostility
- ArmyStats.hostility now uses Scala Hostility type
- AvailableAttackDecisionCommandFactory no longer imports proto
- AvailableFreeForAllDecisionCommandFactory no longer imports proto
- AttackDecisionCommandChooser uses Hostility.Enemy instead of proto
- BattleFilter no longer needs protoToScalaHostility converter
- AvailableCommandConverter uses HostilityConverter.toProto
This completes Phase 8b of the deproto migration, reducing direct
proto imports in library/ from 8 files to 4 (LLM prompt generators).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix AvailableCommandConverterTest to use Scala Hostility enum
Update test to use net.eagle0.eagle.model.state.Hostility.Self
instead of proto net.eagle0.common.hostility.Hostility.SELF_HOSTILITY.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add retention-days to all GitHub Actions artifacts
- Use retention-days: 1 for artifacts deployed to external storage
(sysroot, installer, mac app builds)
- Use retention-days: 3 for debug logs and test results
Prevents artifact storage quota from being exceeded.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Delete Mac build artifacts after successful deploy
Automatically deletes signed-mac-app and notarized-mac-app artifacts
after deployment completes. These ~250MB artifacts are only needed to
pass the app between workflow jobs; once deployed, they're redundant.
This prevents artifact storage from accumulating even if retention-days
doesn't expire them quickly enough.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Delete ALL old build artifacts after deploy (not just current run)
Both Mac and Windows installer workflows now delete ALL artifacts
with their respective names after successful deployment:
- Mac: signed-mac-app, notarized-mac-app
- Windows: eagle-installer
This ensures no artifact buildup even from failed/stale runs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Increase retention for test/build logs to 7 days
These are small files useful for debugging, so keep them longer:
- test.json (~100KB each)
- editor_win.log / editor_mac.log (~60KB each)
- failed-test-logs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Use 5-day retention to match repository maximum
The repository has a 5-day maximum retention policy configured.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Delete old Mac builds from DigitalOcean when pruning appcast
The appcast keeps the last 10 versions, but the old DMG files were never
deleted from S3. Now when items are removed from the appcast, the
corresponding DMG files are also deleted from DigitalOcean Spaces.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add cleanup job that runs even when build fails
Previously, artifact cleanup only happened in the deploy job, which
doesn't run if earlier jobs fail. This left behind large artifacts
from failed builds, eventually hitting the storage quota.
The new cleanup job runs on ubuntu-latest with `if: always()` so it
executes regardless of whether build-and-sign, wait-notarization,
or deploy succeeded or failed.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* 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>
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>
Unity auto-updated audio import settings:
- Serialized version 6 → 8
- Quality setting adjusted
- Preload audio data setting moved
Font SDF asset regenerated.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Convert all remaining LLM prompt generators to Scala types
This converts all remaining prompt generators from proto types to Scala
types (LlmRequestT). Updates include:
- All prompt generators now use LlmRequestT.* instead of proto types
- LlmResolver uses Scala type matches for all generators
- Proto fallback cases removed (now throw exceptions for non-LLM types)
- Updated BUILD files with correct dependencies
- Fixed test file to use Scala types
Note: ChronicleUpdatePromptGeneratorTest still needs to be updated to use
Scala types in a follow-up PR.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix ChronicleUpdatePromptGeneratorTest to use Scala types
Update the test to use LlmRequestT.ChronicleUpdateMessage and related
Scala types instead of proto types.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The deferred flag logic was inverted - we were collecting notifications
where deferred=true (hold for later) instead of deferred=false (send now).
This bug was introduced in commit 8d3bc914ed when migrating from proto
types to Scala types. The original proto-based code used notificationsToDeliver
which already handled the deferred distinction. The new Scala code used
NotificationConverter.toProto() which returns (proto, deferred) tuple,
but the .collect pattern incorrectly filtered for deferred=true.
Affected features:
- Divine command notifications (learning about quests)
- Quest completion/failure notifications
- Diplomacy event notifications (alliance, truce, etc.)
- All other immediate notifications
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add in-app warning when running from Downloads folder
Sparkle auto-update framework cannot update apps running from the
Downloads folder or other transient locations. This adds a native
macOS alert that warns users when they launch the app from an
invalid location and instructs them to move it to Applications.
The check detects:
- /Downloads/ - where macOS downloads files
- /tmp/ or /private/tmp/ - temporary directories
- /.Trash/ - deleted files
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix build: import AppKit for NSAlert
NSAlert is part of AppKit, not Foundation. Changed import to AppKit
which also includes Foundation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Offer to move app to /Applications and relaunch
Instead of just showing a warning, now offers to:
1. Copy the app to /Applications (removing existing if present)
2. Launch the new copy
3. Terminate the current instance
This provides a seamless experience - user just clicks "Move to
Applications" and the app handles everything automatically.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Switch Mac distribution from ZIP to DMG
Changes the Mac app distribution from a ZIP file to a DMG disk image.
This provides a better installation experience and enables enforcing
proper installation:
1. **DMG with Applications alias**: Users see the app and an
Applications folder alias, making drag-to-install intuitive.
2. **Read-only enforcement**: DMGs are mounted read-only, so if
users try to run the app directly from the DMG, we detect it
and show a blocking dialog telling them to install first.
3. **Sparkle works from any writable location**: Unlike the previous
Downloads-folder check, this approach allows the app to run from
anywhere writable (~/Desktop, ~/Games, /Applications, etc.).
Changes:
- SparklePlugin.m: Replace location warning with read-only volume check
- SparkleUpdater.cs: Update P/Invoke bindings for new functions
- SparkleInitializer.cs: Check for read-only volume instead of path
- mac_build_handler.go: Create DMG instead of ZIP using hdiutil
- invitation_handlers.go: Update download URL and instructions for DMG
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Update BreakAllianceResolutionMessagePromptGenerator to accept
LlmRequestT.BreakAllianceResolutionMessage instead of the proto type.
Add Scala type matching in LlmResolver and update BUILD dependency
from proto to Scala types.
Also updates enum handling from proto DiplomacyOfferStatus to Scala Status.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Update BreakAllianceMessagePromptGenerator to accept LlmRequestT.BreakAllianceMessage
instead of the proto type. Add Scala type matching in LlmResolver and update BUILD
dependency from proto to Scala types.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Update AllianceResolutionMessagePromptGenerator to accept
LlmRequestT.AllianceOfferResolutionMessage instead of the proto type.
Add Scala type matching in LlmResolver and update BUILD dependency
from proto to Scala types.
Also updates enum handling from proto DiplomacyOfferStatus to Scala Status.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
docker_build.yml was missing TWITCH_CLIENT_ID and TWITCH_CLIENT_SECRET
in its env block and export statements. All other OAuth providers
(Discord, Google, GitHub, Apple) were present, but Twitch was omitted
when the provider was added.
This caused Twitch to not appear on the invitation landing page after
Eagle deployments, even though auth_build.yml had the secrets.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Update AllianceOfferMessagePromptGenerator to accept LlmRequestT.AllianceOfferMessage
instead of the proto AllianceOfferMessage type. Add Scala type matching in LlmResolver
and update BUILD dependency from proto to Scala types.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Update TruceResolutionMessagePromptGenerator to accept
LlmRequestT.TruceResolutionMessage and use the Scala Status enum
instead of proto DiplomacyOfferStatus. Add Scala type matching in
LlmResolver.promptGenerator before falling back to proto matching.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Update TruceOfferMessagePromptGenerator to accept
LlmRequestT.TruceOfferMessage instead of the proto TruceOfferMessage
type. Add Scala type matching in LlmResolver.promptGenerator before
falling back to proto matching.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add --no-deps to nginx and admin docker compose commands to prevent
them from cascading to auth. The auth container has secrets (like
TWITCH_CLIENT_ID) that are only available in auth_build.yml, not in
docker_build.yml. Without --no-deps, docker compose would recreate
auth with blank env vars when it detected config changes.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Update DivineMessagePromptGenerator to accept LlmRequestT.DivineMessage
instead of the proto DivineMessage type. Add Scala type matching in
LlmResolver.promptGenerator before falling back to proto matching.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add sworn brotherhood guidance tutorial
- Add guidance_sworn_brotherhood tutorial that triggers when:
- Player has 4+ provinces, OR
- SwearBrotherhood command is available with good candidates (server-determined)
- Explains that sworn siblings become faction leaders for direct province control
- Mentions succession benefit if Warlord falls
Also update guidance_expand to note that vassals manage provinces without
a Warlord or sworn sibling present.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Improve sworn brotherhood tutorial with candidate detection
- Use SwornBrotherChooser logic to identify good candidates:
charisma >= 65, constitution >= 65, loyalty >= 95, ranked by power
- Dynamically update tutorial text to name the specific candidate
- Add 95 loyalty requirement and permanence info to description
- Keep 4+ provinces as alternative trigger condition
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix hero stat access - use int directly, not StatView
Charisma, Constitution, Strength, Agility, Wisdom are int properties,
only Loyalty uses StatView?.Stat pattern.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix TextEntry access - use .Text property for string value
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Use gender-neutral language: Brotherhood → Kinship
- Command header: "Swear Kinship with Hero"
- Tutorial text uses "sworn siblings" and "swear kinship"
- Internal IDs updated: guidance_sworn_brotherhood → guidance_sworn_kinship
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Native plugin (SparklePlugin.m):
- Log app version and build number from Info.plist
- Log feed URL configuration
- Log whether public key is set
- Log auto-check plist setting
- Log current auto-check state after initialization
- Log last update check date
Unity initializer (SparkleInitializer.cs):
- Log Application.version and Unity version
- Log platform and product name
- Log AutomaticallyChecksForUpdates setting
This helps debug why Sparkle updates may not be working.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This is the next step in the incremental migration from proto to Scala types
in the LLM processing pipeline.
Changes:
- LlmRequestWithGameState now takes GeneratedTextRequestT instead of
GeneratedTextRequest (proto)
- LlmResolver.resolveLlmRequests returns Scala types in the result
- Proto conversion happens at boundaries:
- Converting to proto for LLMResponse when streaming
- Converting to proto for receiveStreamingLlmFailure
- Converting to proto in promptGenerator for individual prompt generators
- UnrequestedTextHandler no longer needs to convert to proto when calling
LlmResolver
The prompt generators still expect proto types - they will be updated
incrementally in follow-up changes.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The nginx config is already deployed by docker_build.yml (which has
nginx/** in its trigger paths). Adding it to auth_build.yml was
redundant and broken (the production server isn't a git repo).
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When a user tries to sign in with an OAuth provider but doesn't have an
existing account, the auth service returns InvitationRequired status.
Previously this fell through to the default case and threw a generic
"Unknown OAuth status" exception, providing a poor user experience.
Now displays a friendly message explaining that an invitation is required
to create a new account, and suggests using an invitation link or signing
in with the original provider if they already have an account.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Add nginx/nginx.conf and docker-compose.prod.yml to trigger paths
- Pull latest nginx config and restart nginx during deployment
This ensures nginx config changes (like OAuth callback routes) are
automatically deployed when merged to main.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Store Scala types in ClientTextStore instead of proto
This change updates the ClientTextStore layer to store the native Scala
type `GeneratedTextRequestT` in memory instead of the proto type
`GeneratedTextRequest`. Proto conversion now only happens at persistence
boundaries (when saving to/loading from disk).
Key changes:
- ClientText.scala: Changed llmRequest field to use GeneratedTextRequestT
- ClientTextStore.scala: Updated interface to accept Scala types
- ClientTextStoreImpl.scala: Added toProto/fromProto conversion at persistence
- GameController.scala: Removed proto conversion in allNewLlmRequests
- UnrequestedTextHandler.scala: Uses Scala types internally, converts to proto
only when passing to LlmResolver
- GamesManager.scala: Uses Scala types directly when creating UnrequestedClientText
- Updated BUILD files for new dependencies and visibility
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix tests for ClientTextStore Scala types migration
- Update UnrequestedTextHandlerTest to use Scala types instead of proto
- Add GeneratedTextRequestConverter import for proto conversions in test mocks
- Change test requests from FixedHeroName to LlmRequestT.DivineMessage to properly test LLM resolution path
- Add visibility for test packages in BUILD files
- Remove unused proto dependency from generator_utilities_test
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
During blue-green deployment, the old (active) instance should not wait
for the flush marker - it would be waiting for itself to stop, which
causes a 30s timeout warning.
Now compares deployment timestamp to instance start time:
- If deployment started AFTER instance: skip wait (we're the old instance)
- If deployment started BEFORE instance: wait (we're the new instance)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Steam OpenID 2.0 redirects back to /oauth/steam/callback after
authentication, but nginx was not configured to proxy this path to the
auth service, resulting in 404 errors.
Added the location block alongside the existing /oauth/callback and
/oauth/apple/callback blocks.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Add GitHub, Apple, Steam, and Twitch to handleLoginStart switch
- Add sign-in buttons with icons for all providers to login.html
- Add CSS styles for new provider buttons
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add Steam and Twitch sign-in buttons to Unity client
- Add steamLoginButton and twitchLoginButton fields
- Add steamProviderIcon and twitchProviderIcon for stored accounts
- Wire up button click handlers in SetupOAuthUI()
- Add icon mapping for stored account display
Note: Unity scene needs Steam/Twitch button GameObjects and icons wired up.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add Steam and Twitch OAuth button assets
- Add Steam and Twitch (glitch) logo icons for OAuth buttons
- Consolidate Discord icon to OAuth Buttons folder
- Remove unused Google OAuth button variants
- Update Gameplay.unity with new button references
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The TWITCH_CLIENT_ID and TWITCH_CLIENT_SECRET were added to
docker-compose.prod.yml but not to the GitHub Actions workflow
that deploys the auth service.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add game state guidance tutorials
Add four contextual tutorials that trigger based on game state analysis:
1. guidance_loyalty_danger - November warning when heroes have low loyalty
Suggests using Feast or Give Gift to boost loyalty before year end
2. guidance_neighbor_danger - Alert when province borders hostile faction
Suggests Organizing Troops or seeking Diplomacy
3. guidance_recruit_heroes - When stable province has recruitable heroes
Suggests using Travel, Divine, and Recruit commands
4. guidance_expand - When player has stable province but only one territory
Suggests using March to claim new provinces
These provide strategic guidance for new players based on their game state.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix compilation errors in guidance trigger checks
- MarchAvailableCommand.OneProvinceCommands to access destination provinces
- FactionRelationshipView.TargetFactionId instead of FactionId
- province.FullInfo.Gold instead of model.CurrentGold
- Net.Eagle0.Eagle.Common.RecruitmentStatus instead of Views namespace
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Use Any() instead of ToList().Count for efficiency
Any() short-circuits on first match rather than materializing entire list.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Simplify recruit heroes gold check
Just check for 100 gold - January was when this would naturally be true,
not an alternative condition.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
docker-compose validates the entire file even when deploying just auth.
Remove the :? validation since docker_build.yml already validates
SHARDOK_ADDRESS explicitly before deploying Eagle.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Document that WarlordRow, VassalRow1, VassalRow2 are registered dynamically
by HeroesAndBattalionsPanelController when hero rows are created from prefabs.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Steam: OpenID 2.0 authentication (no API key required)
- Twitch: Standard OAuth 2.0 with user:read:email scope
- Add TWITCH_CLIENT_ID and TWITCH_CLIENT_SECRET to deployment config
- Add Steam/Twitch buttons to invitation landing page
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add fromProto methods to LlmRequestConverter and GeneratedTextRequestConverter
to enable converting proto types back to Scala types. This is preparation for
storing Scala types in-memory in ClientTextStore, with proto conversion only
happening at persistence boundaries.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Clear all children of storedAccountsContainer and disable them
immediately before destroying (fixes visual duplication from
deferred Destroy)
- Add delete button to StoredAccountButton to remove saved accounts
- Wire up delete button to OAuthManager.RemoveStoredAccount
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Divine reveals what you must do to impress a hero and recruit them,
not their stats/abilities.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Security improvement: Eagle no longer has access to JWT private keys.
- JwtService.scala: Simplified to validation-only (removed signing methods)
- Loads public key from /etc/eagle0/keys/public.pem (shared volume from auth)
- Falls back to extracting public key from JWT_PRIVATE_KEY for backward compat
- AuthServiceImpl.scala: Local fallbacks throw UNIMPLEMENTED (signing in Go auth)
- docker-compose.prod.yml: Removed JWT_PRIVATE_KEY from Eagle, volume now read-only
Auth service unchanged - still bootstraps PEM files from JWT_PRIVATE_KEY env var.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Clicking a selected unit during setup would trigger a placement action
instead of deselecting the unit, because HasPlacementAction returned
true for the unit's own tile. Now the placement check excludes the
currently selected tile, allowing the deselection logic to run.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Change version scheme from git describe (which picked up unrelated tags
like busybox-1.35.0) to commit-count based versions like 1.0.9548.
This gives automatic, always-incrementing version numbers that Sparkle
can properly compare for updates.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* 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>
- Use ditto instead of cp -R to copy Sparkle.framework (preserves bundle structure)
- Skip individual signing of Sparkle's internal XPC services, apps, and executables
- Use --deep flag when signing Sparkle.framework to handle its internal components
- Verify cached Sparkle.framework has proper symlink structure, re-download if corrupted
Fixes "bundle format unrecognized" error during codesigning.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
After investigating a bad deployment where Eagle connected to "shardok"
instead of the actual Hetzner address, found the root cause: GitHub
Actions secrets can occasionally fail to load, causing the heredoc to
expand with default values instead of the actual secrets.
Changes:
- Add validation at deploy start to check critical env vars are present
- Add network connectivity check to Shardok before blue-green deployment
(blocks deployment if Shardok is unreachable)
- Remove the misleading "shardok:40042" default - now SHARDOK_ADDRESS
must be explicitly set, making configuration errors immediately obvious
- docker-compose.prod.yml now uses :? syntax to require SHARDOK_ADDRESS
The env var validation will abort deployment early with a clear error
message if secrets failed to load, rather than deploying with broken
config.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add a new apply overload that takes Vector[ActionResultT] directly,
avoiding proto conversion at the call site. The conversion to proto
for storage still happens internally, but callers like NewGameCreation
can now pass Scala types directly.
Removed the unused proto-based apply overload since all callers that
need proto types use the case class constructor directly.
This makes NewGameCreation fully protoless.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The logout button was wired to QuitButtonClicked in the Unity scene,
causing the app to exit instead of returning to the connection panel.
- Change scene to wire logout button to OnLogoutClicked
- Make OnLogoutClicked public so it's visible in Unity inspector
- Remove redundant programmatic listener setup
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add loading indicator when switching environments in lobby
Shows "Switching environment..." text while waiting for the server
to respond after changing the environment dropdown. Text is cleared
when the lobby data arrives and populates the game lists.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* add lobby connection status and fix 16:10 aspect ratio
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Make NewGameCreation protoless
Convert NewGameCreation to build Scala ActionResultC internally instead of
protobuf ActionResult. Proto conversion now only happens at the boundary
when storing to PersistedHistory.
Changes:
- NewGameCreation.scala: Use ActionResultC, Scala RoundPhase, ChronicleEntry,
FactionC, FactionRelationship instead of proto equivalents
- StartGameActionResultUtils.scala: Change from proto ActionResult to
ActionResultC throughout
- GameParametersUtils.scala: Add provinceWithOverrideScala method
- Add withId methods to HeroT/HeroC and BattalionT/BattalionC traits
- Update BUILD.bazel files with visibility for new_game_creation package
- Update test to use Scala types
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* empty check
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The toProto method was not serializing newProvinces, causing provinces
to be lost when persisting game state. This led to stack overflow during
game creation as the phase advancer would loop infinitely with no provinces.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Export proto types from converters for caller visibility
ActionResultProtoConverter and GameStateConverter return proto types,
so those proto dependencies need to be exported for callers to use
the return values.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix missing proto exports and ServerSetupHelpers warnings
- Export shardok_battle_scala_proto from ShardokBattleConverter
- Export game_state_scala_proto and shardok_battle_scala_proto from GamesManager
- Remove unused ManagedChannelBuilder import from ServerSetupHelpers
- Remove unused default parameter from private newShardokInterface method
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Add @unused annotation to parameters that are intentionally unused
(userName and name parameters that need future verification)
- Add missing game_state_scala_proto dependency to BUILD.bazel
- Add CustomBattleManagerTest with tests for CustomBattleGameController
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Change EngineAndResults.results to return Vector[ActionResultT] (Scala) instead of Vector[ActionResult] (proto)
- Update PostResults to use Scala ActionResultT and ShardokBattle types
- Update GameController methods to use Scala types internally
- Add GeneratedTextRequestConverter for Scala-to-proto conversion when needed for LLM requests
- Add recipientFactionIds field to ClientTextVisibilityExtensionT trait
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Remove verbose debug logs that were added during Apple Sign-In debugging:
- Token exchange client_id/redirect_uri
- id_token length
- Parsed user info (id/email)
Error logging is retained for troubleshooting.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Apple inconsistently returns email_verified and is_private_email as
either boolean (true/false) or string ("true"/"false"). Added custom
AppleBool type that unmarshals both formats.
Also added missing claims from Apple's documentation:
- nonce, nonce_supported, c_hash, auth_time
See: https://developer.apple.com/forums/thread/121411🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Log the id_token length and any parse errors to help debug
"Failed to parse user info" errors.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The key is stored as base64-encoded PEM. The previous code base64
decoded it but then tried to use the result directly as key bytes.
The fix: after base64 decoding, PEM decode to extract the actual
key bytes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
MapGenerator now returns Scala ProvinceT instead of proto Province.
GameParametersUtils works with Scala types internally, converting to
proto only in NewGameCreation when needed for ActionResult.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
docker-compose ignores AUTH_IMAGE env var for unknown reasons.
Fix with multiple approaches:
1. Tag pulled image as :latest locally (belt)
2. Pass AUTH_IMAGE explicitly on command line (suspenders)
3. Add debug output to diagnose .env file issues
4. Verify by comparing image digests, not tags
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>