Files
kvarta_project/docs/ai-log.md
T
nunuchek 25246ad0c4 fix(api): доверять X-Real-IP со шлюза docker-сети в IPv4-mapped виде
Node отдаёт адрес сокета как ::ffff:172.31.120.1, а TRUSTED_PROXIES
записан без префикса — все посетители попадали в один rate-limit.
2026-10-17 16:17:24 +03:00

24 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Ошибки ИИ
Записи по ходу работы: что ИИ сделал не так, чем поймали, что исправили. Источник для раздела README «где ИИ ошибся».
| # | Шаг | Ошибка ИИ | Чем поймали | Исправление |
|---|---|---|---|---|
| 1 | План, деплой | Предложил `docker compose run …`, не проверив сервер; там нет плагина compose, только `docker-compose` v2.23 | Осмотр сервера по ssh перед планом деплоя | Везде `docker-compose` |
| 2 | План, Telegram | Предложил скрипт `tg:setup` через `getUpdates`, а позже — long polling в api; два потребителя одного потока обновлений конфликтуют | Сам заметил при проектировании восстановления через бота | Скрипт заменён командой бота `/setup` (ADR-0002) |
| 3 | Шаг 1, каркас | Сгенерировал `onlyBuiltDependencies` для pnpm, а в pnpm 11 формат сменился на `allowBuilds` — нативный better-sqlite3 молча не собрался | Ошибка `ERR_PNPM_IGNORED_BUILDS` и проверочный запуск `new Database(':memory:')` | `allowBuilds` + `pnpm rebuild better-sqlite3` |
| 4 | Шаг 1, каркас | Поставил самую свежую ESLint 10, плагины eslint-config-next её ещё не поддерживают | `pnpm peers check` | ESLint 9 |
| 5 | План, контракт | В плане «X-Real-IP доверяем только с 127.0.0.1» — в Docker запрос от nginx приходит со шлюза bridge-сети, и все посетители попали бы в один rate-limit | Агент api при реализации (вывод, не проверен на Docker) | `TRUSTED_PROXIES` + фиксированная подсеть в compose |
| 6 | Шаг 6, тесты | Тест «два одновременных запроса» проходит и без транзакции: better-sqlite3 синхронный — тест ничего не доказывает | Самопроверка агента api | Оставлен как регрессионный; защита — `IMMEDIATE`-транзакция, отмечено честно |
| 7 | Шаг 2, телефон | Первая версия считала российский `8 912…` «неверным кодом оператора», а 10 цифр без префикса — «иностранным номером» | Дописанные тесты на граничные вводы | Явное правило: `7…`, `8[1-9]…` и > 11 цифр — иностранные |
| 8 | Шаг 4, landing | Агент не увидел существующий `next.config.ts` (фильтр `grep -v .next` — точка в regex съела `next.config`) и перезаписал его, потеряв standalone, transpilePackages и прокси на api | Сообщение инструмента «updated» вместо «created» и `git status` с `M` | `git checkout` файла до коммита |
| 9 | Шаг 4, landing | Мутировал ref внутри апдейтера `setState` — в StrictMode апдейтер вызывается дважды, предзаполненный комментарий посчитался бы текстом посетителя | Самопроверка агента при перечитывании хука | Комментарий хранится в одном state со значениями формы |
| 10 | Шаг 8, landing | Синхронное исключение при сборке тела запроса оставляло кнопку навсегда в «Отправляем…» | Ревью своего кода агентом | Сборка тела внутри промиса с `catch` → блок «позвоните» |
| 11 | Шаг 5, deploy | Запустил сборку под amd64, не проверив эмуляцию на Apple Silicon — `exec format error` | Первый же `RUN` в сборке | Скрипт распознаёт ошибку и подсказывает включить Rosetta в Docker Desktop |
| 12 | Шаг 5, deploy | Пустой аргумент `previous` в `ssh … bash -s --` исчез бы при склейке строки и сдвинул остальные аргументы | Ревью агентом до прогона | Заглушка `-` вместо пустой строки |
| 13 | План, деплой | Заявил цели «образ ≤ 150–160 МБ» без расчёта: один бинарник node весит ~120 МБ, на `node:22-slim` это недостижимо | Замер `docker image ls` агентом deploy | Ужали с 281 до 224 МБ, по сети (gzip) — 64 и 71 МБ |
| 14 | Шаг 10, бэкапы | Копия из `db.backup()` унаследовала режим WAL: в архив попадала WAL-база, рядом с временными копиями оставались `-wal`/`-shm` | Тест `backup.test.ts` на чистоту временного каталога | Временная копия переводится в `journal_mode = DELETE` перед gzip |
| 15 | Шаг 9, тесты | Тест «без CHAT_ID» передавал `undefined`, и деструктуризация подставляла значение по умолчанию — тест проверял не то | Сам тест повёл себя не так, как ожидалось | `null` вместо `undefined` |
| 16 | План, восстановление | В плане пауза приёма включалась после снимка pre-restore — заявка, пришедшая во время gzip снимка, пропала бы | Агент api при реализации | Пауза до снимка, ожидание опустошения очереди Telegram |
| 17 | Шаг 5, прод | `TRUSTED_PROXIES=172.31.120.1` не срабатывал: Node отдаёт адрес сокета как `::ffff:172.31.120.1`, и все посетители снова делили один лимит. Тесты этого не ловили — в них адреса без префикса | Проверка на проде: 6 запросов с X-Real-IP «A», затем «B» получил 429 | Нормализация IPv4-mapped адресов + тест; на сервере проверено пробным контейнером |