# lawref.evaluator.prepared

*module*

Подготовленная программа: анализы, зависящие только от IR, — один раз на объект.

Профиль 02.09.2026 (kz-income-tax, 18,8 МБ, 9790 узлов, ПУСТОЕ дело,
тёплый вызов): 2,94 с процессорного времени, из них ~90 % — величины,
которые не зависят ни от дела, ни от запроса и пересчитывались на КАЖДОМ
вызове `evaluate`: разыменование констант обходом всего IR (0,87 с),
классификация и страты строгих правил (0,7 с), анализ агрегатов (0,46 с),
установка замыканий [§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) (0,54 с), хвосты proof-id (0,39 с), проверка хэшей
фрагментов [§194](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/27-part-xxvi-static-semantics-and-diagnostics.ru.md#194-sourceprovenance-checks) (0,5 с), таблицы символов календарного окружения (0,14 с).
Тот же класс, что закрыл DECISION-0054 для programHash: считалось не право,
а константа программы. Здесь константы программы получают один носитель.

Два уровня, по тому, от чего величина зависит:

- **от IR целиком** — хвосты proof-id (E-0097), issue [§194](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/27-part-xxvi-static-semantics-and-diagnostics.ru.md#194-sourceprovenance-checks) — живут в записи
  `program_derived` (DECISION-0054) и делят с хэшем пин объекта, вытеснение и
  детектор мутации;
- **от «вида» программы** — IR, срезанного осями вызова, которые его
  фильтруют: дата права ([§92.3](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/13-part-xiii-context-scope-applicability.ru.md#92-edition-applicability), E-0095), выбор прочтений ([§152](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/21-part-xx-interpretations-and-theories.ru.md#152-interpretation)–[§156](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/21-part-xx-interpretations-and-theories.ru.md#156-evaluation-of-alternatives)) и
  константы дела ([§168](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/24-part-xxiii-cases-snapshots-evaluation.ru.md#168-case-file)/[§186](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/26-part-xxv-proof-graph-and-explanations.ru.md#186-counterfactual); они же — таблица разыменования [§54](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/09-part-ix-values-terms-expressions.ru.md#54-const)). Вид —
  `ProgramView`: узлы после трёх проходов и ленивые производные от них
  (строгие правила с рангами [§110](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/16-part-xv-defeasible-semantics-and-conflict-resolution.ru.md#110-stratification), поражаемые правила с рангами и
  замыканием приоритетов [§106](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/16-part-xv-defeasible-semantics-and-conflict-resolution.ru.md#106-priority-relation), установка замыканий [§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), таблицы TermEnv,
  индекс сигнатур). Виды живут в той же записи, что хэш, под ключом осей.

Байты выхода не меняются ни при каком порядке вызовов — по построению:
кэшируются ЧИСТЫЕ функции от (IR, оси), а issues, которые эти функции
порождали по ходу, записаны и воспроизводятся в том же порядке и тем же
числом раз, что и до кэша (см. `strict_closure`, `defeasible_closure`).
Проверка — differential: `lawc` этот кэш не знает, и байты обязаны сойтись.

**Мутация.** Раньше каждый вызов работал на свежих копиях (при непустой
таблице констант) либо на самом IR (при пустой); теперь узлы вида делятся
между вызовами всегда, как делился IR. Мутировать входные узлы движку
запрещено и до кэша (прецедент `_conjuncts`, DECISION-0054); детектор — та
же плановая сверка: на вызовах вида со счётчиком-степенью двойки вид
собирается заново и сравнивается структурно, расхождение — громкий
RuntimeError. Канарейка — `engines/lawref/selftest.py`.

**Память.** Разыменование [§54](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/09-part-ix-values-terms-expressions.ru.md#54-const) стало copy-on-write (`constants._walk`
возвращает ТОТ ЖЕ объект, если под ним ничего не изменилось), поэтому вид
держит в основном ссылки на узлы IR, а не копию программы; видов на
программу — не больше `_VIEW_CAP`, вытеснение LRU.

## lawref.evaluator.prepared.ProgramView

*class*

```python
class ProgramView
```

Узлы программы после фильтров осей вызова и ленивые производные от них.

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

### lawref.evaluator.prepared.ProgramView.calls

*attribute* · *instance attribute*

```python
calls = 0
```

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

### lawref.evaluator.prepared.ProgramView.dependent_predicates

*attribute* · *instance attribute*

```python
dependent_predicates = dependent_predicates or set()
```

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

### lawref.evaluator.prepared.ProgramView.excluded_rules

*attribute* · *instance attribute*

```python
excluded_rules = excluded_rules
```

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

### lawref.evaluator.prepared.ProgramView.exclusion_reasons

*attribute* · *instance attribute*

```python
exclusion_reasons = exclusion_reasons
```

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

### lawref.evaluator.prepared.ProgramView.interpretation_required

*attribute* · *instance attribute*

```python
interpretation_required = interpretation_required
```

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

### lawref.evaluator.prepared.ProgramView.key

*attribute* · *instance attribute*

```python
key = key
```

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

### lawref.evaluator.prepared.ProgramView.nodes

*attribute* · *instance attribute*

```python
nodes = nodes
```

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

### lawref.evaluator.prepared.ProgramView.positions_dependent

*attribute* · *instance attribute*

```python
positions_dependent = positions_dependent
```

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

### lawref.evaluator.prepared.ProgramView.replay_issues

*attribute* · *instance attribute*

```python
replay_issues = replay_issues
```

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

### lawref.evaluator.prepared.ProgramView.selected_interps

*attribute* · *instance attribute*

```python
selected_interps = selected_interps
```

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

### lawref.evaluator.prepared.ProgramView.table

*attribute* · *instance attribute*

```python
table = table
```

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

### lawref.evaluator.prepared.ProgramView.derived

*method*

```python
def derived(name: str, compute: Callable[[], Any]) -> Any
```

Ленивая чистая функция от узлов вида; считается один раз.

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

## lawref.evaluator.prepared.program_view

*function*

```python
def program_view(ir: dict, legal_time: str, options: dict, case: dict) -> ProgramView
```

Вид программы для этого вызова: из кэша записи программы либо собранный.

Ключ — ровно те оси, от которых зависят три прохода: дата права, список
выбранных прочтений КАК ПОДАН (порядок влияет на порядок issues о
неизвестных прочтениях), константы дела в порядке подачи (порядок задаёт
порядок эфемерных деклараций в узлах, [§211](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/29-part-xxviii-compiler-pipeline.ru.md#211-determinism)) и предъявленный состав
редакций [§92](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/13-part-xiii-context-scope-applicability.ru.md#92-edition-applicability) (E-0167): он решает, исполняется ли условие 3 вообще, — без
него один и тот же IR на одной дате отдавал бы из кэша чужой вид.

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