From 2bff44a9fabb5921efa907fcd9d06d1c2d41df1f Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 19 Aug 2026 20:33:51 +0200 Subject: [PATCH] fix: launcher-started headless sessions and character refresh never ran at all MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The launcher spawned the headless host as acdream-headless --config but HeadlessCommandLine.Parse reads arguments[0] as the COMMAND and accepts only "validate" or "run". So every launcher-started headless session and every "Refresh characters" died on its first instruction with Invalid command. Run --help for usage. (exit 64) The user's own cache shows it six times over two days. It was invisible because the failure is an exit code in a status file, not something the UI says out loud — which is how it survived a whole campaign whose gates exercised the headless host through its CLI directly, never through the launcher's spec. The graphical host takes a bare "--session-config" and has no command word; this sibling call was written to match it. Both headless call sites now pass "run" first. A probe is an ordinary "run" whose session config carries mode: "probe" — the difference is in the document, not the command line, so one fix repairs refresh and headless play together. LauncherHeadlessCommandLineContractTests is the connection that was missing: it takes the argument vector the launcher will really use and hands it to the parser the host will really use, for probe and for headless play, and pins that the graphical arguments are deliberately NOT a headless command line. The two sides cannot drift again without failing here. Headless.Tests already referenced both assemblies, so this needed no new coupling. Also LU7, at the user's direction: a selected character now offers only Play and Headless. Choosing a character means choosing to play AS that character, so "Character select" — which deliberately picks no character — belongs to the account page alone, where it already lives. The per-character GuiSelect command and its capability are removed rather than left as dead surface. Full solution 14,374 passed, 0 failed under the release-gate filter. Co-Authored-By: Claude Opus 5 --- .../Orchestration/LauncherExecutableSet.cs | 15 ++- src/AcDream.Launcher/MainWindow.axaml | 14 ++- .../ViewModels/LauncherWindowViewModel.cs | 12 --- ...auncherHeadlessCommandLineContractTests.cs | 94 +++++++++++++++++++ .../LauncherWindowViewModelTests.cs | 5 +- 5 files changed, 117 insertions(+), 23 deletions(-) create mode 100644 tests/AcDream.Headless.Tests/LauncherHeadlessCommandLineContractTests.cs diff --git a/src/AcDream.Launcher.Core/Orchestration/LauncherExecutableSet.cs b/src/AcDream.Launcher.Core/Orchestration/LauncherExecutableSet.cs index 4add2816..8014ff20 100644 --- a/src/AcDream.Launcher.Core/Orchestration/LauncherExecutableSet.cs +++ b/src/AcDream.Launcher.Core/Orchestration/LauncherExecutableSet.cs @@ -100,7 +100,14 @@ public sealed class LauncherExecutableSet return mode == LaunchMode.Headless ? new LauncherProcessSpec( paths.HeadlessHostPath, - ["--config", configFilePath], + // "run" is REQUIRED and positional: HeadlessCommandLine.Parse + // reads arguments[0] as the command and accepts only + // "validate" or "run". Passing "--config" first made every + // launcher-started headless session die instantly with + // "Invalid command. Run --help for usage." (exit 64) — the + // graphical host takes a bare "--session-config", and this + // sibling call was written to match it. + ["run", "--config", configFilePath], paths.WorkingDirectory, StderrLogPath: stderrLogPath) : new LauncherProcessSpec( @@ -119,7 +126,11 @@ public sealed class LauncherExecutableSet ExecutablePaths paths = RequireAvailable(LaunchMode.Headless); return new LauncherProcessSpec( paths.HeadlessHostPath, - ["--config", configFilePath], + // Same grammar as CreatePlaySpec's headless arm — see its comment. + // A probe is an ordinary "run" whose session config carries + // mode: "probe"; the difference is in the document, not the + // command line. + ["run", "--config", configFilePath], paths.WorkingDirectory, StderrLogPath: stderrLogPath); } diff --git a/src/AcDream.Launcher/MainWindow.axaml b/src/AcDream.Launcher/MainWindow.axaml index bb638338..ab945311 100644 --- a/src/AcDream.Launcher/MainWindow.axaml +++ b/src/AcDream.Launcher/MainWindow.axaml @@ -193,20 +193,18 @@ - +