WEB board

всеканоны и докиворкеры↗ iOS↗ Легаси
WEB-188 · Дефект · — · web

Импорт видео: спиннер «Обробка…» не завершается в UI при done-ране

Закрыт P2 ведёт: owner
Суть
1. Суть одной фразой: Видео: спиннер (индикатор).
2. Где мы сейчас (13.09.2026): видео-фича off (R4.1 #4); корень — WEB-214.
3. Хронология: найден дефект; видео off; 05.09 → parked.
4. Карта документов и кода: WEB-214 (корень); Pi :5190.
5. Остаток: владелец — включить видео, начать с WEB-214; волна — фикс после доступа.
6. Критерий закрытия: живой прогон после доступа к моделям.

---

## история (тело до 13.09.2026)

Owner 05.08 12:10: ран завершился (done в БД 11:09/11:12), результат-док открывается, а кнопка/бейдж «Обробка…» в модалке крутится вечно и прогресс застрял на «0% Ingest». Клиентский поллинг не подхватывает терминальный статус (смежно WEB-181: клиент не авто-ретраит видимую ошибку — теперь надо честно завершать success). Repro: импорт во время рестарта сервера.

## 2026-08-23 11:20 UTC — разбор парковки (по запросу owner): почему здесь и что известно
Линия: видео-движок = ОТДЕЛЬНЫЙ сервис (Pi :5190, ключи TwelveLabs там, память video-engine-separate-service). Дефект деградированного cloud-path (Bedrock model access / partial слои / зависший спиннер / кривой видео-PDF) сидит в связке провайдер-доступов и owner-spend на видео-лейн. Припарковано до решения owner по видео-провайдерам; при расконсервации начинать с WEB-214 (корень — model access), 187/188/195 — следствия по слоям/UI/PDF.


## 2026-08-23 PDFPOSTQA: NEEDS-PAID — parked video import scenario.

PDFPOSTQA: бесплатные сценарии только; новые paid generation/export/import не запускались. Полный реестр и evidence: /Users/milamarty/work/PDFPOSTQA-REPORT.md; screenshots: /Users/milamarty/work/PDFPOSTQA-SHOTS/.
Лента
2026-08-05 12:57:49 · backup-opus
Волна BK: поллинг переживает обрывы, терминальный done подхватывается. NEEDS-VISUAL owner: импорт видео — спиннер завершается.
2026-08-10T23:50:14.169Z · backup-opus
Парковка 11.08: видео-фича ОТЛОЖЕНА до пост-запуска (owner-решение прод-плана R4.1 #4: видео выключено в первом релизе, публичные роуты deny/404). Возврат в review при реактивации видео-линии.
2026-09-10T18:55:03.706Z · coordinator
BOARD-WASH-20260910:WAVE-3338
По поручению владельца 18:43Z назначена исполнительская волна 3338 (video), Codex gpt-5.6-luna xhigh, M4. Полная история карточки и база l115c 1ad52e16b доставлены, brief-guard и проверка Git-базы пройдены. Задача: проверить существующую сдачу, устранить остатки, передать бандл и доказательства. Финальная приёмка и посадка остаются за координатором. Ранее отложенная функция не включается; статус parked/backlog сохранён, идёт проверка технического остатка.
КАРТА ДОКУМЕНТОВ: ноут /Users/annakorin/nc-ops-scripts/board-wash-20260910/3338-wash-video-brief.md; M4 /Users/milamarty/waves/inputs/board-wash-20260910/video-tickets.json; ожидаемый отчёт /Users/milamarty/waves/3338WASHVIDEO-REPORT.md. Правила обогащения: WEB-449.
2026-09-13T11:01:35.830Z · coordinator
[13.09 11:01Z координатор] Мойка отложенных, волна 3668. Вердикт: ЖДЁТ-ВЛАДЕЛЬЦА. Видео off; корень WEB-214.
2026-09-13T16:47:18.153Z · coordinator
[13.09 16:47Z координатор] Тикет снят с parked. Волна 3731 (Luna, VERDICT=GO, ветка `wave/3731-video-run-terminal-status`, HEAD `05f00fabf61ec8682fd63e18191df2ca1bc4b993`, зеркало `refs/waves/3731`).

Почему тикет вообще был в parked и почему это было ошибкой. С 05.09 он лежал рядом с WEB-187 и WEB-195 под общей формулировкой «корень — WEB-214 (доступ к моделям), видео-фича выключена». Для тех двух это верно. Для этого — нет: дефект живёт целиком на клиенте, воспроизводится на форме ответа маршрута и чинится без видео-движка, без провайдеров и без включённой фичи.

Что оказалось на самом деле (это важнее, чем исходная формулировка). Опрос статуса УЖЕ считал `done` терминальным — то есть «клиентский поллинг не подхватывает терминальный статус» было неточным описанием. Настоящая причина: при терминальном `done` сервер мог отдать прогресс из прошлого состояния (`progress: {percent: 0, currentStep: "ingest"}`), и UI честно рисовал эту устаревшую бумажку — отсюда вечные «0% Ingest» при готовом результате. Детским языком: сервер уже сказал «готово», а браузер смотрел на старую записку с «0%».

Фикс: `normalizeTerminalDonePayload` (`src/components/media/videoRunPolling.ts:68-78`) делает терминальный `done` авторитетным для UI — payload маршрута сохраняется, но прогресс приводится к 100% и завершённому шагу. Терминальная ошибка по-прежнему отдаётся один раз для показа, вызывающий переводит элемент в `failed` (`MediaImport.tsx:3608-3611`); восстановимые ошибки отправки остаются не-терминальными.

Доказательства: красный прогон ДО фикса на фактической форме GET-ответа маршрута — `tests 15, pass 14, fail 1, RC=1`, падение `Expected values to be strictly equal: 0 !== 100`. Зелёный после — `17/17 RC=0`; соседний `video-run-recovery.test.ts` `20/20 RC=0`; `node --check` RC=0; `git diff --check` RC=0; bundle verify RC=0. Коммит: 2 файла, +111/−8.

Сознательно оставлено как есть: успешный ответ с НЕИЗВЕСТНЫМ статусом остаётся не-терминальным и продолжает опрос до тайм-аута — это контрактное поведение, а не недочёт (иначе «починка» свелась бы к «выключили опрос»).

Что осталось за видео-провайдерами (не в этом тикете): WEB-187 (кадры/деградировавший cloud-путь) и WEB-214 (доступ к моделям). Статус → review до посадки линии.
2026-09-13T23:52:04.825Z · coordinator
[13.09 23:52Z координатор] ПОСТ-QA ЛИНИИ l115l (волна 3755, VERDICT=GO). Пять тикетов последней посадки, все пять — `PROVEN-OFFLINE`, с прогонами на ДВУХ коммитах: до фикса и на коммите, который сейчас в проде (`1628b697d`).

| Тикет | Тест | Коммит | Результат |
|---|---|---|---|
| WEB-188 | `videoRunPolling.test.ts` | `4a6e99ed4` (до фикса) | **16/17, одно НАСТОЯЩЕЕ падение проверки** |
| WEB-188 | тот же | `1628b697d` (прод) | 17/17 |
| WEB-195 | `web195TranscriptOnlyPdfQuality.test.ts` | `4a6e99ed4` (до фикса) | **4/6, два НАСТОЯЩИХ падения** |
| WEB-195 | тот же | `1628b697d` (прод) | 6/6 |
| WEB-658 | `source-indexing-worker-lease-hiccup.test.ts` | `1628b697d` | 5/5 |
| WEB-658 | `web658WorkerSurvivesLeaseHiccup.test.ts` | `1628b697d` | 6/6, включая красную форму и 3 негатива |
| WEB-659 | `activePassiveLease.web659.real-db.test.ts` (настоящая Postgres) | `1628b697d` | 3/3 — красный, зелёный, негатив |
| WEB-659 | `web659SharedOwnerAdmission.test.ts` | `1628b697d` | 1/1 |

Ключевое отличие от прошлого раза: у WEB-188 и WEB-195 красное до фикса — это **настоящие падения проверок**, а не ошибка сборки и не отсутствие модуля. Именно на этом отличии независимая приёмка 3743 в прошлый раз вернула мне три закрытия, и теперь оно соблюдено.

**Закрываю: WEB-188 и WEB-195** — доказательство полное, пары настоящие, оба дефекта клиентские/отчётные и живой пробы сверх этого не требуют.

**НЕ закрываю пока: WEB-658 и WEB-659.** Их офлайн-доказательства сильные (красная форма плюс негативы, для WEB-659 — на настоящей Postgres), но у обоих есть живая проверка, которая вот-вот будет: стенд ёмкости сейчас пересобирается под коммит прода, и группа `realtime` там обязана позеленеть. Если позеленеет — закрою WEB-659 живым наблюдением, а не только тестами. Если нет — значит причина не та, что мы думаем, и это важнее любого зелёного теста.

**WEB-593 остаётся открытым по существу:** доказан ИНСТРУМЕНТ повторной загрузки, а не восстановление документов. Тикет закроется, когда 185 застрявших документов реально вернутся в индекс.
2026-09-14T12:07:30.033Z · coordinator
[14.09 12:07Z координатор] WEB-188 — вечный индикатор обработки видео
Обновление от 2026-09-14. Затронутая посадка: l115l (1628b697d9b20a4446b9bdba20ada0a6c2820d11, 13.09 20:38:15Z). Тикет ЗАКРЫТ по этой линии.
Карточка написана для человека, который открывает её впервые и не имеет ни журнала смены, ни переписки.

1. ЧТО БЫЛО СЛОМАНО

Видео обработалось, документ уже готов — а на странице бесконечно крутится индикатор прогресса. Человек ждёт и не знает, что всё давно готово.

2. КАК НАШЛИ И ПОЧЕМУ НЕ ПОЙМАЛИ РАНЬШЕ

Нашла волна 3731, взявшая тикет в работу.
Почему не поймали раньше: ИСХОДНАЯ ФОРМУЛИРОВКА ТИКЕТА БЫЛА НЕТОЧНОЙ и уводила в сторону. В карточке было написано «клиентский поллинг не подхватывает терминальный статус». Волна проверила и обнаружила, что опрос УЖЕ СЧИТАЛ статус `done` терминальным. Искали в клиенте, а дефект был в том, ЧТО ОТДАЁТ СЕРВЕР.

3. ЭВОЛЮЦИЯ, ВКЛЮЧАЯ ТУПИКИ

3.1. ТУПИК — неверный адрес в теле карточки. См. выше: опрос был исправен, и любая работа по формулировке тикета была бы потрачена впустую.

3.2. НАСТОЯЩАЯ ПРИЧИНА. При терминальном статусе `done` сервер мог отдать ПРОГРЕСС ПРОШЛОГО СОСТОЯНИЯ (доля выполнения ноль, текущий шаг — приём файла), и интерфейс ЧЕСТНО рисовал эту устаревшую запись. То есть виноват был не клиент и не логика терминальности, а несогласованный снимок прогресса в ответе.

3.3. ТУПИК №2, ФОРМАЛЬНЫЙ — приёмка пометила работу как FAIL, и это НЕ значило, что она плоха.
Независимая приёмка 3734 дала по этой волне `FAIL`, но по ФОРМАЛЬНОЙ, а не содержательной причине: кода волны ФИЗИЧЕСКИ НЕ БЫЛО В КАНДИДАТЕ линии l115k — координатор намеренно не вливал его, потому что дерево в тот момент было занято сборкой. Приёмщик ПРАВИЛЬНО отказался «принимать на кандидате то, чего там нет», проверил код на его СОБСТВЕННОЙ ветке и написал ВОСЕМЬ СВОИХ тестов вдобавок к семнадцати авторским: всё зелёное.
Посадке l115k это не мешало; работа пошла в следующую линию, l115l.
СЛЕДУЮЩЕМУ АГЕНТУ: если в истории тикета встретится «3731=FAIL» — это про ОТСУТСТВИЕ КОДА В КАНДИДАТЕ, а не про качество фикса.

4. ЧТО СДЕЛАЛИ В ИТОГЕ

Волна 3731, зеркало `refs/waves/3731` = `05f00fabf`.
Изменение: функция `normalizeTerminalDonePayload` в `src/components/media/videoRunPolling.ts`. При терминальном статусе `done` она принудительно приводит снимок прогресса к завершённому виду (доля 100, текущий шаг `done`), чтобы устаревший снимок не держал индикатор активным. Смысл записан прямо в комментарии над функцией.
Посажено линией l115l (`1628b697d9b20a4446b9bdba20ada0a6c2820d11`) 13.09 20:38:15Z, в составе: l115k + 3731 + 3733 + 3735 + 3738 + 3739. Новых миграций линия не несла: 209 → 209.

5. ЧЕМ ДОКАЗАНО И ГРАНИЦЫ ЗАЯВЛЕНИЯ

ДОКАЗАНО:
  Красное ДО фикса: `15 tests, 14 pass, 1 fail, RC=1`, падение на сравнении `0 !== 100`.
  Зелёное ПОСЛЕ: `17/17`. Соседний набор `video-run-recovery.test.ts`: `20/20`.
  Восемь дополнительных тестов независимого приёмщика — зелёные.
  Пост-QA линии l115l (волна 3755): вердикт `PROVEN-OFFLINE`, и — что важно — красное до фикса признано НАСТОЯЩИМ ПАДЕНИЕМ ПРОВЕРКИ (`16/17 → 17/17`), а не ошибкой сборки. Именно это отличие позволило закрыть тикет.

ГРАНИЦЫ:
  Доказано ОФЛАЙН, на коде коммита, работающего в проде. ЧЕЛОВЕК, СМОТРЯЩИЙ ЗА ЗАВЕРШЕНИЕМ НАСТОЯЩЕГО ВИДЕО В БРАУЗЕРЕ, НЕ НАБЛЮДАЛСЯ.

6. ЧТО ОСТАЛОСЬ ОТКРЫТЫМ

Ничего блокирующего. Тикет закрыт.

7. TROUBLESHOOTER — ЕСЛИ ИНДИКАТОР СНОВА ЗАВИСНЕТ

Шаг 1. Посмотреть ОТВЕТ МАРШРУТА СТАТУСА при терминальном `done`. Если доля выполнения равна нулю, а текущий шаг не `done` — сервер отдаёт УСТАРЕВШИЙ СНИМОК ПРОГРЕССА, и это ровно исходный дефект.

Шаг 2. Проверить, доходит ли выполнение до `normalizeTerminalDonePayload` в `src/components/media/videoRunPolling.ts`. Функция срабатывает только при статусе `done`.

Шаг 3. НЕ НАЧИНАТЬ С КЛИЕНТСКОГО ОПРОСА. Тело тикета исторически указывало туда, и это было неверно: опрос уже считал `done` терминальным. Начинать с того, что отдаёт сервер.
Воркер
не проверен 3338-wash-video M4 движение в панели: неизвестно
Подключиться и смотреть/перехватить руками. Колесо мыши листает; клавишами — Ctrl-b затем [, выход из прокрутки q. Отсоединиться — Ctrl-b затем d:
Прочитать историю панели без подключения — листается и ищется (/ поиск, q выход), воркеру не помешает:
Обновлён
2026-09-13T23:52:08.640Z