docs: Campaign LA plan — slices LA0-LA11, recon-grounded; spec corrections

Plan doc with twelve slices, dependencies, review protocol (Opus
dual-lens: architectural + retail-faithful), and ledger. Three parallel
recon reports grounded the slice bodies:

- Retail select screen is gmCharacterManagementUI: flat listbox +
  Enter/Delete/Restore + dialogs. NO 3D preview (that machinery is
  chargen-only gmCG3DView) — the spec 3D-preview slice is deleted, the
  old retail-ui/05-panels.md pedestal claim is uncited and wrong.
  Restore + CharacterError join scope; delete sends account+slot.
- Chat-command core (parser/router/catalog/ChatVM) is dependency-clean
  BCL+Core; extraction to Runtime is a move, not a rewrite.
- Probe reuses the NoCharacters early-exit shape (graceful teardown at
  the CharacterList stage exists today); roster plumbing is new.
- Bake tool needs --progress-json + explicit --out; no whole-file SHA
  exists — launcher records/verifies its own.
- UI Studio is deleted (Campaign V) — stale references corrected.

Roadmap + CLAUDE.md Current state carry the campaign pointer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-14 15:10:39 +02:00
parent afb4e82943
commit 9b0bb25581
4 changed files with 484 additions and 26 deletions

View file

@ -40,7 +40,7 @@ Decided this session (2026-08-14):
| Question | Decision |
|---|---|
| Headless launch purpose | **Run plugins** (VirindiTank-style) + login commands; same on GUI. Launcher selects which plugins per character |
| Character-select screen | **Retail screen, no Create.** 3D rotating preview + Enter World + Delete; Create Character deferred to its own campaign |
| Character-select screen | **Retail screen, no Create.** CORRECTED by 2026-08-14 recon: retail's `gmCharacterManagementUI` is a flat listbox + Enter Game / Delete / Restore buttons + dialogs — **no 3D preview exists on retail's select screen** (that machinery, `gmCG3DView`, is character-CREATION-only). We port what retail actually had; Create deferred to its own campaign |
| Campaign scope | **Everything now** — launch flows + first-run install + update pipeline in one campaign |
| Launcher ↔ client coupling | **Approach A: file-contract orchestrator** (config in, status events out; no game-protocol code in the launcher) |
| Update feed | **GitHub Releases** (manifest.json + per-RID zips as release assets) |
@ -211,23 +211,50 @@ enters immediately. New behavior: **no character selector in options →
stop at the retail character-select screen.** Selection there feeds the
same `EnterWorld` path.
- **Ownership:** J-owner pattern. A Runtime-owned selection state (roster,
highlighted entry, pending-delete confirmation) with typed commands
(highlight / enter / delete-request / delete-confirm); App projects the
authored screen. Headless never uses it (config always carries a
selector; the loader already requires one).
- **UI:** imported retail screen via `LayoutImporter` (OP3/FA recipe).
The concrete LayoutDesc id and widget tree come from the
grep-named-first workflow + `docs/research/retail-ui/` during the plan;
retail decomp is the behavior oracle for list interaction, Enter World,
and Delete (including retail's delete confirmation flow — ACE serves
the character-delete message).
- **3D preview:** a void-scene render path — the selected character's
Setup + ObjDesc appearance rendered with a dedicated camera/lighting,
reusing the existing creature-appearance pipeline outside a landblock.
Own slice; the retail screen's rotation behavior is the oracle.
- **Non-goals:** Create Character (own future campaign); a login screen
("back" exits the client — credentials always arrive via config/env).
**CORRECTED 2026-08-14 (named-retail recon):** retail's screen is
`gmCharacterManagementUI` (`acclient.h:56545`) — a `UIElement_ListBox`
character list plus Create / Enter Game / Delete / Restore buttons and
dialog contexts (delete-confirm, please-wait, entering-world, error).
**It has NO 3D preview** — the rotating-model viewport (`gmCG3DView` /
`UIElement_Viewport` / `CreatureMode`) exists only on character
CREATION's appearance/heritage/profession pages. The earlier
"3D preview on a pedestal" belief traced to one uncited line in
`docs/research/retail-ui/05-panels.md` §13. We port the real screen; a
preview would be a deliberate divergence we are NOT taking.
- **Ownership:** J-owner pattern. A Runtime-owned selection state (roster
incl. greyed/pending-delete seconds, highlighted entry, pending-delete
confirmation) with typed commands (highlight / enter / delete-request /
delete-confirm / restore); App projects the authored screen. Headless
never uses it (config always carries a selector; the loader already
requires one).
- **UI:** imported retail screen. The root layout id is resolved
indirectly — retail calls
`UIMainFramework::CreateAndAddRootElement(0x10000005, 0x1000039a)` and
resolves the concrete DataID via `DBObj::GetDIDByEnum(..., 5)` (the same
GetDIDByEnum machinery OP8 already ported for key names, category 4).
Child widget ids from the decomp: listbox `0x1000039d`, create
`0x100003a0` (hidden/no-op this campaign), enter `0x100003a2`, delete
`0x1000039f`, restore `0x1000039e`. Behavior oracles:
`RebuildCharacterList@0x004ec3a0`, `SelectCharacter@0x004ec160`,
`UpdateButtons@0x004ec240` (Delete↔Restore visibility swap on greyed
state), `EnterGame@0x004ed440`,
`MakeDeleteCharacterConfirmationDialog@0x004ecca0`.
- **Wire (new messages):** `CharacterDelete` 0xF655 (outbound: account
String16L + **slot index**, per `Proto_UI::SendDeleteCharacter@0x00546b30`
— NOT the guid; inbound: opcode-only ack, then a fresh CharacterList),
`CharacterRestore` 0xF7D9 (guid) with response 0xF643, and a
`CharacterError` 0xF659 parser (currently absent — acdream cannot
surface any character-stage server error today). Enter-world's
two-phase handshake (0xF7C8 → 0xF7DF → 0xF657) is already implemented.
- **Open items** carried to the plan: the concrete layout DataID (dump
enum-table 5 from installed DATs), and whether retail rendered any
render-loop-level background scene behind the UI (the pseudo-C only
proves the UI class owns no viewport) — both resolved in the screen
slice before the user visual gate.
- **Non-goals:** Create Character (own future campaign; the Create button
exists on the authored screen but is disabled); a login screen ("back"
exits the client — credentials always arrive via config/env).
- Retail-workflow rules apply: any behavioral deviation ships with its
divergence-register row in the same commit.
@ -284,7 +311,8 @@ same `EnterWorld` path.
`RuntimeOptions`, stdin credential resolution, status-event writer
output shape, plugin-set narrowing, login-command execution order.
- **Character-select:** Runtime selection-state tests; authored screen
via UI Studio dumps/screenshots; behavior against retail oracle.
exercised by focused App tests (UI Studio no longer exists — deleted at
Campaign V); visuals settle at the user gate.
- **Headless plugin host:** fixture plugin in the Headless suite
(load, capability flag, teardown).
- **Connected gates (user-driven):** every launch mode against local ACE
@ -301,12 +329,22 @@ same `EnterWorld` path.
after reading CH seams; do not regress CH-accepted chat behavior.
2. Current App plugin-loading behavior (what loads today, when) — read
before wiring the session-driven set.
3. Retail character-select LayoutDesc id + widget tree — research task
(grep-named + `docs/research/retail-ui/`).
4. Character-delete wire message + ACE handling — verify against
holtburger/ACE before implementing the Delete flow.
5. Void-scene preview: lighting/camera parameters need the retail oracle
(what does retail actually render behind the character?).
3. RESOLVED by recon except the concrete root-layout DataID: widget tree +
behavior symbols are in §7; the DataID hides behind `GetDIDByEnum`
enum-table 5 (enum `0x10000005`) — dump the table from installed DATs
in the screen slice.
4. RESOLVED by recon: delete = 0xF655 account+slot (ack opcode-only, then
fresh CharacterList); restore = 0xF7D9 guid → 0xF643 response; ACE's
`secondsSincePendingDelete` computes to a constant 1 during the grace
window (ACE quirk — treat any non-zero as "pending delete", don't
display it as a countdown). One decomp ambiguity (whether retail's
restore carries two extra strings) is noted as a likely decompiler
artifact — ACE + holtburger agree on guid-only; follow them and note
the register row is NOT needed (wire consensus, no behavior change).
5. Whether retail rendered a render-loop-level background scene behind the
character-management UI — pseudo-C only proves the UI class owns no
viewport. Resolve in the screen slice (user memory of retail + the
visual gate settle it).
6. Bake tool 4 invocation surface from Launcher.Core (in-process reference
vs child process) — child process preferred to keep Launcher.Core free
of game-solution references; confirm the tool's CLI is sufficient.