What is real, what is synthetic, and what has been withdrawn
| Source | What it is | Used for |
|---|---|---|
| Public price lists real |
Tariffs captured from LMT, Tele2 and Bite websites. Raw HTML archived before parsing, so every figure traces to a page and a timestamp. | Market pressure, personas pricing |
| Synthetic subscriber base generated |
12,000 subscribers with known causal structure, calibrated to published Baltic churn rates. No operator data of any kind. | Retention economics, vendor plane |
The synthetic base exists because individual treatment effects cannot be observed in real data — you would need a live randomised campaign running for a quarter. Here the true effect is known by construction, so the argument can be shown now and the experiment that would confirm it designed properly.
| robots.txt | respected |
| Request rate | 3-second delay per host, 40-page cap per run |
| User agent | Identifies itself and carries a contact address |
| Archive | Raw HTML stored before parsing — re-parsing never re-fetches |
| Rule | Why |
|---|---|
| Compare standard rates, not headlines | A promotion is a temporary position. In this market the headlines do not even rank in the same order as the standard rates — Tele2's unlimited plan promotes below its own 25 GB plan. |
| Test availability before comparing | A “from €X” figure is not a firm rate, and an age-gated tariff is not available to someone outside the age band. Both are excluded. Without this the cheapest plan in the market is usually one restricted to schoolchildren. |
| Report unserved as a finding | An operator with no qualifying family plan is competitively exposed. Substituting a cheaper unrelated tariff would hide exactly that. |
| Risk model training set | Control-group rows only — a model fitted on the whole base learns a churn rate last quarter's campaign already altered |
| Ground truth | Used to value campaigns, never to rank them; ranking uses model estimates throughout |
| Commercial assumptions | Horizon, margin, contact cost and offer depth are configuration, not constants — they are business decisions |
| Base calibration | Enforced in CI; a generator drifting outside published churn bands fails the build |
| Gap | Consequence |
|---|---|
| Multi-line pricing not extracted | Family and Connected Home personas show unserved for every operator |
| Evidence store | Decisions are produced but not persisted append-only, so the decision log — the asset that cannot be regenerated — does not yet exist |
| Lithuania and Estonia | Configured but URLs unverified; only Latvia is live |
| Scheduled refresh | Pages are snapshots, regenerated by hand |
Everything is deterministic apart from the model fits, which are seeded. Collection, extraction, pricing and reporting run without any LLM and without an API key — allowance parsing is rule-based across Latvian, Lithuanian and Estonian, with the method recorded per field. 35 regression tests cover the defects this project actually shipped and then found; each cites the case that produced it.