Просмотр исходного кода

Merge remote-tracking branch 'origin/cfclub' into cfclub

Agnes Agent 5 дней назад
Родитель
Сommit
f8b617943c

+ 163 - 0
docs/tdd/orchestration/report-dev-2026-11-07-1436.md

@@ -0,0 +1,163 @@
+# 研发负责人工作汇报(研发负责人 · 闭环巡检 · 2026-11-07 14:36 轮)
+
+> **说明**:本报告依据 `tdd-orchestrator` 研发负责人模板填写,数据来自本轮(`2026-11-07 14:36`)只读核查(`--status` / `--changes --days 1` / `--clean-check`)、git 真理核验(`git fetch` + `git merge-base --is-ancestor`)与远端 `docs/tdd/**` 文档(`git show origin/cfclub:...` 复核)。
+> **角色纪律**:本轮为只读核查 + 主理人裁决跟进 + 汇报汇总;未代写实现、未改业务代码、未动 `.tdd/state.json` 与他人 WIP;未用 `--auto` / `--auto --dry-run`(已知证据误配缺陷)。
+
+---
+
+## 基本信息
+
+| 项目 | 内容 |
+|------|------|
+| 汇报人 | 研发负责人(tdd-orchestrator · 自动化闭环巡检) |
+| 汇报周期 | 2026年11月07日 14:36(本轮,文件名时间戳全局唯一) |
+| 负责模块/单元范围 | cfc 全量(scope 空) |
+
+---
+
+## 一、本轮工作数据汇总
+
+### 1.1 问题接收与处置
+
+| 指标 | 数量 | 填写说明 |
+|------|------|----------|
+| **收到的测试反馈问题数*** | `5` 个 | 本轮从测试渠道(`docs/tdd/test/blockers.md` 远端版)接收到的需研发/运维处理的新增项:**B-41**(langgraph flaky 固化,测试侧自处置)/ **B-42**(远程派发通道不可用,测试/运维)/ **B-43**(线上 LangGraph 部署包落后·问卷生成 500·B-34 复发,研发/运维)/ **B-44**(管理端驾驶舱「孩子总数」文案与统一用户模型冲突,研发)/ **B-45**(2 条前端用例只在工作区未入库,测试/流程)。均为既有项的衍生/复开,无新增实现缺口键 |
+| **已解决问题数*** | `0` 个 | 本轮研发侧新闭环 0;B-37(`sleep.config.tags` blank-key)由测试侧 09:30 复验 **10/10 绿** 已解除,但 `--mark-green` 属测试角色动作(TR-11 在途),研发不代确认,状态机维持 YELLOW;B-34/B-35 前轮已解除、B-34 本轮因线上未发布被重开为 B-43 |
+| **待解决研发问题数** | `8` 个 | ① **B-31**(P0 安全,application.yml 7 处明文凭据);② **B-39**(P0,cfc-backend `pom.xml` 默认 `skipTests=true` 假绿);③ **B-40**(P0,CI 4 个软门禁 + integration 包被排除);④ **B-42**(派发通道不可用,主理人/运维);⑤ **B-43**(LangGraph 未发布到 251);⑥ **B-44**(驾驶舱文案);⑦ **B-36**(根级 23 例全 skip);⑧ **B-38**(26 红类/69 红例归因,含 platform_fee 列越界真实候选) |
+
+### 1.2 任务分发与会话管理
+
+| 指标 | 数量 | 填写说明 |
+|------|------|----------|
+| **分派任务数*** | `0` 个 | 本轮 0 派发:① 能力键 TDD 池空(⏳ 0,账本 0 pending / 0 dispatched);② `--changes --days 1` **0 新增能力键**(0→0),O-12 已裁决「不建伪实现任务」,不满足 `--add-task` 溯源铁律;③ 本环境无可达 opencode 派发通道(见 B-42 根因) |
+| **恢复智能体会话数*** | `0` 个 | 无中断会话需恢复(hub.py 最近可达快照 5 节点 250 会话全部 idle,busy=0 interrupted=0) |
+| **当前在用会话数** | `0` 个 | 本环境无可达 opencode 会话(config.json 会话 id 为占位符;B-42 实测 189 节点可达但鉴权/remote_dir 错配、251 节点 9000/4090 closed) |
+| **会话平均利用率** | `N/A` | 池空 + 派发通道不可用 → 本轮无派发对象 |
+
+> **R2「不留空闲」核验(核心职责——确保所有会话都分派了开发任务)**:
+> - 5 节点 250 会话全 idle(busy=0 interrupted=0,配额 0/3),**无在途 / 0 dispatched / 0 pending**。
+> - **结论**:本轮「确保所有会话分派开发任务」**无法落地**,根因 = ① 任务池空(无开发任务可派)+ ② 派发通道在本环境不可用(**B-42**:auth 未配置 + `remote_dir` 错配本地 cwd↔远端 `c:/code/cfc` + opencode 版本错配 2.0.31↔2.2.5 + 251 节点 9000/4090 closed)。非「会话空闲未派」,而是**无任务可派 + 无通道可达**。待主理人/运维修复 B-42 通道 + O-7/O-12 契约批次前置后,方可实质派发填补 226 SC 缺口。
+
+### 1.2b 各会话当前工作状态列表
+
+> 沿用最近可达快照(2026-11-07 09:54 巡检,5 节点 250 会话全 idle,busy=0 interrupted=0):
+
+| 会话标签 | 会话 ID | 当前状态 | 当前任务 | 静默/空闲时长 | 工作目录 | 备注 |
+|----------|---------|----------|----------|---------------|----------|------|
+| lan-189 | (50 会话) | `idle` | — | 长 | 干净 | 配额 0/3,剩余 3;B-42 实测 TCP 4090 OPEN 但鉴权/remote_dir 错配 |
+| lan-204 | (50 会话) | `idle` | — | 长 | 干净 | 配额 0/3,剩余 3 |
+| local-lan-4090 | (50 会话) | `idle` | — | 长 | 干净 | 配额 0/3,剩余 3 |
+| mes-local-45278 | (50 会话) | `idle` | — | 长 | 干净 | 配额 0/3,剩余 3;O-11 已会话执行完毕 |
+| pub-iwintrue | (50 会话) | `idle` | — | 长 | 干净 | 配额 0/3,剩余 3;CFC 部署/契约核对主通道,本环境不可达 |
+
+> **任务账本**(.workbuddy/tasks.json):34 条 = 33 done + 1 failed(S-cfc-20 tokens=0),0 pending / 0 dispatched。
+
+### 1.3 变更感知与新任务建立
+
+| 指标 | 数量 | 填写说明 |
+|------|------|----------|
+| **感知需求/设计变更数*** | `6` 提交 / `238` 文件 | `--changes --days 1` + 远端复核:设计文档 94(EVD-10~15 契约、商城价格排序需求等)+ 测试用例文档 16(测试负责人 63~69 轮 + 1325 轮浏览器自动化)+ 其他 128;**无新增能力键(0→0),变化集中在既有键** |
+| **新增入池任务数*** | `0` 个 | `--add-task` 需合法 `unit.domain.action` + `--requirement`/`--design` 溯源,本轮 0→0 无新键;O-12 裁决「不建伪实现任务」 |
+| **既有键状态流转数** | `0` 次 | 状态机自 06:16 轮稳定(DONE12 / GREEN9 / YELLOW1 / RED0),本轮无阶段流转 |
+
+### 1.4 本轮 git 提交与推送证据(真理证据)
+
+> 本实例本轮为只读核查 + 汇报轮;以下为本轮**核验确认的推进证据**(皆已推 origin/cfclub,非本实例产出;测试团队 13:30 后 6 提交全为测试侧动作,无研发实现提交)。
+
+| 提交 sha | 关联任务/能力键 | 阶段 | 触及文件类型 | 目标分支 | 已推送远端 | 备注 |
+|----------|-----------------|------|--------------|----------|------------|------|
+| `d21a887cc` | Merge remote-tracking → cfclub | 维护 | 合并 | cfclub | 是 | IN CHAIN ✅(本轮基底) |
+| `504c3e5eb` | 测试 13:25 轮:浏览器自动化 11/11 全绿 + B-34 复发(B-43) + B-42 根因 + B-44 | 测试证据 | 测试(e2e-live)+ 台账 | cfclub | 是 | IN CHAIN ✅ |
+| `dab532903` | 测试第11轮:登记 B-41 + B-42 | 台账 | 台账 | cfclub | 是 | IN CHAIN ✅ |
+| `e03671c14` | 测试第11轮:闭环报告+台账刷新(3765 例 +632) | 台账 | 台账 | cfclub | 是 | IN CHAIN ✅ |
+| `d30c68dc0` | 测试 13:25 续:商城价格排序契约 4 例全绿 + B-45 | 测试证据 | 测试(shop-sort-api)+ 台账 | cfclub | 是 | IN CHAIN ✅ |
+| `dceeaaeaf` | 游客域 SC-G-01/03/04/05/07/08/09/10/11/12(10 SC) | GREEN 证据 | 测试(71 例三类流契约) | cfclub | 是 | IN CHAIN ✅,sc-key-map 已登记跑通(累计 15/241) |
+
+### 1.5 状态机阶段分布与部署状态
+
+| 阶段 | 键数 | 备注 |
+|------|------|------|
+| UNTRACKED | `0` | 已发现未入池 |
+| RED | `0` | 无待开发 |
+| YELLOW | `1` | sleep.config.tags(B-37 已 10/10 绿,待测试侧 `--mark-green`,TR-11 在途;研发不代确认) |
+| GREEN | `9` | admin.auth.login / health.report.upload / health.report.createReport / dan.assessment.getActiveMaterial / dan.report.parseFormat / task.history.view / growth.history.view / achievement.grid.fiveCol / langgraph.questionnaire.generate |
+| DONE | `12` | auth.guest.jwtGuard / auth.guest.wechatLogin / dimension.registry.getTree / dimension.registry.mapLegacyCode / family.familyMembers.addMember / family.familyMembers.listMembers / family.parent.switchRole / guide.trainingPlan.getPlanDetail / guide.trainingPlan.getPlanList / reward.cfEstimate.expectedCf / task.parent.createTask / task.parent.review |
+
+| 部署项 | 值 | 说明 |
+|--------|-----|------|
+| 本轮部署次数 | `0` 次 | 本实例未派发部署 |
+| 最新 `last_deployed` | `292087771` | .tdd/deploy_state.json:confirmed 20 + pending 1(292087771 done at 11-06 19:22);⚠ tasks.json 顶层 `last_deployed=None`(其他槽位重写时字段丢失,可从 deploy_state 推导回填) |
+| GREEN 部署链验证 | 部分通过 | **部署缺口 8+ 提交**在 292087771 后未确认 testing 部署(41f1fc93/ba6b48d2/30ae86d7d/9628df3d9/1b15b67d2/def9c83a3/895e500e1/504c3e5eb 等),登记待部署侧核对;测试侧已在干净 worktree 旁路复验主要键,不阻断 GREEN 证据 |
+
+### 1.6 数据源健康度
+
+| 数据源 | 读取键数 | 是否正常 | 备注 |
+|--------|----------|----------|------|
+| 需求侧(⏳ wish) | `0` | 正常 | 目标池 0 键,非静默失效 |
+| 契约常量(contract) | `0` | 正常 | CFC 当前启用发现/跟踪模式,契约常量仅作标注用 |
+| 测试用例引用(test_case_map) | `0` | 正常 | — |
+| 执行结果回填(case_results) | `0` | 正常 | — |
+
+---
+
+## 二、下一步需要研发处理的事项
+
+| 序号 | 事项描述 | 依赖/前置条件 | 预估完成时间 | 优先级 | 状态 |
+|------|----------|---------------|--------------|--------|------|
+| 1 | **B-42 派发通道修复**(auth 配置 + `remote_dir` 对齐 `c:/code/cfc` + opencode 版本对齐 2.0.31↔2.2.5 + 251 节点 9000/4090 开放) | 主理人/运维在具备 opencode 会话的主机配置 `machine.json` 凭据与节点可达性 | 运维就绪后 1 轮 | **P0** | 待启动(阻塞=派发通道,直接阻断「确保会话分派开发任务」) |
+| 2 | **B-43 LangGraph 重新发布到 251**(问卷生成 500·B-34 复发) | 运维重新构建并发布 `cfc-langgraph` 到 `ai.etotem.com.cn` | 运维就绪后 1 轮 | **P0** | 待启动(阻塞=线上 AI 生成能力) |
+| 3 | **B-39** cfc-backend `pom.xml` 默认 `skipTests` 改 false + CI 守卫「0 XML 即失败」 | 研发改 `:28` 默认 + `.gitlab-ci.yml` 加守卫 | 11-08 前 | **P0** | 待启动 |
+| 4 | **B-40** CI 分级门禁(blocking / observed)+ integration 包按白名单转 blocking | 研发改 `.gitlab-ci.yml` | 11-08 前 | **P1** | 待启动 |
+| 5 | **B-31** application.yml 7 处明文凭据改为 `${ENV}` + 轮换泄露凭据 | 运维生成新凭据 + 环境变量注入 | 运维就绪后 1 轮 | **P0** | 待启动(阻塞=安全) |
+| 6 | **B-44** 驾驶舱「孩子总数」文案改为「未开通登录的子成员」+ TC-A-U04 增断言 | 研发改文案(统一用户模型口径) | 11-08 前 | **P1** | 待启动 |
+| 7 | **B-45** `cfc-frontend/__tests__/specs/` 入库(团队邀请分享 / 优惠券已领取按钮) | 研发/资产作者 `git add` + 提交 | 11-07 当日 | **P2** | 待启动(测试侧不代提交) |
+| 8 | **226 SC 实现缺口**(15/241 已跑通):EVD-3~15 契约已合入 origin,剩余 SC 需按 O-7/O-12 节奏「立契约→用例齐备→入池派发」 | ① B-42 通道修复;② O-7 契约补建批次前置 | 通道修复后滚动 | **P0**(战略) | 待启动(前置阻塞=派发通道 + 契约批次) |
+
+---
+
+## 三、阻塞与风险
+
+| 阻塞项 | 影响范围 | 阻塞类型 | 处置进展 | 需要协调 |
+|------|----------|----------|----------|----------|
+| **B-42 派发通道不可用** | 全局:后端 2367 @Test + Playwright 547 spec 共 2914 脚本化用例零实测;且无法将 226 SC 开发任务派发给 opencode 会话 | 测试/运维 | 13:25 根因收敛:189 在线但 auth 未配 + remote_dir 错配 + 版本错配;251 9000/4090 closed | **主理人/运维**(配置凭据 + 节点可达 + 版本对齐) |
+| B-43 LangGraph 未发布(B-34 线上复发) | 线上 `/api/family/questionnaire` AI 生成 100% 失败 | 研发/运维 | B-34 本地已修但部署包落后,重开为 B-43 | 运维(重新发布) |
+| B-39 / B-40 测试假绿 + 软门禁 | 任何标准 Maven/CI 接入得到假绿;CI 绿灯≠可发布 | 研发 | 待研发改 pom 默认 + CI 分级 | 研发 |
+| B-31 明文凭据 | application.yml 7 处可读凭据 | 研发/运维 | O-10 已裁决 `${ENV}` 占位 + 轮换 | 运维(新凭据注入) |
+| 226 SC 实现缺口 | 用户场景 226/241 未跑通 | 研发 | EVD-3~15 契约已合入;O-7/O-12 节奏待派发通道修复后滚动 | 主理人(O-7 授权) |
+| B-36 根级 23 例全 skip | 接口层验收归零 | 测试/环境 | 待 seed 账号 + globalSetup 自动登录取 token | 测试侧 |
+
+---
+
+## 四、下轮工作规划
+
+| 计划项 | 预计完成时间 | 前置依赖 | 涉及会话标签 | 备注 |
+|--------|--------------|----------|--------------|------|
+| B-42 通道修复后,按 O-7 节奏滚动派发 P0 共 133 SC(任务22/商城18/家庭15/五维12) | 通道修复后 1 轮起 | B-42 派发通道可用 | pub-iwintrue / mes-local-45278 | 填补 226 SC 缺口的第一步 |
+| sleep.config.tags `--mark-green` 收口 | 测试侧复验后 | TR-11 测试侧 | — | 研发不代确认 |
+| B-39/B-40/B-31 代码侧修复 | 11-08 前 | 研发改 pom/CI/yml | — | 消除测试假绿与凭据泄露 |
+
+---
+
+## 附录 A:主理人裁决落地追踪
+
+| 裁决号 | 主题 | 落地状态 |
+|--------|------|----------|
+| O-1~O-11 | 前序裁决 | 已全部生效 |
+| **O-12** | 契约核对批次实现缺口(EVD-10 规划师域 3 SC + EVD-11 平台运营域 5 SC) | **已裁决生效**(2026-11-07 13:30):G-1/G-2 走端点组合复用补证不建伪任务;G-3 先立契约+用例齐备再入池;A-04 判已下线待产品确认;A-14/19/20/21 回传产品确认验收 |
+
+## 附录 B:SC 覆盖台账
+
+- sc-key-map 唯一 SC ID = **241** ✓(O-8 已归一,DEV_GOAL 顶层目标 241)
+- 累计已跑通:**15/241**(O-3 收口 SC-G-02/G-06/P-03/P-10/P-11 + 2026-11-07 游客域补证 SC-G-01/03/04/05/07/08/09/10/11/12)
+- EVD-14(17 SC)/ EVD-15(8 SC)契约核对产物已合入 origin/cfclub,待测试侧补三类流证据后纳入累计
+- 剩余 226 SC:契约主体已合入(EVD-3~15),实现入池派发待 B-42 通道修复 + O-7/O-12 节奏
+
+## 附录 C:测试团队协同状态(远程测试团队)
+
+- 本轮(13:30 后)测试团队 6 提交全为测试侧动作:浏览器自动化首次落地 **11/11 全绿**(admin5 + frontend6);cfc-langgraph pytest **51 passed / 12 skipped / 0 failed**(B-41 flaky 连续第 2 次满跑未复现);干净基线 **286 类 / 2375 例 / 95.16%**;台账 3765(+632);新增 15 条带 `TC-` 锚点用例(B-30 锚点缺口部分闭环)
+- 测试团队反馈 **B-41~B-45**:B-41(langgraph flaky,测试侧自处置)/ B-42(派发通道,研发/运维)/ B-43(LangGraph 未发布,研发/运维)/ B-44(驾驶舱文案,研发)/ B-45(前端用例未入库,测试/流程)
+- 「与远程测试团队确保所有测试用例通过」进展:测试侧通过率稳定 95%+,主要残留为环境红(B-13)、A 类装置漂移(B-18 同族)、契约/实现不一致(B-38)与根级 skip(B-36);**B-39/B-40 假绿与软门禁是测试结论可信度的首要威胁,需研发优先修复**
+
+---
+
+*本报告单文件精确入库(hash-object + 临时索引法,不触碰主工作树 index),message 不含能力键名防误配。*

+ 173 - 0
docs/tdd/orchestration/report-test-2026-11-07.md

@@ -0,0 +1,173 @@
+# 测试负责人工作汇报(闭环自动化测试 · TDD 测试负责人)
+
+> 周期:2026-11-07 12:57 GMT+8(第 11 轮 / 11-07 第 4 轮)
+> 汇报人:`cfc-tester`(测试负责人,本会话/本运行环境)
+> 档位:review 档(本地可执行部分实跑;远程派发部分因本环境无 opencode 通道标记未实测)
+> 配置拓扑(来自 `.tdd/config.json`):浏览器自动化 `web-4091`(本机:4091) + `web-189-4090`(192.168.16.189:4090) 派发 `cfc.iwintrue.com/admin`、`/frontend`;`langgraph` 与后端 JUnit 派发至 `opencode.iwintrue.com`(192.168.16.251)。
+
+---
+
+## 基本信息
+
+| 项目 | 内容 |
+|------|------|
+| 汇报人 | `cfc-tester`(测试负责人) |
+| 汇报周期 | 2026-11-07 ~ 2026-11-07(第 11 轮 / 11-07 第 4 轮) |
+| 负责模块/单元 | 全栈测试闭环巡检:cfc-frontend / cfc-web(Jest)、cfc-langgraph(pytest)、cfc-backend(JUnit,待派发)、Playwright 浏览器自动化(待派发) |
+
+---
+
+## 一、本轮工作数据汇总
+
+### 1.1 测试用例建设
+
+| 指标 | 数量 | 填写说明 |
+|------|------|----------|
+| **本轮新增测试用例数*** | `0` 个(测试负责人侧) | 本轮为验证执行巡检,未新建用例;但检测到研发侧新增:Playwright +104 spec、Java @Test +159(见附录 C / 1.6)。新增用例的 RED/契约补登属研发职责,已提示。 |
+| **本轮验证执行测试数*** | `466` 个 | FE 313 + web 90 + langgraph 63 collected(含 12 skipped)= 466 |
+| **用例通过率** | `97.4%`(454/466,含跳过);`100%`(454/454,不计跳过) | 本环境实跑部分全绿;后端/Playwright 2914 脚本化用例零实测(见 1.2/三) |
+
+### 1.2 会话与任务分发
+
+| 指标 | 数量 | 填写说明 |
+|------|------|----------|
+| **分派任务数*** | `0` 个 | 本运行环境无 opencode 客户端,且 `192.168.16.189` 连接拒绝不可达,无法向 `web-4091`/`web-189-4090`/`opencode.iwintrue.com` 派发 |
+| **恢复智能体会话数*** | `0` 个 | 无会话可恢复 |
+| **当前在用会话数** | `0` 个(本环境) | 派发类会话均在本环境不可达/无客户端 |
+
+### 1.2b 各会话当前工作状态列表
+
+| 会话标签 | 会话 ID | 当前状态 | 当前任务 | 静默/空闲时长 | 工作目录 | 备注 |
+|----------|---------|----------|----------|---------------|----------|------|
+| web-4091(本机:4091) | — | `unknown`(本环境无客户端) | 浏览器自动化 cfc.iwintrue.com/frontend | — | 不干净(无法核验) | 配置要求本机派发,本运行环境无 opencode 客户端 |
+| web-189-4090(192.168.16.189:4090) | — | `unreachable` | 浏览器自动化 | — | — | 192.168.16.189 TCP 连接拒绝,节点不可达 |
+| opencode.iwintrue.com(192.168.16.251) | — | `unknown`(仅 HTTP 可达) | JUnit 后端 + langgraph pytest | — | — | HTTP 404 可达但无 opencode 派发通道;langgraph 改为本环境本地实跑 |
+| 本地 Jest/pytest | — | `done` | FE/web/langgraph 单元 | — | 干净 | 本环境本地执行,结果见 1.1 |
+
+> 状态判定:本运行环境仅能执行本地 Jest/pytest;远程浏览器自动化与后端 JUnit 派发通道不可用,按 TDD 纪律标记「未实测」,**不写为通过**。
+
+### 1.3 问题处置
+
+| 指标 | 数量 | 填写说明 |
+|------|------|----------|
+| **新测试发现问题数*** | `2` 个 | ① langgraph 历史 flaky 用例本轮转绿但需研发固化防回弹;② 本环境无派发通道致后端 2367 + Playwright 547 共 2914 脚本化用例零实测 |
+| **已解决测试相关问题数*** | `0` 个 | 本轮仅验证执行,未改实现/测试代码 |
+| **待解决测试相关问题数** | `4` 个 | P0:远程派发通道不可用;P0:langgraph flaky 固化;P1:795 文档用例零执行;P1:SRS 池未启用 + 异常流零执行 |
+
+### 1.4 本轮 git 提交与推送证据(真理证据)
+
+| 提交 sha | 关联任务/能力键 | 阶段 | 触及文件类型 | 目标分支 | 已推送远端 | 备注 |
+|----------|-----------------|------|--------------|----------|------------|------|
+| 本轮无 RED/YELLOW/GREEN 阶段提交 | — | — | — | — | — | 本轮为验证执行巡检,未改动实现/测试源码 |
+| 本汇报 + 台账刷新提交 | report-test-2026-11-07 / inventory / automation-status | 团队共享文档 | docs/tdd/*.md | cfclub | 否(待推送,本地落后远程 5 提交) | 见第五节推送结果 |
+
+> 用 `git merge-base --is-ancestor <sha> origin/cfclub` 判定:本环境本地分支落后 `origin/cfclub` 5 个提交,且工作区含他人未提交改动,无法安全快进推送(见第五节)。
+
+### 1.5 状态机阶段分布(全局进度)
+
+| 阶段 | 键数 | 备注 |
+|------|------|------|
+| UNTRACKED | 0 | — |
+| RED | 0 | — |
+| YELLOW | 0 | — |
+| GREEN | 5 | `.tdd/state.json` 内 5 个手工登记键均为 GREEN(health.report.createReport / dan.assessment.getActiveMaterial / guide.trainingPlan.getPlanDetail / admin.auth.login / health.report.upload) |
+| DONE | 0 | — |
+
+> 注:state.json 仅覆盖 5 个手工登记键;后端 handler(1943)、文档用例(784+11)等以 discover/inventory 台账为准,未纳入 state.json。
+
+### 1.6 数据源健康度
+
+| 数据源 | 读取键数 | 是否正常 | 备注 |
+|--------|----------|----------|------|
+| 需求侧(SRS 池) | `0` | 异常/未启用 | `srs_section` 标注「CFC 未启用」,0 锚点;目标②缺口根因之一 |
+| 契约常量(contract) | 正常 | 正常 | API_REFERENCE 扫描正常 |
+| 测试用例引用(test_case_map) | `3765` | 正常 | 结构化 784 + Playwright 547 + Python 67 + Java @Test 2367 + 场景 11 |
+| 执行结果回填(case_results) | `795` 文档用例 已执行=0 | 异常/缺口 | 文档用例/场景零执行验证;异常流 TC-*-E/-P 仅文档标注零执行 |
+
+---
+
+## 二、向研发提出的改进建议
+
+| 序号 | 建议标题 | 问题描述 | 根因分析 | 建议方案 | 优先级 | 状态 |
+|------|----------|----------|----------|----------|--------|------|
+| 1 | 固化 langgraph flaky 用例 | `test_analysis_graph_offline_run_returns_interpretation` 历史满跑失败、隔离单跑 PASS | 顺序相关/模块级 state 污染(疑似 chromadb 原生依赖 + 模块级 fixture) | mock chromadb + 确定性 fixture + 单文件隔离运行;CI 中固定 `--forked` 或用例级隔离 | P0 | 待评估 |
+| 2 | 接入远程派发通道 | 后端 JUnit 2367 + Playwright 547 本环境零实测 | 本运行环境无 opencode 客户端;192.168.16.189 不可达 | 在具备 opencode 会话(web-4091 / web-189-4090 / opencode.iwintrue.com)的主机运行本自动化,或为本环境配置派发凭证与节点可达性 | P0 | 待评估 |
+| 3 | 启用 SRS 池 + 闭环 795 用例执行 | 795 文档用例/场景零执行验证;异常流零执行 | SRS 池未启用 + 无执行级验证链路 | 启用 SRS 需求侧锚点;将 784 结构化用例接入 CI 实跑;补充异常流 TC-*-E/-P 执行级校验 | P1 | 待评估 |
+| 4 | 修 test_inventory 汇总失真 | `automation-status.md` 顶表「已执行=0/通过=22/失败=5」为历史残留 | skill 脚本汇总逻辑未随侧车刷新 | 改 `test_inventory.py` 汇总逻辑根除(超出本自动化改码边界,需 skill 维护) | P2 | 待评估 |
+
+---
+
+## 三、本轮阻塞与风险
+
+| 阻塞项 | 影响范围 | 阻塞类型 | 处置进展 | 需要协调 |
+|--------|----------|----------|----------|----------|
+| 远程派发通道不可用 | 后端 2367 @Test + Playwright 547 spec | 测试(环境) | 待响应 | 主理人/运维提供 opencode 派发环境或节点可达性 |
+| langgraph flaky 固化为题 | 1 用例(历史失败,本轮绿) | 测试(自处置) | 处理中(本轮绿,待研发固化) | 研发改 fixture |
+| 795 文档用例/场景零执行 + SRS 未启用 | 覆盖目标② | 测试(覆盖) | 待处理 | 研发/测试共建 SRS 池与执行链路 |
+
+---
+
+## 四、下轮工作计划
+
+| 计划项 | 预计完成时间 | 前置依赖 | 备注 |
+|--------|--------------|----------|------|
+| 固化 langgraph flaky | 2026-11-08 | 研发改 fixture | 防回弹 |
+| 接入派发通道后重跑后端/Playwright | 环境就绪后 | 主机/凭证 | 闭环 2914 脚本化用例 |
+| 启用 SRS 池 + 闭环 795 用例 | 研发/测试共建 | SRS 锚点 | 覆盖目标② |
+| 修 test_inventory 失真 | skill 维护排期 | skill 维护 | P2 |
+
+---
+
+## 五、与基线对比 / 变更声明 / 推送与通知
+
+**基线(2026-11-07 09:13)**:FE 313/313、web 90/90、langgraph 58/1/12(EXIT 1)、台账合计 3133(Playwright 443、Java @Test 2208)。
+
+**本轮(2026-11-07 12:57)**:
+- FE 313/313 ✅(与基线一致)
+- web 90/90 ✅(与基线一致)
+- langgraph **51 passed / 0 failed / 12 skipped(EXIT 0)** — 较基线 EXIT 1(1 失败)**转绿**(历史 flaky 本轮未复现,但需研发固化防回弹);收集数 71→63(疑似未提交测试改动调整,需研发确认)
+- 台账合计 **3765**(Playwright 443→547、Java @Test 2208→2367)— **SRS/场景/测试有新增,触发「更新测试用例」**:新增 Playwright 104 + Java 159 需研发补契约/RED
+
+**变更结论:结果有变化(langgraph 由失败转绿 + 台账总量增长)→ 已触发「提交远程 + 通知研发」**。
+
+**推送结果(运行时裁定)**:本地 `cfclub` 落后 `origin/cfclub` 5 个提交,且工作区含他人未提交改动;本运行环境**不执行 `pull --rebase`/force-push**(避免覆盖他人 WIP)。已生成针对性本地提交(仅含本汇报 + 刷新后的 `docs/tdd/test/inventory.md`、`automation-status.md`),推送被拒(non-fast-forward)。需操作员在干净基线 `git pull --rebase origin cfclub` 后推送,或由有权限者在远端合并。详细 blocker 见 `docs/tdd/test/blockers.md` 与 `docs/tdd/orchestration/pending.md`。
+
+**通知研发**:已通过 Agent Mail 向研发协调邮箱发送本轮结果变更摘要 + 报告位置 + 需研发处置项(P0 langgraph 固化、P0 派发通道、P1 SRS/795 用例)。
+
+---
+
+## 附录
+
+### 附录 A:本轮新增测试用例明细
+| 用例 ID | 能力键 | 目标单元 | 用例标题 | 预期失败契约 | 是否已登记 RED | 登记 sha |
+|---------|--------|----------|----------|--------------|----------------|----------|
+| (无) | — | — | — | — | — | — |
+
+### 附录 B:任务分派与会话恢复明细
+| 任务号 | 目标会话标签 | 分派时间 | 分派类型 | 当前状态 | 恢复会话标签 | 恢复原因 | 恢复时间 |
+|--------|--------------|----------|----------|----------|--------------|----------|----------|
+| (无) | — | — | — | — | — | — | — |
+
+### 附录 C:新发现问题明细
+| 问题 ID | 能力键/任务号 | 问题类型 | 路由去向 | 登记时间 | 处置状态 | 关联 sha |
+|---------|---------------|----------|----------|----------|----------|----------|
+| P-1 | langgraph.test_analysis_graph_offline_run_returns_interpretation | 缺陷/异常(flaky) | 研发/测试自处置 | 2026-11-07 | 待处理(本轮绿) | — |
+| P-2 | cfc-backend JUnit(2367)/Playwright(547) | 阻塞(环境·派发通道) | 测试→运维/主理人 | 2026-11-07 | 待处理 | — |
+| P-3 | 795 文档用例/场景 零执行 | 覆盖缺口 | 研发/测试 | 2026-11-07 | 待处理 | — |
+| P-4 | SRS 池未启用 + 异常流 TC-*-E/-P 零执行 | 覆盖缺口 | 研发/测试 | 2026-11-07 | 待处理 | — |
+
+---
+
+## 门禁结论
+
+**结论:不通过(双目标均未达成)**
+
+- **目标① 所有测试用例全部覆盖并通过**:未达成。本环境实跑 454 通过 / 0 失败(FE 313 + web 90 + langgraph 51);但后端 2367 @Test + Playwright 547 spec 共 2914 脚本化用例因派发通道不可用**零实测**;langgraph 历史 flaky 虽本轮绿仍需固化。
+- **目标② 覆盖全部 SRS + 用户场景含异常流**:未达成。SRS 池未启用(0 锚点);795 文档用例/场景 0 执行验证;异常流 TC-*-E/-P 仅文档标注零执行;后端 130 handler uncovered(discover:covered=32 / partial=196 / uncovered=130 / no-handler=1)。
+- **本轮亮点 / 变更**:langgraph 本轮 EXIT 0(历史失败转绿);台账总量 3133→3765,SRS/场景/测试有新增(已提示研发补契约)。
+
+**改善路径(闭环至「通过」所需)**:① 在具备 opencode 派发通道的主机运行本自动化,闭环后端/Playwright 2914 用例;② 研发固化 langgraph flaky;③ 启用 SRS 池并将 795 文档用例接入 CI 实跑 + 异常流执行级校验;④ 修 test_inventory 汇总失真。
+
+---
+**模板版本**:v1.1(2026-11-03) · 适用对象:测试负责人
+**生成本环境**:cfc-tester 自动化巡检(review 档,本地可跑部分实跑,远程派发部分标记未实测)

+ 236 - 0
docs/tdd/test/automation-run-2026-11-07-1325.md

@@ -0,0 +1,236 @@
+# XAF(浠艾福)测试闭环执行报告 · 2026-11-07 13:25
+
+- **执行人**:陆谨(软件测试专家团主理人 / 测试架构师)· 定时自动化,全程无人工干预
+- **执行档**:review(内网目标 + 已部署站点只读访问;未安装依赖、未改生产源码、未重置任何数据库)
+- **仓库/分支/HEAD**:`d:/cfc` · `cfclub` · `dab532903`(跑测前后一致)
+- **工作区口径**:主工作区含 57 项 WIP(其中 11 个后端测试文件 `M`),本轮后端结果为**「WIP 代入」口径**,非干净基线
+- **权威干净基线(对照)**:`7c3bdae58` 全量 clean = **286 类 / 2375 例 / 95.16%**;`7c3bdae58..HEAD` 的 `cfc-backend/src`、`cfc-web/src`、`cfc-frontend`、`cfc-langgraph/src` **源码零变更**(仅 docs/tools 提交)
+- **SRS 闸门基线**:`9daeaa660`
+
+---
+
+## 一、环境与执行通道(本轮实测,非沿用结论)
+
+| 目标 | 探测结果 | 判定 |
+|---|---|---|
+| `http://cfc.iwintrue.com/admin` | 302 → `/admin/` 200,`text/html`,`<title>浠艾福管理后台</title>` | ✅ 浏览器自动化可用 |
+| `http://cfc.iwintrue.com/frontend` | 302 → `/frontend/` 200,uni-app H5,落地 `#/pages/index-home/index` | ✅ 浏览器自动化可用 |
+| 后端 API | `POST /api/admin-auth/login-by-password` → 200 `{code:200,...token}` | ✅ 存活 |
+| 本机节点(浏览器自动化) | Chromium 1243 已装;4091(opencode web)、9082(backend)在听 | ✅ 已执行 2 个任务(串行) |
+| `192.168.16.189:4090`(浏览器自动化节点) | TCP **OPEN**,opencode `v2.0.31` 可连;但 `/openapi.json` → 401,`opencode run --server` 报 `Location not found: D:\cfc` | ⚠️ 节点在线、**派发通道不可用**(客户端 2.2.5 与服务端 2.0.31 协议/路径不匹配) |
+| `192.168.16.251`(langgraph 主机) | 22/80/443/3306/6379/8080 OPEN;**9000 CLOSED**、4090 CLOSED | ⚠️ 主机在线、**opencode 派发未暴露** |
+| LangGraph 服务(部署态) | `https://ai.etotem.com.cn/health` → `{"status":"ok"}`,`cfc-langgraph v0.3.0` | ✅ 线上服务可达,已做 API 级实测 |
+
+> 结论:**「每个节点只能跑一个浏览器自动化任务」已遵守**——两个浏览器任务在本机严格串行执行;189 节点因派发协议不可用未能承接,已如实标注,不记为通过。
+
+---
+
+## 二、用例总数 · 通过 · 失败
+
+| 层 | 套件 | 用例 | 通过 | 失败 | 错误 | 跳过 | 耗时 |
+|---|---|---|---|---|---|---|---|
+| 浏览器 E2E · 管理端(本机,/admin) | 1 | 5 | **5** | 0 | 0 | 0 | 2.7 min |
+| 浏览器 E2E · 小程序 H5(本机,/frontend) | 1 | 6 | **6** | 0 | 0 | 0 | 1.4 min |
+| 接口契约 E2E · 商城价格排序(本机) | 1 | 4 | **4** | 0 | 0 | 0 | 22.5 s |
+| cfc-frontend Jest(单元/组件) | 16 | 313 | **313** | 0 | 0 | 0 | 71 s |
+| cfc-web Jest(单元/组件) | 12 | 90 | **90** | 0 | 0 | 0 | 303 s |
+| 根级 tests Jest(接口/集成) | 2 | 23 | 0 | 0 | 0 | **23(全 skip)** | 30 s |
+| cfc-langgraph pytest(本地脚本级) | — | 63 | **51** | 0 | 0 | 12 | 352 s |
+| LangGraph 线上 API(部署态 vs 251) | — | 3 | 2 | **1** | 0 | 0 | — |
+| cfc-backend surefire(主工作区 WIP 口径) | 见 §三 | 见 §三 | 见 §三 | 见 §三 | 见 §三 | 见 §三 | 进行中 |
+
+**本轮已实测合计(不含后端与 skip)**:**471 例 / 470 通过 / 1 失败**(通过率 99.79%);另 23 例根级套件全 skip(0 执行,不计入通过)。
+
+---
+
+## 三、后端全量(进行中,如实标注)
+
+- 命令:`mvn -o -B clean test -DskipTests=false -Dmaven.test.failure.ignore=true`(JDK1.8 + Maven 3.6.1,全量不排除)
+- 状态:**尚未跑完**(本机同时承载其他自动化会话,单类耗时显著劣化,`ActivityControllerTest` 461.7s、`AIChatControllerTest` 407.9s)
+- 截至报告时刻进度:**81 类 / 744 例 / 737 通过 / 5F / 2E(99.06%)**,红类 4 个;其中已确认的 2 个为:
+  - `GuideFamilyTaskControllerTest` 20 例 **2F 1E**
+  - `GuideRolePermissionTest` 21 例 **1F 1E**
+  - 两者均为**主工作区 WIP 中被修改的测试文件**,且与 B-29 遗留清单(GuideFamilyTask 3 + GuideRolePermission 2)逐例吻合 → 属已知遗留,非本轮新增回归
+  - 另 2 个红类随进度滚动产生,最终归因待全量跑完后按 XML `message`/堆栈首帧重算
+- 对照口径:源码零变更 → 预期收敛于干净基线 **286 类 / 2375 例 / 95.16%**(WIP 代入理论值 96.93%,B-29 已验证过一次)
+- 节流说明:本机同时承载其他自动化会话,单类耗时为常态的 3–4 倍(`ActivityControllerTest` 461.7s、`AIChatControllerTest` 407.9s),全量预计需数小时,故本节按「进度快照」呈现
+- **本轮不以后端数字作为门禁达标依据**,仅作进度与红类观测;下一轮以跑完的全量结果替换本节
+
+---
+
+## 四、浏览器自动化明细(本轮核心增量)
+
+### 4.1 管理端 `/admin`(5/5)
+
+| 用例 | 断言要点 | 证据 |
+|---|---|---|
+| TC-A-U01 登录页可渲染 | 标题「浠艾福管理后台」、`用户名/手机号`+`密码` 输入框、`登 录` 按钮 | `_artifacts_live/admin-01-login.png` |
+| TC-A-U02 滑块人机验证 | `.slider-captcha` 存在,真实鼠标分步拖动到轨道末端后提示文案改变(验证通过) | `admin-02-captcha-passed.png` |
+| TC-A-U03 未登录拦截 | 未登录访问 `/admin/` 落到登录页 | — |
+| TC-A-U04 管理员真实登录 | 填账号+密码+拖滑块+点登录 → **离开登录页**、渲染后台主框架菜单 >3 项 | `admin-04-logged-in.png`(运营驾驶舱,14 组侧边菜单) |
+| TC-A-U05 菜单可访问 | 遍历一级菜单 ≥6 个,视图切换成功,**0 个未捕获 JS 异常** | `admin-05-menu-walk.png` |
+
+> **重要:本轮推翻了历史「管理端真实登录无法自动化」的判断**——登录页滑块是自研 `.slider-thumb` 组件,Playwright 鼠标轨迹分步拖动可通过;`cfc-web` 登录链路端到端可用(此前 12:25 轮记为「仅探针 302 可达」)。
+
+### 4.2 小程序 H5 `/frontend`(6/6)
+
+| 用例 | 断言要点 | 证据 |
+|---|---|---|
+| TC-F-U01 首页渲染 | hash 路由 `#/pages/*`、`uni-page` 根节点、TabBar 四入口(首页/健康/成长/我的)、0 未捕获异常 | `frontend-01-index.png` |
+| TC-F-U02 核心内容 | 五维(身/心/智/富/行)+ 能量沙盘 + 正文 >200 字 | `frontend-02-index-content.png` |
+| TC-F-U03 切「健康」 | 路由变化且仍渲染页面、0 异常 | `frontend-03-health.png` |
+| TC-F-U04 切「成长」 | 路由变化、0 异常 | `frontend-04-growth.png` |
+| TC-F-U05 未登录「我的」 | 出现登录/注册入口(鉴权引导) | `frontend-05-mine.png` |
+| TC-F-U06 首屏资源 | js/css 关键资源 0 加载失败 | — |
+
+### 4.3 需求变更 → 用例更新登记(本轮 SRS 闸门)
+
+本轮合并远端后发现 **4 条需求变更说明**(`docs/需求分析/2026-11-06~07-*.md`,远端 `6a3e6a2d2` 补齐)。按「SRS/场景变更必须更新用例」逐条处置:
+
+| 需求变更 | 实现状态 | 用例处置 | 结论 |
+|---|---|---|---|
+| 商城价格排序(`2026-11-07-商城价格排序需求变更.md`) | dev 中(纯前端) | **新增 4 例接口契约** `tests/e2e-live/shop-sort-api.spec.js`(TC-S-U01~04):锁定前端 `buildSort()` 三种取值被后端正确消费(price asc/desc/默认),并覆盖非法列名白名单(不 5xx、不注入) | ✅ **4/4 通过**(后端排序能力已完备,前端 UI 完成后可直接复用) |
+| 优惠券「已领取券」按钮修复 | WIP 已有 `cfc-frontend/__tests__/specs/coupon-claimed-actions.test.js` | 用例存在且随 313 例执行 | ⚠️ 文件**未入库**(untracked)→ 登 B-45 |
+| 我的团队收益口径 + 邀请分孚 | WIP 已有 `__tests__/specs/team-invite-share.test.js` | 用例存在且执行通过 | ⚠️ 同上,未入库 → B-45 |
+| 报告上传页关联商品卡片 | 已实现(WIP `cfc-backend` 测试 11 文件) | 无独立新增用例;由 `Article/Guide*` 等既有契约类与 5 个后端测试类覆盖 | ⚠️ 建议补端到端一键用例(G-2) |
+
+### 4.4 新增用例与 SRS/场景映射(B-30 的锚点缺口部分闭环)
+
+新增自动化用例 **15 条**,全部带 `TC-` 编号锚点(此前全仓 348 个测试资产中 TC 编号出现 **0 次**):
+
+| TC 编号 | 场景/SRS 域 | 载体 |
+|---|---|---|
+| TC-A-U01~U05 | 管理员登录 → 后台主框架 → 核心菜单(`docs/flows/admin-overview-flow.md`) | `tests/e2e-live/admin-live.spec.js` |
+| TC-F-U01~U06 | 游客浏览首页 → 五维沙盘 → TabBar 导航 → 未登录「我的」鉴权引导 | `tests/e2e-live/frontend-live.spec.js` |
+| TC-S-U01~U04 | 商城按价格排序(需求变更 S1)后端消费契约 + 排序列白名单 | `tests/e2e-live/shop-sort-api.spec.js` |
+
+配置 `tests/playwright.config.live.js` **刻意不带 globalSetup**——默认配置的 `globalSetup` 会执行 `reset-db.sh` 重置共享测试库,打已部署站点的 UI 套件绝不能触发。
+
+---
+
+## 五、LangGraph(251 部署态)实测
+
+| 探测 | 结果 |
+|---|---|
+| `GET /health` | 200 `{"status":"ok"}` |
+| `PUT /api/v1/chat`(POST,真实用户问题) | **200**,返回完整中文答复(74.8s)、`conversation_id`、`trace_id` 齐全 → 对话主链路可用 |
+| `POST /api/v1/questionnaire/generate` | **500** `{"error":"graph 执行失败: 'GenerateRequest' object has no attribute 'prompt_template'"}` |
+| 本地 `pytest tests --ignore=tests/qna` | **51 passed / 12 skipped / 0 failed**(352s,B-41 flaky 连续第 2 次满跑未复现) |
+
+> **本轮最高优先级发现(P0)**:线上 LangGraph **问卷生成 100% 失败**,且报错与 B-34 完全同源。台账在 67 轮以「本地 pytest 2 例转绿」把 B-34 判为「已解除」——**实测证明该结论只对仓库代码成立,对部署实例不成立**(线上 `GenerateRequest` schema 仍只有 `member_name/respondent_name/...`,无 `prompt_template` 字段,说明修复未发布)。已重开 B-34 为「未发布/未部署」,新增 B-43。
+
+---
+
+## 六、五要素问题清单
+
+### P0-1 · 线上 LangGraph 问卷生成 500(B-34 复发:修复未部署)
+1. **问题描述**:`POST https://ai.etotem.com.cn/api/v1/questionnaire/generate` 返回 500,错误 `'GenerateRequest' object has no attribute 'prompt_template'`;线上 `/openapi.json` 的 `GenerateRequest` 仍无该字段。
+2. **测试 bug 点**:`cfc-langgraph/src/schemas/questionnaire.py`(`GenerateRequest` 缺 `prompt_template`)与 `src/graphs/questionnaire.py:39 build_prompt` 直读该字段;**部署包为修复前版本**(线上 v0.3.0 schema 可证)。
+3. **最优修改方案**:① `GenerateRequest` 增 `prompt_template: Optional[str] = None`(与 `TongueRequest` 对齐);② `build_prompt` 用 `getattr(req, 'prompt_template', None)` 防御;③ **重新构建并发布 251 上的 LangGraph 镜像/进程**,发布后用同一条 curl 回归。
+4. **修改目的**:恢复 `/api/family/questionnaire` 的 AI 问卷生成(当前 100% 静默降级),使核心 AI 链路可用。
+5. **闭环方式**:同一 curl 返回 200 且问卷 JSON 非空;`pytest tests/test_graph.py` 2 例仍绿;线上 `/openapi.json` 的 `GenerateRequest` 出现 `prompt_template`。
+
+### P0-2 · 浏览器自动化节点 189 无法承接任务(B-42 根因收敛)
+1. **问题描述**:`192.168.16.189:4090` 网络与 opencode 服务均在线(v2.0.31),但 API 需鉴权(`/openapi.json` 401),`opencode run --server` 报 `Location not found: D:\cfc`——客户端把本地 cwd 当作远端目录下发,远端实际为 `c:/code/cfc`。
+2. **测试 bug 点**:`.tdd/config.json` 的 `opencode_sessions.production` 只声明 id/label,**未提供鉴权方式与远端目录映射**;本机 opencode CLI 2.2.5 与该服务端 2.0.31 版本不匹配。
+3. **最优修改方案**:① 在 189 上把 opencode 升级到 2.2.x 或锁本机 CLI 到 2.0.x;② 配置项补充 `auth`(token 环境变量)与 `remote_dir`;③ 修好前,浏览器任务统一由本机串行承接(本轮即如此)。
+4. **修改目的**:恢复「双节点并行」设计意图,让 /admin 与 /frontend 可真正并行而非串行。
+5. **闭环方式**:`opencode run --server http://192.168.16.189:4090 "echo ok" c:/code/cfc` 返回 OK;两节点同时各跑 1 个 Playwright 任务成功。
+
+### P1-1 · 根级接口套件 23 例全 skip(B-36 仍未闭环)
+1. **问题描述**:`tests/` 根级 jest 本轮 2 套件 23 例**全部 skipped,0 例执行**——接口层验收从「假红」变「真空」。
+2. **测试 bug 点**:`tests/integration/mini-games-api.spec.js:6-11`、`plan-chain.spec.js:21-22` 门控 `CFC_API_E2E==='1' && CFC_API_TOKEN`,且 `ADMIN_PHONE/ADMIN_PASSWORD` 为空。
+3. **最优修改方案**:新增 `tests/.env.test` + `globalSetup` 自动登录取 token 写入 `CFC_API_TOKEN`,`API_BASE_URL` 指向 `http://cfc.iwintrue.com`。
+4. **修改目的**:让 23 例在每轮默认执行,恢复接口层信号。
+5. **闭环方式**:`CFC_API_E2E=1 npm test` 实跑 23 例且失败均有归因;纳入 CI。
+
+### P1-2 · `pom.xml` 默认 `skipTests=true`(B-39 未修)
+1. **问题描述**:`cfc-backend/pom.xml:28` 默认跳过测试,`mvn test` 会 `BUILD SUCCESS` 但 0 用例执行(假绿)。
+2. **测试 bug 点**:`cfc-backend/pom.xml:28` + `:232` surefire 引用 `${skipTests}`。
+3. **最优修改方案**:改为 `false`,跳过由命令行 `-DskipTests` 显式控制;CI 增加「surefire-reports XML 数 > 0」守卫。
+4. **修改目的**:消除本地/CI「假绿」风险。
+5. **闭环方式**:默认 `mvn test` 产出 >0 XML;CI 守卫在 0 XML 时报红。
+
+### P2-1 · 管理端驾驶舱文案与统一用户模型不一致(新发现)
+1. **问题描述**:登录后「运营驾驶舱」统计卡显示「**孩子总数 4256**」,而 AGENTS.md 已明确「新功能禁止再引入『孩子』概念,统一用能登录用户/不能登录用户」。
+2. **测试 bug 点**:`cfc-web` 运营驾驶舱统计卡文案(`admin-04-logged-in.png` 可见)。
+3. **最优修改方案**:文案改为「未开通登录的子成员」或按口径拆为「能登录用户 / 不能登录用户」两卡。
+4. **修改目的**:产品口径统一,避免运营侧误读。
+5. **闭环方式**:TC-A-U04 增断言:驾驶舱不出现「孩子总数」字样(当前为红灯基线,改文案后转绿)。
+
+---
+
+## 七、20 类测试覆盖矩阵(三态)
+
+| # | 类型 | 状态 | 证据 |
+|---|---|---|---|
+| 1 | 单元测试 | 已发现 | 后端 290 测试文件 / 2366 `@Test`;前端 Jest 16 套 313 例 |
+| 2 | 测试覆盖率 | 未发现 | 无 JaCoCo(pom 无插件、离线不可得)→ 仅 surefire 口径 |
+| 3 | 变异测试 | 未发现 | 无 PIT |
+| 4 | 集成测试 | 已发现 | `@SpringBootTest` 类 + 根级 23 例(**全 skip**) |
+| 5 | 契约测试 | 已发现 | 多契约类(历史红类含 MapContract 等) |
+| 6 | 接口 API 测试 | 已发现 | 根级 API 套件 + 本轮 admin 登录接口实测 |
+| 7 | 系统/端到端 | **已发现(本轮新增)** | `tests/e2e-live/` 11 例真实浏览器 |
+| 8 | 验收 UAT | 待核实 | 无 UAT 记录 |
+| 9 | BDD 规格 | 待核实 | 未发现 feature 文件 |
+| 10 | 冒烟测试 | 已发现 | `ci-smoke.spec.js` + 本轮两站点冒烟 |
+| 11 | 回归测试 | 已发现 | 每轮全量复跑 + 零漂移对账 |
+| 12 | 探索性测试 | 未发现 | 无 charter 记录 |
+| 13 | 性能测试 | 未发现 | 无 k6/JMeter 资产 |
+| 14 | 可靠性测试 | 未发现 | 无故障注入/恢复演练 |
+| 15 | 安全测试 | 待核实 | 历史 `docs/SECURITY_REVIEW_2026-11.md` 静态结论;本轮未跑 SAST/DAST |
+| 16 | 兼容性测试 | **已发现(本轮)** | 桌面 1440×900 + 移动 375×812 双视口真实渲染 |
+| 17 | 可用性测试 | 待核实 | 无真实用户测试 |
+| 18 | 无障碍测试 | 未发现 | 无 axe/WCAG 用例 |
+| 19 | QA 流程 | 待核实 | CI 存在但 4 个测试 job 为软门禁(B-40) |
+| 20 | 质量指标 | 已发现 | 本轮报告 + `docs/tdd/tools/test_report_metrics.py` |
+
+---
+
+## 八、门禁结论
+
+**⛔ 不通过(有条件通过路径已明确)。**
+
+| 门禁 | 阈值 | 本轮实测 | 判定 |
+|---|---|---|---|
+| G1 后端用例通过率 | ≥95% | 未跑完(进度 48 类 98.88%);干净基线 95.16% | ⚠️ 待全量 |
+| G2 红例数 | 0 | 未跑完;基线 74 例(含环境红 8) | ❌ |
+| G3 前端 Jest | 100% | 313/313 + 90/90 | ✅ |
+| G4 测试可编译 | 0 错 | 全量 clean 已进入测试阶段(编译通过) | ✅ |
+| G5 用例集完整性 | 无沉默 | 48 类 48 报告,沉默 0(进行中) | ⚠️ 待全量 |
+| G6 CI 阻断门禁 | 4 个测试 job 必须阻断 | 4 个仍 `allow_failure`(B-40) | ❌ |
+| G7 状态机 RED 键 | 0 RED | 历史 3 伪 RED 键(本轮未变更) | ❌ |
+| G8 契约红类 | 0 | 未跑完;历史 3–4 类 | ⚠️ 待全量 |
+| G9 langgraph pytest | ≥95% | 51 pass / 0 fail(执行口径 100%) | ✅ |
+| **G10 线上 LangGraph 功能可用** | 关键接口 200 | **问卷生成 500**(P0-1) | ❌ |
+| **G11 浏览器 E2E** | 100% | **15/15**(admin 5 + frontend 6 + 商城排序接口 4) | ✅(首次达标) |
+| **G12 接口层零执行** | 23 例必须实跑 | 23/23 skip | ❌ |
+| **G13 测试资产入库** | 新增资产全部入库 | 本轮 11 例 + 配置待提交(见 §九) | ⚠️ 提交后转 ✅ |
+
+**转「通过」的必要条件**:① 发布 LangGraph 修复并复验问卷生成 200;② 后端全量跑完且红例=0(或环境红显式豁免);③ 4 个 CI job 转 blocking;④ 根级 23 例恢复执行;⑤ 189 派发通道修复后可双节点并行。
+
+---
+
+## 九、产出与提交
+
+| 产出 | 路径 | 状态 |
+|---|---|---|
+| 本报告 | `docs/tdd/test/automation-run-2026-11-07-1325.md` | 待提交 |
+| 管理端 UI 套件(5 例) | `tests/e2e-live/admin-live.spec.js` | 待提交 |
+| 小程序 H5 UI 套件(6 例) | `tests/e2e-live/frontend-live.spec.js` | 待提交 |
+| 商城排序契约套件(4 例) | `tests/e2e-live/shop-sort-api.spec.js` | 待提交 |
+| 线上 UI 配置(无 globalSetup) | `tests/playwright.config.live.js` | 待提交 |
+| 阻断台账 | `docs/tdd/test/blockers.md`(B-34 重开、B-42 根因收敛、新增 B-43) | 待提交 |
+| 截图证据 | `_artifacts_live/*.png`(9 张) | 本地留存,不入库 |
+| 日志 | `_r1125_backend.log`、`_r1125_agg.py`、`_r1125_partial.json`、`_lg_openapi.json`、`_lg_q.json` | 本地留存 |
+
+---
+
+## 十、未实测 / 待核实
+
+- 后端**其余 238 类未跑完**(本机负载导致,预计需更长时间),本轮不据此下达标结论
+- JaCoCo 行/分支覆盖率、PIT 变异:离线不可得
+- 性能/可靠性/无障碍/探索性:无工具、无资产(已给测试设计)
+- SAST/DAST/依赖漏洞深扫:需联网授权
+- `192.168.16.251` 上的 pytest **未在 251 本机执行**(opencode 派发未暴露、SSH 无凭据);本轮为「本机 pytest + 线上 API 级」替代口径,已在 §五 标注
+- 管理端其余 60+ UI spec 未在本轮执行(仅新增 5 例线上冒烟),历史 spec 依赖 `globalSetup` 重置库,需独立环境

Разница между файлами не показана из-за своего большого размера
+ 787 - 787
docs/tdd/test/automation-status.md


+ 6 - 1
docs/tdd/test/blockers.md

@@ -38,10 +38,15 @@
 | B-31 | `application.yml` 7 处明文凭据(安全) | 研发 | `cfc-backend/src/main/resources/application.yml` 存在 **7 处** `password/secret/api-key/appsecret` 明文赋值(本台账不复制取值),覆盖数据源口令、微信 appsecret 与第三方 key。任何具备仓库读权限者可直取测试/生产凭据;且历史提交已留在 git 历史中。建议:改 `${ENV}` 占位 + 本地 `.env`(gitignore)+ 配置中心/Secret 管理;**同时轮换已泄露凭据**;CI 加明文凭据扫描(gitleaks 规则集) | 测试(62 轮静态扫描登记) | 2026-11-06 23:10 | 待处理 |
 | B-32 | 场景/用例度量口径跨轮漂移(度量不可比) | 测试 | 本轮实测:场景 **215**、TC **784**(异常流 474);上一轮报告记「场景 244、TC 492(异常流 237)」。差异非增长(90 分钟内不可能新增 292 条 TC),而是**解析口径不同**(是否计入 `### 场景`(08/09 butler 17 条)、是否计入 `TC-*-N*` 负向编号、类型列是否含「权限」判异常)。与 B-15/B-27 同族:**度量脚本未入库、口径未固化** → 跨轮数字不可相减。建议:把 `_r2135_scene.py` 等脚本收进 `docs/tdd/tools/` 并入库,报告固定写「口径定义 + 脚本版本」,禁止手算 | 测试(62 轮登记) | 2026-11-06 23:10 | **部分解除(62 轮自处置)**:度量脚本已落地 `docs/tdd/tools/test_report_metrics.py`(聚合 + 逐例归因 + 场景覆盖 + 恒等式,纯只读、带 CLI 参数),本轮数字用该脚本复算完全一致(231 类/1847 例/1727 绿/93.50%/36 红类/116 红例;场景 215、TC 784、异常流 474、TC 锚点 0/784;恒等式 1838−20+3=1821,参数化净增 26 → 1847)。**但脚本无法入库**:被 `.gitignore:70 *.py` 全局忽略 → 见 B-33,需放行规则后才算真闭环 |
 | B-33 | `.gitignore:70 *.py` 全局忽略 Python 文件 —— 度量脚本「写了就丢」的真正根因 | 研发/流程 | 62 轮把度量工具落到 `docs/tdd/tools/test_report_metrics.py` 后 `git add` 被拒:`git check-ignore -v` → **`.gitignore:70:*.py`**。这解释了 B-15(`discover_tests.py`/`test_inventory.py` 全仓丢失、历史度量不可复现)与 B-32(口径跨轮漂移)为何反复发生:**任何 Python 度量/校验脚本在本仓库都不可能入库**,只存在于某个工作区,分支切换或清理即消失。影响面:度量脚本、数据 seed 脚本、报告解析脚本(`docs/scripts/fingerprint_engine.py` 等)同受此规则约束(后者是否被例外放行需核实)。建议:把规则细化为 `/*.py`(仅忽略仓库根临时脚本)或追加例外 `!docs/tdd/tools/*.py`、`!docs/scripts/**/*.py`,再由研发提交一次;测试侧脚本需随之入库。**注意:`.gitignore` 属研发所有,测试角色不代改** | 测试(62 轮实测登记) | 2026-11-06 23:10 | 待处理 |
-| B-34 | LangGraph 问卷生成 100% 失败降级(`GenerateRequest` 缺 `prompt_template`) | 研发 | **P0 生产缺陷**。`c3925c97e feat(langgraph): 全部提示词迁移到Java后端可配置` 只给 `cfc-langgraph/src/schemas/tongue.py:14` 的 `TongueRequest` 加了 `prompt_template`,**未给 `src/schemas/questionnaire.py:39 GenerateRequest` 加**;而 `src/graphs/questionnaire.py:39 build_prompt` 直接读 `req.prompt_template` → 每次调用必抛 `AttributeError`。链路:Java `AiGateway:321` 恒发送 `prompt_template` → pydantic 静默丢弃额外字段 → 图节点必抛 → 接口 500 → `AiGateway` catch 后返回 null → 调用方**静默降级模板**。即 `/api/family/questionnaire` 的 AI 生成能力 100% 不可用。`c3925c97e` 已在 `origin/cfclub` 祖先链 → 现网同样受影响。65 轮 pytest 实测 2 例必红即此因。修复:`GenerateRequest` 增 `prompt_template: Optional[str] = Field(default=None, ...)`(与 `TongueRequest:14` 对齐);`build_prompt` 可加 `getattr` 防御。闭环:`pytest tests/test_graph.py` 2 例转绿 + `/api/v1/questionnaire/generate` 返回 200 且 `questionnaire_json` 非空 + `AiGateway` 不再打「调用失败」warn | 测试(65 轮实测登记) | 2026-11-07 02:15 | **已解除(67 轮实测复验,2026-11-07 09:30)**:cfc-langgraph pytest 全量 `51 passed / 12 skipped / 0 failed`(302s),`test_graph` 2 例复原、此前 2 红清零 → 修复生效实证;API 端 `/api/v1/questionnaire/generate` 200 复验待下轮(TR-10 跟踪) |
+| B-34 | LangGraph 问卷生成 100% 失败降级(`GenerateRequest` 缺 `prompt_template`) | 研发 | **P0 生产缺陷**。`c3925c97e feat(langgraph): 全部提示词迁移到Java后端可配置` 只给 `cfc-langgraph/src/schemas/tongue.py:14` 的 `TongueRequest` 加了 `prompt_template`,**未给 `src/schemas/questionnaire.py:39 GenerateRequest` 加**;而 `src/graphs/questionnaire.py:39 build_prompt` 直接读 `req.prompt_template` → 每次调用必抛 `AttributeError`。链路:Java `AiGateway:321` 恒发送 `prompt_template` → pydantic 静默丢弃额外字段 → 图节点必抛 → 接口 500 → `AiGateway` catch 后返回 null → 调用方**静默降级模板**。即 `/api/family/questionnaire` 的 AI 生成能力 100% 不可用。`c3925c97e` 已在 `origin/cfclub` 祖先链 → 现网同样受影响。65 轮 pytest 实测 2 例必红即此因。修复:`GenerateRequest` 增 `prompt_template: Optional[str] = Field(default=None, ...)`(与 `TongueRequest:14` 对齐);`build_prompt` 可加 `getattr` 防御。闭环:`pytest tests/test_graph.py` 2 例转绿 + `/api/v1/questionnaire/generate` 返回 200 且 `questionnaire_json` 非空 + `AiGateway` 不再打「调用失败」warn | 测试(65 轮实测登记) | 2026-11-07 02:15 | **⚠️ 重新打开(2026-11-07 13:25 线上实测)**:67 轮的「已解除」只对**仓库代码**成立,对**部署实例不成立**。本轮直连部署态 LangGraph 实测:`POST https://ai.etotem.com.cn/api/v1/questionnaire/generate` → **HTTP 500** `{"error":"graph 执行失败: 'GenerateRequest' object has no attribute 'prompt_template'"}`;线上 `/openapi.json`(cfc-langgraph v0.3.0)的 `GenerateRequest` 仍只有 `member_name/respondent_name/relationship_type/member_profile/respondent_profile/family_context`,**无 `prompt_template`** → 部署包为修复前版本。B-43 为其发布侧跟踪条目 |
 | B-35 | DAN 报告解析 A1/A2/B4 仍失效(修复提交未覆盖该版式) | 研发 | `DanReportParseServiceTest` 65 轮全量实测 **4 例红**:`testParseA2_standardCoreLiteracyFormat:262`(姓名 `expected 俊辰 but null`)、`testParseB4_regression:280`(仍走降级 `expected false but true`)、`testParseA1_shouldFallbackToGenericPattern`、`testParseA1_shouldExtractCognitiveDimensionFromDetailPage`(`expected not null`)。而 `c611ec55d fix(dan): 修复 A1/B4 认知维度解析失效` 声称已修 → **修复未覆盖这两种版式**(`testParseA2_regression` 同批绿、`standardCoreLiteracy` 版式红,说明是版式分支漏配)。健康报告解析为核心链路(AGENTS.md「健康报告 PDF 上传与解析」)。建议按真实样本补版式分支 + 回归样本入库。闭环:4 例转绿 + 用 3 份真实报告样本走 `/api/health/report/parse` 人工比对字段 | 测试(65 轮实测登记) | 2026-11-07 02:15 | **已解除(67 轮实测复验,2026-11-07 09:30)**:`DanReportParseServiceTest` 全量 **38/38 GREEN**,此前 A1/A2/B4 4 红清零 → 版式分支补配生效;真实样本人工比对待下轮(TR-11 跟踪) |
 | B-36 | 根级黑盒 API 套件由「12 恒红」转为「默认 skip」,接口层覆盖归零 | 测试/环境 | WIP 将 `tests/integration/mini-games-api.spec.js:6-11`、`plan-chain.spec.js:21-22` 改为 `CFC_API_E2E==='1' && CFC_API_TOKEN` 门控(原写死 `Bearer test-token` 打真服务 → 恒 401/500)。方向正确(消除 12 个伪红),但 65 轮实测 **23 例全部 skip、0 执行**(`Tests: 23 skipped`)→ 接口层验收从「假红」变「真空」,且 `plan-chain` 仍依赖空的 `ADMIN_PHONE/ADMIN_PASSWORD`。建议:新增 `tests/.env.test` + seed 账号,`globalSetup` 自动登录取 token 写入 `process.env.CFC_API_TOKEN`,CI 注入 `API_BASE_URL=http://127.0.0.1:9082`。闭环:本地 9082 在听时 `CFC_API_E2E=1 npm test` 23 例实跑且红例有归因;CI 出绿灯记录 | 测试(65 轮实测登记) | 2026-11-07 02:15 | 待处理 |
 | B-37 | sleep.config.tags blank-key 实现缺口(契约测试 1 红,阻断该键转 GREEN) | 研发 | 复验轮(TR-6 派发 10 键复验)`SleepConfigTagsMechanismContractTest` **9/10 绿 + 1 红**:`publicValue_blankKey_returnsError:99` 期望 blank key(`"   "`)→ 500 但实得 200。实现 `PublicConfigController.getValue`(L21-28)只查 `key==null||isEmpty()`,未 reject 全空白串 → blank key 被当作正常查询放行。测试由 `f11062b42`(提交信息注明「21绿/1红 blank-key 缺陷」)引入,属**已知实现缺口、非测试误报**。`HealthSleepControllerContractTest` 12/12 绿不受影响。该键 `--mark-green` 已**暂缓**、状态机保持 YELLOW。修复:`getValue` 校验改为 `key==null || key.trim().isEmpty()`(或判 `matches("\\s*")`),并确认无调用方依赖空白 key 语义。闭环:`SleepConfigTagsMechanismContractTest` 10/10 绿后再 `--mark-green sleep.config.tags` | 测试(复验派发登记) | 2026-11-07 05:50 | **已解除(67 轮实测复验,2026-11-07 09:30)**:`SleepConfigTagsMechanismContractTest` 全量 **10/10 GREEN**,blank-key 红清零 → 修复生效;`--mark-green sleep.config.tags` 键不在本工作区账本,由研发负责人侧执行 |
 | B-38 | 本轮 26 红类/69 红例归因(含 GUIDE integration 真实候选) | 研发/测试 | 67 轮全量实测 2219 例 96.71%:26 红类 40F+29E=69 例,归因分层:① **B-13 环境红 3 类 8 例**(primaryDataSource→Hikari→`Communications link failure`);② **A 类断言/装置漂移 ~15 类 47 例**(B-18 同族:doNothing/matcher 误用、NPE、桩缺失、文案断言过期——ArticleWorkflow「仅已驳回」vs「仅已驳回或已撤回」、StatsController「未关联家庭」vs「请先创建或加入家庭」、WishController childId→memberId、MindFortune 401 vs 400);③ **契约断言与实现不一致 6 类 13 例**(DimensionRegistryMapContractTest 3F、EnergySystemFlowTest 6F expected 200 but 500、MembershipPaySwitch/PaymentServiceORDRouting/OrchestrationEngine/OrchestrationIntegration 各 1F);④ **真实问题候选 1 类 1 例**:`GuideModuleIntegrationTest.testGuidePackageCreationFlow` → `MysqlDataTruncation: Out of range value for column 'platform_fee'`(schema 与代码不一致,需研发核验迁移脚本)。建议:① 修 B-13 数据源指向;② 测试侧下轮同步 3 处文案断言 + 修 A 类装置(B-18 清单);③ 研发核验 platform_fee 列定义与 DimensionRegistryMap 契约口径;④ EnergySystemFlowTest 500 需研发确认集成流依赖 | 测试(67 轮实测登记) | 2026-11-07 10:20 | 待处理 |
 | B-39 | `cfc-backend/pom.xml` 默认 `skipTests=true`(测试零执行假绿) | 研发 | **P0 流程黑洞**。`cfc-backend/pom.xml:28` 声明 `<skipTests>true</skipTests>`,`:232` 的 surefire 配置引用 `${skipTests}`。本轮(2026-11-07 10:59)实测:在干净隔离树 `D:\_wt_r1054`@`9359de39a` 执行文档标准命令 `mvn -o -B clean test` → **9:49 BUILD SUCCESS,日志明确 `[INFO] Tests are skipped.`,surefire-reports 下 0 个 XML**——即“构建成功但零用例执行”。影响:① 任何按标准 Maven 命令接入的本地验证/CI 都会得到**假绿**,覆盖率与通过率无从谈起;② 本台账历史上多轮“全量通过”结论必须显式带 `-DskipTests=false` 才成立;③ 新入职/新流水线极易踩坑。`.gitlab-ci.yml:16` 注释已记录该陷阱并以 `-DskipTests=false` 绕过,但**根因未修** → 每个使用方都要自己记得加参数。建议:去掉 `:28` 的默认 `<skipTests>true</skipTests>`(或改为 `<skipTests>false</skipTests>`,把“跳过”交给命令行 `-DskipTests` 显式控制);同时在 CI 增加一条守卫:`mvn -o -B test -DskipTests=false` 后断言 `surefire-reports/*.xml` 数量 > 0,否则 job 失败。闭环:默认 `mvn test` 能跑出用例;或 CI 守卫在“0 XML”时报红 | 测试(本轮实测登记) | 2026-11-07 11:10 | 待处理 |
 | B-40 | CI 4 个测试 job 为软门禁 + 集成层被排除 | 研发/流程 | `.gitlab-ci.yml` 现有 7 个 job,其中 **4 个 `allow_failure` 语义**:`backend-contract-junit:134`、`langgraph-pytest:165`、`tests-jest:225`(e2e-playwright 亦按注释兜底),即**测试失败不阻断流水线**;且 `backend-contract-junit:142` 使用 `-Dtest='!**/integration/**'` 排除整个 `integration` 包(1794/2219 例)——而本轮 26 个红类中 **14 个位于 `com.etotem.cfc.integration.*`**(含 `EnergySystemFlowTest` 6F、`DimensionRegistryMapContractTest` 3F、`PlannerBindInviteFlowTest` 4F 等),**恰是 CI 视野之外的部分**。后果:CI 绿灯 ≠ 可发布,红灯也不拦人。建议:① 分两级门禁——`test-blocking`(全绿子集,必须过)与 `test-observed`(已知红,允许失败但产出趋势);② 把 `integration` 包按类白名单逐批转 blocking;③ 报告 G6 口径从“有无 CI”升级为“blocking job 覆盖例数占比”。闭环:任一 blocking job 红 → 流水线红;且 blocking 覆盖 ≥ 当前全量 80% | 测试(本轮实测登记) | 2026-11-07 11:10 | 待处理 |
+| B-41 | langgraph.test_analysis_graph_offline_run_returns_interpretation 历史 flaky 需固化 | 研发 | 本轮(2026-11-07 12:57)cfc-langgraph pytest 全量 51 passed / 0 failed / 12 skipped(EXIT 0);历史满跑失败、隔离单跑 PASS 的 `TestGrowthReportInterpretB04::test_analysis_graph_offline_run_returns_interpretation` 本轮未复现,但属顺序相关/模块级 state 污染型 flaky(疑似 chromadb 原生依赖 + 模块级 fixture),存在回弹风险。建议:mock chromadb + 确定性 fixture + 单文件隔离运行(`--forked`);CI 固定隔离。闭环:连续 3 轮满跑全绿后再视为修复生效 | 测试(第11轮 巡检登记) | 2026-11-07 12:57 | 待处理 |
+| B-42 | 后端 JUnit/Playwright 远程派发通道在本运行环境不可用(2914 脚本化用例零实测) | 测试/运维 | 配置(`.tdd/config.json`)要求后端 JUnit 2367 @Test 与 langgraph pytest 派发至 `opencode.iwintrue.com`(192.168.16.251)、Playwright 547 spec 派发至 `web-4091`(本机:4091)/`web-189-4090`(192.168.16.189:4090);但本运行环境无 opencode 客户端、192.168.16.189 TCP 连接拒绝不可达,仅能本地实跑 Jest/pytest。后果:后端 2367 + Playwright 547 共 2914 脚本化用例零实测,目标①「所有用例通过」无法核验。建议:在具备 opencode 会话(web-4091/web-189-4090/opencode.iwintrue.com)的主机运行本自动化,或为本环境配置派发凭证与节点可达性。闭环:派发通道可用后重跑后端/Playwright,覆盖率与通过率可计 | 测试(第11轮 巡检登记) | 2026-11-07 12:57 | **根因收敛(2026-11-07 13:25 复测)**:189 节点**网络与服务均在线**(TCP 4090 OPEN,opencode `v2.0.31` 可连),此前「TCP 连接拒绝」结论已过期。真实卡点两条:① HTTP API 需鉴权,`GET /openapi.json` → 401 `Authentication required`(`.tdd/config.json` 的 `opencode_sessions.production` 未声明鉴权方式);② `opencode run --server http://192.168.16.189:4090 ...` 报 `Location not found: D:\cfc`——本机 CLI 2.2.5 把**本地 cwd** 当远端目录下发,而远端项目目录是 `c:/code/cfc`(`opencode api` 报 `Operation not found: GET /global/health`,版本 2.0.31 与 2.2.5 协议不符)。251 侧:22/80/443 open,**9000 与 4090 均 closed** → langgraph pytest 无法派发到 251 本机。处置建议:升级/对齐 opencode 版本 + 配置 `auth` 与 `remote_dir`。本轮已按「单节点串行」完成 11 例浏览器用例(本机) |
+| B-43 | 线上 LangGraph 部署包落后于仓库(问卷生成 500,B-34 复发) | 研发/运维 | 部署态实测(`https://ai.etotem.com.cn`,`cfc-langgraph v0.3.0`):`POST /api/v1/questionnaire/generate` → **500** `'GenerateRequest' object has no attribute 'prompt_template'`;线上 `/openapi.json` 的 `GenerateRequest` 无 `prompt_template` 字段 → 部署的是修复前版本。仓库侧修复(`GenerateRequest` 补字段 + `build_prompt` 防御)已在 cfclub,但**未发布到 251**。后果:`/api/family/questionnaire` AI 生成 100% 失败并静默降级为模板。建议:重新构建并发布 LangGraph 服务,发布后按同一 curl 回归。闭环:该接口 200 且问卷 JSON 非空 + 线上 openapi 出现 `prompt_template` | 测试(13:25 轮线上实测登记) | 2026-11-07 13:25 | 待处理 |
+| B-44 | 管理端「运营驾驶舱」文案与统一用户模型冲突(孩子总数) | 研发 | 真实登录后驾驶舱统计卡显示「**孩子总数 4256**」(截图 `_artifacts_live/admin-04-logged-in.png`),而 AGENTS.md 规定「新功能禁止再引入『孩子』概念,统一用能登录用户/不能登录用户(memberType=login/local)」。属口径遗留,非功能缺陷。建议改为「未开通登录的子成员」或拆为两卡。闭环:TC-A-U04 增断言「驾驶舱不出现『孩子总数』」,改文案后转绿 | 测试(13:25 轮 UI 实测登记) | 2026-11-07 13:25 | 待处理 |
+| B-45 | 两条需求变更的前端用例只在工作区、未入库(团队邀请分享 / 优惠券已领取按钮) | 测试/流程 | 合并远端后核对当日 4 条需求变更:`2026-11-06-我的团队收益口径与邀请分孚需求变更.md` 与 `2026-11-06-优惠券已领取券按钮修复需求变更.md` 的用例已存在并**随 cfc-frontend 16 套件 313 例实际执行通过**(`__tests__/specs/team-invite-share.test.js`、`__tests__/specs/coupon-claimed-actions.test.js`),但 `git status` 显示 `cfc-frontend/__tests__/specs/` 整体为 `??`(untracked)→ 只存在于主工作区,`git clean -fd` 即静默丢失。与 B-23(SC-X-22 契约未入库)同源。建议:由研发/资产作者 `git add cfc-frontend/__tests__/specs/` 后提交;测试侧不代提交研发 WIP。闭环:`git ls-files cfc-frontend/__tests__/specs/` 有命中且 `git status` 该路径干净 | 测试(13:25 轮实测登记) | 2026-11-07 13:25 | 待处理 |

Разница между файлами не показана из-за своего большого размера
+ 786 - 786
docs/tdd/test/inventory.md


+ 160 - 0
tests/e2e-live/admin-live.spec.js

@@ -0,0 +1,160 @@
+/**
+ * ================================================================
+ * 管理端(cfc-web)线上 UI 级 E2E · TC-A-U01 ~ TC-A-U06
+ * ================================================================
+ * 目标站点:http://cfc.iwintrue.com/admin
+ * 场景来源:docs/flows/admin-overview-flow.md(管理员登录 → 后台主框架 → 核心菜单)
+ *
+ * 只读约束:不新增/修改/删除任何业务数据;仅执行登录(会话建立)。
+ * 凭据来源:环境变量 CFC_ADMIN_PHONE / CFC_ADMIN_PASSWORD(缺省用测试环境公共测试账号),
+ *          禁止在报告/日志中打印明文口令。
+ *
+ * DOM 依据(2026-11-07 实测):
+ *   input[placeholder="用户名 / 手机号"]、input[placeholder="密码"]、
+ *   input[placeholder="登录身份(输入手机号后自动带出)"]、
+ *   滑块验证 .slider-captcha > .slider-track / .slider-thumb、按钮「登 录」
+ * ================================================================
+ */
+const { test, expect } = require('@playwright/test');
+const fs = require('fs');
+const path = require('path');
+
+const ART = path.join(__dirname, '..', '..', '_artifacts_live');
+if (!fs.existsSync(ART)) fs.mkdirSync(ART, { recursive: true });
+
+const PHONE = process.env.CFC_ADMIN_PHONE || '13800138000';
+const PASSWORD = process.env.CFC_ADMIN_PASSWORD || 'admin123';
+
+/** 拖动滑块到最右侧(Element UI 滑块验证) */
+async function dragSliderToEnd(page) {
+  const thumb = page.locator('.slider-thumb').first();
+  const track = page.locator('.slider-track').first();
+  await thumb.waitFor({ state: 'visible', timeout: 15000 });
+  const tb = await thumb.boundingBox();
+  const rb = await track.boundingBox();
+  if (!tb || !rb) throw new Error('滑块/轨道 boundingBox 不可得');
+  const y = tb.y + tb.height / 2;
+  const endX = rb.x + rb.width - tb.width / 2 - 2;
+  await page.mouse.move(tb.x + tb.width / 2, y);
+  await page.mouse.down();
+  // 分步移动,模拟真人拖动(部分实现按轨迹校验)
+  const steps = 18;
+  for (let i = 1; i <= steps; i++) {
+    const x = tb.x + tb.width / 2 + ((endX - (tb.x + tb.width / 2)) * i) / steps;
+    await page.mouse.move(x, y, { steps: 2 });
+    await page.waitForTimeout(18);
+  }
+  await page.mouse.up();
+}
+
+test.describe('管理端 UI · 登录与主框架', () => {
+  test('TC-A-U01 登录页可渲染(标题/账号/密码/登录按钮)', async ({ page }) => {
+    const resp = await page.goto('/admin/login', { waitUntil: 'domcontentloaded' });
+    expect(resp, '登录页应有 HTTP 响应').not.toBeNull();
+    expect(resp.status(), '登录页状态码应 < 400').toBeLessThan(400);
+
+    await expect(page).toHaveTitle(/浠艾福管理后台/);
+    await expect(page.locator('input[placeholder="用户名 / 手机号"]')).toBeVisible();
+    await expect(page.locator('input[placeholder="密码"]')).toBeVisible();
+    await expect(page.getByRole('button', { name: /登\s*录/ })).toBeVisible();
+    await page.screenshot({ path: path.join(ART, 'admin-01-login.png') });
+  });
+
+  test('TC-A-U02 滑块人机验证存在且可拖动通过', async ({ page }) => {
+    await page.goto('/admin/login', { waitUntil: 'domcontentloaded' });
+    await expect(page.locator('.slider-captcha')).toBeVisible();
+    await expect(page.locator('.captcha-hint')).toContainText(/拖动到最右侧/);
+
+    await dragSliderToEnd(page);
+    // 成功后提示文案变化(验证通过/成功),或滑块填充色铺满
+    const hint = page.locator('.captcha-hint');
+    await expect(hint).not.toContainText(/拖动到最右侧/, { timeout: 10000 });
+    const text = (await hint.innerText()).trim();
+    expect(text.length, '滑块通过后应有反馈文案').toBeGreaterThan(0);
+    await page.screenshot({ path: path.join(ART, 'admin-02-captcha-passed.png') });
+  });
+
+  test('TC-A-U03 未登录访问后台受保护路由被拦截到登录页', async ({ page }) => {
+    await page.goto('/admin/', { waitUntil: 'domcontentloaded' });
+    await page.waitForTimeout(2500);
+    const url = page.url();
+    const loginVisible = await page.locator('input[placeholder="密码"]').isVisible().catch(() => false);
+    expect(
+      url.includes('/login') || loginVisible,
+      `未登录访问 /admin/ 应落到登录页,实际 URL=${url} 登录表单可见=${loginVisible}`
+    ).toBeTruthy();
+  });
+
+  test('TC-A-U04 管理员真实登录(滑块 + 凭据)进入后台主框架', async ({ page }) => {
+    await page.goto('/admin/login', { waitUntil: 'domcontentloaded' });
+    await page.locator('input[placeholder="用户名 / 手机号"]').fill(PHONE);
+    await page.locator('input[placeholder="密码"]').fill(PASSWORD);
+
+    // 「登录身份」为只读带出项,若为空则尝试点选首个身份选项
+    const roleInput = page.locator('input[placeholder="登录身份(输入手机号后自动带出)"]');
+    if (await roleInput.isVisible().catch(() => false)) {
+      const roleVal = await roleInput.inputValue().catch(() => '');
+      if (!roleVal) {
+        await roleInput.click();
+        const opt = page.locator('.el-select-dropdown__item, .el-option').first();
+        if (await opt.isVisible({ timeout: 3000 }).catch(() => false)) await opt.click();
+      }
+    }
+
+    await dragSliderToEnd(page);
+    await page.waitForTimeout(600);
+
+    await page.getByRole('button', { name: /登\s*录/ }).click();
+    await page.waitForTimeout(5000);
+
+    const afterUrl = page.url();
+    const loggedIn = !afterUrl.includes('/login');
+    // 兼容后端拒绝(例如身份未选)时给出可诊断的信息
+    expect(loggedIn, `登录后应离开登录页,实际 URL=${afterUrl}`).toBeTruthy();
+
+    // 后台主框架特征:侧边菜单项存在
+    const menuCount = await page.locator('.el-menu-item, .el-submenu__title, .sidebar-container a').count();
+    expect(menuCount, '登录后应渲染后台菜单').toBeGreaterThan(3);
+    await page.screenshot({ path: path.join(ART, 'admin-04-logged-in.png'), fullPage: false });
+  });
+
+  test('TC-A-U05 后台核心菜单可访问(用户管理/数据看板)', async ({ page }) => {
+    await page.goto('/admin/login', { waitUntil: 'domcontentloaded' });
+    await page.locator('input[placeholder="用户名 / 手机号"]').fill(PHONE);
+    await page.locator('input[placeholder="密码"]').fill(PASSWORD);
+    const roleInput = page.locator('input[placeholder="登录身份(输入手机号后自动带出)"]');
+    if (await roleInput.isVisible().catch(() => false)) {
+      const v = await roleInput.inputValue().catch(() => '');
+      if (!v) {
+        await roleInput.click();
+        const opt = page.locator('.el-select-dropdown__item, .el-option').first();
+        if (await opt.isVisible({ timeout: 3000 }).catch(() => false)) await opt.click();
+      }
+    }
+    await dragSliderToEnd(page);
+    await page.waitForTimeout(600);
+    await page.getByRole('button', { name: /登\s*录/ }).click();
+    await page.waitForTimeout(5000);
+    test.skip(page.url().includes('/login'), '登录未通过(见 TC-A-U04),本用例无法在无会话下判定');
+
+    const menuTexts = await page.locator('.el-menu-item, .el-submenu__title').allInnerTexts();
+    const joined = menuTexts.join('|').replace(/\s+/g, '');
+    expect(joined.length, '菜单文案不应为空').toBeGreaterThan(0);
+
+    const errors = [];
+    page.on('pageerror', (e) => errors.push(String(e)));
+    // 逐个点击前 6 个一级菜单,断言 URL/内容变化且无未捕获异常
+    const items = page.locator('.el-menu-item');
+    const n = Math.min(await items.count(), 6);
+    let navigated = 0;
+    for (let i = 0; i < n; i++) {
+      const before = page.url();
+      await items.nth(i).click({ timeout: 8000 }).catch(() => {});
+      await page.waitForTimeout(1200);
+      if (page.url() !== before || (await page.locator('.el-main, .main-container, #app > div').count()) > 0) navigated++;
+    }
+    expect(navigated, '至少应有一个菜单可切换视图').toBeGreaterThan(0);
+    expect(errors, '菜单遍历期间不应出现未捕获 JS 异常').toHaveLength(0);
+    await page.screenshot({ path: path.join(ART, 'admin-05-menu-walk.png') });
+  });
+});

+ 134 - 0
tests/e2e-live/frontend-live.spec.js

@@ -0,0 +1,134 @@
+/**
+ * ================================================================
+ * 小程序 H5(cfc-frontend uni-app)线上 UI 级 E2E · TC-F-U01 ~ TC-F-U06
+ * ================================================================
+ * 目标站点:http://cfc.iwintrue.com/frontend/(uni-app H5,hash 路由)
+ * 场景来源:用户场景「游客浏览首页 → 五维能量沙盘 → 商品/活动 → TabBar 切换 → 我的(未登录)」
+ *
+ * DOM 依据(2026-11-07 实测):
+ *   落地路由 #/pages/index-home/index,title=首页,
+ *   TabBar 文案:首页 / 健康 / 成长 / 我的(uni-app 原生 tabbar)
+ *
+ * 只读约束:游客态浏览,不登录、不下单、不提交表单。
+ * ================================================================
+ */
+const { test, expect } = require('@playwright/test');
+const fs = require('fs');
+const path = require('path');
+
+const ART = path.join(__dirname, '..', '..', '_artifacts_live');
+if (!fs.existsSync(ART)) fs.mkdirSync(ART, { recursive: true });
+
+/** 采集页面运行期异常与失败请求 */
+function watch(page) {
+  const errors = [];
+  const failed = [];
+  page.on('pageerror', (e) => errors.push(String(e).slice(0, 300)));
+  page.on('requestfailed', (r) => failed.push(`${r.method()} ${r.url().slice(0, 120)}`));
+  return { errors, failed };
+}
+
+/** 点击 TabBar 项(uni-app H5 渲染为 uni-tabbar > .uni-tabbar__item) */
+async function clickTab(page, label) {
+  const inTabbar = page.locator('uni-tabbar .uni-tabbar__item', { hasText: label }).first();
+  if (await inTabbar.count()) {
+    await inTabbar.click();
+    return true;
+  }
+  const byText = page.getByText(label, { exact: true }).last();
+  if (await byText.count()) {
+    await byText.click();
+    return true;
+  }
+  return false;
+}
+
+test.describe('小程序 H5 UI · 游客浏览与导航', () => {
+  test('TC-F-U01 首页可渲染(标题/落地路由/TabBar 四项)', async ({ page }) => {
+    const w = watch(page);
+    const resp = await page.goto('/frontend/', { waitUntil: 'domcontentloaded' });
+    expect(resp, '首页应有 HTTP 响应').not.toBeNull();
+    expect(resp.status(), '首页状态码应 < 400').toBeLessThan(400);
+
+    await page.waitForTimeout(5000);
+    expect(page.url(), '应落到 hash 路由首页').toMatch(/#\/pages\//);
+    await expect(page).toHaveTitle(/首页|浠艾福/);
+
+    // uni-app 页面根节点已挂载
+    expect(await page.locator('uni-page, .uni-page').count(), '应渲染 uni-page 根节点').toBeGreaterThan(0);
+
+    // TabBar 四项
+    const tabTexts = await page.locator('uni-tabbar .uni-tabbar__item, .uni-tabbar__label').allInnerTexts();
+    const joined = tabTexts.join('|').replace(/\s+/g, '');
+    expect(joined, 'TabBar 应包含 首页/健康/成长/我的').toMatch(/首页/);
+    expect(joined).toMatch(/健康/);
+    expect(joined).toMatch(/成长/);
+    expect(joined).toMatch(/我的/);
+
+    await page.screenshot({ path: path.join(ART, 'frontend-01-index.png') });
+    expect(w.errors, `首页不应有未捕获 JS 异常:${w.errors.join(' ;; ')}`).toHaveLength(0);
+  });
+
+  test('TC-F-U02 首页核心内容渲染(五维沙盘 / 三步引导)', async ({ page }) => {
+    await page.goto('/frontend/', { waitUntil: 'domcontentloaded' });
+    await page.waitForTimeout(5000);
+    const body = (await page.locator('body').innerText()).replace(/\s+/g, '');
+
+    // 五维能量(身/心/智/行/富)与能量沙盘区块
+    for (const dim of ['身', '心', '智', '富', '行']) {
+      expect(body, `首页应出现五维之一「${dim}」`).toContain(dim);
+    }
+    expect(body).toMatch(/沙盘|五维/);
+    expect(body.length, '首页正文应有实质内容').toBeGreaterThan(200);
+    await page.screenshot({ path: path.join(ART, 'frontend-02-index-content.png') });
+  });
+
+  test('TC-F-U03 TabBar 切换到「健康」正常跳转且无异常', async ({ page }) => {
+    const w = watch(page);
+    await page.goto('/frontend/', { waitUntil: 'domcontentloaded' });
+    await page.waitForTimeout(4500);
+    const before = page.url();
+
+    const clicked = await clickTab(page, '健康');
+    test.skip(!clicked, 'TabBar「健康」不可点(DOM 结构与预期不符),需更新选择器');
+    await page.waitForTimeout(3500);
+
+    expect(page.url(), '切 Tab 后路由应变化').not.toBe(before);
+    expect(page.url()).toMatch(/#\/pages\//);
+    const pageCount = await page.locator('uni-page, .uni-page').count();
+    expect(pageCount, '切换后仍应渲染页面根节点').toBeGreaterThan(0);
+    await page.screenshot({ path: path.join(ART, 'frontend-03-health.png') });
+    expect(w.errors, `切 Tab 过程不应有未捕获异常:${w.errors.join(' ;; ')}`).toHaveLength(0);
+  });
+
+  test('TC-F-U04 TabBar 切换到「成长」正常跳转', async ({ page }) => {
+    const w = watch(page);
+    await page.goto('/frontend/', { waitUntil: 'domcontentloaded' });
+    await page.waitForTimeout(4500);
+    const before = page.url();
+    const clicked = await clickTab(page, '成长');
+    test.skip(!clicked, 'TabBar「成长」不可点,需更新选择器');
+    await page.waitForTimeout(3500);
+    expect(page.url()).not.toBe(before);
+    await page.screenshot({ path: path.join(ART, 'frontend-04-growth.png') });
+    expect(w.errors, `切 Tab 过程不应有未捕获异常:${w.errors.join(' ;; ')}`).toHaveLength(0);
+  });
+
+  test('TC-F-U05 未登录访问「我的」应出现登录/注册入口(鉴权引导)', async ({ page }) => {
+    await page.goto('/frontend/#/pages/mine/index', { waitUntil: 'domcontentloaded' });
+    await page.waitForTimeout(4500);
+    const body = (await page.locator('body').innerText()).replace(/\s+/g, '');
+    const hasLoginEntry = /登录|注册|立即登录/.test(body);
+    expect(hasLoginEntry, '未登录态「我的」页应提供登录入口').toBeTruthy();
+    await page.screenshot({ path: path.join(ART, 'frontend-05-mine.png') });
+  });
+
+  test('TC-F-U06 首屏关键资源加载无失败(无 requestfailed)', async ({ page }) => {
+    const w = watch(page);
+    await page.goto('/frontend/', { waitUntil: 'networkidle', timeout: 60000 }).catch(() => {});
+    await page.waitForTimeout(2000);
+    // 过滤掉已知的媒体/统计类非关键失败不做断言,只统计 js/css 类资源
+    const critical = w.failed.filter((u) => /\.(js|css)(\?|$)/i.test(u));
+    expect(critical, `关键静态资源不应加载失败:${critical.join(' ;; ')}`).toHaveLength(0);
+  });
+});

+ 74 - 0
tests/e2e-live/shop-sort-api.spec.js

@@ -0,0 +1,74 @@
+/**
+ * ================================================================
+ * 商城价格排序 · 接口级契约 E2E · TC-S-U01 ~ TC-S-U04
+ * ================================================================
+ * 需求来源:docs/需求分析/2026-11-07-商城价格排序需求变更.md(变更 S1)
+ *   - 后端能力已完备(SortInterceptor 读 body.sort 数组 + SortUtil 白名单注入)
+ *   - 本次为纯前端改动(排序栏 UI + buildSort() 传参)
+ *   → 故先锁定「后端排序契约」:前端 buildSort() 的三种取值必须被后端正确消费。
+ *
+ * 契约映射(对应 buildSort 输出):
+ *   price_asc  → [{column:'price',order:'asc'}]
+ *   price_desc → [{column:'price',order:'desc'}]
+ *   default    → [](不传,走后端默认 sort_order asc + created_at desc)
+ *
+ * 只读:仅查询商品列表,不写任何数据。
+ * ================================================================
+ */
+const { test, expect } = require('@playwright/test');
+
+const API = process.env.LIVE_API_BASE || 'http://cfc.iwintrue.com';
+
+async function listProducts(request, body) {
+  const resp = await request.post(`${API}/api/product/list`, { data: body, timeout: 30000 });
+  expect(resp.status(), '商品列表接口应 200').toBe(200);
+  const json = await resp.json();
+  expect(json.code, `业务码应为 200,实际 ${json.code} ${json.message || ''}`).toBe(200);
+  const data = json.data || {};
+  const rows = data.records || data.list || data.rows || (Array.isArray(data) ? data : []);
+  return rows;
+}
+
+function prices(rows) {
+  return rows.map((r) => Number(r.price)).filter((n) => !Number.isNaN(n));
+}
+
+test.describe('商城价格排序 · 后端排序契约(需求变更 S1)', () => {
+  test('TC-S-U01 price asc 生效(对应 buildSort → price_asc)', async ({ request }) => {
+    const rows = await listProducts(request, { page: 1, size: 8, sort: [{ column: 'price', order: 'asc' }] });
+    expect(rows.length, '应返回商品').toBeGreaterThan(1);
+    const p = prices(rows);
+    const sorted = [...p].sort((a, b) => a - b);
+    expect(p, `价格应非降序,实际 ${p.join(',')}`).toEqual(sorted);
+  });
+
+  test('TC-S-U02 price desc 生效(对应 buildSort → price_desc)', async ({ request }) => {
+    const rows = await listProducts(request, { page: 1, size: 8, sort: [{ column: 'price', order: 'desc' }] });
+    expect(rows.length).toBeGreaterThan(1);
+    const p = prices(rows);
+    const sorted = [...p].sort((a, b) => b - a);
+    expect(p, `价格应非升序,实际 ${p.join(',')}`).toEqual(sorted);
+  });
+
+  test('TC-S-U03 默认排序不变(对应 buildSort → default,不传 sort)', async ({ request }) => {
+    const rows = await listProducts(request, { page: 1, size: 8 });
+    expect(rows.length, '默认查询应返回商品').toBeGreaterThan(0);
+    // 默认口径:sort_order asc, created_at desc —— 不做值断言,只锁定「结构不回归」
+    expect(rows[0]).toHaveProperty('id');
+    expect(rows[0]).toHaveProperty('price');
+  });
+
+  test('TC-S-U04 非法排序列被白名单拦截(不 5xx、不注入)', async ({ request }) => {
+    const resp = await request.post(`${API}/api/product/list`, {
+      data: { page: 1, size: 5, sort: [{ column: 'price;drop table product', order: 'asc' }] },
+      timeout: 30000,
+    });
+    expect(resp.status(), '非法列名不应导致 5xx').toBeLessThan(500);
+    const json = await resp.json();
+    expect([200, 400], `应返回 200(忽略非法列)或 400(显式拒绝),实际 ${json.code}`).toContain(json.code);
+    if (json.code === 200) {
+      const rows = (json.data && (json.data.records || json.data.list)) || [];
+      expect(rows.length, '合法查询仍应返回数据(证明未抛异常)').toBeGreaterThan(0);
+    }
+  });
+});

+ 56 - 0
tests/playwright.config.live.js

@@ -0,0 +1,56 @@
+/**
+ * Playwright 线上 UI 级 E2E 配置(针对已部署站点 cfc.iwintrue.com)
+ * ================================================================
+ * 与 playwright.config.js 的关键区别:
+ *   - **无 globalSetup**:默认配置的 globalSetup 会执行 reset-db.sh 重置共享测试库,
+ *     对本配置(打生产/测试已部署站点)绝对禁止。
+ *   - 只做只读浏览器自动化:不写业务数据(唯一例外是登录,属只读会话建立)。
+ *   - workers=1、fullyParallel=false:单节点串行,符合「每个节点只能跑一个浏览器自动化任务」。
+ *   - 两个 project 按 spec 分流:admin 走桌面视口,frontend(小程序 H5)走移动视口。
+ *
+ * 运行(单节点、串行):
+ *   npx playwright test -c playwright.config.live.js --project=admin-desktop
+ *   npx playwright test -c playwright.config.live.js --project=frontend-mobile
+ */
+var path = require('path');
+
+var LIVE_BASE = process.env.LIVE_BASE_URL || 'http://cfc.iwintrue.com';
+
+module.exports = {
+  testDir: path.join(__dirname, 'e2e-live'),
+  timeout: 150000,
+  expect: { timeout: 15000 },
+  retries: 0,
+  workers: 1,
+  fullyParallel: false,
+  outputDir: path.join(__dirname, '.live-artifacts'),
+  reporter: [['list']],
+  use: {
+    baseURL: LIVE_BASE,
+    headless: true,
+    ignoreHTTPSErrors: true,
+    actionTimeout: 20000,
+    navigationTimeout: 60000,
+    screenshot: 'only-on-failure',
+    trace: 'off',
+    video: 'off',
+  },
+  projects: [
+    {
+      name: 'admin-desktop',
+      testMatch: /admin-live\.spec\.js$/,
+      use: { browserName: 'chromium', viewport: { width: 1440, height: 900 } },
+    },
+    {
+      name: 'frontend-mobile',
+      testMatch: /frontend-live\.spec\.js$/,
+      use: { browserName: 'chromium', viewport: { width: 375, height: 812 }, isMobile: true, hasTouch: true },
+    },
+    {
+      // 接口级契约(商城价格排序需求变更 S1):无需浏览器,走 request fixture 直连 /api
+      name: 'shop-api',
+      testMatch: /shop-sort-api\.spec\.js$/,
+      use: { browserName: 'chromium' },
+    },
+  ],
+};

Некоторые файлы не были показаны из-за большого количества измененных файлов