arxo-codegen 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. arxo_codegen/__init__.py +5 -0
  2. arxo_codegen/__main__.py +7 -0
  3. arxo_codegen/active-outcomes.json +6837 -0
  4. arxo_codegen/check.py +158 -0
  5. arxo_codegen/check_active_canaries.py +262 -0
  6. arxo_codegen/check_api.py +76 -0
  7. arxo_codegen/check_conformance.py +414 -0
  8. arxo_codegen/check_matrix.py +131 -0
  9. arxo_codegen/check_packages.py +281 -0
  10. arxo_codegen/check_semantic_probes.py +293 -0
  11. arxo_codegen/check_xlsx.py +320 -0
  12. arxo_codegen/consumer_runner.py +316 -0
  13. arxo_codegen/core_support.py +203 -0
  14. arxo_codegen/coverage.py +341 -0
  15. arxo_codegen/deliver.py +180 -0
  16. arxo_codegen/examples/xlsx/pricing.lawir.json +409 -0
  17. arxo_codegen/examples/xlsx/pricing.xlsx.json +52 -0
  18. arxo_codegen/excel_plan.py +602 -0
  19. arxo_codegen/harness/verify/ci/gates/backends/check_backends.py +1248 -0
  20. arxo_codegen/harness/verify/ci/gates/backends/l1_fixtures.py +463 -0
  21. arxo_codegen/inert-nodes.json +62 -0
  22. arxo_codegen/inert.py +91 -0
  23. arxo_codegen/inputs.py +59 -0
  24. arxo_codegen/package.py +565 -0
  25. arxo_codegen/plan.py +4053 -0
  26. arxo_codegen/recertify_registry.py +100 -0
  27. arxo_codegen/runtime-cert.json +5259 -0
  28. arxo_codegen/runtime_cert.py +233 -0
  29. arxo_codegen/survey.py +272 -0
  30. arxo_codegen/target_runner.py +101 -0
  31. arxo_codegen/targets/bsl/cert.bsl +113 -0
  32. arxo_codegen/targets/bsl/emit.py +356 -0
  33. arxo_codegen/targets/bsl/json.bsl +243 -0
  34. arxo_codegen/targets/bsl/runner.bsl +210 -0
  35. arxo_codegen/targets/bsl/runtime.bsl +858 -0
  36. arxo_codegen/targets/cs/Cert.cs +90 -0
  37. arxo_codegen/targets/cs/Runner.cs +154 -0
  38. arxo_codegen/targets/cs/Runtime.cs +243 -0
  39. arxo_codegen/targets/cs/emit.py +223 -0
  40. arxo_codegen/targets/dart/cert.dart +99 -0
  41. arxo_codegen/targets/dart/emit.py +206 -0
  42. arxo_codegen/targets/dart/json.dart +166 -0
  43. arxo_codegen/targets/dart/runner.dart +154 -0
  44. arxo_codegen/targets/dart/runtime.dart +293 -0
  45. arxo_codegen/targets/erl/cert.erl +64 -0
  46. arxo_codegen/targets/erl/emit.py +216 -0
  47. arxo_codegen/targets/erl/rt.erl +238 -0
  48. arxo_codegen/targets/erl/runner.erl +130 -0
  49. arxo_codegen/targets/ex/cert.exs +62 -0
  50. arxo_codegen/targets/ex/emit.py +204 -0
  51. arxo_codegen/targets/ex/runner.exs +124 -0
  52. arxo_codegen/targets/ex/runtime.ex +227 -0
  53. arxo_codegen/targets/go/apiBase.go +483 -0
  54. arxo_codegen/targets/go/apicheck.go +97 -0
  55. arxo_codegen/targets/go/cert.go +218 -0
  56. arxo_codegen/targets/go/emit.py +1806 -0
  57. arxo_codegen/targets/go/evaluate.go +1826 -0
  58. arxo_codegen/targets/go/runner.go +38 -0
  59. arxo_codegen/targets/go/runtime.go +2612 -0
  60. arxo_codegen/targets/java/Cert.java +91 -0
  61. arxo_codegen/targets/java/Json.java +148 -0
  62. arxo_codegen/targets/java/Runner.java +151 -0
  63. arxo_codegen/targets/java/Runtime.java +243 -0
  64. arxo_codegen/targets/java/emit.py +198 -0
  65. arxo_codegen/targets/kt/Cert.kt +67 -0
  66. arxo_codegen/targets/kt/Json.kt +103 -0
  67. arxo_codegen/targets/kt/Runner.kt +126 -0
  68. arxo_codegen/targets/kt/Runtime.kt +196 -0
  69. arxo_codegen/targets/kt/emit.py +195 -0
  70. arxo_codegen/targets/lua/cert.lua +82 -0
  71. arxo_codegen/targets/lua/emit.py +250 -0
  72. arxo_codegen/targets/lua/json.lua +133 -0
  73. arxo_codegen/targets/lua/runner.lua +167 -0
  74. arxo_codegen/targets/lua/runtime.lua +541 -0
  75. arxo_codegen/targets/php/cert.php +84 -0
  76. arxo_codegen/targets/php/emit.py +229 -0
  77. arxo_codegen/targets/php/json.php +144 -0
  78. arxo_codegen/targets/php/runner.php +189 -0
  79. arxo_codegen/targets/php/runtime.php +660 -0
  80. arxo_codegen/targets/py/api_base.py +334 -0
  81. arxo_codegen/targets/py/apicheck.py +51 -0
  82. arxo_codegen/targets/py/cert.py +140 -0
  83. arxo_codegen/targets/py/emit.py +1140 -0
  84. arxo_codegen/targets/py/evaluate.py +980 -0
  85. arxo_codegen/targets/py/runner.py +38 -0
  86. arxo_codegen/targets/py/runtime.py +1634 -0
  87. arxo_codegen/targets/rb/cert.rb +73 -0
  88. arxo_codegen/targets/rb/emit.py +203 -0
  89. arxo_codegen/targets/rb/runner.rb +130 -0
  90. arxo_codegen/targets/rb/runtime.rb +255 -0
  91. arxo_codegen/targets/rs/Cargo.lock +142 -0
  92. arxo_codegen/targets/rs/Cargo.toml +16 -0
  93. arxo_codegen/targets/rs/cert.rs +106 -0
  94. arxo_codegen/targets/rs/emit.py +233 -0
  95. arxo_codegen/targets/rs/prefetch.rs +1 -0
  96. arxo_codegen/targets/rs/runner.rs +181 -0
  97. arxo_codegen/targets/rs/runtime.rs +448 -0
  98. arxo_codegen/targets/scala/Cert.scala +77 -0
  99. arxo_codegen/targets/scala/Json.scala +140 -0
  100. arxo_codegen/targets/scala/Runner.scala +121 -0
  101. arxo_codegen/targets/scala/Runtime.scala +269 -0
  102. arxo_codegen/targets/scala/emit.py +193 -0
  103. arxo_codegen/targets/swift/cert.swift +105 -0
  104. arxo_codegen/targets/swift/emit.py +231 -0
  105. arxo_codegen/targets/swift/json.swift +208 -0
  106. arxo_codegen/targets/swift/runner.swift +133 -0
  107. arxo_codegen/targets/swift/runtime.swift +528 -0
  108. arxo_codegen/targets/ts/apiBase.ts +250 -0
  109. arxo_codegen/targets/ts/apicheck.ts +37 -0
  110. arxo_codegen/targets/ts/cert.ts +96 -0
  111. arxo_codegen/targets/ts/emit.py +1214 -0
  112. arxo_codegen/targets/ts/evaluate.ts +894 -0
  113. arxo_codegen/targets/ts/package-lock.json +45 -0
  114. arxo_codegen/targets/ts/package.json +9 -0
  115. arxo_codegen/targets/ts/runner.ts +31 -0
  116. arxo_codegen/targets/ts/runtime.ts +1369 -0
  117. arxo_codegen/targets/xlsx/README.ru.md +176 -0
  118. arxo_codegen/targets/xlsx/emit.py +198 -0
  119. arxo_codegen/temporal.py +59 -0
  120. arxo_codegen/test_alias_types.py +171 -0
  121. arxo_codegen/test_api_contract.py +89 -0
  122. arxo_codegen/test_calendar_policy.py +48 -0
  123. arxo_codegen/test_check.py +49 -0
  124. arxo_codegen/test_check_active_canaries.py +96 -0
  125. arxo_codegen/test_check_conformance.py +132 -0
  126. arxo_codegen/test_check_semantic_probes.py +85 -0
  127. arxo_codegen/test_check_xlsx.py +52 -0
  128. arxo_codegen/test_consumer_runner.py +175 -0
  129. arxo_codegen/test_core_support.py +80 -0
  130. arxo_codegen/test_e0212_decision_time.py +63 -0
  131. arxo_codegen/test_head_aggregate.py +71 -0
  132. arxo_codegen/test_inert.py +143 -0
  133. arxo_codegen/test_instant_types.py +113 -0
  134. arxo_codegen/test_key_conflict_scope.py +92 -0
  135. arxo_codegen/test_key_conflict_targets.py +17 -0
  136. arxo_codegen/test_package_layout.py +163 -0
  137. arxo_codegen/test_plan_language_version.py +44 -0
  138. arxo_codegen/test_positions_refusal.py +43 -0
  139. arxo_codegen/test_recertify_registry.py +77 -0
  140. arxo_codegen/test_ruby_target.py +41 -0
  141. arxo_codegen/test_runtime_cert.py +83 -0
  142. arxo_codegen/test_secondary_key_conflict_scope.py +17 -0
  143. arxo_codegen/test_stage_r.py +108 -0
  144. arxo_codegen/test_standalone_inputs.py +71 -0
  145. arxo_codegen/test_support.py +150 -0
  146. arxo_codegen/test_survey_inputs.py +115 -0
  147. arxo_codegen-0.4.0.dist-info/LICENSE +202 -0
  148. arxo_codegen-0.4.0.dist-info/METADATA +1673 -0
  149. arxo_codegen-0.4.0.dist-info/RECORD +152 -0
  150. arxo_codegen-0.4.0.dist-info/WHEEL +5 -0
  151. arxo_codegen-0.4.0.dist-info/entry_points.txt +13 -0
  152. arxo_codegen-0.4.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1673 @@
1
+ Metadata-Version: 2.1
2
+ Name: arxo-codegen
3
+ Version: 0.4.0
4
+ Summary: CLIR code-generation backends and delivery CLI
5
+ License: Apache-2.0
6
+ Requires-Python: >=3.12
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: jsonschema<5,>=4.26
10
+ Requires-Dist: fastjsonschema<3,>=2.22
11
+
12
+ # arxohq/codegen — самостоятельная кодогенерация модели
13
+
14
+ Standalone repository: требуется явный валидированный `LAW_CODEGEN_CORE_KIT`;
15
+ соседний checkout law-dsl не используется.
16
+
17
+ Несовместимый выпуск 0.4.0 (этап R, 15.10.2026) принимает ровно `0.2.11` и
18
+ `law.core.ir/0.7`; прежние и будущие штампы отвергаются до планирования.
19
+ Реестр R.2 пересертифицирован: 1689 текущих векторов, 503 совпадения,
20
+ 1186 закреплённых отказов/случаев вне профиля; py/ts/go, расхождений и
21
+ крахов нет. Приёмка 0.4.0 выполняется на установленном wheel через
22
+ `ci/verify.py`; vendor-пины и встроенный native lock обновляются вместе.
23
+ Push и теги выполняет владелец; локальная поставка — `candidate`. 51 errata квалифицирована портом, именованным отказом
24
+ или границей профиля; старые семантические пины из active registry удалены.
25
+ `positions` временно снят у `ts`, `py`, `go`: прямой вычислительный вход
26
+ отвечает `POSITIONS_UNAVAILABLE` до создания модели, перенос рангов — W6. Модель с `norm_template` либо нормативной головой
27
+ отказывает `NORM_RANKS_UNAVAILABLE` для любого вопроса. Цели больше не
28
+ объявляют `norm`; прежние эмиттеры лестницы и эффектов удалены.
29
+
30
+ Текущий сертифицированный профиль — только `py`, `ts`, `go`. Вторичные
31
+ цели и XLSX явно отказывают на `admission:current` до отдельного порта;
32
+ наличие их исторических исходников не означает поддержку текущего выпуска.
33
+ Одна числовая source-канарейка понижается текущим компилятором в `Number`:
34
+ проверяется `NUMBER_UNAVAILABLE`, пять остальных сохраняют mutation proof.
35
+
36
+ Публичный план — `law-backend-plan/0.20`: `basePlanVersion` выбирает
37
+ исполнительный формат 0.7–0.19, `admission.reservedAssertionIds` сохраняет
38
+ идентичности не-assertion узлов корня и зависимостей. Возможность
39
+ `admission:current` обязательна для текущей поставки py/ts/go. Transport case
40
+ с коллизией id получает `LDC-E1382`; typed temporal bounds проверяются до
41
+ вывода. `Number` даёт `NUMBER_UNAVAILABLE` до старой сверки типов слотов;
42
+ агрегаты в judgment-моделях — `AGGREGATE_JUDGMENT_UNAVAILABLE` (E-0357);
43
+ witness-bound backchain — `JUDGMENT_WITNESS_UNAVAILABLE` (E-0189).
44
+
45
+ Пределы текущей числовой арифметики не заменяются произвольной точностью:
46
+ границы i64/i128 дают `NUMERIC_LIMIT_UNAVAILABLE`; компактное ненулевое
47
+ десятичное расширение >4096 — `DECIMAL_EXPANSION_UNAVAILABLE` (E-0320).
48
+ Нулевые экстремальные exponent/scale нормализуются без огромных степеней
49
+ и циклов. Это явные границы среза, а не поддержка всего WP-13.
50
+
51
+ Быстрый цикл над свежим комплектом, без упаковки wheel:
52
+
53
+ ```sh
54
+ python check_conformance.py --core-kit KIT --fixtures FIXTURES --targets ts \
55
+ --vector-glob 'LAY-L0-INST-*' --report instant.json
56
+ ```
57
+
58
+ `--vector ID` и `--vector-glob 'PATTERN'` повторяются и объединяются;
59
+ опечатка или шаблон без совпадений — ошибка. Отчёт такого запуска `partial`,
60
+ пара инертности всегда проверяется целиком. Явный `--outcomes` сохраняет
61
+ проверку выбранных пинов, включая отказ при устаревшем хэше входов.
62
+ Без него development-запуск сверяет голдены без приёмки полного реестра.
63
+
64
+ Полная проверка реестра (не обычный TS-цикл):
65
+
66
+ ```sh
67
+ python check_conformance.py --core-kit KIT --fixtures FIXTURES --targets py,ts,go \
68
+ --outcomes active-outcomes.json --timeout 180 --report conformance.json
69
+ ```
70
+
71
+ Все совпадения и отказы требуют свежих пинов; лишний пин также ошибка.
72
+ `recertify_registry.py` закрепляет полный завершённый observation без повторной
73
+ сборки: проверяет хэш каждого execution-source и всего fixture payload,
74
+ полноту inventory, три цели, отсутствие ошибок и поимённую квалификацию
75
+ каждого утраченного совпадения. Частичный отчёт, изменённый источник,
76
+ неизвестный исход, crash/divergence и неквалифицированная потеря отвергаются.
77
+ В R.2 отдельно квалифицированы все 145 совпадений последней сертификации
78
+ и их объединение с прежним harness — 525 ID.
79
+
80
+ Принято DECISION-0188. Срез слоя **L0**: пакет Law DSL превращается в обычный
81
+ исходник на целевом языке, который считает **без движка правил**. Профиль —
82
+ `core-executable` (§8.4): ответ и статус, без доказательства, хэшей и
83
+ канонического документа.
84
+
85
+ **Excel:** цель `xlsx` выпускает самостоятельную книгу с формулами по отдельному
86
+ ограниченному профилю `excel-calculator/0.1`. [Сборка, настройки и границы](targets/xlsx/README.ru.md).
87
+ Это протокол `workbook`: книга пересчитывается в Excel, JSON-раннера у неё нет.
88
+
89
+ ## Устройство
90
+
91
+ ```
92
+ CLIR (ir.json)
93
+ │
94
+ plan.py ВСЯ семантика среза и ВСЕ отказы; языка не знает
95
+ │
96
+ план (law-backend-plan/0.7) таблицы, правила, соединения, гарды, термы,
97
+ │ либо 0.8 (L1), 0.9 окна действия §31, собственные факты пакета;
98
+ │ у 0.8 — возможности, страты и фазы §111;
99
+ │ у 0.9 — агрегаты §59 и замыкания §70;
100
+ │ 0.10 с norm снят до W6;
101
+ │ у 0.11 — календарные функции §84–§86;
102
+ │ у 0.12 — Rational §35; у 0.13 — Quantity §49;
103
+ │ у 0.14 — политика округления §50;
104
+ │ у 0.15 — Boolean/Natural, границы месяца, `_` §98;
105
+ │ у 0.16 — датированные приоритеты §92.3 и `prepare`;
106
+ │ у 0.17 — условный терм §56 и вычисляемый аргумент тела;
107
+ │ у 0.18 — функции §46, параметры A3, `let`, `match`;
108
+ │ у 0.19 — канал суждения §47.3
109
+ ├── targets/ts/emit.py → model.ts + api.ts (+ evaluate.ts, apiBase.ts, runtime.ts, runner.ts)
110
+ ├── targets/py/emit.py → model.py + api.py (+ evaluate.py, api_base.py, runtime.py, runner.py)
111
+ ├── targets/go/emit.py → model.go + api.go (+ evaluate.go, apiBase.go, runtime.go, runner.go)
112
+ ├── excel_plan.py → targets/xlsx/emit.py → model.xlsx (профиль Excel, только план 0.7)
113
+ └── targets/{rb,rs,java,cs,kt,erl,ex,lua,swift,php,dart,scala,bsl}/emit.py —
114
+ Ruby, Rust, Java, C#, Kotlin, Erlang, Elixir, Lua, Swift, PHP, Dart,
115
+ Scala, встроенный язык 1С (BSL на OneScript): та же раскладка, свой
116
+ рантайм и раннер, только план 0.7
117
+ ```
118
+
119
+ Цель — каталог `targets/<цель>/` с эмиттером и дескриптором в нём же:
120
+ `available()` (тулчейн на машине), `MODEL` (путь модели в проекте),
121
+ `layout(proj, main)` (рантайм, раннер либо харнесс, файлы сборки языка),
122
+ `build(proj)` (команда сборки или `None`), `run(proj, main)` (argv-префикс).
123
+ Ворота, поставка и потребитель ветвятся по дескриптору, а не по языку;
124
+ `--targets` выбирает цели, по умолчанию `ts,py,go`, `all` в воротах и consumer —
125
+ все с протоколом `runner` и доступным тулчейном. Поставка `./law dev backend`
126
+ также видит цель `xlsx`; её отдельный профиль проверяет `check_xlsx.py`.
127
+
128
+ Дескриптор `PROTOCOL = "workbook"` вместо runner предоставляет `artifacts(plan,
129
+ options) -> {имя: bytes}` и `PROFILE`. Все артефакты готовятся до записи первого
130
+ файла. Паспорт фиксирует фактический профиль и хэши бинарных файлов. Ограничения
131
+ Excel и развёртка формул живут в `excel_plan.py`, сериализатор не решает правила.
132
+
133
+ **Отказ живёт в плане, а не в эмиттере.** Иначе один и тот же пакет означал бы
134
+ в разных языках разное: где-то отказ, где-то тихо другой ответ. Незнакомое
135
+ объявление, незнакомое поле правила или незнакомая форма терма дают не
136
+ «меньше ответа», а ДРУГОЙ ответ — поэтому пропускать нельзя.
137
+
138
+ **Инертная конструкция — единственное исключение, и оно доказывается, а не
139
+ предполагается** (CODEGEN-COVERAGE-PLAN §1.1, W1). Манифест `inert-nodes.json`
140
+ перечисляет конструкции, которые присутствуют в пакете, но на ответ
141
+ поддержанных видов вопросов не влияют: `source_work` §28, `anchor` §33,
142
+ `revision` §32, `entrenchment` §92.3, `factor_vocabulary` и `precedent_decl`
143
+ §201, `counterfactual_decl` §186, поле правила `visibility` §23.2. План
144
+ пропускает ровно перечисленное (`INERT_NODE_KINDS`, `INERT_RULE_FIELDS`); у
145
+ каждой строки — пара векторов `LAY-L0-INERT-<КОНСТРУКЦИЯ>` / `…-BARE` над одной
146
+ программой, делом и вопросом, чьи голдены оракула совпадают по `results`,
147
+ `positions`, `issues`, `conflicts`, `proofGraph` (manifest и `resultHash`
148
+ различаются по построению). `check_conformance` требует обе половины среди
149
+ активных векторов, совпадение половины с конструкцией на каждой цели и
150
+ равенство документов; строка без свидетеля — отказ ворот (`inert.py`).
151
+ `reference` §30.2 в манифест НЕ входит: без применимой на legal_time редакции
152
+ оракул опустошает результаты (`SOURCE_RESOLUTION_FAILED`), и это отказ плана.
153
+
154
+ Проверено на векторе `ERR-E0095-CLOSED-EDITION-RULE`: пока план молча
155
+ игнорировал жизненный цикл редакции, порождённый код отвечал `TRUE_ONLY` там,
156
+ где голден даёт `NEITHER`. Ловят такие места только векторы.
157
+
158
+ ## Формат плана `law-backend-plan/0.7` и `law-backend-plan/0.8`
159
+
160
+ Версия у формата есть, поэтому поля перечислены здесь, а не читаются из кода
161
+ генератора. Изменение состава полей меняет версию. Каждый эмиттер печатает
162
+ закрытый список версий (`SUPPORTED_PLAN*` в `check_plan`) и отвергает любую
163
+ другую, как и возможность плана вне своего `CAPABILITIES`: незнакомое поле
164
+ плана печаталось бы молча, а это другой ответ, не меньший. Сегодня `ts`,
165
+ `py`, `go` печатают версии 0.7–0.19, остальные 13 программных целей и `xlsx` —
166
+ только 0.7. Ворота начинают с отрицательной пробы — план версии `999` обязан
167
+ быть отвергнут всеми эмиттерами. Проба не ловит смену СМЫСЛА поля под
168
+ прежним номером: это держат только векторы.
169
+
170
+ История: `0.1` — без `params` у таблиц; `0.2` (09.09.2026) — таблицы несут
171
+ объявленные типы параметров для проверки аргументов дела §189; `0.3`
172
+ (09.09.2026, ночь) — `consts` и `bases` верхнего уровня, вид `Date` в
173
+ типах и термах, вид `enum` для членов перечислений §38.
174
+ `0.4` (10.09.2026) — арифметика §58: операции `add`, `sub`, `mul`, `div`,
175
+ `mod` и функции `min`, `max`, `abs`, `pow`, `round`; пары видов проверяются
176
+ планом статически по таблицам §58. `0.5` (14.09.2026) — `keys` у таблиц
177
+ (ключи отношений §45.1, раннер печатает `KEY_CONFLICT`) и `filters` у
178
+ правил (константа в аргументе атома тела). `0.6` — `api`; `0.7` — окна
179
+ редакций §92.3 и мир импортов (DECISION-0236). `0.8` (14.09.2026,
180
+ DECISION-0237) — план поражаемого слоя, описан ниже в своём разделе. План
181
+ без возможностей по-прежнему `0.7` и байтово тот же: у 343 планов,
182
+ построенных до 0.8 (векторы и снимки корпуса), отпечатки совпали.
183
+
184
+ **Верхний уровень.** `planVersion` — строка версии. `package` — паспорт из
185
+ CLIR (`name`, `namespace`, `version`, `semanticHash`); эмиттеры его не
186
+ читают, он нужен человеку и будущей поставке. `world` — зависимости мира
187
+ (`name`, `namespace`, `version`), `excluded` — id узлов, снятых проекцией
188
+ §92.3 на всей оси (информация для человека). `tables`, `rules`, `facts`,
189
+ `api` — ниже.
190
+
191
+ **`tables`** — по одной записи на объявленное отношение: `pred` (URN,
192
+ единственный ключ доступа во время исполнения), `name` (последний сегмент,
193
+ даёт имя переменной в порождённом коде), `arity`, `params` — объявленные
194
+ типы параметров по порядку (URN типа `std` либо типа-сущности). Тип каждого
195
+ параметра проверяется планом на входе в срез, а не только у атомов правил:
196
+ таблица с параметром вне среза (Rational, Instant, Date) не может хранить
197
+ значения дела, и отказ обязан жить в плане, а не всплывать в раннере одной
198
+ из целей. `keys` — ключи отношения §45.1 как списки позиций столбцов
199
+ (пустой список — ключей нет). Порождённый модуль после насыщения считает
200
+ группы строк с одинаковой проекцией на ключ; строки уникальны по ключу
201
+ строки, поэтому группа из двух и более строк — различные accepted-кортежи,
202
+ и на каждую такую группу раннер печатает один `KEY_CONFLICT` (§196). У
203
+ оракула в счёт идут утверждения и strict-выводы — то же множество, что в
204
+ таблицах среза. Ответ от конфликта не меняется: evaluator ничего не
205
+ выбирает и не удаляет (`LAY-L0-KEYS`).
206
+
207
+ **`rules`** — по записи на правило, в порядке CLIR:
208
+
209
+ | поле | что несёт |
210
+ |---|---|
211
+ | `name` | уникальное имя в порождённом коде: порядковый номер плюс последний сегмент `id` |
212
+ | `id` | исходный URN правила; идёт комментарием в порождённый код |
213
+ | `window` | `null` либо `{start, end}` — полуинтервал `[start, end)` по оси `legal_time` (§31) |
214
+ | `atoms` | что перебирать, по порядку; строка атома `i` связывается с переменной `r{i}` |
215
+ | `joins` | равенства повторной переменной: пары `[[строка, столбец, тип], …]` |
216
+ | `filters` | константы в аргументах атомов тела: `{row, col, t, term}` — ячейка обязана быть равна терму-литералу (`eq` рантайма, равенство значений) |
217
+ | `guards` | отсев: `{op, l, r}`, где `op` — `lt`, `le`, `gt`, `ge`, `eq`, `ne` |
218
+ | `head` | что положить: `{pred, args}` |
219
+
220
+ **`facts`** — собственные утверждения пакета: `{pred, args}`. Кладутся в
221
+ таблицы до насыщения.
222
+
223
+ **Константа в теле.** Оракул сопоставляет неперемённый аргумент атома тела
224
+ с ground-атомом по каноническим байтам терма — по значению И типу
225
+ (`solver.solve`), а рантайм среза сравнивает `eq` по значению. Поэтому тип
226
+ литерала обязан совпасть с объявленным типом слота статически: литерал
227
+ Integer в Decimal-слоте у оракула не совпадает ни с одной строкой, а
228
+ рантайм по величине ответил бы иначе — это отказ плана «тип аргумента тела
229
+ не совпадает с параметром §189». Член перечисления проверяется по
230
+ принадлежности слоту, а не по «типу члена»: одно имя члена бывает у
231
+ нескольких перечислений пакета (`kz.corpus.land_code`, `FarmHousehold` в
232
+ четырёх). Сущности типа не несут, как у оракула. Подстановочный `_` §98
233
+ (E-0165) и терм с вычислением в аргументе тела — названные отказы.
234
+ Вектор — `ERR-E0052-DECIMAL-CANON`: `price(v0, 110000 USD)` в теле.
235
+
236
+ **`consts`** — константы §54 со значением: `{id: {type, term}}`. Раннер
237
+ разыменовывает `const_ref` в аргументах дела и запроса до исполнения
238
+ (§54.1: ниже ссылок нет) и сверяет тип константы с параметром §189 —
239
+ только у константы std-типа: оракул разыменовывает константы до сверки, и
240
+ константа-сущность становится `entity_ref`, который типа не несёт (до
241
+ 15.09.2026 раннеры сверяли и её; у `eu.pil.jurisdictions` подтип с именем
242
+ `пакет#Тип` не находил базового — 3 ложных NEITHER).
243
+ **`bases`** — наследование сущностей `{подтип: базовый}`: константа подтипа
244
+ подходит параметру базового типа, как у оракула. Ссылка `const_ref`, не
245
+ стоящая в `consts`, — член перечисления §38 либо имя без объявления: оракул
246
+ оставляет её в ключе атома как есть, поэтому раннер берёт её значением-
247
+ идентичностью (вид `enum`), без проверки типа. Ссылка на функцию или
248
+ параметр A3 через `const_ref` — названный отказ плана.
249
+
250
+ **Чужой тип `пакет#Тип`.** Компилятор понижает `algo.description::Algorithm`
251
+ в имя `algo.description#Algorithm`, а не в URN объявления. Вид слота план
252
+ берёт у объявления `namespace#Тип`, где namespace — пакета мира с точно этим
253
+ именем; строки типов в `params`, `consts` и `bases` не меняются, потому что
254
+ оракул сравнивает их строками (`facts._conforms`). Имя без пакета в мире или
255
+ без объявления — вне среза. 15.09.2026 это открыло 27 снимков
256
+ (`algo.*` 15, `mathlib.*`, `isabelle.order`, `arxo.*`, `eu.pil.jurisdictions`).
257
+
258
+ **Алиас типа §40 и сущности `law.std.actors` §261 (W2, часть 1, 27.09.2026).**
259
+ Объявление `type_decl` с `typeKind: alias` прозрачно на исполнении, как у
260
+ оракула (`facts._unalias`, E-0097/E-0098): план ведёт таблицу `aliases` и
261
+ берёт имя типа за цепочкой алиасов в сигнатурах, константах, наследовании,
262
+ параметрах функций §46, `let` и параметрах A3. Цель алиаса компилятор пишет в
263
+ форме `имя.пакета#Тип`; она разрешается через паспорт мира (корень и
264
+ импорты), цель без пакета в мире остаётся строкой и уходит в отказ «тип
265
+ аргумента вне среза генератора: <цель>». Нормализуются ТОЛЬКО ссылки на
266
+ объявленные алиасы: сырые строки `имя.пакета#Тип` (абзац выше) не трогаются.
267
+ Типы `Actor`, `NaturalPerson`, `LegalPerson`, `Organization`,
268
+ `PublicAuthority`, `Official`, `Court`, `Collective` — сущности (вид `text`,
269
+ идентичность), как сущности пакета: `entity_ref` дела типа не несёт, §189 их
270
+ не сверяет. `Event` §79 и `Instant` §2.15 остаются отказом до частей 2–3 W2.
271
+ Свидетели — `LAY-L0-ALIAS-STD-PARAM`, `LAY-L0-ALIAS-ENTITY-CHAIN`,
272
+ `LAY-L0-STD-ACTOR-PARAM`; в корпусе алиасов 1255, из них 1250 с целью в
273
+ форме компилятора.
274
+
275
+ **`LegalContext`, `LegalRole`, `Jurisdiction` и слот `Interval<T>` (W2, релиз 0.1.5).**
276
+ Первые три — идентичности того же рода, что Actor: у оракула нет вида значения,
277
+ дело подаёт `entity_ref`. Слот `Interval<T>` §37 — непрозрачная идентичность:
278
+ литерала такого вида у оракула нет, интервальный терм дела раннер отвергает
279
+ поимённо («аргумент вне среза раннера: interval»), литерал-интервал в теле
280
+ правила — отказ плана. Свидетели — `LAY-L0-STD-CONTEXT-JURISDICTION`
281
+ (совпадение) и `LAY-L0-INTERVAL-SLOT-TERM` (закреплённый отказ раннера).
282
+
283
+ **Ошибка головы поподстановочная (W5, errata E-0265, релиз 0.1.6).** Ошибка
284
+ терма или агрегата головы снимает только свою подстановку: issue — по каждому
285
+ коду однажды, остальные подстановки применяются, как у оракула после E-0265.
286
+ Агрегат `sum`/`min`/`max`/`only` без политики `empty` в голове больше не отказ
287
+ плана (`headAgg`). Статус зависимого результата: NEITHER — статус кода (E-0105),
288
+ иная истинность — итог §181.2 (`ruleErrorStatus` раннеров). Свидетели —
289
+ `LAY-L0-AGG-HEAD-EMPTY-FIRST`, `LAY-L0-AGG-HEAD-ALL-FILLED`, `ERR-E0265-*`.
290
+
291
+ **Instant §2.15 (W2, часть 2, 28.09.2026).** Вид значения `inst`: запись
292
+ мгновения хранится как подана и служит идентичностью атома (E-0214), а
293
+ равенство и порядок берутся по МОМЕНТУ (E-0048): `12:00:00+05:00` и
294
+ `07:00:00Z` равны. §82.2 требует ASCII-григорианскую дату 0001–9999, полные часы/минуты/секунды
295
+ и обязательное смещение 00:00–23:59. Невалидная запись даёт `TYPE_ERROR`;
296
+ дробь произвольной длины сравнивается точно, без усечения. Запись без
297
+ смещения также не проходит admission;
298
+ Date с Instant не сравниваются (§57, у оракула `DATE_INSTANT_MISMATCH`) —
299
+ отказ плана при известных видах и рантайма при неизвестных. Функции §84.1:
300
+ `hours_between`/`minutes_between` — полных единиц со знаком, дробь к нулю;
301
+ `add_duration(after, n unit)` — целая Quantity секунд/минут/часов/суток,
302
+ ответ канонично в UTC без дробных секунд. Литерал Quantity длительности
303
+ сворачивается планом в пару (число, единица), как у календарных функций.
304
+ Возможность `value:instant` объявлена у py, ts, go; контракт рантайма —
305
+ случаи `instant-*`, `hours-between-*`, `minutes-between-*`, `add-duration-*`
306
+ в `runtime-cert.json`. Свидетели — `LAY-L0-INST-EQ-MOMENT`, `LAY-L0-INST-ORDER`,
307
+ `LAY-L0-INST-HOURS-BETWEEN`, `LAY-L0-INST-MINUTES-BETWEEN`,
308
+ `LAY-L0-INST-ADD-DURATION`.
309
+
310
+ **Event §79 (W2, часть 3, 28.09.2026) — срез идентичности.** Корни `Event` и
311
+ `Action` и типы-события пакетов (сущности с базой `urn:law:std#Event`) — слоты
312
+ идентичности, как сущности: событие в факте есть `entity_ref` на закреплённый
313
+ снимок (§79.1). Конструктор-запись события в факте раннер отвергает
314
+ поимённо («аргумент вне среза раннера: record»): его поля читает свёртка
315
+ §161.1, а это территория процедур (W7). Свидетели — `LAY-L0-EVENT-REF-IDENTITY`
316
+ (совпадение) и `LAY-L0-EVENT-RECORD-VALUE` (закреплённый отказ раннера).
317
+
318
+ **Порядок проверок в цикле.** Соединение и фильтр печатаются на уровне атома,
319
+ который их связывает, а не во внутреннем цикле: план не меняется, ответы и
320
+ отказы прежние (`eq` на text, enum, date, dec не отказывает; правило с
321
+ соединением над деньгами печатается прежним порядком). Декартово
322
+ произведение шести атомов тела `algo.description` без этого шло минутами на
323
+ пустом деле.
324
+
325
+ **`api`** (DECISION-0234) — поверхность модуля, выведенная планом по
326
+ правилам `@arxo/law-forms`: `questions` — `pub`-отношения с производящим
327
+ правилом, читающим в теле другой предикат; `inputs` — предикаты, достижимые
328
+ из вопросов и либо эмпирические, либо без производителя. У записи `pred`,
329
+ `name` (уникальное имя функции), `params`, `kinds`; у вопроса ещё `inputs`.
330
+ Цель с `API` и `emit_api` печатает по нему файл — устройство и контракт в
331
+ разделе «Единый вход и библиотека (DECISION-0423)» ниже. Над каждой функцией —
332
+ комментарий из меток отношения и производящих правил с якорями
333
+ `@source локатор · редакция` (поля `labels` и `sources` плана); в файле есть
334
+ реестры по имени.
335
+
336
+ ## Единый вход и библиотека (DECISION-0423)
337
+
338
+ **Один вычислительный вход.** У `ts`, `py`, `go` семантика ответа живёт в одном
339
+ рукописном модуле цели — `evaluate.py`, `evaluate.ts`, `evaluate.go`: дело и
340
+ вопрос в канонической форме §168/§172 (той же, что у векторов и оракула) →
341
+ ответ исполняемого профиля. В нём разбор сырых литералов §208, сверка
342
+ аргументов дела §189, разворот контекста (правовое время, политика срока,
343
+ снимок календаря), насыщение, issues и лестница статусов вопроса (конфликт
344
+ эффектов §127 → конфликт ключа E-0242 → запрос судье §47.3 → ошибка терма
345
+ E-0105/§181.2). Раннер — транспорт JSON к этому входу, API — адаптер
346
+ аргументов к нему же, поэтому API и раннер отвечают одинаково по построению.
347
+ До 0.2.0 часть этой семантики жила только в раннере, и API на одном деле
348
+ давал другой ответ (Decimal в слоте сущности: раннер `NEITHER`/`TYPE_ERROR`,
349
+ API `TRUE_ONLY`/`COMPUTED`).
350
+
351
+ **Состояние на вызов.** Модель — фабрика: эмиттер оборачивает тело модуля
352
+ модели в `new_model()` (Python), `newModel()` (TypeScript, Go); таблицы,
353
+ опоры, кандидаты и issues — локальные переменные вызова, правила — замыкания
354
+ над ними, тела правил не переписываются. Контекст вычисления: у Python —
355
+ `contextvars`, у TypeScript — контекст, который синхронный `evaluate` ставит на
356
+ время вызова и снимает в `finally`, у Go — `evalContext` экземпляра модели, чьи
357
+ методы в фабрике затеняют одноимённые функции рантайма (`legalTime`,
358
+ `add_legal_term` и соседи). Глобального правового времени и мьютекса нет:
359
+ вызовы из потоков Python и горутин Go независимы.
360
+
361
+ **Значения входа — литерал-термы §53.** Конструкторы общей части API
362
+ (`api_base.py`, `apiBase.ts`, `apiBase.go`) строят ту же запись, что векторы:
363
+ `integer`, `natural`, `decimal`, `money`, `quantity`, `rational`, `text`,
364
+ `boolean`, `date`, `instant`, `entity`, `member` (в Go — `IntegerOf`,
365
+ `DecimalOf`, … с суффиксом `Of`: имена типов рантайма заняты). Decimal и Money —
366
+ из десятичной строки по лексике §208, без float; валюта обязательна. Значение
367
+ языка без конструктора переводится только по объявленному типу параметра
368
+ (строка в слоте сущности — `entity`, в слоте Text — `text`, целое в слоте
369
+ Integer — `integer`); вид, который по типу слота не читается однозначно, —
370
+ ошибка формы вызова. Литерал-терм с чужим типом проходит в `evaluate`, и там
371
+ сверка §189 даёт `TYPE_ERROR`, как у раннера и оракула.
372
+
373
+ **Ответ одной формы.** `Answer` у L0 и L1 и у всех трёх целей: `query_id`,
374
+ `result_kind` (`PROPOSITION` у вопроса истинности, `COLLECTION` у строк),
375
+ `truth_status`, `evaluation_status` (перечень §175), `issues` (коды),
376
+ `judgment_requests`, `rows` (литерал-термы). Python — frozen dataclass,
377
+ TypeScript — readonly размеченное объединение по `resultKind`, Go — структура.
378
+ `rows_*` идёт тем же входом (внутренний вид `rows`, которого протокол раннера не
379
+ знает) и отдаёт тот же конверт.
380
+
381
+ **Ошибки.** Ошибка формы вызова — `InputError`; названный отказ среза —
382
+ `Refused` (Python, TypeScript) и `*RefusedError` (Go, `errors.As`), паники на
383
+ границе нет; семантический `TYPE_ERROR` — issue и статус ответа. В Go ошибка
384
+ перевода аргумента запоминается в факте и возвращается вызовом `Ask*`.
385
+
386
+ **Функции.** Python: `fact_<вход>`, `negated_<вход>` (L1), `ask_<вопрос>(facts,
387
+ on, …, *, query_id=None)`, `rows_<вопрос>`, реестры `QUESTIONS`, `INPUTS`,
388
+ `NEGATED`. TypeScript: объекты `facts`, `negated`, `ask`, `rows`, реестры
389
+ `questions`, `inputs`, `negatedInputs`. Go: `Fact<Имя>`, `Negated<Имя>`,
390
+ `Ask<Имя>`, `Rows<Имя>`, реестры `Questions`, `Inputs`, `Negated`.
391
+
392
+ **Имена из StableId (0.3.0).** План даёт каждому вопросу и входу нейтральное
393
+ имя — локальную часть StableId: ASCII-имя как прежде, буквы других
394
+ письменностей сохраняются (NFC), прочие знаки сворачиваются в `_`. Эмиттер
395
+ делит имя на слова (по `_` и горбам camelCase с учётом регистра Unicode) и
396
+ печатает его по правилу языка: Python — snake_case, TypeScript — camelCase,
397
+ Go — PascalCase из одних букв и десятичных цифр (идентификатор Go не
398
+ принимает комбинирующих знаков). Совпадения ПОСЛЕ преобразования разводятся
399
+ суффиксом (`_2` у Python, `2` у TypeScript и Go) в порядке плана, то есть по
400
+ StableId. Реестры по строке и идентификатор вопроса по умолчанию
401
+ (`urn:query:api:<имя>`) держат нейтральное имя — оно одинаково у всех целей.
402
+ До 0.3.0 кириллическое имя превращалось в подчёркивания: у пакетов `ru-tk-*`
403
+ Go-API ломался на одинаковых `FactX`, а TypeScript — на одинаковых именах
404
+ таблиц модели; внутренний слаг с буквами вне ASCII теперь получает 8 знаков
405
+ SHA-256 локальной части, а повторное совпадение имени таблицы после префикса
406
+ пространства имён — названный отказ плана.
407
+
408
+ **Проверка.** `check_api.py` (`arxo-codegen-check-api`, шаг приёмки
409
+ `ci/verify.py`) печатает API-пакеты комплекта на целях с API и исполняет каждое
410
+ задание раннером и харнессом API (`targets/<цель>/apicheck.*`, у Go — второй
411
+ бинарь под тегом `apicheck`): положительное, с отрицательным фактом L1, с
412
+ литералом чужого типа (§189), с синтетическим ключом §45.1 при читающем и
413
+ независимом вопросе. Исходы обязаны совпасть у API и раннера каждой цели и у
414
+ целей между собой; затем пакет всех заданий с повтором первого исполняется
415
+ одним процессом последовательно и параллельно, а канарейка портит статус и
416
+ issues в `api_base.py` — расхождение обязано найтись. Харнессам запрещено
417
+ достраивать отсутствующие поля ответа. На коде 0.1.6 эта проверка даёт 55
418
+ расхождений (45 — литерал чужого типа, 10 — конфликт ключа).
419
+
420
+ **Терм** — одна из четырёх форм:
421
+
422
+ | форма | поля |
423
+ |---|---|
424
+ | ячейка строки | `{k: "var", row, col, t}`, где `t` — `money`, `dec`, `text`, `bool`, `date` или `enum` |
425
+ | литерал | `{k: "lit", t: "money", v, cur}`, `{k: "lit", t: "dec", v}`, `{k: "lit", t: "text", v}`, `{k: "lit", t: "date", v}`, `{k: "lit", t: "enum", v}` |
426
+ | бинарная операция | `{k: "bin", op, l, r}`, `op` — `add`, `sub`, `mul`, `div`, `mod` |
427
+ | вызов функции | `{k: "call", fn, args}`, `fn` — `round`, `min`, `max`, `abs`, `pow`, `div_round`; в плане 0.14 у `round` и `div_round` — `ak`, статические виды аргументов (DECISION-0259) |
428
+
429
+ **Таблицы видов §58 в срезе** (Integer, Decimal, Money; Rational и Quantity
430
+ вне среза). `add`/`sub`/`min`/`max` — только совпадающие виды. `mul` —
431
+ скаляр §49: Integer и Decimal масштабируют Money, два Money не
432
+ перемножаются. `div` — `Decimal/Decimal` и `Money/Money` одной валюты дают
433
+ Decimal, `Money/Decimal` — Money; `Integer/Integer` даёт Rational и
434
+ отвергается планом. `mod` и `pow` — только Integer, остаток евклидов,
435
+ показатель `0..4096`, результат в 64-битном диапазоне. `abs` сохраняет вид,
436
+ `round` над Integer — тождество (E-0110). Пара вне таблицы — отказ плана,
437
+ как TYPE_ERROR у оракула. Вид результата, зависящий от ЗНАЧЕНИЯ, — не
438
+ 10-гладкое частное, `INEXACT_DIVISION`, деление на ноль, предел `pow` —
439
+ решает рантайм названным отказом: оракул на тех же входах даёт issue и
440
+ `RUNTIME_ERROR`, а не ответ, поэтому отказ здесь честен, а ответ был бы
441
+ другим.
442
+
443
+ Числа во всех литералах — СТРОКИ: десятичная запись не проходит через
444
+ двоичную плавающую точку ни в плане, ни в порождённом коде.
445
+
446
+ ## Поражаемый слой L1 (DECISION-0237)
447
+
448
+ **Возможности.** План сначала определяет, что программе нужно сверх L0:
449
+ `defeasible` (сила `defeasible`/`defeater` §104), `negation` (отрицательная
450
+ голова, литерал тела или собственный факт §99/§113), `priority`
451
+ (`priority_rule` §106), `status:<вид>` (статусные тесты §65 `not_known`,
452
+ `unknown`, `refuted`, `monotone`, `supported`). Вызывающий может добавить
453
+ возможность сам: ворота просят `negation`, когда дело несёт отрицательный
454
+ факт. Пустое множество — прежний план `0.7`. Непустое — план `0.8` с полем
455
+ `capabilities` (отсортированный список). Дескриптор цели объявляет
456
+ `CAPABILITIES`; `deliver.emit_project` сверяет их ДО записи первого файла и
457
+ отказывает поставке названной причиной «цель rb не поддерживает возможности
458
+ плана: …» (код 2). Сейчас `0.8` печатают `ts`, `py`, `go`; остальные 13
459
+ целей его отвергают — проба ворот это проверяет.
460
+
461
+ **Поля плана 0.8 сверх 0.7.** У правила: `strength`; у атома — `accept`,
462
+ сырые статусы §62 строки, при которых генератор её перечисляет
463
+ (`established` — `TRUE_ONLY`, `monotone`/`supported` — `TRUE_ONLY`, `BOTH`);
464
+ `checks` — литералы, которые не перебираются, а проверяются над уже
465
+ связанными переменными: `{pred, accept, args}`, где `accept` уже учёл
466
+ полярность литерала (§63) и вид теста; у головы — `polarity`; `subst` —
467
+ значения всех переменных тела, идентичность применения. У факта —
468
+ `polarity`. Верхний уровень: `strata.strict` (списки имён правил по рангам
469
+ строгого слоя), `strata.defeasible` (по рангам §110), `priorities` (пары
470
+ «выше, ниже» транзитивного замыкания §106 по именам правил), `schedule` —
471
+ фазы §111: `strict`, `closure` (зарезервирована, `policies: []`), затем на
472
+ каждую поражаемую страту `candidates` (с `rules`), `defeat`, `surviving`,
473
+ `strict`, `status`. Эмиттер, встретив неизвестную фазу или `closure` с
474
+ политиками, отказывает.
475
+
476
+ **Что решает план (всё структурное).** Генераторы — положительные
477
+ `established`/`monotone` литералы тела по порядку; всё прочее — проверки,
478
+ и переменная, не связанная генератором, — отказ §190/§113 (отсутствие не
479
+ связывает). Ground-тело без генератора допускается. Ранги строгого слоя —
480
+ порт `strict.strict_ranks`, поражаемого — порт `defeasible.stratify` с
481
+ выравниванием голов обеих полярностей и дефитеров (E-0078/E-0080).
482
+ Названные отказы:
483
+
484
+ | причина | когда |
485
+ |---|---|
486
+ | `программа не стратифицируема (§110, NON_EXECUTABLE_UNSTRATIFIED)` | цикл предикатного графа с немонотонным ребром; ранги не сходятся |
487
+ | `рекурсия с порождением значений арифметикой (DECISION-0237 п. 11)` | голова с `bin`/`call` в той же компоненте, что чтение тела; действует и для L0 |
488
+ | `дефитер без обычного кандидата той же полярности (§110 п. 5, NON_EXECUTABLE_STRATUM_BARRIER)` | нет производителя либо его окна редакций §92.3 не покрывают окно дефитера |
489
+ | `цикл приоритетов (§106, PRIORITY_CONFLICT)` | цикл безусловных рёбер; отказ, а не совпадение с оракулом |
490
+ | `условный приоритет (§117/§118)` | `condition` не `true` либо `priorityKind` не `direct` |
491
+ | `датированный приоритет (§92.3)` | окно редакции приоритета не вся ось |
492
+ | `поздний производитель (§111, LDC-E4103)` | strict-правило читает статусно (не `monotone`) предикат с поражаемым производителем; strict `not_known`/`unknown`/`refuted` над строгой цепочкой к поражаемому выводу; поражаемое правило читает предикат (или делит голову), строгая цепочка которого завершается не ниже его страты |
493
+ | `статусный тест вне среза генератора: conflicted` | `conflicted`, `opposed` — оракул их не исполняет |
494
+ | `объявление вне среза генератора: closure_policy/None`, `…: interpretation*`, `…: reference/None` | замыкание §70, прочтения §156, динамическая ссылка §30.2; инертные объявления манифеста `inert-nodes.json` сюда не попадают |
495
+
496
+ **Модель (одинаково в `py`, `ts`, `go`).** Строки таблиц и параллельно —
497
+ их ключи; опоры — биты на ключ строки: strict/asserted `+`/`−` и
498
+ surviving-defeasible `+`/`−`. Статус §62 выводится при чтении из битов.
499
+ Strict-правило ставит бит своей полярности; поражаемое правило кладёт
500
+ кандидата в таблицу кандидатов страты с ключом «имя правила + ключ
501
+ подстановки». `defeat` — локальный helper над полным снимком кандидатов:
502
+ группа — `(предикат, ключ строки)`, кандидат побеждён strict-битом
503
+ комплемента (§107.1), противоположным обычным кандидатом с ребром
504
+ приоритета над ним (§107.2) или дефитером той же полярности, над которым у
505
+ кандидата ребра нет (§107.3); побеждённые не удаляются по ходу обхода.
506
+ Выжившим ставится surviving-бит. Страты печатаются циклами по `schedule`.
507
+ Контракт рантайма (восемь функций) и сертификация не менялись.
508
+
509
+ **Раннер и API.** L1-модель принимает отрицательные факты дела; вопрос
510
+ `truth` отвечает статусом §62 атома, отрицательный вопрос зеркалит стороны
511
+ §63. `collect` и `rows_*` перечисляют только `TRUE_ONLY`-строки. API
512
+ отдаёт статус буквально: у Python `(status, issues)`, у TypeScript
513
+ `Answer.status` типа `Status`, у Go `Answer.Status`; отрицательные входы —
514
+ `negated_*`, `negated.*`, `Negated<Имя>` и реестры `NEGATED`,
515
+ `negatedInputs`, `Negated`. Входы вопроса считаются по чтениям всех правил,
516
+ включая дефитеры и проверки; дефитер производителем вопроса не считается.
517
+ L0-модели получили тот же интерфейс раннера (`LAYER`, установленные строки,
518
+ статус строки, отказ на отрицательный факт) без смены поведения.
519
+
520
+ **Ворота.** `layer: L1` исполняется наравне с L0; голден без результата у
521
+ L1-вектора обязан быть отказом плана с той же причиной (`structural:
522
+ <код>` в манифесте: `NON_EXECUTABLE_UNSTRATIFIED`,
523
+ `NON_EXECUTABLE_STRATUM_BARRIER`, `LDC-E4101`). Пол `MIN_CHECKED_L1`
524
+ отдельный. Фикстуры `verify/ci/gates/backends/l1_fixtures.py`: семь
525
+ программ с ответом из прозы, каждая в перестановке правил и фактов и с
526
+ канарейкой — той же программой без решающей нормы, чей ответ обязан
527
+ отличаться (обе стороны выжили, strict против defeasible, дефитер
528
+ преодолён и не преодолён, один выживший среди побеждённых, умолчание §113,
529
+ монотонная рекурсия); три структурных контрпримера (default-цикл, поздний
530
+ производитель, дефитер без кандидата). API-шаг идёт и на L1-пакетах
531
+ `doctrine-ius-privatum`, `ru-zolotaya-rybka` с заданиями, где первый вход
532
+ подан отрицательным фактом.
533
+
534
+ ## Агрегаты §59 и замыкания §70 (DECISION-0239)
535
+
536
+ **Возможности и версия.** `aggregate` — агрегат в теле или голове правила,
537
+ `closure` — узел `closure_policy`. План с любой из них — `law-backend-plan/0.9`
538
+ (поля 0.8 плюс ниже); без них план остаётся 0.8 или 0.7 байтово (отпечатки
539
+ всех построенных до правки планов совпали). Печатают `ts`, `py`, `go`.
540
+
541
+ **Поля 0.9.** У правила `steps` — порядок тела, как его выбирает `solve`
542
+ оракула: `{gen: i}` открывает цикл по атому, `{check: i}` и `{guard: i}`
543
+ стоят там, где связаны их переменные; `fallible` — в гарде есть агрегат,
544
+ способный дать ошибку значения; `addends` — агрегаты `sum` монотонных гардов
545
+ для проверки знака слагаемых. Верхний уровень: `aggregates` — подпланы
546
+ `{name, op, params, atoms, joins, filters, checks, guards, steps, element,
547
+ distinct, empty}`, где `params` — виды внешних переменных, а терм
548
+ `{k: "param", i, t}` читает `i`-ю из них; терм `{k: "agg", fn, args, t}`
549
+ вызывает подплан. `closures` — `{name, id, pred, domain, window}`; окно по
550
+ дню §2.15 переведено в полуинтервал. Расписание: `strict`, затем на каждый
551
+ уровень E-0113 `closure` с `policies` и снова `strict`, затем страты L1.
552
+ У фазы `strict` может быть `waiting` — имена strict-правил, которые этот
553
+ проход не исполняет: их замыкания ещё не материализованы (errata E-0174).
554
+ Поле печатается только непустым, поэтому прочие планы 0.9 байтово прежние.
555
+ Эмиттер печатает для каждого набора `waiting` константу страт без этих
556
+ правил, а незнакомое поле фазы отвергает.
557
+
558
+ **Семантика.** Агрегаты `count`, `sum`, `min`, `max`, `only` над comprehension
559
+ §52.1: генератор перебирает установленные строки, `distinct` — по ключу
560
+ значения, `collect all` — со повторами; `count` даёт Integer, остальные —
561
+ вид элемента (Integer, Decimal, Money). `only` — функциональный lookup §51
562
+ (errata E-0256): значение, если различное значение ровно одно (различие — по
563
+ тому же ключу, что `distinct`, поэтому над `collect all` кратность
564
+ безразлична); пусто — `EMPTY_AGGREGATE`, два и более различных —
565
+ `CARDINALITY_CONFLICT`. Он всегда фоллибелен: в гарде — ошибка значения G3
566
+ ниже, в голове правила — поподстановочная ошибка головы (E-0265, W5; до
567
+ релиза 0.1.6 — отказ плана). Политики у `only` нет:
568
+ `options` — отказ плана (у оракула `TYPE_ERROR`). План с фоллибельным
569
+ агрегатом несёт `backchain`: ошибка агрегата правила-кандидата доезжает до
570
+ статуса вопроса `truth` по E-0105 (`NEITHER` + `RUNTIME_ERROR`). Перечисление — ребро `completion`
571
+ (+1 в рангах обоих слоёв). Замыкание ставит strict-негатив члену домена
572
+ (`TRUE_ONLY`), у которого нет strict/asserted positive-опоры; strict-негатив
573
+ по §107.1 побеждает поражаемого кандидата.
574
+
575
+ **Ожидание замыкания (E-0174).** Strict-правило, тест которого опровергается
576
+ негативом замыкания, ждёт материализации этого замыкания. Это `unknown(P)`
577
+ любой полярности, `not_known(not P)` и перечисление в генераторе агрегата.
578
+ План переносит `rules.closure_waits` оракула: зависимости чтения делятся
579
+ по §65, ожидание транзитивно через строгих производителей. Правило ждёт до
580
+ уровня своего старшего замыкания. Голый `not P`, `established` и `monotone`
581
+ отрицательного литерала не ждут: замыкание делает их тест только истинным.
582
+
583
+ **Ошибки значения (G3).** `sum`/`min`/`max` без `options.empty` над пустой
584
+ коллекцией в гарде: правило копит выводы и при ошибке не применяет ни одного,
585
+ модель печатает `EMPTY_AGGREGATE` один раз на правило; отрицательное
586
+ слагаемое монотонного гарда `sum` — `MONOTONE_GUARD_NEGATIVE_ADDEND` и
587
+ пропуск подстановки. Коды идут в `issues` раннера и API и сверяются с
588
+ голденом (`BACKEND_ISSUES`).
589
+
590
+ **Удержание issue монотонного гарда (errata E-0198) в срез НЕ переносится, и
591
+ переносить нечего.** Ядро с 20.09.2026 предъявляет ошибку проверки
592
+ МОНОТОННОГО АГРЕГАТНОГО ГАРДА §111 по состоянию НЕПОДВИЖНОЙ ТОЧКИ страты:
593
+ ошибка промежуточного прохода, не повторившаяся на последнем, в документ не
594
+ попадает. Удержание наблюдаемо ровно там, где коллекция гарда РАСТЁТ внутри
595
+ его страты, то есть где generator читает ВЫВОДИМЫЙ предикат, — а это и есть
596
+ отказ плана G2 из таблицы ниже. Замер 20.09.2026 живьём на целях `ts,py,go`:
597
+ пять векторов errata (`ERR-E0198-GUARD-EARLY-EMPTY-SILENT`, `…-SILENT-02`,
598
+ `…-GUARD-FIXPOINT-EMPTY-SPEAKS`, `…-BLOCKS-02`, `…-GUARD-LATE-ERROR-SPEAKS`)
599
+ отвергнуты планом с той же причиной, что и семейство `ERR-E0116-*`; шестой,
600
+ `ERR-E0198-EMPIRICAL-GUARD-UNCHANGED`, в срезе И СОВПАЛ — у него generator
601
+ читает одну эмпирику, коллекция заморожена входом дела, и errata его не
602
+ касается по собственной границе. Отказ закреплён дважды: структурной
603
+ фикстурой `monotone-guard-derived` и шестью строками манифеста. Поэтому
604
+ абзац G3 выше остаётся верным дословно: у среза «правило копит выводы и при
605
+ ошибке не применяет ни одного» относится к ЗАКРЫТОЙ коллекции, а растущая в
606
+ срез не входит. Появится акт, ради которого G2 откроют, — удержание придётся
607
+ переносить тем же изменением, и это условие входа названо здесь, а не в
608
+ календаре.
609
+
610
+ **Вид `empty`-политики §59 (errata E-0195) сверяется в обеих точках.** В
611
+ правиле — планом: вид политики обязан совпасть и с видом результата,
612
+ выведенным планом из терма элемента, и с ОБЪЯВЛЕННЫМ типом проекции
613
+ comprehension §52.1 (`variable.type.name` — тот самый носитель, с которым
614
+ сверяется оракул: он не зависит от числа решений generator-а). Расхождение
615
+ любого из двух — отказ плана, потому что у оракула тот же вход даёт
616
+ `TYPE_ERROR`, то есть ДРУГОЙ ответ, а не меньший. В вопросе `term` — раннером
617
+ каждой цели: политика не-`value` и политика чужого вида над ПУСТОЙ коллекцией
618
+ дают `TermError TYPE_ERROR` (ответ без значения, `evaluationStatus`
619
+ `TYPE_ERROR`, issue `TYPE_ERROR`), совпавший вид и непустая коллекция
620
+ отвечают как прежде. Текста issue порождённый модуль не печатает вовсе —
621
+ байтовый контракт среза здесь КОД, и он тот же, что у оракула. До правки
622
+ 20.09.2026 все три цели отвечали на коллекцию `Money` с политикой `Integer`
623
+ значением `Integer 0` и `COMPUTED`. Векторы `ERR-E0195-EMPTY-POLICY-*` этого
624
+ места не достают: их comprehension несёт СВЯЗКУ, и раннер отказывает раньше
625
+ агрегата, — поэтому норму держат три пробы ворот (`EMPTY_POLICY_PROBES` в
626
+ `check_backends.py`) на том же пакете и деле без связок: свидетель нормы и две
627
+ пробы против переусердствования.
628
+
629
+ **Отказы 0.9.**
630
+
631
+ | причина | когда |
632
+ |---|---|
633
+ | `агрегат вне среза генератора: average` | операция вне `count`/`sum`/`min`/`max`/`only` |
634
+ | `агрегат only с options вне среза генератора (§51, errata E-0256)` | у `only` задана политика; у оракула тот же вход — `TYPE_ERROR` |
635
+ | `вычисляемый элемент comprehension вне среза генератора (§52.1)` | элемент не переменная и не литерал |
636
+ | `монотонный агрегатный гард (E-0116) над выводимым предикатом` | гард, который оракул считает монотонным, читает выводимый предикат (G2); над одними входами — исполняется |
637
+ | `вид empty-политики агрегата не совпадает с видом элемента коллекции (§59, errata E-0195)` | вид политики разошёлся с видом результата плана либо с объявленным типом проекции comprehension; у оракула тот же вход — `TYPE_ERROR` |
638
+ | `агрегат с ошибкой значения над незавершённым входом` | фоллибельный гард strict-правила читает то, что в его страте ещё растёт |
639
+ | `замыкание §70 вне среза генератора (NON_EXECUTABLE_CLOSURE)` | предикат и домен не одной сигнатуры либо домен не `const_ref` |
640
+ | `замыкание §70 над поражаемым производителем` | у предиката или домена поражаемый либо поздний производитель (G5) |
641
+ | `программа не стратифицируема (§110, …)` | цикл через `completion` или вершину замыкания |
642
+
643
+ Фикстуры ворот: `count-over-derived`, `empty-sum-issue` (канарейка — те же
644
+ статусы без issue), `closure-negative`, `closure-strict-positive`,
645
+ `closure-unknown-reader` и `closure-transitive-default` (E-0174: ответ
646
+ NEITHER, канарейка без замыкания — TRUE_ONLY); структурные
647
+ `completion-cycle`, `monotone-guard-derived`, `closure-over-defeasible`.
648
+
649
+ ## Нормативный runtime — отказ до W6
650
+
651
+ Текущая семантика норм использует ранги E-0325/E-0328–E-0332/E-0339.
652
+ По выбору владельца она переносится в W6. План отвергает наличие шаблона
653
+ нормы или нормативной головы с `NORM_RANKS_UNAVAILABLE` для всех вопросов;
654
+ прямой `evaluate(..., {kind: "positions"})` — `POSITIONS_UNAVAILABLE` до
655
+ создания модели. Hand-built план с `norms` либо возможностью `norm`
656
+ отвергается и эмиттером. Прежней лестницы статусов и материализации эффектов
657
+ в трёх эмиттерах нет. Обычный truth о цели эффекта эту границу не обходит.
658
+
659
+ ## Календарные функции §84–§86 (DECISION-0245)
660
+
661
+ **Возможности и версия.** `calendar:days_between`, `calendar:period`
662
+ (`add_calendar_period`, `add_legal_term` календарных единиц),
663
+ `calendar:business` (`add_business_days`, `add_legal_term` дневных единиц).
664
+ План с любой из них — `law-backend-plan/0.11`; 0.7–0.10 байтово прежние
665
+ (отпечатки 1656 планов совпали). `days_between` безошибочна над слотами Date и
666
+ модели не меняет: пакет только с ней остаётся L0-моделью. Функции с ошибкой
667
+ значения требуют накопления выводов и печатаются L1-моделью 0.9. Печатают
668
+ `ts`, `py`, `go`; прочие цели отвергают план пробой версии.
669
+
670
+ **Термы.** `{k: "call", fn, args}`; счётчик — `{k: "qty", v, unit}`, только
671
+ литерал Quantity с единицей, допустимой функции (E-0029/E-0109), целый.
672
+ Не Date, чужая единица, дробный счётчик — отказ плана (у оракула всегда
673
+ TYPE_ERROR). `add_legal_term` §86.1 заменяется одной из двух операций по
674
+ единице. Места: гарды (`cal` у гарда), головы (`headFallible` у правила),
675
+ границы окон позиций L2 (`{k: "calbound", term}` либо `{k: "calbound",
676
+ error}`, когда граница не Date).
677
+
678
+ **Окружение.** `semanticVersion` CLIR, `deadlinePolicies` (узлы
679
+ `deadline_policy` по id), `calendars` — снимки `calendar_snapshot` с
680
+ датасетом (покрытие, недельный график, исключения) либо кодом ошибки загрузки:
681
+ байты ресурса сверяются с `datasetHash` и схемой `calendar-dataset` при
682
+ построении плана; у вектора байты — `calendar.resource.json` рядом. Раннер до
683
+ вывода разворачивает профили контекста §89.1, выбирает политику
684
+ (`context.deadlinePolicy`: у CLIR 0.1 объект, у 0.2 ссылка на узел) и снимок
685
+ (`options.calendarSnapshot`; два снимка без выбора — отказ
686
+ `AMBIGUOUS_CALENDAR`). Потребитель закрепляет встроенную политику сценария
687
+ узлом программы, как корпусный путь оракула (`kz.engine._pin_deadline_policies`).
688
+
689
+ **Ошибки.** Рантайм — порт `compute_deadline` и `Calendar` по порядку
690
+ проверок; коды `MISSING_POLICY`, `DEADLINE_POLICY_INVALID`,
691
+ `MISSING_CALENDAR`, `CALENDAR_OUT_OF_RANGE`, `CALENDAR_DATASET_INVALID`,
692
+ `CALENDAR_DATASET_HASH_MISMATCH` — issues модели. Гард: код ловится по
693
+ подстановкам, правило на проходе не срабатывает; два разных кода — отказ
694
+ рантайма. Голова: ошибка снимает только свою подстановку — issue по каждому
695
+ коду однажды, остальные подстановки применяются (errata E-0265, W5; до
696
+ релиза 0.1.6 ошибка у части подстановок была отказом рантайма). Граница окна:
697
+ issue и граница «нет». Фоллибельное правило обязано читать завершённое ниже
698
+ своей страты. Вопрос `truth` с ответом NEITHER получает статус E-0105:
699
+ раннер обходит `backchain` (правила с разыменованной головой и литеральными
700
+ конъюнктами) от цели, собирает коды ошибок правил-кандидатов и берёт
701
+ старший статус (`MISSING_INPUT`, `MISSING_POLICY`, …).
702
+
703
+ Фикстуры ворот: `business-days-holiday` (канарейка без исключения календаря),
704
+ `calendar-month-end` и `window-calendar-bound` (без политики),
705
+ `guard-out-of-range`, `days-between-l0`.
706
+
707
+ ## Rational §35/§58 (DECISION-0252)
708
+
709
+ **Возможность и версия.** `value:rational` получает программа, в которой
710
+ Rational может появиться: параметр отношения, литерал или константа Rational,
711
+ `Integer/Integer`, операция над Rational, а также значение-зависимое частное
712
+ `Decimal/Decimal` и `Money/Money`. Статический вид последнего виден только при
713
+ компиляции терма, поэтому `build_plan` строит план дважды: первый проход
714
+ отмечает встречу, второй строит `law-backend-plan/0.12` модели L1 0.9. Планы,
715
+ где Rational не возникает, байтово прежние. Печатают `ts`, `py`, `go`.
716
+
717
+ **Виды.** Статический вид терма — `Integer`, `Decimal`, `Money`, `Rational`
718
+ либо динамический `Decimal|Rational`. Пара, у которой все исходы у оракула
719
+ `TYPE_ERROR` (Rational с Decimal или Money, `Rational/Integer`, `mod` не над
720
+ целыми, `round` над Rational), — отказ плана; пара с динамическим операндом
721
+ получает вид своих успешных исходов. Сравнение Rational с иным видом — отказ.
722
+
723
+ **Поля 0.12.** У терма `bin` — `lk`, `rk`: статические виды операндов (`i`,
724
+ `d`, `m`, `r`, `n`), по ним рантайм `arith` решает пару §58. Литерал —
725
+ `{k: "lit", t: "rat", v: "n/d"}`. У гарда с возможной ошибкой значения — `err`
726
+ (механика `cal`), у правила — `headFallible` и `kindChecks: [[позиция, "d"|"r"]]`:
727
+ динамический вид в слоте Decimal либо Rational сверяется по значению, чужой
728
+ вид — issue `TYPE_ERROR` по ключу (правило, предикат, позиция, вид) и пропуск
729
+ подстановки (E-0077). `backchain` — у каждого плана 0.12 (статус E-0105).
730
+
731
+ **Ошибки.** `DIVISION_BY_ZERO`, `INEXACT_DIVISION`, `TYPE_ERROR` — issues модели
732
+ по фазам DECISION-0245 п. 6. Расхождение валют денег не фоллибельно, как и
733
+ раньше: `CURRENCY_MISMATCH` — названный отказ раннера. Правило с фоллибельным
734
+ термом, читающее незавершённое в своей страте, ошибку не ловит: момент ошибки
735
+ у оракула зависел бы от хода фикспойнта, и она становится названным отказом
736
+ раннера. Вопрос `term` исполняет раннер сам: ошибка значения — `RUNTIME_ERROR`
737
+ с issue у любого плана. Rational в деле и вопросе — только канонической
738
+ записью §168.1, иная — отказ раннера.
739
+
740
+ **Канарейки значений.** Язык фикстур L1 деления и Rational не знает, поэтому
741
+ решающие нормы плана 0.12/0.13 держит `VALUE_CANARIES` в `check_backends.py`:
742
+ у векторов `LAY-L0-RAT-*`/`LAY-L0-QTY-*` из плана снимается `kindChecks`,
743
+ `err`, `headFallible` либо статический вид операнда деления, и модуль цели `py`
744
+ обязан разойтись с голденом — иначе ворота падают: норма не видна.
745
+
746
+ ## Quantity §49 v1 и счётчик-связыватель §86.1 (DECISION-0256)
747
+
748
+ **Возможность и версия.** `value:quantity` получает программа, где Quantity —
749
+ значение первого класса: параметр отношения, литерал или константа вне
750
+ литерала календарного счётчика, операция над Quantity, счётчик-связыватель.
751
+ План — `law-backend-plan/0.13` модели L1 0.9; возможность независима от
752
+ `value:rational`, но `Quantity/Quantity` даёт вид `Decimal|Rational`, и такой
753
+ план несёт обе. Календарный план со счётчиком-литералом остаётся 0.11.
754
+ Сборка — тот же второй проход `build_plan` (`value_caps`).
755
+
756
+ **Виды и поля.** Статический вид `Quantity` без тега, код операнда `q`.
757
+ Литерал — `{k: "lit", t: "qty", v, unit}`. Пары §58 — порт `values.binary`:
758
+ пара, у которой все исходы у оракула `TYPE_ERROR` (`Q×Q`, Quantity с Money или
759
+ Rational, `Q/Integer`, `Decimal/Q`, `mod`, сравнение с иным видом), — отказ
760
+ плана. Счётчик-связыватель календарной функции — `{k: "qtyvar", fn, term}`:
761
+ рантайм `counter` проверяет вид, единицу функции и целочисленность; у
762
+ `add_legal_term` со связывателем план несёт `calendar:period` и
763
+ `calendar:business`, ветвь выбирает единица на деле.
764
+
765
+ **Ошибки.** Фоллибельны `Q±Q`, сравнение и `min`/`max` двух Quantity, деление:
766
+ `DIMENSION_MISMATCH` (разные теги), `TYPE_ERROR` (разные теги в частном — раньше
767
+ нуля), `DIVISION_BY_ZERO`, `INEXACT_DIVISION` — issues модели; статус E-0105 и
768
+ вопроса `term` у кодов несовместимости видов — `TYPE_ERROR`. Равенство при
769
+ сопоставлении фактов и ключах — «не равно» без issue. Расхождение валют денег
770
+ не фоллибельно (DECISION-0252) — асимметрия названа в DECISION-0256.
771
+
772
+ ## Политика округления §50: `round` и `div_round` (DECISION-0259)
773
+
774
+ **Возможности и версия.** `value:div_round` получает программа с любым
775
+ `div_round`; `value:round` — программа с `round`, у которого точность или режим
776
+ не литерал, режим не `HALF_UP` либо значение динамического вида
777
+ `Decimal|Rational`. Любая из них — `law-backend-plan/0.14` модели L1 0.9; сборка —
778
+ тот же второй проход `build_plan` (`value_caps`). Программа с одним литеральным
779
+ `round(…, n, "HALF_UP")` остаётся прежней версии байтово и печатает прежний
780
+ `round`.
781
+
782
+ **Виды и поля.** У вызова `round` и `div_round` в плане 0.14 — `ak`: статические
783
+ виды аргументов (`i`, `d`, `m`, `q`, `n`; `t` — Text режима). Рантайм не
784
+ угадывает вид по значению: `round` над Integer — тождество Integer (E-0110).
785
+ Отказ плана — там, где у оракула всегда `TYPE_ERROR`: `div_round` вне пар
786
+ `Money/Decimal`, `Quantity/Decimal`, `Decimal/Decimal`, `Money/Money`
787
+ (E-0036, E-0170), Rational в аргументе, точность не Integer и не Decimal, режим
788
+ не Text, литеральная точность дробная, отрицательная или у `div_round` больше
789
+ 34, литеральный режим вне семи. Динамический вид проверяет рантайм.
790
+
791
+ **Ошибки.** `TYPE_ERROR`, `CURRENCY_MISMATCH`, `DIVISION_BY_ZERO` — issues модели
792
+ по фазам DECISION-0245 п. 6, в порядке проверок оракула. `round` фоллибелен при
793
+ нелитеральной политике или динамическом виде, `div_round` — всегда, кроме
794
+ ненулевого литерального делителя при литеральной политике и паре не
795
+ `Money/Money`. Расхождение валют у `div_round` — issue, у оператора `/` —
796
+ прежний отказ (DECISION-0252 п. 4); асимметрия названа в DECISION-0259.
797
+ Решающие нормы плана держат канарейки `VALUE_CANARIES` над `LAY-L0-RND-*`:
798
+ `err`, `headFallible`, литеральный режим, литеральная точность, `ak`.
799
+
800
+ ## Boolean, Natural, границы месяца и `_` (DECISION-0266–0268)
801
+
802
+ **Возможности и версия.** `value:boolean`, `value:natural`
803
+ (DECISION-0266), `calendar:month_bound` (DECISION-0267), `wildcard`
804
+ (DECISION-0268). Любая из них — `law-backend-plan/0.15`; модель не меняется,
805
+ и эмиттер узнаёт её по полям, как у 0.11 (`schedule` — L1, `aggregates` —
806
+ 0.9). Планы без этих возможностей байтово прежние (отпечатки 1825 из 1869
807
+ путей; остальные — новые планы либо смена первой причины отказа).
808
+
809
+ **Boolean.** Литерал `{k: "lit", t: "bool", v}`, ячейка вида `bool`, в
810
+ деле — только JSON-булево (иное — названный отказ раннера). Равенство по
811
+ значению, порядок не определён: гард с `lt`…`ge` над Boolean или Boolean
812
+ против иного вида — отказ плана (у оракула всегда `TYPE_ERROR`,
813
+ `LAY-L0-BOOL-ORDER`); арифметика — отказ плана. Ключ строки `b:true` отличен
814
+ от Integer 1 и Text `"true"`. Вопрос `term`: сравнение только `eq`/`ne`.
815
+
816
+ **Natural.** Ячейка вида `dec`, тип-строка `urn:law:std#Natural` в `params`.
817
+ Арифметика и статические виды считают Natural как Integer (`_num`), вид
818
+ результата — Integer, как у оракула (`Typed integer`). Сверка §189 тип-строк
819
+ без нормализации: Integer в Natural-слоте дела — `TYPE_ERROR` и утверждение
820
+ не принято (`LAY-L0-NATURAL-SLOT-INTEGER`); Integer-терм в Natural-слот
821
+ головы и Natural-ячейка в Integer-слот — отказ плана (E-0077,
822
+ `LAY-L0-NATURAL-INTO-INTEGER-SLOT`); переменная Natural-ячейки в Natural-слот
823
+ сохраняет запись (`LAY-L0-NATURAL-HEAD-COPY`). Отрицательное значение в деле
824
+ — названный отказ раннера.
825
+
826
+ **Границы месяца §84.2.** `{k: "call", fn: "month_start" | "month_end",
827
+ args: [Date]}` — чистая функция над Date без политики §86 и снимка §85
828
+ (`values.month_bound_value`), не фоллибельна и накопления 0.9 не требует; не
829
+ Date статически или арность не 1 — отказ плана. В вопросе `term` арность и
830
+ вид — `TermError TYPE_ERROR` (`ERR-E0084-MONTH-BOUND-ARITY`, `-KIND`).
831
+
832
+ **`_` §98.** У атома `wild` (столбцы `_`) и `keep`: по столбцу `_` нет ни
833
+ фильтра, ни соединения, а на уровне цикла атома эмиттер пропускает строку с
834
+ уже виденной проекцией на `keep` — строки, различающиеся только в `_`, дают
835
+ одну подстановку (`solver.solve`, E-0165 MANY-ONE-APPLICATION). Множество
836
+ создаётся заново на каждой подстановке внешних атомов; полностью связанный
837
+ литерал с `_` в теле 0.9 остаётся генератором на своём шаге. `_` под
838
+ отрицанием, в статусном тесте не из `GENERATING` и в голове — отказ плана.
839
+
840
+ ## Датированные приоритеты §92.3 и подготовленный контекст (DECISION-0269)
841
+
842
+ **Возможность и версия.** `priority:dated` получает план, где узел
843
+ `priority_rule` датирован якорем на редакцию; версия —
844
+ `law-backend-plan/0.16` модели L1. Планы без датированных рёбер байтово
845
+ прежние.
846
+
847
+ **Замыкание с окнами.** У оракула проекция §92.3 снимает неприменимые узлы
848
+ приоритета до замыкания (`editions.project_editions`,
849
+ `defeasible._priority_closure`), поэтому окно ребра участвует в замыкании:
850
+ ось права делится границами окон датированных рёбер на отрезки, на каждом
851
+ строится замыкание Уоршелла по активным рёбрам, пара «выше, ниже» получает
852
+ объединение отрезков, где достижима (`_priority_windows`). Пары на всей оси
853
+ — `priorities`, как прежде; остальные — `datedPriorities: [выше, ниже,
854
+ окна]`; у норм-правил L2 — `normPriorities` и `datedNormPriorities`. Цикл на
855
+ отрезке — отказ плана с названным отрезком; прерывистое окно исходного ребра
856
+ (две и более редакции с разрывом) — отказ. Окно ребра с окнами правил-сторон
857
+ не пересекается: путь замыкания через снятое проекцией правило держится по
858
+ id, как у оракула (`LAY-L1-PRIORITY-DATED-PATH-EXCLUDED-RULE`).
859
+
860
+ **Подготовленный контекст.** Модуль печатает `prepare()`: по `legalTime`
861
+ дела один раз на исполнение строит активное множество рёбер `PRIO_ACTIVE`
862
+ (и `NORM_PRIO_ACTIVE`) из недатированных пар и датированных с окном
863
+ `[start, end)`, покрывающим дату; `defeat` и эффекты полномочий читают его.
864
+ `saturate()` начинается с `prepare()`, так что раннер и API готовят контекст
865
+ сами. Это первый шаг рычага Astra (аналог `prepared.program_view` оракула):
866
+ ключ контекста сегодня — дата права, дальше — выбор прочтений и значения
867
+ параметров A3.
868
+
869
+ ## Условный терм §56 и вычисляемый аргумент атома тела (DECISION-0270)
870
+
871
+ **Возможности и версия.** `term:if` (условный терм) и `term:body`
872
+ (вычисляемый аргумент атома тела: `binary`, `call`, `if`). Любая — план
873
+ `law-backend-plan/0.17` модели L1 0.9 с механикой ошибок значения; планы
874
+ без них байтово прежние.
875
+
876
+ **`if`.** `{k: "if", c, t, e}`, условие — `bool`, `cmp`, `and`, `or`
877
+ (`values.is_data_condition`); статусный тест в условии — отказ плана. Ветви
878
+ одного статического вида, иначе отказ. Эмиттер печатает ленивую ветвь
879
+ (тернарный оператор, условное выражение Python, замыкание Go).
880
+
881
+ **Вычисляемый аргумент.** Фильтр атома с `pre`: значение считается на уровне
882
+ атома ДО открытия его цикла (`solver.solve`), тип обязан совпасть с типом
883
+ слота статически, как у константы тела; при `err` — try/catch с кодом по
884
+ подстановке и issue правила после прохода, как у гарда. Генератор с таким
885
+ аргументом берётся, лишь когда переменные аргумента связаны (pick);
886
+ полностью связанный литерал — проверка статуса по ключу с тем же
887
+ перехватом. Внутри comprehension агрегата ошибка аргумента — отказ плана.
888
+ Обход E-0105 в раннере не заземляет конъюнкт с вычисляемым аргументом.
889
+
890
+ ## Функции §46, параметры A3, `let` и `match` §55.1 (DECISION-0273)
891
+
892
+ **Возможности и версия.** `function` (вызов функции пакета в терме
893
+ правила), `param:a3` (параметр-датасет A3 в вычисляемом терме), `term:let`,
894
+ `term:match`. Любая — план `law-backend-plan/0.18` модели L1 0.9 с
895
+ механикой ошибок значения; пакет, чьи функции читает только вопрос `term`
896
+ (T071), получает план прежней модели версии 0.18 с полем `functions`.
897
+ Планы без всего этого байтово прежние. `build_plan` добирает возможности до
898
+ неподвижной точки: параметр или функция внутри вычисляемого аргумента тела
899
+ видны только проходу с `term:body`.
900
+
901
+ **Функции.** `functions: [{name, id, params, paramTypes, returns,
902
+ returnType, body, fallible?}]`, тело — терм над `{k: "fvar", i}`, вызов —
903
+ `{k: "fcall", fn, args}`. Эмиттер печатает `f_<name>` по позиционным
904
+ параметрам и таблицу `FUNCTIONS` для раннера (вопросы `term`/`collect`:
905
+ арность и виды аргументов — TYPE_ERROR оракула, вид результата —
906
+ объявленный). Статически: арность, типы аргументов против параметров, тип
907
+ тела против объявленного, рекурсия — отказ плана (у оракула — TYPE_ERROR
908
+ и NON_EXECUTABLE_FUNCTION_RECURSION при исполнении). Ошибка значения в
909
+ теле — фоллибельность вызова, коды по подстановкам как у 0252; виды
910
+ операндов в теле печатаются всегда.
911
+
912
+ **Параметры A3.** `a3Params: [{id, type, windows: [[start, end, term]]}]`
913
+ — план разбирает тело датасета (`a3.lower_parameter`) структурно: цепочка
914
+ `if` по окнам дат, вызов функции строки, `Result/Ok [литерал]`, хвост
915
+ `MissingParameterValue`; иная форма, ось Instant, тип строки не равный
916
+ типу значения — отказ. Терм `{k: "a3", id}`; `prepare()` (подготовленный
917
+ контекст 0269) выбирает окно по legalTime один раз на дело, разрыв —
918
+ `TermError("MISSING_PARAMETER_VALUE")` по подстановке, issue правила,
919
+ вывода нет; обход E-0105 код пропускает — NEITHER/COMPUTED, как у оракула
920
+ (молчание права). Параметр читается вызовом над `const_ref`
921
+ (`round(MRP, 0, "HALF_UP")`, как в корпусе), в гарде и в аргументе атома
922
+ (`pre`/`err`). Голый `const_ref` параметра в голове или атоме — у оракула
923
+ позиция идентичности с отложенным вычислением при чтении связанной
924
+ переменной; в срезе это названный отказ плана (29 пакетов корпуса).
925
+ Параметр в вопросе `term` — отказ раннера.
926
+
927
+ **`let` и `match`.** `{k: "let", i, v, b}` с переменной `{k: "lvar", i}`
928
+ печатается немедленно вызываемой функцией; тип значения обязан совпасть с
929
+ объявленным. `{k: "match", s, arms: [{m, v}], x?: false}` — только над
930
+ перечислением, образцы — вариант без полей (идентичность члена, E-0067) и
931
+ `_`; ветви одного вида; печать — цепочка `eq` по порядку ветвей, хвост —
932
+ `TermError("NON_EXHAUSTIVE_MATCH")` (статус NON_EXECUTABLE); неполный
933
+ `match` фоллибелен, полный по членам — нет. Образцы с полями, `bind`,
934
+ `value` — отказ. Таблица статусов раннера дополнена кодами оракула
935
+ (NON_EXHAUSTIVE_MATCH, NON_EXECUTABLE_FUNCTION_RECURSION, UNKNOWN_FUNCTION,
936
+ OPAQUE_NON_EXECUTABLE → NON_EXECUTABLE, RESOURCE_LIMIT).
937
+
938
+ ## Канал суждения §47.3 (DECISION-0274)
939
+
940
+ **Возможность и версия.** `judgment` — программа объявляет отношение
941
+ суждения (`symbol_decl` relationKind judgment, `external_decl` канала с
942
+ органом и формой запроса). План `law-backend-plan/0.19`; модель не меняется
943
+ (L0 остаётся L0): план несёт `judgments` (орган, `requestSchema`),
944
+ `backchain` с силой правил и гардами (у L0 — впервые), `backchainConsts`.
945
+
946
+ **Обход — в рантайме цели**, один код для раннера и публичного API: порт
947
+ `whynot._collect_judgments` и соседей над готовым состоянием модели. От
948
+ цели без опоры — правила с унифицируемой головой, конъюнкты размечаются под
949
+ подстановкой цели (пара опор §62; несвязанная переменная — UNDETERMINED);
950
+ правило с заведомо невыполненным конъюнктом мертво (E-0101, кроме
951
+ refuted-теста над самим отношением при NEITHER), поражаемое правило, чей
952
+ вывод поразил бы сработавший дефитер без приоритета над ним, мертво
953
+ (E-0103 — по применениям страт `APPLIED`), дефитер стоящего вывода жив,
954
+ лишь если способен его снять (E-0100 — опоры §107.1 битами); неопределённый
955
+ судейский конъюнкт — запрос, прочий — вглубь по паре (атом, полярность)
956
+ (E-0111); судейский конъюнкт с несвязанными переменными перечисляется по
957
+ активному домену (аргументы всех атомов таблиц в порядке §208, кап 8 ДО
958
+ фильтра по NEITHER, DECISION-0036 (б)). Носители кроме тела: `valid_when`
959
+ полномочия при осуществлении (E-0100), кроме эффекта, запертого иммунитетом
960
+ (E-0120, карта `BLOCKED_IMMUNITY` из материализации — она же больше не
961
+ выпускает `POWER_INVALID_EXERCISE` при судье), `condition` цели обязанности
962
+ в вопросе `positions`. Гард в обходе — мини-вычислитель рантайма над
963
+ литералами §53 (переменные, литералы, константы §54, арифметика §58); иной
964
+ терм — UNDETERMINED (правило живо, как у оракула при исключении). Лестница
965
+ §175: конфликт эффектов, затем судья, затем ошибка терма E-0105.
966
+
967
+ **Ответ судьи §47.3.1.** Утверждение с объектом `judgment` принимается при
968
+ совпадении органа с декларацией и формы payload с `requestSchema`
969
+ (виды `_TYPE_KINDS`); иначе issue `JUDGMENT_AUTHORITY_MISMATCH` /
970
+ `JUDGMENT_PAYLOAD_MISMATCH` и никакой опоры. Ворота сверяют
971
+ `judgmentRequests` с голденом кроме `proof`; `Answer` L1-API несёт
972
+ `evaluationStatus` и `judgmentRequests`.
973
+
974
+ ## Редакции и мир импортов (DECISION-0236)
975
+
976
+ Жизненный цикл редакции §31 не доходит до эмиттеров: план считает
977
+ применимость редакции во всех точках излома её событий (порт
978
+ `applicability.edition_state`) и сворачивает в окна `[start, end)`; окно
979
+ правила — пересечение `effective` с объединением окон датирующих редакций
980
+ (достаточно одной применимой). Якорь, не разрешающийся в датированную
981
+ редакцию, и граница не дневной гранулярности узел не датируют; пустое окно —
982
+ узел в `excluded`; несколько окон — названный отказ. Датированный факт —
983
+ правило с окном над таблицей `always` с одной строкой.
984
+
985
+ Мир импортов: `build_plan(ir, world)`, где `world` — CLIR зависимостей
986
+ (замыкание). `world_index(ROOT)` индексирует снимки по имени пакета,
987
+ `resolve_world(ir, index, extra)` строит замыкание; `deliver.py --dep FILE`
988
+ подаёт зависимость явно, остальное ищется по снимкам; у вектора мир —
989
+ `producer.ir.json` рядом. Узлы с занятым StableId и тем же `contentHash`
990
+ пропускаются (копии фактов у потребителя); имя таблицы при коллизии
991
+ локальных имён получает префикс пространства имён. Вопросы API — только у
992
+ корня.
993
+
994
+ ## Виды вопросов раннера
995
+
996
+ Вопрос приходит с делом, и план его не видит: вид вопроса — свойство раннера.
997
+ Дескриптор цели объявляет `QUERY_KINDS`; ворота исполняют вектор только
998
+ тогда, когда его вид поддержан ВСЕМИ активными целями. `ts`, `py`, `go`
999
+ исполняют `truth`, `term` и `collect` (DECISION-0234): comprehension §52.1 по
1000
+ установленным атомам таблиц с гардами, элемент — ячейка или литерал,
1001
+ `distinct` — множество по каноническим байтам, порядок элементов —
1002
+ канонические байты §208; агрегаты `count`, `sum`, `min`, `max` §59, пустая
1003
+ коллекция без `options.empty` — названный отказ `EMPTY_AGGREGATE`; `only` §51
1004
+ отвечает сам: пусто — `TermError EMPTY_AGGREGATE`, два различных значения
1005
+ (канонические байты) — `TermError CARDINALITY_CONFLICT`, оба со статусом
1006
+ `RUNTIME_ERROR`, политика у `only` — `TermError TYPE_ERROR`; политика
1007
+ не-`value` либо чужого вида над пустой коллекцией — `TermError TYPE_ERROR`
1008
+ (errata E-0195, вид элемента — из `variable.type.name` comprehension);
1009
+ арифметика §58 и функции рантайма над результатом. Вид результата раннер
1010
+ выводит статически теми же таблицами §58, что план: у оракула вид — часть
1011
+ значения. Запись, поданная делом и сущностью, и Text, — названный отказ:
1012
+ рантайм их не различает. Ответ `term` — `resultKind: DATA` с литерал-термом
1013
+ §53, `collect` — `COLLECTION` со списком; ворота сравнивают значение с
1014
+ голденом оракульским каноном (`lawref.canon`), а не сериализатором цели.
1015
+
1016
+ С DECISION-0252 и DECISION-0256 арифметику вопроса `term` раннер исполняет
1017
+ рантаймом `arith` над точными видами значений, а не таблицей видов: ошибка
1018
+ значения — ответ без значения со статусом по таблице `queries.query_calc`
1019
+ (`TYPE_ERROR` для `TYPE_ERROR`, `CURRENCY_MISMATCH`, `DIMENSION_MISMATCH`,
1020
+ `DATE_INSTANT_MISMATCH`, иначе `RUNTIME_ERROR`). Сравнение `lt`…`ne` в вопросе —
1021
+ `values.binary`: валюта, единица, затем вид (`TYPE_ERROR`); числовые виды и
1022
+ Date, ответ — `Boolean`. Литерал Rational операнда вычисляется `ratTerm`
1023
+ (сократимая запись нормализуется, ERR-E0005); записи дела и вопроса `truth`
1024
+ — только канонические, `rat`. С DECISION-0259 `round` и `div_round` в вопросе
1025
+ исполняют `roundMode` и `divRound` с точными видами аргументов: ошибка политики
1026
+ — ответ без значения со статусом той же таблицы (`CURRENCY_MISMATCH` —
1027
+ `TYPE_ERROR`, `DIVISION_BY_ZERO` — `RUNTIME_ERROR`); пара вне E-0036/E-0170,
1028
+ Rational в аргументе, точность не числом и режим не Text — названный отказ.
1029
+
1030
+ ## Контракт рантайма
1031
+
1032
+ Операции ниже ведут себя одинаково во всех целях; всё остальное печатает
1033
+ эмиттер. Состав контракта цели объявлен в `runtime_cert.py`: 19 базовых
1034
+ операций (`BASELINE_OPERATIONS`) обязательны для каждой цели и достаточны
1035
+ для плана 0.7; ещё 13 (`EXTENSION_OPERATIONS`: `arith`, `counter`,
1036
+ `divRound`, `monthStart`, `monthEnd`, `qty`, `rat`, `ratTerm`, `roundMode`,
1037
+ `instant`, `hoursBetween`, `minutesBetween`, `addDuration`) — операции видов
1038
+ значений и функций поздних планов: Rational, Quantity, округление §50,
1039
+ границы месяца, Instant. Полный набор сегодня несут `ts`, `py`, `go`; новый
1040
+ целевой язык начинает с базового. Каждый громкий отказ ниже — значение одного
1041
+ названного типа `Refused` (класс исключения в Python и TypeScript, тип
1042
+ `panic` в Go); раннер узнаёт только его. Любая другая ошибка — крах.
1043
+
1044
+ | функция | обязательство |
1045
+ |---|---|
1046
+ | `dec(строка)` | точное десятичное значение произвольной длины (канонической записи плана; сырое поле литерала читают `lit*` — см. «Сырое поле литерала §208») |
1047
+ | `money(строка, валюта)` | то же плюс валюта |
1048
+ | `add`, `sub`, `min`, `max` | точно, масштаб — больший из двух; виды и валюты обязаны совпасть, иначе громкий отказ |
1049
+ | `mul(a, b)` | точное произведение; масштаб результата — сумма масштабов; два Money — громкий отказ |
1050
+ | `div(a, b)` | точное частное при 10-гладком знаменателе, наименьший точный масштаб; иначе громкий отказ (Money — `INEXACT_DIVISION`); ноль — громкий отказ |
1051
+ | `mod(a, b)` | евклидов остаток над целыми; ноль и нецелые — громкий отказ |
1052
+ | `abs`, `pow` | вид сохраняется; `pow` только над целыми с пределами показателя и значения |
1053
+ | `round(значение, разрядов, режим)` | только названный режим; `HALF_UP` округляет ОТ НУЛЯ; над Integer — тождество |
1054
+ | `cmp(a, op, b)` | порядок; разные валюты и сравнение денег с безразмерным — громкий отказ |
1055
+ | `eq(a, b)` | равенство значений, а не записей: `2469` и `2469.00` равны |
1056
+ | `key(строка)` | канонический ключ строки таблицы для дедупликации; виды различимы: сущность, дата, член перечисления, деньги, число |
1057
+ | `date(строка)` | календарный день §2.15 в канонической записи ISO; порядок и равенство — по дню, с другими видами не сравнивается (§57) |
1058
+ | `sym(идентификатор)` | член перечисления §38 или ссылка без объявления: равенство по идентичности, порядок не определён (§57) |
1059
+ | Boolean в `eq`, `cmp`, `key` | DECISION-0266: значение языка (`boolean`); `eq` — только Boolean того же значения; `cmp` — `eq`/`ne`, иначе громкий отказ «§57: вид Boolean не упорядочен»; ключ `b:true`/`b:false`, отличный от числа и текста; в арифметике — громкий отказ видов |
1060
+ | `month_start(дата)`, `month_end(дата)` | §84.2 (DECISION-0267): первое и последнее число месяца в канонической записи, високосность по григорианскому правилу; не Date, не ISO-дата, несуществующий день — `TermError TYPE_ERROR`; политика §86 и снимок §85 не читаются |
1061
+ | `setLegalTime` / `legalTime` | правовое время дела; без него окна §31 не вычисляются |
1062
+ | `rat(строка)` | Rational §35 (DECISION-0252): только каноническая запись `n/d` §168.1 — несократимая, знаменатель положителен, ноль `0/1`; части — по лексике §208 (errata E-0203); иная запись — громкий отказ, а не сокращение; целые произвольной длины |
1063
+ | `arith(op, a, b, lk, rk)` | план 0.12: `add`, `sub`, `mul`, `div`, `mod` по таблице §58 `values.binary` над статическими видами операндов `i` Integer, `d` Decimal, `m` Money, `r` Rational, `n` Decimal либо Rational по классу значения; вид, расходящийся со значением, — громкий отказ. `Integer/Integer` и `Rational/Rational` — Rational (целое частное остаётся Rational); `Decimal/Decimal`, `Money/Money` одной валюты — Decimal при 10-гладком частном, иначе Rational; `Money*Rational` в обоих порядках — Money, 10-гладкость после сокращения; ошибка значения — `TermError` с кодом оракула: `DIVISION_BY_ZERO`, `INEXACT_DIVISION`, `CURRENCY_MISMATCH`, `TYPE_ERROR` (Rational с Decimal или Money, `Rational/Integer`, `mod` не над целыми) |
1064
+ | `ratTerm(строка)` | литерал Rational операнда вопроса `term` — разбор оракула: `n/d` с целыми частями по лексике §208 (errata E-0203: `[+-]?[0-9]+`, без пробела, `_` и не-ASCII цифры), сократимая запись и знак в знаменателе нормализуются; не `n/d` — `TermError TYPE_ERROR`, знаменатель 0 — `DIVISION_BY_ZERO` (ERR-E0005; каноничность §168.1 — у записей дела и вопроса `truth`, там `rat`) |
1065
+ | `qty(строка, единица)` | Quantity §49 v1 (DECISION-0256): точное десятичное значение и тег единицы — байты записи, без реестра единиц и алиасов; отличен от Money |
1066
+ | `arith` над `q` | вид `q` — Quantity: `Q±Q`, сравнение, `min`/`max` разных тегов — `TermError DIMENSION_MISMATCH`, Quantity с иным видом — `TYPE_ERROR`; `Integer`/`Decimal` × Q — Q того же тега; `Q×Q`, Q с Money или Rational — `TYPE_ERROR`; `Q/Decimal` — Q, неточно `INEXACT_DIVISION`, ноль `DIVISION_BY_ZERO`; `Q/Q` одного тега — Decimal либо Rational по значению, разных тегов — `TYPE_ERROR` раньше нуля; `Q/Integer`, `Decimal/Q`, `mod` — `TYPE_ERROR` |
1067
+ | Quantity в `add`, `sub`, `min`, `max`, `abs`, `round`, `cmp`, `eq`, `key` | тег сохраняется; разные теги в `add`/`sub`/`min`/`max`/`cmp` — `DIMENSION_MISMATCH`, Quantity с иным видом — `TYPE_ERROR`, в `eq` — ложь; ключ `q:<тег>:<число>` с экранированием тега, отличный от денег с тем же тегом |
1068
+ | `counter(величина, функция)` | счётчик-связыватель §86.1: Quantity с единицей, допустимой функции (`add_business_days` — дневные, `add_calendar_period` — календарные, `add_legal_term` — все пять), целый; иначе `TermError TYPE_ERROR`; возвращает число и единицу; знак не проверяет (отрицательный — `DEADLINE_POLICY_INVALID` операции) |
1069
+ | `add_legal_term(дата, число, единица)` | §86.1: `business_day`/`calendar_day` — `add_business_days`, прочие — `add_calendar_period` |
1070
+ | `roundMode(значение, точность, режим, vk, pk)` | план 0.14 (DECISION-0259): `round` §50 с проверкой политики в порядке оракула — вид значения (`n` с Rational — `TermError TYPE_ERROR`), точность Integer либо целый Decimal (`pk`; дробная или отрицательная — `TYPE_ERROR`), режим из семи (иначе `TYPE_ERROR`); округление — порт `_round_fraction` на целых; Integer — тождество; точность не короче дробной части — значение то же, без дополнения нулями |
1071
+ | `divRound(делимое, делитель, точность, режим, ak, bk, pk)` | план 0.14: пары `m/d`, `q/d`, `d/d`, `m/m`, иначе `TYPE_ERROR` → у `m/m` разные валюты `CURRENCY_MISMATCH` → ноль `DIVISION_BY_ZERO` → точность целая `0..34`, иначе `TYPE_ERROR` → режим; частное точное, округление всегда, масштаб результата — точность; `m/d` — Money той же валюты, `q/d` — Quantity той же единицы, `m/m` — Decimal без тега |
1072
+ | Rational в `add`, `sub`, `min`, `max`, `abs`, `cmp`, `eq`, `key` | с Rational — по значению; Rational с иным видом в `add`/`sub`/`min`/`max`/`cmp` — `TermError TYPE_ERROR`, в `eq` — ложь; ключ `r:n/d`, отличный от числа и денег: `2/1` и Decimal `2` — разные ключи |
1073
+ | `instant(строка)` | Instant §2.15 (W2, часть 2): запись хранится как подана и служит идентичностью атома (E-0214); `eq` и `cmp` — по моменту (E-0048): `12:00:00+05:00` и `07:00:00Z` равны; ASCII-григорианская дата и обязательное корректное смещение; дробь любой длины сравнивается точно; невалидная запись — `TYPE_ERROR`, с Date не сравнивается (§57) |
1074
+ | `hoursBetween`, `minutesBetween`, `addDuration` | §84.1: полных часов или минут со знаком, дробь к нулю; `addDuration(мгновение, число, единица)` — секунды, минуты, часы или сутки, ответ канонично в UTC без дробных секунд; запись без смещения отвергается admission |
1075
+
1076
+ ### Сырое поле литерала — грамматика §208, а не средства языка цели
1077
+
1078
+ Errata E-0197, E-0202 и E-0203 (20.09.2026) закрыли у обоих оценщиков один и
1079
+ тот же дефект: сырое поле `value` литерала §53 читалось конструктором РОДНОГО
1080
+ языка, и «так делает мой язык» становилось нормой. У целей генератора дефект
1081
+ был тот же и разошёлся сильнее, потому что языков три, а не два.
1082
+
1083
+ **Правило одно.** Поле литерала читается по грамматике §208, названной явно, а
1084
+ не тем, что примет `int()`, `Decimal()`, `Number()`, `parseFloat`, `strconv`
1085
+ или `math/big`. Носитель поля проверяется ПЕРВЫМ (`rawLit` / `raw_lit` /
1086
+ `rawOfMessage`, `rawOfValue`), и только потом — запись:
1087
+
1088
+ | вид | канон §208 | иная форма |
1089
+ |---|---|---|
1090
+ | `Text`, `Date`, `Instant`, `Rational`, `currency`, `unit` | JSON-строка | `TermError TYPE_ERROR` |
1091
+ | `Integer` | целое JSON-число знакового 64-битного диапазона | `TermError TYPE_ERROR` |
1092
+ | `Decimal`, `Money`, `Quantity` | JSON-строка по лексике ниже либо целое JSON-число | `TermError TYPE_ERROR` |
1093
+ | `Boolean` | JSON-булево | громкий отказ (DECISION-0266) |
1094
+ | `Natural` | неотрицательное целое JSON-число | громкий отказ (DECISION-0266) |
1095
+
1096
+ **Две лексики — дословный порт `lawref/values.py`** (`_DECIMAL_LEXIS`,
1097
+ `_FRACTION_PART_LEXIS`) и `law-eval/src/values/literal.rs`:
1098
+
1099
+ ```
1100
+ десятичное: ^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$
1101
+ часть дроби: ^[+-]?[0-9]+$
1102
+ ```
1103
+
1104
+ Форма УЗКАЯ по решению errata E-0203, и это не придирка к алфавиту: сужены
1105
+ ровно пробельный символ, `_`, не-ASCII цифра и `Infinity`/`NaN` — четыре оси,
1106
+ у которых радиус по всему дереву нулевой. **Экспонента (`1e2` → `100`),
1107
+ ведущие и хвостовые нули (`1.50` → `1.5`), знак `+`, `.5`, `5.` и сократимая
1108
+ дробь (`2/4` → `1/2`) остаются законной записью входа и нормализуются.**
1109
+ Ужесточать сверх этого нельзя: `ERR-E0203-DECIMAL-EXPONENT`,
1110
+ `-DECIMAL-CANON-FORMS` и `-RATIONAL-REDUCIBLE` стоят в векторах именно
1111
+ границей, без которой «запись судится» не отличалось бы от «нормализация
1112
+ отменена».
1113
+
1114
+ В Python якорь обязан быть `\Z`, а не `$`: Python-овый `$` пропускает
1115
+ хвостовой перевод строки, и `"1.5\n"` прошёл бы. В JavaScript без флага `m` и
1116
+ в RE2 без `(?m)` `$` уже строг.
1117
+
1118
+ **Граница i64 у TypeScript.** `JSON.parse` сводит число к `double`, и целое
1119
+ знакового 64-битного диапазона теряет знаки НА ЧТЕНИИ: на
1120
+ `ERR-E0202-CANON-NUMBER-I64-MAX` в ответ уходило `9223372036854776000` — не
1121
+ меньший ответ, а другой, то есть ровно то, что DECISION-0188 запрещает. Отказа
1122
+ здесь достаточно не было: `py` и `go` это целое несут точно, а отказ одной цели
1123
+ при ответе двух других ворота читают расхождением. Радиус перехода мерился и
1124
+ оказался мал: `Dec.u` в `runtime.ts` УЖЕ `bigint`, и печать результата уже идёт
1125
+ носителем `{int: "<цифры>"}`. Поэтому чинится только чтение — `RT.parseJson`
1126
+ разбирает текст, пропуская строки отдельной ветвью, и заменяет целое вне
1127
+ ±2^53 тем же носителем `{int: "<цифры>"}`, который читает и харнесс API.
1128
+ `Number.MAX_SAFE_INTEGER` нигде не становится нормой: границей нормы остаётся
1129
+ i64 §208, а 2^53 — лишь порог, за которым нужен точный носитель.
1130
+
1131
+ **JSON-число вне канона §208** (float либо целое вне i64) оракул и движок
1132
+ отвергают ОТКАЗОМ СОБРАТЬ ДОКУМЕНТ — error-объектом §172, а не issue. У
1133
+ порождённого модуля такого канала нет: профиль `core-executable` документа не
1134
+ строит. Поэтому тот же вход даёт названный отказ раннера одним текстом у всех
1135
+ целей: `JSON-число вне канона §208 (только целое знакового 64-битного
1136
+ диапазона) вне среза раннера`.
1137
+
1138
+ **Текст issue модуль не печатает, и контракт здесь — КОД.** В ответе раннера
1139
+ любого вида (раздел «Контракт раннера») `issues` — список кодов; ворота
1140
+ сверяют коды (`BACKEND_ISSUES`), а не формулировки. Дословные
1141
+ сообщения оракула (`term: Integer ожидает целое JSON-число, получено {"a":1}`)
1142
+ стоят в векторах и в errata, а у модуля тот же случай — код `TYPE_ERROR`.
1143
+
1144
+ **Долг, названный числами.** (1) Литералы ПРОГРАММЫ (`ir.json`) читает
1145
+ `plan.py`, и он остался permissive: 570 литералов `Integer` в векторах
1146
+ записаны JSON-строкой (`"11"`), тогда как оракул после E-0197 даёт на такой
1147
+ записи `TYPE_ERROR`. Сегодня это ничего не ломает — ни одно из этих правил в
1148
+ своём векторе не вычисляется, — но норма у плана и у раннера разная, и это
1149
+ записано, а не забыто. (2) Сокращение дроби в `plan.py` (ветвь `RAT`)
1150
+ существует потому, что канон ядра дробь НЕ сокращает, а рантайм цели требует
1151
+ канона §168.1; владелец решил 20.09.2026, что канон ядра сокращать начнёт —
1152
+ отдельной errata. **После той errata костыль подлежит снятию тем же
1153
+ изменением**, и до неё он не трогается.
1154
+
1155
+ ### Сертификация рантайма
1156
+
1157
+ Контракт выше исполняем: `runtime-cert.json` — фикстура вызовов и ожиданий,
1158
+ записанных ИЗ этого раздела, а не снятых с реализации. Сравнение целей между
1159
+ собой общую ошибку не находит: коллизия ключа `["a|e:b","c"]` /
1160
+ `["a","b|e:c"]` жила во всех трёх рантаймах одинаково и теряла второй факт.
1161
+ Харнесс каждой цели (`targets/<цель>/cert.*`) исполняет фикстуру и печатает
1162
+ результат, ожиданий он не читает; ворота сверяют каждый случай с ожиданием
1163
+ до исполнения векторов. Отображение в фикстуре — с явным масштабом
1164
+ (`2.00 KZT`, не `2 KZT`), поэтому она держит и масштаб результата, а не
1165
+ только его значение. Литералы: `{dec}`, `{money, cur}`, `{text}`, `{date}`,
1166
+ `{sym}`, `{row}` — у всех харнессов; `{rat}`, `{qty, unit}`, `{bool}`,
1167
+ `{instant}` — только у харнессов полного контракта (`COMMON_LITERAL_TAGS` и
1168
+ `EXTENSION_LITERAL_TAGS` в `runtime_cert.py`); ожидание — строка, булево, `null`,
1169
+ `{refused: текст}` либо `{termError: код}` (ошибка значения плана 0.12,
1170
+ DECISION-0252); Rational отображается `n/d`. Новый случай
1171
+ контракта — строка фикстуры, новая цель — харнесс на 60–120 строк.
1172
+
1173
+ Ключ дедупликации экранирует разделитель и экран внутри текста (`|` → `\|`,
1174
+ `\` → `\\`): длина в префиксе была бы разной у языков (байты, code points,
1175
+ UTF-16), экранирование — нет.
1176
+
1177
+ ## Контракт раннера
1178
+
1179
+ Раннер принимает `case.json` и `query.json` и передаёт их единому входу
1180
+ `evaluate` (у `ts`, `py`, `go`; раздел «Единый вход и библиотека»); у прочих
1181
+ целей раннер сам подаёт факты дела в таблицы, насыщает и отвечает. Три исхода
1182
+ различаются протоколом выхода, а не одним ненулевым кодом:
1183
+
1184
+ | исход | код | stdout |
1185
+ |---|---|---|
1186
+ | ответ | 0 | JSON по виду вопроса, таблица ниже |
1187
+ | названный отказ среза | 3 | `{"refused": текст}` |
1188
+ | крах | любой другой | что угодно; ворота валят |
1189
+
1190
+ Форма ответа зависит от вида вопроса (`QUERY_KINDS` дескриптора). `value` —
1191
+ литерал-терм §53, `issues` — список кодов:
1192
+
1193
+ | вид | ответ |
1194
+ |---|---|
1195
+ | `truth` у `ts`, `py`, `go` | `{queryId, truthStatus, evaluationStatus, judgmentRequests, issues}` |
1196
+ | `truth` у остальных целей | `{queryId, truthStatus, evaluationStatus, issues}` |
1197
+ | `term` | `{queryId, resultKind: "DATA", value, evaluationStatus, issues}`; при ошибке значения `value` нет, `evaluationStatus` — `TYPE_ERROR` либо `RUNTIME_ERROR` |
1198
+ | `collect` | `{queryId, resultKind: "COLLECTION", value, evaluationStatus, issues}` |
1199
+ | `positions` | именованный отказ `POSITIONS_UNAVAILABLE` до W6 |
1200
+
1201
+ Текст отказа обязан совпадать между целями побайтово; отказ одной цели при
1202
+ ответе другой — расхождение. Отказы, зависящие от данных дела, закономерно
1203
+ возникают при исполнении, но их перечень общий; основные:
1204
+
1205
+ | текст | когда |
1206
+ |---|---|
1207
+ | `аргумент вне среза раннера: <kind>` | аргумент дела не `value`, не `entity_ref` и не `const_ref` — например `record` (конструктор события) или `interval` |
1208
+ | `аргумент вне среза раннера: <URN типа>` | значение типа, которого раннер не знает |
1209
+ | `§113: отрицательный факт вне L0-среза` | утверждение дела с `polarity` ≠ `positive` у модели L0; модель L1 принимает `negative` |
1210
+ | `вид запроса вне среза раннера: <kind>` | вид вопроса вне `QUERY_KINDS` цели: у `ts`, `py`, `go` — не из `truth`, `term`, `collect`; у остальных целей — любой, кроме `truth` |
1211
+ | `POSITIONS_UNAVAILABLE` | `positions` временно снят в R до переноса рангов norm runtime в W6; отказ одинаков у библиотеки и раннера трёх целей |
1212
+ | `ошибка значения вне среза раннера: <код>` | ошибка значения вне вопроса `term` и вне правил модели, например в гарде comprehension вопроса |
1213
+ | отказы рантайма из таблицы выше | валюты, режим округления, порядок на Text, Date с другим видом, порядок над перечислением, `legalTime` |
1214
+
1215
+ **Типы аргументов дела** сверяются с `params` таблицы до преобразования
1216
+ значений — зеркало `check_argument_types` оракула (§189/§48, errata E-0046):
1217
+ несогласованное по арности или типу утверждение не принимается, в `issues`
1218
+ ответа встаёт `TYPE_ERROR`; `entity_ref` типа не несёт и не проверяется;
1219
+ предикат без сигнатуры принимается и никем не читается. До этой проверки на
1220
+ `ERR-E0046-CASE-ARG-TYPE` Python и Go отвечали `TRUE_ONLY` против голдена
1221
+ `NEITHER`, а ворота этого не видели: TypeScript отказывал на Integer первым,
1222
+ и вектор шёл в «отказ рантайма».
1223
+
1224
+ **Коды `issues`** сверяются между целями целиком, а с голденом — по кодам,
1225
+ которые срез порождает сам: набор `BACKEND_ISSUES` ворот, 18 кодов —
1226
+ `TYPE_ERROR`, `KEY_CONFLICT` и коды ошибок значения §58, агрегатов §59,
1227
+ календаря §85/§86, эффектов полномочий §127–§128 и ответа судьи §47.3.1.
1228
+ Без сверки с голденом `KEY_CONFLICT` был бы невидим воротам: он не меняет
1229
+ статус, и модуль, молча не считающий ключи, совпадал бы с голденом по
1230
+ статусу и с соседями по пустому списку. Коды вне набора с голденом не
1231
+ сверяются, только между целями.
1232
+
1233
+ **Аргументы запроса** сверяются с той же сигнатурой, но без `TYPE_ERROR`:
1234
+ ключ ground-атома у оракула (§208) включает тип значения, поэтому запрос с
1235
+ `Integer 1` к утверждению `Decimal 1` атома не находит и отвечает `NEITHER`
1236
+ без issue, а разная запись одного Decimal (`0.5` и `0.50`) — один атом.
1237
+ Рантайм сравнивает числа по величине и без проверки отвечал бы `TRUE_ONLY`.
1238
+ Векторы — `LAY-L0-QUERY-ARG-TYPE` и `LAY-L0-QUERY-ARG-SCALE`; несогласованный
1239
+ по арности или типу запрос просто не совпадает ни с одной строкой.
1240
+
1241
+ **Типы головы правила** проверяются планом статически (E-0077): тип слота
1242
+ тела, литерала, константы или результата `mul`/`round` обязан совпасть с
1243
+ объявленным типом параметра головы; сущности не проверяются, как у оракула.
1244
+ Оракул отвергает такой вывод при исполнении, рантайм среза положил бы Integer
1245
+ в Decimal-слот и ответил по величине. Вектора среза на это нет: исходник с
1246
+ такой головой отвергает уже `lawc check` (LDC-E2104), а обязательство
1247
+ оракула держат `ERR-E0077-*` над Quantity, которые план отвергает раньше.
1248
+
1249
+ ## Запуск
1250
+
1251
+ ```sh
1252
+ python3 verify/ci/gates/backends/check_backends.py # шаг 73/73 полного конвейера
1253
+ python3 verify/ci/gates/backends/check_backends.py --require ts,py,go # без тулчейна — отказ
1254
+ python3 verify/ci/gates/backends/check_backends.py --keep /tmp/backends # оставить проекты
1255
+ python3 verify/ci/gates/backends/check_backends.py --write-manifest # закрепить исходы
1256
+ python3 engines/backends/targets/go/emit.py <ir.json> model.go # одна цель вручную
1257
+ ```
1258
+
1259
+ Цель Python исполняется всегда: без неё ворота сообщали бы «зелено», не
1260
+ исполнив ни одного бэкенда. TypeScript требует `node` и `tsc` с типами Node:
1261
+ собственный проект тулчейна `targets/ts/package.json` (`npm ci` там), запасной
1262
+ путь — `apps/constructor/node_modules`. Go требует `go` в `PATH`. Отсутствие
1263
+ тулчейна НАЗЫВАЕТСЯ в отчёте, а не молчит; `--require` делает его отказом.
1264
+
1265
+ Ворота идут шагом рабочего профиля (`run_all.sh` → `work_profile.py`) на
1266
+ каждом прогоне с обязательными `ts,py,go` (`py` при `--python-only`): они
1267
+ читают только conformance-векторы, корпус им не нужен, и DECISION-0184 это
1268
+ не нарушает. Workflow профиля ставит node, `npm ci` и go явно. В полном
1269
+ конвейере — шаг 73/73.
1270
+
1271
+ Ворота исполняют каждый вектор ВСЕМИ доступными целями и различают три
1272
+ исхода по протоколу раннера. Крах любой цели валит ворота. Исход каждого
1273
+ L0-вектора закреплён в `verify/ci/data/backends-vectors.json`: `match`,
1274
+ `plan: <причина>`, `query: <вид>`, `golden: результатов <n>`,
1275
+ `runtime: <текст отказа>`. Закреплённый исход обязан повториться, закреплённый
1276
+ вектор — существовать, незакреплённое совпадение — ошибка регистрации.
1277
+ Одно число исполненных векторов позволило бы потерять старый случай и
1278
+ компенсировать его новым, поэтому пол `MIN_CHECKED` остался второй защитой.
1279
+
1280
+ ## Поставка порождённого модуля
1281
+
1282
+ ```sh
1283
+ ./law dev backend corpus/clir/kz-grammar-tasymal.lawir.json --target ts --target go --out DIR
1284
+ ```
1285
+
1286
+ `deliver.py` печатает пакет в `DIR/<цель>/`: `model.*`, `runtime.*`,
1287
+ `runner.*`, у `ts`, `py`, `go` ещё `api.*`, `evaluate.*` и общую часть API (у Go
1288
+ ещё `go.mod`), `plan.json` и паспорт `law-backend.json`
1289
+ формата `law.backend-delivery/1`: версия плана, цель, паспорт пакета из
1290
+ CLIR, семантический хэш §209 самого CLIR (в снимках поле `semanticHash`
1291
+ бывает нулевым, поэтому считается здесь), профиль и SHA-256 каждого файла.
1292
+ Паспорт связывает модуль с каноном. Отказ плана — отказ поставки с
1293
+ названной причиной и кодом 2: «меньше пакета» печатать нельзя. Ворота
1294
+ печатают проекты той же функцией `emit_project`, так что проверяется ровно
1295
+ то, что уезжает. Устанавливаемый пакет — следующий раздел.
1296
+
1297
+ ## Устанавливаемые пакеты (DECISION-0423 §2.7)
1298
+
1299
+ ```sh
1300
+ arxo-codegen-package root.lawir.json --target py --name NAME --version 1.0.0 --out DIR
1301
+ arxo-codegen-package root.lawir.json --target ts --name @scope/name --version 1.0.0 --out DIR
1302
+ arxo-codegen-package root.lawir.json --target go --name NAME --version 1.0.0 \
1303
+ --module example.com/org/name --out DIR
1304
+ ```
1305
+
1306
+ `package.py` печатает библиотеку тем же эмиттером, что и поставка, и меняет
1307
+ только раскладку и импорты:
1308
+
1309
+ | цель | раскладка |
1310
+ |---|---|
1311
+ | Python | `pyproject.toml`, `src/<импорт>/__init__.py` (реэкспорт `api` и `info()`), `api.py`, `_internal/` (рантайм, модель, `evaluate`, общая часть API — относительные импорты), `py.typed`, `api-map.json` данными пакета |
1312
+ | TypeScript | `package.json` (`exports`, `types`, `files`, `engines`), `src/index.ts`, `src/api.ts`, `src/internal/`, собранный `dist/` с `.d.ts` — собственным тулчейном цели; Node не нужен рантайму (порядок UTF-8 без `Buffer`) |
1313
+ | Go | `go.mod` с путём `--module`, код в корневом `package <имя>` (приватность — регистром имён), `EvaluateJSON`, `Info()`, команда `cmd/<имя>-eval` с протоколом раннера |
1314
+
1315
+ В каждом пакете — `README.md` с исполняемым примером (факты по входам первого
1316
+ вопроса и сам вопрос), `api-map.json` формата `law.codegen-api-map/0.1`
1317
+ (функция → вопрос или вход → StableId → производящие правила → якоря
1318
+ источника) и паспорт `law-backend.json`: поля поставки плюс `distribution`
1319
+ (имя, версия, у Go — модуль и пакет), `generator` (версия arxo-codegen),
1320
+ `coreKit` (SHA-256 манифеста комплекта ядра), `capabilities` плана и `api`
1321
+ (`abi: law.codegen-api/1`, `valueFormat: law.codegen-value/1`) — провенанс
1322
+ артефакта, а не доказательство вычисления; тот же текст вшит в пакет и
1323
+ отдаётся `info()` / `Info()`.
1324
+
1325
+ **Значения между канонами.** `to_value`/`from_value` (Python),
1326
+ `toValue`/`fromValue` (TypeScript), `ToValue`/`FromValue` (Go): текст
1327
+ `law.codegen-value/1` — канонический JSON литерал-терма §53 (ключи по
1328
+ порядку, без пробелов, Integer — числом, без экранирования HTML). Один терм
1329
+ даёт одни байты во всех трёх языках, поэтому значение из модуля одного канона
1330
+ передаётся в модуль другого или в сервис без знания языка. Раннер и план ворот в пакет не входят. Минимальные
1331
+ версии тулчейна — константы целей `TOOLCHAIN_MIN` (Python 3.13, Node 20,
1332
+ Go 1.25), равные версиям проверок CI; тест сверяет их с
1333
+ `.github/workflows/candidate.yml`. Имя пакета — параметр; для канонов,
1334
+ которые публикует Arxo, — семья `-lib`: `@arxo/canon-<имя>-lib`,
1335
+ `arxo-canon-<имя>-lib`, `github.com/arxohq/canon-<имя>-lib` (выбор владельца
1336
+ 28.09.2026); `@arxo/canon-<имя>` без суффикса — пакет самого канона
1337
+ (DECISION-0349 §8).
1338
+
1339
+ **Проверка.** `check_packages.py` (`arxo-codegen-check-packages`, шаг приёмки
1340
+ `ci/verify.py`): для API-пакетов комплекта печатает пакет каждой цели и ставит
1341
+ его так, как получит потребитель, — Python каталогом из `src/`, TypeScript
1342
+ тарболом `npm pack` без сети, Go модулем-потребителем с `replace`; задания
1343
+ `check_api` исполняются через опубликованную поверхность пакета и обязаны
1344
+ совпасть с плоским API Python; пример README обязан исполниться; канарейка
1345
+ портит статус и issues в установленном Python-пакете — расхождение обязано
1346
+ найтись.
1347
+
1348
+ ## Обзор корпуса и первый потребитель
1349
+
1350
+ `./law dev backend-survey --report FILE` проходит все снимки CLIR (с
1351
+ миром импортов по снимкам; третий обзор — `SURVEY-2026-09-14-editions.ru.md`,
1352
+ план 0.7: 778 снимков, строится у 60; четвёртый —
1353
+ `SURVEY-2026-09-14-l1.ru.md`, план 0.8: строится у 106 — L0 те же 60, L1
1354
+ 46, из них `kz.*` 26; пятый — `SURVEY-2026-09-14-closure-aggregates.ru.md`,
1355
+ план 0.9: 780 снимков, строится у 123, из них с `aggregate` 10, с `closure` 9;
1356
+ шестой — `SURVEY-2026-09-15-norms.ru.md`, план 0.10: 802 снимка, строится у
1357
+ 151, с `norm` 24, из них `kz.*` 12)
1358
+ (`corpus/clir/` и собственные снимки `packs/`) обзорным режимом плана,
1359
+ который собирает ВСЕ отказы пакета, а не первый. Отчёт 09.09.2026 —
1360
+ `SURVEY-2026-09-09.ru.md`, второй (план 0.5, 14.09.2026) —
1361
+ `SURVEY-2026-09-14.ru.md`: 771 снимок, план строится у 50 против 31, из
1362
+ 19 новых пакетов 8 — словари `vocab.*` с ключами, остальные — грамматика,
1363
+ фикх и три акта КЗ с константами в теле. Первый же обзор дал рычаг: узел `publication`
1364
+ (провенанс §27.3, из семантического хэша исключён по §28) отвергал 621
1365
+ пакет; после его пропуска план строится у 31 пакета вместо 5. Два следующих
1366
+ рычага обзора оказались не тем, чем читались: «константа не найдена»
1367
+ (193 пакета) почти целиком была членами перечислений и параметрами A3, а
1368
+ 131 «несовпадение типа головы» — ложным срабатыванием статической
1369
+ типизации, приписывавшей Money любой операции. После плана 0.3 (Date,
1370
+ константы, перечисления) принятых по-прежнему 31: у пакетов с датами и
1371
+ перечислениями всегда есть вторая причина. Дальше по пакетам:
1372
+ `defeasible`/`defeater` (367), объявления вне среза (451: `norm_template`,
1373
+ `priority_rule`, `closure_policy`, `external_decl`), типы (362: `Quantity`,
1374
+ `Event`, `Rational`, `Instant`), функции (179) — всё это семантика.
1375
+
1376
+ `./law dev backend-consumer <пакет>` поставляет пакет корпуса, понижает его
1377
+ собственные `.lawtest` компилятором и спрашивает поставленный модуль.
1378
+ Наборы берутся обнаружением ворот `.lawtest` (`corpus/lawtests/discovery.py`):
1379
+ у каждой части свой мир (`world`, `concat`) — он же мир плана — и
1380
+ resolve-контекст §23 для понижения; часть с `merge = "concat"` или снимками
1381
+ §47.2 — названный пропуск. Сценарии понижаются через `./law engine
1382
+ lower-test --imports`; файл, который не понижается, — ошибка; если дело несёт
1383
+ отрицательный факт, план строится с возможностью `negation`. Без имён
1384
+ пакетов потребитель сверяет все пакеты манифеста
1385
+ `verify/ci/data/backends-consumers.json` (число сценариев, исходы, статусы;
1386
+ пол `MIN_CONSUMER_MATCHED`), `--write-manifest` закрепляет названные. Замер
1387
+ печатается и не закрепляется: страты, правила, пики кандидатов и групп
1388
+ (`STATS` модели `py`, третий аргумент раннера) и время по целям. 14.09.2026
1389
+ (DECISION-0237 этап 6): `kz.corpus.stazh_gossluzhby`, `kz.grammar.bas_arip`,
1390
+ `kz.grammar.kirme_sozder` — 144 из 144 на `ts,py,go`; без дефитеров модуль
1391
+ меняет 7 ответов, без поражаемых правил — 32. 15.09.2026 (DECISION-0242):
1392
+ вопрос `positions` сверяется по `result_kind`, `normative_status`,
1393
+ `evaluation_status` и наблюдениям `position`, `position_status`,
1394
+ `position_count`, `positions_present` (порт `lawref.lawtest`); `concat`
1395
+ наборов входит в мир плана. 19 пакетов с позициями — 825 совпадений из 841
1396
+ сценария, без норм-правил модуль меняет 79 ответов, без эффектов — 16; пол
1397
+ `MIN_CONSUMER_MATCHED` 336 → 1161.
1398
+ 09.09.2026: 24 принятых пакета со сценариями (грамматика казахского,
1399
+ `myco.primety`, `stacks.sets`, разрешения mathlib/isabelle), 300 сценариев,
1400
+ 298 совпали, 2 пропущены как `why_not`, расхождений и крахов нет. Это и
1401
+ есть первый потребитель: правила грамматики с сущностями и Text.
1402
+
1403
+ ## Замер 09.09.2026 (план 0.2, протокол исходов)
1404
+
1405
+ | исход | штук |
1406
+ |---|---|
1407
+ | L0-векторов рассмотрено | 340 |
1408
+ | отказ плана | 100 |
1409
+ | запрос не `truth` | 189 |
1410
+ | голден без результата (фатальная диагностика) | 12 |
1411
+ | исполнено всеми тремя целями | 39 |
1412
+ | совпало с голденом и между целями | 36 |
1413
+ | единогласный отказ рантайма | 3 |
1414
+ | крах, расхождение между целями, расхождение с голденом | 0 |
1415
+ | сертификация рантайма | 111 случаев × 3 цели |
1416
+
1417
+ Против первого замера (25 / 19 / 6): два вектора ушли из «отказа рантайма»
1418
+ в совпадение (`ERR-E0041`, `ERR-E0046-CASE-ARG-TYPE` — Integer в TypeScript
1419
+ и проверка типов дела), два — в отказ плана (`T151`, `T155` — параметры
1420
+ Rational и Instant), один — из совпадения в отказ плана по ключам §45.1
1421
+ (`LAY-L0-KEYS`), один крах Go на `const_ref` стал названным отказом; два
1422
+ новых вектора запроса (`LAY-L0-QUERY-ARG-*`) совпали после проверки типов
1423
+ запроса. Шаг ворот целиком — 12 секунд, включая сертификацию, `tsc` и
1424
+ 24 сборки `go build`.
1425
+
1426
+ Замер 14.09.2026 (план 0.5, цели `ts,py,go`): рассмотрено 385, отказ плана
1427
+ 127, запрос не `truth` 201, голден без результата 14, исполнено 43, совпало
1428
+ 40, отказ рантайма 3, расхождений и крахов 0. Из отказа плана в совпадение
1429
+ ушли `LAY-L0-KEYS` (`KEY_CONFLICT` сверен с голденом) и
1430
+ `ERR-E0052-DECIMAL-CANON` (константа в теле); пять `ERR-E0165-WILDCARD-*`
1431
+ сменили причину на названный отказ по `_` §98, шесть векторов с ключами —
1432
+ на следующую причину (календарные функции, отрицательный литерал).
1433
+ Канарейки: сдвиг значения фильтра в порождённом коде даёт `NEITHER`, эмиттер
1434
+ с `key_conflicts` ≡ 0 валит ворота расхождением issues на `LAY-L0-KEYS`.
1435
+ Десять целей разом не прогонялись (тяжёлый прогон); семь остальных
1436
+ эмиттеров и раннеров проверены только пробой версии плана.
1437
+
1438
+ Замер 14.09.2026, вечер (план 0.6, DECISION-0234, цели `ts,py,go`):
1439
+ рассмотрено 385, план построен и вид поддержан 240, совпало 49, отказ
1440
+ рантайма 191 (единицы, границы, календарь, Rational — названные отказы),
1441
+ расхождений и крахов 0. Девять новых совпадений — `term` и `collect`.
1442
+ Поставка `kz.grammar.tasymal`: `api.ts` под `tsc --strict`, `api.go` под
1443
+ `go build`, `api.py` на сценарии `KZ-TAS-01` — `TRUE_ONLY` с фактом,
1444
+ `NEITHER` без, снова `TRUE_ONLY`: состояние между вызовами не протекает.
1445
+
1446
+ Замер 14.09.2026, ночь (план 0.8, DECISION-0237, цели `ts,py,go`, шаг
1447
+ ворот 2 мин 36 с): L0 — рассмотрено 385, совпало 56 (было 51, все прежние
1448
+ целы; в совпадение ушли `ERR-E0018-MONOTONE-EVAL`, `T171`, `T002`,
1449
+ `LAY-L0-NEGATIVE-STRICT-HEAD`, `…-BOTH`), отказ плана 119, отказ рантайма
1450
+ 191; L1 — рассмотрено 44, совпало 24, структурных отказов 3
1451
+ (`LAY-L1-STATUS-CYCLE`, `T009`, `ERR-E0099-UNLESS-HEAD-UNBOUND`), отказ
1452
+ плана 17 (агрегаты в гардах 7, прочтения 4, приоритеты 3, квантор,
1453
+ Rational, поздний производитель); расхождений и крахов 0. API — 48 вопросов
1454
+ на 5 пакетах; 18 заданий на двух L1-пакетах дополнительно сверены с
1455
+ оракулом вне ворот — 0 расхождений. `MIN_CHECKED` 51 → 56,
1456
+ `MIN_CHECKED_L1` = 27, манифест 429 исходов.
1457
+
1458
+ Замер 14.09.2026, ночь (план 0.9, DECISION-0239, цели `ts,py,go`): L0 —
1459
+ совпало 66 (+10 к 56: closure — `LAY-L0-CLOSURE`, `…-BINARY`,
1460
+ `…-EFFECTIVE-IN/OUT`, `ERR-E0097`, `ERR-E0113-CLOSURE-TWO-LEVELS`; агрегаты —
1461
+ `LAY-L0-AGGREGATE-DERIVED-STRICT`, `LAY-L0-BODY-AGGREGATE`, `T164`,
1462
+ `ERR-E0112-COMPLETION-HEAD-AGGREGATE`), L1 — 31 совпадение и 5 структурных
1463
+ отказов (+7 `T199`–`T230`, остальное — новые векторы E-0172/E-0173 соседней
1464
+ работы), расхождений и крахов 0; полы 66 и 36, манифест 439.
1465
+
1466
+ Замер 15.09.2026 (план 0.10, DECISION-0242, цели `ts,py,go`, шаг ворот
1467
+ около 3 мин): L0 — совпало 73, L1 — 31 и 5 структурных отказов, L2 —
1468
+ совпало 11 (`LAY-L2-DUTY`, `…-MAINTENANCE-SHORT-WINDOW`,
1469
+ `…-MAINTENANCE-OUT-OF-DOMAIN`, `…-CONFLICT-TWO-LIBERTIES-NONE`,
1470
+ `…-NORM-BODY-AGGREGATE`, `T014`–`T017`, `T032`, `T146`), отказ плана 101, единогласный
1471
+ отказ рантайма 1 (`LAY-L2-CONFLICT-EXPLICIT-DUTIES`, `incompatible` в
1472
+ деле), L3 — совпало 9 (`LAY-L3-LIBERTY-*`, `LAY-L3-RECURSION-CLOSURE`,
1473
+ `T005`, `T018`, `T019`, `T031`, `EVT-NO-PROCEDURE-L1-CONSERVATIVITY`);
1474
+ расхождений и крахов 0. `T182` сменил причину отказа на конфликт позиций
1475
+ по ключу §45.1 (K5). Пол `MIN_CHECKED_L2` = 20, манифест 744 исхода.
1476
+ Структурный голден L1 при отказе плана по форме вне среза (условный терм
1477
+ головы `ERR-E0176-DEFEASIBLE-SELF-CYCLE`) закрепляется отказом плана;
1478
+ ошибкой ворот остаётся построенный план и отказ другой структурной
1479
+ причиной.
1480
+
1481
+ Замер 16.09.2026 (план 0.15, DECISION-0266–0268, цели `ts,py,go`, шаг
1482
+ ворот около 4 мин без конкуренции): рассмотрено L0 507, L1 94; план построен и
1483
+ вопрос поддержан у 445; L0 — совпало 225, отказ плана 110, отказ рантайма 151;
1484
+ L1 — 32 и 5 структурных отказов; L2 — 26; L3 — 9; расхождений и крахов 0.
1485
+ Манифест перепинован целиком — 913 исходов (292 совпадения): до этого 77
1486
+ совпадений стояли незакреплёнными (в том числе `LAY-L0-RND-*` 0259) и 145
1487
+ исходов изменились с прежних закреплений, большинство — из 0252/0256/0259,
1488
+ чей перепин откладывался; из 0266–0268 — `ERR-E0084-MONTH-*` 7 и
1489
+ `ERR-E0165-WILDCARD-*` 4 в совпадение, 16 новых векторов (14 совпадений, 2
1490
+ отказа плана по замыслу). Полы `MIN_CHECKED` 82 → 225, `MIN_CHECKED_L1`
1491
+ 36 → 37, `MIN_CHECKED_L2` 21 → 35. Обзор корпуса — `SURVEY-2026-09-16-values.ru.md`:
1492
+ 228 из 826, план 0.15 у 7 пакетов; потребители `bio.mendel` 5/5 и
1493
+ `eu.social_security.coordination` 50/50 закреплены.
1494
+
1495
+ Замер 16.09.2026, вечер (план 0.16, DECISION-0269, цели `ts,py,go`):
1496
+ рассмотрено L0 507, L1 104; план построен и вопрос поддержан у 454; L1 — 41
1497
+ совпадение и 5 структурных отказов (+9 `LAY-L1-PRIORITY-DATED-*`, вектор
1498
+ цикла на отрезке — отказ плана), L0, L2, L3 прежние; расхождений и крахов 0;
1499
+ манифест 923 исхода, `MIN_CHECKED_L1` 37 → 46. Обзор корпуса —
1500
+ `SURVEY-2026-09-16-priorities.ru.md`: 242 из 826, план 0.16 у 14 пакетов;
1501
+ потребители — 12 пакетов закреплены (+250 совпадений, `MIN_CONSUMER_MATCHED`
1502
+ 2181 → 2431), `kz.corpus.kacd_svod_pravil` с 27 названными отказами
1503
+ `AMBIGUOUS_CALENDAR`.
1504
+
1505
+ Замер 16.09.2026, поздний вечер (план 0.18, DECISION-0273, цели `ts,py,go`,
1506
+ шаг ворот 8 мин 37 с): L0 — совпало 251 (+17: `LAY-L0-A3-PARAM-*`,
1507
+ `LAY-L0-FN-*`, `LAY-L0-LET-HEAD`, `LAY-L0-MATCH-*`, `T071`, `T158`,
1508
+ `T304`, `T324`–`T326`, `T351`), отказ плана 103, отказ рантайма 150; L1 —
1509
+ 54 и 5 структурных отказов; L2 — 26; L3 — 9; расхождений и крахов 0;
1510
+ манифест 943 исхода, `MIN_CHECKED` 234 → 251. Обзор корпуса —
1511
+ `SURVEY-2026-09-16-functions.ru.md`; потребители и их полы — в журнале
1512
+ `docs/status/2026-09-16.ru.md`.
1513
+
1514
+ Пол храповика `MIN_CHECKED` в воротах: снижение числа исполненных векторов —
1515
+ регрессия среза, а не «просто меньше». Поднимается вместе с расширением плана.
1516
+
1517
+ ## Чего стоит целевой язык (замер 10.09.2026, план 0.4)
1518
+
1519
+ | цель | эмиттер | рантайм | раннер | харнесс |
1520
+ |---|---:|---:|---:|---:|
1521
+ | `ts` | 184 | 251 | 124 | 72 |
1522
+ | `py` | 145 | 344 | 141 | 97 |
1523
+ | `go` | 181 | 471 | 238 | 162 |
1524
+ | `rb` | 149 | 255 | 113 | 73 |
1525
+ | `rs` | 189 | 448 | 163 | 106 |
1526
+ | `java` | 156 | 243 | 140 + 148 JSON | 91 |
1527
+ | `cs` | 175 | 243 | 140 | 90 |
1528
+ | `kt` | 154 | 196 | 115 + 103 JSON | 67 |
1529
+ | `erl` | 184 | 238 | 118 | 64 |
1530
+ | `ex` | 176 | 227 | 115 | 62 |
1531
+ | `lua` (14.09, план 0.5) | 229 | 541 | 154 + 133 JSON | 82 |
1532
+ | `swift` (14.09, план 0.5) | 209 | 528 | 124 + 208 JSON | 105 |
1533
+ | `php` (14.09, план 0.5) | 207 | 660 | 174 + 144 JSON | 84 |
1534
+ | `dart` (14.09, план 0.5) | 183 | 293 | 142 + 166 JSON | 99 |
1535
+ | `bsl` (14.09, план 0.6) | 310 | 858 | 184 + 243 JSON | 113 |
1536
+
1537
+ Эмиттер почти одинаков между языками. Дорожает рантайм там, где нет
1538
+ точных целых или они многословны (`math/big` в Go, `num-bigint` в Rust),
1539
+ и раннер там, где нет JSON в стандартной библиотеке (Java и Kotlin несут
1540
+ свой разбор, числа в нём сохраняются текстом). Число строк «втрое» не
1541
+ предсказывается: предсказывают бигинт и JSON.
1542
+
1543
+ Что ловит сертификация при переносе, одинаково в разных языках: собственные
1544
+ `max`, `abs` и `min` рантайма затеняют встроенные (Python, Erlang, Elixir);
1545
+ общий каталог сборки складывает все модели в один бинарь (Rust);
1546
+ печать UTF-8 как latin1 (`~s` в Erlang); порождённое имя сталкивается с
1547
+ именем рантайма (`add` в Go). Ни одно из этого не видно векторам, пока
1548
+ ответ случайно совпадает.
1549
+
1550
+ ## Что целевой язык навязывает сам
1551
+
1552
+ - Go отвергает неиспользуемую переменную цикла, и эмиттер обязан её гасить;
1553
+ у Go одно пространство имён — вставка строки в модели зовётся `addRow`.
1554
+ - Целые произвольной длины есть не везде: `bigint`, `int`, `math/big`,
1555
+ `BigInteger`, `num-bigint` — разные решения одной задачи.
1556
+ - Порядок обхода обязан быть детерминированным: строки лежат в массивах, а не
1557
+ в множествах и словарях с недетерминированным порядком; у Erlang и Elixir
1558
+ состояние таблиц чистое и передаётся через правила.
1559
+ - Rust собирает все проекты в общий `--target-dir`, поэтому крейт и бинарь
1560
+ именуются по каталогу вектора; C# запускается как `dotnet app.dll`,
1561
+ apphost без `DOTNET_ROOT` не стартует; Kotlin, Erlang и Elixir на этой
1562
+ машине поставлены Homebrew и в workflow профиля не входят.
1563
+ - Lua и Swift не дают ни длинных целых, ни пригодного JSON: у обоих свой
1564
+ модуль по основанию 10^7 со знаком (деление в столбик, цифра частного
1565
+ двоичным поиском) и свой разбор, где число — исходная запись. В Swift
1566
+ разбор нужен потому, что `JSONSerialization` отдаёт числа как NSNumber.
1567
+ - Lua: массивы с единицы — эмиттер сдвигает номера столбцов и ключей;
1568
+ экранов `\uXXXX` в строке нет — литерал печатается экранами `\ddd`; у
1569
+ функции не больше 200 локальных имён — правила лежат в массиве функций.
1570
+ Отказ — таблица с метатаблицей `Refused` в `error`, крах — любое другое
1571
+ значение ошибки. Проверено на Lua 5.5.0; дескриптор принимает 5.3+.
1572
+ - Swift: код верхнего уровня допустим только в `main.swift` — раскладка
1573
+ кладёт раннер или харнесс под этим именем; отказ — `throw`, поэтому каждое
1574
+ выражение модели с вызовом рантайма стоит под `try`; внутри `enum RT`
1575
+ голые `max`/`min` означают функции рантайма — целые сравниваются через
1576
+ `Swift.max`. `swiftc` на проект — десятки секунд, поэтому у цели есть
1577
+ `build_many`: сборки всех проектов ворот идут параллельно (одного бинаря
1578
+ на все модели не выходит — у каждой тот же `enum Model`). Foundation не
1579
+ подключается. Шаг ворот `py,swift` — 644 с при load average около 100.
1580
+ - PHP: `==` сравнивает строки, похожие на числа, как числа (`"1.0" == "1"`),
1581
+ поэтому текст сравнивается только `===`, а границы окна §31 — `strcmp`;
1582
+ инициализатор статического свойства допускает только константу — константы
1583
+ §54 и собственные факты пакета печатаются функциями; bcmath и gmp — не ядро
1584
+ (в образе `php:8.3-cli` их нет), длинные целые свои, как у Lua; `json_decode`
1585
+ отдаёт дробную запись как float — числа разбираются записью, строки JSON
1586
+ разбирает ядро. Литералы модели — в одинарных кавычках: без интерполяции `$`.
1587
+ - Dart: у `BigInt` `%` евклидов, а `~/` усекает — остаток с усечением только
1588
+ `remainder`; кода верхнего уровня нет — факты пакета в `packageFacts()`;
1589
+ `exit()` не дожидается записи stdout, поэтому код выхода ставится через
1590
+ `exitCode`; `jsonDecode` превращает длинное целое в double — разбор свой;
1591
+ в строковом литерале `$` — интерполяция и экранируется. `dart run file.dart`
1592
+ работает без pubspec, сборки нет.
1593
+ - PHP, Dart и Scala на этой машине не поставлены; проверены в docker через
1594
+ обёртки PATH (`php`, `dart`, `scala-cli` → `docker exec` в долгоживущий
1595
+ контейнер) и, как Kotlin и Erlang, в workflow профиля не входят. Шаги ворот:
1596
+ `py,php` — 21,5 с, `py,dart` — 56 с.
1597
+ - BSL (встроенный язык 1С) исполняется OneScript (`oscript`), без лицензии
1598
+ платформы; решение и пределы — DECISION-0235 (черновик). `Число` у OneScript —
1599
+ `.NET decimal` (28–29 значащих цифр), у платформы — 38 цифр, и оба режут
1600
+ дробь МОЛЧА (проба OneScript 2.2.0: `1/3` и дробь из 30 знаков — без ошибки),
1601
+ громко падает только переполнение целой части. Поэтому длинные целые свои:
1602
+ разряды по основанию 10^7 в `Массив`, масштаб рядом; частное целых — только
1603
+ через `%`, а `%` усекает к нулю (`-7 % 3 = -1`), евклидов остаток свой.
1604
+ Цифры печатаются `Формат(БАЗА + р, "ЧГ=0")`: `Строка()` у платформы
1605
+ группирует разряды, `Формат(0)` без `ЧН` — пустая строка. Регистр не
1606
+ различается, `И` — ключевое слово, а `pow`, `round`, `min`, `max`, `date`
1607
+ методом класса быть не могут, — операции контракта зовутся `op_*`, правила
1608
+ `Правило_<name>` со сверкой уникальности без учёта регистра. JSON свой: число
1609
+ остаётся записью, `ЗаписьJSON` переносит строки. Модули — классы
1610
+ `ПодключитьСценарий`; отказ — исключение с префиксом `law-refused|`,
1611
+ необработанное исключение OneScript печатает в stdout с кодом 1 (крах);
1612
+ stdout — UTF-8 без BOM при исходниках с BOM. Интерпретатор перебирает
1613
+ декартово произведение атомов на порядки медленнее: у `kz.corpus.bin`
1614
+ (правило из пяти атомов) дело шло 43 с, поэтому эмиттер выносит литералы из
1615
+ циклов и ставит ПРЕФИКС проверок, которые отказать не могут (фильтры и
1616
+ соединения не над Money), в цикл, где связаны их строки, — 0,21 с. На этой
1617
+ машине `oscript` — релиз 2.2.0 `osx-arm64` без подписи кода (ядро убивает
1618
+ его с кодом 137 до ad-hoc `codesign -s -`); в workflow профиля цель не
1619
+ входит, как Kotlin, Erlang и Elixir. Шаг ворот `py,bsl` — 11 с.
1620
+
1621
+ ## Границы среза
1622
+
1623
+ Вне плана (на 28.09.2026): условные приоритеты §117/§118, прочтения §152–§157, ограничения
1624
+ §93.1, динамическая ссылка `reference` §30.2, Magnitude и алгебра единиц
1625
+ (`magnitude`, `unit_convert`, `unit_exponent`), слой событий §79–§82 сверх
1626
+ идентичности события (запись события и её поля, свёртка §161.1), судейский
1627
+ конъюнкт с вычисляемым аргументом и гард с вызовом/`if` в обходе суждения
1628
+ (UNDETERMINED — безопасная сторона), голый `const_ref`
1629
+ параметра A3 в голове или атоме (позиция идентичности), параметр A3 по оси
1630
+ Instant, функции с `record`/`list`/рекурсией и образцы `match` с полями,
1631
+ кванторы в телах, `average`,
1632
+ жизненный цикл редакции с прерывистой применимостью, вопросы `why_not` и
1633
+ `interpretation_analysis`, порядок над перечислением и сравнение членов
1634
+ разных перечислений (E-0067), порядок над Boolean, монотонный агрегатный
1635
+ гард над выводимым предикатом (G2) — а с ним и удержание issue такого гарда
1636
+ до неподвижной точки (errata E-0198): удержание наблюдаемо только на растущей
1637
+ коллекции, а растущая коллекция в срез не входит (разбор — в G3 выше). Что вошло в срез
1638
+ позднее первых версий, описано разделами выше: замыкание §70 и агрегаты
1639
+ (0239), нормы L2 и `positions` (0242), календарь §84–§86 (0245), Rational
1640
+ (0252), Quantity (0256), `round`/`div_round` (0259), Boolean, Natural,
1641
+ границы месяца и `_` §98 (0266–0268), датированные приоритеты (0269),
1642
+ условный терм и вычисляемый аргумент тела (0270), функции §46, параметры
1643
+ A3, `let` и `match` (0273), канал суждения §47.3 (0274), импорт чужих
1644
+ пакетов (0236), инертные конструкции манифеста (0406, W1), алиасы типов §40,
1645
+ std-сущности §261 и идентичности `LegalContext`, `LegalRole`, `Jurisdiction`,
1646
+ Instant §2.15 и событие как идентичность §79 (W2),
1647
+ поподстановочная ошибка головы и агрегат в голове без политики `empty`
1648
+ (E-0265, W5). Каждая
1649
+ из форм вне плана — названный отказ плана, а не тихий пропуск. Внутри среза: `Date`
1650
+ как хранимое и сравнимое по дню значение (`LAY-L0-DATE-ORDER-*`),
1651
+ константы §54 в деле и запросе (`ERR-E0032`), члены перечислений в фактах
1652
+ и гардах равенства (`ERR-E0067-ENUM-GUARD-*`), константы в атомах тела
1653
+ (`ERR-E0052-DECIMAL-CANON`), ключи отношений §45.1 с `KEY_CONFLICT`
1654
+ (`LAY-L0-KEYS`).
1655
+
1656
+ Пропускаются молча только узлы провенанса §194, которые по SPEC не меняют ни
1657
+ одной нормы: `source_publication`, `publication` (§27.3, вне семантического
1658
+ хэша по §28), `fragment`. `source_work` §28 с 27.09.2026 — инертная
1659
+ конструкция манифеста со свидетелями (DECISION-0406), а не молчаливый пропуск.
1660
+
1661
+ Про ключи отдельно (решение 09.09.2026, история): ключевая проекция даёт
1662
+ `KEY_CONFLICT` при разных accepted-кортежах, evaluator при этом ничего не
1663
+ выбирает и не удаляет, так что ответ бэкенда совпадал бы с голденом
1664
+ (`LAY-L0-KEYS`). Но диагностику срез тогда не порождал, а поле, которое план
1665
+ молча не читает, — именно тот пропуск, который DECISION-0188 запрещает.
1666
+ Поэтому ключи до плана 0.5 были отказом: четыре вектора ушли из совпадения и
1667
+ отказа по типам в отказ по ключам. С плана 0.5 (14.09.2026) модуль считает
1668
+ `KEY_CONFLICT` сам, и ворота сверяют его с голденом.
1669
+
1670
+ Доказательство и байтовый контракт сюда НЕ входят: для профиля
1671
+ `core-auditable` пришлось бы перенести на каждый целевой язык канонический
1672
+ порядок §208, семантический хэш §209 и рендер proof graph. Это отдельная
1673
+ работа и отдельное решение.