☿ Kairos War Room
Gated source note · private

Kairos — Cancellation Reason Tables Read (2026-09-02)

First content read of the three warehouse tables both prior audits parked. Aggregate only: no customer rows, no identifiers, no individual free text. Recurring-phrase output is gated at five or more distinct users per phrase.

Why this was never read

Two exhaustive object ledgers found these tables and dispositioned them correctly for their own decisions. The Payment Recovery ledger parked them because the churn and refund signal was already consumed through the curated claude.* layer, so the raw upstream tables added no separate recovery counterfactual. The Retention Economics ledger classified them DECISION_CANDIDATE_CURRENTLY_BLOCKED — the right evidence for the retention question, blocked by the expired BigQuery principal. Lee re-authenticated on 2026-09-02 and the block cleared.

The lesson is not that the audits missed something. It is that PARKED and CURRENTLY_BLOCKED both read as "handled" on a status board while one of them means "nobody has looked yet."

What each table actually is

postgre.mv_vpn_cancel_reasons — system classifier, stale. Not a survey. 96,565 rows in three buckets: BILLING_RETRY 39,089, BILLING RETRY 32,879, VOLUNTARY 24,597. The two spellings are the same bucket split by a label typo across 72,000 rows. The comment column holds between 9 and 52 distinct machine strings per bucket, averaging about 20 characters — status text, not customer voice. Every row carries _airbyte_extracted_at = 2026-04-28; the Airbyte sync has been dead for four months.

App_usage.App_usage_cancel_reasons — a real in-app cancellation survey, live. Multi-select picked list plus an optional free-text field capped at 100 characters. Running 2025-06-19 through 2026-09-01, current. Platform split: iOS 7,014 · Android 6,419 · Windows 4,847 · Mac 192. MN has been collecting stated cancellation reasons for fourteen and a half months across four platforms.

Reasons are multi-select, so the correct denominator is distinct respondents (9,060) and the shares overlap. Ranking by single-reason rows understates every reason that is commonly picked in combination.

Reason distinct users % of 9,060 respondents
cancelOther 3,251 35.9%
cancelTooExpensive 3,240 35.8%
cancelDisconnects 1,807 19.9%
cancelError7040 1,649 18.2%
cancelUsabilityIssues 1,633 18.0%
cancelTesting 1,565 17.3%
cancelMissingFeatures 1,374 15.2%
cancelSpeed 1,341 14.8%
cancelUnableToAccessBlockedSites 1,314 14.5%
cancelDowntimes 1,138 12.6%
cancelLatency 1,086 12.0%

Any technical reason reaches 5,501 users — 60.7% — against price at 35.8%. Below the top two, everything sits in a tight 12–20% band; ordinal rank inside that band is not a meaningful distinction.

About 180 mentions carry truncated values (cance, canc, ca, c), a minor pipeline defect.

App_usage.users_churn_reasons — misleadingly named. A behavioural churn-feature table, not reasons. 119,954 rows, one per churned user, 2023-04-30 through 2026-09-02. Thirty-eight columns of behaviour: traffic at 7, 14 and 28 days, device counts, event counts, error counts, connection-success counts, was_connected flags, refund flag, churn revenue. 8.5% refunded.

The finding

56.7% of churned users carry was_connected_total = false — roughly 68,000 people with no successful connection recorded before they left.

Epistemic status: hypothesis, not fact. was_connected_total semantics are uncommissioned. It may mean "no connection telemetry recorded" rather than "never connected"; telemetry coverage by platform, app version and consent is unknown, and the table spans a period during which instrumentation may have changed. Commissioning that one field with Simonas is the highest-value semantic question currently open.

If it holds even directionally it converges with two independent records: a numbered error is the third-largest stated cancellation reason, and the ban-chain pile dominates the public-review sweep of 2026-09-02. The shared story is that a large share of buyers never reach a working product, and that the business has been reading this as price sensitivity.

What the free text says

3,947 responses carry free text; 2,255 distinct values. A regex theme pass classified only about a third, so those buckets are lower bounds rather than a distribution. The recurring-phrase pass explains why: most of the field is filler — nothing (26 users), cancel (19), . (15), plus single letters, ok, yes, no, good. The field is optional and people type past it.

What survives as genuine signal:

Consequences

  1. Do not recommend that MN build cancellation-reason capture. They run it. The defect is different: response volume peaked at 1,947 in February 2026 and has fallen to about 900 per month, it covers only app-surface cancellations, and it is self-selected. It is under-collected and under-read, not absent.
  2. The episodic-user hypothesis is worth sizing, not headlining. If a material slice of churn is task completion, the "preventable customer loss" denominator is smaller than gross churn and the ICP question changes shape.
  3. mv_vpn_cancel_reasons needs its sync repaired or formally retired, and the BILLING_RETRY / BILLING RETRY label split fixed. A stale table with a live-sounding name is the failure mode the truth guard exists for.
  4. Commission was_connected_total before any number derived from it is quoted outward.

Method and limits

All figures come from aggregate queries against mysterium-bq on 2026-09-02 under the re-authenticated sarunas@mysterium.network principal. No forbidden purchase-token object was touched. No raw customer row, identifier or individual free-text string was returned or is recorded here. Survey coverage against total churn is partial and self-selected; none of the picked-list counts should be read as population incidence.

September 7 revalidation — preserve the active rework

The earlier recommendation to repair/retire the classifier and investigate response decline is narrowed by current source checks. The in-app survey continues to record submissions: 902 rows in August and 214 dated September 1–6 (partial month). These are submission rows, not unique people or an exposure denominator. No decay claim follows.

MN already has a P0 cancellation rework in progress. On the current ClickUp read, app task 869dyxrqy is ready for deploy, dashboard task 869dyxruu is Closed, and analytics task 869dyxrze is to do under Simonas Rimkus. The specifications call for optional reasons, a full journey funnel and separation of ineligible retention branches. The detailed analytics Google Doc is not accessible to Šaras’s existing identity. Task closure/readiness is not deployment evidence. Preserve this work; do not create a replacement survey or dashboard.

A second live refresh at 17:29Z confirms the same ownership state after it changed earlier today: the app remains ready for deploy, the dashboard is Closed, and analytics remains to do under Simonas with an October 2 due date. The dashboard’s only comment points to app.test.mysteriumvpn.com, so its Closed status is not production-deployment evidence. An aggregate-only BigQuery check still finds 18,633 survey rows, 214 dated September 1–6 and none dated September 7 at query time. This preserves the existing work and does not turn submission rows into an eligible/exposed denominator.

The old client snapshot displays “Error7040”; the new app task explicitly names “Error7042”. That establishes differing specified labels, not code equivalence or deployed taxonomy. Client code records the survey on submission and exposes a separate Manage path; all cancellations are therefore not a valid inferred exposure denominator.

postgre.mv_vpn_cancel_reasons still has 96,565 rows from one April 28 Airbyte generation. A project-wide search across every accessible US-region view definition, routine definition and table DDL found only the table's own DDL reference. Job-history and transfer-configuration reads remain permission-denied. That negative metadata read does not establish retirement or external non-use.

A current alternative path now exists. postgre.mv_vpn_users_revenue_pgadmin was rebuilt on September 7 with 433,648 rows, including 120,698 rows carrying the same three classifier labels; looker.users_revenue was refreshed later that day with the same total and classified-row counts. The two current tables match on 424,528 of 433,648 row keys across the six common classifier fields. The April classifier is not simply copied into the current Postgre table: after removing its obsolete type = churn label, only 50,139 of 96,565 old rows match. Treat the current tables as the usable current classifier path, not as proven direct lineage or semantically interchangeable history.

The live ClickUp team endpoint returned 5,422 visible tasks across 55 pages. None names mv_vpn_cancel_reasons; the existing Q1 churn-reason task 869bcf20n remains complete under Simonas and supplies no source, report URL, consumer or replacement in its body or comments. Leave the April table untouched. If a decision requires historical continuity or pipeline cleanup, the remaining owner fact is the scheduled/external consumer and confirmation that the current Postgre-to-Looker path is the approved replacement. Evidence: 30-resources/mysterium/2026-09-07-mv-vpn-cancel-reasons-lineage-read.json (SHA-256 34f7ae9e1e494e8e7c7f343f920321942d87b39add8afebe49e55b9d3cc3543a).

Private current evidence: 30-resources/mysterium/2026-09-07-queue-current-source-check.json and 30-resources/mysterium/2026-09-07-cancellation-work-live-status.json; operational state in Kairos — Pawn Move Queue. No MN system was changed and no human message was sent.

Browser rendering of the War Room source library. The vault remains canonical.