Connect optional Smuggler reserve shipment and saved interception

Prototype one free accompanying force into an empty territory with explicit opt-out, shared human/AI pricing, physical elite custody and Guild recipient accounting. Bind pending quotes to their unique saved Guild decision; preserve legacy unstamped full-price declarations. Smuggler battle collection and combined modules remain unfinished and gated. Repair a randomly dealt-out Karama fixture by relocating its actual physical card without changing inventory.

Validation: 70 focused tests; typecheck, lint, 4477 offline tests, production build and 43 HTTP tests passed. Independent rules/persistence review completed, including interruption and cancellation. Browser checked the price toggle, three arrivals for two spice, and refresh with unchanged private cards. Two genuine four-profile Basic/Advanced samples completed without rejected candidates but did not use Smuggler.

Preserved all 427 opening rooms and original seat/recovery rows (461 rooms after QA/HTTP). SQLite integrity and foreign keys passed. Server remained healthy after the reported power outage; no restart or reset.

Verified source tree: bf6e02bf6873ca6d6b01c83481489f55b2c7db0184a7056364343440dae88803
This commit is contained in:
Quintine committed 2026-09-13 20:19:33 +10:00
1 parent 06f46c6f79
commit 9381ef140d
18 files changed
+1015 -25

No files matched your search

+26 -4
View File
@@ -20,6 +20,29 @@ import { enterDiscoveryCollection } from './fixture-discovery';
const reload = (game: Game): Game => JSON.parse(JSON.stringify(game)) as Game;
function giveExistingCard(
game: Game,
owner: string,
predicate: (card: Game['deck'][number]) => boolean,
) {
const recipient = game.players.find((player) => player.id === owner)!;
const zones = [game.deck, game.discard, ...game.players.map((p) => p.hand)];
for (const zone of zones) {
const index = zone.findIndex(predicate);
if (index < 0) continue;
const [card] = zone.splice(index, 1);
recipient.hand.push(card);
return card;
}
assert.fail('The initialized physical deck has no Karama card.');
}
function physicalCardIds(game: Game) {
return [...game.deck, ...game.discard, ...game.players.flatMap((p) => p.hand)]
.map((card) => card.id)
.sort();
}
function initialized(
factions: [FactionId, FactionId, FactionId],
expansions: string[] = [],
@@ -116,10 +139,9 @@ function pendingAdvisorEntry(
const beneGesserit = game.players[1];
beneGesserit.reserves--;
beneGesserit.forces['cistern:0'] = 1;
const karama = game.deck.find((card) => card.effect === 'karama');
assert.ok(karama);
game.deck = game.deck.filter((card) => card.id !== karama.id);
actor.hand.push(karama);
const custody = physicalCardIds(game);
giveExistingCard(game, actor.id, (card) => card.effect === 'karama');
assert.deepEqual(physicalCardIds(game), custody);
game = nextTurn(game);
game = applyAction(game, actor.id, entryAction(game));
assert.equal(game.discoveryEntry?.stage, 'arrival');