Skip to content

fix(upstream): fileUpstreamDriftIssues unconditionally PATCHes unchanged GitHub drift issues every 6 hours #4503

Description

@JSONbored

Part of #4496. P7 — medium severity, medium confidence.

Context

fileUpstreamDriftIssues (src/upstream/ruleset.ts:275+) runs from the "file-upstream-drift-issues" job, which src/index.ts:237 enqueues unconditionally inside isFullSyncWindow (index.ts:110, hour % 6 === 0) — every 6 hours, independent of whether any new drift was actually detected in that window.

For every stored open drift report (up to 20, ruleset.ts:290), it resolves the existing GitHub issue (validateRecordedGitHubIssue or findGitHubIssueForFingerprint, one GET) and then ALWAYS calls updateGitHubDriftIssue (ruleset.ts:294-313) — one PATCH — unconditionally. There is no comparison of the freshly-built payload against what's already posted before deciding to write. githubDriftIssuePayload/githubDriftIssueBody (ruleset.ts:1115-1153) are pure functions of report fields (fingerprint, severity, affectedAreas, summary, ruleset ids) with no timestamp or other always-changing field — and upsertUpstreamDriftReport upserts on the fingerprint conflict target, so an unresolved/open report with unchanged content produces byte-identical payload/body across cycles.

Net effect: for every drift report that stays open (which can be for a long time until a human resolves it), this cron path issues a redundant GET+PATCH to the same GitHub issue every 6 hours indefinitely with identical content — wasted GitHub API calls, and a PATCH also bumps the issue's "updated" timestamp and notifies watchers/assignees with no real change. Bounded to 20 reports, so not catastrophic, but a real, evidenced instance of the same "missing before-write comparison" bug class found elsewhere tonight.

Requirements

  • Before calling updateGitHubDriftIssue, compare the freshly-built payload (or a hash of it) against the previously-recorded issue body/labels/assignees for that fingerprint, and skip the PATCH when unchanged — mirroring the ai_review/ai_slop frozen-reuse pattern already used elsewhere in this codebase for the identical "don't redo unchanged work" problem.
  • Ensure a genuinely changed report (severity escalated, affected areas grew, etc.) still gets a fresh PATCH — this must not accidentally suppress a real update.
  • Invariant + regression tests (non-negotiable): an invariant test asserting a second fileUpstreamDriftIssues run for an UNCHANGED open report makes zero PATCH calls; a regression test reproducing the exact bug (two consecutive 6-hour cycles with an unchanged report, asserting only the first PATCHes); a negative-path test confirming a report whose content genuinely changed (e.g. severity escalation) DOES trigger a fresh PATCH.

Deliverables

  • Payload/content comparison before updateGitHubDriftIssue, skipping the PATCH when unchanged
  • Genuinely changed reports still PATCH
  • Invariant test: unchanged report = zero PATCH calls on repeat runs
  • Regression test: two consecutive cycles, unchanged report, only first PATCHes
  • Negative-path test: changed report still PATCHes

Expected outcome

Open drift issues stop getting redundant, byte-identical PATCHes every 6 hours — cutting GitHub API load and eliminating the spurious "updated" notification noise for assignees/watchers on issues that haven't actually changed.

References

  • src/upstream/ruleset.ts:275-313 (the unconditional PATCH loop)
  • src/upstream/ruleset.ts:1016-1080, 1115-1153 (the resolve helpers + the pure payload/body builders)
  • src/db/repositories.ts:1638-1671 (upsertUpstreamDriftReport, the fingerprint-keyed upsert)
  • src/index.ts:110, 237 (the 6-hour cron trigger)

Effort

S

Metadata

Metadata

Assignees

Labels

gittensor:bugGittensor-scored bug fix — scores a 0.05x multiplier.maintainer-onlyOwner-only work — yields no Gittensor points.orbGittensory Orb related - maintainer self-hosting analytics.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions