From a2332915d37d19e581468a8a53bb0ae7b05fc325 Mon Sep 17 00:00:00 2001 From: MSCodeBase Agent Date: Sun, 4 Oct 2026 16:35:01 +0300 Subject: [PATCH] docs(issues): inventory of own gaps, split into fix vs add Twenty items, each with an internal file:line referent and a measured number, sorted by cost/benefit rather than by importance. F (fix): the product returns no typed degraded states, so an empty result is indistinguishable from a failed check (F1); there is no exact runtime guard on the MCP surface, only a floor that importorskip can remove (F2a); seven names in the write-annotation set are never registered (F2b); the layer bucket labelled transitional cannot block and has no expiry (F3); the suite that proves every guard can fail is wired into neither CI nor pre-commit (F4); the protocol guard is currently red with seven findings and unmonitored (F5); three confirmed doc/code drifts (F6). A (add): per-row freshness proof at the retrieval boundary (A1); generated agent prompt with a build hash (A2); hard negatives required by the frozen manifest schema (A3); schema snapshot for action discriminators (A4); a counter for the line-based parser fallback, which is currently unmeasured (A5); fail-closed behaviour when the project root is unset (A6); one recorded mutmut run (A7). Also records what not to adopt: shrinking to 8 mega-tools, a JSON vector store with a lexical prefilter, and an offline memory harness with no production consumer - all three are regressions against what we already have. --- ISSUE.md | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/ISSUE.md b/ISSUE.md index 920cb33b..57b36405 100644 --- a/ISSUE.md +++ b/ISSUE.md @@ -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/`, которого у нас нет и не нужно. \ No newline at end of file