WEB-303 · Дефект · Инфраструктура · web
Доска: автогенератор id падает UNIQUE constraint failed: web_issues.id — новые тикеты создаются только с явным id
Закрыт
P2
ведёт: —
Суть
## Симптом
POST /api/web/issues **без поля id** → 400 `{ok:false, error:"UNIQUE constraint failed: web_issues.id"}`.
Тот же POST **с явным** `id` проходит 200.
## Почему это опасно (тихая ошибка)
Обёртки создания тикетов читают `j.id` из ответа и получают `undefined`, после чего PATCH тела уходит на `/api/web/issues/undefined` и **молча возвращает успех**. Итог: тикета нет, тело потеряно, скрипт печатает `created undefined` и идёт дальше. Сбой не виден, пока кто-то не откроет доску глазами. Поймано 21.08 при заведении WEB-301/302.
## Диагностика на момент находки
GET списка отдаёт 370 тикетов, максимальный номер **WEB-300**, свободные номера 7, 8, 9, 59, 294-299. При этом WEB-301 / WEB-302 / WEB-401 **не существовали** — значит генератор считает НЕ по максимуму видимых. Похоже на `count(*) + 1` при наличии строк, не попадающих в выдачу списка.
## Фикс
1. Генерировать id как `max(числовой суффикс) + 1` по **всей** таблице (или отдельной последовательностью), а при коллизии — повторять, а не отдавать 400.
2. **PATCH на несуществующий id обязан возвращать 404, а не успех.** Это вторая половина дефекта и она опаснее первой: именно она превращает сбой в тишину.
3. Негативный тест на оба пункта (правило смены: верификатор без негативного теста бесполезен).
## Обходной путь до фикса
Создавать тикеты, **явно передавая** `id`, и после записи проверять длину тела:
```
GET /api/web/issues/<id> → body.length > 0
```
⚠️ Осторожно со скриптами через `ssh … node -e "…"`: одинарные обратные кавычки внутри двойных кавычек zsh исполняет как подстановку команды и тело тикета приезжает покорёженным (поймано на этом же тикете). Русский текст и разметку слать **файлом .mjs через scp**, а не инлайном.
Доказательства
[25.08 ~18:50Z] ЗАКРЫТО оператором (не codex — M1 board-инфра). КОРЕНЬ: nextId() брал счётчик из meta seq, который отставал от явно созданных id (сегодня WEB-357..361 создавались с явными id как workaround) → автоген выдавал занятый id → UNIQUE constraint failed. ФИКС в board-core.mjs (общий слой — чинит и iOS доску): next = max(seq, MAX(CAST(substr(id,prefix+2) AS INTEGER)))+1, seq подтягивается. Бэкап: board-core.mjs.bak-web303-20260825. Сервер перезапущен (супервизор поднял, pid 91023), API жив. ДОКАЗАТЕЛЬСТВО: POST без id → ok:true id=WEB-362 (корректный 361+1, самотест-тикет создан и закрыт). 📎 Трейл: M1 ~/Projects/nc-agent-ops/board/board-core.mjs (nextId), бэкап рядом; самотест WEB-362.
Воркер
не привязан — привязать:
curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \
-d '{"issueId":"WEB-303","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-08-25T14:34:22.270Z