From a58b8bc92cb05e5e1a7f3fc1260cb725515673a4 Mon Sep 17 00:00:00 2001 From: Quintine <15965533+Quintine@users.noreply.github.com> Date: Fri, 16 Oct 2026 08:56:01 +1100 Subject: [PATCH] Compose concealed No-Field battle cards before reserve costs --- components/game-table.tsx | 14 +- components/harass-withdraw.tsx | 9 + docs/CURRENT_STATUS.md | 29 ++- docs/ECAZ_OCCUPY_RULES.md | 9 + docs/HARASS_WITHDRAW_RUNTIME.md | 47 ++++- docs/IMPLEMENTATION_STATUS.md | 31 +++ docs/REINFORCEMENTS_RUNTIME.md | 22 ++- docs/RICHESE_NO_FIELD_RULES.md | 26 ++- docs/RULE_DECISIONS.md | 3 +- docs/STONE_BURNER_ENGINE_AUDIT.md | 4 +- game/card-presentation.ts | 4 +- game/engine.ts | 46 +++-- game/harass-withdraw.ts | 2 + game/reference.ts | 33 ++-- tests/fixture-harass-no-field.ts | 241 +++++++++++++++++++++++ tests/fixture-no-field-reinforcements.ts | 177 +++++++++++++++++ tests/harass-no-field.test.ts | 204 +++++++++++++++++++ tests/no-field-reinforcements.test.ts | 208 +++++++++++++++++++ 18 files changed, 1064 insertions(+), 45 deletions(-) create mode 100644 tests/fixture-harass-no-field.ts create mode 100644 tests/fixture-no-field-reinforcements.ts create mode 100644 tests/harass-no-field.test.ts create mode 100644 tests/no-field-reinforcements.test.ts diff --git a/components/game-table.tsx b/components/game-table.tsx index 07042ef5..029620fd 100644 --- a/components/game-table.tsx +++ b/components/game-table.tsx @@ -5354,12 +5354,20 @@ export function GameTable({
Reinforcements may occupy either card slot but is neither a weapon nor a defense. It adds 2 to battle strength without changing the physical dial or spice support. - The cost is three reserve forces ({g.battle.reinforcements.normal} normal,{' '} - {g.battle.reinforcements.elite} elite) sent to the Tanks on any revealed outcome; - the card is discarded even after a traitor call or explosion. + The cost is three reserve forces sent to the Tanks on any revealed outcome. + Available for that quote: {g.battle.reinforcements.normal} normal,{' '} + {g.battle.reinforcements.elite} elite. + The card is discarded even after a traitor call or explosion. {g.battle.reinforcements.blocked && ` ${g.battle.reinforcements.blocked}`}
)} + {g.battle.reinforcements?.noField &&+ Provisional native order: your No-Field first materializes{' '} + {g.battle.reinforcements.noField.materializes} forces, leaving{' '} + {g.battle.reinforcements.noField.reservesAfterReveal} own reserves. + Three must remain after that reveal for Reinforcements; + a later Harass return cannot fund or change its original cost. +
} {reinforcementsSelected && (selectedWeaponId === 'ecaz-harass-withdraw' || selectedDefenseId === 'ecaz-harass-withdraw') && (diff --git a/components/harass-withdraw.tsx b/components/harass-withdraw.tsx index b35da52b..9bb271a9 100644 --- a/components/harass-withdraw.tsx +++ b/components/harass-withdraw.tsx @@ -51,6 +51,15 @@ export function HarassWithdrawGuide({ className="notice space-y-2" >
+ Provisional materialization-first timing: your No-Field stays concealed + until both plans are sealed. Its native + reveal materializes {preview.noField.materializes} ordinary forces at + the marker’s sector, leaving {preview.noField.reservesAfterReveal} own + reserves before card effects. Harass returns only actual undialed + counters after that reveal; it never returns a concealed marker or + resets its used-token history. +
} {preview.occupy &&
Occupy returns only the card user’s own undialed fighters, not their
ally’s army. {preview.occupy.cardUser === preview.occupy.ecaz && !preview.occupy.canceled
diff --git a/docs/CURRENT_STATUS.md b/docs/CURRENT_STATUS.md
index 3b36b3d3..9e4b013f 100644
--- a/docs/CURRENT_STATUS.md
+++ b/docs/CURRENT_STATUS.md
@@ -29,6 +29,25 @@ optional skill bands, combinations and public expansion starts remain open.
Rules implementation continues; comprehensive assurance and administration
remain deferred until all rules are implemented.
+**Concealed No-Field battle cards — 16 October.** Own Harass privately forecasts
+native reserve-limited ordinary counters at their actual sector, without moving
+them before both plans seal. Original reveal/history precedes card effects.
+Reinforcements requires three own reserves after projected materialization,
+before future Harass returns; token3/reserves3 now rejects the first fee plan
+instead of sealing a battle that stalls at Traitor resolution. Harass-only stays
+legal. Owner-only optional previews add no saved Game field or new mode gate.
+This existing-order composition is explicitly provisional, not a new FAQ.
+
+New genuine-shipment tests34/34; related17-file210/210, types/lint and build pass
+with existing warnings. Sixteen fresh programs/all four policies accept48
+continuation actions. Four actual390px journeys cover mixed-sector full return,
+zero marker, blocked fee/legal fallback and funded native Occupy. Co-side cases
+finish original CHOAM market into phase7 with original fixed/variable losses;
+no overflow/browser/application error. Three guide topics import with all15
+facets Partial; their25/16/28 evidence paths and three new anchors resolve.
+Owned QA hosts/tabs/scaffolds removed; no human game/D1/deployment changed.
+[Exact order, boundaries and scoped proof](HARASS_WITHDRAW_RUNTIME.md#own-no-field-composition--16-october-2026).
+
**Selected Richese weapon/card interaction wave — 16 October.**
- Clean selected CHOAM/Ix profiles now admit Stone/Mirror with native
Ixians/Tleilaxu in Basic/Advanced; Ecaz/Moritani stays Advanced-only.
@@ -45,8 +64,8 @@ Earlier questions remain unanswered; no new publisher ruling, state field,
public start or played-game conversion is implied. New Ix Tech/Stronghold,
other module bands and winner-changing allocation boundaries remain guarded.
-Latest affected thirteen-file99/99, TypeScript, lint and build pass. Eight fresh
-Node native programs/all four policies accept42 continuation actions; two
+The preceding clean-Ix affected thirteen-file99/99, TypeScript, lint and build pass.
+Eight fresh Node native programs/all four policies accept42 continuation actions; two
original47/10-cache programs also complete without independent Ecaz cards.
Two real390px control journeys prove Advanced copied Tooth activation versus
unused original (both deaths, actual cyborg losses, physical Mirror kept,
@@ -61,9 +80,9 @@ resolve; all ten facets stay Partial. Exact policies/earlier bounded proof:
[Harass/Stone](HARASS_WITHDRAW_RUNTIME.md#provisional-stone-burner-timing--16-october-2026),
[Harass/Reinforcements](REINFORCEMENTS_RUNTIME.md#combined-utility-plan--16-october-2026),
[Reinforcements/Stone](REINFORCEMENTS_RUNTIME.md#native-stone-composition--16-october-2026).
-Own concealed No-Field Harass and other profile/module limits remain. No human
-server, saved room, D1, deployment or new assurance campaign changed; final
-assurance follows all rules.
+Other profile/module limits remain. Own concealed No-Field Harass follows the
+separate checkpoint above. No human server, saved room, D1, deployment or new
+assurance campaign changed; final assurance follows all rules.
**Ixian Nexus purchased-card interaction wave — 16 October.**
Purchased-card replacement now composes:
diff --git a/docs/ECAZ_OCCUPY_RULES.md b/docs/ECAZ_OCCUPY_RULES.md
index c6960e9e..b2b4b46d 100644
--- a/docs/ECAZ_OCCUPY_RULES.md
+++ b/docs/ECAZ_OCCUPY_RULES.md
@@ -258,6 +258,15 @@ Its +2 normal score adds no undialed counters; original reserve cost and fixed
versus variable on-board losses stay separate. Other optional modules and
Basic/unprofiled co-side card battles remain guarded.
+Own concealed markers use the
+[provisional materialization-first card contract](HARASS_WITHDRAW_RUNTIME.md#own-no-field-composition--16-october-2026).
+Actual reveal precedes original reserve cost and successful return. Richese-held
+Harass returns only its actual variable ordinary counters; Ecaz-held Harass
+returns only its original fixed-pool undialed counters. Neither changes the
+original mandatory Ecaz commitment. Reinforcements checks only its card user's
+post-reveal reserves, not the allied marker's owner's supply. Marker-only
+co-side does not admit same-owner mixed ordinary/marker Occupy.
+
Native/pure67 cases, relevant284-case batch and27 actual native-program
cases pass, including all four legal policies. Two natural captured games
exposed an existing mandatory-discard callback comparing casualties to the
diff --git a/docs/HARASS_WITHDRAW_RUNTIME.md b/docs/HARASS_WITHDRAW_RUNTIME.md
index b27589b9..de9db4f7 100644
--- a/docs/HARASS_WITHDRAW_RUNTIME.md
+++ b/docs/HARASS_WITHDRAW_RUNTIME.md
@@ -90,6 +90,48 @@ the card adds no fictitious weapon or defense value. Stronger profiles value
saved forces when expecting defeat and discourage use when expecting to win.
This is a legal strategy path, not strength calibration.
+## Own No-Field composition — 16 October 2026
+
+Under standing choose-recommended, this prototype preserves the existing native
+materialization-first order: both plans seal, the original No-Field reveals at
+its actual sector and materializes `min(value, current reserves)`, then Harass
+chooses/returns actual undialed ordinary counters. It never returns a concealed
+marker or resets token/last-shipment history. This is an explicit composition
+of reveal-time instructions, not a newly retrieved combined publisher FAQ.
+
+Before commitment, the owner's private force and location quote includes those
+prospective ordinary counters without mutating reserves, board or token. Marker0
+still permits leader/cards; reserve shortage caps the actual materialization.
+Mixed ordinary/marker sectors choose a complete return only after revelation.
+An opposing successful Traitor cancels Harass, not the native marker reveal.
+Original fixed Ecaz and variable ally pools remain owner-labelled.
+
+In a supported same-plan Reinforcements profile, the original reserve fee must
+remain funded **after native marker materialization and before Harass returns**.
+Token3/reserves3 therefore cannot seal Reinforcements, even if a later return
+would refill reserves. The same owner may still use Harass alone. Token3/
+reserves6 can materialize3, pay original3, then return its undialed counters.
+Preview facts are owner-only and add no persisted Game field.
+
+### Scoped native proof
+
+The34 new genuine-shipment cases pass; the frozen17-file related union
+passes210/210, with types/lint and application build. Zero and reserve-capped
+markers, both slots, mixed-sector full allocations, real Traitors, original
+fixed/variable owners, first-plan funding rejection and component history are
+covered. Build retains existing module-register, chunk-size and route warnings.
+
+Sixteen fresh native programs across all four policies accept48 continuation
+actions. Four styled390px journeys pass without overflow or browser/application
+errors: mixed Basic returns3 marker-sector plus1 other-sector counters and
+loses2; Advanced zero plays the card without invented counters; deficient
+Occupy keeps Reinforcements and legally returns3 with Harass; funded Occupy
+materializes3, pays3, returns2 and loses its original1 variable counter.
+The latter has Richese reserves2/Tanks4 and Ecaz Tanks3, preserving original
+fixed losses. Both co-side journeys finish the actual CHOAM market and clear
+battle/decision into phase7. Owned hosts/tabs/scaffolds were removed; no human
+game, D1, deployment or new recovery campaign changed. Full acceptance remains.
+
## Current boundaries
- Basic and Advanced now support explicit ordinary/elite and sector allocation
@@ -104,8 +146,9 @@ This is a legal strategy path, not strength calibration.
The ordinary invariant-allocation guard remains. Clean selected Ix follows
the [separate priority contract](MIRROR_WEAPON_ENGINE_AUDIT.md#clean-selected-ix-composition--16-october-2026);
other profile/module limits, public starts and played-game retrofit stay gated.
- A card user's own still-concealed No-Field remains guarded until its original
- reveal; this wave does not certify Harass on a concealed native marker.
+ Own concealed No-Field use follows the
+ [materialization-first contract](#own-no-field-composition--16-october-2026).
+ Other profile/module boundaries remain unchanged.
- Co-present armies remain guarded in Basic or without the supported Advanced
Occupy profile. The [combined-army follow-up](ECAZ_OCCUPY_RULES.md#independent-cards-with-combined-armies--2-october-2026)
returns only actual card-user forces: original fixed Ecaz commitment or the
diff --git a/docs/IMPLEMENTATION_STATUS.md b/docs/IMPLEMENTATION_STATUS.md
index 36ba8a28..fb32242c 100644
--- a/docs/IMPLEMENTATION_STATUS.md
+++ b/docs/IMPLEMENTATION_STATUS.md
@@ -1,5 +1,36 @@
# Dune implementation status
+## 16 October 2026 — Concealed No-Field Harass and original reserve funding
+
+Preserve native materialization before effects. Private owner previews forecast
+the actual reserve-capped ordinary counters at the marker sector, without an
+early force/token transfer. Harass then selects/returns actual undialed counters,
+never a virtual marker or reset history. Zero, caps, mixed sectors, original
+fixed Ecaz/variable ally owners and Traitor cancellation remain connected.
+
+Reproduced actual Advanced token3/reserves3 acceptance followed by native
+materialization to0 and a failing first Traitor action. Reinforcements now
+requires three own reserves after projected reveal, before any future return,
+and rejects the first unfunded plan immutably while retaining legal Harass-only.
+This materialize → original fee → successful return composition is explicitly
+provisional, not a new publisher FAQ. No Basic Richese fee profile or other
+optional module is admitted; no persisted Game field is added.
+
+New genuine-shipment regressions34/34; frozen17-file210/210, types/lint and
+build pass with existing module-register/chunk-size/route warnings. Sixteen
+fresh native programs across four policies accept48 continuation actions.
+Four real390px journeys cover mixed Basic complete-sector allocation, Advanced
+zero/card legality, blocked native co-side fee/kept card and funded original
+fee before return. The latter ends Richese reserves2/Tanks4 and Ecaz Tanks3;
+both co-side controls finish original CHOAM market into phase7. No overflow,
+browser/application error or original component-history loss was observed.
+
+Actual generated guide import,25/16/28 topic evidence paths and three anchors
+resolve; all15 facets remain Partial. Owned QA scaffolds/hosts/tabs removed.
+No human game, D1, deployment or new broad/HTTP/recovery assurance changed.
+Full rules, optional interactions and final acceptance remain unfinished.
+[Canonical order and proof](HARASS_WITHDRAW_RUNTIME.md#own-no-field-composition--16-october-2026).
+
## 16 October 2026 — Clean selected Ix Stone and physical Mirror
Replaced the blanket Ix guard only for clean selected CHOAM/Ix profiles,
diff --git a/docs/REINFORCEMENTS_RUNTIME.md b/docs/REINFORCEMENTS_RUNTIME.md
index 4b80a973..241d3353 100644
--- a/docs/REINFORCEMENTS_RUNTIME.md
+++ b/docs/REINFORCEMENTS_RUNTIME.md
@@ -35,8 +35,9 @@ This is an implementation interpretation, not a newly retrieved publisher FAQ
or an answer to the earlier reserve-payment question. The source audit's printed
effects and original provisional all-outcome/normal-first cost are unchanged.
-Three reserves must exist at commitment. A future return cannot fund a deficient
-plan or change its quoted normal/elite mix. For example, two original ordinary
+Three original own reserves must remain funded at commitment after projecting
+any own No-Field revelation. A future return cannot fund a deficient plan or
+change its quoted normal/elite mix. For example, two original ordinary
and one elite reserve are spent even when Harass subsequently returns four
ordinary counters. Both cards discard once. The +2 remains normal-score-only,
not extra physical dial, support or winner casualties. Opposing successful
@@ -50,6 +51,23 @@ Basic/unprofiled co-side and other module gates stay; Stone uses the
[physical-token composition below](#native-stone-composition--16-october-2026).
No persisted state or fresh/public profile is added.
+### Own concealed No-Field funding
+
+The existing native battle path materializes a No-Field after both seals and
+before card costs. Precommit availability now forecasts that same owner's
+remaining reserves. Three must remain at that point, before any future Harass
+return; another player's marker/reserves cannot affect or fund this card.
+This is the explicitly provisional existing-order composition, not a new
+publisher ruling or a waiver of the original fee.
+
+Before-state token3/reserves3 accepted Reinforcements, revealed into reserves0
+and then failed the first Traitor action. The new first-plan check rejects that
+unfunded commitment without reserving cards or revealing a token; Harass alone
+remains legal. Token0/reserves3 remains funded. Native actual revelation,
+reserve-cap, token identity/history, support and fixed/variable losses stay.
+No Basic Richese Reinforcements or other new mode is admitted.
+
+
### Scoped proof
The affected eleven-file offline union passes **134/134**, with types/lint and
diff --git a/docs/RICHESE_NO_FIELD_RULES.md b/docs/RICHESE_NO_FIELD_RULES.md
index 07833a8b..e1918143 100644
--- a/docs/RICHESE_NO_FIELD_RULES.md
+++ b/docs/RICHESE_NO_FIELD_RULES.md
@@ -2,6 +2,30 @@
Audit date: 2026-09-06. This document distinguishes the printed token lifecycle from engine integration and unresolved combinations. It does not certify Richese, Homeworlds, or any other gated game mode.
+## Concealed battle-card composition — 16 October 2026
+
+Harass now composes with own concealed No-Fields in supported Basic/Advanced
+and existing Advanced Occupy card profiles. Standing choose-recommended keeps
+the native order: seal both plans, materialize the reserve-limited token at
+its actual sector, then resolve original reserve cost and successful Harass
+return before ordinary losses. This priority composition remains explicitly
+provisional, not a new dedicated publisher FAQ.
+
+Private planning forecasts actual ordinary counters and their marker-sector
+location without moving them early. Zero remains a real marker with leader/
+card legality; shortage caps by actual reserves. Mixed sectors select all
+undialed physical counters only after reveal. Harass neither returns a virtual
+marker nor clears its last-use/history fence. Opposing successful Traitor
+cancels only the return, never the original component revelation.
+
+Reinforcements requires three original own reserves **after projected native
+materialization**, before any later return. A proven token3/reserves3 plan
+previously sealed, materialized to0 and stalled on Traitor resolution; the
+new first-plan guard rejects that commitment and retains a legal Harass-only
+alternative. Other owners' reserves/markers are not shared. Owner-only preview
+facts are view data, not new saved Game fields or a historical-save migration.
+Existing mode, mixed-co-side, full-plan and optional-module gates remain.
+
## Advanced first versions — 8 October 2026
Advanced ordinary mixed battles and discounted allied shipments now reuse
@@ -124,7 +148,7 @@ This table records the original adapter contract. The implemented subset and cur
## Unresolved or compositional boundaries
1. **Reserve shortage when destroyed.** Voluntary/battle text explicitly caps by reserves; the destruction sentence says to lose the indicated quantity without repeating the shortage clause. The pure model caps materialization to avoid creating physical units, but that is conservation-based composition for storm/worm losses, not an independently answered FAQ. It does not select substitute losses from unrelated board forces.
-2. **Mixed forces and battle dials.** The FAQ forbids dialing more than reserves in a No-Field battle. A territory can also contain ordinary forces (the Smuggler answer demonstrates mixed presence), but no retrieved answer expressly defines that limit with existing forces, multiple sectors, typed units or independently committed reserve uses. Basic and Advanced prototypes use an explicitly provisional additive pool; this does not settle the source question. Combined Occupy remains guarded.
+2. **Mixed forces and battle dials.** The FAQ forbids dialing more than reserves in a No-Field battle. A territory can also contain ordinary forces (the Smuggler answer demonstrates mixed presence), but no retrieved answer expressly defines that limit with existing forces, multiple sectors, typed units or independently committed reserve uses. Basic and Advanced prototypes use an explicitly provisional additive pool; this does not settle the source question. Same-owner mixed ordinary/marker Occupy remains guarded; the existing marker-only Advanced Occupy profile supports the owner-specific card composition above.
3. **Full-plan special Karama and structured Truthtrance.** The ordinary Atreides dial prohibition is explicit. Whether a broader entire-plan power or a particular promise question overrides it was not resolved by a retrieved specific answer. Do not leak the value through private legal-plan previews, public maximum dials, error strings or bot reasoning.
4. **Homeworld use.** R3 recommends Richese in a Homeworld combination but supplies no retrieved No-Field-specific rule defining off-planet hidden custody, one-on-planet versus off-planet coexistence, or homeworld threshold disclosure. General module availability is not an answer to those lifecycle questions. The isolated model represents a single Arrakis marker and does not authorize Homeworld placement.
5. **Capturing, transferring or returning a concealed marker as a force.** Movement-as-a-force and Gamont's explicit forced reveal do not establish a general conversion for every card or Guild transport-to-reserves. A generic return helper must not erase a hidden token without a source-backed reveal/custody cause. This module exposes no such automatic escape from last-use history.
diff --git a/docs/RULE_DECISIONS.md b/docs/RULE_DECISIONS.md
index a0ce6311..bb68603a 100644
--- a/docs/RULE_DECISIONS.md
+++ b/docs/RULE_DECISIONS.md
@@ -657,7 +657,8 @@ dedicated retrieved worked FAQs. [Contract and remaining limits](HOMEWORLD_OCCUP
| Basic Moritani / Leader Skills | Ordinary Terror death returns the assigned skill once; own revival offers the existing private optional replacement. Sabotage does not take skills, alliances do not transfer assignments, and mandatory Planetologist/Diplomat disposal excludes those cards from ally retention. This is composition of existing contracts, not a new ruling or resolution of unfinished skill effects. Advanced and other roster/module combinations remain gated. | [Integration contract](MORITANI_LEADER_SKILLS.md) |
| Ecaz Loyalty | Before initial Traitor dealing, randomly set aside one native Ecaz card face up permanently. No Karama effect; no change to its leader disc. Resolving after existing pre-Traitor setup is an explicit sequencing composition; native ordinary-disc Skills now precede this draw through public assignment. Older dealt saves are not retrofitted; wider Skills/assassination combinations remain gated. | [Official sources and runtime](ECAZ_LOYALTY.md), [native skill composition](LEADER_SKILLS_RUNTIME.md#native-ecaz-ordinary-disc-composition--4-october-2026) |
| Sapho battle order | First/last reorders remaining Battle-phase choosers at clean boundaries. Phase priority persists through aftermath; actual legal pairs determine eligibility. Separating the chooser from physical attacker/tie advantage is an explicit implementation interpretation of the card's distinct alternatives, not a dedicated FAQ ruling. Last does not prevent others from choosing the holder as an opponent. | [Runtime scope and source interpretation](SAPHO_BATTLE_ORDER.md) |
-| Harass & Withdraw | Either slot but neither category; private Prescience answers null. Return uniquely determined own undialed physical forces before weapon losses, canceled by an opposing successful Traitor. Mandatory after-use discard overrides winner retention. Explosion ordering and own-Traitor disposal are explicit timing/specificity inferences, consistent with the recorded Planetologist precedent. | [Runtime and sources](HARASS_WITHDRAW_RUNTIME.md) |
+| Harass & Withdraw | Either slot but neither category; private Prescience answers null. Return own undialed physical forces before weapon losses, selecting complete typed/sector returns after reveal where necessary; canceled by an opposing successful Traitor. Mandatory after-use discard overrides winner retention. Explosion ordering and own-Traitor disposal are explicit timing/specificity inferences, consistent with the recorded Planetologist precedent. | [Runtime and sources](HARASS_WITHDRAW_RUNTIME.md) |
+| No-Field battle cards | Under standing choose-recommended, preserve native materialization before card effects: actual reserve-limited token reveal, original Reinforcements cost, then successful Harass return. Privately forecast physical counters/sector and require three own reserves after reveal before sealing the fee card; future returns or ally reserves cannot fund it. Zero, original token/history, support and fixed/variable owners remain native. This is explicitly provisional composition, not a new publisher FAQ or user answer. Other profile/module and inspection gates stay. | [Harass contract](HARASS_WITHDRAW_RUNTIME.md#own-no-field-composition--16-october-2026) · [Native lifecycle](RICHESE_NO_FIELD_RULES.md#concealed-battle-card-composition--16-october-2026) |
| Clean Ix Stone / Tooth / Mirror | Reuse the existing CHOAM prototype order: original Stone first, then uncopied Tooth. The earlier priority question is unanswered; standing choose-recommended extends that order provisionally, not as a publisher ruling or user answer. Printed Mirror copy-first and user-selected physical retention stay independent. Clean selected Ix/Richese supports native Ixians/Tleilaxu in both modes; Ecaz/Moritani stays Advanced-only. Typed winner ambiguity, new Ix Tech/Stronghold and other module bands remain guarded. | [Source and profile distinction](MIRROR_WEAPON_ENGINE_AUDIT.md#clean-selected-ix-composition--16-october-2026) |
| Expansion-faction setup | Selecting Ecaz/Moritani factions does not select their independent three-card variant. Ordinary decks contain 33/35/47 cards; Richese's ten-card cache stays separate. Ecaz allocates six total forces within Imperial Basin sectors 9–11, retaining fourteen reserves. Flexible sector allocation is the literal printed territory constraint; Fremen→Ecaz→Advanced BG order is an implementation inference, while Moritani-last is printed. | [Source and profile contract](EXPANSION_FACTIONS_PROTOTYPE.md) |
| Ixian Technology on Richese lots | Superseded 8 October by the authorized supplied Advanced physical p34, which expressly excludes Richese auctions from Ixian Technology. Richese cache/Black Market lots now open directly; the once-per-round Technology use remains for ordinary lots. The temporary per-lot decline scaffolding and its tests were deleted. | [Auction source boundary](RICHESE_AUCTION_RULES.md#13-september-saved-per-lot-technology-decline--superseded-8-october) |
diff --git a/docs/STONE_BURNER_ENGINE_AUDIT.md b/docs/STONE_BURNER_ENGINE_AUDIT.md
index 9c799975..7ccf7b6d 100644
--- a/docs/STONE_BURNER_ENGINE_AUDIT.md
+++ b/docs/STONE_BURNER_ENGINE_AUDIT.md
@@ -31,7 +31,9 @@ The earlier question remains unanswered. This is an interpretation selected
under the standing choose-recommended priority, not newly retrieved publisher
authority. Clean Ix now follows the separate
[selected-deck priority contract](MIRROR_WEAPON_ENGINE_AUDIT.md#clean-selected-ix-composition--16-october-2026);
-own concealed No-Field Harass and other profile/module boundaries remain.
+own concealed No-Field Harass follows its
+[materialization-first contract](HARASS_WITHDRAW_RUNTIME.md#own-no-field-composition--16-october-2026).
+Other profile/module boundaries remain.
The 6 September evidence above
is historical; the current checkpoint has its own bounded proof.
diff --git a/game/card-presentation.ts b/game/card-presentation.ts
index 94a5f6cb..fb5db56c 100644
--- a/game/card-presentation.ts
+++ b/game/card-presentation.ts
@@ -120,8 +120,8 @@ export function cardPresentation(card: VisibleCard): CardPresentation {
availability: ecaz.card.effect === 'recruits'
? 'Recruits has development controls during Revival. Earlier paid revivals, pending transactions, later free-rate changes and repeated use this turn remain guarded pending their rulings.'
: ecaz.card.effect === 'harassWithdraw'
- ? 'Harass & Withdraw has development controls in either battle-card slot, with physical allocation choices after reveal. Stone Burner provisionally compares undialed forces remaining after successful withdrawal; additional optional modules remain guarded.'
- : 'Reinforcements has development controls in either battle-card slot, including the explicit Advanced Occupy card variant. It spends three of the card user’s own reserve forces and adds +2 normal battle score without extra support or physical tokens. Original or copied Stone Burner ignores that score bonus but still charges the reserve cost. All-outcome payment and paying before same-plan Harass returns are provisional; returned forces cannot fund or change the cost. Other profile and optional-module limits remain guarded.',
+ ? 'Harass & Withdraw has controls in either battle-card slot, with physical allocation after reveal. Under provisional materialization-first order, an own concealed No-Field reveals natively before actual undialed counters return; token history is not reset. Stone Burner provisionally compares forces remaining after successful withdrawal. Other profile and optional-module limits remain guarded.'
+ : 'Reinforcements has controls in either battle-card slot, including the explicit Advanced Occupy card variant. It spends three own reserves and adds +2 normal score, not support or physical tokens. Own No-Field reveal must leave three original reserves before this cost; later Harass returns cannot fund it. Original/copy Stone ignores the score bonus but still charges reserves. All-outcome payment and native cost-before-return order are provisional. Other profile/module limits remain guarded.',
topics: RULE_TOPICS.filter((topic) => topic.id === (ecaz.card.effect === 'harassWithdraw' ? 'card-harass-withdraw' : ecaz.card.effect === 'reinforcements' ? 'card-reinforcements' : 'card-recruits')),
};
const topicIds =
diff --git a/game/engine.ts b/game/engine.ts
index eccd3786..c8e212f7 100644
--- a/game/engine.ts
+++ b/game/engine.ts
@@ -8250,20 +8250,21 @@ function killTerritory(
n -= lost;
}
}
+/** Private planning forecast; public alternatives remove the foreign marker
+ * before requesting a pool. Actual reveal remains the native component action. */
+function prospectiveNoFieldForces(p: Player, territory: string): number {
+ const marker = p.noField?.deployed;
+ return marker?.location.territory === territory
+ ? Math.min(p.reserves, p.noField!.tokens.find(token => token.id === marker.tokenId)!.value)
+ : 0;
+}
function combatForces(
g: Game,
p: Player,
t: string,
opponent: Player,
): CombatForces {
- const marker = p.noField?.deployed;
- const prospective =
- marker?.location.territory === t
- ? Math.min(
- p.reserves,
- p.noField!.tokens.find((token) => token.id === marker.tokenId)!.value,
- )
- : 0;
+ const prospective = prospectiveNoFieldForces(p, t);
const army = combatArmy(g, p.id, t);
const physical = army.normal + army.elite;
const elite = army.elite;
@@ -13753,7 +13754,8 @@ function currentReinforcementsCost(g: Game, p: Player) {
const combinedBlock = combinedCardBattleBlock(g);
requireRule(!combinedBlock, combinedBlock ?? 'This combined card battle is unavailable.');
try {
- return quoteReinforcements(p.reserves - (p.elites?.reserves ?? 0), p.elites?.reserves ?? 0);
+ const materializes = prospectiveNoFieldForces(p, g.battle?.territory ?? '');
+ return quoteReinforcements(p.reserves - materializes - (p.elites?.reserves ?? 0), p.elites?.reserves ?? 0);
} catch (error) {
if (error instanceof ReinforcementsError) throw new RuleError(error.message);
throw error;
@@ -13763,7 +13765,9 @@ function reinforcementsPreview(g: Game, p: Player) {
const battle = g.battle;
if (!battle || ![battle.attacker, battle.defender].includes(p.id) ||
!p.hand.some(isReinforcements)) return null;
- const normal = Math.min(3, Math.max(0, p.reserves - (p.elites?.reserves ?? 0)));
+ const materializes = prospectiveNoFieldForces(p, battle.territory);
+ const reservesAfterReveal = p.reserves - materializes;
+ const normal = Math.min(3, Math.max(0, reservesAfterReveal - (p.elites?.reserves ?? 0)));
const elite = Math.min(3 - normal, Math.max(0, p.elites?.reserves ?? 0));
const combinedBlock = combinedCardBattleBlock(g);
let blocked: string | null = null;
@@ -13774,8 +13778,12 @@ function reinforcementsPreview(g: Game, p: Player) {
else if (combinedBlock)
blocked = combinedBlock;
else if (normal + elite < 3)
- blocked = 'Reinforcements needs three physical forces in your reserves.';
- return { blocked, normal, elite };
+ blocked = materializes
+ ? 'Your No-Field reveals before Reinforcements; three own reserves must remain after materialization.'
+ : 'Reinforcements needs three physical forces in your reserves.';
+ return { blocked, normal, elite,
+ ...(p.noField?.deployed?.location.territory === battle.territory
+ ? { noField: { materializes, reservesAfterReveal } } : {}) };
}
function recruitsInteractionPending(g: Game) {
@@ -20705,11 +20713,16 @@ function harassWithdrawContext(g: Game, p: Player): HarassWithdrawContext {
let blocked = harassModuleBlock(g);
if (!blocked && (g.status !== 'playing' || g.phase !== 6 || ![b.attacker, b.defender].includes(p.id)))
blocked = 'Use Harass & Withdraw in your own battle.';
- if (!blocked && p.noField?.deployed?.location.territory === b.territory)
- blocked = 'Reveal your No-Field before using Harass & Withdraw.';
const locations = Object.fromEntries(Object.entries(p.forces)
.filter(([key, amount]) => amount > 0 && splitLocation(key).territory === b.territory)
.map(([key, amount]) => [key, { normal: amount - (p.elites?.forces[key] ?? 0), elite: p.elites?.forces[key] ?? 0 }]));
+ const materializes = prospectiveNoFieldForces(p, b.territory);
+ if (materializes) {
+ const marker = p.noField!.deployed!;
+ const key = location(marker.location.territory, marker.location.sector);
+ if (locations[key]) locations[key].normal += materializes;
+ else locations[key] = { normal: materializes, elite: 0 };
+ }
const profile = ecazBattleProfile(g);
const forces = profile?.lead === p.id
? p.id === profile.ecaz ? profile.ecazForces : profile.allyForces
@@ -20744,8 +20757,11 @@ function harassWithdrawPreview(g: Game, p: Player): HarassWithdrawPreview | null
const b = g.battle;
if (!b || ![b.attacker, b.defender].includes(p.id) || !p.hand.some(isHarassWithdraw)) return null;
const context = harassWithdrawContext(g, p);
+ const materializes = prospectiveNoFieldForces(p, b.territory);
return { ...context, card: HARASS_WITHDRAW_CARD, allowAllocation: b.harassAllocationVersion === 1,
- blocked: context.blocked ?? (b.revealed || b.plans[p.id] ? 'Your Battle Plan is already sealed.' : null) };
+ blocked: context.blocked ?? (b.revealed || b.plans[p.id] ? 'Your Battle Plan is already sealed.' : null),
+ ...(p.noField?.deployed?.location.territory === b.territory
+ ? { noField: { materializes, reservesAfterReveal: p.reserves - materializes } } : {}) };
}
function validateResidualPoison(
g: Game,
diff --git a/game/harass-withdraw.ts b/game/harass-withdraw.ts
index bab3c368..82d657ce 100644
--- a/game/harass-withdraw.ts
+++ b/game/harass-withdraw.ts
@@ -33,6 +33,8 @@ export type HarassWithdrawPreview = HarassWithdrawContext &
card: typeof HARASS_WITHDRAW_CARD;
/** New battle protocol allows the physical return to be chosen after reveal. */
allowAllocation?: boolean;
+ /** Owner-only forecast; actual marker reveal still precedes card effects. */
+ noField?: Readonly<{ materializes: number; reservesAfterReveal: number }>;
}>;
export type HarassWithdrawQuote = {
returned: { normal: number; elite: number };
diff --git a/game/reference.ts b/game/reference.ts
index a75c88d3..3ac60f76 100644
--- a/game/reference.ts
+++ b/game/reference.ts
@@ -1034,9 +1034,10 @@ export const RULE_TOPICS: RuleTopic[] = [
'The map shows a concealed marker without its denomination to everyone. Richese privately sees its token values, last-use restriction and reveal control. Movement selection keeps the marker separate from physical forces. No hidden denomination or physical token identity is included in another player’s projected board state.',
'Marker-only battles use the owner’s private reserve-limited force pool. Both plans must be sealed before the token materializes. A zero-token battle still resolves with its leader, cards and traitor decisions. Ordinary Atreides prescience cannot request this opponent’s dial; other eligible plan elements remain available.',
'Basic and Advanced ordinary mixed battles have a provisional private pool: physical forces already here plus the token value capped by current reserves. The Battle Wheel labels that interpretation explicitly. Both plans seal before materialization, then native support, casualties and aftermath use actual forces. This first version does not adjudicate the FAQ mixed dial cap.',
+ 'In supported card profiles, provisional materialization-first order forecasts the own marker privately without moving forces early. Both plans seal, the original token reveals and materializes its reserve-limited ordinary counters at its sector, then Harass selects/returns actual undialed counters. Reinforcements must leave three original reserves after that reveal and before any future Harass return; an unfunded card cannot seal a plan. Marker0, last-used identity and original reveal-event history remain native.',
'Concealed collection, occupancy and control use one-force presence. Exposed storm sectors and unprotected worm destruction reveal and remove the resulting actual forces. Storm shelter and other sectors remain separate. Trip to Gamont at a location with a marker reveals it, even if ordinary forces also occupy that sector, then returns one actual force if present. It is still used if zero forces remain. Canceling the CHOAM power leaves the marker concealed.',
'All four AI profiles choose from their own private token inventory, pay only the one-force shipment price, move the marker explicitly and reveal positive tokens when useful. They retain zero-token presence and use only public information to assess opponents. They also offer safe positive tokens from their own inventory during the allied opportunity, fund them with their own spice and accept or decline private offers using their own force and payment information. A human Richese player’s offer window holds an AI ally’s shipment. Pending responses, decisions and Truthtrance retain priority.',
- 'Combined Ecaz Occupy and entire-plan special Karama inspection against a No-Field remain guarded. Homeworld custody, broader Truthtrance and interrupted compositions remain unfinished. Mixed-pool and discounted-price prototypes in both modes are not publisher-adjudicated formulas. Richese starts remain disabled.',
+ 'Marker-only No-Fields compose with the existing Advanced Occupy profile, including card-user-specific Harass and reserve costs. Same-owner mixed ordinary/marker Occupy and entire-plan special Karama inspection remain guarded. Homeworld custody, wider Truthtrance, other modules and complete Richese starts remain unfinished; mixed-pool, price and card-effect sequencing are explicit prototype interpretations.',
],
related: [
'richese-cards',
@@ -1050,35 +1051,35 @@ export const RULE_TOPICS: RuleTopic[] = [
area: 'Implementation',
status: 'Partial',
detail:
- 'Existing own lifecycle, hazards and battle bounds remain. Native allied Guild rate cancellation bills one marker and preserves the existing provisional bank payee. Exact supplier approval cannot increase; release retains original token/army and a new full-price offer needs fresh consent. Engine, fresh lender controls and AI share authorized recipient pricing rather than inferring another player’s state from a personal view. Wider source combinations remain partial.',
+ 'Native lifecycle, hazards and authorized allied pricing remain. Own concealed Harass now forecasts actual materialization and sector counters without moving them before both seals. Reinforcements checks original post-reveal reserves before allowing a plan, preventing the accepted-plan funding deadlock. Marker history, physical owners, fixed Ecaz commitment and other profile gates remain separate.',
evidence: ['game/shipment-price.ts', 'game/engine.ts'],
},
{
area: 'Player controls',
status: 'Partial',
detail:
- 'Existing private inventory, movement, reveal and battle controls remain. Actual390px controlled supplier loan cancels half1 without spending or revealing its prior three-marker, shows authoritative full2, makes a new five-token offer, obtains Guild consent and original interception, then spends supplier2/materializes Richese3 plus Guild5 once. No recipient wallet is exposed. Earlier saved-room evidence remains historical, not new assurance.',
- evidence: ['components/richese-allied-no-field.tsx', 'components/richese-no-field.tsx'],
+ 'Private inventory, movement/reveal and original loan controls remain. Owner-only Harass and reserve-cost previews explain projected materialization and post-reveal funding. Actual mixed-sector selection occurs after native revelation; invalid partial returns disable confirmation. Four mobile card journeys include zero marker, early blocked cost/legal fallback and funded native Occupy through original CHOAM market.',
+ evidence: ['components/richese-allied-no-field.tsx', 'components/richese-no-field.tsx', 'components/harass-withdraw.tsx', 'components/game-table.tsx'],
},
{
area: 'AI',
status: 'Partial',
detail:
- 'Own and allied legal participation remains. Fresh offer calculations consume the same authorized recipient quote as human controls; no inferred rival wallet or exact strategic first-candidate pin. Four controlled low-budget probes choose no optional reoffer here while the recipient executes a legal ordinary shipment. Full Richese strategy/calibration stays deferred.',
+ 'Own/allied pricing participation remains. All four profiles use private own materialization, original reserve funding and physical return quotes; unfunded Reinforcements is excluded while Harass remains legal. Sixteen native programs exercise ordinary, funded and fallback paths without strategic tuning or calibration.',
evidence: ['game/bots.ts'],
},
{
area: 'Documentation',
status: 'Partial',
detail:
- 'Source distinctions, lifecycle, typed loan consent and current controls remain documented. Guild-recipient bank payee and one-marker price precedence stay provisional. Full2 supplier reapproval and the authoritative fresh-quote bridge are explicit; original versus controlled evidence and remaining sponsored/card combinations are not conflated.',
- evidence: ['docs/RICHESE_ALLIED_SHIPMENT_AUDIT.md', 'docs/GUILD_RATE_KARAMA_RULES.md'],
+ 'Source, original loan consent/pricing and current controls remain documented. The provisional materialize-then-original-reserve-cost-then-Harass sequence is explicit, not a new publisher FAQ or user ruling. Token history and remaining mixed/inspection/module boundaries remain separate.',
+ evidence: ['docs/RICHESE_ALLIED_SHIPMENT_AUDIT.md', 'docs/GUILD_RATE_KARAMA_RULES.md', 'docs/RICHESE_NO_FIELD_RULES.md'],
},
{
area: 'Verification',
status: 'Partial',
detail:
- 'Current affected25-file263/263 passes across frozen237+26 selections. Seven controlled allied source cases cover zero/five, recipient full price, supplier release/reapproval, poverty and independent price. Actual390px complete supplier reoffer/consent fixes observed fresh-quote drift; API and four controlled continuation probes pass. Earlier own/battle/persisted evidence remains historical. No new saved/deployed or complete-mode acceptance.',
+ 'Earlier allied/native/recovery proof remains historical. New actual-shipment card regressions cover zero/caps, mixed sectors, original funding, typed physical custody, Traitor cancellation and marker history; mobile and four-profile continuations are bounded current evidence. No new saved/deployed or complete-mode assurance is claimed.',
evidence: [
'tests/richese-no-field.test.ts',
'tests/richese-no-field-engine.test.ts',
@@ -1091,6 +1092,10 @@ export const RULE_TOPICS: RuleTopic[] = [
'tests/richese-allied-no-field-bots.test.ts',
'tests/richese-allied-no-field-recovery.test.ts',
'tests/allied-marker-guild-rate.test.ts',
+ 'tests/harass-no-field.test.ts',
+ 'tests/fixture-harass-no-field.ts',
+ 'tests/no-field-reinforcements.test.ts',
+ 'tests/fixture-no-field-reinforcements.ts',
],
},
],
@@ -4974,6 +4979,7 @@ export const RULE_TOPICS: RuleTopic[] = [
'Have three own forces in reserves when sealing. Classic/paired Ecaz/Moritani Basic/Advanced and explicitly selected native Advanced Occupy prototypes spend ordinary reserves first and then elite reserves, moving exactly three to corresponding Tanks subpools at resolution. These are the actual card holder’s reserves, not the variable ally’s pool; no extra forces arrive.',
'The normal-outcome score gains 2 without adding to the physical dial, spice support or on-board casualties. The card and its reserve cost are consumed even on a Traitor victory, a mutual Traitor result or a Lasgun/Shield explosion; successful Traitor calls still decide the outcome directly.',
'All-outcome payment, normal-first selection and reserve transfer counting as battle losses remain provisional interpretations, not publisher rulings. Same-plan Harass pays the original three-reserve cost before returns; returned reserves cannot fund or change it. In native Advanced Occupy, original or copied Stone ignores +2 normal score but still charges the reserve cost. Basic/unprofiled co-side and other profile/module limits remain guarded.',
+ 'With your own concealed No-Field, the provisional native order materializes its reserve-limited counters before this cost. At least three own reserves must remain after that reveal, before any future Harass return; otherwise Reinforcements is unavailable before the first plan seals. This checks only the actual card user, never its ally’s marker or reserves.',
],
related: ['battle-cards', 'battle', 'card-recruits', 'card-harass-withdraw', 'implementation-checklist'],
checklist: [
@@ -4981,7 +4987,7 @@ export const RULE_TOPICS: RuleTopic[] = [
{ area: 'Player controls', status: 'Partial', detail: 'Owner-only availability, exact cost guidance and both battle selectors support the card; private category answers remain None.', evidence: ['components/battle-preparation.tsx', 'components/game-table.tsx'] },
{ area: 'AI', status: 'Partial', detail: 'Four profiles consider legal physical slot candidates and the normal score bonus; strategy and calibration remain unfinished.', evidence: ['game/bots.ts'] },
{ area: 'Documentation', status: 'Partial', detail: 'The printed effect and unresolved Traitor, explosion, type-selection and accounting interpretations are distinguished from this temporary prototype.', evidence: ['docs/ECAZ_TREACHERY_RULES.md', 'docs/REINFORCEMENTS_RUNTIME.md'] },
- { area: 'Verification', status: 'Partial', detail: 'Original proof plus same-plan Harass and native Stone/Mirror cases cover original typed costs, physical-token victory, fixed/variable Occupy, Traitor/explosion and retention. Actual mobile controls and legal four-profile smoke are bounded evidence; complete modes, calibration and final assurance remain open.', evidence: ['tests/stone-reinforcements.test.ts', 'tests/harass-reinforcements.test.ts', 'tests/reinforcements.test.ts', 'tests/reinforcements-engine.test.ts', 'tests/ecaz-occupy-cards-runtime.test.ts', 'docs/ECAZ_OCCUPY_RULES.md'] },
+ { area: 'Verification', status: 'Partial', detail: 'Original typed cost, Stone/Mirror and same-plan Harass proof now includes actual No-Field first-plan funding rejection, legal fallback, zero/funded costs, Traitor cancellation and actual Occupy owners. Mobile and four-profile continuations are bounded; complete modes/calibration/final assurance remain open.', evidence: ['tests/no-field-reinforcements.test.ts', 'tests/fixture-no-field-reinforcements.ts', 'tests/stone-reinforcements.test.ts', 'tests/harass-reinforcements.test.ts', 'tests/reinforcements.test.ts', 'tests/reinforcements-engine.test.ts', 'tests/ecaz-occupy-cards-runtime.test.ts', 'docs/ECAZ_OCCUPY_RULES.md'] },
],
},
{
@@ -4999,14 +5005,15 @@ export const RULE_TOPICS: RuleTopic[] = [
'For multiple legal physical allocations, choose all undialed ordinary and elite counters by sector after both plans reveal and before traitor declarations. Unique returns are automatic. The selected return is public; it is not an extra sealed plan element. Stone Burner provisionally compares the undialed forces remaining after successful withdrawal, including same-plan, opposing and copied Stone. Additional optional modules remain guarded. The printed card forbids use on your own Homeworld.',
'In explicitly selected Advanced Occupy, only the actual card user’s undialed forces return. An Ecaz user preserves its original mandatory fixed count at free full strength; an allied user uses its native variable commitment, while canceled Occupy uses its selected lead’s ordinary own pool. Returned reserves remain outside losses and Face Dance. Basic/unprofiled co-side, public starts and full acceptance remain gated.',
'In supported same-plan Reinforcements, the provisional order pays three original own reserves before Harass returns undialed counters. Future returns cannot fund or change that reserve quote; neither the +2 score bonus nor that cost adds to the physical commitment. Both cards discard after use. An opposing successful Traitor cancels Harass but not the provisional Reinforcements cost.',
+ 'With an own concealed No-Field, this prototype provisionally uses the original materialization-first order. Before sealing, privately forecast its actual ordinary counters at the original marker sector; do not move them early. Both plans seal before native revelation, then any return allocation uses actual counters. Marker0 remains legal with a leader/cards, reserve shortage caps materialization, and withdrawal does not return/reset the token. Opposing successful Traitor cancels Harass, not that native reveal.',
],
related: ['battle-cards', 'battle', 'card-recruits', 'ecaz-modules', 'implementation-checklist'],
checklist: [
- { area: 'Implementation', status: 'Partial', detail: 'Physical slot commitment, category inspection, revealed typed/sector returns, exact committed losses, opponent-Traitor cancellation, mandatory disposal and continuation are connected. Richese/Stone uses provisional post-withdrawal comparison with original fixed Occupy costs; other optional modules remain gated.', evidence: ['game/harass-withdraw.ts', 'game/stone-burner.ts', 'game/battle-card-slots.ts', 'game/battle-resolution-quote.ts', 'game/engine.ts'] },
- { area: 'Player controls', status: 'Partial', detail: 'Either slot, compatible pair selection, category-null commitment and card inspection are usable. Owners select typed/sector returns after reveal; Stone guidance and revealed counts visibly explain provisional post-withdrawal timing and Traitor cancellation.', evidence: ['components/battle-preparation.tsx', 'components/harass-withdraw.tsx', 'components/revealed-battle.tsx', 'components/game-table.tsx'] },
+ { area: 'Implementation', status: 'Partial', detail: 'Physical slots, private category answers, typed/sector return, committed losses, Traitor cancellation and mandatory disposal remain. Own No-Field now projects reserve-limited sector counters before commitment and uses actual native revelation/history. Same-plan cost cannot be funded by future returns. Other module/profile boundaries remain.', evidence: ['game/harass-withdraw.ts', 'game/stone-burner.ts', 'game/battle-card-slots.ts', 'game/battle-resolution-quote.ts', 'game/engine.ts'] },
+ { area: 'Player controls', status: 'Partial', detail: 'Either slot and after-reveal typed/sector choices remain. Owner-only No-Field forecasts and post-reveal reserve notices distinguish virtual marker from actual counters; zero marker, mixed-sector full return, blocked-cost fallback and funded card controls are usable.', evidence: ['components/battle-preparation.tsx', 'components/harass-withdraw.tsx', 'components/revealed-battle.tsx', 'components/game-table.tsx'] },
{ area: 'AI', status: 'Partial', detail: 'All four profiles have a legal shared-quote path and preserve inspection secrecy. Retreat strategy and combined-game strength remain uncalibrated.', evidence: ['game/bots.ts', 'tests/ecaz-battle-bots.test.ts'] },
- { area: 'Documentation', status: 'Partial', detail: 'The guide distinguishes printed effects, explicit inference, provisional post-withdrawal Stone timing and remaining module boundaries.', evidence: ['docs/HARASS_WITHDRAW_RUNTIME.md', 'docs/ECAZ_TREACHERY_RULES.md', 'docs/STONE_BURNER_ENGINE_AUDIT.md'] },
- { area: 'Verification', status: 'Partial', detail: 'Focused Stone/Harass and Occupy quote/native regressions cover winner reversal, original costs, typed returns, Mirror retention and Traitor precedence. Two actual mobile native-control journeys and legal four-profile smoke are bounded evidence, not full combination or deployed acceptance. Historical recovery proof is separate.', evidence: ['tests/stone-harass-quote.test.ts', 'tests/stone-harass-runtime.test.ts', 'tests/fixture-stone-harass.ts', 'tests/ecaz-occupy-harass-quote.test.ts', 'tests/harass-allocation-engine.test.ts', 'tests/harass-allocation-recovery.test.ts', 'tests/harass-withdraw-choice-controls.test.tsx', 'tests/harass-withdraw.test.ts', 'tests/ecaz-battle-controls.test.tsx', 'tests/ecaz-battle-bots.test.ts', 'tests/ecaz-battle-recovery.test.ts'] },
+ { area: 'Documentation', status: 'Partial', detail: 'Printed effects, provisional materialization-first and Stone/withdrawal timing are distinguished from remaining profile/module limits and final assurance.', evidence: ['docs/HARASS_WITHDRAW_RUNTIME.md', 'docs/ECAZ_TREACHERY_RULES.md', 'docs/STONE_BURNER_ENGINE_AUDIT.md', 'docs/RICHESE_NO_FIELD_RULES.md'] },
+ { area: 'Verification', status: 'Partial', detail: 'Original Stone/Harass/Occupy proof plus actual No-Field shipment/reveal cases cover zero, caps, mixed sectors, original costs, history and Traitor cancellation. Current mobile/legal-policy proof remains bounded, not full combination/deployed or new recovery certification.', evidence: ['tests/harass-no-field.test.ts', 'tests/fixture-harass-no-field.ts', 'tests/no-field-reinforcements.test.ts', 'tests/stone-harass-quote.test.ts', 'tests/stone-harass-runtime.test.ts', 'tests/fixture-stone-harass.ts', 'tests/ecaz-occupy-harass-quote.test.ts', 'tests/harass-allocation-engine.test.ts', 'tests/harass-allocation-recovery.test.ts', 'tests/harass-withdraw-choice-controls.test.tsx', 'tests/harass-withdraw.test.ts', 'tests/ecaz-battle-controls.test.tsx', 'tests/ecaz-battle-bots.test.ts', 'tests/ecaz-battle-recovery.test.ts'] },
],
},
{
diff --git a/tests/fixture-harass-no-field.ts b/tests/fixture-harass-no-field.ts
new file mode 100644
index 00000000..cb58e2a3
--- /dev/null
+++ b/tests/fixture-harass-no-field.ts
@@ -0,0 +1,241 @@
+import assert from 'node:assert/strict';
+import {
+ applyAction, createGame, initializeEcazTreacheryGameForAudit, joinGame,
+ newPlayer, viewGame, type Action, type Game,
+} from '../game/engine';
+import { botActions } from '../game/bots';
+import type { NoFieldValue } from '../game/richese-no-field';
+import { finishHarassPreparation, takeHarassCard } from './fixture-harass-withdraw';
+import { nextRicheseStrongholdsNativeStep } from './fixture-richese-strongholds';
+
+export type HarassNoFieldStep = { actor: string; action: Action };
+export type HarassNoFieldOptions = {
+ advanced?: boolean;
+ marker?: NoFieldValue;
+ /** Physical own reserves immediately before the actual marker shipment. */
+ reserves?: number;
+ ordinaryAtMarker?: number;
+ ordinaryOtherSector?: number;
+ slot?: 'weapon' | 'defense';
+ dial?: number;
+ support?: number;
+ traitor?: 'owner' | 'opponent' | 'both';
+ seatIds?: [string, string, string];
+ /** Continue the original fresh independent-variant initializer, without redealing. */
+ initial?: Game;
+};
+export type HarassNoFieldFixture = {
+ initial: Game;
+ afterSetup: Game;
+ beforeShipment: Game;
+ shipmentStep: HarassNoFieldStep;
+ afterShipment: Game;
+ beforeBattle: Game;
+ battleStep: HarassNoFieldStep;
+ /** Open original battle, all native preparation completed, neither plan sealed. */
+ game: Game;
+ owner: string;
+ opponent: string;
+ observer: string;
+ territory: string;
+ location: string;
+ otherLocation: string;
+ token: { id: string; value: NoFieldValue };
+ materializes: number;
+ planActions: [HarassNoFieldStep, HarassNoFieldStep];
+ nativeSteps: HarassNoFieldStep[];
+ staging: string[];
+};
+export function harassNoFieldPlayer(game: Game, actor: string) {
+ const player = game.players.find(p => p.id === actor);
+ assert.ok(player, `Missing original seat ${actor}`);
+ return player;
+}
+const clean = (game: Game) => !game.response && !game.decision && !game.phaseOpening;
+
+function initialize(game: Game): Game {
+ // Only the original initializer is deterministic; live token IDs/events remain native.
+ const descriptor = Object.getOwnPropertyDescriptor(crypto, 'getRandomValues');
+ crypto.getRandomValues =