docs← Back to article

Markdown for LLMs

nb-06 — When the register may stay silent

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

Download this articlePlain text ↗
# nb-06 — When the register may stay silent

*Northbridge course, intermediate ([nb-05](/tutorials/northbridge/nb-05-suitable-applicant/) →
[nb-06](/tutorials/northbridge/nb-06-register-silence/) → [nb-07](/tutorials/northbridge/nb-07-document-vs-fact/)). All law is
fictional; every register, snapshot and document in this series is
synthetic and unofficial. Engine `law 0.1.0`, semantics `law.core/0.2`,
std `0.2.0`. Northbridge is a synthetic training story: nothing here
describes a real deployment or makes any legally valid claim.*

## Situation

A Northbridge clerk checks two files. Carl's file says he is on the
residents' file index, but holds no residency record for him. Dana's
file holds nothing at all — she is not even on the index. The clerk
asks the same question twice: "is this person a filed resident?" For
Carl the answer is no. For Dana the answer is: we cannot say.

Both answers are silences, but different ones. Carl's file is complete:
the register guarantees that everything filed by 1 January 2026 is in
it, so a missing record means the filing never happened. Dana stands
outside the register's coverage entirely, so the same missing record
means nothing at all.

This article teaches the `closure` construct that draws that line: a
full domain, a pinned snapshot, and the key rule that keeps conflicting
entries visible.

## Prerequisites

[nb-02: Why a missing fact is not a refusal](/tutorials/northbridge/nb-02-missing-fact/)
taught the four truth statuses (`TRUE_ONLY`, `FALSE_ONLY`, `BOTH`,
`NEITHER`): by default, silence yields `NEITHER`.
[nb-05: Who counts as a suitable applicant](/tutorials/northbridge/nb-05-suitable-applicant/)
introduced the applicant relations from the vocabulary package.

This article adds one idea: an explicit, scoped declaration that turns
some of that silence into a derived negative.

## Minimal example

Excerpt from `packs/examples/language-demo/register/package.law`
(the closure block, lines 17–23; rule bodies and the evidence pipeline
cut):

```law
closure ResidentsFile {
    predicate filed_resident;
    domain on_resident_file;
    snapshot "urn:snapshot:demo-northbridge-residents:2026";
    complete_as_of @2026-01-01T00:00:00Z;
    derive_explicit_negative true;
}
```

Five lines, five ideas — each term explained at first use.
`closure ResidentsFile` names a completeness claim over one predicate:
`filed_resident`. The register says "we know everything about who
filed".

`domain on_resident_file` bounds that claim. Completeness applies only
to applicants satisfying `on_resident_file` — the file index. Anyone
off the index is outside the closure, and silence about them stays
silence.

`snapshot` pins *which* register copy the claim is about
(`urn:snapshot:demo-northbridge-residents:2026`). The claim is tied to
that frozen copy, not to "the register" in the abstract.

`complete_as_of` pins *when*: the copy is complete for everything
filed up to `@2026-01-01T00:00:00Z`.

`derive_explicit_negative true` says what silence inside the domain
becomes: a domain member with no record derives the explicit negation
— a real `FALSE_ONLY`, not a `NEITHER`.

Excerpt from `packs/examples/language-demo/vocabulary/package.law`
(line 21; surrounding relations cut):

```law
pub relation application_filed(a: Applicant, on: Date) kind empirical { key(a); }
```

**`key(a)`** declares a functional dependency: one applicant, one
filing date. Asserting two dates for Ann does not silently overwrite —
the engine keeps the fact and raises a `KEY_CONFLICT` issue, so the
conflict stays visible in the record.

Excerpt from `packs/examples/language-demo/register/tests/register.lawtest`
(the closure pair, lines 5–20; remaining seven tests cut):

```law
test "on file without a record — not a resident" {
    given {
        context { legal_time @2026-03-01; decision_time @2026-03-01T09:00:00Z; knowledge_time @2026-03-01T09:00:00Z; timezone "UTC"; }
        assert "file-carl": on_resident_file(entity_ref("urn:demo:northbridge:carl")) { origin case_input; }
    }
    evaluate truth(filed_resident(entity_ref("urn:demo:northbridge:carl")));
    expect truth_status == FALSE_ONLY;
}

test "outside the domain silence is silence" {
    given {
        context { legal_time @2026-03-01; decision_time @2026-03-01T09:00:00Z; knowledge_time @2026-03-01T09:00:00Z; timezone "UTC"; }
    }
    evaluate truth(filed_resident(entity_ref("urn:demo:northbridge:dana")));
    expect truth_status == NEITHER;
}
```

Read the two tests side by side. Carl's test asserts one fact — he is
on the index — and asks about `filed_resident`: the answer is
`FALSE_ONLY`. Dana's test asserts nothing at all and asks the same
question: the answer is `NEITHER`. Same missing record, different
coverage, different answer.

## Command and result

The register package tests against its own world plus the vocabulary
(world pinned in `suite.json`), so one command checks everything:

```sh
law test packs/examples/language-demo/register
```

Observed result (engine `law 0.1.0`):

```text
law test demo.northbridge.register: мир demo.northbridge.register, demo.northbridge.vocabulary
  ok   [demo.northbridge.register] tests/register.lawtest / on file without a record — not a resident
  ok   [demo.northbridge.register] tests/register.lawtest / outside the domain silence is silence
  ok   [demo.northbridge.register] tests/register.lawtest / file record accepted
  ok   [demo.northbridge.register] tests/register.lawtest / key conflict: two filing dates
  ok   [demo.northbridge.register] tests/register.lawtest / policy selected, authenticity pinned
  ok   [demo.northbridge.register] tests/register.lawtest / without pinned authenticity support is rejected
  ok   [demo.northbridge.register] tests/register.lawtest / without a policy support stays transport
  ok   [demo.northbridge.register] tests/register.lawtest / bare assertion of the protected under a policy
  ok   [demo.northbridge.register] tests/register.lawtest / refuting document
итого: 9 проверено, 9 прошли, 0 не прошли, 0 не исполнены; код 0
```

The package also passes the static check:

```sh
law engine check packs/examples/language-demo/register/package.law
```

```text
check OK: packs/examples/language-demo/register/package.law
```

Four of the nine tests carry this article:

| Test | Setup | Expects |
|---|---|---|
| `on file without a record — not a resident` | Carl on the index, no record | `FALSE_ONLY` |
| `outside the domain silence is silence` | Dana off the index | `NEITHER` |
| `file record accepted` | Ann's `filed_resident` asserted | `TRUE_ONLY` via `AdmitFromFile` |
| `key conflict: two filing dates` | Ann filed on two dates | `TRUE_ONLY` + `issue(KEY_CONFLICT)` |

All nine tests pass: each answer matched its expectation. A passing
test is not a ruling in anyone's favour — it only says the engine's
answer agreed with the test's `expect` line.

The summary line is in Russian: `итого: 9 проверено, 9 прошли, 0 не
прошли, 0 не исполнены; код 0` — 9 checked, 9 passed, 0 failed,
0 skipped, exit code 0. The first line names the test world (`мир`):
the register package plus the vocabulary it reads.

The key-conflict test deserves a close read. The queried literal
(`application_filed` on the first date) is still `TRUE_ONLY` — the
engine deletes neither entry — but the run attaches a `KEY_CONFLICT`
issue. The conflict is reported, not resolved and not hidden.

## Why this construct

The task is to let the register say "no" when its coverage justifies
it, without letting that "no" leak to people the register never
covered.

A `closure` writes the coverage down — predicate, domain, snapshot id,
as-of time — so the negative is derived *from stated completeness*,
with the scope inspectable in the source. Carl's `FALSE_ONLY` cites
the closure; it is not a clerk's assumption.

The nine tests are the proof. The closure pair pins the
inside/outside-domain split, the conflict test pins the visible issue,
and the remaining five tests (the evidence pipeline, covered in
[nb-07: A document is not yet a proven fact](/tutorials/northbridge/nb-07-document-vs-fact/))
confirm the closure changes nothing outside its predicate.

What is not proven: that the register copy is *actually* complete. The
tests prove the engine derives negatives exactly inside the declared
domain and snapshot; no test can prove the clerks filed everything by
the as-of date. Completeness is a declared claim, not a verified fact.

<details>
<summary>Why not a default rule?</summary>

A default rule ("everyone without a record is not a resident") would
give Carl's answer too, but it would also answer for Dana — wrongly,
since the register never covered her. Keeping `NEITHER` as the default
and closing only the indexed domain (the default-silence discipline of
[nb-02: Why a missing fact is not a refusal](/tutorials/northbridge/nb-02-missing-fact/) plus
one explicit boundary) keeps Dana's silence silent.

Likewise, without `key(a)` the two filing dates would coexist quietly;
the key turns a data-entry collision into an observable issue.

</details>

## Changed condition

Change one condition: put Dana on the index. Add to her test's `given`
block:

Sketch — one line to insert into the test's `given` block, not
runnable alone; the flipped outcome is quoted below.

```law
assert "file-dana": on_resident_file(entity_ref("urn:demo:northbridge:dana")) { origin case_input; }
```

Her `filed_resident` question flips from `NEITHER` to `FALSE_ONLY` —
she becomes Carl. Nothing about Dana's records changed (there still are
none); only her coverage changed, and coverage decides *whether*
silence speaks.

The reverse also holds. Asserting a record for Carl (`filed_resident`
for Carl) flips his answer from `FALSE_ONLY` to `TRUE_ONLY` by the
assertion itself — an actual record beats the closure's negative — and
`resident_admitted` follows via `AdmitFromFile`. A record decides
*what* is said.

## Typical mistake

The mistake is reading the closure as universal: "Dana has no record,
so she is not a resident."

The observed consequence is a status the author did not expect:
`truth(filed_resident(dana))` stays `NEITHER`, and any downstream rule
waiting for a derived `not filed_resident(dana)` never fires.

The fix is not to argue with the engine but to check coverage: is Dana
in `on_resident_file`? If she should be, assert the index fact — and
she becomes `FALSE_ONLY`, honestly. If the register genuinely does not
cover her, `NEITHER` is the correct finding: report it and complete the
file instead of inventing a denial.

## Limits

- **One predicate, one domain, one snapshot.** The closure speaks only
  about `filed_resident`, only for index members, only as of the pinned
  copy and time. A second register (vehicles, fines) needs its own
  closure; the engine does not generalize coverage across predicates.
- **Declared, not verified.** `complete_as_of` records when the copy
  claims completeness; the engine trusts the declaration. A late filing
  missing from the copy still derives a negative — the failure mode of
  every closed file, stated plainly.
- **Keys report, they do not resolve.** `KEY_CONFLICT` flags the double
  filing and both entries survive; choosing the correct date is a human
  correction to the file, not an engine inference.
- **Verified profile:** engine `law 0.1.0`, semantics `law.core/0.2`.
  Refusals or behaviors outside this profile are implementation facts,
  never language-wide inability claims.

## Exercise

Dana's file starts exactly as in `outside the domain silence is
silence` (nothing asserted). Without running the engine, predict, then
check with `law test`:

1. `truth(filed_resident(dana))` after asserting only
   `on_resident_file(dana)` — status?
2. `truth(resident_admitted(dana))` after asserting only
   `on_resident_file(dana)` — status, and which rule (if any) fires?
3. `truth(filed_resident(dana))` after asserting both
   `on_resident_file(dana)` and `filed_resident(dana)` — status?
4. In `key conflict: two filing dates`, what would change if
   `application_filed` had no `key(a)` — the `TRUE_ONLY`, the
   `KEY_CONFLICT` issue, or both?

Write each prediction down first; run the suite; explain any miss in
one sentence. Checkable solution:
[solutions/nb-06-solutions.md](/tutorials/northbridge/solutions/nb-06-solutions/).

## Sources

Three levels, separated:

1. **Northbridge use** (this article, verified): the `ResidentsFile`
   closure and the `key(a)` on `application_filed`, verified by the
   nine tests above (`law test
   packs/examples/language-demo/register`: 9 checked, 9 passed).
2. **Domain template:** for any "is X on file?" question, declare the
   coverage as a closure — predicate, membership relation as domain,
   snapshot id plus as-of time — with `derive_explicit_negative true`
   only where the file genuinely claims completeness; declare functional
   dependencies with `key(...)` so collisions surface as issues instead
   of silent overwrites.
3. **Confirmed example elsewhere:** budget-linked benefit parameter
   (Kazakhstan Social Code, 2026 financial year) — package
   `kz.corpus.socialcode`,
   `corpus/laws/kz/codes/social-code/00-package.law:4978-4989`,
   construct `effective [start, end)` finite rule window
   (`AddressedAssistanceMciFromBudget2026` holds only inside
   `[@2026-01-01, @2027-01-01)`). Why this form fits: scope is
   declared, not assumed — the rule switches off by date rather than
   by package edit, the same explicit-bounds discipline as the
   closure's domain-plus-moment coverage. Evidence:
   `docs/research/constructs/19-time/corpus-forms.en.md` section 1
   (rated exemplary there). Limit of verification: presence of the
   named construct at the cited lines only, confirmed by direct source
   read; no claim about deployment, runtime behavior, or legal
   correctness.

<details>
<summary>Cited, not claimed: the tariff snapshot</summary>

The same package holds a `tariff` function with
`snapshot_from context.tariffs`, which pins external values to a
snapshot the same way. It has no scenario in this suite, so it is
cited, not claimed.

</details>

- Source: `packs/examples/language-demo/register/package.law`
  (closure lines 17–23, key in `packs/examples/language-demo/vocabulary/package.law`
  line 21)
- Tests: `packs/examples/language-demo/register/tests/register.lawtest`
- Suite tour: `packs/examples/language-demo/README.md`
- Prerequisite: [nb-05: Who counts as a suitable applicant](/tutorials/northbridge/nb-05-suitable-applicant/); next:
  [nb-07: A document is not yet a proven fact](/tutorials/northbridge/nb-07-document-vs-fact/)