Skip to content
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
772362f
fix(engine): rehydrate the persisted RNG stream in the native restore…
lgray Aug 7, 2026
80df4c8
fix(server): drop the saved RNG stream position when restoring a session
lgray Aug 8, 2026
454ad71
fix(engine-wasm): re-seed the whole RNG identity triple for worker sc…
lgray Aug 8, 2026
492a9ad
fix(engine): refuse a persisted LoopShortcut offer that narrows its b…
lgray Aug 8, 2026
1a86789
chore(engine): re-baseline the GameState stack-budget ceiling on a fr…
lgray Aug 10, 2026
2b611b7
feat(engine): journal CR 603.5 "may" answers so a bounded shortcut ca…
lgray Aug 10, 2026
afa464f
feat(engine): journal CR 608.2b target answers on the same loop-answe…
lgray Aug 10, 2026
2993caf
feat(engine): publish the bounded shortcut's own declaration on the o…
lgray Aug 11, 2026
f6e67d1
fix(engine): validate the published declaration through one authority
lgray Aug 11, 2026
bb3f794
docs(engine): cite loop-shortcut seams by symbol instead of rotting l…
lgray Aug 10, 2026
f292805
feat(engine): publish what a loop-shortcut's declared count actually …
lgray Aug 10, 2026
393cd44
fix(engine): adapt the C3/C4 stack to the published declaration field
lgray Aug 11, 2026
f0762a6
fix(engine): close six review findings on the loop-shortcut preview
lgray Aug 11, 2026
9174c29
fix(engine): close four review follow-ups on the loop-shortcut preview
lgray Aug 11, 2026
ffbd3da
fix(engine): redact a declared template on every carrier, not just th…
lgray Aug 11, 2026
6dd5c80
fix(engine): discriminate the pin-level redaction axis; walk the whol…
lgray Aug 12, 2026
7eaf8a0
feat(engine): parameterize a target schedule's subject
lgray Aug 12, 2026
d0a7084
feat(engine): give a TARGET-class seat pin its own spelling
lgray Aug 12, 2026
5f23a52
test(engine): describe the seat-pin census by what it actually asserts
lgray Aug 12, 2026
4ab31f6
test(engine): rewrite the census comment from the probe table
lgray Aug 12, 2026
5b5e931
test(engine): correct two overstated clauses in the census comment
lgray Aug 12, 2026
bc04573
fix(engine): ask the slot question through the named accessor at the …
lgray Aug 12, 2026
58c509e
docs(engine): correct two CR bearings in decision_template
lgray Aug 12, 2026
62f9007
test(engine): pin the loop-shortcut episode boundary
lgray Aug 13, 2026
daad5bd
fix(engine-tests): replace hand-rolled carrier scan with syn; pin the…
lgray Aug 13, 2026
db70af4
fix(engine-tests): correct the abort-entry PROBE-PINNED clause and re…
lgray Aug 13, 2026
60f70b3
test(engine): pin the offer-writer census header with a probe-pin man…
lgray Aug 13, 2026
59101ad
fix(engine-tests): stop a comment counting itself, and adjudicate the…
lgray Aug 13, 2026
d40f701
fix(engine-wasm): stop a native test aborting the process, and correc…
lgray Aug 13, 2026
1e6fb08
fix(engine-tests): re-derive the census coordinate for upstream #4155
lgray Aug 13, 2026
512694b
fix(engine-tests): make the censuses measure code, not prose
lgray Aug 13, 2026
3fab647
feat(client): let the player choose a bounded loop-shortcut's count
lgray Aug 13, 2026
2f32507
refactor(engine-tests): one authority for "which part of this line is…
lgray Aug 13, 2026
decef86
fix(engine): resolve template-free shortcut declarations against the …
lgray Aug 13, 2026
2e8e3ae
test(engine): re-derive the CR 603.5 prompt census coordinate after C2
lgray Aug 13, 2026
2cedd93
feat(protocol): bump both wire contracts for the loop-shortcut surfac…
lgray Aug 13, 2026
6650d1b
fix: repair four comments that claimed mechanisms nothing enforced
lgray Aug 13, 2026
7a1510a
fix(lobby-broker): repair the v31 changelog twin left behind by its pair
lgray Aug 14, 2026
cf239c9
fix(client): scope the loop-shortcut picker to the engine-issued offer
lgray Aug 14, 2026
5d4c680
test(engine): re-derive the CR 603.5 prompt census coordinate after #…
lgray Aug 14, 2026
67f0412
test(engine): correct a false universal about this file's board loaders
lgray Aug 14, 2026
0e4e223
test(engine): adjudicate the WaitingFor reach-guard for #7382's new v…
lgray Aug 14, 2026
90ba04a
Merge branch 'main' into item4/bounded-loop-shortcut
lgray Aug 14, 2026
09ed812
fix(PR-7375): add Polish preview plural forms
matthewevans Aug 14, 2026
beff50c
fix(PR-7375): restore locale key parity
matthewevans Aug 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
248 changes: 236 additions & 12 deletions Tiltfile

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion client/src/adapter/generated/interaction/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ export type InteractionDamageAssignmentMode = "normal" | "asThoughUnblocked";

export type InteractionShortcutCountSpec = { "type": "fixed", "data": { min: number, max: number, suggested: number, } } | { "type": "untilLethal" };

export type InteractionShortcutPreviewFamily = "mana" | "life" | "damage" | "mill" | "counters" | "tokens" | "cards" | "casts" | "combats" | "turns" | "triggers";

export type InteractionShortcutPreviewEntry = { family: InteractionShortcutPreviewFamily, player: number | null, amount: number, };

export type InteractionShortcutPreview = { count: number, entries: Array<InteractionShortcutPreviewEntry>, };

export type InteractionShortcutPointKind = "targets" | "convokeTaps" | "mode" | "mayChoice" | "unlessBreak" | "manaColor";

export type InteractionShortcutPoint = { group: number, kind: InteractionShortcutPointKind, min: number, max: number, unique: boolean, ordered: boolean, readOnly: boolean, candidateIds: Array<InteractionChoiceId>, };
Expand All @@ -93,7 +99,7 @@ export type InteractionShortcutDecision = { "type": "decline" } | { "type": "acc

export type InteractionShortcutReply = { "type": "accept" } | { "type": "shorten", "data": { atIteration: number, } };

export type InteractionResponseSpec = { "type": "select", "data": { constraint: SelectionConstraint, confirm: ConfirmSemantics, } } | { "type": "assignAmounts", "data": { minTotal: number, maxTotal: number, exactTotal: number | null, } } | { "type": "assignDamage", "data": { total: number, modes: Array<InteractionDamageAssignmentMode>, confirm: ConfirmSemantics, } } | { "type": "sequence", "data": { min: number, max: number, unique: boolean, includeAll: boolean, engineValidated: boolean, escape: InteractionChoiceId | null, confirm: ConfirmSemantics, } } | { "type": "groupedSequence", "data": { groups: Array<InteractionGroupConstraint>, unique: boolean, confirm: ConfirmSemantics, } } | { "type": "manaGroups", "data": { groups: Array<InteractionGroupConstraint>, maxBatch: number, escape: InteractionChoiceId | null, confirm: ConfirmSemantics, } } | { "type": "text", "data": { allowArbitrary: boolean, maxLen: number, confirm: ConfirmSemantics, } } | { "type": "deckPartition", "data": { minMainTotal: number, maxMainTotal: number, confirm: ConfirmSemantics, } } | { "type": "relations", "data": { edges: Array<InteractionRelationConstraint>, min: number, max: number, sourceConstraint: InteractionRelationSourceConstraint, allowGroups: boolean, confirm: ConfirmSemantics, } } | { "type": "number", "data": { min: number, max: number, confirm: ConfirmSemantics, } } | { "type": "shortcut", "data": { count: InteractionShortcutCountSpec, points: Array<InteractionShortcutPoint>, allowDecline: boolean, confirm: ConfirmSemantics, } } | { "type": "shortcutReply", "data": { minIteration: number, maxIteration: number, confirm: ConfirmSemantics, } };
export type InteractionResponseSpec = { "type": "select", "data": { constraint: SelectionConstraint, confirm: ConfirmSemantics, } } | { "type": "assignAmounts", "data": { minTotal: number, maxTotal: number, exactTotal: number | null, } } | { "type": "assignDamage", "data": { total: number, modes: Array<InteractionDamageAssignmentMode>, confirm: ConfirmSemantics, } } | { "type": "sequence", "data": { min: number, max: number, unique: boolean, includeAll: boolean, engineValidated: boolean, escape: InteractionChoiceId | null, confirm: ConfirmSemantics, } } | { "type": "groupedSequence", "data": { groups: Array<InteractionGroupConstraint>, unique: boolean, confirm: ConfirmSemantics, } } | { "type": "manaGroups", "data": { groups: Array<InteractionGroupConstraint>, maxBatch: number, escape: InteractionChoiceId | null, confirm: ConfirmSemantics, } } | { "type": "text", "data": { allowArbitrary: boolean, maxLen: number, confirm: ConfirmSemantics, } } | { "type": "deckPartition", "data": { minMainTotal: number, maxMainTotal: number, confirm: ConfirmSemantics, } } | { "type": "relations", "data": { edges: Array<InteractionRelationConstraint>, min: number, max: number, sourceConstraint: InteractionRelationSourceConstraint, allowGroups: boolean, confirm: ConfirmSemantics, } } | { "type": "number", "data": { min: number, max: number, confirm: ConfirmSemantics, } } | { "type": "shortcut", "data": { count: InteractionShortcutCountSpec, points: Array<InteractionShortcutPoint>, allowDecline: boolean, preview: InteractionShortcutPreview | null, confirm: ConfirmSemantics, } } | { "type": "shortcutReply", "data": { minIteration: number, maxIteration: number, confirm: ConfirmSemantics, } };

export type InteractionOpportunityResponse = { "type": "exactChoices", "data": { choices: Array<InteractionChoice>, } } | { "type": "schema", "data": { spec: InteractionResponseSpec, candidates: Array<InteractionChoice>, } };

Expand Down
20 changes: 19 additions & 1 deletion client/src/adapter/ws-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,24 @@ export class NativeEngineVersionMismatchError extends Error {
* `crates/server-core/src/protocol.rs`. Bump in lockstep when either side
* adds, removes, renames, or changes the type of a protocol variant field.
*
* 31 β€” WaitingFor::LoopShortcut publishes the engine-issued declaration, and
* InteractionResponseSpec::Shortcut publishes preview, the per-axis
* consequence of the offered count. Both are optional and neither type
* sets deny_unknown_fields, so a v30 peer still PARSES the frame β€” a
* capability bump like 24, not a parse bump. UNLIKE 24, no pairing is left
* for the capability gap to bite in, so this entry names no silent-drop
* hazard: full-game floors are exact-match on BOTH sides
* (MIN_SUPPORTED_SERVER_PROTOCOL below, and MIN_SUPPORTED_PROTOCOL in
* crates/server-core/src/protocol.rs, each equal to their own
* PROTOCOL_VERSION), so a v31/v30 full-game pair is refused at the
* handshake and never sends an action frame. The one-version window that
* does exist is lobby-only (LOBBY_MIN_SUPPORTED_SERVER_PROTOCOL below /
* MIN_SUPPORTED_PROTOCOL in crates/lobby-broker/src/protocol.rs) and it
* cannot carry this capability either: DeclareShortcut rides
* ClientMessage::Action, which LobbyClientMessage has no variant for at
* all, and which reject_if_disabled in crates/phase-server/src/main.rs
* answers under ServerMode::LobbyOnly with an explicit rejection rather
* than a silent drop.
* 30 β€” Serialized player-action completion provenance and modal continuations.
* 29 β€” Added requester-correlated ResolveAllRejected response frames.
* 28 β€” Added native ResolveAll request/result frames.
Expand Down Expand Up @@ -237,7 +255,7 @@ export class NativeEngineVersionMismatchError extends Error {
* into a MulliganDecisionPhase::BottomCards sub-phase on
* WaitingFor::MulliganDecision.
*/
export const PROTOCOL_VERSION = 30;
export const PROTOCOL_VERSION = 31;

/**
* Lowest server protocol version this client will accept in the handshake.
Expand Down
5 changes: 4 additions & 1 deletion client/src/components/hud/HudBadges.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,10 @@ const UNBOUNDED_FAMILY_GLYPH: Record<UnboundedFamily, string> = {
triggers: "✴",
};

const UNBOUNDED_FAMILY_LABEL_KEY: Record<UnboundedFamily, string> = {
/** Exported for `LoopShortcutModal`'s preview lines: the engine's
* `InteractionShortcutPreviewFamily` is the same 11 literals as `UnboundedFamily`, so the preview
* reuses these labels instead of minting a parallel 11-key catalog in 7 locales. */
export const UNBOUNDED_FAMILY_LABEL_KEY: Record<UnboundedFamily, string> = {
mana: "badges.unboundedMana",
life: "badges.unboundedLife",
damage: "badges.unboundedDamage",
Expand Down
Loading
Loading