Files
Dune/docs/PORTABLE_SNOOPER_ENGINE_AUDIT.md
T

20 KiB
Raw Blame History

Portable Snooper integration audit

Advanced Voice names for ordinary cards — 28 October 2026

Prototyped / Partial. The user-authorized revised source, physical p. 28 (printed p. 26), permits Voice to name a specific card or type. Its parenthetic examples and the separate publisher FAQ do not independently settle naming every ordinary face. The Advanced preview provisionally permits exact named ordinary projectile, poison, Lasgun, shield, snooper, Worthless and Cheap Hero/Heroine cards. Basic retains its prior category choices. Identical physical copies share the printed name; another card in the same category does not match. Named hero prohibitions account for any other available hero before allowing a leaderless plan. Special-card categories retain their previous handling; named playable Specials, wider combinations and final acceptance remain open.

Native and borrowed Voice, the saved response, plan matcher, structured power controls and the existing four legal bot policies share the same command. Real Advanced battle actions use a documented conserved-card/alliance supplement; the Basic gate uses a controlled mode change at the Voice boundary. Twelve related files pass 168 cases. No human browser or deployed battle has been verified for this slice.

Named Voice choice — 27 October 2026

Prototyped / Partial. Voice can name the canonical Portable Snooper separately from the broader poison-defense category. A specific requirement is satisfied by the actual card in the original defense plan when the player can comply; a prohibition blocks it in both the original plan and its printed late-defense window. Ordinary Snooper remains distinct. Generic poison-defense Voice continues to include both cards. Existing native and borrowed Voice controls use the shared choice and matcher; saved earlier commands retain their meaning.

The user-authorized revised rulebook physical p. 28 / printed p. 26 permits Voice to specify a card or card type. The physical Portable face subjects its late play to Voice as recorded below. The runtime cases use real battle actions with a documented conserved-card and alliance supplement; control cases use a controlled projection. The later Advanced ordinary-name prototype above supersedes the earlier absence; wider combinations, browser and deployed acceptance remain open.

Audit: 2026-09-06. Scope: the canonical card's ordinary defense role, late play and existing battle continuations. The source contract below informed the bounded integration recorded at the end. The current-hook list is historical; the final checkpoint records completed implementation. Full expansion certification remains unverified.

Primary evidence

The photographed publisher face is Defense – Poison and protects the leader against poison. Its extra permission is to play after revealing the Battle Plan when no defense was played, subject to Bene Gesserit Voice. It separately prohibits play after a plan containing both a weapon and a Worthless card. Its final instruction is the usual discard-after-use wording. The actual face was visually read at /tmp/dune-rules/choam-snooper-reading.png, enlarged from the physical component photograph linked by the gallery. The local photograph and its hash are recorded in RICHESE_ACQUISITION_RULES.md; uploader printing/date is unverified. The face, not retailer commentary, is the evidence.

The GF9 CHOAM/Richese FAQ, p10, explicitly distinguishes Portable Snooper from Snooper for CHOAM duplicate exchange. The November 2020 FAQ, pp1 and8, gives the general winner-retention rule, specific Artillery/used-Tooth disposal exceptions, and Chemistry-only protection against Poison Tooth. Publisher-indexed text was freshly retrieved for these statements. Bounded official searches found no Portable-specific answer overriding retention or placing its declaration relative to traitor/Tooth decisions.

The base rules, pp10–11,18 and23 supply ordinary leader/hero and battle-card limits, simultaneous public plan reveal, traitor resolution, Voice constraints and unchanged inspected plan elements. These general rules compose with the extra timing permission; they do not convert the card into a late-only Special action.

Confirmed composition and practical contract

Ordinary plan use is supported. Its printed category is a poison defense; the late permission is an additional option, not a prohibition on selecting it as the original defense. Support both Basic and Advanced battles without changing the physical card identity. The current inventory intentionally stores it as kind:'special'; use an exact-canonical role adapter rather than rewriting persisted cards into ordinary Snooper IDs/kinds. An unverified forged effect:'portableSnooper' is insufficient authority.

Late use adds protection without editing the revealed plan. Begin only after the public battle reveal, not the private advanced Atreides inspection of one sealed plan. Keep the original weapon, defense, leader, dial and support immutable. Record the additional played card separately. A real defense in the original plan blocks this extra defense; a lone Worthless card is not actual protection and the separate weapon-plus-Worthless ban supports adding Portable alongside it. This includes the engine's lone Worthless stored in its defense field. Respect the ordinary two-card weapon/defense limit: two existing Worthless cards leave no third card space. A leader or Cheap Hero must still have been played.

Voice still applies. A poison-defense prohibition applies to this late defense. A poison-weapon prohibition does not forbid a defense merely because both involve poison. Ordinary Voice compulsion must already have been met in the sealed plan when possible: a holder cannot omit its only compulsory poison defense and promise to add Portable later. When compulsion was impossible, or required another category, it does not independently prohibit an otherwise legal late defense. A compelled lone Worthless can coexist with Portable subject to the card limits. Reuse the actual Voice match functions, including canonical poison-defense role recognition, rather than checking card names or only kind:'snooper'.

Damage and disposal stay under ordinary battle precedence. Portable stops an ordinary poison weapon or Chemistry used as a poison weapon. It only supplies the poison half of defense against Poison Blade; the projectile half still kills an otherwise unprotected leader. It is not a shield, does not create or stop a Lasgun–shield explosion, does not stop Lasgun or Artillery, and does not stop Poison Tooth. A successful traitor declaration still decides the battle before ordinary weapon effects. Existing Tooth activation remains a genuine choice; Portable does not override its exception. Winning players may retain a used Portable under the general FAQ rule, including when played late. A losing player discards it, except an eligible Moritani allied-retention choice. Normal mutual-loss/explosion cleanup discards it. No retrieved specific exception makes this card mandatory-discard for a winner. Do not mistake the generic face disposal sentence for the special Artillery/Tooth erratum.

These are general-rule compositions with explicit provenance, not invented Portable-specific FAQs. Existing unsupported Mirror Weapon/Stone Burner or other future card combinations remain outside this slice until their own effects exist; they do not prevent ordinary poison-defense support.

Opportunity and source timing boundary

The text grants late use but does not order that declaration before/after traitor calls or Poison Tooth activation. Preserve current battle-effect precedence regardless of the input sequence. A new decision appearing only when somebody holds Portable would disclose possession. Automatically passing only nonholders produces a timing oracle as well.

The accepted minimal application contract reuses each combatant's existing genuine traitor-declaration opportunity. Before submitting their own traitorCall, they may use the separate Portable action. Both combatants must already submit their traitor decisions before battle resolution, so an opponent's fast request cannot eliminate this opportunity. No additional acknowledgment or holder-only public stage is needed. After the actor's own traitor declaration, reject late use as a stale relinquished opportunity. The private control should make that consequence clear. This is an explicit asynchronous scheduling convention, not an assertion that the publisher mandates Portable-before-own-traitor-call. Current Tooth choices finish before traitor declarations; their decision must continue to block unrelated mutations. Keep genuine Harkonnen allied traitor/Karama response continuations intact.

Do not automatically play Portable just because it would save a leader, and do not require a redundant global ready stage. Declaring or withholding a held defense is strategic. Opponents see no possession/eligibility metadata; the card and its public extra protection appear only after actual play. A private blocker may explain the holder's own hand/plan/Voice conditions. A public outcome already decided by a traitor does not make Portable a shield or change precedence; do not use that fact to reclassify its card identity or trigger unrelated effects.

Current engine work required

  1. Role everywhere: add exact isPortableSnooper and shared isDefenseCard handling in battle-cards.ts; defenseTypes gives poison-defense semantics while isShield remains false. Update validBattleCardPair, plan legality, all defense search/filter loops, Voice availability, Prescience feasibility, bots and the battle editor. A normal Portable defense also satisfies Chemistry's need for another defense when Chemistry is a weapon. Keep physical canonical matching for CHOAM unchanged.
  2. Truth and inspection: battle-promises.ts::parsePlanClaim currently whitelists only treacheryDeck(['ix']), and truthtrance.ts::TRUTH_CARD_NAMES has the same limitation. Add the supported canonical card name to battle-plan and hand-fact choices, including actual editor options. An ordinary Portable defense must be reachable when answering a structured promise. Existing structured claims describe the original slot and therefore remain evaluated against the immutable sealed plan; a promised empty original defense can coexist with this explicit later card permission. A genuinely broader freeform promise about never playing a defense remains broader than that slot claim and must not be silently advertised as machine-enforced by this model.
  3. Separate state and event: use Battle.lateDefense?: Record<playerId,cardId> and the current battle event. Do not replace plans[player].defense, especially a Worthless card or an inspected null. Effective combat defense can consult the overlay; original plan display/Prescience/full-plan insight must distinguish original commitment from the later public card.
  4. Commitment custody: keep the late card in its owner's hand as an explicitly played/reserved battle card, matching existing plan-card custody. Extend transferCardBlock, direct discard/exchange/cash-in reservations and physical played-card enumeration to include it. Both outgoing-card checks in Distrans, paused Richese gifts and saved accepted Ix replacements must reject competing use. Central paid-Box and Truthtrance locks retain priority; late play cannot overwrite a pending decision/response. No new randomness, fee, bank payment or faction-income window is needed.
  5. Resolution and cleanup: resolveBattle currently fetches defense only from each plan. Supply the effective defense to ordinary effects, and add late card IDs to the used set. The same set must feed losing-player disposal, winner cleanup and moritaniRetention.played/eligible; otherwise the card can escape disposal or incorrectly disappear before the retention decision. Deduplicate physical IDs. Keep Poison Tooth, explosion, leader death, bounty, casualties, capture, Face Dance and technology continuations in their existing order.
  6. Recovery and bot behavior: validate actor, exact card, battle event, both-plan reveal, own traitor-declaration status and current custody on every request. Old saved battles without an event cannot accept a forged new event; an explicit compatibility policy may keep them unsupported without rewriting sealed state. Bots should consider their private Portable option before their own traitor declaration and honor source restrictions. Private preview must not mutate, resolve damage or draw RNG.

Accepted action/projection shape

{ type: 'portableSnooper', card: canonicalPortableId, event: battleEvent }
Battle.lateDefense?: Record<string, string>;

// Held-card option: owner only. Public battle gets lateDefense after actual use.
portableSnooper: null | { card: Card; event: string | null; blocked: string | null };

On valid use, reserve the held physical card, record the public late defense and log the declaration once. Do not rewrite the sealed plan, auto-call a traitor, award income, discard immediately or resolve the battle prematurely. Normal winner/loser cleanup decides its final physical destination. The engine's immutable action clone and room CAS reject duplicate/stale requests without another record or disposal.

Focused acceptance tests

  • Normal defense-slot use against poison, Chemistry and Poison Blade; shield/Lasgun/Tooth/Artillery exceptions; leader versus hero and no-leader rejection; exact canonical identity and Chemistry alternate-role pairing.
  • Late empty slot and lone Worthless in either stored slot; weapon-plus-Worthless, two Worthless and existing genuine defense rejection; original plan remains byte-for-byte unchanged.
  • Actual Voice compel/prohibit/default-role behavior; original null or Worthless Prescience answer preserved; real structured Portable-name Truth question and reachable plan; no promise silently reinterpreted as a changed original slot.
  • Fast opponent traitor request cannot resolve before the holder acts; own completed declaration closes the option; active Tooth/Truth/Harkonnen response preserves ownership and resumes normally; no public hand-possession oracle.
  • Ordinary winner retain/discard, loser disposal, successful/double traitor, explosion, Moritani ally retention, advanced casualties and subsequent capture/Face Dance/technology all account for one late physical card exactly once.
  • JSON/restart, public revealed-late versus private unused card, authenticated concurrent late use and traitor submission, stale event/duplicate action, no random projection work, and four AI levels.

No additional user question was sent. The declaration scheduling convention and any broader freeform promise scope are recorded honestly; the ordinary role, Voice checks, damage, separate commitment state and standard retention are concrete implementation work.

Portable Snooper runtime and Richese portrait checkpoint (2026-09-06)

  • Portable Snooper now supports its ordinary poison-defense slot and optional play after both plans reveal, for all twelve holders in Basic and Advanced development fixtures. Exact canonical role predicates preserve saved kind: special identities without admitting unrelated cards. Normal plan searches, Voice, Prescience, structured Truthtrance names, player selectors and AI use that same role. It stops ordinary poison and weapon Chemistry, but not Tooth, Artillery, Lasgun or the projectile half of Poison Blade.
  • Late play uses the spare battle-card slot and preserves the complete original plan. A lone Worthless in either slot is allowed; a real defense, weapon-plus-Worthless or two occupied slots blocks it. Voice compulsion must be met originally when possible, and forbidden poison defense remains forbidden late. The existing own traitor-declaration opportunity is the explicit application deadline, independent of hand contents; no extra holder-only public window or acknowledgement was added.
  • Battle.lateDefense records the public played card separately, with exact battle-event and physical-custody validation. The card stays reserved in its owner's hand through remaining decisions and appears in the public component inspector only after play. The effective defense feeds combat, while the original plan remains the source for inspected and structured promised slots. Winner, loser and Moritani retention cleanup all account for that one physical card. Current decisions, Truthtrance and responses retain priority; duplicate and stale requests cannot replay protection or cleanup.
  • Source review corrected an earlier retention assumption: the official November 2020 FAQ permits winning battle-card retention even without a keep footer, while explicitly naming Tooth/Artillery exceptions. Portable therefore uses ordinary winner retention. Mirror's ordinary retention follows the same source; inheritance of a copied Tooth/Artillery discard exception remains a separately pending user ruling. PORTABLE_SNOOPER_ENGINE_AUDIT.md and MIRROR_WEAPON_ENGINE_AUDIT.md record provenance and boundaries. Mirror itself remains inactive.
  • 1,352/1,352 rules/client/component tests and 110/110 persisted/API tests pass, plus TypeScript, lint and production build. This adds five pure, five root engine, seven independent review and six all-profile AI cases, plus three persisted recovery cases. The recovery race forces both first-commit orderings between late defense and own traitor declaration. Logs: /tmp/dune-portable-final-full.log (35.29 seconds), /tmp/dune-portable-final-multiplayer.log (17.89 seconds), /tmp/dune-portable-final-type.log, /tmp/dune-portable-final-lint.log, /tmp/dune-portable-final-build.log.
  • Browser QA verified the ordinary defense selector, sealed an empty original defense, played Portable against revealed Chaumas, and inspected the separate public late card. A fresh invitation preserved the exact pending v46 state and event before resolution. Feyd-Rautha survived, Harkonnen won 8–6, and the normal keep/discard choice retained Portable. Final v51 has three forces at Pasty Mesa, eight spice, Maula Pistol plus exactly one Portable, no Portable in discard, no battle or decision, and a living Feyd-Rautha used in that territory. The enlarged guide was visually inspected. Snapshots: /tmp/dune-portable-browser-pending-v46.json and /tmp/dune-portable-browser-complete-v51.json. All other 1,174 saved room hashes still match the last maintenance backup. This is a focused battle/reconnect check, not complete expansion-game acceptance.
  • Final integrated review fixed a late-defense AI omission for Cheap Hero accompanied by the Kwisatz Haderach. All four profiles now preserve the +2 when protection changes defeat into victory; an ordinary zero-strength hero still does not consume the card. The real Advanced Atreides regression follows actual power preparation, legal spice support and both traitor decisions. Portable’s enlarged inspector now opens its specific timing/interaction guide, and its summary explicitly offers both normal and late use. The final 18 guide/reference tests also pass.
  • Five original Richese portraits bring ordinary coverage to 50/60, completing both factions in each of the first two expansions as well as the six base factions. All forty-five prior files and manifest entries are unchanged. Full-resolution inspection, four existing asset tests and actual 192px inspector / 80px circular browser review pass. The exact temporary review server was stopped. See RICHESE_PORTRAITS.md. Ten Ecaz/Moritani ordinary portraits and separate special-disc artwork remain unfinished.
  • Four Richese effects remain inactive: Semuta Drug, Stone Burner, Mirror Weapon and Juice of Sapho. Complete faction/module interactions, mobile acceptance and full Advanced/expansion games remain unfinished. No start gate was removed. The live development server remains the verified session started during the preceding maintenance; the removed automation stays removed and the full goal stays active.