From 3ac0f3b5ff7d78f0d2493a2bd0a8294957ee7c66 Mon Sep 17 00:00:00 2001 From: Matei Date: Fri, 12 Jun 2026 14:29:28 -0400 Subject: [PATCH] docs(mapgen-studio): JSDoc the load-bearing seams + honest-boundary notes P7 idiom wave: engine-contract JSDoc (StudioEngines, the Run-in-Game orchestrator incl. the materialization-proof correctness boundary), the authored-setup-state contract on Civ7StudioSetupConfig, documented deliberate-opacity casts at the oRPC read seam (+ observedAt fallback parity), and a stale Game-bar comment fix. Deliberately NOT folding the per-feature oRPC error envelopes into one helper (distinct shapes; native oRPC error-handling refactor tracked separately). --- .../src/app/hooks/useSetupDataQueries.ts | 8 ++++++- .../src/features/civ7Setup/setupConfig.ts | 9 ++++++++ .../src/server/studio/engines.ts | 22 +++++++++++++++++++ .../src/ui/components/AppHeader.tsx | 4 ++-- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/apps/mapgen-studio/src/app/hooks/useSetupDataQueries.ts b/apps/mapgen-studio/src/app/hooks/useSetupDataQueries.ts index 2d0bbd7050..986815797b 100644 --- a/apps/mapgen-studio/src/app/hooks/useSetupDataQueries.ts +++ b/apps/mapgen-studio/src/app/hooks/useSetupDataQueries.ts @@ -67,6 +67,10 @@ export function useSetupDataQueries(): { return { status: "ok", directory: body.directory ?? "", + // The contract deliberately types `configurations` as opaque records: + // the saved-config file shape is owned by @civ7/direct-control + // (listCiv7SavedGameConfigurations), and re-declaring it in the oRPC + // contract would just drift. The cast states that trust explicitly. configurations: body.configurations as unknown as ReadonlyArray, updatedAt: body.observedAt ?? new Date().toISOString(), }; @@ -84,11 +88,13 @@ export function useSetupDataQueries(): { if (!body) { return { status: "idle" }; } + // Same deliberate-opacity trust as `configurations` above: the catalog + // shape is owned by the server's civ7Resources catalog builder. const catalog = body.catalog as unknown as Civ7SetupCatalog; return { status: "ok", catalog, - updatedAt: catalog.observedAt, + updatedAt: catalog.observedAt ?? new Date().toISOString(), }; }, [setupCatalogQuery.data, setupCatalogQuery.error, setupCatalogQuery.isError]); diff --git a/apps/mapgen-studio/src/features/civ7Setup/setupConfig.ts b/apps/mapgen-studio/src/features/civ7Setup/setupConfig.ts index 3b424e0a2e..5bf528aa73 100644 --- a/apps/mapgen-studio/src/features/civ7Setup/setupConfig.ts +++ b/apps/mapgen-studio/src/features/civ7Setup/setupConfig.ts @@ -12,6 +12,15 @@ export type Civ7StudioSavedConfigRef = Readonly<{ path: string; }>; +/** + * The authored game-setup state behind the header's Game bar — the single + * source for what launches in Civ7. At launch the engine loads + * `savedConfig` (if any) into Civ7 first and then re-applies EVERY key in + * `gameOptions`/`playerOptions` on top, so these maps must contain only + * values the user (or an exact saved-config application) authored: any + * extra key silently overrides the loaded file (see + * `studioSetupConfigFromSavedConfigFile` / `studioSetupDriftsFromSavedConfig`). + */ export type Civ7StudioSetupConfig = Readonly<{ savedConfig?: Civ7StudioSavedConfigRef; mapScript?: string; diff --git a/apps/mapgen-studio/src/server/studio/engines.ts b/apps/mapgen-studio/src/server/studio/engines.ts index 4789b8fcde..797157c908 100644 --- a/apps/mapgen-studio/src/server/studio/engines.ts +++ b/apps/mapgen-studio/src/server/studio/engines.ts @@ -433,6 +433,17 @@ export interface AutoplayEngineResult { type SaveDeployOperationStore = ReturnType; export type SaveDeployEngineResult = ReturnType; +/** + * The studio's stateful server engines — the one place long-running Civ7 + * operations (run-in-game, save/deploy, autoplay) execute and are tracked. + * + * Contract: engines serialize through a process-wide operation queue (one + * Civ7-mutating operation at a time), record progress in TTL-bounded + * operation stores keyed by request id, and throw `RunInGameHttpError` + * (legacy HTTP status + structured `details`) for every client-visible + * failure — transports map it 1:1, so error codes stay stable across the + * Vite middleware and Bun daemon mounts. + */ export interface StudioEngines { /** Process-lifetime identity — clients reconcile run-in-game state against it. */ readonly serverInstanceId: string; @@ -504,6 +515,17 @@ export function createStudioEngines(options: Readonly<{ repoRoot: string }>): St }; } + /** + * The Run-in-Game orchestrator: validate the request, materialize the map + * config + generated script, build-and-deploy the swooper mod, prove the + * deployed script carries this run's materialization markers (request id / + * config hash / envelope hash / native river markers — the correctness + * boundary: a launch must never run a stale script), restart or exit Civ7 + * to shell as needed, start the prepared single-player game, and wait for + * the in-game mapgen log proof. Progress is recorded phase-by-phase in the + * operation store under the request id; duplicate requests return the + * already-tracked operation instead of double-launching. + */ async function runRunInGameStartEngine( body: RunInGameStartEngineBody, ): Promise { diff --git a/apps/mapgen-studio/src/ui/components/AppHeader.tsx b/apps/mapgen-studio/src/ui/components/AppHeader.tsx index 6828720cf0..cbcc0f3c0d 100644 --- a/apps/mapgen-studio/src/ui/components/AppHeader.tsx +++ b/apps/mapgen-studio/src/ui/components/AppHeader.tsx @@ -46,8 +46,8 @@ export interface AppHeaderProps { onHeaderHeightChange?: (height: number) => void; /** * The live-game command cluster (Pass-5 zoning v2: top = Game, bottom = - * World/Map). Composed INLINE into the Game bar row, between the - * saved-config selector and the trailing setup disclosure. + * World/Map). Composed INLINE into the Game bar row, after the Config + * cluster (selector + setup gear, Z-wave Game bar v3). */ gameConsole?: React.ReactNode; }