Plan ledger: fellowship two-session automated gate PASSED live 2026-08-12 (five of six runs reproduced the decisive cross-session assertion); the allegiance bot gate is DEFERRED behind AllegianceGateEnabled=false pending docs/ISSUES.md #384, with commit citations for every fix this slice landed (confirmation relay, name-matched proximity, the fellowship-only finalization). Gate script §FA6: the fellowship automated-gate recipe + actual PASSED result (the two-session config, the six proof points per stage, the literal decisive-assertion log lines), the allegiance deferral writeup, and a new [TWO-CLIENT] manual step (25) the user's own connected gate can run to help disambiguate #384 (ACE-side rule vs wire-builder defect vs harness-specific drop) using two real graphical clients instead of the testaccount/testaccount2 pair. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
914 lines
50 KiB
Markdown
914 lines
50 KiB
Markdown
# Campaign FA connected-gate test script
|
||
|
||
**Status: FA3, FA4, and FA5 (below) all owe their connected gates.** Launch
|
||
with `ACDREAM_LIVE=1` against the local ACE server (`testaccount` /
|
||
`+Acdream`). Anything marked **INERT** is authored and clickable but
|
||
deliberately does nothing yet — that is the correct, contracted behavior
|
||
for this slice (D1), not a bug. **FA6's bot-vs-ACE fellowship gate PASSED
|
||
live 2026-08-12 (§FA6 below) and needs no connected re-gate; its
|
||
allegiance half is DEFERRED (`docs/ISSUES.md` #384) with one
|
||
`[TWO-CLIENT]` manual step in §FA6 that can help settle it.**
|
||
|
||
FA3 is the panel SHELL only: mount, F3/F4 open paths, tab switching, all
|
||
four pages' empty states, and Friends/Squelch read-only lists. Fellowship
|
||
roster rows, the create-fellowship dialog, live vitals, and every
|
||
Allegiance swear/break/kick action are FA4/FA5 scope — do not report their
|
||
absence here.
|
||
|
||
**FA4 makes the Fellowship page fully live** (§FA4 below): roster rows,
|
||
the `0x00A6` panel-open/vitals-stream gate, the create flow, member
|
||
actions + confirmations, the four option checkboxes, and the open/close
|
||
caption swap. **Honest limitation up front: several FA4 steps need a
|
||
SECOND character** (recruit, the inbound invite dialog, watching another
|
||
member's vitals update). If a second ACE account/character is not
|
||
available for this gate, run every step marked **[SOLO]** and defer the
|
||
steps marked **[TWO-CLIENT]** to Campaign FA slice FA6's bot-vs-ACE gate
|
||
(`docs/plans/2026-08-11-fellowship-allegiance-campaign.md`, D8) — do not
|
||
treat an unrun two-client step as a failure.
|
||
|
||
---
|
||
|
||
## FA3 — the social panel shell
|
||
|
||
### Opening the panel — F3/F4, keyboard only
|
||
|
||
1. **Press F3.** The social panel opens on its **Allegiance** tab (the
|
||
authored default tab — Allegiance is ALSO the left-most tab drawn on
|
||
the tab strip, at x=0; the default tab and the left-most tab are the
|
||
SAME tab, verified by re-deriving the authored tab table directly from
|
||
the fixture, the live-mount probe, and each page's own `P0x57`
|
||
action-map id — see `SocialPanelController`'s class doc for the full
|
||
citation. **[FA3 fix-round correction, mechanism MF-1]** an earlier
|
||
draft of this script claimed Friends was drawn left-most; that was
|
||
wrong). No toolbar button opens this panel — retail authors none
|
||
(lane A §6.1) — so there is nothing to click for this step besides the
|
||
keybind itself.
|
||
2. **Press F3 again** while the panel is open and already on the
|
||
Allegiance tab. The panel closes — same toggle-closes-on-repeat-press
|
||
shape as every other `Toggle*Panel` action (e.g. F11/Options,
|
||
Spellbook).
|
||
3. **Press F4.** The social panel opens directly on its **Fellowship**
|
||
tab (F4 switches AND opens in one press, even if the panel was
|
||
closed).
|
||
4. **With the panel open on Fellowship, press F3.** The panel does NOT
|
||
close — it switches to the Allegiance tab (F3 is scoped to its own
|
||
tab, not a blanket close). Symmetrically, pressing F4 while open on
|
||
Allegiance switches to Fellowship rather than closing.
|
||
5. **Click the panel's own close (X) button**, top-right of the window
|
||
chrome. The panel closes — same action as a repeat F3/F4 press on the
|
||
already-active tab.
|
||
6. **Drag the window by its border/chrome; resize it from the BOTTOM
|
||
edge only.** Same shared `gmPanelUI` geometry policy as every sibling
|
||
main panel (Options, Character, Inventory, Spellbook, the
|
||
indicator-detail panels): draggable, resizable from the bottom edge
|
||
only, remembers its height across a close/reopen within the session.
|
||
|
||
### Exclusivity vs sibling panels
|
||
|
||
7. **Open Character Info (or Inventory, or Options), then press F3.**
|
||
The other panel closes and the social panel opens on Allegiance — the
|
||
SAME retail `gmPanelUI` one-active-panel-at-a-time behavior every
|
||
sibling panel already has (`RetailPanelUiController.RegisterMainPanel`
|
||
shares one geometry rectangle across all ten registered main panels).
|
||
8. **With the social panel open, open Character Info.** The social panel
|
||
closes. Confirms the exclusivity is bidirectional, not just
|
||
"opening the social panel closes others."
|
||
|
||
### Tab switching
|
||
|
||
9. **Click each of the four tabs** (left-to-right authored order:
|
||
**Allegiance, Fellowship, Friends, Squelch** — only ONE tab is
|
||
highlighted "open" at a time. **[FA3 fix-round correction, mechanism
|
||
MF-1]** an earlier draft of this script had the order as
|
||
Friends/Allegiance/Fellowship/Squelch; that was wrong — re-derived
|
||
directly from the fixture's authored `0x2E` tab table). Each switches
|
||
the visible page; exactly one page is visible at a time. No crash, no
|
||
stuck state, switching back and forth repeatedly is safe.
|
||
10. **Note the tab captions read correctly, in this left-to-right order**
|
||
— "Allegiance", "Fellowship", "Friends", "Squelch" — not blank. A
|
||
blank caption on any tab button would be the #375 missing-string-
|
||
resolver class of bug; report it immediately if seen.
|
||
|
||
### Fellowship page — empty state
|
||
|
||
11. **On a character with NO fellowship** (the default state), open the
|
||
Fellowship tab. Expect: an editable fellowship-name text field, a
|
||
**Create Fellowship** button, and FOUR checkboxes (Ignore Fellowship
|
||
Requests / Auto-Accept Requests / Share XP / Share Loot — **[FA3
|
||
fix-round correction, mechanism SF-4]** an earlier draft hedged this
|
||
as "three... a fourth may also be present"; the fixture and the
|
||
decomp both settle it at four). No member list, no
|
||
leader/quit/open/recruit/dismiss/disband buttons visible — those
|
||
belong to the OTHER (in-fellowship) frame, which is hidden. **[FA4
|
||
correction]** the Create Fellowship button, the name field, and the
|
||
four checkboxes are now LIVE, not inert — see §FA4 below for their
|
||
full behavior; this step's own scope is only the frame/control
|
||
PRESENCE, still correct as written.
|
||
12. **If the test character IS currently in a fellowship** (uncommon for
|
||
`+Acdream`'s default state, but possible if a prior session left one
|
||
active, or if you ran §FA4's Create step below), open the Fellowship
|
||
tab instead expecting: a fellowship name display, a member roster
|
||
ListBox, and six buttons (Leader/Quit/Open/Recruit/Dismiss/Disband).
|
||
**[FA4 correction]** all six buttons are now LIVE — see §FA4.
|
||
|
||
### Allegiance page — empty state
|
||
|
||
13. **On a character with NO allegiance profile ever received this
|
||
session** (the default state — nothing has queried allegiance data
|
||
yet), open the Allegiance tab. Expect: the monarch block and patron
|
||
block BOTH HIDDEN (no visible "Monarch:" / "Patron:" labels or
|
||
name lines), while your own character line (name/followers/rank) and
|
||
the vassal list area remain visible per their authored layout. The
|
||
Swear/Break/Kick buttons and the "Ignore Allegiance Requests"
|
||
checkbox are present and **INERT**.
|
||
14. **This state does NOT change for the rest of this gate — that is the
|
||
correct, contracted FA3 behavior, not a bug. [FA3 fix-round
|
||
correction, mechanism MF-2]** an earlier draft of this script sent
|
||
you to `@allegiance info` expecting it to make the blocks visible,
|
||
and told you to report it if that didn't happen. That trigger CANNOT
|
||
fire and the instruction was wrong: FA3 never sends the `0x001F
|
||
AllegianceUpdateRequest` subscription (FA5 scope, per the plan's
|
||
corrected D6/CF-1 note), and `0x0020 AllegianceUpdate` — the ONLY
|
||
inbound message that can populate this panel's data — is never
|
||
provoked by anything FA3 does. Running `@allegiance info` in chat
|
||
still works exactly as before FA3 (it is an independent, already-
|
||
shipped command) and WILL print your monarch/patron/vassal data as
|
||
chat TEXT — but its response (`0x027C`) stopped seeding this panel's
|
||
profile back in FA2 (`4272ad0e`), so it has zero effect on this page.
|
||
**The correct, expected combination for this entire gate is:**
|
||
`@allegiance info` prints real data to the chat window (if you have
|
||
an allegiance), AND the Allegiance page's monarch/patron blocks stay
|
||
hidden throughout — report NEITHER half as a bug. The panel blocks
|
||
becoming visible at any point during this gate (see "What to report"
|
||
below) is the actual anomaly to watch for.
|
||
|
||
### Friends page — read-only list
|
||
|
||
15. **Open the Friends tab.** If the test account has any friends
|
||
server-side, their NAMES appear as rows in the list (no online/
|
||
offline styling, no icons — names only, this slice's explicit
|
||
scope). If the account has none, the list is empty (no placeholder
|
||
text is invented).
|
||
16. **The three action-shaped buttons and the "Appear Offline"-shaped
|
||
checkbox are INERT** — clicking/toggling them does nothing (register
|
||
row AD-79). This is CONTRACTED for FA3; do not report it.
|
||
|
||
### Squelch page — read-only list
|
||
|
||
17. **Open the Squelch tab.** If the test account has any squelched
|
||
characters or accounts, their names appear as rows (characters and
|
||
account-level squelches both listed, names only). Empty otherwise.
|
||
18. **The three action-shaped buttons are INERT** (register row AD-79,
|
||
same as Friends). CONTRACTED, do not report.
|
||
|
||
### Scrolling a long roster
|
||
|
||
**[FA3 fix-round addition, blast N-8/MF-1]** the two ListBoxes' own
|
||
scrollbars were unwired until this fix round — completely unreachable
|
||
past the visible extent, not merely awkward to use. This step exercises
|
||
the fix directly.
|
||
|
||
19. **If the test account's Friends or Squelch roster is longer than
|
||
fits in the panel's visible area** (the Friends box is 270×400 but
|
||
the panel itself is only ~300×362, so as few as a dozen or so
|
||
entries can already overflow — check either tab), **drag the
|
||
scrollbar thumb (or click the track/arrows) and confirm the rows
|
||
scroll and every name remains reachable**, all the way to the last
|
||
row. This is a **soft check** if the test account's roster is
|
||
genuinely too short to overflow — do not manufacture a long roster
|
||
just to exercise this step, but if the roster IS long enough, a
|
||
scrollbar that does not move the list, or a list that will not
|
||
scroll at all, IS a bug — report it.
|
||
|
||
### Live update while the panel is closed
|
||
|
||
20. **This is a soft check, not required to pass/fail the gate:** if
|
||
Friends/Squelch state changes server-side while the social panel is
|
||
CLOSED (e.g. via `@friend` chat commands, if any are wired), then
|
||
reopening the panel afterward should show the up-to-date list
|
||
immediately (the read-only binding polls every frame WHILE THE PANEL
|
||
IS VISIBLE — **[FA3 fix-round correction, blast SF-2]** an earlier
|
||
draft of this script said the poll runs "regardless of which tab or
|
||
window is visible"; that changed in the fix round — the rebuild is
|
||
now gated on the social panel's own visibility so it does no DAT
|
||
work while closed, but it still catches up in full the moment the
|
||
panel is shown again, so the user-visible outcome described here is
|
||
unchanged. See `SocialFriendsPageController`/
|
||
`SocialSquelchPageController`'s own doc comments). Report if the
|
||
list is stale on reopen.
|
||
|
||
### Window restore-open behavior across relaunch
|
||
|
||
**[FA3 fix-round addition, blast N-8]** stated honestly rather than left
|
||
untested: the social panel is NOT in acdream's short list of windows
|
||
whose OPEN/CLOSED state is force-reset every session
|
||
(`stateManagedVisibilityWindows` — Combat/JumpPowerbar/
|
||
ExternalContainer/Vendor). This is a deliberate, documented choice
|
||
(the FA seams doc), and it matches the EXACT SAME convention every
|
||
sibling main panel already uses — Options, Spellbook, Character,
|
||
Inventory, and Vitae all restore their own last open/closed state too.
|
||
|
||
21. **Open the social panel (F3 or F4), then close the client and
|
||
relaunch (or simply log out and back in without closing the
|
||
window).** The EXPECTED, non-bug behavior is that the social panel
|
||
reopens automatically in whatever tab/open state it was left in —
|
||
the SAME restore-open behavior every other main panel already has.
|
||
This is not something to report; it is only listed here so the
|
||
behavior is understood in advance rather than surprising anyone
|
||
mid-gate.
|
||
|
||
---
|
||
|
||
### What to report
|
||
|
||
- Any tab caption that renders blank instead of its retail name.
|
||
- The panel opening on the wrong DEFAULT tab (should be Allegiance, and
|
||
Allegiance is also the left-most tab) on a fresh F3 press with the
|
||
panel previously closed.
|
||
- F3/F4 failing to switch tabs while the panel is already open, or
|
||
closing the panel when it should only switch tabs.
|
||
- The panel NOT closing/opening exclusively with sibling `gmPanelUI`
|
||
panels (Character/Inventory/Options/etc.).
|
||
- The Fellowship page showing the WRONG frame for the character's actual
|
||
fellowship state (e.g. showing the roster/six-button frame while
|
||
`+Acdream` has no fellowship).
|
||
- The Allegiance page's monarch/patron blocks becoming VISIBLE at ANY
|
||
point during this gate — there is no code path in FA3 that can make
|
||
that happen (see step 14); if it happens, that is a real bug (an
|
||
accidental wire connection), not the false-defect trigger an earlier
|
||
draft of this script pointed at.
|
||
- Any INERT button/checkbox producing a VISIBLE effect (a Create
|
||
Fellowship, Swear, Friends, or Squelch action that unexpectedly does
|
||
something) — this would mean either an accidental wire connection or
|
||
a stale INERT claim in this script.
|
||
- A Friends/Squelch row showing anything other than a plain name (an
|
||
exception, a blank row, garbled text).
|
||
- A Friends/Squelch scrollbar that does not move the list, or a
|
||
longer-than-visible roster with no way to reach its later rows (step
|
||
19) — only when the test account's roster is actually long enough to
|
||
overflow.
|
||
- Any crash, hang, or exception in the log while opening/closing/
|
||
switching tabs repeatedly.
|
||
|
||
### Explicitly NOT in scope for this gate
|
||
|
||
- Allegiance monarch/patron/vassal LIVE population, swear/break/kick
|
||
wire sends, and their confirmation dialogs (FA5).
|
||
- Friends/Squelch add/remove/appear-offline/clear wire sends (D1 — out
|
||
of campaign scope entirely, register row AD-79).
|
||
- The bot-vs-ACE two-session fellowship gate (FA6).
|
||
|
||
---
|
||
|
||
## FA4 — the Fellowship page fully live
|
||
|
||
**Prerequisite:** open the Fellowship tab (F4, or click it from the
|
||
already-open panel). All step numbers below restart at 1 for this
|
||
section's own numbering.
|
||
|
||
### Create (name + shareXP) — [SOLO]
|
||
|
||
1. **With no fellowship, type a name into the fellowship-name field and
|
||
watch the Create Fellowship button.** It is DISABLED while the field
|
||
is empty/whitespace-only and ENABLES the instant you type a
|
||
non-blank character — this IS retail's refusal mechanism (lane B
|
||
§2.2: the button itself is the guard; there is no separate error
|
||
message to expect).
|
||
2. **Before clicking Create, toggle the "Share Fellowship Experience and
|
||
Luminance" checkbox** (on or off — your choice) and note its state.
|
||
3. **Click Create Fellowship.** The panel switches from the empty-state
|
||
frame to the populated frame: your fellowship's name appears, you
|
||
appear as the sole roster row (with your own name, level, and
|
||
vitals bars), and the six member-action buttons appear. **[fix-round
|
||
SF-7 correction: the previous wording here contradicted itself —
|
||
"only Quit" followed by "Disband and Open should ALSO be enabled" —
|
||
which would prime a tester to report correct behavior as a defect.]**
|
||
Quit, Disband, AND Open should ALL be enabled (you're the leader of a
|
||
real fellowship now); Leader and Dismiss stay DISABLED because no
|
||
other member is selected yet (see "button states" below).
|
||
4. **Check the ACE server confirms `ShareXP` matches what the checkbox
|
||
said** at the moment you clicked Create (a chat command like
|
||
`@fellow` status, or simply trusting the wire — this is a soft
|
||
check, not required to fail the gate over).
|
||
|
||
### Roster with a second character — [TWO-CLIENT] (bot alternative: FA6)
|
||
|
||
5. **Recruit a second character into the fellowship**: log a second
|
||
ACE account/character into a separate acdream (or retail) client,
|
||
select them in the world (click them), then click **Recruit** on the
|
||
first client. If accepted (see the invite steps below), the second
|
||
character appears as a NEW roster row on the first client —
|
||
confirm their name, level, and vitals bars render (not blank, not a
|
||
crash).
|
||
6. **With two members in the fellowship, scroll the roster if it
|
||
overflows the visible area** and confirm both rows remain reachable
|
||
(same scrollbar mechanism FA3 already gated, `UiTemplateListBox`).
|
||
|
||
### Vitals updating while the panel is open AND the frozen-roster
|
||
behavior when closed — [TWO-CLIENT], the `0x00A6` gate made observable
|
||
|
||
7. **With the Fellowship tab open and a second member in the
|
||
fellowship, have that second character take damage or use
|
||
stamina/mana** (attack a monster, cast a spell, etc.). Their
|
||
health/stamina/mana bars on the FIRST client's roster should update
|
||
within a second or two — this is the vitals stream D4 turns on
|
||
(`0x00A6` panel-open declaration gates ACE's `0x02C0` sends, lane B
|
||
§4.5).
|
||
8. **Close the social panel (or switch to a different tab), have the
|
||
second character take more damage, then reopen the Fellowship tab.**
|
||
EXPECTED: the roster shows the LAST vitals it had before the panel
|
||
closed (frozen), then updates live again within a second or two of
|
||
reopening — this is the direct, observable consequence of `0x00A6`
|
||
being sent `false` on hide and `true` on show. A roster that keeps
|
||
updating in real time WHILE THE PANEL IS CLOSED would mean D4 is not
|
||
actually gating anything — report that as a bug.
|
||
9. **[fix-round MUST-FIX 3, new step] Reconnect while the Fellowship page
|
||
is left open.** With the Fellowship tab open and a fellowship active,
|
||
force a disconnect/reconnect (kill the ACE connection, or use
|
||
whatever local reconnect trigger is available) WITHOUT closing the
|
||
social panel or switching tabs. Once the client re-enters the world
|
||
on the new session, confirm fellow vitals resume updating (repeat
|
||
step 7's damage-and-watch check). EXPECTED: vitals update normally on
|
||
the new session, exactly as before the reconnect. BUG if vitals stay
|
||
frozen for the rest of the new session — that means `0x00A6` was
|
||
never re-declared after the generation reset. (Re-review re-fix
|
||
2026-08-12: the re-declaration fires from the post-world
|
||
`EnteredWorld` seam via `SocialPanelController.RedeclareAfterWorldEntry`,
|
||
NOT the pre-world `ResetSessionDeclaration`, which only clears the
|
||
latch — declaring before world entry was silently dropped by the
|
||
world-gated command, the original defect.)
|
||
10. **Your OWN row's vitals should always update** (your own vitals are
|
||
driven by the existing player-vitals pipeline, not the fellowship
|
||
`0x02C0` stream) — this is expected and not a sign that `0x00A6` is
|
||
failing to gate the OTHER member's stream.
|
||
|
||
### Recruit/dismiss/quit/disband/leader flows with their confirmations
|
||
|
||
11. **[SOLO, self only]** With a solo (1-member) fellowship you lead,
|
||
click **Quit**. You leave the fellowship (no confirmation dialog for
|
||
Quit — retail has none, lane B §2.5) and the panel reverts to the
|
||
empty-state frame.
|
||
12. **[SOLO, self only]** Create another fellowship, then click
|
||
**Disband** instead. Same visible outcome (empty-state frame) via
|
||
the wire's `disband=true` flag — no confirmation dialog either.
|
||
13. **[TWO-CLIENT]** With two members, select the OTHER member's roster
|
||
row (click their name) and click **Dismiss**. They are removed from
|
||
the roster; their own client sees themselves leave the fellowship.
|
||
14. **[TWO-CLIENT]** With two members, select the other member's row
|
||
and click **Leader** (Assign Leadership). The `LeaderGuid` changes —
|
||
confirm via the leader's name tinting gold in the roster (this
|
||
controller's own visual cue — lane A's row template has no
|
||
dedicated leader marker, see `SocialFellowshipPageController`'s
|
||
class doc, register row AD-82) — and confirm the enable/disable
|
||
states flip: the OLD leader's Disband/Open buttons should now be
|
||
DISABLED, the NEW leader's (on their own client) should now be
|
||
ENABLED.
|
||
15. **[TWO-CLIENT, the leader-handoff rule]** As the CURRENT leader of a
|
||
2+-member fellowship, click **Quit** (not Disband). EXPECTED:
|
||
leadership transfers to the other member FIRST (their client should
|
||
briefly show themselves as leader), THEN you leave — this is
|
||
retail's pre-quit leader hand-off (lane B §2.5/§3.6, already ported
|
||
in `RuntimeFellowshipState.RequiresLeaderHandoffBeforeQuit`). Report
|
||
if the fellowship is left leaderless or the WRONG member becomes
|
||
leader.
|
||
16. **[fix-round MUST-FIX 4, new step] Select a fellow in the WORLD
|
||
(click their 3D model), not their panel row.** EXPECTED: Dismiss and
|
||
Assign-Leader enable exactly as if you had clicked their panel row,
|
||
and their row shows the panel's own selection highlight — retail's
|
||
`gmFellowshipUI::UpdateFellowSelection` reverse-selection arm (world
|
||
click selects the panel row too), reproduced against acdream's own
|
||
guid-keyed row tracking rather than a ported generic ListBox
|
||
selection model (register row AD-82).
|
||
|
||
### The open-toggle caption swap — [SOLO]
|
||
|
||
17. **With a fellowship active, note the Open/Close button's caption.**
|
||
A CLOSED fellowship shows **"Open"**; click it and it should flip to
|
||
show **"Close"** IMMEDIATELY on click, not after a delay (retail's
|
||
button reads as the ACTION available, not the current state, and
|
||
pre-toggles its own state before the server echo — lane A §4.1,
|
||
fix-round NIT N-0). Click again to flip back.
|
||
18. **The Open/Close button is enabled only when you are the leader** —
|
||
confirm it is disabled (greyed / unclickable) if you are a member
|
||
but not the leader (needs a second client to observe from the
|
||
non-leader side).
|
||
|
||
### The invite dialog — [TWO-CLIENT]
|
||
|
||
**[fix-round MUST-FIX 2 correction, 2026-08-12: the OLD steps 18/19 here
|
||
tested whether acdream's CLIENT gates the invite dialog on the
|
||
Ignore/AutoAccept option bits. That mechanism never existed in retail and
|
||
has been deleted from acdream (D6's correction) — retail's client shows
|
||
the confirmation dialog UNCONDITIONALLY for every request it receives;
|
||
ACE alone decides whether to send one at all, by filtering
|
||
server-side. The steps below test the CORRECTED behavior: the dialog
|
||
always shows regardless of the SECOND client's own checkbox state. If
|
||
your local ACE build implements the server-side filter (lane B: refuses
|
||
the recruiter outright when the target has Ignore set; auto-joins
|
||
without a confirmation when the target has AutoAccept set), you will
|
||
observe the FIRST client's Recruit failing/auto-succeeding with NO
|
||
dialog ever reaching the second client at all — that is ACE's behavior,
|
||
not a client bug, and is out of scope for this gate either way.]**
|
||
|
||
19. **Baseline: on the SECOND client, ensure both "Ignore Fellowship
|
||
Requests" and "Automatically Accept Fellowship Requests" are
|
||
UNCHECKED** (Character tab or this page's own checkboxes — either
|
||
surface, they're the same value). From the FIRST client (in a
|
||
fellowship, as leader or with Recruit rights), select the second
|
||
character in the world and click **Recruit**. The SECOND client
|
||
should show a confirmation dialog asking to join the fellowship,
|
||
with the server's message text rendered VERBATIM (no added
|
||
"Continue?" suffix — type 4 is not in the 2/3/5/6 suffix set).
|
||
**Accept it** — the second character joins, appears on the first
|
||
client's roster.
|
||
20. **Repeat with "Ignore Fellowship Requests" CHECKED on the second
|
||
client (the FIX ROUND's regression check — this is the DEFAULT
|
||
state for a character who has never touched the option).** EXPECTED:
|
||
the dialog STILL shows on the second client exactly as in step 19
|
||
(assuming your local ACE does not itself refuse the recruit
|
||
server-side first) — the second character's OWN checkbox state must
|
||
NOT silently swallow the invite with no dialog and no chat line. If
|
||
it does, that is the exact regression MUST-FIX 2 exists to prevent.
|
||
21. **Repeat with "Automatically Accept Fellowship Requests" CHECKED on
|
||
the second client** (and Ignore unchecked — the two are mutually
|
||
exclusive; checking one should auto-uncheck the other, confirm that
|
||
too). Same expectation as step 20: the dialog still shows on the
|
||
client (acdream no longer auto-responds on its behalf); if your
|
||
local ACE implements the auto-accept-without-confirmation
|
||
server-side behavior, you may instead see the character join
|
||
immediately with no dialog — that is ACE's mechanism, not this
|
||
client's.
|
||
22. **Reject an invite** (baseline state, dialog showing): click Reject
|
||
instead of Accept. The second character does NOT join; no crash or
|
||
stuck dialog state on either client.
|
||
|
||
### Share-column expectations vs ACE — [SOLO, low member counts]
|
||
|
||
23. **With a solo (1-member) fellowship, Share XP checked, the stats
|
||
text should read a percentage of 100%** (the even-split table's
|
||
first entry, lane B §7.2). With 2+ members (if a second client is
|
||
available) sharing evenly, the percentage should drop per the
|
||
table (75% at 2, 60% at 3, …). **This is a SOFT / informational
|
||
check, not a fail-the-gate item:** if you happen to reach exactly 9
|
||
members, the panel will show **31%** (retail's own 0.3111111) while
|
||
ACE's actual XP grant math currently uses .3 — a KNOWN, filed
|
||
divergence between ACE and retail, not an acdream bug (register row
|
||
AD-80). Do not report a 9-member mismatch between the panel's
|
||
percentage and the XP you actually received.
|
||
24. **[fix-round MUST-FIX 1, new step — a HARD check, not soft like step
|
||
23's ACE note] At exactly 6 and 8 fellows (even split, Share XP
|
||
checked), the panel must show 44% and 34% respectively — NOT 45% or
|
||
35%.** Retail TRUNCATES the percentage (`_ftol2`), it does not
|
||
round; both stored float constants (`0.44999998807907104` at 6,
|
||
`0.3499999940395355` at 8) sit just BELOW the nice decimal, so
|
||
retail's own truncation lands one point lower than naive rounding
|
||
would produce. This is a genuine acdream-vs-retail bug if it shows
|
||
45%/35% instead of 44%/34% — unlike step 23's 9-member note, this is
|
||
NOT an ACE-divergence exemption.
|
||
25. **With Share XP UNCHECKED, the stats text should read "0%"** for
|
||
every member (retail's own literal `pct = 0.0f` branch, not a gap).
|
||
|
||
### What to report (FA4-specific — in addition to the FA3 list above)
|
||
|
||
- The Create button staying enabled with an empty/whitespace-only name
|
||
field, or staying disabled once real text is typed.
|
||
- Any roster row showing a blank name, a frozen level, or a vitals bar
|
||
that never updates AT ALL while the panel is open and a second member
|
||
is taking damage (contrast with step 8's EXPECTED freeze while the
|
||
panel is CLOSED — that one is correct, not a bug).
|
||
- Fellow vitals staying frozen for the REST OF A NEW SESSION after a
|
||
reconnect (step 9) — distinct from step 8's expected freeze, which
|
||
clears within a second or two of reopening the tab.
|
||
- A membership change (join/leave) resetting your scroll position to the
|
||
top of a long roster — the whole point of the FA3 carry-forward this
|
||
slice closed (`UiTemplateListBox.FlushPreservingScroll`).
|
||
- Quit/Disband/Dismiss/AssignLeader/SetOpen not reaching the server (no
|
||
visible effect on either client), or reaching it with the WRONG guid
|
||
(e.g. Dismiss removing the wrong member).
|
||
- The leader hand-off (step 15) leaving the fellowship leaderless, or
|
||
transferring leadership to the wrong member.
|
||
- Selecting a fellow in the WORLD (step 16) NOT enabling Dismiss/Leader
|
||
or NOT highlighting their row.
|
||
- An invite dialog NOT appearing on the second client regardless of that
|
||
client's own Ignore/Auto-Accept checkbox state (steps 19-21) — unless
|
||
your local ACE build itself refuses/auto-joins the recruit
|
||
server-side first, in which case no request ever reaches the second
|
||
client and that is expected, not a bug.
|
||
- The two option checkboxes NOT staying mutually exclusive (both ending
|
||
up checked at once).
|
||
- A 6- or 8-fellow percentage reading 45%/35% instead of 44%/34% (step
|
||
24 — a real acdream bug, not an ACE-divergence exemption).
|
||
- Any crash, hang, or exception in the log during create/recruit/
|
||
dismiss/quit/disband/leader/open actions, during a reconnect with the
|
||
panel left open, or while the invite dialog is open.
|
||
|
||
### Explicitly NOT in scope for this gate (FA4)
|
||
|
||
- Allegiance monarch/patron/vassal LIVE population, swear/break/kick
|
||
wire sends, and their confirmation dialogs (FA5).
|
||
- Friends/Squelch add/remove/appear-offline/clear wire sends (D1 — out
|
||
of campaign scope entirely, register row AD-79).
|
||
- The bot-vs-ACE two-session fellowship gate (FA6) — every step marked
|
||
**[TWO-CLIENT]** above may be deferred there if a second account is
|
||
not available for this connected gate.
|
||
- Retail's exact `StringInfo`-templated row/stats sentence and
|
||
`ACCharGenData::FormatName` name canonicalization — acdream renders
|
||
plain numeric composites and the raw typed name instead (register row
|
||
AD-81); do not report "the text doesn't read like a full sentence" or
|
||
"my typed name wasn't auto-capitalized" as bugs.
|
||
|
||
---
|
||
|
||
## FA5 — the Allegiance page fully live
|
||
|
||
**Prerequisite:** open the Allegiance tab (F3, or click it — it is the
|
||
authored DEFAULT tab, so simply pressing F3 from a cold start lands here).
|
||
All step numbers below restart at 1 for this section's own numbering.
|
||
|
||
**Honest limitation up front, same shape as FA4's:** swearing allegiance
|
||
genuinely needs a SECOND character (you cannot swear to yourself, and
|
||
retail requires the target to already be online and selected in the
|
||
world). If a second ACE account/character is not available, run every
|
||
step marked **[SOLO]** and defer the steps marked **[TWO-CLIENT]** to
|
||
Campaign FA slice FA6's bot-vs-ACE gate
|
||
(`docs/plans/2026-08-11-fellowship-allegiance-campaign.md`, D8) — do not
|
||
treat an unrun two-client step as a failure.
|
||
|
||
### The data subscription (CF-1) — profile populates on SHOW, not on
|
||
`@allegiance info` — [SOLO]
|
||
|
||
**[FA2 re-review CF-1 correction, carried into this slice's own contract:
|
||
the panel's data comes from `0x001F AllegianceUpdateRequest` → `0x0020
|
||
AllegianceUpdate`, NOT from the `0x027B`/`0x027C` `@allegiance info`
|
||
chat-command pair, which is text-only and does not feed the panel (FA2
|
||
MF-2's fix). Do not use `@allegiance info` as a trigger for anything in
|
||
this section.]**
|
||
|
||
1. **With NO allegiance (a fresh character), open the Allegiance tab.**
|
||
The self line (your name/rank/follower count) renders at the top;
|
||
both the monarch and patron blocks are HIDDEN (no allegiance means
|
||
neither relationship exists) — the empty state FA3 already gated, now
|
||
reachable live.
|
||
2. **If you already have an allegiance (monarch and/or patron), the
|
||
blocks should populate WITHIN A SECOND OR TWO of opening the tab** —
|
||
this is the `0x001F(1)` → `0x0020` round trip. If the blocks stay
|
||
empty indefinitely after opening the tab, that is the CF-1 subscribe
|
||
failing to reach the server — a real bug, not a display gap.
|
||
3. **Close the panel (or switch to Fellowship), then reopen the
|
||
Allegiance tab.** The data should still be there (or repopulate
|
||
quickly) — closing/reopening exercises the visible-branch toggle
|
||
(`0x001F(0)` on hide, `0x001F(1)` on show) without losing the profile.
|
||
4. **[reconnect, the FA4 MF-3-REOPEN analogue] Force a disconnect/
|
||
reconnect while ANY allegiance data was previously visible.** Once
|
||
the client re-enters the world on the new session, open (or confirm
|
||
already-open) the Allegiance tab. EXPECTED: the profile repopulates
|
||
on the new session within a second or two — this is
|
||
`RedeclareAfterWorldEntry`'s UNCONDITIONAL post-world `0x001F(1)`
|
||
resend, which fires regardless of whether the panel happens to be
|
||
open at that exact moment (retail's own `RecvNotice_PlayerDescReceived`
|
||
arm does not check panel visibility either — see the class doc's CF-1
|
||
section). BUG if the profile stays empty/stale for the rest of the new
|
||
session — that would mean the exact bug class MF-3-REOPEN caught for
|
||
Fellowship's `0x00A6` recurred here for `0x001F`.
|
||
|
||
### Monarch / patron / self blocks — [SOLO if you already have an
|
||
allegiance, otherwise needs a TWO-CLIENT swear first]
|
||
|
||
5. **Your own line (top of the page) always shows your allegiance's
|
||
name, your follower count, and your rank as plain numbers** — retail's
|
||
exact sentence wording is NOT reproduced (the unported `StringInfo`
|
||
substitution engine, register row AD-85, same class as FA4's AD-81);
|
||
do not report "it just shows a number, not a sentence" as a bug.
|
||
6. **If you are a VASSAL (you have a patron who is not the monarch)**,
|
||
the patron block shows their name and a follower-adjacent
|
||
"experience passed up" number. If your patron IS the monarch
|
||
(single-tier allegiance), the patron block should be HIDDEN and the
|
||
monarch block should show an ADDITIONAL sub-line (the
|
||
"experience passed up" number) that does not appear for a
|
||
multi-tier allegiance — this is retail's `PatronSlashMonarchLabel`
|
||
swap (fix-round SF-7, the coarser FA3 gate's own owed correction).
|
||
7. **If you ARE the monarch of your allegiance**, the monarch block
|
||
should be HIDDEN (retail hides it when the monarch IS the viewer,
|
||
not just when there is no monarch at all) — confirm this specifically
|
||
if you can arrange to be a monarch; it is easy to miss since "no
|
||
monarch block" looks identical whether you have no allegiance at all
|
||
or you're sitting at the top of one.
|
||
8. **A logged-out monarch or patron's block should render visually
|
||
dimmed** (their name still shows, but the block itself looks
|
||
"greyed"/disabled) — retail's `SetState(1)`/`SetState(0xD)` per-block
|
||
online/offline toggle. This needs a second client to log out while
|
||
you're watching, so mark it **[TWO-CLIENT]** if you can't arrange it
|
||
solo.
|
||
|
||
### Vassal list — [TWO-CLIENT for a populated list; SOLO to confirm the
|
||
empty case]
|
||
|
||
9. **With no vassals, the vassal list box is simply empty** (no
|
||
placeholder text, matching retail — there is no "you have no vassals"
|
||
frame the way Fellowship has an empty-state frame swap).
|
||
10. **With one or more vassals (needs a second character sworn to you —
|
||
see the swear steps below), each row shows the vassal's name and an
|
||
"experience passed up" number** (their OWN tithed amount, not
|
||
yours — distinct from the monarch/patron block's number, which is
|
||
YOUR OWN tithed amount).
|
||
11. **An OFFLINE vassal's row should visually distinguish itself**
|
||
(acdream tints the name grey rather than porting retail's dedicated
|
||
offline-marker element visually — confirm SOME visible difference
|
||
between an online and offline vassal row).
|
||
12. **With several vassals, scroll the list if it overflows the visible
|
||
area** and confirm every row remains reachable via the scrollbar
|
||
(same `UiTemplateListBox` mechanism FA3/FA4 already gated).
|
||
13. **Sibling order — a SOFT/informational check, not a fail-the-gate
|
||
item.** If you can arrange three or more vassals sworn to the same
|
||
patron in a KNOWN order, the list should render them in REVERSE
|
||
swear order (the last one to swear appears FIRST) — this is retail's
|
||
own tree-assembly rule (lane C §4.4 point 3), not a display bug. Hard
|
||
to verify without careful setup; report what you observe but do not
|
||
treat a mismatch as an automatic fail without double-checking the
|
||
actual swear order first.
|
||
|
||
### Swear — [TWO-CLIENT]
|
||
|
||
14. **On the SECOND client (the would-be vassal, with NO current
|
||
patron), select the FIRST client's character in the world** (click
|
||
their 3D model) and click **Swear** on the Allegiance page.
|
||
EXPECTED: a confirmation dialog appears on the SECOND client asking
|
||
to confirm swearing allegiance — the message is whatever retail's
|
||
template resolves to VERBATIM, or the target's bare name if the
|
||
template did not resolve (register row AD-85 — do not report "the
|
||
dialog doesn't read like a full sentence" as a bug).
|
||
15. **Accept the dialog on the second client.** EXPECTED: the FIRST
|
||
client (the new patron) receives ITS OWN confirmation dialog asking
|
||
to accept the new vassal — this is the SERVER-DRIVEN
|
||
`ConfirmationType` 1 round trip (`GameplayConfirmationController`,
|
||
generic, no allegiance-specific code), showing ACE's own message
|
||
(typically just the vassal's bare name, lane C §6.4 — again, not a
|
||
full sentence; not a bug).
|
||
16. **Accept the dialog on the first client too.** Both clients' Allegiance
|
||
pages should update: the second character now shows a patron block
|
||
naming the first character; the first character now shows the second
|
||
character as a new row in their vassal list.
|
||
17. **Swear button enable rule** — with NO current patron and a player
|
||
selected in the world who is not already in your allegiance, Swear
|
||
should be ENABLED; with a patron already sworn, or nothing selected,
|
||
or yourself selected, it should be DISABLED. **[register row AD-84]**
|
||
acdream does not check "is the selection a player" the way retail
|
||
does — selecting a non-player object (a monster, an item) may leave
|
||
Swear lit when retail would grey it out; the server refuses the send
|
||
either way, so this is a cosmetic gap, not a wire-behavior bug — do
|
||
not fail the gate over it, but do note it if you observe it.
|
||
18. **Reject the swear-target dialog (step 14) instead of accepting.** No
|
||
dialog reaches the first client; nothing changes on either side.
|
||
19. **Reject the accept-swear dialog (step 15) instead of accepting.** The
|
||
second character does NOT become a vassal; both clients' allegiance
|
||
state stays as it was before step 14.
|
||
|
||
### Break and Kick — [TWO-CLIENT] (needs an existing swear relationship
|
||
from the steps above, or a pre-existing allegiance)
|
||
|
||
20. **As the VASSAL, click Break.** A confirmation dialog appears asking
|
||
to break allegiance FROM your CURRENT patron (target latched at
|
||
click time, matching retail — changing your world selection after
|
||
clicking Break should NOT change who you break from, since Break's
|
||
target is your patron, not a world selection at all). Accept it:
|
||
your patron block clears/hides; the patron's vassal list loses your
|
||
row.
|
||
21. **As the PATRON, select a vassal row (click their name in the vassal
|
||
list — NOT the world) and click Kick.** A confirmation dialog
|
||
appears asking to kick that SPECIFIC vassal. Accept it: they are
|
||
removed from your vassal list; their own client's patron block
|
||
clears/hides.
|
||
22. **Kick button enable rule** — with no vassal row selected, Kick
|
||
should be DISABLED; select any vassal row and it should ENABLE.
|
||
23. **Break button enable rule** — with no patron, Break should be
|
||
DISABLED; with any patron, it should be ENABLED.
|
||
24. **Reject either dialog** (Break or Kick) instead of accepting — no
|
||
change on either client, no crash or stuck dialog state.
|
||
|
||
### The `IgnoreAllegianceRequests` checkbox — [SOLO]
|
||
|
||
25. **The checkbox on this page and the SAME-named row on the Options →
|
||
Character tab are the SAME value** — toggling one should reflect on
|
||
the other (open both surfaces side by side, or toggle-then-reopen to
|
||
confirm). This bit is a pure server-side filter with no client-side
|
||
consumer in retail either (lane C §1.6) — checking it does not change
|
||
any OTHER client-visible behavior; do not report "checking it doesn't
|
||
seem to do anything" as a bug, that is correct.
|
||
|
||
### ACE-zeroed fields — honest, not a bug
|
||
|
||
26. **You will never see officer names, a message-of-the-day, a lock
|
||
indicator, or an "allegiance age" anywhere on this page.** Retail's
|
||
OWN `gmAllegianceUI` has no widgets for any of these either (they are
|
||
chat-verb-only in the 2013 client — `@allegiance officer`, `@allegiance
|
||
motd`, etc., all out of this campaign's scope) — do not report their
|
||
absence as a bug. Separately, even the CHAT-VERB versions of these
|
||
features (if you try them) will show blank/default values against
|
||
the currently-targeted ACE build, because ACE deliberately zeroes
|
||
these fields on the wire regardless of the allegiance's real state
|
||
(register row AD-86) — that is an ACE-vs-retail divergence, not an
|
||
acdream bug either way.
|
||
|
||
### What to report (FA5-specific — in addition to the FA3/FA4 lists above)
|
||
|
||
- The Allegiance tab NOT being the panel's default (step 1 of §FA3 already
|
||
covers the tab itself; this section is about its DATA) — the profile
|
||
never populating after opening the tab (step 2), or staying stale
|
||
after a reconnect (step 4).
|
||
- The monarch block staying VISIBLE while you are the monarch yourself
|
||
(step 7), or the patron block staying visible while your patron is the
|
||
monarch (step 6) — both are the exact SF-7 per-relationship gate this
|
||
slice fixed; a coarse "both blocks show whenever I have ANY profile"
|
||
regression would be the FA3 bug resurfacing.
|
||
- The monarch/patron "experience passed up" sub-line failing to appear
|
||
under the correct block per step 6's single-tier-vs-multi-tier
|
||
distinction.
|
||
- A vassal row not appearing at all after a successful swear (step 16),
|
||
or an online/offline vassal being visually indistinguishable (step 11).
|
||
- Swear/Break/Kick sending the WRONG target guid — e.g. Break targeting
|
||
something other than your actual current patron, or Kick targeting a
|
||
vassal other than the one you clicked.
|
||
- Either confirmation-dialog direction of Swear (steps 14/15) not
|
||
appearing, or accepting one but not sending the matching wire command.
|
||
- The `IgnoreAllegianceRequests` checkbox NOT staying in sync between
|
||
this page and the Options → Character tab (step 25).
|
||
- Any crash, hang, or exception in the log during swear/break/kick,
|
||
during a reconnect with the Allegiance tab open, or while a
|
||
confirmation dialog is open.
|
||
|
||
### Explicitly NOT in scope for this gate (FA5)
|
||
|
||
- The bot-vs-ACE two-session swear gate (FA6) — every step marked
|
||
**[TWO-CLIENT]** above may be deferred there if a second account is
|
||
not available for this connected gate.
|
||
- Retail's exact `StringInfo`-templated sentences for followers/rank/
|
||
experience-passed-up and the three confirmation dialogs — acdream
|
||
renders plain numbers and, for dialogs, either retail's unsubstituted
|
||
template text or the bare target name (register row AD-85); do not
|
||
report "it just shows a number" or "the dialog doesn't read like a
|
||
full sentence" as bugs.
|
||
- Officer/MOTD/lock/ban/hometown management — chat-verb-only in retail,
|
||
out of this campaign's scope entirely (§4 of the plan), and unreliable
|
||
against the currently-targeted ACE build regardless (register row
|
||
AD-86).
|
||
- Swear's missing "target is a player" enable-rule check (register row
|
||
AD-84) — a cosmetic superset-of-retail gap, not a wire-behavior bug.
|
||
|
||
---
|
||
|
||
## FA6 — the bot-vs-ACE two-session gate
|
||
|
||
Unlike FA3-FA5, this is not a graphical-client gate: no window is
|
||
launched. The coordinator runs `acdream-headless run` with a config
|
||
declaring TWO sessions — `testaccount`/`+Acdream` as the fellowship
|
||
LEADER/allegiance PATRON, `testaccount2` as the fellowship RECRUIT/
|
||
allegiance VASSAL — both selecting policy id `fellowship-allegiance-gate`
|
||
with `role: "leader"`/`role: "recruit"`. Both sessions run in the SAME
|
||
process, ticked sequentially on one dedicated update thread.
|
||
|
||
### Automated fellowship gate — the shipped, passing part
|
||
|
||
**Recipe:**
|
||
|
||
```json
|
||
{
|
||
"version": 1,
|
||
"sessions": [
|
||
{
|
||
"id": "fa6-bot-a-leader",
|
||
"endpoint": { "host": "127.0.0.1", "port": 9000 },
|
||
"account": "testaccount",
|
||
"character": { "name": "+Acdream" },
|
||
"policy": { "id": "fellowship-allegiance-gate", "role": "leader" },
|
||
"credential": { "provider": "environment", "reference": "FA6_BOT_A_PASSWORD" }
|
||
},
|
||
{
|
||
"id": "fa6-bot-b-recruit",
|
||
"endpoint": { "host": "127.0.0.1", "port": 9000 },
|
||
"account": "testaccount2",
|
||
"character": { "index": 0 },
|
||
"policy": { "id": "fellowship-allegiance-gate", "role": "recruit" },
|
||
"credential": { "provider": "environment", "reference": "FA6_BOT_B_PASSWORD" },
|
||
"characterOptions": { "FellowshipAutoAcceptRequests": true }
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
The Recruit session's `character` selector deliberately does NOT hard-code
|
||
a name — `{"index": 0}` picks the first available character on
|
||
`testaccount2`, and the Leader bot discovers that character's real name
|
||
live (`FellowshipAllegianceGateCoordinator.RecruitCharacterName`, set by
|
||
the Recruit bot's own `HeadlessSessionHost` the instant CharacterList
|
||
selection resolves it) rather than trusting a config-file guess. Run it:
|
||
`acdream-headless run --config fa6-gate.json` with `FA6_BOT_A_PASSWORD`/
|
||
`FA6_BOT_B_PASSWORD` set in the environment. The process is
|
||
self-terminating — every policy stage carries its own timeout, and the
|
||
run ends (gracefully, with ACE-confirmed per-character logout) on its own
|
||
once both bots reach `Done` or fault. Do not `timeout`/Ctrl+C the process
|
||
mid-run: an external kill during an in-world session leaves ACE holding a
|
||
stale session for ~3 minutes (CLAUDE.md's documented logout-before-
|
||
reconnect discipline; one of this slice's OWN runs discovered this
|
||
firsthand from wrapping a probe run in bash's `timeout`, not from the
|
||
gate's own code path).
|
||
|
||
**What the fellowship gate proves, per stage:**
|
||
|
||
1. **Proximity.** The Leader sends retail's admin `@teleallto` (no target
|
||
= "teleport everyone online to me") on a 5 s retry, then polls
|
||
`RuntimeFriendlyTargetQuery.FindPlayerByName` for the Recruit bot's
|
||
discovered name specifically — NOT the ambiguous "nearest any player"
|
||
query, which a live run found picks up a stray third character on a
|
||
shared ACE dev instance if one happens to be online nearby.
|
||
2. **Create + recruit.** `Fellowship.Create` then `Fellowship.Recruit`
|
||
against the name-matched guid.
|
||
3. **The decisive assertion.** The Recruit bot's OWN
|
||
`RuntimeFellowshipState` — a completely separate process's canonical
|
||
Runtime owner, not the Leader's local echo — flips
|
||
`IsInFellowship=true`, `MemberCount=2`, `LeaderGuid=<Leader's guid>`.
|
||
This is the proof that `0x02BE`/`0x02C0` inbound reached a real second
|
||
client, not just that the Leader's own send succeeded.
|
||
4. **D4 panel-open + vitals.** The Leader declares `SetPanelOpen(true)`
|
||
(`0x00A6`) and asserts the Recruit's own fellowship member row already
|
||
carries nonzero vitals (`MaxHealth`) — headless bots have no panel to
|
||
freeze/unfreeze, so this proves the declaration sends and that vitals
|
||
data is present, not the freeze-while-closed half of D4 (that remains
|
||
a graphical-client-only observation, FA4's own owed connected gate).
|
||
5. **Mid-flow reconnect.** Both bots reconnect
|
||
(`commands.Session.Reconnect`) after establishing, then assert the
|
||
fellowship state re-seeds from scratch (a fresh generation clears
|
||
`RuntimeFellowshipState` per FA2 D2) — proving reconnect-idempotence
|
||
over the real wire, with a genuine second account watching.
|
||
6. **Teardown.** The Leader disbands (`Fellowship.Quit(disband: true)`);
|
||
both bots assert their own `IsInFellowship` clears.
|
||
|
||
**Result — PASSED, 2026-08-12 (five of six live runs; the sixth, run 2,
|
||
hit a since-fixed wrong-target bug before the decisive stage was even
|
||
reached):**
|
||
|
||
```
|
||
[fa6-recruit] DECISIVE ASSERTION PASSED: own RuntimeFellowshipState
|
||
flipped IsInFellowship=true, MemberCount=2, LeaderGuid=0x5000000A —
|
||
recruit inbound path reached THIS process's own Runtime owner
|
||
[fa6-leader] panel-open declared; recruit vitals name='+Horan'
|
||
maxHealth=201
|
||
```
|
||
|
||
Two real defects surfaced and were fixed IN this slice, both now covered
|
||
by conformance tests: headless hosts silently dropped every server-driven
|
||
confirmation (`HeadlessSessionHost.OnConfirmationRequest` was `null`); and
|
||
`RuntimeFriendlyTargetQuery.FindClosestOtherPlayer`'s "nearest any player"
|
||
query picked up a stray third character (`+Je`, `0x50000001`) instead of
|
||
the actual Recruit bot after `@teleallto`, fixed by the name-matched
|
||
`FindPlayerByName`.
|
||
|
||
### Allegiance bot gate — DEFERRED (docs/ISSUES.md #384)
|
||
|
||
**Status: not shipped as an automated gate.** The allegiance half of the
|
||
SAME policy classes (Leader's `WaitForVassal`; Recruit's `Swear`/
|
||
`WaitSwornSeed`/`Break`/`WaitBrokenSeed`) is fully written and wired —
|
||
proximity, the `0x001D` swear send, a confirmation-relay seam on the
|
||
Leader (the would-be patron, per `docs/research/2026-08-11-fa-allegiance-
|
||
wire.md` §1.3's "Target (would-be patron)" steps 4-7), the decisive
|
||
`TotalVassals`/`TryGetPatron` assertions, break, and reconnect-idempotence
|
||
— but is gated OFF by default (`AllegianceGateEnabled = false` in both
|
||
policy classes) because it has never actually completed over the wire.
|
||
Six live runs against local ACE all showed the same symptom: ACE returns
|
||
**nothing** to the swear — no `0x0274` confirmation, no `0x0020` tree
|
||
update, no WeenieError — confirmed at 0.005 m separation between the two
|
||
bots (ruling out retail's 2.0 m swear-distance gate). See `docs/ISSUES.md`
|
||
#384 for the full evidence trail and register row AD-87 for the
|
||
divergence this creates (the allegiance wire/state code is unit-tested
|
||
and locally-echoed but not wire-verified end-to-end).
|
||
|
||
**[TWO-CLIENT] Manual counterpart for the user's connected gate** — this
|
||
is the step that can settle #384 outside the headless harness:
|
||
|
||
25. With TWO graphical clients logged into different characters near each
|
||
other (the existing FA5 `[TWO-CLIENT]` swear steps 14-16 already
|
||
exercise this UI path), have character A select character B and click
|
||
Swear. **Report specifically:** does B's client show the incoming
|
||
"accept swear" confirmation dialog at all? If B accepts, does A's
|
||
Allegiance page then show B in A's vassal list, and does B's page show
|
||
A as patron? If the SAME silent non-response reproduces in the
|
||
graphical client (no dialog ever appears on B's side, no error on
|
||
A's), that points at an ACE-side rule or a genuine defect in acdream's
|
||
`0x001D` wire builder rather than anything specific to the headless
|
||
harness — the two clients are real user accounts, not `testaccount`/
|
||
`testaccount2`, which also rules out any GM-flag-specific explanation.
|
||
If it works normally in the graphical client, the symptom is scoped
|
||
to `testaccount`/`testaccount2` specifically (a GM flag, a rank/self-
|
||
allegiance precondition, or state left over from this session's
|
||
testing) — re-run the headless gate with a different account pair
|
||
before reopening #384 as a real wire bug.
|
||
|
||
### Explicitly NOT in scope for this gate
|
||
|
||
- A fully verified allegiance bot gate — deferred to #384; do not report
|
||
its absence as a regression, and do not re-run the headless allegiance
|
||
path repeatedly hoping for a different result without new information
|
||
(six identical live results is enough evidence, not bad luck).
|
||
- Kick — no test exercises it (would need a THIRD account to have a
|
||
vassal to kick that isn't also the tester).
|
||
- Kick/kick-confirmation UI — FA5's own `[TWO-CLIENT]` scope, unrelated
|
||
to this bot gate.
|