# nb-17 — Numeric families and support boundaries *Northbridge course, math branch (needs beginner only: [nb-01: First permit: facts, a rule and a question](/tutorials/northbridge/nb-01-first-permit/) → [nb-04: The permit fee](/tutorials/northbridge/nb-04-permit-fee/)). All law is fictional; every resident, report, invoice and certificate is synthetic and unofficial. No real municipal deployment or legal-validity claims. Tool `law 0.1.0`, language version `0.2`, semantics `law.core/0.2`, std `0.2.0` (from `law --version`, quoted below).* ## Situation Mira, the Northbridge permits clerk, receives two numbers she is asked to certify. A footbridge inspection report states that a load factor stays inside a proven square-root bound. A foreign contractor submits an invoice in USD and asks the office to convert it to KZT at the day rate, and a second contractor asks the office to solve a tariff equation for him. The auditor's question is blunt: *which of these numbers can our pinned machine prove, and which must it refuse — and is each refusal a property of our build or of the language itself?* You will run the certified calls the pinned build does prove, then watch it refuse the rest — each refusal naming the exact layer that rejected it. The answer is never "the language cannot do this"; it is always "this build, at this layer, for this version." Each new term is introduced where it is first used. ## Prerequisites [nb-01: First permit: facts, a rule and a question](/tutorials/northbridge/nb-01-first-permit/): facts, strict rules, the four truth statuses (`TRUE_ONLY` means established, `NEITHER` means established neither way), `law test` as the way to check a claim. [nb-04: The permit fee](/tutorials/northbridge/nb-04-permit-fee/): Money arithmetic and rounding — the everyday numbers this article contrasts with the gated families. New here: certified bounds calls that execute only with an explicit profile argument, names outside the `0.2` support list, and three refusal codes at three different layers — each introduced where it first appears below. ## Minimal example Excerpts 1–2 are from `packs/examples/language-demo/math02/package.law` (identifiers as written; relations, imports and unrelated rules cut). Standalones A–C are complete probe files from `packs/examples/language-demo/boundaries/evidence/snippets/` (checked with `law engine check`; the `.law.txt` suffix only keeps the package scanner from picking them up). Excerpt 1 — self-determinism under one profile (lines 172–175). A rule that compares a certified root bound with *itself*. ```law rule SqrtSelf strict { when sqrt_bounds(4, "p8/0.1") == sqrt_bounds(4, "p8/0.1"); then sqrt_self(); } ``` Look at the `when` line: the same certified call (`sqrt_bounds(4, "p8/0.1")`) on both sides of `==`. The quoted `"p8/0.1"` is the **cert profile** — the name of the exact proven certificate the result carries. A **derivation** is the recorded history of how a value was computed; the same derivation under the same certificate is equal to itself, and this self-equality is what `0.2` guarantees. Excerpt 2 — the same call under two profiles (lines 186–189). Only the profile name changes. ```law rule CertCross strict { when sin_bounds(0, "p8/0.1") == sin_bounds(0, "p16/0.1"); then cert_cross(); } ``` Look at what changed: only the right-hand profile, `"p8/0.1"` → `"p16/0.1"`. The profile name is part of the derivation's history — so a different certificate means a different derivation, and the equality does not hold. The suite expects `NEITHER` for this rule. Standalone A — full content of `sqrt02.law.txt`: exact root without a certificate, in language `0.2`. ```law language "law.core" version "0.2"; package probe version "0.1.0"; namespace "urn:probe"; function f(x: Rational) -> Rational = exact_sqrt(x); ``` Look at the last line: a bare `exact_sqrt` with no profile. The name is simply not in the `0.2` support list, so the file is refused with two codes, not executed. This tiny frozen probe is a **refusal slice**: its only job is to show one boundary loudly. It is checked, never executed. Standalone B — full content of `solve02.law.txt`: a solver call, same shape, same fate. ```law language "law.core" version "0.2"; package probe version "0.1.0"; namespace "urn:probe"; function f(x: Rational) -> Rational = solve_linear(x, 1); ``` Look at the last line again: same four-line shape, different forbidden name. Solver terms (`solve_linear`, the 0.6 family) and the `Number` answer domain (0.4) live behind the same boundary as exact algebra — refused in this profile. Standalone C — full content of `v04.law.txt`: a newer language version than the tool implements. ```law language "law.core" version "0.4"; package probe version "0.1.0"; namespace "urn:probe"; relation r(x: Integer) kind empirical; ``` Look at the first line: the header declares language `0.4`. The refusal happens at the version layer, before any name is even looked up — the content below the header is never examined. ## Command and result The tool, language and semantics versions are five separate facts, and every refusal below names exactly one of them. The tool version is what is installed; the language version is what the file header declares; the exact semantics is the `law.core/0.2` revision plus the std release; implementation support is the std v1-list of declared names plus effect descriptors; the observed run is what the commands below printed: ```sh law --version ``` Observed: ```text law 0.1.0 семантика: law.core/0.2 std для языка 0.2: 0.2.0 ``` The Russian lines pin the semantics (`семантика: law.core/0.2`) and the std release for language 0.2 (`std для языка 0.2: 0.2.0`). A **numeric family** is a group of number-like types and functions that are versioned and supported together — integers and rationals in one family, certified real functions in another, solvers and conversions in further ones. The executable side — every family `0.2` supports — is green: ```sh law test packs/examples/language-demo/math02 ``` Observed (last lines; engine `law 0.1.0`): ```text ok [demo.northbridge.math02] tests/math02.lawtest / unit conversion by value ok [demo.northbridge.math02] tests/math02.lawtest / quantity returned into a unit ok [demo.northbridge.math02] tests/math02.lawtest / unit exponent итого: 42 проверено, 42 прошли, 0 не прошли, 0 не исполнены; код 0 ``` All 42 answers matched their expectations (`итого: 42 проверено, 42 прошли, 0 не прошли, 0 не исполнены; код 0` — 42 checked, 42 passed, 0 failed, 0 unexecuted, exit code 0). The neighbouring boundaries package is green too: ```sh law test packs/examples/language-demo/boundaries ``` Observed: `итого: 21 проверено, 21 прошли, 0 не прошли, 0 не исполнены; код 0` — 21 checked, 21 passed, 0 failed, 0 unexecuted, exit code 0. ```text итого: 21 проверено, 21 прошли, 0 не прошли, 0 не исполнены; код 0 ``` Both suites passing means every supported call behaved as declared. Now the refusals, each naming its layer. The version refusal first: ```sh law engine check packs/examples/language-demo/boundaries/evidence/snippets/v04.law.txt ``` Observed: `error LDC-E1401: версия языка "0.4" не поддерживается` ("language version 0.4 is not supported"), followed by the migration report ([§266.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/35-part-xxxiii-a-language-versioning.ru.md#2661-language-versions): `0.1 [withdrawn]`, `0.2 [supported]`, revisions `0.2.1`–`0.2.4`). The machine refuses the *version*, not the content: this build reads `0.2` and will not read `0.4` headers at all. The exact-algebra refusal carries two codes at once: ```sh law engine check packs/examples/language-demo/boundaries/evidence/snippets/sqrt02.law.txt ``` Observed, in full — two codes, hence two layers rejected the file: ```text packs/examples/language-demo/boundaries/evidence/snippets/sqrt02.law.txt:4:10: error LDC-E2403: COMPUTABILITY_NOT_PROVED (LDC-E2403): function "f" не доказана как PURE_DETERMINISTIC: вызов "exact_sqrt" не имеет доступного effect descriptor (imported/unknown dependency) (§46/§47.5) packs/examples/language-demo/boundaries/evidence/snippets/sqrt02.law.txt:4:39: error LDC-E2105: функция "exact_sqrt" не объявлена (ни в файле, ни в std v1-списке) ``` The first code (`LDC-E2403`) says the function is not proved `PURE_DETERMINISTIC` — the call has no available effect descriptor. The second (`LDC-E2105`) says the name is not declared, neither in the file nor in the std v1-list. Support and semantics each said no. The solver refusal is the identical pair for a different name: ```sh law engine check packs/examples/language-demo/boundaries/evidence/snippets/solve02.law.txt ``` Observed: `error LDC-E2403 ... вызов "solve_linear" не имеет доступного effect descriptor ...` ("the call has no available effect descriptor") plus `error LDC-E2105: функция "solve_linear" не объявлена (ни в файле, ни в std v1-списке)` ("the function is not declared, neither in the file nor in the std v1-list"). `fx_convert`, `pi`, `temp_convert`, `angle_convert` and `ln2` report the same pair (verified per snippet); a query used as a term (`query02.law.txt`) reports `E2105` alone — one layer, not two (see [nb-14: Why this answer and what would change it](/tutorials/northbridge/nb-14-why-this-answer/)). ## Why this construct Mira's auditor question — which numbers can the pinned machine prove? — is answered by knowing the usable surface without guessing. Integers, Rationals, Decimals, Money, Quantities, date/time spans, `HALF_UP` rounding, `unit_convert` with the pinned `units.si`, and certified bounds calls with an explicit profile all execute — 42 of 42 in the suite above. Read `"p8/0.1"` as a certificate identity, not a precision wish. `SqrtSelf` (same derivation, same profile) is `TRUE_ONLY`; `SqrtX` (`sqrt_bounds(4, "p8/0.1") == bounds_const(2, 1)`, lines 167–170 of the package) is `NEITHER`. A foreign derivation is never equal, however plausible its digits look. Cross-profile equality fails to keep parallel certificates apart. `CertCross` (`p8` vs `p16`) is `NEITHER` with `COMPUTED`: both sides computed, and the comparison honestly reports inequality of derivations. The mirror image — any profile equals itself — replays with the `p32` probe in Changed condition below (`TRUE_ONLY`, 43 of 43). Each refusal sits at its layer. `E2105` (not declared in the file or the std v1-list) is an *implementation-support* fact; `E2403` (computability not proved, no effect descriptor) is an *exact semantics* fact. Both gates must pass, and the probes fail both. `query02` fails only the first gate, which is why it carries one code instead of two. The version refusal separates the tool from the language. `E1401` says tool `law 0.1.0` implements no `0.4` semantics; the attached migration report names the supported branch (`0.2`, revisions `0.2.1`–`0.2.4`). It never says `0.4` content is meaningless — only that this build will not read it.
Why not hand-written digits or a bare exact call? Hand-writing the digits (`bounds_const(2, 1)` where the root belongs) looks equal and is not — the suite keeps `BoundsOk`/`SqrtX` at `NEITHER` precisely to catch that substitution. Reading a bare `exact_sqrt` where a certified call belongs is caught louder, at check time.
The evidence is the 42/42 and 21/21 suites, the `TRUE_ONLY` / `NEITHER` / `NEITHER` triple (`sqrt_self`, `sqrt_x`, `cert_cross`), and the five quoted refusal outputs — each executed above, none asserted in prose. What is not proven is that any certificate is mathematically correct, or that `p16` is "better" than `p8`. The machine proves which comparisons hold under which certificate; choosing a profile for a real bridge report is engineering judgment, and Northbridge's bridge is fiction. ## Changed condition `SqrtSelf` derives `sqrt_self()` as `TRUE_ONLY` — the identical call with the identical profile on both sides. Change exactly one token, the right-hand profile `"p8/0.1"` → `"p16/0.1"` (`CertCross`), and the same shape of question becomes `NEITHER`: both bounds computed (`COMPUTED`), the equality of derivations gone. Change the profile consistently on both sides instead and the status returns to `TRUE_ONLY`. One name changed asymmetrically flips the answer; changed symmetrically it changes nothing — because equality here compares derivations, not digits. Replay recipe (standalone scratch copy; the repo suite is untouched): ```sh cp -r packs/examples/language-demo/math02 /tmp/nb17-p32 ``` Append after `SqrtSelf` in `/tmp/nb17-p32/package.law`: ```law relation sqrt_self32() kind institutional; rule SqrtSelf32 strict { when sqrt_bounds(4, "p32/0.1") == sqrt_bounds(4, "p32/0.1"); then sqrt_self32(); } ``` Add `/tmp/nb17-p32/tests/p32.lawtest`: ```law language "law.core" version "0.2"; package demo.northbridge.math02 version "0.1.0"; namespace "urn:law:demo:northbridge:math02"; test "p32 self equals itself" { 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(sqrt_self32()); expect truth_status == TRUE_ONLY; } ``` Register `tests/p32.lawtest` in `/tmp/nb17-p32/law.toml` suites and run: ```sh law test /tmp/nb17-p32 ``` Observed: `p32 self equals itself` ok, `43 проверено, 43 прошли, 0 не прошли` — 43 checked, 43 passed, 0 failed. The fresh profile behaves exactly like `p8`: equal to itself, silent toward others. ## Typical mistake The mistake is asserting a certified result against a hand-written constant: Excerpt — `SqrtX` from `packs/examples/language-demo/math02/package.law` lines 167–170 (the rule is real; the mistake is the author's TRUE_ONLY expectation). ```law rule SqrtX strict { when sqrt_bounds(4, "p8/0.1") == bounds_const(2, 1); then sqrt_x(); } ``` The author reads "root of four is two" and expects `TRUE_ONLY`. The observed consequence (`bounds root: foreign derivation is unequal`) is `truth_status == NEITHER`. The constant on the right carries a different derivation from the certified call on the left, so the comparison fails — determinism (`BoundsSame`, `SqrtSelf`, `DivSelf`: identical expression against itself, all `TRUE_ONLY`) is the only equality the bounds surface offers. The fix: never compare a certified call with anything except the textually identical call. If the digits must appear, recompute them through the same call on both sides. ## Limits Every status above holds for the verified profile only: tool `law 0.1.0`, language `0.2`, semantics `law.core/0.2`, std `0.2.0`. Newer tool builds, other language versions and other std releases have their own support lists; re-run, do not assume. Refusals are profile facts. `E1401`/`E2105`/`E2403` describe this build's version support, std v1-list and effect descriptors. None states that `Number`, `Algebraic`, `RealExpr`, solvers or conversions are inexpressible in general — the audit records all three families as `normative_boundary`. Certificates pin identity, not truth. A `TRUE_ONLY` on `sqrt_self()` certifies that one derivation equals itself, not that the certificate's mathematics is right. One observed extra boundary: a `query` declaration used as a term is refused with `E2105` alone ([nb-14: Why this answer and what would change it](/tutorials/northbridge/nb-14-why-this-answer/)) — same code, one layer, no computability complaint. ## Exercise Without running the engine, predict, then check with the commands above: 1. `sqrt_self()` versus `sqrt_x()`: which truth status each, and what distinguishes the two comparisons? 2. `cert_cross()` (`p8` vs `p16`): which truth status, which evaluation status, and what does that pair mean? 3. `law engine check` over `sqrt02.law.txt`: which two codes, in which order, and which layer (version / support / semantics) does each name? 4. `law engine check` over `v04.law.txt`: which code, and does it claim anything about the `0.4` language in general? 5. `fx_convert` (`fx02.law.txt`) and `DoubleIt`-in-condition (`query02.law.txt`): which codes each, and what would have to change — and at which of the five version layers — before either could execute? Write down each prediction first; run the commands; explain any miss in one sentence. Check your work against the [full solution: self-equality, cross-profile silence and refusal layers](/tutorials/northbridge/solutions/nb-17-solutions/). ## Sources - Source: `packs/examples/language-demo/math02/package.law` (certified rules `SqrtX`, `SqrtSelf`, `CertAll`, `CertCross`, rounding `RoundHalf`, unit rules `ConvOk`, `QBackOk`, `ExpoOk`) - Tests: `packs/examples/language-demo/math02/tests/math02.lawtest` (42 tests: spans, bounds, aggregates, derivation-sensitive comparisons, determinism probes) - Refusal slices: `packs/examples/language-demo/boundaries/evidence/snippets/v04.law.txt` (`E1401`), `sqrt02.law.txt`, `solve02.law.txt`, `fx02.law.txt`, `pi02.law.txt`, `ln02.law.txt`, `temp02.law.txt`, `angle02.law.txt` (`E2105 + E2403`), `query02.law.txt` (`E2105` alone); frozen outputs `evidence/ev-*.txt` - Audit: `packs/examples/language-demo/matrix-audit-P11.json` (`types`: `Number`, `Algebraic`, `RealExpr` — `normative_boundary`) - Suite tour: `packs/examples/language-demo/README.md` (math02 §, boundaries § with the full frozen-refusal table) - Prerequisite: [nb-01: First permit: facts, a rule and a question](/tutorials/northbridge/nb-01-first-permit/), [nb-04: The permit fee](/tutorials/northbridge/nb-04-permit-fee/); next: [nb-15: Quantities and dimensions](/tutorials/northbridge/nb-15-quantities/) Three levels: 1. **Northbridge use** (this article): the clerk certifies only what the pinned build proves — self-identical certified bounds (`TRUE_ONLY`), honest silence on cross-profile and foreign-derivation comparisons (`NEITHER`), and loud, layer-named refusals for solvers, conversions and newer versions — verified by the 42/42 plus 21/21 suites and the quoted `E1401` / `E2105 + E2403` outputs above. 2. **Domain template:** whenever a regime gates numeric methods by version, separate the five layers (tool, language, exact semantics, implementation support, observed run); execute only under explicit certificate profiles; compare certified values solely with textually identical calls; freeze every refusal as a replayable slice with its codes; never read a profile refusal as a language-wide inability. 3. **Confirmed example elsewhere:** the same `E2105 + E2403` pair fires for seven unrelated names (`exact_sqrt`, `solve_linear`, `fx_convert`, `pi`, `ln2`, `temp_convert`, `angle_convert`) and `E1401` for the `0.4` header — the boundary machinery is uniform across families, per the boundaries README table and the frozen `ev-*.txt` exhibits, not a quirk of one square root. 4. **Confirmed external formalization (corpus):** VIN check digit via Euclidean remainder (US, 49 CFR) — package `us.cfr.vin`, `corpus/laws/us/vin/02-check-digit.law:282-287`: `then vin_check_remainder(vin, total mod 11)` — the remainder is taken of a whole total as `Integer mod Integer` (`0 <= r < |b|` for any signs), never of an inexact magnitude (`Decimal` has no remainder). A family boundary in live law: this computation belongs to the Integer family and is refused elsewhere. Evidence: `docs/research/constructs/21-expressions-quantities/corpus-forms.en.md` [§9](/tutorials/northbridge/nb-01-first-permit/#9-three-levels) (rated exemplary). Limit of verification: presence of the named construct at the cited lines only, confirmed by direct file read; no claim about deployment, runtime behaviour, or legal correctness.