# lawref.evaluator.whynot

*module*

[§185](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/26-part-xxv-proof-graph-and-explanations.ru.md#185-why-not) `why_not(P)` — blocker graph вида «тело правила не выполнено».

**Почему это отдельный запрос, а не поле ответа.** В движке несработавшего
правила не существует как события: `solve()` перечисляет подстановки,
удовлетворяющие телу, и правило, которое не сработало, просто даёт ПУСТОЕ
перечисление. Точки, где кто-то говорит «правило R не применилось, потому что
второй конъюнкт не установлен», в фикспойнте нет и быть не должно. [§185](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/26-part-xxv-proof-graph-and-explanations.ru.md#185-why-not) это и
предполагает: `why_not(P)` **строит** blocker graph — то есть считает отдельно,
уже по готовому store, а не сопровождает каждый вывод.

**Главное правило модуля — не соврать в сторону «ложно».** [§185](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/26-part-xxv-proof-graph-and-explanations.ru.md#185-why-not): «Why-not не
должен утверждать, что отсутствующее условие ложно». В открытом мире [§61](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/10-part-x-propositions-and-four-valued-support.ru.md#61-literal)–[§63](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/10-part-x-propositions-and-four-valued-support.ru.md#63-explicit-negation)
это различие уже есть и его не надо изобретать: у пары опор четыре значения, и
`NEITHER` («опор нет ни за, ни против») — не то же самое, что `FALSE_ONLY`
(«есть опора против»). Разметка конъюнктов ложится на них один в один:

    TRUE_ONLY  → SATISFIED       конъюнкт выполнен
    FALSE_ONLY → NOT_SATISFIED   есть опора ПРОТИВ — это и есть «refuted»
    BOTH       → CONFLICTED      опоры с обеих сторон [§112](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/16-part-xv-defeasible-semantics-and-conflict-resolution.ru.md#112-ambiguity-policy)
    NEITHER    → UNDETERMINED    ничего не известно; «ложно» здесь запрещено

Несвязанная переменная даёт `UNDETERMINED` по той же причине: про конъюнкт с
неизвестным аргументом нельзя сказать ничего, кроме «не определено».

**Словарь нормативен и до сих пор не заполнялся.** `ApplicabilityStatus` и
`TriggerStatus` объявлены схемой ([§174.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/25-part-xxiv-queries-and-results.ru.md#1741-result-kinds-и-обязательные-dimensions): `RULE_APPLICATION` обязан нести оба),
но во всём дереве из них эмитировалось ровно одно зашитое значение. Здесь они
получают смысл, ради которого заводились.

**Границы v1 названы, а не обойдены.** Диагностируются правила, чья голова —
Literal и унифицируется с целью. Норм-шаблоны [§122](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/18-part-xvii-normative-positions.ru.md#122-канонические-modalities)–[§124](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/18-part-xvii-normative-positions.ru.md#124-goal-types) (голова
`norm_template_ref`), правила вне среза `strict_closure` (нетривиальный scope,
негативная голова) и defeasible-механика [§104](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/16-part-xv-defeasible-semantics-and-conflict-resolution.ru.md#104-defeasible-candidate)–[§115](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/16-part-xv-defeasible-semantics-and-conflict-resolution.ru.md#115-конфликтный-результат) в разбор не входят: у них
своя причина несрабатывания, и приписывать им конъюнктную было бы неверно.
Каждое такое правило попадает в отчёт со статусом `UNDETERMINED` и причиной —
молча пропущенное правило неотличимо от разобранного.

## lawref.evaluator.whynot.CONJUNCT_STATUS

*attribute* · *module attribute*

```python
CONJUNCT_STATUS = {'TRUE_ONLY': 'SATISFIED', 'FALSE_ONLY': 'NOT_SATISFIED', 'BOTH': 'CONFLICTED', 'NEITHER': 'UNDETERMINED'}
```

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

## lawref.evaluator.whynot.FOLD_GOAL_KINDS

*attribute* · *module attribute*

```python
FOLD_GOAL_KINDS = (('validPredicate', 'valid'), ('enteredPredicate', 'entered'), ('currentStatePredicate', 'current'))
```

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

## lawref.evaluator.whynot.HEAD_ARGUMENT_DETAIL

*attribute* · *module attribute*

```python
HEAD_ARGUMENT_DETAIL = 'вычисляемый аргумент головы: равенство с целью не проверено — считать в объяснении нельзя (§185)'
```

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

## lawref.evaluator.whynot.JUDGMENT_ENUM_CAP

*attribute* · *module attribute*

```python
JUDGMENT_ENUM_CAP = 8
```

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

## lawref.evaluator.whynot.TERM_ERROR_PRECEDENCE

*attribute* · *module attribute*

```python
TERM_ERROR_PRECEDENCE = ('MISSING_INPUT', 'MISSING_POLICY', 'NON_EXECUTABLE', 'EXTERNAL_UNAVAILABLE', 'TYPE_ERROR', 'RUNTIME_ERROR', 'RESOURCE_LIMIT')
```

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

## lawref.evaluator.whynot.TERM_ERROR_SILENT

*attribute* · *module attribute*

```python
TERM_ERROR_SILENT = frozenset({'MISSING_PARAMETER_VALUE'})
```

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

## lawref.evaluator.whynot.TERM_ERROR_STATUS

*attribute* · *module attribute*

```python
TERM_ERROR_STATUS = {'MISSING_POLICY': 'MISSING_POLICY', 'DEADLINE_POLICY_INVALID': 'MISSING_POLICY', 'MISSING_CALENDAR': 'MISSING_INPUT', 'CALENDAR_DATASET_INVALID': 'MISSING_INPUT', 'CALENDAR_DATASET_HASH_MISMATCH': 'MISSING_INPUT', 'CALENDAR_OUT_OF_RANGE': 'MISSING_INPUT', 'EXTERNAL_SNAPSHOT_MISSING': 'EXTERNAL_UNAVAILABLE', 'EXTERNAL_SNAPSHOT_CALL_MISSING': 'EXTERNAL_UNAVAILABLE', 'EXTERNAL_SNAPSHOT_HASH_MISMATCH': 'EXTERNAL_UNAVAILABLE', 'EXTERNAL_SNAPSHOT_PROGRAM_HASH_MISMATCH': 'EXTERNAL_UNAVAILABLE', 'EXTERNAL_SNAPSHOT_CONTEXT_MISMATCH': 'EXTERNAL_UNAVAILABLE', 'EXTERNAL_SNAPSHOT_TYPE_MISMATCH': 'EXTERNAL_UNAVAILABLE', 'EXTERNAL_SNAPSHOT_DUPLICATE_CALL': 'EXTERNAL_UNAVAILABLE', 'TYPE_ERROR': 'TYPE_ERROR', 'CURRENCY_MISMATCH': 'TYPE_ERROR', 'DIMENSION_MISMATCH': 'TYPE_ERROR', 'DATE_INSTANT_MISMATCH': 'TYPE_ERROR', 'RESOURCE_LIMIT': 'RESOURCE_LIMIT', 'OPAQUE_NON_EXECUTABLE': 'NON_EXECUTABLE', 'NON_EXHAUSTIVE_MATCH': 'NON_EXECUTABLE', 'UNKNOWN_FUNCTION': 'NON_EXECUTABLE', 'NON_EXECUTABLE_FUNCTION_RECURSION': 'NON_EXECUTABLE'}
```

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

## lawref.evaluator.whynot.candidate_rules

*function*

```python
def candidate_rules(index: dict[str, list[dict]], goal: dict) -> list[dict]
```

Кандидаты по цели: корзина её предиката (пусто — правил нет вовсе).

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

## lawref.evaluator.whynot.computed_head_arguments

*function*

```python
def computed_head_arguments(head: dict) -> list[dict]
```

Вычисляемые аргументы головы правила-кандидата (E-0164) — по записи
`{position, term, status: UNEVALUATED, detail}` на каждый аргумент, чей
вид не переменная и не ground-значение (`call` [§86](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/12-part-xii-events-actions-temporal-model.ru.md#86-deadline-policy), арифметика [§58](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/09-part-ix-values-terms-expressions.ru.md#58-arithmetic),
`let`), в порядке позиций. Пусто у литеральной головы.

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

## lawref.evaluator.whynot.defeater_judgment_blockers

*function*

```python
def defeater_judgment_blockers(goal: dict, nodes: list[dict], store: SupportStore, judgment_decls: dict[str, dict], legal_time: str, env: Any = None, proof_nodes: list[dict] | None = None) -> list[dict]
```

E-0100, носитель «условие дефитера» (клауза `unless` [§149](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/20-part-xix-definitions-presumptions-fictions-exceptions.ru.md#149-unless), правило
`defeater` [§95.3](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/15-part-xiv-rules.ru.md#953-defeater)): судьи, прочитанные из тел живых дефитеров СТОЯЩЕЙ цели.

Зовётся при паре TRUE_ONLY (вывод стоит поражаемо): корень обхода — только
дефитеры головы цели, способные снять вывод (`_defeater_can_remove`);
ниже корня обход тот же, что у `judgment_blockers` ([§47.5](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#475-computability-classification) транзитивно),
и живость E-0101 читается по конъюнктам дефитера так же. Результат —
статус остаётся TRUE_ONLY, evaluation status REQUIRES_JUDGMENT с
запросом: ответ судьи способен снять вывод. Общее правило, которое не
сработало, дефитера не активирует — цель тогда NEITHER, и это путь
`judgment_blockers`, где дефитер головы разбирается наравне (E-0066).

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

## lawref.evaluator.whynot.fold_blockers

*function*

```python
def fold_blockers(goal: dict, nodes: list[dict], store: SupportStore, proof_nodes: list[dict], env: Any = None) -> list[dict]
```

[§185](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/26-part-xxv-proof-graph-and-explanations.ru.md#185-why-not) над предикатами свёртки [§164.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/22-part-xxi-procedures.ru.md#1641-носитель-у-трёх-предикатов-decision-0156) (DECISION-0156 [§2.9](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/01-part-i-audit.ru.md#29-externalsymbol-заменён-effectcapability-model)).

До DECISION-0156 попытка перехода понижалась в правило `<P>/<T>/valid`, и
[§185](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/26-part-xxv-proof-graph-and-explanations.ru.md#185-why-not) называл его блокером с недостающими конъюнктами гарда. Правила больше
нет: переход есть элемент узла `procedure` [§201.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/28-part-xxvii-canonical-legal-ir.ru.md#2011-узел-procedure-decision-0156), а исход попытки —
proof-узел `procedure_step` [§180.2](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/26-part-xxv-proof-graph-and-explanations.ru.md#1802-proof-узлы-свёртки-процедур-decision-0156) с `outcome` и `reason`. Отчёт читает
ЕГО и переводит в тот же словарь блокеров: `rule` — StableId ПЕРЕХОДА,
`conjuncts` — недостающие посылки причины. Ничего не вычисляется заново:
свёртка уже прошла, и [§185](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/26-part-xxv-proof-graph-and-explanations.ru.md#185-why-not) её только называет.

Граница названа: конъюнкты гарда размечаются по ИТОГОВОМУ store, тогда
как свёртка читала префикс [§161.2](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/22-part-xxi-procedures.ru.md#1612-что-видит-гард-и-почему-свёртка-стратифицируется). У гарда над предикатом ниже барьера
(обычный случай — барьер [§161.2](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/22-part-xxi-procedures.ru.md#1612-что-видит-гард-и-почему-свёртка-стратифицируется) иного и не допускает) они совпадают; у
гарда, читающего свёртку ДРУГОГО экземпляра [§163.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/22-part-xxi-procedures.ru.md#1631-completed-называет-экземпляр-decision-0156), могут разойтись, и
тогда сводка блокера остаётся `UNDETERMINED`, а не объявляет посылку
выполненной.

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

## lawref.evaluator.whynot.fold_goal

*function*

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

Узел `procedure` [§201.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/28-part-xxvii-canonical-legal-ir.ru.md#2011-узел-procedure-decision-0156) и вид цели, если цель — предикат свёртки.

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

## lawref.evaluator.whynot.head_compatible

*function*

```python
def head_compatible(head: dict, goal: dict) -> dict[str, dict] | None
```

Подстановка, при которой голова правила МОЖЕТ совпасть с целью (E-0105).

От `unify_head` отличается ровно двумя допущениями, и оба названы:
вычисляемый аргумент головы (`call` [§86](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/12-part-xii-events-actions-temporal-model.ru.md#86-deadline-policy), арифметика [§58](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/09-part-ix-values-terms-expressions.ru.md#58-arithmetic)) считается
совместимым с любым аргументом цели — его значение без вычисления
неизвестно, а [§185](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/26-part-xxv-proof-graph-and-explanations.ru.md#185-why-not) запрещает считать в объяснении; аргумент-переменная
ЦЕЛИ (частично связанный подцель обхода) совместим с любым аргументом
головы. Обход ошибок терма ищет правила, которые МОГЛИ БЫ вывести цель,
и ложное «несовместимо» здесь дороже ложного «совместимо»: первое
прячет причину, второе называет лишнее правило по имени.

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

## lawref.evaluator.whynot.judgment_blockers

*function*

```python
def judgment_blockers(goal: dict, nodes: list[dict], store: SupportStore, judgment_decls: dict[str, dict], legal_time: str, env: Any = None, proof_nodes: list[dict] | None = None) -> list[dict]
```

[§47.3](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#473-judgment) + [§185](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/26-part-xxv-proof-graph-and-explanations.ru.md#185-why-not) (DECISION-0025, добор DECISION-0036): ground-литералы
judgment-relations, стоящие между ground-целью и её возможным выводом.

Словарь — строго [§185](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/26-part-xxv-proof-graph-and-explanations.ru.md#185-why-not), через тот же `_conjunct_report`: запрос порождается
там, где конъюнктный разбор ставит UNDETERMINED, плюс два добора
DECISION-0036, живущие ТОЛЬКО здесь (сам [§185](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/26-part-xxv-proof-graph-and-explanations.ru.md#185-why-not)-отчёт why_not не тронут):

- refuted-тест над judgment-relation при паре опор NEITHER — «опровергнуто
  ли» решает судья (adjudicated может быть отрицательным [§73](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#73-origin)); НЕ
  транзитивно — FALSE_ONLY производных достижим лишь negative-выводами
  вне v1-разбора;
- judgment-конъюнкт с несвязанными переменными перечисляется по активному
  домену (кап JUDGMENT_ENUM_CAP кандидатов, порядок [§208](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/28-part-xxvii-canonical-legal-ir.ru.md#208-canonical-serialization)); не-judgment
  конъюнкт с переменной, не связанной ни целью, ни свидетелем store
  (errata E-0189, `_witness_substs`), по-прежнему вне разбора.

Судья под `not_known`/`unknown` при NEITHER — SATISFIED, запроса нет.
Границы разбора — `_judgment_skip_reason`: с errata E-0066 сюда входят и
поражаемые правила [§104](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/16-part-xv-defeasible-semantics-and-conflict-resolution.ru.md#104-defeasible-candidate) (отчёт [§185](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/26-part-xxv-proof-graph-and-explanations.ru.md#185-why-not) у `why_not` при этом не тронут —
он по-прежнему объясняет их несрабатывание механикой [§107](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/16-part-xv-defeasible-semantics-and-conflict-resolution.ru.md#107-defeat)–[§108](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/16-part-xv-defeasible-semantics-and-conflict-resolution.ru.md#108-surviving-support)).
Правило, у которого иной конъюнкт УЖЕ не выполнен, запроса не даёт
(errata E-0101, `_rule_is_dead`): судья называется только там, где его
ответ может изменить исход; при нескольких правилах одной головы запрос
собирается по живым, и голова без живого правила отвечает COMPUTED.
Поражаемое правило, чей вывод поразил бы уже сработавший дефитер [§107.3](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/16-part-xv-defeasible-semantics-and-conflict-resolution.ru.md#107-defeat),
запроса не даёт по той же мере (errata E-0103, `_conclusion_defeated`);
`proof_nodes` — proof-граф вычисления, в котором лежат применения
дефитеров (без него поражение не читается — так зовут только юнит-тесты).

Рекурсия — транзитивное распространение [§47.5](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#475-computability-classification) (JUDGMENT_DEPENDENT по
transitive dependency graph): неопределённая НЕ-судейская подцель
разбирается своими правилами; цикл держит `visited`. Живых ветвей бывает
несколько, и у каждой свой судья (errata E-0111): посещение ключуется
парой (атом, полярность), отрицательная подцель спускается в правила с
отрицательной головой, собираются ВСЕ живые ветви. До errata посещение
ключевалось без полярности, и на развилке `g ⇐ p` / `g ⇐ q ⇐ not p` ветвь,
дошедшая до `p` первой, закрывала `not p` для второй — какая первая,
решал порядок обхода реализации, и оракул с движком называли разных
судей. Порядок результата — канонические байты [§208](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/28-part-xxvii-canonical-legal-ir.ru.md#208-canonical-serialization) объекта `request`
(`request_key`): воспроизводимость [§211](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/29-part-xxviii-compiler-pipeline.ru.md#211-determinism).

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

## lawref.evaluator.whynot.order_requests

*function*

```python
def order_requests(literals: list[dict]) -> list[dict]
```

Запросы судье в каноническом порядке E-0111, без дублей по ключу.

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

## lawref.evaluator.whynot.position_judgment_blockers

*function*

```python
def position_judgment_blockers(positions: list[dict], store: SupportStore, judgment_decls: dict[str, dict]) -> list[dict]
```

E-0100, носитель `condition` цели [§123](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/18-part-xvii-normative-positions.ru.md#123-duty)/[§124](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/18-part-xvii-normative-positions.ru.md#124-goal-types): судьи, от которых зависит
статус позиции обязанности (achievement/maintenance) в запросе `positions`.

Живость — по [§134](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/19-part-xviii-norm-lifecycle-fulfillment-violation.ru.md#134-positioninstance-statuses): позиция ACTIVE либо UNDETERMINED (в окне ответ судьи
даёт SATISFIED/VIOLATED, после окна — разрешает неопределённость);
PENDING, CREATED, DEFEATED, SATISFIED и VIOLATED ответом судьи не
меняются. Условие — ground-литерал payload (`condition_literal`), пара
NEITHER.

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

## lawref.evaluator.whynot.power_judgment_blockers

*function*

```python
def power_judgment_blockers(goal: dict, positions: list[dict], store: SupportStore, judgment_decls: dict[str, dict], blocked_effect: dict | None = None) -> list[dict]
```

E-0100, носитель `valid_when` полномочия [§127](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/18-part-xvii-normative-positions.ru.md#127-power): судьи, от которых
зависит действительность ОСУЩЕСТВЛЁННОГО полномочия с эффектом-целью.

Живость — по [§127](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/18-part-xvii-normative-positions.ru.md#127-power): позиция ACTIVE, событие `exercise` в деле (без
попытки эффекта нет не из-за судьи, запроса нет), `valid_when` читает
отношение суждения при паре NEITHER.

E-0120 ([§47.3](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#473-judgment)/[§128](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/18-part-xvii-normative-positions.ru.md#128-immunity)) — третье лицо живости, после E-0101 (мёртвое
правило) и E-0103 (поражённый вывод): полномочие, чей эффект заперт
применимым и не побеждённым иммунитетом [§128](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/18-part-xvii-normative-positions.ru.md#128-immunity) на ЭТОМ деле, не живо —
ответ судьи исхода не изменит, и запроса от `valid_when` нет. Как и у
`_conclusion_defeated`, вердикт ЧИТАЕТСЯ по уже вычисленному, а не
решается заново: материализация эффектов [§127](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/18-part-xvii-normative-positions.ru.md#127-power) сравнила `protected_effect`
и приоритеты [§106](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/16-part-xv-defeasible-semantics-and-conflict-resolution.ru.md#106-priority-relation) (`effects.immunity_verdict`) и записала исход в карту
`blocked_effects` под ключом эффекта — `reason: "immunity"`. Пересчитать
его здесь нельзя: связка `_rule`, на которой стоит [§106](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/16-part-xv-defeasible-semantics-and-conflict-resolution.ru.md#106-priority-relation), снимается с
позиций до ответа.

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

## lawref.evaluator.whynot.request_key

*function*

```python
def request_key(literal: dict) -> str
```

E-0111: ключ запроса судье — канонические байты [§208](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/28-part-xxvii-canonical-legal-ir.ru.md#208-canonical-serialization) объекта `request`
(отношение, полярность, аргументы). Он же задаёт порядок элементов
`results[].judgmentRequests` и нумерацию proof-узлов `judgment`: два
запроса одного отношения с разной полярностью — разные запросы, и один
вопрос обязан давать один документ при любом порядке обхода.

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

## lawref.evaluator.whynot.rules_by_head

*function*

```python
def rules_by_head(nodes: list[dict]) -> dict[str, list[dict]]
```

Правила по ПРЕДИКАТУ ГОЛОВЫ, внутри — в порядке `id` (DECISION-0114 [§2.4](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/01-part-i-audit.ru.md#24-не-была-определена-точная-модель-defeasible-вывода)).

Обходы [§185](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/26-part-xxv-proof-graph-and-explanations.ru.md#185-why-not) (`term_error_blockers`, `judgment_blockers` и соседи) ищут
правила, чья голова совместима с целью, и обе совместимости —
`unify_head` и `head_compatible` — отвергают несовпадение предикатов
ПЕРВОЙ проверкой. Значит правило с другим предикатом головы не может
попасть в ответ ни при каком продолжении обхода, и индекс отдаёт ровно то
же множество кандидатов, что полный скан.

Порядок сохранён намеренно и является частью контракта: внутри корзины
правила лежат в том же порядке `id`, в каком их обходил скан, поэтому
порядок посещения подцелей, порядок вставки в `found` и, значит, байты
ответа не меняются. Селфтест сверяет обе стороны — множество и порядок.

Замер, ради которого индекс заведён (04.09.2026): у обхода стоял
`sorted(n for n in nodes if kind == "rule")` ВНУТРИ `walk`, то есть все
узлы программы фильтровались и сортировались на каждый шаг обхода. На
`kz-entrepreneurial-code` (5890 узлов, 1704 сценария) половина тёплого
вызова уходила в `term_error_blockers`: 31 тыс. вызовов `head_compatible`
на один вопрос.

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

## lawref.evaluator.whynot.term_error_blockers

*function*

```python
def term_error_blockers(goal: dict, nodes: list[dict], store: SupportStore, issues: list[dict], legal_time: str, env: Any = None) -> list[dict]
```

E-0105 ([§58](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/09-part-ix-values-terms-expressions.ru.md#58-arithmetic)/[§175](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/25-part-xxiv-queries-and-results.ru.md#175-evaluationstatus)/[§185](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/26-part-xxv-proof-graph-and-explanations.ru.md#185-why-not)): ошибки терма у правил, которые могли бы вывести
цель, — то, из-за чего результат NEITHER есть не «право молчит», а «право
не посчитано».

Обход — тот же, что у запроса судьи [§47.5](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#475-computability-classification): правила, чья голова совместима
с целью (`head_compatible`: вычисляемый аргумент головы не мешает), и
дальше в их конъюнкты без опоры (пара NEITHER у связанного, любой
несвязанный — по предикату). У каждого правила читается `store.term_errors`
— те же пары (правило, код), по которым вычисление уже выпустило issue:
здесь ничего не считается заново, а только называется. Сообщение берётся
из той самой issue. Порядок — по правилу и коду ([§211](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/29-part-xxviii-compiler-pipeline.ru.md#211-determinism)).

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

## lawref.evaluator.whynot.term_error_inputs

*function*

```python
def term_error_inputs(blockers: list[dict], query_tail: str) -> list[dict]
```

`missing_inputs` [§174](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/25-part-xxiv-queries-and-results.ru.md#174-evaluationresult) — по одному InputRequirement на (правило, код).

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

## lawref.evaluator.whynot.term_error_status

*function*

```python
def term_error_status(blockers: list[dict]) -> str
```

[§175](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/25-part-xxiv-queries-and-results.ru.md#175-evaluationstatus): статус результата по старшему из кодов ошибок терма.

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

## lawref.evaluator.whynot.unify_head

*function*

```python
def unify_head(head: dict, goal: dict) -> dict | None
```

Подстановка, при которой голова правила совпадает с ground-целью.

`None` — головы несовместимы. Цель обязана быть ground: `why_not` спрашивают
про конкретное утверждение («почему не разрешено Бобу»), а не про схему.

Переменная, встреченная дважды, обязана связаться одинаково: `p(x, x)`
против цели `p(a, b)` не унифицируется. Без этой проверки правило с
повторной переменной диагностировалось бы по неверной подстановке.

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

## lawref.evaluator.whynot.why_not

*function*

```python
def why_not(goal: dict, nodes: list[dict], store: SupportStore, legal_time: str, env: Any = None, excluded: tuple[list[dict], dict[str, str]] | None = None, proof_nodes: list[dict] | None = None) -> list[dict]
```

Отчёт по кандидатам-правилам для ground-цели.

Порядок — по id правила ([§211](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/29-part-xxviii-compiler-pipeline.ru.md#211-determinism)): отчёт обязан быть воспроизводим.
`excluded` — правила, исключённые проекцией [§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), и причина по
id: в вычислении их нет, в объяснении они — NOT_APPLICABLE, тем же
словарём, что правило вне `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).

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