nb-06 — When the register may stay silent
Northbridge course, intermediate (nb-05 →
nb-06 → nb-07). 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
Section titled “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
Section titled “Prerequisites”nb-02: Why a missing fact is not a refusal
taught the four truth statuses (TRUE_ONLY, FALSE_ONLY, BOTH,
NEITHER): by default, silence yields NEITHER.
nb-05: Who counts as a 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
Section titled “Minimal example”Excerpt from packs/examples/language-demo/register/package.law
(the closure block, lines 17–23; rule bodies and the evidence pipeline
cut):
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):
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):
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
Section titled “Command and result”The register package tests against its own world plus the vocabulary
(world pinned in suite.json), so one command checks everything:
law test packs/examples/language-demo/registerObserved result (engine law 0.1.0):
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 не исполнены; код 0The package also passes the static check:
law engine check packs/examples/language-demo/register/package.lawcheck OK: packs/examples/language-demo/register/package.lawFour 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
Section titled “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) 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.
Why not a default rule?
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 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.
Changed condition
Section titled “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.
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
Section titled “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
Section titled “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_ofrecords 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_CONFLICTflags 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, semanticslaw.core/0.2. Refusals or behaviors outside this profile are implementation facts, never language-wide inability claims.
Exercise
Section titled “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:
truth(filed_resident(dana))after asserting onlyon_resident_file(dana)— status?truth(resident_admitted(dana))after asserting onlyon_resident_file(dana)— status, and which rule (if any) fires?truth(filed_resident(dana))after asserting bothon_resident_file(dana)andfiled_resident(dana)— status?- In
key conflict: two filing dates, what would change ifapplication_filedhad nokey(a)— theTRUE_ONLY, theKEY_CONFLICTissue, or both?
Write each prediction down first; run the suite; explain any miss in one sentence. Checkable solution: solutions/nb-06-solutions.md.
Sources
Section titled “Sources”Three levels, separated:
- Northbridge use (this article, verified): the
ResidentsFileclosure and thekey(a)onapplication_filed, verified by the nine tests above (law test packs/examples/language-demo/register: 9 checked, 9 passed). - 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 trueonly where the file genuinely claims completeness; declare functional dependencies withkey(...)so collisions surface as issues instead of silent overwrites. - 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, constructeffective [start, end)finite rule window (AddressedAssistanceMciFromBudget2026holds 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.mdsection 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.
Cited, not claimed: the tariff snapshot
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.
- Source:
packs/examples/language-demo/register/package.law(closure lines 17–23, key inpacks/examples/language-demo/vocabulary/package.lawline 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; next: nb-07: A document is not yet a proven fact
Documentation for Arxo. Writings — blog.arxo.io.
Anonymous visit counts on stats.arxo.io, no cookies.