Skip to content

checkSubmissionFreshness aborts an entire completed attempt on a single transient live-state fetch failure, unlike its sibling caller #7089

Description

@JSONbored

Context

packages/loopover-miner/lib/live-issue-snapshot.js's fetchLiveIssueSnapshot is a single, bounded (timeout-only,
no retry) GraphQL round-trip to GitHub. It has exactly two callers in the package:

The fail-closed OUTCOME itself (never proceed on unavailable live state) is intentional and correct, and this
issue does not change it. The gap is that checkSubmissionFreshness gives a transient failure zero chance to
resolve itself before failing closed, unlike its sibling caller of the identical fetchLiveIssueSnapshot
function, which already established (in #6058) that a bounded retry-with-backoff is the right way to ride out
exactly this kind of transient unavailability before committing to a decision. The cost of getting this wrong is
also higher here than in the post-submission case: aborting pre-submission discards an entire completed
create/iterate loop's local work, not just a soft-claim election.

Requirements

  • checkSubmissionFreshness (packages/loopover-miner/lib/submission-freshness-check.js) MUST retry a failed/
    null fetchLiveIssueSnapshot result with bounded attempts and backoff before treating it as
    "live_state_unavailable", mirroring claim-conflict-resolver.js's existing resolveClaimConflict retry
    shape: a maxAttempts (default matching claim-conflict-resolver.js's DEFAULT_SNAPSHOT_MAX_ATTEMPTS = 3),
    an injectable sleepFn, and an injectable backoffMs (defaulting to http-retry.js's
    defaultRetryBackoffMs, the same function claim-conflict-resolver.js already reuses rather than
    reimplementing).
  • The retry loop MUST stop early and proceed the instant a real (non-null, well-formed) snapshot is obtained —
    it must not always burn every attempt.
  • After all attempts are exhausted with no usable snapshot, the function MUST still fail closed exactly as
    today ("live_state_unavailable", fresh: false) — this issue only widens the window before that fail-closed
    decision, it does not change or weaken it.
  • The new maxAttempts/sleepFn/backoffMs MUST be optional parameters on checkSubmissionFreshness's
    existing deps/options shape (not required, and not changing the function's existing required-dependency
    validation for claimLedger/fetchLiveIssueSnapshot/eventLedger), so every existing caller continues to
    work unchanged with sane defaults.

Deliverables

  • checkSubmissionFreshness in packages/loopover-miner/lib/submission-freshness-check.js retries a
    failed/null fetchLiveIssueSnapshot call with bounded attempts + backoff before aborting as
    live_state_unavailable.
  • A test asserting a fetchLiveIssueSnapshot that fails on its first call but succeeds on a later attempt
    (within the retry budget) results in { fresh: true } (or the correct non-live_state_unavailable
    outcome), not a premature abort.
  • A test asserting a fetchLiveIssueSnapshot that fails on every attempt still results in
    { fresh: false, reason: "live_state_unavailable" } after the bounded retries are exhausted, and that the
    abort audit event is still appended exactly once (not once per failed attempt).

Test Coverage Requirements

99%+ Codecov patch coverage on every changed line and branch in submission-freshness-check.js, including the
retry-succeeds-mid-loop, retry-exhausted, and default-parameter branches, plus the two regression tests above
using an injected sleepFn (never a real timer) so the test suite stays fast and deterministic.

Expected Outcome

checkSubmissionFreshness rides out the same class of transient GitHub unavailability (a brief 5xx, or
GraphQL-index propagation lag) its sibling claim-conflict-resolver.js already retries for, instead of
discarding a fully-completed candidate's create/iterate work on a blip that a bounded retry would have resolved
— while still failing exactly as closed as before once the retry budget is genuinely exhausted.

Links & Resources

  • packages/loopover-miner/lib/submission-freshness-check.js:57-65 — the single, non-retried call to change.
  • packages/loopover-miner/lib/claim-conflict-resolver.js:83-106 (resolveClaimConflict) — the existing sibling
    retry pattern (feat(miner): add retry/backoff to claim-conflict-resolver's post-submission live-state check #6058) to mirror.
  • packages/loopover-miner/lib/http-retry.js (defaultRetryBackoffMs) — the shared backoff function both
    callers should share.
  • packages/loopover-miner/lib/live-issue-snapshot.js — the shared fetchLiveIssueSnapshot both callers use.

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:bugGittensor-scored bug fix — scores a 0.05x multiplier.help wantedExtra attention is needed

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions