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:
parent
0bcc7ba3a3
commit
db9ad53c1c
38 changed files with 2397 additions and 40 deletions
|
|
@ -135,6 +135,7 @@ public sealed class LiveSessionControllerTests
|
|||
public Action? OnReset { get; set; }
|
||||
public Action? OnConnecting { get; set; }
|
||||
public Action? OnConnected { get; set; }
|
||||
public Action? OnRoster { get; set; }
|
||||
public Action? OnSelected { get; set; }
|
||||
public Action? OnActivate { get; set; }
|
||||
public Action? OnEntered { get; set; }
|
||||
|
|
@ -146,6 +147,7 @@ public sealed class LiveSessionControllerTests
|
|||
public bool ThrowOnBind { get; set; }
|
||||
public bool ThrowOnConnecting { get; set; }
|
||||
public bool ThrowOnConnected { get; set; }
|
||||
public bool ThrowOnRoster { get; set; }
|
||||
public bool ThrowOnSelected { get; set; }
|
||||
public bool ThrowOnActivate { get; set; }
|
||||
public bool ThrowOnEntered { get; set; }
|
||||
|
|
@ -158,6 +160,7 @@ public sealed class LiveSessionControllerTests
|
|||
public List<TestCommandBus> CommandBuses { get; } = [];
|
||||
public List<LiveSessionCharacterSelection> Selections { get; } = [];
|
||||
public List<RuntimeGenerationToken> ResetGenerations { get; } = [];
|
||||
public List<LiveSessionRosterReport> Rosters { get; } = [];
|
||||
|
||||
public LiveSessionBinding BindSession(WorldSession session)
|
||||
{
|
||||
|
|
@ -231,6 +234,15 @@ public sealed class LiveSessionControllerTests
|
|||
throw new InvalidOperationException("connected failure");
|
||||
}
|
||||
|
||||
public void ReportRoster(LiveSessionRosterReport roster)
|
||||
{
|
||||
calls.Add("roster");
|
||||
Rosters.Add(roster);
|
||||
OnRoster?.Invoke();
|
||||
if (ThrowOnRoster)
|
||||
throw new InvalidOperationException("roster failure");
|
||||
}
|
||||
|
||||
public void ApplySelectedCharacter(LiveSessionCharacterSelection selection)
|
||||
{
|
||||
calls.Add("selected");
|
||||
|
|
@ -290,7 +302,7 @@ public sealed class LiveSessionControllerTests
|
|||
Assert.Equal(
|
||||
[
|
||||
"reset", "resolve", "create", "bind", "report-connecting",
|
||||
"connect", "report-connected", "selected", "enter:1",
|
||||
"connect", "report-connected", "roster", "selected", "enter:1",
|
||||
"activate", "entered",
|
||||
],
|
||||
calls);
|
||||
|
|
@ -302,6 +314,32 @@ public sealed class LiveSessionControllerTests
|
|||
Assert.True(host.CommandBuses[0].Active);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Start_ReportsRosterFromCharacterListBeforeSelection()
|
||||
{
|
||||
var calls = new List<string>();
|
||||
var operations = new TestOperations(calls);
|
||||
var host = new TestHost(calls);
|
||||
var controller = new LiveSessionController(operations);
|
||||
|
||||
LiveSessionStartResult result = controller.Start(LiveOptions(), host);
|
||||
|
||||
Assert.Equal(LiveSessionStartStatus.Connected, result.Status);
|
||||
LiveSessionRosterReport roster = Assert.Single(host.Rosters);
|
||||
Assert.Equal("Canonical", roster.AccountName);
|
||||
Assert.Equal(11, roster.SlotCount);
|
||||
Assert.Equal(
|
||||
[
|
||||
new LiveSessionRosterEntry(0x50000001u, "Grey", 10u),
|
||||
new LiveSessionRosterEntry(0x50000002u, "Ready", 0u),
|
||||
],
|
||||
roster.Entries);
|
||||
// "roster" must land strictly before "selected" — the launcher's
|
||||
// char-select screen (LA7/LA8) will read the roster before any
|
||||
// selection has been made.
|
||||
Assert.True(calls.IndexOf("roster") < calls.IndexOf("selected"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Start_DisabledAndMissingCredentialsResetButNeverConstructSession()
|
||||
{
|
||||
|
|
@ -488,7 +526,7 @@ public sealed class LiveSessionControllerTests
|
|||
[
|
||||
"deactivate", "detach-events", "dispose-session", "detach-session",
|
||||
"reset", "resolve", "create", "bind", "report-connecting",
|
||||
"connect", "report-connected", "selected", "enter:1",
|
||||
"connect", "report-connected", "roster", "selected", "enter:1",
|
||||
"activate", "entered",
|
||||
],
|
||||
calls);
|
||||
|
|
@ -742,6 +780,7 @@ public sealed class LiveSessionControllerTests
|
|||
[InlineData("connecting")]
|
||||
[InlineData("connected")]
|
||||
[InlineData("characters")]
|
||||
[InlineData("roster")]
|
||||
[InlineData("selected")]
|
||||
[InlineData("activate")]
|
||||
[InlineData("entered")]
|
||||
|
|
@ -755,6 +794,7 @@ public sealed class LiveSessionControllerTests
|
|||
case "connecting": host.ThrowOnConnecting = true; break;
|
||||
case "connected": host.ThrowOnConnected = true; break;
|
||||
case "characters": operations.ThrowOnCharacters = true; break;
|
||||
case "roster": host.ThrowOnRoster = true; break;
|
||||
case "selected": host.ThrowOnSelected = true; break;
|
||||
case "activate": host.ThrowOnActivate = true; break;
|
||||
case "entered": host.ThrowOnEntered = true; break;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue