Skip to content

refactor(core): extract drop-claim jobs and post-claim handoff #597

Description

@jamezrin

Part of #583. Depends on #584, #585, #593 and #599. Complements #590, which covers only Twitch channel points and its claim-only alarm coordination.

Problem

Claiming that is not channel points has no owner in the #583 plan:

  • runDropClaims(platform): the manual-watch drop-claim job for both Twitch and Kick (TWITCH_DROP_CLAIMS_ALARM_NAME, KICK_DROP_CLAIMS_ALARM_NAME), scheduled by reconcileManualWatchClaimAlarms and cancelled by clearManualWatchClaimAlarmsBestEffort, with dropClaimOperations and waitingClaimRewardIds.
  • runClaimHandoff / claimHandoffs: the post-claim handoff loop to the successor reward.
  • claimRewardNow: the popup's manual claim message.
  • In-tick claiming: runSchedulerTick calls claimReadyRewards → adapter.isClaimReady / adapter.claimReward while the platform lock is held. refactor(core): split scheduler decisions from side effects #599 moves this out of the lock into an interim executor handler.
  • The Kick challenge job's alarm is registered alongside these jobs in reconcileManualWatchClaimAlarms, although refactor(kick): isolate challenge and page-context lifecycle policy #588 owns its policy.

Without an owner, #590 and #588 each have to reach into this shared code, and #591 cannot become a thin facade.

Scope

Claim failure model

There is no local claim journal: the provider's inventory is the source of truth. Recording a claim intent before sending would not help, because after a crash the engine still could not tell whether the request reached the provider.

  • Within one process, the in-flight set guarantees at most one claim request per reward across overlapping ticks, manual claims, jobs and the handoff loop.
  • Across a restart, the process may have stopped after the provider accepted a claim and before state.json or storage.local was saved. On restart the reward is still unclaimed locally, and nothing is published for it yet. The next discovery re-reads provider inventory before any claim runs. A reward the provider reports as claimed is no longer claim-ready, so no request is sent, and it is recorded as claimed without publishing activity.
  • If the reward is sent again anyway (inventory still stale), what happens depends on the platform:
    • Twitch (verified): Twitch answers DROP_INSTANCE_ALREADY_CLAIMED, which runClaim already treats as success. The claim publishes its activity and notification once, from the new process. A claim accepted just before a crash therefore publishes either once or not at all, never twice from the same process.
    • Kick (unverified): Kick's response to a duplicate claim has not been observed. Keep today's handling unchanged. Since fix(kick): keep definitive client errors off the page-tab fallback #606 (v1.14.0), a definitive 4xx from drops/claim is rethrown from the background transport without a page-tab retry. claimReward then classifies it as link_required only when campaign.accountLinked === false, and otherwise rethrows it into the claim failure path. Don't assume a duplicate means success or failure. Record the observed response in docs/architecture.md once seen. Any change to how it is classified is a separate behavior-change PR, not part of this refactor.

Document this model in docs/architecture.md.

Acceptance criteria

Out of scope

Changing claim payloads, reward eligibility, ranking or claim ordering. A persistent claim journal. Reclassifying Kick's duplicate-claim response.


Rebased on v1.14.0 as released: Kick's current claim path includes #606, and handoff follows #571's ranking. Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    • Status
      Backlog

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions