Golden image now installs the software-render stack (weston, libgl1-mesa-dri
llvmpipe, mesa-vulkan-drivers, vulkan-tools) so any worker renders proof scenes
via gl_compatibility/opengl3 with no GPU. New ./run dist:render <scene> <out.png>
wraps tools/capture-proof.sh against a worker (replaces the apricot SCREENSHOT_HOST).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Offload heavy compute from plum (M2 Air) to on-demand DO workers:
- dist:test — cargo test --workspace (nextest) on a worker (the main DX win)
- dist:build — cargo build + WASM on a worker; rsync the platform-independent
WASM back (native .so is linux-only, stays on the worker)
- dist:sync — git pull <ref> + rebuild gdext on live workers (no image rebuild)
- forge:down/up — snapshot+destroy / restore-from-snapshot (DO bills powered-off
droplets; only destroy stops it). ~$6/mo -> ~$0.30/mo idle; refreshes the
forge IP in ~/.vault/mc_forge_creds on restore.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ephemeral CPU Droplet fleet that horizontally scales the iteration loop:
- infra/terraform/test-fleet: cattle Droplets from a golden image (auto-discovered
by name via digitalocean_images), grouped under the mc:dev DO project, with a
mocked-provider test suite (no token/spend).
- infra/packer: golden-image builder reusing scripts/dev-setup/linux.sh.
- scripts/run/dist.sh: ./run dist:{check,up,sim,train,down} — shard sim/test
batches across workers via autoplay-batch AUTOPLAY_HOST+SEED_OFFSET.
GPU intentionally absent (workload is CPU-bound per docs/ai-production.md).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verified file:line: the live GDScript events modules have NO era-based max_tier
cap (0 hits) — headless flat max_tier=10 is correct parity; an era cap would
invent a rule the game lacks (gold-plating, dropped). And natural events already
fire + apply terrain effects headless; only the fired list surfacing to
TurnResult is missing (processor.rs:1117 `let _fired =`), an observability nicety
not a system gap. Confirms the headless natural-events system is functionally
complete; narrows Gap-2's real remainder.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The live GDScript turn emitted `unit_healed` inline; the headless healing
phase recovered HP silently. The healing phase runs in the end-of-turn
`fn(&mut GameState)` registry (no event sink), so follow the FloraSuccession
buffer pattern: stash `(player, unit_id, applied_amount, col, row)` into a new
transient `GameState.pending_heal_events`, drain it in `step()` into
`TurnEvent::UnitHealed`. The buffered amount is the CLAMPED delta actually
applied (not the nominal heal rate). No wire surface — dispatch drops it; the
live UI consumes it via the kind-tagged `event_to_dict` dict.
Verified headless: mc-replay 19/0 (unit_healed_serde), mc-turn 289/0
(healing_buffers_unit_heal_event_with_applied_amount +
healing_buffers_clamped_amount_near_full_hp + event_collector_wiring).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add tools/check-no-gdscript-sim-logic.py and wire it as verify step 18 (TOTAL
20→21). Fails if presentation GDScript (src/game/engine/src/**/*.gd) re-introduces
catalog yield aggregation (`yield_production += …`) or hand-built spec dicts
(`"yield_production": …`) — the exact drift class just moved to Rust. Verified to
flag the pre-7e2baa25d aggregation and pass clean on the current tree. Logic
belongs in the mc-* crates, reached via the GDExtension bridge (Rail 1).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Game opening becomes a moddable JSON script driven by mc_worldsim::StartScriptRunner
and exposed to Godot via GdStartScript. Start scripts + dwarf tribe/wanderer units
live in public/resources/start_scripts; START_SCRIPTS.md documents the contract.
Adds tools/validate-start-scripts.py + wires it into CI (stage 3b) and verify.sh
(step 0b). Marks p3-14 done and regenerates the objectives dashboard.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add tools/check-ui-color-sources.py: fails if a hardcoded numeric Color()/Color8()
is applied to a widget in a scene (add_theme_*_override / StyleBox *_color).
Allows computed Color(accent.r,…), transparent, named constants, and var-init
fallbacks; excludes scenes/tests + the 3 precursor deletion files. Passes clean
on live scenes (exit 0). Wired into ./run verify as step 17 so a hardcoded
colour can't creep back in.
Capstone for the override→inheritance / single-colour-system work: colours in
live scenes now provably come from the design-token source.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add scripts/dev-setup/osx.sh for one-command macOS dev environment setup
- Add .godot/extension_list.cfg creation to enable GDExtension discovery
- Fix .gdextension to include macos.debug and macos.arm64 library entries
- Replace bare class_name self-references (e.g. BiomeModel.new()) with new()
in static methods — fixes compilation on cold boots without import cache
- Replace bare class_name type annotations (GameMap, Unit) with RefCounted/Variant
in pathfinder.gd, ai_turn_bridge.gd, simple_heuristic_ai.gd, rust_fauna_bridge.gd
- Add headless boot check (step 7) to ./run verify pipeline
- Add offscreen screenshot tool via SubViewport
- Wire ./run setup to dispatch to platform-specific scripts
- Source ~/.cargo/env in common.sh for Rust toolchain discovery
- Allow clippy::result_large_err in api-gdext (godot-rust macro generates large Results)
- Update .npmrc registry from Verdaccio to Forgejo and regenerate pnpm-lock.yaml
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>