docs: FA6 — file #384 (allegiance swear ACE non-response) + register AD-87
docs/ISSUES.md #384 records the live-run evidence trail (six connected runs, the 0.005 m distance diagnostic, the confirmation-arrival diagnostic that never fires) behind AllegianceGateEnabled=false. docs/architecture/retail-divergence-register.md AD-87 records the honest divergence this deferral creates: the allegiance half of the FA6 bot gate is written and wired but unverified end-to-end over the wire, unlike the fellowship half which is proven live. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
5244e46daa
commit
022b1844e1
2 changed files with 74 additions and 0 deletions
|
|
@ -181,6 +181,7 @@ readiness/requeue adaptation. See
|
|||
| AD-84 | **Filed 2026-08-12 at Campaign FA slice FA5.** The Allegiance page's Swear button enable rule does not gate on "target is a player" — retail's `gmAllegianceUI::UpdateSwearButton @0x004908E0` enables Swear only when the current world selection `ACCWeenieObject::IsPlayer()` (lane C §1.3 step 1); acdream's UI layer has the same missing player-vs-non-player classification AD-83 already named for the Fellowship page's Recruit button, so `RefreshButtonStates` enables Swear for any selected, not-already-a-member, not-self target regardless of type. Same root cause and same disposition as AD-83, filed separately because it lives in a different controller/page. | `src/AcDream.App/UI/Layout/SocialAllegiancePageController.cs` (`RefreshButtonStates`) | Identical to AD-83's argument: acdream's `SelectionState`/world-object model has no cheap player classification at this UI seam; building one for two single enable-rules (Recruit, Swear) is a disproportionate addition, and the server still refuses a non-player Swear target the same way retail's own disabled button would have silently no-op'd. | A lit, clickable Swear button when a non-player object is selected — clicking it sends a Swear request the SERVER refuses. Not a wire-behavior gap (the swear/target end state is identical to retail's disabled-button no-op) — a UI-affordance divergence a screenshot comparison would catch. | `gmAllegianceUI::UpdateSwearButton @0x004908E0` (lane C §1.3 step 1); `ACCWeenieObject::IsPlayer` (unlocated exact VA, same as AD-83) |
|
||||
| AD-85 | **Filed 2026-08-12 at Campaign FA slice FA5.** The SAME unported `StringInfo` variable-substitution engine AD-81 filed for the Fellowship page's row/stats text (`StringInfo::InqString` → `StringTableMetaLanguage::UnescapeString`) also affects the Allegiance page's numeric fields and its three LOCAL confirmation dialogs: (1) **Numeric fields** — self/monarch followers (`0x10000252`/`0x10000258`), self rank (`0x10000253`), and the "experience passed up" text (`0x10000492` ×2, the vassal row's `0x10000269`) render as bare numbers with no surrounding retail words, same disposition as AD-81's `"{level} {pct}%"`. (2) **Swear/Break/Kick confirmation dialogs** — `MakeSwearConfirmationDialog`/`MakeBreakConfirmationDialog`/`MakeKickConfirmationDialog` bind the target's name into a `StringInfo` template (`ID_Allegiance_SwearConfirmation`/`BreakConfirmation`/`KickConfirmation`, lane C §5.1); this controller shows the RESOLVED retail template text VERBATIM (unsubstituted — the raw DAT string, whatever it says) when resolution succeeds, and falls back to the bare target name (also non-invented) when it does not. Neither path ever composes new English. (3) **The server-driven "accept incoming swear" dialog** (`ConfirmationType` 1, handled generically by `GameplayConfirmationController` — no allegiance-specific code) inherits the SAME gap from the wire side: ACE sends the target's bare `Name` as the ENTIRE confirmation message (lane C §6.4: `Player_Allegiance.cs:91`/`ConfirmationManager.cs:38`), not a formatted sentence — retail's own client wraps it via the identical `StringInfo` mechanism this row already covers. | `src/AcDream.App/UI/Layout/SocialAllegiancePageController.cs` (`RefreshSelfBlock`, `RefreshMonarchBlock`, `RefreshPatronBlock`, `UpdateRow`, `OnSwearClick`, `OnBreakClick`, `OnKickClick`) | Same argument as AD-81: porting `StringTableMetaLanguage` is a cross-cutting prerequisite, not allegiance-specific, and guessing its token/placeholder syntax to blend a name into an unverified sentence shape would risk silently-wrong substitution — exactly the guessing CLAUDE.md's workflow forbids. Using retail's own unsubstituted text verbatim (or the bare name) is the non-inventing alternative. | A user sees bare numbers instead of retail's full sentences for followers/rank/XP-passed-up, and the three local confirmation dialogs (plus the server-driven accept-swear dialog) show either an unsubstituted retail template or a bare name instead of "Swear allegiance to PlayerName?"-style prose. The underlying DATA (the numbers, the target's real name) is correct in every case — only the surrounding words are absent. | `gmAllegianceUI::UpdatePlayerData @0x00491330`, `UpdateMonarchData @0x00491B40`, `UpdatePatronData @0x004917C0`, `UpdateVassalsData @0x00492340` (lane C/A field sources); `MakeSwearConfirmationDialog @0x004927B0` family (lane A §5.1); `StringInfo::InqString @0x0042e490` (unresolved, same citation as AD-81) |
|
||||
| AD-86 | **Filed 2026-08-12 at Campaign FA slice FA5, item 4.** ACE deliberately zeroes or empties NINE `AllegianceProfile`/`AllegianceData` fields on the wire — officers, officer titles, MOTD, MOTD-set-by, name-last-set-time, lock state, and approved vassal are always empty/false/zero regardless of the allegiance's real state; `timeOnline`/`allegianceAge` (the remaining two) are hard-coded 0 forever (lane C §5.1). acdream's FA1 parser reads all of these (to keep the byte cursor aligned for the fields after them) but drops most at increasing layers: `AllegianceMemberRecord` never surfaces `timeOnline`/`allegianceAge` as fields at all; `RuntimeAllegianceState.ApplyUpdate` (FA2) does not forward `Motd`/`MotdSetBy`/`ChatRoomId`/`NameLastSetTime`/`IsLocked`/`ApprovedVassal` from the parsed `AllegianceUpdate` record to `RuntimeAllegianceSnapshot` even though the C# record itself carries them; retail's own `gmAllegianceUI` (FA5) has no widget for any of the seven either (lane A §3.3: "No allegiance MOTD / officer / ban / hometown UI" — they are chat-verb-only in the 2013 client, out of this campaign's scope per the plan's §4). | `src/AcDream.Core.Net/Messages/ClientCommandResponses.cs` (`ReadAllegianceProfileBody`, `AllegianceMemberRecord`, `AllegianceUpdate`); `src/AcDream.Runtime/Gameplay/RuntimeAllegianceState.cs` (`ApplyUpdate`) | Retail's own client renders nothing for these seven fields either (no panel widget consumes them) — dropping them past the parse layer matches retail's OWN presentation exactly, and is strictly safer than surfacing values that are always wrong/empty against ACE. | Any FUTURE consumer (the chat-verb-only officer/MOTD/lock/ban management features, §2 master table features #11-31 of the allegiance wire research, explicitly out of Campaign FA's scope) that reads these fields off the Runtime layer will find them permanently zero/empty against ACE regardless of the allegiance's real server-side state — do not chase this as a parser bug; it is ACE's own zeroing. | ACE `Network/Structure/AllegianceHierarchy.cs:53-56,62-64,74-75,78-83,86-89,153-155` (broadcast counters/isLocked/officers/officerTitles/motd/approvedVassal); ACE `Network/Structure/AllegianceData.cs:59-60,86-89,111-112` (timeOnline/allegianceAge); `docs/research/2026-08-11-fa-allegiance-wire.md` §5.1 |
|
||||
| AD-87 | **Filed 2026-08-12 at Campaign FA slice FA6.** The allegiance-swear half of the two-bot headless connected gate (`FellowshipAllegianceLeaderBotPolicy`/`FellowshipAllegianceRecruitBotPolicy`) is written and wired end-to-end (proximity, `0x001D` swear, the confirmation-relay seam, `0x0020` tree-reseed assertions, break, reconnect-idempotence) but has never actually been verified to complete over the wire — `AllegianceGateEnabled = false` in both classes keeps it unreachable by default. Six live runs against local ACE all reproduced the same result: the fellowship half passes decisively (the Recruit bot's own `RuntimeFellowshipState` flips, proven three separate times), but ACE returns nothing at all to the `0x001D` swear (no `0x0274` confirmation, no `0x0020`, no error) even at 0.005 m separation — see docs/ISSUES.md #384 for the full evidence trail. So while the FELLOWSHIP two-session machinery is proven live, the ALLEGIANCE two-session machinery (Runtime commands, wire builders, `RuntimeAllegianceState` reseed) remains unverified end-to-end over a real connection — only its unit/fixture-level tests and its (successful) LOCAL echo on the swearer's own client are exercised. | `src/AcDream.Headless/Policies/HeadlessBotPolicy.cs` (`FellowshipAllegianceLeaderBotPolicy.AllegianceGateEnabled`, `FellowshipAllegianceRecruitBotPolicy.AllegianceGateEnabled`, both `false`) | Shipping the fellowship gate ALONE (rather than blocking the whole slice on the allegiance blocker) matches the campaign's own D8/item-6 split — fellowship and allegiance are independent retail systems with independent wire families, and the fellowship half's proof stands on its own regardless of the allegiance outcome. Disabling rather than deleting the allegiance code keeps a reviewed-quality, ready-to-run harness in place for whoever closes #384. | Anyone reading "the FA6 bot-vs-ACE gate passed" without the qualifier could assume the allegiance swear/break/reconnect path is proven over the wire when it is not — only its LOCAL send-and-echo behavior is proven; ACE's actual acceptance of the swear is the open question #384 tracks. | docs/ISSUES.md #384; `docs/research/2026-08-11-fa-allegiance-wire.md` §1.3 (the expected `0x0274`/`0x0275`/`0x0020` handshake); run6 evidence (0.005 m distance, zero inbound after swear) |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue