Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions ISSUE.md
Original file line number Diff line number Diff line change
Expand Up @@ -671,3 +671,60 @@
1. Запустить pytest-testmon или coverage на тех же 5 проектах, сравнить связи с ours.
2. Добавить ранг «что именно тестируется» (имя теста + последний вызов перед assert).
3. Разметить вручную 20–30 тестов, посчитать точность.

---

# P2 — Инвентаризация пробелов собственной системы (2026-10-04)

> Метод: инвентаризация проведена по коду с измерением (каждое утверждение — `file:line`,
> числа получены командой). Запись **не** ссылается на источник заимствования: это список
> собственных пробелов, а не заимствованные идеи. Все referents внутренние.
> Разделение принципиально: **F** = исправить своё, **A** = дополнить своё.
> Что уже есть и что дублировать не надо: память с терминальными статусами и каскадной
> ретракцией, протокол замороженных экспериментов с фальсификаторами, clean-state
> проверка, `UNKNOWN` ≠ `ALLOW`, контроли с право�� падать.

## F — исправить (по цене/пользе)

| # | Пробел | Где | Почему это дефект | Цена |
|---|---|---|---|---|
| **F1** | В ответах инструментов нет типизированных состояний деградации: «0 результатов» неотличимо от «не смог проверить» | `src/mcp/tools/search_tools.py:197,607` | Наше правило §19.6 существует только как **детектор** в гейте; продукт по-прежнему отдаёт голое значение. Тихий ноль на выходе — самый опасный вид | **S** |
| **F2a** | Нет точного runtime-guard'а на состав MCP-поверхности. Есть только floor `>= 40` под `importorskip` и `== 65` против **текстового** счётчика | `tests/test_mcp_schema_flat.py:38`, `tests/test_auto_doc_updater.py:157`, `src/core/auto_doc_updater.py:459` | Инструмент, выпадающий из `tool_classes`, не ломает ни один тест. Единственный точный guard помечен `importorskip` — при отсутствии SDK он просто исчезает | **S** |
| **F2b** | `_write_tool_names` содержит 7 имён, которые никогда не регистрируются как инструменты | `src/mcp/server_tools.py:246-257` | `readOnlyHint` считается для несуществующих имён → аннотация для призраков | **S** |
| **F2c** | 20 из 52 классов `MCPTool` не в `tool_classes`; часть дублирует inline-реализацию **того же имени** (`GetLogsTool`/`get_logs`, `GetHealthReportTool`/`get_health_report`, `ReadLiveFileTool`/`read_live_file`, `DualArmHealthCheckTool`/`dual_arm_health_check`, `NotifyChangeTool`/`notify_change`) | `src/mcp/tools/` + `src/mcp/server_tools.py` | Два кодовых пути на одно имя: правится один — второй остаётся. Тот же класс, что расхождение 18/34/2 | **M** |
| **F3** | Ведро слоёв `core→src.sources.*` объявлено «transitional», но печатает счётчик и **выходит 0**; срока давности нет | `scripts/check_layer_boundaries.py:80-84,105` | «Временное» без срока — постоянное. Правило, которое не может заблокировать, скоро перестанут читать | **S** (реестр исключений с обязательным протуханием) |
| **F4** | `tools/verification/run_all.py` — «докажи, что каждый guard умеет падать» — не в CI и не в pre-commit | `tools/verification/run_all.py` | Сильнейший актив репо опционален | **S** (одна строка в `.github/workflows/ci.yml`) |
| **F5** | `scripts/audit_protocol_guards.py` сейчас **красный**: exit 1, 7 находок (6 rate-tools без проверки пустой популяции + 1 артефакт без sha256) и не в CI | прогон 2026-10-03; `scripts/audit_protocol_guards.py:66,87-98` | T10/T11 не имеют тестовых утверждений (проверяется только `check_191`). Гейт, который красный и не запускается, хуже отсутствующего | **S** |
| **F6** | Три подтверждённых расхождения док↔код | `src/mcp/server_tools.py:6,10` (31/64 против фактических 32/65) · `.githooks/pre-commit:8-16` (8 против фактических 11) · `tools/verification/run_all.py:71-73` (публикует 8/62.5% при фактических 7) | Ровно класс из §19.11: число, которое нельзя получить сегодняшней командой | **S** |
| **F7** | Порог покрытия 38% при 67k строк; `mypy --strict` не включён | `.github/workflows/ci.yml:93` | Низкая планка не ловит регрессию качества; типизация отсутствует целиком | **M** |
| **F8** | Нет CI-задачи, которая падает, если lane flaky/отменён. Нужен `if: always()` + `needs:` по всем lane + проверка результата каждой | `.github/workflows/ci.yml` | Flaky lane, проскочивший мимо required-checks, тихо убирает гейт | **S** |
| **F9** | В negative-control манифесте `drift_gate` и `stale_detector` имеют `expected_exit=0` — «негативный контроль», ожидающий успех | `scripts/negative_controls/manifest.json` | Либо контроль назван неверно, либо он проверяет не то, что думают. Проверено запуском: `drift_gate` красный ещё и в другом дереве | **S** |
| **F10** | `experiments/` (193 файла, 29 974 строки) исключены из сбора pytest | `pyproject.toml:172` | Их собственные тесты не идут в CI; причина («`*_test.py` ломали сюиту») лечится переименованием, а не исключением каталога | **M** |

## A — дополнить (нет вообще)

| # | Чего не хватает | Зачем | Цена |
|---|---|---|---|
| **A1** | **Fail-closed доказательство свежести на границе выдачи.** Сейчас есть отдельный гейт `stale_detector` и отпечаток по git HEAD в `verify_on_read.py:577-601`, но выдача не проверяет свежесть той строки, которую возвращает | Превращает «индекс протух?» из глобального флага в доказательство **для каждого ответа**. Инструмент, печатающий метрику, обязан доказать, что мерил то, что отдаёт | **M** |
| **A2** | **Генерация `AGENTS.md`/системного промпта с build-hash + побайтовая сверка зеркал.** Автогенерация есть только для счётчиков README | Три копии промпта расходятся, и расхождение обнаруживается глазами. С артефактом-хешем дрейф становится падающим гейтом | **M** |
| **A3** | **Hard-negatives как обязательная колонка** в `frozen/*/manifest.json` | Механизм заморозки есть (`scripts/frozen_overlap_check.py`, `HYPOTHESES.md` с фальсификатором), но требования «≥2 отрицательных контроля на кейс» нет — контроль остаётся привычкой ревьюера | **S** |
| **A4** | **Schema-snapshot для действий-дискриминаторов.** `graph_tools.py:186-198` (8 действий) и `codebase_tool.py:107` (`action_map`): новое значение в перечислении = тихий runtime `else` | Тот же класс, что F2a, но на внутренних маршрутах вместо MCP-поверхности | **S** |
| **A5** | **Счётчик доли `fallback_lines`.** `parser.py:185-186,2488` помечает построчные окна `FALLBACK_CHUNK_LINES=56` с типом `fallback_lines`, но сколько файлов реально ушло в этот путь **не измеряется нигде** | 16 языков tree-sitter выглядят полным покрытием, пока часть файлов индексируется вслепую. Число «покрытие парсером 99%» сегодня недостоверно | **S** |
| **A6** | **Политика отказа при незаданном корне.** Fail-closed где-то есть (`find_project_root` в хуке), но в других точках незаданный корень приводит к молчаливому `return` вместо блока | Молчаливый возврат при отсутствии условия — источник fail-open путей | **S** |
| **A7** | **Один прогон `mutmut` со счётом** либо явная пометка «не измерено» | Заявлен в `pyproject.toml:117`, вне CI, на Windows пропускается без WSL. Сейчас это декларация, а не измерение | **S** |

## Порядок (обоснован, а не «сначала самое важное»)

1. **F5** — гейт уже красный: чинить чужое, пока оно не начало вредить. 7 находок — это готовый список работы.
2. **F6** — три числа в документации, которые нельзя получить сегодняшней командой. Сегодняшняя работа уже оплатила эту цену дважды.
3. **F1** — единственная позиция, где дефект бьёт по пользователю агента, а не по нам.
4. **F2a + F4 + F8** — три строки, которые закрывают целый класс «сильное есть, но не выполняется».
5. **A3, A4, A5** — дешёвые, делают существующие механизмы обязательными вместо рекомендуемых.
6. **F2b, F2c, F3, F7, F10, A6** — после, по отдельным задачам.
7. **A1, A2, A7** — M/S, но требуют отдельного замера до начала.

## Чего НЕ надо брать (у нас уже есть или хуже)

- Сужать 65 инструментов до 8 «мега-тулов». Меньший размер поверхности не заменяет её защиты: без гварда на дискриминаторы новое значение в перечислении — тихий `else`. Наша проблема не размер, а отсутствие снимка схемы (F2a, A4).
- Собственный re-вектор-индекс. Мы уже на LanceDB с IVF-guard'ом; JSON-хранилище с лексическим пре-фильтром — шаг назад.
- Свой `memory/decay` без потребителя. 4 120 строк офлайн-харнесса, выключенного по умолчанию и не подключённого к продуктовому пути, — это наш будущий `src/memory/`, которого у нас нет и не нужно.
Loading