From dc24bdeb7a8ff3be1b40ea2d19bf3771cc6fa22b Mon Sep 17 00:00:00 2001 From: fireshaper Date: Sun, 4 Oct 2026 12:15:39 -0400 Subject: [PATCH] Step 10F.2: board preview and open slot config Co-Authored-By: Claude Opus 5.5 --- memory-bank/progress.md | 1 + packages/content/src/data/config.ts | 4 ++++ packages/content/src/schemas.ts | 7 +++++++ packages/content/test/schemas.test.ts | 16 ++++++++++++++++ 4 files changed, 28 insertions(+) diff --git a/memory-bank/progress.md b/memory-bank/progress.md index a9909ec..1f68e60 100644 --- a/memory-bank/progress.md +++ b/memory-bank/progress.md @@ -194,3 +194,4 @@ Spec: `memory-bank/change-requests/CR-005-ui-overhaul.md`. New decisions recorde Spec: `memory-bank/change-requests/CR-006-boards-that-fit.md`. New decisions recorded as plan ยง0.5.52โ€“0.5.53. - [x] **Step 10F.1 โ€” Client: weight warning.** `carryLimit(state, locationId)` (`LocationBoards.tsx`): the largest capacity among the player's ships at the location, else the whole fleet, else null. Contract cards show the mass in rose and a rose "Too heavy" pill when it is over that limit. Test: `weight-warning.test.tsx`. +- [x] **Step 10F.2 โ€” Content: board config.** GameConfig gains `boardPreviewSlots` (1) and `boardOpenSlotsPerBand` (1), non-negative integers. Test: `schemas.test.ts` (missing-field coverage plus a negative/fractional rejection test). diff --git a/packages/content/src/data/config.ts b/packages/content/src/data/config.ts index 7df7068..9e617eb 100644 --- a/packages/content/src/data/config.ts +++ b/packages/content/src/data/config.ts @@ -39,6 +39,10 @@ export const gameConfig: GameConfig = { { min: 21, max: 100, count: 3 }, { min: 101, max: 400, count: 2 }, ], + // Boards that fit the fleet (CR-006): a band no ship can carry keeps one + // preview job; flyable bands keep one open job, the rest fit a ship and pilot. + boardPreviewSlots: 1, + boardOpenSlotsPerBand: 1, // Mars at its closest needs no Long Haul; Jupiter at its farthest needs level 5. longHaulCutoffsMillionKm: [100, 250, 400, 600, 800], xpPerDelivery: { base: 10, perMillionKm: 0.2, perCargoTier: 5 }, diff --git a/packages/content/src/schemas.ts b/packages/content/src/schemas.ts index 0cd583d..a689ed0 100644 --- a/packages/content/src/schemas.ts +++ b/packages/content/src/schemas.ts @@ -158,6 +158,13 @@ export const gameConfigSchema = z .refine((band) => band.min <= band.max, { message: "min must not exceed max" }), ) .min(1), + /** + * Contracts kept in a mass band the player's fleet can't carry: a preview + * of bigger ships' work (decision 0.5.52). The rest move to flyable bands. + */ + boardPreviewSlots: nonNegativeInt, + /** Contracts per flyable band left open (skill-gated, first contact) rather than fit (0.5.52). */ + boardOpenSlotsPerBand: nonNegativeInt, /** One cut-off per Long Haul level (decision 0.5.7). */ longHaulCutoffsMillionKm: increasingList, xpPerDelivery: z.object({ diff --git a/packages/content/test/schemas.test.ts b/packages/content/test/schemas.test.ts index 2b9c8cd..8705b92 100644 --- a/packages/content/test/schemas.test.ts +++ b/packages/content/test/schemas.test.ts @@ -72,6 +72,8 @@ const validGameConfig = { { min: 1, max: 20, count: 4 }, { min: 21, max: 100, count: 4 }, ], + boardPreviewSlots: 1, + boardOpenSlotsPerBand: 1, longHaulCutoffsMillionKm: [100, 200, 300, 400, 500], xpPerDelivery: { base: 10, perMillionKm: 0.1, perCargoTier: 5 }, pilotLevelXpThresholds: [100, 250, 500], @@ -151,6 +153,20 @@ describe("GameConfig refinements", () => { expect(gameConfigSchema.safeParse(config).success).toBe(false); }); + it("rejects negative or fractional board slot counts (CR-006)", () => { + for (const bad of [-1, 0.5]) { + expect( + gameConfigSchema.safeParse({ ...validGameConfig, boardPreviewSlots: bad }).success, + ).toBe(false); + expect( + gameConfigSchema.safeParse({ ...validGameConfig, boardOpenSlotsPerBand: bad }).success, + ).toBe(false); + } + expect( + gameConfigSchema.safeParse({ ...validGameConfig, boardOpenSlotsPerBand: 0 }).success, + ).toBe(true); + }); + it("rejects Long Haul cut-offs that are not increasing", () => { const config = { ...validGameConfig, longHaulCutoffsMillionKm: [100, 100, 300] }; expect(gameConfigSchema.safeParse(config).success).toBe(false);