Skip to content

[Bug] Mirror never sets is_transferred=true #1010

Description

@Martin-Blau

Summary

Shortly, gittensor's transferred-issue anti-gaming gate is dead on the mirror path.
das-github-mirror declares issues.is_transferred BOOLEAN NOT NULL DEFAULT FALSE (packages/db/03_issues.sql:18) and exposes the column on the public miner API (packages/das/src/api/miners/miners.service.ts:83,149 — both getIssues and the inlined linked-issue rows on getPullRequests). No code path in das-github-mirror ever writes is_transferred = true:

  • webhook/handlers/issue.handler.ts does not branch on payload.action === "transferred". It upserts the same set of fields for every action; isTransferred is omitted from the upsert payload, so the DB default (false) sticks.
  • webhook/github-fetcher.service.ts:869-892 backfill issue query only requests timelineItems(itemTypes: [LABELED_EVENT, UNLABELED_EVENT], first: 30)TRANSFERRED_EVENT is not in the list.
  • Grep isTransferred|is_transferred across packages/das/src shows zero write sites.

On the gittensor side, the mirror path treats this field as the first anti-gaming gate in mirror_scan.py:_classify_issue:

if issue.is_transferred:
    bt.logging.debug(f'  issue #{issue.issue_number} ({issue.repo_full_name}): ignore (transferred)')
    return 'ignore'

Because the mirror always returns is_transferred=false, this branch is unreachable in production. The mirror-path docstring (mirror_scan.py:12-19) explicitly lists not issue.is_transferred among the gates, but the gate never fires.

Why this is a regression and not a fresh oversight

The legacy (pre-mirror) scan path used to detect transfers and had this exact class of bug — fixed by issues #404 and #405 with PRs #415 / #420 / #429. The mirror migration shipped in PR #796 routed OSS scoring + issue discovery through the mirror service for mirror_enabled repos. The mirror integration carried over the field shape (is_transferred returned in API responses, parsed into MirrorIssue.is_transferred, gated in _classify_issue) but not the producer — nothing on the mirror side actually populates it.

Net result: the anti-gaming protection added by #420/#429 silently regressed for every repo on the mirror path.

Reproduction

  1. Pick a mirror-enabled tracked repo. Open an issue, then transfer it to a different repo using GitHub's UI.
  2. GitHub fires issues webhook with action="transferred". The mirror's IssueHandler.handle runs and upserts the row — is_transferred is never touched.
  3. Confirm directly:
    SELECT issue_number, state, is_transferred
      FROM issues
     WHERE repo_full_name = '<owner>/<repo>'
       AND issue_number   = <n>;
    is_transferred is false.
  4. GET /api/v1/miners/<authorId>/issues returns the row with "is_transferred": false.
  5. In mirror_scan._classify_issue the issue does not short-circuit at line 431; it falls through to normal 'solved' / 'not-solved-closed' classification.

Expected behavior

Mirror must populate is_transferred=true for any issue that has been transferred. Two producer paths are needed for parity with the rest of the schema:

  1. Live webhook (issue.handler.ts): when payload.action === "transferred", set data.isTransferred = true in the upsert. (Optional: also persist the destination from payload.changes.new_repository / payload.changes.new_issue for observability — not required to fix the gate.)

  2. Backfill (github-fetcher.service.ts:869): add TRANSFERRED_EVENT to timelineItems(itemTypes: [...], first: 30) and, when seen, set isTransferred=true on the upsert. Without this, transfers that happened before App install or before the most-recent backfill window stay invisible.

Impact

Notes

  • Tests covering the gate exist (tests/validator/issue_discovery/test_mirror_scan.py:184 test_transferred_ignored, tests/validator/oss_contributions/mirror/test_scoring.py:549 test_transferred_blocks) — they pass because they construct is_transferred=True directly. They don't exercise the producer side, which is where the real bug lives.
  • This issue is purely about the producer (mirror). The gittensor consumer is already correct.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions