feat(mapgen-studio): migrate authoring/run state to Zustand persist stores, reads to TanStack Query, and fix runInGame contract selectedConfig.id optional#1614
Merged
Conversation
This was referenced Jun 12, 2026
refactor(studio): decompose App.tsx non-React corpus into feature modules and shared utilities
#1608
Merged
Merged
Owner
Author
This was referenced Jun 12, 2026
Merged
|
Railway preview (MapGen Studio): not provisioned for this PR. Policy (Graphite stacks): previews are created only for the top-of-stack PR by default.
Debug: |
This was referenced Jun 12, 2026
This was referenced Jun 12, 2026
Merged
mateicanavra
force-pushed
the
design/craft-a11y
branch
from
June 12, 2026 20:44
6b6dd12 to
ffb68cf
Compare
mateicanavra
force-pushed
the
design/data-model
branch
from
June 12, 2026 20:44
3350c75 to
89b15ea
Compare
Owner
Author
Merge activity
|
mateicanavra
changed the base branch from
design/craft-a11y
to
graphite-base/1614
June 12, 2026 21:08
Close the highest-stakes type hole on the assertNoRawControlFields-protected
run-in-game start path: the contract declared selectedConfig.id as required
(z.string()) while parseRunInGameSetupRequest + the engine read it defensively
(optional, defaulting to "studio-current" for disposable runs). Make id
optional to match the validator, then drop the
`as unknown as Parameters<typeof orpcClient.runInGame.start>[0]` cast in
features/runInGame/api.ts so the assembled request is fully input-typed end to
end. No behavior change: omitting selectedConfig when absent is wire-identical
to the prior explicit-undefined (server reads `selectedConfig ?? {}`).
Also lands the OpenSpec change scaffold (proposal/tasks/design/spec) for the
data-model slice.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…thoring/run stores
Complete the data model deferred from the client-data slice (architecture/10 §2–§3):
Reads → oRPC-native TanStack Query:
- useSetupDataQueries: savedConfigs + setupCatalog now load via
orpc.civ7.<proc>.queryOptions() into useQuery; retry + refetch-on-focus come from
the query client defaults (replacing the hand-rolled load/retry/focus effect). The
derived { status, directory, configurations, updatedAt, error } / { status, catalog,
updatedAt, error } view shapes are unchanged.
- useOperationStatusPolls: run-in-game + save-deploy status polling moves from
self-rescheduling setTimeout effects to useQuery refetchInterval polls keyed on the
active runStore request id. Cadence (document.hidden ? 3000 : 1000), terminal/non-
running stop, and the 404 → synthetic uncertain / operation-status-missing mapping
are preserved verbatim (the queryFn invokes the existing refresh* callbacks).
initialData keeps the seeded operation fresh for one interval so there is no extra
immediate mount fetch. The live status/snapshot poll + its inlined setupConfig read
stay imperative (hard core).
Persisted Zustand stores (localStorage schema is hard-core parity — copied, not fixed):
- authoringStore (persist): owns worldSettings/recipeSettings/setupConfig/
pipelineConfig/overridesDisabled/repoBackedPresetOverridesByRecipe. Its persist
storage adapter delegates to the reference impl (loadStudioAuthoringState /
saveStudioAuthoringState, STUDIO_AUTHORING_STATE_KEY), so the on-disk schema is
byte-identical; the manual save effect is removed.
- runStore (persist): owns the run/save correlation bridge. Its fan-out storage adapter
maps the persisted slice onto the SAME four legacy keys (RUN_IN_GAME_LAST_REQUEST/
SNAPSHOT/SOURCE + MAP_CONFIG_SAVE_LAST_REQUEST) with the SAME serializers; lastRun
Snapshot/lastSaveDeployConfig stay session-only (not persisted), as before.
StudioShell drops the corresponding useState + manual persistence/localStorage effects
for store selectors. No query results are mirrored into Zustand.
Verified: tsc clean (app + studio-server), build + worker-bundle check, 138/138 tests
(incl. AppFooter). Runtime: zero /api calls; savedConfigs/setupCatalog + run/save status
poll over /rpc; status polls fire once then stop on 404 (no retry storm); live poll
verbatim; localStorage round-trips the same authoring + four run/save keys byte-for-byte;
zero console errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mateicanavra
force-pushed
the
design/data-model
branch
from
June 12, 2026 21:10
89b15ea to
89c8be0
Compare
This was referenced Jun 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Migrates
StudioShell's scattered state and data-fetching to a coherent architecture: persisted Zustand stores, oRPC-native TanStack Query reads, and a contract type-hole fix.Persisted Zustand stores
Introduces
authoringStoreandrunStoreto replace the six authoringuseStates and the run/save correlationuseStates inStudioShell, along with the manualsaveStudioAuthoringStatepersistence effect and the scatteredlocalStorage.setItemcalls. Both stores delegate to the existing serializers and localStorage keys verbatim —STUDIO_AUTHORING_STATE_KEYwith itsschemaVersion:1/savedAtenvelope for authoring, and the fourRUN_IN_GAME_LAST_*/MAP_CONFIG_SAVE_LAST_REQUEST_KEYkeys for run state — so the on-disk schema is byte-identical before and after.oRPC-native TanStack Query reads
Replaces the hand-rolled load/retry/focus-refetch effect for saved setup configs and the setup catalog with
useSetupDataQueries, which usesorpc.civ7.savedConfigs.queryOptions()andorpc.civ7.setupCatalog.queryOptions()intouseQuery. The derived view shapes consumed bysetupControlOptionsare unchanged; retry-on-failure and refetch-on-window-focus are provided by the shared query client defaults.Replaces the self-rescheduling
setTimeoutstatus-poll effects for run-in-game and save-deploy withuseOperationStatusPolls, which usesrefetchInterval-drivenuseQuerys keyed on the active request id (sourced fromrunStore). The adaptivedocument.hidden ? 3000 : 1000ms cadence, terminal-stop predicate, and 404 → syntheticuncertain/operation-status-missingmapping are all preserved. The terminal run-in-game toast is kept as a derived-state effect inside the same hook. The live-runtime status/snapshot poll remains imperative (its request-key staleness gate and adaptive backoff are not migrated).Contract fix
Changes
selectedConfig.idin therunInGame.startcontract fromz.string()toz.string().optional(), aligning it withparseRunInGameSetupRequest, which already readsselected.iddefensively. This removes theas unknown as Parameters<typeof orpcClient.runInGame.start>[0]cast infeatures/runInGame/api.tsand restores full input type checking on theassertNoRawControlFields-protected start path.