fix(#262): reindex counter polls past the stale pre-reindex snapshot #264
Reference in New Issue
Block a user
Delete Branch "fix/262-reindex-progress-realtime"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Чинит баг #262 (follow-up #242): счётчик «Indexed X of Y» при реиндексе эмбеддингов замирал на 0 до перезагрузки страницы. closes #262
Корень — чисто клиентский. Сразу после «Reindex now» клиент ещё держит ДО-реиндексный снапшот
settings. Для уже полностью проиндексированного воркспейса он читается какreindexing=false, indexedPages>=totalPages. Эффект, гасящий дедлайн опроса, вычислялisReindexComplete(settings)против этого устаревшего снапшота → «готово» → снимал дедлайн ДО того, как придёт первый пост-реиндексный поллинг. Опрос не стартовал, счётчик замирал на 0 (перезагрузка просто тянула один свежий GET). Бэкенд при этом корректен:getMasked()отдаёт живойprogress.done/reindexing=true, воркер инкрементитdoneпо странице.Фикс — гейт
seenActive. Завершение засчитывается только после того, как поллинг реально увидел активный прогон:reindexSeenActiveRef— сбрасывается вfalseвonSuccessмутации (ДО установки дедлайна), латчится вtrueв эффекте приsettings?.reindexing.isReindexComplete(status, seenActive)иnextReindexPollInterval({..., seenActive})требуютseenActive— устаревший «полностью проиндексирован» снапшот больше не читается как «готово».reindexing=trueс момента enqueue на весь прогон) гарантирует, чтоseenActiveлатчится рано, поэтому реальное завершение по-прежнему останавливает опрос быстро; capREINDEX_POLL_CAP_MSпроверяется ПЕРВЫМ и всегда выигрывает → runaway-поллинга нет. Второй подряд реиндекс работает (ref сбрасывается вonSuccess).How verified
На стенде (
apps/client):vitest run ai-provider-settings.spec.tsx→ 30 passed (вкл. новый нон-вакуумный кейс «does NOT stop on the stale pre-reindex snapshot»: приseenActive:false, reindexing:false, indexed>=total→ возвращает интервал, а неfalse; на старом коде вернул быfalse).tsc -p apps/client/tsconfig.json --noEmit→ 0.eslintобоих файлов → 0 ошибок (7 warnings — пред-существующие, идентичны на HEAD).Внутренний ревью (отдельный субагент: прошёл всю последовательность шагов, серверный pre-seed, терминацию по cap, render-timing рефа, второй прогон) — APPROVE, гонок/runaway/поломки 2-го прогона не найдено.
Известные нюансы (некритичны, по итогам внутр. ревью)
reindexing=true→seenActiveостанется false → опрос идёт вхолостую до cap (120с). Счётчик при этом показывает корректное значение, спиннер не залипает (isReindexButtonLoadingгейтится наstatus===true). Ограничено cap-ом, осознанный trade-off.nextReindexPollInterval/isReindexComplete); проводкаref/effect/refetchIntervalбез юнит-теста (в файле нет компонентных рендер-тестов). Готов добавить рендер-тест, если хочешь зафиксировать и интеграцию.Checklist
Ревью
67312a375— раунд 1 (6 аспектов вкл. COHERENCE; Lite+regressions — клиентский state-machine фикс ~75 строк прод-логики, без sensitive-путей).Вердикт: PASS. Баг #262 закрыт, фикс корректен — проверено по коду многими аспектами.
Что чинит #264 (follow-up #242): счётчик «Indexed X of Y» замирал на 0 до перезагрузки. Корень чисто клиентский: сразу после «Reindex now» клиент держит ДО-реиндексный снапшот
settings, который для уже-полностью-проиндексированного воркспейса читается какreindexing=false, indexed>=total;isReindexComplete(settings)против устаревшего снапшота → «готово» → снимал дедлайн опроса ДО первого пост-реиндексного поллинга → опрос не стартовал.Фикс (
seenActive-гейт) — корректен:onSuccessсбрасываетreindexSeenActiveRef=falseДО установки дедлайна → refetchInterval (читает ref ЖИВО) стартует опрос (устаревший снапшот больше не «complete»); первый pre-seed-поллинг (reindexing=true) короткозамыкает refetchInterval ДО гейта и латчитseenActive=trueв эффекте; реальное завершение (reindexing=false, indexed>=totalпри латченном seenActive) снимает дедлайн. Окно устаревшего снапшота закрыто, реальное завершение по-прежнему останавливает.seenActiveважен — завершающий (reindexing=false), он на 5с позже активного, латч давно сфлашен;.currentчитается живо (нет деструктуризации-снапшота); эффект-deps[reindexDeadline, settings]включаютsettings. Кросс-run-лика нет (onSuccessресетит; устаревшийtrueинертен приdeadline===null). Cap (120с) всегда выигрывает, таймер чистится на unmount.reindexing-ветка короткозамыкает ДО гейта); пустой воркспейс (0/0) терминируется; cap не тронут.isReindexComplete(stale, false)→false+nextReindexPollInterval(stale, seenActive:false)→INTERVAL(на пре-фикс коде вернули бы противоположное), И реальное завершение(.., true)→true/false. Все seenActive-ветки обоих хелперов покрыты.conventions (
useRef-идиом, single-source-of-truth сохранён) / simplification (минимальный фикс, комменты документируют реальную гонку pre-seed↔stale-snapshot) — LGTM.Объективные проверки: vitest сам прогнать не могу (нет node_modules); тесты независимо верифицированы аспектом test-coverage как не-вакуозные и запирающие баг обе стороны; кодер отчитался о прогоне. Поведенческая корректность state-machine проверена 4 аспектами трейсом. Готово к мержу.
Маркер
reviewed_headобновлён на67312a375.