Skip to content
docs
Arxo ↗

Coming from DMN

For LLMs7 sections

In short: a DMN decision table is a set of rows plus a hit policy that says what happens when several rows match. In Arxo there is no table-wide hit policy: each row is a defeasible rule, and what DMN decides through the hit policy is written where it applies. Rows that agree simply support the same conclusion; rows that disagree both fire until a priority says which wins; first-hit order is an explicit guard; a default output is a closure over the cases on file; and a missing input is an answer of its own, NEITHER. This page follows the portable table of the DMN comparison. The Arxo package quoted here passes law engine check; its scenarios and the DMN engines of the experiment have not been run yet, so the page shows how the table is written, not measured outcomes.

DMNArxoWhat changes for you
A row of a decision tableA rule … defeasible: the input entries become the when condition, the output entry becomes the headEach row has a name, and an answer can say which rows supported it
UNIQUE or ANY: rows overlap and disagree (a runtime incident in the table engines)Two rules that both fire for the case, with no priority between them; Arxo’s tabular form reports the same overlap statically, before executionA violated hit policy is something you see in the answer or in a diagnostic, rather than an engine failure
COLLECT with rows that agreeSeveral rules with the same head: each is an independent supportAgreement needs no policy
FIRSTThe later row carries a guard, not <earlier conclusion>, that encodes the order of the source tableRow order is spelled out in the rule that depends on it
PRIORITY (excluded from the portable baseline)Precedence between particular rules is a priority with a reasonThere is no table-wide ordering of outputs
Default output entry, no matchA closure over the cases on file: a case in the domain with no supporting row gets an explicit negativeThe default applies only to the cases the register covers
Null inputAbsent facts: a predicate without a closure answers NEITHER; a closed predicate answers FALSE_ONLYThe difference between “unknown” and “no” is chosen per predicate, not per engine
A table edit that changes a thresholdAn exception rule with a priority and reason explicit_exception; it needs the revision fact to fireCases under the old revision are untouched by the new rule
COLLECT aggregation (SUM, MIN, MAX, COUNT) and list outputsNot built by this package: it names which rows fired; the aggregated values stay in the neutral case bankParity here is observed, not computed

Here is a table from the DMN conformance test suite that the experiment uses as an input: two rows, a COLLECT policy with MAX aggregation, and a default output of 0.

xml
<decisionTable aggregation="MAX" hitPolicy="COLLECT">
<input id="d_Extra_days_case_1_dt_i_age" label="Age">
<inputExpression typeRef="number">
<text>Age</text>
</inputExpression>
</input>
<input id="d_Extra_days_case_1_dt_i_years" label="Years of Service">
<inputExpression typeRef="number">
<text>Years of Service</text>
</inputExpression>
</input>
<output id="d_Extra_days_case_1_dt_o" label="Extra days">
<defaultOutputEntry>
<text>0</text>
</defaultOutputEntry>
</output>
<rule id="d_Extra_days_case_1_dt_r1">
<inputEntry id="d_Extra_days_case_1_dt_r1_i1">
<text>&lt;18,&gt;=60</text>
</inputEntry>
<inputEntry id="d_Extra_days_case_1_dt_r1_i2">
<text>-</text>
</inputEntry>
<outputEntry id="d_Extra_days_case_1_dt_r1_o1">
<text>5</text>
</outputEntry>
</rule>
<rule id="d_Extra_days_case_1_dt_r2">
<inputEntry id="d_Extra_days_case_1_dt_r2_i1">
<text>-</text>
</inputEntry>
<inputEntry id="d_Extra_days_case_1_dt_r2_i2">
<text>&gt;=30</text>
</inputEntry>
<outputEntry id="d_Extra_days_case_1_dt_r2_o1">
<text>5</text>
</outputEntry>
</rule>
</decisionTable>

The Arxo package encodes the experiment’s own portable table rather than this file line by line, but the closest pair of rows looks like this: two rows granting the same extra amount, one for long service and one for a young employee. In this package the input bands arrive as facts about the case (years_long, age_minor), so the FEEL tests do not appear in the rule bodies; and the package covers only the under-18 band of the first row above.

Arxo Law
rule ExtraVeteran defeasible {
for c: Case;
when years_long(c);
then extra5(c);
}
rule ExtraYoung defeasible {
for c: Case;
when age_minor(c);
then extra5(c);
}

When both rows match, both rules support extra5(c). There is nothing to reconcile, so there is no hit policy to choose.

Under UNIQUE or ANY, two matching rows with different outputs are an error in the table engines. In Arxo the same situation is two rules with different conclusions for one case and no priority between them:

Arxo Law
rule ConflictA defeasible {
for c: Case;
when overlap_case(c);
then award_standard(c);
}
rule ConflictB defeasible {
for c: Case;
when overlap_case(c);
then award_senior(c);
}

Both rules fire, and nothing in the package picks one: the priority is left out on purpose, so the overlap stays in the answer for a person to resolve. For the same table in Arxo’s tabular form, the overlap is reported before execution. The DMN comparison calls this a difference of stage, not of meaning.

FIRST makes the order of rows part of the table’s meaning. In Arxo the order is written into the rule that depends on it: the second row only fires when the first row’s outcome is established false, which here comes from the closure on award_standard shown below.

Arxo Law
rule FirstRow1 defeasible {
for c: Case;
when first_case(c);
then award_standard(c);
}
rule FirstRow2 defeasible {
for c: Case;
when first_case(c) and not award_standard(c);
then award_senior(c);
}

A negative literal in a body needs the fact to be established false, so when FirstRow1 fires, FirstRow2 stays silent. The experiment also switches one table from a conflicting policy to FIRST; on the Arxo side the same pair of rules covers the switched revision.

A default output entry answers when no row matches. The Arxo counterpart is a closure: a statement that the register of cases is complete as of a given time, so a case in it with no supporting row is explicitly not awarded the standard outcome. Rules that do support it are not affected.

Arxo Law
closure AwardDefault {
predicate award_standard;
domain case_on_file;
snapshot "urn:stand:dmn-hitoverlap:cases:2026-09-30";
complete_as_of @2026-09-30T00:00:00Z;
derive_explicit_negative true;
}

A table edit that moves an age threshold from 18 to 21 becomes an exception rule that only fires with the revision fact, plus a priority over the row it overrides:

Arxo Law
rule Threshold21Exception defeasible {
for c: Case;
when rev_b_threshold_21(c) and age_young_edge(c);
then not award_standard(c);
}
priority Threshold21OverStd {
prefer Threshold21Exception over RowStd;
reason explicit_exception;
}
  • Gain: a named policy per conflict. Instead of one hit policy for the whole table, each overlap is either agreement, a declared priority with a reason, or a conflict kept visible.
  • Gain: “unknown” separate from “no”. A missing input does not have to be folded into the default; the DMN comparison notes that the standard leaves no match and null input open and engines diverge.
  • Give up: the table as the reading surface. Analysts read a DMN table directly, in mature editors, and exchange it between engines through a shared interchange file with a community conformance suite. This page shows the rule form, and this package does not compute aggregations or list outputs.
  • Combining rather than replacing. The comparison’s combined pattern keeps the tabular fragment in DMN and lets Arxo frame it with editions, conflicts and review trails.

Documentation for Arxo. Writings — blog.arxo.io.

Anonymous visit counts on stats.arxo.io, no cookies.