Skip to content

fix: gate retroactive issue body edits in mirror issue discovery#827

Open
statxc wants to merge 1 commit intoentrius:testfrom
statxc:fix/mirror-issue-edited-after-merge-gate
Open

fix: gate retroactive issue body edits in mirror issue discovery#827
statxc wants to merge 1 commit intoentrius:testfrom
statxc:fix/mirror-issue-edited-after-merge-gate

Conversation

@statxc
Copy link
Copy Markdown
Contributor

@statxc statxc commented Apr 27, 2026

Summary

Restores legacy parity in mirror_scan._classify_issue. PR #796 ported the PR-side post-merge edit gate (solving_pr.edited_after_merge at mirror_scan.py:461) but missed the issue-side counterpart (issue.body_or_title_edited_at > pr.merged_at, originally added in #434). MirrorIssue.last_edited_at is already fetched and parsed at gittensor/utils/mirror/models.py:271 — it just was never read by the scoring path.

Without this gate, a miner can author a deliberately vague issue, wait for a third party's PR to merge a fix, then edit the issue body to retroactively match what the PR fixed and collect full discovery_earned_score they didn't earn. The check is added inline in _classify_issue, mirroring the shape of the neighboring solving_pr.edited_after_merge block. Returns 'not-solved-closed' so the issue still counts toward credibility (matches legacy semantics) but contributes no discovery score.

Legacy parity table

Gate Pre-#796 Post-#796 This PR
PR body edited after merge oss_contributions/scoring.py:493 checked pr.last_edited_at > pr.merged_at ✅ Ported as solving_pr.edited_after_merge unchanged
Issue body/title edited after merge issue_discovery/scoring.py:253 checked issue.body_or_title_edited_at > pr.merged_at (added by #434) ❌ No replacement ✅ Ported as issue.last_edited_at > sp.merged_at

Relationship to #822

#822 fixes the cross-miner one-issue-per-PR rule in run_mirror_issue_discovery. This PR fixes a missing anti-gaming gate in _classify_issue. Different functions, different test classes, different attack vectors — the only mechanical overlap is the _issue_dict test helper signature, which 3-way merges cleanly in either order.

Related Issues

Closes #826

Type of Change

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • Other (describe below)

Testing

  • Tests added/updated
  • Manually tested

Three new tests in TestClassifyIssue (mirroring the existing test_solving_pr_edited_after_merge_counts_as_closed pattern):

Test Setup Asserts
test_issue_edited_after_solving_pr_merge_counts_as_closed last_edited_at = merged_at + 1s 'not-solved-closed' (gate fires)
test_issue_edited_before_solving_pr_merge_is_solved last_edited_at = merged_at − 1d 'solved' (legitimate pre-merge edits not penalized)
test_issue_never_edited_is_solved last_edited_at = None 'solved' (gate is null-safe)

Commands run:

  • uv run ruff check gittensor/validator/issue_discovery/mirror_scan.py tests/validator/issue_discovery/test_mirror_scan.py — All checks passed
  • uv run ruff format --check <same paths> — 2 files already formatted
  • uv run pyright <same paths> — 0 errors, 0 warnings, 0 informations
  • uv run python -m pytest tests/validator/issue_discovery/test_mirror_scan.py -v34 passed (3 new + 31 existing)
  • uv run python -m pytest (full suite) — 646 passed

Out of scope

  • The PR-body gate (solving_pr.edited_after_merge) is correctly ported and unchanged here.
  • This PR uses the mirror's authoritative last_edited_at directly — it does not populate the legacy body_or_title_edited_at field on the adapted Issue, since that field is unread on the mirror path (mirror_scan.py:504 already passes body_or_title_edited_at=None).

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Changes are documented (if applicable)

Restores legacy parity. PR entrius#796 ported the PR-side post-merge edit gate
(solving_pr.edited_after_merge) but missed the issue-side counterpart
(issue.body_or_title_edited_at > pr.merged_at, originally added in entrius#434).
MirrorIssue.last_edited_at is fetched and parsed but never consumed.

Without this gate a miner can author a deliberately vague issue, wait for
a third party's PR to merge a fix, then edit the issue body to retroactively
match the fix and collect full discovery_earned_score.

The check is added inline in _classify_issue, mirroring the shape of the
neighboring solving_pr.edited_after_merge gate. Tests cover the post-merge
edit (closed), pre-merge edit (solved), and never-edited (solved) cases.
@xiao-xiao-mao xiao-xiao-mao Bot added the bug Something isn't working label Apr 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] mirror_scan missing issue-body edit anti-gaming gate

1 participant