feat(products): diff resolves hosted image URLs to existing records — INV-12 over images (SD-0002 §6.3)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 00:19:23 -07:00
parent 906bc87c96
commit 984dc98dee
3 changed files with 90 additions and 6 deletions
+29 -2
View File
@@ -21,10 +21,11 @@ from __future__ import annotations
import hashlib
import json
from dataclasses import dataclass, field
from dataclasses import dataclass, field, replace
from decimal import Decimal
from .models import CLEAR_DEFAULTS, CanonicalProduct, CanonicalVariant
from . import hosted
from .models import CLEAR_DEFAULTS, CanonicalProduct, CanonicalVariant, RowError
@dataclass
@@ -119,6 +120,7 @@ def compute_diff(catalog: dict[str, CatalogProduct], products: list[CanonicalPro
summary = {"adds": 0, "updates": 0, "unchanged": 0, "errors": 0}
for product in products:
current = catalog.get(product.handle)
_resolve_hosted_images(product, current)
if product.errors:
product_plan = ProductPlan(kind="error", canonical=product, catalog=current)
detail: dict = {"errors": [e.as_json() for e in product.errors]}
@@ -145,6 +147,31 @@ def compute_diff(catalog: dict[str, CatalogProduct], products: list[CanonicalPro
return DiffResult(records=records, summary=summary, fingerprint=fingerprint, plan=plan)
def _resolve_hosted_images(product: CanonicalProduct, current: CatalogProduct | None) -> None:
"""Recognize re-imported hosted image URLs (INV-12). A hosted URL is resolved
to this product's existing image with that id — re-keyed onto that image's
source_url so the by_src match treats it as the existing image (never an add,
never a fetch). A hosted URL whose id is not one of this product's images
(cross-storefront, deleted, or a brand-new product) is a row error."""
current_by_id = {i.id: i for i in current.images} if current else {}
resolved: list = []
for image in product.images:
hosted_id = hosted.parse_image_id(image.source_url)
if hosted_id is None:
resolved.append(image)
continue
match = current_by_id.get(hosted_id)
if match is None:
product.errors.append(
RowError(image.line_number, "Image Src",
f"image '{image.source_url}' refers to an unknown hosted id")
)
resolved.append(image)
continue
resolved.append(replace(image, source_url=match.source_url))
product.images = resolved
def _add_plan(product: CanonicalProduct) -> ProductPlan:
return ProductPlan(
kind="add",