Measured, not asserted
Published
A service credit is decided by a subtraction and a table, and both halves are closed by construction. Uptime is computed as the remainder, so availability reconciles against the calendar no matter which minutes were counted; a credit is a published percentage of a real invoice, so it always looks like a credit. Neither half is the question. The question is which minutes count, and that is decided by two named sets and an ordered table. This test renamed them against a real obfuscation engine and got both directions out of one object: one rename paid nearly six times the credits owed, another on the same object paid none, and every arm reported that the books balanced.
The reconciliation everybody writes is closed by construction
The fixture settles one month across three customers: nine incidents, of which three are excluded by cause -- a customer misconfiguration, a third-party carrier failure and a force-majeure event -- and two start inside agreed maintenance windows. That leaves four countable incidents, 400 minutes of downtime, and 1,895 in credits across the three accounts.
The check the provider runs is the sensible one: downtime and uptime must add back to the period. It passes in every arm measured below, including every arm that moves money, because uptime is defined as the period minus the downtime. It cannot fail. A reconciliation that is closed by construction is an invariant of the code, not evidence about the result.
What is not closed by construction is which incidents entered the subtraction, and that is a set-membership question about named sets.
One direction over-pays by a factor of six
Rename the excluded-cause list and the library falls back to an empty list: nothing is excluded. The run reports counted=7 excluded-by-cause=0 foots=true and TOTAL-CREDIT=10827.50 against a correct 1,895. The provider has just credited its customers for a customer's own misconfiguration, a carrier failure outside its control and a force-majeure event, and the ledger balances perfectly.
Renaming the cause field on the incident records instead produced the identical figure. Configuration route and record route, one answer -- the same pattern as in the routing areas.
The maintenance windows behave the same way and arrive at a third figure. Rename the window list, or the incident start time the overlap is computed from, or the window boundaries themselves: all three give counted=6 excluded-by-window=0 TOTAL-CREDIT=4125. Three independent names, one answer.
Rename both exclusion sets and every one of the nine incidents counts: 13,587.50. That is the arm finance eventually notices, because it is a large number moving the wrong way, and somebody reviews it at quarter end.
The other direction pays nothing and nobody checks
On the same configuration object sits the ordered credit tier table. Rename it and the library falls back to an empty table, which means no tier ever applies and no credit is ever due. TOTAL-CREDIT=0. Renaming the availability threshold inside each tier entry produced the identical result.
Every other figure on that line is correct. Four incidents counted, three excluded by cause, two by window, 400 minutes of downtime, availability computed correctly to three decimal places for all three customers, and the books balance. The only thing that changed is that two customers who were owed money were paid nothing.
The two directions are not symmetric in who notices them. Over-crediting moves a number on the provider's own income statement and gets reviewed. Under-crediting is noticed only by the customer, who has to compute the availability themselves from a status page to know they were owed anything -- and the status page is generated by the same system.
Availability is a SOC 2 trust services criterion and an ISO 27001 control objective, and this is one of the places where the reported figure and the contractual consequence come apart; our ISO 27001 and SOC 2 notes cover the front-end side of that.
The grouping key, the cap, and the loud arms
Renaming the customer identifier collapses every incident into a single bucket: customers=3 becomes customers=1. Three separate accounts' outages pool together, which is the grouping-key failure this series has measured in several other domains. Here it is partly loud -- the invoice lookup for the collapsed key comes back undefined and the total goes NaN -- so it would be caught. That is worth stating plainly: not every arm is quiet, and this one is not.
Renaming the credit cap was inert, because the cap never binds on this month's numbers. Renaming the per-tier credit percentage gives a NaN total. Renaming the incident duration or the period length refuses the run outright, with the provider's own rule reporting that a duration is not a number.
So the value-versus-meaning contrast holds here too: lose the numbers and the settlement stops; lose which incidents the numbers are taken over, or which tier applies to the result, and the settlement runs, reconciles and pays the wrong amount.
As in the other areas, pairing the exclusion rename with the flag that enables the provider's credit rule, or with the rule function itself, was byte-identical to the exclusion rename alone. The rule checks that downtime does not exceed the period. It never would.
Why an availability figure is not evidence
The output of this system is a percentage with three decimal places, computed from real timestamps, reconciling exactly against a calendar month. It is the most authoritative-looking number in the whole domain and it is compatible with every wrong answer above.
That is not a flaw in the arithmetic. It is what happens when a figure is defined as a remainder: the remainder absorbs whatever the first term did, so the identity holds for any set of counted minutes, including sets nobody agreed to. The same is true of the credit itself, which is always a published percentage of a real invoice and therefore always looks like a credit.
The consequence for anybody reviewing one of these systems is that reading the output tells you almost nothing. What tells you something is comparing the set of incidents that entered the calculation against the incident list as the operations team knows it, and comparing the tier applied against the tier the contract names for that availability.
What to change, whether or not you obfuscate
Assert the membership, not the total. Publish the count of incidents excluded and the reason for each, and reconcile that list against the incident register maintained outside the billing code. A change from three exclusions to zero is visible instantly; a change in the total credit is not, because the total is supposed to vary.
Make an empty exclusion set and an empty tier table errors rather than defaults. An empty list of exclusions and a list you never supplied are indistinguishable to the library, and both of these libraries default to the permissive reading. That is the right default for a vendor who cannot know your contract, and the wrong one for you.
Assert which tier applied. The credit percentage that was used, alongside the availability that selected it, is a two-field assertion that catches the entire under-payment direction.
And test the halves. Renaming the exclusion list alone and the tier table alone move the answer in opposite directions on the same object; a single wide pattern covering both would have shown one effect masking the other.
How this was measured
Five telecommunications areas were driven through the real obfuscation engine on five presets -- the ES5 default, the modern target, the emit gate's option set on both targets, and the string transforms. All twenty-five combinations produced output byte-identical to the unprotected run.
The member-renaming column is ninety-six patterns on two presets: one hundred and ninety-two verdicts, one hundred and seventy-eight of which moved the output. The fourteen that did not are the seven options pinned equal to the library's own default, measured on both presets; one of the seven is in this area.
Ninety-two arms were first checked in the unprotected world by deleting each option key and record field to confirm it changes the answer at all, so that no verdict below rests on an arm that could not have moved.
Every figure quoted was read by executing the protected artifact and reading the whole output line, including the reconciliation flag, which reads true in every money-moving arm.
Frequently asked questions
Did obfuscation on its own break service-credit settlement?
No. Five presets -- the ES5 default, the modern target, the emit gate's option set on both targets, and the string transforms -- all produced output byte-identical to the unprotected run across all five areas measured this pass.
What actually broke it, then?
Member renaming reaching the excluded-cause list, the maintenance windows, the incident fields those sets are matched against, or the ordered credit tier table.
How much did the numbers move?
Renaming the excluded-cause list took total credits from 1,895 to 10,827.50. Renaming the credit tier table on the same configuration object took them to zero. Both arms reported that downtime and uptime reconciled against the calendar.
Why did the reconciliation pass in every arm?
Because uptime is computed as the period minus the downtime, so the identity holds for any set of counted minutes. A reconciliation closed by construction is an invariant of the code, not evidence about the result.
Which direction is more dangerous?
Under-payment, because of who notices. Over-crediting moves a number on the provider's own income statement and gets reviewed; paying nothing is visible only to the customer, who would have to recompute availability from a status page the same system produced.
What check would have caught both directions?
Asserting membership and selection rather than totals: publish which incidents were excluded and why, reconcile that against the incident register, and record which credit tier was applied alongside the availability that selected it.
Can I still obfuscate a billing front end?
Yes. Keep the policy configuration and the matching record field names out of member-renaming patterns; identifier renaming, string encoding and control-flow transforms are unaffected.
Related reading