Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/db/repositories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1279,6 +1279,13 @@ export async function getOpenUpstreamDriftReportByFingerprint(env: Env, fingerpr
return row ? toUpstreamDriftReportRecord(row) : null;
}

/** Lookup a drift report by its stable fingerprint regardless of status (resolved reports included). */
export async function getUpstreamDriftReportByFingerprint(env: Env, fingerprint: string): Promise<UpstreamDriftReportRecord | null> {
const db = getDb(env.DB);
const [row] = await db.select().from(upstreamDriftReports).where(eq(upstreamDriftReports.fingerprint, fingerprint)).limit(1);
return row ? toUpstreamDriftReportRecord(row) : null;
}

export async function persistScorePreview(env: Env, preview: ScorePreviewRecord): Promise<void> {
const db = getDb(env.DB);
await db.insert(scorePreviews).values({
Expand Down
7 changes: 5 additions & 2 deletions src/upstream/unmodeled-scoring-drift.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
getLatestUpstreamRulesetSnapshot,
listUpstreamDriftReports,
getUpstreamDriftReportByFingerprint,
upsertUpstreamDriftReport,
} from "../db/repositories";
import type { UpstreamDriftArea, UpstreamDriftReportRecord, UpstreamDriftSeverity } from "../types";
Expand All @@ -23,7 +23,10 @@ export async function syncUnmodeledScoringConstantDrift(
},
): Promise<UpstreamDriftReportRecord | null> {
const fingerprint = await unmodeledScoringConstantsFingerprint();
const existing = (await listUpstreamDriftReports(env, 50)).find((report) => report.fingerprint === fingerprint) ?? null;
// Key by fingerprint directly — `listUpstreamDriftReports` is capped and ordered by `updatedAt`, so an older
// unmodeled-constants report can fall off the newest-50 window and be treated as missing (duplicate open reports,
// lost issue metadata, resolve no-ops).
const existing = (await getUpstreamDriftReportByFingerprint(env, fingerprint)) ?? null;
const now = nowIso();

if (args.unmodeledConstants.length === 0) {
Expand Down
51 changes: 50 additions & 1 deletion test/unit/unmodeled-scoring-drift.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { listUpstreamDriftReports, updateUpstreamDriftReportIssue } from "../../src/db/repositories";
import { listUpstreamDriftReports, updateUpstreamDriftReportIssue, upsertUpstreamDriftReport } from "../../src/db/repositories";
import { syncUnmodeledScoringConstantDrift, unmodeledScoringConstantsFingerprint } from "../../src/upstream/unmodeled-scoring-drift";
import { createTestEnv } from "../helpers/d1";

Expand Down Expand Up @@ -102,4 +102,53 @@ describe("unmodeled scoring constant drift", () => {
expect(report?.summary).toMatch(/, …$/);
expect(report?.severity).toBe("high");
});

it("looks up the stable unmodeled-constants fingerprint even when it falls off the newest-50 drift list", async () => {
const env = createTestEnv();
const fingerprint = await unmodeledScoringConstantsFingerprint();
const opened = await syncUnmodeledScoringConstantDrift(env, { unmodeledConstants: ["ALPHA"] });
expect(opened?.fingerprint).toBe(fingerprint);

// Push the unmodeled report out of `listUpstreamDriftReports(env, 50)`'s recency window and seed linked-issue
// metadata directly on the row — do NOT call `updateUpstreamDriftReportIssue` here; that helper rewrites
// `updatedAt` to now and would put the row back inside the capped list, defeating the regression.
await upsertUpstreamDriftReport(env, {
...opened!,
updatedAt: "2020-01-01T00:00:00.000Z",
issueNumber: 811,
issueUrl: "https://github.com/JSONbored/gittensory/issues/811",
});
for (let index = 0; index < 51; index++) {
await upsertUpstreamDriftReport(env, {
id: `newer-${index}`,
fingerprint: `newer-drift-${index}`,
severity: "low",
status: "open",
summary: `newer drift ${index}`,
affectedAreas: ["registry"],
previousRulesetId: null,
currentRulesetId: null,
issueNumber: null,
issueUrl: null,
payload: { changes: ["noop"] },
generatedAt: "2026-06-30T00:00:00.000Z",
updatedAt: `2026-06-30T${String(index).padStart(2, "0")}:00:00.000Z`,
});
}
expect((await listUpstreamDriftReports(env, 50)).some((report) => report.fingerprint === fingerprint)).toBe(false);

const updated = await syncUnmodeledScoringConstantDrift(env, { unmodeledConstants: ["ALPHA", "BETA"] });
expect(updated).toMatchObject({
id: opened!.id,
fingerprint,
status: "open",
issueNumber: 811,
issueUrl: "https://github.com/JSONbored/gittensory/issues/811",
payload: expect.objectContaining({ unmodeledUpstreamConstants: ["ALPHA", "BETA"] }),
});

await upsertUpstreamDriftReport(env, { ...updated!, updatedAt: "2020-01-01T00:00:00.000Z" });
const resolved = await syncUnmodeledScoringConstantDrift(env, { unmodeledConstants: [] });
expect(resolved).toMatchObject({ id: opened!.id, fingerprint, status: "resolved" });
});
});
Loading