check

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 13, 2026 License: AGPL-3.0 Imports: 15 Imported by: 0

Documentation

Overview

acceptance_edit_after_verdict.go — правка ОДОБРЕННОЙ приёмки после вердикта объявляется в самом документе.

Порт с монорепо (`internal/repohygiene/acceptanceeditafterverdict.go`, снят вынесением службы — `kacho#2597`). Изменилось: путь дома приёмок (был `services/iam/docs/engineering/acceptance`, здесь — `docs/engineering/acceptance` от корня СВОЕГО модуля) и обход git через `platformtree`/`gitenv` вместо внутреннего `repoRoot(t)` монорепо. Осталось дословно: имя функции гейта (`TestAcceptanceEditedAfterItsVerdictSaysSo`), сам разбор и текст находки.

Предмет

Вердикт есть утверждение о РЕВИЗИИ, которую прочитал проверяющий: правка её не переносит и не отзывает задним числом. Документ, чья строка состояния правлена раньше, чем сам файл, и не назвавший эту правку прямо, объявляет вердикт о ревизии, которую никто не читал.

Почему пара величин, а не одна

Признак — сравнение двух отметок: когда последний раз правилась СТРОКА СОСТОЯНИЯ и когда сам документ. Обе берутся в ЭПОХЕ: строковое сравнение дат в двух часовых поясах даёт неверный порядок.

acceptance_probe_coordinate.go — гейт: имя пробы, названное приёмкой КООРДИНАТОЙ, резолвится функцией в дереве.

Порт с монорепо (`internal/repohygiene/acceptanceprobecoordinate.go`, снят вынесением службы — `kacho#2597`). До этой правки держатель (`TestAcceptanceProbeCoordinateResolves`) не существовал ни в одном файле, а восемь живых приёмок службы (`roles-come-as-data-not-migrations.md`, `model-block-prose-has-a-home.md`, `model-generated-from-manifest.md`, `classes-form-of-role-right.md`, `retire-tenant-condition-surface.md`, `system-role-segments-resolve.md`, `module-manifest-resources-roles-deprecated.md`, `module-manifest-roles-and-seed-grants.md`) продолжали называть его своим.

Предмет

Приёмка ссылается на пробу, чтобы читатель мог её открыть и прогнать. Проба переживает не всякую правку: её переименовывают, сводят с соседней, снимают вместе с предметом — и делают это в СВОЁМ изменении, которое чужой приёмки не касается. Тогда координата остаётся стоять, а функции за ней нет.

Класс — утверждение, пережившее свой предмет, и он опаснее обычной устаревшей строки: следующий идёт по названному адресу, не находит ничего и делает вывод О ДЕРЕВЕ, а не о документе.

Почему координатой считается ТОЛЬКО целый пролёт кода

Имя пробы встречается в приёмке в трёх видах, и лишь один из них — координата:

`TestFoo`                            — КООРДИНАТА: пролёт целиком есть имя
`go test -run '^TestFoo$' -count=1`  — предикат: имя стоит внутри команды
«проба TestFoo снята вместе с предметом» — проза разбора

Проверка по подстроке краснела бы на втором и третьем, то есть на СОБСТВЕННОМ объяснении документа. Поэтому документ читается РАЗОБРАННЫМ: огороженные блоки кода пропускаются целиком, а из строки берутся только пролёты `…`, чьё содержимое ЦЕЛИКОМ есть имя пробы.

Почему резолв по ПРЕФИКСУ, а не по равенству

Корпус называет пробу двумя законными формами: полным именем и ИДЕНТИФИКАТОРОМ СЦЕНАРИЯ (`TestIAMCT112`), которым отбирают семейство — `-run '^TestIAMCT112'`. Отсюда правило: координата резолвится, если ОБЪЯВЛЕННОЕ имя начинается с неё. Направление существенно: `TestFoo‹хвост›` → `TestFoo` этим правилом не прощается — объявленное короче координаты и её префиксом не является.

НАЗВАННЫЙ ДОМ: чужой репозиторий — вне суждения, но НЕ прощён

Служба вынесена из монорепо (`kacho#2598`), и приёмки уехали вместе с ней, а гейты дерева остались там, где судят своё дерево. Отсюда третья форма записи координаты:

`TestFoo`                              — координата ЭТОГО дерева, судится;
`PRO-Robotech/kacho:TestFoo`           — координата ЧУЖОГО дома;
`PRO-Robotech/kacho@d941344bd9:TestFoo` — чужой дом, связанный ревизией.

Форма ВЗЯТА, а не придумана здесь: `owner/name:` у координаты ввёл `PRO-Robotech/kacho-workspace` `scripts/docs-gate/check-03-holding-claim-resolves.py` по тому же поводу — вынос службы оставил его координаты нерезолвящимися в стволе продукта. Оттуда же довод в пользу двоеточия: спутать `owner/name:путь` с git-формой `<ревизия>:<путь>` нельзя, потому что слева требуется слэш, которого у ревизии не бывает. Отличие одно и вынужденное: там дом стоит у ПУТИ, здесь — у ИМЕНИ, потому что координата ЭТОГО гейта и есть имя.

Чужой дом ВНЕ суждения по построению: ни подтвердить, ни опровергнуть объявление функции в чужом репозитории этот гейт не может — дерева рядом нет, а сеть в прогоне гейта запрещена. Доктрина в дереве уже есть и здесь не заводится второй раз: `carried_coordinate_ledger.go` §«Кросс-репо координата — вне суждения ОБОИХ сторон».

РЕЗОЛВ в названном доме здесь НЕ делается, и это остаток с предметом, а не граница по незнанию: воркспейс его умеет (опознаёт дерево идентичностью `origin`, отвечает третьей категорией, когда дерева рядом нет), но в конвейере службы чужого дерева нет НИКОГДА — ветвь была бы вечно-пустой, то есть мёртвым стражем ровно того вида, который запрещает ban #16. Заводится вместе с резолвером дома и инъекцией на двух синтетических деревьях — `kaname#44`.

«Вне суждения» отличается от «прощено» ТРЕМЯ свойствами, и все три обязательны:

  1. чужая координата СЧИТАЕТСЯ, а её дома ПЕЧАТАЮТСЯ переписью. Дом, стоящий в корпусе один раз, тем самым виден — опечатка в имени репозитория не уходит молча. Связанные РЕВИЗИЕЙ считаются отдельно: они не проверяемы даже там, где дерево дома есть;
  2. приставка, домом НЕ являющаяся (`kacho:TestFoo`, `IAM-MV-04:TestFoo`), — НАХОДКА. Без этого любой не-разобранный префикс снимал бы координату с суждения, и приставка стала бы способом спрятать адрес, а не назвать дом;
  3. ДВА СТОРОЖА, и живут они в гейте (`acceptance_probe_coordinate_test.go`), а не здесь, потому что им нужен дом ЭТОГО дерева — ядро судит значения, и для него всякий непустой дом чужой. Первый отвергает корпус, где ВСЕ координаты объявлены чужими: судить нечего, и «находок ноль» означало бы «ноль прочитанного». Второй ловит СВОЙ дом, названный чужим (`PRO-Robotech/kaname:TestFoo`), — иначе всякая мёртвая координата закрывается припиской своего же имени репозитория. Дом дерева выводится из `go.mod` (`OwnHomeOfTree`), а не складывается литералом.

Замер перед введением формы: пролётов вида `<что-то>:<имя-пробы>` в корпусе приёмок — НОЛЬ в обе стороны (и разобранных домом, и не разобранных), то есть правило 2 не краснеет ни на одной существующей строке.

applier_never_deletes.go — разбор пакета применителя ролей на предмет удаления строки роли (приёмка `docs/engineering/acceptance/roles-come-as-data-not-migrations.md` §3.1, держатель Г3; сценарий MOD-RD-15).

Порт с монорепо (`internal/repohygiene/applierneverdeletes.go`, снят вынесением службы — `kacho#2597`). Осталось дословно: имя функции гейта (`TestMODRD15ApplierNeverDeletesARoleRow`), сам разбор (обе оси, оба образца) и текст находки. Изменилось: пакет-цель — `internal/apps/kaname/moduleroles` (в монорепо — `services/iam/internal/apps/kaname/moduleroles`), путь-константа без префикса `services/iam/`.

Предмет

Роль с выдачами удалить нельзя (`ON DELETE RESTRICT`) — применитель встал бы на первой же роли, которой кто-то пользуется. Форма отзыва роли выбрана решением: строка ПОМЕЧАЕТСЯ снятой, а не удаляется (`docs/engineering/architecture/role-withdrawal-is-a-mark.md`).

Что здесь считается находкой — ДВЕ оси, и первая сильнее

  1. порт применителя объявляет ОДНОЗНАЧНО удаляющий глагол НАД СТРОКОЙ РОЛИ (судит имена методов интерфейса: глагол + предмет глагола);
  2. в пакете стоит оператор `DELETE` над таблицей ролей строковым литералом узла разбора, а не подстрокой текста.

Чего разбор НЕ видит — названо, а не спрятано

Запрос, собранный из кусков в рантайме, и запрос, приехавший параметром — первое не встречается в дереве, второе ловит первая ось: чтобы позвать чужое удаление, порт обязан его объявить.

assertion_admission_calls.go — разбор обращений к базе, разложенных по функциям (приёмка F2, сценарий F2-28).

Порт с монорепо (`internal/repohygiene/assertionadmissioncalls.go`, снят вынесением службы — `kacho#2597`). Держатель этого гейта уже цитируется ПРОД-КОММЕНТАРИЕМ в дереве службы: `internal/repo/kaname/pg/client_assertion_replay_repo.go` — «Гейт TestAssertionAdmissionIsASingleDatabaseCall стережёт это число». Осталось дословно: имя функции гейта, сам разбор и текст находки. Изменилось: путь без префикса `services/iam/`.

Предмет

Число обращений к базе внутри одной функции. Допуск однократности обязан делать РОВНО ОДНО: «не предъявлялось ли уже» и «погасить» неделимы, и неделимыми их делает первичный ключ таблицы, а не аккуратность вызывающего. Пара «посмотреть — записать» проходит ВСЕ последовательные пробы: окна между чтением и записью при последовательном прогоне не существует.

Почему владелец соединения берётся из ОБЪЯВЛЕНИЯ ТИПА, а не из списка имён

Разбор читает объявления структур файла и берёт в носители соединения те поля, ЧЕЙ ТИП называет драйвер базы. Переименование поля исход не меняет, чужое одноимённое поле находкой не становится.

authz_wrapper_outcome_lanes.go — булева ОБЁРТКА вопроса о правах не вызывается там, где у неё есть парная форма с исходом (задача #1045).

Порт с монорепо (`internal/repohygiene/authzwrapperoutcomelanes_test.go`, снят вынесением службы — `kacho#2597`). В кaname живёт как минимум одна такая пара: `authzguard.SubjectIsClusterAdmin` (bool) рядом с `SubjectIsClusterAdminE`/`SubjectIsClusterAdminPlainE` (bool, error) — то есть предмет ЖИВ, и гейт переносится. Изменилось: перечень «прод-корней» и путь модуля читаются из СВОЕГО дерева, а не из монорепо; имя гейта сохранено дословно.

Что ищется — СВОЙСТВО, и оно ВЫВОДИТСЯ ИЗ ДЕРЕВА

Перечня имён у этого гейта нет и быть не может: пары выводятся —

F  — экспортированная функция пакета, возвращающая РОВНО `bool`;
FE — функция ТОГО ЖЕ пакета с именем `F`+`E` либо `F`+`PlainE`,
     возвращающая `(bool, error)`.

Есть пара ⇒ автор пакета УЖЕ объявил, что у этого вопроса три исхода, а не два. Значит вызов булевой половины из ЧУЖОГО пакета — выбор, а не необходимость, и выбран он в пользу формы, из которой «хранилище не ответило» достать нельзя.

Граница названа честно

Вызовы ВНУТРИ пакета, объявившего пару, под гейт не подпадают: там булева половина и есть тело обёртки. Узнавание идёт по ИМЕНИ ИМПОРТА файла, а не по типам.

basic_credential_proof_run.go — доказательство формы базового удостоверения обязано ПРОИЗВОДИТЬСЯ ПРОГОНОМ, а не просто лежать в дереве (`PRO-Robotech/kacho#1253`).

Порт с монорепо (`internal/repohygiene/basiccredentialproofrun_test.go`, снят вынесением службы — `kacho#2597`). Осталось дословно: имя функции гейта, сам разбор `run:`-тел workflow и текст находки. Изменилось: пути без префикса `services/iam/`, конвейер — `.github/workflows/` СВОЕГО репозитория.

Предмет

Во всём сквозном прогоне не было НИ ОДНОГО зелёного утверждения, читающего секрет базового удостоверения из УСПЕШНОГО ответа. Проверка, которую никто не зовёт, ничего не производит: её зелёное существует только в чужой голове.

Читается ИСПОЛНЯЕМАЯ часть, а не текст

YAML разбирается, берутся тела `run:`, и из них выбрасываются строки оболочечных комментариев. Гейт по подстроке краснел бы на собственном объяснении и зеленел бы на шаге, откуда вызов сняли, а комментарий оставили.

build_stamp_word.go — разбор объявления строковой константы синтаксическим деревом, а не текстом.

Предмет

Слово «сборка штамп не проставила» стоит в прозе (шапках, документации) не реже, чем в объявлении. Поиск по подстроке нашёл бы собственное объяснение и остался бы зелёным при разошедшихся объявлениях — узел объявления константы судится вместо строки файла ровно по этой причине.

carried_coordinate_ledger.go — разбор ведомости координат, переносимых до снятия внешнего провайдера аутентификации клиента (приёмка F2, сценарий F2-46, §9.4).

Порт с монорепо (`internal/repohygiene/carriedcoordinateledger.go`, снят вынесением службы — `kacho#2597`). Осталось дословно: имя функции гейта (`TestCarriedCoordinateLedgerExpiresOnItsOwn`), сам разбор и словарь исходов. Изменилось: путь ведомости без префикса `services/iam/` (`docs/engineering/architecture/client-assertion-carried-over-coordinates.md`) и область зеркальной колонки — раньше сужалась до `services/iam/` внутри монорепо, теперь это ВЕСЬ репозиторий службы (у неё нет больше соседей по дереву). Ведомость службы, будучи скопирована целиком, несла координаты со старым префиксом `services/iam/` — они не резолвились НИ ОДНА (кросс-репо staleness, а не намеренное послабление); исправлено тем же изменением, которым перенесён гейт.

Предмет

Фаза F2 завела принимающую сторону и ничего не сносит. У каждой координаты, дожившей до снятия внешнего провайдера, обязан быть назван исход из закрытого словаря; четвёртого — «осталось как есть, потому что не заметили» — не существует. У исхода «оставлено» обязан быть ПРЕДИКАТ СНЯТИЯ.

Гейт двусторонний

  • ПОЛНОТА: координата, живущая в дереве и не названная ведомостью, — находка;
  • САМОИСТЕЧЕНИЕ: запись, чьей координаты в дереве больше нет, — находка.

Кросс-репо координата — вне суждения ОБОИХ сторон

Ведомость несёт координаты не только своего репозитория (напр. `services/registry/...` — сервис реестра остался в монорепо продукта, а не уехал со службой доступа). Такую координату этот гейт не проверяет НИ ПОЛНОТОЙ, ни САМОИСТЕЧЕНИЕМ: он не может ни подтвердить, ни опровергнуть существование файла в ЧУЖОМ репозитории. «В скоупе» решает первый сегмент пути — совпадает с одним из ФАКТИЧЕСКИ отслеживаемых верхних каталогов своего дерева.

catalog_check_wiring.go — сверка каталога прав с копией края обязана быть ЗВАНОЙ конвейером, а не просто существующей целью Makefile.

ПРЕДМЕТ У ГЕЙТА ФОРМА-ПРОВЕРКА, А НЕ ФОРМА-СИНХРОНИЗАЦИЯ (kaname#15)

Портированная редакция требовала вызова `sync-permission-catalog` — и была НЕИСПОЛНИМА by construction: эта цель ПИШЕТ файл. Задание, правящее дерево, вердикта о дереве не даёт: «копии совпали» после собственного `cp` истинно всегда, а рабочая копия ранера уезжает от коммита. Поэтому решение записано в самом рецепте, а гейт судит его: конвейер зовёт `check-permission-catalog` (ничего не пишет, годна обеим посадкам) и НЕ зовёт пишущую форму, которая объявлена ручной операцией выпуска.

Отсюда и переименование пробы: портированное имя было `TestCatalogSyncTargetIsCalledByThePipeline`, и после смены предмета оно утверждало бы неверное — цель синхронизации конвейером не звана НАМЕРЕННО.

Вторая половина того же решения — ОТКУДА БЕРЁТСЯ КОПИЯ КРАЯ. Замер, которого у портированной редакции не было: копия края приезжает в самостоятельный клон МОДУЛЕМ-ПИНОМ платформы (`go.mod`), поэтому сверка исполнима и здесь, а не только в полном чекауте монорепо. Предикат и его граница — в рецепте, у самой цели.

Порт с монорепо (`internal/repohygiene/catalogcheckwiring_test.go`, снят вынесением службы — `kacho#2597` с заявлением «предмет уехал вместе со службой»). Заявление НЕВЕРНО для kaname: своя половина цепочки уехала СЮДА — цель `sync-permission-catalog` объявлена в корневом Makefile службы (`GATEWAY_CATALOG`/`IAM_CATALOG_EMBED`), и её собственный комментарий говорит: «Копия каталога у iam ОБЯЗАНА побайтово совпадать с копией шлюза». Предмет, которым монорепошный гейт стерёг ПРОВЯЗКУ, здесь тот же самый, только половина цепочки живёт по обе стороны границы репозитория.

Здесь СУЖЕННАЯ форма монорепошного гейта: он спрашивал достижимость через ОБЩИЙ анализатор рецептов Makefile (`gatetargetwiring.go`, там же судят гейты `services/*`), которого в этом репозитории нет и заводить его ради двух целей с нулём зависимых — избыточно (ни у `check-permission-catalog`, ни у `sync-permission-catalog` сегодня НЕТ ни одной цели-потребителя внутри Makefile: обе стоят сами по себе, реаситься до них нечему). Поэтому проверяется прямая достижимость: зовёт ли ХОТЬ ОДИН шаг конвейера имя цели напрямую (`make check-permission-catalog` либо `make -C … <цель>`). Появится цель-потребитель — предикат обязан расшириться до реситься-графа, как в монорепо; это отдельное изменение, а не молчаливое сужение.

Чем этот гейт НЕ является

Он не сверяет копии побайтово — это отдельный предмет, и живёт он внутри рецепта `check-permission-catalog`. Предмет ЭТОГО гейта — ПРОВЯЗКА: существует ли у сверки вызывающий среди того, что исполняется САМО, и не зовёт ли конвейер пишущую форму вместо неё.

catalog_writer_lock.go — всякий прод-писатель строк `kaname.catalog_*` берёт глобальный транзакционный замок каталога (приёмка `docs/engineering/acceptance/plan-confirms-what-apply-withdraws.md` §7, объём О11, держатель Г1; kacho#1034).

Порт с монорепо (`internal/repohygiene/catalogwriterlock.go`, снят вынесением службы — `kacho#2597` с заявлением «обход служб жив, но исполняемых операторов записи в этот каталог ноль». Заявление было верно ТОЛЬКО для монорепо (там дерево служб доступа исчезло) — приёмка сама называет отсутствие держателя пунктом Н5 и требует его. Механизм в дереве службы ЖИВ дословно: `internal/repo/kaname/pg/catalog_writer.go` несёт `CatalogLockKey`/`LockCatalog` (`pg_advisory_xact_lock`) и три `UpsertModule`/`UpsertResource`/`UpsertVerb` на том же получателе (`catalogWriter`) — единица суждения по-прежнему сходится. Осталось дословно: весь алгоритм (регулярки, разбор, единица суждения, тексты находок). Изменилось: путь-константа без префикса `services/iam/`, обход — от корня СВОЕГО репозитория целиком (предмет запрета — «ВСЯКИЙ прод-писатель», не только сегодняшний).

Предмет

Подтверждение применения (отпечаток состояния модуля) есть CAS ТОЛЬКО потому, что между чтением отпечатка и записью строк не может встать второй писатель. Обеспечивает это не сравнение, а `pg_advisory_xact_lock`, взятый В ТЕЛЕ КАКОГО-ЛИБО МЕТОДА писателя (гейт не проверяет порядок — это держит интеграционная проба, не разбор).

Единица суждения — ТИП (получатель метода), а не файл и не пакет-вызывающий

Замок и запись могут лежать в РАЗНЫХ методах ОДНОГО получателя в ОДНОМ пакете (как здесь: `LockCatalog` и `UpsertModule` — оба методы `catalogWriter` в `internal/repo/kaname/pg`) — тогда они СХОДЯТСЯ в одну единицу суждения и гейт молчит. Замок, взятый ЧУЖИМ пакетом (например, вызывающим use-case, держащим порт `CatalogWriter`), этот гейт НЕ видит — он не о том, кто ЗОВЁТ замок, а о том, что писатель СПОСОБЕН его взять (несёт метод, который это делает).

census_fixture_producer.go — проба, которая пересчитывает ВСЕ строки таблицы, не вправе наполнять её сама в обход производителя.

Порт с монорепо (`internal/repohygiene/censusfixtureproducer_test.go`, снят вынесением службы — `kacho#2597`). Гейт уже цитируется ШЕСТЬЮ местами в дереве службы (прод-код `internal/repo/kaname/pg/scalegrid/{census,seed}.go` и пробы `relverdict`/`parentedge`) как «гейт дерева `TestCensusFixturesSeedThroughTheProducer`» — предмет жив, и цитаты уже ждут держателя. Осталось дословно: имя функции гейта, разбор и текст находки. Изменилось: пакет-производитель узнаётся ОДНОЙ величиной (каталог = хвост импорта: у самостоятельного модуля службы расхождения, из-за которого монорепо держало ДВЕ константы, больше нет), обход — от корня СВОЕГО модуля, без сегмента `services/iam`.

Предмет

Утверждение вида «у каждой строки зеркала есть цепь предков» — квантор по всему множеству. Если множество наполнила сама проба прямой записью в таблицу, утверждается свойство ФИКСТУРЫ: она положила ровно то, что потом пересчитала. Такая проба остаётся зелёной, даже если производитель перестал писать цепь ЦЕЛИКОМ.

Единица суждения — ПАКЕТ, а не файл

В Go все `_test.go` каталога с одним именем пакета собираются в один бинарь и делят помощников. Проба, сеющая через помощника из соседнего файла, сеет через производителя ровно так же.

Разбор идёт по СТРОКОВЫМ ЛИТЕРАЛАМ, а не по тексту файла

Текстовый поиск нашёл бы обе примеет и в комментарии, объясняющем эту же дисциплину (в том числе в шапке самой пробы полноты).

client_expiry_immutable.go — разбор операторов правки, разложенных по столбцам (приёмка F2, §9.4, решение §2.10).

Порт с монорепо (`internal/repohygiene/clientexpiryimmutable.go`, снят вынесением службы — `kacho#2597` с заявлением «снято вместе с предметом: таблицы user_oauth_clients в применённых миграциях дерева нет». Заявление было верно ТОЛЬКО для монорепо (там дерево служб доступа исчезло целиком) — в дереве службы обе таблицы объявлены (сегодня — в консолидированной `internal/migrations/0001_initial.sql`), с колонкой `expires_at`. Осталось дословно: имя функции гейта, сам разбор и текст находки. Изменилось: пути без префикса `services/iam/`.

Предмет

Срок клиента неизменяем после создания. На этой предпосылке стоит структурная гарантия: срок выданного токена не превышает остатка срока клиента, и проверять это на пути запроса не нужно ровно потому, что срок не двигается. Сдвинь его — и гарантия держится ничем.

Что здесь считается ПРАВКОЙ СТОЛБЦА

UPDATE t SET expires_at = $2 WHERE id = $1   ← правка: столбец назван в SET
INSERT INTO t (…, expires_at) VALUES (…)     ← СОЗДАНИЕ: срок назначается
SELECT expires_at FROM t WHERE id = $1       ← чтение

Чего разбор НЕ видит — названо, а не спрятано

  1. оператор, собранный из частей во время выполнения;
  2. правка через функцию базы или триггер, а не оператором `UPDATE`;
  3. `UPDATE` без имени таблицы в том же литерале.

derived_id_single_source.go — разбор объявлений деривации детерминированного идентификатора (приёмка `docs/engineering/acceptance/roles-come-as-data-not-migrations.md` §3.3; приёмка `seed-identity-names-its-own-service.md` §6, держатель `TestDeterministicIDDerivationIsDeclaredOnce`).

Предмет

Идентификатор системной роли, служебной учётки модуля и связки OAuth есть функция ИМЕНИ: применённые миграции адресуют строки выражением `'<префикс>' || substr(md5('<имя>'), 1, 17)`, и на эти идентификаторы ссылаются выданные права. Вторая копия формулы разойдётся с первой МОЛЧА — и разойдётся ровно там, где обе отвечают «идентификатор вычислен»: полученное значение остаётся синтаксически верным и перестаёт находить строку. Наблюдаемо это только по отказу в доступе у арендатора, у которого право не отзывали.

Порт с монорепо (`internal/repohygiene/derivedidsinglesource.go`, снят вынесением службы — `kacho#2597`): формула не переехала с кодом целиком, а была написана заново рядом с единственным домом — `internal/domain/derived_id.go`, чей собственный комментарий уже называет этот гейт держателем и ожидает его присутствия. До этой правки объявление не охранялось ничем: единственность была фактом дня, а не удержанным свойством.

Что здесь считается ОБЪЯВЛЕНИЕМ

Файл прод-дерева, импортирующий `crypto/md5`. Иных потребителей MD5 в этом продукте нет и быть не должно: как примитив защиты MD5 запрещён (сам импорт в доме несёт отметку `#nosec G501`), а как контрольная сумма он здесь не применяется — значит всякое его вычисление есть деривация идентификатора.

Распознаватель знает ВСЕ формы импорта by construction

Он читает ПУТЬ импорта из разобранного дерева, а не имя пакета в тексте, поэтому псевдоним (`md5b "crypto/md5"`), точечный (`. "crypto/md5"`) и пустой (`_ "crypto/md5"`) импорты опознаются одинаково. Упоминание пути в комментарии или в строковом литерале импортом НЕ является — разбор судит узел, а не подстроку.

Чего разбор НЕ видит — названо, а не спрятано

  1. **своя реализация MD5 руками** — развёрнутый алгоритм без импорта. Это другой класс, и ловит его обзор, а не этот гейт;
  2. **вычисление на стороне базы** — `md5()` в тексте миграции. Это ВТОРАЯ сторона равенства, а не вторая копия Go-функции, и этот гейт её не судит.

injection_named_exists.go — разбор: доказательство способности упасть, НАЗВАННОЕ пробой, обязано существовать (#2479).

───────────────────────────────────────────────────────────────────────────── ПРЕДМЕТ

Проба, чья шапка говорит «способность упасть доказана инъекцией — такой-то файл», делает утверждение о ДЕРЕВЕ. Утверждение это не компилируется и никем не сверяется: файл переименовали, унесли вместе с рефакторингом либо не написали вовсе — а шапка продолжает обещать доказательство. Гейт, потерявший способность краснеть, на чистом дереве выглядит РОВНО ТАК ЖЕ, как исправный, поэтому обещание доказательства читается как само доказательство.

Замер, из которого разбор выведен: на ревизии заведения упоминаний в комментариях было 122, не резолвилось ОДНО — шапка гейта осей адреса называла файл, которого в дереве не было с первого её дня.

───────────────────────────────────────────────────────────────────────────── У ЭТОГО РАЗБОРА ЕСТЬ БЛИЗНЕЦ В ПЛАТФОРМЕ — И ЭТО НАЗВАНО, А НЕ УМОЛЧАНО

Тот же разбор, поднятый до корня МОНОРЕПО, живёт в `tools/injectionproofgate` (задача #2519): этот обходит корень своего модуля и о платформе не высказывается — в самостоятельном клоне службы монорепо нет вовсе. Обещания соседних модулей платформы этому модулю не принадлежат.

Свести обе реализации в одну сегодня НЕЛЬЗЯ: модуль службы пинит платформу псевдоверсией, поэтому импортировать пакет, которого в пинованной ревизии ещё нет, он не может. Предикат сведения: пин указывает на ревизию, где тот пакет существует; до этого две реализации об одном предмете живут рядом осознанно.

───────────────────────────────────────────────────────────────────────────── СУДЯТСЯ КОММЕНТАРИИ, И ЭТО РЕШЕНИЕ, А НЕ УПРОЩЕНИЕ

Координата доказательства пишется ПРОЗОЙ — это её законное место, и разбор читает разобранные группы комментариев, а не сырой текст файла. Строковые литералы намеренно НЕ судятся: в них живут синтетические имена, которые инъекции кладут во временные каталоги, и дерева они не называют вовсе. Судить их значило бы краснеть на чужой фикстуре — то есть на собственном доказательстве.

Величина «в строках» всё равно печатается переписью: молчание о целом виде вхождений неотличимо от их отсутствия.

───────────────────────────────────────────────────────────────────────────── ЗАКОННЫХ ФОРМ КООРДИНАТЫ ТРИ, И РАСПОЗНАВАТЕЛЬ ЗНАЕТ ВСЕ ТРИ

Форма, о которой он не знает, даёт не красное и не зелёное, а МОЛЧАНИЕ. Измерено на этом дереве: наивный резолвер («имя рядом с называющим») дал 4 непопадания из 138, и все четыре оказались ЗАКОННЫМИ формами, а не находками.

СОСЕД        `<имя>_injection_test.go` — файл в каталоге называющего;
ПО МОДУЛЮ    то же имя, но доказательство лежит в ДРУГОМ пакете модуля
             (проба одного пакета ссылается на инъекцию соседнего);
КООРДИНАТОЙ  `internal/<пакет>/<имя>_injection_test.go` — путь от корня
             МОДУЛЯ службы, а не от корня монорепо: в самостоятельном клоне
             приставки `services/iam/` нет, и счёт от неё указывал бы наружу
             дерева.

Имя в примерах — ОБРАЗЕЦ с угловыми скобками, а не координата, и это не подгонка текста под инструмент: координата, названная в прозе, есть утверждение о дереве, и выдуманная — ложное. Разбор судит объявленные координаты, поэтому пример обязан не выглядеть ею.

module_identity_seeded_only_by_baseline.go — личность модуля заводит ТОЛЬКО применённая базовая миграция (ПР-5 приёмки WIRE-1, задача продукта #2098).

Порт с монорепо (`internal/repohygiene/moduleidentityseededonlybythebaseline_test.go`, снят вынесением службы — `kacho#2597`). В монорепо предикат жил ЦЕЛИКОМ внутри `_test.go` (пакет-«внешний тест», без отдельного `.go`); здесь он вынесен в не-тестовый файл, потому что кроме гейта его читает пересчёт.

ПРЕДМЕТ (дословно из монорепо)

Личности модулей посеяны БАЗОВОЙ миграцией, то есть приезжают и в установку, где платформы нет вовсе. Строка, которую арендатор, развернувший службу доступа отдельно, нести не обязан, — это данные окружения, и в миграцию они не идут ни при каких условиях (`data-integrity.md` §«Данные СТЕНДА заводятся посевом»).

СОСТОЯНИЕ НА ДЕНЬ ПЕРЕНОСА — ДРУГОЕ, чем в монорепо, и это названо числом

В монорепо (на день заведения гейта) долг был НЕПОДВИЖЕН: применителя у раздела `seed` манифеста не было, применённую миграцию не поправить (ban #5), а свежая установка без платформы несла пять личностей чужого продукта.

В kaname этот долг ЗАКРЫТ отдельной задачей — #2452 (`20260909202745_module_identities_leave_the_baseline.sql`, применена ДО выноса службы из монорепо, то есть закрытие произошло в исходном дереве, а не в этом порту): пять личностей (`kacho-vpc`, `kacho-compute`, `kacho-nlb`, `kacho-registry`, `kacho-storage`) СНЯТЫ этой миграцией, а применитель заведён (`internal/apps/kaname/moduleseed`), позван из композиционного корня. Остаются ДВЕ: `kacho-bootstrap-admin` (личность самой службы, не модульная — `IsModule()` false) и `kacho-api-gateway` (модульная, остаток, названный вслух в шапке миграции — у края манифеста нет вовсе, применителя заводить некому, снимается вместе со своим производителем).

Обе остаются посеянными БАЗОВОЙ миграцией (`0001_initial.sql`, первой в порядке применения) — то есть `moduleIdentitiesSeededOutsideTheBaseline` сегодня находит ноль нарушений. Это НЕ делает гейт вакуумным: он и дальше судит регресс — появление НОВОЙ миграции, сеющей модульную личность НЕ через применитель (ровно тот класс, который #2452 только что убрал). Обновлять эту прозу нормой `#2098` больше не нужно: предмет её первой половины («свежая установка без платформы несёт ноль модульных СА, кроме остатка api-gateway») закрывает #2452, а не этот гейт; гейт стережёт РОСТ.

РОДСТВО, КОТОРОЕ НАДО НАЗВАТЬ — И РАЗЛИЧИЕ (дословно из монорепо)

У ролей модуля такой судья есть отдельным семейством (Г2, `migrationnotawriterofmodulerole`, НЕ входит в этот батч) — запрещает миграции писать роль модуля, у которого есть манифест, ПОЛНОСТЬЮ (у ролей применитель есть). У личностей применитель тоже теперь есть — но не для ВСЕХ (api-gateway — остаток), поэтому запрет здесь остаётся УЗКИМ: не «личность модуля не сеет никакая миграция», а «личность модуля не сеет НИКАКАЯ МИГРАЦИЯ КРОМЕ БАЗОВОЙ» — тот же периметр, что был в монорепо.

ЧЕГО ГЕЙТ НЕ ЛОВИТ (названо, чтобы на него не сослались шире предмета)

Он про ПОСЕВ, а не про живую базу: строку, заведённую не миграцией, он не читает by construction. И он ничего не утверждает о ПОЛНОТЕ посева.

named_verb_form_expiry.go — разбор ПАРЫ: отвергается ли поимённая форма права роли прод-кодом манифеста и есть ли в дереве пробы шести сценариев её полноты. Находка — ровно одно их сочетание: форма принята, полнота не проверяется.

Порт с монорепо (`internal/repohygiene/namedverbformexpiry.go`, снят вынесением службы — `kacho#2597`). Изменилось: пакет (`repohygiene` → `check`), путь домена (`services/iam/internal/manifest/` → `internal/manifest/`, префикс `services/iam/` снят — в kaname код службы лежит от корня репозитория). Осталось дословно: разбор узла (не подстроки), закрытый перечень шести сценариев, различение двух законных причин молчания.

Почему этот перенос ОБЯЗАТЕЛЕН, а не факультативен

Прод-код `internal/manifest/roleexport/check.go` (написанный ДО выноса службы, когда `internal/repohygiene` был тем же репозиторием) буквально называет держателя: «ЧЕМ ДЕРЖИТСЯ ЗАПРЕТ ... гейтом дерева `internal/repohygiene` `TestNamedVerbFormReturnsOnlyWithItsCompletenessCheck`». До этого файла держателя в дереве kaname не было ни одного: цитата утверждала присутствие проверки, которой не существует.

Предмет — ПАРА, а не любая её половина (дословно из монорепо)

Принять перечень ИМЁН, не умея проверить его полноту по классу, значит свести его к классу МОЛЧА и выдать право ШИРЕ просимого (замер приёмки: 55 вхождений из 92 в черновике vpc совпадают с именем класса). Молчание гейта законно ПО ДВУМ причинам, и различать их вызывающему не нужно: форма отвергается сентинелом (возвратов > 0) ЛИБО она вернулась вместе со своей проверкой (все шесть проб MOD-RL найдены).

Замер на дне переноса (kaname@issue-2597, kacho#2597)

Сентинел `ErrRoleRuleVerbsRetired` в `internal/manifest/` не объявлен и не возвращается (задача #1844 сняла его вместе с формой, которую он отвергал — `f5bb097046` в истории монорепо). Все шесть проб MOD-RL (04, 04a, 18, 18a, 19, 19a) в дереве ЕСТЬ (`internal/manifest/rolenamedverbs_test.go`, `internal/manifest/roleexport/namedverbs_test.go`). Гейт сегодня молчит по ВТОРОЙ законной причине — форма вернулась вместе со своей проверкой, ровно как того требует приёмка #1844, — а не по первой. Это и есть содержание переноса: без держателя регресс (снятие одной из шести проб при живом возврате перечня имён) не был бы замечен НИЧЕМ.

Чего разбор НЕ видит — названо, а не спрятано (дословно из монорепо)

Возврат сентинела, собранный в рантайме (через переменную, через таблицу видов). Такой формы в этом пакете нет, и появись она — гейт увидит ноль возвратов и покраснеет, то есть ошибётся В СТОРОНУ находки, а не молчания.

role_operation_response_state.go — анализатор «ответ операции над ролью не несёт вычисленного состояния, и это ОБЕСПЕЧЕНО, а не совпало».

Порт с монорепо (`internal/repohygiene/roleoperationresponsestate.go`, снят вынесением службы — `kacho#2597`). Дословно: весь анализатор. Изменилось: пакет (`repohygiene` → `check`); `ServiceRoot` теперь пустая строка у вызывающего — в kaname дерево службы лежит от корня, а не под `services/iam`.

Предмет

Контракт роли обещает арендатору буквально следующее: нулевое значение `health` и `lifecycle` означает «ЭТИМ ОТВЕТОМ НЕ ВЫЧИСЛЕНО» и никогда «роль здорова» либо «роль объявлена»; его несут ответы операций `Create`/`Update`, а `Get` и `List` заполняют состояние всегда.

Обещание держится ТОЛЬКО ТЕМ, что производителя вычисленного состояния никто не звал на пути мутации. Свойство «by construction» тем и плохо, что его снятие ТИХОЕ.

Что судится

ПЕРЕВОДЧИК ответа операции над ролью — не-тестовая функция, у которой (а) результаты ровно `(*anypb.Any, error)`, (б) есть параметр типа `domain.Role` и (в) в теле стоит вызов перевода `dto.Transfer`. Каждый такой переводчик обязан звать проекцию (`domain.Role.WithoutComputedState`). Не зовёт — находка с координатой.

ЧЕГО ОН НЕ СУДИТ

  1. ПОЛНОТУ набора производных полей — предмет пробы самой проекции;
  2. ЛИШНЮЮ РАБОТУ — расход без последствий для контракта;
  3. ЧТЕНИЯ (`Get`/`List`) — у них результат не `(*anypb.Any, error)`.

Падает на ПУСТОМ ОБХОДЕ

Ноль прочитанных файлов, ноль разобранных функций либо ноль найденных переводчиков роли — «находок ноль» неотличимо от «прочитано ноль».

role_scope_chain_stays_empty.go — САМОИСТЕКАЮЩИЙ гейт решения «меточную ось сужать НЕ НАДО» (задача продукта #1913, приёмка `docs/engineering/acceptance/role-withdrawal-has-a-producer.md` §2.8, §10 шаг 8).

Порт с монорепо (`internal/repohygiene/rolescopechainstaysempty.go`, снят вынесением службы — `kacho#2597`). Дословно: весь предикат, распознаватели, закрытый перечень ярусных источников. Изменилось: пакет (`repohygiene` → `check`), путь-приставка обхода (`services/iam/` снята — в kaname код службы лежит от корня).

Что здесь стережётся — ФАКТ О ДЕРЕВЕ, на котором стоит решение

Приёмка решила НЕ сужать меточную ось выдачи по живости роли, и решение стоит целиком на одном факте: у роли МОДУЛЯ цепь областей ПУСТА, поэтому меточная выдача не достаёт её ни живую, ни снятую. Факт измерен и верен сегодня — и НЕ ЗАЩИЩЁН НИЧЕМ:

  • роль модуля всегда кластерного яруса, потому что `owner_module` и `cluster_id` пишет ОДИН оператор, а не ограничение схемы;
  • у роли кластерного яруса цепь пуста, потому что ни одна ветвь производителя звеньев её не отбирает: ветви берут `account_id` и `project_id`, а у роли модуля они пусты оба.

Значит производителя звена для `iam_role` может завести любая соседняя работа. Появится он — решение §2.8 станет неверным, а доступ вернётся МОЛЧА. Этот гейт и есть то единственное, что об этом скажет.

ЧТО СЧИТАЕТСЯ НАХОДКОЙ — ИСТОЧНИК ветви, а не имя типа

Ветвей, производящих звено для `iam_role`, две, и обе ЗАКОННЫ:

(5a) роль АККАУНТА  → источник `account_id`
(5b) роль ПРОЕКТА   → источник `project_id`

Находка — ТРЕТЬЯ ветвь: та, что производит звено для `iam_role`, не спрашивая ни одного из двух ярусных столбцов.

ЧЕГО РАЗБОР НЕ ВИДИТ — НАЗВАНО, А НЕ СПРЯТАНО

  1. звено, приехавшее ЗЕРКАЛОМ ресурса (writer одинаков для всех типов);
  2. запрос, собранный из кусков в рантайме;
  3. производителя вне дерева kaname.

role_verb_projection_sole_writer.go — разбор операторов записи в проекции роли: «кто АВТОРУЕТ строку, кто её СНИМАЕТ, переселяет ли снимающий-не-автор снятое» (порт с монорепо `internal/repohygiene/roleverbsolewriter_test.go`, держатель `TestIAMRV112_RoleVerbProjectionHasASoleWriter`, снят вынесением службы доступа — `kacho#2597`).

Предмет

Таблицы `kaname.role_verb` и `kaname.role_rule_ref` — проекции роли, то, из чего цепь вердикта собирает ответ «разрешено ли действие». Класс, ради которого гейт заведён: ДВЕ реализации знают, что такое законная строка, и расходятся МОЛЧА — обе компилируются, у обеих есть пробы, и различаются они только на входе, который ни одна проба не подаёт.

Единица — «авторует», а не «пишет» (решение kacho#1034, перенесено дословно)

Что такое законная строка, объявляет ТОЛЬКО `INSERT`: он называет колонки и значения. `DELETE`/`UPDATE` строку не авторуют — они её снимают или правят. Значит класс живёт в АВТОРСТВЕ, а не в записи вообще, и единица гейта — функция, ВНОСЯЩАЯ строку. Снимающий не-автор обязан ПЕРЕСЕЛИТЬ снятое в `kaname.role_grant_orphan` ТЕМ ЖЕ оператором — иначе отобранное право неотличимо от никогда не выданного.

Что изменилось при переносе, а что осталось дословно

Изменилось: пакет (`repohygiene` → `check`), путь-константа слоя (без префикса `services/iam/` — код лежит от корня модуля kaname), обход дерева (`gitenv`+`git ls-files` вручную → `github.com/PRO-Robotech/kacho/pkg/treecorpus` и `internal/testsupport/platformtree.RequireCorpus`, потому что предмет — внутри службы, дерева платформы модулю не нужно). Осталось дословно: имена таблиц-проекций, форма распознавателя (узел-литерал разобранного AST, а не текст файла), три оси проверки (авторство · переселение · слой) и имя держателя `TestIAMRV112_RoleVerbProjectionHasASoleWriter` — на нём стоят цитаты `docs/engineering/acceptance/role-verb-projection-sole-writer.md`, `rule-segments-have-a-referent.md` и `module-manifest-roles-and-seed-grants.md`, написанные ДО выноса, когда `internal/repohygiene` был тем же репозиторием.

Граница названа, а не умолчана

Гейт читает СТРОКОВЫЕ ЛИТЕРАЛЫ непроверочного кода Go, разобранного в дерево, и приписывает находку объемлющей функции. Он НЕ видит: SQL миграций (там запись законна — миграция и есть схема), проб (им положено готовить состояние), имя таблицы, собранное из кусков (слепая зона, предикатом по подстроке не ловится ничем).

seeded_service_accounts.go — разбор посева служебных учёток по цепочке миграций: вставка заводит строку, удаление её снимает.

Порт ОБЩЕГО вспомогательного разбора с монорепо (жил как package-private `seededSA`/`foldSeededServiceAccounts` в `internal/repohygiene/moduleserviceaccounthasacomponent_test.go`, снят вынесением службы — `kacho#2597`). Перенесён в НЕ-тестовый файл и экспортирован, потому что нужен как минимум ДВУМ гейтам монорепо: `moduleidentityseededonlybythebaseline` (перенесён этим же изменением, `module_identity_seeded_only_by_baseline.go`) и `moduleserviceaccounthasacomponent` (семейство, НЕ входящее в батч `kacho#2597` этого агента — предмета оно судит компоненты дерева, отдельная от «когда посеяно» ось). Держать разбор внутри теста значило бы, что следующий перенос его продублирует, не заметив: инструкция переноса прямо требует проверить, не появился ли общий помощник, и переиспользовать его — вместо копии.

system_role_row_is_never_deleted.go — разбор прод-кода kaname на предмет удаления строки СИСТЕМНОЙ роли (порт с монорепо `internal/repohygiene/systemrolerowisneverdeleted.go`, снят вынесением службы доступа — `kacho#2597`; решение `docs/engineering/architecture/role-withdrawal-is-a-mark.md`, задача продукта #1913).

Предмет

Роль, объявленная манифестом модуля, системная BY CONSTRUCTION: применитель ставит `IsSystem: true`, а `roles.is_system` вычисляется из `cluster_id`. Значит вопрос «как снять роль модуля» есть вопрос «что делает прод-код со строкой системной роли», и у него два взаимоисключающих ответа: удалить строку либо пометить её снятой. Линия выбрала ВТОРОЙ:

  1. **роль в работе не удалится вовсе.** Выдачи ссылаются на роль ключом `access_bindings_role_fk … ON DELETE RESTRICT`; удаление роли, за которой стоит хоть одна строка выдачи, отвергается `SQLSTATE 23503`;
  2. **если бы не отвергалось — три проекции уехали бы МОЛЧА**: селекторы правил, проекция глаголов и проекция объявленных сегментов;
  3. **отзыв перестал бы достигать ПРЕДЪЯВЛЕНИЯ наблюдаемо.** У снятой пометкой строки есть что читать на пути запроса; у удалённой читать нечего.

Что здесь считается находкой — ОДНА ось, и она про оператор

Оператор `DELETE` над таблицей `roles`, не сужённый на ПОЛЬЗОВАТЕЛЬСКУЮ роль. Сужение опознаётся в ДВУХ законных написаниях: `is_system = false` (и `NOT is_system`) — прямо по вычисляемой колонке; `cluster_id IS NULL` — по кластерному якорю, из которого она и вычисляется.

Ось судит СТРОКОВЫЙ ЛИТЕРАЛ узла разбора, а не текст файла: слово `DELETE` стоит и в комментариях, объясняющих сам запрет. Оператор отличается от ПРОЗЫ, цитирующей оператор, по ПРОДОЛЖЕНИЮ за именем таблицы (`WHERE`, `RETURNING`, `USING`, `;`, конец литерала) — привязки к началу строки недостаточно.

Что изменилось при переносе, а что осталось дословно

Изменилось: пакет (`repohygiene` → `check`), путь-константа (`iamGoPrefix = "services/iam/"` → обход всего дерева модуля без префикса, код лежит от корня). Осталось дословно: обе регулярки сужения и продолжения, форма распознавателя (`ScanRoleDeletes`), имя держателя `TestSystemRoleRowIsNeverDeleted`.

Чего разбор НЕ видит — названо, а не спрятано

  1. **миграции.** Каждая снимала роль удалением до решения — это СТАТУС-КВО, который решение и заменяет, а не находка сегодняшнего дерева;
  2. **каскад по чужому ключу.** Строка роли уезжает вместе со своим ярусом (аккаунт, проект). Для СИСТЕМНОЙ роли ярус — кластер;
  3. **запрос, собранный из кусков в рантайме** либо приехавший параметром.

token_claim_foreign_brand.go — разбор ИМЁН КЛЕЙМ выпущенного токена: не называет ли клеймо, которое читает предъявитель токена БЕЗ нашего исходного кода, чужой платформенный бренд (порт, СУЖЕННЫЙ ДО ОСИ А, с монорепо `internal/repohygiene/tokenclaimforeignbrand.go`, снят вынесением службы доступа — `kacho#2597`; задача продукта #2127, семейство приёмки IAM-SEV-NAME-05).

Предмет

Служба — самостоятельный продукт, ставящийся в чужом облаке. Норма разделения (решение владельца, kacho#2076): продукт наследует КОД, но не ИМЯ. Имя клейма читается оператором чужого облака без нашего исходного кода — достаточно раскодировать токен, — поэтому приставка имени клейма есть идентичность, а не код. Свой словарь — `kaname_`; чужой, платформенный — `kacho_`. `internal/domain/principal_claims.go` уже несёт три клейма формы `kaname_*` — предмет живой, не гипотетический.

Что здесь ПОРТИРОВАНО (ось А), а что НЕТ (ось Б) — сказано прямо

Монорепошный предок судил ДВЕ оси: А — имя клейма принадлежит своему словарю; Б — у имени из своего словаря нет двойника в чужом словаре НИГДЕ в отслеживаемом дереве, включая не-Go текст (посевные наборы, профиль развёртывания, собранные коллекции проб, клиентская страница). Здесь перенесена ТОЛЬКО ось А — разбор Go-кода по четырём позициям чеканки и чтения. Ось Б требует единого свода дерева kaname И его нынешних потребителей (документация арендатора, коллекции проб), который на дату переноса не собран в одном месте; расширение до неё — отдельная работа, не сделанная здесь умышленно, а не забытая молча.

Позиции, которые СУДЯТСЯ (перенесены дословно)

claims := map[string]any{"kacho_user_id": …}     ← ключ состава
pt, _ := claims["kacho_principal_type"].(string) ← чтение по имени
case "kacho_mfa_at":                             ← разбор по имени
verifiedClaim(vt, "kacho_principal_type")        ← имя передано вызовом

Чего разбор НЕ видит — названо, а не спрятано

  1. имя клейма, собранное из частей либо взятое переменной;
  2. приставка, отданная предикату (`strings.HasPrefix(k, "kacho_")`) — ось Б предка ловила и её; здесь эта форма вне наблюдения;
  3. не-Go текст.

token_claim_single_source.go — разбор мест, СОБИРАЮЩИХ состав утверждений выданного токена (порт с монорепо `internal/repohygiene/tokenclaimsinglesource.go`, снят вынесением службы доступа — `kacho#2597`; приёмка F2, сценарий F2-42, §2.11).

Предмет

Токен принципалу выдают ДВА пути: обратный вызов прежнего провайдера, пока он жив, и собственный эндпоинт (`internal/service/token_enrichment_own_lane.go`). Пока перечень утверждений и правила их вычисления живут у каждого свои, различие между ними НЕ ЯВЛЯЕТСЯ НИЧЬЕЙ НАХОДКОЙ: оно не выражено и потому не может покраснеть. Первая же правка одной стороны разойдётся с другой молча — у ПРИНЦИПАЛА, чей токен выдан не тем путём.

Что здесь считается СБОРКОЙ, а что ПОТРЕБЛЕНИЕМ

claims := map[string]any{"kaname_user_id": …, "kaname_account_id": …}  ← СБОРКА
return s.userTokenClaims(row, user, subject, hookCtx)                ← потребление

Потребителей должно быть МНОГО — они и есть цель: второй способ дойти до той же сборки, а не второй состав. Находкой является ВТОРАЯ СБОРКА.

Почему судится ИМЯ КЛЮЧА, а не тип отображения

Предмет — СЛОВАРЬ утверждений, поэтому место опознаётся по именам ключей, а тип значений в счёт не идёт.

Почему порог по числу ключей, а не «хоть один»

Префикс утверждений встречается и вне состава — им же названы метрики и поля контекста. Место, назвавшее один ключ, состава не объявляет.

Что изменилось при переносе, а что осталось дословно

Изменилось: пакет (`repohygiene` → `check`), путь-константа владельца (без префикса `services/iam/`). Осталось дословно: обе функции разбора, форма узла (составной литерал `map[string]…`, узел вызова), имя держателя `TestTokenClaimsAreAssembledInOnePlace`.

Чего разбор НЕ видит — названо, а не спрятано

  1. состав, собранный присваиваниями по одному ключу за раз;
  2. имя ключа, собранное из частей или взятое переменной;
  3. состав, объявленный в чужом языке.

withdrawal_producer_arrives_with_the_applier.go — разбор прод-дерева на СОГЛАСИЕ двух фактов: применитель ролей модуля приводится в действие, и производитель отзыва роли существует (порт с монорепо `internal/repohygiene/withdrawalproducerarriveswiththeapplier.go`, снят вынесением службы доступа — `kacho#2597`; задача продукта #1913).

Предмет

Сверка `moduleroles.Reconcile` объявляет вид расхождения `LiveNotDeclared` — «строка живёт, объявления у неё больше нет». Пока применитель НЕ приводится в действие в проде, отсутствие производителя снятия — остаток, а не дефект. Как только применитель начинают звать (`cmd/kaname/serve.go`, `moduleroles.NewApplier`, задача #1034/#2010 — В KANAME ЭТО УЖЕ ПРОИЗОШЛО), остаток становится дефектом, необратимым ДЛЯ АРЕНДАТОРА: роль, которую нельзя снять, живёт вечно, и право, выданное через неё, продолжает действовать после того, как модуль перестал её объявлять.

Что здесь считается находкой — ОДНО согласие, а не два запрета

Находка — состояние «применитель приводится в действие, производителя отзыва нет». Ни одна половина по отдельности находкой не является:

приводится в действие · производитель есть  → норма, работа сделана
приводится в действие · производителя нет   → НАХОДКА
не приводится        · производителя нет    → остаток
не приводится        · производитель есть   → норма, производитель приехал раньше

Обе половины судятся по УЗЛУ РАЗБОРА, а не по слову

«Применитель приводится в действие» — это ВЫЗОВ через импортированный пакет, а не упоминание его имени. «Производитель отзыва» — это оператор ЗАПИСИ над `roles`, ставящий пометку снятия В СПИСКЕ ПРИСВОЕНИЙ.

Что изменилось при переносе, а что осталось дословно

Изменилось: пакет (`repohygiene` → `check`), путь импорта применителя (без `services/iam/` — код лежит от корня модуля kaname: путь становится `github.com/PRO-Robotech/kaname/internal/apps/kaname/moduleroles`). Осталось дословно: обе регулярки, форма связывания импорта (по псевдониму, а не по последнему сегменту пути), различение узла разбора и узла-прозы, имя держателя `TestWithdrawalProducerArrivesWithTheApplier`.

Чего разбор НЕ видит — названо, а не спрятано

  1. миграцию как производителя (запрещена отдельным гейтом её же смысла);
  2. производителя вне дерева модуля;
  3. запрос, собранный из кусков в рантайме.

Index

Constants

View Source
const (
	// CarriedOutcomeKept — оставлено как путь к прежнему контуру. ТОЛЬКО этот
	// исход требует предиката снятия.
	CarriedOutcomeKept = "оставлено"
	// CarriedOutcomeRemoved — снято.
	CarriedOutcomeRemoved = "снято"
	// CarriedOutcomeRewritten — переписано на наш идентификатор.
	CarriedOutcomeRewritten = "переписано"
	// CarriedOutcomeNotSubject — предметом не является.
	CarriedOutcomeNotSubject = "не предмет"
)

Закрытый словарь исходов.

View Source
const (
	// CensusMirrorTable / CensusEdgeTable — таблицы, чья совместная
	// упомянутость в ОДНОМ SQL-литерале и есть перепись покрытия: она
	// спрашивает про рёбра у строк зеркала, то есть квантифицирует по
	// множеству.
	CensusMirrorTable = "kaname.resource_mirror"
	CensusEdgeTable   = "kaname.resource_parent_edge"
	// RawEdgeWriteMarker — прямая запись рёбер в обход производителя.
	RawEdgeWriteMarker = "INSERT INTO kaname.resource_parent_edge"
	// EdgeProducerCall — имя экспортированной функции-производителя.
	EdgeProducerCall = "UpsertTx"
	// EdgeProducerDir — каталог пакета-производителя, ОТ КОРНЯ своего модуля
	// (было `services/iam/internal/repo/kaname/pg/resource_mirror`).
	EdgeProducerDir = "internal/repo/kaname/pg/resource_mirror"
	// EdgeProducerImport — хвост пути импорта. В самостоятельном модуле
	// службы совпадает с каталогом (расхождение, из-за которого монорепо
	// нёс ДВЕ константы, было следствием сегмента `services/iam`, которого
	// у своего модуля нет).
	EdgeProducerImport = EdgeProducerDir
)
View Source
const (
	TokenClaimOwnNamespace     = "kaname"
	TokenClaimForeignNamespace = "kacho"
)

TokenClaimOwnNamespace / TokenClaimForeignNamespace — свой и чужой словарь.

View Source
const CatalogCheckTarget = "check-permission-catalog"

CatalogCheckTarget — имя цели Makefile, СВЕРЯЮЩЕЙ копии и ничего не пишущей. Ровно её обязан звать конвейер.

View Source
const CatalogLockKeyIdent = "CatalogLockKey"

CatalogLockKeyIdent — имя константы ключа.

View Source
const CatalogLockKeyValue = "kaname.module_catalog"

CatalogLockKeyValue — её значение (принимается и литералом).

View Source
const CatalogSyncTarget = "sync-permission-catalog"

CatalogSyncTarget — имя цели Makefile, ПЕРЕПИСЫВАЮЩЕЙ каталог прав из копии края. Ручная операция выпуска: конвейер её звать не вправе.

View Source
const DerivedIDPackage = "crypto/md5"

DerivedIDPackage — путь импорта, вычисляющий дайджест.

View Source
const RoleGrantOrphanTable = "kaname.role_grant_orphan"

RoleGrantOrphanTable — куда обязано переселяться снятое не-автором.

View Source
const RoleRuleRefTable = "kaname.role_rule_ref"

RoleRuleRefTable — вторая проекция того же объявления: каждый объявленный сегмент правила (kacho#1030). Предмет у таблиц разный, а требование к автору — одно.

View Source
const RoleRuleVerbsSentinel = "ErrRoleRuleVerbsRetired"

RoleRuleVerbsSentinel — имя сентинела, которым пред-разборная проверка отвергает снятый ключ. Объявлено ЗДЕСЬ, потому что гейт живёт в общем пакете, а сентинел — в `internal/manifest`, закрытом правилом видимости Go. Что имя не разошлось с продуктом, утверждает сам гейт: ноль возвратов на живом дереве — находка, а не молчание.

View Source
const RoleScopeChainTable = "resource_parent_edge"

RoleScopeChainTable — таблица звеньев цепи.

View Source
const RoleScopeChainType = "iam_role"

RoleScopeChainType — тип объекта модели прав, чью цепь областей стережёт гейт.

View Source
const RoleVerbTable = "kaname.role_verb"

RoleVerbTable — проекция «роль → тип объекта × глагол».

View Source
const RoleVerbWriterLayer = "/internal/repo/"

RoleVerbWriterLayer — слой, которому принадлежит SQL проекции. Писатель вне него — находка, даже если он один.

View Source
const WithdrawalApplierImportPath = "github.com/PRO-Robotech/kaname/internal/apps/kaname/moduleroles"

WithdrawalApplierImportPath — пакет применителя ролей модуля.

Variables

View Source
var AcceptanceProbeCoordinateExemptions []DeadProbeCoordinate

AcceptanceProbeCoordinateExemptions — ВЕДОМОСТЬ ПУСТА, и это её цель, а не её поломка. Заводя запись — назови номер задачи и предикат снятия в комментарии рядом.

View Source
var NamedVerbScenarios = []string{"04", "04a", "18", "18a", "19", "19a"}

NamedVerbScenarios — шесть сценариев, чья проверка полноты и есть содержание отсрочки. Перечень ЗАКРЫТ и выписан: он приезжает из приёмки `#1090`, а не из дерева, и выводить его неоткуда — проб этих сценариев в дереве ноль by construction, то есть вывод из дерева дал бы пустой перечень и гейт, которому нечего требовать.

View Source
var ProbeCoordinateHomeShape = regexp.MustCompile(
	`^([A-Za-z0-9][A-Za-z0-9._-]*/[A-Za-z0-9][A-Za-z0-9._-]*)(?:@([0-9a-fA-F]{7,40}))?$`)

ProbeCoordinateHomeShape — форма НАЗВАННОГО ДОМА: `<владелец>/<репозиторий>` и, необязательно, `@<ревизия>`. Ревизия — только шестнадцатеричная и не короче семи знаков: короткая или произвольная строка после `@` сделала бы домом любую опечатку.

View Source
var ProbeCoordinateShape = regexp.MustCompile(`^(Test|Fuzz|Benchmark|Example)[A-Za-z0-9_]{2,}$`)

ProbeCoordinateShape — форма имени пробы Go. Три знака минимум после вида: голое `Test` резолвилось бы префиксом ко всему дереву.

View Source
var RoleProjectionTables = []string{RoleVerbTable, RoleRuleRefTable}

RoleProjectionTables — таблицы, у каждой из которых автор обязан быть один.

View Source
var RoleScopeChainTierSources = []string{"account_id", "project_id"}

RoleScopeChainTierSources — ярусные столбцы, из которых звено роли берётся ЗАКОННО. Перечень закрытый и короткий by construction.

Functions

func AcceptanceDocsOfTree added in v0.2.0

func AcceptanceDocsOfTree(root string) (map[string]string, error)

AcceptanceDocsOfTree — приёмки, живущие в каталоге приёмок службы. Перечень ВЫВОДИТСЯ обходом, а не выписывается.

Порт сужен до posture этого дерева: монорепо держало приёмки НЕСКОЛЬКИХ служб под `services/*/docs/engineering/acceptance/`, а самостоятельный клон службы несёт СВОЙ единственный каталог прямо у корня — `docs/engineering/acceptance/`. Сужение измерено, а не предположено: в дереве продукта второго дома приёмок не было никогда (`polyrepo.md` §«У приёмки домов ДВА» — второй дом был именно этот, iam-only).

func AdjudicateCarriedLedger added in v0.2.0

func AdjudicateCarriedLedger(
	ledgerPath string,
	rows []CarriedLedgerRow,
	sections map[string]CarriedLedgerSection,
	treeHas func(string) bool,
	inScope func(string) bool,
	mustBeNamed []string,
) []string

AdjudicateCarriedLedger — что не так с ведомостью относительно дерева.

treeHas — состав дерева; inScope — координата принадлежит СВОЕМУ репозиторию и подлежит суждению (кросс-репо путь пропускается обеими проверками — ни полнотой, ни самоистечением: этот гейт не может судить чужой репозиторий); mustBeNamed — координаты, которые ведомость обязана назвать.

func ApplierPortVerbDeletesTheRoleRow added in v0.2.0

func ApplierPortVerbDeletesTheRoleRow(name string) (bool, string)

ApplierPortVerbDeletesTheRoleRow — предикат оси 1: удаляет ли метод порта СТРОКУ РОЛИ. Второе значение — почему, дословно для текста находки.

func AuditAcceptanceEditsAfterVerdict added in v0.2.0

func AuditAcceptanceEditsAfterVerdict(root, dir string) ([]AcceptanceEditFinding, AcceptanceEditCensus, error)

AuditAcceptanceEditsAfterVerdict — вердикт о доме приёмок.

func AuditNamedInjections

func AuditNamedInjections(root string) ([]InjectionFinding, InjectionCensus, error)

AuditNamedInjections обходит дерево модуля от root и возвращает находки с переписью.

Обращения к пробе здесь нет намеренно: разбор, роняющий её изнутри, инъекции не поддаётся — синтетическому дереву некуда было бы подать вход.

func AuditRoleOperationResponseState added in v0.2.0

AuditRoleOperationResponseState выносит вердикт о дереве.

func AuthorVerbOf added in v0.2.0

func AuthorVerbOf(table string) string

AuthorVerbOf — оператор, которым строка ВНОСИТСЯ.

func BaselineMigrationOf added in v0.2.0

func BaselineMigrationOf(ordered []string) string

BaselineMigrationOf — применённая базовая миграция: первая в порядке применения. Порядок goose лексикографический, поэтому перечень уже отсортирован вызывающим.

func CallsMakeTarget added in v0.2.0

func CallsMakeTarget(body, target string) bool

CallsMakeTarget — исполняемое тело шага (`run:` без строк оболочечных комментариев) зовёт названную цель формой `make <цель>` либо `make -C <каталог> <цель>` (в этом репозитории цель лежит в корневом Makefile, поэтому `-C` без аргумента либо `-C .` — тоже законная форма).

func ConstStringValue added in v0.2.0

func ConstStringValue(src []byte, name string) (string, bool, error)

ConstStringValue — значение строковой константы, объявленной в исходнике.

Возвращает признак находки отдельно от значения: «объявления нет» и «объявлено пустым» — разные состояния, и схлопывать их нельзя.

func DeclaredProbesOfTree added in v0.2.0

func DeclaredProbesOfTree(root string) ([]string, error)

DeclaredProbesOfTree — имена всех проб дерева, объявленных в отслеживаемых файлах проб.

func ExecutableRunBodies added in v0.2.0

func ExecutableRunBodies(raw string) ([]string, int, error)

ExecutableRunBodies — тела `run:` без строк оболочечных комментариев.

func FoldSeededServiceAccounts added in v0.2.0

func FoldSeededServiceAccounts(ordered []string, bodies map[string]string) (
	alive map[string]SeededServiceAccount, unknownForms []string, stmtsTouched int,
)

FoldSeededServiceAccounts складывает посев по цепочке: вставка заводит строку, удаление её снимает. Возвращает живые строки, находки формы и объём осмотренного.

Формы записи названы ЯВНО, и незнакомая — находка, а не молчание: форма, о которой разбор не знает, уводит предмет из-под наблюдения, ничего не нарушив (`testing.md` §«Гейт на класс» п. 7).

Разбор НЕ понимает PL/pgSQL-блоков `DO $$ ... $$` — наивное деление на `;` внутри такого блока даёт много фрагментов, большинство отбрасываются (не называют таблицу или несут не INSERT/UPDATE/DELETE), но статический `DELETE FROM kaname.service_accounts WHERE name IN (...)` внутри такого блока распознаётся корректно, потому что сам оператор синтаксически цел между соседними `;`. Динамическое снятие через переменную (`WHERE name = ANY(var)`) разбором НЕ узнаётся — это названо явно шапкой такой миграции продукта (`20260909202745_module_identities_leave_the_baseline.sql`): имена стоят литералами в статическом операторе именно для того, чтобы этот разбор их видел.

func InvocationsOf added in v0.2.0

func InvocationsOf(script string, bodies []string) int

InvocationsOf — сколько тел `run:` действительно зовут названный скрипт.

func IsGeneratedFile added in v0.2.0

func IsGeneratedFile(f *ast.File) bool

IsGeneratedFile — узел разбора несёт отметку `Code generated … DO NOT EDIT.`

func JudgeCensusFixtures added in v0.2.0

func JudgeCensusFixtures(byDir map[string][]CensusFileFacts) []string

JudgeCensusFixtures выносит вердикт по КАТАЛОГАМ: ключ — каталог тестового пакета, значение — факты его файлов.

func MakefileDeclaresTarget added in v0.2.0

func MakefileDeclaresTarget(makefile, target string) bool

MakefileDeclaresTarget — объявлена ли цель в тексте Makefile.

func MissingScenarioProbes added in v0.2.0

func MissingScenarioProbes(funcNames []string) []string

MissingScenarioProbes — сценарии из закрытого перечня, у которых в дереве нет ни одной пробы.

Принимается перечень ИМЁН функций, а не текст файлов: подстрока `TestMODRL04` стои́т и внутри `TestMODRL04a`, и гейт, судящий вхождение, зачёл бы одну пробу за две. Имя сравнивается с ПРЕФИКСОМ и следующим за ним символом.

func ModuleIdentitiesSeededOutsideTheBaseline added in v0.2.0

func ModuleIdentitiesSeededOutsideTheBaseline(alive map[string]SeededServiceAccount, baseline string) []string

ModuleIdentitiesSeededOutsideTheBaseline — судья, отделённый от дерева ради инъекции. Находка — живая МОДУЛЬНАЯ учётка, чью строку завела не базовая миграция.

func NamedVerbFormFinding added in v0.2.0

func NamedVerbFormFinding(sentinelReturns int, probeNames []string) []string

NamedVerbFormFinding — РЕШЕНИЕ гейта одной функцией: сценарии, оставшиеся без пробы, когда форма уже не отвергается.

Объявлено здесь, а не внутри гейта, ровно затем, чтобы инъекция гоняла ТОТ ЖЕ предикат, что и прогон дерева: второй, написанный в пробе «по образцу», разошёлся бы с первым молча — и разошёлся бы именно там, где расхождение не видно, потому что оба отвечают одинаково на сегодняшнем входе.

Пустой результат означает молчание, и у него ДВЕ законные причины: форма отвергается (`sentinelReturns > 0`) либо она вернулась вместе со своей проверкой (пробы найдены). Различать их вызывающему не нужно — обе законны.

func OwnHomeOfTree added in v0.2.0

func OwnHomeOfTree(root string) (string, error)

OwnHomeOfTree — дом ЭТОГО дерева в форме `владелец/репозиторий`, выведенный из пути модуля в `go.mod`.

Нужен НЕ ядру, а гейту: ядро судит поданные значения и про дома знает только то, что дом непустой — чужой. Свой дом, названный в приёмке чужим, ядру неотличим от настоящего чужого, и именно эту дыру закрывает сторожевая ось гейта — она сверяет перепись домов с этим значением.

Путь модуля берётся у `go.mod`, а не собирается литералом: литерал разошёлся бы с переименованием репозитория молча.

func ParseCarriedCoordinateLedger added in v0.2.0

func ParseCarriedCoordinateLedger(body string) (
	[]CarriedLedgerRow, map[string]CarriedLedgerSection, CarriedLedgerCensus,
)

ParseCarriedCoordinateLedger разбирает ведомость.

func ProbeCoordinateResolves added in v0.2.0

func ProbeCoordinateResolves(name string, declared []string) bool

ProbeCoordinateResolves — объявленное имя начинается с координаты. declared обязан быть отсортирован: имя с префиксом P сортируется не раньше P, поэтому кандидат ровно один и находится двоичным поиском.

func ProdGoRoots added in v0.2.0

func ProdGoRoots(root string, tracked []string) ([]string, error)

ProdGoRoots — верхнеуровневые каталоги дерева, несущие отслеживаемый не-тестовый Go-код. Выведены обходом, а не выписаны.

func RemovalVerbsOf added in v0.2.0

func RemovalVerbsOf(table string) []string

RemovalVerbsOf — операторы, которыми строка СНИМАЕТСЯ либо правится.

Чтение (`SELECT … FROM`, `JOIN`) в перечень не входит намеренно: читатели у проекции законны и служат близнецом, на котором гейт молчит.

func SQLCreateTableBody added in v0.2.0

func SQLCreateTableBody(upSection, table string) string

SQLCreateTableBody — тело объявления таблицы, разобранное из НАКАТА (Up-секции без комментариев) миграции. sqlText — полный текст ФАЙЛА миграции (порт зовёт `migrations.MigrationUpSection` сам, чтобы не тянуть зависимость на пакет `migrations` из пакета `check` без нужды — вызывающий подаёт уже подготовленный текст наката).

func ScanApplierDeletes added in v0.2.0

func ScanApplierDeletes(path string, src []byte) (sites []ApplierDeleteSite, census ApplierDeleteCensus, err error)

ScanApplierDeletes разбирает один файл пакета применителя.

func ScanCatalogWriteLocking added in v0.2.0

func ScanCatalogWriteLocking(files []CatalogSource) ([]CatalogWriteFinding, CatalogWriteCensus, error)

ScanCatalogWriteLocking разбирает состав прод-файлов и отвечает, какие единицы пишут строки каталога, не запирая его. Файлы группируются ПО ПАКЕТУ (каталогу).

func ScanClaimAssemblies added in v0.2.0

func ScanClaimAssemblies(path string, src []byte, prefix string, minKeys int) (
	[]ClaimAssembly, ClaimAssemblyCensus, error,
)

ScanClaimAssemblies разбирает один файл и собирает места сборки состава.

prefix — префикс имени ключа состава; minKeys — сколько РАЗНЫХ ключей делают место сборкой.

func ScanClaimBuilderCalls added in v0.2.0

func ScanClaimBuilderCalls(path string, src []byte, builders map[string]bool) (
	[]ClaimBuilderCall, ClaimAssemblyCensus, error,
)

ScanClaimBuilderCalls разбирает один файл и собирает вызовы сборщиков состава.

func ScanDerivedIDDeclarations added in v0.2.0

func ScanDerivedIDDeclarations(path string, src []byte) (sites []DerivedIDImportSite, census DerivedIDCensus, err error)

ScanDerivedIDDeclarations разбирает один файл и возвращает импорты `crypto/md5` вместе с объёмом осмотренного.

func ScanFixedRefusalTexts

func ScanFixedRefusalTexts(root string, files []string) (RefusalTextCensus, []RefusalTextFinding, error)

ScanFixedRefusalTexts разбирает названные файлы и называет конструкции статуса на полосах чужой причины, чей текст не доказан фиксированным.

root служит только для печати координат. Пустой обход ошибкой ЗДЕСЬ не объявляется: пустоту судит вызывающий — у гейта дерева и у пробы инъекции законные пороги пустоты разные.

func ScanRoleDeletes added in v0.2.0

func ScanRoleDeletes(path string, src []byte) (sites []RoleDeleteSite, census RoleDeleteCensus, err error)

ScanRoleDeletes разбирает один файл Go.

func ScanRoleScopeChain added in v0.2.0

func ScanRoleScopeChain(path, src string) (found []RoleScopeChainSite, census RoleScopeChainCensus)

ScanRoleScopeChain разбирает один файл — SQL миграции либо прод-исходник Go.

func ScanRoleWithdrawalWiring added in v0.2.0

func ScanRoleWithdrawalWiring(path string, src []byte) (drive, mark []RoleWithdrawalSite, census RoleWithdrawalCensus, err error)

ScanRoleWithdrawalWiring разбирает один файл Go.

Возвращает две половины раздельно — приведение применителя в действие и производителя отзыва, — потому что находка есть их НЕСОГЛАСИЕ, а не любая из них. Сводит половины вызывающий.

func ScanSQLUpdates added in v0.2.0

func ScanSQLUpdates(path string, src []byte, tables []string) ([]SQLUpdate, SQLUpdateCensus, error)

ScanSQLUpdates разбирает один файл и собирает операторы правки названных таблиц.

func ScanTokenClaimForeignBrand added in v0.2.0

func ScanTokenClaimForeignBrand(path string, src []byte) ([]TokenClaimUse, TokenClaimCensus, error)

ScanTokenClaimForeignBrand разбирает один файл Go и возвращает употребления имени клейма из ЧУЖОГО словаря (namespace == TokenClaimForeignNamespace) в одной из четырёх законных позиций чеканки/чтения.

func ScenarioProbeName added in v0.2.0

func ScenarioProbeName(scenario string) string

ScenarioProbeName — имя пробы сценария в написании этого дерева.

func SeedScopeUnfit added in v0.2.0

func SeedScopeUnfit(ordered []string, alive map[string]SeededServiceAccount) string

SeedScopeUnfit — ПРЕДМЕТА нет. Отдельной функцией, а не тремя ветвями внутри пробы: способность гейта отличить «ноль находок» от «ноль прочитанного» проверяется инъекцией, а инъекция не может спросить `require`/`t.Fatalf` напрямую.

func SortedFuncNames added in v0.2.0

func SortedFuncNames(byFunc map[string]FunctionDatabaseCalls) []string

SortedFuncNames — имена разобранных функций для текста отказа.

func TreeModulePath added in v0.2.0

func TreeModulePath(root string) (string, error)

TreeModulePath — путь модуля, чтобы импорт файла сопоставлялся с каталогом дерева. Читается из go.mod, а не выписывается.

Types

type AcceptanceEditCensus added in v0.2.0

type AcceptanceEditCensus struct {
	DocsRead     int
	NoStateLine  []string
	NoHistory    []string
	EditedAfter  int
	CarryingNote int
}

AcceptanceEditCensus — объём осмотренного.

Величин ТРИ, и печатаются все: «приёмок N · правлено после вердикта M · из них с записью K». Одно число здесь скрывало бы ровно тот случай, ради которого гейт заведён — документ, который никто не правил, и документ, чью правку никто не назвал, дают одинаковый ноль находок.

func (AcceptanceEditCensus) String added in v0.2.0

func (c AcceptanceEditCensus) String() string

type AcceptanceEditFinding added in v0.2.0

type AcceptanceEditFinding struct {
	File           string
	StateLine      int
	StateChangedAt int64
	FileChangedAt  int64
}

AcceptanceEditFinding — приёмка, правленая ПОСЛЕ объявления своего состояния, и не объявившая этого.

func (AcceptanceEditFinding) String added in v0.2.0

func (f AcceptanceEditFinding) String() string

type ApplierDeleteCensus added in v0.2.0

type ApplierDeleteCensus struct {
	InterfaceMethods int
	StringLiterals   int
	Comments         int
}

ApplierDeleteCensus — объём осмотренного одним файлом.

type ApplierDeleteSite added in v0.2.0

type ApplierDeleteSite struct {
	File string
	Line int
	// Kind — ось: `port-verb` либо `sql-literal`.
	Kind string
	// What — что именно найдено: имя метода либо начало литерала.
	What string
}

ApplierDeleteSite — координата находки.

type BoolWrapperCall added in v0.2.0

type BoolWrapperCall struct {
	File string
	Line int
	Pair WrapperPair
}

BoolWrapperCall — одно употребление булевой половины из чужого пакета.

type CarriedLedgerCensus added in v0.2.0

type CarriedLedgerCensus struct {
	Lines                 int
	Sections              int
	Tables                int
	Rows                  int
	RowsWithoutCoordinate int
}

CarriedLedgerCensus — объём осмотренного.

type CarriedLedgerRow added in v0.2.0

type CarriedLedgerRow struct {
	Section     string
	Line        int
	Coordinate  string
	Outcome     string
	OutcomeCell string
}

CarriedLedgerRow — строка ведомости.

type CarriedLedgerSection added in v0.2.0

type CarriedLedgerSection struct {
	Title               string
	Line                int
	HasRemovalPredicate bool
}

CarriedLedgerSection — раздел документа.

type CatalogSource added in v0.2.0

type CatalogSource struct {
	Path string
	Src  []byte
}

CatalogSource — один прод-файл на вход разбора. Инъекция подаёт синтетический состав, не трогая рабочую копию.

type CatalogWriteCensus added in v0.2.0

type CatalogWriteCensus struct {
	Files          int
	Parsed         int
	Funcs          int
	Executors      int
	StringLiterals int
	Comments       int
	TextMatches    int
	TextFiles      int
	Executed       int
	ExecutingFiles int
	WriteUnits     int
	LockedUnits    int
	LockSites      int
}

CatalogWriteCensus — объём осмотренного.

type CatalogWriteFinding added in v0.2.0

type CatalogWriteFinding struct {
	File string
	Line int
	Unit string
	What string
	Why  string
}

CatalogWriteFinding — координата находки: писатель без замка.

type CensusFileFacts added in v0.2.0

type CensusFileFacts struct {
	Rel      string
	Census   bool
	RawWrite bool
	Producer bool
}

CensusFileFacts — то, что гейт узнаёт об одном файле.

func CensusFactsOf added in v0.2.0

func CensusFactsOf(rel string, body []byte, producerPkg string) (CensusFileFacts, error)

CensusFactsOf разбирает один файл. Разбор, а не текст: комментарий, объясняющий эту же дисциплину, производителем не является.

type ClaimAssembly added in v0.2.0

type ClaimAssembly struct {
	File string
	Line int
	Func string
	Keys []string
}

ClaimAssembly — место, собирающее состав утверждений.

type ClaimAssemblyCensus added in v0.2.0

type ClaimAssemblyCensus struct {
	MapLiterals      int
	EmptyMapLiterals int
	KeyedLiterals    int
	Calls            int
}

ClaimAssemblyCensus — объём осмотренного одним файлом.

type ClaimBuilderCall added in v0.2.0

type ClaimBuilderCall struct {
	File   string
	Line   int
	Func   string
	Callee string
}

ClaimBuilderCall — вызов сборщика состава: чем состав ПОТРЕБЛЯЕТСЯ.

type DatabaseCallCensus added in v0.2.0

type DatabaseCallCensus struct {
	Structs   int
	Handles   []string
	Functions int
	Calls     int
	DBCalls   int
}

DatabaseCallCensus — объём осмотренного одним файлом.

func ScanDatabaseCallsByFunction added in v0.2.0

func ScanDatabaseCallsByFunction(path string, src []byte) (
	map[string]FunctionDatabaseCalls, DatabaseCallCensus, error,
)

ScanDatabaseCallsByFunction разбирает один файл и раскладывает обращения к базе по функциям.

type DatabaseCallSite added in v0.2.0

type DatabaseCallSite struct {
	File   string
	Line   int
	Verb   string
	Handle string
}

DatabaseCallSite — координата обращения к базе.

type DeadProbeCoordinate added in v0.2.0

type DeadProbeCoordinate struct {
	Name  string
	Docs  []string
	Issue int
	Note  string
}

DeadProbeCoordinate — ПОСЛАБЛЕНИЕ: координата, о которой известно, что она не резолвится, и чей предмет принадлежит ДРУГОМУ кругу приёмки.

Запись заводится ПО ФАКТУ, а не с запасом. Послабление ИСТЕКАЕТ САМО, и оба конца — находка: имя стало резолвиться → исключать нечего; имя больше не стоит ни в одном документе → исключать нечего.

type DeleteRevokeCensus

type DeleteRevokeCensus struct {
	Dirs         int // каталогов под api/ осмотрено
	Files        int // не-тестовых файлов Go разобрано
	EmitCalls    int // вызовов EmitReconcileEvent найдено
	OwnTyped     int // каталогов, у которых выведен СВОЙ тип
	WithDeleteGo int // из них несущих delete.go
	Population   int // судимых каталогов (свой тип И delete.go)
	Satisfying   int // из них эмитирующих отзыв своего типа
}

DeleteRevokeCensus — перепись одного обхода. Печатается ВСЕГДА: «ноль находок» обязано быть отличимо от «ноль прочитанного».

func ScanDeleteRevokeProducers

func ScanDeleteRevokeProducers(apiDir string) (DeleteRevokeCensus, []string, error)

ScanDeleteRevokeProducers обходит каталоги use-case под apiDir и называет те, у которых путь снятия собственного объекта остался без производителя отзыва.

Возвращает перепись, находки (по одной на каталог, в порядке имён — отказ на одном и том же дереве читается одинаково от прогона к прогону) и ошибку обхода. Пустой обход ошибкой ЗДЕСЬ не объявляется: пустоту судит вызывающий, потому что у гейта дерева и у пробы инъекции законные пороги пустоты разные.

func (DeleteRevokeCensus) String

func (c DeleteRevokeCensus) String() string

type DerivedIDCensus added in v0.2.0

type DerivedIDCensus struct {
	// Imports — объявлений импорта прочитано.
	Imports int
}

DerivedIDCensus — объём осмотренного одним файлом.

type DerivedIDImportSite added in v0.2.0

type DerivedIDImportSite struct {
	File string
	Line int
	// Form — форма импорта: `plain` · `alias` · `dot` · `blank`. Печатается
	// переписью, чтобы «ноль находок» было отличимо от «форму не узнали».
	Form string
}

DerivedIDImportSite — координата импорта, вычисляющего MD5.

type FunctionDatabaseCalls added in v0.2.0

type FunctionDatabaseCalls struct {
	Name  string
	Line  int
	Calls []DatabaseCallSite
}

FunctionDatabaseCalls — обращения одной функции.

type InjectionCensus

type InjectionCensus struct {
	// GoFiles — исходников модуля разобрано.
	GoFiles int
	// NamingFiles / InComments — файлов, называющих доказательство, и самих
	// упоминаний В КОММЕНТАРИЯХ (судимая полоса).
	NamingFiles int
	InComments  int
	// InStrings — упоминаний в строковых литералах (полоса НЕ судимая; названа,
	// чтобы её отсутствие в вердикте не приняли за отсутствие вхождений).
	InStrings int
	// Resolved — из судимых упоминаний нашли свой файл.
	Resolved int
}

InjectionCensus — объём осмотренного. Печатается ВСЕГДА: «ноль находок» обязано быть отличимо от «ноль прочитанного».

type InjectionFinding

type InjectionFinding struct {
	NamedBy    string // координата называющего, от корня обхода
	Coordinate string // что названо
}

InjectionFinding — упоминание, у которого нет предмета.

type ProbeCoordinate added in v0.2.0

type ProbeCoordinate struct {
	Name string
	// Home — названный дом `владелец/репозиторий`. Пусто — дом ЭТО дерево.
	Home string
	// Rev — ревизия названного дома, если названа.
	Rev string
	// Span — пролёт, как он стоит в документе. Нужен находке: без него читатель
	// не найдёт строку, у которой имя пробы лишь хвост.
	Span string
	// HomeMalformed — приставка есть, а домом она не является.
	HomeMalformed bool
	Doc           string
	Line          int
}

ProbeCoordinate — одно вхождение координаты: имя, дом и место, где оно стоит.

func ProbeCoordinatesIn added in v0.2.0

func ProbeCoordinatesIn(doc, body string) []ProbeCoordinate

ProbeCoordinatesIn разбирает документ и возвращает координаты — только их.

type ProbeCoordinateCensus added in v0.2.0

type ProbeCoordinateCensus struct {
	Docs        int
	Declared    int
	Coordinates int
	Resolved    int
	Exempted    int
	// Foreign — координаты, назвавшие ЧУЖОЙ дом: вне суждения, но в переписи.
	Foreign int
	// RevisionBound — из них связанные РЕВИЗИЕЙ чужого дома. Величина отдельная,
	// потому что отвечает на другой вопрос: сколько координат указывает в ПРОШЛОЕ
	// состояние чужого дерева, то есть не проверяемо даже там, где дерево есть.
	RevisionBound int
	// ForeignHomes — различные названные дома, по алфавиту. Печатаются, чтобы дом,
	// стоящий в корпусе один раз, был виден: опечатка в имени репозитория иначе
	// уходит молча.
	ForeignHomes []string
	Findings     []string
}

ProbeCoordinateCensus — перепись обхода. «Ноль находок» обязано быть отличимо от «ноль прочитанного», поэтому объём осмотренного — отдельное утверждение.

func JudgeProbeCoordinates added in v0.2.0

func JudgeProbeCoordinates(docs map[string]string, declared []string, exemptions []DeadProbeCoordinate) ProbeCoordinateCensus

JudgeProbeCoordinates — судящее ядро. Вход подаётся значениями, а не читается из дерева: инъекция обязана уметь дать ему свой вход, не трогая рабочую копию, из которой запущена.

type RefusalTextCensus

type RefusalTextCensus struct {
	Files          int // файлов Go (не-тестовых) разобрано
	Constructions  int // конструкций статуса найдено всего
	Population     int // из них на полосах чужой причины (код назван синтаксически)
	Fixed          int // из них с доказуемо фиксированным текстом
	CodeNotLiteral int // конструкций с ВЫЧИСЛЯЕМЫМ кодом — вне популяции (слепая зона)
	Constants      int // пакетных строковых констант проиндексировано
}

RefusalTextCensus — перепись одного обхода. Печатается ВСЕГДА: «ноль находок» обязано быть отличимо от «ноль прочитанного».

func (RefusalTextCensus) String

func (c RefusalTextCensus) String() string

type RefusalTextFinding

type RefusalTextFinding struct {
	File string // путь относительно названного корня
	Line int
	Code string // Unavailable / Internal
	Expr string // выражение текста, как оно записано
}

RefusalTextFinding — одна конструкция, чей текст не доказан фиксированным.

type RoleDeleteCensus added in v0.2.0

type RoleDeleteCensus struct {
	StringLiterals int
	Comments       int
	Statements     int
	Guarded        int
}

RoleDeleteCensus — объём осмотренного одним файлом.

type RoleDeleteSite added in v0.2.0

type RoleDeleteSite struct {
	File string
	Line int
	What string
}

RoleDeleteSite — координата находки.

type RoleOperationResponseStateCensus added in v0.2.0

type RoleOperationResponseStateCensus struct {
	Files            int
	Funcs            int
	AnypbFuncs       int
	RoleTranslators  int
	ProjectionCalled int
}

RoleOperationResponseStateCensus — объём осмотренного.

func (RoleOperationResponseStateCensus) String added in v0.2.0

type RoleOperationResponseStateFinding added in v0.2.0

type RoleOperationResponseStateFinding struct {
	File string
	Line int
	Func string
}

RoleOperationResponseStateFinding — переводчик, не зовущий проекцию.

func (RoleOperationResponseStateFinding) String added in v0.2.0

type RoleOperationResponseStateOptions added in v0.2.0

type RoleOperationResponseStateOptions struct {
	Root             string
	ServiceRoot      string
	DomainPkg        string
	RoleType         string
	TransferFunc     string
	ProjectionMethod string
}

RoleOperationResponseStateOptions — вход анализатора.

type RoleProjectionOp added in v0.2.0

type RoleProjectionOp struct {
	// Func — объемлющая функция; пустое имя означает пакетный уровень.
	Func string
	// Verb — какой оператор найден.
	Verb string
	// Authors — оператор ВНОСИТ строку.
	Authors bool
	// Relocates — ТОТ ЖЕ литерал переселяет снятое в RoleGrantOrphanTable.
	//
	// Признак читается по литералу, а не по функции: переселение и снятие
	// обязаны быть неделимы, а неделимы они ровно тогда, когда стоят в одном
	// операторе.
	Relocates bool
}

RoleProjectionOp — один найденный оператор над проекцией.

func RoleProjectionWritesIn added in v0.2.0

func RoleProjectionWritesIn(filename, src, table string) ([]RoleProjectionOp, int, error)

RoleProjectionWritesIn разбирает исходник Go и возвращает операторы над таблицей проекции, приписанные объемлющей функции, плюс число строковых литералов, называющих таблицу вообще (перепись предпосылки: читатели тоже считаются).

Признак судит УЗЕЛ-ЛИТЕРАЛ разобранного дерева, а не текст файла: имя таблицы встречается и в комментариях — в том числе в комментариях, объясняющих эту самую проверку, — и гейт по подстроке краснел бы на собственном объяснении.

func RoleVerbWritesIn added in v0.2.0

func RoleVerbWritesIn(filename, src string) ([]RoleProjectionOp, int, error)

RoleVerbWritesIn — совместимая обёртка для инъекции по первой проекции.

type RoleScopeChainCensus added in v0.2.0

type RoleScopeChainCensus struct {
	Statements  int
	Branches    int
	TierSourced int
}

RoleScopeChainCensus — объём осмотренного одним файлом.

type RoleScopeChainSite added in v0.2.0

type RoleScopeChainSite struct {
	File string
	Line int
	What string
}

RoleScopeChainSite — координата одной находки.

type RoleWithdrawalCensus added in v0.2.0

type RoleWithdrawalCensus struct {
	AppliedImports  int
	Selectors       int
	StringLiterals  int
	Comments        int
	WritesOverRoles int
}

RoleWithdrawalCensus — объём осмотренного одним файлом.

type RoleWithdrawalSite added in v0.2.0

type RoleWithdrawalSite struct {
	File string
	Line int
	What string
}

RoleWithdrawalSite — координата одной из двух половин.

type SQLUpdate added in v0.2.0

type SQLUpdate struct {
	File    string
	Line    int
	Func    string
	Table   string
	Columns []string
}

SQLUpdate — оператор правки, найденный в литерале.

type SQLUpdateCensus added in v0.2.0

type SQLUpdateCensus struct {
	StringLiterals        int
	SQLLiterals           int
	Updates               int
	UpdatesWithoutColumns int
}

SQLUpdateCensus — объём осмотренного одним файлом.

type SeededServiceAccount added in v0.2.0

type SeededServiceAccount struct {
	ID, Name, Description, Where string
}

SeededServiceAccount — посеянная служебная учётка.

func (SeededServiceAccount) IsModule added in v0.2.0

func (s SeededServiceAccount) IsModule() bool

IsModule — учётка объявляет себя модульной.

type TokenClaimCensus added in v0.2.0

type TokenClaimCensus struct {
	Literals  int
	Shaped    int
	Positions int
}

TokenClaimCensus — объём осмотренного одним файлом.

type TokenClaimForm added in v0.2.0

type TokenClaimForm string

TokenClaimForm — в какой позиции стоит имя.

const (
	TokenClaimFormKey   TokenClaimForm = "ключ состава"         // #nosec G101 -- подпись позиции разбора, не удостоверение
	TokenClaimFormRead  TokenClaimForm = "чтение по имени"      // #nosec G101 -- подпись позиции разбора, не удостоверение
	TokenClaimFormCase  TokenClaimForm = "разбор по имени"      // #nosec G101 -- подпись позиции разбора, не удостоверение
	TokenClaimFormArg   TokenClaimForm = "имя в вызове"         // #nosec G101 -- подпись позиции разбора, не удостоверение
	TokenClaimFormConst TokenClaimForm = "объявление константы" // #nosec G101 -- подпись позиции разбора, не удостоверение
)

Значения — ПРОЗА для человека, читающего находку, а не материал удостоверения. Сканер безопасности видит здесь форму «имя с `token`/`claim` получает строковый литерал» и по ней подозревает вшитое удостоверение (G101, уверенность LOW). Подозрение снимается предметом: перечисляется ПОЗИЦИЯ имени в разборе (ключ состава · чтение · разбор · аргумент · объявление), и ни одно значение не участвует ни в одной проверке подлинности — они попадают только в текст находки. Подавление стоит НА КАЖДОЙ СТРОКЕ объявления, а не на `const (`: оговорка на строке блока этим сканером к отдельным объявлениям не применяется — проверено прогоном пина (до правки level=error 5, после 0), а не прочтением. Ни на что за пределами этих пяти строк подавление не распространяется.

type TokenClaimUse added in v0.2.0

type TokenClaimUse struct {
	File      string
	Line      int
	Func      string
	Namespace string
	Name      string
	Form      TokenClaimForm
}

TokenClaimUse — одно употребление имени клейма чужого словаря.

type VerbFormSentinelCensus added in v0.2.0

type VerbFormSentinelCensus struct {
	// CompositeLits — составных литералов прочитано. Предпосылка оси: разбор
	// ищет присваивание внутри литерала, и ноль литералов означает, что
	// искать было негде.
	CompositeLits int
	// Idents — идентификаторов прочитано: объём разбора в самой мелкой единице.
	Idents int
	// SentinelReturns — возвратов сентинела найдено.
	SentinelReturns int
}

VerbFormSentinelCensus — объём, осмотренный разбором ОДНОГО файла.

func ScanVerbFormSentinel added in v0.2.0

func ScanVerbFormSentinel(rel string, src []byte) (VerbFormSentinelCensus, error)

ScanVerbFormSentinel считает ВОЗВРАТЫ сентинела в одном файле Go.

Возвратом считается присваивание `kind: <Сентинел>` в составном литерале, стоящем внутри `return` — то есть исход функции, а не упоминание имени.

type WrapperPair added in v0.2.0

type WrapperPair struct {
	PkgDir   string
	BoolName string
	ENames   []string
}

WrapperPair — вопрос, у которого автор пакета объявил ОБЕ формы.

func (WrapperPair) OutcomeForms added in v0.2.0

func (p WrapperPair) OutcomeForms() string

OutcomeForms — половины с исходом, перечисленные для сообщения.

type WrapperScanReport added in v0.2.0

type WrapperScanReport struct {
	Roots      []string
	Files      int
	Generated  int
	DotImports int
	Pairs      []WrapperPair
	Found      []BoolWrapperCall
}

WrapperScanReport — что именно осмотрено. Печатается ВСЕГДА.

func ScanBoolWrapperCalls added in v0.2.0

func ScanBoolWrapperCalls(root string, roots []string, under func(string) ([]string, error)) (WrapperScanReport, error)

ScanBoolWrapperCalls — два прохода по одному корпусу: сперва вывести пары, затем найти их употребления. under — обходчик поддерева (обычно `treecorpus.Under`), подаётся вызывающим, чтобы инъекция могла подставить синтетическое дерево без записи в рабочую копию.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL