# Часть VI. Package и module system ## 22. Заголовок файла Версия в `language "law.core" version ""` сверяется с реестром языка `spec/feature-registry.json` ТОЧНО (errata E-0156): `0.1.0` не есть `0.1`, версия вне реестра — `LDC-E1401` (§266.1). Ревизия семантики, которую конверт CLIR и хэши §209 несут для этой версии, — `semanticsId` реестра. ```law language "law.core" version "0.1"; package kz.bvr version "2026.1.0"; namespace "urn:kz:law:bvr"; ``` Заголовок МОЖЕТ нести метки пакета той же формы, что метки деклараций (§218), — они называют формализуемый акт: ```text label ru-KZ official "О республиканском бюджете на 2026 – 2028 годы"; ``` Метки объявляются один раз на package, а не в каждом его файле; повторное объявление с иным содержимым — ошибка (errata E-0025). Каждый `.law` файл ДОЛЖЕН принадлежать ровно одному package. Несколько файлов одного package образуют единое namespace tree. Package name состоит из ASCII lowercase identifiers, разделённых точкой; canonical pattern: `[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*`. Для публикуемых registry packages СЛЕДУЕТ использовать минимум два сегмента (`kz.bvr`, `law.std.time`), тогда как одно-сегментное имя допустимо для локального/встроенного package. Package и symbol используют разные separators: ```text kz.sources // package kz.sources::MinisterOrder // public symbol из package project.operator // field access ``` Local symbols могут квалифицироваться `module::Symbol`. Публичный namespace определяется manifest-URI, поэтому package rename не должен менять stable `@id` существующих public declarations. ## 23. Import ```law import law.std.time version "0.1.0"; import kz.sources version "2026.2.0"; import kz.common.actors version "1.0.0"; ``` Алиас `as` и селекция `::{…}` зарезервированы для 0.2 и отвергаются `LDC-E0206` (errata E-0070, вектор T117): в 0.1 пакет адресуется своим именем, а его `pub`-символы — квалификацией `kz.sources::Symbol`. Source code МОЖЕТ указывать version range, но canonical build использует exact version из lockfile. Исключение — пакет с названным профилем канона §170.1 (DECISION-0170): под профилем версия `import` ОБЯЗАНА быть точной и равной пину элемента B; импорт-диапазон под профилем — `LDC-E1110`. Resolve-контекстом импорта при профиле являются версии B, а не состав контекста: видимость имён по-прежнему даёт только `import`, и `LDC-E1110`/`LDC-E1111` действуют без изменений. Названная ТОЧНАЯ версия — утверждение закона о зависимости, и resolve-контекст обязан её подтвердить: расхождение точной версии `import` с версией предъявленной зависимости — `LDC-E1110` (errata E-0122). Диапазон от сверки освобождён — выбор внутри него и есть работа резолвера. Тем же правилом связан манифест §10: строка `[dependencies]` обязана совпасть с `import`-ом по версии и иметь опору в тексте закона — иначе `LDC-E1111`. Обратное (import без строки манифеста) диагностикой компилятора не является: манифест — authoring-описание, и его полнота ведётся вне компилятора. Мир исполнения замыкается ОДНОЙ версией каждого пакета (errata E-0123): две версии одного пакета в одном мире — `LDC-E1112`, потому что тождество §36 есть пара «номинальный тип, StableId», и вторая версия того же публичного типа раздваивает его. Пин §11 при этом обязан сходиться не только у корня: если импортируемый пакет в мире ПРИСУТСТВУЕТ, его байты обязаны быть теми, на которых понижен потребитель (`LDC-E1104`). Замкнутость мира этим не требуется — потребитель, читающий у соседа только типы, его узлов не тянет. **Прелюдия `law.std` (errata E-0154, DECISION-0166 §4.3).** Десять пакетов §257–§266 импортируются НЕЯВНО: их имена доступны всякому файлу без `import`, а версия std определена версией семантики языка, объявленной `language "law.core" version` (`0.1` → `0.1.0`, `0.2` → `0.2.0`; таблица — каталог `packs/std/index.json`). Явный `import law.std. version ""` допустим и означает ПОДТВЕРЖДЕНИЕ встроенного пина: точная версия, не равная версии std для версии языка, — `LDC-E1110` (тот же класс, что у E-0122); повторный явный импорт одного пакета std — `LDC-E1103`, как у любого пакета. Такой импорт не создаёт зависимости: пакет прелюдии не предъявляется resolve-контекстом §11, не пишется в `law.lock`, не входит в `envelope.imports` (§209 — std есть часть языка, его идентичность даёт ревизия семантики), и строка `[dependencies]` манифеста с ним — только подтверждение той же версии (`LDC-E1111`), а не зависимость для закрепления. Имя `law.std` и всё под `law.std.` зарезервировано за прелюдией: `package law.std.` — `LDC-E1113` (право объявлять std принадлежит каталогу, не пакету), контекст, предъявляющий пакет прелюдии, — тот же `LDC-E1113`, импорт пакета прелюдии, которого §257–§266 не называют, — `LDC-E1114`. Все четыре звучат на обоих путях компилятора (§210.1); `check` без контекста судит о них, потому что контекст им не нужен. Import whole package делает доступными только его `pub` symbols через package name/alias; unqualified import selection делает перечисленные symbols доступными локально. Wildcard import в `core-production` запрещён. Два unqualified imports одного local name создают compile error, пока автор не задаст alias. Импорт пакета объявляется ОДИН раз на package, а не в каждом его файле (тот же принцип, что у меток §22 — errata E-0025): повторный `import` того же пакета — `LDC-E1103`, самоимпорт — `LDC-E1102`, и оба звучат на ОБОИХ путях компилятора, `check` и `lower`. До errata E-0093 проверка жила только в `check`: пакет с `import` в каждом модуле отвергался одной командой и понижался другой молча, а ворота, зовущие `check` перед своей проверкой, такой пакет пропускали. **Ссылка без предъявленного контекста (errata E-0126).** Qualified-ссылка `pkg::symbol` разрешается только против resolve-контекста §11; позиция ссылки роли не играет — голова правила (`then pkg::rel(...)`), тело, аргумент, сигнатура и `@source` суть одна и та же ссылка §23. `lower` без предъявленного контекста ОБЯЗАН отказать: `LDC-E1104` на каждом объявленном `import`-е и `LDC-E1105` на каждой qualified-ссылке; документ с литералом, чей `predicate` пуст, и с `envelope.imports = []` у пакета с импортами не порождается никогда — такое правило не сработало бы ни разу, а semantic hash §209 заявил бы замкнутость мира, которой нет. `check` без контекста о зависимостях НЕ судит (DECISION-0102: контекст §23 — не предмет статики текста, и `check` не вправе краснеть на сотне пакетов корпуса по причине, к тексту отношения не имеющей); это единственная намеренная асимметрия стадий §210.1, и она — свойство входа: под ОДНИМ предъявленным контекстом множества кодов `check` и `lower` совпадают. До errata `lower` шёл через фильтр стадии `check` и понижал пакет с импортами без контекста молча, с rc=0: у пилота из тринадцати правил каждая чужая голова уезжала в CLIR как `"predicate": ""`. Package dependency graph в Core 0.1 ДОЛЖЕН быть acyclic. Взаимно рекурсивные legal rules размещаются в одном package либо зависят от вынесенного interface/common package; recursion контролируется predicate stratification, а не циклическими package imports. Resolver должен показывать минимальный dependency cycle и suggested extraction fix. ### 23.1. Экспортируемые факт-группы (аддитивно, DECISION-0069) ```law pub facts Lexicon { assert classified(entity_ref("urn:kz:lexicon#ana"), Zhinishke); assert classified(entity_ref("urn:kz:lexicon#qala"), Zhuan); } ``` ```law facts kz.grammar.lexicon::Lexicon; ``` Факт-группа — именованная единица экспорта фактов §71 через границу пакета. Тело группы состоит ТОЛЬКО из `assert`: это гарантия грамматики, а не дисциплины — правило соседа по-прежнему не исполняется, иначе стратификация §110 стала бы межпакетной. Группа лоуверится в обычные assertion-узлы (они факты и своего пакета тоже) с группо-локальными StableId `{namespace}#{Группа}/assert-{j}` — рост группы не перенумеровывает анонимные assert-ы пакета снаружи (принцип E-0027 на границе группы). Членство и публичность несёт provenance узла (`factGroup`, `visibility`): у Assertion нет поля visibility, и это носитель того же рода, каким A3 метит порождённые узлы (`a3Kind`). Включение — ЯВНАЯ строка `facts пакет::Группа;` у потребителя: молчаливого слияния фактов соседа не существует. Assertion-узлы группы материализуются в CLIR потребителя БАЙТОВО — id, package и provenance производителя сохраняются, и провенанс чужого факта виден в proof-графе §179 своим StableId. Материализованные узлы входят в узлы потребителя, то есть в его `semanticHash` §209: смена словаря производителя меняет программу потребителя, и закрепление достигается транзитивно — через `contentHash` зависимости, который уже пиннится в `law.lock` §11 и в `envelope.imports`; отдельного хэша группы в lock НЕ заводится, второй носитель того же факта разошёлся бы с первым молча. Состав групп, как и exports §24, считает ТОЛЬКО резолвер (`resolver.fact_groups`) и предъявляет компилятору контекстом `--imports` (ключ `factGroups`); в envelope §11 состав не переносится. Непубличная группа наружу не выдаётся. Включение несуществующей или непубличной группы, включение без предъявленного контекста и повторное включение одной группы — статическая ошибка `LDC-E1327` при lowering (как §24-видимость: контекст — вход lowering-а, у `check` его нет). ## 24. Visibility ```law pub entity Person; internal relation compiler_helper(person: Person); ``` По умолчанию declaration является `internal`. Production API и межпакетные references могут обращаться только к `pub` declarations. Член перечисления экспортируется ВМЕСТЕ СО СВОИМ ТИПОМ: у `pub enum` в составе экспорта стоит и имя типа, и имя каждого его члена. Собственной видимости у члена нет и быть не может — `visibility` есть префикс верхнеуровневой декларации, а продукция `enum_variant` места для него не имеет. Частично публичное перечисление в 0.1 не выражается, и это требование, а не умолчание: домен перечисления перечисляется ЦЕЛИКОМ — проверкой тотальности §A3.3.1, замыканием §70 над предикатом членства, — и домен, часть которого невидима потребителю, давал бы на один вопрос разные ответы по разные стороны границы пакета. StableId члена — `{namespace}#{Член}`: тот же, каким разрешается локальная ссылка на него, и собственного узла CLIR у члена при этом нет (§55.1). Отсюда правило разрешения совпадений: если имя члена совпало с именем публичной декларации того же пакета, состав экспорта называет StableId ДЕКЛАРАЦИИ. Узел, у которого id может быть переопределён (§14), называет его сам; синтезированное имя члена не вправе его вытеснять. До 31.08.2026 экспортировался только тип, и `pub enum` был декларацией без употребления: `check` принимал ссылку на член, `lower` отвергал её `LDC-E1105` (errata E-0065). Запрет безусловен, а его ПРОВЕРКА — условна, и различие обязано быть видимым. Компилятор судит о составе экспорта зависимости по тому, что предъявил резолвер (`exports` рядом с LockedImport §11; в сам envelope поле не уезжает — форма §11 закреплена и входит в §209). Отсюда три различимых состояния, а не два: - **состав предъявлен, символ в нём есть** — ссылка разрешается; - **состав предъявлен, символа в нём нет** — `LDC-E1105`. Сюда же относится ПУСТОЙ предъявленный состав: он означает «у зависимости нет ни одной `pub`-декларации», то есть межпакетная ссылка на неё запрещена целиком; - **состав НЕ предъявлен** — проверить §24 нечем. Ссылка принимается (контекст, собранный из одного LockedImport без байтов зависимости, состава знать не может и вправе существовать), но непроведённая проверка ДОЛЖНА называться: `LDC-W1105`. Молчание здесь неотличимо от «проверено и разрешено», а стоит за ним, возможно, ссылка на `internal`. Правило — тоже declaration, и `pub rule` экспортирует его StableId (errata E-0092): квалифицированная ссылка `pkg::Rule` в позиции аргумента понижается в `const_ref` на `{namespace}#{Rule}` — тот же терм, каким понижается голое локальное имя правила, и тот же идентификатор, который публикует `rule_of` §118/§180 о применении. Это делает выразимым факт о старшинстве ЧУЖОЙ нормы по её имени (`norm_source(kz.corpus.labour_code::UnilateralChangeIsUnlawful, LabourCode)`), а не только по адресу применения. Видимость правила несёт его собственный узел CLIR (`visibility: "public"`), как у прочих экспортируемых видов; у правила §206-развёртки её несёт каждый порождённый узел. Само правило соседа при этом НЕ исполняется — импорт по-прежнему несёт интерфейс, а не узлы (§23). Прочтение §152 — тоже declaration, и `pub interpretation` экспортирует его StableId (DECISION-0152): квалифицированная ссылка `pkg::Reading` законна в позиции `extends` §157 и в `alternatives` группы §155, то есть группа прочтений собирается над пакетами, каждый из которых закрепляет СВОЙ первоисточник, а не над копиями чужих норм. Видимость несёт узел самого прочтения (`visibility: "public"`); вложенные члены прочтения её не получают — адресуемо прочтение целиком, а не его правило по отдельности. Правила соседа при этом, как и всегда, НЕ исполняются от одного импорта: чтобы они участвовали в выводе, его пакет обязан стоять в мире. Симметрично `pub` НЕ делает адресуемой позицию `include`/`exclude` §152: там стоит не имя прочтения, а имя объявления, чью каноническую экспансию (errata E-0115) вычисляет lowering из самого объявления — а объявления соседа у него нет. Квалифицированная ссылка в этой позиции отвергается прямо (DECISION-0152 §2 п. 2), а не молча вносит корневой узел без экспансии. Пустой и непредъявленный состав до 29.08.2026 были одним значением, и следствие было ровно тем, против чего §24 написан: пакет без единой `pub`-декларации молча разрешал ссылаться на любой свой символ. ## 25. Namespace extensions Расширение задаётся namespaced annotations: ```text @kz.mining::severity("critical") rule R defeasible { ... } ``` Неизвестная annotation ДОЛЖНА сохраняться при round-trip. Она НЕ ДОЛЖНА менять core semantics, если соответствующий feature/profile не объявлен в manifest. ## 26. Feature gates ```text @requires_feature("law.decision/0.1") ``` Compiler ДОЛЖЕН отказать, если семантически активная feature отсутствует. Неизвестные purely-informational annotations могут быть сохранены с warning. ---