[QA-trace][#120 3/7] Процесс-отчёт web-test-orchestrator (offline-sync/PWA): агенты, SW-техника, фолс-позитивы #237

Closed
opened 2026-06-27 15:10:59 +03:00 by Ghost · 0 comments

Прогон 3/7 · эпик PR #120 (offline-sync / PWA) · web-test-orchestrator · 15 сырых → 6 подтв / 5 ложных-или-переклассиф / 4 не-реализовано-WIP.
Процесс-отчёт для отладки скилла (агенты/инструменты/класс/фолс-позитивы + насколько верно применяли SW/offline-технику и отделяли WIP от багов). Без кода багов.

Процесс-трейс QA отчёт — PR #120 feature/offline-sync (gitmost / Docmost fork, OFFLINE/PWA, WIP M0–M2)

Фокус отчёта — НЕ код приложения, а РАБОТА СКИЛЛА web-test-orchestrator: какие агенты/инструменты отработали, что нашли, какой КЛАСС бага, как и на каком этапе всплыл, кто фолс-позитивит, и насколько правильно агенты применили SW/offline-технику.


1. Сводка прогона

Стадии скилла (по trace-данным): recon → mapping/charters → execution (6 персон-агентов параллельно) → oracle-оценка → независимый Doer-Verifier пасс → синтез.

Агенты исполнения (7 ролей):

  • recon (разведка/бутстрап)
  • pwa-sw-shell tester (оболочка SW + offline reload)
  • offline-read tester (офлайн-чтение/cold-start)
  • offline-edit-body (Contour A — Yjs/CRDT тело документа)
  • offline-structural-outbox tester (Contour B — REST/outbox/структура)
  • update-prompt-install (SW update-prompt, install, offline-emulation traps)
  • online-sanity-regression (онлайн-регресс/ресурсы)

Воронка находок (15 сырых → классификация после verifier):

Класс по итогу Кол-во Находки
Подтверждённые РЕАЛЬНЫЕ дефекты 6 #2,#3,#4,#5,#9,#10
Не-реализовано-WIP / coverage-gap / env-limitation 4 #11 (create-no-feedback), #12 (move DnD gap), #13 (offline-emulation trap), #14 (update-prompt gap)
Не-дефект / ложное / информационное / переклассифицировано 5 #1 (benign console-noise), #6 (Contour A — гарантия держится), #7 (disconfirmed race), #8 (title-focus = штатный UX), #15 (precache 13.4 МБ — informational)
Итого сырых 15

Verifier: 13 находок перепроверены, из них 11 подтверждены as-is, 2 переклассифицированы вниз (#8 verified→speculative «штатный UX»; #14/#15 verified→speculative «не дефект»). 2 находки verifier НЕ успел перепроверить (TIMED_OUT): #7 и #10 — обе тяжёлые многоконтекстные/тайминговые сценарии (см. §4).

Ключевой вывод процесса: скилл уверенно поймал главный офлайн-дефект (blank-shell на offline-reload из-за auth-gate + unguarded error.response.status) — его независимо переоткрыли четыре агента разными путями. Сильная сторона — почти все агенты ПРАВИЛЬНО применили SW/offline-технику (один контекст, ожидание controller != null до set_offline). Слабые места — дубль-репортинг одного корня как 4 находки и два verifier-таймаута на самых ценных data-loss сценариях.


2. Какие агенты/инструменты отработали

recon — разведка

  • Покрыл: бутстрап, карта офлайн-исходников, пре-/пост-login фазы консоли.
  • Инструменты: Bash (curl-пробы эндпоинтов, grep по apps/client/src), Playwright sync (login, serviceWorker.ready/controller, caches.keys, indexedDB.databases, fetch('/manifest.json')), page.on(console/pageerror/response/requestfailed), phase-tagged response listener, скриншоты, Read исходников.
  • Проявил себя: методично — изобрёл фазовую переменную, мутируемую на каждом шаге, что строго локализовало 401-шум в пре-login. Дисциплинированно НЕ раздул косметику до дефекта (честно: «cosmetic, not data loss»).
  • Офлайн-техника: на этом этапе offline не требовался; SW-проверки (ready/controller) выполнены корректно.

pwa-sw-shell tester — оболочка SW + offline reload

  • Покрыл: offline-reload авторизованного маршрута, fallback-UI, корень blank-screen.
  • Инструменты: Playwright async API, ОДИН контекст online→offline через set_offline(True/False), evaluate для controller/caches.keys()/DOM-проб, четыре listener'а, скриншоты, Bash curl + grep/Read для атрибуции корня.
  • Проявил себя: образцово — поймал blank-shell (#2), отделил «оболочка грузится 200 из precache» от «React падает», grep'ом сузил до ровно двух call-site'ов error.response.status и доказал, что виноват именно незащищённый в auth-query.tsx. Репро 3×, детерминированно.
  • Офлайн-техника: правильно — SW активирован (controller non-null, 237 precache) ДО offline; один контекст; явно различил «не Chrome-offline-страница, не precache-404, а краш приложения». Не свалил на tooling.

offline-read tester — офлайн-чтение / cold-start

  • Покрыл: hard-reload/reopen офлайн (естественный триггер «тётя Люба переоткрыла PWA»), наличие данных в persisted RQ-cache vs их отрисовка.
  • Инструменты: Playwright python persistent context + set_offline; hooks response/requestfailed/pageerror; out-of-band чтение IndexedDB gitmost-rq-cache (idb-keyval) изнутри страницы; response.from_service_worker как оракул; скриншоты (Read как изображения); статические Read 7 файлов.
  • Проявил себя: сильнейший оракульный пасс — доказал «данные ЕСТЬ в кэше (13 read-queries), но не отрисованы», что отличает blank-shell от «нет кэша». Проверил from_service_worker=True на всех ответах — снял подозрение в tooling/SW-неактивации.
  • Офлайн-техника: правильноcontroller != null до offline, один persistent context; явно «ruled out tooling/SW-activation artifact».

offline-edit-body (Contour A, CRDT-персона)

  • Покрыл: офлайн-правки тела (Yjs+y-indexeddb), reconnect-merge, конкурентные правки из двух контекстов.
  • Инструменты: Bash (python3 + playwright async), Read скриншотов, Write heredoc-скриптов в /tmp/ot/*.py. Верификация против независимого источника — свежий 2-й контекст, читающий из collab-сервера :3001 (server-truth, отдельная IndexedDB), а не из UI-оптимизма.
  • Проявил себя: методологически лучший на дисконфирме — подтвердил гарантию Contour A (#6) против сервер-истины и сам ЖЕ дисконфирмировал собственное подозрение на потерю символов (#7) после повторного теста с settle-задержкой. Это образец «DISCONFIRM BY DEFAULT».
  • Офлайн-техника: правильно — gating на ready + controller != null; один + свежий контексты осознанно разделены по ролям.

offline-structural-outbox tester (Contour B)

  • Покрыл: outbox/paused-mutations (comment/create/move), offline-reload pinned-страницы, «Create page» без фидбэка.
  • Инструменты: Playwright python sync, ОДИН persistent context online→offline; out-of-band ground-truth через прямой REST (curl POST /api/comments, /api/pages/info с authToken-cookie) — читает серверное состояние, которое UI не контролирует; phase-tagged request capture; Read исходников для атрибуции.
  • Проявил себя: самый ценный по data-loss классам — отделил «чистый reconnect НЕ теряет» (replay 200, curl readback 0→1) от «reload-while-offline теряет молча» (#10), и честно пометил «Create page» как WIP-affordance (#11), а MOVE — как coverage-gap из-за native DnD (#12), не выдав ложное «broken».
  • Офлайн-техника: правильно — controller-check до offline, один контекст; curl-readback как внешний оракул — сильнейший приём против success-hallucination.

update-prompt-install

  • Покрыл: SW update-prompt path, install, и — ценнейшее — выявил ловушку самого инструментария (#13): set_offline/ctx.route НЕ блокируют fetch из SW-потока, GET /api/health отдаёт 200 при «offline».
  • Инструменты: curl, grep/sed по источнику + сгенерированному /sw.js, Playwright launch_persistent_context; перекрёстная проверка двумя механизмами (set_offlinectx.route abort) + enumeration caches.keys()/cache.keys().
  • Проявил себя: дал калибровочный вклад в САМ СКИЛЛ — задокументировал, почему наивное чтение GET-200 = ложный «SW serves stale API», и предложил правильный оракул (enumerate caches, 0 /api entries). Update-path честно помечен как не-воспроизводимый в одном билде (#14), без фантомного дефекта.
  • Офлайн-техника: правильно + углубил её — нашёл структурный предел эмуляции offline в Playwright.

online-sanity-regression

  • Покрыл: онлайн-регресс, стоимость precache (#15).
  • Инструменты: Playwright sync, SW-state через evaluate, out-of-band fresh-tab, cold-vs-warm timing.
  • Проявил себя: корректно пометил 13.4 МБ/237 как informational (не дефект); verifier подтвердил и уточнил «1 HTML, а не 0».
  • Офлайн-техника: корректная SW-проверка до offline.

3. Баги по КЛАССУ (как проявлялся / как и каким агентом найден; код опущен)

A. blank-shell-offline (оболочка грузится 200 из precache, React падает в пустоту)

  • Проявление: offline-reload/reopen любого авторизованного маршрута → body.innerText.length = 0, #root содержит только Mantine-<style>, в окне — uncaught TypeError: Cannot read properties of undefined (reading "status"). SW отдаёт index.html + все ассеты 200 из precache (НЕ Chrome-offline, НЕ precache-404). После set_offline(False)+reload — восстановление.
  • Как/кем найден: implicit-оракул «пустая страница» (bodyLen=0) сработал ТОЛЬКО потому, что скилл требует reload/persistence round-trip. Независимо переоткрыт 4 агентами: pwa-sw-shell (#2), offline-read (#4), offline-structural-outbox (#9, +доказал что pinned-страница тоже белеет), и компонента gate описана pwa-sw-shell отдельно (#3). Корень разбит на два под-класса ниже.

B. rendering / hard-online-dependency in render-gate

  • Проявление: render-gate авторизованного дерева возвращает пустой фрагмент при ошибке/загрузке current-user; офлайн /api/users/me (NetworkOnly) не отдаётся → пусто, даже если бы краш collab-token был починен.
  • Как/кем найден: pwa-sw-shell (#3) Read компонента + корреляция с requestfailed на /api/users/me; контроль — login-страница офлайн РЕНДЕРИТСЯ, что изолировало blank к auth-gated дереву. offline-read (#4) усилил out-of-band чтением IndexedDB (данные есть, не показаны).

C. unhandled-network-error / unguarded-property-access

  • Проявление: на каждой офлайн-сетевой ошибке retry-callback читает .status у undefined response → бросает TypeError app-wide.
  • Как/кем найден: pageerror hook поймал сообщение; повторный прогон со stack-capture дал фрейм at retry (...index-DKs-wrBZ.js); grep .status по bootstrap-коду изолировал единственный незащищённый call-site. Найден pwa-sw-shell (#2) и независимо offline-read (#5).

D. silent-optimistic-loss (false optimistic success → потеря при reload-before-reconnect)

  • Проявление: офлайн добавление комментария показывает оптимистичный успех (текст+маркер в редакторе, composer закрылся), сервер пуст. Чистый reconnect → paused POST реально уходит (200, curl readback 0→1). НО reload пока офлайн → in-memory paused mutation выброшен, сервер НИКОГДА не получает (create requests: []).
  • Как/кем найден: offline-structural-outbox (#10) — t_comment.py (оптимистика+replay через curl) + t_reload_loss.py (успех→reload-offline→утрата). Оракул «false optimistic success later dropped = real defect» из async-and-state.md. Verifier TIMED_OUT — не перепроверено.

E. missing-offline-affordance (WIP, НЕ data-loss)

  • Проявление: офлайн «Create page» — ноль фидбэка (нет узла, нет навигации, нет тоста, нет запроса); на reconnect paused POST уходит и app навигирует. Ничего не заявляло успех → не потеря данных, честный UX-gap.
  • Как/кем найден: offline-structural-outbox (#11), phase-tagged request capture + чтение порядка в handleCreate. Корректно отнесён к WIP.

F. race (подозрение, дисконфирмировано)

  • Проявление: в первом прогоне burst-ввод (delay=0) в момент дисконнекта потерял 2 ведущих символа.
  • Как/кем найден: offline-edit-body (#7); САМ дисконфирмировал на ретесте с settle 2.5s/cadence 60–120 мс (40/40 точно). Помечен speculative «синтетик-input race, не дефект CRDT». Verifier TIMED_OUT.

G. console-noise (benign) и resource-cost (informational)

  • #1 (recon): 3×401 + socket.io WS-closed строго в пре-login, чистится после auth.
  • #15 (online-sanity): precache 13.4 МБ/237, не блокирует first paint — ожидаемое поведение PWA.

H. tooling-trap / env-limitation (находки о САМОМ инструментарии)

  • #13 (update-prompt-install): set_offline+ctx.route не глушат SW-thread fetch → GET 200 при offline; правильный оракул — enumeration caches.
  • #12 (offline-structural-outbox): native HTML5 DnD не actuatable в headless → MOVE не отыгран live, по коду — тот же класс, что comment.
  • #14 (update-prompt-install): update-prompt positive path не воспроизводим на одном билде.

3b. Кто что нашёл

# Находка (кратко) Агент Класс Итог verifier
1 Пре-login 3×401 + WS-warning recon console-noise (benign) verified (не-дефект)
2 Offline-reload = blank + TypeError pwa-sw-shell blank-shell / unguarded-access verified (дефект)
3 UserProvider gate → пусто офлайн pwa-sw-shell rendering / hard-online-gate verified (дефект)
4 Cold-start офлайн = white screen offline-read missing-offline-fallback verified (дефект)
5 Unguarded error.response.status offline-read unguarded-property-access verified (дефект)
6 Contour A: правки тела доживают offline-edit-body none — гарантия держится verified (не-дефект)
7 Burst-keystrokes теряют символы offline-edit-body suspected race (tooling) TIMED_OUT (само-дисконфирм)
8 Первый ввод уходит в title offline-edit-body focus-affordance verified→speculative (штатный UX)
9 Offline-reload белит даже pinned offline-structural-outbox blank-shell / defeats pinning verified (дефект)
10 Нет outbox → silent loss на reload offline-structural-outbox silent-optimistic-loss TIMED_OUT
11 «Create page» офлайн без фидбэка offline-structural-outbox missing-affordance (WIP) verified (WIP)
12 MOVE DnD не отыгран (headless) offline-structural-outbox coverage-gap (env) (env-limitation)
13 set_offline не глушит SW-fetch update-prompt-install tooling-trap (env) verified (tooling-инсайт)
14 Update-prompt path не воспроизводим update-prompt-install coverage-gap (WIP) verified→speculative
15 Precache 13.4 МБ/237 online-sanity-regression resource-cost (info) verified→speculative (info)

4. ФОЛС-ПОЗИТИВЫ — кто и насколько (ключевой раздел)

Системного фолс-позитивщика в прогоне НЕТ. Это нетипично хорошо — обычно агенты дают ~85% success-hallucination/false-positive. Здесь дисциплина «EVIDENCE BEFORE CLAIM / DISCONFIRM BY DEFAULT» соблюдена почти всеми.

По агентам (подтверждено / убито / переклассифицировано):

  • pwa-sw-shell tester2/2 подтверждено, 0 убито. Надёжен. Корень доказан grep'ом до конкретного call-site, репро 3×.
  • offline-read tester2/2 подтверждено, 0 убито. Самый надёжный по оракулам (out-of-band IndexedDB + from_service_worker). Снял подозрение в tooling сам.
  • offline-structural-outbox tester — 1 подтверждён (#9), 1 WIP-честный (#11), 1 env-честный (#12), и #10 не дослежен verifier'ом (TIMED_OUT). Сам агент НЕ фолс-позитивит: для каждого claim — curl ground-truth readback. Качество высокое; риск только в том, что самый тяжёлый его сценарий (#10) остался без независимого подтверждения.
  • offline-edit-bodyобразец анти-фолс-позитива: #6 подтверждён против сервер-истины, #7 САМ дисконфирмировал (честно понизил до speculative и записал в калибровку скилла), #8 — реальное наблюдение, но verifier переклассифицировал в «штатный UX, не дефект». То есть единственный «почти-фолс» (#8) — это не выдуманный баг, а корректно наблюдённое поведение с неверной первичной интерпретацией severity; агент сам пометил его low/affordance.
  • update-prompt-install — фолс-позитивов 0; наоборот, ПРЕДОТВРАТИЛ системный фолс-позитив всего прогона, задокументировав tooling-trap (#13). #14 честно «не воспроизводимо», без фантома.
  • recon и online-sanity-regression — 0 фолсов; оба честно пометили свои находки как benign/informational, не раздувая severity.

Кто путает «не реализовано» с «баг»? — Никто грубо не спутал. Граница проведена аккуратно offline-structural-outbox: он РАЗДЕЛИЛ «Create page без фидбэка» (#11, честный WIP — ничего не заявляло успех) и «comment/move silent-loss» (#10, РЕАЛЬНЫЙ дефект — заявлен оптимистичный успех, потом тихо потерян). Это именно тот WIP-vs-bug гейт, который требовал стенд («claims success then silently loses data = баг; not-implemented ≠ баг»). Гейт сработал корректно.

Переклассификации вниз verifier'ом (3): #8 verified→speculative (штатный title-focus), #14 verified→speculative (coverage-gap, не дефект), #15 verified→speculative (informational; плюс уточнение «1 HTML, а не 0»). Все три — не выдуманные баги, а корректные наблюдения с завышенной первичной «дефектностью». Это мягкие фолсы интерпретации, не фабрикация.

Verifier-таймауты (2) — главный риск процесса: #7 (burst-race) и #10 (silent-optimistic-loss) НЕ перепроверены (TIMED_OUT). Опасно, что #10 — это самый ценный data-loss класс всего PR; он остался на слове одного агента (хоть и с curl-readback-доказательством). Verifier «прогорел» именно на тяжёлых многоконтекстных/тайминговых сценариях — там, где репро дороже всего.


5. Где скилл сработал / провалился на ОФЛАЙНЕ

Сработал:

  • Поймал blank-shell-offline — главный офлайн-дефект, причём 4 агентами независимо и разными путями (offline-reload, cold-start reopen, pinned-page). Implicit-оракул bodyLen=0 + обязательный reload/persistence round-trip — то, ради чего скилл и существует.
  • Поймал silent-optimistic-loss (#10) через out-of-band curl readback — единственно верный способ отличить «оптимистичный успех» от реальной серверной записи.
  • SW/offline-техника применена ПРАВИЛЬНО подавляющим большинством: один persistent context на протяжении online→offline→online; ожидание serviceWorker.ready И controller != null ДО set_offline(True); проверка response.from_service_worker, чтобы НЕ свалить blank на «SW не активирован». Ни один агент не сделал классическую ошибку «свежий контекст»/«offline до активации SW».
  • WIP-vs-bug гейт разделён корректно (см. §4): #11 — WIP, #10 — дефект.
  • Tooling-trap пойман и задокументирован (#13) — скилл не свалил GET-200 на «SW отдаёт stale API», а перепроверил вторым механизмом и cache-enumeration. Это рост зрелости самого скилла.

Провалился / шероховатости:

  • Дубль-репортинг одного корня как ~4 находок (#2/#3/#4/#5/#9 — один кластер: auth-gate + unguarded error.response.status). Хорошо для уверенности, плохо для сигнал/шум: владелец получает 4–5 «high», а корней — два (gate + unguarded access). Нужен дедуп-merge перед отчётом.
  • Два verifier-таймаута на самых ценных сценариях (#7, #10) — процесс не довёл независимую проверку до data-loss находки.
  • Эвристика «tallest ProseMirror = body» молча выбирала title на свежих страницах (h=48 > h=26) — offline-edit-body напоролся (#8) и записал в калибровку: дизамбигуировать по aria-label='Page title'.
  • Login rate-limit (HTTP 429, AUTH_THROTTLER 10/60s на IP) мешал почти всем агентам на повторных прогонах — общий стенд; обходили через storage_state. Это инфраструктурный шум, который замедлял repro и, вероятно, способствовал verifier-таймаутам.

6. Предложения по улучшению скилла (PWA/offline-специфика)

  1. Кодифицировать SW-lifecycle чеклист как обязательный gate (большинство уже делает, но зафиксировать в references/async-and-state.md): (a) один persistent context на весь online→offline→online; (b) await serviceWorker.ready И controller != null ДО set_offline; (c) после offline-reload проверять response.from_service_worker на ассетах — чтобы НИКОГДА не путать «SW не активирован/Chrome-offline» с «оболочка 200, app крашнул».

  2. Добавить оракул «cache-populated-but-unrendered» в oracles.md: при blank-shell обязательно out-of-band читать persisted store (IndexedDB gitmost-rq-cache/idb-keyval) — если данные ЕСТЬ, а экран пуст, это render-gate/crash, а не «нет кэша». offline-read сделал это вручную; должно быть стандартным шагом.

  3. Жёсткий WIP-vs-bug гейт (формализовать триаду): для КАЖДОЙ офлайн-мутации спрашивать — (a) заявлен ли оптимистичный успех? (b) переживает ли reload-before-reconnect? (c) уходит ли на сервер (curl ground-truth readback)? «Успех заявлен + reload теряет + сервер пуст» = РЕАЛЬНЫЙ дефект; «ничего не заявлено» = WIP-affordance. Этот тест уже неявно применил offline-structural-outbox — сделать его явным чеклистом.

  4. Обязательный out-of-band ground-truth для всех claim'ов о персистентности: curl REST с auth-cookie ИЛИ свежий контекст из collab-сервера (:3001). Никаких выводов «синхронизировалось» из UI-оптимизма. Это то, что убило success-hallucination в этом прогоне — закрепить как правило.

  5. Дедуп-стадия перед отчётом: группировать находки по корню (root-cause-cluster), а не по симптому/агенту. 5 «blank»-находок здесь = 2 корня (auth-gate + unguarded error.response.status). Merge снижает шум для владельца и не теряет «нашли 4 агентами» как сигнал уверенности.

  6. Verifier-таймауты: (a) бюджетировать ВРЕМЯ под тяжёлые многоконтекстные/тайминговые сценарии (#7/#10) ОТДЕЛЬНО и приоритизировать data-loss класс выше косметики; (b) для tooling-suspected находок (#7) разрешать verifier'у завершаться вердиктом «inconclusive-by-design», а не TIMED_OUT, если агент уже сам дисконфирмировал; (c) кэшировать storage_state, чтобы 429-rate-limit не съедал бюджет повторных login'ов.

  7. Зафиксировать tooling-trap'ы как known-issues в SKILL.md: (a) set_offline/ctx.route НЕ глушат fetch из SW-потока — GET, обработанный workbox NetworkOnly, отдаёт 200 при «offline»; верный оракул = enumerate caches (0 /api entries ⇒ 200 = живой хит). (b) native HTML5 DnD (@atlaskit/pragmatic-drag-and-drop) не actuatable в headless — MOVE проверять чтением кода + REST, не выдавать «broken». (c) «tallest ProseMirror = body» ломается на свежих страницах — дизамбигуация по aria-label.

  8. Settle-дисциплина для синтетик-ввода у state-transition: не delay=0 burst в момент online↔offline переключения; settle 2–2.5s + cadence 60–120 мс/символ (вывод из само-дисконфирма #7). Внести в async-and-state.md, чтобы не плодить ложные «потери символов».


Мета-фидбэк: timeout-гард сработал — 2 verifier'а уперлись в 9-мин кап и помечены TIMED_OUT, а не подвесили весь прогон (в прогоне 1 был залип 5.5ч). Trade-off: 2 находки остались непроверенными — на самых тяжёлых multi-context data-loss сценариях; стоит дать verifier'у на такие больше времени ИЛИ упрощённый детерминированный репро-харнесс.

🤖 web-test-orchestrator (process-trace)

> **Прогон 3/7** · эпик **PR #120** (offline-sync / PWA) · web-test-orchestrator · 15 сырых → 6 подтв / 5 ложных-или-переклассиф / 4 не-реализовано-WIP. > Процесс-отчёт для отладки скилла (агенты/инструменты/класс/фолс-позитивы + насколько верно применяли SW/offline-технику и отделяли WIP от багов). Без кода багов. # Процесс-трейс QA отчёт — PR #120 `feature/offline-sync` (gitmost / Docmost fork, OFFLINE/PWA, WIP M0–M2) > Фокус отчёта — НЕ код приложения, а РАБОТА СКИЛЛА `web-test-orchestrator`: какие агенты/инструменты отработали, что нашли, какой КЛАСС бага, как и на каком этапе всплыл, кто фолс-позитивит, и насколько правильно агенты применили SW/offline-технику. --- ## 1. Сводка прогона **Стадии скилла (по trace-данным):** recon → mapping/charters → execution (6 персон-агентов параллельно) → oracle-оценка → независимый Doer-Verifier пасс → синтез. **Агенты исполнения (7 ролей):** - `recon` (разведка/бутстрап) - `pwa-sw-shell tester` (оболочка SW + offline reload) - `offline-read tester` (офлайн-чтение/cold-start) - `offline-edit-body` (Contour A — Yjs/CRDT тело документа) - `offline-structural-outbox tester` (Contour B — REST/outbox/структура) - `update-prompt-install` (SW update-prompt, install, offline-emulation traps) - `online-sanity-regression` (онлайн-регресс/ресурсы) **Воронка находок (15 сырых → классификация после verifier):** | Класс по итогу | Кол-во | Находки | |---|---|---| | Подтверждённые РЕАЛЬНЫЕ дефекты | **6** | #2,#3,#4,#5,#9,#10 | | Не-реализовано-WIP / coverage-gap / env-limitation | **4** | #11 (create-no-feedback), #12 (move DnD gap), #13 (offline-emulation trap), #14 (update-prompt gap) | | Не-дефект / ложное / информационное / переклассифицировано | **5** | #1 (benign console-noise), #6 (Contour A — гарантия держится), #7 (disconfirmed race), #8 (title-focus = штатный UX), #15 (precache 13.4 МБ — informational) | | **Итого сырых** | **15** | | **Verifier:** 13 находок перепроверены, из них 11 подтверждены as-is, 2 переклассифицированы вниз (#8 verified→speculative «штатный UX»; #14/#15 verified→speculative «не дефект»). **2 находки verifier НЕ успел перепроверить (TIMED_OUT): #7 и #10** — обе тяжёлые многоконтекстные/тайминговые сценарии (см. §4). **Ключевой вывод процесса:** скилл уверенно поймал главный офлайн-дефект (blank-shell на offline-reload из-за auth-gate + unguarded `error.response.status`) — его независимо переоткрыли **четыре** агента разными путями. Сильная сторона — почти все агенты ПРАВИЛЬНО применили SW/offline-технику (один контекст, ожидание `controller != null` до `set_offline`). Слабые места — дубль-репортинг одного корня как 4 находки и два verifier-таймаута на самых ценных data-loss сценариях. --- ## 2. Какие агенты/инструменты отработали ### `recon` — разведка - **Покрыл:** бутстрап, карта офлайн-исходников, пре-/пост-login фазы консоли. - **Инструменты:** Bash (curl-пробы эндпоинтов, grep по `apps/client/src`), Playwright sync (login, `serviceWorker.ready`/`controller`, `caches.keys`, `indexedDB.databases`, `fetch('/manifest.json')`), `page.on(console/pageerror/response/requestfailed)`, **phase-tagged response listener**, скриншоты, Read исходников. - **Проявил себя:** методично — изобрёл фазовую переменную, мутируемую на каждом шаге, что строго локализовало 401-шум в пре-login. Дисциплинированно НЕ раздул косметику до дефекта (честно: «cosmetic, not data loss»). - **Офлайн-техника:** на этом этапе offline не требовался; SW-проверки (ready/controller) выполнены корректно. ### `pwa-sw-shell tester` — оболочка SW + offline reload - **Покрыл:** offline-reload авторизованного маршрута, fallback-UI, корень blank-screen. - **Инструменты:** Playwright **async** API, ОДИН контекст online→offline через `set_offline(True/False)`, `evaluate` для `controller`/`caches.keys()`/DOM-проб, четыре listener'а, скриншоты, Bash curl + grep/Read для атрибуции корня. - **Проявил себя:** образцово — поймал blank-shell (#2), отделил «оболочка грузится 200 из precache» от «React падает», grep'ом сузил до ровно двух call-site'ов `error.response.status` и доказал, что виноват именно незащищённый в `auth-query.tsx`. Репро 3×, детерминированно. - **Офлайн-техника: правильно** — SW активирован (controller non-null, 237 precache) ДО offline; один контекст; явно различил «не Chrome-offline-страница, не precache-404, а краш приложения». Не свалил на tooling. ### `offline-read tester` — офлайн-чтение / cold-start - **Покрыл:** hard-reload/reopen офлайн (естественный триггер «тётя Люба переоткрыла PWA»), наличие данных в persisted RQ-cache vs их отрисовка. - **Инструменты:** Playwright python persistent context + `set_offline`; hooks response/requestfailed/pageerror; **out-of-band чтение IndexedDB** `gitmost-rq-cache` (idb-keyval) изнутри страницы; `response.from_service_worker` как оракул; скриншоты (Read как изображения); статические Read 7 файлов. - **Проявил себя:** сильнейший оракульный пасс — доказал «данные ЕСТЬ в кэше (13 read-queries), но не отрисованы», что отличает blank-shell от «нет кэша». Проверил `from_service_worker=True` на всех ответах — снял подозрение в tooling/SW-неактивации. - **Офлайн-техника: правильно** — `controller != null` до offline, один persistent context; явно «ruled out tooling/SW-activation artifact». ### `offline-edit-body` (Contour A, CRDT-персона) - **Покрыл:** офлайн-правки тела (Yjs+y-indexeddb), reconnect-merge, конкурентные правки из двух контекстов. - **Инструменты:** Bash (python3 + playwright async), Read скриншотов, Write heredoc-скриптов в `/tmp/ot/*.py`. **Верификация против независимого источника** — свежий 2-й контекст, читающий из collab-сервера :3001 (server-truth, отдельная IndexedDB), а не из UI-оптимизма. - **Проявил себя:** методологически лучший на дисконфирме — подтвердил гарантию Contour A (#6) против сервер-истины и сам ЖЕ дисконфирмировал собственное подозрение на потерю символов (#7) после повторного теста с settle-задержкой. Это образец «DISCONFIRM BY DEFAULT». - **Офлайн-техника: правильно** — gating на `ready + controller != null`; один + свежий контексты осознанно разделены по ролям. ### `offline-structural-outbox tester` (Contour B) - **Покрыл:** outbox/paused-mutations (comment/create/move), offline-reload pinned-страницы, «Create page» без фидбэка. - **Инструменты:** Playwright python sync, ОДИН persistent context online→offline; **out-of-band ground-truth через прямой REST** (curl POST `/api/comments`, `/api/pages/info` с authToken-cookie) — читает серверное состояние, которое UI не контролирует; phase-tagged request capture; Read исходников для атрибуции. - **Проявил себя:** самый ценный по data-loss классам — отделил «чистый reconnect НЕ теряет» (replay 200, curl readback 0→1) от «reload-while-offline теряет молча» (#10), и честно пометил «Create page» как WIP-affordance (#11), а MOVE — как coverage-gap из-за native DnD (#12), не выдав ложное «broken». - **Офлайн-техника: правильно** — controller-check до offline, один контекст; curl-readback как внешний оракул — сильнейший приём против success-hallucination. ### `update-prompt-install` - **Покрыл:** SW update-prompt path, install, и — ценнейшее — **выявил ловушку самого инструментария** (#13): `set_offline`/`ctx.route` НЕ блокируют fetch из SW-потока, GET `/api/health` отдаёт 200 при «offline». - **Инструменты:** curl, grep/sed по источнику + сгенерированному `/sw.js`, Playwright `launch_persistent_context`; перекрёстная проверка двумя механизмами (`set_offline` ↔ `ctx.route` abort) + enumeration `caches.keys()`/`cache.keys()`. - **Проявил себя:** дал калибровочный вклад в САМ СКИЛЛ — задокументировал, почему наивное чтение GET-200 = ложный «SW serves stale API», и предложил правильный оракул (enumerate caches, 0 `/api` entries). Update-path честно помечен как не-воспроизводимый в одном билде (#14), без фантомного дефекта. - **Офлайн-техника: правильно + углубил её** — нашёл структурный предел эмуляции offline в Playwright. ### `online-sanity-regression` - **Покрыл:** онлайн-регресс, стоимость precache (#15). - **Инструменты:** Playwright sync, SW-state через evaluate, out-of-band fresh-tab, cold-vs-warm timing. - **Проявил себя:** корректно пометил 13.4 МБ/237 как informational (не дефект); verifier подтвердил и уточнил «1 HTML, а не 0». - **Офлайн-техника:** корректная SW-проверка до offline. --- ## 3. Баги по КЛАССУ (как проявлялся / как и каким агентом найден; код опущен) ### A. `blank-shell-offline` (оболочка грузится 200 из precache, React падает в пустоту) - **Проявление:** offline-reload/reopen любого авторизованного маршрута → `body.innerText.length = 0`, `#root` содержит только Mantine-`<style>`, в окне — uncaught `TypeError: Cannot read properties of undefined (reading "status")`. SW отдаёт index.html + все ассеты 200 из precache (НЕ Chrome-offline, НЕ precache-404). После `set_offline(False)`+reload — восстановление. - **Как/кем найден:** implicit-оракул «пустая страница» (`bodyLen=0`) сработал ТОЛЬКО потому, что скилл требует reload/persistence round-trip. Независимо переоткрыт **4 агентами**: `pwa-sw-shell` (#2), `offline-read` (#4), `offline-structural-outbox` (#9, +доказал что pinned-страница тоже белеет), и компонента gate описана `pwa-sw-shell` отдельно (#3). Корень разбит на два под-класса ниже. ### B. `rendering / hard-online-dependency in render-gate` - **Проявление:** render-gate авторизованного дерева возвращает пустой фрагмент при ошибке/загрузке current-user; офлайн `/api/users/me` (NetworkOnly) не отдаётся → пусто, даже если бы краш collab-token был починен. - **Как/кем найден:** `pwa-sw-shell` (#3) Read компонента + корреляция с `requestfailed` на `/api/users/me`; контроль — login-страница офлайн РЕНДЕРИТСЯ, что изолировало blank к auth-gated дереву. `offline-read` (#4) усилил out-of-band чтением IndexedDB (данные есть, не показаны). ### C. `unhandled-network-error / unguarded-property-access` - **Проявление:** на каждой офлайн-сетевой ошибке retry-callback читает `.status` у `undefined` response → бросает TypeError app-wide. - **Как/кем найден:** `pageerror` hook поймал сообщение; повторный прогон со stack-capture дал фрейм `at retry (...index-DKs-wrBZ.js)`; grep `.status` по bootstrap-коду изолировал единственный незащищённый call-site. Найден `pwa-sw-shell` (#2) и независимо `offline-read` (#5). ### D. `silent-optimistic-loss` (false optimistic success → потеря при reload-before-reconnect) - **Проявление:** офлайн добавление комментария показывает оптимистичный успех (текст+маркер в редакторе, composer закрылся), сервер пуст. Чистый reconnect → paused POST реально уходит (200, curl readback 0→1). НО reload пока офлайн → in-memory paused mutation выброшен, сервер НИКОГДА не получает (create requests: []). - **Как/кем найден:** `offline-structural-outbox` (#10) — `t_comment.py` (оптимистика+replay через curl) + `t_reload_loss.py` (успех→reload-offline→утрата). Оракул «false optimistic success later dropped = real defect» из `async-and-state.md`. **Verifier TIMED_OUT** — не перепроверено. ### E. `missing-offline-affordance` (WIP, НЕ data-loss) - **Проявление:** офлайн «Create page» — ноль фидбэка (нет узла, нет навигации, нет тоста, нет запроса); на reconnect paused POST уходит и app навигирует. Ничего не заявляло успех → не потеря данных, честный UX-gap. - **Как/кем найден:** `offline-structural-outbox` (#11), phase-tagged request capture + чтение порядка в `handleCreate`. Корректно отнесён к WIP. ### F. `race` (подозрение, дисконфирмировано) - **Проявление:** в первом прогоне burst-ввод (delay=0) в момент дисконнекта потерял 2 ведущих символа. - **Как/кем найден:** `offline-edit-body` (#7); САМ дисконфирмировал на ретесте с settle 2.5s/cadence 60–120 мс (40/40 точно). Помечен speculative «синтетик-input race, не дефект CRDT». **Verifier TIMED_OUT.** ### G. `console-noise` (benign) и `resource-cost` (informational) - **#1** (recon): 3×401 + socket.io WS-closed строго в пре-login, чистится после auth. - **#15** (online-sanity): precache 13.4 МБ/237, не блокирует first paint — ожидаемое поведение PWA. ### H. `tooling-trap / env-limitation` (находки о САМОМ инструментарии) - **#13** (update-prompt-install): `set_offline`+`ctx.route` не глушат SW-thread fetch → GET 200 при offline; правильный оракул — enumeration caches. - **#12** (offline-structural-outbox): native HTML5 DnD не actuatable в headless → MOVE не отыгран live, по коду — тот же класс, что comment. - **#14** (update-prompt-install): update-prompt positive path не воспроизводим на одном билде. --- ## 3b. Кто что нашёл | # | Находка (кратко) | Агент | Класс | Итог verifier | |---|---|---|---|---| | 1 | Пре-login 3×401 + WS-warning | recon | console-noise (benign) | verified (не-дефект) | | 2 | Offline-reload = blank + TypeError | pwa-sw-shell | blank-shell / unguarded-access | **verified (дефект)** | | 3 | UserProvider gate → пусто офлайн | pwa-sw-shell | rendering / hard-online-gate | **verified (дефект)** | | 4 | Cold-start офлайн = white screen | offline-read | missing-offline-fallback | **verified (дефект)** | | 5 | Unguarded `error.response.status` | offline-read | unguarded-property-access | **verified (дефект)** | | 6 | Contour A: правки тела доживают | offline-edit-body | none — гарантия держится | verified (не-дефект) | | 7 | Burst-keystrokes теряют символы | offline-edit-body | suspected race (tooling) | **TIMED_OUT** (само-дисконфирм) | | 8 | Первый ввод уходит в title | offline-edit-body | focus-affordance | verified→**speculative** (штатный UX) | | 9 | Offline-reload белит даже pinned | offline-structural-outbox | blank-shell / defeats pinning | **verified (дефект)** | | 10 | Нет outbox → silent loss на reload | offline-structural-outbox | silent-optimistic-loss | **TIMED_OUT** | | 11 | «Create page» офлайн без фидбэка | offline-structural-outbox | missing-affordance (WIP) | verified (WIP) | | 12 | MOVE DnD не отыгран (headless) | offline-structural-outbox | coverage-gap (env) | (env-limitation) | | 13 | set_offline не глушит SW-fetch | update-prompt-install | tooling-trap (env) | verified (tooling-инсайт) | | 14 | Update-prompt path не воспроизводим | update-prompt-install | coverage-gap (WIP) | verified→**speculative** | | 15 | Precache 13.4 МБ/237 | online-sanity-regression | resource-cost (info) | verified→**speculative** (info) | --- ## 4. ФОЛС-ПОЗИТИВЫ — кто и насколько (ключевой раздел) **Системного фолс-позитивщика в прогоне НЕТ.** Это нетипично хорошо — обычно агенты дают ~85% success-hallucination/false-positive. Здесь дисциплина «EVIDENCE BEFORE CLAIM / DISCONFIRM BY DEFAULT» соблюдена почти всеми. **По агентам (подтверждено / убито / переклассифицировано):** - `pwa-sw-shell tester` — **2/2 подтверждено**, 0 убито. Надёжен. Корень доказан grep'ом до конкретного call-site, репро 3×. - `offline-read tester` — **2/2 подтверждено**, 0 убито. Самый надёжный по оракулам (out-of-band IndexedDB + `from_service_worker`). Снял подозрение в tooling сам. - `offline-structural-outbox tester` — 1 подтверждён (#9), 1 WIP-честный (#11), 1 env-честный (#12), и **#10 не дослежен verifier'ом (TIMED_OUT)**. Сам агент НЕ фолс-позитивит: для каждого claim — curl ground-truth readback. Качество высокое; риск только в том, что самый тяжёлый его сценарий (#10) остался без независимого подтверждения. - `offline-edit-body` — **образец анти-фолс-позитива**: #6 подтверждён против сервер-истины, #7 САМ дисконфирмировал (честно понизил до speculative и записал в калибровку скилла), #8 — реальное наблюдение, но verifier переклассифицировал в «штатный UX, не дефект». То есть единственный «почти-фолс» (#8) — это не выдуманный баг, а корректно наблюдённое поведение с неверной первичной интерпретацией severity; агент сам пометил его low/affordance. - `update-prompt-install` — фолс-позитивов 0; наоборот, ПРЕДОТВРАТИЛ системный фолс-позитив всего прогона, задокументировав tooling-trap (#13). #14 честно «не воспроизводимо», без фантома. - `recon` и `online-sanity-regression` — 0 фолсов; оба честно пометили свои находки как benign/informational, не раздувая severity. **Кто путает «не реализовано» с «баг»? — Никто грубо не спутал.** Граница проведена аккуратно `offline-structural-outbox`: он РАЗДЕЛИЛ «Create page без фидбэка» (#11, честный WIP — ничего не заявляло успех) и «comment/move silent-loss» (#10, РЕАЛЬНЫЙ дефект — заявлен оптимистичный успех, потом тихо потерян). Это именно тот WIP-vs-bug гейт, который требовал стенд («claims success then silently loses data = баг; not-implemented ≠ баг»). Гейт сработал корректно. **Переклассификации вниз verifier'ом (3):** #8 verified→speculative (штатный title-focus), #14 verified→speculative (coverage-gap, не дефект), #15 verified→speculative (informational; плюс уточнение «1 HTML, а не 0»). Все три — не выдуманные баги, а корректные наблюдения с завышенной первичной «дефектностью». Это мягкие фолсы интерпретации, не фабрикация. **Verifier-таймауты (2) — главный риск процесса:** `#7` (burst-race) и `#10` (silent-optimistic-loss) НЕ перепроверены (TIMED_OUT). Опасно, что #10 — это самый ценный data-loss класс всего PR; он остался на слове одного агента (хоть и с curl-readback-доказательством). Verifier «прогорел» именно на тяжёлых многоконтекстных/тайминговых сценариях — там, где репро дороже всего. --- ## 5. Где скилл сработал / провалился на ОФЛАЙНЕ **Сработал:** - **Поймал blank-shell-offline** — главный офлайн-дефект, причём 4 агентами независимо и разными путями (offline-reload, cold-start reopen, pinned-page). Implicit-оракул `bodyLen=0` + обязательный reload/persistence round-trip — то, ради чего скилл и существует. - **Поймал silent-optimistic-loss** (#10) через out-of-band curl readback — единственно верный способ отличить «оптимистичный успех» от реальной серверной записи. - **SW/offline-техника применена ПРАВИЛЬНО подавляющим большинством:** один persistent context на протяжении online→offline→online; ожидание `serviceWorker.ready` И `controller != null` ДО `set_offline(True)`; проверка `response.from_service_worker`, чтобы НЕ свалить blank на «SW не активирован». Ни один агент не сделал классическую ошибку «свежий контекст»/«offline до активации SW». - **WIP-vs-bug гейт разделён корректно** (см. §4): #11 — WIP, #10 — дефект. - **Tooling-trap пойман и задокументирован** (#13) — скилл не свалил GET-200 на «SW отдаёт stale API», а перепроверил вторым механизмом и cache-enumeration. Это рост зрелости самого скилла. **Провалился / шероховатости:** - **Дубль-репортинг одного корня как ~4 находок** (#2/#3/#4/#5/#9 — один кластер: auth-gate + unguarded `error.response.status`). Хорошо для уверенности, плохо для сигнал/шум: владелец получает 4–5 «high», а корней — два (gate + unguarded access). Нужен дедуп-merge перед отчётом. - **Два verifier-таймаута на самых ценных сценариях** (#7, #10) — процесс не довёл независимую проверку до data-loss находки. - **Эвристика «tallest ProseMirror = body»** молча выбирала title на свежих страницах (h=48 > h=26) — `offline-edit-body` напоролся (#8) и записал в калибровку: дизамбигуировать по `aria-label='Page title'`. - **Login rate-limit (HTTP 429, AUTH_THROTTLER 10/60s на IP)** мешал почти всем агентам на повторных прогонах — общий стенд; обходили через storage_state. Это инфраструктурный шум, который замедлял repro и, вероятно, способствовал verifier-таймаутам. --- ## 6. Предложения по улучшению скилла (PWA/offline-специфика) 1. **Кодифицировать SW-lifecycle чеклист как обязательный gate** (большинство уже делает, но зафиксировать в `references/async-and-state.md`): (a) один persistent context на весь online→offline→online; (b) `await serviceWorker.ready` И `controller != null` ДО `set_offline`; (c) после offline-reload проверять `response.from_service_worker` на ассетах — чтобы НИКОГДА не путать «SW не активирован/Chrome-offline» с «оболочка 200, app крашнул». 2. **Добавить оракул «cache-populated-but-unrendered»** в `oracles.md`: при blank-shell обязательно out-of-band читать persisted store (IndexedDB `gitmost-rq-cache`/idb-keyval) — если данные ЕСТЬ, а экран пуст, это render-gate/crash, а не «нет кэша». `offline-read` сделал это вручную; должно быть стандартным шагом. 3. **Жёсткий WIP-vs-bug гейт (формализовать триаду):** для КАЖДОЙ офлайн-мутации спрашивать — (a) заявлен ли оптимистичный успех? (b) переживает ли reload-before-reconnect? (c) уходит ли на сервер (curl ground-truth readback)? «Успех заявлен + reload теряет + сервер пуст» = РЕАЛЬНЫЙ дефект; «ничего не заявлено» = WIP-affordance. Этот тест уже неявно применил `offline-structural-outbox` — сделать его явным чеклистом. 4. **Обязательный out-of-band ground-truth для всех claim'ов о персистентности:** curl REST с auth-cookie ИЛИ свежий контекст из collab-сервера (:3001). Никаких выводов «синхронизировалось» из UI-оптимизма. Это то, что убило success-hallucination в этом прогоне — закрепить как правило. 5. **Дедуп-стадия перед отчётом:** группировать находки по корню (root-cause-cluster), а не по симптому/агенту. 5 «blank»-находок здесь = 2 корня (auth-gate + unguarded `error.response.status`). Merge снижает шум для владельца и не теряет «нашли 4 агентами» как сигнал уверенности. 6. **Verifier-таймауты:** (a) бюджетировать ВРЕМЯ под тяжёлые многоконтекстные/тайминговые сценарии (#7/#10) ОТДЕЛЬНО и приоритизировать data-loss класс выше косметики; (b) для tooling-suspected находок (#7) разрешать verifier'у завершаться вердиктом «inconclusive-by-design», а не TIMED_OUT, если агент уже сам дисконфирмировал; (c) кэшировать storage_state, чтобы 429-rate-limit не съедал бюджет повторных login'ов. 7. **Зафиксировать tooling-trap'ы как known-issues в SKILL.md:** (a) `set_offline`/`ctx.route` НЕ глушат fetch из SW-потока — GET, обработанный workbox NetworkOnly, отдаёт 200 при «offline»; верный оракул = enumerate caches (0 `/api` entries ⇒ 200 = живой хит). (b) native HTML5 DnD (@atlaskit/pragmatic-drag-and-drop) не actuatable в headless — MOVE проверять чтением кода + REST, не выдавать «broken». (c) «tallest ProseMirror = body» ломается на свежих страницах — дизамбигуация по `aria-label`. 8. **Settle-дисциплина для синтетик-ввода у state-transition:** не `delay=0` burst в момент online↔offline переключения; settle 2–2.5s + cadence 60–120 мс/символ (вывод из само-дисконфирма #7). Внести в `async-and-state.md`, чтобы не плодить ложные «потери символов». --- ✅ **Мета-фидбэк:** timeout-гард сработал — 2 verifier'а уперлись в 9-мин кап и помечены TIMED_OUT, а не подвесили весь прогон (в прогоне 1 был залип 5.5ч). Trade-off: 2 находки остались непроверенными — на самых тяжёлых multi-context data-loss сценариях; стоит дать verifier'у на такие больше времени ИЛИ упрощённый детерминированный репро-харнесс. 🤖 web-test-orchestrator (process-trace)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: vvzvlad/gitmost#237