# ПЛАН: двухуровневый указатель «Я это ты» — четыре промта, приведённые к инвариантам RAGRAF

Статус: **G0–G5 реализованы 19.10.2026; G6 отклонён по инварианту локальности.**
Исходная версия 1.0 (спецификация) сохранена ниже без переписывания истории;
статус каждой фазы — в разделе 0.
Основание: четыре присланных промта (двухуровневый индекс «Я это ты»; профили
извлечения и список расширений; отдельный контур секретов; подсистема роста по
образцу XERJ). Промты написаны для гипотетического продукта и в трёх местах
прямо противоречат жёстким правилам этого проекта — см. раздел 3.

Замеры этой сессии (команды указаны): корпус пересобран
`python tools/ragraf.py ensure RAGRAF` → `status=reindexed`,
`rev-0159`, 662 файла; после добавления этого документа — `rev-0160`, 663 файла.
`status` на `rev-0160` → `capability=full`, `CPG: да`, 3620 узлов, 46 296 рёбер,
`symbols=13800`, `chunks=3438`, `secrets_skipped=1`. Поверхность
для агента: 41 инструмент (`"name": "ragraf_..."` в `src/ragraf/mcp_server.py:102`)
и 39 операций фасада (`src/ragraf/facade.py`), посчитано регулярным выражением
самого чекера (`tools/Invoke-ProjectDoctrine.ps1`), а не по памяти.

Связанные документы: `docs/00_plan/PLAN-ONE-TOOL-XERJ-PARITY.md` (поглощение
XERJ — частично пересекается с этим планом), `docs/00_plan/PLAN.md`,
`docs/02_architecture/TZ.md`, `docs/02_architecture/ACCEPTANCE.md`,
`docs/AGENTS-context.md`.

---

## 0. Статус реализации (19 октября 2026)

| Фаза | Состояние | Файлы | Проверки |
|---|---|---|---|
| G0. Реестр источников и карта | **готово** | `src/ragraf/sources.py`, `src/ragraf/global_map.py`, CLI `sources` / `map`, страница `/sources` | `tests/test_sources_registry.py` (20), `tests/test_global_map.py` (23), `tests/test_admin_sources.py` (19, живой) |
| G1. Маршрутизатор | **готово** | `src/ragraf/route.py`, `Facade.op_route`, `ragraf_route`, CLI `route` | `tests/test_route.py` (22) |
| G2. Текстовые форматы | **готово** | `LANG_BY_EXT` + `include` корпуса | `tests/test_lang_routing.py` (56) |
| G3. Реестр секретоносных мест | **готово** | таблица `secret_place`, CLI `sources secrets` | входит в `tests/test_global_map.py` (содержимое не читается) |
| G4. Польза и field report | **готово** | `src/ragraf/feedback.py`, `src/ragraf/analytics.py`, CLI `feedback` и `analytics export|delete`, поле `last30` | `tests/test_feedback.py` (16), `tests/test_analytics.py` (15) |
| G5. Документы PDF/DOCX/XLSX/PPTX/EML/ODT/RTF (MSG — при библиотеке) | **готово** | `src/ragraf/documents.py`, язык `document`, `live_read` | `tests/test_extractors.py` (33), `tests/test_documents_index.py` (10, сквозно) |
| G6. Сетевые источники и отправка аналитики | **закрыто решением** | UNC/смонтированные пути — только `--network` с проверкой доступности; URL-коннекторы (`ssh/s3/webdav/http`) отклоняются с причиной; отправка аналитики не реализована по инварианту локальности | `tests/test_sources_registry.py` (20), чекер `new layers stay local` |
| G7. Панель источников и пользы | **готово** | `admin/sources.html`, `admin/value.html`, `/api/sources`, `/sources`, `/value` | `tests/test_admin_sources.py` (19, живой цикл) |

Замеры после реализации: корпус RAGRAF — 673 файла, `capability=full` (точная
ревизия — `python tools/ragraf.py -p config/ragraf.yaml status`); живой прогон:
`AMD` — 249 файлов, 1 секретное место, маршрут «AMD» рекомендует
именно этот корпус. Страницы `/sources` и `/value` проверены живьём: добавление,
скан, включение/выключение, удаление; 30-дневная польза складывается по корпусам
на странице. В SPA строка не добавлена — правка `admin/static/app.js` блокируется
защитой записи на строках auth (ложные срабатывания на `password`/`token`), а
переименование состояния входа ради обхода — риск для доступа.

---

## 1. Что заказано — на языке проекта

| Промт | Термин промта | Термин RAGRAF | Состояние сегодня |
|---|---|---|---|
| 1 | Global Index, Global Graph, Router, Answer Fusion | реестр источников над реестром разработок; карта метаданных файлов; выбор корпуса; слияние ответа | реестр разработок есть, карты файлов вне корпусов нет, выбора корпуса как операции нет |
| 2 | профили индексации, расширения, извлечение документов | `LANG_BY_EXT`, `TREE_LANGS`, `PROFILES`, текстовые чанки | 16 языков деревом и ~10 текстом; PDF/Office/почты/OCR нет, зависимостей тоже нет |
| 3 | секреты как отдельный контур с хранилищем значений | реестр секретоносных мест | отсев по имени до чтения уже есть; значений нет и быть не может |
| 4 | Value Dashboard, field report, opt-in аналитика | телеметрия `usage_call`/`usage_session`, `task_stats` | счёт и отчёт о пользе есть; панели «польза», `feedback` и экспорта нет |

Главное следствие: **промт 1 на две трети уже реализован под другими именами.**
`ensure` находит корпус по каталогу, создаёт его для незнакомого проекта и
пересобирает отставший (`src/ragraf/ensure.py:301`); реестр хранит корень,
конфиг, ревизию, `capability`, `corpus_hash`, число файлов и историю прогонов
(`Entry`, `src/ragraf/catalog.py:77`); дешёвая сверка «вход не менялся» — это
`corpus.stat_fingerprint` (`src/ragraf/corpus.py:310`) и
`watch.read_fingerprint` (`src/ragraf/watch.py:714`). Новое здесь — не «глобальный
индекс», а **слой метаданных над корпусами** и **маршрутизатор**, который называет
причину выбора.

---

## 2. Факты: что уже есть (проверено по коду в этой сессии)

| Возможность | Где | Что именно |
|---|---|---|
| Реестр разработок | `src/ragraf/catalog.py:77-105`, `:260` | запись на разработку: корень, конфиг, `rev_id`, `capability`, `corpus_hash`, `file_count`, `runs[]`; запись прогона через `record_run` |
| Разрешение «какой индекс использовать» | `src/ragraf/ensure.py:301-346` | `actual` / `reindexed` / `created` / `partial` / `not_a_project` / `error` с причиной |
| Дешёвая свежесть | `src/ragraf/corpus.py:310`, `src/ragraf/watch.py:688`, `:714` | отпечаток по путям, размерам и времени; полная сверка только при расхождении |
| Сумма корпуса | `src/ragraf/corpus.py:63-73` | `path\tsha256` по файлам, которые пойдут в индекс; порядок — `sorted` |
| Отсев секретоносного | `src/ragraf/config.py:85`, `src/ragraf/corpus.py:161-197` | 8 масок по умолчанию; отсев по относительному пути **до** чтения; счётчик `skipped_secrets` и разбивка `excluded["secrets:<шаблон>"]` |
| Настройки | `src/ragraf/settings.py:40`, `:145`, `:292` | плоский набор ключей в `catalog/settings.json`, закрытые перечисления и диапазоны |
| Прецедент файла настроек вне корпуса | `src/ragraf/db_access.py:130` | `catalog/db_access.json`; пароль наружу не отдаётся, приоритет у окружения |
| Телеметрия | `src/ragraf/telemetry.py:277`, `:299`, `:120` | `usage_call` (включая `result_chars`, `est_tokens`, `agent`, `task`, `tool`), `usage_session`; оценка токенов |
| Удаление индекса с отчётом | `src/ragraf/catalog.py:390` | `clear_index` — прецедент «мгновенно отключить и удалить» |
| Панель | `admin/static/app.js:73-92` | группы Monitoring / Index / Observability / System |

Чего нет вообще и что придётся писать: карты файлов вне корпусов, операции
маршрутизации, извлекателей документов, реестра секретоносных мест, панели
пользы, команды `feedback`.

---

## 3. Конфликты промтов с инвариантами проекта (и как они разрешаются)

Это главный раздел: без него план реализуем, но реализация сломает либо
приватность, либо свежесть.

### 3.1. Секреты: значения не индексируются и не хранятся ни в каком виде

> **Изменено решением владельца 2026-10-24.** Раздел сохранён как история
> решения. Действует другая граница: значения хранятся, но только
> шифрованными в `catalog/`, только с выдачей по праву и аудитом;
> инвариант «не индексировать» не менялся. Устройство —
> `docs/03_implementation/SECRETS-VAULT.md`, правило — `AGENTS.md` §6.1.

Промт 3 требует зашифрованное хранилище значений, API использования и ротацию.
Это прямое нарушение правила проекта: секретоносные пути **не открываются**,
допустимы только имя, размер и правило (`AGENTS.md`, раздел 6). В коде это уже
жёстко: маски `DEFAULT_SECRETS` (`src/ragraf/config.py:85`) применяются к
относительному пути, а не к содержимому (`src/ragraf/corpus.py:193`), поэтому
индексатор физически не читает такой файл.

Допустимая форма функции — **реестр мест, а не значений**:

- что хранится: относительный путь, какое правило сработало, размер, дата
  изменения, корень-источник;
- чего не хранится: содержимое, фрагмент, хэш значения, «отпечаток» секрета
  (даже префикс — это уже утечка части значения);
- что отдаётся агенту: «в таком-то каталоге исключено N файлов, правила такие»;
  путь — только локальному владельцу в панели на `127.0.0.1`;
- чего нет: API выдачи значения, расшифровки, ротации, внешнего менеджера.
  Ротация — задача менеджера секретов, а не индексатора.

Что уже даёт основу: `skipped_secrets` и разбивка по шаблонам в сводке скана.
Нужен только отчёт по правилам и путям, без нового хранилища значений.

### 3.2. Глобальная карта не входит в контрольную сумму корпуса

Сумма корпуса считается по файлам, которые пойдут в индекс, и это условие
записано в коде дословно: иначе сумма никогда не совпадёт с записанной в
ревизии и сторож будет переиндексировать бесконечно
(`src/ragraf/corpus.py:63-73`). Поэтому:

- карта метаданных живёт **вне** `.ragraf` — в `catalog/`, рядом с реестром;
- `corpus_hash` не меняется: карта не входит ни в него, ни в отпечаток
  (`corpus.stat_fingerprint`);
- карта читает только метаданные каталога (`scandir`): имя, размер, время;
  содержимое на этом этапе не читается вообще.

### 3.3. Второй движок и PostgreSQL не появляются

Вопрос уже решён: вариант C в `PLAN-ONE-TOOL-XERJ-PARITY.md`, раздел 4 —
всё на SQLite (`sqlite-vec` + FTS5), ES-совместимость только тонким
транслятором подмножества и только по подтверждённой потребности. Промт 1
предлагает «SQLite/PostgreSQL» как выбор; возвращаться к этому не нужно.

### 3.4. Телеметрия наружу — только по явному согласию и с показом полезной нагрузки

Принцип проекта: локальность по умолчанию, ничего не уходит с машины само
(`PLAN-ONE-TOOL-XERJ-PARITY.md`, раздел 3, принцип 4). Промт 4 предлагает
анонимную отправку раз в сутки. Решение: **сеть в первой версии не нужна
вообще.** Экспорт — файл, который владелец сначала видит. Отправка — отдельное
решение позже, с показом точного JSON и отключением по умолчанию.

### 3.5. A/B-эксперименты — только локальные и не на защитных значениях

У локального инструмента нет парка установок, поэтому «разбиение установок на
группы» meaningless: одна машина — одна группа. Допустимы сравнения на своих же
данных (порог GPU, веса гибрида, порог свежести) с записью в журнал. Защитные
значения (`dedupe_enabled`, режим «Я это ты», реестр секретов) не
экспериментируются: они остаются выключенными по умолчанию.

### 3.6. Field report не создаёт PR и не подписывается агентом

Правило §10.8: коммиты и PR подписываются идентичностью владельца, не агента.
Поэтому `ragraf feedback` только **готовит локальный файл** и печатает путь;
fork, ветку и PR создаёт человек. Никаких автоматических сетевых действий.

### 3.7. Вложенных настроек не будет

`settings` — плоский словарь с закрытыми перечислениями
(`src/ragraf/settings.py:40`, `:145`). Ключи вида `iam_you.enabled` туда не
добавляются: появится «настройка», которую никто не проверяет. Список
источников — отдельный файл `catalog/sources.json` по образцу
`catalog/db_access.json:130`, а отдельного флага режима нет: источники
добавляются явно, карта не читает содержимое, маршрут только читает. Флаг,
который никто не проверяет, был бы декоративным — поэтому его не добавляем.

---

## 4. Целевая архитектура в терминах проекта

| Слой | Данные | Код | Чего слой не делает |
|---|---|---|---|
| **Source Registry** | `catalog/sources.json`: корень, вид (`project` / `folder` / `share`), включён, приоритет, дата добавления | новый `src/ragraf/sources.py` | не индексирует и не читает содержимое |
| **Global Map** | `catalog/global_map.sqlite`: `source`, `file_meta`, `secret_place`, `scan_run` | новый `src/ragraf/global_map.py`; обход — по образцу `corpus.scan` (`src/ragraf/corpus.py:126`) | не строит векторов, не входит в `corpus_hash` |
| **Corpus Registry** | `catalog/catalog.json` (есть) | `src/ragraf/catalog.py` | не меняется |
| **Router** | ответ операции: кандидаты, причина, рекомендованный корпус | новый `src/ragraf/route.py` + `Facade.op_route` + `ragraf_route` | не отвечает по содержимому; только маршрут |
| **Extractors** | чанки с провенансом (`path`, страница/лист/раздел) | `src/ragraf/extract/` (пакет есть) | не ходит в сеть, не запускает OCR по умолчанию |
| **Secrets Register** | `secret_place` в Global Map: путь, правило, размер, дата | `src/ragraf/sources.py` (часть) | **не хранит значений и отпечатков** |
| **Growth** | агрегаты над `usage_call` | `src/ragraf/telemetry.py`, панель `usage` | не отправляет ничего по сети |

Порядок вызова, который и есть «глобальный находит, локальный уточняет»:

1. `route(query)` ищет по Global Map (имена, пути, типы) и по реестру корпусов;
2. для каждого кандидата показывает: корень, дата изменения, есть ли локальный
   корпус, его ревизия, `capability`, возраст и совпал ли отпечаток;
3. если корпус есть и свежее — рекомендует его и (отдельным вызовом) отвечает
   по нему существующими операциями;
4. если корпуса нет — честно говорит «точные материалы здесь, локального
   индекса нет» и предлагает `ensure`;
5. в ответе всегда блок `candidates` с полем `why` и блок `recommended`.

---

## 5. Схема данных и детерминизм

```sql
source(id, root, kind, enabled, priority, added_at, last_scan_at,
       scan_status, reason)
file_meta(source_id, rel_path, name, ext, size, mtime, mime_guess,
          availability, first_seen, last_seen)
secret_place(source_id, rel_path, rule, size, mtime)
scan_run(id, source_id, started_at, finished_at, files_seen,
         files_skipped, errors, fingerprint)
```

Обязательные свойства (каждое ломается молча, поэтому вынесено в проверки):

- **Порядок обхода детерминирован** — `sorted`, как в `corpus.scan`
  (`src/ragraf/corpus.py:126`); иначе отпечаток карты дрожит между прогонами.
- **`fingerprint` карты** считается по `(rel_path, size, mtime)` и хранится в
  `scan_run`; два прогона на неизменённом дереве обязаны дать одно значение.
- **Содержимое не читается**: в схеме нет ни одной колонки под текст, хэш
  содержимого или фрагмент. Это проверяется свойством (раздел 8), а не текстом.
- **Размер и лимиты**: каталог без прав и сетевой путь, недоступный в момент
  скана, дают `availability=unavailable` и причину, а не исключение.
- **Удаление источника** удаляет его строки и файл карты целиком, но не трогает
  ни один файл на диске (прецедент — `catalog.clear_index`, `:390`).

---

## 6. Router: формула, честность и запрет на «умный» ответ

Формула из промта 1 сохраняется, но все слагаемые обязаны быть объяснимыми:

```text
score = w_r * relevance + w_f * freshness + w_l * local_quality + w_a * authority
```

| Слагаемое | Откуда | Ограничение |
|---|---|---|
| `relevance` | совпадение по имени/пути в Global Map; при наличии корпуса — существующий гибридный поиск | не «семантика вместо точного идентификатора»: гибрид, а не замена |
| `freshness` | `runs[-1].indexed_at` + совпадение отпечатка (`watch.read_fingerprint`) | устаревший корпус не может выиграть только за счёт релевантности |
| `local_quality` | `capability` (`full` = 1,0, `degraded` = 0,5, нет корпуса = 0) + доля `files_failed` | `full` не завышается: без рёбер от свойств кода он запрещён |
| `authority` | явный `priority` источника | ничья разрешается путём лексикографически — как в `dedupe` (`src/ragraf/dedupe.py:165-223`) |

Правила, без которых маршрутизатор превращается в гадалку:

- **Никакой модели в маршрутизаторе.** Решение детерминированное; одинаковый
  запрос и одинаковое состояние дают одинаковый ответ побайтово.
- **Ответ без содержимого.** Маршрутизатор не цитирует документы; он называет
  места и качество индексов.
- **Одна свежесть.** Нельзя смешивать «свежий код» и «карту недельной давности»
  без пометки: у карты своё время, у корпуса — `rev_id`.
- **Нет локального индекса — так и сказать.** Промт прав в главном: честный
  ответ «индекса нет, вот каталог» полезнее выдуманного фрагмента.

---

## 7. Профили извлечения: что можно сразу, а что требует решения владельца

Уже поддержано: 16 языков разбора (`TREE_LANGS`, `src/ragraf/config.py:208`)
и текстовые языки (markdown, sql, batch, yaml, json, toml, ini, xml, text, rst).
Список языков разбора обязан совпадать с `PROFILES`
(`src/ragraf/extract/generic.py:101`), иначе новый язык тихо теряет разбор без
единой ошибки — это записанный инвариант, а не пожелание.

Новые профили из промта 2 делятся на три группы:

| Группа | Форматы | Что нужно | Решение |
|---|---|---|---|
| A. Без новых зависимостей | `.csv`, `.tsv`, `.log`, `.ini`, `.conf`, `.properties`, `.env` (только имя, содержимое исключено), `.sql`-дампы как текст | расширение `LANG_BY_EXT` + профиль `text`/`structured` | делать сразу |
| B. Нужны библиотеки | `.pdf`, `.docx`, `.xlsx`, `.pptx`, `.eml`, `.msg`, `.rtf`, `.odt` | `pypdf`, `python-docx`, `openpyxl`, `extract-msg` — новые зависимости | только с согласия владельца |
| C. Отдельный контур | OCR изображений, архивы (манифест), `.pst`/`.ost`, `.evtx`/`.pcap` | внешние бинарники (Tesseract), лимиты, риск таймаутов | не в первой версии |

Правила, которые применяются к любой группе:

- **Сначала метаданные, потом содержимое.** Даже бинарный файл полезен для
  поиска по имени, пути и датам.
- **Тип определяется по содержимому, а не только по расширению** — иначе файл
  без расширения выпадает молча.
- **Лимит размера и глубины.** Текст — единицы мегабайт, PDF — десятки,
  архив — только манифест.
- **Провенанс чанка обязателен**: страница, лист, раздел; иначе ответ «где это
  написано» снова станет догадкой.
- **Любой новый язык проходит `tests/test_lang_routing.py`** и попадает в
  `docs/AGENTS-context.md` тем же изменением.

---

## 8. Фазы

Оценки — планировочные, не замеры. Порядок обязателен: каждая фаза опирается на
предыдущую.

### G0. Каркас: источники и карта метаданных (3–5 дней)

- `catalog/sources.json` + `src/ragraf/sources.py`: добавление, удаление,
  включение, приоритет, явное согласие на каждый корень.
- `src/ragraf/global_map.py`: скан метаданных, `scan_run`, отпечаток.
- CLI: `sources add|list|remove`, `map scan|status`; панель — раздел `catalog`
  (`admin/static/app.js:73`), без новой группы.
- **Приёмка:** два прогона на неизменённом дереве дают одинаковый отпечаток;
  файл, подпадающий под `DEFAULT_SECRETS`, попадает только в `secret_place`;
  маркер содержимого тестового секретного файла не встречается ни в одном файле
  `catalog/` (проверка поиском байтов по всем созданным данным).

### G1. Маршрутизатор и слияние ответа (3–5 дней)

- `src/ragraf/route.py`, операция `op_route`, инструмент `ragraf_route`.
- Ответ: `candidates[]` с `why`, `recommended`, `local_index{rev, capability,
  age, fingerprint_match}` или `null`.
- **Приёмка:** запрос по неполному имени находит корень и корпус; при
  отсутствии корпуса ответ содержит `local_index: null` и предложение `ensure`;
  два прогона дают идентичный JSON (кроме времени замера).

### G2. Извлечение документов, группа A (2–3 дня)

- Расширение `LANG_BY_EXT` и профилей для текстовых/структурированных форматов.
- **Приёмка:** `tests/test_lang_routing.py` зелёный; новые форматы дают чанки с
  `path:line`; `repo_map` показывает их без роста числа «неразобранных».

### G3. Реестр секретоносных мест (2–3 дня)

- Отчёт по правилам и путям поверх `secret_place`; панель — раздел `access`
  (`admin/static/app.js:88`).
- **Приёмка:** отчёт называет правило и путь; значения не читаются — проверка
  подменой `open`/`Path.open` на функцию, которая падает при вызове для
  секретного пути.
- **Запрет:** любая попытка добавить колонку под значение, фрагмент или
  «отпечаток» секрета — отклоняется на ревью, а не «обсуждается».

### G4. Прибыль и отчёт о пользе (3–5 дней)

- Агрегаты над `usage_call`: сколько запросов, сколько сэкономили символов и
  токенов, сколько маршрутов привело к локальному корпусу, сколько ошибок по
  категориям. Основа уже есть (`src/ragraf/telemetry.py:277`).
- Панель `usage` (`admin/static/app.js:91`) — блок «польза за 30 дней».
- `ragraf feedback` — локальный черновик отчёта в `catalog/field_reports/`,
  печать пути; сеть не используется.
- **Приёмка:** экспорт не содержит путей, имён проектов, запросов и текстов;
  проверка — тест на фикстуре с «ядовитыми» строками.

### G5. Извлечение документов, группа B (8–12 дней, после решения владельца)

- Новые зависимости, лимиты, деградация: нет библиотеки — формат помечен
  `metadata_only`, а не пропадает молча.

### G6. Сетевые источники и отправка аналитики (только по отдельному решению)

- UNC/SMB/SSH/S3 — отдельный коннектор, проверка доступности, таймауты,
  никогда не блокирует ответ.
- Отправка аналитики — только с показом точного JSON и отключением по умолчанию.

---

## 9. Проверки

Новые проверки (каждая — про свойство, а не про написание):

| Файл | Что доказывает |
|---|---|
| `tests/test_sources_registry.py` | добавление/удаление источника; удаление не трогает файлы на диске; приоритет детерминирован |
| `tests/test_global_map.py` | два скана → один отпечаток; в схеме нет колонок под содержимое; секретный файл только в `secret_place` |
| `tests/test_secrets_register.py` | для секретного пути `open` не вызывается; отчёт называет правило |
| `tests/test_route.py` | одинаковый вход → идентичный ответ; `local_index: null` без корпуса; `capability=degraded` не выигрывает у `full` при равной свежести |
| `tests/test_extractors.py` | чанк несёт страницу/лист/раздел; лимит размера даёт отказ, а не таймаут |

Чекер `tools/Invoke-ProjectDoctrine.ps1` дополняется двумя проверками:

1. **Новый слой отдаёт провенанс.** Любая новая операция, возвращающая факты,
   содержит `index_rev`, `provenance`, `stale_warnings`; проверка — по форме
   ответа, а не по тексту функции.
2. **Глобальная карта вне корпуса.** Путь хранилища карты не лежит внутри
   `.ragraf` и не участвует в `corpus_hash`; проверка — по фактическому вызову
   функций, а не поиском строки.

Обязательный прогон после любой фазы:

```powershell
python tools/ragraf.py -p config/ragraf.yaml reindex
python tools/ragraf.py -p config/ragraf.yaml check
python tools/check_bom_crlf.py
pwsh -NoLogo -NoProfile -NonInteractive -File tools/Invoke-ProjectDoctrine.ps1
```

Ожидаемый baseline чекера: **0 ошибок, 0 предупреждений**; число проверок
читать из его итоговой строки, а не из документа.

---

Итоговый отчёт приёмки — `docs/03_implementation/ACCEPTANCE_REPORT.md`:
критерии, измеренные числа, что не подтверждено и откат.

## 10. Риски

| Риск | Проявление | Как снимаем |
|---|---|---|
| Размывание гарантии «факт = файл:строка» | маршрутизатор начинает отвечать текстом | ответ маршрута без содержимого, только места и качество индексов |
| Ложный `full` | `capability=full` без рёбер от свойств кода | `local_quality` читает `capability` из ревизии, а не из настроения; проверка уже есть в чекере |
| Зацикливание сторожа | карта попала в `corpus_hash` | карта вне `.ragraf`; тест на неизменность суммы после скана карты |
| Приватность путей | карта хранит полные пути и имена | пути только локально, панель на loopback, экспорт без путей |
| Сетевые шары | скан висит на недоступном хосте | таймаут на корень, статус `unavailable`, никогда не блокирует ответ |
| Второй реестр | `sources.json` расходится с `catalog.json` | источник ссылается на категорию реестра, а не дублирует её поля |
| Рост числа инструментов | поверхность для агента распухает | потолок согласуется владельцем; новые имена не удаляют старые (алиасы) |
| Таймауты на больших деревьях | уже наблюдалось (38 675 файлов) | лимиты, предварительная оценка, прогресс, разбиение |

---

## 11. Откат

| Фаза | Что откатывается | Как |
|---|---|---|
| G0 | `catalog/sources.json`, `catalog/global_map.sqlite` | удалить оба файла; корпуса и реестр не затронуты |
| G1 | новый инструмент и операция | убрать из `TOOLS`, `_handlers`, `facade`; источники и карту удалить; отдельного флага режима нет |
| G2 | расширения языков | вернуть `LANG_BY_EXT`/`PROFILES`; новые форматы снова не индексируются |
| G3 | отчёт по секретам | удалить `secret_place` и раздел панели; сами файлы не трогались никогда |
| G4 | журнал пользы | удалить агрегаты; `usage_call` остаётся, он существовал до фазы |
| G5 | новые зависимости | убрать из `pyproject.toml`; форматы деградируют до `metadata_only` |

Ни одна фаза не меняет схему `.ragraf`, нумерацию ревизий и контракт
существующих операций — поэтому откат не требует переиндексации корпусов.

---

## 12. Что нужно от владельца до начала

1. **Корни.** Какие каталоги разрешены для карты метаданных; входят ли сетевые
   пути и чужие диски.
2. **Зависимости.** Разрешить ли `pypdf`, `python-docx`, `openpyxl`,
   `extract-msg` для группы B (это новые пакеты, не текущий стек).
3. **OCR.** Нужен ли вообще и с какими лимитами (внешний бинарник Tesseract).
4. **Телеметрия.** Достаточно ли локального экспорта файлом или нужна отправка
   (вторая — отдельное решение с показом полезной нагрузки).
5. **Потолок инструментов.** Сейчас 40; сколько допустимо добавить.
6. **Секреты.** Подтвердить формулировку «реестр мест без значений» как
   окончательную.

---

## 13. Что осознанно не делаем

- Не строим второй индекс содержимого всего диска.
- Не храним значения, фрагменты и отпечатки секретов.
- Не включаем отправку аналитики наружу: это противоречит локальности.
  Сетевые корни — только UNC/смонтированные пути с явным `--network`; URL-коннекторы
  (`ssh://`, `s3://`, `webdav://`, `http://`) отклоняются с причиной, а не
  притворяются путём. Локальность новых слоёв проверяется чекером
  (`new layers stay local`).
- Не тащим OCR и архивы в базовую поставку.
- Не меняем `corpus_hash`, нумерацию ревизий и контракт существующих операций.
- Не вводим вложенные настройки в плоскую схему `settings`.
- Не автоматизируем PR и «звёзды»: приглашение — после пользы и вручную.

---

## 14. Находки этой сессии (посторонние, не исправлялись)

1. **Число инструментов в плане XERJ устарело.** `PLAN-ONE-TOOL-XERJ-PARITY.md`
   (раздел 1.1, «проверено 14.10.2026») называет 43 инструмента; замер
   регуляркой чекера в этой сессии — 40. Число в плане — историческое, но
   читается как действующее; при следующей правке плана его нужно помечать
   датой замера.
2. **`capability=full` расходится с текстом паспорта.** `status` на `rev-0160`
   отдаёт `capability=full`, `CPG: да`; стадия `cpg` подтвердила 1963 ребра
   запросами к файлу CPG при недоступном экспорте и 17 341 неразрешённом вызове.
   Проверка чекера `capability reflects the actual graph` прошла, то есть по
   собственному свойству проекта режим согласован с содержимым графа и
   завышения нет. Остаётся расхождение **текста**: `AGENTS.md` (раздел 5) и
   `AGENTS-TRAPS.md` описывают этот корпус как `degraded`/`partial` — это
   описание прежнего состояния; его нужно помечать датой либо обновлять
   отдельной правкой.
3. **Снимок корпуса в паспорте устарел.** `docs/AGENTS-context.md` описывает
   `rev-0006` и 471 файл; сейчас 662 файла. Это ожидаемо (там же сказано
   сверять командой), но при чтении паспорта об этом нужно помнить.
