Compose native Guild rate with module and Homeworld price sources

This commit is contained in:
Quintine committed 2026-10-15 20:55:05 +11:00
1 parent 0db84c81b7
commit a12250320c
24 files changed
+1176 -50

No files matched your search

+114 -21
View File
@@ -1111,6 +1111,7 @@ type KaramaUse =
type PendingHomeworldShipment = HomeworldShipmentIntent & {
guildSecretEvent?: string;
guildNexusEvent?: string;
guildRateEvent?: string;
/** Absent in older world-to-world declarations. */
route?: 'arrakis';
event: string;
@@ -8412,9 +8413,13 @@ function leaderSkillsIntegrity(g: Game) {
}
if (!noFieldShipment && (g.leaderSkills || g.pendingShipment?.smuggler)) {
const decisions = homeworldSavedDecisions(g).filter(d => d.kind === 'guildShipment');
requireRule(decisions.length === (g.pendingShipment ? 1 : 0),
'The saved Leader Skills shipment lost its unique Guild interception decision.');
const rates = g.pendingShipment?.guildRateEvent
? savedNoFieldResponses(g).filter(response => response.kind === 'guildRate' &&
response.guildRateEvent === g.pendingShipment!.guildRateEvent) : [];
requireRule(decisions.length + rates.length === (g.pendingShipment ? 1 : 0),
'The saved Leader Skills shipment lost its unique Guild interception or rate response.');
for (const decision of decisions) validateGuildShipmentDecision(g, decision);
for (const rate of rates) validateGuildRateResponse(g, rate);
}
if (!g.leaderSkills) return;
requireRule(g.leaderSkills.assignments.every((assignment) =>
@@ -9993,6 +9998,9 @@ function decideGuildBetrayal(g: Game, p: Player, action: Action): void {
continuation.transport, settlement); break;
}
requireRule(getPlayer(g, frame.invoice.shipper).shipped, 'The funded native shipment did not complete.');
if (g.guildRateBlocked?.turn === g.turn &&
g.guildRateBlocked.player === frame.invoice.shipper)
delete g.guildRateBlocked;
const sourceReceipt = nexusGuildBetrayalSourceReceipt(frame.invoice);
const receipt = nexusRule(() => createGuildBetrayalReceipt({turn: g.turn, players: g.players},
frame.invoice, {recipient: settlement.recipient, holderFaction: use ? p.faction : null,
@@ -14444,7 +14452,7 @@ function finishAutomaticDecision(g: Game): boolean {
if (homeworldShipmentAutomatic(g)) {
const shipment = g.pendingHomeworldShipment!;
g.decision = null;
commitHomeworldShipment(g, shipment);
if (!openHomeworldGuildRate(g, shipment)) commitHomeworldShipment(g, shipment);
return true;
}
const market = g.choamMarket;
@@ -24813,6 +24821,7 @@ function finishResponse(g: Game, canceled: boolean, settlement?: BureaucratSettl
if (response.kind === 'nexusGuildCunning') validateGuildCunningResponse(g,response);
if (response.kind === 'guildRate') {
if (g.pendingGuildTransport) validateGuildTransportRateResponse(g, response);
else if (g.pendingHomeworldShipment?.guildRateEvent) validateHomeworldGuildRateResponse(g, response);
else validateGuildRateResponse(g, response);
}
if (response.kind === 'moritaniPlacement') {
@@ -24887,6 +24896,7 @@ function finishResponse(g: Game, canceled: boolean, settlement?: BureaucratSettl
}
if (response.kind === 'guildRate') {
if (g.pendingGuildTransport) finishGuildTransportRateResponse(g, response, canceled);
else if (g.pendingHomeworldShipment?.guildRateEvent) finishHomeworldGuildRateResponse(g, response, canceled);
else finishGuildRateResponse(g, response, canceled);
return;
}
@@ -26141,6 +26151,8 @@ function homeworldShipmentQuote(g: Game, intent: HomeworldShipmentIntent & {rout
requireRule(g.homeworlds?.custody, 'Homeworld shipment requires saved physical custody.');
const context = {
...homeworldContext(g),
nativeGuildRateCanceled: g.guildRateBlocked?.turn === g.turn &&
g.guildRateBlocked.player === intent.player,
players: homeworldContext(g).players.map((seat) => ({...seat, ally: getPlayer(g, seat.id).ally})),
};
const order = {player: intent.player, destination: intent.destination, sources: intent.sources};
@@ -26184,6 +26196,13 @@ function homeworldShipmentIntegrity(g: Game) {
const decisions = homeworldSavedDecisions(g).filter((d) => d.kind === 'homeworldShipmentGuild');
const shipment = g.pendingHomeworldShipment;
if (!shipment && !decisions.length) return;
if (shipment?.guildRateEvent) {
const responses = savedNoFieldResponses(g).filter(response => response.kind === 'guildRate');
requireRule(!decisions.length && responses.length === 1 && !g.pendingShipment,
'The saved Homeworld rate declaration lost its single response.');
validateHomeworldGuildRateResponse(g, responses[0]);
return;
}
requireRule(shipment && decisions.length > 0 && !g.pendingShipment,
'The saved Homeworld shipment needs its original Guild decision.');
validateHomeworldShipment(g, shipment);
@@ -26243,7 +26262,78 @@ function declareHomeworldShipment(g: Game, p: Player, intent: HomeworldShipmentI
g.decision = {kind: 'homeworldShipmentGuild', player: guild.id, shipper: p.id,
destination: intent.destination, amount: quote.amount, event: shipment.event};
log(g, `${p.name} declared ${quote.amount} physical forces for ${combatLocationName(g, intent.destination)}. Payment and departure await the Guild interception decision.`);
} else commitHomeworldShipment(g, shipment);
} else if (!openHomeworldGuildRate(g, shipment)) commitHomeworldShipment(g, shipment);
}
function homeworldGuildRateFullCost(g: Game, shipment: PendingHomeworldShipment): number | null {
const p = getPlayer(g, shipment.player);
if (p.faction !== 'guild' || shipment.guildSecretEvent ||
g.karamaShipping?.player === p.id ||
(g.guildRateBlocked?.turn === g.turn && g.guildRateBlocked.player === p.id))
return null;
return shipment.cost === Math.ceil(shipment.amount / 2) && shipment.cost < shipment.amount
? shipment.amount : null;
}
function openHomeworldGuildRate(g: Game, shipment: PendingHomeworldShipment): boolean {
const fullCost = homeworldGuildRateFullCost(g, shipment);
if (fullCost === null) return false;
validateHomeworldShipment(g, shipment);
// The Homeworld validator already authenticated this exact Cunning frame.
const cunning = shipment.guildNexusEvent
? currentGuildCunning(g, shipment.player) as ReturnType<typeof validateGuildCunningShipment>
: null;
const event = crypto.randomUUID();
shipment.guildRateEvent = event;
if (cunning) {
cunning.shipment!.frame = JSON.stringify(shipment);
saveGuildCunning(g, cunning);
}
g.pendingHomeworldShipment = shipment;
g.response = { kind: 'guildRate', owner: shipment.player, passed: [], guildRateEvent: event,
intent: `${getPlayer(g, shipment.player).name} declared ${shipment.amount} physical forces to ${combatLocationName(g, shipment.destination)} at the native Guild rate of ${shipment.cost} spice. Karama removes only this faction discount, not Junction permission or another Homeworld benefit. The exact typed source and approved payment split must cover the full ${fullCost}; otherwise the declaration returns unused and a replacement this turn uses full price.` };
log(g, 'Native Guild Homeworld shipment waits for its rate response before payment or physical departure.');
return true;
}
function validateHomeworldGuildRateResponse(g: Game, response: ResponseWindow) {
const shipment = g.pendingHomeworldShipment;
const fullCost = shipment ? homeworldGuildRateFullCost(g, shipment) : null;
requireRule(response.kind === 'guildRate' && shipment && fullCost !== null &&
response.owner === shipment.player && typeof shipment.guildRateEvent === 'string' &&
shipment.guildRateEvent.length > 0 && response.guildRateEvent === shipment.guildRateEvent,
'The saved native Homeworld rate lost its original declaration.');
validateHomeworldShipment(g, shipment);
return { shipment, fullCost };
}
function finishHomeworldGuildRateResponse(g: Game, response: ResponseWindow, canceled: boolean) {
const { shipment, fullCost } = validateHomeworldGuildRateResponse(g, response);
const p = getPlayer(g, shipment.player);
// The rate validator already authenticated this exact Cunning frame.
const cunning = shipment.guildNexusEvent
? currentGuildCunning(g, p.id) as ReturnType<typeof validateGuildCunningShipment>
: null;
g.pendingHomeworldShipment = null;
delete shipment.guildRateEvent;
if (canceled) {
g.guildRateBlocked = { turn: g.turn, player: p.id };
const bounds = shipmentPaymentBounds(fullCost, p.spice, aidFor(g, p)?.amount ?? 0);
if (shipment.allyPayment < bounds.minimum || shipment.allyPayment > bounds.maximum) {
if (cunning) {
delete cunning.shipment;
saveGuildCunning(g, cunning);
}
log(g, `Karama removed the native Guild discount. ${p.name}'s original Homeworld payment split cannot fund full price; no spice or typed forces moved. A replacement this turn uses full price.`);
return;
}
shipment.cost = fullCost;
}
if (cunning) {
cunning.shipment!.frame = JSON.stringify(shipment);
saveGuildCunning(g, cunning);
}
commitHomeworldShipment(g, shipment);
if (canceled && !g.pendingGuildBetrayal) {
delete g.guildRateBlocked;
log(g, `${p.name} paid the full ${fullCost} spice for the unchanged native Homeworld shipment.`);
}
}
function noFieldSkillProof(shipment: PendingShipment): string {
return JSON.stringify({ ...shipment, noFieldSkillProof: undefined });
@@ -26327,7 +26417,8 @@ function leaderSkillNoFieldIntegrity(g: Game): boolean {
else validateLeaderSkillNoFieldShipment(g, shipment);
const initial = responses.filter(r => r.kind === 'richeseNoField');
const guild = decisions.filter(d => d.kind === 'guildShipment');
requireRule(initial.length + guild.length === 1 &&
const rates = responses.filter(r => r.kind === 'guildRate');
requireRule(initial.length + guild.length + rates.length === 1 &&
responses.every(r => r.noFieldSkillProof === undefined || r.kind === 'richeseNoField') &&
decisions.every(d => !('noFieldSkillProof' in d) || d.kind === 'guildShipment'),
'The saved No-Field shipment lost its unique cancellation response or Guild decision.');
@@ -26335,6 +26426,7 @@ function leaderSkillNoFieldIntegrity(g: Game): boolean {
response.noFieldSkillProof === shipment.noFieldSkillProof,
'The No-Field response changed its original declaration.');
for (const decision of guild) validateGuildShipmentDecision(g, decision);
for (const rate of rates) validateGuildRateResponse(g, rate);
return true;
}
function validatePhysicalShipment(g: Game, shipment: PendingShipment) {
@@ -26484,24 +26576,20 @@ const CLASSIC_FACTIONS: Partial<Record<FactionId, true>> = {
};
/** The independent Karama rate is a card benefit, not Guild's cancelable rate. */
function guildRateModeSupported(g: Game): boolean {
return !g.homeworlds && !g.leaderSkills && !g.discoveryEnabled &&
!g.techTokens && !g.strongholdCards;
}
function ordinaryGuildRateFullCost(g: Game, shipment: PendingShipment): number | null {
const guild = byFaction(g, 'guild');
const p = getPlayer(g, shipment.player);
if (!guild || (p.id !== guild.id && !(p.ally === guild.id && guild.ally === p.id)) ||
!guildRateModeSupported(g) ||
g.karamaShipping?.player === p.id ||
(g.guildRateBlocked?.turn === g.turn && g.guildRateBlocked.player === p.id) ||
shipment.source || shipment.noField || shipment.alliedNoField ||
shipment.smuggler || shipment.smugglerCompanion || shipment.guildSecretEvent ||
shipment.source || shipment.alliedNoField || shipment.guildSecretEvent ||
shipment.nexusEvent || shipment.richesePair)
return null;
const fullCost = reserveShipmentCost({ faction: p.faction, halfRate: false },
territory(shipment.territory).type, shipment.amount);
return shipment.cost < fullCost ? fullCost : null;
territory(shipment.territory).type, shipment.amount - (shipment.smuggler ? 1 : 0));
const halfCost = reserveShipmentCost({ faction: p.faction, halfRate: true },
territory(shipment.territory).type, shipment.amount - (shipment.smuggler ? 1 : 0));
return shipment.cost === halfCost && shipment.cost < fullCost ? fullCost : null;
}
function openGuildRate(g: Game, shipment: PendingShipment): boolean {
const fullCost = ordinaryGuildRateFullCost(g, shipment);
@@ -26513,6 +26601,8 @@ function openGuildRate(g: Game, shipment: PendingShipment): boolean {
shipment.turn ??= g.turn;
const event = crypto.randomUUID();
shipment.guildRateEvent = event;
if (shipment.noFieldSkillProof !== undefined)
shipment.noFieldSkillProof = noFieldSkillProof(shipment);
if (cunning) {
cunning.shipment!.frame = JSON.stringify(shipment);
saveGuildCunning(g, cunning);
@@ -26523,7 +26613,7 @@ function openGuildRate(g: Game, shipment: PendingShipment): boolean {
owner: byFaction(g, 'guild')!.id,
passed: [],
guildRateEvent: event,
intent: `${getPlayer(g, shipment.player).name} declared ${shipment.amount} reserve forces to ${territory(shipment.territory).name} at the Guild rate of ${shipment.cost} spice. Karama can remove this discount before any payment or arrival. The original forces would cost ${fullCost} at full price; if the original authorized split cannot pay that amount, the declaration returns unused and a replacement this turn uses full price.`,
intent: `${getPlayer(g, shipment.player).name} declared ${shipment.noField ? 'one concealed No-Field' : `${shipment.amount} reserve forces`}${shipment.smugglerCompanion ? ' with one free Smuggler companion' : shipment.smuggler ? ' including one free Smuggler companion' : ''} to ${territory(shipment.territory).name} at the Guild rate of ${shipment.cost} spice. Karama can remove this discount before any payment or arrival, but does not remove the independent free companion. The same paid units would cost ${fullCost} at full price; if the original authorized split cannot pay that amount, the declaration returns unused and a replacement this turn uses full price.`,
};
log(g, `${getPlayer(g, shipment.player).name} declared a Guild-rate reserve shipment; payment and arrival wait for the rate response.`);
return true;
@@ -26540,18 +26630,19 @@ function validateGuildRateResponse(g: Game, response: ResponseWindow) {
response.guildRateEvent === shipment.guildRateEvent &&
shipment.turn === g.turn && fullCost !== null &&
shipment.cost === reserveShipmentCost({ faction: p.faction, halfRate: true },
territory(shipment.territory).type, shipment.amount),
territory(shipment.territory).type, shipment.amount - (shipment.smuggler ? 1 : 0)),
'The saved Guild-rate response lost its original physical shipment.');
return { shipment, fullCost };
}
function guildRateIntegrity(g: Game) {
const responses = savedNoFieldResponses(g).filter(response => response.kind === 'guildRate');
const expected = Number(!!g.pendingShipment?.guildRateEvent) +
Number(!!g.pendingGuildTransport);
Number(!!g.pendingGuildTransport) + Number(!!g.pendingHomeworldShipment?.guildRateEvent);
requireRule(expected <= 1 && responses.length === expected,
'The saved Guild-rate shipment lost its single Karama response.');
if (responses.length) {
if (g.pendingGuildTransport) validateGuildTransportRateResponse(g, responses[0]);
else if (g.pendingHomeworldShipment?.guildRateEvent) validateHomeworldGuildRateResponse(g, responses[0]);
else validateGuildRateResponse(g, responses[0]);
}
const blocked = g.guildRateBlocked;
@@ -26582,12 +26673,14 @@ function finishGuildRateResponse(g: Game, response: ResponseWindow, canceled: bo
shipment.cost = fullCost;
checkShipmentIncomeFunding(g, shipper, fullCost, shipment.allyPayment);
}
if (shipment.noFieldSkillProof !== undefined)
shipment.noFieldSkillProof = noFieldSkillProof(shipment);
if (cunning) {
cunning.shipment!.frame = JSON.stringify(shipment);
saveGuildCunning(g, cunning);
}
commitShipment(g, shipment);
if (canceled) {
if (canceled && !g.pendingGuildBetrayal) {
delete g.guildRateBlocked;
log(g, `Karama removed Guild's half-price rate. ${shipper.name} paid the full ${fullCost} spice for the unchanged shipment.`);
}
@@ -26599,7 +26692,7 @@ function guildTransportSignature(intent: PendingGuildTransport): string {
}
function ordinaryGuildTransportFullCost(g: Game, p: Player, frame: GuildTransportOrder): number | null {
const guild = byFaction(g, 'guild');
if (!guild || !guildRateModeSupported(g) ||
if (!guild ||
(p.id !== guild.id && !(p.ally === guild.id && guild.ally === p.id)) ||
g.karamaShipping?.player === p.id ||
(g.guildRateBlocked?.turn === g.turn && g.guildRateBlocked.player === p.id))
@@ -26796,7 +26889,7 @@ function finishGuildTransportRateResponse(g: Game, response: ResponseWindow, can
return;
}
commitGuildTransport(g, shipper, frame, intent.fromReserves);
if (canceled) {
if (canceled && !g.pendingGuildBetrayal) {
delete g.guildRateBlocked;
log(g, `Karama removed Guild's half-price transport rate. ${shipper.name} paid the full ${fullCost} spice for the unchanged physical transport.`);
}
@@ -30835,7 +30928,7 @@ function applyActionInner(
decision.event === shipment.event && decision.shipper === shipment.player &&
decision.destination === shipment.destination && decision.amount === shipment.amount,
'The Guild decision no longer matches this Homeworld shipment.');
commitHomeworldShipment(g, shipment);
if (!openHomeworldGuildRate(g, shipment)) commitHomeworldShipment(g, shipment);
} else if (decision.kind === 'guildShipment') {
requireRule(
action.allow === true,
+2 -1
View File
@@ -68,6 +68,7 @@ export function guildHomeworldShipmentChoice(
const quote = quoteGuildHomeworldShipment(
{
advanced: view.advanced,
nativeGuildRateCanceled: view.guildRateCanceled,
storm: view.storm,
mobileStronghold: view.mobileStronghold?.location ?? null,
discoveries: view.discoveries,
@@ -225,7 +226,7 @@ export function guildHomeworldShipmentActions(
);
const amount = Math.min(
total - retain,
budget * 2,
budget * (view.guildRateCanceled ? 1 : 2),
[3, 5, 7, 9][difficulty],
desired,
);
+3 -2
View File
@@ -58,7 +58,8 @@ function printedSector(key: string) {
/** Guild's own special shipment from one Arrakis territory to a Homeworld.
* The caller owns timing, payment, alliance funding, responses and arrival
* effects. No private card, concealed-force value or transport usage is read.
* effects. Native rate cancellation changes only the half tariff to full price.
* No private card, concealed-force value or transport usage is read.
* The mobile stronghold's interior remains protected when its pointer is in
* storm. A Homeworld destination never becomes a fabricated Arrakis sector. */
export function quoteGuildHomeworldShipment(
@@ -206,7 +207,7 @@ export function quoteGuildHomeworldShipment(
...transferred,
amount,
elite: 0,
cost: Math.ceil(amount / 2),
cost: context.nativeGuildRateCanceled ? amount : Math.ceil(amount / 2),
origin: origin!,
boardSources,
boardForces,
+5 -1
View File
@@ -67,6 +67,7 @@ export function homeworldShipmentChoice(
}
const context = {
advanced: view.advanced,
nativeGuildRateCanceled: view.guildRateCanceled,
players: view.players.map((player) => ({
id: player.id,
faction: player.faction,
@@ -231,7 +232,10 @@ export function homeworldShipmentActions(
const amount = Math.min(
pools.reduce((sum, pool) => sum + pool.capacity, 0),
[3, 5, 7, 9][difficulty],
budget * (me.faction === 'guild' || useNexus ? 2 : 1),
budget *
((me.faction === 'guild' && !view.guildRateCanceled) || useNexus
? 2
: 1),
Math.max([1, 3, 4, 5][difficulty], required),
);
if (amount <= 0) continue;
+9 -2
View File
@@ -13,6 +13,8 @@ export type HomeworldShipmentContext = Omit<
'players'
> & {
players: (HomeworldReserveSeat & { ally: string | null })[];
/** Only the native Guild faction rate; independent transport grants survive. */
nativeGuildRateCanceled?: boolean;
};
export type HomeworldShipmentIntent = {
player: string;
@@ -34,7 +36,9 @@ function requireShipment(
* Guild may also return a foreign garrison to its own world; other own-world
* exceptions need separately established permission. The caller owns timing, usage,
* funding, bank payment, responses and arrival effects. Guild's E3 half rate
* applies to its own shipment; an alliance alone does not grant that rate. */
* applies to its own shipment unless its native rate is canceled; an alliance
* alone does not grant that rate. Independent transport prices belong to their
* own grant, not this native cancellation flag. */
export function quoteHomeworldShipment(
context: HomeworldShipmentContext,
custody: HomeworldCustody,
@@ -128,7 +132,10 @@ export function quoteHomeworldShipment(
...transferred,
amount,
elite,
cost: player.faction === 'guild' ? Math.ceil(amount / 2) : amount,
cost:
player.faction === 'guild' && !context.nativeGuildRateCanceled
? Math.ceil(amount / 2)
: amount,
sources: transferred.receipts.filter((r) => r.homeworld !== destination.id),
};
}
+1
View File
@@ -137,6 +137,7 @@ const cancellationSources = {
guildRate: (g) => ({
pending: g.pendingShipment ?? null,
...(g.pendingGuildTransport ? { transport: g.pendingGuildTransport } : {}),
...(g.pendingHomeworldShipment ? { homeworld: g.pendingHomeworldShipment } : {}),
active: g.active,
discountedRateBlocked: g.guildRateBlocked ?? null,
purchasedRate: g.karamaShipping ?? null,
+9 -9
View File
@@ -142,7 +142,7 @@ const phaseDetails = [
[
'At the start of this phase, resolve the Atreides spice foresight response. If allowed, Atreides privately inspects the next card, including when using double spice blow.',
'Select a destination and sector. Ship from reserves before making a force move.',
'Native Guild or its reciprocal ally’s discounted ordinary reserve shipment or eligible cross-planet transport waits for a Karama response before paying, including expansion/Nexus tables without Homeworlds, Skills, Discovery, Tech or Strongholds. Guild’s own return waits too. The same physical group and approved split must cover full price after cancellation; otherwise the declaration returns unused and a replacement this turn uses full price. Cunning retains its separate second allowance and original shipped history. Full-price return and unaffordable withdrawal remain provisional policies; income, independent purchased rates and transport permission are distinct.',
'A native Guild or reciprocal ally’s discounted reserve/cross shipment waits before payment, including original module tables. Native Guild typed Homeworld transport and return retain their own source pools. Canceling native price preserves a free Smuggler companion; an own concealed No-Field prices one marker, never its hidden value. The same group and approved funding must pay full price or return uncommitted for a full-price replacement. Cunning retains its second allowance and original history. Junction sponsorship, independent Nexus/card prices and permission cancellation are distinct; affordability/return policies remain provisional.',
'For movement, select the source forces. Enable Combine sectors to include several groups from the same territory.',
'A Fremen move using the two-territory advantage has a Karama response. Cancellation leaves the forces in place and limits this move to one territory; city ornithopters and a later extra move keep their normal rules.',
'Guild transport moves one exact physical group across the planet or returns it to reserves. Native half-price cancellation is separate from transport permission, including supported expansion/Nexus tables and Cunning’s second shipment. Ordinary movement still checks range, storm and stronghold occupancy.',
@@ -1431,7 +1431,7 @@ export const RULE_TOPICS: RuleTopic[] = [
area: 'Documentation',
status: 'Partial',
detail:
'Source-bound effects, copies and Duke guides remain. Native own-arrival continuation explicitly records child-first/live-token order as provisional, preserving actual controls and unsupported paired/module guards. Separate native rate cancellation now admits expansion/Nexus reserve/cross/return and Cunning receipts; Homeworld/Skills/Discovery/Tech/Stronghold rate compositions remain unfinished.',
'Source-bound effects, copies and Duke guides remain. Native own-arrival child-first/live-token order stays provisional. Native rate price-source composition now retains original modules, independent Smuggler/own-marker pricing, typed Homeworlds and Cunning receipts; immune Junction and borrowed prices remain distinct. Allied-marker/special receipts, permission cancellation and full combinations remain open.',
evidence: ['docs/ECAZ_AMBASSADORS_RULES.md', 'docs/DEFERRED_SHIPMENT_ARRIVAL.md', 'docs/BOT_ARRIVAL_PREFLIGHT.md'],
},
{
@@ -2556,7 +2556,7 @@ export const RULE_TOPICS: RuleTopic[] = [
'If eligible to bid, use the card to take the current auction card without paying spice. A full hand cannot bypass the bidding limit.',
'Holding Karama also permits bids beyond your available spice. A winner can choose to spend it; an outbid player keeps it.',
'Use Cancel with Karama during a power response. Supported responses include Voice, prescience, Atreides auction and spice peeks, spiritual advisors, Emperor gifts and revival, faction income, Harkonnen bonus cards and allied traitors, plus Fremen storm and worm powers, elite combat strength and Fremen free spice support.',
'Native Guild half-price cancellation covers ordinary reserve shipment, eligible cross-planet transport and Guild return before payment or arrival, including expansion/Nexus tables without Homeworlds, Skills, Discovery, Tech or Strongholds. Cunning’s second reserve/cross shipment retains its original allowance and history. Full price applies to the same group and approved split if affordable; otherwise the uncommitted attempt returns and its replacement is full price this turn. The return tariff and unaffordable outcome remain documented table policies. Income, transport permission and independent Karama prices are separate.',
'Cancel a native Guild discount before payment or transfer on supported reserve/cross/return and typed Homeworld sources. Original module selection does not immunize the faction rate. Independent Smuggler counters remain free; an own No-Field bills one concealed marker. Cunning keeps its original second allowance. Full price uses the same approved split or releases the uncommitted attempt for a full-price replacement. Junction Homeworld sponsorship and independent card/Nexus prices stay separate. Income, transport permission and remaining special receipts are distinct; the no-debt/return policy remains provisional.',
'During shipment, use Guild rates for the active player before they ship. They choose and pay for their shipment at the rounded half rate, paid to the bank. You may provide this benefit for another player; it expires if they move or finish without shipping.',
'Advanced Bene Gesserit may spend Worthless cards as Karama. Each conversion opens a separate cancellation response; the Worthless card is discarded even if the power is canceled. Normal Karama cards resolve without that conversion response.',
'A pending Worthless purchase keeps space for its original auction card. Gifts and hand exchanges must leave enough room for every committed incoming card. Refreshing restores the same auction, unused shipment opportunity or original canceled power.',
@@ -4653,7 +4653,7 @@ export const RULE_TOPICS: RuleTopic[] = [
'Native Richese Cunning uses the original signed two-No-Field shipment in supported single/paired E2 or selected mixed Ix+CHOAM native Nexus tables with classics, optional Skills/Homeworlds/Discovery/Tech/Advanced Strongholds. Select two distinct unused physical tokens: one reveals immediately into reserve-capped forces, one remains concealed; pay one marker invoice and spend the Nexus. The previous shipment token cannot be either choice, and the concealed one becomes last-used. Native/Guild response and actual payer/counter sources remain; a nested destination must be revealed. Prevention keeps physical tokens and unpaid spice, after any printed card cost. Sandtrout, arbitrary companions, shared allocation and other pending combinations retain their guards.',
'Richese Betrayal has a separate opt-in auction preview. Before payment, an unallied rival may spend its physical Richese Nexus card to discard a public self-cache win without any purchase debit, or redirect another buyer’s cache/Black Market payment to the bank while preserving delivery and earned bonus. Every publicly possible holder acknowledges the same opportunity; private Use eligibility cannot determine public timing. Normal hidden self-purchases and private special-Karama acquisitions are not connected. See the dedicated guide for its existing local runtime/browser evidence and remaining release gates; that evidence does not verify Nexus Kull.',
'Guild Cunning is declared when finishing your ordinary Shipment and Movement turn. It spends the Nexus and opens one Karama response. Allowance grants a second native shipment at normal Guild prices, including supported cross-shipment, return and Homeworld routes. Cancellation leaves the completed first turn intact. Only an unused Hajr move can follow the second shipment; no ordinary movement or third move is granted. The physical Ornithopter card combination remains unavailable. The separate guild-betrayal profile admits only original native shipment producers and explicitly blocks this second-shipment offer before Nexus spending; Cunning outside that profile is unchanged.',
'In supported native expansion/Nexus tables without other physical modules, Cunning’s second reserve/cross shipment has the normal prepayment rate-Karama window. Its original shipped flag stays true. Allowance preserves rounded half price; cancellation pays full price once or returns the uncommitted second attempt without losing that allowance. A replacement stays full price. The first shipment and ordinary movement never reopen.',
'Supported Cunning reserve/cross/typed Homeworld shipments retain native rate cancellation without clearing the original shipped history. Original module/source, event, paid group and approved split survive allow/cancel; unaffordable release preserves only the second attempt at full price. The first shipment and ordinary movement never reopen. Junction sponsorship and independent borrowed prices are not canceled by this native flag.',
'With Guild absent, its Secret Ally is an explicit optional source for one ordinary shipment at Guild prices. It supports physical reserve shipment, cross-shipment and return to the holder’s own reserves, including the native-Homeworld return when Homeworlds are enabled, plus half-price independently legal world-to-world shipment. Fremen may retain free reinforcement or choose the paid card route beyond its normal range, outside the storm. Native faction, typed sources and normal movement stay unchanged. Arbitrary foreign-world transport and concealed No-Fields are not granted.',
'Borrowed Guild/Richese tariffs do not change native faction or physical reserve origin. Non-Guild/non-Fremen off-planet arrivals can accrue Heighliners once for collection at the original phase end. A paid borrowed Guild route from Fremen southern reserves is still on-planet and does not trigger it; stopped and native-Guild-only shipments likewise remain excluded. The existing Emperor bank-auction purchase retains the actual buyer wallet and creates no industry activity.',
'Guild Betrayal has a separate fresh classic/native-Guild development preview. Before a fully funded positive shipment settles, an unallied holder may spend the physical Guild Nexus to take the full original fee, including their own pre-funded payment, instead of Guild, bank or occupied Junction income. Original donor escrow is consumed once and original physical delivery is unchanged. Neutral public acknowledgements reveal no fee/source; only private eligibility exposes Use. Zero/stopped shipments create no award or Nexus cost. See the dedicated guide; new verification remains Planned and ordinary Homeworld occupation/rounding questions stay local.',
@@ -4662,11 +4662,11 @@ export const RULE_TOPICS: RuleTopic[] = [
],
related: ['ecaz-modules', 'spice-blow', 'alliance-funding', 'choam-worthless', 'choam-kull', 'nexus-choam-kull', 'nexus-richese-betrayal', 'nexus-guild-betrayal', 'nexus-ixian-replacement', 'nexus-ixian-betrayal'],
checklist: [
{area:"Implementation",status:'Partial',detail:"Physical Traitor roles, permanent markers, optional-zone receipts and legal recycling remain integrated. Native expansion/Nexus rate cancellation now covers reserve/cross/return. Cunning reserve receipts bind the temporary rate event and settled price; unaffordable release retains only the second allowance. Both validators reuse source-aware shipment availability instead of clearing original shipped history. Remaining physical-module rate compositions and full acceptance stay open.",evidence:['game/nexus-cards.ts', 'game/nexus-card-phase.ts', 'game/nexus-traitor-exchange.ts', 'game/traitor-declarations.ts', 'game/nexus-face-dancers.ts', 'game/nexus-suboids.ts', 'game/nexus-advisors.ts', 'game/nexus-sardaukar-options.ts', 'game/nexus-fremen-revival.ts', 'game/nexus-fremen-cunning.ts', 'game/nexus-ecaz-betrayal.ts', 'game/nexus-ecaz-inquiry.ts', 'game/nexus-ecaz-duke.ts', 'game/duke-vidal.ts', 'game/nexus-emperor-secret-ally.ts', 'game/choam-power-options.ts', 'game/nexus-moritani-options.ts', 'game/nexus-moritani-betrayal.ts', 'game/nexus-richese-options.ts', 'game/nexus-richese-betrayal.ts', 'game/nexus-guild-cunning-options.ts', 'game/nexus-guild-secret-ally-options.ts', 'game/nexus-module-profile.ts', 'game/engine.ts', 'game/nexus-tleilaxu-revival.ts', 'game/nexus-borrowed-voice.ts', 'game/combat-response-quote.ts', 'game/karama-context.ts', 'game/nexus-fremen-protection.ts', 'game/admin-seat-ai-shape.ts', 'game/richese-cancellation.ts', 'game/bot-mobility.ts', 'game/planetologist-movement.ts', 'game/revival.ts', 'game/nexus-fremen-revival-options.ts','game/nexus-emperor-payment.ts', 'game/nexus-emperor-secret-ally.ts', 'game/revival-resume.ts', 'game/revival-cancellation.ts', 'game/homeworld-revival-deployment.ts', 'game/homeworld-revival-return.ts', "game/nexus-emperor-purchase.ts", "game/auction-continuation-quote.ts", "game/nexus-emperor-purchase.ts", "game/ambassador-phase.ts", "game/ambassador-resume.ts", "game/battle-aftermath-quote.ts", "game/ecaz-duke-acquisition.ts", "game/ecaz-duke-revival.ts", "game/nexus-ecaz-duke-options.ts", "game/nexus-play-boundary.ts"]},
{area:"Player controls",status:'Partial',detail:"Earlier physical Hero and historical Traitor-return/recycling controls retain source-bound proof. Three current390px journeys exercise native full-price cancellation, unaffordable withdrawal/full-price replacement and Cunning reserve repricing through Finish Guild turn. Original groups, paid first shipment and no ordinary extra movement remain; no overflow/browser errors. The Cunning scene uses the explicitly controlled existing fixture, not the action-only native producer.",evidence:['components/nexus-cards.tsx', 'components/nexus-traitors.tsx', 'components/nexus-tleilaxu.tsx', 'components/nexus-suboids.tsx', 'components/nexus-advisors.tsx', 'components/nexus-sardaukar.tsx', 'components/choam-power-cost.tsx', 'components/moritani-terror.tsx', 'components/nexus-moritani-betrayal.tsx', 'components/nexus-ecaz-betrayal.tsx', 'components/nexus-ecaz-inquiry.tsx', 'components/nexus-ecaz-duke.tsx', 'components/nexus-richese-betrayal.tsx', 'components/game-table.tsx', 'components/nexus-tleilaxu-revival.tsx', 'components/battle-preparation.tsx', 'components/nexus-fremen-revival.tsx','components/nexus-emperor-payment.tsx', 'components/nexus-emperor-secret-ally.tsx', "components/nexus-emperor-seller-purchase.tsx", "components/nexus-emperor-ambassador-purchase.tsx", "components/richese-special-karama.tsx"]},
{area:"AI",status:'Partial',detail:"All four actual owner policies legally pass the new native expansion/Nexus rate response, pay rounded two for three and reach the original Ambassador offer; none cancels in that position. Existing Cunning bots and earlier Hero/Traitor-return participation remain covered. Human cancellation/retained-fighter branches are distinct proof. Strategy and calibration remain deferred.",evidence:['game/nexus-card-options.ts', 'game/nexus-traitor-options.ts', 'game/nexus-tleilaxu-options.ts', 'game/nexus-suboid-options.ts', 'game/nexus-advisor-options.ts', 'game/nexus-sardaukar-options.ts', 'game/choam-power-options.ts', 'game/nexus-moritani-options.ts', 'game/nexus-moritani-betrayal-options.ts', 'game/nexus-ecaz-betrayal-options.ts', 'game/nexus-ecaz-inquiry-options.ts', 'game/nexus-ecaz-duke-options.ts', 'game/nexus-richese-options.ts', 'game/bot-nexus-richese-betrayal.ts', 'game/nexus-guild-cunning-options.ts', 'game/bots.ts', 'game/nexus-fremen-revival-options.ts','game/nexus-emperor-payment.ts', 'game/nexus-emperor-secret-ally-options.ts']},
{area:"Documentation",status:'Partial',detail:"Physical Hero, permanent victims and historical references stay distinct. Native rate admission and Cunning receipt transitions are documented separately from income and transport permission. Existing full-price return and unaffordable replacement policies remain provisional; no new publisher priority or numerical ruling. Homeworlds, Skills, Discovery, Tech and Stronghold rate combinations and public gates remain separate.",evidence:['docs/NEXUS_CARD_RULES.md', 'docs/NEXUS_ATREIDES_RULES.md', 'docs/NEXUS_HARKONNEN_RULES.md', 'docs/NEXUS_TLEILAXU_RULES.md', 'docs/NEXUS_IXIAN_CUNNING_RULES.md', 'docs/NEXUS_CUNNING_RUNTIME.md', 'docs/NEXUS_BENE_GESSERIT_RULES.md', 'docs/NEXUS_ADVISOR_RUNTIME.md', 'docs/NEXUS_EMPEROR_RULES.md', 'docs/NEXUS_SARDAUKAR_RUNTIME.md', 'docs/NEXUS_FREMEN_RULES.md', 'docs/NEXUS_ECAZ_RULES.md', 'docs/NEXUS_CHOAM_RULES.md', 'docs/NEXUS_CHOAM_RUNTIME.md', 'docs/NEXUS_MORITANI_RULES.md', 'docs/NEXUS_MORITANI_RUNTIME.md', 'docs/NEXUS_RICHESE_RUNTIME.md', 'docs/NEXUS_RICHESE_RULES.md', 'docs/NEXUS_GUILD_CUNNING_RUNTIME.md', 'docs/NEXUS_GUILD_RULES.md', "docs/NEXUS_EMPEROR_SECRET_ALLY_RUNTIME.md", "docs/RULE_DECISIONS.md", "docs/RICHESE_AMBASSADOR_RULES.md", "docs/RICHESE_AMBASSADOR_IMPLEMENTATION.md", "docs/RICHESE_ACQUISITION_RULES.md", "docs/DUKE_VIDAL_RULES.md", "docs/ECAZ_DUKE_ACQUISITION.md", "docs/ECAZ_REVIVAL_RULES.md"]},
{area:"Verification",status:'Partial',detail:"Current affected nine-file101/101 and five action-only original API/policy paths pass. Six controlled Cunning reserve/cross cases cover allow/cancel/unaffordable replacement without reopening the first turn. Three actual390px journeys prove full price, no-debt withdrawal/replacement and Cunning turn completion. Earlier134/134 Hero/Traitor lifecycle evidence remains historical. No saved/deployed or full-module assurance claim.",evidence:['tests/mixed-nexus-modules-runtime.test.ts', 'tests/paired-e3-nexus-modules-runtime.test.ts', 'tests/sample-custody.test.ts', 'tests/ambassador-terror-overlap-engine.test.ts', 'tests/single-nexus-e1-runtime.test.ts', 'tests/single-nexus-e2-runtime.test.ts', 'tests/e3-nexus-ecaz-runtime.test.ts', 'tests/e3-nexus-moritani-runtime.test.ts', 'tests/homeworld-classic-nexus-skills-runtime.test.ts', 'tests/homeworld-classic-discovery-nexus-runtime.test.ts', 'tests/nexus-cards.test.ts', 'tests/nexus-card-engine.test.ts', 'tests/nexus-fremen-cunning.test.ts', 'tests/nexus-fremen-cunning-recovery.test.ts', 'tests/nexus-fremen-cunning-summon.test.ts', 'tests/nexus-fremen-cunning-summon-recovery.test.ts', 'tests/nexus-ecaz-betrayal-quotes.test.ts', 'tests/nexus-ecaz-betrayal.test.ts', 'tests/nexus-ecaz-betrayal-recovery.test.ts', 'tests/nexus-ecaz-inquiry.test.ts', 'tests/nexus-ecaz-duke-quote.test.ts', 'tests/nexus-ecaz-duke-engine.test.ts', 'tests/nexus-ecaz-duke-recovery.test.ts', 'tests/nexus-traitor-recovery.test.ts', 'tests/nexus-face-dancer-engine.test.ts', 'tests/nexus-suboids-engine.test.ts', 'tests/normal-free-support.test.ts', 'tests/nexus-suboid-controls.test.ts', 'tests/nexus-advisor-controls.test.ts', 'tests/nexus-advisor-bots.test.ts', 'tests/nexus-sardaukar-controls.test.ts', 'tests/nexus-sardaukar-bots.test.ts', 'tests/nexus-choam-controls.test.ts', 'tests/nexus-choam-bots.test.ts', 'tests/nexus-moritani-controls.test.ts', 'tests/nexus-moritani-bots.test.ts', 'tests/nexus-moritani-betrayal.test.ts', 'tests/nexus-richese-controls.test.ts', 'tests/nexus-richese-bots.test.ts', 'tests/nexus-richese-betrayal-engine.test.ts', 'tests/nexus-richese-betrayal-recovery.test.ts', 'tests/nexus-guild-cunning-controls.test.ts', 'tests/native-guild-rate-karama.test.ts', 'tests/nexus-guild-cunning-engine.test.ts', 'tests/nexus-guild-cunning-bots.test.ts', 'tests/nexus-module-payments-runtime.test.ts', 'tests/nexus-module-battles-runtime.test.ts', 'tests/paired-ix-nexus-modules-runtime.test.ts', 'tests/paired-choam-nexus-modules-runtime.test.ts', 'tests/classic-nexus-skills-payments-runtime.test.ts', 'tests/classic-nexus-skills-battles-runtime.test.ts', 'tests/nexus-skills-modules-payments-runtime.test.ts', 'tests/nexus-skills-modules-battles-runtime.test.ts', 'tests/paired-ix-nexus-skills-modules-runtime.test.ts', 'tests/paired-choam-nexus-skills-modules-runtime.test.ts', 'tests/ecaz-nexus-skills-modules-runtime.test.ts', 'tests/moritani-nexus-skills-modules-runtime.test.ts', 'docs/NEXUS_CARD_RULES.md', 'tests/nexus-tleilaxu-revival.test.ts', 'tests/fixture-nexus-tleilaxu-secret-ally.ts', 'tests/nexus-tleilaxu-secret-ally-runtime.test.ts', 'tests/nexus-borrowed-voice.test.ts', 'tests/fixture-nexus-borrowed-voice.ts', 'tests/nexus-borrowed-voice-runtime.test.ts', 'tests/nexus-fremen-protection.test.ts', 'tests/fixture-nexus-fremen-protection.ts', 'tests/nexus-fremen-protection-runtime.test.ts', 'tests/fixture-stronghold-factions.ts', 'tests/fixture-nexus-advisor-fresh.ts', 'tests/nexus-advisor-fresh-runtime.test.ts', 'tests/fixture-nexus-advisor-storm.ts', 'tests/nexus-advisor-storm-runtime.test.ts', 'docs/NEXUS_ADVISOR_RUNTIME.md', 'tests/nexus-fremen-free-quota.test.ts', 'tests/fixture-nexus-fremen-free-quota.ts', 'tests/nexus-fremen-free-quota-runtime.test.ts', 'tests/nexus-fremen-revival.test.ts','tests/nexus-emperor-payment.test.ts','tests/fixture-nexus-emperor-payment.ts','tests/nexus-emperor-payment-runtime.test.ts','docs/NEXUS_EMPEROR_RULES.md', 'tests/nexus-emperor-extra-source.test.ts', 'tests/nexus-emperor-extra-continuation.test.ts', 'tests/fixture-nexus-emperor-extra.ts', 'tests/nexus-emperor-extra-runtime.test.ts', 'tests/fixture-single-nexus-e1.ts', 'docs/NEXUS_EMPEROR_SECRET_ALLY_RUNTIME.md', "tests/fixture-nexus-emperor-seller.ts", "tests/nexus-emperor-seller-purchase.test.ts", "tests/nexus-emperor-seller-runtime.test.ts", "tests/fixture-nexus-emperor-ambassador.ts", "tests/nexus-emperor-ambassador-purchase.test.ts", "tests/nexus-emperor-ambassador-runtime.test.ts", "tests/fixture-nexus-emperor-special.ts", "tests/nexus-emperor-special-purchase.test.ts", "tests/nexus-emperor-special-runtime.test.ts", "tests/karama-intents.test.ts", "tests/fixture-nexus-emperor-bank.ts", "tests/nexus-emperor-bank-profile.test.ts", "tests/nexus-emperor-bank-runtime.test.ts", "tests/duke-captured-custody.test.ts", "tests/nexus-ecaz-duke-captured.test.ts", "tests/fixture-nexus-duke-capture.ts", "tests/nexus-duke-capture-runtime.test.ts", "Line truncated
{area:"Implementation",status:'Partial',detail:"Earlier physical Traitor roles and receipts remain integrated. Native rate now follows original price/source rather than blanket module flags: independent Smuggler/own-marker billing, typed Homeworlds and Cunning world frames retain exact source/payment/allowance. Manual/automatic interception, Skills and No-Field integrity recognize the original rate stage. Deferred Betrayal retains full-price authority until actual settlement. Immune Junction and borrowed sources stay distinct; allied markers, permission cancellation and full acceptance remain open.",evidence:['game/nexus-cards.ts', 'game/nexus-card-phase.ts', 'game/nexus-traitor-exchange.ts', 'game/traitor-declarations.ts', 'game/nexus-face-dancers.ts', 'game/nexus-suboids.ts', 'game/nexus-advisors.ts', 'game/nexus-sardaukar-options.ts', 'game/nexus-fremen-revival.ts', 'game/nexus-fremen-cunning.ts', 'game/nexus-ecaz-betrayal.ts', 'game/nexus-ecaz-inquiry.ts', 'game/nexus-ecaz-duke.ts', 'game/duke-vidal.ts', 'game/nexus-emperor-secret-ally.ts', 'game/choam-power-options.ts', 'game/nexus-moritani-options.ts', 'game/nexus-moritani-betrayal.ts', 'game/nexus-richese-options.ts', 'game/nexus-richese-betrayal.ts', 'game/nexus-guild-cunning-options.ts', 'game/nexus-guild-secret-ally-options.ts', 'game/nexus-module-profile.ts', 'game/engine.ts', 'game/nexus-tleilaxu-revival.ts', 'game/nexus-borrowed-voice.ts', 'game/combat-response-quote.ts', 'game/karama-context.ts', 'game/nexus-fremen-protection.ts', 'game/admin-seat-ai-shape.ts', 'game/richese-cancellation.ts', 'game/bot-mobility.ts', 'game/planetologist-movement.ts', 'game/revival.ts', 'game/nexus-fremen-revival-options.ts','game/nexus-emperor-payment.ts', 'game/nexus-emperor-secret-ally.ts', 'game/revival-resume.ts', 'game/revival-cancellation.ts', 'game/homeworld-revival-deployment.ts', 'game/homeworld-revival-return.ts', "game/nexus-emperor-purchase.ts", "game/auction-continuation-quote.ts", "game/nexus-emperor-purchase.ts", "game/ambassador-phase.ts", "game/ambassador-resume.ts", "game/battle-aftermath-quote.ts", "game/ecaz-duke-acquisition.ts", "game/ecaz-duke-revival.ts", "game/nexus-ecaz-duke-options.ts", "game/nexus-play-boundary.ts"]},
{area:"Player controls",status:'Partial',detail:"Four current390px journeys exercise composed original Smuggler shipping and ground movement, typed native return with approved donor1, poor world release/full-price replacement, and explicitly controlled own No-Field cancellation/reveal. Native instructions and prices agree after cancellation; one marker never exposes its hidden value in billing. Three original scenes, one controlled; no overflow/browser errors. Earlier Hero/Traitor/Cunning surfaces remain historical evidence, not full acceptance.",evidence:['components/nexus-cards.tsx', 'components/nexus-traitors.tsx', 'components/nexus-tleilaxu.tsx', 'components/nexus-suboids.tsx', 'components/nexus-advisors.tsx', 'components/nexus-sardaukar.tsx', 'components/choam-power-cost.tsx', 'components/moritani-terror.tsx', 'components/nexus-moritani-betrayal.tsx', 'components/nexus-ecaz-betrayal.tsx', 'components/nexus-ecaz-inquiry.tsx', 'components/nexus-ecaz-duke.tsx', 'components/nexus-richese-betrayal.tsx', 'components/game-table.tsx', 'components/nexus-tleilaxu-revival.tsx', 'components/battle-preparation.tsx', 'components/nexus-fremen-revival.tsx','components/nexus-emperor-payment.tsx', 'components/nexus-emperor-secret-ally.tsx', "components/nexus-emperor-seller-purchase.tsx", "components/nexus-emperor-ambassador-purchase.tsx", "components/richese-special-karama.tsx"]},
{area:"AI",status:'Partial',detail:"Both typed Homeworld owner quotes and bot budgets honor the actual canceled native tariff without repricing independent Nexus/Junction sources. All four policies legally pass the original native Homeworld rate in this position; human cancellation is separate evidence. Earlier Cunning/Hero/Traitor participation remains covered; no strategic tuning or calibration.",evidence:['game/nexus-card-options.ts', 'game/nexus-traitor-options.ts', 'game/nexus-tleilaxu-options.ts', 'game/nexus-suboid-options.ts', 'game/nexus-advisor-options.ts', 'game/nexus-sardaukar-options.ts', 'game/choam-power-options.ts', 'game/nexus-moritani-options.ts', 'game/nexus-moritani-betrayal-options.ts', 'game/nexus-ecaz-betrayal-options.ts', 'game/nexus-ecaz-inquiry-options.ts', 'game/nexus-ecaz-duke-options.ts', 'game/nexus-richese-options.ts', 'game/bot-nexus-richese-betrayal.ts', 'game/nexus-guild-cunning-options.ts', 'game/bots.ts', 'game/nexus-fremen-revival-options.ts','game/nexus-emperor-payment.ts', 'game/nexus-emperor-secret-ally-options.ts']},
{area:"Documentation",status:'Partial',detail:"Native price/source composition supersedes blanket module exclusions without canceling immune Homeworld or independent card benefits. Original versus controlled proof, approved escrow, private marker billing, Cunning world receipts and deferred full invoices are explicit. Earlier no-debt/return policies remain provisional; no new numeric or publisher-priority ruling. Allied markers, special receipts, permission cancellation and public acceptance remain separate.",evidence:['docs/NEXUS_CARD_RULES.md', 'docs/NEXUS_ATREIDES_RULES.md', 'docs/NEXUS_HARKONNEN_RULES.md', 'docs/NEXUS_TLEILAXU_RULES.md', 'docs/NEXUS_IXIAN_CUNNING_RULES.md', 'docs/NEXUS_CUNNING_RUNTIME.md', 'docs/NEXUS_BENE_GESSERIT_RULES.md', 'docs/NEXUS_ADVISOR_RUNTIME.md', 'docs/NEXUS_EMPEROR_RULES.md', 'docs/NEXUS_SARDAUKAR_RUNTIME.md', 'docs/NEXUS_FREMEN_RULES.md', 'docs/NEXUS_ECAZ_RULES.md', 'docs/NEXUS_CHOAM_RULES.md', 'docs/NEXUS_CHOAM_RUNTIME.md', 'docs/NEXUS_MORITANI_RULES.md', 'docs/NEXUS_MORITANI_RUNTIME.md', 'docs/NEXUS_RICHESE_RUNTIME.md', 'docs/NEXUS_RICHESE_RULES.md', 'docs/NEXUS_GUILD_CUNNING_RUNTIME.md', 'docs/GUILD_RATE_KARAMA_RULES.md', 'docs/GUILD_HOMEWORLD_TRANSPORT_RULES.md', 'docs/NEXUS_GUILD_RULES.md', "docs/NEXUS_EMPEROR_SECRET_ALLY_RUNTIME.md", "docs/RULE_DECISIONS.md", "docs/RICHESE_AMBASSADOR_RULES.md", "docs/RICHESE_AMBASSADOR_IMPLEMENTATION.md", "docs/RICHESE_ACQUISITION_RULES.md", "docs/DUKE_VIDAL_RULES.md", "docs/ECAZ_DUKE_ACQUISITION.md", "docs/ECAZ_REVIVAL_RULES.md"]},
{area:"Verification",status:'Partial',detail:"Current affected18-file198/198 includes32 new source cases. Twenty-one runtime/policy paths pass:17 original-action and four controlled Cunning/No-Field paths. Four actual390px journeys prove paid/free separation, exact typed return, no-debt full-price replacement and actual concealed-marker reveal; original movement/turn resumes once. Six controlled Betrayal Use/Pass cases retain full fee until deferred payment. Earlier lifecycle evidence stays historical; no saved/deployed or complete-module assurance.",evidence:['tests/mixed-nexus-modules-runtime.test.ts', 'tests/paired-e3-nexus-modules-runtime.test.ts', 'tests/sample-custody.test.ts', 'tests/ambassador-terror-overlap-engine.test.ts', 'tests/single-nexus-e1-runtime.test.ts', 'tests/single-nexus-e2-runtime.test.ts', 'tests/e3-nexus-ecaz-runtime.test.ts', 'tests/e3-nexus-moritani-runtime.test.ts', 'tests/homeworld-classic-nexus-skills-runtime.test.ts', 'tests/homeworld-classic-discovery-nexus-runtime.test.ts', 'tests/nexus-cards.test.ts', 'tests/nexus-card-engine.test.ts', 'tests/nexus-fremen-cunning.test.ts', 'tests/nexus-fremen-cunning-recovery.test.ts', 'tests/nexus-fremen-cunning-summon.test.ts', 'tests/nexus-fremen-cunning-summon-recovery.test.ts', 'tests/nexus-ecaz-betrayal-quotes.test.ts', 'tests/nexus-ecaz-betrayal.test.ts', 'tests/nexus-ecaz-betrayal-recovery.test.ts', 'tests/nexus-ecaz-inquiry.test.ts', 'tests/nexus-ecaz-duke-quote.test.ts', 'tests/nexus-ecaz-duke-engine.test.ts', 'tests/nexus-ecaz-duke-recovery.test.ts', 'tests/nexus-traitor-recovery.test.ts', 'tests/nexus-face-dancer-engine.test.ts', 'tests/nexus-suboids-engine.test.ts', 'tests/normal-free-support.test.ts', 'tests/nexus-suboid-controls.test.ts', 'tests/nexus-advisor-controls.test.ts', 'tests/nexus-advisor-bots.test.ts', 'tests/nexus-sardaukar-controls.test.ts', 'tests/nexus-sardaukar-bots.test.ts', 'tests/nexus-choam-controls.test.ts', 'tests/nexus-choam-bots.test.ts', 'tests/nexus-moritani-controls.test.ts', 'tests/nexus-moritani-bots.test.ts', 'tests/nexus-moritani-betrayal.test.ts', 'tests/nexus-richese-controls.test.ts', 'tests/nexus-richese-bots.test.ts', 'tests/nexus-richese-betrayal-engine.test.ts', 'tests/nexus-richese-betrayal-recovery.test.ts', 'tests/nexus-guild-cunning-controls.test.ts', 'tests/native-guild-rate-karama.test.ts', 'tests/native-guild-rate-modules.test.ts', 'tests/native-guild-homeworld-rate.test.ts', 'tests/native-guild-rate-betrayal.test.ts', 'tests/nexus-guild-cunning-engine.test.ts', 'tests/nexus-guild-cunning-bots.test.ts', 'tests/nexus-module-payments-runtime.test.ts', 'tests/nexus-module-battles-runtime.test.ts', 'tests/paired-ix-nexus-modules-runtime.test.ts', 'tests/paired-choam-nexus-modules-runtime.test.ts', 'tests/classic-nexus-skills-payments-runtime.test.ts', 'tests/classic-nexus-skills-battles-runtime.test.ts', 'tests/nexus-skills-modules-payments-runtime.test.ts', 'tests/nexus-skills-modules-battles-runtime.test.ts', 'tests/paired-ix-nexus-skills-modules-runtime.test.ts', 'tests/paired-choam-nexus-skills-modules-runtime.test.ts', 'tests/ecaz-nexus-skills-modules-runtime.test.ts', 'tests/moritani-nexus-skills-modules-runtime.test.ts', 'docs/NEXUS_CARD_RULES.md', 'tests/nexus-tleilaxu-revival.test.ts', 'tests/fixture-nexus-tleilaxu-secret-ally.ts', 'tests/nexus-tleilaxu-secret-ally-runtime.test.ts', 'tests/nexus-borrowed-voice.test.ts', 'tests/fixture-nexus-borrowed-voice.ts', 'tests/nexus-borrowed-voice-runtime.test.ts', 'tests/nexus-fremen-protection.test.ts', 'tests/fixture-nexus-fremen-protection.ts', 'tests/nexus-fremen-protection-runtime.test.ts', 'tests/fixture-stronghold-factions.ts', 'tests/fixture-nexus-advisor-fresh.ts', 'tests/nexus-advisor-fresh-runtime.test.ts', 'tests/fixture-nexus-advisor-storm.ts', 'tests/nexus-advisor-storm-runtime.test.ts', 'docs/NEXUS_ADVISOR_RUNTIME.md', 'tests/nexus-fremen-free-quota.test.ts', 'tests/fixture-nexus-fremen-free-quota.ts', 'tests/nexus-fremen-free-quota-runtime.test.ts', 'tests/nexus-fremen-revival.test.ts','tests/nexus-emperor-payment.test.ts','tests/fixture-nexus-emperor-payment.ts','tests/nexus-emperor-payment-runtime.test.ts','docs/NEXUS_EMPEROR_RULES.md', 'tests/nexus-emperor-extra-source.test.ts', 'tests/nexus-emperor-extra-continuation.test.ts', 'tests/fixture-nexus-emperor-extra.ts', 'tests/nexus-emperor-extra-runtime.test.ts', 'tests/fixture-single-nexus-e1.ts', 'docs/NEXUS_EMPEROR_SECRET_ALLY_RUNTIME.md', "tests/fixture-nexus-emperor-seller.ts", "tests/nexus-emperor-seller-purchase.test.ts", "tests/nexus-emperor-seller-runtime.test.ts", "tests/fixture-nexus-emperor-ambassador.ts", "tests/nexus-emperor-ambassador-purchase.test.ts", "tests/nexus-emperor-ambassador-runtime.test.ts", "tests/fixture-nexus-emperor-special.ts", "tests/nexus-emperor-special-purchase.test.ts", "tests/nexus-emperor-special-runtime.test.ts", "tests/karama-intents.test.ts", "tests/fixture-nexus-emperor-bank.ts", "tests/nexus-emperor-bank-profile.test.tsLine truncated
],
},
{