Area
Public API
Motivation / use case
I am developing Shilvia.ProgressBridge, a read-only progression comparison Mod targeting Slay the Spire 2 v0.107.1 / progress schema 21.
A future opt-in phase needs to conditionally commit a complete raw progress.save JSON document while preserving properties unknown to the consumer and using the save store already owned by the running game.
The currently available paths do not provide that boundary:
ProgressSaveManager.SaveProgress reaches the live store but reserializes the typed DTO, so unknown JSON properties cannot be guaranteed to survive.
- direct local-file replacement can preserve the document but bypasses the live
CloudSaveStore and save-batch coordination.
- exposing or reflecting into a private save-store field would not be a supported consumer contract.
- a generation check followed by an unlocked commit leaves a race with ordinary game saves.
Progress Bridge remains read-only unless every required safety capability is explicitly available.
Proposed solution
Would RitsuLib consider a narrow, versioned service such as IRawProgressCommitBridge?
The service would not return ISaveStore / CloudSaveStore and would not accept arbitrary paths. It would expose:
- A descriptor with protocol version, provider version, supported progress schemas, connection to the live game-owned store, and explicit feature flags.
- One active-profile-only conditional raw-progress commit operation.
- A request containing:
- active profile identity and vanilla/modded environment;
- schema number;
- expected local fingerprint, length, and last-modified value;
- expected Cloud-enabled/persisted state and observed remote generation;
- proposed raw JSON, fingerprint, and byte length;
- caller-generated transaction ID.
- Structured outcomes for generation conflict, profile change, validation failure, local replacement failure, Cloud failure with local data preserved, and recovery-required states.
For protocol 1, the provider would own this ordering:
- Validate request shape, schema, active profile, raw JSON, and proposed fingerprint.
- Acquire an exclusive save window shared with every save operation capable of changing active progress.
- Re-read local and Cloud generation through the live store.
- Return
GenerationConflict without transaction artifacts on mismatch.
- Durably create recovery metadata that is local-only and outside Cloud-managed profile deletion.
- Begin the live Cloud save batch.
- Commit the caller's validated raw document through the live store, retaining official backup/temp/flush behavior.
- Verify the destination fingerprint and record the recoverable state.
- End the Cloud batch in
finally.
- Complete or retain the journal according to the verified outcome, then release the exclusive window.
Unknown properties must be preserved by committing the validated raw payload rather than serializing a SerializableProgress DTO.
A key feasibility question is whether RitsuLib can provide an exclusive window that also covers ordinary game progress saves without Harmony patches. A Mod-only lock would not close the compare/commit race and should not advertise that capability. If this is not possible with the current game API, confirming that limitation would still be very helpful.
Alternatives considered
- Typed
ProgressSaveManager.SaveProgress: rejected because it cannot guarantee unknown-property preservation.
- Direct profile-file replacement: rejected because it bypasses the live game-owned Cloud store and batch.
- Reflecting into RitsuLib/game internals: rejected because it is not a stable public contract.
- BaseLib: its current public API does not expose an equivalent raw progress persistence bridge.
- Generic recursive JSON merge: rejected because progression fields and Epoch prerequisites require versioned, field-specific semantics.
Additional context
The consumer-side design currently has:
- an exact protocol/schema/feature handshake that fails closed;
- pure field-specific merge and official-deserializer validation;
- destination/Cloud generation guards;
- a pure recovery journal/state machine;
- temporary-directory-only physical fault tests;
- 74 passing synthetic tests;
- no runtime writer and no profile write trial.
I can provide the proposed DTO/result shapes and consumer-side contract tests if this API is feasible. No real profile data is needed.
Area
Public API
Motivation / use case
I am developing Shilvia.ProgressBridge, a read-only progression comparison Mod targeting Slay the Spire 2 v0.107.1 / progress schema 21.
A future opt-in phase needs to conditionally commit a complete raw
progress.saveJSON document while preserving properties unknown to the consumer and using the save store already owned by the running game.The currently available paths do not provide that boundary:
ProgressSaveManager.SaveProgressreaches the live store but reserializes the typed DTO, so unknown JSON properties cannot be guaranteed to survive.CloudSaveStoreand save-batch coordination.Progress Bridge remains read-only unless every required safety capability is explicitly available.
Proposed solution
Would RitsuLib consider a narrow, versioned service such as
IRawProgressCommitBridge?The service would not return
ISaveStore/CloudSaveStoreand would not accept arbitrary paths. It would expose:For protocol 1, the provider would own this ordering:
GenerationConflictwithout transaction artifacts on mismatch.finally.Unknown properties must be preserved by committing the validated raw payload rather than serializing a
SerializableProgressDTO.A key feasibility question is whether RitsuLib can provide an exclusive window that also covers ordinary game progress saves without Harmony patches. A Mod-only lock would not close the compare/commit race and should not advertise that capability. If this is not possible with the current game API, confirming that limitation would still be very helpful.
Alternatives considered
ProgressSaveManager.SaveProgress: rejected because it cannot guarantee unknown-property preservation.Additional context
The consumer-side design currently has:
I can provide the proposed DTO/result shapes and consumer-side contract tests if this API is feasible. No real profile data is needed.