# Источники поставки: откуда берётся каждый файл обвязки

Документ отвечает на один вопрос: **откуда взялся каждый файл поставки и чем это
подтверждено**. Он появился потому, что обвязка существовала только в публикации:
живое дерево не содержало ни `CHANGELOG`, ни `CONTRIBUTING`, ни `SECURITY`, ни
`CITATION`, а `.github/**` было старее публичного набора. Сборка «из живого дерева»
в таком состоянии теряла обвязку целиком — и заметить это можно было только
сверкой, которой до сих пор никто не делал.

**Публичный `main` на момент сверки:** `637d4a52687b60ef207f908ad556c7b5abf7cc0e`
(2026-10-28, «выпуск 0.4.1: дата реестра и размеры индекса пишутся генератором»),
репозиторий https://github.com/paulmann/RAGRAF, клон публикации вне живого дерева
(`git clone`, затем
`git fetch origin`, `git rev-parse origin/main`, `git archive origin/main`).

Проверка этих утверждений — `tools/check_community_wrapper.py` (24-я проверка
чекера конвенций). Она сверяет три вещи: файл, строку таблицы и признак
совпадения. Состав поставки проверяют `tools/release/**` и 25-я проверка
(`tools/check_release_manifest.py`): манифест, сборщик, проверяльщик и гейт. Молчаливого пропуска нет: файл без строки в таблице — провал,
строка с неверным хэшем — провал, признак «совпадает» при различных хэшах — провал.

## 1. Таблица источников

Таблица пересчитана **после** публикации, и голова сверки названа в заголовке
раздела выше: на той голове все строки совпадали с публичным `main` побайтово.
Правка 28.10.2026 (этап И33) вывела из совпадения пять переводов, а запись
выпуска 0.4.1 — ещё и `CHANGELOG.md` с `CITATION.cff`; до публикации честный
признак у них был «нет», и это объявлялось здесь, а не забывалось. Выпуск
0.4.1 опубликован головой `637d4a5`, и таблица пересчитана на ней — все
строки снова «да».
Хэш в абзаце не повторяется намеренно: пересчёт идёт после коммита, поэтому сам
коммит таблицы сдвигает `main` — вписанное здесь значение устаревало бы от правки
того же файла. Прежние редакции этого
абзаца называли 27 строк — число было от прежнего состава обвязки, а не измерением
сегодняшнего; актуальное число строк равно числу файлов `community/**`, и это
проверяется, а не запоминается. Сверка не «на глаз»:
`tools/check_community_wrapper.py` (24-я проверка чекера) сравнивает три вещи —
файл, строку таблицы и признак совпадения — и падает на любом расхождении, включая
случай «написано да, а хэши разные».

Порядок при следующей публикации: выпуск уходит в `main`, затем таблица
пересчитывается на новой голове. Таблица, оставленная на прежней голове, — это
описание прошлого, а не текущего состояния.


> **Изменение 26.10.2026 (этап И17).** `community/EULA.md` переписан: поставка
> одна, Pro и Enterprise — уровни лицензии внутри неё, переход между уровнями —
> активация лицензии, а не установка (решение владельца от 26.10.2026). Выпуск
> отправлен, хэши совпадают, адрес источника указывает на голову выпуска.

> **Изменение 26.10.2026 (этап И18).** В `community/EULA.md` добавлен уровень
> Pro — `secret-broker` переведён в предмет (код есть и проверен), а `connectors`
> и `fleet` сняты из предмета всех уровней и названы в плане развития. Тогда выпуск
> НЕ отправлялся, и строка честно говорила «не совпадает с main».

> **Изменение 26.10.2026 (этап И19).** Выпуск 0.4.0 опубликован: голова
> `18d7bfe9584eb94bf3ffd56a4c96420142a5b2cf`, тег `v0.4.0` на ней же. Строки
> `community/EULA.md`, `community/CHANGELOG.md`, `community/CITATION.cff` и
> `community/ROADMAP.md` пересчитаны на этой голове — все четыре теперь «да»,
> расхождений в таблице нет. Таблица, оставленная на прежней голове, была бы
> описанием прошлого; поэтому порядок «сначала выпуск, потом таблица» и соблюдён.
> **Изменение 27.10.2026 (этап И20).** Переводы README объявлены: `community/README.ru.md`
> стал русской копией основного `README.md` — тогда основной документ писался по-русски,
> и две разные русские редакции одного текста расходились молча (с этапа И23 основной
> документ английский, а русский файл — самостоятельный перевод), — а
> `community/README.{de,es,hi,zh}.md` объявлены **частичными переводами** с шапкой,
> которая называет недостающее содержание (раздел 1а). Тогда же исправлено то, чего не
> видно глазами: по 9 относительных ссылок в каждом переводе вели на `docs/ru/**` и
> `docs/DEFECTS.md`, которых в поставке нет; в блоках команд оставался русский
> заполнитель `-p <конфиг>`; числа поверхности 0.4.0 стояли рядом с картиной 0.3.x.
> Отправлено без тега, двумя правками документов: `e97ff8c` — сами переводы,
> `d217aec` — числа собственного индекса по записи `rev-0377` (пересборка корпуса
> после правки документов сдвинула число рёбер: 67 428 → 67 413). Выпуск версии не
> объявлялся; таблица ниже пересчитана на голове `d217aec`.

> **Изменение 27.10.2026 (этап И30).** Пять переводов README изменены в живом
> дереве после переноса из публичного `main`. Две правки, обе про блоки команд:
> подстановка в команде больше не переводится (`-p <конфиг>` → `-p <config>`,
> тринадцать строк русского) и переведённый аргумент поискового запроса возвращён к
> исходному тексту (`"reindex latency"` в немецком и испанском). Сверх того в каждом
> переводе обновлены числа собственного индекса RAGRAF (869 файлов, 5 227 узлов,
> 68 845 рёбер — запись `rev-0415`).
> Изменение опубликовано, и таблица пересчитана на голове публикации `bca3f3947d8c47cbabe1f75b2b2162e8b0925cc9`: хэш в
> дереве равен хэшу `main`, признак — «да».

> **Изменение 27.10.2026 (этап И31).** Пять переводов README изменены в живом
> дереве после переноса из публичного `main`: в каждом обновлены числа
> собственного индекса RAGRAF (871 файл, 5 252 узла, 69 361 ребро — запись
> `rev-0421`). Других правок в переводах не было: строки о проверяемости фактов
> и блоки команд не менялись.
> Таблица пересчитана на голове публикации этих правок: хэш в дереве равен хэшу
> `main`, признак — «да». Причина правки одна и названа в разделе о проверках:
> ячейки таблиц переводов получили сторожа, а числа индекса в них — тот же
> сторож, что у `README.md` (`Test-DocumentedNumbers`).

> **Изменение 27.10.2026 (этап И32).** Пять переводов README изменены в живом
> дереве после переноса из публичного `main`: в каждом обновлены числа
> собственного индекса RAGRAF (запись `rev-0424`). Других правок в переводах не
> было: пункты списков во всех пяти сошлись с `README.md` по позициям и
> защищённой части при первом же прогоне новой проверки.
> Таблица пересчитана на голове публикации этих правок: хэш в дереве равен хэшу
> `main`, признак — «да».

> **Что проверяет новая 40-я проверка и чего она НЕ проверяет.** Пункты списков
> перевода сверяются с `README.md` ПОЗИЦИЯМИ: вид и номер маркера, отступ,
> обрамлённый код (точно и по порядку), адреса ссылок, числа (значением и набором
> с учётом кратности) и число разделителей `<br>`. Замер 27.10.2026: списков 4 и
> пунктов 22 в каждом из шести файлов (11 + 3 + 3 + 5), всего 132 пункта;
> расхождений защищённой части — 0 во всех пяти переводах. Правило разбора, виды
> пунктов, перечень списков и якоря оглавления по языку объявлены ДАННЫМИ —
> `docs/03_implementation/lists.json`, — а не словарём в коде: строгость меняется
> правкой данных.
> НЕ проверяется: смысл и правильность пункта (перевод, неверно пересказавший
> пункт, пройдёт зелёным); отрисовка списка GitHub — ширина отступа, «плотный» и
> «свободный» список не воспроизводятся; куда ведёт якорь оглавления —
> существует ли названный им заголовок, — это следующий слой, названный в отчёте
> этапа; признак «да/нет» внутри пункта (в списках README его нет — замер: 0).

Хэши — sha256, полные (64 знака). Столбец «совпадает» означает: файл в дереве
побайтово равен файлу публичного `main`, то есть перенос выполнен без правки
содержимого.

**Правка 27.10.2026 (правило «`.ragraf` не попадает в git»).** Числа
собственного индекса в переводах `README.{de,es,ru,hi,zh}.md` обновлены по записи
`rev-0397` (861 файл / 5 165 узлов / 67 914 рёбер), поэтому пять переводов
разошлись с публичным `main`: до публикации этой правки столбец «совпадает» у них
стоял «нет» — объявленное расхождение, а не забытая синхронизация. Публикация
этапа И25 (голова `9ca6ff422d07…`) расхождение закрыла: таблица пересчитана
на новой голове, все 23 строки «да».

**Изменение 28.10.2026 (этап И33).** Строка реестра «по реестру на <дата>:
N корпусов, M проиндексированы» переписана генератором `tools/registry_line.py`
в шести документах (дата реестра 28.10.2026, 42 корпуса, 33 проиндексированы).
Правка изменила sha256 всех пяти переводов, поэтому столбец «совпадает» у них
стоит «нет», а «sha256 в дереве» пересчитан тем же прогоном генератора — ручной
пересчёт шестидесятизначных хэшей и был источником следующего расхождения.
Столбец «sha256 в публичном main» не трогался: он описывает ОПУБЛИКОВАННУЮ
копию, и генератор её не видит. Публикация закроет расхождение пересчётом
таблицы на новой голове — так же, как это сделано 27.10.2026.

| файл в дереве | источник (URL + коммит) | sha256 в публичном main | sha256 в дереве | совпадает | путь в поставке |
|---|---|---|---|---|---|
| community/CHANGELOG.md | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/CHANGELOG.md | 31f174f70537032cb2fc2661df7c4a6090c6648e330dc932ceb94c0c72ca6e00 | 31f174f70537032cb2fc2661df7c4a6090c6648e330dc932ceb94c0c72ca6e00 | да | CHANGELOG.md |
| community/CITATION.cff | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/CITATION.cff | e889cb270a37b13ed3d38fa839ff1520ff7c0fa24435c4b73cfb50ed20eab344 | e889cb270a37b13ed3d38fa839ff1520ff7c0fa24435c4b73cfb50ed20eab344 | да | CITATION.cff |
| community/CODE_OF_CONDUCT.md | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/CODE_OF_CONDUCT.md | 34596ad809e62b3c5a0f9cfb3106c971c21b4f145b18c7e70704959d6996b580 | 34596ad809e62b3c5a0f9cfb3106c971c21b4f145b18c7e70704959d6996b580 | да | CODE_OF_CONDUCT.md |
| community/CONTRIBUTING.md | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/CONTRIBUTING.md | 4702989e9f57c205f752d6e5977b692316fae0fa4c61c32355d5289e05d98689 | 4702989e9f57c205f752d6e5977b692316fae0fa4c61c32355d5289e05d98689 | да | CONTRIBUTING.md |
| community/install.ps1 | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/install.ps1 | 5a55c8d88ba70066e43147d9b772d98352f46ca82a88ef18a2d8ada71509e8e0 | 5a55c8d88ba70066e43147d9b772d98352f46ca82a88ef18a2d8ada71509e8e0 | да | install.ps1 |
| community/mkdocs.yml | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/mkdocs.yml | fafbfabe326de2139b7075cc831c358729070a28f0e1b90baa26dbf8bcd4a0e4 | fafbfabe326de2139b7075cc831c358729070a28f0e1b90baa26dbf8bcd4a0e4 | да | mkdocs.yml |
| community/README.de.md | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/README.de.md | 7ee1a143e137597df7363d9a08fd184d9a5dfe1df8e1c0c8b9241e4e49824788 | 7ee1a143e137597df7363d9a08fd184d9a5dfe1df8e1c0c8b9241e4e49824788 | да | README.de.md |
| community/README.es.md | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/README.es.md | 50ccfbe41899ab7cf61e2e9584ab4a49eb610b6fa7f62cbd9cbb3389e38de02d | 50ccfbe41899ab7cf61e2e9584ab4a49eb610b6fa7f62cbd9cbb3389e38de02d | да | README.es.md |
| community/README.hi.md | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/README.hi.md | cc048560478bd9f12e36c1ccd03ad9b7acff37110d0165d05b4515fbd8cb2586 | cc048560478bd9f12e36c1ccd03ad9b7acff37110d0165d05b4515fbd8cb2586 | да | README.hi.md |
| community/README.zh.md | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/README.zh.md | aad5015ba6c2276efd67ea82686b420b15fe2c8ebd09eb65f87e69b34cc6b2a7 | aad5015ba6c2276efd67ea82686b420b15fe2c8ebd09eb65f87e69b34cc6b2a7 | да | README.zh.md |
| community/README.ru.md | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/README.ru.md | 8ecb7405dc2e5c339dc018aa7c507ded9894a2d2e336ca70ca3729c978887b8d | 8ecb7405dc2e5c339dc018aa7c507ded9894a2d2e336ca70ca3729c978887b8d | да | README.ru.md |
| community/EULA.md | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/EULA.md | de3a473f73d39dedb64f362384554f25584979d62b51f8ea50c5d531bab75a3a | de3a473f73d39dedb64f362384554f25584979d62b51f8ea50c5d531bab75a3a | да | EULA.md |
| community/ROADMAP.md | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/ROADMAP.md | 1973da88532a9dab0b96873df9acf57575b286af40f4904a36e87ccf5c6457be | 1973da88532a9dab0b96873df9acf57575b286af40f4904a36e87ccf5c6457be | да | ROADMAP.md |
| community/SECURITY.md | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/SECURITY.md | dbc7d9c2bd4d3e2d1481a36d00655633c3b45603d1f6753a26c9357414495700 | dbc7d9c2bd4d3e2d1481a36d00655633c3b45603d1f6753a26c9357414495700 | да | SECURITY.md |
| community/.github/dependabot.yml | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/.github/dependabot.yml | 37360b6fa679b5639b08465c684bca8d86c56f9cdf5b1e8c9dd3e1d813f79532 | 37360b6fa679b5639b08465c684bca8d86c56f9cdf5b1e8c9dd3e1d813f79532 | да | .github/dependabot.yml |
| community/.github/PULL_REQUEST_TEMPLATE.md | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/.github/PULL_REQUEST_TEMPLATE.md | 30c5a2c60f7d1933b201b326d55ce6955aad46457d3d65503c9b666706ab82b0 | 30c5a2c60f7d1933b201b326d55ce6955aad46457d3d65503c9b666706ab82b0 | да | .github/PULL_REQUEST_TEMPLATE.md |
| community/.github/release.yml | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/.github/release.yml | 48c9059c309e7e04177c3e649c02ff3aa5b93b0045a55eff3998004e4356560c | 48c9059c309e7e04177c3e649c02ff3aa5b93b0045a55eff3998004e4356560c | да | .github/release.yml |
| community/.github/ISSUE_TEMPLATE/config.yml | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/.github/ISSUE_TEMPLATE/config.yml | 0bc9435ed1245c47bbde5fc751fb937c75b554553be3010da264be10d2c89654 | 0bc9435ed1245c47bbde5fc751fb937c75b554553be3010da264be10d2c89654 | да | .github/ISSUE_TEMPLATE/config.yml |
| community/.github/ISSUE_TEMPLATE/defect-report.yml | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/.github/ISSUE_TEMPLATE/defect-report.yml | abab94c9ded87699c6046f527486ff84c0abb6d1df8c7f3447baeb25f58fc2a6 | abab94c9ded87699c6046f527486ff84c0abb6d1df8c7f3447baeb25f58fc2a6 | да | .github/ISSUE_TEMPLATE/defect-report.yml |
| community/.github/ISSUE_TEMPLATE/doc-divergence.yml | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/.github/ISSUE_TEMPLATE/doc-divergence.yml | 743c9e238dcaaa3eb10f4739a53f7ec7cab43d81ff3c1d93adfab768adcf3aff | 743c9e238dcaaa3eb10f4739a53f7ec7cab43d81ff3c1d93adfab768adcf3aff | да | .github/ISSUE_TEMPLATE/doc-divergence.yml |
| community/.github/workflows/ci.yml | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/.github/workflows/ci.yml | ea6d8ae5a2007164ca196a0e1b6fc7e71976c9a354ebe55efc50f32d7e6b3a99 | ea6d8ae5a2007164ca196a0e1b6fc7e71976c9a354ebe55efc50f32d7e6b3a99 | да | .github/workflows/ci.yml |
| community/.github/workflows/codeql.yml | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/.github/workflows/codeql.yml | 5c09bbc2cec2fe16c338d4527100b92b63badfc6dbf3dea34b2652d5a0726bfb | 5c09bbc2cec2fe16c338d4527100b92b63badfc6dbf3dea34b2652d5a0726bfb | да | .github/workflows/codeql.yml |
| community/.github/workflows/pages.yml | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/.github/workflows/pages.yml | 4735800e7da31eb2b1ef550ccff883867e072118f8edb164b8ddbddd65abeb38 | 4735800e7da31eb2b1ef550ccff883867e072118f8edb164b8ddbddd65abeb38 | да | .github/workflows/pages.yml |
| scripts/install.ps1 | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/scripts/install.ps1 | f6cee5d8bb7f98039c297b578cc86c15fb439e14ed2206289d63382031be5996 | f6cee5d8bb7f98039c297b578cc86c15fb439e14ed2206289d63382031be5996 | да | scripts/install.ps1 |
| scripts/ragraf.ps1 | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/scripts/ragraf.ps1 | f7dbe0bb262b92456945a7863e6263bce3b4678f1cb04ae5f9e81c6a05488813 | f7dbe0bb262b92456945a7863e6263bce3b4678f1cb04ae5f9e81c6a05488813 | да | scripts/ragraf.ps1 |
| scripts/install-joern.ps1 | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/scripts/install-joern.ps1 | c3ebcf0e2c344b95e1fc1d9c3f60937ac87fe6fac7d76351683c8b8faf4f8a27 | c3ebcf0e2c344b95e1fc1d9c3f60937ac87fe6fac7d76351683c8b8faf4f8a27 | да | scripts/install-joern.ps1 |
| scripts/PSScriptAnalyzerSettings.psd1 | https://github.com/paulmann/RAGRAF/blob/637d4a52687b60ef207f908ad556c7b5abf7cc0e/scripts/PSScriptAnalyzerSettings.psd1 | dd5fbe1cb5be2fa1bc9ca6f9fc5b45b3b1ec40cecb452ae5b2d938c6c9473657 | dd5fbe1cb5be2fa1bc9ca6f9fc5b45b3b1ec40cecb452ae5b2d938c6c9473657 | да | scripts/PSScriptAnalyzerSettings.psd1 |

> **Изменение 27.10.2026 (этап И29).** Пять переводов README изменены в живом
> дереве после переноса из публичного `main`: в каждом обновлены числа собственного
> индекса RAGRAF (867 файлов, 5 212 узлов, 68 580 рёбер — запись `rev-0411`), больше
> ничего.
> Изменение опубликовано, и таблица пересчитана на голове публикации
> `600c2cf6f1b9c3f2ec63a795b72ee8032307f0a7`: хэш в дереве равен хэшу `main`, признак — «да».

> **Изменение 27.10.2026 (этап И28).** Пять переводов README изменены в живом
> дереве после переноса из публичного `main`: в каждом обновлены числа собственного
> индекса RAGRAF (865 файлов, 5 191 узел, 68 311 рёбер — запись `rev-0407`), больше
> ничего. Изменение опубликовано, и таблица пересчитана на голове публикации
> `407974446ee0c3246f97b6b861dc3637f892e55f`: хэш в дереве равен хэшу `main`, признак — «да».

> **Изменение 24.10.2026 (этап И5).** `community/SECURITY.md` отредактирован в
> живом дереве после переноса из публичного `main`: дефекты C-01 и C-04 закрыты,
> и документ называет, чем именно они проверены и что осталось вне модели угроз.
> Поэтому хэш в дереве больше не равен хэшу публичного `main`, а признак
> «совпадает» стоит «нет» — это заявленное расхождение, а не порча переноса.


**Девять позиций обвязки из задания** — `CITATION.cff`, `CODE_OF_CONDUCT.md`,
`CONTRIBUTING.md`, `SECURITY.md`, `ROADMAP.md`, `README.ru.md`, `CHANGELOG.md`,
`mkdocs.yml`, `.github/**` — на месте. Строк в таблице двадцать две: восемнадцать
файлов `community/**` (по числу файлов; `.github/**` раскрыт по файлам, потому что
проверять каталог целиком значит не заметить потерю одного файла внутри него),
`community/install.ps1` отдельным решением (раздел 2) и четыре файла `scripts/`
(раздел 4). Признак «совпадает» означает побайтовое равенство публичному `main`:
пять строк обвязки помечены «нет» после правки лицензионных текстов (раздел 5).

Каталога `community/` до этого изменения не существовало: перенос аддитивен,
ни один файл владельца не удалён и не перезаписан этим переносом.

## 1а. Переводы README: состояние каждого

Состояние каждого перевода записано здесь потому, что молчаливое расхождение хуже
отставшего числа: числа проверяются, а смысл — нет. Измерено 27.10.2026 (этап И20):
в переводах стояли числа 0.4.0, а содержание описывало 0.3.x — ни брокера секретов,
ни уровней лицензии, ни снятых `connectors` и `fleet`; сверх того 9 относительных ссылок
в каждом файле вели на `docs/ru/**` и `docs/DEFECTS.md`, которых в поставке нет, а в
блоках команд оставался русский заполнитель `-p <конфиг>`.

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

| перевод | состояние | на каком состоянии остановлен | чего нет | где полный текст |
|---|---|---|---|---|
| `README.ru.md` | **полный на 0.4.0** — русский перевод (этап И23, 27.10.2026) | текущее, 27.10.2026 | ничего: те же разделы, те же числа, те же ссылки, что и в `README.md`; отличие только в языке текста и в шапке с переключателем языков | [`README.md`](https://github.com/paulmann/RAGRAF/blob/main/README.md) — основной документ на английском |
| `README.de.md` | **полный на 0.4.0** — переведён целиком (этап И22, 27.10.2026) | текущее, 27.10.2026 | ничего: те же разделы, те же числа, те же ссылки, что и в `README.md`; отличие только в языке текста и в шапке с переключателем языков | [`README.md`](https://github.com/paulmann/RAGRAF/blob/main/README.md) — основной документ, он же образец для следующих языков |
| `README.es.md` | **полный на 0.4.0** — переведён целиком (этап И24, 27.10.2026) | текущее, 27.10.2026 | ничего: те же разделы, те же числа, те же ссылки, что и в `README.md`; отличие только в языке текста и в шапке с переключателем языков | [`README.md`](https://github.com/paulmann/RAGRAF/blob/main/README.md) — основной документ на английском |
| `README.hi.md` | **полный на 0.4.0** — переведён целиком (этап И25, 27.10.2026) | текущее, 27.10.2026 | ничего: те же разделы, те же числа, те же ссылки, что и в `README.md`; отличие только в языке текста и в шапке с переключателем языков | [`README.md`](https://github.com/paulmann/RAGRAF/blob/main/README.md) — основной документ на английском |
| `README.zh.md` | **полный на 0.4.0** — переведён целиком (этап И26, 27.10.2026) | текущее, 27.10.2026 | ничего: те же разделы, те же числа, те же ссылки, что и в `README.md`; отличие только в языке текста и в шапке с переключателем языков | [`README.md`](https://github.com/paulmann/RAGRAF/blob/main/README.md) — основной документ на английском |

**Основной документ — английский (этап И23, 27.10.2026).** До этого этапа `README.md`
писался по-русски, а `README.ru.md` был его побайтовой копией с шапкой переключателя
языков: две русские редакции одного текста расходились молча, и состояние «полный»
выражалось равенством. Владелец изменил это решение — документ переднего края на
GitHub пишется по-английски, — и распределение ролей стало обычным: `README.md` —
основной английский текст, `README.ru.md` — русский перевод, `README.de.md` — немецкий,
все три проверяются одинаково (структура разделов, ссылки, числа). Состояние `mirror`
из объявления снято: файла, который обязан быть побайтовой копией другого, больше нет,
а правило, которое никем не объявлено, выглядит защитой, ничего не защищая. Шапка
каждого частичного перевода по-прежнему несёт свой признак неполноты — этим состояние
«полный» и отличается от «частичный». Ссылки переводов сверяются обходом:
относительная ссылка, не находящая файла в поставке, — провал (до правки таких было 45).

**Немецкий перевод переведён целиком (этап И22, 27.10.2026).** До этого этапа он был
частичным: 13 разделов вместо 12, содержание 0.3.x, шапка неполноты. Теперь он повторяет
`README.md` раздел в раздел, включая операции платных уровней, живой слой баз данных,
разбор документов и хранилище секретов, и состояние «полный» подтверждается фактом, а не
обещанием шапки: нет ни одного признака неполноты; шапка называет полный перевод, версию
0.4.0 и основной документ; число разделов `##` (12) и подразделов `###` (7) совпадает с
`README.md`; профиль каждого раздела — сколько в нём блоков команд и строк таблиц —
совпадает по порядку; каждая относительная ссылка основного документа встречается и в
переводе; числа индекса равны активной записи ревизии. Остальные два перевода (`hi`,
`zh`) оставались частичными с прежним перечнем недостающего: этап менял состояние ОДНОГО
языка, и объявление об этом живёт в одном месте — `docs/03_implementation/translations.json`.

**Испанский перевод переведён целиком (этап И24, 27.10.2026).** Он был последним
из тех, у кого шапка честно называла недостающее содержание; теперь он повторяет
`README.md` раздел в раздел: 12 разделов `##`, 7 подразделов `###`, 11 блоков команд
и 91 строка таблиц — те же и в том же порядке, что в основном документе (именно это и
проверяется: профиль каждого раздела по числу блоков команд и строк таблиц, а не
сходство слов). Шапка называет полный перевод, версию 0.4.0 и основной документ;
признака неполноты нет; каждая относительная ссылка основного документа встречается и
здесь; числа индекса равны активной записи ревизии. Состояние объявлено ПРАВКОЙ ДАННЫХ:
запись `es` в `docs/03_implementation/translations.json` переведена из `partial` в
`full`, — и ни одна строка `tools/check_translations.py` ради этого не менялась. Это и
было целью этапа И21: объявление живёт в данных, проверка только подтверждает его.
Частичных переводов не осталось: все пять приведены к текущему продукту целиком.

**Китайский перевод переведён целиком (этап И26, 27.10.2026).** Он был
последним частичным: текст стоял на редакции 0.3.x, с лишним разделом «使用场景»
(сценарии), которого в основном документе уже нет, с подразделом «一条命令快速安装»
вместо подразделов `README.md`, без подраздела хранилища секретов в разделе
лицензии, со старой таблицей документации и старым разделом лицензии (уровни
Community / Pro / Enterprise вместо формулировки BUSL-1.1 с четырьмя источниками
истины). Отдельно исправлен переключатель языков: строка 4 называла `README.md`
русским документом — это было верно до этапа И23 и перестало быть верным после,
когда основной документ стал английским (в ru/de/es/hi исправлено там же, в zh
оставалось). Теперь перевод повторяет `README.md` раздел в раздел: 12 разделов
`##`, 7 подразделов `###`, 11 блоков команд и 91 строка таблиц — те же и в том же
порядке, что в основном документе (проверяется профиль каждого раздела по числу
блоков команд и строк таблиц, а не сходство слов, — совпадение по числу
подразделов при другом составе ничего не доказывает: профиль сверяется по
порядку, а не счётом).
Шапка называет полный перевод, версию 0.4.0 и основной документ; признака
неполноты нет; каждая относительная ссылка основного документа встречается и
здесь; числа индекса равны активной записи ревизии. Ярлык «Русский» в
переключателе оставлен намеренно: по нему проверка отличает язык перевода от
остатков оригинала, и кириллица допускается только в этой строке. Состояние
объявлено ПРАВКОЙ ДАННЫХ: запись `zh` в
`docs/03_implementation/translations.json` переведена из `partial` в `full`, и ни
одна строка `tools/check_translations.py` ради этого не менялась. Перечень
признаков неполноты по языкам (`incomplete_markers`) сохранён целиком: это
свойство языка, и без него маркер неполноты можно было бы вернуть в файл
незаметно.

**Хинди-перевод переведён целиком (этап И25, 27.10.2026).** Он был последним
переводом, чья шапка честно называла недостающее содержание: текст стоял на
редакции 0.3.x — без уровней лицензии, без брокера секретов, без контура качества
и общего индекса, без операций SSO, SIEM, офлайн-лицензии и аттестации, без
`route` и `neighbors`, без разбора документов Office/PDF, со старым разделом
лицензии и старой таблицей документации. Теперь он повторяет `README.md` раздел
в раздел: 12 разделов `##`, 7 подразделов `###`, 11 блоков команд и 91 строка
таблиц — те же и в том же порядке, что в основном документе (проверяется профиль
каждого раздела по числу блоков команд и строк таблиц, а не сходство слов). Шапка
называет полный перевод, версию 0.4.0 и основной документ; признака неполноты нет;
каждая относительная ссылка основного документа встречается и здесь; числа индекса
равны активной записи ревизии. Состояние объявлено ПРАВКОЙ ДАННЫХ: запись `hi`
в `docs/03_implementation/translations.json` переведена из `partial` в `full`, и ни
одна строка `tools/check_translations.py` ради этого не менялась. Отдельно назван
дефект ЧУЖОГО инструмента: линтер `ghagent doc` даёт на оглавлении хинди ложные
`md.link-anchor`, потому что `_anchor_keep` отбрасывает знаки категории `Mc`, а к ним
относятся знаки гласных деванагари; якоря в файле записаны в точном виде GitHub и
совпадают с заголовками побайтово, под дефект линтера заголовки не переписывались.

**Состояние перевода — проверяемое свойство, а не обещание (этап И21, 27.10.2026).**
Тридцать третья проверка чекера (`Test-TranslationStates`) вызывает
`tools/check_translations.py` и падает, если у частичного перевода исчезла шапка или
она называет меньше трёх недостающих пунктов; если вернулась ссылка `docs/ru/**` или
`docs/DEFECTS.md`; если в примерах команд остался русский заполнитель (правило
относится к переводам на другие языки — у русского перевода кириллица и `<конфиг>`
объявлены его языком); если кириллица появилась вне ярлыка «Русский»; если
относительная ссылка — включая изображения — не находит файла в составе поставки.
Проверка вдобавок сверяет перечень переводов в `community/` с тем, что инструмент
осмотрел: новый файл `README.<язык>.md` не может остаться непроверенным при зелёном
прогоне.

Измерено в тот же день и исправлено: шапки четырёх переводов ссылались на
`assets/social-preview.png`, которого не было ни в живом дереве, ни в поставке.
Замер ссылок этапа И20 изображения не видел — шаблон ссылки исключал их
(`(?<!!)`), поэтому обложка была битой у читателя при зелёном замере. Файл
восстановлен из сохранённой копии `editions/legacy-v0.1.0/assets/` (sha256
сверен) и внесён в манифест поставки; проверка сторожит и это. Прогон
27.10.2026: 33 проверки, 0 ошибок, 0 предупреждений.

**Публикация этапа И27 (27.10.2026).** Голова
`600c2cf6f1b9c3f2ec63a795b72ee8032307f0a7` — «этап И27 — терминология переводов объявлена
данными и проверяется»; тег не ставился, выпуск версии не объявлялся. Пять
переводов `README.{de,es,hi,ru,zh}.md` изменились правкой ОДНОГО слова в каждом
месте (термин `stale` приведён к каноническому варианту: «устаревший», «veraltet»,
«obsoleto», «पुराना», «过时»), поэтому их хэши пересчитаны на новой голове; прочие
строки обвязки не менялись, и те же байты есть в новом коммите. Все строки основной
таблицы — «да»: расхождений нет.

**Терминология переводов стала проверяемой (этап И27, 27.10.2026).** Тридцать пятая
проверка чекера (`Test-Glossary`) закрывает то, чего не видели ни состояния переводов,
ни сверка чисел: `tools/check_translations.py` подтверждает, что перевод полный, и сам
называет границу — «полный» там означает «приведён к текущему продукту и подтверждён
структурой, ссылками и числами», а не «переведён без потерь». Слово при этом не сторожил
никто, и термин расходился молча. Замер до правки на одном термине `stale`: в русском
переводе «отставший индекс» в шаге установки против «устаревшая строка» ниже в том же
файле; в немецком — «zurückgebliebener Index» против «veraltete Zeile»; в испанском —
«índice atrasado» и «línea atrasada», то есть отставание по времени там, где речь о
состоянии факта; в китайском — «过期» (истёк срок) и «过时» (устарел) для одного понятия.
Подмена слова на синоним проходила зелёной: ни одна проверка слово не считала.

Объявление терминов вынесено в данные — `docs/03_implementation/glossary.json`: семь
терминов (`corpus`, `degraded`, `slice`, `stale`, `provenance`, `break-even`, `delivery`),
по языку список допустимых переводов, первый из которых канонический, и признак
обязательности только там, где термин называет операцию, режим или объект продукта.
Пять расхождений приведены к каноническому варианту правкой ОДНОГО слова в каждом месте,
а не переписыванием абзацев. **Граница названа прямо и здесь:** проверяется СЛОВО, а не
смысл фразы, поэтому пропущенное при сохранённой структуре раздела предложение не видит ни
эта проверка, ни прежняя; зелёный `check_glossary` означает «термины названы одним словом»,
а не «перевод верен».

**Проза переводов стала проверяемой (этап И28, 27.10.2026).** Тридцать шестая
проверка чекера (`Test-ProseProfile`) закрывает следующий слой после терминологии и
самый вероятный вид потери при переводе. Прежние проверки видели структуру раздела
(число блоков команд и строк таблиц — функция `section_profile` в
`tools/check_translations.py`) и слово
термина, но не прозу: перевод, из которого пропал абзац, оставался «полным» —
структура совпала, ссылки на месте, числа сошлись, а текста меньше. Пропавшее
предложение внутри абзаца не видел вообще ни один замер.

Счёт вынесен в `tools/check_prose_profile.py`: файл разбивается на разделы `##`,
внутри — на абзацы прозы, внутри абзаца — на предложения по знакам языка (латинские
`. ! ? …` с условием справа, китайские `。！？`, деванагари `।`; объявленные сокращения
концом предложения не считаются, абзац без знака конца — одно предложение).
Допуски объявлены ДАННЫМИ — `docs/03_implementation/prose.json`, по языку: абзацев в
разделе и предложений в абзаце, — и сняты замером, а не назначены: первый замер
черновым счётчиком дал расхождения во всех пяти переводах, и все они оказались
дефектами СЧЁТА, а не пропущенной прозой (строки-продолжения списков считались
абзацами; `。` без пробела после себя не признавалась концом предложения; `.` после
жирного шрифта, `**On the model cache.**`, не признавалась концом предложения;
сокращение `в.` съедало конец слова, «проектов.»). После их устранения расхождение
равно нулю, поэтому объявленный допуск — 0 и по абзацам, и по предложениям: он ловит
и пропавший абзац, и склейку двух абзацев в один, и потерянное предложение внутри
абзаца. Проверено тремя порчами в песочнице с приманками: каждая даёт ровно одну
находку с номером раздела, номером абзаца и обоими числами, откат возвращает зелёный
прогон, пустой вход — провал.

**Граница названа прямо и здесь:** проверяется СЧЁТ, а не смысл. Предложение может
быть переведено наоборот — счёт этого не увидит; проза в списках, таблицах, цитатах
и блоках команд не считается (её сохранность сторожат другие проверки: профиль
раздела по блокам команд и строкам таблиц, наличие ссылок); законное для языка
различие (одно английское предложение передано двумя) при нулевом допуске станет
находкой, и расширение допуска — решение владельца, названное числом в данных, а не
правка проверки. Зелёный `check_prose_profile` означает «пропущенной прозы по счёту
нет», а не «перевод верен».

**Внешние адреса и якоря переводов стали проверяемыми (этап И29, 27.10.2026).**
Тридцать седьмая проверка чекера (`Test-TranslationAddresses`) закрывает последний
слой той же природы, что терминология и проза: АДРЕСА. Имя из задания этапа —
`Test-DocumentAddresses` — уже занято проверкой ссылок `файл:строка` (она
двадцать девятая), поэтому новая названа иначе: переименование работающей
проверки сломало бы ссылки в документах и её собственную историю.

Прежние проверки видели только ОТНОСИТЕЛЬНЫЕ цели: функция `relative_targets` в
`tools/check_translations.py` (строка 227) отбрасывает всё, что начинается с `http://`, `https://`,
`mailto:`, `ftp://` и `#`, а покрытие ссылок (строка 342 того файла) сравнивает
относительные цели между собой. Значит внешний адрес и якорь в переводе не
сторожил никто: перевод, где ссылка на вики заменена на «см. репозиторий», а
якорь `#installation` ведёт в никуда, проходил зелёным. Замер до правки
27.10.2026: в `README.md` восемь внешних адресов (четыре изображения-бейджа, три
цели ссылок и две страницы вики), пять изображений (четыре бейджа и обложка
`assets/social-preview.png`) и тринадцать якорей на одиннадцать заголовков — и всё
это есть в каждом из пяти переводов, лишнего нет. Свойство выполнялось, но не
проверялось: расхождение появилось бы молча.

Разбор вынесен в `tools/check_document_addresses.py`. Он отдельно разбирает
изображения и ссылки (включая вложенный бейдж `[![CI](badge.svg)](url)`) и требует
от перевода того же ТЕКСТА цели — сравнение идёт по точному написанию адреса, а
не по похожести, — а от якоря двух свойств: он указывает на существующий заголовок
СВОЕГО файла (текст якоря не сравнивается: заголовки перевода переведены, и слаг у
них свой — немецкий `#inhalt` против английского `#contents`) и стоит на том же
месте, что в основном документе: k-й якорь ведёт в раздел с тем же номером. Второе
свойство и есть проверка «ведёт туда, а не в никуда»: якорь в чужой раздел
разрешается, но местом выдаёт себя.

Объявление — ДАННЫЕ: `docs/03_implementation/addresses.json` (виды адресов,
перечень основного документа числом, допуск на повторы и требование порядка по
языку). По всем трём видам допуск равен нулю и снят замером, как у прозы. Там же
записан дефект первого замера: черновой шаблон назвал ШЕСТЬ внешних адресов
вместо восьми, потому что на бейдже совпадал с внутренним изображением и терял
цель внешней ссылки. Проверено четырьмя порчами в песочнице: подмена внешнего
адреса, удаление изображения, якорь в никуда и якорь в чужой раздел — каждая даёт
ровно одну находку с номером строки и обоими адресами, откат возвращает зелёный
прогон, пустой вход — провал.

**Граница названа прямо и здесь:** доступность адреса по сети не проверяется —
сети у проверки нет; проверяется совпадение текста цели и существование якоря в
своём файле. `https://example.org/removed` пройдёт зелёным, если он назван так же,
как в основном документе: это проверка ПОЛНОТЫ перевода, а не живости ссылки.
Правило слагов повторено по документации GitHub, а не вызовом его кода, поэтому
якорь сверяется с заголовками своего файла (там обе стороны считаются одним
правилом), а совпадение с отрисовкой GitHub названо границей. Смысл фраз не
проверяется и здесь.

**Блоки команд переводов стали проверяемыми (этап И30, 27.10.2026).** Тридцать
восьмая проверка чекера (`Test-CommandBlocks`) закрывает последний слой той же
природы, что терминология, проза и адреса: САМА КОМАНДА внутри блока. Имя из
задания этапа свободно, и это проверено, а не предположено: `Test-DocumentAddresses`
занято проверкой ссылок `файл:строка` (она двадцать девятая), поэтому новая названа
по предмету — `Test-CommandBlocks`.

До этого этапа блок команд перевода проверялся ЧИСЛОМ. Функция `section_profile` в
`tools/check_translations.py` (строка 193) считает блоки команд и строки таблиц на
каждый заголовок `##`, поэтому перевод, где в команде
`ragraf -p <config> query search "reindex latency"` строка запроса переведена, а
подстановка `-p <config>` заменена на `-p <конфиг>`, проходил зелёным: блок на
месте, строк столько же. Замер до правки 27.10.2026: 11 блоков в каждом из шести
файлов, 70 строк внутри, из них 13 строк-комментариев и 8 пустых, то есть 49
командных строк — и в них 15 расхождений в трёх переводах: русский 13 (тринадцать
строк с `-p <конфиг>`, в одной из них ещё и переведённый аргумент), немецкий 1
(`"Verzögerung der Neuindizierung"`), испанский 1 (`"latencia de reindexación"`);
хинди и китайский 0. Свойство не выполнялось, и заметить это было нечем.

Разбор вынесен в `tools/check_command_blocks.py`. Блок берётся по языку ограждения
(`powershell`, `bash`, `json`); строка, начинающаяся с `#`, — комментарий и в
сравнение не входит; у остальных сравнивается часть до первого `#`, которому
предшествует пробел, поэтому хвостовой комментарий переводится свободно; строка,
оканчивающаяся обратным слешем, склеивается со следующей и сравнивается как одна
команда. Сравнение позиционное: k-я команда перевода против k-й команды `README.md`,
с номером строки файла и обоими текстами в находке. Число команд внутри блока
сверяется тоже — иначе пропавшая строка выглядела бы как сдвиг остальных.

Объявление — ДАННЫЕ: `docs/03_implementation/commands.json` (правило разбора
строки, языки ограждения, перечень основного документа числом, допуск на
расхождение и перечень исключений с причиной по языку). По всем пяти языкам допуск
равен нулю и снят замером, как у прозы и адресов. Исключение, объявленное и не
использованное, — находка: иначе перечень допусков описывал бы прошлое и разрешал
бы то, чего уже нет.

Расхождения приведены к допуску правкой ТЕКСТА, а не допуском: подстановка в
команде не переводится (тринадцать строк русского заменены на `-p <config>`),
переведённый аргумент поискового запроса возвращён к исходному тексту в трёх
переводах. Допуск «аргумент запроса можно переводить» был бы широким и спрятал бы
подмену самой команды — решение владельца по таким допускам записывается числом в
данных, а не правкой проверки.

Проверено порчами в песочнице, каждая с откатом в зелёное: переставленный параметр
в команде перевода, удалённая строка команды из блока и переведённая подстановка
внутри команды — ровно по одной находке с номером строки и обоими текстами; сверх
задания проверены переведённый хвостовой комментарий (находкой не становится),
одинаковый и разный перенос строки `\`, использованное и неиспользованное
исключение, пустой вход (в переводе и в основном документе — провал, а не PASS).

**Граница названа прямо и здесь:** смысл команды и её выполнимость не проверяются —
запуска команд у проверки нет; проверяется совпадение ТЕКСТА. Команда, которая в
основном документе неверна, пройдёт зелёным, если перевод повторил её символ в
символ. Правило разбора повторено по виду документов, а не выведено из разметки
Markdown, поэтому обратные кавычки внутри строки командой не считаются, а `#` без
пробела перед ним комментарием не считается. Сравнение идёт по точному тексту:
законная правка основного документа (`-p <config>` → `--project <config>`) сделает
расходящимися все переводы сразу — это и есть свойство, а не помеха, потому что
команда перевода обязана быть тем же текстом, а не похожим.

**Ячейки таблиц переводов стали проверяемыми (этап И31, 27.10.2026).** Тридцать
девятая проверка чекера (`Test-TableRows`) закрывает последний слой той же
природы, что терминология, проза, адреса и блоки команд: СОДЕРЖИМОЕ ЯЧЕЙКИ.

До этого этапа таблица перевода проверялась ЧИСЛОМ строк, и только им: та же
функция `section_profile` в `tools/check_translations.py` (строка 193) считает
строки таблиц на каждый заголовок `##`. Поэтому перевод, где в таблице метрик
ячейка `2.0 / 3.1` стала `3,1 / 2,0`, а идентификатор операции `search` заменён
переводом, проходил зелёным: строк столько же. Замер до правки 27.10.2026:
таблиц 8 в каждом из шести файлов, содержательных строк 83, ячеек 197, столбцов
от 2 до 6; расхождений защищённой части 5 в четырёх переводах (ru 1, de 1,
es 1, hi 1, zh 2). Свойство не выполнялось, и заметить это было нечем.

Разбор вынесен в `tools/check_table_rows.py`. Ячейки делятся по `|`, но `|`
внутри обратных кавычек и экранированный ячейку не делят. Внутри ячейки
защищены обрамлённый код (имя операции, файла, хранилища), адрес ссылки, ЧИСЛА
(значением, а не текстом) и число разделителей `<br>`; переводимый текст не
сравнивается вовсе. Строка заголовка данными не считается: `Operation` →
`Операция` — правильный перевод, а `search` → `поиск` в том же столбце —
находка. Числа сравниваются значением и НАБОРОМ внутри ячейки, а не
последовательностью: языки меняют порядок (`(12 of 13)` → `(13 में से 12)`,
`1 MB per 1–2 source files` → `每 1–2 个源文件 1 MB`), и это замерено, а не
предположено; позиции чисел между ячейками и строками сравниваются, поэтому
подмена местами значений двух столбцов остаётся находкой.

Объявление — ДАННЫЕ: `docs/03_implementation/tables.json` (виды ячеек, правило
разбора, вид КАЖДОЙ колонки, перечень основного документа числами, допуск и
перечень исключений с причиной по языку). Вид колонки — решение, а не вывод из
текста: столбец, названный «число», не может содержать фразу, и это проверяется
на основном документе. По всем пяти языкам допуск равен нулю. Три исключения
названы с причиной и проверяются на использование: подпись формата ссылки
`file:line` переведена словами языка перевода (`файл:строка`, `Datei:Zeile`,
`archivo:línea`) — формат ссылки в этих документах называется по-своему, а
примеры вывода инструмента остаются в блоках команд и сторожатся проверкой
этапа И30. Исключение, объявленное и не использованное, — находка: иначе
перечень допусков описывал бы прошлое и разрешал бы то, чего уже нет.

Проверено порчами в песочнице, каждая с откатом в зелёное: заменённое число в
ячейке одного перевода, удалённая строка таблицы, подменённый идентификатор
операции и подменённый адрес ссылки — ровно по одной находке с номером строки
файла и обоими значениями. Сверх задания проверены неиспользованное исключение
(находка) и пустой вход (провал, а не PASS); всего тридцать пять свойств
песочницы.

**Граница названа прямо и здесь:** смысл и правильность ячейки не проверяются —
перевод, который перевёл пояснение неверно, пройдёт зелёным, если защищённая
часть совпала. Отрисовка таблицы GitHub не воспроизводится: ширина
строки-разделителя `---` не сравнивается, перенос внутри ячейки сверяется по
числу `<br>`, а не по тому, что покажет браузер. Правило разбора повторено по
виду документов, а не выведено из разметки Markdown: `|` внутри адреса ссылки
ячейку делит, и объявление это называет. Зелёный `check_table_rows` означает
«подмены чисел, кода и адресов в ячейках нет», а не «таблица переведена верно».

**Числа в переводах получили сторожа (этап И22).** До него числа индекса в четырёх
переводах обновлялись вручную — этап И21 обновил их в шести файлах и записал это в отчёт,
то есть у чисел не было проверки, а у проверки — власти. Теперь числа каждого перевода
сверяются с активной записью ревизии через `tools/rev_snapshot.py` — тот же инструмент и то
же правило, что у чисел `README.md` (`Test-DocumentedNumbers`): совпало с активной
ревизией — наблюдение; совпало с одной из прошлых — предупреждение с её `rev_id` и датой;
такого состояния не было ни в одной ревизии — находка. На публикации варианта «отстал» нет:
`tools/release/gate_release.py` вызывает ту же проверку в режиме `--strict`, где отставание
становится отказом.

## 2. Решение по установщику

Установщик существовал в **трёх** версиях, а не в двух. Измерено:

| версия | размер | строк | sha256 (12) | функции | где |
|---|---|---|---|---|---|
| живое дерево (было) | 34 242 | 588 | ca4407fb1a47 | 5 | `scripts/install.ps1` |
| публичный main, `scripts/` | 37 794 | 803 | 223530f6ded7 | 10 | репозиторий, коммит 284ba03 (2026-10-12) |
| публичный main, корень | 51 062 | — | 4ad85ec94240 | 26 | репозиторий, коммит 6341be8 (2026-10-16) |
| старая сборочная копия | 37 794 | 803 | 223530f6ded7 | 10 | `.staging/release-v0.2/RAGRAF/scripts/install.ps1` |

**База выбрана по содержимому: `scripts/install.ps1` публичного `main`** (37 794
байта, sha256 `223530f6ded7…`). Основания, каждое проверено:

1. В ней есть исправления D-01…D-12: `Stop-Install`, `Close-InstallTranscript`,
   `Write-SummaryJson`, `Get-FreeGb`, `Get-LastErrorLine`, `Invoke-Ragraf`,
   плюс параметры `-NoTranscript`, `-SummaryJson` и `-TimeoutSec` у
   `Invoke-Checked`. В живой версии всего этого не было.
2. **Живой CI требовал того, чего живой установщик не умел.** Замер:
   `.github/workflows/ci.yml:125` вызывал
   `scripts/install.ps1 -SummaryJson install-summary.json`, а живая версия
   параметра `$SummaryJson` не объявляла вовсе — то есть CI в этом дереве
   был бы красным на шаге установки. Это не мнение о версиях, а несогласие
   интерфейса внутри одного дерева.
3. Старая сборочная копия содержит ровно тот же файл (хэши совпадают), поэтому
   «взять из 14.10» и «взять из публичного main» здесь дают одно и то же.

**Что перенесено из отвергнутой версии:** ничего, и это утверждение проверено,
а не предположено. Единственная функция, которой не было в выбранной версии, —
`Fail-Out` (живая строка 139). Её роль целиком закрыта `Stop-Install` (строка 174):
тело совпадает шаг в шаг — `Write-Host "STOP: …"`, `Show-Summary`, `exit $Code` —
и добавлены два действия, которых у `Fail-Out` не было: `Write-SummaryJson` и
`Close-InstallTranscript`. Коды возврата те же (`0/1/2/3`,
`$script:ExitOk/ExitError/ExitUsage/ExitEnvironment`), поэтому менять контракт
не пришлось. Функции `Get-FreeGb` и `Get-LastErrorLine` в живой версии
отсутствовали, а проверка свободного места и разбор последней строки ошибки
в выбранной версии есть — то есть выбранная версия строже, а не мягче.

**Корневой установщик.** В живом дереве он теперь есть как
`community/install.ps1` — побайтовая копия корневого `install.ps1` публикации
(51 062 байта, sha256 `4ad85ec94240…`). Это отдельное решение, названное прямо:
без него корневой установщик — тот самый постоянный адрес, на который указывает
инструкция пользователю, — существовал бы **только в публикации**, и следующая
сборка из живого дерева потеряла бы точку входа. Роль двух файлов разная и
пересечения не имеют:

* `community/install.ps1` — установщик развёртывания: скачивает пакет, разворачивает
  каталог, подключает клиентов, вызывает `scripts/install.ps1` как штатный путь;
* `scripts/install.ps1` — установщик зависимостей: venv, пакет, проверка
  работоспособности, отчёт для CI.

Второй копии в дереве нет: проверка обходит все `install.ps1` вне `community/`,
`.staging`, `.venv`, `.tools`, `.zwork`, `.ragraf` и требует ровно одного файла —
`scripts/install.ps1`, — и разбирает его парсером PowerShell.

**Проверено после замены:** размер 37 794, sha256 `223530f6ded7…`, 803 строки,
`Parser::ParseFile` — 0 ошибок. Прежняя версия сохранена:
`.ragraf/backup-stage2/install.ps1.bak-20261024-stage2` (34 242 байта, хэш сверен).

## 3. Решение по `.github/**`

Набор файлов в живом дереве и в публичном `main` совпадал по именам (9 файлов),
но `ci.yml` в них различался. Расхождение выравнено **в живом дереве**:

| файл | было (живое) | стало (живое = `community/` = публичный `main`) | вывод |
|---|---|---|---|
| `.github/workflows/ci.yml` | 8 532 | 8 791, `ea6d8ae5a200…` | выровнено копией из `community/` |
| остальные 8 файлов | — | хэши совпадали и совпадают | не трогались |

Различие — три строки: в публичной версии в шаг синтаксической проверки добавлен
каталог `bench` с комментарием, что измеритель — такой же код проекта.

Почему выровнено, а не «оставлено как есть». Публикуемое дерево берёт `ci.yml` из
`community/`, то есть проверяет `bench`; живое дерево до правки проверяло на один
каталог меньше. Значит зелёный CI живого дерева не доказывал того же для поставки.
После переноса оба дерева проверяют один и тот же набор каталогов. Откат:
`.ragraf/backup-stage3/.github_workflows_ci.yml.bak-20261024-stage3`.


## 4. Три файла `scripts/`, которые были старее публичных: решения

Два файла в живом дереве были старее публичных, третий отсутствовал. Решение по
каждому — «перенести»; размер и хэш замерены в этапе 3, число строк и функций —
в этапе 2 (таблица ниже — сводка двух замеров).

| файл | было в живом дереве | стало | основание |
|---|---|---|---|
| `scripts/ragraf.ps1` | 1 841 Б, 59 строк, 0 функций | 4 525 Б, 113 строк, `Resolve-Interpreter` | без разрешения интерпретатора обёртка берёт первый `python` из PATH; этот случай записан в самом установщике (в PATH лежал встроенный интерпретатор без модуля `venv`), плюс появляется порог Python ≥ 3.13 |
| `scripts/install-joern.ps1` | 5 314 Б, 137 строк, 1 функция | 20 431 Б, 425 строк, 6 функций | живая версия качает архив Joern без проверки SHA-256 и не восстанавливает `JAVA_HOME`/`PATH` — то есть ставит сторонний бинарник без проверки целостности |
| `scripts/PSScriptAnalyzerSettings.psd1` | отсутствовал | 1 594 Б | на файл ссылается шаг PSScriptAnalyzer в `ci.yml` (`-Settings scripts/PSScriptAnalyzerSettings.psd1`); без него CI публикуемого дерева падал бы на отсутствующем файле настроек |

Источник — публичный `main`, коммит `59ec2618a6225fa83bebed20fc6c0d190f62512d`:
файлы скачаны по адресу коммита, хэши совпали с копиями в старой сборочной копии
(`.staging/release-v0.2/RAGRAF/scripts/`), то есть снимок 14.10 для этих трёх
файлов был верен. Прежние версии сохранены:
`.ragraf/backup-stage3/scripts_*.bak-20261024-stage3`.


## 5. Тексты лицензии в обвязке: расхождение закрыто

Этап 2 перенёс байты публикации эпохи v0.1.0, а лицензия живого дерева с этапа 1 —
BUSL-1.1. Четыре файла называли прежнюю лицензию. Этап 3 их исправил — ссылкой на
файл лицензии, а не пересказом её условий: пересказ и был причиной того, что дерево
публиковало условия PolyForm при поле `license`, где стояло MIT.

| файл | было | стало |
|---|---|---|
| `community/README.ru.md` (этап 3; номера строк не повторяются: с этапа И20 файл — зеркало `README.md`) | «PolyForm Noncommercial 1.0.0» и утверждение `license = "PolyForm-Noncommercial-1.0.0"` | ссылка на `LICENSE`, выражение `BUSL-1.1`, история смены лицензий — в `CHANGELOG.md` |
| `community/README.ru.md` (этап 3, та же причина) | «значок показывает Other, потому что PolyForm отсутствует в каталоге» | «значку верить нельзя, источники истины — `LICENSE` и `pyproject.toml`» |
| `community/CITATION.cff:34` | `license: PolyForm-Noncommercial-1.0.0` | `license: BUSL-1.1` (нормализуется именно это выражение; `BSL-1.1` даёт `InvalidLicenseExpression` — замер этапа 1) |
| `community/mkdocs.yml:26` | подпись в подвале сайта «PolyForm Noncommercial License 1.0.0» | «Business Source License 1.1» |
| `community/CONTRIBUTING.md:73` | условие вклада под PolyForm | условие вклада под BUSL-1.1 плюс ссылка на `CLA.md` |
| `community/CHANGELOG.md` | записи о смене лицензий нет | запись «Changed (licence metadata, 2026-10-24)»; прежние записи **не переписаны** |

Функциональный дефект обвязки, найденный при этой правке и исправленный:
`community/install.ps1` распознавал в тексте `LICENSE` только «PolyForm Noncommercial»
и «MIT License», поэтому на исправной поставке с BUSL-1.1 установщик сообщал
«лицензия: не опознана» — отчёт расходился с фактом. Добавлена первая ветвь
(`Business Source License`), прежние сохранены: установщик читает и поставки эпохи
MIT/PolyForm. Проверено разбором парсером PowerShell (0 ошибок).

Граница проверок названа прямо: 23-я проверка (`tools/check_licensing.py`)
осматривает документы ТЕКУЩЕГО состояния по явному списку
(`tools/check_licensing.py:106`) — корневые `README.md`, `NOTICE`, `CLA.md`,
`SUPPORT.md`, `docs/03_implementation/LICENSING.md`. Файлы `community/**` в список
не входят: они описывают поставку, а не текущее состояние проекта. Их согласие
проверяют другие правила — 24-е (таблица источников и хэши) и 25-е (сборка,
проверяльщик, гейт).


## 6. Дата и способ проверки

Снято 24.10.2026. Команды, которыми получены числа:

```powershell
git clone --depth 50 https://github.com/paulmann/RAGRAF.git $sbx
git -C $sbx fetch origin --depth 50
git -C $sbx rev-parse origin/main        # 59ec2618a6225fa83bebed20fc6c0d190f62512d

# хэши источника и приёмника
Get-FileHash <файл> -Algorithm SHA256
# побайтовая сверка переноса
python tools\check_community_wrapper.py  # осмотрено 166 свойств, сверка файла, строки и признака
pwsh -NoProfile -File tools\Invoke-ProjectDoctrine.ps1   # 24-я проверка
# разбор установщика
[System.Management.Automation.Language.Parser]::ParseFile('scripts\install.ps1',[ref]$null,[ref]$e)
```

Отдельно проверено, что перенос не задел старую сборочную копию: хэши ниже сняты
до переноса и после — совпадают.

**28.10.2026** снимок заменён сборкой выпуска 0.4.1 тем же порядком (сборка вне
дерева → проверка → замена рабочего дерева клона, `.git` сохранён). Хэши в таблице
ниже сняты с НОВОГО снимка; значения прежнего остались в таблице «было» и
проверкой не используются. Иначе проверка требовала бы неизменности снимка,
который по порядку публикации обязан меняться при каждом выпуске.

## 7. Сборочная копия: состав и контроль

`.staging/release-v0.2/RAGRAF` — рабочее дерево клона публикации. До 24.10.2026 в
нём лежал снимок, собранный **вручную** 14.10.2026; этап 4 заменил его сборкой
конвейера (639 файлов, `checksums.sha256` на 639 строк). Прежнее содержимое
сохранено рядом как `RAGRAF.bak-<метка времени>`, каталог `.git` клона при замене
не тронут.

Прямая команда `build_release.py --out .staging/… --force` невозможна: сборщик
отказывается писать внутрь живого дерева (код 2, замер 24.10.2026). Порядок замены
описан в `RELEASE-PIPELINE.md` §2: сборка вне дерева → проверка → замена рабочего
дерева клона.

Хэши ключевых файлов снимка — чтобы изменение было видно:

| файл | sha256 |
|---|---|
| .staging/release-v0.2/RAGRAF/CHANGELOG.md | 31f174f70537032cb2fc2661df7c4a6090c6648e330dc932ceb94c0c72ca6e00 |
| .staging/release-v0.2/RAGRAF/CONTRIBUTING.md | 4702989e9f57c205f752d6e5977b692316fae0fa4c61c32355d5289e05d98689 |
| .staging/release-v0.2/RAGRAF/SECURITY.md | dbc7d9c2bd4d3e2d1481a36d00655633c3b45603d1f6753a26c9357414495700 |
| .staging/release-v0.2/RAGRAF/ROADMAP.md | 1973da88532a9dab0b96873df9acf57575b286af40f4904a36e87ccf5c6457be |
| .staging/release-v0.2/RAGRAF/README.ru.md | 8ecb7405dc2e5c339dc018aa7c507ded9894a2d2e336ca70ca3729c978887b8d |
| .staging/release-v0.2/RAGRAF/scripts/install.ps1 | f6cee5d8bb7f98039c297b578cc86c15fb439e14ed2206289d63382031be5996 |

Прежние значения (ручной снимок 14.10.2026) — для сравнения, проверкой не
используются:

| было в снимке 14.10 | sha256 |
|---|---|
| было `.staging/release-v0.2/RAGRAF/CHANGELOG.md` | 0dd74d9f49caf2952bd838b7c8c1fdb3f332f892ba3efa115a84f6d07b8af7c6 |
| было `.staging/release-v0.2/RAGRAF/CONTRIBUTING.md` | 7613e2a281203159178a067cd3ca4121cece674ce25dca1a7e9d2cbf68429883 |
| было `.staging/release-v0.2/RAGRAF/SECURITY.md` | 8a94f86fd64b71063f9479a32721890d661bb3c69221f874d6c1a13c9456bf92 |
| было `.staging/release-v0.2/RAGRAF/ROADMAP.md` | 11505345faafb10a7a9d388889f0370cbcd1d9a9a55896bfcf1cc8042d6a4a2f |
| было `.staging/release-v0.2/RAGRAF/README.ru.md` | 1a6bea4cae23e652612353f47090aa2bbef2474ad28368cf6470c4d0850c1669 |

Три файла не изменились (SECURITY, scripts/install.ps1 — и `ROADMAP` совпал с
публичным `main`), поэтому в таблице «было» их нет или значения совпадают.

Снимок `.staging/release-v0.2/RAGRAF` пересобран конвейером в этапе 4.5:
замена выполнена инструментом `tools/release/replace_staging_tree.ps1`, прежнее
дерево лежит рядом как `RAGRAF.bak-20261024-v021`, `.git` клона сохранён, сверка
sha256 сошлась (641 файл). Поэтому таблица выше описывает **текущий** снимок, а не
ручной снимок 14.10.2026: прежние значения оставлены во второй таблице для
сравнения и проверкой не используются.

Отсюда правило, которое остаётся в силе: **источник обвязки — `community/**` в
живом дереве, а не снимок**. История этого клона расходилась с публичной: прежняя
локальная вершина — ветка `local-history-6a62a07` (4 своих коммита, 14 недостающих,
разные начальные коммиты `bfc6a4a` против `28b9455`), поэтому выпуск 0.2.0 был
поставлен на `origin/main`, а не поверх неё. Сейчас расхождения нет: HEAD `f5727a7`
= `origin/main` (замер 24.10.2026: `rev-list --left-right --count HEAD...origin/main`
даёт `0 0`).

## 8. Этап 4: что изменилось в обвязке

Сделано (замерено 24.10.2026):

1. **Версия 0.2.0 объявлена в одном месте** — `src/ragraf/__init__.py`.
   `pyproject.toml` читает её через `dynamic`, `community/CITATION.cff` повторяет
   значение (формат CFF требует поля) и получил дату выпуска `2026-10-24`.
   `community/CHANGELOG.md` получил раздел `[0.2.0]`.
2. **Таблица источников сверена заново**: хэши `community/CHANGELOG.md` и
   `community/CITATION.cff` обновлены (содержимое изменилось), признак «совпадает»
   у обоих остался «нет» — правка названа, а не спрятана.
3. **Три файла вернулись в поставку** (`tests/test_db_index.py`,
   `tools/check_admin.py`, `tools/fix_admin_check.py`): значение доступа вынесено
   в `RAGRAF_DB_PASSWORD`/файл доступа машины, два подставных значения объявлены
   маркером. Подробности и измеренная причина — `RELEASE-PIPELINE.md` §5.
4. **Живой конфиг корпуса убран из поставки**, `examples/ragraf.example.yaml`
   возвращён: до этого сборка несла `root: RAGRAF` — путь автора.
5. **Появилась карта изданий** (`editions/`, `tools/release/editions_report.py`):
   у каждого файла живого дерева есть место — публикация либо объявленное
   удержание. Обоснование — указание владельца «урезанное издание ничего не
   отменяет».

Закрыто этапом 4.5 (24.10.2026): навигация сайта переписана под фактический
состав поставки (59 целей, все существуют; домашняя страница `docs/index.md`
добавлена; русские ветви `docs/ru/**` заменены прямыми ссылками на
`docs/02_architecture/**`, `docs/03_implementation/**`, `docs/05_article/**`), и
пути машины автора убраны из поставки — 86 файлов, из них 21 документ. Проверяльщик
получил девятое правило `paths`, гейт — пятую порчу «путь машины»; `mkdocs build
--strict` на собранном дереве даёт код 0.

Осталось названным, но не сделанным: публикация в PyPI и сайт `ragraf.ru` — вне
конвейера.
