Decision (ratified 2026-07-21): poll, not webhook. GitHub's repo-transfer acceptance is asynchronous and can move the repo to an owner outside our GitHub App's installation entirely, at which point no webhook is guaranteed deliverable to us at all -- polling GET /repos/{owner}/{repo} (or a lightweight metadata call) on a schedule is the implementation-certain option; a webhook approach isn't reliably available for the "we lose access" case, which is the most likely completion outcome of a real transfer to a customer's own account.
Detection logic:
- Poll periodically (existing cron/scheduled-job infra, not a new one) for each pending transfer: check whether the repo now resolves under the target owner (accepted) or the app's access to it errors/404s in a way consistent with ownership having moved (also accepted, from our side we may simply lose visibility -- record it as accepted-and-departed).
- Expiry: 7 days. If neither happens within 7 days of initiation, mark the transfer
expired (GitHub's own transfer-acceptance email link itself has no indefinitely-open window in practice; 7 days is a reasonable customer-facing wait before treating it as abandoned and letting a new transfer be initiated).
- In-flight AMS work against a repo mid-transfer: pause dispatch for that repo for the duration of a pending transfer (reuse the existing per-repo pause mechanism, don't invent a new one) -- resume once accepted-and-confirmed-still-installed, or if expired/declined.
Problem: initiateAprRepoTransfer (src/orb/apr-repo-transfer.ts) returns { initiated: true } on a 2xx response, but GitHub repo transfers are asynchronous and acceptance-gated. Nothing detects whether a pending transfer was accepted, declined, or expired.
Area: src/orb/apr-repo-transfer.ts, a scheduled polling job (mirror existing cron-invoked patterns, not a new scheduling mechanism).
Deliverables:
Resources
Contributor-eligible. Detection mechanism, expiry window, and in-flight-work handling are all decided above -- remaining work is implementation against existing patterns (scheduled job, per-repo pause), not an open design question.
Decision (ratified 2026-07-21): poll, not webhook. GitHub's repo-transfer acceptance is asynchronous and can move the repo to an owner outside our GitHub App's installation entirely, at which point no webhook is guaranteed deliverable to us at all -- polling
GET /repos/{owner}/{repo}(or a lightweight metadata call) on a schedule is the implementation-certain option; a webhook approach isn't reliably available for the "we lose access" case, which is the most likely completion outcome of a real transfer to a customer's own account.Detection logic:
expired(GitHub's own transfer-acceptance email link itself has no indefinitely-open window in practice; 7 days is a reasonable customer-facing wait before treating it as abandoned and letting a new transfer be initiated).Problem:
initiateAprRepoTransfer(src/orb/apr-repo-transfer.ts) returns{ initiated: true }on a 2xx response, but GitHub repo transfers are asynchronous and acceptance-gated. Nothing detects whether a pending transfer was accepted, declined, or expired.Area:
src/orb/apr-repo-transfer.ts, a scheduled polling job (mirror existing cron-invoked patterns, not a new scheduling mechanism).Deliverables:
Resources
src/orb/apr-repo-transfer.ts(initiateAprRepoTransfer)Contributor-eligible. Detection mechanism, expiry window, and in-flight-work handling are all decided above -- remaining work is implementation against existing patterns (scheduled job, per-repo pause), not an open design question.