0.167.31
Fixed
- Retry с backoff для embedding-вызовов в reindex + актуализация комментария об индексе (#215 — P2). (1) #213 [A-025]: единичный 429/503 от embedding-API на батче помечал весь reindex-job как
failedбез retry и backoff — для большой БД и лимитированного провайдера это означало ручной перезапуск с нуля. ДобавленReindexer.embedWithRetry(reindexFacts/reindexChunks): до 3 попыток с экспоненциальным backoff (1с, 2с; package-level состояние с RWMutex, тестовый мутатор живёт только в_test.go), сон прерывается отменой ctx (graceful shutdown/cancel), per-attempt guard 120с (выше клиентских 60с провайдеров — не затрагивает текущих, но ограничивает будущие реализации без собственного HTTP-таймаута), pre-flight ctx-проверка и единая точка выходаctx.Err()при отмене. Классификация: не-2xx ответы OpenAI/Ollama-провайдеров теперь оборачиваются в новый*embeddings.StatusError(HTTP-статус + обрезанное до 2КБ тело сstrings.Clone);isTransientEmbedErrorделегирует общемуproviders.IsTransientError(408/429/5xx; JSON-структурныеerror.type/codeиз тела — так 429 insufficient_quota корректно нетранзиентен; не-JSON тела не сканируются — статус-код единственный сигнал), из сетевых — таймауты (net.Error.Timeout) и connection refused/reset (консистентно сproviders.isTransientGoError; кейс — Ollama на localhost при гонке старта), DNS-failure — нетранзиентен. Финальная ошибка несёт retry-exhaustion-метаданные (after 3 attempts), terminal Error-лог с job_id/agent_id отличим от per-attempt warn’ов (полеwill_retry). (2) #215 [A-027]: пакетный комментарийreindex.goутверждалUNIQUE INDEX reindex_jobs(status) WHERE …, тогда как фактическая миграция 077 —ON reindex_jobs((1)) WHERE status IN ('pending','running')(constant-expression partial unique = максимум один active job); комментарий приведён к факту. Тесты (internal/embeddings): таблица isTransientEmbedError (429/5xx/408 транзиентны; 400/401/404, DNS, plain errors — нет; JSON-vs-plain insufficient_quota; net timeout; wrapped-цепочки); интеграционные — 2×429 → job completed за 3 вызова, вечный 503 → failed после 3 попыток с exhaust-текстом, 400 → failed за 1 вызов (нет ретраев), пустые delays → нет паники; httptest-обёртки StatusError для OpenAI/Ollama; обрезка тела; инвариантembedMaxAttempts == len(delays)+1. Прогон-raceчистый.