{
 "generatedAt": "2026-10-15T17:02:16.637Z",
 "commit": "26ebdb0",
 "suites": [
  {
   "id": "gate",
   "name": "Гейт: convert + validate + reconcile",
   "status": "pass",
   "ms": 2427,
   "tail": "\n> kosmokvest-frontend@2.0.0 gate\n> node tools/convert/convert.js && node tools/validate/validate.js && node tools/convert/reconcile-verdict-md.js\n\nWROTE content contract to C:\\Obsidian\\New\\Projects\\25-Космоквест\\.04-Src\\01-Frontend\\content\n  scenes.json      : 111 scenes\n  edges            : 171\n  deaths.txt       : 12 confirmed death (verdicts §1.5) of 57 candidates\n  final            : [ '27.3' ]\n  start            : [ '0' ]\n  max choices/scene: 7\n  images resolved  : 111 | null(placeholder): 0\n  missing source img: 2.2:2.2.webp, 2.3:2.3.webp, 3.1:3.1.webp, 3.2:3.2.webp, 4.1:4.1.webp, 4.2:4.2.webp, 5.1:5.1.webp, 5.3:5.3.webp, 6.1:6.1.webp, 6.2:6.2.webp, 7.1:7.1.webp, 7.2:7.2.webp, 7.3:7.3.webp, 8:8.webp, 9.3:9.3.webp, 9.4:9.4.webp, 10:10.webp, 10.1:10.1.webp, 10.2:10.2.webp, 10.3:10.3.webp, 10.4:10.4.webp, 11.1:11.1.webp, 11.2:11.2.webp, 11.4:11.4.webp, 14:14.webp, 14.1:14.1.webp, 14.2:14.2.webp, 14.3:14.3.webp, 16.1:16.1.webp, 16.2:16.2.webp, 16.4:16.4.webp, 16.5:16.5.webp, 16.6:16.6.webp, 17.2:17.2.webp, 19:19.webp, 19.1:19.1.webp, 23.2:23.2.webp, 23.5:23.5.webp, 25.1:25.1.webp, 25.2:25.2.webp, 26.3:26.3.webp, 27.1:27.1.webp, 27.3:27.3.webp\naudio tracks=5 total=8.5MB\n=== VALIDATE ===\nscenes=111 edges=171 deaths=12 maxChoices=7 reachable=111 terminals=1 missingMedia=0\n\nPASS (structural + media). Images: all referenced .webp present @1280x1920, no stray/leftover. Audio gate: music.m4a per warning above if absent.\nreconciled 57 rows; tally death=12 soft=45 owner=0; needs-owner=[]\n"
  },
  {
   "id": "typecheck",
   "name": "TypeScript --noEmit",
   "status": "pass",
   "ms": 8230,
   "tail": "\n> kosmokvest-frontend@2.0.0 typecheck\n> tsc --noEmit\n\n"
  },
  {
   "id": "unit",
   "name": "Vitest unit",
   "status": "pass",
   "ms": 10128,
   "tail": ""
  },
  {
   "id": "e2e",
   "name": "Playwright e2e (chromium + app + atlas)",
   "status": "pass",
   "ms": 109656,
   "tail": ""
  }
 ],
 "checks": [
  {
   "id": "C-01",
   "name": "Сцены: ровно 111 узлов",
   "status": "pass",
   "detail": "111 узлов"
  },
  {
   "id": "C-02",
   "name": "Рёбра графа: 171 переход",
   "status": "pass",
   "detail": "171 рёбер"
  },
  {
   "id": "C-03",
   "name": "Локали: 10 файлов text/<lang>.json",
   "status": "pass",
   "detail": "de, en, es, fr, id, ja, pt-BR, ru, tr, zh-CN"
  },
  {
   "id": "C-04",
   "name": "app.json: langs ⊇ собранные",
   "status": "pass",
   "detail": "app.langs=[en,es,pt-BR,fr,de,ru,zh-CN,ja,tr,id]"
  },
  {
   "id": "C-05",
   "name": "Ключи сцен: паритет всех локалей к ru (111)",
   "status": "pass",
   "detail": "111 ключей × все языки"
  },
  {
   "id": "C-06",
   "name": "UI-строки: паритет всех ui/<lang>.json к ru",
   "status": "pass",
   "detail": "62 ключей × все языки"
  },
  {
   "id": "C-07",
   "name": "Разметка: нет запрещённых тегов в телах сцен (все языки)",
   "status": "pass",
   "detail": "чисто"
  },
  {
   "id": "C-08",
   "name": "Арт: все уникальные webp собраны в dist/web/assets",
   "status": "pass",
   "detail": "129 собрано / 100 уникальных (96 имён — арт переиспользуется между сценами)"
  },
  {
   "id": "C-09",
   "name": "Звуки: ≥10 sfx в dist/web/sfx",
   "status": "pass",
   "detail": "20 файлов"
  },
  {
   "id": "C-10",
   "name": "Глоссарий: файл терминов существует и непустой",
   "status": "pass",
   "detail": "1 терминов в glossary.ts"
  },
  {
   "id": "A-01",
   "name": "APK: файл существует и > 1 МБ",
   "status": "pass",
   "detail": "kosmoquest-debug-2.0.0-20261015b.apk, 16.8 МБ, 2026-10-15"
  },
  {
   "id": "A-02",
   "name": "APK: весь арт внутри (webp = уникальным исходникам)",
   "status": "pass",
   "detail": "129 webp в assets/public"
  },
  {
   "id": "A-03",
   "name": "APK: звуки внутри (≥11 m4a: sfx+музыка)",
   "status": "pass",
   "detail": "25 m4a"
  },
  {
   "id": "A-04",
   "name": "APK: index.html бандла внутри",
   "status": "pass",
   "detail": "assets/public/index.html есть"
  }
 ],
 "live": [
  {
   "name": "prod /",
   "url": "https://kosmoquest.pages.dev/",
   "status": "pass",
   "http": 200,
   "ms": 1214
  },
  {
   "name": "prod /humor-tree → редирект",
   "url": "https://kosmoquest.pages.dev/humor-tree",
   "status": "pass",
   "http": 200,
   "ms": 1371
  },
  {
   "name": "dev / (хаб)",
   "url": "https://kq-dev.pages.dev/",
   "status": "pass",
   "http": 200,
   "ms": 1674
  },
  {
   "name": "dev /play (игра)",
   "url": "https://kq-dev.pages.dev/play",
   "status": "pass",
   "http": 200,
   "ms": 880
  },
  {
   "name": "dev /humor-tree",
   "url": "https://kq-dev.pages.dev/humor-tree",
   "status": "pass",
   "http": 200,
   "ms": 769
  },
  {
   "name": "atlas /",
   "url": "https://kq-atlas.pages.dev/",
   "status": "pass",
   "http": 200,
   "ms": 761
  }
 ],
 "apk": {
  "file": "kosmoquest-debug-2.0.0-20261015b.apk",
  "sizeMB": "16.8",
  "mtime": "2026-10-15",
  "webp": 129,
  "sfx": 25
 },
 "totals": {
  "pass": 125,
  "fail": 0,
  "unit": {
   "p": 57,
   "n": 57
  },
  "e2e": {
   "p": 44,
   "n": 44
  },
  "checks": {
   "p": 14,
   "n": 14
  },
  "live": {
   "p": 6,
   "n": 6
  }
 },
 "unitTests": [
  {
   "suite": "content.test.ts",
   "title": "app.store_url = market:// (Android-обёртка)",
   "status": "pass",
   "ms": 3.0682999999999083,
   "failure": ""
  },
  {
   "suite": "content.test.ts",
   "title": "app.store_url_web = https Play (веб/CF Pages)",
   "status": "pass",
   "ms": 0.5223000000000866,
   "failure": ""
  },
  {
   "suite": "content.test.ts",
   "title": "privacy/web/compare — https, ads_enabled=false",
   "status": "pass",
   "ms": 0.5337000000001808,
   "failure": ""
  },
  {
   "suite": "graph.test.ts",
   "title": "одинаковое число вершин (111)",
   "status": "pass",
   "ms": 2.761299999999892,
   "failure": ""
  },
  {
   "suite": "graph.test.ts",
   "title": "одинаковый набор id",
   "status": "pass",
   "ms": 2.1304000000000087,
   "failure": ""
  },
  {
   "suite": "graph.test.ts",
   "title": "одинаковый набор рёбер (171)",
   "status": "pass",
   "ms": 2.374200000000201,
   "failure": ""
  },
  {
   "suite": "graph.test.ts",
   "title": "один и тот же старт",
   "status": "pass",
   "ms": 1.2079000000001088,
   "failure": ""
  },
  {
   "suite": "graph.test.ts",
   "title": "терминалы совпадают (final/без выбора)",
   "status": "pass",
   "ms": 1.2144000000000688,
   "failure": ""
  },
  {
   "suite": "graph.test.ts",
   "title": "нет висячих ссылок",
   "status": "pass",
   "ms": 9.18119999999999,
   "failure": ""
  },
  {
   "suite": "graph.test.ts",
   "title": "все 171 рёбра покрываются, все 111 достижимы из старта",
   "status": "pass",
   "ms": 11.139599999999973,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "точное совпадение",
   "status": "pass",
   "ms": 2.5120999999999185,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "базовый префикс (en-US → en)",
   "status": "pass",
   "ms": 0.5961999999999534,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "диалект uk → ru",
   "status": "pass",
   "ms": 0.5774999999998727,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "pt → pt-BR если доступно",
   "status": "pass",
   "ms": 0.3293999999998505,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "pt → pt-BR нет → fallback en",
   "status": "pass",
   "ms": 0.34580000000005384,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "неизвестный → fallback en",
   "status": "pass",
   "ms": 0.48500000000012733,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "autonymKey нормализует дефис",
   "status": "pass",
   "ms": 0.686200000000099,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "есть как минимум ru + en",
   "status": "pass",
   "ms": 2.772999999999911,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "de: текст всех 111 сцен без запрещённой разметки",
   "status": "pass",
   "ms": 92.97879999999986,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "de: набор id = scenes (111) и cid-паритет",
   "status": "pass",
   "ms": 4.3310999999998785,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "de: паритет {…}-плейсхолдеров vs ru (body + choices)",
   "status": "pass",
   "ms": 12.53710000000001,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "de: ui-ключи = ru, автонимы канон (не переведены)",
   "status": "pass",
   "ms": 1.892699999999877,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "en: текст всех 111 сцен без запрещённой разметки",
   "status": "pass",
   "ms": 74.68489999999997,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "en: набор id = scenes (111) и cid-паритет",
   "status": "pass",
   "ms": 4.623900000000049,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "en: паритет {…}-плейсхолдеров vs ru (body + choices)",
   "status": "pass",
   "ms": 8.380200000000059,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "en: ui-ключи = ru, автонимы канон (не переведены)",
   "status": "pass",
   "ms": 1.6971000000000913,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "es: текст всех 111 сцен без запрещённой разметки",
   "status": "pass",
   "ms": 102.20979999999986,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "es: набор id = scenes (111) и cid-паритет",
   "status": "pass",
   "ms": 5.585199999999986,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "es: паритет {…}-плейсхолдеров vs ru (body + choices)",
   "status": "pass",
   "ms": 15.605999999999995,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "es: ui-ключи = ru, автонимы канон (не переведены)",
   "status": "pass",
   "ms": 0.9058999999999742,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "fr: текст всех 111 сцен без запрещённой разметки",
   "status": "pass",
   "ms": 75.00340000000006,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "fr: набор id = scenes (111) и cid-паритет",
   "status": "pass",
   "ms": 3.460499999999911,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "fr: паритет {…}-плейсхолдеров vs ru (body + choices)",
   "status": "pass",
   "ms": 11.542100000000119,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "fr: ui-ключи = ru, автонимы канон (не переведены)",
   "status": "pass",
   "ms": 1.2413000000001375,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "id: текст всех 111 сцен без запрещённой разметки",
   "status": "pass",
   "ms": 79.58249999999998,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "id: набор id = scenes (111) и cid-паритет",
   "status": "pass",
   "ms": 3.643000000000029,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "id: паритет {…}-плейсхолдеров vs ru (body + choices)",
   "status": "pass",
   "ms": 7.630100000000084,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "id: ui-ключи = ru, автонимы канон (не переведены)",
   "status": "pass",
   "ms": 1.2714000000000851,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "ja: текст всех 111 сцен без запрещённой разметки",
   "status": "pass",
   "ms": 83.55169999999998,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "ja: набор id = scenes (111) и cid-паритет",
   "status": "pass",
   "ms": 3.8922999999999774,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "ja: паритет {…}-плейсхолдеров vs ru (body + choices)",
   "status": "pass",
   "ms": 20.967300000000023,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "ja: ui-ключи = ru, автонимы канон (не переведены)",
   "status": "pass",
   "ms": 10.260299999999916,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "pt-BR: текст всех 111 сцен без запрещённой разметки",
   "status": "pass",
   "ms": 59.33400000000006,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "pt-BR: набор id = scenes (111) и cid-паритет",
   "status": "pass",
   "ms": 4.030500000000302,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "pt-BR: паритет {…}-плейсхолдеров vs ru (body + choices)",
   "status": "pass",
   "ms": 6.537500000000364,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "pt-BR: ui-ключи = ru, автонимы канон (не переведены)",
   "status": "pass",
   "ms": 1.0648000000001048,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "ru: текст всех 111 сцен без запрещённой разметки",
   "status": "pass",
   "ms": 66.47859999999991,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "ru: набор id = scenes (111) и cid-паритет",
   "status": "pass",
   "ms": 22.940100000000257,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "tr: текст всех 111 сцен без запрещённой разметки",
   "status": "pass",
   "ms": 48.865999999999985,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "tr: набор id = scenes (111) и cid-паритет",
   "status": "pass",
   "ms": 2.8571999999999207,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "tr: паритет {…}-плейсхолдеров vs ru (body + choices)",
   "status": "pass",
   "ms": 6.2283000000002176,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "tr: ui-ключи = ru, автонимы канон (не переведены)",
   "status": "pass",
   "ms": 0.9850000000001273,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "zh-CN: текст всех 111 сцен без запрещённой разметки",
   "status": "pass",
   "ms": 55.537300000000414,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "zh-CN: набор id = scenes (111) и cid-паритет",
   "status": "pass",
   "ms": 3.4985999999998967,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "zh-CN: паритет {…}-плейсхолдеров vs ru (body + choices)",
   "status": "pass",
   "ms": 7.229099999999562,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "zh-CN: ui-ключи = ru, автонимы канон (не переведены)",
   "status": "pass",
   "ms": 1.0707000000002154,
   "failure": ""
  },
  {
   "suite": "i18n.test.ts",
   "title": "app.langs ⊇ собранные (остальные pending — допустимо)",
   "status": "pass",
   "ms": 0.998700000000099,
   "failure": ""
  }
 ],
 "e2eTests": [
  {
   "suite": "a11y.spec.ts [chromium]",
   "title": "TC-21 a11y axe dark-тема: 0 critical/serious",
   "status": "pass",
   "ms": 16875,
   "failure": ""
  },
  {
   "suite": "a11y.spec.ts [chromium]",
   "title": "TC-21 a11y axe light-тема: 0 critical/serious",
   "status": "pass",
   "ms": 7109,
   "failure": ""
  },
  {
   "suite": "a11y.spec.ts [chromium]",
   "title": "TC-21 a11y axe сцена сChoices + image (игровой экран): 0 critical/serious",
   "status": "pass",
   "ms": 5072,
   "failure": ""
  },
  {
   "suite": "a11y.spec.ts [chromium]",
   "title": "TC-21 a11y axe экран settings: 0 critical/serious",
   "status": "pass",
   "ms": 2943,
   "failure": ""
  },
  {
   "suite": "a11y.spec.ts [chromium]",
   "title": "TC-21 a11y axe экран about: 0 critical/serious",
   "status": "pass",
   "ms": 2866,
   "failure": ""
  },
  {
   "suite": "a11y.spec.ts [chromium]",
   "title": "TC-21 a11y axe экран death: 0 critical/serious",
   "status": "pass",
   "ms": 2480,
   "failure": ""
  },
  {
   "suite": "a11y.spec.ts [chromium]",
   "title": "TC-21 a11y axe экран final: 0 critical/serious",
   "status": "pass",
   "ms": 2352,
   "failure": ""
  },
  {
   "suite": "bugcheck.spec.ts [chromium]",
   "title": "FIX-1 превью настроек меняется при смене схемы",
   "status": "pass",
   "ms": 13438,
   "failure": ""
  },
  {
   "suite": "bugcheck.spec.ts [chromium]",
   "title": "FIX-2 трёхуровневый SFX: пульсар верный / warp петля / whoosh гибель",
   "status": "pass",
   "ms": 8625,
   "failure": ""
  },
  {
   "suite": "bugcheck.spec.ts [chromium]",
   "title": "FIX-3 Чередовать: по ended играет music-2, потом обратно music-1",
   "status": "pass",
   "ms": 3269,
   "failure": ""
  },
  {
   "suite": "death.spec.ts [chromium]",
   "title": "DTH-01 death-hint: первая гибель — подсказка, вторая — без неё",
   "status": "pass",
   "ms": 17364,
   "failure": ""
  },
  {
   "suite": "death.spec.ts [chromium]",
   "title": "DTH-02 death-retry: «Попробуй ещё раз» ведёт к главе главы",
   "status": "pass",
   "ms": 3287,
   "failure": ""
  },
  {
   "suite": "death.spec.ts [chromium]",
   "title": "DTH-03 death-hint персистент: после reload хинта уже нет",
   "status": "pass",
   "ms": 4529,
   "failure": ""
  },
  {
   "suite": "gate.spec.ts [chromium]",
   "title": "TC-05 autoplay обходит все 111 сцен без ошибок",
   "status": "pass",
   "ms": 13757,
   "failure": ""
  },
  {
   "suite": "gate.spec.ts [chromium]",
   "title": "TC-03 текст движка = text/ru.json (плейсхолдеры → URL)",
   "status": "pass",
   "ms": 9473,
   "failure": ""
  },
  {
   "suite": "gate.spec.ts [chromium]",
   "title": "TC-06 render-smoke: граф идентичен, экраны не в ошибке",
   "status": "pass",
   "ms": 1917,
   "failure": ""
  },
  {
   "suite": "gate.spec.ts [chromium]",
   "title": "TC-02b меню: старт-сцена рисуется, Продолжить скрыт без сейва",
   "status": "pass",
   "ms": 2623,
   "failure": ""
  },
  {
   "suite": "i18n.spec.ts [chromium]",
   "title": "TC-06 render-smoke ×языки: каждый собранный язык обходит 111 сцен без ошибок",
   "status": "pass",
   "ms": 24657,
   "failure": ""
  },
  {
   "suite": "i18n.spec.ts [chromium]",
   "title": "TC-07 смена языка ru→en без потери позиции и сейва",
   "status": "pass",
   "ms": 3113,
   "failure": ""
  },
  {
   "suite": "layout.spec.ts [chromium]",
   "title": "TC-16 DOM-layout: scene-text рендерится, нет горизонтального overflow",
   "status": "pass",
   "ms": 15159,
   "failure": ""
  },
  {
   "suite": "layout.spec.ts [chromium]",
   "title": "TC-16b choices: кнопки не сжаты ниже tap-target и не вылезают за панель",
   "status": "pass",
   "ms": 6270,
   "failure": ""
  },
  {
   "suite": "layout.spec.ts [chromium]",
   "title": "TC-17 PNG-сцен ≥20: рендер и запись без ошибок",
   "status": "pass",
   "ms": 9053,
   "failure": ""
  },
  {
   "suite": "layout.spec.ts [chromium]",
   "title": "TC-17 tofu zh/ja: глифы CJK отличается от replacement-char (иначе env-limited)",
   "status": "pass",
   "ms": 864,
   "failure": ""
  },
  {
   "suite": "nav.spec.ts [chromium]",
   "title": "NAV-03 scene→choice→back: prev-сцена через браузерный back",
   "status": "pass",
   "ms": 14866,
   "failure": ""
  },
  {
   "suite": "nav.spec.ts [chromium]",
   "title": "NAV-05 refresh на ?scene=13 восстанавливает ту же сцену",
   "status": "pass",
   "ms": 5109,
   "failure": ""
  },
  {
   "suite": "nav.spec.ts [chromium]",
   "title": "NAV-06 меню→настройки→back: экраны адресуемы историей",
   "status": "pass",
   "ms": 4941,
   "failure": ""
  },
  {
   "suite": "nav.spec.ts [chromium]",
   "title": "NAV-07 ?atlas редиректит на kq-atlas.pages.dev",
   "status": "pass",
   "ms": 1844,
   "failure": ""
  },
  {
   "suite": "ux.spec.ts [chromium]",
   "title": "UX-01 continue: Продолжить ведёт на сохранённую сцену",
   "status": "pass",
   "ms": 17253,
   "failure": ""
  },
  {
   "suite": "ux.spec.ts [chromium]",
   "title": "UX-02 overwrite: Начать при сейве спрашивает диалогом",
   "status": "pass",
   "ms": 6793,
   "failure": ""
  },
  {
   "suite": "ux.spec.ts [chromium]",
   "title": "UX-03 persist: размер шрифта сохраняется после reload",
   "status": "pass",
   "ms": 3755,
   "failure": ""
  },
  {
   "suite": "ux.spec.ts [chromium]",
   "title": "UX-04 chapter-chip: глава на сцене, нет на прологе",
   "status": "pass",
   "ms": 2028,
   "failure": ""
  },
  {
   "suite": "ux.spec.ts [chromium]",
   "title": "UX-05 sound-hint: 🔇 пульсирует до включения",
   "status": "pass",
   "ms": 1629,
   "failure": ""
  },
  {
   "suite": "ux.spec.ts [chromium]",
   "title": "UX-06 glossary: термин в сцене → модалка; экран словаря",
   "status": "pass",
   "ms": 1669,
   "failure": ""
  },
  {
   "suite": "ux.spec.ts [chromium]",
   "title": "UX-07 langpick: первый запуск показывает выбор языка",
   "status": "pass",
   "ms": 1559,
   "failure": ""
  },
  {
   "suite": "ux.spec.ts [chromium]",
   "title": "UX-08 map: после финала в меню есть карта, прыжок на сцену",
   "status": "pass",
   "ms": 2111,
   "failure": ""
  },
  {
   "suite": "web.spec.ts [chromium]",
   "title": "Б5 preview: /=200, /privacy.html=200, /_headers-статика, __test есть",
   "status": "pass",
   "ms": 2455,
   "failure": ""
  },
  {
   "suite": "web.spec.ts [chromium]",
   "title": "Б5.4 web-режим: platform=web, store_url резолвится в https Play",
   "status": "pass",
   "ms": 1813,
   "failure": ""
  },
  {
   "suite": "app-smoke.spec.ts [app]",
   "title": "В4 app-smoke: __test.platform()===\"app\"",
   "status": "pass",
   "ms": 766,
   "failure": ""
  },
  {
   "suite": "app-smoke.spec.ts [app]",
   "title": "В4 app-smoke: платформенный store_url = market:// (app-ветка)",
   "status": "pass",
   "ms": 707,
   "failure": ""
  },
  {
   "suite": "app-smoke.spec.ts [app]",
   "title": "В4 app-smoke: обход 111 сцен без ошибок в app-режиме",
   "status": "pass",
   "ms": 1321,
   "failure": ""
  },
  {
   "suite": "atlas.spec.ts [atlas]",
   "title": "ATL-01 tree→scene: back возвращает дерево, forward — сцену (кросс-домен)",
   "status": "pass",
   "ms": 2194,
   "failure": ""
  },
  {
   "suite": "atlas.spec.ts [atlas]",
   "title": "ATL-02 все элементы атласа — реальные ссылки с href",
   "status": "pass",
   "ms": 963,
   "failure": ""
  },
  {
   "suite": "atlas.spec.ts [atlas]",
   "title": "ATL-03 ctrl+click открывает сцену в новой вкладке",
   "status": "pass",
   "ms": 1315,
   "failure": ""
  },
  {
   "suite": "atlas.spec.ts [atlas]",
   "title": "ATL-04 grid⇄tree: view-параметр адресуем историей",
   "status": "pass",
   "ms": 1153,
   "failure": ""
  }
 ],
 "checklist": [
  {
   "g": "Меню",
   "n": "«Начать новую игру» — старт сцены 0",
   "cov": "TC-02b",
   "link": "/play?scene=0",
   "covShown": "TC-02b",
   "status": "pass"
  },
  {
   "g": "Меню",
   "n": "«Продолжить» при сейве — главная кнопка, ведёт на сохранённую сцену",
   "cov": "UX-01",
   "link": "/play",
   "covShown": "UX-01",
   "status": "pass"
  },
  {
   "g": "Меню",
   "n": "«Начать» при сейве → диалог перезаписи (Да/Нет)",
   "cov": "UX-02",
   "link": "/play",
   "covShown": "UX-02",
   "status": "pass"
  },
  {
   "g": "Меню",
   "n": "«Карта пройденного» появляется после финала, прыжок на сцену",
   "cov": "UX-08",
   "link": "/play",
   "covShown": "UX-08",
   "status": "pass"
  },
  {
   "g": "Меню",
   "n": "«Словарь» — экран терминов",
   "cov": "UX-06",
   "link": "/play?glossary",
   "covShown": "UX-06",
   "status": "pass"
  },
  {
   "g": "Меню",
   "n": "«Настройки» / «О проекте» — экраны адресуемы историей",
   "cov": "NAV-06",
   "link": "/play?settings",
   "covShown": "NAV-06",
   "status": "pass"
  },
  {
   "g": "Меню",
   "n": "Hero-арт меню (фон сцены 0, blur)",
   "cov": "TC-06",
   "link": "/play",
   "covShown": "TC-06",
   "status": "pass"
  },
  {
   "g": "Первый запуск",
   "n": "Экран выбора языка показывается один раз",
   "cov": "UX-07",
   "link": "/play",
   "covShown": "UX-07",
   "status": "pass"
  },
  {
   "g": "Сцена",
   "n": "Текст сцены рендерится без запрещённой разметки",
   "cov": "TC-03",
   "link": "/play?scene=12.1",
   "covShown": "TC-03",
   "status": "pass"
  },
  {
   "g": "Сцена",
   "n": "Арт сцены загружается (webp)",
   "cov": "TC-17",
   "link": "/play?scene=3.2",
   "covShown": "TC-17",
   "status": "pass"
  },
  {
   "g": "Сцена",
   "n": "Кнопки выборов ≥ tap-target, не вылезают за панель",
   "cov": "TC-16b",
   "link": "/play?scene=1",
   "covShown": "TC-16b",
   "status": "pass"
  },
  {
   "g": "Сцена",
   "n": "Переход по выбору; browser back возвращает prev-сцену",
   "cov": "NAV-03",
   "link": "/play?scene=1",
   "covShown": "NAV-03",
   "status": "pass"
  },
  {
   "g": "Сцена",
   "n": "Чип «Глава N» на сцене; нет на прологе",
   "cov": "UX-04",
   "link": "/play?scene=3",
   "covShown": "UX-04",
   "status": "pass"
  },
  {
   "g": "Сцена",
   "n": "Термин-ссылка → модалка глоссария",
   "cov": "UX-06",
   "link": "/play?scene=1",
   "covShown": "UX-06",
   "status": "pass"
  },
  {
   "g": "Сцена",
   "n": "🔊-тумблер + миг-подсказка до первого включения",
   "cov": "UX-05",
   "link": "/play?scene=1",
   "covShown": "UX-05",
   "status": "pass"
  },
  {
   "g": "Сцена",
   "n": "Нет горизонтального overflow (mobile/desktop)",
   "cov": "TC-16",
   "link": "/play?scene=27",
   "covShown": "TC-16",
   "status": "pass"
  },
  {
   "g": "Гибель",
   "n": "Первая гибель → поддерживающий хинт; вторая — без",
   "cov": "DTH-01",
   "link": "/play?scene=4.1",
   "covShown": "DTH-01",
   "status": "pass"
  },
  {
   "g": "Гибель",
   "n": "«Попробуй ещё раз» ведёт к главе",
   "cov": "DTH-02",
   "link": "/play?scene=4.1",
   "covShown": "DTH-02",
   "status": "pass"
  },
  {
   "g": "Гибель",
   "n": "Хинт персистентен после reload",
   "cov": "DTH-03",
   "link": "/play?scene=4.1",
   "covShown": "DTH-03",
   "status": "pass"
  },
  {
   "g": "Настройки",
   "n": "Темы ×4 (system/dark/light/cosmos) — кнопки переключают",
   "cov": "TC-21",
   "link": "/play?settings",
   "covShown": "TC-21",
   "status": "pass"
  },
  {
   "g": "Настройки",
   "n": "Слайдер размера шрифта — persist после reload",
   "cov": "UX-03",
   "link": "/play?settings",
   "covShown": "UX-03",
   "status": "pass"
  },
  {
   "g": "Настройки",
   "n": "Слайдер громкости + выбор мелодии",
   "cov": "manual",
   "link": "/play?settings",
   "covShown": "manual",
   "status": "manual"
  },
  {
   "g": "Настройки",
   "n": "Селект языка: смена ru→en без потери позиции",
   "cov": "TC-07",
   "link": "/play?settings",
   "covShown": "TC-07",
   "status": "pass"
  },
  {
   "g": "Финал",
   "n": "Сцена 27.3 + кнопка оценки → Google Play (web: https)",
   "cov": "Б5.4",
   "link": "/play?scene=27.3",
   "covShown": "Б5.4",
   "status": "manual"
  },
  {
   "g": "О проекте",
   "n": "Ссылки web/privacy открываются; privacy.html = 200",
   "cov": "Б5",
   "link": "/privacy.html",
   "covShown": "Б5",
   "status": "pass"
  },
  {
   "g": "Навигация",
   "n": "Refresh на ?scene=N восстанавливает сцену",
   "cov": "NAV-05",
   "link": "/play?scene=13",
   "covShown": "NAV-05",
   "status": "pass"
  },
  {
   "g": "Навигация",
   "n": "?atlas редиректит на kq-atlas.pages.dev",
   "cov": "NAV-07",
   "link": "/play",
   "covShown": "NAV-07",
   "status": "pass"
  },
  {
   "g": "Атлас",
   "n": "tree→scene: back/forward истории; grid⇄tree",
   "cov": "ATL-01",
   "link": "https://kq-atlas.pages.dev/",
   "covShown": "ATL-01",
   "status": "pass"
  },
  {
   "g": "Языки ×10",
   "n": "Обход 111 сцен без ошибок на каждом языке",
   "cov": "TC-06",
   "link": "/play?scene=1",
   "covShown": "TC-06",
   "status": "pass"
  },
  {
   "g": "Языки ×10",
   "n": "CJK-глифы не tofu (zh/ja)",
   "cov": "TC-17",
   "link": "/play",
   "covShown": "TC-17",
   "status": "pass"
  },
  {
   "g": "a11y",
   "n": "axe: 0 critical/serious (dark/light/choices/экраны)",
   "cov": "TC-21",
   "link": "/play",
   "covShown": "TC-21",
   "status": "pass"
  },
  {
   "g": "Оффлайн",
   "n": "Web: бандл self-contained; APK: все ассеты внутри",
   "cov": "A-02",
   "link": "/play",
   "covShown": "A-02",
   "status": "pass"
  },
  {
   "g": "APK",
   "n": "platform=app: store_url=market://, обход 111 сцен",
   "cov": "В4",
   "link": "/play",
   "covShown": "В4",
   "status": "pass"
  }
 ]
}