Measured Behaviour

Does obfuscation break sprinkler hazard classification?

What a sprinkler system has to deliver is not written on the system. It is derived from what is stored underneath it, so a run can be wrong about the REQUIREMENT while every figure about the SYSTEM is exact. We measured which way that derivation fails, and the answer turned out to be a design choice that costs nothing.

A requirement that has to be worked out before it can be checked

A sprinkler system is judged against a design density - millimetres per minute of water over an assumed area of operation. That figure comes from a hazard class, and the hazard class comes from what is in the aisle and how high it is stacked. Racked aerosols are the top of the ladder; tinned goods are near the bottom; storage over a free-height threshold escalates one class. A system designed for an office protecting racked plastics is not slightly under-specified. It is the wrong system.

We built seven aisles in one distribution shed. Racked aerosols at 4.2 metres under a system designed for exactly that. Racked plastics at five metres under a system a class and a half too light. Paper on the floor, textiles at four metres, tinned goods, racked furniture with no hydraulic calculation behind it, and one aisle of lithium cells - a commodity that is not on the insurer's ladder at all.

The correct run fails four of the seven and passes 4,200 square metres, none of it under-designed. Every commodity, every stack height and every installed density in that run came off a survey and an as-fitted drawing, and every one of them survives every arm below intact.

The same loss, failing in both directions

The interesting name here is the commodity classification table. Rename it and the library has no way to turn "aerosols" into a hazard class. What happens next is decided by one more option: what an unrecognised commodity falls back to.

This fixture pins that fallback at the HEAVIEST class on the ladder, on the principle that a commodity you cannot classify is a commodity you do not understand. With that in place, renaming the classification table fails CLOSED and fails hard: passed goes from 3 to 1, only the aisle with the biggest system in the shed survives, and 2,200 square metres are accepted instead of 4,200. Six aisles are refused, the shed cannot be signed off, and somebody investigates that afternoon.

Renaming the fallback ON ITS OWN is almost nothing. It reverts to the vendor's LIGHT HAZARD, and exactly one aisle in the shed ever reaches the fallback - the lithium cells. Passed goes from 3 to 4, and 1,250 square metres of genuinely under-designed storage is accepted. A single row moving in a seven-row report.

Rename BOTH and every aisle falls to light hazard. Passed goes from 3 to 5, 7,600 square metres are accepted, and the counter held outside the bundle - floor area passed as adequately protected that the insurer's engineer recorded as under-designed - goes from 0 to 4,000 square metres. Racked plastics, escalated textiles and lithium cells all sail through under a design intended for an office.

Both member-renaming presets produced identical figures, and executing the protected artifacts reproduced them exactly.

The half that would have screamed was switched off by the half that did not

Read those three results in order and the structure is the point. One name alone: loud, safe, fixed the same day. The other name alone: one row, easy to miss, and the row it moves is the one commodity nobody has a rule for. Both together: the shed opens.

The strict fallback is what makes the first arm loud. It is also the thing the second arm removes. So the pair does not produce the average of two outcomes; it produces the silent one, because the loud behaviour was CONTINGENT on a name that the same pattern reaches.

That is worth stating plainly because it changes what a matrix of single-name results is evidence for. A clean, defensible reading of "renaming the classification table fails safely" is true and is not transferable. It holds only while the fallback holds, and the fallback is one key away on the same literal.

One detail from the measurement is worth keeping because it surprised us. In the both-names arm, one aisle FAILED that had passed in the correct run: the tinned goods. Light hazard carries a LARGER assumed area of operation than the class those goods actually sit in, so the aisle missed on coverage while five heavier aisles passed. A lighter class is not uniformly easier to satisfy, and a single refusal in the report is not evidence that the classification is working.

Two options on one object whose defaults point opposite ways

The height uplift is the other half of the derivation, and it carries a pairing that is easy to get backwards.

One option switches the uplift on. Its vendor default is FALSE - permissive, because a library cannot know your free-height rule. Rename it and nothing escalates: passed goes from 3 to 4 and 950 square metres of under-designed storage is accepted. Renaming the RECORD FIELD that carries the stack height produces identical counters, by a different route - an unreadable height fails the comparison, so nothing escalates either.

The other option sets the height above which the uplift applies. Its vendor default is ZERO - conservative, because escalating everything is the safe way to be wrong. Rename it and EVERY aisle escalates: passed drops from 3 to 1, and the shed is refused.

Two options, one object, opposite directions. And the union is counter-identical to the permissive half alone: with the uplift switched off entirely it does not matter what threshold it would have used. So the wide pattern produces the quiet outcome and hides the loud one, which is the reverse of what the same file's fallback pair does. The severity of losing a name is not a property of the name. It is whichever way THAT library's default happens to point, and two options on one object pointed opposite ways.

The blunt arms behave as expected: losing the density table accepts 8,200 square metres with 4,000 under-designed, and losing the enforcement flag accepts the whole shed. Neither tells you anything the narrow arms did not.

What the as-commissioned test cannot show

The negative control was the shed on the day it opened: the same seven aisles storing the thing the system was specified for, stacked low, on the floor. Seven passed, 11,300 square metres, nothing under-designed.

Every arm that lightens a derived class is inert in it. A requirement nothing was going to miss cannot be shown to matter by lowering it. The arms that control does catch are the ones that derive a HEAVIER class than the truth, and those fail loudly on the first run.

That asymmetry ran through all five areas we measured this pass. It is the same reason a quiet weeknight cannot test occupant load limits and a correctly raised permit cannot test impairment compensatory measures. A fixture built from the system working is blind to the ways the system stops applying.

What to do about it

Choose the fallback deliberately and write it down. An unrecognised input is not a light one; it is an unclassified one, and treating it as the heaviest class turned a silent 1,250 square metre release into a shed-wide refusal that gets investigated. That is one option value and it costs nothing at design time.

Then keep the fallback out of any pattern that also reaches the classifier. A strict default is only a mitigation while it is still readable, and this file demonstrates it being removed by the same regexp that removed the thing it was mitigating. Our RenameMembers documentation covers the regexp form; the discipline is to read the resulting name set as a set, not key by key.

And hold the truth outside the bundle. A register of what the insurer's engineer actually found, under flat string keys, is what separated "the design schedule reconciles" from "4,000 square metres of racked plastics are under a system designed for an office". Every counter in this article reconciled in every arm. Only the register disagreed.

Frequently asked questions

Does obfuscation change a sprinkler design calculation?

Not by itself. Across five presets, protecting the model without member renaming produced byte-identical output. What changed the required densities was member renaming reaching the names the hazard class is derived from.

What was the worst result?

Renaming the commodity classification table together with the unrecognised-commodity fallback. Passed went from 3 aisles to 5, and 4,000 square metres of storage the insurer's engineer recorded as under-designed was accepted as adequately protected.

Why did the classification table fail safely on its own?

Because the fixture pins the fallback for an unrecognised commodity at the heaviest hazard class. With no table, every aisle fell to that class, six of seven were refused, and the shed could not be signed off - loud and safe.

So the mitigation is the fallback?

Yes, and that is exactly why it needs protecting too. The strict fallback is one key away on the same object as the table it is mitigating, so a single pattern reaching both removes the safety and the thing it was making safe in one edit.

Why did one aisle fail in the both-names arm?

Light hazard carries a larger assumed area of operation than the class the tinned goods actually sit in, so that aisle missed on coverage while heavier aisles passed. A lighter class is not uniformly easier to satisfy.

Do the two height options behave the same way?

No, and that is the point. The option that switches the uplift on has a permissive vendor default and fails open; the option that sets the height threshold has a conservative one and fails closed. Their union is identical to the permissive half alone.

What would a configuration audit have seen?

Every option arm here flips a printed configuration line, so a configuration diff catches those. It does not catch the record-field arms - renaming the commodity on the records and renaming the classification table produced identical counters, and only one of them appears in a configuration dump.

Related reading