docs(objectives): revise p2-65 bullet-9 + p2-72a array-removal resume notes (stale-premise audit)

p2-65 (stays partial K=8/9): rewrite Phase-7 resume note. The 'no-persistent-
GdGameState / discards combat_balance' premise is STALE — game_state.gd:269
loads combat_balance.json into the persistent _gd_state singleton via
GdGameState::set_combat_balance_json (api-gdext/src/lib.rs:3541), and the driver
objective p2-55f is already done with its bullet 2 marked [x]. Bullet 9 must NOT
be closed on that path (it bypasses mc-state entirely — would be an
objective-integrity reframe). Remaining work is architectural consolidation
(mc_state::SimConfig owning global config), now an owner decision, not a
mechanical close-out.

p2-72a (stays partial): array-removal increment re-checked and NOT executed.
All three gate-feasibility blockers reproduce — cold Godot import cache, no
mc-godot Docker image (freeze-safe path unbuilt), operator-gated visual proof
unavailable this session. game_state.gd UNTOUCHED (npc_buildings count = 33).
Clean stop re-confirmed; predecessor's execution recipe unchanged.

Regen objectives index. No status frontmatter changed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
autocommit 2026-06-04 20:55:08 -07:00
parent f80609097b
commit 3a04052385
3 changed files with 89 additions and 26 deletions

View file

@ -1,5 +1,5 @@
{
"generated_at": "2026-06-05T03:47:56Z",
"generated_at": "2026-06-05T03:54:45Z",
"totals": {
"done": 243,
"in_progress": 1,
@ -3105,7 +3105,7 @@
"status": "partial",
"scope": "game1",
"owner": "simulator-infra",
"updated_at": "2026-05-12",
"updated_at": "2026-06-04",
"blocked_by": [],
"summary": "The bridge-cse lane did **not** execute the npc_buildings-array-removal increment. Reason: it is the highest-regression file in the lane (`game_state.gd`, touching the live world map which `project_standin_blockers_worldmap.md` already flags as fragile) and the increment requires the full `build-gdext.sh` + GUT + visual-verification cycle (screenshot tail, operator-gated). It was correctly the first-to-cut item under budget. Precise resume map for the next lane (verified by grep 2026-06-03):"
},

View file

@ -608,31 +608,68 @@ the objectives index. The concurrent design-system lane's files
`scenes/tests/ui_theme_proof.*`) were left modified/untracked and were NEVER
staged — verified at every commit.
## Phase 7 resume note (the ONLY remaining bullet — start here next session)
## Phase 7 resume note (the ONLY remaining bullet — OWNER DECISION; premise revised 2026-06-04 Wave-D-cont)
**Goal (bullet 9 / closes p2-55f bullet 2):** add `mc_state::SimConfig` carrying
`combat_balance` (8-field `mc_core::CombatBalance`) + room for future global
config. Wire it ONCE at api-gdext extension registration; have `GdGameState::init`
populate `self.inner.combat_balance` from it. Today every ephemeral `GdGameState`
instantiates a fresh default and discards `combat_balance` — this is the
no-persistent-`GdGameState` constraint that triggered the whole objective.
**Status of bullet 9: OPEN. K stays 8/9 → `partial`.** Do NOT close it by
pointing at the existing `set_combat_balance_json` path (see "stale premise"
below) — that path never touches mc-state and so cannot be cited as mc-state's
proof-of-value. Closing bullet 9 on it would be the objective-integrity §15
reframe forbidden by the rail. Bullet 9 is genuinely larger than a mechanical
close-out, and its original justification has changed — so this session
deliberately leaves it `partial` (the brief pre-authorized exactly this: "if the
last bullet is genuinely larger than a close-out, leave honest partial with a
precise note").
**Steps:**
**STALE PREMISE (the load-bearing 2026-06-04 finding — verified read-only):**
The earlier resume note claimed "every ephemeral `GdGameState` instantiates a
fresh default and discards `combat_balance` — the no-persistent-`GdGameState`
constraint that triggered the whole objective." **That constraint no longer
holds.** The persistent singleton EXISTS and the value IS delivered at boot:
- `src/game/engine/src/autoloads/game_state.gd:269`
`_load_combat_balance_into(_gd_state)` (the persistent `_gd_state` singleton,
not an ephemeral instance) → `:238 _gd_state.set_combat_balance_json(raw)`.
- `api-gdext/src/lib.rs:3541 fn set_combat_balance_json` parses via
`mc_turn::combat_balance::load_combat_balance` and writes
`self.inner.combat_balance = cb`.
- The DRIVER objective **p2-55f** is already `status: done`, and **its bullet 2
("DataLoader / GameState init reads `combat_balance.json` and populates the
field at game start") is marked `[x]`** with cited end-to-end evidence
(`p2-55f` line 23 + the passing test
`combat_balance_json_load_threads_nondefault_duration_into_game_state`).
So the *outcome* p2-65 bullet 9 was meant to unblock is already delivered, by a
path that routes api-gdext → mc_turn → mc_core and **bypasses mc-state entirely.**
**What bullet 9 actually still asks for (and why it's an owner decision):**
bullet 9 is p2-65's "proof-of-value that retroactively justifies the crate
split." Satisfying it on its own terms means making mc-state OWN the global
sim-config surface: introduce `mc_state::SimConfig { combat_balance:
mc_core::CombatBalance, … }`, route the boot load through it, and retire the
ad-hoc `set_combat_balance_json` setter in favour of the mc-state-owned config.
That is an **architectural consolidation of an already-working path**, not a
missing capability — a stale-premise refactor whose original urgency has
evaporated. It is the OWNER's call whether mc-state should own global config at
all now, or whether bullet 9 should be re-scoped/struck given the driver closed
independently. Do NOT unilaterally build `SimConfig` to satisfy a literal reading
of a resume note written under a premise that no longer holds; do NOT
unilaterally close the bullet either. Surface to the objective owner.
**If the owner elects to build it** (the original `SimConfig` design, now purely
for architectural consolidation, not to unblock anything):
1. New `mc-state/src/config.rs`: `pub struct SimConfig { pub combat_balance:
mc_core::CombatBalance, … }` + `Default`. Re-export `mc_state::SimConfig`.
2. api-gdext registration (`lib.rs` `#[gdextension]` init or a `GdSimConfig`
singleton): load `combat_balance.json` via DataLoader once, build `SimConfig`.
3. `GdGameState::init` (or the `GameState` constructor it calls) reads the
singleton and sets `self.inner.combat_balance`.
4. **Risk (from §Risk register):** if the singleton is a `OnceCell`, cargo-test
parallelism may fight it. Mitigate by giving `GameState` a
`with_combat_balance(cb)` constructor for tests and reserving the `OnceCell`
for the api-gdext path only.
2. api-gdext: load `combat_balance.json` once into `SimConfig`; have
`GdGameState` pull from it instead of the standalone setter.
3. Retire `set_combat_balance_json` (route through `SimConfig`) — full removal,
not a parallel path (Zero-Tech-Debt rail).
4. **Risk (§Risk register):** `OnceCell` vs cargo-test parallelism — give
`GameState` a `with_combat_balance(cb)` ctor for tests; reserve the
`OnceCell` for the api-gdext path only.
5. **Gate:** trio (`serde_roundtrip` + `full_turn_golden` + parity) +
`cargo build -p api-gdext` + an api-gdext test asserting a loaded
`combat_balance.json` reaches `state.combat_balance` THROUGH `SimConfig`.
Only then bullet 9 ✓ → flip `partial``done` and regen the index.
**Gate:** the same trio (`serde_roundtrip` + `full_turn_golden` + parity) +
`cargo build -p api-gdext` + a check that a loaded `combat_balance.json` actually
reaches `state.combat_balance` (assert in a new api-gdext test). When green and
bullet 9 ✓ → flip status `partial``done` and regen the index.
**Effort:** SM, ~half a session. Discrete design work — do NOT bundle with other
objectives. It is the proof-of-value that retroactively justifies the crate split.
**Effort:** SM, ~half a session of discrete design work — do NOT bundle with
other objectives. Owner-gated by the premise change above.

View file

@ -7,7 +7,7 @@ scope: game1
category: architecture
owner: simulator-infra
created: 2026-05-11
updated_at: 2026-05-12
updated_at: 2026-06-04
blocks: [p2-72a-save-format-migration, p2-72a, p2-72]
---
@ -461,3 +461,29 @@ All as ONE commit, then gate:
**Blocker for next session:** none technical — needs a session with runway to build the
Docker godot image + warm the import cache, and an operator present for the visual-proof
sign-off.
## 2026-06-04 Wave-D continuation — array-removal STILL not executed (gate unreachable; clean stop re-confirmed)
**Not advanced. `game_state.gd` UNTOUCHED. `grep -c npc_buildings game_state.gd` = 33
(unchanged). Status stays `partial`.** This lane owned the increment and re-checked
gate feasibility before touching the highest-regression live-world-map file. The three
2026-06-04-session-2 blockers all REPRODUCE this session (verified, not assumed):
1. **Godot import cache COLD**`src/game/.godot/` absent (verified `ls`). A direct
flatpak `godot --headless --import` on this RUN host carries the documented
host-freeze risk: `scripts/godot-docker.sh:4-7` states cold `--headless --import`
"kernel-panicked plum twice and freeze apricot when run cold." The safe path is the
Docker runner.
2. **No `mc-godot` Docker image built**`docker images` shows only unrelated
e2e/mailpit images; the freeze-safe `scripts/godot-docker.sh` path needs a heavy
cold image build + warm-import volume first (no `mc-src` warm volume exists).
3. **World-map visual proof is operator-gated** — phase-gate-protocol +
`feedback_phase_gate_screenshots` forbid self-approving the screenshot. No `$SCREENSHOT_HOST`
set and no operator present this session to sign off.
Per never-regress: a tree with readers re-routed + serialize re-pointed but the array
delete ungated (no GUT-green, no operator visual sign-off) is worse than not starting.
Clean stop re-confirmed — the predecessor's execution recipe (above, §"Precise execution
recipe for the next session") is still the correct, complete plan; it remains blocked
solely on (a) runway to build the Docker godot image + warm the import cache, and (b) an
operator present for the visual-proof sign-off. No code or recipe change needed.