From 2c6bb02ccb57733de4299857e1d9c287fcc763a0 Mon Sep 17 00:00:00 2001 From: Quintine <15965533+Quintine@users.noreply.github.com> Date: Fri, 16 Oct 2026 03:19:26 +1100 Subject: [PATCH] Compose Harkonnen Ixian replacement before native purchase bonus --- components/nexus-ixian-replacement.tsx | 7 ++ docs/CURRENT_STATUS.md | 18 +++++ docs/IMPLEMENTATION_STATUS.md | 22 ++++++ docs/NEXUS_CARD_RULES.md | 2 +- docs/NEXUS_CARD_RUNTIME.md | 2 +- docs/NEXUS_IXIAN_REPLACEMENT_RULES.md | 75 ++++++++++++++++---- docs/RULE_DECISIONS.md | 2 +- game/engine.ts | 4 +- game/reference.ts | 13 ++-- tests/fixture-harkonnen-ixian-replacement.ts | 36 ++++++++++ tests/harkonnen-ixian-replacement.test.ts | 73 +++++++++++++++++++ tests/nexus-ixian-replacement-engine.test.ts | 30 -------- 12 files changed, 229 insertions(+), 55 deletions(-) create mode 100644 tests/fixture-harkonnen-ixian-replacement.ts create mode 100644 tests/harkonnen-ixian-replacement.test.ts diff --git a/components/nexus-ixian-replacement.tsx b/components/nexus-ixian-replacement.tsx index c3efe09b..edddb5c4 100644 --- a/components/nexus-ixian-replacement.tsx +++ b/components/nexus-ixian-replacement.tsx @@ -39,6 +39,13 @@ export function NexusIxianReplacement({ game, act, busy }: { Only the buyer can choose. The original payment stays paid; this is not another purchase or auction.{' '} Purchased-card timing and preview limits

+ {buyer?.faction === 'harkonnen' && ( +

+ Provisional Harkonnen order: choose this replacement before the original income and + bonus-card responses. Replacement is not another purchase: it grants no second bonus + and does not open a bonus slot at your eight-card limit. +

+ )} {canRespond && offer.purchased && (

{offer.purchased.name}

diff --git a/docs/CURRENT_STATUS.md b/docs/CURRENT_STATUS.md index 58c13dc5..8c8cf79c 100644 --- a/docs/CURRENT_STATUS.md +++ b/docs/CURRENT_STATUS.md @@ -29,6 +29,24 @@ optional skill bands, combinations and public expansion starts remain open. Rules implementation continues; comprehensive assurance and administration remain deferred until all rules are implemented. +**Harkonnen Ixian replacement preserves its native bonus — 16 October.** +Original purchased-card Use/Keep now precedes income and bonus under a +visibly provisional order matching the native Ixian ally pipeline. A +replacement is not a second purchase; the original bonus stays cancelable +and replacing at eight cards creates no extra slot. No publisher priority +or explicit user-selected sequence is claimed. + +Three new behavior cases; affected seven-file64/64, eight original runtime +paths and eight legal policy actions pass. Two original390px journeys +exercise Basic Use/bonus cancellation and Advanced Keep/bonus allowance, +preserving actual income, wallets, card identities and one next-lot advance. +The eight-card fixture separately controls conserved hand acquisition. +Width/scroll390; no browser/application error. No human server, D1, saved +room, deployment or public gate changed. +[Selected policy and scoped evidence](NEXUS_IXIAN_REPLACEMENT_RULES.md#harkonnen-purchase-and-bonus-composition--16-october-2026). +Final types/lint and production build pass with existing warnings. Current +source anchor and19 topic paths resolve; all five facets remain Partial. + **Ixian Nexus replacement follows native BG purchases — 16 October.** Successful original BG Worthless-as-Karama acquisition/payment now earns the same normal purchased-card replacement, after its native counter. Denied diff --git a/docs/IMPLEMENTATION_STATUS.md b/docs/IMPLEMENTATION_STATUS.md index 28cb2e73..c998fa26 100644 --- a/docs/IMPLEMENTATION_STATUS.md +++ b/docs/IMPLEMENTATION_STATUS.md @@ -1,5 +1,27 @@ # Dune implementation status +## 16 October 2026 — Harkonnen Ixian replacement and original bonus + +Removed the Harkonnen buyer fence from the existing normal-purchase offer +and pending-source validation. Under the standing choose-recommended +instruction, selected the native Ixian ally order: replacement choice, +original income, original cancelable bonus, next lot. The actual table +labels this provisional; it is not publisher timing or an explicit user +ordering decision. Replacement earns no second bonus or eight-card slot. + +Three meaningful cases replace the old guard pin. Affected seven-file64/64, +eight original runtime paths, eight legal policy actions and two actual390px +journeys pass. Basic replaces Snooper16 with Shield11, permits Emperor12→14, +then denies only the bonus, leaving Harkonnen three cards/spice8/spent Nexus. +Advanced keeps Chaumas4 and one bonus Snooper16, retaining four cards/ +spice10/Nexus. Both advance once. Eight-card boundary uses explicit conserved +hand acquisition; original action sources stage no played state. No human +server, D1, saved room, deployment or public gate changed. +[Source, selected policy and proof](NEXUS_IXIAN_REPLACEMENT_RULES.md#harkonnen-purchase-and-bonus-composition--16-october-2026). +Final types/lint and production build pass with module-register, large-chunk +and Unknown-route warnings. Current source anchor and19 topic paths resolve; +all five facets remain Partial, without new broad or HTTP assurance. + ## 16 October 2026 — Ixian Nexus replacement after native BG conversion Removed the printed-Karama-only producer flag from normal auction settlement. diff --git a/docs/NEXUS_CARD_RULES.md b/docs/NEXUS_CARD_RULES.md index 2b2c62c1..56568cfa 100644 --- a/docs/NEXUS_CARD_RULES.md +++ b/docs/NEXUS_CARD_RULES.md @@ -1540,7 +1540,7 @@ The private holder offer contains CHOAM's name; it includes the hand count only Later focused source contracts cover [Emperor strength, purchases and revival](NEXUS_EMPEROR_RULES.md), [Ixian purchased-card replacement](NEXUS_IXIAN_REPLACEMENT_RULES.md), [Fremen worm and revival effects](NEXUS_FREMEN_RULES.md), and [Richese's separate shipment and auction panels](NEXUS_RICHESE_RULES.md). These audits distinguish implemented work from remaining source/timing questions; current runtime evidence is tracked in [implementation status](IMPLEMENTATION_STATUS.md). Bounded Richese auction and Guild shipment Betrayal checkpoints have their own direct local verification below; neither inherits certification of its complete faction, Nexus family or live deployment from earlier evidence. -The [bounded Ixian Secret Ally replacement integration](NEXUS_IXIAN_REPLACEMENT_RULES.md#bounded-local-runtime-contract) now connects genuine normal paid and printed Karama-paid purchases in a fresh classic/base-deck/Nexus-only local profile. It replaces exactly the purchased physical card, keeps the original settlement once and permits full-hand and genuinely depleted-deck same-card redraw. Harkonnen buyers, cache/Black Market/special origins and combined modules remain guarded. Neutral buyer acknowledgement is an inferred development privacy convention, not publisher policy. Bounded native, private, authenticated recovery, CLI and phone verification is Partial; full-module and deployed acceptance remain open. The adopted unofficial Advanced core PDF contains no Nexus face and settles none of these pending Nexus boundaries. +The [bounded Ixian Secret Ally replacement integration](NEXUS_IXIAN_REPLACEMENT_RULES.md#bounded-local-runtime-contract) connects genuine normal paid and Karama purchases in the original fresh classic or Basic Tleilaxu profile. Successful native BG conversion qualifies after its native counter. Harkonnen's explicitly provisional order is replacement, original income, original cancelable bonus; no extra bonus or eight-card slot is gained. Replace only the purchased physical card and preserve settlement/full-hand/same-card semantics. Cache/Black Market/special origins and combined modules remain guarded. Neutral timing and provisional ordering are product composition, not publisher policy. The adopted Advanced core PDF supplies no Nexus face or priority ruling. Full-module, recovery and deployed acceptance remain open; source-qualified rule/control proof is recorded in the linked contract. **Atreides is the first complete effect family to implement.** It has independent rulebook and physical-face evidence, and the engine already has private plan-element inspection. The Nexus effect needs its own cause and receipt rather than temporarily changing the inspecting faction. The second element must differ from the already inspected element. Preserve the original disclosed value if an allowed later battle edit occurs; follow the existing plan commitment rules instead of refreshing an old observation. The card specifies an element, not the entire hand or battle plan. A weapon/defense request uses actual played-card classification, including the published Reinforcements/Harass exclusions, rather than leaking a card merely because it occupies that slot. diff --git a/docs/NEXUS_CARD_RUNTIME.md b/docs/NEXUS_CARD_RUNTIME.md index f025a5bf..b9e8ff1b 100644 --- a/docs/NEXUS_CARD_RUNTIME.md +++ b/docs/NEXUS_CARD_RUNTIME.md @@ -31,7 +31,7 @@ See the [source protocol](NEXUS_CARD_RULES.md#bounded-ixian-betrayal-source-prot and [current local contract](#bounded-ixian-betrayal-local-runtime--1-october-2026); the historical lifecycle counts below do not verify this later feature. -**Later Ixian Secret Ally replacement prototype:** [NEXUS_IXIAN_REPLACEMENT_RULES.md](NEXUS_IXIAN_REPLACEMENT_RULES.md#bounded-local-runtime-contract) records the fresh local `ixian-replacement` profile: exact just-purchased normal paid/printed Karama card, discard before private deck draw, full-hand and genuinely depleted-deck same-card behavior, unchanged original payment and native sale continuation once. Harkonnen buyers, cache/Black Market/special origins and combined modules remain guarded. The neutral buyer choice is an inferred development privacy convention, not publisher timing. Its bounded native/SQLite/four-bot/CLI/phone evidence is **Partial development verified**; this historical lifecycle checkpoint does not certify full effects or enable public/module starts. +**Later Ixian Secret Ally replacement prototype:** [NEXUS_IXIAN_REPLACEMENT_RULES.md](NEXUS_IXIAN_REPLACEMENT_RULES.md#bounded-local-runtime-contract) records the fresh local `ixian-replacement` profile: exact normal purchased card, discard before private deck draw, full-hand/same-card behavior and unchanged settlement once. Successful native BG conversion purchases now qualify after their native counter. Harkonnen uses the visibly provisional native replacement-before-income/bonus order, preserving its original cancelable bonus and capacity. Cache/Black Market/special origins and combined modules remain guarded. Neutral buyer timing and provisional ordering are product composition, not publisher priority. Historical native/recovery/CLI evidence and the newer original-source rule/control proof remain Partial; no public/module gate opens. The later [Emperor Secret Ally revival prototype](NEXUS_EMPEROR_SECRET_ALLY_RUNTIME.md) adds exactly three additional free force returns, separate ordinary allowances, private controls and saved continuation. Its purchase alternative and full module acceptance remain pending. diff --git a/docs/NEXUS_IXIAN_REPLACEMENT_RULES.md b/docs/NEXUS_IXIAN_REPLACEMENT_RULES.md index c9fb49ef..484838ae 100644 --- a/docs/NEXUS_IXIAN_REPLACEMENT_RULES.md +++ b/docs/NEXUS_IXIAN_REPLACEMENT_RULES.md @@ -2,6 +2,53 @@ Source audit: 2026-09-10. Bounded local prototype integration and verification: 2026-10-01. **Partial development verified**, not complete Ixian/Nexus family or expansion acceptance. This document records the source and runtime boundary of the fresh `ixian-replacement` profile. Existing [Nexus release gates](NEXUS_CARD_RUNTIME.md) remain in force. +## Harkonnen purchase and bonus composition — 16 October 2026 + +Harkonnen buyers now receive the same neutral purchased-card choice in +the existing original classic Basic/Advanced or Basic Tleilaxu profile. +The printed one-for-one replacement preserves the purchase-earned bonus, +its original hand cap and native Karama cancellation; it is not a second +purchase and earns no extra bonus. The bonus itself is never a replacement +target. + +**Selected provisional ordering:** under the standing choose-recommended +instruction, preserve the existing native Ixian ally sale pipeline: +replacement choice → original income response → original Harkonnen bonus +response → next lot. The Nexus face does not explicitly order these two +draws; this is conservative product composition, not a publisher ruling or +an explicit user-selected ordering. The actual table labels it provisional. +No owner-selected ordering control or bonus-first inspection is introduced. + +After Use, the actual next deck card is the replacement; any allowed bonus +then draws the following card. After Keep, the next card is the bonus. +Canceling that later bonus cannot undo replacement, restore Nexus, refund +the purchase or repeat income. At eight held cards after purchase, replacing +one-for-one still leaves eight and creates no bonus slot. + +Three new behavior cases and the affected seven-file union pass **64/64**. +Eight original-action runtime paths cover both modes, Use/Keep and native +bonus allow/cancel; eight legal policy actions pass. Genuine setup, actual +worm/alliance and two Nexus draws, original Atreides purchase/Keep and the +paid Harkonnen second lot precede every original source. The eight-card +boundary separately controls only conserved undealt hand acquisition. + +Two actual styled390×844 journeys pass. Basic replaces purchased Snooper16 +with Shield11, allows Emperor12→14 and cancels only the bonus: Harkonnen +keeps three cards/spice8 and spent Nexus. Advanced keeps purchased Chaumas4, +allows original income and one Snooper16 bonus, retaining four cards/spice10 +and Nexus. Both advance the original lot once. Width/scroll390; no browser/ +application errors. Only an owned memory-only loopback host ran; no human +server, D1, saved room, deployment or public start changed. Proof is outside +Git: `/tmp/dune-harkonnen-replacement-runtime.json`, +`/tmp/dune-harkonnen-replacement-ui-proof.json`. +Special origins, other modules and final assurance remain separate; all +five checklist facets stay Partial. +Final types/lint and production build pass, retaining module-register, +large-chunk and Unknown-route warnings. Current source anchor and19 topic +evidence paths resolve with no reference parse diagnostics; all five facets +remain Partial. No broad suite or HTTP assurance run. + + ## Native BG converted purchases — 16 October 2026 Original normal-auction purchases now offer the same independent Ixian @@ -34,8 +81,9 @@ with width/scroll390 and no browser/application error. Only an owned memory-only loopback host ran; no human server, D1, saved room, deployment or public gate changed. Proof remains outside Git: `/tmp/dune-bg-ixian-runtime.json`, `/tmp/dune-bg-ixian-ui-proof.json`. -Broader profiles, Harkonnen bonus priority, special sale origins and final -assurance remain separate; all five checklist facets remain Partial. +Broader profiles, special sale origins and final assurance remain separate; +the later provisional Harkonnen composition is recorded above. All five +checklist facets remain Partial. Final `npm run check:quick` passes; the current source anchor and17 topic evidence paths resolve, with no reference parse diagnostics. No production build, broad suite or HTTP assurance was run for this bounded rule slice. @@ -83,13 +131,13 @@ November's Karama table expressly permits stopping the native Ixian ally from di ## Ordering and unresolved boundaries -**Harkonnen draw order:** the purchase earns its bonus, and Nexus applies to the just-purchased card. Neither inspected paragraph explicitly orders these two post-purchase draws. Choosing replacement after viewing the bonus can change the informed decision; bonus cancellation can also affect the deck. Preserve separate purchased, replacement and bonus identities and report the priority gap before selecting a fixed order. Owner-selected ordering is a possible implementation proposal, not a retrieved official ruling. The free bonus itself is never an eligible purchased target. +**Harkonnen draw order:** the purchase earns its original bonus, while Nexus applies only to the purchased card. The inspected source does not explicitly order these draws. Under the standing choose-recommended instruction, the selected provisional composition reuses native Ixian ally ordering: replacement first, then original income and bonus responses. The table labels this policy; it is not publisher timing or an explicit user-selected sequence. Replacement grants no second bonus or extra capacity, and the free bonus is never the purchased target. **Other immediate disposal effects:** use the ordinary physical-discard pipeline. Applicable Ecaz poison income and Semuta reactions must neither duplicate the discarded card nor cause a second replacement. Precise competing immediate-effect priority is not supplied by this Nexus face. Existing Semuta and occupation source gaps remain separate. **Nullentropy and custody interruptions:** a paid Box search freezes the searched pile in the existing subsystem. The replacement cannot draw or reshuffle that pile while browsing. Conversely, playing or transferring the just-purchased card cannot leave a live replacement offer pointing at custody it no longer owns. Preserve or settle the exact purchase continuation; do not reopen an expired purchase from an arbitrary historical card ID. Whether a player can interleave a particular anytime power before exercising an immediate purchase right needs an explicit sequencing policy, not an invisible loss of the right. See the [Box audit](NULLENTROPY_BOX_ENGINE_AUDIT.md). -**Private opportunity timing:** a pause offered only to the eligible Ixian-card holder may reveal the secret identity. This bounded prototype instead gives every publicly unallied supported buyer with public held-Nexus presence the same neutral **Purchased card choice**, independently of its hidden face. An irrelevant-face buyer can Pass but cannot Use. This is an inferred development privacy convention, not publisher-prescribed timing, a ruling on all anytime interleavings or approval of every unresolved private-response design. The Harkonnen-buyer and unsupported-origin guards depend on public faction/origin, not secret face. +**Private opportunity timing:** a pause offered only to the eligible Ixian-card holder may reveal the secret identity. This bounded prototype instead gives every publicly unallied supported buyer with public held-Nexus presence the same neutral **Purchased card choice**, independently of its hidden face. An irrelevant-face buyer can Pass but cannot Use. This is an inferred development privacy convention, not publisher-prescribed timing, a ruling on all anytime interleavings or approval of every unresolved private-response design. Unsupported-origin guards depend on public origin, not secret face. ## Bounded local runtime contract @@ -105,7 +153,7 @@ and never add seats to an admitted game or fabricate Nexus hands. ### Connected purchase and choice 1. Only an actual normal-auction purchase is connected: paid spice or genuine Karama acquisition/payment, including direct acquisition before a positive bid. Native Advanced BG Worthless conversion must first complete its existing response; successful free purchases then get the same replacement choice. A canceled direct conversion makes no purchase; canceled winning payment follows its original funded spice-payment recovery and, if purchased, supplies a paid receipt. The original source, purchased physical card and native sale continuation remain bound after genuine acquisition. Gifts, bonus draws, Technology swaps, old hand cards and historical purchase IDs cannot create an opportunity. -2. A Harkonnen **buyer** and Richese cache, Black Market or special-acquisition origins are guarded, not adjudicated. A Harkonnen rival is allowed and retains its ordinary native play outside this offer. These bounds preserve the unresolved independent Nexus cache scope and Harkonnen bonus priority above; they are not printed prohibitions or imports of the native `ixAllyCard` origin filter. +2. Harkonnen buyers use the visibly provisional replacement-before-income/bonus order above, preserving the original cancelable bonus and eight-card limit. Richese cache, Black Market and special-acquisition origins remain guarded under their separate independent Nexus scope; the native `ixAllyCard` origin filter is not imported as authority. 3. Every publicly possible supported buyer receives the same event and public buyer identity, regardless of its secret Nexus face. Only that buyer receives Pass and the exact already-owned purchased card for inspection; only a truly eligible unallied holder of the physical Ixian Nexus receives Use. Opponents receive neither the purchased/replacement face nor private price, receipt, deck order or eligibility reason. The buyer may inspect its own actually held Nexus through the existing authorized inspector; the neutral prompt grants no rival entitlement. 4. **Pass** retains the purchased card and Nexus, closes this opportunity and continues the original sale once. **Use** spends that singleton Nexus once, discards exactly the just-purchased physical Treachery Card, then draws the actual next deck card privately. There is no card selector, arbitrary hand exchange, new Karama counter, refund, repayment or re-auction. 5. The discard opens a full legal hand's slot before the draw. An exhausted deck recycles eligible Treachery discards, including that just-discarded card; drawing the same physical card again is legal. The unsold auction row is not the draw deck. Earlier Atreides auction inspection does not grant a new peek of this replacement. @@ -160,14 +208,13 @@ Existing base-profile full-hand and genuinely depleted-deck regressions already exercise the same discard-first/replenishment path and are retained rather than duplicated with an artificially emptied native deck. -This is ordinary composition of the printed replacement and existing native -setup/auction lifecycle, not a new Nexus ruling or a completed family claim. -Harkonnen buyers, special sale origins, private opportunity timing, -Richese cache scope and Harkonnen bonus ordering keep their bounds above. -No pending timing/cancellation, cache, bonus or combined-module question is -settled by this added deck/faction admission. Verification of this integration -is reported with the frozen native payment wave, separately from the earlier -base-profile evidence below. +This is ordinary composition of printed replacement and existing native +setup/auction lifecycle, not a new Nexus ruling or completed family claim. +Special sale origins, private opportunity timing, cache scope and combined +modules keep their bounds above. This earlier deck/faction integration did +not choose Harkonnen ordering; the later explicit provisional composition +is recorded at the opening of this document. Native verification below +belongs to the frozen native payment wave, not the newer Harkonnen proof. Bounded native verification passes four meaningful regressions in the shared 110-case affected batch; final native/guide26/26, types/lint and build pass. @@ -224,4 +271,4 @@ custody/history), `tests/nexus-ixian-replacement-engine.test.ts` and genuine `tests/prototype-room.test.ts` (fresh profile and exact-version backed-up CLI). This evidence is not complete family/module or deployed acceptance. -Native Harkonnen-buyer bonus/cancellation/cap sequencing, independent Richese cache/Black Market/special scope, expansion-card discard reactions, Semuta/Ecaz priority and anytime interleaving remain pending outside this profile. Full Ixian/Nexus families, combined modules, public starts, complete games and live release acceptance remain separate gates. +Independent Richese cache/Black Market/special scope, expansion-card discard reactions, Semuta/Ecaz priority and anytime interleaving remain outside this profile. Harkonnen's connected replacement-before-bonus sequence is explicitly provisional, not a settled publisher priority. Full Ixian/Nexus families, combined modules, public starts, complete games and live release acceptance remain separate gates. diff --git a/docs/RULE_DECISIONS.md b/docs/RULE_DECISIONS.md index a7366e1d..bfb2a8d2 100644 --- a/docs/RULE_DECISIONS.md +++ b/docs/RULE_DECISIONS.md @@ -694,7 +694,7 @@ dedicated retrieved worked FAQs. [Contract and remaining limits](HOMEWORLD_OCCUP | Advanced Richese ally contribution | The authorized supplied Advanced Richese Bidding clause routes Richese's contribution to its ally's cache/Black Market purchase to Emperor or bank, even when Emperor buys. Preserve the original donor leg separately from buyer-to-Richese credit; cancellation redirects only the Emperor leg without paying twice. Basic retains R2's earlier receipt. Native Banker counts only final bank legs; exhausted-cache counts, positive Black Market self-bidding and Technology custody remain unresolved. | [Source distinction and recipient contract](RICHESE_AUCTION_RULES.md#authorized-advanced-contribution-recipients--4-october-2026) | | Richese Nexus Betrayal auctions | Pause at a valid original prepayment quote. Public self-cache veto discards the exact card without debit/acquisition; another buyer's positive cache/Black Market invoice keeps price/contributions/delivery and earned Harkonnen bonus while overriding the recipient to bank. All publicly possible unallied other-faction held-Nexus seats acknowledge uniformly; private identity controls only own Use. This neutral protocol is an application choice, not publisher timing. Normal hidden-family self-purchases, special-Karama veto and unsupported overlays remain excluded; no native Karama exception certifies them. Bounded engine/SQL/bot/phone scenarios are verified locally; broader verification remains Partial and public starts/full Nexus gates stay closed. | [Printed face, profile, API and saved continuation](NEXUS_RICHESE_RULES.md#bounded-betrayal-auction-contract--30-september-2026), [common privacy protocol](NEXUS_CARD_RULES.md#richese-betrayal-prepayment-protocol), [auction source boundaries](RICHESE_AUCTION_RULES.md) | | Guild Nexus Betrayal shipment payment | Replace one original funded positive fee in full, including own pre-funded payment and the printed full occupied-Junction override; preserve original payer/contributions, consume already-debited donor escrow once and deliver the original physical shipment once. Native applicable stop/rate responses precede the bounded gate; zero/stopped shipments supply no award or Nexus cost. Public possible held-Nexus seats acknowledge uniformly; private Use changes no public price/source disclosure. Fresh classic/native-Guild/Nexus with optional genuinely seeded Homeworlds only; unsupported native Cunning/Richese shipment overlays block before card/parent commitment, and restored transport independently requires the native Guild grant. Bounded engine/SQL/bot/CLI/phone custody and refresh are locally verified; no new priority/immunity, ordinary occupation/rounding ruling or extra shipment. Broad/public/live gates remain closed. | [Printed source and bounded contract](NEXUS_GUILD_RULES.md#nexus-guild-betrayal), [common protocol](NEXUS_CARD_RULES.md#guild-betrayal-funded-payment-protocol), [internal checklist](../game/reference.ts) | -| Ixian Nexus Secret Ally replacement | Fresh classic/base33 Basic/Advanced or Basic Tleilaxu/classic Ix47, Ixians absent and Nexus alone. Spend the singleton Nexus to discard exactly the normal purchased card before drawing the actual deck top; paid, printed Karama and successful native BG conversion purchases retain their original payment/income/suffix. Native BG cancellation resolves first: denied direct acquisition supplies no purchase; funded winning-payment recovery supplies a paid receipt. Full-hand/same-card redraw remain legal. Neutral buyer timing is product policy, not publisher priority. Harkonnen buyers, special origins and other overlays remain guarded; no public start or retrofit. | [Printed operation and base contract](NEXUS_IXIAN_REPLACEMENT_RULES.md#bounded-local-runtime-contract), [BG composition](NEXUS_IXIAN_REPLACEMENT_RULES.md#native-bg-converted-purchases--16-october-2026), [native integration](NEXUS_IXIAN_REPLACEMENT_RULES.md#native-basic-tleilaxu-integration--2-october-2026) | +| Ixian Nexus Secret Ally replacement | Fresh classic/base33 Basic/Advanced or Basic Tleilaxu/classic Ix47, Ixians absent and Nexus alone. Replace only the normal purchased card with the actual deck top; paid, printed Karama and successful native BG conversion retain original settlement. Native BG counter resolves first; funded winning-payment recovery supplies a paid receipt. Harkonnen now uses visibly provisional native order: replacement, original income, original cancelable bonus. No second bonus or eight-card capacity gain. This sequencing and neutral buyer timing are product policy, not publisher priority. Special origins/other overlays remain guarded; no public start or retrofit. | [Base contract](NEXUS_IXIAN_REPLACEMENT_RULES.md#bounded-local-runtime-contract), [BG composition](NEXUS_IXIAN_REPLACEMENT_RULES.md#native-bg-converted-purchases--16-october-2026), [provisional Harkonnen order and evidence](NEXUS_IXIAN_REPLACEMENT_RULES.md#harkonnen-purchase-and-bonus-composition--16-october-2026) | | Ixian Nexus Betrayal: Bidding or Technology | Fresh native-Ixian/classic-or-Tleilaxu Basic/Advanced local `ixian-betrayal` uses Ix47 Treachery and Nexus alone, with no initial Nexus deal or old-save retrofit. Resolve actual native Karama/BG counters first; a native denial opens no Nexus gate or cost. After all native passes, uniform publicly possible unallied non-Ixian held-Nexus seats acknowledge before the native effect; concealed face governs only private Use. These are bounded privacy/ordering inferences, not publisher universal priority. One physical Ixian Nexus denies one current Bidding attempt (normal count, no Ixian inspection/return) or one Advanced Technology declaration (both card locations preserved, attempt remains used); all-pass allows the original effect and suffix once. No whole-phase ban, completed undo, invented direct-Nexus Karama counter or blanket immunity. Richese special-lot exchange, wider priority/modules, complete games, strategy and public/live release remain open; verification is Planned pending frozen direct evidence. | [Photographed face and native source protocol](NEXUS_CARD_RULES.md#bounded-ixian-betrayal-source-protocol--1-october-2026), [fresh local runtime and acceptance](NEXUS_CARD_RUNTIME.md#bounded-ixian-betrayal-local-runtime--1-october-2026), [five-facet internal topic](../game/reference.ts) | | Harkonnen Nexus Betrayal local preview | Fresh classic/base33/Nexus-only `harkonnen-betrayal` admits genuine personal/allied native matching calls. Native allied Karama/BG allowance comes first; a provisional uniform acknowledgement precedes the original outcome. Use returns the exact physical traitor immediately and preserves one automatic private actual Mentat replacement. The local ordering/privacy convention is not publisher/user priority and does not answer the previously asked universal reaction policy; public starts, old-game retrofit, arbitrary stacks and combined modules remain gated. Bounded native/private/recovery/CLI/phone verification is Partial development verified, not complete modes or deployment. | [Bounded source contract](NEXUS_HARKONNEN_RULES.md#bounded-betrayal-preview-contract--1-october-2026), [runtime limits](NEXUS_CARD_RUNTIME.md) | | CHOAM Secret Ally | Absent-faction Collection trade spends one actual Worthless card and the Nexus for two bank spice. It is separate from native Cunning and Auditor; the battle inspection alternative remains missing. | [Prototype contract](NEXUS_CHOAM_SECRET_ALLY.md) | diff --git a/game/engine.ts b/game/engine.ts index 7f9a7f47..e7f86d9d 100644 --- a/game/engine.ts +++ b/game/engine.ts @@ -9969,7 +9969,7 @@ function ixianNexusReplacementIntegrity(g: Game): void { return; } const buyer = getPlayer(g,pending.buyer); - requireRule(!buyer.ally && buyer.faction !== 'harkonnen' && !!g.nexusCards?.cards?.hands[buyer.id] && + requireRule(!buyer.ally && !!g.nexusCards?.cards?.hands[buyer.id] && g.decision?.kind === 'nexusIxianReplacement' && g.decision.player === buyer.id && g.decision.event === pending.event && Object.keys(g.decision).every(key => ['kind','player','event'].includes(key)) && @@ -9984,7 +9984,7 @@ function offerIxianNexusReplacement(g: Game): boolean { const sale = g.currentAuctionSale; const buyer = sale && getPlayer(g,sale.winner); if (!sale || !buyer || sale.origin !== 'normal' || - buyer.faction === 'harkonnen' || buyer.ally || !g.nexusCards?.cards?.hands[buyer.id]) return false; + buyer.ally || !g.nexusCards?.cards?.hands[buyer.id]) return false; requireRule(!g.response && !g.phaseOpening && !g.truthtrance && !g.pendingKarama, 'Finish the original auction priority before offering the purchased-card choice.'); const event = ixianReplacementEvent(g.turn,buyer.id,g.nexusIxianReplacementCursor!.sequence,g.auction!.index); diff --git a/game/reference.ts b/game/reference.ts index 9e91efd3..05e0b272 100644 --- a/game/reference.ts +++ b/game/reference.ts @@ -4516,11 +4516,12 @@ export const RULE_TOPICS: RuleTopic[] = [ 'This fresh local profile admits two-to-six unique classic-faction seats in Basic or Advanced with base Treachery, or additionally Basic Tleilaxu plus classic opponents with the original Ix47 deck and Ixians absent. Nexus alone; optional Sandtrout is omitted only in the fresh native variant. Advanced Tleilaxu, other native expansions, Leader Skills, Homeworlds, Semuta, Discoveries, Ecaz variants and other overlays remain guarded. No public start or saved-game upgrade.', 'Nexus has no starting deal. A draw requires a qualifying closing Nexus with an alliance. A two-seat profile can continue its base game but cannot naturally create an unallied Secret Ally holder; actual replacement needs at least three seats, or four for two simultaneous unallied recipients.', 'The photographed publisher face grants an independent Secret Ally operation when Ixians are absent. Remain unallied and hold the actual singleton Ixian Nexus. Native Ixian allied replacement is a different cancelable advantage and is not removed or used as a fallback. The adopted unofficial Advanced rulebook supplies relevant core rules, not this absent Nexus face or a new Nexus ruling.', - 'Wait for a genuine normal-auction purchase, paid with spice or through Karama, including direct acquisition before a positive bid. A successful native Advanced BG Worthless-as-Karama conversion gets the same independent replacement choice after its native counter resolves. Canceling direct acquisition supplies no purchase; a canceled winning-payment conversion follows native funded spice recovery and, if bought, supplies a paid receipt instead. Harkonnen buyers and cache, Black Market or special-purchase origins remain guarded pending their separate bonus priority and Nexus scope. Gifts, bonus draws, Technology swaps and old hand cards are not purchases.', + 'Wait for a genuine normal-auction purchase, paid with spice or through Karama, including direct acquisition before a positive bid. A successful native Advanced BG Worthless-as-Karama conversion gets the same independent replacement choice after its native counter resolves. Canceling direct acquisition supplies no purchase; a canceled winning-payment conversion follows native funded spice recovery and, if bought, supplies a paid receipt instead. Cache, Black Market and special-purchase origins retain their separate guarded Nexus scope. Gifts, bonus draws, Technology swaps and old hand cards are not purchases.', 'Every publicly unallied supported buyer with public held-Nexus presence receives the same neutral Purchased card choice, irrespective of the secret face. An irrelevant-face buyer may Pass but cannot Use. This is an inferred development privacy convention, not publisher-prescribed timing or a general resolution of pending anytime and response-priority questions.', 'Only the buyer may inspect its already-owned purchased card in this choice. You may inspect your own actually held Nexus through the existing authorized inspector; the prompt does not identify anybody else’s Nexus. Rivals receive no purchased or replacement face, private price, receipt, deck order or eligibility reason. Earlier Atreides auction inspection does not grant a peek of the replacement.', 'Pass keeps your purchased card and Nexus and closes this purchase opportunity. Use spends your physical Ixian Nexus once and discards exactly that just-purchased Treachery Card before drawing the actual next deck card. You cannot select another held card, replacement face, price or recipient; both actions bind only the offered event.', 'A full legal hand can replace because the discard opens its slot first. If the draw deck is empty, recycle eligible Treachery discards including the card just discarded: the same physical card can legally return. The unsold auction row is not the draw deck, and no different-card guarantee or new auction is created.', + 'Harkonnen uses a visibly provisional order matching the native Ixian ally sale pipeline: this replacement choice, original income response, original bonus-card response, then the next lot. It is product composition, not a printed priority ruling. Use draws the replacement before any allowed bonus; Keep leaves that top card for the bonus. Replacement grants no second bonus or slot at the eight-card limit. Canceling the later native bonus never undoes the replacement or original payment.', 'Keep the original payment, funded contribution and free/paid source. There is no refund or second debit. Original income and the native auction continuation finish once after the choice, preserving existing response and decision priority. Other lot, card, transfer and payment actions cannot bypass the pending source lock; independent own-seat autopilot changes remain available.', 'Refresh restores the same pending event or closed outcome without another payment, draw, shuffle or Nexus spend. A later genuine purchase may create a new event; closed, stale, foreign or malformed events cannot reopen an old one. Later transfers and reshuffles do not invalidate history merely because the old physical card has moved again. Receipt links are nonrecursive so saved history stays linear across retained purchases.', 'Easy, Medium, Hard and Brutal use their own projected legal Use/Pass choices. These are minimal legal policies, not calibrated Nexus strategy. Frozen native, private controls, actual local CLI/phone and authenticated recovery evidence verifies only this bounded path. Full faction/module games, wider compositions, public starts and deployed acceptance remain separate gates.', @@ -4528,11 +4529,11 @@ export const RULE_TOPICS: RuleTopic[] = [ example: 'You buy a card for four spice and inspect it privately. Pass keeps it and the Nexus. Use keeps the original four-spice purchase settled, spends Ixian Nexus, discards that exact card and draws the next deck card. If the draw deck is empty, its new discard joins the actual recycled pile and can be drawn again.', related: ['nexus-cards', 'bidding', 'ix-technology', 'card-karama', 'alliance-funding', 'privacy', 'implementation-checklist'], checklist: [ - { area: 'Implementation', status: 'Partial', detail: 'The fresh bounded profile binds the exact canonical normal purchase, physical replacement and closed-event history to native settlement. Successful BG converted acquisition/payment now composes after its native counter; denied direct acquisition and funded paid recovery remain distinct. Harkonnen buyers, special origins and combined modules remain guarded.', evidence: ['game/nexus-ixian-replacement.ts', 'game/engine.ts', 'tools/prototype-room.ts'] }, - { area: 'Player controls', status: 'Partial', detail: 'Neutral Purchased card choice separates buyer-only purchased-card inspection and legal Use/Pass from public waiting; own-held Nexus inspection grants no rival entitlement. Broader presentation acceptance remains open.', evidence: ['components/nexus-ixian-replacement.tsx', 'components/game-table.tsx'] }, - { area: 'AI', status: 'Partial', detail: 'All four minimal legal policies consume only their own projected event and eligibility while the underlying auction waits. Eight original-state legal actions cover BG converted acquisition/payment. Full strategy and difficulty calibration remain unfinished.', evidence: ['game/bots.ts'] }, - { area: 'Documentation', status: 'Partial', detail: 'Photographed face, exact physical target, full-hand/empty-deck rules, original settlement once, privacy convention and unresolved cache/Harkonnen/priority boundaries are recorded without PDF Nexus authority or full-family claims.', evidence: ['docs/NEXUS_IXIAN_REPLACEMENT_RULES.md', 'docs/NEXUS_CARD_RULES.md', 'docs/RULE_DECISIONS.md'] }, - { area: 'Verification', status: 'Partial', detail: 'Earlier base/native Tleilaxu acquisition, custody, private/recovery and phone evidence remains historical. New BG composition: four meaningful behavior cases, affected nine-file74/74, four original Use/Pass runtime paths, eight legal policy actions and two actual390px original-source replacement/Keep journeys pass. Controlled counter custody separately covers denied direct acquisition versus native funded paid recovery. No new saved/deployed assurance; full families/modules remain open.', evidence: ['tests/nexus-ixian-replacement.test.ts', 'tests/nexus-ixian-replacement-engine.test.ts', 'tests/fixtures/nexus-ixian-native-depleted.json', 'tests/fixture-nexus-ixian-replacement.ts', 'tests/fixture-native-ixian-replacement.ts', 'tests/native-ixian-replacement.test.ts', 'tests/bg-ixian-replacement.test.ts', 'tests/fixture-bg-ixian-replacement.ts'] }, + { area: 'Implementation', status: 'Partial', detail: 'The bounded profile binds the exact normal purchase, physical replacement and closed history to native settlement. BG conversion and paid recovery remain source-specific. Harkonnen composes replacement before original income/bonus, preserving native bonus cancellation and capacity. Special origins and combined modules remain guarded.', evidence: ['game/nexus-ixian-replacement.ts', 'game/engine.ts', 'tools/prototype-room.ts'] }, + { area: 'Player controls', status: 'Partial', detail: 'Neutral Purchased card choice separates buyer-only inspection and Use/Keep from public waiting. Harkonnen sees the provisional replacement-before-income/bonus explanation. Two original390px journeys exercise Use then bonus cancellation, or Keep then one allowed bonus; no overflow/browser error. Broader acceptance remains open.', evidence: ['components/nexus-ixian-replacement.tsx', 'components/game-table.tsx'] }, + { area: 'AI', status: 'Partial', detail: 'Four minimal legal policies consume only their own projected event while the underlying auction waits. Eight original-state actions cover BG conversion, and eight more cover Basic/Advanced Harkonnen replacement/bonus continuation. Full strategy and calibration remain unfinished.', evidence: ['game/bots.ts'] }, + { area: 'Documentation', status: 'Partial', detail: 'Printed physical operation, settlement, full-hand/empty-deck rules and explicit provisional Harkonnen replacement-before-bonus policy are recorded. Cache scope, competing effects and complete-family acceptance remain separate; no publisher priority is invented.', evidence: ['docs/NEXUS_IXIAN_REPLACEMENT_RULES.md', 'docs/NEXUS_CARD_RULES.md', 'docs/RULE_DECISIONS.md'] }, + { area: 'Verification', status: 'Partial', detail: 'Earlier base/native evidence remains historical. BG composition passes affected74/74, four original runtime paths, eight policies and two390px journeys. Harkonnen adds three meaningful cases, affected64/64, eight original runtime paths, eight policies and two390px journeys covering separate native bonus cancellation/allowance; the eight-card boundary explicitly controls conserved hand acquisition. No new saved/deployed assurance; full families/modules remain open.', evidence: ['tests/nexus-ixian-replacement.test.ts', 'tests/nexus-ixian-replacement-engine.test.ts', 'tests/fixtures/nexus-ixian-native-depleted.json', 'tests/fixture-nexus-ixian-replacement.ts', 'tests/fixture-native-ixian-replacement.ts', 'tests/native-ixian-replacement.test.ts', 'tests/bg-ixian-replacement.test.ts', 'tests/fixture-bg-ixian-replacement.ts', 'tests/harkonnen-ixian-replacement.test.ts', 'tests/fixture-harkonnen-ixian-replacement.ts'] }, ], }, { diff --git a/tests/fixture-harkonnen-ixian-replacement.ts b/tests/fixture-harkonnen-ixian-replacement.ts new file mode 100644 index 00000000..89d6a87e --- /dev/null +++ b/tests/fixture-harkonnen-ixian-replacement.ts @@ -0,0 +1,36 @@ +import assert from 'node:assert/strict'; +import { applyAction, createGame, joinGame, newPlayer, type Action } from '../game/engine'; +import { createIxianNexusReplacementFixture, settleIxianReplacementFixture } from './fixture-nexus-ixian-replacement'; + +/** Original four-seat setup, qualifying alliance/Nexus draw and normal paid + * purchases. Atreides retains its original starting Karama as a real counter. + * Only undealt deck/Nexus order is selected; no played state is staged. */ +export function createOriginalHarkonnenReplacement(advanced = false) { + const lobby = createGame('HARKREPLACE', newPlayer('a', 'Atreides', 'atreides'), advanced, []); + joinGame(lobby, newPlayer('e', 'Emperor', 'emperor')); + joinGame(lobby, newPlayer('h', 'Harkonnen', 'harkonnen')); + joinGame(lobby, newPlayer('g', 'Guild', 'guild')); + const first = createIxianNexusReplacementFixture({ initial: lobby, face: 'richese', + otherDraw: { player: 'h', face: 'ixians' } }); + let game = settleIxianReplacementFixture(applyAction(first.game, first.buyer, + { type: 'nexusIxianReplacementPass', event: first.event })); + for (let n = 0; game.auction!.active !== 'h' && n < 20; n++) + game = applyAction(game, game.auction!.active, { type: 'passBid' }); + const lot = game.auction!.cards[game.auction!.index]; + const beforeBid = structuredClone(game); + game = applyAction(game, 'h', { type: 'bid', amount: 2 }); + let beforePurchase = structuredClone(game); + let purchaseAction: Action = { type: 'passBid' }, actor = ''; + for (let n = 0; !game.currentAuctionSale && n < 30; n++) { + beforePurchase = structuredClone(game); + actor = game.decision?.player ?? game.auction!.active; + purchaseAction = game.decision?.kind === 'auctionPayment' + ? { type: 'decision', karama: false } : { type: 'passBid' }; + game = applyAction(game, actor, purchaseAction); + } + assert.ok(game.currentAuctionSale); + assert.equal(game.currentAuctionSale.winner, 'h'); + return { game, beforeBid, beforePurchase, purchaseAction, actor, lot, + buyer: 'h', counter: 'a', next: game.deck[0], afterNext: game.deck[1], + provenance: 'Original four-seat setup, native worm/alliance and two natural Nexus draws, actual Atreides first purchase/Keep then paid Harkonnen second lot; no played-state staging.' }; +} diff --git a/tests/harkonnen-ixian-replacement.test.ts b/tests/harkonnen-ixian-replacement.test.ts new file mode 100644 index 00000000..e299168c --- /dev/null +++ b/tests/harkonnen-ixian-replacement.test.ts @@ -0,0 +1,73 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { applyAction, viewGame, type Game } from '../game/engine'; +import { createOriginalHarkonnenReplacement } from './fixture-harkonnen-ixian-replacement'; +import { settleIxianReplacementFixture } from './fixture-nexus-ixian-replacement'; + +const player = (game: Game, id: string) => game.players.find(p => p.id === id)!; +function passIncome(state: Game): Game { + let game = state; + for (let n = 0; game.response?.kind === 'emperorIncome' && n < 10; n++) { + const actor = game.players.find(p => !game.response!.passed.includes(p.id))!; + game = applyAction(game, actor.id, { type: 'passResponse' }); + } + return game; +} + +for (const advanced of [false, true]) { + void test(`${advanced ? 'Advanced' : 'Basic'} Harkonnen replacement precedes one original cancelable purchase bonus`, () => { + for (const replace of [false, true]) for (const cancelBonus of [false, true]) { + const f = createOriginalHarkonnenReplacement(advanced); + const source = f.game.pendingNexusIxianReplacement!; + assert.equal(source.card.id, f.lot.id); + assert.equal(player(f.game, 'h').hand.length, 3, 'No bonus is drawn before the replacement choice'); + assert.ok(viewGame(f.game, 'h').nexusIxianReplacement!.canUse); + assert.equal(f.game.response, null); + let game = applyAction(f.game, 'h', { type: replace ? 'nexusIxianReplacementUse' : 'nexusIxianReplacementPass', event: source.event }); + assert.equal(player(game, 'h').hand.some(c => c.id === f.lot.id), !replace); + assert.equal(player(game, 'h').hand.some(c => c.id === f.next.id), replace); + game = passIncome(game); + assert.equal(game.response?.kind, 'harkonnenBonus'); + assert.equal(player(game, 'h').hand.length, 3); + const bonus = replace ? f.afterNext : f.next; + assert.ok(bonus); + if (cancelBonus) { + const counter = player(game, f.counter).hand.find(c => c.effect === 'karama')!; + game = applyAction(game, f.counter, { type: 'card', card: counter.id, mode: 'cancel' }); + } + game = settleIxianReplacementFixture(game); + assert.equal(player(game, 'h').hand.length, cancelBonus ? 3 : 4); + assert.equal(player(game, 'h').hand.some(c => c.id === bonus.id), !cancelBonus); + assert.equal(player(game, 'h').spice, player(f.beforeBid, 'h').spice - 2); + assert.equal(player(game, 'e').spice, player(f.beforeBid, 'e').spice + 2); + assert.equal(game.nexusCards!.cards!.hands.h, replace ? null : 'ixians'); + assert.equal(game.nexusIxianReplacementHistory!.length, 2); + assert.equal(game.nexusIxianReplacementHistory!.at(-1)!.source.card.id, f.lot.id); + assert.equal(game.pendingNexusIxianReplacement, null); + } + }); +} + +void test('Harkonnen replacement at eight cards cannot manufacture a purchase-bonus slot', () => { + for (const advanced of [false, true]) for (const replace of [false, true]) { + const f = createOriginalHarkonnenReplacement(advanced); + const parent = structuredClone(f.beforePurchase), owner = player(parent, 'h'); + // CONTROLLED capacity boundary: conserve original undealt cards into + // Harkonnen custody without changing the actual auction, money or Nexus. + while (owner.hand.length < 7) { + const index = parent.deck.findIndex(c => c.effect !== 'karama'); assert.ok(index >= 0); + owner.hand.push(parent.deck.splice(index, 1)[0]); + } + let game = applyAction(parent, f.actor, f.purchaseAction); + assert.equal(player(game, 'h').hand.length, 8); + const count = game.deck.length; + const source = game.pendingNexusIxianReplacement!; assert.equal(source.card.id, f.lot.id); + game = settleIxianReplacementFixture(applyAction(game, 'h', + { type: replace ? 'nexusIxianReplacementUse' : 'nexusIxianReplacementPass', event: source.event })); + assert.equal(player(game, 'h').hand.length, 8); + assert.equal(game.deck.length, count - Number(replace)); + assert.equal(player(game, 'h').hand.some(c => c.id === f.lot.id), !replace); + assert.equal(player(game, 'h').spice, owner.spice - 2); + assert.equal(game.nexusIxianReplacementHistory!.length, 2); + } +}); diff --git a/tests/nexus-ixian-replacement-engine.test.ts b/tests/nexus-ixian-replacement-engine.test.ts index 2f636ae1..d97d0358 100644 --- a/tests/nexus-ixian-replacement-engine.test.ts +++ b/tests/nexus-ixian-replacement-engine.test.ts @@ -270,36 +270,6 @@ void test('a later genuine purchase creates a new source while the closed prior inventory(use); }); -void test('a genuine Harkonnen buyer holding Ixian Nexus gets native purchase/bonus only, never an unresolved prototype offer',() => { - const lobby = createGame('HARKBUYER',newPlayer('a','Atreides','atreides'),false,[]); - joinGame(lobby,newPlayer('e','Emperor','emperor')); - joinGame(lobby,newPlayer('h','Harkonnen','harkonnen')); - joinGame(lobby,newPlayer('g','Guild','guild')); - const f = createIxianNexusReplacementFixture({initial:lobby,face:'richese',otherDraw:{player:'h',face:'ixians'}}); - let game = settleIxianReplacementFixture(applyAction(f.game,f.buyer,{type:'nexusIxianReplacementPass',event:f.event})); - assert.equal(game.nexusCards!.cards!.hands.h,'ixians'); - for (let step = 0; game.auction!.active !== 'h' && step < 20; step++) - game = applyAction(game,game.auction!.active,{type:'passBid'}); - const balance = owner(game,'h').spice; - const purchased = game.auction!.cards[game.auction!.index].id; - game = applyAction(game,'h',{type:'bid',amount:2}); - for (let step = 0; game.auction?.bidder === 'h' && !game.currentAuctionSale && - game.auction.index === f.auctionIndex + 1 && step < 30; step++) { - if (game.decision?.kind === 'auctionPayment') - game = applyAction(game,'h',{type:'decision',karama:false}); - else game = applyAction(game,game.auction.active,{type:'passBid'}); - } - assert.equal(game.pendingNexusIxianReplacement,null); - assert.equal(viewGame(game,'a').nexusIxianReplacement,null); - assert.equal(viewGame(game,'h').nexusIxianReplacement,null); - game = settleIxianReplacementFixture(game); - assert.equal(owner(game,'h').spice,balance - 2); - assert.ok(owner(game,'h').hand.some(c => c.id === purchased)); - assert.equal(owner(game,'h').hand.length,4,'The actual native Harkonnen bonus remains active.'); - assert.equal(game.nexusCards!.cards!.hands.h,'ixians'); - assert.equal(game.nexusIxianReplacementHistory!.length,1); - inventory(game); -}); void test('direct printed Karama acquisition preserves the original zero bid and native free continuation',() => { const f = createIxianNexusReplacementFixture();