docs← Back to article

Markdown for LLMs

Sum over items, not over grounds

The source Markdown for this article. Copy it into your assistant or download it as a text file.

Download this articlePlain text ↗
# Sum over items, not over grounds

## Intention

I want to include each payment in the sum once, regardless of how many grounds apply.

Two grounds for excluding one payment once produced an understated base; the witness below repeats the error on a 500 KZT payment.

## Incorrect form and why it stays silent

```text title="Incorrect form"
sum(collect all x: Money, p: Payment, g: Ground where excluded_by(p, g) and amount(p, x))
```

List keeps substitutions: a payment with two grounds appears twice. First derive a single excluded flag per payment, then sum the payments.

## Correct form

```law
language "law.core" version "0.2";
package recipes.l.r06 version "0.1.0";
namespace "urn:recipe:l-calc:06";

entity Payment;
entity Ground;
relation amount(p: Payment, x: Money) { key(p); }
relation excluded_by(p: Payment, g: Ground);
relation excluded(p: Payment);
relation total(x: Money);
rule Excluded strict { for p: Payment; for g: Ground; when excluded_by(p, g); then excluded(p); }
rule Sum strict { when true; then total(sum(collect all x: Money, p: Payment where excluded(p) and amount(p, x))); }
```

## Frozen execution scene

| Facts and choice | Question | Answer |
|---|---|---|
| 1. one payment with two grounds | `truth(total(500 KZT))` | `TRUE_ONLY` / `COMPUTED` |
| 2. mistaken sum over grounds | `sum(collect all x: Money, p: Payment, g: Ground where excluded_by(p, g) and amount(p, x))` | `1000 KZT` / `COMPUTED` |
| 3. two 500 payments, grounds do not multiply the sum | `truth(total(1000 KZT))` | `TRUE_ONLY` / `COMPUTED` |

```law
test "one payment on two grounds" {
    given {
        context {
            legal_time @2026-09-13;
            decision_time @2026-09-13T09:00:00+05:00;
            knowledge_time @2026-09-13T09:00:00+05:00;
            timezone "Asia/Almaty";
        }
        assert amount(entity_ref("urn:recipe:l-calc:06:p"), 500 KZT); assert excluded_by(entity_ref("urn:recipe:l-calc:06:p"), entity_ref("urn:recipe:l-calc:06:g")); assert excluded_by(entity_ref("urn:recipe:l-calc:06:p"), entity_ref("urn:recipe:l-calc:06:h"));
    }
    evaluate truth(total(500 KZT));
    expect truth_status == TRUE_ONLY;
    expect evaluation_status == COMPUTED;
    
}
```

```law
test "mistaken total by grounds" {
    given {
        context {
            legal_time @2026-09-13;
            decision_time @2026-09-13T09:00:00+05:00;
            knowledge_time @2026-09-13T09:00:00+05:00;
            timezone "Asia/Almaty";
        }
        assert amount(entity_ref("urn:recipe:l-calc:06:p"), 500 KZT); assert excluded_by(entity_ref("urn:recipe:l-calc:06:p"), entity_ref("urn:recipe:l-calc:06:g")); assert excluded_by(entity_ref("urn:recipe:l-calc:06:p"), entity_ref("urn:recipe:l-calc:06:h"));
    }
    evaluate sum(collect all x: Money, p: Payment, g: Ground where excluded_by(p, g) and amount(p, x));
    expect value == 1000 KZT;
    expect evaluation_status == COMPUTED;
    
}
```

```law
test "two payments, grounds do not multiply" {
    given {
        context {
            legal_time @2026-09-13;
            decision_time @2026-09-13T09:00:00+05:00;
            knowledge_time @2026-09-13T09:00:00+05:00;
            timezone "Asia/Almaty";
        }
        assert amount(entity_ref("urn:recipe:l-calc:06:p"), 500 KZT); assert excluded_by(entity_ref("urn:recipe:l-calc:06:p"), entity_ref("urn:recipe:l-calc:06:g")); assert excluded_by(entity_ref("urn:recipe:l-calc:06:p"), entity_ref("urn:recipe:l-calc:06:h")); assert amount(entity_ref("urn:recipe:l-calc:06:a"), 500 KZT); assert excluded_by(entity_ref("urn:recipe:l-calc:06:a"), entity_ref("urn:recipe:l-calc:06:g"));
    }
    evaluate truth(total(1000 KZT));
    expect truth_status == TRUE_ONLY;
    expect evaluation_status == COMPUTED;
    
}
```



## Counterfactual

Mutation: `x: Money, p: Payment where excluded(p)` → `x: Money where excluded(p)`; expected `LDC-E1317`. Additional counterfactuals are shown as separate table rows.

## Boundary

A key does not fix the unit of count and does not prove journal completeness. Without payments sum is empty and yields EMPTY_AGGREGATE; zero needs a separate completeness ground.

## Pitfall

Summing over grounds multiplies the payment by its number of grounds; sum over the derived per-payment flag instead.