Flag rules — QA

S3 Sensitivity Science · PRD044 · Issue #78

The flag rules that today's data cannot fire

Two of the ten Flag rules on the company dashboard do not trip on the warehouse as it stands, and one branch of a third is a state the live months never reach. Each is driven here from a hard-coded fixture through the same rule text the live fence runs, so a rule that has never fired is not a rule nobody has seen fire.

Nothing on this page reads a mart. Every fence below is a literal rowset standing in for the input the live rule divides, so this page renders the same on any dataset and on a build with no warehouse at all. The rules on /company, /company/acquisition and /company/retention are the ones that ship; these are the same selects with a fabricated input, and they exist because a rule whose where clause has never been true is a rule whose words have never been read.

The three states below are, in order: a channel that spent real money and took no order at all; the two cancellation marts disagreeing; and a calendar month whose subscriber churn cannot be computed, which must trip its rule in neither direction.


Spend against a channel with no attributed order

SERIOUS. Fires when a channel carries more than £100 of spend in the window and zero attributed orders. It does not fire on the all-time window today — Performance Max has £284.49 of spend and one order, Paid Search - Brand £394.37 and one — but it does on narrower ones, and the fixture below pins the words either way.

SERIOUS 1

acquisition 394.4
Paid Search - Brand spent £394.37 and took no order
Paid Search - Brand carries £394.37 of spend and 0 attributed orders on analytics.channel_reporting over the window, against a bar of £100 of spend.

One card, from three rows. Paid Display is under the £100 bar and Performance Max took an order, so neither is a channel that spent and got nothing — and the rule is silent about both rather than reporting them at a lower severity. The card states the spend, the order count and the bar, and stops there.


Cancellations do not reconcile across the two marts

SERIOUS, and a data-integrity rule rather than a business one. It fires when the row count on analytics.loop_cancellations differs from the cancelled movement summed over every date on analytics.loop_subscriber_performance. Both read 24 as at data through 2026-08-04, so it is silent on the live page — which is the outcome the retention page needs and the reason the words have to be proved somewhere else.

SERIOUS 1

retention 1
Cancellations do not reconcile: 23 rows against a movement of 24
analytics.loop_cancellations carries 23 rows against a cancelled movement of 24 summed over every date on analytics.loop_subscriber_performance, a difference of 1. The two count the same event, so while they disagree every retention figure resting on either mart is unverified.

The detail line says what stops being trustworthy, not what to do about it. That is the line ADR 0007 draws: a computed flag states a fact — these two marts count the same event and disagree by one — and the judgement about which figures to stop relying on follows from the fact rather than from a sentence telling a reader to act. On the live page this card is absent, and its absence is the reconciliation holding.


A month whose churn cannot be computed

The subscriber-churn rule fires at 25% on the latest complete calendar month. A month with a zero opening base has no rate at all — not 0%, not 100% — and must trip the rule in neither direction. June 2026 is such a month on the live data, but it is not the latest complete one, so the live page never puts it in front of the rule.

Nothing tripped, above the bar
June 2026 in the fixture carries 4 cancellations against an opening base of 0, so its rate is NULL. NULL > 0.25 is not true, and the rule emits no card.
Nothing tripped, below the bar either
The same NULL against the same bar, compared the other way. NULL <= 0.25 is not true either, so the month is not reported as healthy. That is what 'not computable' has to mean if it is to mean anything.

Both panels empty is the pass. A rate that cannot be computed is not a rate of zero and not a rate of one hundred; nullif is what keeps it out of both, and it is applied in the fence rather than in the source precisely so the NULL survives all the way to the comparison. On the live page the rule reads July 2026 — 16 cancellations against an opening base of 35 — and fires at 45.7%, so both branches of the rule are exercised: the firing one by the data, the not-computable one here.

Data baked at

The stamp is the only thing on this page that reads the warehouse, and it is here for the same reason it is on every other page: on a static site a rebuild is the data refresh, so a QA page with no date on it cannot be told apart from a QA page that was never rebuilt.