Prototype Jacurutu income, Testing Station and Shrine effects

Connect automatic unambiguous Jacurutu physical-force income, a sole-occupant
Testing Station storm choice and Shrine reciprocal physical card roles to
engine actions, player controls, legal AI and saved continuation. Preserve
Weather Control exclusion, exact card/force custody, private hand boundaries
and committed effects after restart. Shared role checks cover existing ordinary
and special Karama paths; reserved cards cannot enter another pending use.

Keep Orgiz, mixed-force Jacurutu allocation and contested Cistern/Testing
Station benefits explicitly pending. These are bounded prototypes; complete
Discovery/Advanced/expansion and publication gates remain closed.

Validation: types, lint and 4,345 offline tests passed (32 added), production
build passed, and 40 HTTP tests passed. Independent cross-reviews repaired
reserved-card deadlock, physical-card selection, alternate-role permission,
storm source/token binding and public-entitlement bot ordering. The first broad
run found five failures; focused repairs preceded the stable repeated check.

Browser A5EJQ2W4 used genuine Discovery setup/reveal/free entry with targeted QA
positioning. A human increased storm movement from two to three sectors,
moving sector 6 to 9. Refresh restored station forces and the private Snooper.
This was targeted control/recovery evidence, not an unmodified full game.
All 187 opening saved rooms remained unchanged; 31 QA/HTTP rooms were added
(218 total). Reused the running server; no restart or save reset.

Source-bound report:
/home/quintine/.local/share/dune/checkpoints/20260913-discovery-location-effects/verification-final/report.json
Source fingerprint: beecb85ab7ec39319115389c115434d161591e8549802c542a6343a0fc19bea6
Durations: check 140540ms; build 6741ms; integration 24956ms.
Aggregate goal counter 63,116,444 to 64,295,091 at final verification:
1,178,647 tokens before final Git/push overhead for three bounded functions.
Standing authorization: Push all from now on.
This commit is contained in:
Quintine committed 2026-09-13 14:50:09 +10:00
1 parent ab0ab52849
commit f0d3d9c464
31 files changed
+2524 -138

No files matched your search

+27 -18
View File
@@ -16,6 +16,7 @@ import {
type TruthQuestion,
} from '@/game/truthtrance';
import type { Action, GameView } from '@/game/engine';
import { canUseAsTruthtranceRole } from '@/game/shrine';
export function Truthtrance({
game: g,
@@ -29,6 +30,20 @@ export function Truthtrance({
const w = g.truthtrance!;
const me = g.players.find((p) => p.id === g.me)!;
const current = w.queue[0];
const truthCards =
me.hand?.filter((card) => canUseAsTruthtranceRole(g, me, card)) ?? [];
const declaration = (cards: typeof truthCards): Action => ({
type: 'card',
card: cards[0].id,
...(cards.length > 1 ? { cards: cards.slice(1).map((card) => card.id) } : {}),
...(cards.some((card) => card.effect === 'karama')
? {
shrineTruthtrance: cards
.filter((card) => card.effect === 'karama')
.map((card) => card.id),
}
: {}),
});
const [target, setTarget] = useState(
g.players.find((p) => p.id !== me.id)!.id,
);
@@ -135,27 +150,21 @@ export function Truthtrance({
</ul>
{!w.passed.includes(me.id) && (
<>
{me.hand
?.filter((c) => c.effect === 'truthtrance')
.slice(0, 1)
.map((c) => (
{truthCards.map((c) => (
<div key={c.id}>
{button('Declare my Truthtrance', {
type: 'card',
card: c.id,
})}
{button(
c.effect === 'karama'
? 'Use my Karama as Truthtrance'
: 'Declare my Truthtrance',
declaration([c]),
)}
</div>
))}
{(me.hand?.filter((c) => c.effect === 'truthtrance').length ??
0) > 1 &&
button('Declare both Truthtrances', {
type: 'card',
card: me.hand!.find((c) => c.effect === 'truthtrance')!.id,
cards: me
.hand!.filter((c) => c.effect === 'truthtrance')
.slice(1)
.map((c) => c.id),
})}
{truthCards.length > 1 &&
button(
`Declare all ${truthCards.length} as Truthtrance`,
declaration(truthCards),
)}
{button('Pass Truthtrance priority', { type: 'truthPass' })}
</>
)}