# lawref.evaluator.facts

*module*

Факты дела в support: приём, ключи, замыкание домена.

`ingest` — assertions → accepted support ([§71](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#71-assertion)–[§73](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#73-origin), [§78.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#781-epistemic-acceptance-phase));
`check_keys` — relation keys [§45.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#451-relation-keys) / data quality [§196](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/27-part-xxvi-static-semantics-and-diagnostics.ru.md#196-data-quality-checks);
`materialize_closures` — closed-world declarations [§70](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/10-part-x-propositions-and-four-valued-support.ru.md#70-closed-world-declarations).

## lawref.evaluator.facts.ACCEPTED_ORIGINS

*attribute* · *module attribute*

```python
ACCEPTED_ORIGINS = {'case_input', 'source_asserted', 'external_snapshot', 'adjudicated', 'derived'}
```

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/evaluator/facts.py#L17-L17)

## lawref.evaluator.facts.assertion_table

*function*

```python
def assertion_table(nodes: list[dict]) -> tuple[list[dict], list[dict]]
```

[§78.2.3.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#78231-идентичность-входов-один-stableid--одно-содержание) для `assertion` (errata E-0158): один StableId — одно содержание.

Две записи одного `id` с побайтово равным каноническим содержанием [§208](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/28-part-xxvii-canonical-legal-ir.ru.md#208-canonical-serialization) —
одна запись (дубль сворачивается, первая остаётся на месте); с различным —
fatal issue `ASSERTION_ID_COLLISION`, и результат не вычисляется. До
errata обе реализации складывали утверждения в таблицу по `id`, и второе
молча заменяло первое: перестановка входного массива меняла правовой
вывод без единой issue. Форма и место — зеркало `EVIDENCE_ID_COLLISION`
(`evidence._table`); правило одинаково для узла программы и записи дела.

Проверка стоит в ingest [§71](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#71-assertion) ПЕРВОЙ — до приёмки [§78.2](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#782-исполнимый-профиль-evidencepolicy-lawcoreevidence-policy01), до разыменования
констант [§54](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/09-part-ix-values-terms-expressions.ru.md#54-const) и до фильтров origin [§73](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#73-origin) и valid [§72](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#72-temporal-dimensions-assertion): иначе две реализации,
поставившие её в разные места, разошлись бы байтами на деле, где первое
утверждение отвергается политикой или окном. `caseHash` при этом НЕ
пересчитывается — дедуплицируется рабочее представление ingest, а не
предъявленное дело (гейт Astra).

Возвращает `(узлы без дублей, коллизии)`; узлы иных видов проходят как есть.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/evaluator/facts.py#L207-L252)

## lawref.evaluator.facts.check_argument_types

*function*

```python
def check_argument_types(node: dict, signatures: dict, constants: dict, bases: dict, issues: list[dict], aliases: dict | None = None) -> bool
```

Арность и типы аргументов утверждения против сигнатуры [§189](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/27-part-xxvi-static-semantics-and-diagnostics.ru.md#189-type-checks) (errata E-0046).

Тот же контракт, что у компилятора: `assert born_on(42, 7)` в акте
отвергается `LDC-E2104` («[§48](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#48-отсутствие-implicit-conversions): неявных конверсий нет»), а через
транспортный case-документ то же утверждение принималось молча и
порождало правовой вывод о числе 42. Проверка не выбирает и не чинит —
несогласованное утверждение не принимается, и причина названа.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/evaluator/facts.py#L105-L152)

## lawref.evaluator.facts.check_derived_types

*function*

```python
def check_derived_types(literal: dict, rule_id: str, store: SupportStore, issues: list[dict]) -> bool
```

Типы аргументов ВЫВЕДЕННОГО атома против сигнатуры [§189](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/27-part-xxvi-static-semantics-and-diagnostics.ru.md#189-type-checks) (errata E-0077).

Та же дисциплина, что у дела (E-0046), — до правки два входных пути одного
support жили под разной: утверждение дела с Rational в Decimal-слоте
отвергалось громко, а голова правила клала ту же пару МОЛЧА (замер
kz.tarify Н5: Money-слот принял Quantity[tenge_per_kWh] со статусом
TRUE_ONLY; phys.zadachnik: Decimal-слот принял Rational 1/3). Отказ
зеркален делу: несогласованный вывод НЕ принимается, причина названа.
Арность здесь не сверяется — у выведенной головы её держит компилятор
([§189](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/27-part-xxvi-static-semantics-and-diagnostics.ru.md#189-type-checks), путь исходника), транспортного канала у вывода нет.
`entity_ref` и неизвестные `const_ref` пропускаются, как у дела:
проверка утверждает только то, что может доказать.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/evaluator/facts.py#L155-L197)

## lawref.evaluator.facts.check_judgment_answer

*function*

```python
def check_judgment_answer(node: dict, nodes: list[dict], issues: list[dict]) -> bool
```

[§47.3.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#4731-ответ-судьи-errata-e-0089) (errata E-0089): ответ судьи с объектом `judgment` принимается,
только если орган совпадает с декларацией отношения, а `payload` — с
`request_schema` (тот же тест вида, что у аргумента утверждения дела,
E-0046). Ответ без `judgment` — утверждение самого отношения, как до
errata (v1-совместимость). Непринятый ответ не даёт опоры: результат
остаётся REQUIRES_JUDGMENT с тем же JudgmentRequest.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/evaluator/facts.py#L361-L417)

## lawref.evaluator.facts.check_keys

*function*

```python
def check_keys(store: SupportStore, nodes: list[dict], issues: list[dict]) -> None
```

Relation keys [§45.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#451-relation-keys) / data quality [§196](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/27-part-xxvi-static-semantics-and-diagnostics.ru.md#196-data-quality-checks): два различных accepted-кортежа с
одинаковой key-проекцией — `KEY_CONFLICT`; evaluator ничего не выбирает и не
удаляет, конфликт попадает в отчёт.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/evaluator/facts.py#L420-L450)

## lawref.evaluator.facts.extracted_without_evidence

*function*

```python
def extracted_without_evidence(node: dict) -> bool
```

DECISION-0360: извлечённый факт (`was_generated_by` с `extractor`)
при `origin case_input` и пустом `evidence`. Принимается в поддержку;
вызывающий получает warning, а не новый origin и не отказ.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/evaluator/facts.py#L255-L269)

## lawref.evaluator.facts.ingest

*function*

```python
def ingest(store: SupportStore, nodes: list[dict], mode: str, legal_time: str, issues: list[dict], signature_index: tuple[dict, dict, dict, dict] | None = None, acceptance=None) -> dict[str, dict]
```

Assertions → accepted support ([§71](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#71-assertion)–[§73](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#73-origin); [§78.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#781-epistemic-acceptance-phase) — тривиальная acceptance L0).
`assumed_for_simulation` принимается только в mode=simulation ([§73](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#73-origin)); assertion
с valid-интервалом вне legal_time не участвует в support ([§72](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#72-temporal-dimensions-assertion), ось valid).

При выбранной EvidencePolicy (`acceptance`) утверждение ЗАЩИЩЁННОГО
предиката [§78.2.4](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#7824-полнота-границы-доверия) не принимается ни при каком origin и ни из какого
носителя: ни из дела, ни из программы, ни из импорта, ни из snapshot-а.
Проверка стоит ПЕРВОЙ — раньше origin, valid и типов: обход границы
доверия не должен зависеть от того, прошло ли утверждение остальные
сита. Причина уходит в типизированный отчёт и одним info-issue в
документ: молчание здесь и есть тот отказ, ради которого политика
заводится.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/evaluator/facts.py#L272-L358)

## lawref.evaluator.facts.materialize_closures

*function*

```python
def materialize_closures(store: SupportStore, nodes: list[dict], registry: 'ProofRegistry', issues: list[dict], legal_time: str | None = None, prepared: tuple | None = None) -> None
```

Closed-world declarations [§70](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/10-part-x-propositions-and-four-valued-support.ru.md#70-closed-world-declarations): при deriveExplicitNegative отсутствие
positive-записи для члена pinned-домена порождает explicit negative с
closure-сертификатом в proof ([§231.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/31-part-xxx-scalability.ru.md#2311-lazy-closure-и-comprehensions)). Errata E-0087: закрытый предикат —
любой арности, домен — предикат ТОЙ ЖЕ арности (его установленные
кортежи и есть закрытая область), `effective` [§87](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/12-part-xii-events-actions-temporal-model.ru.md#87-rule-temporal-qualifiers) уважается — вне
`legal_time` замыкание не порождает ни одного негатива. Домен — const_ref
на предикат; eager-материализация (lazy anti-join [§231.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/31-part-xxx-scalability.ru.md#2311-lazy-closure-и-comprehensions) — оптимизация
вехи M4+).

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/evaluator/facts.py#L475-L545)

## lawref.evaluator.facts.prepare_closures

*function*

```python
def prepare_closures(nodes: list[dict]) -> tuple[dict[str, int], list[dict], list[list[dict]] | None]
```

Установка замыканий [§70](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/10-part-x-propositions-and-four-valued-support.ru.md#70-closed-world-declarations) — чистая функция от узлов: арности деклараций,
политики в порядке id и их УРОВНИ (errata E-0113) — группы политик по
возрастанию уровня, внутри группы порядок id. Вид программы
(`evaluator.prepared`) считает её один раз; на kz-income-tax (9790
узлов) это было 0,5 с на КАЖДЫЙ вызов. `None` в третьей позиции — цикл
между замыканиями (страховка: его называет `stratification_problem`).

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/evaluator/facts.py#L453-L472)

## lawref.evaluator.facts.signature_index

*function*

```python
def signature_index(nodes: list[dict]) -> tuple[dict, dict, dict, dict]
```

Индекс сигнатур по узлам программы — то, что вид программы
(`evaluator.prepared`) считает один раз; утверждения дела в него не
входят по построению (`_signature_index` читает только декларации).

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/evaluator/facts.py#L200-L204)
