docs← Back to article

Markdown for LLMs

Exercise 9. An anchor the compiler checks

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

Download this articlePlain text ↗
# Exercise 9. An anchor the compiler checks

An exercise for the [real article: source and
anchor](/tutorials/real-article-source/) page. The page's norm is anchored
by the string key `@source("KZ-SOCIAL-CODE-224-VII-ART-175")`. In CLIR
such an anchor stays an opaque string: tools walking from the norm to the
text arrive nowhere by it.

**Setup.** Replace the string anchor with a fragment-identifier reference
and confirm over CLIR that the anchor became an address. Then write
a positive test: a foreigner with established disability permanently
residing in the country is entitled to benefit. The test must pass, and
the first attempt at writing it against the page's norm will fail. Find
out why.

**Hint.** A case fact writes an interval just like a norm:
`[@2024-01-01, infinity)`. Look at how the norm reads the interval: it
holds a variable. Check on a small package whether the engine matches
such an interval against a fact.

Below is the solution. Try it yourself first.

## Solution

The page's ontology unchanged, except one thing: the jurisdiction
constant in the case fact is fed by name, `KZ`, not by an `entity_ref`
with the same address — a constant reference and an entity reference are
not equal in a case.

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

entity Actor;
entity Person : Actor;
entity DisabilityStatus;
entity Benefit;

const KZ: Jurisdiction = entity_ref("urn:law:jurisdiction:KZ");
const DisabilityBenefit: Benefit = entity_ref("urn:law:tutorial:kz:social#DisabilityBenefit") {
    label ru-KZ official "Пособие по инвалидности";
};

relation disability_established(person: Person, status: DisabilityStatus, from: Date) kind institutional;
relation permanently_resident(person: Person, jurisdiction: Jurisdiction, during: Interval<Date>) kind institutional;
relation citizen_of(person: Person, jurisdiction: Jurisdiction) kind institutional;
relation foreign_citizen(person: Person) kind institutional;
relation stateless_person(person: Person) kind institutional;
relation entitled(person: Person, benefit: Benefit) kind institutional;

source SOCIAL_CODE {
    kind code;
    jurisdiction KZ;
    number "224-VII";
}

edition SOCIAL_CODE_2023_RU of SOCIAL_CODE {
    language ru-KZ;
    officiality official;
    adopted @2023-04-20;
    in_force [@2023-07-01, infinity);
    materialization_status ABSTRACT_ONLY;
}

fragment SOCIAL_CODE_ART175_TEACHING in SOCIAL_CODE_2023_RU {
    kind article;
    locator "article/175";
    text ru-KZ unofficial """Учебная выдержка, не официальный текст: право на пособие по
инвалидности имеют лица с установленной инвалидностью, постоянно проживающие
на территории Республики Казахстан.
""";
    content_hash "sha256:46a9b7144940cc1e85c2878749f2903a7ba915b8f72bf9ec29a72808dbc7483d";
}
```

The label reads: "Disability benefit". The quoted fragment reads: "Teaching excerpt, not the official text: persons with established disability permanently residing on the territory of the Republic of Kazakhstan are entitled to disability benefit."

An anchor by fragment identifier. The residence condition is changed,
and that is the task's second half:

```law
@source(SOCIAL_CODE_ART175_TEACHING)
rule DisabilityBenefitEntitlement strict {
    label ru-KZ official "Статья 175. Право на пособие по инвалидности";
    for person: Person;
    for status: DisabilityStatus;
    for from: Date;
    for during: Interval<Date>;
    when disability_established(person, status, from)
        and permanently_resident(person, KZ, during)
        and (citizen_of(person, KZ) or foreign_citizen(person) or stateless_person(person));
    then entitled(person, DisabilityBenefit);
}
```

The rule label reads: "Article 175. The right to disability benefit".

Why changed. On the page, residence is recorded as
`permanently_resident(person, KZ, [from, infinity))`: an interval with
a variable inside. As of 05.09.2026, neither engine matches such a term
against a case fact: feed a fact with the interval `[@2024-01-01,
infinity)` and a 1 January 2024 establishment date and the norm stays
silent. A small three-rule package shows the boundary exactly: an
interval literal in a body matches, a variable over the whole interval
matches, an interval with a variable inside does not, and the comparison
`during == [from, infinity)` is silent too. There is no function reading
an interval's start on the closed surface. So the page's norm never fires
on a case, while the compiler says nothing about it: silently dead law,
and the find is recorded in STATUS.

What is lost. The "resides since the disability establishment date" link
is not expressed in this form: the norm only requires a permanent
residence fact with some interval. If the article truly establishes that
link, it awaits another form: a separate "resides since date" fact of
type `Date` and a date comparison.

A positive test:

```law
test "иностранец с установленной инвалидностью, проживающий постоянно, — право есть" {
    given {
        context {
            decision_time @2026-06-01T09:00:00+05:00;
            knowledge_time @2026-06-01T09:00:00+05:00;
            legal_time @2026-06-01;
            timezone "Asia/Almaty";
        }
        assert disability_established(entity_ref("urn:tutorial:ivanova"), entity_ref("urn:tutorial:group2"), @2024-01-01) {
            id "assert-disability";
            origin case_input;
        }
        assert permanently_resident(entity_ref("urn:tutorial:ivanova"), KZ, [@2024-01-01, infinity)) {
            id "assert-resident";
            origin case_input;
        }
        assert foreign_citizen(entity_ref("urn:tutorial:ivanova")) {
            id "assert-foreign";
            origin case_input;
        }
    }
    evaluate truth(entitled(entity_ref("urn:tutorial:ivanova"), DisabilityBenefit));
    expect truth_status == TRUE_ONLY;
    expect evaluation_status == COMPUTED;
}
```

The test name reads: "A permanently residing foreigner with established disability is entitled."

The second test is the same person without the residence fact: `NEITHER`
in the same file.

What changed in CLIR. The same norm, two anchors:

```text
$ lawc lower 09-real-article-source.law.md   # строковый ключ
…#DisabilityBenefitEntitlement/alt/1 | anchors: ['KZ-SOCIAL-CODE-224-VII-ART-175']
$ lawc lower 39-exercise-real-article-source.law.md   # идентификатор
…#DisabilityBenefitEntitlement/alt/1 | anchors: ['urn:law:tutorial:kz:social#SOCIAL_CODE_ART175_TEACHING']
```

The comments read "string key" and "identifier".

The second anchor is a node address in the same package: by it the
depth measure finds the article, and the sources tool the text. The first
is just a string. One more boundary: the compiler silently accepts
a nonexistent identifier in `@source`, and CLIR receives an address with
nothing at it.

This also shows what the norm lowered into: three alternatives `/alt/1`,
`/alt/2`, `/alt/3`, one per `or` branch in the condition. This matters
for tests: the `applied(DisabilityBenefitEntitlement)` observation will
not find such a rule in the program, because three rules stand under
this name in CLIR. So this task's tests are limited to the answer status.

## Check

Both tests are in the ready-made test file, executed
on the page's program. The first of them fails on the page's norm,
and that is how the find was obtained. What is **not** checked:
the identifier's existence in the anchor. The compiler accepts
`@source(НЕТ_ТАКОГО)` ("NO_SUCH_THING") without diagnostics, and CLIR
receives an address with nothing at it. This is verified when pinning
corpus sources, not by the package check, and is named here as
a boundary, not hidden.

What the exercise teaches. The teaching excerpt is marked `unofficial`,
and the edition `ABSTRACT_ONLY`: the package holds no bytes of the real
article, and the hash reconciles against the excerpt, not the statute.
An identifier anchor does not make the excerpt law, but it makes the
norm-to-text link addressable. And the main point: a green `check` does
not mean the norm can fire. A positive test for every norm is not
a formality but the only way to learn it is alive.