Adopt authorized Advanced Moritani normal-reveal forfeiture
This commit is contained in:
1 parent
b89e93a67f
commit
934b2ee4d7
9 files changed
+96
-19
No files matched your search
@@ -25,6 +25,19 @@ authorization and deployment/publication safeguards remain intact. Use the
|
||||
[bounded context index](DEVELOPMENT.md#context-index) and
|
||||
[feedback/proof schedule](DEVELOPMENT.md#feedback-and-proof-schedule).
|
||||
|
||||
**Authorized Advanced Moritani forfeiture:** the supplied PDF physical35 resolves
|
||||
the old duration question: a normal own-Traitor-Card reveal forfeits Assassinate
|
||||
Leaders for the rest of the game, across turns and opponents. The persisted
|
||||
normal-call flag already enforced that scope; guidance, public history and the
|
||||
log now name the sourced forfeiture. Basic, exceptional leader custody and the
|
||||
private-step UX preview gates are unchanged. A permanent engine regression
|
||||
exercises a genuine turn advance plus a real later loss with no opportunity and
|
||||
the owner-facing forfeiture reason. See
|
||||
[the cutover](MORITANI_ASSASSINATE_LEADERS.md#authority-and-advanced-duration-cutover)
|
||||
and [authorized source](RULE_DECISIONS.md#authorized-source-amendment--1-october-2026).
|
||||
Final types/lint, coherent assassination/guide38/38, build and390px Prototyped
|
||||
guide pass; four changed-region local links/anchors are valid.
|
||||
|
||||
**Independent cards + Advanced Occupy — bounded Development Verified:** fresh
|
||||
explicit CLI `--ecaz-treachery` adds all three physical cards36/50/38/50.
|
||||
Own Ecaz Harass returns only its floor count while original mandatory ceil
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
# Dune implementation status
|
||||
|
||||
## 2 October 2026 — authorized Advanced Moritani normal-reveal forfeiture
|
||||
|
||||
The user-authorized supplied revision, physical p35 Assassinate Leaders D,
|
||||
resolves the previously open duration: Moritani must not have revealed its
|
||||
Traitor Card normally **this game**. A normal own-card reveal therefore
|
||||
forfeits the ability for the rest of the game, across later turns and all
|
||||
opposing factions, regardless of that battle's result.
|
||||
|
||||
The persisted `normalTraitorCall` flag already enforced exactly that scope, so
|
||||
no state-machine change was needed. Owner-facing blocked text, the public
|
||||
reveal log and internal guidance now name the sourced forfeiture instead of an
|
||||
unresolved-duration guard. The change is source alignment, not a newly located
|
||||
GF9 erratum or a Basic ruling; exceptional leader custody and the independent
|
||||
private-step UX/public-preview gates are unchanged.
|
||||
|
||||
An exact normal-reveal case passes. A new permanent engine regression advances a
|
||||
genuine turn, swaps to a still-living opposing faction and stages a real later
|
||||
loss: the flag persists, no assassination opportunity or decision appears and
|
||||
the owner sees the game-long forfeiture reason. Throwaway native smoke confirmed
|
||||
the same real-reveal and turn-2 persistence. No database reset, new assurance
|
||||
campaign or preview-gate change.
|
||||
|
||||
Final `check:quick` types/lint, coherent assassination/guide38/38, build and
|
||||
390px Partial / Development: Prototyped guide pass; four changed-region local
|
||||
links/anchors are valid. The throwaway driver is removed and the verifier
|
||||
worker is closed.
|
||||
|
||||
## 2 October 2026 — independent Treachery cards with Advanced Occupy
|
||||
|
||||
Existing `ecaz-occupy` now accepts explicit fresh CLI `--ecaz-treachery`,
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
preview. Normal game-start and publication gates remain closed. This is the
|
||||
battle-loss ability, separate from the random Assassination Terror token.
|
||||
|
||||
## Authority and unresolved interpretation
|
||||
## Authority and Advanced duration cutover
|
||||
|
||||
The [GF9 Ecaz & Moritani rulebook](https://www.gf9games.com/dune/wp-content/uploads/EcazMoritani-Rulebook-LOWRES.pdf),
|
||||
pp. 6, 14 and 16, supplies the ability, captured-own-leader answer and Karama table.
|
||||
Independent source review found no official clarification of the disputed duration
|
||||
below. The runtime uses the following undisputed sequence:
|
||||
The historical publisher review found no official duration clarification.
|
||||
The user-authorized revised Advanced PDF now supplies an explicit game-long
|
||||
condition, recorded below. The runtime retains the following sequence:
|
||||
|
||||
1. Native Advanced Moritani loses a normal battle against a surviving opposing
|
||||
leader disc, with no traitor called.
|
||||
@@ -23,12 +24,19 @@ below. The runtime uses the following undisputed sequence:
|
||||
private physical replacement. Use the advantage once per opposing faction.
|
||||
Karama has no effect on this ability.
|
||||
|
||||
The printed statement that a normal traitor reveal loses this advantage does not
|
||||
specify a battle, faction or game duration. The prototype records the normal call
|
||||
and explicitly guards further use; it does not label that guard as a settled
|
||||
permanent forfeiture. The user has been asked whether to keep that interaction
|
||||
gated or choose a duration. Previously normally revealed cards and exceptional
|
||||
leader custody also remain outside this first runtime contract.
|
||||
**Authorized Advanced cutover — 2 October 2026:** the supplied
|
||||
`UNOFFICIAL_Revised_Dune_Rulebook_v.2.3_web.pdf`, physical p35 / logical p33,
|
||||
Assassinate Leaders D condition(d), says Moritani must **“not yet [have]
|
||||
revealed your Traitor Card normally this game.”** A normal own-card reveal
|
||||
therefore forfeits the advantage for the rest of this game, across later turns
|
||||
and all opposing factions. The existing persisted normal-call flag already
|
||||
enforced this scope; owner guidance and public reveal history now describe
|
||||
the sourced forfeiture rather than an unresolved-duration guard.
|
||||
|
||||
This resolves the old duration question under the authorized Advanced source,
|
||||
not as a newly located GF9 erratum. Basic, exceptional leader interactions and
|
||||
the independent private-step UX/public-preview gates remain unchanged. See
|
||||
[source identity and precedence](RULE_DECISIONS.md#authorized-source-amendment--1-october-2026).
|
||||
|
||||
The captured-own-leader answer preserves original faction identity: a Moritani
|
||||
leader used by Harkonnen does not become a Harkonnen-faction assassination card.
|
||||
|
||||
@@ -121,6 +121,21 @@ See [combined-card boundaries](ECAZ_OCCUPY_RULES.md#independent-cards-with-combi
|
||||
Comprehensive recovery/privacy/custody review follows all rules rather than
|
||||
becoming another prototype assurance wave.
|
||||
|
||||
## Advanced Moritani normal-reveal duration cutover — 2 October 2026
|
||||
|
||||
The authorized supplied revision resolves the previously open duration:
|
||||
**Assassinate Leaders D, physical PDF page 35**, requires that Moritani has
|
||||
**not yet revealed its Traitor Card normally this game**. A normal own-card
|
||||
reveal therefore forfeits Assassinate Leaders for the rest of the game, across
|
||||
later turns and every opposing faction, whether that battle was won or lost.
|
||||
|
||||
This supersedes the historical “normal call, unresolved duration” guard as
|
||||
source resolution, not a newly located GF9 erratum or a Basic ruling. The
|
||||
persisted normal-call flag already enforced game scope; owner guidance, public
|
||||
reveal history and the log now name the sourced forfeiture. Exceptional leader
|
||||
custody and the independent private-step UX/public-preview gates are unchanged.
|
||||
See [the cutover and Advanced sequence](MORITANI_ASSASSINATE_LEADERS.md#authority-and-advanced-duration-cutover).
|
||||
|
||||
## Recorded contracts and implementation boundaries
|
||||
|
||||
| Topic | Decision to preserve | Detailed authority and evidence |
|
||||
|
||||
+2
-2
@@ -7581,7 +7581,7 @@ function projectedMoritaniAssassinate(g: Game, viewer: string) {
|
||||
const own = viewer === state.owner;
|
||||
const choices = pending && own ? nexusRule(() => moritaniAssassinateChoices(moritaniAssassinateContext(g,pending))) : null;
|
||||
return {owner:state.owner,blocked:own && state.normalTraitorCall
|
||||
? 'Assassinate Leaders after a normal traitor call awaits clarification of the printed loss-of-advantage rule.' : null,
|
||||
? 'Moritani forfeited Assassinate Leaders for the rest of this game after revealing its Traitor Card normally.' : null,
|
||||
pending:pending ? {event:pending.event,opponent:pending.opponent,territory:pending.territory,
|
||||
cards:choices?.cards ?? [],blocked:choices?.blocked ?? null} : null,
|
||||
history:state.opportunities.filter(r => r.stage === 'revealed' || r.stage === 'replaced').map(r => ({
|
||||
@@ -20400,7 +20400,7 @@ function resolveBattle(g: Game, retreat?: DiplomatRetreatSelection) {
|
||||
?.name ?? identity);
|
||||
log(g, `${holder.name} revealed ${name} as a traitor.`);
|
||||
if (holder.id === g.moritaniAssassinate?.owner)
|
||||
log(g, `${holder.name} called a traitor normally. Further Assassinate Leaders use awaits clarification of the printed loss-of-advantage rule.`, {faction:'moritani',name:'Assassinate Leaders boundary'});
|
||||
log(g, `${holder.name} revealed a Traitor Card normally and forfeited Assassinate Leaders for the rest of this game.`, {faction:'moritani',name:'Assassinate Leaders forfeited'});
|
||||
}
|
||||
const dead = (l: Leader | undefined) => {
|
||||
if (l) {
|
||||
|
||||
@@ -18,6 +18,7 @@ export type MoritaniAssassinateReceipt = {
|
||||
export type MoritaniAssassinateState = {
|
||||
version: 1;
|
||||
owner: string;
|
||||
/** Authorized Advanced source: normal reveal forfeits this ability for the game. */
|
||||
normalTraitorCall: boolean;
|
||||
opportunities: MoritaniAssassinateReceipt[];
|
||||
};
|
||||
@@ -291,7 +292,7 @@ export function moritaniAssassinateChoices(
|
||||
return stopped('This Assassinate opportunity has already ended.');
|
||||
if (state.normalTraitorCall)
|
||||
return stopped(
|
||||
'Assassinate after an ordinary Moritani traitor call awaits its duration ruling.',
|
||||
'Moritani forfeited Assassinate Leaders for the rest of this game after revealing its Traitor Card normally.',
|
||||
);
|
||||
if (
|
||||
state.opportunities.some(
|
||||
|
||||
+2
-2
@@ -2997,7 +2997,7 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
'After you lose a battle, this advanced advantage is available only if the opposing leader disc survived and no Traitor was called.',
|
||||
'You may reveal a Traitor Card for that opposing faction, but it must name a different leader from the one you just fought. If that named leader is not in the Tanks, kill it and collect spice for its value.',
|
||||
'An already-dead named leader remains a legal reveal, awarding no death or spice. During Mentat Pause, set the revealed card aside face up as a marker and draw a new Traitor Card. You can use this advantage only once against each faction in the game.',
|
||||
'The printed rule says a normal traitor reveal loses this advantage, but its duration remains unresolved. The preview guards further assassination after a normal call. Karama has no effect against Assassinate Leaders.',
|
||||
'The authorized revised Advanced PDF, physical page35, requires that you have not revealed your Traitor Card normally this game. A normal own-card reveal therefore forfeits Assassinate Leaders for the rest of this game, across turns and opponents. Existing state preserves that forfeiture; Karama has no effect against the ability. This is the authorized source, not a new GF9 erratum.',
|
||||
'If Harkonnen has captured your own leader and you hold its Traitor Card, you may call that traitor normally; you may not use Assassinate Leaders on that basis.',
|
||||
'Terror Assassination chooses a random leader when its token is triggered. It does not use this battle-loss condition, different-leader Traitor Card or once-per-faction allowance. Moritani starts remain unavailable in Basic and Advanced.',
|
||||
],
|
||||
@@ -3005,7 +3005,7 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
{area:'Implementation',status:'Partial',detail:'Explicit Advanced Moritani/base-opponent preview before any normal traitor call; native disc deaths, printed bounty, dead-target zero bounty and exact Mentat replacement.',evidence:['game/moritani-assassinate.ts','game/engine.ts']},
|
||||
{area:'Player controls',status:'Partial',detail:'Uniform private choice/decline, named target and bounty, public face-up markers and readable inspection; normal activation awaits private-step UX decision.',evidence:['components/moritani-assassinate.tsx','components/game-table.tsx']},
|
||||
{area:'AI',status:'Partial',detail:'All four profiles choose from the owner-only quote or decline; no hidden rival data or strength-calibration claim.',evidence:['game/bots.ts','tests/moritani-assassinate-engine.test.ts']},
|
||||
{area:'Documentation',status:'Partial',detail:'Official authority, dead-target conditional, normal-call ambiguity, exceptional custody and optional-module boundaries remain explicit.',evidence:['docs/MORITANI_ASSASSINATE_LEADERS.md']},
|
||||
{area:'Documentation',status:'Partial',detail:'Printed effect and authorized Advanced game-long normal-reveal forfeiture are explicit; exceptional leader custody, private-step UX and optional-module boundaries remain open.',evidence:['docs/MORITANI_ASSASSINATE_LEADERS.md','docs/RULE_DECISIONS.md']},
|
||||
{area:'Verification',status:'Partial',detail:'Rules, controls, genuine-setup staged battles, private views, authenticated recovery, concurrent writes and physical replacement chains; full faction acceptance remains open.',evidence:['tests/moritani-assassinate-quote.test.ts','tests/moritani-assassinate-engine.test.ts','tests/moritani-assassinate-controls.test.tsx','tests/moritani-assassinate-recovery.test.ts']},
|
||||
],
|
||||
related: [
|
||||
|
||||
@@ -85,17 +85,30 @@ void test('stale, other-seat, same-leader, dishonest mixed and duplicate assassi
|
||||
assassinationRejects(mentat,'m',{type:'decision',event,card:'guild-1'});
|
||||
});
|
||||
|
||||
void test('a normal Moritani traitor call records an explicit unresolved guard and cannot create an assassination opportunity',()=>{
|
||||
void test('a normal Moritani traitor reveal forfeits assassination and cannot create an opportunity',()=>{
|
||||
const staged=stageAssassinationBattle(assassinationGame(),'guild-0');
|
||||
const done=resolveAssassinationBattle(staged,{normalCall:true});
|
||||
assert.equal(done.moritaniAssassinate!.normalTraitorCall,true);
|
||||
assert.equal(done.moritaniAssassinateCallEvents!.length,1);
|
||||
assert.equal(done.moritaniAssassinate!.opportunities.length,0);
|
||||
assert.match(viewGame(done,'m').moritaniAssassinate!.blocked!,/clarification/);
|
||||
const corrupt=structuredClone(done);corrupt.moritaniAssassinate!.normalTraitorCall=false;
|
||||
assert.throws(()=>viewGame(corrupt,'m'),/original event/);
|
||||
});
|
||||
|
||||
void test('a sourced normal-reveal forfeiture persists across turns and denies a later genuine loss',()=>{
|
||||
const first=assassinationToMentat(resolveAssassinationBattle(stageAssassinationBattle(assassinationGame(['m','g','e']),'guild-0'),{normalCall:true}));
|
||||
assert.equal(first.moritaniAssassinate!.normalTraitorCall,true);assert.equal(first.moritaniAssassinate!.opportunities.length,0);
|
||||
assert.match(viewGame(first,'m').moritaniAssassinate!.blocked!,/rest of this game/);
|
||||
first.turn++;
|
||||
first.players=[first.players[0],first.players[2],first.players[1]];
|
||||
const second=resolveAssassinationBattle(stageAssassinationBattle(first));
|
||||
assert.equal(second.lastBattleContext!.result,'normal');assert.notEqual(second.lastBattleContext!.winner,'m');
|
||||
assert.equal(second.moritaniAssassinate!.normalTraitorCall,true);
|
||||
assert.equal(second.moritaniAssassinate!.opportunities.length,0);
|
||||
assert.notEqual(second.decision?.kind,'moritaniAssassinate');
|
||||
assert.match(viewGame(second,'m').moritaniAssassinate!.blocked!,/rest of this game/);
|
||||
});
|
||||
|
||||
void test('saved assassination receipts and cleanup obligations fail closed after corruption',()=>{
|
||||
const pending=resolveAssassinationBattle(stageAssassinationBattle(assassinationGame()));
|
||||
for(const breakSave of [
|
||||
|
||||
@@ -187,16 +187,16 @@ void test('already-dead targets remain revealable for no death or spice, while p
|
||||
assert.deepEqual(context, before);
|
||||
});
|
||||
|
||||
void test('ordinary traitor-call ambiguity and already normally revealed cards stay explicit private guards', () => {
|
||||
void test('the sourced normal-reveal forfeiture and already normally revealed cards stay explicit private guards', () => {
|
||||
const context = fixture();
|
||||
context.state.normalTraitorCall = true;
|
||||
assert.match(
|
||||
moritaniAssassinateChoices(context).blocked!,
|
||||
/ordinary.*duration ruling/,
|
||||
/forfeited Assassinate Leaders for the rest of this game/,
|
||||
);
|
||||
assert.throws(
|
||||
() => quoteMoritaniAssassinate(context, 'atreides-1'),
|
||||
/duration ruling/,
|
||||
/forfeited Assassinate Leaders for the rest of this game/,
|
||||
);
|
||||
context.state.normalTraitorCall = false;
|
||||
context.normallyRevealed = ['atreides-1'];
|
||||
|
||||
Reference in new issue
Block a user