Automatic Retraction
A conclusion that withdraws itself. Mark a rule as retractable and the engine remembers that its changes are only true while the rule still matches — when the facts behind it move, the conclusion comes back out, and so does anything built on top of it.
The problem it solves
Normally a rule’s writes are permanent. It fires, it changes the context, and that change stays for the rest of the run no matter what happens next. In single-pass execution that is exactly right: nothing runs after it to invalidate anything.
Under inference it is not. Rules re-fire as the context moves, so a rule that matched early can stop matching later — and the change it made is still sitting there:
rule A: income > 100000 → tier = "premium"
rule B: tier == "premium" → discount = 0.2
rule C: fraudFlag == true → income = 0
fraudFlag arrives true. C fires, income drops to 0.
A no longer matches — but tier is still "premium",
and discount is still 0.2. Nothing looks wrong. The trace is clean, every rule did what it was told, and the answer is incorrect. Marking A and B as retractable fixes it: when A stops matching, tier is withdrawn, which pulls the ground out from under B, so discount is withdrawn too.
Turning it on
It is a per-rule choice. Open the rule and switch on Withdraw conclusions automatically, next to the other action options. The rule set has to be using the Inference hit policy — single-pass execution has nowhere to put a retraction, so the setting is rejected outside inference rather than quietly ignored.
Which rules want this Rules that classify or derive: a tier, a risk band, an eligibility flag, a computed discount. Anything where the honest answer to “is this still true?” is “only while its inputs say so.” Leave it off for rules that record something that happened — an audit note, a counter, a decision you want to stand regardless.
What a retractable rule may do
The engine can only promise to undo what it can reach, so a retractable rule is constrained. All four of these are checked when you save, because every one of them fails silently at run time:
| Constraint | Why |
|---|---|
| Context actions only | Add to Context, Remove from Context, Transform, Create JSON, Create XML, Array Loop and Log. An API call, an AI completion, a saved blob or a human-intervention form leaves a mark outside the context that no retraction can reach. |
| Sequential actions | Merging parallel action branches loses the secret flag and the record of which action wrote what, so the engine could not put things back accurately. |
| No reading a key it writes | A rule like IsNull(flag) → set flag stops matching because of its own conclusion. Withdraw the conclusion and it matches again, forever. Perfectly fine without retraction; contradictory with it. |
| Effects knowable in advance | The engine records what a rule supports from its declared writes, so a rule whose writes cannot be listed before it runs cannot be tracked. |
Two reasons for the same conclusion
Support is counted, not assumed. If two retractable rules both conclude status = "open" and only one of them stops matching, the value stays — the other rule is still holding it up. Only when the last supporter goes does the value go.
When two retractable rules write the same key different values, the most recent one wins and the earlier one waits underneath it. Withdraw the newer rule and the older value comes back rather than the key disappearing. The run trace shows both, so you can see what is holding a value and what would surface if it were withdrawn.
A plain rule takes ownership If a rule without retraction writes a key that a retractable rule is supporting, the plain write wins permanently and the support underneath it is discarded. That is a useful way to pin a value — an override that should stick — but it is easy to do by accident, so the rule set check in the editor points it out.
Derived facts withdraw too
A derived fact is supported by its inputs in exactly the same way. If its guard stops holding, or the values it reads go away, the fact is removed rather than left sitting in the context with a stale value. Only facts the engine itself computed are affected — a value you supplied with the run, or one a rule wrote over the top of, is never touched.
Reading it in the trace
Withdrawals appear in the run timeline alongside firings, in the order they happened, so a value that was written and then taken back reads as one story rather than two lists. A withdrawal row is struck through and marked with an undo icon, names the rule (or fact) that lost its support, says why, and can be expanded to show exactly which keys changed and what they went back to. Firings from retractable rules carry a logical chip.
Test runs follow the same truth: a rule that fired and was then withdrawn is not reported as matched, because the run does not stand behind it. The same goes for what-if comparisons — a withdrawn firing does not count toward a rule’s impact.
When rules defeat each other
Two retractable rules can end up in a loop: A’s conclusion stops B matching, withdrawing B’s conclusion starts A matching again, and round it goes. The run always terminates — after a rule has taken the same conclusion back a few times, the engine freezes its support in place and records an Oscillation detected warning rather than continuing.
It freezes the conclusion held rather than withdrawn, on purpose: ending a run with a contested answer is far easier to reason about than ending it with a live rule and no answer at all. Still, a frozen conclusion means the outcome depended on where the loop was cut, which is rarely what you meant — the rule set check in the editor flags support cycles before you ship them.
Worth knowing
- Only the newest supporter of a key holds it. An older retractable rule whose conclusion is shadowed does not also apply; it waits underneath and surfaces if the newer one is withdrawn.
- A rule that reads a key it writes is caught at save time. A cycle through other rules is not blocked — it is a warning from the rule set check, and the oscillation cap is what stops it at run time.
- Retraction changes the values a condition reads, so a rule with an AI condition can be re-evaluated (and re-billed) more often under retraction than without it.