@@ -150,9 +150,12 @@ export async function upsertAdvisoryPackage(
150150// produces zero writes: matching tuples are left untouched (not even an
151151// `updated_at` bump), so no Sequin/Tinybird event fires for them either.
152152//
153- // Tuple in both old and new -> untouched. Tuple only in new -> upserted
154- // (INSERT ON CONFLICT, clearing deleted_at in case it was previously
155- // soft-deleted and OSV brought it back). Tuple only in old -> soft-deleted.
153+ // Tuple in both old and new, already a clean live OSV row -> untouched.
154+ // Tuple only in new, or matching a live deps.dev raw row on the same key ->
155+ // upserted (INSERT ON CONFLICT; clears deleted_at in case it was previously
156+ // soft-deleted, and clears range_raw/unaffected_raw to reclaim the row from
157+ // deps.dev — OSV wins on key overlap per ADR-0001 §Write semantics). Tuple
158+ // only in old -> soft-deleted.
156159// Soft-delete (not hard-delete) is required because the unique key is the
157160// value tuple itself: when OSV corrects a range, the corrected tuple has no
158161// successor row to collapse into, so without deleted_at the stale tuple
@@ -181,8 +184,12 @@ export async function reconcileOsvRanges(
181184 ON CONFLICT (advisory_package_id, COALESCE(introduced_version, ''), COALESCE(fixed_version, ''), COALESCE(last_affected, ''))
182185 DO UPDATE SET
183186 updated_at = NOW(),
184- deleted_at = NULL
187+ deleted_at = NULL,
188+ range_raw = NULL,
189+ unaffected_raw = NULL
185190 WHERE advisory_affected_ranges.deleted_at IS NOT NULL
191+ OR advisory_affected_ranges.range_raw IS NOT NULL
192+ OR advisory_affected_ranges.unaffected_raw IS NOT NULL
186193 ` ,
187194 { advisoryPackageId, values : JSON . stringify ( values ) } ,
188195 )
@@ -191,7 +198,8 @@ export async function reconcileOsvRanges(
191198 await qx . result (
192199 `
193200 UPDATE advisory_affected_ranges ar
194- SET deleted_at = NOW()
201+ SET deleted_at = NOW(),
202+ updated_at = NOW()
195203 WHERE ar.advisory_package_id = $(advisoryPackageId)
196204 AND ar.deleted_at IS NULL
197205 AND ar.range_raw IS NULL
@@ -225,7 +233,8 @@ export async function supersedeDepsDevRanges(
225233 await qx . result (
226234 `
227235 UPDATE advisory_affected_ranges
228- SET deleted_at = NOW()
236+ SET deleted_at = NOW(),
237+ updated_at = NOW()
229238 WHERE advisory_package_id = $(advisoryPackageId)
230239 AND deleted_at IS NULL
231240 AND (range_raw IS NOT NULL OR unaffected_raw IS NOT NULL)
@@ -288,7 +297,7 @@ export async function getRangesForPackages(qx: QueryExecutor, ids: number[]): Pr
288297 a.osv_id
289298 FROM advisory_packages ap
290299 JOIN advisories a ON a.id = ap.advisory_id
291- JOIN advisory_affected_ranges ar ON ar.advisory_package_id = ap.id
300+ JOIN advisory_affected_ranges ar ON ar.advisory_package_id = ap.id AND ar.deleted_at IS NULL
292301 WHERE ap.package_id IN ($(ids:csv))
293302 AND (a.is_critical = TRUE OR a.osv_id LIKE 'MAL-%')
294303 ` ,
0 commit comments