# lawref.impact

*module*

Impact-отчёт черновика — L8.0, работа О-1 плана LAYERS-NEXT («законопроект
как PR»).

Оркестрация без семантики (инвариант CLAUDE.md №5): модуль только вызывает
существующие входы — `evaluate` [§168](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/24-part-xxiii-cases-snapshots-evaluation.ru.md#168-case-file)–[§178](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/25-part-xxiv-queries-and-results.ru.md#178-compliance-result), `semdiff.diff_documents` [§215](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/29-part-xxviii-compiler-pipeline.ru.md#215-semantic-diff) — и
сравнивает канонические байты [§208](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/28-part-xxvii-canonical-legal-ir.ru.md#208-canonical-serialization). Ни одно правило права здесь не
интерпретируется.

Отчёт из трёх частей (LAYERS-NEXT [§3](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/02-part-ii-goals-and-scope.ru.md#3-цели-law-dsl-core-01), О-1):

- (а) семантический diff узлов [§215](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/29-part-xxviii-compiler-pipeline.ru.md#215-semantic-diff) между «до» и «после»;
- (б) сценарии с изменившимся исходом: исход = канонические байты
  {results, positions, conflicts, issues} evaluation-документа; для
  изменившихся прикладываются оба полных документа, включая парные
  proof-графы [§179](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/26-part-xxv-proof-graph-and-explanations.ru.md#179-proof-graph)–[§187](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/26-part-xxv-proof-graph-and-explanations.ru.md#187-proof-hash);
- (в) покрытие норм «после»: норма покрыта, только если ДОКАЗАННО применилась
  в каком-нибудь сценарии — узел `rule_application` [§180](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/26-part-xxv-proof-graph-and-explanations.ru.md#180-rule-application-proof) или `norm_creation`
  [§135](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/19-part-xviii-norm-lifecycle-fulfillment-violation.ru.md#135-activation) в proof-графе (DECISION-0019, вариант C). Непокрытость — отчётная
  величина, не код выхода: решение 0019 ещё черновик.

Сценарий — словарь {"case": {...}, "query": {...}}, той же формы, что
артефакты evaluate-векторов (case.json + query.json; календарный ресурс в v1
не поддержан). Опции исполнения читаются из case["options"] — ровно как в
`run_vector`.

## lawref.impact.DATE_TYPE

*attribute* · *module attribute*

```python
DATE_TYPE = 'urn:law:std#Date'
```

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

## lawref.impact.EDITIONS_SCHEMA_VERSION

*attribute* · *module attribute*

```python
EDITIONS_SCHEMA_VERSION = 'law.impact-editions/0.1'
```

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

## lawref.impact.INPUT_REJECTED_CODES

*attribute* · *module attribute*

```python
INPUT_REJECTED_CODES = ('ASSERTION_NOT_ACCEPTED', 'ASSERTION_OUTSIDE_VALID', 'TYPE_ERROR')
```

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

## lawref.impact.LEGAL_TIME_ARG

*attribute* · *module attribute*

```python
LEGAL_TIME_ARG = 'legal_time'
```

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

## lawref.impact.RULE_KIND

*attribute* · *module attribute*

```python
RULE_KIND = 'rule'
```

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

## lawref.impact.SCHEMA_VERSION

*attribute* · *module attribute*

```python
SCHEMA_VERSION = 'law.impact/0.1'
```

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

## lawref.impact.TEMPLATE_KIND

*attribute* · *module attribute*

```python
TEMPLATE_KIND = 'norm_template'
```

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

## lawref.impact.VERDICT_CHANGE

*attribute* · *module attribute*

```python
VERDICT_CHANGE = 'SEMANTIC-CHANGE'
```

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

## lawref.impact.VERDICT_EXIT

*attribute* · *module attribute*

```python
VERDICT_EXIT = {VERDICT_CHANGE: 1, VERDICT_NO_CHANGE: 0, VERDICT_NOT_DATED: 3, VERDICT_PARAM_AXIS: 4}
```

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

## lawref.impact.VERDICT_NOT_DATED

*attribute* · *module attribute*

```python
VERDICT_NOT_DATED = 'NOT-DATED'
```

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

## lawref.impact.VERDICT_NO_CHANGE

*attribute* · *module attribute*

```python
VERDICT_NO_CHANGE = 'NO-CHANGE'
```

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

## lawref.impact.VERDICT_PARAM_AXIS

*attribute* · *module attribute*

```python
VERDICT_PARAM_AXIS = 'PARAM-AXIS'
```

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

## lawref.impact.build_edition_report

*function*

```python
def build_edition_report(document: dict, time_before: str, time_after: str, scenarios: list[tuple[str, dict]], world: list[dict] | None = None) -> dict[str, Any]
```

Отчёт О-2: тот же каркас, что у О-1, но «до»/«после» — проекции ОДНОГО
документа на две даты. Семантический diff [§215](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/29-part-xxviii-compiler-pipeline.ru.md#215-semantic-diff) между проекциями и есть
«семантический changelog редакции»: нормы, вошедшие в силу и вышедшие из
неё; сценарии — контрфактические: те же факты, изменившееся право.

Контекст сценария по-прежнему НЕ трогается (сдвиг legalTime дела смешал бы
смену редакции с течением времени дела: окна `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), датированные
параметры [§87](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/12-part-xii-events-actions-temporal-model.ru.md#87-rule-temporal-qualifiers), статус позиции [§133](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/18-part-xvii-normative-positions.ru.md#133-norm-template-identity)). Взамен каждая сторона исполняется с
`editionsAsPresented` = дата ЭТОЙ стороны ([§92](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/13-part-xiii-context-scope-applicability.ru.md#92-edition-applicability), errata E-0167): проекция
[§31](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/07-part-vii-source-and-document-model.ru.md#31-lifecycle) применяется ВМЕСТО условия 3 [§92](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/13-part-xiii-context-scope-applicability.ru.md#92-edition-applicability), а не поверх него. До 11.09.2026 поля
не было, и после E-0095 каждая сторона исполнялась по ПЕРЕСЕЧЕНИЮ двух
проекций — на дату отчёта и на дату дела: на `us-liver-status` терялась вся
поздняя редакция (девять расхождений), на паре 2026-06-30 → 2026-07-01
пакета КС врали восемь строк из одиннадцати.

`world` — CLIR-документы зависимостей (DECISION-0193): мир связывается и
проецируется ЦЕЛИКОМ, отчёт — о корне, изменения зависимостей — разделом
`world` (состав мира и мета их проекций). Без мира раздела нет.

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

## lawref.impact.build_report

*function*

```python
def build_report(before: dict, after: dict, scenarios: list[tuple[str, dict]], presented: tuple[str | None, str | None] = (None, None), scope: frozenset[str] | None = None) -> dict[str, Any]
```

Собрать отчёт. `scenarios` — пары (имя, сценарий); порядок в отчёте —
по имени, чтобы байты не зависели от порядка аргументов CLI.

`presented` — даты предъявления состава редакций [§92](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/13-part-xiii-context-scope-applicability.ru.md#92-edition-applicability) (E-0167) по сторонам:
у О-1 обе стороны поданы документами и проекции нет, поэтому `(None, None)`
и байты отчёта прежние; у О-2 стороны суть проекции на две даты, и каждая
исполняется своей.

`scope` (DECISION-0193) — id узлов КОРНЯ, когда стороны суть связанный
мир: сценарии исполняются над всем миром, а семантический diff [§215](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/29-part-xxviii-compiler-pipeline.ru.md#215-semantic-diff) и
цели покрытия DECISION-0019/C — по корню. Иначе правила зависимости-хаба
стали бы «непокрытыми» в отчёте каждого потребителя.

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

## lawref.impact.coverage_targets

*function*

```python
def coverage_targets(document: dict) -> tuple[list[str], list[str]]
```

Нормы документа, требующие покрытия: (rules, norm_templates).

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

## lawref.impact.covered_ids

*function*

```python
def covered_ids(kind: str, doc: dict) -> set[str]
```

Id правил и шаблонов, доказанно применившихся в evaluation-документе.

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

## lawref.impact.dating

*function*

```python
def dating(report: dict) -> dict | None
```

Датируемость О-2-отчёта; None, если отчёт не по оси редакций.

Разрешение якоря от даты не зависит (`_anchor_edition` её не читает),
поэтому обе проекции дают один и тот же список; объединение — страховка
того же рода, что у `unresolvedAnchors` в шапке.

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

## lawref.impact.exit_code

*function*

```python
def exit_code(report: dict) -> int
```

Код выхода CLI: 0 — изменений нет, 1 — изменения, 3 — датировать было
нечем. Отдельный код — чтобы вызывающий конвейер не читал «право не
менялось» там, где проекция вовсе не работала.

Раздел целей (DECISION-0089 [§4.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/02-part-ii-goals-and-scope.ru.md#4-не-цели-01), `--goals`) старше: сломанная цель —
код 4 поверх любого вердикта diff-а, иначе «изменения есть» (1) читалось
бы конвейером как обычная поправка, а не как поправка, убившая цель.

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

## lawref.impact.parameter_cutovers

*function*

```python
def parameter_cutovers(document: dict) -> list[str]
```

Даты, на которых переключаются ДАТИРОВАННЫЕ ПАРАМЕТРЫ акта ([§87](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/12-part-xii-events-actions-temporal-model.ru.md#87-rule-temporal-qualifiers)).

Зачем это в отчёте оси редакций. Поправку можно закодировать двумя
способами, и оба законны: новой РЕДАКЦИЕЙ источника (её и меряет
проекция D1) либо датированным ЗНАЧЕНИЕМ ПАРАМЕТРА — `Cap(legal_time)`
с ветвями `< 2026-01-01` и `>= 2026-01-01`. Второй способ проекция не
видит по построению и не должна видеть: `build_edition_report` намеренно
не трогает legalTime дела, чтобы не смешать две оси.

Отсюда отказ, ради которого функция и написана. На `kz-osms` — акте с
ОДНОЙ редакцией и параметрической поправкой 206-VIII — отчёт печатал
«изменений нет» на паре дат 2025-06-30 → 2026-06-30, хотя на этих самых
датах право отвечает 17 000 ₸ и 34 000 ₸. Отрицательное утверждение обо
всём корпусе делалось там, где мерилась не та ось (замер 01.09.2026).

Считаются литералы, а не вычисляются ответы: функция сообщает, что у акта
ЕСТЬ рубеж в интервале, и не утверждает, что он что-то меняет, — это
работа исполнения (О-1 с двумя legalTime).

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

## lawref.impact.rejected_inputs

*function*

```python
def rejected_inputs(kind: str, doc: dict) -> list[dict]
```

Issues сценария класса «вход не вошёл в расчёт» (см. INPUT_REJECTED_CODES).

Возвращается не счёт, а сами записи: аналитику нужно ИМЯ отвергнутого
утверждения, иначе «дело не то» нечем чинить.

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

## lawref.impact.render_editions_header

*function*

```python
def render_editions_header(report: dict) -> list[str]
```

Строки оси редакций для текстового вида (пусто, если отчёт не О-2).

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

## lawref.impact.render_text

*function*

```python
def render_text(report: dict) -> str
```

Человекочитаемая сводка. Вербализованный вид diff-а — существующей
командой `python -m lawref diff` (LS [§3.6](https://github.com/arxohq/law/blob/master/spec/LAYERS-SURFACES.ru.md#36-привязка-утверждения)); здесь только итоги.

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

## lawref.impact.report_bytes

*function*

```python
def report_bytes(report: dict) -> bytes
```

Канонические байты отчёта ([§208](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/28-part-xxvii-canonical-legal-ir.ru.md#208-canonical-serialization)-эмиттер); контракт детерминизма гейта.

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

## lawref.impact.verdict

*function*

```python
def verdict(report: dict) -> str
```

Вердикт отчёта — одна политика на текстовый вид и на код выхода.

Порядок разбора не симметричен, и это существо дела. «Изменение найдено» —
утверждение ПОЛОЖИТЕЛЬНОЕ и об одной норме: найденное расхождение стоит и
при неполной датировке. «Изменений нет» — утверждение ОТРИЦАТЕЛЬНОЕ и обо
ВСЁМ корпусе, поэтому оно не печатается, пока хоть одна заякоренная норма
осталась вне датировки: про такую норму проекция не ответила, а не
ответила «нет» ([§249](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/33-part-xxxii-interoperability.ru.md#249-legalruleml-adapter) — не угадываем, а сообщаем).

Одного висячего якоря для `NOT-DATED` мало: узел датирует любой
разрешившийся якорь, и в `undatedNodes` попадает лишь узел, у которого не
разрешился НИ ОДИН.

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