-- Migration 106: Explicit image/details research resolution state -- -- image_verified/details_verified stay strict: true only when source-backed -- evidence exists. These status columns let TIP close research loops when a -- public source does not expose an image or enough details, without inventing -- data or abusing verified booleans. ALTER TABLE transceivers ADD COLUMN IF NOT EXISTS image_status VARCHAR(32) NOT NULL DEFAULT 'unknown', ADD COLUMN IF NOT EXISTS image_status_updated_at TIMESTAMPTZ, ADD COLUMN IF NOT EXISTS image_unavailable_verified_at TIMESTAMPTZ, ADD COLUMN IF NOT EXISTS image_unavailable_reason TEXT, ADD COLUMN IF NOT EXISTS details_status VARCHAR(32) NOT NULL DEFAULT 'unknown', ADD COLUMN IF NOT EXISTS details_status_updated_at TIMESTAMPTZ, ADD COLUMN IF NOT EXISTS details_unavailable_verified_at TIMESTAMPTZ, ADD COLUMN IF NOT EXISTS details_unavailable_reason TEXT; DO $$ BEGIN IF EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'transceivers_image_status_check' ) THEN ALTER TABLE transceivers DROP CONSTRAINT transceivers_image_status_check; END IF; ALTER TABLE transceivers ADD CONSTRAINT transceivers_image_status_check CHECK (image_status IN ( 'unknown', 'public_image', 'no_public_image', 'needs_research', 'ambiguous' )); IF EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'transceivers_details_status_check' ) THEN ALTER TABLE transceivers DROP CONSTRAINT transceivers_details_status_check; END IF; ALTER TABLE transceivers ADD CONSTRAINT transceivers_details_status_check CHECK (details_status IN ( 'unknown', 'public_details', 'no_public_details', 'needs_research', 'ambiguous' )); END $$; UPDATE transceivers SET image_status = CASE WHEN image_verified = true THEN 'public_image' WHEN image_status = 'unknown' THEN 'needs_research' ELSE image_status END, image_status_updated_at = COALESCE(image_status_updated_at, NOW()) WHERE image_status IS NULL OR image_status = 'unknown' OR image_verified = true; UPDATE transceivers SET details_status = CASE WHEN details_verified = true THEN 'public_details' WHEN details_status = 'unknown' THEN 'needs_research' ELSE details_status END, details_status_updated_at = COALESCE(details_status_updated_at, NOW()) WHERE details_status IS NULL OR details_status = 'unknown' OR details_verified = true; CREATE INDEX IF NOT EXISTS idx_transceivers_image_status ON transceivers (image_status); CREATE INDEX IF NOT EXISTS idx_transceivers_details_status ON transceivers (details_status); ALTER TABLE transceiver_verification_evidence DROP CONSTRAINT IF EXISTS transceiver_verification_evidence_verification_type_check; ALTER TABLE transceiver_verification_evidence ADD CONSTRAINT transceiver_verification_evidence_verification_type_check CHECK ( verification_type::text = ANY ( ARRAY[ 'price', 'price_unavailable', 'image', 'image_unavailable', 'details', 'details_unavailable', 'competitor_match', 'competitor_no_match', 'competitor_ambiguous', 'artifact_quarantine' ] ) ); COMMENT ON COLUMN transceivers.image_status IS 'Resolution state for image evidence: public_image, no_public_image, needs_research, ambiguous, unknown. image_verified remains true only for source-backed product images.'; COMMENT ON COLUMN transceivers.details_status IS 'Resolution state for detail evidence: public_details, no_public_details, needs_research, ambiguous, unknown. details_verified remains true only for source-backed product details.';