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

@ -146,6 +146,25 @@ Rationale and precedent:
(same class as `RuntimeCharacterOptionsState`'s `HasServerSeed` latch,
`project_settings_options_digest.md` DO-NOT-RETRY row "Applying a
pre-seed Reset/local snapshot over server truth").
**[FA2 fix-round addendum, 2026-08-12
(docs/research/2026-08-12-fa2-review-mechanism.md MUST-FIX 1): this
citation was INVERTED. `RuntimeCharacterOptionsState`'s `HasServerSeed`
latch does NOT survive a reset — `ResetSession`
(`RuntimeCharacterState.cs:1007-1017`) explicitly zeroes the options AND
clears `_hasServerSeed`, with its own doc comment naming the exact hazard
this row then walked into: "a stale seed surviving a session boundary
could let a flush ship the PRIOR character's words over the new one's."
Retail itself clears allegiance at the same per-character-session
boundary Fellowship already clears at
(`ClientAllegianceSystem::OnEndCharacterSession @0x00569FA0` tail-calls
`AllegianceProfile::Clear`, mirroring
`ClientFellowshipSystem::OnEndCharacterSession @0x005690A0`). Corrected:
allegiance is now ALSO a reset stage
(`RuntimeGenerationResetStage.Allegiance`) with the identical
clear-and-relatch shape as the cited precedent. The two owners remain
separate classes because fellowship and allegiance are independent
retail systems with independent wire families, not because their
lifetimes differ — they no longer do.]**
- **Borrow, never copy, the entity table.** Fellowship member rows must
read live names/vitals from `RuntimeEntityObjectLifetime`'s canonical
`ClientObjectTable` the way `RuntimeInventoryState` does
@ -264,6 +283,37 @@ existing lambda or add a second `registrar.Register` for the same type
`OwnedGameEventRegistrar.Register`, `:929-940`) and accept that both fire.
Decide this in the plan, explicitly.
**[FA2 fix-round addendum, 2026-08-12
(docs/research/2026-08-12-fa2-review-blast.md SHOULD-FIX 1): the
parenthetical above is WRONG and was disproved during FA2 implementation.
`GameEventDispatcher.Dispatch`
(`src/AcDream.Core.Net/Messages/GameEventDispatcher.cs:95-117`) does a
single `_handlers.TryGetValue(...)` and invokes the ONE stored
`RegistrationNode`; `OwnedGameEventRegistrar.Register` REPLACES the prior
registration for that `GameEventType`, it does not add a second co-owned
handler. A second `registrar.Register(GameEventType.AllegianceInfoResponse,
...)` call would have silently killed the already-live `@allegiance info`
chat-text output the instant a caller supplied an allegiance-owner
callback. FA2 folded both behaviors into the ONE existing registration
instead (`GameEventWiring.cs`'s `AllegianceInfoResponse` handler) — see
that method's own code comment for the corrected claim. This was also the
right call independent of the correction: FA2's fix round (MUST-FIX 2)
subsequently found that `0x027C` should not feed a Runtime callback at
all, since retail's own handler for it is print-only over a stack-local
profile — so the "second handler" question this section poses is now moot
either way.]**
`0x01C9 FellowshipFellowUpdateDone` and `0x01CA FellowshipFellowStatsDone`
from the table above are **[FA2 fix-round addendum, 2026-08-12
(docs/research/2026-08-12-fa2-review-blast.md SHOULD-FIX 2): correctly
left unregistered by FA2.** FA1 established that both COMDAT-fold onto the
identical no-op body in the Sept-2013 client and are parse-and-ignore only
(`src/AcDream.Core.Net/Messages/GameEvents.cs:779-803`) — there is nothing
for a Runtime owner to consume. This is the disposition for the two rows
this section's own title ("The 11 events FA must register") implied FA
owed a registration for; FA2 registered the other 9 plus the `0x027C`
fold, 10 total.]**
### 2.4 Bindings-record plumbing (both hosts)
`LiveSessionEventRouter`'s five binding records are the host-facing
@ -808,7 +858,7 @@ A/B should confirm this against retail rather than inferring it from ACE.
| Subsystem | Where new code goes | Pattern to follow | Precedent citation |
|---|---|---|---|
| Fellowship state owner | `src/AcDream.Runtime/Gameplay/RuntimeFellowshipState.cs` (new) | J4 owner: `IDisposable` + `ResetSession()` + `CaptureOwnership()`; borrow entity table | `RuntimeInventoryState` ctor `GameRuntime.cs:192-193`; snapshot shape `RuntimeCharacterState.cs:9-51` |
| Allegiance state owner | `src/AcDream.Runtime/Gameplay/RuntimeAllegianceState.cs` (new), ~~wrapping existing `AcDream.Core.Allegiance.AllegianceTree`~~ **[FA1 fix-round addendum, 2026-08-12: `AllegianceTree` was DELETED by Campaign FA slice FA1 (`4281750b`) — there is nothing left to wrap. Wrap `ClientCommandResponses.AllegianceProfileLookups` (the flat `AllegianceMemberRecord` list + `GetData`/`GetPatron`/`FindVassals` walk that replaced it) instead. FA2 is the very next slice — this is its seam-map row.]** | Same; **survives reconnect** — use a `HasServerSeed`-style latch | `RuntimeCharacterOptionsState` seed latch; ~~`AllegianceTree.cs:56-162`~~ **[FA1 fix-round addendum: see `ClientCommandResponses.cs:250-295` (`AllegianceProfileLookups`) instead]** |
| Allegiance state owner | `src/AcDream.Runtime/Gameplay/RuntimeAllegianceState.cs` (new), ~~wrapping existing `AcDream.Core.Allegiance.AllegianceTree`~~ **[FA1 fix-round addendum, 2026-08-12: `AllegianceTree` was DELETED by Campaign FA slice FA1 (`4281750b`) — there is nothing left to wrap. Wrap `ClientCommandResponses.AllegianceProfileLookups` (the flat `AllegianceMemberRecord` list + `GetData`/`GetPatron`/`FindVassals` walk that replaced it) instead. FA2 is the very next slice — this is its seam-map row.]** | Same; ~~**survives reconnect** — use a `HasServerSeed`-style latch~~ **[FA2 fix-round addendum, 2026-08-12 (docs/research/2026-08-12-fa2-review-mechanism.md MUST-FIX 1): WRONG — retail clears the allegiance profile at `OnEndCharacterSession`, the very same boundary Fellowship clears at. Allegiance is now a `RuntimeGenerationReset` stage exactly like Fellowship; the `HasServerSeed` latch clears AND re-arms every generation (matching the `RuntimeCharacterOptionsState` precedent's actual clear-and-relatch behavior, not the persist behavior this row previously (mis)cited it for).]** | `RuntimeCharacterOptionsState` seed latch; ~~`AllegianceTree.cs:56-162`~~ **[FA1 fix-round addendum: see `ClientCommandResponses.cs:250-295` (`AllegianceProfileLookups`) instead]** |
| Owner registration | `GameRuntime.cs` | ctor + `construction.Own` + fault point + `*Owner` prop + view prop + `CaptureOwnership` + shutdown step | `GameRuntime.cs:200-214`, `:419-429`, `:463-470`, `:545-567`, `:703-752` |
| Session reset | `RuntimeGenerationReset.cs` | new `RuntimeGenerationResetStage` ordinal + `Drain` case; **fellowship clears, allegiance re-seeds** | `:21-44`, `:236-349`; shared-stage example `:248-261` |
| Ownership rollup | `RuntimeGameplayOwnership.cs` | add to record + `Capture(...)` signature + `IsConverged` | `:8-21`, `:23-44` |
@ -850,8 +900,16 @@ A/B should confirm this against retail rather than inferring it from ACE.
3. **`0x027C AllegianceInfoResponse` already has a handler**
(`GameEventWiring.cs:192-198`, chat output for `@allegiance info`).
Does FA extend that lambda, register a second handler, or leave the
panel to use a different event? The dispatcher permits multiple owned
handlers per type — but two handlers writing to two different owners is
panel to use a different event? ~~The dispatcher permits multiple owned
handlers per type~~ **[FA2 fix-round addendum, 2026-08-12
(docs/research/2026-08-12-fa2-review-blast.md SHOULD-FIX 1): wrong —
`GameEventDispatcher.Dispatch` invokes only the single
most-recently-registered handler per type; a second `Register` call
REPLACES, it does not co-own. See §2.3's addendum for the full
correction. Answered: extend the existing lambda (done), and FA2's own
fix round then found the extension itself should not exist — retail's
0x027C handler is print-only, so no owner ever gets fed from it.]** —
but two handlers writing to two different owners is
the "two writable copies" smell.
4. **Which of the 6 dimmed rows actually gain consumers in FA v1?** Each
un-dim requires three coordinated edits (§4.2). Under-claiming is safe;
@ -908,8 +966,12 @@ A/B should confirm this against retail rather than inferring it from ACE.
parsers**, **no inbound registrations**, and **no F3/F4 handler**.
2. **Ownership: two new sibling J-owners under `GameRuntime`**
(`RuntimeFellowshipState`, `RuntimeAllegianceState`), NOT children of
`RuntimeCommunicationState` — the lifetimes differ (fellowship is
session-scoped, allegiance survives reconnect) and J4's own precedent is
`RuntimeCommunicationState` — ~~the lifetimes differ (fellowship is
session-scoped, allegiance survives reconnect)~~ **[FA2 fix-round
addendum, 2026-08-12: both are now session-scoped, cleared at every
generation reset — see §1.3's addendum. They remain separate owners
because they are independent retail systems, not because their
lifetimes differ]** and J4's own precedent is
one owner per coupled graph. Each costs eight mechanical edits, listed
with line-cited templates in §1.1.
3. **Inbound has exactly one registration site**