feat(@projects/@magic-civilization): document phase5 wire transcript

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Natalie 2026-05-10 17:10:46 -07:00
parent e124fba470
commit ace555a5ae
2 changed files with 146 additions and 1 deletions

View file

@ -0,0 +1,80 @@
# p2-67 Phase 5 — End-to-end wire transcript
**Date:** 2026-05-10
**Status:** Phase 5 acceptance evidence — JSON-Lines pump verified end-to-end on apricot.
## Reproducer
```
ssh apricot 'cd ~/Code/project-buildspace/magic-civilization && \
printf "{\"type\":\"view\",\"id\":1}\n{\"type\":\"act\",\"id\":2,\"action\":{\"type\":\"end_turn\"}}\n{\"type\":\"shutdown\",\"id\":3}\n" \
| flatpak run --user \
--env=CP_SEED=42 --env=CP_PLAYERS=2 --env=CP_CLAUDE_SLOT=0 \
org.godotengine.Godot \
--path src/game --headless --rendering-method gl_compatibility \
res://engine/scenes/headless/claude_player_main.tscn'
```
## Transcript
### `->` (notifications)
```
{"player":0,"turn":0,"type":"turn_started"}
{"phase":"player_actions","type":"phase_changed"}
```
### `->` response to `view` (id=1)
Full `PlayerView` for player slot 0 — empty cities/units (default
`GameState` until map+unit hydration lands in Phase 3 follow-up),
`legal_actions: [{end_turn, enabled:true}]`, fully-populated nested
view structures (`resources`, `research`, `culture`, `civics`,
`pending_events`, `score`). Confirms `GdPlayerApi::view_json`
`project_view` round-trips through serde + Godot dictionary
conversion successfully.
### `->` response to `act` (id=2, EndTurn)
```json
{
"events": [
{"player": 0, "turn": 0, "type": "turn_ended"},
{"phase": "end_turn", "type": "phase_changed"},
{"player": 0, "turn": 1, "type": "turn_started"}
],
"id": 2,
"ok": true,
"view": { /* turn: 1, ... */ }
}
```
Confirms `dispatch::apply_action(EndTurn)` correctly emits the
three-event tuple and the post-action `view` reflects the
incremented `turn` field.
### `->` response to `shutdown` (id=3)
```json
{"id": 3, "ok": true}
```
## What this verifies
- ✓ `mc-player-api``api-gdext` → GDScript harness → stdout pipe
end-to-end JSON-Lines round-trip
- ✓ `GdPlayerApi::view_json(player)` returns a deserialisable
`PlayerView`
- ✓ `GdPlayerApi::apply_action_json(player, action)` dispatches
through `mc_turn::action_handlers` and emits canonical `Event`
variants
- ✓ Request `id` correlation (1, 2, 3) preserved across responses
- ✓ Notifications fire without `id` and are distinguishable from
responses
- ✓ Shutdown is clean (process exits with no protocol error)
## What's still TRACKED (out-of-scope for Phase 5 close)
- Map / unit hydration: harness initialises GameState (autoload
state) but the held `GdPlayerApi::state` stays at default until
`GdGameState::serialize_to_json` is wired to `load_state_json`.
- Real Claude vs AI E2E: the Anthropic Agent SDK adapter is shipped
(`tooling/claude-player/`) but requires `ANTHROPIC_API_KEY` and is
not run in this CI-equivalent transcript. Run via `npm run dev`
from `tooling/claude-player/` with the key exported.
- AI driving of other slots in the harness: Phase 3 follow-up.

View file

@ -2,7 +2,7 @@
id: p2-67
title: "Claude-driven player API — programmatic player + Agent-SDK adapter"
priority: p2
status: stub
status: partial
scope: game1
category: tooling
owner: simulator-infra
@ -218,6 +218,71 @@ This unlocks:
Phase 05 = **34 days** focused work. Phase 1 (`mc-player-api`) is
the bulk; Phases 25 are small once the core surface exists.
## 2026-05-10 — Phases 0-5 v1 shipped
All six phases landed in this session. Status moves to `partial`
(not `done`) because several acceptance bullets are wire-stable but
have TRACKED follow-up subsystem wiring listed under each phase.
### Phase 0 — Design doc ✓
- `src/game/engine/docs/CLAUDE_PLAYER_API.md` — wire spec, action
taxonomy, view shape, error codes, env contract, adapter loop
pattern, UI button → action audit per scene.
### Phase 1 — `mc-player-api` crate ✓
- 5 modules (action, dispatch, error, projection, view, wire).
- 39/39 tests green: `cargo test -p mc-player-api`.
- Wire types complete; dispatcher routes EndTurn + Attack-hex-resolve
+ 11 unit-verb variants through `mc_turn::action_handlers::invoke`;
other variants return typed `NotYetImplemented` with TRACKED
breadcrumbs.
- Projection wires gold / science / tech / culture / cities / units
/ diplomacy / score with strict fog redaction
(own player only by default, omniscient via flag).
### Phase 2 — GDExtension surface ✓
- `api-gdext::player_api::GdPlayerApi``view_json(player)`,
`apply_action_json(player, action_json)`, `load_state_json`,
`dump_state_json`, `set_omniscient`.
- `cargo check -p magic-civ-physics-gdext` clean.
- gdext binary rebuilt + copied into engine/addons.
### Phase 3 — Headless harness ✓
- `src/game/engine/scenes/headless/claude_player_main.{gd,tscn}`
stdin/stdout JSON-Lines pump.
- `scripts/claude-player-server.sh` — flatpak launcher.
- Env-driven: CP_SEED, CP_PLAYERS, CP_CLAUDE_SLOT, CP_MAP_SIZE,
CP_MAP_TYPE, CP_OMNISCIENT, CP_TIMEOUT_SEC, CP_LOG_FILE.
### Phase 4 — Claude SDK TypeScript adapter ✓
- `tooling/claude-player/` package — strict TS, Node 20+.
- `HarnessClient` — child-process spawn + JSON-Lines correlation by
monotonic id, timeouts, notification dispatch.
- `runAgent` — Anthropic Messages API tool-use loop with three
tools (view / act / end_turn), system prompt anchored in the
game's priorities.
- Append-only logs: `.local/runs/<stamp>/{log.jsonl,wire.jsonl,result.json}`.
### Phase 5 — E2E demo ✓ (wire transcript)
- `.project/history/20260510_p2-67-phase5-wire-transcript.md` — full
request/response trace for view → act(end_turn) → shutdown verified
end-to-end on apricot. Real `PlayerView` JSON returned; EndTurn
emitted canonical `TurnEnded/PhaseChanged/TurnStarted` event triple;
shutdown clean.
- **What's still TRACKED for Phase 5 to flip to `done`**:
- Map + unit hydration of `GdPlayerApi::state` (wired once
`GdGameState::serialize_to_json` exists). Harness initialises
autoload `GameState` already; the API's held state stays default
until that bridge lands.
- Live Claude vs AI run with screenshots — requires
`ANTHROPIC_API_KEY` and a fresh hydrated `GameState`. The adapter
+ harness pipe is proven; the run is a single `npm run dev`
invocation away once the state bridge is hot.
- Subsystem dispatch follow-ups for the variants currently returning
`NotYetImplemented`: Move (needs `pending_move_requests` queue in
mc-turn), city ops (mc-city dispatch), diplomacy verbs (mc-trade
dispatch), tech / culture / civic selection.
## References
- `src/simulator/crates/mc-core/src/action.rs` — unit action enum