Measured Behaviour
Published
A stop payment order is a retraction that points forwards: an instruction not to honour something that has not happened yet. We measured what a rename does to the register that holds them, and to the two qualifiers sitting beside it - and the three fail in opposite directions.
One register, four orders, six cheques
The fixture is a day's presentment file against one chequing account: six items totalling $11,640.00, and four stop orders the customer placed over the preceding six months.
Two of the orders are straightforward. A roofer who never started the work, $845.00. A contractor whose contract was terminated before delivery, $6,150.00. Both cheques are in today's file and both must be returned.
The other two are the interesting ones, because in a correct run neither of them stops anything. One names $1,480.00 for a cheque that is actually $1,840.00 - the customer misremembered the amount when they phoned - and under an exact-amount rule the order does not describe the item, so the cheque is paid. The other was placed on day 230 against a six-month life and lapsed long before the cheque arrived on day 420.
The correct run pays four items worth $4,645.00, returns two worth $6,995.00, and charges $68.00 in stop-payment fees. All five default protection profiles reproduced it exactly. The failures need a member pattern reaching the register or its records; the mechanics are in Protect Members.
The register fails open, and it is the quiet one
Renaming the stop-order register pays all six items. Paid goes from four to six, stopped from two to zero, the value returned from $6,995.00 to $0.00, and the fees from $68.00 to $0.00.
The roofer who never turned up is paid $845.00 and the terminated contractor is paid $6,150.00, and there is no trace of either decision anywhere in the output except a configuration line reading empty register (no item is stopped). The bank's own check passes, printing: six items presented, four paid, two stopped in the correct run - and six items presented, six paid, zero stopped, every item decided against a register of zero orders, in the broken one. Both sentences are true.
Renaming the flag that honours stops reaches the same place. So does the union of the two. The customer finds out at the end of the month, from a statement, about money that has already left.
The two qualifiers fail closed, and they are the loud ones
The amount match and the order's life sit on the same object, one line apart from the register. Both fail in the opposite direction.
Losing the amount match reverts to matching on the serial number alone, so the order naming $1,480.00 stops the $1,840.00 cheque it does not describe. Paid falls from four to three, stopped rises from two to three, the value returned goes to $8,835.00 and the fees to $102.00. A body shop that did the work is handed a returned cheque.
Losing the order life reverts to no stated life, so an order placed six months earlier and long lapsed comes back into force and returns a $2,300.00 landscaping cheque. Paid falls to three, the value returned rises to $9,295.00.
Both of those produce a phone call within the week. A wrongful dishonour is a loud failure with a named victim and, in most jurisdictions, a statutory remedy attached to it. That is worth saying plainly: of the three names on this object, the two that produce an angry customer are the two that get fixed, and the one that quietly pays $6,995.00 of cheques nobody authorised is the one that does not.
The union is the silent half, not the sum
We ran the register and the amount match together expecting the worse of the two, or something new. The union is byte-identical on every counter to the register alone: six paid, zero stopped, $0.00 returned, $0.00 in fees.
The reason is worth carrying. The quiet half does not switch the loud half off, as an enabling flag would. It removes the subject the loud half operates on. An amount-matching rule applied to an empty register cannot dishonour anything, because there is nothing for it to match against.
So the wide, careless pattern is the silent one and the narrow, careful pattern is the one that screams. That inverts the intuition most people bring to scoping a rename: a broader pattern is not more dangerous here, it is quieter, and quieter is worse.
The two qualifiers together behave the way defence in depth is supposed to look and does not. Each alone dishonours one extra cheque. Together they dishonour both plus a third: paid falls from four to two, stopped rises to four, $11,135.00 of the $11,640.00 presented is returned, and the fees reach $136.00. Two half-arms that each move one item are the signature of two controls sharing an object, not evidence that either is safe.
What to assert, and what the fee counter tells you
Renaming the fee alone leaves every other figure identical and drives the fees from $68.00 to $0.00. It is the same pattern this series keeps finding: the price of an event is the first thing to go, and the one figure a controller would notice reads zero precisely because the number that would have made it non-zero is what went missing.
The control run - a customer who has never placed a stop order at all - reports six presented, six paid, zero stopped, $0.00 returned, $0.00 in fees. It is byte-identical to the arm in which the register was renamed. That is the third area in this pass where the fixture a team would naturally build is, by construction, the defect's output.
The assertion that survives is one about the register rather than the file: for each order in force whose serial appears in today's presentment and whose amount matches, require that the item was returned, and name it. That fails immediately when the register is unreadable, because the loop has nothing to iterate. Assert the mapping, not the totals.
On the protection side, the same narrow answer as everywhere else: keep RenameMembers off the objects handed to an installed library. If your presentment or card path is inside PCI scope, the evidence questions that usually follow are covered in our notes on PCI DSS v4 JavaScript requirements 6.4.3 and 11.6.1.
Frequently asked questions
Which is worse, losing the register or losing a qualifier?
Losing the register, by a wide margin - not because more money moves, but because nobody is told. A wrongful dishonour produces a phone call within the week. Paying a stopped cheque produces a statement line the customer may never read.
Did the union of the two make it worse?
No. Renaming the register and the amount match together was byte-identical to renaming the register alone. The quiet half removes the subject the loud half acts on, so the broad pattern was the silent one.
Which setting caused it?
Member renaming reaching the stop-order register, the honour flag, the amount-match rule or the order life. All five default profiles we measured - ES5, modern, both emit-gate configurations and string encoding - produced identical behaviour with no options set.
Why does an exact-amount rule matter at all?
Because a customer stating the wrong amount is common. In the correct run the order naming $1,480.00 does not stop the $1,840.00 cheque, and that is right - the order does not describe the item. Lose the rule and the same order dishonours it.
Would the presentment check have caught anything?
No. It asserts that every item in the file was decided, which is true whether the register held four orders or none. A procedural check verifies which steps ran, never what they ran against.
What assertion would have caught it?
One defined over the register: for every order in force whose serial is in today's file and whose amount matches, require the item to have been returned, and name it. An unreadable register makes that loop empty, which is exactly when it should fail.
How do I scope the member pattern?
Restrict MemberRegexp to names your own code owns on both sides of every boundary and keep it off objects passed into installed libraries. The reference is in Protect Members.
Related reading