A Condition Is Not a Gate Until You Enumerate the Entry Routes
A suppression clause on scenario entry conditions left eighteen item definitions across six plans ungated by construction, because nine item definitions spawn plans by lookup and never pass through a scenario at all.
A condition placed on one way in is not a gate. It is a gate on that way in.
Those sound like the same sentence. They are not, and the distance between them was eighteen item definitions across six plans.
The Shape of the Mechanism
A suppression clause was added to the entry conditions of orchestration scenarios. Order arrives, scenario evaluates, clause matches, nothing runs. Clean, single place, one line to review.
Scenarios are how orchestration plans are normally entered. Normally.
Nine item definitions spawn further plans directly, by lookup. Sub-plans, rollback plans, amend plans — an item in a running plan points at another plan definition by ID and instantiates it. That route never passes through a scenario, so it never meets the clause. Not because the clause is wrong, not because someone forgot a case: because the code path it guards is not on this road at all.
Eighteen item definitions across six plans sat outside the gate by construction.
Of those, two were genuinely reachable. Their parent plans are gated at item level rather than at scenario level, which means the parent still starts — and a plan that starts can still reach the item that spawns the child.
That last sentence is the whole mechanism. A gate that stops work without stopping instantiation leaves every lookup-driven descendant live.
Why Nobody Saw It
Three reasons, and none of them is carelessness.
The requirement does not mention the lookup route. It talks about journeys and about the condition, in the vocabulary of the thing being prevented, not in the vocabulary of how orchestration is entered.
The developer's coverage table does not mention it either. A coverage table is built from the places you knowingly changed, and nobody changed the lookup route — that is exactly why it is missing.
And the route is not visible by reading. It only appears if you query the configuration for records that reference another plan by ID. Until you run that query, the second entrance is not hidden, it is simply not in anyone's picture of the object.
Reviewers verified the path the requirement described. They verified it correctly. The path nobody enumerated is where the gate does not exist.
The Rule
The question to ask about any gate is not where the condition is set. It is by what routes the guarded object can be entered.
Enumerate the routes first. Then check the condition against each one, individually, by name. The order matters: if you start from the condition you will only ever find the routes the condition already knows about, because the condition is a description of the author's mental model of entry, and that model is the thing under test.
This is cheap. It is a configuration query and a list. It is not an environment, not test data, not a run — and in this case it produced a number, eighteen, which is a far better artefact for a conversation than the sentence "I am not sure it covers everything."
Then the Acceptance Criterion Falls Over Too
Once the item-level gating is on the table, the acceptance criterion stops working.
It stated that no non-migration orchestration plan is triggered. For six of the plans that criterion is unsatisfiable as written, because they are gated at item level: the plan is instantiated, and then it runs with every item skipped.
Was it triggered? The plan record exists. Nothing it was going to do happened. Both answers are defensible in English and the two of them imply opposite test results.
And two of those plans are reachable by both routes, which makes it worse in an interesting way: the same plan satisfies the criterion or violates it depending on how the order entered. Not depending on the build. Depending on the path.
The word "triggered" reads as a single event to whoever writes an acceptance criterion. The platform distinguishes between a plan that was never instantiated and a plan that was instantiated with nothing to do, and it holds that distinction in data. The gap between those two models is invisible until a tester has to decide what the expected result actually is — which is the first moment anyone is forced to convert the sentence into an assertion.
A criterion that cannot be written as an assertion is not a criterion. It is a summary.
How to Do Both Checks in Practice
Two passes, and neither needs a running system.
- Build the entry-route inventory before you read the condition. List every way the guarded object can come into existence — normal entry, lookup from another record, API, batch, retry. For orchestration that means querying for definitions that reference a plan by ID, because those are the routes no document names.
- Rewrite every verb in the acceptance criteria as an observable state. Triggered, created, sent, blocked, suppressed. Replace each with the record and field you would query to prove it: plan not instantiated, or plan instantiated with all items Skipped. If two people produce two different replacements, the criterion was ambiguous and you have just found it for free.
The second pass takes twenty minutes and it repays itself the first time it stops an argument at test execution about whether a result is a pass.
Why This Class of Gap Survives Reviews
Because everything about it is locally correct.
The clause is correct. The scenario is the right place for it. The acceptance criterion is a fair summary of the intent. The coverage table honestly lists what was changed. Every artefact is defensible on its own, and the defect lives in the space between them — specifically in an assumption none of them states, which is that scenarios are how plans get entered.
That assumption is true most of the time. Nine item definitions is what "most of the time" is worth.
For the gate you are testing this sprint: how many ways in have you actually listed, and did you list them before or after you read the condition?