Measured, not asserted
Published
In every other area measured this pass the answer itself moved. Here the batch really is good and the right people really did approve it. What can go missing is the RECORD of that, which is the only part a regulator, a customer audit or a court can see.
A signature is three facts, not one
A batch releases when the roles the procedure names have each signed, by different people, with each signature stating what it means -- prepared, reviewed, approved for release. Those are three separate requirements carried by three separate names, and only one of them is the signature itself.
In this test the procedure requires a production signature and a quality signature, forbids one person from signing in both capacities, and requires every signature to state its meaning. Two people sign, the batch has no open deviations, and it releases with the attribution production=r.okonkwo/prepared@d2401 quality=m.halvorsen/approved-for-release@d2402.
Every one of those requirements is enforced by the installed batch-record engine reading a key out of the procedure object the application hands it. None of them is enforced by the signatures.
Released, and signed by nobody
Renaming ^(requiredRoles)$ released the batch with signatures required [none], attribution (nobody). The library could not read the list of roles, substituted its own default -- an empty list, which is the only reasonable default for a library that cannot know your procedure -- and required nothing.
The detail that makes this worth writing down is the line beside it. The same output still reported distinct-signers-required=true. The segregation-of-duties control is present, configured, correctly reported, and completely vacuous, because there are no signatures for it to compare. An audit question of the form 'is four-eyes enforced?' returns a true answer to the wrong question.
The two signature records still exist in the system. Nobody deleted anything. They are simply not attached to the release, and the release does not claim they are.
The decision was right and the attribution was gone
^(meaning|requireMeaning)$ is the quieter cell and the one that names this shape. The batch released -- correctly, the same batch, the same two people, the same absence of deviations -- with the attribution reading production=r.okonkwo/undefined@d2401 quality=m.halvorsen/undefined@d2402.
Renaming the meaning field on its own fails closed and loudly: unqualified-signature(a signature does not state what it means). The requirement that produces that refusal is an option key, and losing both in one pattern removes the requirement and the data together. What is left is a release decision that is correct in every respect except the one that makes it a record.
The signer field behaves the same way from the other direction. Renaming ^(signer)$ alone refused the release, because two unreadable signers compare equal and the distinctness rule fired. Renaming ^(signer|allowSameSigner)$ released it with both parties recorded as undefined -- two signatures by nobody in particular, and the rule that would have objected switched off by the other half of the pattern.
A guard that ran, reported, and read nothing
The caller supplies a release rule refusing any batch with an open deviation. Renaming ^(deviationsOpen)$ released the batch and printed deviations-open=undefined, while the rule reported checked by deviation-check(no open deviations).
That is a ceiling comparison against an unreadable value. The test asks whether the count exceeds zero; an unreadable count does not exceed zero, the refusal is skipped, and the check's own message names the condition it never evaluated. In this run the batch genuinely had no deviations, so nothing bad happened. In a run where it had three, the same output would have appeared.
The counter is printed on the same line as the verdict, which is the only reason it is visible at all. That is worth copying: print the value a guard consumed, not just the guard's conclusion, so a guard reading nothing looks different from a guard reading zero.
What to do about it
Member renaming is opt-in and the pattern is yours. Under default protection none of this occurs. Keep patterns anchored to names your own code owns on both sides, and treat the vocabulary of any record that leaves your bundle -- signature fields especially, since they are also the vocabulary of your audit trail -- as out of scope.
Then phrase the requirements so that losing them fails closed. A list of required roles that defaults to empty is a permission model in reverse: make the absence of an explicit, non-empty role list an error rather than a release with no signatures. Assert the COUNT of applied signatures against the count the procedure demands, rather than trusting that the demand was read.
And record attribution as a value the release cannot exist without. If the meaning of a signature can be undefined and the release still succeed, the meaning is decoration. Requiring it at the point of writing the record costs one line and makes a release that cannot say what it means impossible to write, which is a stronger position than detecting it afterwards.
Frequently asked questions
Does obfuscation break electronic signatures?
Not on its own. Across five presets and five areas measured this pass, protection alone left every output identical to the unprotected run -- twenty-five comparisons, no differences. What changed behaviour here was member renaming, a separate opt-in option whose pattern you write.
What was the worst result measured?
Renaming the option that lists the required signing roles. The engine substituted an empty list, required nothing, and released the batch with the attribution reading 'nobody' -- while still reporting that distinct signers were required.
What does it mean for a decision to be right and the record wrong?
The batch was good, the right two people approved it, and there were no open deviations. Only the record of who signed and in what capacity moved. In a regulated environment that record is the part anybody outside the company can actually see.
Why did losing the signer field alone fail closed?
Because two unreadable signers compare equal, so the rule forbidding one person from signing twice fired and refused the release. Losing the signer field together with the flag that enables that rule released the batch with both signers recorded as undefined.
Why did the deviation check report a batch clear?
It asks whether the open-deviation count exceeds zero. An unreadable count does not exceed zero, so the refusal was skipped and the check reported the condition it never evaluated. Printing the value beside the verdict is what makes that visible.
Is this specific to obfuscation?
No. A schema change, a partial response, a hand-written mock or a library upgrade produce the same undefined field. Renaming is a controlled way to find out which of your release controls depend on a name being readable.
What is the smallest useful change to make?
Make an empty required-role list an error rather than a release, and assert the number of applied signatures against the number the procedure demands. Both fail closed, and neither depends on anybody noticing a missing line in a report.
Related reading