fix(headless,runtime): OP7 review fixes + docs: OP3 re-review REOPEN (narrow)

TWO work products share this commit (a staged-index collision between the
coordinator's docs commit and the OP7 fixer's staged files — content
verified complete and coherent; only this message was wrong before the
amend):

1. OP7 review fixes (all nine findings from
   docs/research/2026-08-11-op7-review.md):
   - M1: HeadlessSessionDescriptor is a record; WithAccount uses 'with' non-destructive record copy,
     so a future property cannot be silently dropped; direct-CLI
     regression test proves CharacterOptions survives --user/--password.
   - M2 root fix: LiveSessionEventRouter skips BOTH Replace and the
     options notification on a trailer-truncated PlayerDescription — a
     truncated re-seed can no longer install zeroed words under an armed
     latch for OP7's automation to flush into 0x01A1.
   - SF1: schema keys validate as ordinal strings against the allowed
     names (numeric / comma-combined aliases rejected). SF2: both-true
     fellowship exclusion rejected at load, naming both keys. SF3: the
     onLoginCompleteSent observer moved after transit.EndTeleport().
     SF4: production-hook coverage for all three LoginComplete sites.
     SF5: test-script OP7 wire expectation corrected (batched ids ride
     only the 0x01A1).

2. docs/research/2026-08-11-op3-rereview.md — OP3 re-review verdict
   REOPEN (narrow): M1 byte-decode independently re-verified (6a 07 at
   all six sites); residuals R1 (gate script promises a timestamp prefix
   acdream doesn't render), R2 (null-controller player-mode still
   refuses), R3 (dormancy pin lacks stimulus) — coordinator fixes follow.

Full Release suite at this tree: 12,956 passed / 4 skipped / 0 failed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-11 03:19:35 +02:00
parent 386076af0f
commit 7b60e71b85
11 changed files with 1007 additions and 23 deletions

View file

@ -209,8 +209,19 @@ public sealed class LiveSessionEventRouter : ILiveSessionEventRouting
onDesiredComponents: null,
onCharacterOptions: (options1, options2, trailerTruncated) =>
{
// MF-2 (Campaign OP OP7 review fix, 2026-08-11): a
// trailer-truncated PlayerDescription's option words are
// the parser's zero placeholders, not server truth (R3,
// above). Installing them AND notifying subscribers let a
// headless seeder diff against zero and flush it into
// 0x01A1 even though HasServerSeed stayed armed from an
// earlier complete seed. Root fix: on truncation, install
// NOTHING and notify NO ONE — the words the caller last
// had (real, seeded) remain current.
if (trailerTruncated)
return;
character.Character.Options.Replace(
options1, options2, armServerSeed: !trailerTruncated);
options1, options2, armServerSeed: true);
character.OnCharacterOptionsChanged?.Invoke(options1, options2);
},
clientTime: character.ClientTime,