docs← Back to article

Markdown for LLMs

nb-11 — Appeal: reading, judgment and precedent

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

Download this articlePlain text ↗
# nb-11 — Appeal: reading, judgment and precedent

*Northbridge is fictional. Every applicant, hearing officer, precedent
and court cited here is synthetic and unofficial. Northbridge is a
training story, not a real deployment, and nothing here is
legal-validity advice. Verified profile: `law 0.1.0`, `law.core/0.2`.*

## Situation

Ann owns a second car and asks the permit office for a second parking
permit. The clerk refuses: under the office's working reading, one
household gets one permit, so `second_permit(ann)` is false. Ann
appeals. At the hearing, three questions land on the table. First,
which reading governs — the clerk's narrow one, or Ann's broad one
under which an otherwise eligible resident gets the second permit?
Second, Ann pleads hardship — a question nobody at the desk can answer;
only the hearing officer's judgment settles it. Third, fellow appellant
Bob lives in the city but keeps a second home outside it, and an old
HIGH court decision on exactly that pattern is put on the table.

Eligibility rules ([nb-01: First permit: facts, a rule and a question](/tutorials/northbridge/nb-01-first-permit/)
through [nb-03: Exceptions and conflicting rules](/tutorials/northbridge/nb-03-exceptions/))
answered "does the rule fire?". Duties
([nb-09: From permit to duties and powers](/tutorials/northbridge/nb-09-duties-powers/))
answered "who owes what once it fires?". This article answers what
happens when the answer is contested.

The three hearing questions map to five constructs. Rival readings are
named as **interpretations** — one reading per container — and the
closed list of rivals plus how one is chosen is an **interpretation
group**. The hardship question marks the point where only a human
decision can move the case: a **judgment** is a fact that only an
authority's decision can supply. The old HIGH court decision is reused
without copying through its **factors** — the fact pattern it turned
on — and the **precedent** itself, which the engine carries to new
facts. Each construct is explained again where it first appears below.

## Prerequisites

You need [nb-01: First permit: facts, a rule and a question](/tutorials/northbridge/nb-01-first-permit/) for facts,
strict rules, `truth(...)` queries and `law test` as the way to check
a claim; [nb-02: Why a missing fact is not a refusal](/tutorials/northbridge/nb-02-missing-fact/)
for the four truth statuses, where `NEITHER` means "no conclusion
either way" and never a refusal; and
[nb-03: Exceptions and conflicting rules](/tutorials/northbridge/nb-03-exceptions/) for
strict rules and how conflicting conclusions resolve. Two issue flags
appear below: `INTERPRETATION_REQUIRED` (no reading chosen) and
`REQUIRES_JUDGMENT` (the authority has not spoken yet).

[nb-09: From permit to duties and powers](/tutorials/northbridge/nb-09-duties-powers/)
showed duties and powers. Appeals resolve what is contested about
relations like those: the hearing in this article is where an
exercised objection — the `MayObject` power from
[nb-09: From permit to duties and powers](/tutorials/northbridge/nb-09-duties-powers/) —
would be heard.

None of the five constructs decides a case by itself. The group
selects the reading, the judgment supplies the missing fact, the
precedent carries a past outcome to matching facts. Where any of the
three is absent, the engine stops with a named issue instead of
guessing. That stop is the limit of automatic decision, and it is the
point of the design.

## Minimal example

All fragments are excerpts from
`packs/examples/language-demo/appeals/package.law` (identifiers as
written; package header, imports, type alias and unrelated relation
declarations cut). Each fragment is marked `Excerpt`.

Excerpt 1 — the narrow reading (lines 21–28). An **interpretation** is
a named container for rules that belong to one reading of the law.

```law
interpretation Narrow {
    status reviewed;
    rule NarrowReading strict {
        for a: Applicant;
        when second_vehicle(a);
        then not second_permit(a);
    }
}
```

Look at what the container holds: an ordinary strict rule. Under
`Narrow`, a second vehicle alone defeats the second permit — the
clerk's reading, stated with no new rule machinery. The novelty is the
`interpretation` wrapper that names this reading as one rival among
several.

Excerpt 2 — the broad reading (lines 30–37). The rival container:
eligibility carries over to the second vehicle.

```law
interpretation Broad {
    status reviewed;
    rule BroadReading strict {
        for a: Applicant;
        when second_vehicle(a) and demo.northbridge.permits::permit_eligible(a);
        then second_permit(a);
    }
}
```

Look at the rule's second premise: it reaches into the permits
package for `permit_eligible`, the relation derived in
[nb-01: First permit: facts, a rule and a question](/tutorials/northbridge/nb-01-first-permit/). Under `Broad`, the same
second-vehicle fact plus eligibility yields the permit instead of
refusing it. Same fact, opposite conclusion — the reading decides.

Excerpt 3 — the group that chooses (lines 39–42). An
**interpretation group** lists the rival readings and declares how one
is picked; `exactly_one` means the context must select precisely one.

```law
interpretation_group SecondVehicle {
    alternatives Narrow, Broad;
    selection exactly_one;
}
```

Look at the two lines inside the group: the closed list of rivals,
and `selection exactly_one`. The case context carries `interpretation
Broad` (or `Narrow`); with no selection the question stays open by
construction, flagged `INTERPRETATION_REQUIRED` rather than defaulted.

Excerpt 4 — the judgment and the rule waiting on it (lines 44–52). A
**judgment** declares a relation whose instances only an authority's
decision can supply — here `hardship`, vested in the `HearingOfficer`.

```law
external judgment relation hardship(a: Applicant) {
    authority HearingOfficer;
}
rule HardshipWaiver strict {
    for a: Applicant;
    when applied_for_permit(a) and hardship(a);
    then hardship_waiver(a);
}
```

Look at the two parts together. The `external judgment` declaration
vests `hardship` in the `HearingOfficer`: only that authority's
decision can supply the fact. `HardshipWaiver` is otherwise an
ordinary strict rule — but its `hardship(a)` premise is one no desk
fact can satisfy. Only an adjudicated judgment makes the waiver true.

Excerpt 5 — the fact pattern a past case turned on (lines 54–66). A
**factors** block names the domain predicate both sides share
(`lives_in_city`) and which side each distinguishing fact favors:
`plaintiff` facts favor following, `defendant` facts favor
distinguishing.

```law
factors ResidencyLine {
    for a: Applicant;
    domain lives_in_city(a);
    plaintiff lives_in_city;
    defendant second_home;
    courts HIGH; LOW;
}
rule SecondHomeNotResident strict {
    for a: Applicant;
    when second_home(a);
    then not resident_for_permit(a);
}
```

Look at the side labels. `lives_in_city` is shared ground marked
`plaintiff`; `second_home` is the `defendant` fact that tells cases
apart. The pattern separates what Ann's and Bob's cases have in common
from the one fact that can distinguish them. The strict rule below the
block is the fallback: where the precedent is distinguished away, a
second home defeats residency.

Excerpt 6 — the past decision itself (lines 68–73). A **precedent** pins
one decided instance of a factors pattern — court, date, winning side
and outcome — so the engine can carry it to new facts.

```law
precedent P1 of ResidencyLine {
    court HIGH;
    decided @2020-06-01;
    plaintiff lives_in_city;
    outcome resident_for_permit(a);
}
```

Look at the four pinned details: court, date, winning-side factors,
and outcome. P1 says the HIGH court granted `resident_for_permit` on
the city-living facts. A new case with the same factors follows it;
one with an extra defendant-side factor is distinguished back to the
general rule.

## Command and result

Run the appeals suite:

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

Observed result (engine `law 0.1.0`) — all seven scenario lines plus
the summary:

```text
law test demo.northbridge.appeals: мир demo.northbridge.appeals, demo.northbridge.calculations, demo.northbridge.permits, demo.northbridge.vocabulary
  ok   [demo.northbridge.appeals] tests/appeals.lawtest / broad reading
  ok   [demo.northbridge.appeals] tests/appeals.lawtest / narrow reading
  ok   [demo.northbridge.appeals] tests/appeals.lawtest / no selection — open question
  ok   [demo.northbridge.appeals] tests/appeals.lawtest / judgment not yet rendered
  ok   [demo.northbridge.appeals] tests/appeals.lawtest / judgment rendered
  ok   [demo.northbridge.appeals] tests/appeals.lawtest / precedent followed
  ok   [demo.northbridge.appeals] tests/appeals.lawtest / precedent distinguished
итого: 7 проверено, 7 прошли, 0 не прошли, 0 не исполнены; код 0
```

All seven tests pass: each answer matched its expectation. That says
the machine selects, waits, follows and distinguishes as declared —
it is not a ruling that Ann deserves the second permit. The first line
names the world under test: the appeals package together with the
calculations, permits and vocabulary packages it is evaluated with.
The summary line is in Russian and says that 7 tests were checked, 7
passed, none failed and none were left unexecuted, with exit code 0.

The package also passes the static check:

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

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

`check OK` means the package file is well-formed; the suite run above
is what exercises its behavior.

What the decisive tests assert (all in
`packs/examples/language-demo/appeals/tests/appeals.lawtest`):

| Test | Setup | Expects |
|---|---|---|
| `broad reading` | `second_vehicle` + resident + registered car, `interpretation Broad` | `truth(second_permit(ann))` TRUE_ONLY |
| `narrow reading` | `second_vehicle` only, `interpretation Narrow` | `truth(second_permit(ann))` FALSE_ONLY |
| `no selection — open question` | `second_vehicle`, no interpretation chosen | `issue(INTERPRETATION_REQUIRED)` |
| `judgment not yet rendered` | applied for permit, no hardship on record | NEITHER + `REQUIRES_JUDGMENT` |
| `judgment rendered` | applied + `hardship` with `origin adjudicated` | `truth(hardship_waiver(ann))` TRUE_ONLY |
| `precedent followed` | `lives_in_city(ann)` only, `interpretation Broad` | `truth(resident_for_permit(ann))` TRUE_ONLY |
| `precedent distinguished` | `lives_in_city(bob)` + `second_home(bob)` | `truth(resident_for_permit(bob))` FALSE_ONLY |

Note what the `Broad` test carries that `Narrow` does not. The broad
rule needs `permit_eligible`, so its test asserts resident and vehicle
facts too. Selecting a reading selects what must be proven: Ann's
permit under `Broad` stands or falls with her eligibility, while under
`Narrow` the second vehicle alone decides.

## Why this construct

The task is to decide a contested case where the rule itself is
disputed, one premise needs an authority's word, and a past decision
constrains the outcome — and to show, for each open point, exactly
what is missing. Each of the five constructs answers a different
question the hearing officer actually asks. Interpretations: what are
the rival readings. The group: which one governs this case. Judgment:
who alone can supply this fact. Factors: what did the past case turn
on. Precedent: what was decided there. One construct per question, no
overloading.

A plain strict rule in the style of
[nb-01: First permit: facts, a rule and a question](/tutorials/northbridge/nb-01-first-permit/) states either reading but
cannot name the rivalry or demand a choice. A closure from
[nb-06: When the register may stay silent](/tutorials/northbridge/nb-06-register-silence/)
closes a register but cannot vest a fact in an authority. Copying P1's
outcome into a new rule freezes one answer where factors keep the
follow-or-distinguish logic alive.

The proof is the seven tests above: both readings executed, the open
question flagged, both judgment states observed, and the precedent
pair — followed TRUE_ONLY, distinguished FALSE_ONLY — run by `law
test`, not asserted in prose. What the tests do not prove is that
Broad *should* win, that Ann's hardship is real, or that P1 was
rightly decided. The tests prove the machine selects, waits, follows
and distinguishes as declared; no test can prove the hearing officer
chose wisely. All cases are fictional data, not legal advice.

## Changed condition

Take `precedent followed` and add one fact: `second_home(bob)`.

With city living only (Ann), `truth(resident_for_permit(ann))` is
TRUE_ONLY — her factors match P1's, so the holding carries over. With
the same city fact plus a second home (Bob), the same query is
FALSE_ONLY. The extra defendant-side factor distinguishes his case
back to the general `SecondHomeNotResident` rule. One condition, two
outcomes — and the difference is the distinguished factor, not a
different rule.

The same one-change logic governs readings. `broad reading` versus
`narrow reading` differ in the selected interpretation, plus the
eligibility facts that Broad's premises require. Assert neither
selection and the third test reports `INTERPRETATION_REQUIRED` instead
of any truth value. The engine never silently defaults to a reading.

## Typical mistake

A tempting assumption is that an unchosen reading defaults to
something — usually to the strict-looking one: "nobody selected Broad,
so Narrow applies and the permit is refused." It does not. The `no
selection — open question` test asserts `second_vehicle` with no
interpretation in context, and the engine answers with
`issue(INTERPRETATION_REQUIRED)`, not FALSE_ONLY. `exactly_one` means
the choice is mandatory input, not an optional hint with a silent
fallback.

The fix is to treat the selected interpretation as part of the case —
like a fact. If a query returns `INTERPRETATION_REQUIRED`, add the
missing selection to the context, not more rules. If it returns
`REQUIRES_JUDGMENT`, the case waits on the authority, not on more desk
evidence.

## Limits

Automatic decision stops at named issues. `INTERPRETATION_REQUIRED`
and `REQUIRES_JUDGMENT` are the engine refusing to guess: no reading
chosen, no judgment on record. They mark work for a human — the
hearing officer — not engine failures.

A judgment fact needs its authority. The rendered test carries
`hardship` with `origin adjudicated`; desk assertions cannot stand in
for the vested authority's word. A judgment relation is usable only
where its authority has spoken.

A precedent binds only inside its factors pattern. P1 moves
`resident_for_permit` for city-living facts and yields to the general
rule where a defendant factor appears. Outside the `ResidencyLine`
domain it says nothing at all.

Readings do not mix. Exactly one alternative governs per context;
Broad's eligibility premises never leak into a Narrow case. Rival
readings coexist in the package but never fire together.

Verified profile: engine `law 0.1.0`, semantics `law.core/0.2`. The
selection discipline (`exactly_one`), the judgment gate
(`REQUIRES_JUDGMENT`) and the follow-or-distinguish behavior are facts
about this profile's implementation, never claims about the language
in general.

## Exercise

Without running the engine, predict, then check with `law test`:

1. In `broad reading`, which asserted facts satisfy Broad's two
   premises, and which single premise would the clerk's file (second
   vehicle only) fail?
2. In `no selection — open question`, why does the engine report an
   issue rather than FALSE_ONLY — whose reading would FALSE_ONLY even
   be?
3. In `judgment not yet rendered`, why is the status NEITHER rather
   than FALSE_ONLY — what is the case waiting for, and who supplies it?
4. In `precedent distinguished`, name the shared domain fact and the
   distinguishing defendant fact, and say which rule decides Bob's case
   once P1 is distinguished away.

Write down each prediction first; run the suite; explain any miss in
one sentence. Checkable solution:
[full solution: predictions and checkable answers](/tutorials/northbridge/solutions/nb-11-solutions/).

## Sources

- Source: `packs/examples/language-demo/appeals/package.law` (readings
  `Narrow` / `Broad`, group `SecondVehicle`, judgment `hardship`, rule
  `HardshipWaiver`, factors `ResidencyLine`, rule
  `SecondHomeNotResident`, precedent `P1`)
- Tests: `packs/examples/language-demo/appeals/tests/appeals.lawtest`
  (the seven reading, judgment and precedent tests)
- Suite tour: `packs/examples/language-demo/appeals/README.md`
- Language reference: `docs/language/09-advanced-cheat-sheet.law.md`
  (interpretations and advanced constructs),
  `docs/language/06-testing-a-package.law.md`
  (the `law test` reference)
- Prerequisite:
  [nb-09: From permit to duties and powers](/tutorials/northbridge/nb-09-duties-powers/);
  next: [nb-12: Allocation in rounds](/tutorials/northbridge/nb-12-allocation-rounds/)

Three levels:

1. **Northbridge use** (this article): Ann's second-permit appeal under
   rival readings, her hardship waiver waiting on the hearing officer,
   and Bob's residency decided by precedent P1 — verified by the seven
   tests above.
2. **Domain template:** whenever a decision is contested, put each
   rival reading in its own interpretation, force the choice with an
   `exactly_one` group, vest authority-only facts in judgment
   relations, and carry past decisions as precedents over explicit
   factors — so every open point surfaces as a named issue instead of
   a silent default.
3. **Confirmed example elsewhere:** the permits package, where the
   eligibility rules that Broad builds on are exercised — verified by
   `law test packs/examples/language-demo/permits` (28 checked, 28
   passed, 0 failed). Confirmed external formalization: the Lefkowitz
   advertisement-as-offer holding (US case law) — package
   `us.caselaw.advertisement_offers`,
   `corpus/laws/us/caselaw-advertisement-offers/03-lefkowitz.law:31-41`,
   construct `precedent ... of Factors` with court/decided/outcome/
   ratio/prefer.

<details>
<summary>What the permits cross-check shows</summary>

The permits suite exercises resident with a car, fines refusal by
priority, and defeater without negation — including `denial wins by
priority` (FALSE_ONLY where the refusal outranks eligibility).
Readings select the rule; eligibility rules do the proving.

</details>

<details>
<summary>Sources and scope of verification</summary>

The Lefkowitz precedent was decided `@1957-12-20`, with its ratio a
subset of the plaintiff-side factors and a `prefer` over
`AdvertisementIsNotAnOffer`. A past decision carried over explicit
factors with a named defeated rule — the same shape as precedent P1
deciding Bob's case. Evidence:
`docs/research/constructs/25-argue-precedent/corpus-forms.en.md`,
section 2 (rated exemplary there). The recorded verification confirms
the construct's presence at the cited lines only, by direct source
read; it makes no claim about deployment, runtime behavior, or legal
correctness.

</details>