Capture all successful state reads with hashes and session-local IDs. Record action intent before POST, retain accepted/rejected/unknown results, and link subsequent observations. Preserve legacy feeds and finalize each invocation synchronously.
5.8 KiB
Run identity and card-evidence provenance
What is available
The vendored mod exposes current_run.run_id through GET /api/v1/compendium.
Its format includes save scope, profile, and start time. The seed describes
content; it is not the identity of an attempt.
The live player serializer exposes hand, draw, discard, and exhaust piles during combat. It does not expose the persistent run deck. The compendium exposes a save path, but the runner does not follow that path or read the save file.
Sources checked:
vendor/STS2MCP/McpMod.Compendium.cs:BuildCurrentRunContext.vendor/STS2MCP/McpMod.StateBuilder.cs:BuildPlayerState.
Ownership and identity
run.py owns one run_state.RunContext per invocation. The context owns the
current PolicyContext and optional combat-pile evidence. The context performs
no network or file operations.
The runner requests identity after each stable in-run observation, before proposing an action. Menu, game-over, unknown, and overlay observations do not trigger this request. Dry-run still performs identity reads.
This deliberately uses the existing full compendium endpoint. It adds one local HTTP read per stable in-run step. A smaller identity endpoint would reduce cost, but this pass does not modify the mod or add a polling cache with stale identities. Live latency has not been measured.
A usable identity requires is_in_progress: true and a nonempty string run_id.
Missing or malformed identity remains unknown. The runner never substitutes a
seed, character name, or floor number as an identity.
- A different reported run ID resets policy memory, duplicate-action guards, rejection counts, and card evidence.
--stop-on-run-endstops before acting in the replacement run, with reasonrun_changed. This reports a run boundary, not victory or defeat.- Observed menu/game-over transitions clear run evidence and active-run policy memory.
- Unknown states and overlays preserve pending-action guards.
- Missing identity or a transport error discards card evidence but retains action guards. Erasing accepted toggles during a metadata outage could toggle them again.
- The last known ID is retained privately across a metadata outage, so recovery with a different ID can still reset state. Decision rows use null during the outage.
Identity is reported, not atomic
The mod derives identity from save metadata. The observation and compendium are separate requests. They are not an atomic snapshot, and save metadata can lag. The runner cannot prove that both responses describe exactly the same instant. A live observation-level run ID would provide a stronger association.
Do not infer exact attribution or game-action completion from this identity alone. Externally switching runs while the bot acts remains a coordination risk.
Card evidence
The runner no longer reads or writes deck.json. Existing files remain untouched.
No card evidence survives a runner restart. Unscoped legacy input to
facts.deck_context becomes unknown.
A combat-pile snapshot is retained only when a reported run ID is available. Its provenance includes:
source: combat_piles;- reported
run_id; observed_stepwithin the current session;- observed act and floor;
persistent_deck: false;- which pile lists were present;
- freshness:
observed_combat_pilesorhistorical_combat_piles.
The counts retain visible temporary cards, including Status cards. Filtering out Status cards would not reconstruct the persistent deck: generated Attack cards, temporary upgrades, and unavailable card data would still remain problems. Pile-list presence does not establish complete coverage.
Evidence can carry into immediate reward decisions in the same reported room. Opening a card reward or skipping it preserves that limited evidence. Other accepted non-combat actions discard it because they can change cards or leave the room. Rejected requests do not establish such a change.
A room change, missing room coordinates, identity failure, new reported run ID,
or observed run exit discards evidence. The policy receives unknown when no
scoped evidence remains. This reduces available synergy context intentionally;
it is safer than presenting stale combat piles as the current persistent deck.
An exact persistent-deck source is still unavailable. This pass labels that limitation; it does not implement a complete deck tracker or a save-file parser.
Minimal trace additions
Decision rows now include reported run_id, run_seed, and available
deck_provenance. The provenance describes available macro-policy evidence;
it does not mean every handler used it. Combat policy uses the combat observation directly.
Identity changes and identity transport failures have separate trace events. Linked session recordings now provide observation/action linkage, unique captures, per-invocation session identity, and synchronous finalization. The save-derived identity and historical outcome-association limits still apply.
Validation
- 279 existing-script assertions passed: 68 facts, 110 policy, and 101 runner. This includes two upgrade-selection assertions added concurrently.
- Runner integration flows cover same-seed run replacement, single-run stopping, metadata outages, temporary cards, provenance, mutation invalidation, room boundaries, unknown rooms, and restart/cache isolation.
- Eighteen temporary whole-process scenarios passed with real HTTP clients and loopback fixtures. Five exercise the new identity/provenance behavior.
- The offline audit replayed 346 stored observations without policy exceptions.
- Dataset integrity passed for 37 runs, 1,052 decisions, and 346 observations.
- Python compilation and shell syntax checks passed.
No live game actions, paid model calls, dependencies, vendor edits, or dataset rewrites were required. Temporary probes were not added to the repository.