docs: Campaign LA session handoff — state, in-flight recovery, process, goal

Self-contained handoff for a fresh session: what the campaign is and which
decisions are settled, the slice ledger with commits, the in-flight slices
and how to recover them from git, the two owed merge items (cross-assembly
contract test, Launcher.Core CI lane), the session landmines (index-sweep,
stale agent worktrees, contract-in-prompts), the binding process, and the
goal text to set.

Committed via pathspec so a live implementer agent index in this worktree
is untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-14 16:30:38 +02:00
parent 498f1c1182
commit c9fc7f4a66

View file

@ -0,0 +1,202 @@
# Campaign LA — session handoff (2026-08-14)
**Read this first, then `docs/plans/2026-08-14-launcher-campaign.md` (the plan +
ledger), then `docs/superpowers/specs/2026-08-14-launcher-campaign-design.md`
(the approved design).** Memory crib:
`claude-memory/project_launcher_direction.md`.
Branch: `claude/acdream-launcher-credentials-4d2f7c`
Worktree: `.claude/worktrees/acdream-launcher-credentials-4d2f7c`
HEAD at handoff: `498f1c11`
---
## 1. What Campaign LA is
One external product — the **acdream launcher** — that is simultaneously the
installer, the updater, and the multi-server / multi-account / multi-character
session launcher (ThwargLauncher UX model), on **Windows and Linux**; plus the
one client-side feature the launcher flow exposes as missing, the **retail
character-management screen**.
Design decisions already made and NOT to be re-litigated (spec §2):
- Avalonia UI; `AcDream.Launcher` (thin) + `AcDream.Launcher.Core` (BCL-only).
- **Credentials in a plaintext file — user-decided.** 0600 on Linux; never in
logs, arguments, session configs, or the status stream.
- **Approach A, file-contract orchestrator:** the launcher speaks NO game
protocol. Config file in → password via child stdin → JSONL status events
out. (Launcher embedding Runtime was REJECTED: a probe login that fails to
tear down gracefully poisons the ACE account ~3 min.)
- Full CRUD in the launcher UI; hand-editing JSON is never required.
- Character enumeration by **headless probe** (connect → CharacterList →
graceful disconnect BEFORE EnterWorld → exit) plus cache-from-observation.
- **Retail char-select has NO 3D preview** — recon-corrected. Retail's
`gmCharacterManagementUI` is a flat listbox + Enter/Delete/Restore + dialogs;
the rotating-model viewport is character-CREATION-only. Create Character is a
future campaign.
- Everything (launch + install + update) in ONE campaign.
- **Linux posture (user-directed):** the full launcher stack ships Linux-tested
in this campaign; GUI *client* launches stay Windows-only until Slice L
resumes later. The launcher renders gui/guiSelect disabled on Linux with an
explicit Slice-L note.
---
## 2. Slice ledger at handoff
| Slice | State | Commits |
|---|---|---|
| LA0 `AcDream.Platform` extraction | **DONE** (review closed) | `cb6502c8`, `a49e92df`, `7a839cba` |
| LA1 launch contract (App CLI + status writer + roster seam) | implemented; **Opus review returned FIX-FIRST**; fix round IN FLIGHT | `db9ad53c` (MIXED — see §4), note `e1322a06` |
| LA2 probe mode + idle policy | implementer IN FLIGHT | branch `campaign-la2` (base `498f1c11`) |
| LA3 `AcDream.Launcher.Core` | implemented; review FIX-FIRST (12 findings); **fix round LANDED — all 12 fixed, 94/94 Windows + WSL**; owes narrow re-review, then merge | `37d74e44`, `26feba81` on branch `campaign-la3` |
| LA7a character wire messages | **DONE + MERGED** | `6a32f375`, `4338b1c1`, `0c8643a7`, merge `fa2de1c4` |
| LA4 Avalonia UI | not started (needs LA3) | — |
| LA5 plugin hosting | not started (needs LA1) | — |
| LA6 login commands | not started (needs LA1, LA5) | — |
| LA7b char-select state + flow | not started (needs LA1) | — |
| LA8 authored char-select screen | not started (needs LA7b) | — |
| LA9 installer / LA10 updater / LA11 closeout | not started | — |
Register: **AD-97** filed (guid-only CharacterRestore request is an adaptation —
retail sends ≥16 bytes, we send 8; ACE ignores the tail).
---
## 3. Work IN FLIGHT at handoff — recover these first
Three agents were running when this handoff was written. Their results arrive
as task notifications in the ORIGINAL session only; a new session must verify
state from git instead of waiting.
1. **LA1 fix round** — main worktree, branch
`claude/acdream-launcher-credentials-4d2f7c`. Findings: F1 (HIGH, required)
the `SessionStatusWriter` must never throw into the login/teardown
transactions and must create its parent directory (an unwritable/missing
status path currently fails a healthy session — first-run trigger);
F2 App reader must TOLERATE `process.paths` (parse-and-ignore, like the
existing `policy`) and explicitly REFUSE `mode: "probe"` with a named error;
F4 production-shape the shared fixture (`process.content`, `standardInput`
credential); F3 reconnect emits `disconnected` first + record the mid-play
drop limitation; F5F8 minor hardening.
2. **LA2 implementer** — worktree `.claude/worktrees/acdream-la2`, branch
`campaign-la2`.
3. **LA3 fix round — COMPLETE at `26feba81`** (worktree
`.claude/worktrees/acdream-la3`, branch `campaign-la3`). All 12 findings
fixed: the CRITICAL `"paths": {}` emission (now omitted entirely), probe
composition (`ComposeProbe` + `mode` field), graceful stop (Linux SIGINT via
`libc kill`, Windows gap filed as **ISSUES #397** with the
CREATE_NEW_PROCESS_GROUP + CTRL_BREAK direction), 0600 temp-file window,
the Launcher.Core dependency-boundary guard, non-throwing parser/tailer,
monotonic supervisor state, `0x`-prefix id parsing, uint
`SecondsGreyedOut`, and `MalformedStatusEvent`. 94/94 Windows AND WSL.
**NEXT: narrow re-review of `26feba81`, then merge `campaign-la3`** (with
the two owed merge items below).
**To recover:** `git -C <worktree> log --oneline -3` and `git status` per
branch. If a fix round committed, run its narrow re-review; if it did not,
re-dispatch it from the finding list above (the reviews' full text is in the
original session transcript, but the finding summaries here are sufficient to
re-derive the work).
**Owed at merge time (do not lose these):**
- **Cross-assembly contract test** when LA1+LA3 meet: feed an
`AcDream.Launcher.Core` composer-produced document to BOTH host loaders
(App + Headless) and assert it parses. This is the permanent anti-drift
enforcement for the pinned contract.
- **CI lane**: add `tests/AcDream.Launcher.Core.Tests` to
`.github/workflows/headless-portability.yml` (both `paths:` filters + the
Linux test array), mirroring what LA0 did for `AcDream.Platform.Tests`.
- After LA2 lands, App's reader must refuse `mode: "probe"` (covered by LA1
fix-round F2 — verify it actually landed).
---
## 4. Landmines / lessons from this session
1. **Never run git state commands in a worktree while an implementer agent is
live in it.** `git add <path>` scopes the ADD; `git commit` commits the whole
INDEX. A docs commit swept 37 in-progress LA1 files into `db9ad53c`; the
marker commit `e1322a06` documents it. Memory:
`claude-memory/feedback_no_commits_beside_live_agents.md`.
2. **Auto-created agent worktrees can be based on stale history.** The first LA3
dispatch landed on a spell-bar-era commit. Always create the worktree
yourself from the campaign HEAD and make the agent verify its base commit as
its first action.
3. **PowerShell 5.1 mangles double quotes inside heredoc commit messages**
keep git commit bodies quote-free.
4. **The pinned contract must live on disk, not in agent prompts.** It now does
(plan §"Pinned launch-contract schema (v1, BINDING)"). The LA3 CRITICAL was
a direct consequence of it living only in prompts.
5. **Reviews have caught something tests could not, four slices running:** lost
Linux CI lanes (LA0), a real-but-mislabeled wire deviation (LA7a → AD-97), a
cross-worktree contract break (LA3), an observability sink that could fail
the transaction it observes (LA1). Do not downgrade the review step.
---
## 5. How we work (binding process)
- **Fable plans, sequences, integrates. Sonnet implements bounded slices. Opus
reviews every slice boundary, dual-lens:** (a) architectural — ownership,
layering, dependency-guard integrity, seams; (b) retail fidelity against
`docs/research/named-retail/` wherever the slice touches retail behavior.
Findings → fix round → NARROW re-review of the fixes → slice DONE in ledger.
- Max 34 agents in parallel INCLUDING children; subagents never spawn
subagents. Every implementer prompt carries: spec+plan paths, files to read
first, the pinned contract text if relevant, acceptance criteria, commit
style, and a base-commit verification as its first action.
- One implementer per worktree; that agent owns the worktree's git index.
- `dotnet build` + `dotnet test` green before a slice is DONE; ≥1 commit per
slice tagged `Campaign LA`; every retail deviation adds its
`docs/architecture/retail-divergence-register.md` row in the same commit;
**no workarounds without explicit user approval**.
- Linux: every slice touching Launcher.Core/Headless/Runtime/Bake/Platform runs
its test projects under WSL or native Ubuntu before it is DONE.
- The ONLY stop-and-wait is a user connected/visual gate. Everything else is
Claude's call — never present the user a work-order menu.
- Keep the plan ledger, `docs/plans/2026-04-11-roadmap.md`, the CLAUDE.md
Current-state pointer, and `claude-memory/` current as slices land.
---
## 6. The goal to set
Set this with `/goal` in the new session (it is the same directive this session
ran under, refreshed for the current state):
```text
GOAL: Ship Campaign LA — the acdream launcher/installer/updater + retail character-select screen.
Start at docs/research/2026-08-14-campaign-la-handoff.md, then the ledger in
docs/plans/2026-08-14-launcher-campaign.md. Recover the three in-flight slices
first (LA1 fix round on the campaign branch, LA2 on campaign-la2, LA3 fix round
on campaign-la3) by inspecting git state, then continue slice by slice.
Process, per slice:
1. Fable plans/sequences/integrates — never present work-order menus; pick and announce.
2. Sonnet subagents implement bounded slices. Each prompt carries spec+plan paths, the
exact files to read first, the pinned contract text when relevant, acceptance criteria
(build+test green), commit style, and a base-commit verification as first action.
Subagents may not spawn subagents. Hard cap 3-4 agents in parallel including children.
One implementer per worktree; that agent owns the index — the orchestrator makes no git
state changes in a worktree while an agent is live in it.
3. Every slice boundary gets an Opus dual-lens review: (a) architectural — ownership,
layering, dependency-guard integrity, seams; (b) retail fidelity against
docs/research/named-retail/ wherever the slice touches retail behavior. Fix findings,
then a narrow re-review of the fixes.
4. dotnet build + dotnet test green before any slice is declared done; Linux (WSL or
native Ubuntu) runs for every slice touching Launcher.Core/Headless/Runtime/Bake/
Platform. One commit per slice minimum, tagged "Campaign LA". Any retail deviation adds
its divergence-register row in the same commit. No workarounds without explicit approval.
5. As slices land: update the plan ledger, docs/plans/2026-04-11-roadmap.md, the CLAUDE.md
Current state pointer, and claude-memory/.
6. The only stop-and-wait is a user connected/visual gate (launch modes vs local ACE,
character-probe round-trip, char-select visual matrix, first-run wizard, update swap).
When a slice reaches one: write the exact test script under docs/research/, announce the
gate, and keep driving any slices not blocked behind it.
DONE = all slices code-complete, automated gates green, dual reviews closed, and the
connected-gate checklist delivered to the user in one script document.
```