merge: Campaign LA LA2 - probe and idle review-closed

# Conflicts:
#	docs/plans/2026-08-14-launcher-campaign.md
#	src/AcDream.Headless/Hosting/HeadlessSessionHost.cs
This commit is contained in:
Erik 2026-08-14 17:30:07 +02:00
commit e01b2cd12f
16 changed files with 1361 additions and 69 deletions

View file

@ -27,6 +27,14 @@ public enum RuntimeSessionStartStatus
Failed,
Inactive,
StaleGeneration,
/// <summary>
/// Campaign LA slice LA2: mirrors
/// <see cref="Session.LiveSessionStartStatus.ProbeComplete"/> — a probe
/// session connected, reported its roster, and gracefully disconnected
/// before EnterWorld. A SUCCESS outcome for the headless process host's
/// exit-code mapping, not a failure.
/// </summary>
ProbeComplete,
}
public readonly record struct RuntimeSessionStartResult(

View file

@ -13,7 +13,23 @@ public sealed record LiveSessionConnectOptions(
int Port,
string User,
string Password,
LiveSessionCharacterSelector? Character = null);
LiveSessionCharacterSelector? Character = null,
/// <summary>
/// Campaign LA slice LA2: short-circuits
/// <see cref="LiveSessionController"/>'s connect transaction right after
/// the roster report (before <c>TrySelectCharacter</c>/
/// <c>ApplySelectedCharacter</c>/<c>EnterWorld</c>) — connect, receive
/// <c>CharacterList</c>, report the roster, gracefully disconnect via the
/// same <c>StopCore</c> teardown the <see cref="LiveSessionStartStatus.NoCharacters"/>
/// path already uses, and return
/// <see cref="LiveSessionStartStatus.ProbeComplete"/>. The pinned launch
/// contract (<c>docs/plans/2026-08-14-launcher-campaign.md</c> LA1's
/// <c>mode</c> field) requires a probe session to omit both
/// <see cref="Character"/> and its policy entirely, but the controller
/// itself does not enforce that pairing — the headless config loader
/// does, before a <see cref="LiveSessionConnectOptions"/> is ever built.
/// </summary>
bool Probe = false);
public interface IRuntimeLiveSessionFramePhase
{

View file

@ -13,6 +13,14 @@ public enum LiveSessionStartStatus
Connected,
Deferred,
Failed,
/// <summary>
/// Campaign LA slice LA2: a <see cref="LiveSessionConnectOptions.Probe"/>
/// session connected, received (and reported) the character roster, and
/// gracefully disconnected BEFORE selection/EnterWorld — deliberately a
/// SUCCESS variant of the <see cref="NoCharacters"/> early-exit shape
/// (same <c>StopCore</c> teardown), not a failure.
/// </summary>
ProbeComplete,
}
public readonly record struct LiveSessionOwnershipSnapshot(
@ -647,6 +655,21 @@ public sealed class LiveSessionController
return new LiveSessionStartResult(LiveSessionStartStatus.Deferred);
}
// Campaign LA slice LA2: the probe short-circuit lands here —
// only after a real CharacterList was returned and its roster was
// reported, before TrySelectCharacter ever runs. A missing
// CharacterList falls through to the existing NoCharacters
// non-success path below; connectivity by itself is not a
// successful character-roster probe. Non-probe callers continue
// through the unchanged selection/enter path.
if (options.Probe && characters is not null)
{
Console.WriteLine(
"live: probe complete — disconnecting before EnterWorld");
StopCore();
return new LiveSessionStartResult(LiveSessionStartStatus.ProbeComplete);
}
if (characters is null
|| !TrySelectCharacter(
characters,

View file

@ -316,6 +316,8 @@ public sealed class LiveSessionHost : IRuntimeSessionCommands
RuntimeSessionStartStatus.Deferred,
LiveSessionStartStatus.Failed =>
RuntimeSessionStartStatus.Failed,
LiveSessionStartStatus.ProbeComplete =>
RuntimeSessionStartStatus.ProbeComplete,
_ => throw new ArgumentOutOfRangeException(
nameof(result),
result.Status,