# Reward decision API audit Status: offline source audit and proposed follow-up. No runtime verification, policy change, or approved API schema. Audited after commit `baf0d529`. The installed game and mod may differ from the vendored source. ## Findings | Question | Source evidence | Conclusion | | --- | --- | --- | | Does ordinary state include the permanent deck? | `vendor/STS2MCP/McpMod.StateBuilder.cs`, `BuildPlayerState` | No. It exposes hand and combat piles only while combat is active. The earlier comment claiming deck inclusion is misleading. | | Does compendium expose the current deck? | `vendor/STS2MCP/McpMod.Compendium.cs`, `BuildCurrentRunContext` | No. It reads selected save metadata and run identity, not current deck cards. Historical `deck_count` is not a current deck. | | Can wiki reconstruct deck ownership? | `vendor/STS2MCP/McpMod.Wiki.cs`, `BuildWikiSearch` | No. Search covers profile-discovered card/relic definitions, not owned instances. | | What identifies offered cards? | `BuildCardRewardState`, `BuildCardInfo` | Observation-local index, model ID, name, type, cost, star cost, description, rarity, upgrade flag, keywords. Model ID is not an instance ID. | | What does `can_skip` prove? | `BuildCardRewardState` | At least one `NCardRewardAlternativeButton` was found. It does not report label, effect, visibility, or enabled state. | | What does skipping execute? | `vendor/STS2MCP/McpMod.Actions.cs`, `ExecuteSkipCardReward` | `altButtons[0].ForceClick()`. The action does not identify or select among different alternatives. | | Does success prove the resulting deck? | `ExecuteSelectCardReward`, `ExecuteSkipCardReward` | No. These return after sending a UI signal/click. A subsequent observation is required. | The source does not establish which alternative types exist in the installed game, or whether a particular skip has resource/relic effects. Do not infer those effects from an action named `skip_card_reward`. Do not infer a side effect exists either. ## Limits of the current bot `run_state.RunContext.capture_combat` stores name counts and a summary, with `persistent_deck: false`. Generated cards and temporary upgrades can remain in that evidence. Draw-pile display order is sorted, not future draw order. `RunContext.observe` invalidates evidence on missing run identity or a room change. `record_result` preserves evidence when opening a card reward or skipping it. Other accepted noncombat actions clear evidence. Thus taking another reward, including gold, can remove the limited deck context before card selection. This is conservative behavior, not proof that the deck changed. The composite policy makes no model call without scoped card evidence. With valid scores but no clear addition, it selects the best-ranked card as a fallback. It does not skip. A harmful addition remains possible. This is not a complete reward policy and is not ready to replace the default. Skip-memory handling also assumes reward skipping can preserve card evidence. Revisit that assumption when alternative effects become available; do not reuse old evidence if an accepted alternative can change the deck. ## Recommended next implementation: observation support first Prefer an in-memory permanent-deck snapshot in the normal state response. Do not add a second AI agent, reconstruct ownership from wiki results, or quietly promote combat piles into an exact deck. A save-file parser would require separate freshness and identity reconciliation and is not the first choice. The following is a proposed contract, not an existing API: 1. Expose every permanent card instance, including duplicate copies and permanent upgrades/modifications. Preserve existing card display fields. Report unavailable data explicitly; distinguish an empty deck from a failed read. 2. Include deck source and completeness. Read it with the ordinary observation. Do not claim atomic run identity while identity still comes from a separate read. 3. Expose each reward alternative's observation-local action identifier, visible label/description, enabled state, and known effect metadata. Unknown effects stay unknown. A label alone cannot certify a side-effect-free skip. 4. Provide a matching action that targets the selected alternative. Keep indices local to one observation, and reject absent or disabled targets. 5. Establish the actual game behavior for plain skips and reward-triggered effects before assigning a zero baseline. Verify the game model API against the installed version; this audit does not establish a permanent-deck member name. Implement these changes in the mod's maintained source first, then update the vendored dependency deliberately. Do not modify the vendor snapshot merely to simulate fields that the installed API cannot provide. ## Bot integration after the observation contract - Prefer the reported permanent deck; retain labeled limited evidence for older APIs. - Build candidates for each card and each supported alternative. - Evaluate additions against the actual alternative, not an assumed free skip. - Keep arithmetic in `policy/scoring.py`; keep effect interpretation, evidence requirements, and acceptance rules in reward-specific code. - Treat inadequate evidence separately from neutral utility. Score confidence alone cannot establish that required deck or effect information exists. - Leave the current experimental weights fixed while changing context and options. ## Acceptance checks Use offline fixtures and model stubs before an authorized live verification: - Duplicate permanent cards survive serialization; temporary combat cards do not become permanent cards. Permanent upgrades/modifications remain distinguishable. - Missing deck data differs from a genuinely empty deck. - Old APIs retain explicit limitations; no invented completeness or instance IDs. - Plain skip, side-effecting alternatives, disabled alternatives, and multiple alternatives map to the correct action. Unknown effects do not receive zero value. - Harmful and neutral offers can lose to a verified plain skip. Forced selections still produce a legal card choice. - Accepted requests are reconciled with fresh state, including delayed transitions and changed indices. A successful click alone does not establish the outcome. No live game requests, model calls, save-file reads, or vendor changes were used for this audit. External strategy research cannot resolve these API semantics. ## Follow-up: separate mod prototype With user approval, a separate Jujutsu checkout was created at `/Users/rasyidanakbar/sts2-mcp`, based on upstream `55e064850a68f3b4cde7e5fd525bf9b2dec4e885`. The extension is published at `git@git.rasyidanaf.com:0xrsydn/sts2-mcp.git`, commit `6c4cc172794f7a7ca42b86f4122582f93321363e`. The bot now pins that revision as the `vendor/STS2MCP` submodule. See [setup instructions](../MOD_SETUP.md). Inspection of installed game assemblies confirmed `Player.Deck`, alternative option IDs, and the reward screen's option/button ordering. Standard alternative generation creates Skip and optionally REROLL, then invokes modification hooks. This does not certify the effects of every active Skip callback. The prototype exposes permanent deck membership, explicit alternative metadata, and an indexed `select_reward_alternative` action with identity and UI guards. Full structured modifier details and verified side-effect-free skip semantics remain unavailable. See the mod checkout's `docs/reward-observations.md`. The Release build passed with no warnings or errors. Nineteen isolated UI-stub checks passed against the new alternative source. The vendor checkout was later updated deliberately to the published submodule revision. Bot policy is unchanged. A subsequent authorized deployment installed the rebuilt DLL after backing up the previous DLL. Launching through Steam succeeded; the game log confirms mod loading and HTTP startup. A read-only GET returned the main menu. The menu has no player or reward data, so live permanent-deck and alternative serialization remain unverified. No action POSTs or model calls were sent. Deployment hash and rollback path are recorded in the mod checkout's `docs/reward-observations.md`. The next checkpoint requires entering a run and reaching a reward screen, then read-only inspection of the new fields before bot integration.