Skip to main content

Product breakdown aliases and exclusions

Launchpad V2 mirrors V1's ProductBreakdownConfigService for two client-level rules that affect the Product breakdown:

  • An alias says that a raw CRM product name is another spelling of a canonical product. Matching is exact after trimming and lowercasing. The matching buckets are combined before the report returns product totals.
  • An exclusion says that a canonical product is not an advertised reporting product. Only that explicitly configured bucket is removed. It is not folded into "Other" and its value is not redistributed.

Unmapped products remain visible. Aliasing never changes total dollars or order weight; it only changes which product bucket owns them. Exclusions are the only configuration that changes the displayed totals. Revenue remains decimal dollars, and initial/upsell lifecycle classification remains owned by the canonical marketing_sales ledger rather than this display configuration.

Configuration is stored in Supabase tables product_crm_aliases and product_crm_exclusions. Migration 045_v1_operational_parity.sql owns their schema: each row has a V2 client UUID and the original V1 numeric v1_id, with uniqueness enforced for both the imported row identity and its client-scoped product value. Migration 061_product_breakdown_config.sql is deliberately an upgrade-safe addition that creates case-insensitive lookup indexes without redefining or replacing those tables. The server trims and lowercases values while loading them.

Some current connector spellings did not exist in the V1 configuration. Those aliases use a separate, explicit provenance namespace: source_provenance is v1_cloud_sql with a non-null v1_id for exact imports, while a verified V2 source alias has a named provenance and a null v1_id. Migration 062 adds only that schema capability and remains safe on an empty client table. The post-client port:v1-product-config importer installs four Top Speed spellings proven from retained attribution events: b29bb / b29 blue brick map to SCG - B29 Blue Brick, and tsg-bfb / ball first board map to BFB. The reproducible read-only query and retained result are checked in under architecture/evidence/top-speed-product-alias-audit-2026-08-26.md. The V1 verifier deliberately compares only rows carrying a non-null V1 identity, so native source alignment does not weaken or create false drift in the V1 audit. This also keeps the dry-run verifier compatible while migration 062 is awaiting deployment.

The importer checks normalized (client, lower(trim(source))) identity before writing, refuses missing tenants, conflicting canonical decisions, or a row owned by the V1 identity/provenance namespace, and verifies all four native mappings have null v1_id plus the exact native provenance after apply. Runtime performs the same normalized duplicate rejection and preserves raw buckets if configuration is ambiguous. The production duplicate preflight and result are retained in architecture/evidence/product-alias-normalized-duplicate-preflight-2026-08-26.md.

The server loads rules only for resolved, tenant-scoped clients. Product filters are expanded to include raw aliases before ClickHouse aggregation, so selecting a canonical product also finds its raw spellings and then combines them exactly once. Each client receives its own bound product predicate: the same raw name can map to different products in two clients without either mapping leaking across tenants. Aggregate access loads only clients already authorized by the report scope. If one configuration cannot be resolved, the original requested filter remains bound for that client; the query never broadens to unfiltered product data. For an unfiltered breakdown, unavailable configuration preserves raw buckets and avoids silent data loss.

V1 production configuration can be reconciled with the dry-run-first command:

npm run port:v1-product-config
npm run port:v1-product-config -- --verify
npm run port:v1-product-config -- --apply

The command requires the governed/local Cloud SQL proxy and Supabase service role environment. It maps V1 numeric client IDs through clients.v1_import, preserves every valid V1 row and timestamp, and performs idempotent upserts on (client_id, v1_id). --verify compares the imported source/canonical values and detects missing or extra imported identities. --apply is a production data write and must follow migration review; the default run is read-only.