← back to Rentv Adintel

db/seed/organizations.js

712 lines

'use strict';
/**
 * Seed organizations, evidence_records, ad_sightings, events, and
 * event_relationships.
 *
 * Hard rules (spec §6.16, §6.17):
 *  - VERIFIED_ADVERTISER status only for companies with direct RENTV ad/email evidence.
 *  - VERIFIED_CONTENT_PARTNER for CoStar (conference presentation — NOT a paid sponsor).
 *  - SPEAKER_OR_PANELIST_ONLY or LIKELY_PROSPECT for panelists.
 *  - Never promote panelist to advertiser/sponsor without separate sponsor evidence.
 *
 * Idempotent via ON CONFLICT on (normalized_name, coalesce(domain,'')) for orgs,
 * and unique name+type checks for events.
 */

const { normalizeName, ADVERTISER_CATEGORIES } = require('../../lib/types');

// ---- 5 VERIFIED_ADVERTISER organizations (§21) ------------------------------

const VERIFIED_ADVERTISERS = [
  {
    display_name: 'Hanley Investment Group',
    domain: 'hanleyinvestment.com',
    advertiser_categories: ['Brokerage and investment sales'],
    headquarters_state: 'CA',
    headquarters_city: 'Corona del Mar',
    description: 'Commercial real estate investment advisory and brokerage firm specializing in retail investment properties.',
    activity: 'RENTV Property Spotlight / dedicated property marketing email',
    observed_at: '2026-07-07',
    confidence: 0.99,
    requires_admin_confirm: true,
    evidence_type: 'EMAIL',
    evidence_tag: 'AUTHORIZED_INTERNAL_EMAIL',
    evidence_source_owner: 'RENTV authorized mailbox',
    evidence_title: 'RENTV Property Spotlight email — Hanley Investment Group property marketing',
    evidence_excerpt: 'Dedicated property marketing email observed in RENTV authorized mailbox. Requires admin confirmation before public exposure.',
    market_normalized: 'statewide california',
  },
  {
    display_name: 'Chase Partners',
    domain: 'chasepartners.com',
    advertiser_categories: ['Brokerage and investment sales'],
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
    description: 'Commercial real estate advisory and brokerage firm based in Southern California.',
    activity: 'Commercial Real Estate Talk sponsor',
    observed_at: '2026-06-29',
    confidence: 0.99,
    requires_admin_confirm: true,
    evidence_type: 'EMAIL',
    evidence_tag: 'AUTHORIZED_INTERNAL_EMAIL',
    evidence_source_owner: 'RENTV authorized mailbox',
    evidence_title: 'RENTV CRE Talk sponsor listing — Chase Partners',
    evidence_excerpt: 'CRE Talk sponsorship observed in RENTV authorized mailbox. Requires admin confirmation before public exposure.',
    market_normalized: 'statewide california',
  },
  {
    display_name: 'Fidelity Mortgage Lenders',
    domain: 'fidelityml.com',
    advertiser_categories: ['Debt fund, mortgage bank, private lender, and capital advisor'],
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
    description: 'Commercial mortgage lender serving the California CRE market.',
    activity: 'Commercial Real Estate Talk sponsor',
    observed_at: '2026-06-29',
    confidence: 0.99,
    requires_admin_confirm: true,
    evidence_type: 'EMAIL',
    evidence_tag: 'AUTHORIZED_INTERNAL_EMAIL',
    evidence_source_owner: 'RENTV authorized mailbox',
    evidence_title: 'RENTV CRE Talk sponsor listing — Fidelity Mortgage Lenders',
    evidence_excerpt: 'CRE Talk sponsorship observed in RENTV authorized mailbox. Requires admin confirmation before public exposure.',
    market_normalized: 'statewide california',
  },
  {
    display_name: 'Rockefeller Group',
    domain: 'rockgroup.com',
    advertiser_categories: ['Developer, owner, investor, REIT, and family office'],
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
    description: 'National real estate developer and investor with California CRE presence.',
    activity: 'Commercial Real Estate Talk sponsor',
    observed_at: '2026-06-29',
    confidence: 0.99,
    requires_admin_confirm: true,
    evidence_type: 'EMAIL',
    evidence_tag: 'AUTHORIZED_INTERNAL_EMAIL',
    evidence_source_owner: 'RENTV authorized mailbox',
    evidence_title: 'RENTV CRE Talk sponsor listing — Rockefeller Group',
    evidence_excerpt: 'CRE Talk sponsorship observed in RENTV authorized mailbox. Requires admin confirmation before public exposure.',
    market_normalized: 'statewide california',
  },
  {
    display_name: 'Provident Savings Bank',
    domain: 'providentbank.com',
    advertiser_categories: ['Commercial bank and credit union'],
    headquarters_state: 'CA',
    headquarters_city: 'Riverside',
    description: 'California community bank serving the CRE lending market.',
    activity: 'Commercial Real Estate Talk sponsor',
    observed_at: '2026-06-29',
    confidence: 0.99,
    requires_admin_confirm: true,
    evidence_type: 'EMAIL',
    evidence_tag: 'AUTHORIZED_INTERNAL_EMAIL',
    evidence_source_owner: 'RENTV authorized mailbox',
    evidence_title: 'RENTV CRE Talk sponsor listing — Provident Savings Bank',
    evidence_excerpt: 'CRE Talk sponsorship observed in RENTV authorized mailbox. Requires admin confirmation before public exposure.',
    market_normalized: 'statewide california',
  },
];

// ---- CoStar Group — VERIFIED_CONTENT_PARTNER (NOT a sponsor) ----------------
// §21: "Do not label as a paid sponsor without separate sponsor evidence."

const COSTAR = {
  display_name: 'CoStar Group',
  domain: 'costar.com',
  advertiser_categories: ['Proptech, data, software, AI, and marketplace'],
  headquarters_state: 'CA',
  headquarters_city: 'Los Angeles',
  description:
    'CoStar Group is a leading provider of commercial real estate data, analytics, and marketplace platforms. ' +
    'WARNING: CoStar presented a Greater Los Angeles market report at a RENTV conference on 2026-03-26 — this is a ' +
    'content/presentation relationship ONLY. Do not label as a paid sponsor without separate sponsor evidence.',
  market_normalized: 'greater los angeles',
  observed_at: '2026-03-26',
  confidence: 0.98,
  event_relationship_status: 'VERIFIED_CONTENT_PARTNER',
  event_activity: 'Greater Los Angeles market report presentation at a RENTV conference',
};

// ---- 22 panelist/prospect orgs (§21) ----------------------------------------
// Brokerages/well-known national firms → LIKELY_PROSPECT (RENTV sales opportunity).
// Others without clear commercial advertising relationship → SPEAKER_OR_PANELIST_ONLY.

const PANELISTS = [
  {
    display_name: 'Wonderful Real Estate Development',
    domain: 'wonderful.com',
    advertiser_categories: ['Developer, owner, investor, REIT, and family office'],
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
    relationship_status: 'SPEAKER_OR_PANELIST_ONLY',
  },
  {
    display_name: 'CBRE',
    domain: 'cbre.com',
    advertiser_categories: ['Brokerage and investment sales', 'Leasing and tenant representation'],
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
    relationship_status: 'LIKELY_PROSPECT',
  },
  {
    display_name: 'Trammell Crow Company',
    domain: 'trammellcrow.com',
    advertiser_categories: ['Developer, owner, investor, REIT, and family office'],
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
    relationship_status: 'LIKELY_PROSPECT',
  },
  {
    display_name: 'Rexford Industrial',
    domain: 'rexfordindustrial.com',
    advertiser_categories: ['Developer, owner, investor, REIT, and family office'],
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
    relationship_status: 'LIKELY_PROSPECT',
  },
  {
    display_name: 'NAI Capital Commercial',
    domain: 'naicapital.com',
    advertiser_categories: ['Brokerage and investment sales'],
    headquarters_state: 'CA',
    headquarters_city: 'Encino',
    relationship_status: 'LIKELY_PROSPECT',
  },
  {
    display_name: 'Premier Workspaces',
    domain: 'premierworkspaces.com',
    advertiser_categories: ['Coworking, flexible office, and business services'],
    headquarters_state: 'CA',
    headquarters_city: 'Irvine',
    relationship_status: 'SPEAKER_OR_PANELIST_ONLY',
  },
  {
    display_name: 'Cushman & Wakefield',
    domain: 'cushmanwakefield.com',
    advertiser_categories: ['Brokerage and investment sales', 'Leasing and tenant representation'],
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
    relationship_status: 'LIKELY_PROSPECT',
  },
  {
    display_name: 'Nikols Mortgage Fund',
    domain: null,
    advertiser_categories: ['Debt fund, mortgage bank, private lender, and capital advisor'],
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
    relationship_status: 'SPEAKER_OR_PANELIST_ONLY',
  },
  {
    display_name: 'Western Alliance Bank',
    domain: 'westernalliancebank.com',
    advertiser_categories: ['Commercial bank and credit union'],
    headquarters_state: 'CA',
    headquarters_city: 'Phoenix',
    relationship_status: 'LIKELY_PROSPECT',
  },
  {
    display_name: 'Commonwealth Land Title Company',
    domain: 'cltic.com',
    advertiser_categories: ['Title, escrow, settlement, and 1031 exchange'],
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
    relationship_status: 'LIKELY_PROSPECT',
  },
  {
    display_name: 'George Smith Partners',
    domain: 'gspartners.com',
    advertiser_categories: ['Debt fund, mortgage bank, private lender, and capital advisor'],
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
    relationship_status: 'LIKELY_PROSPECT',
  },
  {
    display_name: 'Paragon Commercial Group',
    domain: null,
    advertiser_categories: ['Developer, owner, investor, REIT, and family office'],
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
    relationship_status: 'SPEAKER_OR_PANELIST_ONLY',
  },
  {
    display_name: 'IPA',
    domain: 'ipausa.com',
    advertiser_categories: ['Brokerage and investment sales'],
    headquarters_state: 'CA',
    headquarters_city: 'Irvine',
    relationship_status: 'LIKELY_PROSPECT',
  },
  {
    display_name: 'The Festival Companies',
    domain: null,
    advertiser_categories: ['Developer, owner, investor, REIT, and family office'],
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
    relationship_status: 'SPEAKER_OR_PANELIST_ONLY',
  },
  {
    display_name: 'Westside Retail',
    domain: null,
    advertiser_categories: ['Leasing and tenant representation'],
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
    relationship_status: 'SPEAKER_OR_PANELIST_ONLY',
  },
  {
    display_name: 'LaTerra Development',
    domain: 'laterradevelopment.com',
    advertiser_categories: ['Developer, owner, investor, REIT, and family office'],
    headquarters_state: 'CA',
    headquarters_city: 'Brentwood',
    relationship_status: 'SPEAKER_OR_PANELIST_ONLY',
  },
  {
    display_name: 'Cypress Equity Investments',
    domain: null,
    advertiser_categories: ['Developer, owner, investor, REIT, and family office'],
    headquarters_state: 'CA',
    headquarters_city: 'Santa Monica',
    relationship_status: 'SPEAKER_OR_PANELIST_ONLY',
  },
  {
    display_name: 'Colliers',
    domain: 'colliers.com',
    advertiser_categories: ['Brokerage and investment sales', 'Leasing and tenant representation'],
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
    relationship_status: 'LIKELY_PROSPECT',
  },
  {
    display_name: 'Walker Realty Capital',
    domain: null,
    advertiser_categories: ['Debt fund, mortgage bank, private lender, and capital advisor'],
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
    relationship_status: 'SPEAKER_OR_PANELIST_ONLY',
  },
  {
    display_name: 'The Zacuto Group',
    domain: null,
    advertiser_categories: ['Brokerage and investment sales'],
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
    relationship_status: 'SPEAKER_OR_PANELIST_ONLY',
  },
  {
    display_name: 'Eve Capital',
    domain: null,
    advertiser_categories: ['Developer, owner, investor, REIT, and family office'],
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
    relationship_status: 'SPEAKER_OR_PANELIST_ONLY',
  },
  {
    display_name: 'Lyon Stahl Investment Real Estate',
    domain: 'lyonstahl.com',
    advertiser_categories: ['Brokerage and investment sales'],
    headquarters_state: 'CA',
    headquarters_city: 'El Segundo',
    relationship_status: 'LIKELY_PROSPECT',
  },
];

// --------------------------------------------------------------------------
// Helper: upsert one org; return {id, wasInserted}
// --------------------------------------------------------------------------
async function upsertOrg(client, data) {
  const nn = normalizeName(data.display_name);
  const domain = data.domain || null;

  const res = await client.query(
    `INSERT INTO organizations (
       display_name, normalized_name, domain,
       advertiser_categories, description,
       headquarters_state, headquarters_city,
       active_status, first_seen_at, last_seen_at
     ) VALUES ($1,$2,$3,$4,$5,$6,$7,'ACTIVE',now(),now())
     ON CONFLICT (normalized_name, coalesce(domain,''))
     DO UPDATE SET
       display_name=EXCLUDED.display_name,
       advertiser_categories=EXCLUDED.advertiser_categories,
       description=EXCLUDED.description,
       headquarters_state=EXCLUDED.headquarters_state,
       headquarters_city=EXCLUDED.headquarters_city,
       last_seen_at=now()
     RETURNING id, (xmax = 0) AS is_insert`,
    [
      data.display_name,
      nn,
      domain,
      JSON.stringify(data.advertiser_categories || []),
      data.description || null,
      data.headquarters_state || null,
      data.headquarters_city || null,
    ]
  );
  return { id: res.rows[0].id, wasInserted: res.rows[0].is_insert };
}

// Helper: upsert evidence_record; returns id
async function upsertEvidence(client, data) {
  // evidence_records has no unique constraint — check by source_title + evidence_type + observed_at to avoid dupes
  const existing = await client.query(
    `SELECT id FROM evidence_records WHERE source_title=$1 AND evidence_type=$2 AND observed_at=$3 LIMIT 1`,
    [data.source_title, data.evidence_type, data.observed_at]
  );
  if (existing.rows.length > 0) return { id: existing.rows[0].id, wasInserted: false };

  const res = await client.query(
    `INSERT INTO evidence_records (
       evidence_type, source_url, source_title, source_owner,
       observed_at, retrieved_at, excerpt, confidence, export_allowed
     ) VALUES ($1,$2,$3,$4,$5,now(),$6,$7,$8)
     RETURNING id`,
    [
      data.evidence_type,
      data.source_url || null,
      data.source_title,
      data.source_owner || null,
      data.observed_at || null,
      data.excerpt || null,
      data.confidence || 0.5,
      data.export_allowed || false,
    ]
  );
  return { id: res.rows[0].id, wasInserted: true };
}

// Helper: upsert ad_sighting (idempotent by org+relationship_status+observed_at)
async function upsertAdSighting(client, { org_id, relationship_status, observed_at, evidence_id, market_id, confidence, headline }) {
  const existing = await client.query(
    `SELECT id FROM ad_sightings WHERE organization_id=$1 AND relationship_status=$2 AND observed_at=$3 LIMIT 1`,
    [org_id, relationship_status, observed_at]
  );
  if (existing.rows.length > 0) return { id: existing.rows[0].id, wasInserted: false };

  const res = await client.query(
    `INSERT INTO ad_sightings (
       organization_id, relationship_status, observed_at,
       first_observed_at, last_observed_at,
       evidence_id, market_id, confidence,
       verification_status, headline
     ) VALUES ($1,$2,$3,$3,$3,$4,$5,$6,'UNVERIFIED',$7)
     RETURNING id`,
    [org_id, relationship_status, observed_at, evidence_id, market_id || null, confidence || 0.5, headline || null]
  );
  return { id: res.rows[0].id, wasInserted: true };
}

// Helper: upsert event by name+state+start_date
async function upsertEvent(client, data) {
  const existing = await client.query(
    `SELECT id FROM events WHERE name=$1 AND state=$2 AND start_date=$3 LIMIT 1`,
    [data.name, data.state, data.start_date]
  );
  if (existing.rows.length > 0) return { id: existing.rows[0].id, wasInserted: false };

  const res = await client.query(
    `INSERT INTO events (
       name, event_type, start_date, end_date, city, state, official_url, source_evidence_id
     ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8)
     RETURNING id`,
    [
      data.name,
      data.event_type || 'CONFERENCE',
      data.start_date,
      data.end_date || data.start_date,
      data.city || null,
      data.state || null,
      data.official_url || null,
      data.source_evidence_id || null,
    ]
  );
  return { id: res.rows[0].id, wasInserted: true };
}

// Helper: upsert event_relationship (idempotent by event+org+relationship_status)
async function upsertEventRelationship(client, { event_id, org_id, relationship_status, session_title, observed_at, confidence, evidence_id }) {
  const existing = await client.query(
    `SELECT id FROM event_relationships WHERE event_id=$1 AND organization_id=$2 AND relationship_status=$3 LIMIT 1`,
    [event_id, org_id, relationship_status]
  );
  if (existing.rows.length > 0) return { id: existing.rows[0].id, wasInserted: false };

  const res = await client.query(
    `INSERT INTO event_relationships (
       event_id, organization_id, relationship_status,
       session_title, observed_at, confidence, evidence_id
     ) VALUES ($1,$2,$3,$4,$5,$6,$7)
     RETURNING id`,
    [event_id, org_id, relationship_status, session_title || null, observed_at || null, confidence || 0.5, evidence_id || null]
  );
  return { id: res.rows[0].id, wasInserted: true };
}

// --------------------------------------------------------------------------

async function seedOrganizations(client) {
  const counts = {
    orgs_inserted: 0,
    orgs_updated: 0,
    evidence_inserted: 0,
    ad_sightings_inserted: 0,
    event_relationships_inserted: 0,
    events_inserted: 0,
  };

  // Look up CA statewide and Greater LA market IDs
  const caMarket = await client.query(
    `SELECT id FROM markets WHERE normalized_name=$1 LIMIT 1`,
    ['statewide california']
  );
  const laMarket = await client.query(
    `SELECT id FROM markets WHERE normalized_name LIKE '%los angeles%' AND state='CA' LIMIT 1`
  );
  const caMarketId = caMarket.rows[0]?.id || null;
  const laMarketId = laMarket.rows[0]?.id || null;

  // ---- RENTV as organizer org (needed for events) ----
  const rentvOrg = await upsertOrg(client, {
    display_name: 'RENTV',
    domain: 'rentv.com',
    advertiser_categories: ['Association, conference, publication, and media company'],
    description: 'RENTV — leading commercial real estate media company serving California and Arizona.',
    headquarters_state: 'CA',
    headquarters_city: 'Los Angeles',
  });
  if (rentvOrg.wasInserted) counts.orgs_inserted++; else counts.orgs_updated++;

  // ---- Seed VERIFIED_ADVERTISER orgs ----
  for (const va of VERIFIED_ADVERTISERS) {
    const { id: orgId, wasInserted } = await upsertOrg(client, va);
    if (wasInserted) counts.orgs_inserted++; else counts.orgs_updated++;

    // evidence_record
    const { id: evidenceId, wasInserted: evInserted } = await upsertEvidence(client, {
      evidence_type: va.evidence_type,
      source_title: va.evidence_title,
      source_owner: va.evidence_source_owner,
      observed_at: va.observed_at,
      excerpt: va.evidence_excerpt,
      confidence: va.confidence,
      export_allowed: false, // AUTHORIZED_INTERNAL_EMAIL — admin must confirm before export
    });
    if (evInserted) counts.evidence_inserted++;

    // Look up market id by normalized_name
    const mktRes = await client.query(
      `SELECT id FROM markets WHERE normalized_name=$1 LIMIT 1`,
      [va.market_normalized]
    );
    const marketId = mktRes.rows[0]?.id || caMarketId;

    // ad_sighting with VERIFIED_ADVERTISER status, verification_status=UNVERIFIED (admin confirm needed)
    const { wasInserted: adInserted } = await upsertAdSighting(client, {
      org_id: orgId,
      relationship_status: 'VERIFIED_ADVERTISER',
      observed_at: va.observed_at,
      evidence_id: evidenceId,
      market_id: marketId,
      confidence: va.confidence,
      headline: va.activity,
    });
    if (adInserted) counts.ad_sightings_inserted++;

    // organization_markets link
    await client.query(
      `INSERT INTO organization_markets (organization_id, market_id, relationship_type, confidence, evidence_id)
       VALUES ($1,$2,'OPERATES_IN',$3,$4)
       ON CONFLICT (organization_id, market_id, relationship_type) DO NOTHING`,
      [orgId, marketId, va.confidence, evidenceId]
    );

    // Tag: AUTHORIZED_INTERNAL_EMAIL — stored as a tag for admin visibility
    const tagRes = await client.query(
      `INSERT INTO tags (label) VALUES ($1) ON CONFLICT (label) DO UPDATE SET label=EXCLUDED.label RETURNING id`,
      [va.evidence_tag]
    );
    await client.query(
      `INSERT INTO organization_tags (organization_id, tag_id) VALUES ($1,$2) ON CONFLICT DO NOTHING`,
      [orgId, tagRes.rows[0].id]
    );

    // Audit log entry noting admin confirm required (idempotent: one per org+action)
    if (va.requires_admin_confirm) {
      const alExist = await client.query(
        `SELECT id FROM audit_logs WHERE action='SEED_REQUIRES_ADMIN_CONFIRM' AND entity_id=$1 LIMIT 1`,
        [orgId]
      );
      if (alExist.rows.length === 0) {
        await client.query(
          `INSERT INTO audit_logs (action, entity_table, entity_id, actor, detail)
           VALUES ('SEED_REQUIRES_ADMIN_CONFIRM','organizations',$1,'seed',
             $2::jsonb)`,
          [
            orgId,
            JSON.stringify({
              note: 'Record seeded from AUTHORIZED_INTERNAL_EMAIL evidence. Admin must confirm before public exposure.',
              tag: va.evidence_tag,
              observed_at: va.observed_at,
            }),
          ]
        );
      }
    }
  }

  // ---- CoStar Group — VERIFIED_CONTENT_PARTNER ----
  const { id: costarId, wasInserted: costarInserted } = await upsertOrg(client, COSTAR);
  if (costarInserted) counts.orgs_inserted++; else counts.orgs_updated++;

  // evidence for CoStar conference presentation
  const { id: costarEvidenceId, wasInserted: costarEvInserted } = await upsertEvidence(client, {
    evidence_type: 'MANUAL_NOTE',
    source_title: 'RENTV conference — CoStar Group Greater Los Angeles market report presentation',
    source_owner: 'RENTV',
    observed_at: COSTAR.observed_at,
    excerpt:
      'CoStar Group presented a Greater Los Angeles market report at a RENTV conference on ' +
      COSTAR.observed_at +
      '. This is a content/research presentation relationship. ' +
      COSTAR.description.split('WARNING:')[1]?.trim() || '',
    confidence: COSTAR.confidence,
    export_allowed: true,
  });
  if (costarEvInserted) counts.evidence_inserted++;

  // Seed the RENTV conference event (for CoStar + panelists)
  const { id: conferenceEventId, wasInserted: confInserted } = await upsertEvent(client, {
    name: 'RENTV Greater Los Angeles CRE Conference 2026',
    event_type: 'CONFERENCE',
    start_date: '2026-03-26',
    end_date: '2026-03-26',
    city: 'Los Angeles',
    state: 'CA',
    official_url: 'https://www.rentv.com',
    source_evidence_id: costarEvidenceId,
  });
  if (confInserted) counts.events_inserted++;

  // CoStar event_relationship = VERIFIED_CONTENT_PARTNER (NOT SPONSOR)
  const { wasInserted: costarRelInserted } = await upsertEventRelationship(client, {
    event_id: conferenceEventId,
    org_id: costarId,
    relationship_status: 'VERIFIED_CONTENT_PARTNER',
    session_title: 'Greater Los Angeles market report presentation',
    observed_at: COSTAR.observed_at,
    confidence: COSTAR.confidence,
    evidence_id: costarEvidenceId,
  });
  if (costarRelInserted) counts.event_relationships_inserted++;

  // Link CoStar to LA market
  if (laMarketId) {
    await client.query(
      `INSERT INTO organization_markets (organization_id, market_id, relationship_type, confidence, evidence_id)
       VALUES ($1,$2,'OPERATES_IN',$3,$4)
       ON CONFLICT (organization_id, market_id, relationship_type) DO NOTHING`,
      [costarId, laMarketId, COSTAR.confidence, costarEvidenceId]
    );
  }

  // Audit log: explicit warning for CoStar (idempotent)
  const costarAlExist = await client.query(
    `SELECT id FROM audit_logs WHERE action='SEED_CONTENT_PARTNER_WARNING' AND entity_id=$1 LIMIT 1`,
    [costarId]
  );
  if (costarAlExist.rows.length === 0) {
  await client.query(
    `INSERT INTO audit_logs (action, entity_table, entity_id, actor, detail)
     VALUES ('SEED_CONTENT_PARTNER_WARNING','organizations',$1,'seed',$2::jsonb)`,
    [
      costarId,
      JSON.stringify({
        warning:
          'CoStar Group is a VERIFIED_CONTENT_PARTNER (conference presenter), NOT a paid sponsor. ' +
          'Do not label as a paid sponsor without separate sponsor evidence.',
        observed_at: COSTAR.observed_at,
        event: 'RENTV Greater Los Angeles CRE Conference 2026',
      }),
    ]
  );
  } // end idempotent CoStar audit log

  // ---- Seed a second RENTV CRE Talk event for the CRE Talk sponsors ----
  const creTalkEvidenceId = await (async () => {
    const { id } = await upsertEvidence(client, {
      evidence_type: 'EMAIL',
      source_title: 'RENTV CRE Talk — sponsor listing email 2026-06-29',
      source_owner: 'RENTV authorized mailbox',
      observed_at: '2026-06-29',
      excerpt:
        'RENTV CRE Talk sponsors observed in authorized RENTV mailbox on 2026-06-29. ' +
        'Requires admin confirmation before public exposure.',
      confidence: 0.99,
      export_allowed: false,
    });
    return id;
  })();

  const { id: creTalkEventId, wasInserted: cteInserted } = await upsertEvent(client, {
    name: 'RENTV Commercial Real Estate Talk 2026-06-29',
    event_type: 'CONFERENCE',
    start_date: '2026-06-29',
    end_date: '2026-06-29',
    city: 'Los Angeles',
    state: 'CA',
    official_url: 'https://www.rentv.com',
    source_evidence_id: creTalkEvidenceId,
  });
  if (cteInserted) counts.events_inserted++;

  // ---- Seed 22 panelist orgs ----
  const panelEvidenceId = await (async () => {
    const { id } = await upsertEvidence(client, {
      evidence_type: 'MANUAL_NOTE',
      source_title: 'RENTV conference panelist roster — seeded from spec §21',
      source_owner: 'RENTV',
      observed_at: '2026-03-26',
      excerpt:
        'Companies listed as conference participants (panelists/speakers) in RENTV conference records. ' +
        'SPEAKER_OR_PANELIST_ONLY status — no sponsorship claimed without separate evidence.',
      confidence: 0.85,
      export_allowed: true,
    });
    return id;
  })();

  for (const p of PANELISTS) {
    const { id: orgId, wasInserted } = await upsertOrg(client, p);
    if (wasInserted) counts.orgs_inserted++; else counts.orgs_updated++;

    // event_relationship (panelist role) — do NOT use ad_sightings with VERIFIED_* status
    const { wasInserted: relInserted } = await upsertEventRelationship(client, {
      event_id: conferenceEventId,
      org_id: orgId,
      relationship_status: p.relationship_status,
      session_title: null,
      observed_at: '2026-03-26',
      confidence: 0.85,
      evidence_id: panelEvidenceId,
    });
    if (relInserted) counts.event_relationships_inserted++;

    // link to CA statewide market
    if (caMarketId) {
      await client.query(
        `INSERT INTO organization_markets (organization_id, market_id, relationship_type, confidence)
         VALUES ($1,$2,'OPERATES_IN',0.85)
         ON CONFLICT (organization_id, market_id, relationship_type) DO NOTHING`,
        [orgId, caMarketId]
      );
    }
  }

  return counts;
}

module.exports = { seedOrganizations };