# lawref.compose

*module*

Композиция сведений дел [§168.5](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/24-part-xxiii-cases-snapshots-evaluation.ru.md#1685-композиция-сведений-дел-decision-0169) (DECISION-0169 [§1.5](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/01-part-i-audit.ru.md#1-что-сохраняется-без-изменений)) — эталон.

Потребитель берёт из ДРУГОГО дела выбранные утверждения и их опоры: поставщик
экспортировал снимок `law.case-export/0.1` (`lawref.export`), потребитель
закрепил его ресурсом лока вида `case-snapshot` и назвал в `.lawcase`
декларацией `snapshot <Alias> "<путь>";` и элементом дела
`use <Alias> { record "<StableId>"; … }`. Компилятор понижает поверхность в
директивы (`snapshots`, `uses`), а составное дело строит `compose` — одинаково
в обеих реализациях (differential):

- записи копируются как экспортированы: `id`, `origin`, временные поля,
  `evidence`, `contentHash`, `provenance[]` не меняются (identity неизменна);
- маршруты предъявления — `case.sources[]` в порядке `use`;
- `bindings` → `case.constants`; `evidence`, `supportEdges`, `verifications`
  — в свои массивы; порядок — записи дела, затем снимки в порядке `use`,
  внутри снимка — порядок экспорта;
- один `id` с побайтово равным содержанием ПО ЛЮБОМУ ПУТИ — снимок ↔ снимок
  («ромб» A→C и B→C), снимок ↔ дело, дело ↔ дело (локальный дубль не
  исключение, гейт Astra) — одна запись; иное содержимое — `LDC-E1360`; два
  снимка одного дела (пара `package.name` + дело, независимо от версии) —
  `LDC-E1361`, и он проверяется РАНЬШЕ `LDC-E1360`;
- `case.options.evidenceSnapshotHash` = sha256 канонического отсортированного
  массива `contentHash` использованных снимков — ТОЛЬКО при наличии `use`.

Замыкание внутри снимка берётся по ВЫБРАННЫМ записям: документ, общий для
избранной и неизбранной записи поставщика, не протаскивает ребро к
неизбранной пропозиции (группа векторов «выборка» DECISION-0169 [§5](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/02-part-ii-goals-and-scope.ru.md#5-главные-инварианты-дизайна)). Опоры
отбираются по ПРОПОЗИЦИИ БЕЗ ПОЛЯРНОСТИ и по `relation` (`related_edges`), а
не по побайтовому равенству литералов.

Без `use` вход возвращается тем же объектом: ни `sources`, ни пустых
массивов, ни `evidenceSnapshotHash` — байты прежних дел не меняются ([§1.9](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/01-part-i-audit.ru.md#1-что-сохраняется-без-изменений)).

Привязка документа к источнику (для слота `source` профиля 0.2 [§78.2.3](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#7823-входы-политики-типизированная-проекция) и для
проверки вложений в `ask`): документ D относится к источнику S, если D назван
в поле `evidence` хотя бы одной записи из `S.records` ЛИБО D есть `from`
опоры, экспортированной вместе с такой записью: относящейся по таблице
отбора И принадлежащей пространству имён поставщика (`related_edges` с
`namespace`; собственная опора потребителя к той же пропозиции — не импорт).
Документ,
пришедший из ДВУХ снимков, получает ДВА факта `source`: выбор «первого»
скрыл бы половину происхождения (гейт Astra). Носителя «откуда пришёл
документ» у узла нет намеренно (Ф8-2: поле снимка на узле давало бы ложную
коллизию на «ромбе»), а `ask` исполняет составное дело без снимков — поэтому
правило выводится из `case.sources[]`, записей и опор, и только из них.

## lawref.compose.CONTENT_CONFLICT_CODE

*attribute* · *module attribute*

```python
CONTENT_CONFLICT_CODE = 'LDC-E1360'
```

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

## lawref.compose.DOCUMENTS_SUBDIR

*attribute* · *module attribute*

```python
DOCUMENTS_SUBDIR = 'documents'
```

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

## lawref.compose.DUPLICATE_CASE_CODE

*attribute* · *module attribute*

```python
DUPLICATE_CASE_CODE = 'LDC-E1361'
```

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

## lawref.compose.EXPORT_SCHEMA_VERSION

*attribute* · *module attribute*

```python
EXPORT_SCHEMA_VERSION = 'law.case-export/0.1'
```

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

## lawref.compose.SNAPSHOT_CAPABILITY

*attribute* · *module attribute*

```python
SNAPSHOT_CAPABILITY = 'law.case-export/0.1'
```

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

## lawref.compose.SNAPSHOT_CODE

*attribute* · *module attribute*

```python
SNAPSHOT_CODE = 'LDC-E8704'
```

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

## lawref.compose.SNAPSHOT_KIND

*attribute* · *module attribute*

```python
SNAPSHOT_KIND = 'case-snapshot'
```

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

## lawref.compose.SUPPORT_RELATIONS

*attribute* · *module attribute*

```python
SUPPORT_RELATIONS = ('supports', 'refutes')
```

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

## lawref.compose.UNKNOWN_ALIAS_CODE

*attribute* · *module attribute*

```python
UNKNOWN_ALIAS_CODE = 'LDC-E1362'
```

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

## lawref.compose.attachment_path

*function*

```python
def attachment_path(uri: str) -> str
```

`documents[].path` снимка для документа с локальным `uri` поставщика:
путь ВНУТРИ каталога снимка вместе с префиксом `documents/` ([§168.5](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/24-part-xxiii-cases-snapshots-evaluation.ru.md#1685-композиция-сведений-дел-decision-0169));
файл читается как `<каталог снимка>/<path>`, второй префикс не дописывается.

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

## lawref.compose.attachment_problems

*function*

```python
def attachment_problems(snapshot_dir: Path, artifact: dict, label: str | None = None) -> list[str]
```

Вложения снимка по его перечню `documents`: файл есть, байты сходятся.

Перечень снимка — единственный источник истины о его вложениях: лок
потребителя закрепляет только `export.json`, а `uri` импортированных
документов относительны ПОСТАВЩИКУ и по локу потребителя не ищутся.
`label` — каталог снимка в сообщениях (путь из лока, без пути хоста).

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

## lawref.compose.compose

*function*

```python
def compose(root: Path, case: dict, uses: list[dict], snapshots: dict[str, str], lock: dict | None = None, program: list[dict] | None = None, own: tuple[str, str] | None = None) -> dict
```

Составное дело [§168.5](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/24-part-xxiii-cases-snapshots-evaluation.ru.md#1685-композиция-сведений-дел-decision-0169) из директив компилятора.

`uses` — `[{"alias", "records": [StableId…]}, …]` в порядке `use`;
`snapshots` — `{alias: путь от корня пакета}`; `program` — узлы мира
потребителя (константы [§54](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/09-part-ix-values-terms-expressions.ru.md#54-const) участвуют в проверке столкновений [§1.5](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/01-part-i-audit.ru.md#1-что-сохраняется-без-изменений));
`own` — пара (`package.name`, дело) САМОГО потребителя: снимок
собственного дела — тот же `LDC-E1361` ([§168.5](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/24-part-xxiii-cases-snapshots-evaluation.ru.md#1685-композиция-сведений-дел-decision-0169), цикл). Без
`uses` возвращается сам `case` (байты прежних дел не меняются). Отказы —
`AskError` с кодами DECISION-0169 [§4](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/02-part-ii-goals-and-scope.ru.md#4-не-цели-01); `LDC-E8704` — снимок, лок или
вложение.

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

## lawref.compose.const_closure

*function*

```python
def const_closure(seeds: set[str], constants: dict) -> tuple[list[str], list[str]]
```

ТРАНЗИТИВНОЕ замыкание констант: константа, ссылающаяся на константу,
тянет и её. Возвращает `(разрешённые, недостающие)`, оба отсортированы.

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

## lawref.compose.const_seeds

*function*

```python
def const_seeds(records: list[dict], evidence: list[dict]) -> set[str]
```

`const_ref` в аргументах литерала записи, в объекте `judgment`, в
`payload` документа и в `issuer` ([§168.5](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/24-part-xxiii-cases-snapshots-evaluation.ru.md#1685-композиция-сведений-дел-decision-0169), гейт Astra).

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

## lawref.compose.content_hash

*function*

```python
def content_hash(artifact: dict) -> str
```

sha256 канонических байтов артефакта БЕЗ поля `contentHash` (нерекурсивно).

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

## lawref.compose.documenthash_problem

*function*

```python
def documenthash_problem(artifact: dict) -> str | None
```

`documentHash` каждого экспортированного evidence сверяется с байтами
вложения — при экспорте и при КОМПОЗИЦИИ ([§1.2](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/01-part-i-audit.ru.md#1-что-сохраняется-без-изменений), [§1.3](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/01-part-i-audit.ru.md#1-что-сохраняется-без-изменений), гейт Astra):
импортированный документ проверяется перечнем снимка, а не локом
потребителя, и обходить проверку не вправе.

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

## lawref.compose.evidence_sources

*function*

```python
def evidence_sources(case: dict) -> dict[str, list[tuple[str, str]]]
```

evidenceId → [(package.name, case)…] в порядке `sources[]` — для слота
`source` профиля 0.2 ([§78.2.3](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#7823-входы-политики-типизированная-проекция)). Документ, пришедший из ДВУХ снимков,
получает ДВА факта: выбор «первого» скрыл бы половину происхождения.

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

## lawref.compose.imported_evidence_ids

*function*

```python
def imported_evidence_ids(case: dict) -> set[str]
```

Документы, пришедшие по `case.sources[]` (правило привязки — docstring модуля).

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

## lawref.compose.internal_uri

*function*

```python
def internal_uri(item: dict) -> str | None
```

`uri` документа, указывающий ВНУТРЬ пакета (файл, а не внешняя ссылка).

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

## lawref.compose.load_snapshot

*function*

```python
def load_snapshot(root: Path, path: str, lock: dict) -> dict
```

Снимок по пути от корня пакета: артефакт, лок и вложения (LDC-E8704).

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

## lawref.compose.node_namespace

*function*

```python
def node_namespace(node: dict) -> str
```

Пространство имён узла: поле `package` понижения, иначе префикс
StableId до `#` (узел, поданный по wire без `package`).

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

## lawref.compose.proposition

*function*

```python
def proposition(literal) -> bytes
```

Канонические байты литерала БЕЗ полярности — ключ отбора опор.

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

## lawref.compose.read_export

*function*

```python
def read_export(path: Path, label: str | None = None) -> dict
```

`export.json` снимка: форма по схеме и сходящийся `contentHash`.

`label` — имя снимка в сообщениях: путь ИЗ ЛОКА (относительно корня
пакета), а не абсолютный путь хоста, — как у `lawc`; конверт отказа
входит в голдены векторов и не вправе зависеть от каталога checkout.

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

## lawref.compose.related_edges

*function*

```python
def related_edges(edges: list[dict], records: list[dict], namespace: str | None = None) -> list[dict]
```

Опоры, ОТНОСЯЩИЕСЯ к записям по таблице отбора [§168.5](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/24-part-xxiii-cases-snapshots-evaluation.ru.md#1685-композиция-сведений-дел-decision-0169), в порядке входа.

`namespace` — пространство имён ПОСТАВЩИКА: в составном деле опора
считается экспортированной вместе с записью источника, только если она
ему принадлежит (`node_namespace`). Собственная опора потребителя к той
же пропозиции — не импорт: факта `source` она не получает и по перечню
снимка не проверяется ([§78.2.3](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#7823-входы-политики-типизированная-проекция)). Внутри снимка и при экспорте фильтр не
нужен — там все узлы одного поставщика.

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

## lawref.compose.snapshot_attachments

*function*

```python
def snapshot_attachments(root: Path, lock: dict) -> dict[str, set[str]]
```

Перечни вложений ВСЕХ закреплённых снимков: `documents[].path`
(`documents/<uri поставщика>`) → множество sha256. Этим перечнем `ask`
сверяет импортированный документ ([§1.3](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/01-part-i-audit.ru.md#1-что-сохраняется-без-изменений)).

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

## lawref.compose.snapshot_hash

*function*

```python
def snapshot_hash(hashes: list[str]) -> str
```

`evidenceSnapshotHash`: sha256 канонического отсортированного уникального массива.

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

## lawref.compose.source_evidence_ids

*function*

```python
def source_evidence_ids(case: dict, source: dict) -> list[str]
```

Документы источника S ([§1.7](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/01-part-i-audit.ru.md#1-что-сохраняется-без-изменений), [§78.2.3](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#7823-входы-политики-типизированная-проекция)): названные в поле `evidence`
записей `S.records` ЛИБО названные `from` опоры, экспортированной вместе
с такой записью — относящейся по таблице отбора И принадлежащей
пространству имён поставщика (`related_edges` с `namespace`). Порядок —
записи, затем опоры.

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