Feature definitions, ownership, lawful basis and source reconciliation
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.
| System | Key | What it owns | Rows |
|---|---|---|---|
| Billing / BSS | msisdn_hash | Money and contracts. Amounts GROSS of VAT, dates as YYYYMMDD integers. | 12,000 |
| CRM | party_id | The customer relationship. ARPU NET of VAT as a three-month mean, ISO dates. | 12,000 |
| Network | imsi_hash | Usage counters. No customer concept at all. | 12,000 |
| Care | customer_ref | Tickets. Key is ambiguous — two merged systems, half MSISDN and half party id. | 2,309 |
| Identity crosswalk | all 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 | Sources | Owner | Lawful basis | Complete | Conflict |
|---|---|---|---|---|---|
| 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.
| Feature | Disagreement | Rule applied and why |
|---|---|---|
| arpu_eur | 1.3% | CRM preferred (already a 3-month net mean); BSS de-VATed for backfill and for measuring disagreement. |
| contract_months_remaining | 11.9% | BSS authoritative — it is what billing enforces. CRM copies that disagree or are blank are counted, not overwritten. |
| is_active | 1.0% | BSS status A is truth. CRM marks suspended customers CHURNED, which would overstate the churn rate. |
| care_unresolved_6m | 2.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. |
| Stage | What happens to arpu_eur |
|---|---|
| Source | CRM arpu — net of VAT,
three-month mean. BSS billed_amount_eur — gross, single month. |
| Reconcile | BSS de-VATed at 21%. CRM preferred because it already carries the three-month mean the definition requires. BSS backfills gaps. |
| Measure | Residual disagreement beyond the definitional gap recorded per run — currently 1.3%. |
| Consume | Margin 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. |
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. |