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.
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>