Find the Commit Point Before You Judge the Gate

A suppression gate sat on the orchestration layer. Orchestration runs after the Order, the Order Line Items and the Contract are committed — so the safeguard produced the exact divergence it was written to prevent.

A customer with a signed contract, no Asset, no billing account and no provisioning request.

That state was not produced by a bug. It was produced by the safeguard.

What the Feature Was For

Customers inside a migration window should not have journeys running on them. Migration moves the data; a journey running at the same time writes on top of it, and the two versions of the customer diverge.

So a suppression mechanism was added. While the window is open, the journeys do not run.

The condition was correct. It identified the right customers, at the right time, for the right reason. Every review of it confirmed that, and every review of it was answering the wrong question.

Where the Gate Was Placed

On the orchestration layer.

That is the natural place to put it, because that is where the unwanted behaviour is visible. You can watch a plan start. You can watch its items run. When someone says "stop these journeys," orchestration is the thing you can see doing the running, so orchestration is where the hand goes.

Now walk the order forward instead of watching it.

The Order is created. The Order Line Items are created. The Contract is created. All committed. Then — after all of that — orchestration starts, sees the suppression condition, and does nothing.

The Order Half-Exists

An order submitted during the lock leaves a contractually committed customer with nothing downstream of the commit.

No Asset. No billing account. No provisioning request.

The customer has agreed to something and the system has recorded that agreement. The records that make the agreement real were the ones the gate stopped. This is not a journey that was prevented. It is a journey that was cut in half, at exactly the point where the first half is the legally meaningful part.

And the shape of that outcome is worth sitting with: a contractually committed customer whose data does not match what they bought is precisely the data-integrity divergence the feature was written to prevent. The safeguard did not fail to stop the problem. It caused a version of it.

What This Was Not

It was not a wrong condition. The condition was right.

It was not a timing bug, or a race between migration and orchestration, or a missing null check. Nothing in the implementation is defective. You could re-derive this outcome from the design with no environment at all, which is how you know it is not an implementation defect.

It was not caught in review either, and that is the part that generalises. Everyone who looked at it checked whether the condition was correct. Nobody checked whether the layer the condition sits on is upstream of the damage. Those are two different reviews, and only the second one would have found this.

The Question That Reorders the Review

For any suppression, blocking or lock requirement, ask one thing before you look at the logic:

Where is the record committed, and does the enforcement sit before or after that point?

A gate upstream of the commit prevents the state. A gate downstream of the commit does not prevent anything — it selects which half of the state gets written. That is not a weaker version of prevention. It is a different behaviour with a different failure mode, and the failure mode is partial data.

Which gives a small practical rule when you read a requirement that says "stop X from happening":

  • Name the records the transaction writes, in order. Order, line items, contract, downstream artefacts. It takes five minutes and it does not require access to anything.
  • Mark where the enforcement fires in that sequence. Everything to its left is already on disk when the gate runs.
  • Ask what a customer looks like with only the left-hand records. If that description is one somebody in the business would object to, you have found the defect, and you have found it before the build.

The condition was never the risk here. The layer was. A blocking rule inherits the guarantees of the layer it runs on, and orchestration guarantees nothing about what has already been committed.

In the last blocking requirement you tested, do you know which records were already written by the time the block fired?