docs← Back to article

Markdown for LLMs

What we talk about: object, name, and record

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

Download this articlePlain text ↗
# What we talk about: object, name, and record

Aigerim opens the registry Dana sent. In it, two researchers are recorded
identically: "Ivanova A. S.". The first one has two accreditation records:
an old one, whose validity has expired, and a new one. About the second
there is no such evidence. How can the accreditation of each be answered
without attributing one person's evidence to the other?

Let us start by telling apart the person, her name, and the record about
her. Two people may share a name, and one person may correspond to several
records. These distinctions must be kept in the model. For that we will
compose a vocabulary: declare the object types and the relations with
which we will describe people and their accreditation.

The Archive of Veliky Ustin, its registry, and the people in the story
are fictional. The whole example is given on this page. If you want to
run it right away, go to the ["Run"](#run-get-the-answer) section.

## Question: how does a person differ from a record about her?

In this lesson we will treat accreditation as established when a valid
accreditation record corresponds to the person. We will check two cases:
first two Ivanovas, then two records of one of them. In both cases we
need to see which object each piece of evidence concerns.

First, let us write down the expected answers in plain words. We add an
error check to them: what happens if a number is given instead of a name?

| Evidence | Expected answer |
|---|---|
| Two Ivanovas; only the first has a valid record | The second one's accreditation is neither established nor refuted |
| One Ivanova; the old record has expired, the new one is valid | Accreditation established |
| A number given instead of a name | Type error when checking the package |

Matching names give no grounds to treat the second Ivanova as accredited.
The old record does not cancel the new one. And a number instead of
a name breaks the record form we are about to set in the vocabulary.

## Record: vocabulary

The first chapter needed the `Person` type, denoting people. Now we add
a `Record` type for accreditation records. A record also becomes an
object in its own right: it can be given a status and linked to
a particular person.

For statuses we introduce a `RecordStatus` enumeration with two values:
`Active` — the record is valid, `Expired` — its validity has lapsed.
A parameter of this type takes one of the listed values. The enumeration
itself, however, does not forbid several statements about one record's
status. Such a restriction would need a separate entry in the model.

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

entity Person;
entity Record;

enum RecordStatus { Active; Expired }

relation person_name(p: Person, name: Text) kind empirical;
relation accreditation_record(r: Record, p: Person) kind institutional;
relation record_status(r: Record, s: RecordStatus) kind institutional;
relation accredited(p: Person) kind institutional;
```

After each relation name, its parameters are listed in parentheses:
what we pass, in which order, and what type each value must be. This
description is called a signature.

The `person_name` relation links a person to her name. The first
parameter has type `Person`, the second `Text`, the built-in type for
text. There is an `Integer` type for whole numbers and a `Date` type
for dates. The `accreditation_record` relation links a record to
a person, and `record_status` a record to its status. The order matters:
in `accreditation_record` the record comes first, then the person.

The `empirical` marker on `person_name` means we describe observed
evidence: which name stands in the registry. The `institutional` marker
on the other relations points to their link with institutional decisions
and statuses. These markers characterise the meaning of relations; the
concrete source of each piece of evidence must be stated separately.

Now let us write the rule. The variable `p` denotes the person, `r` the
record. They are declared by the `for p: Person` and `for r: Record`
lines. After `when` stand two conditions: the record belongs to this
person and has the `Active` status. When both hold, the rule allows
her accreditation to be established.

```law
rule AccreditedByActiveRecord strict {
    for p: Person;
    for r: Record;
    when accreditation_record(r, p) and record_status(r, Active);
    then accredited(p);
}
```

Note: there is no `person_name` relation in the rule. Deriving the
conclusion needs the record-to-person link and the record's status. That
is why matching names alone do not carry accreditation from one Ivanova
to the other.

## Run: get the answer

You will need a copy of the repository and Rust with Cargo installed.
Open a terminal at the repository root and run the commands below.
Run all further commands in the same window: it will keep the paths you
set. As in the first chapter, `check` will verify the package, and
`lower` will prepare the program and save it into a temporary directory.

```bash
cd engines/lawc
tutorial_dir=../../docs/tutorials
tutorial_page="$tutorial_dir/01-vocabulary.en.law.md"
cargo run -q --profile gate -p law-cli -- check \
    "$tutorial_page"
tutorial_work=$(mktemp -d)
cargo run -q --profile gate -p law-cli -- lower \
    "$tutorial_page" > "$tutorial_work/program.lawir.json"
```

The first test describes two Ivanovas. Each person is matched with her
own `entity_ref`: `urn:tutorial:ivanova` and `urn:tutorial:ivanova-2`.
Their names are identical, their references differ. By these references
the model tells people apart.

The accreditation record has a reference of its own:
`urn:tutorial:rec-2026`. We link it only to the first Ivanova and state
the `Active` status. In the `evaluate` line we ask about the accreditation
of the second. The expected `NEITHER` answer means the accreditation is
neither established nor refuted.

```law
test "две Ивановы: имя не склеивает людей" {
    given {
        context {
            decision_time @2026-03-01T09:00:00+05:00;
            knowledge_time @2026-03-01T09:00:00+05:00;
            legal_time @2026-03-01;
            timezone "Asia/Almaty";
        }
        assert person_name(
            entity_ref("urn:tutorial:ivanova"),
            "Иванова А. С.") {
            id "name-1";
            origin case_input;
        }
        assert person_name(
            entity_ref("urn:tutorial:ivanova-2"),
            "Иванова А. С.") {
            id "name-2";
            origin case_input;
        }
        assert accreditation_record(
            entity_ref("urn:tutorial:rec-2026"),
            entity_ref("urn:tutorial:ivanova")) {
            id "rec-new";
            origin case_input;
        }
        assert record_status(entity_ref("urn:tutorial:rec-2026"), Active) {
            id "status-new";
            origin case_input;
        }
    }
    evaluate truth(accredited(entity_ref("urn:tutorial:ivanova-2")));
    expect truth_status == NEITHER;
}
```

The test name reads: "Two Ivanovas: a shared name does not merge people."

```bash
cargo run -q --profile gate -p law-cli -- test \
    "$tutorial_dir/tests/01-vocabulary-two-ivanovas.lawtest" \
    --lawtest --program "$tutorial_work/program.lawir.json"
```

A `test PASS` message confirms the expected `NEITHER` answer. The input
evidence holds no accreditation record for the second Ivanova. The first
one's name matches hers, but that is not enough to apply the rule. At the
same time, the missing record does not yet mean the second Ivanova's
accreditation is refuted.

In the second test we consider one Ivanova and her two records. The old
one, `rec-2024`, has expired; the new one, `rec-2026`, is valid. Each
record has its own reference and status, so the rule conditions can be
checked for each of them separately.

```law
test "две записи: считается действующая" {
    given {
        context {
            decision_time @2026-03-01T09:00:00+05:00;
            knowledge_time @2026-03-01T09:00:00+05:00;
            legal_time @2026-03-01;
            timezone "Asia/Almaty";
        }
        assert accreditation_record(
            entity_ref("urn:tutorial:rec-2024"),
            entity_ref("urn:tutorial:ivanova")) {
            id "rec-old";
            origin case_input;
        }
        assert record_status(entity_ref("urn:tutorial:rec-2024"), Expired) {
            id "status-old";
            origin case_input;
        }
        assert accreditation_record(
            entity_ref("urn:tutorial:rec-2026"),
            entity_ref("urn:tutorial:ivanova")) {
            id "rec-new";
            origin case_input;
        }
        assert record_status(entity_ref("urn:tutorial:rec-2026"), Active) {
            id "status-new";
            origin case_input;
        }
    }
    evaluate truth(accredited(entity_ref("urn:tutorial:ivanova")));
    expect truth_status == TRUE_ONLY;
}
```

The test name reads: "Two records: the valid one counts."

```bash
cargo run -q --profile gate -p law-cli -- test \
    "$tutorial_dir/tests/01-vocabulary-two-records.lawtest" \
    --lawtest --program "$tutorial_work/program.lawir.json"
cargo run -q --profile gate -p law-cli -- explain \
    "$tutorial_dir/tests/01-vocabulary-two-records.lawtest" \
    --program "$tutorial_work/program.lawir.json"
```

The test should finish with a `test PASS` message: accreditation is
established, the answer is `TRUE_ONLY`. In the explanation, find the
`AccreditedByActiveRecord` rule and the evidence about the `rec-2026`
record: whom it belongs to and in which status it is. Together they give
grounds for the conclusion.

The old `rec-2024` record does not satisfy the `Active` condition.
Moreover, our rule has no conclusion of missing accreditation from the
`Expired` status. So the old record does not refute the result obtained
via the new one. We kept both records and can explain which of them
supports the answer.

## Practice: mix up and catch

Make a copy of the ready-made two-Ivanovas test file
in a temporary directory. In the `test` command, give the path to
this copy instead of the original file. The prepared program stays
the same.

First change only the question: in the `evaluate` line replace
`ivanova-2` with `ivanova`. Now we ask about the first Ivanova, who has
a valid record. Before running the test, explain why you expect
`TRUE_ONLY`, and put that status in the `expect` line. The test should
pass. You may also change the name in one of the `person_name` lines:
it will not affect the answer, since the name is not among the rule
conditions.

For the next experiment, again take a copy of the original test with
the question about the second Ivanova and the `NEITHER` expectation. In
the statement named `rec-new`, replace the `urn:tutorial:ivanova`
reference with `urn:tutorial:ivanova-2`. Now the record belongs to the
second Ivanova. Keep the previous expectation and run the test: it should
finish with `FAIL`, because the obtained answer became `TRUE_ONLY`.
The record-to-person link changed, and the conclusion followed it.

Finally, check how a wrong value type is detected. Make a copy of this
page and in the first test replace the `"Иванова А. С."` text ("Ivanova
A. S.") with the number `42` without quotes. Run the `check` command for
the copy. It should report an `LDC-E2104` error: an `Integer` number was
passed, while the `name` parameter requires `Text`. The compiler does not
turn a number into a name automatically; such a record must be fixed
before running.

## Acceptance: what you can check yourself

Check that you can repeat both cases and explain the results:

- Matching names do not merge two Ivanovas. In the first case the answer
  about the second is `NEITHER`, about the first `TRUE_ONLY`.
- One Ivanova may have two separate records. The valid one lets
  accreditation be established: the answer is `TRUE_ONLY`.
- If the valid record is linked to another person, the answer changes.
  Changing the name text does not affect the conclusion.
- A number instead of a name gives an `LDC-E2104` error when checking
  the package.

## Boundary: what types do not check

Package checking detects a number where text is required and compares
the argument count against the relation declaration. But a typo in
a name is still text. A wrong record-to-person link may also be written
in a type-correct way. The model will reason over the evidence we passed
to it.

Handle `entity_ref` references with particular care. The type of such
a reference is set by the relation parameter in which it is used. The
words `ivanova` and `rec-2026` inside the reference help us read the
example, but by themselves they do not declare a person or a record.
If in the `accreditation_record` statement these references are swapped,
the compiler may accept the record: the first argument is used as
a `Record`, the second as a `Person`. But then the needed link to
the `Active` record is lost. So when checking input evidence, it matters
to verify both the argument order and what each reference denotes. This
is examined in more detail
on [the vocabulary reference](/constructs/vocabulary/).

We did not compute validity periods here either. The `Active` and
`Expired` statuses were given in the input evidence. Checking a status
against dates would need the dates themselves and a matching rule.

Now Aigerim can tell apart two people with the same name and explain
which of several records supports the conclusion. **To understand the
answer, you need to know which object each piece of input evidence
speaks about.**

In these examples we again obtained `TRUE_ONLY` and `NEITHER`. What the
two remaining states mean, and how to work with contradicting statements,
is covered in the lesson
["Four states of support"](/tutorials/four-states/).