# Протоколы замеров и приёмки

**Назначение:** сделать пользу RAGRAF **измеримой числом**, а не «стало удобнее». Каждая метрика имеет процедуру, инструмент снятия, артефакт и порог.
**Основание:** §2.3 плана, §7 ТЗ. **Пилот:** `revoap.com`. **Второй корпус:** `z.batteryservice.ru` (без git → часть метрик не снимается, см. §3).

---

## 1. Правила замеров

1. **Baseline снимается ДО внедрения индекса** и сохраняется в `bench/baseline/`. Замер, сделанный после, не является baseline.
2. **Одинаковые задачи** для baseline и для индекса: формулировка, файлы, критерий «сделано» — идентичны, различие только в доступных инструментах.
3. **Задачи фиксируются заранее** списком `bench/tasks.yaml`; задачи, придуманные после просмотра ответов индекса, в зачёт не идут.
4. **Каждый замер — артефакт**: JSON с числами и markdown с интерпретацией. Без артефакта замер не существует.
5. **Счёт токенов** — `tiktoken`; для сравнений фиксируется один и тот же токенизатор.
6. **Никаких чужих процентов** в качестве цели: ориентиры из литературы используются только как порядок величины, и это указывается рядом с числом.
7. **Замер воспроизводим**: команда снятия записывается в артефакт.

---

## 2. Набор задач для замеров (черновик, уточняется с заказчиком)

| ID | Тип | Формулировка (сокращённо) | Что проверяет |
|---|---|---|---|
| T-01 | Навигация | «Найди обработчик настройки X и покажи, откуда приходят данные» | M1 |
| T-02 | Навигация | «Где формируется ответ API Y и какие поля он отдаёт» | M1 |
| T-03 | Связи | «Кто вызывает сервис Z и что будет, если изменить его сигнатуру» | M1, M3 |
| T-04 | Схема БД | «Какие колонки таблицы T реально читаются кодом и где» | M1, M6 |
| T-05 | Доработка | «Добавь фильтр по дате в список W» (реальная мелкая правка) | M2, M3 |
| T-06 | Доработка | «Добавь новый параметр в настройки S с сохранением в БД» | M2, M3 |
| T-07 | Доработка | «Вынеси повторяющийся блок в метод» (проверка `slice_boundary`) | M2, M5 |
| T-08 | Impact | «Что затронет переименование метода M» | M2 |
| T-09 | Impact | «Что затронет добавление колонки C в таблицу T» | M2 |
| T-10 | Документация | «Что в документации сказано о модуле N» | M1, M6 |

Штатный размер набора — **≥ 20 задач**, из них ≥ 8 — правки.

---

## 3. Матрица «метрика × корпус»

| Метрика | `revoap.com` (есть git) | `z.batteryservice.ru` (нет git) |
|---|---|---|
| M1 навигация | да | да |
| M2 impact по истории | **да** (103 коммита) | нет → только синтетические задачи |
| M3 экономия контекста | да | да |
| M4 сжатие среза | да | да |
| M5 свежесть | да | да |
| M6 проверяемость | да | да |
| M7 стоимость обновления | да | да |

---

## 4. M1 — точность навигации

**Вопрос:** находит ли агент нужный файл и строку с первого запроса?

**Процедура.**
1. Для каждой задачи T-01…T-04, T-10 вручную фиксируется «эталонный ответ»: список `path:line`, которые **обязаны** быть в ответе (составлен по коду, а не по выдаче системы).
2. Baseline: агент получает задачу и работает **только** через `rg` и чтение файлов (индекс недоступен).
3. Опыт: агент получает ту же задачу, где первый шаг — операции фасада; `rg` разрешён только как fallback.
4. Фиксируется: число запросов до первого правильного `path:line`; полный/частичный/пустой ответ; число прочитанных файлов; число прочитанных строк.

**Артефакт:** `bench/results/M1_<corpus>_<rev>.json` + таблица.

| Показатель | Baseline | С индексом | Порог |
|---|---|---|---|
| Доля задач, решённых с первого запроса | замер | замер | улучшение обязательно |
| Среднее число прочитанных файлов на задачу | замер | замер | ≥ 2× снижение |
| Полнота эталонного ответа | замер | замер | ≥ 90% |

---

## 5. M2 — полнота impact-анализа (главная метрика)

**Вопрос:** можно ли по индексу предсказать, какие файлы реально затронет изменение?

**Процедура (gold set из истории).**
1. Из 103 коммитов пилота отбираются **20 коммитов-правок**, где выполнено условие: изменено ≥ 2 файла, и хотя бы один — не тот, где лежит изменённый публичный метод (то есть простая «найти название» не работает).
2. Для каждого коммита берётся состояние **до** коммита, из него строится индекс.
3. Формулируется вопрос «что затронет изменение <символ>», без подсказки путей.
4. `impact` вызывается в трёх режимах: `hard` (только `stated`), `full`, и **baseline** — ручной поиск через `rg`.
5. Считаются precision / recall / F1 по файлам и по символам:
   - **precision** = предсказанные и реально изменённые / предсказанные;
   - **recall** = реально изменённые и предсказанные / реально изменённые.

**Артефакт:** `bench/results/M2_<rev>.json`, включая список коммитов и разбор ошибок (что пропустили и почему: динамический вызов, SQL-строка, `include`-обёртка, конфиг).

**Ориентир:** измеренный потолок в литературе — **precision 69% / recall 79% / F1 55%** (LCIP, `arXiv:1512.07435`, 17 574 изменений, Java, симуляция мутациями). Наш случай другой (реальные правки, PHP), поэтому сравнение — только ориентир порядка; рядом с числом это указывается.

| Показатель | Baseline (`rg`) | `hard` | `full` |
|---|---|---|---|
| precision по файлам | замер | замер | замер |
| recall по файлам | замер | замер | замер |
| F1 | замер | замер | замер |

**Отдельный разбор ошибок обязателен:** без него цифра бесполезна. Классификация пропусков: (а) динамический вызов; (б) SQL/строка; (в) `include`; (г) конфиг-зависимость; (д) не найдено вообще.

---

## 6. M3 — экономия контекста и стоимости задачи

**Вопрос:** сколько токенов и вызовов требует правка с индексом и без?

**Процедура.** Для задач T-05…T-07 (реальные мелкие правки):
1. Baseline: обычная работа агента (`rg` + чтение файлов).
2. Опыт: первый шаг — фасад, затем правка.
3. Фиксируется: токены входного контекста, число tool-вызовов, число прочитанных файлов/строк, результат (правка работает / не работает).

**Артефакт:** `bench/results/M3_<corpus>_<rev>.json`.

| Показатель | Baseline | С индексом |
|---|---|---|
| Медиана токенов на задачу | замер | замер |
| Медиана tool-вызовов | замер | замер |
| Доля задач, решённых без чтения файлов целиком | замер | замер |

**Оговорка:** прямое сравнение граф-индекса с «просто большим контекстом» в литературе не найдено, поэтому цель — собственная базовая линия, а не чужая цифра.

---

## 7. M4 — сжатие среза

**Вопрос:** сколько контекста экономит `slice` и укладывается ли `repo_map` в бюджет?

**Процедура.**
1. Для 20 символов пилота (разной величины: от 20 до 1000 строк) вычисляются токены: (а) тела всех связанных символов (окружение глубины 2), (б) `slice`, (в) `repo_map` с бюджетом.
2. Считается отношение `tokens(slice) / tokens(окружение)` и `tokens(repo_map) / tokens(весь корпус выбранного модуля)`.
3. Отдельно проверяется ФТ-20: `repo_map` при `budget_tokens = 2000` не превышает бюджет.

**Артефакт:** `bench/results/M4_<rev>.json`.

| Показатель | Значение |
|---|---|
| Медиана сжатия среза | замер |
| 10-й и 90-й процентиль | замер |
| Файлов с файлами > 1000 строк, где сжатие хуже всего | список |

**Запрет:** сравнивать полученное число с 68–70% / 90,93% из статьи — эти проценты первоисточником не подтверждены (см. `01_ARTICLE_COVERAGE.md`). Допустимо только упоминание порядка величины.

---

## 8. M5 — свежесть и защита от неверных выводов

**Вопрос:** сколько раз агент опирался на устаревший факт, **не зная** об этом?

**Процедура (сквозной сценарий).**
1. Индексация пилота → `rev-N`.
2. Пачка из 5–10 правок агента, включая **одну extract-method** и **одну миграцию БД**.
3. После каждой правки — регистрация дельты (`drift --scan`), **индекс не трогается**.
4. Прогон 20 задач из набора: фиксируется, у скольких ответов были `stale_warnings`, и были ли случаи, где:
   - ответ опирался на устаревший факт **без** пометки → **инцидент** (цель: 0);
   - ответ содержал пометку и агент перешёл к `live_read` → корректное поведение;
   - агент увидел пометку и всё равно использовал индекс → **инцидент второго рода** (цель: 0).
5. `reindex --commit`; проверяется, что: новая ревизия создана, ledger пуст, архив содержит разделы «изменения», «что устарело», «границы срезов».

**Артефакт:** `bench/results/M5_<corpus>.json` + копия архива `drift/archive/*.md`.

| Показатель | Значение | Порог |
|---|---|---|
| Инцидентов опоры на устаревшее без пометки | замер | **0** |
| Инцидентов второго рода | замер | **0** |
| Доля ответов со `stale_warnings` при непустом ledger | замер | 100% для затронутых путей |
| Записей `slice_boundary` при extract-method | замер | ≥ 1 |

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

---

## 9. M6 — проверяемость

**Вопрос:** можно ли автоматически подтвердить ссылки в ответах?

**Процедура.** Для 200 фактов из ответов всех операций:
1. существует ли файл `evidence_path` в рабочем дереве ревизии;
2. попадает ли `evidence_line` в диапазон узла;
3. совпадает ли `index_rev` факта с текущей ревизией (иначе — должен быть `stale`).

**Артефакт:** `bench/results/M6_<rev>.json`.

| Показатель | Порог |
|---|---|
| Доля фактов с существующим файлом | 100% |
| Доля фактов с попаданием строки в диапазон | ≥ 99% |
| Доля фактов с корректной ревизией | 100% |
| Доля ссылок `live_read`, подтверждённых сравнением с файлом | 100% |

---

## 10. M7 — стоимость индексации и обновления

**Процедура.**
1. Полная индексация пилота: время по стадиям (AST, символы, CPG, БД, маршруты, текст, загрузка, эмбеддинги). Каждая стадия — отдельное число.
2. Переиндексация после правки 5 файлов: время и **доля переиспользованных эмбеддингов**.
3. Замер отклика операций: 100 запросов каждого вида, 95-й процентиль.
4. Проверка воспроизводимости: `drop` + `index` → сравнение метрик M1–M4 и идентификаторов (ФТ-13).
5. Проверка памяти: пиковое потребление процесса CPG.

**Артефакт:** `bench/results/M7_<rev>.json`.

| Показатель | Порог (НФТ) |
|---|---|
| Полная индексация пилота | ≤ 15 мин |
| Переиндексация после правки 5 файлов | ≤ 3 мин |
| `symbol`/`references`/`callers` p95 | ≤ 200 мс |
| `impact` (глубина 3) p95 | ≤ 1 с |
| `search` p95 | ≤ 1 с |
| `slice` p95 | ≤ 2 с |
| Пиковая память CPG | замер, лимит настраивается |
| Совпадение идентификаторов при пересборке | 100% |

---

## 11. Тесты (сводная карта)

| Тест | Что проверяет | Требования |
|---|---|---|
| **M1-T1** | создание ревизии | ФТ-1 |
| **M1-T2** | границы символов (100 символов против файла) | ФТ-2 |
| **M1-T3** | отчёт покрытия, сумма файлов | ФТ-11 |
| **M1-T4** | детерминированность (двойная сборка) | ФТ-13, НФТ-5 |
| **M1-T5** | чтение и валидация конфига | ФТ-37 |
| **M1-T6** | эффект исключений | ФТ-38 |
| **M1-T7** | фиксация версий зависимостей | НФТ-7 |
| **M2-T1** | символьные ссылки на 30 размеченных символах | ФТ-3 |
| **M2-T2** | разделение `calls` / `calls_heuristic` | ФТ-4 |
| **M2-T3** | схема БД и источник | ФТ-5 |
| **M2-T4** | SQL-литералы → таблицы, `SELECT *` | ФТ-6 |
| **M2-T5** | точки входа (15 размеченных) | ФТ-7 |
| **M2-T6** | документация и `documented_by` | ФТ-8 |
| **M2-T7** | разделение текста и структуры | ФТ-9 |
| **M2-T8** | провенанс у 1000 рёбер | ФТ-10 |
| **M2-T9** | срез потока данных на 10 размеченных случаях | ФТ-41 |
| **M2-T10** | `flow` на 10 парах узлов | ФТ-43 |
| **M3-T1** | деградация без CPG (и память) | ФТ-12, НФТ-8 |
| **M3-T2** | секреты не в графе/векторе/логах | ФТ-39, ФТ-40 |
| **M3-T3** | восстановление после прерывания сборки | НФТ-11 |
| **M4-T1** | все 17 операций объёма релиза 1 (поставленный фасад реализует 38): валидный вход и пустой результат | ФТ-14 |
| **M4-T2** | отказ на попытке передать язык запросов | ФТ-15 |
| **M4-T3** | полнота конверта ответа | ФТ-16 |
| **M4-T4** | разделение доказанных и эвристических связей в `impact` | ФТ-18 |
| **M4-T5** | `slice` без тел функций | ФТ-19 |
| **M4-T6** | честное «не найдено» + предложение `live_read` | ФТ-21 |
| **M4-T7** | совпадение MCP-инструментов и CLI | ФТ-22 |
| **M5-T1** | индекс неизменен при чтении | ФТ-24 |
| **M5-T2** | 5 способов обнаружения дельт | ФТ-25 |
| **M5-T3** | append-only (нет удаления) | ФТ-26 |
| **M5-T4** | дедупликация дельт | ФТ-27 |
| **M5-T5** | `slice_boundary` при extract-method | ФТ-29 |
| **M5-T6** | `schema_change` при миграции | ФТ-30 |
| **M5-T7** | `live_read` выделяет отличия | ФТ-31 |
| **M5-T8** | `reindex --commit`: новая ревизия, чистый ledger, архив | ФТ-32 |
| **M5-T9** | содержание архива (3 обязательных раздела) | ФТ-33 |
| **M5-T10** | ротация 3 из 5 при сохранении истории | ФТ-34 |
| **M5-T11** | `drift_ack` не удаляет запись | ФТ-36 |
| **M6-T1** | линтер PowerShell и проверка BOM/CRLF | НФТ-9 |
| **M6-T2** | изоляция артефактов в `.ragraf/` | НФТ-10 |
| **M6-T3** | воспроизводимость ответа по ревизии и параметрам | НФТ-12 |

---

## 12. Итоговый отчёт приёмки

Форма отчёта (`docs/03_implementation/ACCEPTANCE_REPORT.md`):

1. Сводная таблица К1–К9 с фактическим состоянием.
2. Сводная таблица метрик M1–M7: baseline, результат, порог, вердикт.
3. Разбор ошибок M2 по классам (динамика, SQL, `include`, конфиг).
4. Список узких мест с числами (что именно медленно/неточно и насколько).
5. Расхождения с ТЗ с письменным обоснованием (если требования изменены — с указанием, почему и с чьего согласия).
6. Что **не** подтвердилось из ожиданий — прямой раздел, без сглаживания.
7. Рекомендации для релиза 2 (языки, инструменты, узкие места).

**Правило отчёта:** любое утверждение «лучше» сопровождается числом и процедурой из этого документа.
