Related to #476, but a different axis: that one is about the shape of one rule (and / or), this one is about rules feeding each other — A → B → C.
Today they do not. Three separate reasons, each in a different place:
- Inputs are asserted only.
attribute_facts() reads facts; a rule's conclusion lands in derived_facts. A concluded attribute value is never an input to anything.
- Subject scope is the asserted type. The runner filters an entity into a rule by
entities.type_id. A rule that concludes class B does not make its subjects members of B for a rule scoped to B.
- One pass.
run evaluates each rule once, in whatever order they load. Nothing re-reads what the round produced.
The contrast is worth naming: the axiom reasoner already chains. derive() runs a semi-naive fixed point precisely so ceo_of → works_at → employs connects, with a per-predicate cap and cycles handled as contradictions. So chaining is not foreign to this system; it is just not wired for business rules.
What it would take
Feed conclusions back in. Derived typings join the subject scope; derived attribute values join the fact pool. Then rules and axioms are two producers into one round.
A fixed point, not an ordering. Round after round until nothing new, with the cap and the cycle guard the axiom path already has. Without it, whether A → B → C fires depends on the order rules happen to load, which is the worst possible answer — it works on one machine and not another.
Premises become a tree — but the cascade is already free. A chained conclusion's premises are derived rows, so a leaf reading changing has to retire everything downstream of it. That sounds like the expensive part and is not: retirement here is not an incremental walk, it is a full recompute. Every run builds wanted — everything that should hold now — and invalidates every live derived_facts row that is not in it. If the leaf changes, the chain simply is not rebuilt, so the whole tail falls out of wanted and retires in the same pass. What the tree does cost is display: a premise that is itself derived means the entity's proof goes one level deeper than the UI draws today.
One decision to settle: may a rule see its own conclusions, or only other rules'? Self-feeding is where the cheap cycles come from.
Cuts
- Decision record: what a chained conclusion's proof is, and how retirement cascades.
- Runner: conclusions back into the round, fixed point with cap and cycle guard, real-DB tests for
A → B → C, for a cycle, and for a leaf reading changing and taking the chain with it.
- UI: the rule table says which rules feed which — a chain is worth seeing before you delete the rule in the middle of it. On the schema diagram the edges already compose visually:
Well → GasBearingWell → Producer is two rule edges in a row.
Related to #476, but a different axis: that one is about the shape of one rule (
and/or), this one is about rules feeding each other —A → B → C.Today they do not. Three separate reasons, each in a different place:
attribute_facts()readsfacts; a rule's conclusion lands inderived_facts. A concluded attribute value is never an input to anything.entities.type_id. A rule that concludes classBdoes not make its subjects members ofBfor a rule scoped toB.runevaluates each rule once, in whatever order they load. Nothing re-reads what the round produced.The contrast is worth naming: the axiom reasoner already chains.
derive()runs a semi-naive fixed point precisely soceo_of → works_at → employsconnects, with a per-predicate cap and cycles handled as contradictions. So chaining is not foreign to this system; it is just not wired for business rules.What it would take
Feed conclusions back in. Derived typings join the subject scope; derived attribute values join the fact pool. Then rules and axioms are two producers into one round.
A fixed point, not an ordering. Round after round until nothing new, with the cap and the cycle guard the axiom path already has. Without it, whether
A → B → Cfires depends on the order rules happen to load, which is the worst possible answer — it works on one machine and not another.Premises become a tree — but the cascade is already free. A chained conclusion's premises are derived rows, so a leaf reading changing has to retire everything downstream of it. That sounds like the expensive part and is not: retirement here is not an incremental walk, it is a full recompute. Every run builds
wanted— everything that should hold now — and invalidates every livederived_factsrow that is not in it. If the leaf changes, the chain simply is not rebuilt, so the whole tail falls out ofwantedand retires in the same pass. What the tree does cost is display: a premise that is itself derived means the entity's proof goes one level deeper than the UI draws today.One decision to settle: may a rule see its own conclusions, or only other rules'? Self-feeding is where the cheap cycles come from.
Cuts
A → B → C, for a cycle, and for a leaf reading changing and taking the chain with it.Well → GasBearingWell → Produceris two rule edges in a row.