# Ecaz Occupy runtime readiness Read-only architecture review, 7 September 2026, after Guild Ambassador integration. No production, test, package or database state was changed. References below identify the reviewed functions; line numbers are snapshot anchors rather than stable API locations. The independent source reviewer records the formula and interaction rulings in [ECAZ_OCCUPY_RULES.md](ECAZ_OCCUPY_RULES.md), including the remaining rounding discrepancy. Root verification found that the numerical correction comment was written by a community member, not the designer; it is not authoritative errata. Territorial co-occupation remains independently supported by p.8 and the verified October designer clarification. **Recommended next slice: shared territorial coalition identity, ordinary entry/path legality and end-of-movement coexistence.** Dedicated Fremen/Guild Ambassador effects already permit Ecaz co-occupation. Ordinary movement still contradicts that supported resulting board state. Combat, shared desert collection and the special victory threshold require separate consumers and must not be advertised as complete merely because entry is fixed. ## Current surfaces | Surface | Current behavior and relevant code | Required boundary | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Special entry | `fremen-ambassador.ts:133–150` and `guild-ambassador.ts:134–167` independently identify a reciprocal Ecaz pair and count it as one stronghold side. Both permit co-occupation. Fremen relocation also allows a source-approved BG fighter request beside Ecaz while preserving locks. | Replace duplicated public coalition identity with one helper. Preserve each producer's distinct storm, range, HMS, typed custody, stance and source-event rules. | | Ordinary destination/path | `engine.ts:5766` `allowedEntry` rejects allied presence outside Polar Sink; its stronghold limit counts separate players. `pathBlocked` near 8269 also counts players. `bot-mobility.ts:32–158` mirrors both assumptions. | Territorial Ecaz pairs count as one side and are exempt from the ordinary allied-entry prohibition. Paths and final destination must use the same identity. This does not grant range, city ornithopters or HMS shipment permission through an ally. | | Ordinary turn completion | `finishMovementTurn` near `engine.ts:6368` destroys the ending player's co-occupying forces once its ally is absent from the remaining queue, except for the current-turn alliance grace period. | Ecaz territorial coexistence is permanent under Occupy, not another deadline. Keep ordinary non-Ecaz grace/departure behavior intact, including Guild order and Hajr turns. Do not change reserves, casualties or queues merely to implement identity. | | BG | Normal `move` has an additional allied-presence restriction for optional fighter requests near `engine.ts:16360`; `advisors.ts` supplies stance and locks. `settledBoard` automatically releases lone advisors before battle/collection/victory. | Apply the source-reviewed Ecaz exception consistently; retain opposite-stance matching and accompaniment locks. Advisors remain distinct from fighting presence, even when their faction belongs to the coalition. | | Battle enumeration | `board-resolution-quote.ts:104` `quoteBattleBoard` enumerates player pairs in storm order, skipping an ally but independently pairing each ally against a third party. `engine.ts:16511` `chooseBattle` installs that pair directly. | A territorial coalition requires one battle side with a separate choice of acting faction. Preserve the ordinary battle chooser and attack/defend orientation; do not let both allies fight the same opposing group as two unrelated battles. | | Plans, support, powers | `Battle` near `engine.ts:427` has only attacker/defender IDs. `combatForces` near 3314 reads the selected player's troops; `validatePlan` near 9271 uses its leader/cards, force pool and funds. `combat-response-quote.ts:160–186` derives powers from those two IDs. | Separate acting faction, dialed force owner and Ecaz contribution. Do not copy an ally's troops onto a fictitious player or substitute its hand. Native powers, support source, Voice/Prescience, KH, stronghold support and traitor ownership need explicit source-based attribution. | | Resolution and aftermath | `battle-resolution-quote.ts` accepts two `ResolutionCombatant`s. `resolveBattle` near `engine.ts:10509` pays those players, kills the losing selected player's army and loses the winner's selected dial. Explosion is already territorial. `takeBattleLosses` near 3352 allocates one owner's typed losses. Aftermath helpers then handle capture, Face Dance, Auditor, cards and tech. | Add a typed coalition commitment/result before enabling combat. Loss allocation must conserve both physical owners and distinguish win, loss, traitor, explosion and special weapon outcomes. Do not infer the whole coalition's effects from a single `winner` ID. | | Collection | `quoteSpiceCollection` near `board-resolution-quote.ts:157` drains each sector in storm order, crediting the first collector before the second. It already pays each fighter owner's Advanced city income independently. `collect` near `engine.ts:8944` immediately applies balances. | Existing city income can coincide with Ecaz's printed double collection, but desert sharing/agreement and Ecaz Collection cancellation are absent. A future collection quote needs an explicit allocation/continuation; ordinary bribes defer to Mentat and are not a substitute. | | Victory and control | `victory-quote.ts:153–168` deduplicates an allied union's normal strongholds but requires four for every alliance. It has no three jointly occupied strongholds condition. `stronghold-cards.ts:199–206` already awards control to Ecaz when only the reciprocal pair occupies a stronghold. | Keep actual Stronghold Card control separate from victory thresholds and held-card custody. Add the source-confirmed joint condition to the shared victory quote while preserving ordinary four-hold victory, prediction and turn-ten fallbacks. Do not silently treat a tech-token bonus as a jointly occupied map stronghold. | | Human/AI projection | `game-table.tsx:267` uses `battle.ownForces`, then displays one player's force/card plan. `chooseBattle` controls near 3730 target a single player. `bots.ts` battle plans and `bot-mobility.ts` have the same player-centric assumptions. | Movement can consume shared public identity now. Future combat needs authoritative participant/pool descriptors and an Ecaz acting-faction choice before sealed plans. Only the entitled actor receives its private hand, leader availability or plan constraints. | Ordinary shipments, Guild transport, worm entry and other existing producers call some of these common checks. Audit their actual callers when integrating the helper; replacing only the visible `ship` action would leave inconsistent routes. Conversely, the special Ambassador destination grant must retain its own independent action allowance. ## Reproduced current behavior An in-memory Node/tsx probe used `createGame`, `newPlayer`, actual `applyAction` for ordinary shipment/end movement, and the exported pure board/victory quotes. Each case used three seated factions (Ecaz, Emperor ally, Harkonnen opponent), reciprocal Ecaz/Emperor alliance formed on turn one, current turn two, storm18, empty hands and conserved 20-force inventories. These were deliberately constructed development positions, not a claim of a publicly startable Ecaz game. The probe never called room APIs or wrote a save. 1. With Emperor's one force in Carthag11, Ecaz's ordinary one-force shipment there rejects: `You cannot enter a territory occupied by your ally.` 2. With one force each in Carthag11 and only Ecaz remaining in the movement queue, actual Ecaz `endMovement` leaves Emperor's force, removes Ecaz's force and increases Ecaz's Tanks by one. 3. With Ecaz5, Emperor4 and Harkonnen1 in Carthag11, `quoteBattleBoard` returns both Ecaz→Harkonnen and Emperor→Harkonnen battles in that territory. 4. With two forces each in Wind Pass14 and four spice there, `quoteSpiceCollection` returns Ecaz4 and Emperor0. It has no agreement or equal-split choice. 5. With one force from each ally in Arrakeen10, Carthag11 and Sietch Tabr14, phase-eight `quoteVictory` returns no winner. Cases 1–2 demonstrate the immediate coexistence inconsistency. Cases 3–5 demonstrate missing later Occupy behavior; the pair-list observation itself is not proof of a resolved combat formula. These are existing gaps in gated expansion support, not regressions introduced into enabled classic Basic starts. The recently implemented Ambassador grants make their consequences reachable in development journeys. No runtime was modified by this audit. The minimal setup can be reproduced without a file by piping JavaScript to `node --import tsx`: construct the three players with `createGame('OCCUPYREAD', newPlayer('ec','Ecaz','ecaz'), false, [])`, append Emperor `al` and Harkonnen `en`, set the controlled status/phase/order above, assign reciprocal `ally` and `allySinceTurn = 1`, and set each player's reserves to `20 - sum(forces)`. Import `quoteBattleBoard`/`quoteSpiceCollection` from `game/board-resolution-quote.ts` and `quoteVictory` from `game/victory-quote.ts`. Use actual `{type:'ship',amount:1,territory:'carthag',sector:11}` and `{type:'endMovement'}` for the first two observations. ## Smallest shared contract and implementation order 1. **Public territorial identity and coexistence.** A pure helper consumes only seated IDs, factions, reciprocal alliances and an explicit location kind. Its stable identity/members distinguish the same player, an Ecaz alliance and other factions. Homeworlds remain explicitly unmerged. Root's proposed `ecazOccupancyIdentity`/`ecazOccupancyRelation` contract matches this need. Stronghold capacity uses fighting sides; no hidden No-Field denomination or private resources enter identity. Replace the duplicate Ambassador implementations, ordinary engine/path checks and bot filters, then exempt this pair from compulsory allied separation. Test actual shipment/movement/end-turn and canceled/replacement movement paths, not only identity functions. 2. **Keep unsupported combat explicit.** Do not enable full Ecaz starts in the coexistence slice. Before later combat activation, settle a public `BattleSide` contract containing the acting faction, dialed physical owner, contributor and participant set. Ecaz's representative decision belongs before plan/power commitment. Source-confirmed formula/cancellation must enter the same quote used by plan validation, projection, resolution and saved response preflight; an effectful trial on a cloned game is not that contract. 3. **Implement bounded collection and victory separately.** The joint stronghold win is a small shared-quote extension once the source reviewer settles token and advisor details. Desert collection requires a real agreement/fallback allocation rather than two independent storm-order drains. Keep its live balances and proof in a pure quote, with only the unresolved choice persisted. Add the separate Advanced Collection Karama window without reducing the ally's normal income. 4. **Complete combat before broad activation.** Cover selected faction versus force owner for native powers, support, casualty types, bounty, traitors, stronghold effects, Face Dance, capture, Auditor, tech/card aftermath and all already supported weapons. Reuse existing pure casualty/aftermath components with explicit inputs; do not reinterpret an old two-player receipt as a coalition receipt. Match UI/AI and SQL recovery to the new committed participant structure, preserving historical saves without fabricating contributions. The coordinator has chosen step one as the next bounded production task. This document is its pre-change audit, not evidence that the new helper or adapters have landed. It intentionally leaves battle, collection and victory activation to subsequent source-backed slices.