Skip to content

fix(portfolio): scope per-repo WIP caps and diversification buckets by apiBaseUrl, not repoFullName alone #7224

Description

@JSONbored

Context

portfolio-queue.js's #5563 migration gave the store a composite primary key (api_base_url, repo_full_name, identifier) specifically so two forge hosts serving a same-named owner/repo never collide. But two WIP-cap/selection code paths built on top of that store never picked up the host dimension:

  • entriesToPortfolioQueue (packages/loopover-miner/lib/portfolio-queue-manager.js:48-76) buckets active rows by const repoKey = repoFullName.toLowerCase() (line 59) — no apiBaseUrl in the key — before handing buckets to the engine's nextEligibleItems for queue claim-batch.
  • selectNextEligibleTarget (packages/loopover-miner/lib/portfolio-queue-cli.js:113-126) computes repoActiveCount via entries.filter((entry) => entry.status === "in_progress" && entry.repoFullName === topQueued.repoFullName) (lines 121-123) — again no apiBaseUrl filter — for queue next --per-repo-wip.

Verified empirically: with two items on acme/widgets, one on https://api.github.com and one on https://ghe.example.com/api/v3, and perRepoWipCap: 1:

  • selectEligibleBatch(entries, { globalWipCap: 4, perRepoWipCap: 1 }) returns only 1 of the 2 items (should be 2 — they're on different hosts).
  • selectNextEligibleTarget(entries, { globalWipCap: 10, perRepoWipCap: 1 }), given host A has an in_progress row on acme/widgets and host B has a queued row on acme/widgets, returns [] — host B's completely independent, unclaimed item is blocked by unrelated in-progress work on host A.

This isn't a cold-path edge case: queue claim-batch's own default caps are { globalWipCap: 1, perRepoWipCap: 1 } (portfolio-queue-cli.js:409), so this triggers on the out-of-the-box default the moment an operator tracks the same-named repo on two forge hosts — exactly the scenario #5563 was built to support. Precedent: #6764 (closed, milestone "Miner Wave 4.5 — AMS Hardening Round 2") fixed this exact bug class (an apiBaseUrl/forge-host scoping gap left behind by #5563) in manage-poll.js.

Requirements

  • entriesToPortfolioQueue's bucket key must incorporate apiBaseUrl (e.g. a compound key of apiBaseUrl + lowercased repoFullName), not repoFullName alone.
  • selectNextEligibleTarget's repoActiveCount filter must additionally match entry.apiBaseUrl === topQueued.apiBaseUrl.
  • Existing single-host behavior (the overwhelming common case) must be byte-identical — these are additive filter/key changes only.

Deliverables

  • entriesToPortfolioQueue buckets by (apiBaseUrl, repoFullName)
  • selectNextEligibleTarget's per-repo active count is host-scoped
  • Regression test: perRepoWipCap: 1 with two same-named-repo items on different hosts both become eligible (mirrors the existing claimNextBatch Scope local ledger keys by forge host, not bare repoFullName #5563 regression test at test/unit/miner-portfolio-queue-manager.test.ts:155-171, but with a cap that actually binds — that existing test uses perRepoWipCap: 2, which passes regardless of whether hosts are conflated, so it doesn't catch this)
  • Regression test: selectNextEligibleTarget with an in-progress item on host A does not block a queued item with the same repoFullName on host B

Test Coverage Requirements

packages/loopover-miner/lib/**/*.js is covered by Codecov's 99% patch-coverage gate. Both changed functions are pure and synchronous — 100% line+branch coverage on the new/changed conditionals is achievable with the two regression tests above plus the existing single-host tests continuing to pass unchanged.

Expected Outcome

An operator tracking a same-named repo across two forge hosts (e.g. github.com + a GitHub Enterprise instance) gets independent, correctly-scoped WIP-cap enforcement and diversification per host — unrelated in-progress/queued work on one host never throttles or starves the other.

Links & Resources

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