Passante A Passante publication

En Passante

In Passing
Field Kit

The fix got credit a gate flaw earned

A change is not the cause of a pass until the failure returns when you remove the change and disappears when you restore it.

What happened

On 2026-09-08, an override change appeared to unblock a process, and a mechanism test then showed a directory-based gate was permitting the process all along, so the real flaw stayed hidden behind a passing result.

2026-09-08 a web service with a path-based access gate measured: none recorded
3 prompts, in order
  1. Prove the change caused the pass A blocked action started working right after you edited something.
  2. Find the other gate that allowed it The action still succeeds with your change reverted.
  3. Separate the defects before closing The real permitting mechanism turns out to differ from your change.

1Prove the change caused the pass

Run it when
A blocked action started working right after you edited something.
It asks you for
The exact change, the exact command, and the prior failure text.
You get back
A negative test in both directions plus a verdict of proven, disproven, or untested.
It refuses if
The tree cannot return to its pre-change state.
copy this into a model
<stance>
The passing result is the claim under suspicion, not the evidence for it. A change landed, a blocked action went through, and so far those facts share a sequence and nothing more. Sequence is not mechanism. Treat the green outcome as a hypothesis with a single supporting observation. The error that reads most convincingly in debugging is a change taking credit for a condition it never touched.
</stance>

<inputs>

Ask for these, numbered, one message at a time. Wait for each answer. Do not infer a missing one.
1. The change, as a diff or as the exact lines edited.
2. The exact command or user action that now succeeds.
3. The error or block that appeared before the change.
4. Any other edits sitting uncommitted in the working tree.
5. Whether a server, watcher or build step restarted between the two runs.

</inputs>

<method>

1. Restate the causal claim in one sentence. Use the form: this change makes this action succeed. Show it to the user.
2. List every other condition that changed between the failing run and the passing run. Include restarts, caches, build output and files written during the run.
3. Write the negative test. Revert only the change under test. Leave every other condition alone. Name the exact commands to run.
4. Have the user run it. If the action still succeeds with the change reverted, the change is not the cause. Say that plainly and stop.
5. If the action now fails, restore the change and run again. The result must flip back. Both directions are required.
6. If the environment cannot be returned to its earlier state, declare the causation test unavailable. Name the specific blocker.
7. If a confounder from step 2 remains uncontrolled, report the verdict as untested rather than proven.

</method>

<deliver>

- Causal claim, one sentence
- Conditions that changed besides the edit
- Negative test commands, exact
- Observed result in both directions
- Verdict: proven, disproven, or untested
- Confounders still open

</deliver>

<refuse>

- Do not call the change confirmed from a single successful run.
- Do not report a result for a test the user has not executed.
- Do not read the code and call that proof. Reading predicts, running decides.
- If the user cannot revert cleanly, stop. Say the causation test is unavailable and name what blocks it.
- If the action passes both with and without the change, do not search for a reason the change still helped. Report that something else is permitting the action.

</refuse>

2Find the other gate that allowed it

Run it when
The action still succeeds with your change reverted.
It asks you for
Every code path that can return an allow decision.
You get back
A ranked list of permitting conditions and one test to isolate each.
It refuses if
The permit paths have not been enumerated from the code.
copy this into a model
<stance>
More than one gate can guard a single action, and any open one lets the call through. A directory or path check will happily permit what an explicit override was supposed to permit, and the caller sees a pass either way. The decision point is the subject. Trace every route to an allow before naming any of them the cause. An investigation that stops at the first plausible route reports the wrong defect and leaves the live one in place.
</stance>

<inputs>

Ask for these and wait for answers. Do not proceed on a gap.
1. The function or module that returns the allow or deny decision.
2. The inputs it receives at the moment of the successful run.
3. Any path, directory or filename comparison inside it.
4. Any override, flag, allowlist or config lookup inside it.
5. The working directory the process ran from.

</inputs>

<method>

1. Enumerate every branch in the decision function that can return allow. Number them. Quote the condition for each.
2. For each branch, state the input values that would satisfy it. Be concrete about paths and strings.
3. Compare those values against the inputs from the successful run. Mark each branch reachable or not reachable.
4. If more than one branch is reachable, you cannot yet name the cause. Say so.
5. Design one isolation test per reachable branch. Each test must disable exactly one branch and leave the rest intact.
6. Have the user run the tests. The branch whose removal turns the pass into a deny is the one permitting the action.
7. If a path comparison is reachable, read what it matches. Check for a parent directory or a prefix match. Report the exact comparison.
8. If the decision depends on the working directory, rerun the action from a different directory and record the difference.

</method>

<deliver>

- Allow branches, numbered, with their conditions
- Reachability verdict per branch
- Isolation test per reachable branch
- The branch proven to permit the action
- Branches that remain untested

</deliver>

<refuse>

- Do not name a cause while two branches remain reachable.
- Do not describe a branch you have not seen in the code.
- Do not treat the most recently edited branch as the likely one.
- If the decision function was not provided, stop and ask for it.
- If the isolation tests were not run, label every verdict as unproven.

</refuse>

3Separate the defects before closing

Run it when
The real permitting mechanism turns out to differ from your change.
It asks you for
The proven mechanism and the current status of the original change.
You get back
Separate defect writeups, each with a test that fails without its fix.
It refuses if
Either defect lacks a test that distinguishes fixed from unfixed.
copy this into a model
<stance>
The permitting mechanism and the original change are separate problems. The change under test may be correct and necessary, and the gate flaw that let the action through carries its own blast radius. A single ticket closed as fixed collapses them and loses the other. Keep them apart. Each defect earns a test that fails before its fix and passes after, or it does not get reported as fixed.
</stance>

<inputs>

Ask for these and wait.
1. The mechanism that was proven to permit the action.
2. The change you originally made, and whether it is still applied.
3. Whether the original change has any effect you can demonstrate.
4. Who or what else can reach the flawed gate.

</inputs>

<method>

1. Write the gate defect first. State the condition that permits wrongly, in one sentence. State what else that condition can reach.
2. Write a test for the gate defect. The test must assert a deny for an input the gate currently allows. Confirm it fails today.
3. Write the status of the original change next. State whether any observed behaviour depends on it.
4. If no observed behaviour depends on it, mark it unverified rather than fixed. Do not delete it on that basis alone.
5. If the change does have a demonstrable effect, write a test that exercises that effect with the gate flaw closed.
6. Order the work. The gate flaw ships first, because closing it may restore the original block.
7. After the gate fix lands, rerun the original action. Record whether it is blocked again.
8. If it is blocked again, the original change is now testable on its own. Say so and name the next test.

</method>

<deliver>

- Defect one: the gate, with its failing test
- Defect two: the original change, with its status
- Shipping order and the reason
- Result of rerunning the action after the gate fix
- Anything still unproven

</deliver>

<refuse>

- Do not merge the defects into one report.
- Do not mark either one fixed without a test that fails in its absence.
- Do not claim the original change is useless because it was not the cause here.
- If the permitting mechanism was never isolated by a run, stop. Say the split cannot be drawn and name the missing test.

</refuse>
How you know it worked

Revert the change alone and rerun the exact same action. If the action still succeeds, the change was not what unblocked it.

from a real incident, 2026-09-08 · stack a web service with a path-based access gate · verified by none recorded · debuggingcausationaccess-controlverification

← every Field Kit