24 KiB
Richese gift: engine custody and continuation audit
Initial review 2026-09-06, before runtime gift integration; followed below by an implementation checkpoint. The original findings and proposed regressions are retained as historical rationale. This is a bounded review, not certification of all Richese rules. This documentation task changes no engine files.
Authority and boundary
The publisher's CHOAM & Richese rulebook, p6, permits Richese to give an ally a Richese Treachery Card from Richese's hand at any time when the ally's hand is not full. It is a basic alliance ability, separate from special Karama acquisition and the cache auction. It does not create a cache-to-ally transfer, an exchange requirement, payment, a once-per-turn limit, or another faction's gift power. Canonical inventory recognition is already provided by richeseCardDefinition in game/richese-cards.ts.
Canceled gift retry — 27 October 2026
Prototyped / Partial. Under the standing selected-interpretation authority,
the cancellation prevents that one offer, and Richese may offer the retained
card again in the same phase if the current alliance, capacity, ownership and
commitments permit it. Each new declaration receives a fresh response and can
itself be canceled. The Basic gift's any-time wording and the updated physical
Karama's one-use cancellation support this composition; no gift-specific
publisher retry ruling has been found. The old richeseGiftBlocked field stays
as unique per-phase cancellation history for compatible saved state and source
signatures, not a legality limit. The existing private control and legal bot
selection consume the server's updated card quote. Other interactions and full
rules acceptance remain open.
Focused validation covers fresh responses and two distinct printed counters, parent restoration, unique cancellation history, custody and all four minimal profiles. A controlled staged Basic fixture additionally drives five engine actions and three actual server-rendered gift-control callbacks through two cancellations and eventual transfer. It does not establish native prior chronology, browser hydration, mobile or deployed behavior.
Source: GF9 CHOAM & Richese rules, p6, verified in the official indexed PDF and the publisher-text extraction /tmp/dune-rules/choam-lelekan-mirror.txt. The GF9 FAQ, p1, expressly includes alliance abilities under Karama cancellation. Its older phase-duration wording must not override the updated physical Karama card's one-use wording. The R2 p12 Richese cancellation table lists auction, No-Field and Black Market, but does not expressly exempt the gift. Applying the general alliance-cancellation rule to gift is supported composition; no retrieved gift-specific FAQ defines the exact nested-response sequence.
The isolated game/richese-gift.ts validates mutual current alliance, canonical physical hand ownership and supplied current capacity. It returns a normalized identity-bound intent and cloned replacement hands. It deliberately does not validate game timing, battle promises, or reserved cards. Those are authoritative engine responsibilities.
Initial findings for the adapter (historical)
reconcileBattlePromisespresently attributes forbidden voluntary promise-breaking only tocard,bribeandpledgeAidactions. A new gift action will otherwise release the donor's binding answer. Validate the prospective transfer with donor attribution before declaration and again before allowed settlement. Settlement may run under another player's pass or under automatic normalization, so attributing it to the current caller is incorrect. Receiving an unsolicited gift is not the recipient's voluntary promise-breaking action.settleAuctionpushes the ordinary winning card without a fresh hand-capacity check. A gift must not consume the last slot already needed by an unsettled ordinary winning commitment. The Richese-lot equivalent already exists in outerapplyAction, but that outer check is insufficient for effects settled bynormalizeAutomaticGame. Use the same effective winning-commitment check before gift transfer at both declaration and execution.finishResponse'sharkonnenBonusbranch draws after an earlier capacity check inauctionBonus. A gift can fill Harkonnen's eighth slot while that bonus response is paused. Recheck live capacity when granting the bonus and proceed without drawing if full. This preserves the valid gift instead of declaring all pending bonus windows unavailable. The existing branch otherwise permits a ninth card.- A response snapshot must actually suspend
phaseOpening. Leaving the live opening object active causes both dispatcher precedence and automatic-drain logic to hide/block the gift response. Restore its original passes andinitializeflag after cancellation or transfer; do not initialize the next phase during the gift.
These are concrete call-path hazards for the new adapter, not claims that an unimplemented gift action was already exploitable through the public API.
Exact donor reservations
| Existing state | Narrow check | Why |
|---|---|---|
battle.plans[donor] |
Exclude exact weapon, defense, and card-valued leader IDs |
A sealed plan and full-plan inspection must retain their physical cards. Other hand cards remain candidates. |
battle.prescience |
Exclude the fixed card identity when the donor is the inspected combatant | Preserve the promised element without freezing unrelated hand cards. |
moritaniRetention |
Use retentionReservesCard for every reserved played card |
Winner/loser cleanup has not yet disposed of them. |
truthtrance.queue |
Exclude exact queued card IDs | Queued cards remain in hand until their questions finish. Current canonical Richese cards do not include Truthtrance; keep the rule in the shared reservation predicate for future integration. |
Unsold richeseAuction Black Market lot |
Exclude cardId when donor is seller and !currentAuctionSale |
The physical auction card still resides in the seller's hand. richeseLotCard and outer applyAction already enforce this custody. A sold auction's retained bookkeeping reference is not another physical copy. |
Accepted ixAllyCard replacement |
Exclude pendingIxAlly.card while the replacement response is pending, including a suspended parent response |
Prevent undoing the accepted discard/replacement. Before acceptance, the buyer may still choose to keep; losing the card makes replacement unavailable but does not make the whole optional decision impossible. |
| New pending gift | Exclude its exact owner/card identity from every further voluntary spend, gift or exchange | The pending gift keeps the card in the donor's hand until its response settles. It is not a second hand/cache copy. |
ambassadorDiscardBlock already supplies the first four relevant physical checks. It does not cover Black Market or a new gift. karamaSpendingBlock covers battle, prescience and retention, but likewise needs the pending-gift exclusion. The generic discard function currently only checks hand ownership.
Do not turn every selected-but-unaccepted card proposal into an irreversible reservation. In particular, choamMarket.trade remains a two-way proposal until confirmation. The current confirmation code already verifies both exact cards and mutual alliance, and gracefully returns to the market when they are unavailable. Gifting away the proposed Richese return card may therefore invalidate that proposal without duplicating it or locking the game. An unrelated gift need not block the entire CHOAM market. If a broader negotiated-deal rule is added later, distinguish it from physical custody.
Removal and transfer paths
| Path in current engine | Integration action |
|---|---|
discard (ordinary cards, Karama, CHOAM cash-in, combat cleanup, Ambassador discard, Truthtrance completion) |
Add a pending-gift ownership guard as a backstop, and expose the same exclusion through legal-card helpers so the client is not offered a doomed action. Clear/resolve the gift record before its own transfer; transfer is not a discard. |
executeSpecialKaramaIntent, Harkonnen branch |
It directly shuffles and filters the target hand. Reject an extraction targeting the pending gift's owner before RNG while the gift is reserved. Do not silently exclude the card from the random pool: that changes the Harkonnen power's distribution. Other targets remain valid. |
settleRicheseLot |
Its Black Market hand removal is outside discard. Prevent declaring the offered lot card as a gift. Revalidate the pending gift owner still holds it, and preserve existing lot custody checks. |
finishResponse(ixTechnology) |
Direct hand swap, outside discard. The native actor is Ixians, so a Richese-owned pending gift cannot be selected by this path in a valid game. A generic reservation helper may still protect it without faction-specific assumptions. |
choamTradeConfirm |
Direct mutual hand replacement. Its original decision is suspended during a nested gift; after restoration it rechecks actual cards and alliance. Missing proposals fail gracefully. |
handExchange return |
Direct filters/pushes. Keep the exact return count and final capacities valid. A transferred gift may later be used in a genuinely subsequent exchange; do not keep its reservation after settlement. |
| Moritani Sabotage optional gift | Direct hand transfer, but native donor is Moritani and its decision is suspended while a Richese gift resolves. It cannot remove a Richese-owned reserved gift in a valid state. Recipient capacity is already checked. |
| New acquisition, bonus, replacement, Robbery draw | Additions do not remove the reserved gift, but can change recipient capacity or an unsealed plan's feasible choices. Recheck before gift commit. Do not duplicate stored card objects between physical zones. |
An additional post-action invariant that the unresolved gift's canonical card is still held exactly once by its original owner is a useful safety net. It does not replace pre-RNG checks. Do not blindly count sold auction.cards references as physical duplicates: those arrays retain sale bookkeeping while the sold card is already in a hand.
Recipient growth and restored decisions
A sealed battle.plans[recipient], fullPlanInsight, or fixed prescience answer must remain byte-for-byte unchanged. The new card is not silently inserted into that plan and does not reopen planning. Unsealed plans may use newly available resources subject to existing Voice/Truthtrance constraints.
Preserve one slot for an actual unsettled ordinary winning bid and the existing Richese winning commitments (including every positive submitted Silent commitment under current engine policy). Do not reserve a slot merely because the recipient is eligible to bid or has not decided whether to bid. A transferred card may legitimately make a previously uncommitted bidder full; existing automatic full-hand handling should then advance that bidder normally.
CHOAM trade is one-for-one and thus does not overfill a hand that was legal after the gift. A pending CHOAM sale can also remain pending. An already accepted Ecaz Ix discard must still have an available legal card after an interrupting donor gift; the outer applyAction check for the beneficiary currently covers voluntary direct actions, but automatic gift settlement must perform the same prospective check with the donor's identity. A copy choice may remain valid through another available effect; do not blanket-block it.
Nested continuation and privacy
Store the private normalized gift intent and its event plus exact parent response, decision, pendingKarama, and phaseOpening; bind the declaration to current turn/phase and owner/recipient. Clear those live overlays only while the gift response owns control. Reject another simultaneous gift so a singleton pending record cannot be overwritten. A cancellation consumes Karama but leaves both hands unchanged; a successful allowance transfers exactly once, clears the gift reservation, restores the parent, then drains eligible automatic continuations.
A newly received Karama can cancel an original response that is still genuinely open after restoration. Preserve original explicit passes and existing late-cancellation rights. Do not recreate a response that had already resolved. BG Worthless conversion must save and restore the gift response rather than overwrite its parent snapshot. Any supported Fremen summoned-worm/Nexus interruption may change alliances; revalidate and gracefully abort an obsolete gift before restoring the original continuation rather than stranding its response.
The active Truthtrance question/priority overlay currently requires its explicit resolution. This review does not establish a publisher ruling authorizing hand changes between a Truthtrance question and answer. Preserve that specific priority boundary and distinguish it from a blanket prohibition on gifts during ordinary decisions, responses or phase openings.
Public response and logs should identify the donor and gift action, not the chosen card ID/name. Only use authenticated own/authorized ally projections for actionable identity. Do not expose reserved-card lists or opponents' possible cancel cards. After completion, the recipient's own hand is authoritative; prior inspection records remain snapshots, not live access to the new hand.
Originally suggested focused integration regressions
- Declared canonical Karama cannot be spent again or randomly extracted from its donor while gift cancellation is pending; rejected Harkonnen attempt draws no RNG.
- Gift cancellation and automatic allowance each restore exact nested response, BG conversion and phase-opening state once after JSON round-trip.
- Gift cannot remove a sealed/prescience/retention/Black Market card or voluntarily invalidate its donor's Truthtrance promise.
- Gift to an ordinary winning bidder preserves its auction slot; gift during Harkonnen bonus never creates a ninth card.
- Gift during an optional CHOAM trade may invalidate its selected return card gracefully; a different gift does not prevent completing a valid one-for-one exchange.
- Changed alliance or recipient capacity while paused causes no partial transfer or permanently blocked continuation.
- Recipient full-plan insight and submitted plan remain unchanged; unrelated viewers never receive the gift identity before transfer.
Integrated development checkpoint
Implementation — Partial. richeseGift now declares one canonical card physically held by Richese for its current mutual ally. prepareRicheseGift/transferRicheseGift supply exact custody checks; validateRicheseGift adds authoritative capacity, auction-slot and physical commitments, pending exchange feasibility and prospective donor-attributed battle promises. Both declaration and settlement validate current conditions. This is a Basic alliance ability, distinct from purchasing from the cache and from activating any card effect. No spice payment, random draw, Harkonnen bonus or Ixian purchase replacement is created by the gift.
pendingRicheseGift holds the exact event/intent, turn/phase and interrupted response, decision, pending Karama and phase-opening state. Ordinary decisions/responses/openings can be suspended. Active Truthtrance remains a specific priority boundary. The response is richeseGift; cancellation leaves both hands unchanged, while allowance transfers once. Either path restores the parent and resumes automatic continuation. An obsolete intent aborts safely rather than partially changing hands. A newly gifted Karama can affect an original response still open after restoration; original passes and completed decisions are not invented again.
The exact pending card is reserved against further voluntary spending/removal. The Harkonnen random-hand path rejects extraction of the reserved donor hand before RNG, rather than secretly changing its distribution. Sealed plans, fixed prescience, played-card retention, Black Market stock and accepted replacement cards remain reserved. Gifts preserve winning purchase capacity and committed discard/exchange feasibility. Harkonnen bonus resolution now rechecks capacity instead of silently granting a ninth card. Completed recipient plans and historical private inspections remain unchanged. These changes address the initial hazards above without replacing the underlying source with a blanket ban on gifts during other decisions.
Historical cancellation guard, superseded above. The initial engine blocked the canceled physical card for the current turn/phase pending a repeat-use interpretation. This was an implementation guard, not a verified printed restriction.
Player controls — Partial. components/richese-gift.tsx, integrated into the private-hand console, gives the owner eligible-card selection, readable CardRules, full CardInspector, unavailable-card explanations and a single explicit Give button. It adds no confirmation modal or invented recipient-acceptance action. Pending identity is visible only to the two allies; the normal response panel resolves cancellation/allowance. Inspection changes no game state. Busy/blocked state prevents another declaration. Browser acceptance remains pending at this checkpoint.
AI — Partial. game/bots.ts gives only strict canonical Richese Karama when Bidding exposes a lower ally hand count and Richese needs space in a full own hand. It uses only owner legal choices and public counts, never ally contents; no outside-Bidding card need is inferred from absent counts. All four profiles preserve response/decision/Truthtrance priority. The former no-retry behavior is superseded above. This policy does not imply full gift strategy or support for every card interaction.
Documentation — Partial. The in-app richese-gift topic records five separate facets and links to the collection, acquisition, alliances, Karama and Truthtrance topics. The acquisition topic now points to the integrated gift control while retaining the unfinished Distrans/Nullentropy search boundary. Primary-source and historical audit statements above remain distinguishable from later implementation choices. Full Richese start gates remain closed.
Verification — Partial. Traceable suites are tests/richese-gift.test.ts (pure physical model), tests/richese-gift-review.test.ts (engine authority/privacy/continuation), tests/richese-gift-bots.test.ts (four-profile bounded policy and actual transfer/cancellation), and tests/richese-gift-recovery.test.ts (persisted/API recovery). Static-render checks exercised owner selection/inspection, disabled blocked actions, pending recipient identity and outsider absence. Final integrated test totals, gift browser success and full Richese games are deliberately not claimed here. Parent verification remains in progress.
Integrated verification checkpoint
The completed registered suites pass 1,230/1,230 rules/client and 93/93 persisted/API scenarios. TypeScript, lint and production build pass. Gift-specific checks comprise six pure custody, five engine matrix/privacy/recovery cases, eight independent review, four all-profile AI and three production SQLite/API recovery cases. The five engine cases include all ten canonical identities in both Basic and Advanced across all nine phases. The independent stale-alliance regression caught an actual hand overwrite; exact original intent validation now aborts safely before either hand assignment.
Desktop browser QA in synthetic QPNV5XJJ completed an uncontested transfer, reopened a separate contested gift through a fresh invitation tab, and displayed the recipient’s private pending card without adding it to their hand or asking for redundant acceptance. Synthetic role swapping was used only to inspect the recipient interface, not to claim real seat-switch recovery. At that checkpoint, mobile, full Richese games and same-card cancellation retry were unverified; the later provisional retry evidence is recorded above. Full logs and saved fixture versions are recorded in IMPLEMENTATION_STATUS.md; no expansion gate was removed.
The final follow-up review also covers the mandatory Harkonnen hand-exchange return: an incoming gift may not consume its reserved target slots. A real take/gift/return fixture checks both blocked-full and permitted-spare-capacity cases, completing after JSON reload with conserved cards. Six additional registered pure Distrans tests contribute to the current full-suite total but do not activate that next card effect.