Commit graph

3822 commits

Author SHA1 Message Date
Erik
267804465e docs(launcher): close Campaign LA4 LA5 and LA7 2026-08-14 19:36:00 +02:00
Erik
5535d0adac merge: Campaign LA LA5 - plugin hosting review-closed 2026-08-14 19:33:27 +02:00
Erik
f820eb258d fix(plugins): close LA5 ownership races 2026-08-14 19:28:14 +02:00
Erik
60f627998c merge: Campaign LA LA4 - Avalonia launcher review-closed 2026-08-14 19:14:25 +02:00
Erik
ae2cbbee8c fix(launcher): require executable Linux hosts 2026-08-14 19:12:32 +02:00
Erik
fbe9c8a288 fix(plugins): close LA5 host lifecycle review 2026-08-14 19:05:13 +02:00
Erik
7691cf75e2 merge: Campaign LA LA7b - selection state and flow review-closed 2026-08-14 19:05:03 +02:00
Erik
ff40656293 fix(runtime): isolate Campaign LA7b delete state 2026-08-14 19:03:43 +02:00
Erik
10a712d66b fix(launcher): close LA4 review findings 2026-08-14 19:02:20 +02:00
Erik
1b9e7e41f9 fix(runtime): close Campaign LA7b review findings 2026-08-14 18:55:48 +02:00
Erik
0e82cbf700 feat(runtime): own character selection flow 2026-08-14 18:22:17 +02:00
Erik
d0a9c65d85 feat(launcher): Campaign LA add Avalonia desktop shell 2026-08-14 18:15:14 +02:00
Erik
95f4be94db feat(plugins): complete Campaign LA5 cross-host hosting 2026-08-14 18:12:59 +02:00
Erik
6c4cd2bbc6 docs(launcher): close Campaign LA2 integration 2026-08-14 17:31:33 +02:00
Erik
e01b2cd12f merge: Campaign LA LA2 - probe and idle review-closed
# Conflicts:
#	docs/plans/2026-08-14-launcher-campaign.md
#	src/AcDream.Headless/Hosting/HeadlessSessionHost.cs
2026-08-14 17:30:07 +02:00
Erik
1c5e66c05b fix(launcher): Campaign LA close LA2 review findings 2026-08-14 17:20:02 +02:00
Erik
3313577dcc docs(launcher): Campaign LA close LA1 and LA3 2026-08-14 17:17:56 +02:00
Erik
8a03a25fc3 test(launcher): Campaign LA enforce composer-host contract 2026-08-14 17:16:13 +02:00
Erik
7749545dc4 merge: Campaign LA LA3 - Launcher.Core review-closed 2026-08-14 17:11:25 +02:00
Erik
347a1a5d16 fix(launcher): Campaign LA LA3 narrow review fixes 2026-08-14 17:06:47 +02:00
Erik
890cf267fc docs(launcher): record Campaign LA LA1 fix-round gates 2026-08-14 17:00:46 +02:00
Erik
d511e4c348 fix(launcher): close Campaign LA LA1 review findings 2026-08-14 17:00:09 +02:00
Erik
000ea979d5 test: Campaign LA finish LA2 probe and idle gates
Prove idle play remains passive and live until cancellation, then converges through one truthful status teardown. Keep probe mode string-only so numeric enum aliases cannot expand the pinned v1 contract, and record the Windows/WSL gates.
2026-08-14 16:49:51 +02:00
Erik
4edc122085 docs: Campaign LA handoff — worktree paths, stopped-agent recovery, kickoff prompt
Both remaining agents were stopped for token budget and their partial work
committed as WIP (75a6724d LA1 fix round, c6019424 LA2). The handoff now
carries: full worktree paths with branches and HEADs, exactly what each
stopped agent had finished versus what it still owes, and a paste-ready
kickoff prompt naming all three resumable items plus the two owed merge
items.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 16:34:06 +02:00
Erik
c601942467 wip: Campaign LA LA2 probe mode + idle policy — INCOMPLETE, stopped mid-task
Agent was stopped for token budget. Landed here: probe flag through
LiveSessionConnectOptions + the StartCore short-circuit, the mode field
with JsonRequired-to-semantic-validation move, host exit-code mapping,
and 34 passing tests including 3 new probe tests (agent last reported
green before the stop). NOT DONE: the idle-policy unit tests (next
step), full-suite verification, and the WSL run.

Build/test state UNVERIFIED at this commit. Next session: finish idle
policy tests, run Runtime+Headless Release suites Windows and WSL, then
dispatch the Opus dual-lens review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 16:33:00 +02:00
Erik
75a6724d5b wip: Campaign LA LA1 fix round — INCOMPLETE, stopped mid-task
Agent was stopped for token budget partway through the LA1 review fix
round. Landed here: F1 best-effort SessionStatusWriter, F2 App reader
tolerance (paths/mode), F5 argument-parsing hardening, plus new tests.
NOT DONE: F4 shared-fixture production shape (was the next step), F3
reconnect disconnected edge + recorded limitation, F6 exited
idempotency/reasons, F7 structural redaction test, F8 platform-guard
test + comment fix, optional RuntimeOptions PrintMembers redaction.

Build/test state UNVERIFIED at this commit. Next session: finish the
remaining findings, run the suites, then narrow re-review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 16:32:52 +02:00
Erik
c9fc7f4a66 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>
2026-08-14 16:30:38 +02:00
Erik
26feba8186 fix(launcher): Campaign LA LA3 review fixes — contract paths omission, probe composition, graceful stop, hygiene
Opus review of LA3 returned FIX FIRST; this addresses every finding in
scope (F1-F5, F7-F12; F6 CI-lane addition excluded per instructions):

- F1 (CRITICAL): SessionProcessSettings.Paths is now nullable and left
  null by SessionConfigComposer unless a caller supplies overrides, so
  the JSON key is entirely absent instead of "paths":{} — the App-side
  loader's strict UnmappedMemberHandling.Disallow would otherwise reject
  every gui/guiSelect session-config document at load.
- F2: added SessionConfigComposer.ComposeProbe and a nullable
  SessionDescriptor.Mode field ("probe", omitted for normal play) per
  the pinned contract — no character/policy/plugins/loginCommands.
- F3: LauncherProcessSupervisor.Stop now tries
  ILauncherChildProcess.TryRequestGracefulStop (Linux: libc SIGINT via
  LibraryImport, K4-proven graceful headless logout) before
  CloseMainWindow. Windows has no reliable no-window-console equivalent
  today; filed docs/ISSUES.md #397 with the CREATE_NEW_PROCESS_GROUP +
  CTRL_BREAK fix direction. Stop()'s blocking-timeout contract is now
  documented for LA4.
- F4: LauncherProfileStore.Save chmods the Linux temp file to 0600
  immediately after creation, before any credential is serialized;
  failure paths and Load() clean up a stale .tmp.
- F5: added LauncherCoreDependencyBoundaryTests asserting Launcher.Core
  references exactly AcDream.Platform and no packages.
- F7: StatusEventParser.Parse no longer throws on a whitespace/null
  line; StatusFileTailer.ReadNewEvents swallows the File.Exists/open
  TOCTOU window (FileNotFoundException/DirectoryNotFoundException/
  IOException) instead of throwing.
- F8: Start() now kills (entire process tree) and disposes a child that
  started successfully but failed while being fed its stdin password,
  instead of orphaning it.
- F9: SetState is monotonic — once Exited, no later transition applies
  or fires StateChanged, closing a Start()-path race where a
  synchronously-exiting child could be "resurrected" to Running.
- F10: CharacterIdFormat.TryParse now requires the "0x" prefix (an
  unprefixed hand-typed decimal id is also valid hex and was silently
  misread); a parsed id of 0 is treated as unusable and falls back to
  the name selector; LauncherProfileStore.MergeRoster normalizes both
  sides through TryParse/ToHexString instead of raw string equality, so
  a legacy unprefixed-hex row self-heals via name match instead of
  duplicating.
- F11: StatusCharacterEntry.SecondsGreyedOut is now uint, matching
  CharacterRosterEntry and the host writer.
- F12: added MalformedStatusEvent, returned for a recognized `e` whose
  payload doesn't match its shape, distinguished from UnknownStatusEvent
  (an unrecognized `e`).

AllowUnsafeBlocks was added to AcDream.Launcher.Core.csproj — required
by the LibraryImport source generator's function-pointer marshalling
stub for F3's Linux SIGINT P/Invoke.

Verification: dotnet build AcDream.slnx -c Release green (0 errors);
dotnet test tests/AcDream.Launcher.Core.Tests -c Release green at 94/94
on native Windows and under WSL (Ubuntu, verified across multiple runs
for the timing-sensitive SIGINT/sharing-violation tests, no flakes
observed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 16:29:39 +02:00
Erik
498f1c1182 docs: Campaign LA ledger — LA7a DONE+merged (fa2de1c4); LA1 implemented, review dispatching
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 16:08:45 +02:00
Erik
fa2de1c46e merge: Campaign LA LA7a — character wire messages (review-closed)
CharacterDelete 0xF655 (account+slot), CharacterRestore 0xF7D9/0xF643
(guid-only adaptation, register AD-97), CharacterError 0xF659 (retail
26-member enum). Opus retail-lens review PASS, narrow re-review MERGE:
6a32f375 + 4338b1c1 + 0c8643a7.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 16:07:58 +02:00
Erik
0c8643a7f2 docs: AD-97 wording nit from LA7a narrow re-review — correct artifact-appearance count
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 16:05:51 +02:00
Erik
e1322a06ae note: db9ad53c is a MIXED commit — docs + LA1 in-progress files
The running LA1 implementer had pre-staged its work-in-progress; git
commit takes the whole index regardless of what git add named, so the
docs commit swept in 37 LA1 files mid-implementation. Content is intact;
LA1 completion commit(s) follow with the remainder, and the LA1 slice
review covers the combined range. Process rule adopted: no orchestrator
commits in a worktree while an implementer agent is live in it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 16:05:17 +02:00
Erik
db9ad53c1c docs: Campaign LA — pinned launch-contract schema COMMITTED into plan LA1
The LA3 Opus review process note was right: the contract both sides
implement lived only in orchestrator prompts, which is exactly the drift
mode the pin exists to prevent (and it produced the paths-key CRITICAL).
The schema, field rules, probe-mode discriminator, and status vocabulary
are now a binding plan section; amendments change this text first,
implementations second. Ledger: LA3 fix round dispatched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 16:04:32 +02:00
Erik
0bcc7ba3a3 docs: Campaign LA — spec §11.4 corrected per LA7a review; LA7b hazards recorded
The spec seeded the wrong claim (restore extra strings = decompiler
artifact, no register row needed); the LA7a Opus review decoded the
PDB-paired binary and showed the two constant-string arguments are real,
making our guid-only request an adaptation — AD-97 filed on the LA7a
branch. Plan LA7 now carries the review-surfaced LA7b hazards (ACE
silent no-reply restore path, SendToLogon/SendToControl routing,
NumErrors sentinel).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 16:02:20 +02:00
Erik
4338b1c1f3 fix(net): Campaign LA LA7a review fixes — AD-97 register row, corrected restore justification
The Opus retail-lens review decoded the PDB-paired binary at
CPlayerSystem::RestoreCharacter@0x0055d760 and refuted the
uninitialized-edx justification: the two extra arguments are real
push imm32 of a constant PStringBase (BN mis-renders them, but they
pack to >=4 bytes each), so retail 0xF7D9 is >=16 bytes where ours
is 8. The guid-only CODE stands (ACE reads only the guid; holtburger
consensus) but it is an adaptation, not a corrected decompile — filed
as divergence register AD-97 and the doc comment now states the true
mechanism.

Also from the review: the 0xF643 conditional-parse doc now names BOTH
ACE flag-only failure branches (NameInUse + Corrupt); CharacterError
0x08 doc corrected (ACE misnames it ServerCrash2 — the port corrects
an ACE misnaming; ACE omits three values, not four); LA7b hazard notes
added (ACE silent no-reply on unknown restore guid; retail SendToLogon
vs SendToControl routing; NumErrors never rendered); two review-nit
tests (flag=0 Undef flag-only, non-Ok body with trailing bytes
ignored).

Core.Net suite: 953 passed / 0 failed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 16:01:52 +02:00
Erik
205b09cf32 docs: Campaign LA ledger — LA3 implemented (37d74e44), LA7a implemented (6a32f375), both reviews in flight
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 15:50:41 +02:00
Erik
37d74e4402 feat(launcher): Campaign LA LA3 — AcDream.Launcher.Core profile store, composer, supervisor, status tailer
New AcDream.Launcher.Core (BCL-only, ProjectReference: AcDream.Platform
ONLY) plus tests/AcDream.Launcher.Core.Tests, both registered in
AcDream.slnx. This is the file-contract orchestrator core the Avalonia
launcher (LA4) will bind to — the game solution (Core/Runtime/App/
Headless) stays entirely out of this dependency graph, so the launcher
can never accidentally grow a game-protocol coupling.

- Profiles/: LauncherProfileStore owns launcher-profiles.json (spec §5
  schema: version 1, servers[]/accounts[]/characters[]), strict
  camelCase System.Text.Json (UnmappedMemberHandling.Disallow), typed
  CRUD (add/edit/remove server; add/edit/remove account; edit character
  settings), and MergeRoster (fold a reported roster into an account's
  characters[] while preserving user-owned launchMode/plugins/
  loginCommands, adding new rows with default guiSelect, and retaining
  rows absent from the roster — they may be pending-delete). 0600 on
  Linux via File.SetUnixFileMode after save.
- Launching/: SessionConfigComposer builds the pinned session-config
  contract (Headless K1 shape + plugins/loginCommands/
  loginCommandDelayMs/statusFile) from a profile character + install
  record — character selector omitted entirely for guiSelect, policy
  {id:"idle"} only for headless, credential always standardInput/
  session. Passwords never enter this document (proven by a dedicated
  test). LauncherProcessSupervisor spawns a host, feeds the password to
  stdin then closes it, and exposes Starting/Running/Exited lifecycle;
  Stop calls CloseMainWindow falling back to Kill after a timeout, both
  reachable through an injectable ILauncherChildProcess/factory seam so
  the state machine is unit-testable without real OS process timing.
- Status/: StatusEventParser decodes the v1 status.jsonl vocabulary
  (started/connected/characterList/enteredWorld/pluginLoaded/
  pluginFailed/disconnected/exited); an unrecognized "e" or a malformed
  line degrades to a typed Unknown event rather than throwing.
  StatusFileTailer incrementally reads new lines, tolerating a
  not-yet-existing file and a partial trailing line (only advances its
  read position past confirmed '\n' boundaries; a truncated tail is
  simply re-read next poll, never parsed early).
- Integrity/: streaming SHA-256 + hex verify for later pak/download
  checks (LA9/LA10).

Tests: 71 passed (profile CRUD + roster-merge matrix + strict-schema
rejection; composer golden-shape tests for gui/guiSelect/headless +
password-absence; supervisor tests against both an injected fake child
(state-machine determinism) and a real spawned `dotnet --version`
child (genuine cross-platform stdin/exit-code proof); tailer tests
incl. partial-line and not-yet-existing-file; SHA-256 tests). Verified
green on Windows (Release) and native WSL/Linux (Release) — the Linux
0600 test executes its real assertion body under WSL rather than
early-returning.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 15:49:13 +02:00
Erik
6a32f37589 feat(net): Campaign LA LA7a — CharacterDelete/CharacterRestore/CharacterError wire messages
Ports the three character-management wire messages LA7 (design spec §7,
plan §11 item 4) identified as missing before the character-select
screen (LA8) can be built: delete, restore, and the server error channel.
Message types + tests only — no WorldSession/Runtime/UI wiring, that is
LA7b.

CharacterDelete (0xF655): outbound account+SLOT-INDEX request per
Proto_UI::SendDeleteCharacter@0x00546b30 (retail packs the account as
String16L then writes the trailing u32 directly after — NOT the character
guid; CPlayerSystem::DeleteCharacter@0x0055f830 resolves that slot via
CharacterSet::GetSlot before sending). The server's ack reuses the same
opcode with an empty body (ACE GameMessageCharacterDelete.cs); a fresh
CharacterList follows separately per CharacterHandler.cs:322 — that
refresh flow is explicitly out of scope here (LA7b).

CharacterRestore (0xF7D9 request / 0xF643 response): guid-only request,
per ACE (CharacterHandler.cs:331-385, ReadUInt32 only) and holtburger
(CharacterRestoreRequestData, guid-only) independent consensus. The
decompiled call site (Proto_UI::SendAdminRestoreCharacter@0x00546cf0)
appears to pack two extra strings, but its only caller
(CPlayerSystem::RestoreCharacter@0x0055d760) passes an uninitialized
local (`class PStringBase<char>* edx;`, never assigned) as the second
argument and `this` (a CPlayerSystem*, not a string) as the third —
textbook decompiler register-corruption, not real arguments. No
divergence-register row: this follows the correct reading of a corrupted
decompile, not a deviation from retail (spec §11 item 4). The response
reuses opcode 0xF643, a genuine retail collision with
CharacterCreateResponse (ACE's own comment: "This is a duplicate...",
GameMessageOpcode.cs:42); GameMessageCharacterRestore.cs always writes a
success shape (flag=1 + guid + name + secondsGreyedOut), but retail's
CharacterRestore handler can also reply via the CharacterCreateResponse
path on failure (e.g. NameInUse) with a flag-only body and no trailing
fields — the parser mirrors that conditionality instead of assuming the
four fields are always present.

CharacterError (0xF659): u32 error code, confirmed directly from retail's
inbound dispatcher UIQueueManager::ProcessNetBlobData@0x0055b000 ->
CPlayerSystem::Handle_CharacterError@0x0055d5d0, which reads
`enum charError` straight off the wire. The Code enum is a verbatim port
of retail's own enum charError (docs/research/named-retail/acclient.h:
4038-4067, 26 members incl. CHAR_ERROR_NUM_ERRORS) rather than a subset
filtered through ACE — retail's header names four members ACE's C#
CharacterError enum omits (LoggedOn, NoPremade, AccountInUse,
CharacterIsBooted) because ACE's server never sends them, though a
genuine retail server could. The 32-bit storage-width compiler sentinel
FORCE_charError_32_BIT is deliberately excluded (not a real value).
Unknown codes never throw — RawErrorCode always preserves the wire value.
Today acdream cannot surface any character-stage server error; this is
the first parser for the family.

46 new tests (byte-exact builder assertions, ACE-serializer-shaped
parser fixtures via the existing AceWireWriter test helper, all 26
retail error codes round-tripped, unknown/truncated/wrong-opcode
handling). Full Core.Net.Tests suite: 951 passed, 0 failed, 0 skipped.
Release build green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 15:46:48 +02:00
Erik
7a839cba71 docs: Campaign LA LA0 DONE — ledger closed; arch-doc wording nit from narrow re-review
The re-review closed all six findings and flagged one docs-only nit: the
Platform layer block described App as reaching Platform transitively when
the same commit made the reference direct, and spoke of the launcher in
the present tense. Both corrected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 15:32:52 +02:00
Erik
a49e92df3a fix(platform): Campaign LA LA0 review fixes — CI Linux lanes, arch doc, self-guard
Opus dual-lens review of cb6502c8 passed with six findings; this lands
the fix round:
1. headless-portability.yml: AcDream.Platform src/tests join both path
   triggers and the presentation-free build/test arrays — the moved XDG
   tests run on ubuntu-latest again (they had fallen out of every Linux
   lane).
2. acdream-architecture.md: AcDream.Platform gets its own layer block;
   Runtime may-reference clause updated (the guard changed in cb6502c8,
   its human-readable twin had not).
3. PlatformDependencyBoundaryTests: the BCL-only contract (zero
   project/package references) is now enforced, not just observed.
4. memory/project_linux_graphical.md canonical seam renamed.
5. Plan LA0 recon corrected: the K0 Headless guard was never the guard
   needing amendment (it asserts Headless own refs); Runtime own-refs
   guard was — the commit did the right thing, the plan text now says so.
6. App declares its AcDream.Platform reference explicitly per its own
   convention instead of riding transitivity.

Platform.Tests: 4 passed (3 moved + the new guard).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 15:30:04 +02:00
Erik
cb6502c8a5 feat(platform): Campaign LA LA0 — extract ApplicationPathSet to AcDream.Platform
The launcher (LA3/LA4) needs the XDG/Windows path contract
(ApplicationPathSet/IApplicationPathEnvironment) without pulling in any
gameplay assembly. Move it out of AcDream.Runtime into a new BCL-only
AcDream.Platform project so the launcher-side Launcher.Core project can
reference it directly per the campaign plan (docs/plans/2026-08-14-launcher-campaign.md,
LA0). Namespace renamed AcDream.Runtime.Platform -> AcDream.Platform;
code is otherwise byte-identical (no logic changes).

AcDream.Runtime now carries a ProjectReference to AcDream.Platform and
re-exports it transitively, so App and Headless keep resolving the type
without a direct reference and K0's Headless single-ProjectReference
guard (HeadlessAssemblyReferencesOnlyTheRuntimeProject) stands unchanged.
The sibling Runtime dependency-boundary guard
(RuntimeProjectDeclaresOnlyApprovedProjectDependencies) does assert
Runtime's own project-reference set, so it needed a deliberate,
documented addition of AcDream.Platform to its expected list.

Moved tests/AcDream.Runtime.Tests/Platform/ApplicationPathSetTests.cs to
a new tests/AcDream.Platform.Tests/ project (namespace
AcDream.Platform.Tests) referencing only AcDream.Platform. Registered
both new projects in AcDream.slnx.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 15:19:02 +02:00
Erik
7f24af1a37 docs: Campaign LA Linux posture — full launcher stack Linux-tested; GUI waits for Slice L
User decision 2026-08-14: everything the launcher does ships Linux-tested
in this campaign (launcher UI, install/update with manual DAT picker,
headless launches with plugins + login commands, probe, per-slice Linux
test runs, Linux connected-gate section at LA11). GUI client launches
stay Windows-only until Slice L resumes later; the launcher renders GUI
modes disabled on Linux with an explicit note, and the host-agnostic
session-config contract means Slice L lights them up with no launcher
changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 15:17:53 +02:00
Erik
9b0bb25581 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>
2026-08-14 15:10:39 +02:00
Erik
afb4e82943 docs: Campaign LA design spec — launcher/installer/updater + char select
Approved brainstorm outcome for the alpha launcher campaign: Approach A
file-contract orchestrator (session config in, stdin credential, JSONL
status events out), full in-UI CRUD for servers/accounts/credentials,
headless character-list probe, retail character-select screen (no
Create), plugins + login commands on both hosts, first-run DAT
locate/bake install, GitHub Releases update feed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 14:53:08 +02:00
Erik
cca8f20738 docs: OP re-gate round closeout — pass-1 batch USER-PASSED, OP8 findings fixed, remaining gates deferred
ISSUES: #372/#374/#375/#378-#382/#385 flipped DONE (re-gate USER-PASSED
2026-08-14); #396 records the live-verified crash fix. CLAUDE.md Current
state: Campaign OP paragraph now carries the 2026-08-14 round and the
still-owed full OP3-OP6 sections + OP8 visual re-check.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 13:53:10 +02:00
Erik
2a81e813cb fix #396 crash: map WaitDialog class type 0x19 to UiDialogRoot; dialog-open failure lands on the contracted refusal path
The user's first click on a mapping button killed the client: the live
dialog catalog's wait root 0x31 carries retail class type 0x19 (WaitDialog),
which DatWidgetFactory left unmapped, so the root built as a plain
UiDatElement and RetailWaitDialogView's ctor threw out of UiButton.OnClick
into the render loop. The unit test missed it by standing the confirmation
fixture (type 0x13, mapped) in for the wait root — the structural-false-
negative class again. Pins: DatWidgetFactoryTests theory for both dialog
root types, plus an installed-DAT UiDialogRoot/0x3D/0x3E assertion in the
env-gated keyboard probe. OpenCaptureInstructions now converts a dialog
construction failure into its contracted 0-return (log + capture refused,
retail's own OpenMapWarnDialog failure shape) instead of crashing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 13:48:29 +02:00
Erik
30fa6ee507 fix #394 #395 #396: OP8 re-gate round — caption font, retail key names, capture dialog
Three findings from the user's first Configure Keyboard look (OP8 gate,
2026-08-14), each root-caused against the named retail decomp:

- #394 row-caption font: the synthesized action-label UiText never set
  DatFont and fell to the debug bitmap font. The authored row template
  (0x21000009/0x1000002F, retail UIOption_ActionKeyMap) carries FontDid
  0x4000000A (18px serif) — Bind now takes resolveTemplateFont and applies
  the template's own authored font, resolved once per template pair.

- #395 key captions: raw enum spellings ("Shift+ShiftLeft") replaced by the
  port of CInputManager_WIN32::GetNameFromKey @0x00687F40 /
  GetNameFromKey_Internal @0x00687800 (RetailKeyNames): DAT string-table
  override by DIK-name hash (key enum 4 -> 0x2300000A, meta enum 5 ->
  0x2300000B, delimiter enum 3 -> 0x23000007 — GetDIDByEnum category 4,
  live-probed), else the OS keyboard layout's own key name ("SKIFT") via
  PlatformKeyNameProvider (Win32 GetKeyNameTextW — register row AD-96 for
  the DirectInput-vs-GetKeyNameText adaptation), else the DIK-suffix
  spelling. Bare modifier-key bindings show only the key name.

- #396 capture feedback: clicking a mapping button now opens retail's
  instruction dialog (InitiateBinding @0x004899D0 -> OpenMapWarnDialog
  @0x00488A00): a type-2 WAIT dialog on retail's MapWarn queue key
  0x10000001 with ID_ActionKeyMap_MapInstructions (0x23000004, ACTION
  variable interpolated), closed on key hit or ESC through the capture
  callback; capture is not armed if the dialog cannot open, matching
  retail. New RetailWaitDialogView (wait root 0x31 — same authored
  popup/message pair 0x3D/0x3E as the confirmation root, live-DAT probed)
  behind a shared IRetailDialogView presenter seam.

Probe evidence (env-gated, kept):
KeyboardConfigLiveMountProbeTests.ProbeKeyboardFontsAndKeyNameStrings.
Register: AD-96 filed. Gate script OP8 section updated (step 4 rewritten;
the "pressed/active state is enough" contract is retired).

Full Release solution suite green (13,424 passed / 4 skips).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 13:45:55 +02:00
Erik
1528e5693b Merge branch 'main' into claude/latest-commits-cb0c8f
# Conflicts:
#	tests/AcDream.App.Tests/Streaming/LandblockBuildOriginTests.cs
2026-08-14 12:52:34 +02:00
Erik
3505dddaf9 docs: CLAUDE.md Current state - 2026-08-13/14 gate block + secure trade shipped
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 12:51:42 +02:00
Erik
0f15c74147 chore: secure-trade closeout - strip [trade] gate probes, roadmap ledger
The two-client user gate PASSED 2026-08-14 (open both ways, stage with
the retail trading marker, accept/decline, executed swap, Clear All,
cancel text). Every TEMPORARY [trade] probe line from gate rounds 1-2 is
stripped (ItemInteractionController, SelectionInteractionController,
SecureTradeUiController, LiveSessionCommandRouter, WorldSession,
RuntimeTradeState). Roadmap gains the shipped-trade ledger row.

Suites after strip: App 4,992/3, Runtime 1,626 - green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 12:48:31 +02:00