docs(@projects/@magic-civilization): 🔎 p3-26 Gap-2 — era max_tier cap is non-parity; fired-event surfacing is observability-only

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>
This commit is contained in:
Natalie 2026-06-27 06:29:41 -04:00
parent ac5efa4bec
commit cbc68a68c1
4 changed files with 107 additions and 0 deletions

View file

@ -39,6 +39,15 @@ expansion, tech/science, fauna encounters, combat/siege, diplomacy. Verified liv
gap 1), global (solar/glacial). Deterministic from seed per `EVENT_FREQUENCY_SPEC.md`; gap 1), global (solar/glacial). Deterministic from seed per `EVENT_FREQUENCY_SPEC.md`;
triggers from climate (gap 1), damage targets in biology (fauna/ecology). Wire into the triggers from climate (gap 1), damage targets in biology (fauna/ecology). Wire into the
turn. (Magic category excluded — M4.) turn. (Magic category excluded — M4.)
- **VERIFIED 2026-06-27 — two listed "remaining" sub-items reassessed:**
(a) **era-based max_tier cap = NON-PARITY (gold-plating, dropped).** The live GDScript events
modules have NO `max_tier`/era-tier cap (0 hits in `src/game/engine/src/modules/events/`).
Headless uses `max_tier=10` (processor.rs:1117); adding an era cap would invent a rule the live
game lacks. Leave flat unless a design adds the cap to the game first.
(b) **surfacing fired events = minor OBSERVABILITY gap only.** Events already fire AND apply
terrain effects headless; `process_events` returns the fired list but `step()` discards it
(`let _fired =`, processor.rs:1117). The SYSTEM runs in self-play; only replay/observation
visibility is missing. Low priority (parallels the p3-29 §A event surface, render-gated payoff).
- [ ] **Gap 3 — Equipment / crafting.** `mc-city/recipes.rs` + `enqueue_item` exist but - [ ] **Gap 3 — Equipment / crafting.** `mc-city/recipes.rs` + `enqueue_item` exist but
there is no headless `Craft`/`Equip` `PlayerAction` and crafting isn't in the bench there is no headless `Craft`/`Equip` `PlayerAction` and crafting isn't in the bench
turn. Add the action(s) + dispatch + wire recipe resolution (gating resources, quality) turn. Add the action(s) + dispatch + wire recipe resolution (gating resources, quality)

View file

@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAITESTFIXTUREKEYdummy0000000000000000000000 test-fixture

View file

@ -0,0 +1,79 @@
# No-spend test harness for the fleet module.
# terraform test (from the module dir)
# Uses a MOCKED hcloud provider no API token, no API calls, no servers, no cost.
# Exercises count expansion, the golden-image branch toggle, and the workers guardrail.
mock_provider "hcloud" {}
variables {
hcloud_token = "mock-token-unused"
git_remote = "https://example.com/magic-civilization.git"
ssh_public_key_path = "./tests/fixtures/id_test.pub"
}
# base_image set -> golden data source is skipped (count 0); fleet expands to N.
run "fleet_expands_and_skips_golden_when_base_image_set" {
command = plan
variables {
workers = 3
base_image = "ubuntu-24.04"
}
assert {
condition = length(hcloud_server.worker) == 3
error_message = "expected 3 workers when workers=3"
}
assert {
condition = length(data.hcloud_image.golden) == 0
error_message = "golden data source must be skipped when base_image is set"
}
}
# base_image empty -> golden snapshot is resolved via the label selector.
run "golden_image_branch_active_when_base_image_empty" {
command = plan
variables {
workers = 2
base_image = ""
}
assert {
condition = length(data.hcloud_image.golden) == 1
error_message = "golden data source must be queried when base_image is empty"
}
assert {
condition = length(hcloud_server.worker) == 2
error_message = "expected 2 workers when workers=2"
}
}
# workers = 0 -> zero servers (idle / torn-down state).
run "zero_workers_is_empty_fleet" {
command = plan
variables {
workers = 0
base_image = "ubuntu-24.04"
}
assert {
condition = length(hcloud_server.worker) == 0
error_message = "workers=0 must produce no servers"
}
}
# The validation guardrail rejects an oversize fleet proven without provisioning.
run "rejects_oversize_fleet" {
command = plan
variables {
workers = 99
base_image = "ubuntu-24.04"
}
expect_failures = [var.workers]
}

View file

@ -30,6 +30,7 @@ _dist_read_hosts() {
cmd_dist() { cmd_dist() {
cat <<'EOF' cat <<'EOF'
Distributed test/train fleet (Hetzner). Set TF_VAR_hcloud_token first. Distributed test/train fleet (Hetzner). Set TF_VAR_hcloud_token first.
./run dist:check offline: fmt + validate + mocked test (no token/spend)
./run dist:up <workers> [server_type] [location] e.g. ./run dist:up 10 ./run dist:up <workers> [server_type] [location] e.g. ./run dist:up 10
./run dist:sim <games> [turn_limit] [--destroy-after] ./run dist:sim <games> [turn_limit] [--destroy-after]
./run dist:train <total_steps> [--destroy-after] ./run dist:train <total_steps> [--destroy-after]
@ -37,6 +38,23 @@ Distributed test/train fleet (Hetzner). Set TF_VAR_hcloud_token first.
EOF EOF
} }
cmd_dist_check() {
# Offline IaC verification — no Hetzner token, no API, no servers, no cost.
# fmt (style) + validate (schema typecheck) + test (mocked-provider behaviour).
local root
root="$(_dist_repo_root)"
local dir="$root/$_DIST_TF_DIR_REL"
echo "== terraform fmt =="
terraform -chdir="$dir" fmt -check -recursive || { echo "fmt: run 'terraform -chdir=$dir fmt'" >&2; return 1; }
echo "== terraform init (providers only) =="
terraform -chdir="$dir" init -backend=false -input=false >/dev/null || return 1
echo "== terraform validate (schema typecheck) =="
terraform -chdir="$dir" validate || return 1
echo "== terraform test (mocked hcloud) =="
terraform -chdir="$dir" test || return 1
echo "dist:check OK — config is valid, no resources touched."
}
cmd_dist_up() { cmd_dist_up() {
local n="${1:-}" local n="${1:-}"
[[ "$n" =~ ^[0-9]+$ ]] || { echo "usage: ./run dist:up <workers> [server_type] [location]" >&2; return 1; } [[ "$n" =~ ^[0-9]+$ ]] || { echo "usage: ./run dist:up <workers> [server_type] [location]" >&2; return 1; }