Skip to content

miner(loop): re-entry transitions run-state for the wrong repo, and on an empty queue #9683

Description

@JSONbored

⚠️ Definition of Done: this issue must be completed in full, in a single PR. Do not split this
work across multiple PRs, and do not defer any Deliverable below to a follow-up issue. A PR that
satisfies only some of the Deliverables, stubs a required test, or leaves a checkbox
partially-done does NOT resolve this issue and will be closed.

Context

attemptLoopReentry in packages/loopover-miner/lib/loop-reentry.ts documents itself (lines 138-142) as: "only when it allows — dequeues the next candidate and transitions run-state to "discovering"". The implementation:

let dequeued: LoopReentryResult["dequeued"] = null;
if (decision.reenter) {
  dequeued = portfolioQueue.dequeueNext();
  if (runState && typeof runState.setRunState === "function") {
    runState.setRunState(repoFullName, "discovering");
  }
}

Two defects against that contract:

  1. Wrong repo. portfolioQueue.dequeueNext() is portfolio-wide (portfolio-queue.ts:297-303 — the claim is "deliberately global (no api_base_url filter)"), so the item it returns is frequently for a different repo. loop-cli.ts:626-628 then does claimed = reentry.dequeued as QueueEntry and works that repo. But the run-state row flipped to "discovering" is the one for repoFullName — the repo that just finished. The repo actually about to be worked keeps its stale state.
  2. Unconditional on dequeued. When the queue is empty, dequeued === null and the finished repo is still parked at "discovering" forever. Nothing in loop-cli.ts:630-634 (the branch that sleeps, re-discovers, and re-dequeues) resets it.

run-state.ts describes this store as "the per-repo discover/plan/prepare signal a 'run portfolio' view folds alongside managed PR rows", so manage status's portfolio view reports phantom in-flight discovery on a repo the loop has already left, and never reports it on the repo it moved to.

Requirements

  • The setRunState(..., "discovering") call must only run when dequeued is non-null.
  • When dequeued is non-null, the repo written must be dequeued.repoFullName (the repo about to be worked), not the just-finished repoFullName.
  • The dequeueNext() call itself, the decision.reenter gate, the audit event append, and the returned LoopReentryResult shape must all be unchanged.
  • The docstring at lines 138-143 must be updated to state which repo's run-state is transitioned and that no transition happens when the queue is empty.

⚠️ Required pattern: keep the existing if (runState && typeof runState.setRunState === "function") duck-typed guard exactly as-is and only tighten its condition + argument. It does NOT satisfy this issue to also write a second run-state row for the finished repo, to add a new "idle" transition, to move the transition into loop-cli.ts, or to change dequeueNext() into a repo-scoped dequeue.

Deliverables

  • attemptLoopReentry calls runState.setRunState(dequeued.repoFullName, "discovering") and only when dequeued is non-null.
  • The function's docstring names dequeued.repoFullName as the transitioned repo and states the empty-queue no-op.
  • A new named regression test in test/unit/miner-loop-reentry.test.ts asserts that when dequeueNext() returns an item for repo b/b while the candidate's repo is a/a, the injected run-state store receives setRunState("b/b", "discovering") and never setRunState("a/a", ...).
  • A second new case asserts that when dequeueNext() returns null and decision.reenter is true, setRunState is never called.
  • A third case asserts that when decision.reenter is false, setRunState is still never called (unchanged behaviour).

All Deliverables above are required in a single PR. A PR that satisfies only some of them — for example adding the dequeued null-guard while still writing the finished repo's name — does not resolve this issue.

Test Coverage Requirements

packages/loopover-miner/lib/**/*.ts IS inside Codecov's coverage.include in vitest.config.ts, so the 99%+ branch-counted codecov/patch gate applies exactly as for src/**. Both arms of the new dequeued guard need a test (dequeued / not dequeued), as does the existing decision.reenter arm. The fix needs a named regression test that fails against the current code.

Expected Outcome

loopover-miner state get <repo> and manage status's portfolio view report "discovering" on the repo the loop is actually about to work, and stop parking a finished repo in "discovering" when the queue runs dry.

Links & Resources

packages/loopover-miner/lib/loop-reentry.ts:138-190, packages/loopover-miner/lib/loop-cli.ts:602-634, packages/loopover-miner/lib/portfolio-queue.ts:289-303, packages/loopover-miner/lib/run-state.ts.

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