docs← Back to article

Markdown for LLMs

Closed world: when silence means "no"

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

Download this articlePlain text ↗
# Closed world: when silence means "no"

In all previous tutorials a missing fact yielded `NEITHER`: law stays
silent. That is the right default caution: from the archive not knowing
about your accreditation it does not follow that you have none.

But sometimes it does. The archive keeps the accreditation registry, and
keeps it **exhaustively**: if a person is not in it, she has no
accreditation. Not "unknown" but no. That is a legal fact about the
registry's completeness, and it must be **declared**, not implied.

```law
language "law.core" version "0.2";
package tutorial.archive version "0.4.0";
namespace "urn:law:tutorial:archive";

entity Person;

relation known_reader(p: Person) kind institutional;
relation accredited(p: Person) kind institutional;
relation in_researcher_registry(p: Person) kind institutional;
relation must_be_escorted(p: Person) kind institutional;
```

## Declaring completeness

```law
closure AccreditationClosure {
    predicate accredited;
    domain known_reader;
    snapshot ARCHIVE_ACCREDITATION_2026;
    complete_as_of @2026-03-01T00:00:00+00:00;
    derive_explicit_negative true;
}
```

It reads like this: the predicate `accredited` is **exhaustively**
described by the `ARCHIVE_ACCREDITATION_2026` snapshot as of 1 March
2026, but only for those falling into the `known_reader` domain.

The four fields carry four different obligations, and none is spare:

| Field | What is declared |
|---|---|
| `predicate` | exactly which predicate is closed: not the package, not the ontology as a whole |
| `domain` | about whom the registry may speak exhaustively |
| `snapshot` | against which source; this is an address, not a word |
| `complete_as_of` | at which moment completeness is claimed: the registry is complete **as of a date**, not forever |

`derive_explicit_negative true` means a missing entry produces
a **full negative fact**, not a mere lookup failure. The difference shows
in the proof: such a negation carries a closure certificate, and the
report states on what ground the system decided the entry is absent.

## What the engine answers

| Reader | Facts | `accredited` |
|---|---|---|
| Ivanova | in domain, entry present | `TRUE_ONLY` |
| Ivanova | in domain, no entry | `FALSE_ONLY` |
| visiting researcher | outside domain | `NEITHER` |

The middle row is what `closure` is written for. The top and bottom rows
are the same without it.

**Closure is local.** About those outside the domain the registry says
nothing, and the system answers `NEITHER`. The engine is not being
cautious here but reads the declaration literally: the archive claimed
completeness about its own readers, not about all people in the world.
The temptation to write `domain` wider is worth resisting: widening the
domain widens the claim the archive answers for.

## Why this belongs in a norm

Negation in a rule body works only where negation can be established.

```law
rule EscortRequired strict {
    for p: Person;
    when in_researcher_registry(p) and not accredited(p);
    then must_be_escorted(p);
}
```

| Reader | Facts | `must_be_escorted` |
|---|---|---|
| Ivanova | in domain, in researcher registry, no accreditation | `TRUE_ONLY` |
| visitor | outside domain, in researcher registry | `NEITHER` |

Without a declared closure this rule would **never** fire: `not
accredited(p)` requires an established negation, and there is nowhere to
take it from. There is no negation by failure in the core; the silence
of rules remains silence (we saw that in the second tutorial). `closure`
is the only way to turn a missing entry into a fact, and that way
requires an explicit claim about the source's completeness.

## What it costs

Closure is a strong claim about the world, and it ages. `complete_as_of`
fixes a moment; a norm executed at a later date relies on a snapshot
that may be stale. Checking the snapshot's freshness is the job of the
process around the system, not of the engine: the core executes what was
declared and does not guess when the declaration stopped being true.

Hence the practical rule: `closure` is declared where completeness has
a **holder**, an authority obliged to keep the registry and answering
for its completeness. If there is no such authority, `NEITHER` is the
honest answer, and replacing it with `FALSE` by declaring completeness
means shifting onto the system a responsibility nobody took.

## How law answers "no"

The three tools with which law answers "no" can now be told apart:

| Mechanism | Answer | Ground |
|---|---|---|
| norm with a negative head | `FALSE_ONLY` | the norm directly forbids |
| `closure` | `FALSE_ONLY` | the registry is declared complete |
| `defeater` | `NEITHER` | the ground of application has lapsed |
| none of the above | `NEITHER` | the input is incomplete |

Next is [the test in the language of law](/tutorials/writing-tests/):
a check a lawyer will read is written in the same language as the
norm. After it comes a real act: article 175 of the Social Code of the
Republic of Kazakhstan, its source, the content-hash anchor, and
reconciliation with the pinned edition.

The exercise for this page is [/tutorials/exercise-closed-world/](/tutorials/exercise-closed-world/).