lawref.evaluator.rules
Статический анализ правил, общий для strict и defeasible замыканий.
Барьер полноты §111/§190: агрегат читает предикат целиком, поэтому источник обязан быть завершён до чтения. Оба замыкания задают этот вопрос одинаково — и получают одинаковый ответ.
Functions
| Name | Description |
|---|---|
aggregate_barrier_problem | §111/§190: агрегат (домен квантора) читает предикат ЦЕЛИКОМ, поэтому |
all_generator_reads | ВСЕ generator-чтения правила, включая изъятые E-0116, — для барьера |
body_condition_literals | Чтения условных термов тела и scope литералами — носитель |
body_condition_reads | Чтения опоры внутри условных термов §56 ТЕЛА и scope: [(вид статуса, |
closure_levels | Уровни замыканий §70 (errata E-0113): уровень замыкания на единицу выше |
closure_waits | Ожидание замыканий §70 строгим слоем (errata E-0174): id строгого |
condition_leaves | Листья условия цели §124 (errata E-0218): связки and/or над |
condition_literal | payload.goal.condition → ground-литерал (v1: атом или established-атом). |
condition_reads | Чтения условных термов §56 головы и тела вместе — для рангов и графа |
defeasible_chain_predicates | Errata E-0172 (§110 п. 5/§111): предикаты, которые строгая цепочка |
defeasible_head_literals | Errata E-0238: пары (предикат, полярность) голов обычных defeasible- |
defeasible_predicates | Предикаты с ПОРАЖАЕМЫМ производителем (defeasible/defeater, §104) — |
defeasible_rule_status | Та же классификация для §104 (defeasible/defeater) — порт тела |
derived_predicates | Предикаты, которые программа выводит правилами (любой силы). |
effect_reachable_predicates | Предикаты, достижимые от голов эффектов полномочий §127 строго- |
generator_problem | Причина, по которой generator comprehension правила вне исполнимого |
generator_reads | Чтения generator-ов агрегатов §59 и доменов кванторов §52 правила — |
goal_condition_compound | Составное условие цели §124 (E-0218), исполнимое по §64, — или None. |
goal_condition_literal | §124.3: forbearance reads the negative action atom (E-0179). |
goal_condition_sides | §64 над парами листьев условия цели (errata E-0218): стороны t/f |
head_condition_literals | Чтения условного терма головы §56 литералами: (вид статуса, литерал) — |
head_condition_reads | Чтения опоры внутри условного терма ГОЛОВЫ §56: [(вид статуса, предикат)]. |
monotone_guard_aggregates | Агрегаты §59 МОНОТОННЫХ АГРЕГАТНЫХ ГАРДОВ §111 |
monotone_guard_growing | Растёт ли коллекция монотонного агрегатного гарда §111 ВНУТРИ страты |
monotone_guard_reads | Чтения generator-ов МОНОТОННЫХ агрегатных гардов §111 (errata E-0116) — |
monotone_guard_sum_aggregates | Монотонные гарды с агрегатом sum (§111, errata E-0116) — те, чьи |
norm_goal_status | Классификация цели §124: (исход, причина). |
norm_rule_status | Классификация правила с norm-головой §135 — порт цикла norms.py. |
rule_execution_status | Единая точка для внешнего наблюдателя: пропустит ли исполнитель это |
scope_conjuncts | Конъюнкты scope правила — §90: «scope семантически объединяется с |
stratification_problem | §110 п.1–2: цикл предикатного графа с не-monotone ребром — issue, иначе None. |
strict_positive_chain | Строго-позитивные рёбра программы: (голова строгого правила, предикаты |
strict_positive_closure | Семена плюс головы строгих правил, читающих достигнутый предикат, до |
strict_rule_status | Классификация строгого правила: (исход, причина, конъюнкты). |
aggregate_barrier_problemfunction#
def aggregate_barrier_problem(node: dict, defeasible: set[str] | frozenset[str]) -> str | None§111/§190: агрегат (домен квантора) читает предикат ЦЕЛИКОМ, поэтому
его источник обязан быть завершён до чтения. До DECISION-0088 завершённым
признавался только невыводимый предикат; теперь строгие производители
завершаются стратами строгого слоя (strict.strict_ranks), и барьер
держит лишь СТРОГОГО читателя над предикатом с ПОРАЖАЕМЫМИ
производителями: страты поражаемого слоя есть только у поражаемых правил.
Errata E-0112: обёртка monotone(P)/supported(P) барьер НЕ снимает —
перечисление есть ребро completion §110 независимо от статуса.
Errata E-0116 барьер тоже не трогает: изъятие снимает НЕМОНОТОННОСТЬ
перечисления, но не поражаемость производителей — опора поражаемого
правила может быть отнята дефитером, и тест monotone(P) над ней не
монотонен. Поэтому здесь зовётся all_generator_reads, а не
generator_reads.
all_generator_readsfunction#
def all_generator_reads(node: dict) -> list[tuple[str, str]]body_condition_literalsfunction#
def body_condition_literals(node: dict) -> list[tuple[str, dict]]Чтения условных термов тела и scope литералами — носитель
body_condition_reads; полярность нужна ожиданию замыкания §70.
body_condition_readsfunction#
def body_condition_reads(node: dict) -> list[tuple[str, str]]Чтения опоры внутри условных термов §56 ТЕЛА и scope: [(вид статуса, предикат)] — errata E-0177.
Позиция терма в теле не важна: гард-сравнение, формула §64.2, квантор
§52, фильтр comprehension, аргумент литерала. Гард, истинный на ветви
else, публикует голову, пока тест ложен, и вывод её не отзывает, —
немонотонность та же, что у условной головы (head_condition_reads).
До errata flatten_body отдавал гард целиком (guard), и ранги, отказы и
граф §110 его чтения пропускали: читатель вставал в страту производителя.
closure_levelsfunction#
def closure_levels(nodes: list[dict]) -> dict[str, int] | NoneУровни замыканий §70 (errata E-0113): уровень замыкания на единицу выше
уровней всех замыканий, чьи негативы читают — транзитивно, через любую
зависимость — производители его предиката или домена; без такой
зависимости — уровень 1. Считается по ТОМУ ЖЕ графу, что и
stratification_problem: вершина-замыкание достигает другую вершину-
замыкание ровно тогда, когда её производители читают тот негатив.
None — цикл между замыканиями (его называет stratification_problem
раньше, чем сюда доходит вызов; здесь — страховка).
closure_waitsfunction#
def closure_waits(rules: list[tuple[dict, list[tuple[str, dict]]]], nodes: list[dict], derived: set[str] | frozenset[str]) -> dict[str, frozenset[str]]Ожидание замыканий §70 строгим слоем (errata E-0174): id строгого правила → замыкания, до материализации которых оно не исполняется.
§70 ставит замыкание вершиной графа страт: producers(P ∪ domain) → closure(P) → readers(¬P). Сэндвич §231.1 повторяет строгий фикспойнт
до замыкания и после него, а вывод append-only. Правило, чей тест
опровергается негативом замыкания (unknown(P), not_known(¬P),
перечисление ¬P), сработало бы в проходе ДО замыкания и не отозвалось бы.
Такое правило ждёт замыкание — это подъём над вершиной, а не отказ.
Ожидание транзитивно: правило ждёт и то, чего ждут строгие производители
опор, опровергающих его тест, а производитель завершён, когда завершены
все его чтения. Правило, чей тест замыкание делает только истинным
(голый not P), не ждёт: второй проход сэндвича его и так исполняет.
Значения — только id замыканий, достижимых по графу §110, поэтому правило не ждёт замыкания, которое само питает: уровни E-0113 ставят такое замыкание выше. Пустой словарь — прежний сэндвич байт в байт.
condition_leavesfunction#
def condition_leaves(condition) -> list[dict] | NoneЛистья условия цели §124 (errata E-0218): связки and/or над
литералами и established-литералами. None — форма вне подмножества.
Лист читается парой САМОГО литерала: established, поставленный
понижением по умолчанию §66, Boolean-подъёмом §64.1 здесь не является —
иначе condition not A при неизвестном A давал бы контрпример §137.
condition_literalfunction#
def condition_literal(condition) -> dict | Nonepayload.goal.condition → ground-литерал (v1: атом или established-атом).
Живёт здесь, а не в norms.py, по той же причине, что и классификации
правил: тот же вопрос задают снаружи — norm_goal_status ниже и ворота
verify/ci/gates/silence/check_dead_rules.py. Вторая копия списка
допустимых форм разошлась бы с первой молча.
condition_readsfunction#
def condition_reads(node: dict) -> list[tuple[str, str]]defeasible_chain_predicatesfunction#
def defeasible_chain_predicates(nodes: list[dict]) -> set[str]defeasible_head_literalsfunction#
def defeasible_head_literals(nodes: list[dict]) -> set[tuple[str, str]]defeasible_predicatesfunction#
def defeasible_predicates(nodes: list[dict]) -> set[str]Предикаты с ПОРАЖАЕМЫМ производителем (defeasible/defeater, §104) —
DECISION-0088: строгий читатель not_known/агрегата над ними отказывается.
defeasible_rule_statusfunction#
def defeasible_rule_status(node: dict, derived: set[str]) -> tuple[str, str | None, list[tuple[str, dict]]]derived_predicatesfunction#
def derived_predicates(nodes: list[dict]) -> set[str]Предикаты, которые программа выводит правилами (любой силы).
effect_reachable_predicatesfunction#
def effect_reachable_predicates(nodes: list[dict]) -> set[str]Предикаты, достижимые от голов эффектов полномочий §127 строго-
позитивными рёбрами (errata E-0134): after (у modify — и before)
каждого norm_template модальности power, затем головы строгих правил,
чьё тело читает такой предикат через established/supported/monotone,
до фикспойнта. Поражаемые правила цепочку не продолжают: после
материализации они не повторяются (DECISION-0021 §4.3) и опоры не дают.
generator_problemfunction#
def generator_problem(node: dict) -> str | NoneПричина, по которой generator comprehension правила вне исполнимого подмножества, либо None (§52.1, errata E-0219).
Comprehension стоит в теле, голове или scope — под агрегатом §59 или
доменом квантора §52, на любой глубине. До errata такой generator не
проверялся при классификации вовсе: его отвергал перебор уже во время
вычисления, и ValueError уносил evaluation-документ целиком — на ЛЮБОЙ
вопрос к программе, а ворота молчания падали исключением вместо того,
чтобы назвать правило.
generator_readsfunction#
def generator_reads(node: dict, derived: set[str] | frozenset[str] = frozenset()) -> list[tuple[str, str]]Чтения generator-ов агрегатов §59 и доменов кванторов §52 правила —
(статус, предикат) в порядке встречи, без повторов, тело И голова.
DECISION-0088: это зависимости §110 для рангов обоих слоёв; errata
E-0112: каждое — ребро completion, и статус обёртки (monotone,
supported) вид ребра НЕ меняет: перечисление множества не монотонно.
Errata E-0116: изъяты чтения МОНОТОННОГО АГРЕГАТНОГО ГАРДА §111 — их
отдаёт monotone_guard_reads, и это рёбра monotone, а не completion.
derived — предикаты, выводимые правилами программы
(derived_predicates): без них гард не распознать, поэтому пустое
умолчание означает «выводимых нет» и изъятие шире не делает.
goal_condition_compoundfunction#
def goal_condition_compound(goal: dict) -> dict | Nonegoal_condition_literalfunction#
def goal_condition_literal(goal: dict) -> dict | None§124.3: forbearance reads the negative action atom (E-0179).
Keep the authored goal intact: conflicts and weak permission use its action. Only the lifecycle/knowledge projection is desugared.
goal_condition_sidesfunction#
def goal_condition_sides(condition: dict, store) -> tuple[list[str], list[str]]head_condition_literalsfunction#
def head_condition_literals(node: dict) -> list[tuple[str, dict]]head_condition_readsfunction#
def head_condition_reads(node: dict) -> list[tuple[str, str]]Чтения опоры внутри условного терма ГОЛОВЫ §56: [(вид статуса, предикат)].
Такое чтение немонотонно ровно так же, как not_known в теле: ветвь else
срабатывает, ПОКА тест ложен, и если предикат получит опору позже, правило
сработает второй раз — а факт, выведенный ранним else, останется в store.
Поэтому читатель обязан стоять ВЫШЕ страты производителей (§110/§113), и
зависимость обязана быть видна ранжированию.
До errata: чтения жили только в теле, deps_of их и собирал; условие в
голове не попадало ни в conjuncts, ни в generator_reads, и правило
вставало в нулевую страту рядом с производителем.
monotone_guard_aggregatesfunction#
def monotone_guard_aggregates(node: dict, derived: set[str] | frozenset[str]) -> list[dict]Агрегаты §59 МОНОТОННЫХ АГРЕГАТНЫХ ГАРДОВ §111 (errata E-0116).
Гард — ПОЗИТИВНЫЙ конъюнкт тела (голова и любая иная позиция не считаются)
вида AGG(collect v … where G) OP c, где: пара AGG/OP — из
_MONOTONE_GUARD_OPS; c не перечисляет ничего сам (в терме порога нет
comprehension, значит нет и чтения выводимого предиката); G читает
выводимые предикаты ТОЛЬКО через monotone/supported §65.
Знак слагаемых sum статика не видит, и требовать конъюнкт v >= 0
нельзя: он МЕНЯЕТ каноническое тело §206 и вместе с ним порядок
конъюнктов — измерено на us.ofac_50, где добавленный конъюнкт передвинул
гард перед monotone(blocked(lead)) и сумма считалась по пустой
коллекции (EMPTY_AGGREGATE §59) в первом же раунде. Поэтому проверка
отрицательного слагаемого — на ИСПОЛНЕНИИ гарда:
MONOTONE_GUARD_NEGATIVE_ADDEND (§111, errata E-0116).
Такой тест не убывает вместе с множеством: least fixed point §103 по нему
определён так же, как по точечному monotone(P), и generator-чтения —
рёбра monotone, а не completion.
monotone_guard_growingfunction#
def monotone_guard_growing(node: dict, derived: set[str] | frozenset[str]) -> boolРастёт ли коллекция монотонного агрегатного гарда §111 ВНУТРИ страты (errata E-0198).
Растёт ровно тогда, когда generator гарда читает ВЫВОДИМЫЙ предикат (по
§111 — только через monotone(P)/supported(P) §65): правило стоит в
страте своих производителей и повторяется, пока гард не выполнен, поэтому
коллекция первого прохода — подмножество коллекции неподвижной точки, и
ошибка вычисления над ней ещё не окончательна. У гарда, чей generator
читает одну эмпирику, коллекция заморожена входом дела: первый проход
равен неподвижной точке, удерживать нечего, и байты такого правила правка
E-0198 не двигает по построению.
Замер 20.09.2026 по 1 281 снимку корпуса: монотонных агрегатных гардов
353, растущих — 32 в 27 правилах, и среди растущих не-count ровно один
(us-ofac-50#BlockedEntityByAggregateOwnership2014).
monotone_guard_readsfunction#
def monotone_guard_reads(node: dict, derived: set[str] | frozenset[str]) -> list[tuple[str, str]]Чтения generator-ов МОНОТОННЫХ агрегатных гардов §111 (errata E-0116) —
(статус, предикат) в порядке встречи, без повторов. Дополнение к
generator_reads: вместе они дают все generator-чтения правила.
monotone_guard_sum_aggregatesfunction#
def monotone_guard_sum_aggregates(node: dict, derived: set[str] | frozenset[str]) -> list[dict]Монотонные гарды с агрегатом sum (§111, errata E-0116) — те, чьи
слагаемые исполнитель обязан проверить на знак: монотонность суммы
держится на неотрицательности, а статика её не видит.
norm_goal_statusfunction#
def norm_goal_status(template: dict) -> tuple[str, str | None]Классификация цели §124: (исход, причина).
Дефект, ради которого заведена (измерен 30.08.2026 на ст. 910 п. 2 ГК РК,
Особенная часть). Цель condition a(x) or b(x) проходит lawc check и
лоуверинг молча — схема AchievementGoal объявляет condition как
Formula, то есть §124 дизъюнкцию РАЗРЕШАЕТ, — а на исполнении
обязанность не разряжается никогда. Улика на одном деле: соседняя
обязанность с однолитеральной целью перешла в SATISFIED, а эта,
содержащая тот же литерал внутри or, осталась ACTIVE, и ни одного issue
выдано не было. Класс тот же, что у or в теле правила, но тише: там
исполнитель хотя бы печатает NON_EXECUTABLE_RULE.
Отказ v1 честен (неверных ответов не даёт), но обязан быть НАЗВАН —
отсюда причина, которую печатает norms.py и требуют ворота молчания.
norm_rule_statusfunction#
def norm_rule_status(node: dict, template_ids: set[str]) -> tuple[str, str | None, list[tuple[str, dict]]]Классификация правила с norm-головой §135 — порт цикла norms.py.
Два отказа: шаблон не разрешается и тело вне v1-подмножества. Именно
здесь пропали KurultaiDissolutionBarrier и PresidentialOfficialImmunityRule
Конституции — у них норм-голова, поэтому строгое замыкание их не видит
вовсе, а тело с or не проходит flatten_body.
rule_execution_statusfunction#
def rule_execution_status(node: dict, derived: set[str], template_ids: set[str], defeasible: set[str] | frozenset[str] = frozenset(), effect_reachable: set[str] | frozenset[str] = frozenset(), defeasible_chain: set[str] | frozenset[str] = frozenset(), defeasible_heads: set[tuple[str, str]] | frozenset[tuple[str, str]] = frozenset()) -> tuple[str, str | None]Единая точка для внешнего наблюдателя: пропустит ли исполнитель это
правило и почему. Диспетчер по силе и виду головы — ровно тот, что
разложен по трём модулям оракула (strict, defeasible, norms).
Оговорка: покрывает СТАТИЧЕСКИЕ отказы. Отказы, видимые только на деле
(power без exercise-паттерна §127, priority с несвязанными переменными
§118), рождаются в effects.py/defeasible.py во время вычисления и
статикой не предсказуемы — ворота их не видят и не притворяются.
scope_conjunctsfunction#
def scope_conjuncts(node: dict) -> list[tuple[str, dict]]Конъюнкты scope правила — §90: «scope семантически объединяется с
when, но сохраняется отдельно в IR для indexing, analysis и объяснений».
Слово «объединяется» нормативно, и до 28.08.2026 обе реализации его не
исполняли: нетривиальный scope давал skipped с причиной «за пределами
v1». Отказ был ЧЕСТНЫМ (неверных ответов не давал), но цена его молчалива —
норма, у которой условие применимости выражено по §91 отдельно от
условия срабатывания §91.1, не исполнялась вовсе. В корпусе так молчали
четырнадцать норм Закона «О правовых актах», включая пункт 1 статьи 5
(«все иные акты не могут противоречить нормативным постановлениям
Конституционного Суда»).
Разбор — тот же flatten_body, а не второй: §90 объединяет scope с when,
значит и подмножество конструкций у них одно. Своя копия разошлась бы с
телом ровно так же, как разошлись бы два списка исходов классификации.
Тривиальный scope (None либо true) даёт пустой список — форма
{"kind": "boolean", "value": true} уже возвращает [] из flatten_body.
stratification_problemfunction#
def stratification_problem(nodes: list[dict]) -> dict | None§110 п.1–2: цикл предикатного графа с не-monotone ребром — issue, иначе None.
Детект как в статике lawc: для каждого НЕ-monotone ребра (потребитель →
продюсер) ищется обратная достижимость по полному графу; найденный путь
замыкает цикл, содержащий это ребро. Только-monotone циклы (transitive
closure через supported §103) легальны и не репортятся. Ребро
completion (E-0112) и рёбра вершин-замыканий (E-0113) — не monotone.
Объём — циклы, в которых участвует хотя бы одно strict-правило: именно их
strict-LFP §103 иначе молча вычислил бы по немонотонному оператору
(established гасится приходом негативной опоры: TRUE_ONLY → BOTH).
Чисто defeasible-циклы детектируются расхождением рангов в
defeasible_closure и репортятся своим сообщением.
strict_positive_chainfunction#
def strict_positive_chain(nodes: list[dict]) -> list[tuple[str, list[str]]]Строго-позитивные рёбра программы: (голова строгого правила, предикаты
его положительных конъюнктов established/голый атом, supported,
monotone в scope и теле) — в порядке узлов. Общий носитель обходов
E-0134 (эффект полномочия) и E-0172 (поражаемый вывод): оба спрашивают,
куда поздний производитель доходит строгими правилами, и одна копия списка
статусов не разойдётся со второй. Правило вне v1-разбора рёбер не даёт.
strict_positive_closurefunction#
def strict_positive_closure(seeds: set[str], chain: list[tuple[str, list[str]]]) -> set[str]Семена плюс головы строгих правил, читающих достигнутый предикат, до
фикспойнта (strict_positive_chain).
strict_rule_statusfunction#
def strict_rule_status(node: dict, derived: set[str], defeasible: set[str] | frozenset[str] = frozenset(), effect_reachable: set[str] | frozenset[str] = frozenset(), defeasible_chain: set[str] | frozenset[str] = frozenset(), defeasible_heads: set[tuple[str, str]] | frozenset[tuple[str, str]] = frozenset()) -> tuple[str, str | None, list[tuple[str, dict]]]Классификация строгого правила: (исход, причина, конъюнкты).
Живёт здесь, а не в теле цикла strict_closure, потому что тот же вопрос
задают снаружи: ворота verify/ci/gates/silence/check_dead_rules.py обязаны знать, какие
правила корпуса исполнитель молча пропустит. Вторая копия этого списка
разошлась бы с первой — и ворота докладывали бы о живых нормах, которых
нет, либо молчали о мёртвых (тот же класс отказа, что у реестров в
CLAUDE.md). Один список, два потребителя.
DECISION-0088 (WP-41): негативная голова §99, not_known §113 и агрегат по
выводимому предикату §111 исполняются стратами строгого слоя
(strict.strict_ranks). Отказ остаётся у строгого читателя not_known
или агрегата над предикатом с ПОРАЖАЕМЫМИ производителями (defeasible):
§113 ставит читателя выше страты производителей, а страты поражаемого слоя
есть только у поражаемых правил — такое правило обязано быть defeasible.
Errata E-0134 (§111/§127): тот же отказ у строгого not_known/unknown/
refuted над предикатом из effect_reachable — достижимым от головы
эффекта полномочия строго-позитивной цепочкой (effect_reachable_predicates):
эффект материализуется после всех страт (§280.3), и умолчание считалось бы
по снимку без него, не отзываясь потом.
Errata E-0172 (§110 п. 5/§111): тот же отказ у строгого not_known и
перечисления над предикатом из defeasible_chain — выводимым строгой
цепочкой из поражаемого вывода (defeasible_chain_predicates, без самих
поражаемых голов). Strict closure страты исполняется до её кандидатов,
поэтому такой предикат полон лишь стратой выше источника, а у строгого
читателя страт поражаемого слоя нет. Сообщение своё: у предиката нет
поражаемого производителя, есть цепочка.
Errata E-0238 (§107/§111/§127): отказ и у строгого правила, чья голова —
КОМПЛЕМЕНТ головы обычного defeasible-кандидата (defeasible_heads: пары
«предикат, полярность» правил силы defeasible), а тело или scope
положительно читает предикат из effect_reachable: противник поражаемого
вывода опоздал бы к его conflict group, и документ получил бы BOTH.
Строгая опора той же полярности, что у кандидатов, комплемента не создаёт
и не отклоняется.
Errata E-0176 (§56/§111): условный терм головы — status-sensitive читатель
при любом статусе теста (ветвь else публикуется, пока тест ложен), и отказ
звучит над всеми тремя поздними производителями: поражаемым, звеном
цепочки и эффектом полномочия.
Documentation for Arxo. Writings — blog.arxo.io.
Anonymous visit counts on stats.arxo.io, no cookies.