diff --git a/backend/src/tasks/handlers/entry-point-discovery.ts b/backend/src/tasks/handlers/entry-point-discovery.ts index ea2d3c43..05584482 100644 --- a/backend/src/tasks/handlers/entry-point-discovery.ts +++ b/backend/src/tasks/handlers/entry-point-discovery.ts @@ -501,6 +501,35 @@ async function updateDispensaryWithPlatformId( source: string, slug: string ): Promise { + // Self-healing: Check if platform_dispensary_id already exists on another dispensary + const existingResult = await pool.query(` + SELECT id, name FROM dispensaries + WHERE platform_dispensary_id = $1 AND id != $2 + `, [platformId, dispensaryId]); + + if (existingResult.rows.length > 0) { + const existing = existingResult.rows[0]; + console.log(`[EntryPointDiscovery] Platform ID ${platformId} already exists on dispensary ${existing.id} (${existing.name})`); + console.log(`[EntryPointDiscovery] Marking dispensary ${dispensaryId} as duplicate of ${existing.id}`); + + // Mark current dispensary as duplicate and disable it + await pool.query(` + UPDATE dispensaries + SET + id_resolution_status = 'duplicate', + id_resolution_error = $2, + crawl_enabled = false, + stage = 'duplicate', + updated_at = NOW(), + last_modified_at = NOW(), + last_modified_by_task = $3, + last_modified_task_id = $4 + WHERE id = $1 + `, [dispensaryId, `Duplicate of dispensary ${existing.id} (${existing.name})`, task.role, task.id]); + + return; // Don't queue product_discovery for duplicates + } + // Update dispensary with platform ID and stage checkpoint // Stage transitions: discovered/validated → promoted (ready for crawling) await pool.query(`