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>
This commit is contained in:
Erik 2026-08-14 16:33:00 +02:00
parent 498f1c1182
commit c601942467
14 changed files with 830 additions and 35 deletions

View file

@ -46,11 +46,33 @@ internal sealed record HeadlessSessionDescriptor
[JsonRequired]
public string Account { get; init; } = string.Empty;
[JsonRequired]
public HeadlessCharacterSelector Character { get; init; } = new();
/// <summary>
/// Campaign LA slice LA2: ABSENT (<see langword="null"/>) for normal play
/// sessions; <see cref="HeadlessSessionMode.Probe"/> for the LA2 probe
/// (connect → characterList → graceful disconnect, never EnterWorld) —
/// the pinned launch-contract schema's <c>mode</c> field
/// (<c>docs/plans/2026-08-14-launcher-campaign.md</c> LA1/LA2).
/// <see cref="Character"/>/<see cref="Policy"/> requiredness depends on
/// this value, which is why their requiredness lives in
/// <see cref="HeadlessConfigurationLoader"/>'s semantic validation rather
/// than a <c>[JsonRequired]</c> attribute — that attribute fires during
/// deserialization, before <see cref="Mode"/> can be inspected at all.
/// </summary>
public HeadlessSessionMode? Mode { get; init; }
[JsonRequired]
public HeadlessBotPolicyDescriptor Policy { get; init; } = new();
/// <summary>
/// Required for play sessions (<see cref="Mode"/> absent); MUST be
/// omitted for probe sessions (<see cref="HeadlessSessionMode.Probe"/>) —
/// the pinned contract keeps the shape unambiguous by forbidding a probe
/// session from also declaring a selector. Enforced by
/// <see cref="HeadlessConfigurationLoader.ValidateSession"/>, not
/// <c>[JsonRequired]</c> (see this record's own doc on <see cref="Mode"/>).
/// </summary>
public HeadlessCharacterSelector? Character { get; init; }
/// <summary>Same mode-dependent requiredness as <see cref="Character"/>:
/// required for play sessions, forbidden for probe sessions.</summary>
public HeadlessBotPolicyDescriptor? Policy { get; init; }
[JsonRequired]
public HeadlessCredentialReference Credential { get; init; } = new();
@ -140,6 +162,19 @@ internal sealed class HeadlessBotPolicyDescriptor
public HeadlessBotPolicyRole? Role { get; init; }
}
/// <summary>
/// Campaign LA slice LA2: see <see cref="HeadlessSessionDescriptor.Mode"/>.
/// The pinned launch-contract schema defines exactly two states for a
/// session — ABSENT (mapped to <see langword="null"/>, meaning "play") or
/// the literal string <c>"probe"</c> — so <see cref="Probe"/> is the only
/// member; there is no explicit "play" spelling.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter<HeadlessSessionMode>))]
internal enum HeadlessSessionMode
{
Probe,
}
/// <summary>See <see cref="HeadlessBotPolicyDescriptor.Role"/>.</summary>
[JsonConverter(typeof(JsonStringEnumConverter<HeadlessBotPolicyRole>))]
internal enum HeadlessBotPolicyRole