Skip to content

feat(orb): detect APR repo-transfer acceptance/expiry via scheduled poll - #8005

Merged
JSONbored merged 1 commit into
JSONbored:mainfrom
rsnetworkinginc:apr-transfer-detection-7741
Jul 22, 2026
Merged

feat(orb): detect APR repo-transfer acceptance/expiry via scheduled poll#8005
JSONbored merged 1 commit into
JSONbored:mainfrom
rsnetworkinginc:apr-transfer-detection-7741

Conversation

@rsnetworkinginc

Copy link
Copy Markdown
Contributor

Summary

Closes #7741initiateAprRepoTransfer returns { initiated: true } on a 2xx, but GitHub repo transfers are asynchronous and acceptance-gated; nothing detected whether a pending transfer was accepted, declined, or expired. This implements the ratified poll-based detection (poll, not webhook — a transfer to a customer's own account moves the repo outside our App's installation, so no webhook is reliably deliverable).

What changed (src/orb/apr-repo-transfer.ts)

Detection/expiry are pure, injectable functions (the live loop is wired but v8 ignore'd, matching the existing relay-drain pattern), so the logic is unit-tested directly:

  • probeAprRepoTransferGET /repos/{owner}/{repo} with the App installation token. 404 → accepted-and-departed (ownership moved, App lost access); 2xx whose owner.login is now the target → resolved-under-target (accepted); anything else → still pending.
  • classifyAprRepoTransferOutcome — pure: resolved → accepted, departed → accepted_departed, else expired once now - initiatedAt >= 7 days (APR_REPO_TRANSFER_EXPIRY_MS), else pending.
  • pollPendingAprRepoTransfers — per pending transfer: probe → classify → reconcile.

Pause reuse (deliverable 2): setAprRepoDispatchPaused toggles the existing per-repo agentPaused kill-switch via upsertRepositorySettings — no new mechanism. requestAprRepoTransfer freezes dispatch on initiation; the poll resumes on accepted-and-still-installed or expired/declined, and leaves accepted_departed alone (nothing left to dispatch).

Scheduling (reused, not reinvented): a new poll-apr-repo-transfers job type, hourly enqueue in src/index.ts gated behind a default-off LOOPOVER_APR_TRANSFER_POLL flag (keeps existing cron tests byte-identical), and a dispatch arm in src/queue/job-dispatch.ts. The pending-transfer store fail-empties (no-op) until #7664 persists rows — mirroring the merged #8001 loadAprIdeaCompletion precedent.

Tests (deliverable 3)

Extended test/unit/orb-apr-repo-transfer.test.ts in its existing style: accepted, accepted-departed, expired-at-7-days, still-pending-within-window, plus probe/pause/flag cases; added a flag-on enqueue test to test/unit/index.test.ts.

Verified locally on top of current main: npm run typecheck clean, @loopover/engine build clean, git diff --check clean, 82 tests pass, and 100% patch coveragesrc/orb/apr-repo-transfer.ts and src/index.ts both report zero uncovered statements and zero uncovered branches. The job-dispatch.ts live-loop arm is v8 ignore'd (the injectable logic it binds is covered directly in the unit test).

GitHub repo transfers are asynchronous and acceptance-gated, so initiateAprRepoTransfer's
{initiated:true} only means a transfer is PENDING. This adds the ratified poll-not-webhook
detection (JSONbored#7741):

- classifyAprRepoTransferOutcome + probeAprRepoTransfer: probe the repo via the App
  installation token and mark accepted / accepted-and-departed / expired-at-7-days /
  still-pending. Pure and injectable, unit-tested without the live cron.
- pollPendingAprRepoTransfers: reconcile every pending transfer and the per-repo AMS
  pause, reusing the EXISTING agentPaused kill-switch (setAprRepoDispatchPaused) - keep
  dispatch frozen while pending, resume on accepted-and-still-installed or expired/declined,
  leave departed accepts alone. requestAprRepoTransfer now freezes dispatch on initiation.
- Scheduled wiring reuses the existing cron/queue mechanism: an hourly poll-apr-repo-transfers
  job gated behind default-off LOOPOVER_APR_TRANSFER_POLL; the live-loop dispatch arm is
  v8-ignored, all real logic lives in the injectable functions. The pending-transfer store
  fail-empties until JSONbored#7664 lands, mirroring loadAprIdeaCompletion.

100% patch coverage on changed source lines; typecheck/build/diff-check clean.

Closes JSONbored#7741
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@loopover-orb loopover-orb Bot added the gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. label Jul 22, 2026
@loopover-orb

loopover-orb Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Tip

✅ LoopOver review result - approve/merge recommended

Review updated: 2026-07-22 17:23:39 UTC

7 files · 1 AI reviewer · no blockers · readiness 95/100 · CI green · clean

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
This implements poll-based detection of APR repo-transfer acceptance/expiry (#7741), reusing the existing agentPaused kill-switch for dispatch pause/resume, with all IO injected so classifyAprRepoTransferOutcome/probeAprRepoTransfer/pollPendingAprRepoTransfers are unit-tested directly. The feature is correctly flag-gated (LOOPOVER_APR_TRANSFER_POLL, default off) and the live-loop wiring in job-dispatch.ts is a no-op today since loadPendingAprRepoTransfers fail-empties until #7664 lands, matching the documented convergence pattern. One real design question: the module's own header states a transfer to a customer's own account moves the repo outside the App's installation, yet probeAprRepoTransfer's `resolved_under_target` (accepted, still-installed) branch requires a 2xx response with the App's installation token — for the actual customer-transfer flow this feature targets, that branch may never fire in practice (the App would 404 instead), making 'accepted' effectively unreachable for the flagship use case and worth a direct answer from the author rather than silent generality.

Nits — 4 non-blocking
  • src/orb/apr-repo-transfer.ts:203-221 — confirm the `resolved_under_target` (still-installed) outcome is actually reachable for a customer-account transfer, given the module's own doc comment says that transfer type removes the App's installation access entirely; if it's dead for this feature, say so in the comment or drop the distinction.
  • src/orb/apr-repo-transfer.ts:271 pollPendingAprRepoTransfers re-asserts setDispatchPaused(true) every poll pass for a still-pending transfer even though it was already paused at initiation — harmless but worth a one-line note on why the idempotent re-assert exists (clock skew / a settings row that drifted back to unpaused externally).
  • Add a short note near probeAprRepoTransfer clarifying whether fetch's default redirect-follow behavior is relied upon to reach the new path's 2xx body, since that's the crux of the `resolved_under_target` detection.
  • Consider a named constant for the repeated `404` check across initiateAprRepoTransfer-adjacent probes for consistency, though this matches existing conventions in the file.

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ✅ Gate result — Passing (No configured blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #7741
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 16 registered-repo PR(s), 5 merged, 0 issue(s).
Contributor context ✅ Confirmed Gittensor contributor rsnetworkinginc; Gittensor profile; 16 PR(s), 0 issue(s).
Improvement ✅ Minor risk: clean · value: minor
Linked issue satisfaction

Addressed
The PR implements all three deliverables: a scheduled poll (via existing cron/queue infra, hourly, gated behind a flag) that probes GitHub and classifies pending transfers as accepted/accepted_departed/pending/expired at the ratified 7-day window, reuse of the existing per-repo agentPaused kill-switch to pause on initiation and resume on resolution, and unit tests covering accepted, expired, and s

Review context
  • Author: rsnetworkinginc
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: not available
  • Official Gittensor activity: 16 PR(s), 0 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Start here: Triage stale or unlinked PRs.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.

  • @loopover ask <question> answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat <question> answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.

  • Re-run LoopOver review

@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 22, 2026
@codecov

codecov Bot commented Jul 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.30%. Comparing base (2656eaa) to head (2804f59).
⚠️ Report is 3 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8005      +/-   ##
==========================================
- Coverage   91.92%   89.30%   -2.63%     
==========================================
  Files         741       98     -643     
  Lines       75957    22151   -53806     
  Branches    23048     3863   -19185     
==========================================
- Hits        69827    19781   -50046     
+ Misses       5037     2188    -2849     
+ Partials     1093      182     -911     
Flag Coverage Δ
shard-1 58.58% <97.14%> (+1.78%) ⬆️
shard-2 6.39% <2.85%> (-46.29%) ⬇️
shard-3 42.08% <8.57%> (-9.17%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/index.ts 96.15% <100.00%> (+0.03%) ⬆️
src/orb/apr-repo-transfer.ts 100.00% <100.00%> (ø)
src/queue/job-dispatch.ts 99.29% <ø> (-0.71%) ⬇️
src/types.ts 100.00% <ø> (ø)

... and 643 files with indirect coverage changes

@JSONbored
JSONbored merged commit 3962d99 into JSONbored:main Jul 22, 2026
12 checks passed
JSONbored added a commit that referenced this pull request Jul 25, 2026
…8605)

Live diagnosis on the self-hosted ORB box: metagraphed's secret_leak gate
false-positive rate climbed from 38% to 56% over 6 hours, holding
mergeable PRs. Root cause wasn't a pattern-match false positive -- it was
the fail-closed "content exceeded the scan cap" block on regenerated
OpenAPI/JSON-schema artifacts. metagraphed's openapi.json is ~1.9MB and
api-components.schema.json ~514KB, both well past the old 512,000-char
cap; three recent PRs (#8106, #8095, #8005) were all held for this reason
and all merged anyway once manually verified clean.

The underlying fetcher (grounding-wire.ts's makeGithubFileFetcher)
already requests the raw+json media type specifically to bypass GitHub's
Contents API ~1MB base64-JSON envelope ceiling, so the real limit was
always this local constant, not GitHub's. Raising it only expands scan
coverage -- more content becomes fetchable-and-scannable instead of
being marked incomplete-and-blocked -- so this can't weaken detection
on anything the old cap already caught.

4MB gives headroom above the largest observed real case (loopover's own
openapi.json is already at 525KB, past the old cap too). Fetch
count/concurrency stay capped separately, unaffected.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. manual-review Gittensor contributor context

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Design + implement APR repo-transfer acceptance/expiry detection (deferred from #7638)

2 participants