PURPOSE

Stub module that will translate raw reward-producing events (run end, mission collect, pull, chest open) into structured RewardBatch payloads consumable by the reveal/collect UI pipeline. All four describer functions are currently scaffolds returning empty batches with deterministic IDs; no actual diffing or reward extraction is implemented yet.

OWNS

  • WalletSnapshot interface — credits, gems, pullTickets shape used as before/after input.
  • OwnershipSnapshot interface — wraps ownedShipIds: Set<string> for ownership-aware describers.
  • MissionCollectReceipt interface — mission identifier plus list of { type, amount } reward entries.
  • RunRewardsSummary interface — single-field credits summary for arcade runs.
  • Module-local _batchCounter and nextBatchId(prefix) helper producing ${prefix}_${n} unique IDs.
  • Module-local emptyBatch(source, batchId) factory producing a RewardBatch with default presentation and empty counters, progress, milestones arrays.
  • Four exported describer functions: describeRunRewards, describeMissionRewards, describePullRewards, describeChestRewards.

READS FROM

  • ../data/reward-typesRewardBatch type (output shape).
  • ./pullServicePullResponse type (pull describer input).
  • ../data/reward-cardsResolvedReward type (chest describer input).
  • Currently the function bodies do not read any of the before/after snapshots or receipts — all parameters are prefixed with _ and discarded.

PUSHES TO

  • Returns RewardBatch values to callers. No state mutation outside the in-module _batchCounter.
  • Default presentation set to { revealMode: 'default', collectMode: 'default', resolvePolicy: 'immediate' }.
  • source field is set per describer: 'arcade_run', 'mission_collect', 'pull', and the caller-supplied 'journey_chest'.

DOES NOT

  • Does not diff wallet before/after to compute deltas.
  • Does not enumerate granted items, currencies, or unlocks into the counters, progress, or milestones arrays.
  • Does not consult ownership snapshots to detect first-time ship unlocks.
  • Does not persist, telemetry-log, or dispatch the produced batches.
  • Does not validate input snapshots or receipts.
  • Does not reset _batchCounter between sessions — counter is monotonic per module load.

Signals

  • File header /** STUB — not yet implemented */.
  • All function parameters except runId, txnId, chestId, and source are leading-underscore prefixed, marking them as intentionally unused.
  • emptyBatch always returns the same empty-collection shape regardless of source.

Entry points

  • describeRunRewards(before, after, rewards, runId) — called at arcade run completion; returns a batch tagged arcade_run with id run_${runId}_${n}.
  • describeMissionRewards(before, after, receipt) — called on mission collect; returns a batch tagged mission_collect with id mission_${n}.
  • describePullRewards(before, pullResponse, txnId) — called after a gacha pull resolves; returns a batch tagged pull with id pull_${txnId}_${n}.
  • describeChestRewards(before, afterWallet, chestRewards, chestId, source) — called when a chest opens; source is constrained to the literal 'journey_chest'; id is chest_${chestId}_${n}.

Pattern notes

  • Batch IDs combine a domain prefix, an optional caller-supplied identifier (run/txn/chest), and a monotonic counter — guarantees uniqueness within a process even if callers reuse identifiers.
  • The four-function split mirrors the four distinct reward sources rather than a single polymorphic describer, anticipating per-source diffing logic.
  • Snapshot inputs are typed as { wallet, ownership? } objects rather than flat parameter lists, leaving room to add fields (e.g., inventory, passives) without breaking call sites.
  • Chest describer accepts already-resolved rewards (ResolvedReward[]) rather than raw chest IDs — resolution happens upstream.
  • Run and pull describers receive a single after-state surface (wallet only for run; the PullResponse itself for pull), reflecting that those flows produce their reward list elsewhere.
  • _batchCounter is module-scoped, not exported — there is no reset hook.