# Distinguish an unknown sum from zero ## Intention I want to derive a zero sum only when completeness of an empty journal is confirmed. ## Incorrect form and why it stays silent Removing complete turns missing facts into an established zero. Removing the non-emptiness guard computes an empty sum: ```text title="Incorrect form" when opened(j) and count(collect all a: Decimal where item(j, a)) == 0; then total(j, 0.0); ``` ## Correct form ```law language "law.core" version "0.2"; package recipes.v.r05 version "0.1.0"; namespace "urn:recipe:v-negation:05"; entity Journal; relation opened(j: Journal); relation complete(j: Journal); relation item(j: Journal, amount: Decimal); relation total(j: Journal, amount: Decimal); rule Sum strict { for j: Journal; when opened(j) and count(collect all a: Decimal where item(j, a)) >= 1; then total(j, sum(collect all a: Decimal where item(j, a))); } rule Zero strict { for j: Journal; when opened(j) and complete(j) and count(collect all a: Decimal where item(j, a)) == 0; then total(j, 0.0); } ``` ## Frozen execution scene | Facts | Question | Answer | |---|---|---| | opened, no data | total(a, 0.0) | NEITHER | | opened, complete | total(a, 0.0) | TRUE_ONLY | | opened, item(a, 5.0) | total(a, 5.0) | TRUE_ONLY | | no complete; completeness guard removed | total(a, 0.0) | TRUE_ONLY — model error | | empty journal; sum guard removed | total(a, 0.0) | NEITHER, EMPTY_AGGREGATE | ```law test "unknown sum stays unknown" { 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 opened(entity_ref("urn:recipe:v-negation:05:a")); } evaluate truth(total(entity_ref("urn:recipe:v-negation:05:a"), 0.0)); expect truth_status == NEITHER; } ``` ```law test "completed journal fixes zero" { 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 opened(entity_ref("urn:recipe:v-negation:05:a")); assert complete(entity_ref("urn:recipe:v-negation:05:a")); } evaluate truth(total(entity_ref("urn:recipe:v-negation:05:a"), 0.0)); expect truth_status == TRUE_ONLY; } ``` ```law test "present item fixes sum" { 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 opened(entity_ref("urn:recipe:v-negation:05:a")); assert item(entity_ref("urn:recipe:v-negation:05:a"), 5.0); } evaluate truth(total(entity_ref("urn:recipe:v-negation:05:a"), 5.0)); expect truth_status == TRUE_ONLY; } ``` ```law test "dropped guard fabricates zero" { 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 opened(entity_ref("urn:recipe:v-negation:05:a")); } evaluate truth(total(entity_ref("urn:recipe:v-negation:05:a"), 0.0)); expect truth_status == TRUE_ONLY; } ``` ```law test "empty sum errors distinctly" { 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 opened(entity_ref("urn:recipe:v-negation:05:a")); } evaluate truth(total(entity_ref("urn:recipe:v-negation:05:a"), 0.0)); expect truth_status == NEITHER; expect issue(EMPTY_AGGREGATE); } ``` ## Counterfactual runs.without removes `complete(j) and ` or the non-emptiness guard. teaches drops the binding of j from Zero and requires E4101: counting an empty collection once does not replace the journal domain. ## Boundary Absence of tuples by itself yields NEITHER, but not necessarily MISSING_INPUT. An empty sum has its own EMPTY_AGGREGATE; an unresolved comparison input is a different case. ## Pitfall An empty sum gives EMPTY_AGGREGATE while count gives zero. In `calc.obligations` a separate journal-completeness flag does not let missing facts be taken as absence of obligations.