# lawref.mutate

*module*

Детерминированные semantic mutations над CLIR.

Модуль намеренно работает с *уже lowered* Legal IR: он не переписывает
``.law``-исходники и не пытается угадать юридический текст.  Каждый
дескриптор содержит достаточно данных, чтобы его можно было повторно
применить к тому же semantic snapshot через :func:`apply_mutation`.

Границы v1:

* перечисление не имеет скрытого лимита; ``cap`` действует только когда
  вызывающий передал его явно и это отражено в manifest;
* ``invalid`` означает, что операция дала бы невалидный CLIR либо нарушила
  базовую safety-проверку правила; ``stillborn`` означает валидный результат
  с тем же semantic hash.  Stillborn — generator-internal/non-score запись,
  а не mutant, который можно засчитать killed или survived;
* contentHash меняется только у структурно изменённых узлов.  В некоторых
  legacy-пакетах есть post-lowering attachments с историческим contentHash,
  и перерасчёт всех узлов превращал бы один mutant в шумный derived diff.
  Top-level semanticHash пересчитывается всегда.

Это не интерпретатор права.  Он применяет только локальные структурные
операторы, а корректность формы подтверждает нормативная JSON Schema.

## lawref.mutate.COMPARISON_SWAPS

*attribute* · *module attribute*

```python
COMPARISON_SWAPS = {'eq': ('ne', 'ge', 'le'), 'ne': ('eq'), 'lt': ('le', 'eq'), 'le': ('lt', 'eq'), 'gt': ('ge', 'eq'), 'ge': ('gt', 'eq')}
```

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

## lawref.mutate.DEFAULT_OPERATORS

*attribute* · *module attribute*

```python
DEFAULT_OPERATORS = ('drop_conjunct', 'swap_comparison', 'substitute_var', 'bind_entity_ref', 'freshen_var_occurrence', 'toggle_not_known', 'remove_not_known', 'drop_effective', 'remove_priority', 'remove_rule')
```

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

## lawref.mutate.SCHEMA_VERSION

*attribute* · *module attribute*

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

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

## lawref.mutate.InvalidMutation

*class*

```python
class InvalidMutation(MutationError)
```

Bases: `lawref.mutate.MutationError`

A descriptor cannot produce a schema- and safety-valid CLIR mutant.

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

## lawref.mutate.MutationError

*class*

```python
class MutationError(ValueError)
```

Bases: `ValueError`

Descriptor, base document, or caller contract is malformed.

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

## lawref.mutate.apply_mutation

*function*

```python
def apply_mutation(document: dict, descriptor: dict) -> dict
```

Apply one descriptor to CLIR and return a fresh, rehashed document.

The source document is never mutated.  Descriptor replay is guarded by the
semantic snapshot and each recorded ``before`` value, so applying a
descriptor to another edition fails loudly rather than drifting to a
similarly shaped location.  This public entrypoint performs full document
preflight and validates the complete resulting document afterwards; bulk
enumeration uses the internal preflighted path once for throughput.

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

## lawref.mutate.enumerate_mutants

*function*

```python
def enumerate_mutants(document: dict, operators: Iterable[str] | str | None = None, cap: int | None = None, nodes: Iterable[str] | None = None) -> dict[str, Any]
```

Enumerate valid CLIR mutants in canonical descriptor order.

The return object is intentionally JSON-ready::

    {"manifest": ..., "mutants": [{"descriptor": ..., "document": ...}],
     "invalid": [...], "stillborn": [...]}.

``cap=None`` means no cap.  An explicit cap stops after that many valid
mutants in canonical order.  The manifest names exactly how many later
descriptors were not examined, rather than pretending that cap is an
exhaustive result.  ``nodes`` restricts candidates to the named carrier
nodes and is reflected in the manifest; existing callers without it get
byte-identical results (реализация — потребитель ``iter_mutants``).

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

## lawref.mutate.iter_mutants

*function*

```python
def iter_mutants(document: dict, operators: Iterable[str] | str | None = None, cap: int | None = None, nodes: Iterable[str] | None = None)
```

Ленивый близнец ``enumerate_mutants``: поток вместо материализации.

Появился из замера профайлера связывания (31.08.2026): на kz-income-tax
(16,7 МБ, 1681 правило) жадное перечисление — это 1681 deepcopy полного
документа с пересчётом semanticHash каждого, часы и гигабайты ДО первого
использования. Поток отдаёт ``("valid" | "invalid" | "stillborn", запись)``
в том же каноническом порядке и с теми же записями, что жадная форма,
но держит один мутант за раз; ``nodes`` сужает кандидатов по id узла.

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

## lawref.mutate.report_bytes

*function*

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

Canonical, reproducible transport form of an enumeration result.

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