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>
This commit is contained in:
Erik 2026-08-14 16:04:32 +02:00
parent 0bcc7ba3a3
commit db9ad53c1c
38 changed files with 2397 additions and 40 deletions

View file

@ -104,6 +104,8 @@ internal sealed class LiveSessionRuntimeFactory
private readonly LiveSessionCommandSurface _commands;
private readonly Action<string> _log;
private readonly LiveMovementStatsApplier _movementStats;
private readonly SessionStatusWriter _statusWriter;
private readonly string _sessionId;
public LiveSessionRuntimeFactory(
LiveSessionPlayerRuntime player,
@ -112,7 +114,9 @@ internal sealed class LiveSessionRuntimeFactory
LiveSessionInteractionRuntime interaction,
LiveSessionWorldRuntime world,
LiveSessionCommandSurface commands,
Action<string> log)
Action<string> log,
SessionStatusWriter? statusWriter = null,
string sessionId = "app")
{
_player = player ?? throw new ArgumentNullException(nameof(player));
_domain = domain ?? throw new ArgumentNullException(nameof(domain));
@ -122,6 +126,10 @@ internal sealed class LiveSessionRuntimeFactory
_world = world ?? throw new ArgumentNullException(nameof(world));
_commands = commands ?? throw new ArgumentNullException(nameof(commands));
_log = log ?? throw new ArgumentNullException(nameof(log));
// Campaign LA slice LA1: a no-op instance when the caller has no
// status file configured — every call site below stays unconditional.
_statusWriter = statusWriter ?? new SessionStatusWriter(null);
_sessionId = sessionId ?? throw new ArgumentNullException(nameof(sessionId));
// C3c-F1: stat recomputes route through the Runtime movement owner's
// typed application seam; App keeps zero direct controller mutations.
_movementStats = new LiveMovementStatsApplier(
@ -176,9 +184,17 @@ internal sealed class LiveSessionRuntimeFactory
$"connecting to {host}:{port} as {user}",
chatType: 1),
Connected: () =>
{
_domain.Communication.Chat.OnSystemMessage(
"connected — character list received",
chatType: 1)),
chatType: 1);
_statusWriter.Connected(_sessionId);
},
Roster: roster => _statusWriter.CharacterList(_sessionId, roster),
CharacterEntered: selection => _statusWriter.EnteredWorld(
_sessionId,
selection.CharacterId,
selection.CharacterName)),
connectOptions);
}