Skip to main content

Analysis & Tooling

2 min read

predict – "what would this rule do, and what facts must change?"

Run a FactPredicate against the current facts without firing it. Returns whether it would fire, the full clause-by-clause breakdown, and – if it wouldn't – a list of the smallest changes that would make it fire.


The Sherlock moment

LLM emits a rule. predict() says:

"This rule needs cartTotal ≥ 50 to fire; current value is 30. Set cartTotal to at least 50 to make it fire."

…and the LLM rewrites accordingly. Closes the iteration loop with one tree walk.

import { predict } from "@directive-run/core";

const predicate = {
  cartTotal: { $gte: 50 },
  region: { $in: ["US", "EU"] },
};

const facts = { cartTotal: 30, region: "ASIA" };

predict(predicate, facts);
// → {
//     wouldFire: false,
//     whenExplain: [
//       { path: "cartTotal", op: "$gte", expected: 50, actual: 30, pass: false },
//       { path: "region",    op: "$in",  expected: ["US","EU"], actual: "ASIA", pass: false },
//     ],
//     missingChanges: [
//       { path: "cartTotal", op: "$gte", expected: 50, actual: 30,
//         suggestion: "set cartTotal to at least 50 (currently 30)" },
//       { path: "region", op: "$in", expected: ["US","EU"], actual: "ASIA",
//         suggestion: "set region to one of ['US','EU'] (currently 'ASIA')" },
//     ],
//   }

Suggestions per operator

OperatorSuggestion shape
$eqset X to V (currently A)
$nechange X to anything other than V (currently A)
$gt / $gteset X above/to at least V (currently A)
$lt / $lteset X below/to at most V (currently A)
$in / $ninset X to one of [...] / something other than [...]
$exists: trueset X to a non-null value
$exists: falseunset X
$betweenset X between A and B (currently C)
$startsWith / $endsWith / $containsset X to start/end/contain V
$matchesset X to match the pattern /re/ (currently A)
$changedthe previous-vs-current change of X is required to differ

For combinators ($all, $any, $not), predict() recurses into children and surfaces failed leaves with the leaf-level suggestions.


Use cases

  • LLM iteration loop – emit → predict → reprompt. The model converges to a firing rule in 1-2 turns.
  • "Preview this rule" UI – show a user what their proposed rule would do against today's data, including the exact facts that would need to change.
  • Test-suite assertionexpect(predict(rule, facts).wouldFire).toBe(true).

What this does NOT do

  • Not a SAT solverpredict() reports failing leaves, not the minimal set of fact changes that satisfy the rule. For $any: [A, B] with both failed, it lists both – you pick which to change.
  • Doesn't mutate facts – pure read.
  • Doesn't honor $changed without a prev – pass predict(predicate, facts, prev) if your predicate uses $changed.

Reference

Previous
Predicate Codegen (SQL/Mongo/PostgREST)

Stay in the loop. Sign up for our newsletter.

We care about your data. We'll never share your email.

Powered by Directive. This signup uses a Directive module with facts, derivations, constraints, and resolvers – zero useState, zero useEffect. Read how it works

Directive - Constraint-Driven Runtime for TypeScript | AI Guardrails & State Management