docs: FA2 fix-round -- ledger update, D2 correction, seam-map addenda

Plan (docs/plans/2026-08-11-fellowship-allegiance-campaign.md):
- D2 gets the D9-style dated strike/addendum recording the corrected
  allegiance reset semantics (clears at every generation reset; the
  HasServerSeed latch only gates pre-seed rendering WITHIN a session) with
  the three-way evidence citation: the retail OnEndCharacterSession hook,
  the RuntimeCharacterOptionsState precedent's actual clear-and-relatch
  behavior, and the no-character-selector connect path
  (SessionPlayerComposition.cs:1127).
- The architecture blurb and FA2's slice-map contract row get matching
  strike/addendum corrections so the "fellowship session-scoped,
  allegiance survives reconnect" claim does not survive uncorrected
  anywhere in the plan.
- FA2's ledger row: fix-round commit SHAs, corrected delegate-hole/
  wrapper counts (blast SHOULD-FIX 3: 10 not 15, 11 not 12), the
  allegiance register-row re-evaluation conclusion (blast SHOULD-FIX 6 --
  no row needed, MF-1's fix retires the deviation entirely), and the
  reconciled fix-round test totals (13,201/4/0 -> 13,215/4/0, +14,
  arithmetic exact per file).

Seam map (docs/research/2026-08-11-fa-acdream-seams.md), per the FA1
fix-round's established in-place-correction convention:
- SS1.3 and SS9's dispatcher-replaces-not-chains correction is now dated
  and cites the actual GameEventDispatcher.Dispatch behavior, matching the
  code comment already landed in GameEventWiring.cs.
- SS2.3 gets the 0x01C9/0x01CA disposition it was missing (correctly
  left unregistered -- dead COMDAT-fold no-ops per FA1) so FA3 does not
  have to re-derive it or "fix" the gap.
- The SS8 seam-map table's Allegiance-owner row and the executive-summary
  ownership bullet both get the "survives reconnect" claim struck with a
  dated correction to "session-scoped, clears at every generation reset".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-12 02:18:59 +02:00
parent ded23067aa
commit ed8b3ec96d
2 changed files with 117 additions and 11 deletions

View file

@ -21,8 +21,11 @@ panel (the OP3 recipe: `LayoutImporter.ImportInfos(dats, 0x2100006E,
0x1000018F)`, Type-8 tab host + `ActivateTabBehavior`, per-page scoped
controllers, `RetailPanelCatalog` id 12, F3/F4 keybinds). Fellowship and
allegiance state live in TWO new sibling Runtime owners under
`GameRuntime` per the Slice-J pattern (different lifetimes: fellowship is
session-scoped; allegiance survives reconnect behind a seed latch). Wire
`GameRuntime` per the Slice-J pattern. ~~(different lifetimes: fellowship is
session-scoped; allegiance survives reconnect behind a seed latch)~~
**[FA2 fix-round addendum, 2026-08-12: corrected — both owners are
session-scoped and clear at every generation reset; see D2's addendum
below for the full three-way evidence citation.]** Wire
parsers/builders live in `AcDream.Core.Net` beside the H.2 scaffolding
they connect, repair, or replace. Both graphical and headless hosts are
served by the single inbound wiring site.
@ -49,10 +52,51 @@ served by the single inbound wiring site.
is already served by ACE and trivially pinned in-slice — otherwise the
action buttons are honest INERT with register rows (the OP3 precedent),
completed post-campaign.
- **D2 — two sibling Runtime owners.** `RuntimeFellowshipState`
- **D2 —** ~~two sibling Runtime owners. `RuntimeFellowshipState`
(session-scoped, cleared at reset like external-container) and
`RuntimeAllegianceState` (survives reconnect behind a `HasServerSeed`-
style latch). Lane D's 8-edit template per owner; consumers poll via
style latch).~~ **[FA2 fix-round addendum, 2026-08-12: shipped the
OPPOSITE finding for `RuntimeAllegianceState` — the "survives reconnect"
half is proven wrong by three-way evidence
(docs/research/2026-08-12-fa2-review-mechanism.md MUST-FIX 1):
(1) **the retail hook** — `ClientAllegianceSystem::
OnEndCharacterSession @0x00569FA0` tail-calls `AllegianceProfile::Clear`
at exactly the per-character-session boundary this owner's reset would
run at, mirroring the sibling `ClientFellowshipSystem::
OnEndCharacterSession @0x005690A0` Fellowship already honored — FA2's
fellowship half was byte-faithful, the allegiance half was the exact
inverse of retail's behavior at the same hook;
(2) **the cited precedent's actual behavior**
`RuntimeCharacterOptionsState.ResetSession`
(`src/AcDream.Runtime/Gameplay/RuntimeCharacterState.cs:1007-1017`),
the very `HasServerSeed`-style latch this decision named as its
justification, CLEARS and re-latches on every reset; it does not
persist. Its own doc comment names the exact hazard this row then walked
into verbatim: "a stale seed surviving a session boundary could let a
flush ship the PRIOR character's words over the new one's";
(3) **the no-character-selector connect path**
`SessionPlayerComposition.cs:1127-1132` constructs
`LiveSessionConnectOptions` with no character field, so
`LiveSessionController.StartCore` falls through to
`CharacterList.TrySelectFirstAvailable`: which character enters world at
generation N+1 is resolved fresh from a server-supplied list, and
nothing in `RuntimeAllegianceState` keyed on character identity. The
process model does NOT preclude a cross-character reset on the
graphical host, so a stale allegiance tree surviving reset could present
as the WRONG character's monarch/rank/vassal list.
**Corrected semantics:** `RuntimeAllegianceState` is now ALSO a
`RuntimeGenerationReset` stage (`RuntimeGenerationResetStage.Allegiance`)
with the identical clear-and-relatch shape as `RuntimeFellowshipState`
and the `RuntimeCharacterOptionsState` precedent — the profile clears
AND `HasServerSeed` drops to `false` at every generation reset, not just
at terminal `Dispose`. The `HasServerSeed` latch's remaining job is
exactly what its name says: distinguishing "no profile has arrived THIS
generation" from "genuinely no allegiance" WITHIN a session — it is a
within-session rendering gate, not a cross-reconnect persistence
mechanism. The two owners remain separate classes (not merged into one)
because fellowship and allegiance are independent retail systems with
independent wire families, not because their lifetimes differ anymore.]**
Lane D's 8-edit template per owner; consumers poll via
`Snapshot.Revision` — no `IRuntimeEventObserver` member is added (that
would break all five bot policies + the trace recorder).
- **D3 — the H.2 scaffolding is connected, repaired, or deleted — never
@ -128,7 +172,7 @@ FA2, FA3, FA4, FA5, FA6, FA7.
| Slice | Contract (summary) | Gate |
|---|---|---|
| FA1 | Core.Net truth: repair fellowship builders + re-pin golden vectors; add `0x0290`/`0x0291`/`0x00A6`/`0x001F`; the allegiance action set the panel needs (swear/break/kick/info at minimum; the rest of the 27 as builders only where ACE serves them); parsers for the 11 S→C events incl. the profile version gates (extending `ParseAllegianceInfoResponse`) and the tree discard/reversal rules; complete the confirmation triple; ~~add the 8 WeenieError strings~~ **[FA1 fix-round addendum, 2026-08-12: shipped as CONFIRM-ABSENT instead — see D9's addendum. The 8 ids have no retail display text; acdream's silence is already faithful and a conformance test pins it.]**; DELETE `AllegianceTree` | automated only |
| FA2 | `RuntimeFellowshipState` + `RuntimeAllegianceState` (lane D's 8-edit template each); single-site inbound wiring serving BOTH hosts; typed commands/views; snapshot revisions; reset/reconnect semantics (session-scoped vs seed-latched); bot event surface via polling | automated only |
| FA2 | `RuntimeFellowshipState` + `RuntimeAllegianceState` (lane D's 8-edit template each); single-site inbound wiring serving BOTH hosts; typed commands/views; snapshot revisions; reset/reconnect semantics (~~session-scoped vs seed-latched~~ **[FA2 fix-round: both session-scoped — see D2's addendum]**); bot event surface via polling | automated only |
| FA3 | The social panel shell: mount slot `0x1000018F` (catalog id 12), F3/F4 handlers, fixture dump of the slot (closes U3/U4/U6/U7/U10), tab activation, all four pages' empty states, Friends/Squelch read-only binding to J4.1 state | user (connected) |
| FA4 | Fellowship page live: roster rows (adds `UiTemplateListBox` Flush/selection/row-instance-id — lane A sized this), the `0x00A6` show/hide declaration + vitals stream, create dialog (inline name field, shareXP), recruit/dismiss/quit/disband/leader + confirmations, share display per D5, option-row un-dims per D7 | user (connected) + bot |
| FA5 | Allegiance page live: profile parse on show (`0x027B`), monarch/patron/self blocks, flat vassal list with the reversal rule honored, swear/break/kick + confirmations, ACE zeroed-field presentation scoped honestly (register row) | user (connected) + bot |
@ -194,7 +238,7 @@ before anything builds on them.
| Slice | Status | Commit(s) | Review | Gate |
|---|---|---|---|---|
| FA1 | **CLOSED 2026-08-12** — narrow re-review verdict CLOSED, no reopen (`96df892d`, §7 of the mechanism findings doc: all 7 mechanism dispositions re-derived in the diffs, all 6 blast dispositions spot-verified, suite claim corroborated on post-fix binaries); re-review carry-forward CF-1 (two further stale `AllegianceTree` citations in the seam map's FA2 guidance, `:128`/`:214`) closed by coordinator addenda in the same commit as this ledger update — FA2 was contracted not to start before that | `7be86f47` (builders), `6bedbc47` (parsers), `5f9aa16f` (WeenieError), `4281750b` (delete AllegianceTree); fix-round: `ed308087` (mechanism+blast MUST/SHOULD-FIX code+tests), this commit (register/plan/seams doc corrections) | mechanism `docs/research/2026-08-12-fa1-review-mechanism.md` (2 MUST-FIX, 5 SHOULD-FIX, all applied); blast `docs/research/2026-08-12-fa1-review-blast.md` (4 MUST-FIX, 5 SHOULD-FIX, all applied). **Live-surface note (blast SF-1):** FA1 changed the observable output of the ALREADY-LIVE `@allegiance info` command in two retail-faithful ways — vassal print order reversed (now pinned by a 3-vassal test through `FormatAllegianceInfoLines`) and a malformed tree now prints nothing instead of a partial roster (now pinned at the `GameEventWiring` layer) — not a "purely unwired" slice. | automated: Release build + full suite green throughout. **Reconciled totals (blast MF-4):** the ledger's own prior figure (13,149/4/0) is CONFIRMED correct by direct measurement at the pre-fix-round tip `bc693728` (13,153 total = 4+916+1559+15+130+119+4856+877+4677 across all 9 test projects); the campaign-start baseline in §5 is 13,103/4/0, and the diff-verified FA1 delta is **+58 added / 9 deleted (deleted `AllegianceTreeTests.cs`) = net +49**, i.e. 13,103+49=13,152 — one test of drift against the directly-measured 13,153/13,149 baseline, attributed to the §5 baseline being captured at a different point in git history than the FA1 diff's actual parent, not a further miscount. This fix round adds a further **+9 tests** (2 golden vectors for the new `0x001F` builder, 1 D5 `<<1` pin at the `0x02C0` site, 4 MF-1/SF-1 boundary tests, 2 blast SF-1 live-surface pins) — **final: 13,158 passed / 4 skipped / 0 failed (13,162 total), directly measured.** |
| FA2 | **CODE-COMPLETE 2026-08-12** — automated gate only, per contract | `RuntimeFellowshipState`/`RuntimeAllegianceState` (2 new sibling J-owners, `src/AcDream.Runtime/Gameplay/`), the full 8-edit template applied twice (`GameRuntime.cs`, `RuntimeGenerationReset.cs`, `RuntimeGameplayOwnership.cs`/`RuntimeSimulationOwnership.cs`, `GameRuntimeGameplayViews.cs`, `GameRuntimeCommands.cs`, `GameRuntimeEvents.cs`, `GameRuntimeViews.cs`), 12 new `WorldSession.Send*` wrappers, 15 new `GameEventWiring.WireAll` delegate holes registered at the single site (`LiveSessionEventRouter.cs`), both `LiveSocialSessionBindings` construction sites updated (`LiveSessionRuntimeFactory.cs`, `HeadlessSessionHost.cs`), `IRuntimeFellowshipCommands`/`IRuntimeAllegianceCommands` implemented on both host command adapters (`DirectGameRuntimeCommandAdapter`, `CurrentGameRuntimeCommandAdapter` + its `LiveSessionCommandRouter`/`LiveSessionCommandBindings` App-bus plumbing), divergence register rows TS-81 (filed) + TS-80 (narrowed) | mechanism review not yet run (automated-gate slice per contract) | automated: Release build + full suite green throughout; **baseline 13,158/4/0 (13,162 total) → 13,201/4/0 (13,205 total), +43 tests, arithmetic exact** (`tests/AcDream.Runtime.Tests/Gameplay/RuntimeFellowshipStateTests.cs` +14, `RuntimeAllegianceStateTests.cs` +7, `RuntimeGenerationResetTests.cs` +1, `GameRuntimeTests.cs` +2 construction-fault-point cases, `Session/DirectGameRuntimeCommandAdapterTests.cs` +10, `tests/AcDream.Core.Net.Tests/GameEventWiringTests.cs` +9). Seam-doc correction found and fixed during implementation: §2.3's "the dispatcher supports multiple owned handlers per type — both fire" claim does not hold against `GameEventDispatcher.Dispatch` (`src/AcDream.Core.Net/Messages/GameEventDispatcher.cs:95-117`), which invokes only the single most-recently-registered handler per `GameEventType`; a literal second `registrar.Register(GameEventType.AllegianceInfoResponse, ...)` would have silently killed the already-live `@allegiance info` chat-text output. Fixed by folding both behaviors into the ONE existing registration instead (see `GameEventWiring.cs`'s AllegianceInfoResponse handler). Deferred: `0x027A AllegianceLoginNotification`'s two-line retail chat text — the candidate strings resolve through Binary-Ninja-mislabeled symbols (`gmAllegianceUI::RecvNotice_AllegianceLogin @0x00492220`) requiring a DAT string-table lookup not yet done; filed as register row TS-81 rather than inventing English. The seam doc's "borrow RuntimeEntityObjectLifetime for member guid → ClientObject" fellowship recommendation was not implemented — the wire's own `FellowMember`/`FellowshipUpdateFellow` records already carry name + full vitals inline, so no entity-table join is needed; noted, not forced. |
| FA2 | **FIX-ROUND CLOSED 2026-08-12** — both reviews' MUST-FIX/SHOULD-FIX findings applied; automated gate only, per contract | `RuntimeFellowshipState`/`RuntimeAllegianceState` (2 new sibling J-owners, `src/AcDream.Runtime/Gameplay/`), the full 8-edit template applied twice (`GameRuntime.cs`, `RuntimeGenerationReset.cs`, `RuntimeGameplayOwnership.cs`/`RuntimeSimulationOwnership.cs`, `GameRuntimeGameplayViews.cs`, `GameRuntimeCommands.cs`, `GameRuntimeEvents.cs`, `GameRuntimeViews.cs`), ~~12 new `WorldSession.Send*` wrappers, 15 new `GameEventWiring.WireAll` delegate holes~~ **[FA2 fix-round addendum, 2026-08-12 (docs/research/2026-08-12-fa2-review-blast.md SHOULD-FIX 3): both counts were wrong. `WorldSession.cs:2318-2404` adds **11** new `Send*` wrappers (7 fellowship + 4 allegiance) — `SendAllegianceInfoRequest` pre-dates FA2; the **12** figure belongs to a different count, `cced83b4`'s `*RuntimeCmd` records / `LiveSessionCommandBindings` send delegates. `GameEventWiring.cs:107-120` adds **10** delegate holes (9 new + the `0x027C` fold), not 15 — matching the "11 S→C events" the seam doc's §2.3 table names, of which 2 (`0x01C9`/`0x01CA`) are correctly left unregistered (dead COMDAT-fold no-ops) and 1 (`0x027C`) was already registered pre-FA2.]** registered at the single site (`LiveSessionEventRouter.cs`), both `LiveSocialSessionBindings` construction sites updated (`LiveSessionRuntimeFactory.cs`, `HeadlessSessionHost.cs`), `IRuntimeFellowshipCommands`/`IRuntimeAllegianceCommands` implemented on both host command adapters (`DirectGameRuntimeCommandAdapter`, `CurrentGameRuntimeCommandAdapter` + its `LiveSessionCommandRouter`/`LiveSessionCommandBindings` App-bus plumbing), divergence register rows TS-81 (filed) + TS-80 (narrowed); fix-round: `4272ad0e` (mechanism MUST-FIX 1/2 + blast MUST-FIX 1/2 + blast SF-1 + mechanism SF-2 — allegiance reset semantics, 0x027C stops seeding, teardown-ledger off-by-one, conditional delegate holes, disposed-checks-inside-lock), `ded23067` (mechanism SF-3/4/5/6 + blast SF-4/5/7 — RecalculateEvenXPSplitting, locked/departed admission gate, AllegianceProfileLookups reuse, non-null checkpoint defaults, router self/other-quit test, ResetSession disposed-guard parity, GetVassals allocation doc), this commit (register/plan/seams doc corrections) | mechanism `docs/research/2026-08-12-fa2-review-mechanism.md` (2 MUST-FIX, 6 SHOULD-FIX, all applied); blast `docs/research/2026-08-12-fa2-review-blast.md` (2 MUST-FIX, 7 SHOULD-FIX, all applied). **Allegiance register-row re-evaluation (blast SF-6):** the design blast SF-6 asked to be either dropped or given a register row is now fully retired by MF-1's fix — `RuntimeAllegianceState` clears at every generation reset exactly like `RuntimeFellowshipState` and exactly like retail's `OnEndCharacterSession` hook, so there is no remaining acdream-vs-retail deviation for allegiance persistence to name a row for. No register row added; conclusion recorded here per the fix-round contract. | automated: Release build + full suite green throughout. **Pre-fix-round baseline 13,201/4/0 (13,205 total, measured at `12053e61` by the blast review) → fix-round 13,215/4/0 (13,219 total), +14 tests, arithmetic exact:** `RuntimeAllegianceStateTests.cs` net +2 (1 deleted `ApplyInfoResponseSelf_...`, +3 new `ResetSession_*`), `RuntimeFellowshipStateTests.cs` +10 (1 `ResetSession_AfterDispose_...` + 3 `[Theory]` cases for `RecalculateEvenXPSplitting` + 1 `ShareXp`-off case + 1 full-update-never-recomputes case + 4 locked/departed admission-gate cases), `GameRuntimeTests.cs` +1 (`CompletedTeardownStagesAccumulatesExactlyOneFlagPerStage`), `Session/LiveSessionEventRouterTests.cs` +1 (`FellowshipQuit_RoutesSelfGuidToClearAndOtherGuidToRemove`); `RuntimeGenerationResetTests.cs` and `GameEventWiringTests.cs` each renamed one test in place (net 0); `GameRuntimeContractTests.cs` gained two trailing constructor arguments at its sole positional `RuntimeStateCheckpoint` site (compile-fix only, no new test). |
| FA3 | — | | | |
| FA4 | — | | | |
| FA5 | — | | | |