--- name: ui-walkthrough-cdp description: "用户可见波次收口前在 mini-desktop 用 CDP 做真 UI 走查(studio 创作/试玩/发布、admin 审核台)时使用:逮编排器旁路掩盖的真后端缺陷,含 CDP 七坑、免登录 localStorage 注入只读走查、真实访问 origin 取证红线。" --- # 真 UI 走查配方(CDP on mini-desktop) > **为什么必须真 UI 走查**:编排器/批跑/curl 全绿 ≠ 真人 UI 能用。编排器旁路曾掩盖 2 个真后端发布缺陷(`currentVersionId` 生成后未回写 / `getZones` 空桩),**真 UI 走查是逮这类缺陷的唯一手段**(HJ-FE-WALK-001)。链路①(创作→生成→预览)与链路②(发布→审核→入 feed)均用本配方闭合。 > 工具已入仓 `git show 6d2f8789^:docs/agent-specs/2026-06-09-agent-loop-v1/orchestrator/`(已删,git 可查):`player_cdp.py`(游戏试玩 CDP,含 BrowserPool)/ `admin_walk.py`(admin 最小 CDP 客户端:new_target/send/evaljs/nav/shot)/ `run_walk.sh`(chrome 生命周期)/ `probe_bridge_channel.py`(宿主↔iframe 双边信道探针)。 ## 1. 大原则 - **chrome 一律在 mini-desktop 本地起**(6c6g 连续 exit 144,该路线已作废);被测前端/后端同机,全 localhost。 - 浏览器内访问前端**优先 `http://localhost:4173`**(安全上下文,省心)。**经 Tailscale IP/明文 http 现也可用**:宿主 manifest 校验的 `crypto.subtle` 在非安全上下文缺失,已补纯 JS SHA-256 兜底(2026-06-17 `4e486f60`),不再「走 IP 永远 demo 兜底」。 - **红线(此坑血泪)**:上条「IP→demo 兜底」曾被当成「走查改用 localhost 规避」记着,却没意识到**创始人/真实用户正是经 IP 访问 → 线上游戏一直「加载失败」**(demo 退化后 W-CLEAN 占位发 game_error)。教训:**走查环境 localhost = 安全上下文,会系统性掩盖「上下文依赖缺陷」(`crypto.subtle`、`isSecureContext` 门控特性等);用户可见 UI/e2e 走查必须另跑一遍真实访问 origin(IP/明文 http),不能只在 localhost 取证。当走查需要一个 workaround 才能避开某失败时,先问「真实用户会不会也撞上同一失败」。** - chrome 起后必先 `curl -s localhost:9222/json/version` 验活再用。 - **禁从 lili-mac 浏览器 / superpowers-chrome 走查 staging(2026-06-19 实证两次假 502)**:① lili-mac 有系统代理(`HTTPProxy 127.0.0.1` + WPAD `wpad.dat`),Chrome 走代理够不到 Tailscale `100.64.0.7` → 全端 **502 假象**;而 curl 直连绕过代理 → 200,故出现「curl/ssh 显 UP 但 Chrome 见 502」的矛盾,**判据=ssh mini-desktop localhost 200 + 进程活 即为真态**。② superpowers-chrome 持久实例会复用**陈旧会话**(把重部署前的旧 502 截图当现态报,认 `session-<旧id>` 复用即是)。**正解永远是本配方(mini-desktop localhost chrome,无代理、服务+浏览器同机)**;lili-mac CDP 仅个人快验、绝不作 e2e 证。 ## 2. CDP 七坑(Chrome 146 实证,逐个血泪) 1. root 运行必须 `--no-sandbox`; 2. **`--no-zygote` 必加**(否则 `Failed global descriptor lookup` → 渲染进程崩 → 空 DOM); 3. WS 握手 403 → 启动加 `--remote-allow-origins=*`,或 websocket-client 用 `suppress_origin=True`; 4. Chrome 111+ 建 target 用 **`PUT /json/new?url`**(GET 不行); 5. **ssh 直启 chrome 必死**(继承 ssh 会话 fd,断连即杀,nohup/setsid 救不全)——正解=整个流程写成 mini 上的脚本,脚本内 `exec >/tmp/walk_out.txt 2>&1` 重定向自身输出,`nohup bash run_walk.sh &` 启动即返回,远端 until-loop 轮询输出文件; 6. 单文件传 mini:`cat local | ssh mini-desktop 'cat >/tmp/x'`; 7. 轮询禁 `sleep N && cmd`(会被拦),用远端 `for i in ...; do grep -q DONE file && break; sleep 2; done`。 ## 3. studio 走查(创作/试玩/发布链路) - 入口 `:4173`;受控 textarea 填值 = **原型 setter + dispatch input 事件**(触发 Vue v-model,直接赋值无效); - 点按钮用精确文案匹配防子串误中(`includes('满意')` 会误中「不满意,重做」); - 截图 `cat` 管道拉回本地用 Read 查看;游戏试玩复用 `player_cdp.play()`(批跑必须 `continue_on_demo=False`)。 > 这批脚本(`player_cdp.py`/`admin_walk.py` 等)随 orchestrator/ 目录删除,仅存 git(取回:`git show 6d2f8789^:orchestrator/<脚本名>` 落盘后用);游戏画布试玩取证现行改用在树的 `game-runtime/games/_wg1-gen/_shared/play.cdp.cjs`(见 [`game-e2e-cdp-harness.md`](./game-e2e-cdp-harness.md))。 - **localStorage 注入 token 过 `requiresAuth` 走查(免登录直达受保护页只读走查)——2026-06-16 Lane E G0 实证**:只想**只读核实**一个 `meta.requiresAuth` 的页面(如创作页),不必走完整登录表单。先后端拿一枚有效 token(如免短信登录端点 / `test1` token),再用 CDP 在**目标 origin 下**注入 localStorage 后再导航: ```python evaljs("localStorage.setItem('ACCESS_TOKEN', '')") # key 名以前端约定为准(查 utils/auth);有的还需 refreshToken/tenantId nav("http://localhost:4173/<受保护路由>") # 注入后再导航,路由守卫读 token 即放行 ``` 坑:① **必须在同 origin 已开的 target 里 setItem**(localStorage 按 origin 隔离,先 `nav` 到 `:4173` 任一页建立 origin 再 set,空白页 set 不生效);② key 名/附加字段以前端真实约定为准(错名=守卫仍判未登录弹回登录页);③ 仅供**只读走查**,真链路验收(创建→生成→发布)仍要走真登录路径。 ## 4. admin 走查(审核台,链路②) - 接 staging:`game-admin/.env.local` 两行——`VITE_BASE_URL=http://:48080` + **`VITE_APP_WANXIANG_MOCK=false`(不关全是假数据,最易漏)**;chrome 与 admin 同机时 BASE 可 localhost。 - 登录:租户「芋道源码」/ admin / admin123(表单预填,点登录即过,无验证码); - 审核台:静态路由 `/wanxiang/review`(登录后侧栏「绘境运营→项目审核」恒在);「通过」= `POST /admin-api/project/review {decision:1}` → 同事务 publish; - 验收锚点:**UI 队列数变化 + DB 双实证**(`game_project.status=4` + `game_feed_rank` 新行)。 ## 5. 信道探针(回归排查常备) 宿主↔iframe postMessage 断裂用 `probe_bridge_channel.py` 取证:四锚点 + 判别实验(文档代际/iframe 重建监视/存活代理注入),v-for ref 数组案(host→game 全断)即由它锁凶。修复类波次收口前跑一轮防回归。 > 这批脚本(`probe_bridge_channel.py` 等)随 orchestrator/ 目录删除,仅存 git(取回:`git show 6d2f8789^:orchestrator/<脚本名>` 落盘后用);游戏画布试玩取证现行改用在树的 `game-runtime/games/_wg1-gen/_shared/play.cdp.cjs`(见 [`game-e2e-cdp-harness.md`](./game-e2e-cdp-harness.md))。