Baltic CVM Lab Competitive pricing and retention decision intelligence · Latvia

Semantic layer and lineage

Feature definitions, ownership, lawful basis and source reconciliation

The estate does not arrive tidy

Four systems, no shared key, and definitions that disagree. That is the normal condition, and reconciling it is what the semantic plane is for. An earlier version of this work emitted one clean table, which quietly assumed the problem away.

SystemKeyWhat it owns Rows
Billing / BSSmsisdn_hashMoney and contracts. Amounts GROSS of VAT, dates as YYYYMMDD integers.12,000
CRMparty_idThe customer relationship. ARPU NET of VAT as a three-month mean, ISO dates.12,000
Networkimsi_hashUsage counters. No customer concept at all.12,000
Carecustomer_refTickets. Key is ambiguous — two merged systems, half MSISDN and half party id.2,309
Identity crosswalkall three keys Incomplete, as every real one is. 11,656

344 of 12,000 subscribers cannot be resolved to a party id at all and are excluded from every decision — counted, never silently dropped. Nothing downstream can be correct for a subscriber the operator cannot identify.

Feature catalogue

FeatureSourcesOwnerLawful basis CompleteConflict
arpu_eur
Monthly recurring revenue per subscriber, NET of VAT, taken as the three-month mean. CRM is authoritative because it already carries the three-month m
crm.arpu · bss.billed_amount_eur revenue-analytics contract 100.0% 1.3%
contract_months_remaining
Whole months from today to contract expiry; 0 if out of contract.
bss.contract_end_dt · crm.contract_expiry commercial-ops contract 100.0% 11.9%
is_active
Subscriber has a live service relationship at the observation date.
bss.status_cd · crm.lifecycle_stage commercial-ops contract 100.0% 1.0%
usage_trend_3m
Ratio of last-month data usage to the prior three-month mean.
network.usage_ratio_3m network-analytics legitimate interest ePrivacy 100.0%
care_unresolved_6m
Subscriber has an OPEN care ticket at the observation date.
care.ticket_state customer-care contract 97.1% 2.9%
mnp_enquiry_60d
Number-portability enquiry raised in the last 60 days.
bss.status_cd · crm.lifecycle_stage commercial-ops contract 0.0%
has_bundle
Subscriber holds a fixed or convergent product alongside mobile.
crm.bundle_ind commercial-ops contract 100.0%

Completeness is measured against the SLO declared in the registry; conflict is the share of rows where two sources disagree beyond what their definitions explain. The layer never silently picks a side — it applies a declared rule and counts the disagreement, because the disagreement rate is itself a finding about which system can be trusted for which question.

What the conflicts actually cost

FeatureDisagreementRule applied and why
arpu_eur1.3%CRM preferred (already a 3-month net mean); BSS de-VATed for backfill and for measuring disagreement.
contract_months_remaining11.9%BSS authoritative — it is what billing enforces. CRM copies that disagree or are blank are counted, not overwritten.
is_active1.0%BSS status A is truth. CRM marks suspended customers CHURNED, which would overstate the churn rate.
care_unresolved_6m2.9%Key is ambiguous — half the refs are MSISDN hashes, half are party ids, because two systems merged. Tickets that match neither attach to nobody.

Lineage — one feature, end to end

StageWhat happens to arpu_eur
SourceCRM arpu — net of VAT, three-month mean. BSS billed_amount_eur — gross, single month.
ReconcileBSS de-VATed at 21%. CRM preferred because it already carries the three-month mean the definition requires. BSS backfills gaps.
MeasureResidual disagreement beyond the definitional gap recorded per run — currently 1.3%.
ConsumeMargin at risk = arpu × horizon × contribution margin, in the policy engine. Changing the definition here changes every euro figure on the retention page — which is exactly why it lives in git.

Honest gaps in this layer

mnp_enquiry_60d is declared but not derived. The registry marks this a provenance_gap, deliberately not a known_conflict — a conflict is two sources disagreeing, this is no source at all, and collapsing the two would let a missing origin read as a reconciliation problem. No source in this reference implementation carries it; the value is taken from ground truth and labelled 0% complete rather than presented as though it had an origin. A feature that cannot be traced to a source should say so on the screen where people look for its provenance.
Traffic data is declared, not cleared. usage_trend_3m carries an ePrivacy flag: Article 6 permits traffic-data processing for billing, but retention analytics is a different purpose needing its own basis. It is treated as consent-gated until confirmed — the stricter reading, deliberately.
No git commit per feature yet. The concept specifies the commit hash alongside each definition so a decision can be replayed against the exact definition in force at the time. The registry is versioned; the per-feature binding is not built.