← back to Lawyer Directory Builder

migrations/019_site_audit_html.sql

38 lines

-- 019_site_audit_html.sql
-- Persist raw HTML snapshots from site_audit Playwright pass so a downstream
-- regex-only ad-signal detector can mine known tracking-pixel fingerprints.
--
-- Why a separate firm_ad_signals table (and not a column on organizations)?
--   - This is INTERNAL sales-targeting data only. Per
--     feedback_lawyer_directory_compliance.md, internal CRM signals stay isolated
--     from the public profile schema so they can never accidentally render
--     into a public-facing artifact.
--   - Cal §6155 / Rule 7.x: directory-only, no fabricated marketing claims —
--     these signals are inputs for our outreach, not facts about the firm.
--
-- Schema:
--   site_audits.raw_html_path  — relpath under raw_html/ to the persisted HTML
--   firm_ad_signals            — one row per firm, JSONB blob of pixel-detect booleans

ALTER TABLE public.site_audits
  ADD COLUMN IF NOT EXISTS raw_html_path TEXT;

CREATE INDEX IF NOT EXISTS site_audits_raw_html_path_idx
  ON public.site_audits(raw_html_path)
  WHERE raw_html_path IS NOT NULL;

CREATE TABLE IF NOT EXISTS public.firm_ad_signals (
  firm_id            BIGINT PRIMARY KEY REFERENCES public.organizations(id) ON DELETE CASCADE,
  ad_signals         JSONB NOT NULL DEFAULT '{}'::jsonb,
  signals_updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS firm_ad_signals_paid_count_idx
  ON public.firm_ad_signals (((ad_signals->>'paid_ads_count')::int) DESC NULLS LAST);

CREATE INDEX IF NOT EXISTS firm_ad_signals_updated_idx
  ON public.firm_ad_signals (signals_updated_at DESC);

COMMENT ON TABLE public.firm_ad_signals IS
  'INTERNAL sales-targeting only. Tracking-pixel detect output mined from raw_html_path. Admin-gated. Never expose publicly.';