[QA-trace][#120 4/7] Процесс-отчёт web-test-orchestrator (offline) — дедуп-провал: 6 находок = 1 корень #238
Reference in New Issue
Block a user
Delete Branch "%!s()"
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?
Process-trace QA отчёт — PR #120
feature/offline-sync(gitmost / Docmost fork)1. Сводка прогона
Стадии: recon → mapping → charters → execution (персоны-туры) → oracle eval → Doer–Verifier → отчёт. Все офлайн-агенты дошли до execution с реальным Playwright-прогоном; root-cause-агенты дополнительно делали grep/Read исходников клиента.
Агенты (7 ролей):
recon,pwa-sw-shell,offline-read tester,offline-edit-body,offline-structural-outbox,update-prompt-install slice,online-sanity-regression.Воронка 14 сырых находок → после verifier:
reconpwa-sw-shellblank-shell — но подтверждён 5 «соседями»Ключевой факт процесса: 6 из 6 «реальных дефектов» — это ОДИН баг (
auth-query.tsx:27, незащищённыйerror.response.statusв retry-предикатеuseCollabToken). Дедупликации между ролями не было — каждый офлайн-агент пришёл к нему сам.2. Какие агенты/инструменты отработали
recon— карта приложения, SW/cache/IndexedDB-интроспекция, сетевой/консольный шум. Инструменты: Playwrightlaunch_persistent_context(чтобы SW+IndexedDB жили между навигациями),page.on(console/response/requestfailed),page.evaluate(serviceWorker.getRegistration/ready/controller,caches.keys(),indexedDB.databases()),eval_on_selector_all, скриншоты, curl/sw.js/manifest.json. Офлайн-технику толком НЕ применял (остался в онлайне), зато нашёл SW-lifecycle-нюансы — и именно тут перегенерил шум (см. §4). Самый фолс-позитивный.pwa-sw-shell— персона «установленный PWA, потерял сеть». Техника образцовая: один контекст, дождалсяcontroller != null+ 237 precache, ТОЛЬКО потомset_offline(True)+reload. Главное — дисконфирм-контроль: офлайн БЕЗ reload отрисовал полный/home(1279 символов), что изолировало дефект на cold-boot, а не на tooling/офлайн вообще. Нашёл blank-shell + проксимальный TypeError. sync_api. На tooling НЕ свалил.offline-read tester— «тётя Люба читает закэшированное в поезде». Одинlaunch_persistent_contextonline→offline→online. Оракул out-of-band: Postgrestext_contentчерезdocker exec psql. Дисконфирм-контроль: офлайн без reload — контент жив (bodyLen 976); reload — bodyLen 0 + тот самый pageerror. Grep.statusпоapps/client/src→ точное место. Чисто, надёжно.offline-edit-body— контур-A (тело Yjs). Самый строгий: триангуляция ТРЁХ оракулов (Postgrespages.text_contentout-of-band, online hard-reload,indexedDB.databases()). Корректно отделил «правка в открытой вкладке переживает reconnect» (positive, НЕ баг) от «reload офлайн = white-screen» (дефект), и довёл до строкиauth-query.tsx:27, отметив, что соседнийapi-client.ts:25защищёнif (error.response). Надёжный.offline-structural-outbox— контур-B (REST→кэш+outbox). Проверил IndexedDB-аутбокс (gitmost-rq-cache→clientState.mutations), второй браузер-контекст как независимый оракул,from_service_workerна навигации. Лучшая дисциплина WIP-vs-bug: blank-shell классифицировал как РЕАЛЬНЫЙ дефект, а «create без навигации» — как not-implemented-wip, явно зафиксировав «мутации НЕ теряются, реплеются на reconnect». SW-активацию проверил до offline. Надёжный.update-prompt-install slice— персона install/update + глубокое чтение исходников (vite.config,pwa-update-prompt,query-persister,user-provider,auth-query).add_init_scriptсо слушателемunhandledrejectionпоймал стекat retry(...), однозначно указавший на retry-предикатuseCollabToken. Нашёл ВТОРОЙ механизм того же blank-screen (gate-on-live-auth + исключение currentUser изOFFLINE_PERSIST_ROOTS) и корректно пометил его WIP. Надёжный.online-sanity-regression— базлайн/регрессия онлайна. Fresh-profile cold-install контроль, capture websocket-фреймов. Обе находки — корректно как НЕ-регрессии (console-401 baseline; LAN-IP collab = env-portability). Дисциплина framing'а образцовая.Вывод по технике: все 5 офлайн-агентов применили SW-технику ПРАВИЛЬНО — один persistent-контекст, ожидание
ready+controllerдоset_offline, дисконфирм-контроли. Никто не свалил дефект на tooling. Единая трением-точка у всех —chromium-1148вместо ожидаемого пакетом1223(нуженexecutable_path-override) и auth-rate-limit 429 (внешне = «логин не прошёл»). Оба — френдли-огонь скилла/окружения, и агенты корректно НЕ обвинили приложение.3. Баги по КЛАССУ (как проявлялся / как и кем найден)
A. blank-shell-offline / white-screen-of-death (rendering ⨯ unhandled-exception) — ДОМИНИРУЮЩИЙ класс.
Проявление: офлайн reload/cold-boot — SW отдаёт
index.htmlshell (HTTP 200,from_service_worker,titleвыставлен), ноbody.innerText.length=0, в#rootтолько Mantine<style>, ни лоадера, ни «вы офлайн», ни редиректа, ни авто-восстановления на reconnect (нужен ручной reload). Закэшированный контент (IndexedDBkeyval-store+ Yjspage.*) существует, но не рендерится.Найден: на стадии execution, первым —
pwa-sw-shell(online→ждать SW→set_offline→reload, с дисконфирм-контролем). Затем НЕЗАВИСИМО переоткрытoffline-read,offline-edit-body,offline-structural-outbox,update-prompt-install(6 сырых находок).B. unguarded-property-access / uncaught-exception (проксимальный код-дефект под A).
Проявление:
TypeError: Cannot read properties of undefined (reading 'status'), 3/3 на каждый офлайн cold-boot. Офлайн bootstrap-запрос (/api/auth/collab-token,/api/users/me, NetworkOnly) падаетERR_INTERNET_DISCONNECTEDбезresponse, обработчик читает.status→ throw до монтирования React.Найден: pageerror/
unhandledrejection-слушателями; кадр стекаat retryсматчен с retry-предикатомuseCollabToken; добит grep'ом.statusпо исходникам (одно незащищённое место против защищённого соседа).C. partial-offline / degraded-UX (WIP, БЕЗ потери данных).
Проявление: офлайн «Create page» ставит мутацию в очередь (
clientState.mutations {spaceId}), но НЕ навигирует и не даёт редактируемую цель; на reconnect реплеется какUntitled(title=null). Данные не теряются.Найден:
offline-structural-outbox— сверка online (NAVIGATED=True) vs offline (False, 0 редакторов) + дамп IndexedDB-аутбокса +/api/pages/recentпосле reconnect.D. offline-read-path-not-wired (WIP).
Проявление: тот же белый экран на cold-boot, но другой механизм —
UserProviderотдаёт<></>на любую ошибкуuseCurrentUser, аcurrentUserнамеренно исключён изOFFLINE_PERSIST_ROOTS, поэтому persisted-кэш недостижим через relaunch.Найден:
update-prompt-install— чтениемuser-provider.tsx+query-persister.ts.E. sw-activation-race (cosmetic, FP).
active.state='activating'при уже не-nullcontroller— штатное W3C-поведениеclientsClaim.recon.F. benign-load-noise (FP/benign). Pre-login 401 на
/api/users/meи/api/auth/collab-token— ожидаемые анон-пробы.recon+online-sanity.G. config/portability (env). Collab WS на захардкоженный
ws://10.20.30.227:3001. В этом стенде работает.online-sanity.H. residual-test-data (hygiene). Мусорные заметки прошлых QA в General.
recon.3b. Кто что нашёл
useCollabTokenretry4. ФОЛС-ПОЗИТИВЫ — кто и насколько (ключевой раздел)
recon— единственный систематический источник фолс-позитива. 3 находки, 2 убиты verifier'ом какspeculative(~66% FP среди его «находок»). Чём фолсит: штатное поведение платформы → выдаёт за дефект. SWactivating— это спецификация W3Cclients.claim(); pre-login 401 — ожидаемые анон-пробы. Обе воспроизводятся детерминированно, поэтому «verified» по факту репро — но это not-a-bug. Урок:recon-роли нужен жёсткий гейт «нашёл странность ≠ нашёл дефект».Контраст framing'а (важно для отладки скилла): одно и то же явление — pre-login 401 — у
recon(#1, заявлено КАК находка-проба) убито вspeculative, а уonline-sanity(#13, заявлено КАК «benign baseline, не SW-регрессия») прошлоverified. Вердикт verifier'а трекает ФОРМУЛИРОВКУ/скромность претензии, а не само явление. Скромный framing «это базлайн, не наш регресс» проходит; претензия «это дефект» — режется.Все 5 офлайн-агентов — 0 фолс-позитивов по дефектам. 100% их defect-claims подтвердились (5 verified + 1 timed-out-но-корроборирован). Это очень здоровый сигнал: тяжёлая офлайн-техника + дисконфирм-контроли = почти нулевой FP.
Путаница «не реализовано» vs «баг» — НЕ обнаружена. Ни один агент не назвал WIP-пробел «багом». Оба WIP-кейса (#10, #12) явно помечены
not-implemented-wipс правильным дискриминатором: «нет потери данных, нет ложного success-тоста — просто пусто/деградировано». А blank-shell, наоборот, верно классифицирован как РЕАЛЬНЫЙ дефект (это краш/тихий lockout, а не «фича не дописана»). Дискриминация между WIP и дефектом — сильная сторона прогона.Покрытие verifier'а — 1 дыра. Находка #4 (
pwa-sw-shellblank-shell) —TIMED_OUT, независимо не переперепроверена. Риск низкий: тот же корень подтверждён 5 соседними находками. Но это сигнал, что verifier'у нужен бюджет/таймаут-политика (см. §6).Надёжность (от лучшего):
offline-edit-body≈offline-structural-outbox≈update-prompt-install(root-cause до строки, оракулы out-of-band, WIP-дисциплина) >pwa-sw-shell/offline-read(отличная техника, без глубокого root-cause) >online-sanity(корректно, но низкоценные базлайн/env находки) >recon(нужен FP-гейт).5. Где скилл сработал/провалился на ОФЛАЙНЕ
Сработал:
launch_persistent_context, ожиданиеready+controller != nullДОset_offline, переключение online↔offline↔online в одном контексте. Ни одного «свежий контекст / SW не активирован».Провалился / шероховатости:
reconтащит SW-lifecycle-косметику в дефекты (activation race) — на офлайн-стенде это особенно зашумляет, т.к. SW-состояния выглядят «подозрительно», но штатны.auth-query.tsx:27. Скилл не свёл их до прогона verifier'а.chromium-1148vs1223; auth 429 как ложный «логин не прошёл») каждый агент переоткрывал заново — это надо вынести в пресет скилла, а не оставлять на переоткрытие каждой роли.6. Предложения по улучшению скилла (упор на PWA/offline)
SW-lifecycle-чеклист как жёсткий пресет (а не на усмотрение роли): (a)
launch_persistent_contextобязателен; (b) ассертserviceWorker.readyИcontroller != nullДОset_offline; (c) обязательный дисконфирм-контроль «офлайн без reload»; (d) проверкаresponse.from_service_workerна навигации. Все офлайн-роли это сделали — закрепить, чтобы иreconне выпадал.FP-гейт «штатное поведение ≠ дефект» для recon/sanity-ролей. Явный стоп-лист: SW activation race (
clientsClaim), pre-auth 401-пробы, cross-origin WS не перехватывается SW — это НЕ дефекты по умолчанию. Снимет ~2/3 шумаrecon.WIP-vs-bug-гейт как формальный оракул-вопрос: «есть ли (a) потеря данных, (b) ложный success-тост/тихий успех? Если нет, а функция просто отсутствует/пуста → not-implemented-wip; если краш/lockout/тихая потеря → defect». Прогон уже это делал интуитивно — кодифицировать.
Офлайн-оракулы вынести в reference как набор: Postgres
text_contentout-of-band (docker exec psql, рольdocmost/docmost), IndexedDB-аутбокс (keyval-store/gitmost-rq-cache→clientState.mutations), второй браузер-контекст,from_service_worker. Это «золотые» оракулы, которые отличают «не нарисовалось» от «потерялось».Дедуп ПЕРЕД verifier'ом по сигнатуре root-cause (строка/стек-кадр/error-string). 6 находок с
reading 'status'/at retry/auth-query.tsx:27— это один кластер; verifier должен верифицировать его ОДИН раз, а не пять, освободив бюджет на TIMED_OUT-кейсы.Политика verifier-таймаутов: для дорогих офлайн-репро (cold-boot blank) — либо больший бюджет, либо «подтверждение по корроборации»: если N≥3 соседних находок с тем же root-cause уже verified, кластер можно метить
verified-by-corroborationвместоTIMED_OUT.Пресет окружения стенда (chromium
executable_path-override; auth-rate-limit 429 → «всегда curl/api/auth/loginперед тем как винить логин; разноси прогоны на ~18-60с»; SPA делает клиентский редирект → опрашивайpage.url, неwait_for_url(load)). Каждый агент переоткрывал это — вынести в SKILL.md, сэкономит таймбокс.«Один симптом — несколько корней» в шаблон отчёта: явно линковать находки по симптому (white-screen) и по корню (crash-throw vs gate-on-live-auth), чтобы defect/WIP-классификации не выглядели взаимоисключающими.
📌 Ключевой процесс-вывод: 6 «реальных дефектов» = ОДИН корень (offline blank-shell), независимо переоткрытый 5 агентами — нулевая межролевая дедупликация (важный недостаток скилла: один баг раздувается в N находок). recon — главный фолс-позитивщик; все офлайн-агенты применили SW-технику ВЕРНО, никто не свалил на tooling.
🤖 web-test-orchestrator (process-trace)