two-bot gate
Adds the infrastructure docs/plans/2026-08-11-fellowship-allegiance-campaign.md
D8 and docs/research/2026-08-11-fa-acdream-seams.md §6.2 call for:
- HeadlessBotPolicyDescriptor gains an optional typed Role
(HeadlessBotPolicyRole.Leader/Recruit) so two sessions selecting the SAME
policy id run different scripts — fellowship leader/allegiance patron vs
fellowship recruit/allegiance vassal.
- HeadlessBotPolicyFactory.Create widens from Create(string id) to
Create(HeadlessBotPolicyDescriptor, GameRuntime) — the gate policies need
RuntimeFriendlyTargetQuery, which (like its RuntimeHostileTargetQuery
sibling) takes the concrete GameRuntime rather than the narrower
IGameRuntimeView a policy's own Tick receives (IRuntimeEntityView's
snapshot carries no name/PWD-bitfield). The single call site
(HeadlessSessionHost.cs) already has the constructed runtime in scope, so
no new constructor parameter or cross-session coordinator was needed.
- FellowshipAllegianceLeaderBotPolicy / FellowshipAllegianceRecruitBotPolicy:
a full stage-machine pair covering proximity (retail's admin @teleallto —
"teleport everyone online to me" — needs no cross-session name sharing,
unlike @teleto <name>; D8's proximity requirement is load-bearing, recruit
fails without it), fellowship create+recruit, the D4 0x00A6 panel-open
declaration with a vitals-presence assertion, the decisive two-session
assertions (the RECRUIT bot's own RuntimeFellowshipState/
RuntimeAllegianceState flipping — not the Leader's local echo), a
mid-flow reconnect on both bots proving FA2's reset-and-reseed semantics
over the real wire, and teardown (disband / break) with matching
decisive-clear assertions.
Every pre-FA6 policy (idle, lifecycle-smoke, observer-movement,
portal-route-smoke, jump-probe) is unaffected; the widened factory
signature is the only touch point.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>