Ver código fonte

chore(test): R170 全量闭环报告 + API 层入闭环 + 3 处用例缺陷修正 + B-170-1

- 后端全量实跑 45m:363 类 / 3057 例 / 3030 过 / 23 红 / 4 跳 = 99.12%(零漂移第 17 次)
- 新增线上 API 非功能层 32 例(25 过 / 7 跳 / 0 红):冒烟 + 安全 + 性能兼容 + 追溯契约
- 修正 3 处用例自身缺陷:
  * TC-A-N05 探针错(拿登录接口当特权入口)→ /api/admin/families/overview
  * TC-P-N11 路径错(/api/assessment → /api/health/report/upload)+ 用 HTTP 码判业务拒绝 → 改 code≠200
  * TC-P-N02/X-N01 未随 S-cfc-163 会员门控更新 → 假红转显式 skip(不假绿)
- 新发现 B-170-1:DatabaseInitializer:2261 approved_count 回填 SQL 恒失败(28/28 上下文)
- 追溯矩阵新增 §10 锚点双向核对:76 命中 / 85 孤儿 / 291 空转(G9 20.7%)
- 全量 3933 / 3861 = 98.17%(剔 skip 98.80%);门禁:不通过(承袭)
- 归档游离 R168 run 记录 + traceability-p94-p95-c26.spec.js(B-162-1 类)
E2E Test Bot 12 horas atrás
pai
commit
e4adf8f2ac

+ 122 - 0
docs/tdd/automation-run-2026-11-12-R168.md

@@ -0,0 +1,122 @@
+# 每小时测试闭环报告 · R168
+
+> 触发:定时自动化(测试负责人闭环 / review 档,已授权内网 E2E + 测试用例编写)
+> 时间:2026-11-12 12:23 (GMT+8) | Workspace:`d:/cfc`(浠艾福 XAF / cfclub)
+> 对比基线:R167(2026-11-12 09:54)| 本轮轮次标签:R168
+
+## 1. 执行范围
+
+| 层 | 命令 / 目标 | 执行档 | 结果 |
+|---|---|---|---|
+| cfc-web 管理端单测 | `jest --runInBand`(无 coverage) | review | ✅ 116/116 |
+| cfc-frontend 小程序单测 | `jest` | review | ✅ 609/3/12 |
+| 授权内网 E2E 冒烟 | Playwright 无登录冒烟 `cfc.iwintrue.com/admin` + `/frontend`(hosts→192.168.16.251) | review(内网已授权) | ✅ 两路 200 可达 |
+| 后端全量回归 | `mvn -q test`(cfc-backend) | review* | ⛔ 本轮回填受限(≈60min > 单小时预算) |
+| config 化 E2E | `npx playwright test`(含 reset-db globalSetup) | review | ⛔ 被 251 库 Access denied 阻断 |
+| 依赖漏洞 / 性能压测 | SCA / k6 | execute(需联网) | ⛔ 未授权未执行 |
+
+## 2. 用例总数 · 通过 · 失败
+
+| 模块 | 套件 | 用例 | 通过 | 失败 | 跳过 |
+|---|---|---|---|---|---|
+| cfc-web(管理端) | 17 | 116 | 116 | 0 | 0 |
+| cfc-frontend(小程序) | 42 | 624 | 609 | 3 | 12 |
+| E2E 无登录冒烟(/admin+/frontend) | 2 路 | 2 | 2 | 0 | 0 |
+| **后端全量(cfc-backend)** | — | — | — | — | — (未执行,引用 R167 基线 3045 例 / 98.95%) |
+
+- **cfc-frontend 与 R167 基线(609/3/12)完全一致 → 无新增回归。**
+- cfc-web 首次带 `--coverage` 在共享运行器下 worker 被 SIGTERM(exit 143)触发 timeout(exit 124),重试去掉 coverage 后 116/116 全绿 → 超时系插桩开销,非用例失败。
+
+## 3. 覆盖率
+
+- cfc-web:本次 `--coverage` 超时未采集;引用 R167 末值(stmts/branch/func/line ≈ 10.08 / 6.06 / 7.48 / 9.95)作参考,需独立任务复测。
+- cfc-frontend:未带 `--coverage`,无新覆盖率数据;用例数稳定。
+- 后端:引用 R167 基线 98.95%(整体 3889 例 98.23%),本轮未重跑。
+
+## 4. 新增风险
+
+- **无新增回归**:前端 3 红与 R167 完全一致(已知基线红),后端未重跑故不引入新风险判定。
+- **INVITE/CHAT 需求变更(12:19 今日合入)已存在对齐测试资产**,无因变更导致的覆盖缺口放大(见第 5 节)。
+- **后端全量与 config E2E 持续未回填**属累积风险,建议纳入夜间/独立任务(见第 8 节复评条件)。
+
+## 5. 测试用例编写产出(依据 SRS / 需求变更)
+
+检测到两份需求文档于 12:19 今日变更(晚于基线 R167 09:54),但**对应测试资产已由本轮之前的小时级运行写入且内容对齐实现,本次不再新增文件以避免仓库抖动**:
+
+| 需求编号 | 变更文件 | 已存在且对齐的测试资产 | 状态 |
+|---|---|---|---|
+| CHAT-2026-11-12-V1(聊天仅会员可见) | `cfc-frontend/utils/membershipGate.js` 等 10 文件 | `cfc-frontend/__tests__/utils/membershipGate.test.js`(TC-MG-U01~U07:无 token→false 不发请求;`data.isActive===true`→true;过期/缺字段/异常/非布尔→false 全断言) | 可运行,已计入 609 通过集 |
+| INVITE-2026-11-12-V1(团队邀请改造) | `ServiceRoleApplyDTO/ServiceRoleApplication/ServiceRoleApplicationServiceImpl.submit` 等 8 文件 | `cfc-backend/src/test/java/com/etotem/cfc/unit/ServiceRoleApplyBindInviteContractTest.java`(写于 12:19,精准镜像 `submit()`:口径①②③、防冒领 DTO 不暴露 inviterUserId 直传、两级解耦不触发分销副作用) | 已写,**本轮未编译执行**(后端套件未跑) |
+
+> 结论:**本次无新增/修订用例文件**;两项需求变更均已有当前对齐资产。盲目补写将造成仓库抖动,故跳过(符合"需求无变更则跳过编写"原则)。
+
+## 6. 五要素问题清单
+
+**P1a · signature.consignee 契约红(已知基线红)**
+① 问题描述:`consignee-edit.vue` 未内嵌 `<hand-signature>` 组件,且手签名 `onConfirm` 回调未被触发。
+② 测试 bug 点:`cfc-frontend/__tests__/pages/signature/signature.consignee.test.js:150`(AC-R1 `expect(stubs.onConfirm).toHaveBeenCalled()` 收到 0 次);AC-R2 模板断言 `/<hand-signature/` 不匹配。
+③ 最优修改方案:研发确认该组件是否应内嵌 HandSignature 并接线 `onConfirm`;若系设计态则将该用例标 `@skip`+注释,否则补组件与事件。
+④ 修改目的:消除契约红,保证收件人手签名链路真实可用。
+⑤ 闭环:组件补 HandSignature + onConfirm 后重跑该 suite → 全绿即通过。
+
+**P1b · growthHistory.dayGroupView 预期红(设计态红)**
+① 问题描述:全无效业务日期记录分组时实际返回 7、期望 6,属"契约新增→预期红"。
+② 测试 bug 点:`cfc-frontend/__tests__/pages/growth-main/growthHistory.dayGroupView.test.js:287`(RED-A7)。
+③ 最优修改方案:研发确认 `groupByDate` 对全无效记录的跳过语义,按既定契约收敛实现或固化该红为预期。
+④ 修改目的:分组装配逻辑与契约一致。
+⑤ 闭环:实现对齐契约后该用例转绿,或显式登记为预期红。
+
+**P2 · cfc-web `--coverage` 在共享运行器超时**
+① 问题描述:`jest --coverage` 触发全量插桩,worker 被 SIGTERM(143)→ timeout(124),覆盖率未采集。
+② 测试 bug 点:cfc-web `jest --coverage` 在受限运行器(无 coverage 时 116/116 仅 81s)。
+③ 最优修改方案:覆盖率仅在 nightly 任务采集,或在 `collectCoverageFrom` 收窄到变更模块;单测日常跑不带 coverage。
+④ 修改目的:日常闭环稳定快速,覆盖率纳入低频重任务。
+⑤ 闭环:独立 coverage 任务跑通并产出 ≥R167 末值即通过。
+
+**P3 · 后端全量回归未回填**
+① 问题描述:`mvn test` 全量 ≈60min(R167 实测),超出单小时任务预算;`.m2` 为空时 CI 兜底。
+② 测试 bug 点:cfc-backend 全量套件(非单点)。
+③ 最优修改方案:将后端全量回归拆为独立 nightly 自动化(或仅跑变更模块 `mvn test -Dtest=...`)。
+④ 修改目的:维持 ≥98.95% 基线且不过度占用小时任务。
+⑤ 闭环:nightly 跑通 98.95%+ 即回填基线。
+
+**P4 · config 化 E2E 被 251 库 Access denied 阻断**
+① 问题描述:`tests/playwright.config.js` 的 `globalSetup=e2e/helpers/db.js` 执行 `reset-db.sh` 对 192.168.16.251 库 Access denied,整套 E2E 无法启动。
+② 测试 bug 点:`tests/e2e/helpers/db.js`(`execSync('bash reset-db.sh')` → `process.exit(1)`)。
+③ 最优修改方案:为冒烟场景提供不依赖 DB 重置的入口(如 `globalSetup` 可跳过开关),或治理 251 库访问权限。
+④ 修改目的:解除 E2E 启动阻塞,使 admin-desktop/frontend-mobile 登录流可跑。
+⑤ 闭环:配置可跳过 DB reset 后跑通全量 E2E 即通过。
+
+## 7. 待核实项
+
+- 登录态深度 E2E(业务流断言):无 `MACHINE_JSON_PATH` 凭据 → **待核实登录态**,降级为测试设计。
+- cfc-web 覆盖率具体数值:本次未采集,引用 R167 末值,需复测。
+- `signature.consignee` AC-R1 根因(组件未实现 vs 测试桩问题)需研发确认。
+- INVITE 契约测试仅静态对齐 `submit()`,未编译执行 → 待 nightly `mvn` 验证 GREEN。
+- 251 节点登录后业务流、langgraph 派 251 等:本轮未覆盖(需授权/登录态)。
+- 供应商/仓库资料未明确的接口行为一律未臆造,按"待核实"处理。
+
+## 8. 需用户单独授权才能继续的项(未授权未执行 + 测试设计)
+
+- **依赖漏洞 / SCA 扫描**(npm audit、OWASP dependency-check):需联网 → **未授权未执行**。设计:CI 增 `npm audit --production` + 后端 `mvn org.owasp:dependency-check:check`,High/Critical 即失败门禁。
+- **性能压测**(k6/JMeter 打 9082/9000):需联网/打目标环境 → **未授权未执行**。设计:对 `/api/ai/gate`、`/api/service-role/apply` 做并发基准,P95<300ms 为通过标准;需授权压测窗口。
+- **Playwright 公网安装浏览器**:禁止(公网)→ 未执行;本机 Chromium-1228/1243 已就绪,无需公网。
+- **后端全量 mvn(离线但≈60min)**:非联网但超单小时预算 → 降级,建议独立 nightly;若坚持每小时跑需授权放宽单任务时长/资源。
+- **登录态 E2E 全业务流**:需 `MACHINE_JSON_PATH` 凭据注入(绝不写明文)→ 待用户配置凭据后授权执行。
+
+## 9. 门禁结论
+
+**结论:有条件通过(Conditional Pass)**
+
+- 本轮回填的三层全部达标:cfc-web **116/116**;cfc-frontend **609/3/12 与 R167 基线一致(无新增回归)**;授权内网 E2E 冒烟 **/admin、/frontend 均 200 可达**。
+- 限制项:后端全量回归与 config 化 E2E 未执行、cfc-web 覆盖率未采集、3 已知红未收敛——均属已知基线/资源约束,非新引入失败。
+
+**复评条件(满足后升为"通过"):**
+1. 后端全量 `mvn test` 在 nightly/独立任务跑通并维持 ≥98.95%;
+2. 3 已知红(signature.consignee、growthHistory RED-A7)由研发确认设计态或收敛;
+3. cfc-web 覆盖率在独立任务采集并达标(参考 R167 末值);
+4. config E2E 的 251 库 Access denied 阻塞解除,admin-desktop/frontend-mobile 登录流跑通;
+5. 登录态深度 E2E 凭据配置后执行并 PASS。
+
+---
+*约束遵守:未访问外部公网;未修改/提交/推送生产源码;未输出任何密钥/口令/令牌(凭据仅经 env/`MACHINE_JSON_PATH` 注入,报告中一律脱敏);现有 Jest 测试基于 mock API,不据此断言真实服务端行为。*

+ 144 - 0
docs/tdd/automation-run-2026-11-12-R170.md

@@ -0,0 +1,144 @@
+# R170 自动化执行记录(2026-11-12 14:11 ~ 15:20)
+
+> 触发:定时自动化「测试负责人闭环(全部用例通过 + 覆盖 SRS/场景含异常流)+ 浏览器走 cfc.iwintrue.com/admin 与 /frontend(189/本机每节点单任务)+ langgraph 派 251 + 结果有变提交远程并通知研发」
+> 执行档:**review(离线)**。未安装依赖、未启停服务、未改生产代码;仅修正 3 处测试资产缺陷。
+
+## 1. 环境探针(SOP 第一步,禁止凭记忆判定)
+
+| 探针 | 结果 |
+|---|---|
+| `JAVA_HOME=/d/jdk1.8.0_25` | ✅ |
+| `/d/maven/apache-maven-3.6.1/bin/mvn` | ✅ |
+| `node -v` | v22.22.2 |
+| `df -h /d` | 294G / 已用 207G / 可用 **87G**(71%) |
+| `127.0.0.1:9082` | **000**(未监听 → 根级 16 红,B-36) |
+| `192.168.16.251:4096` | **200** |
+| `192.168.16.251:9000/health` | `{"status":"ok"}` / 3.9ms |
+| `192.168.16.189:4090` | **200** |
+| `192.168.16.204:4090` | **000**(B-95 第 30 轮) |
+
+## 2. 基线
+
+```
+git fetch origin cfclub -> behind 1
+git -c rebase.autostash=true pull --rebase --autostash
+HEAD: 8524533c8 (R169 结论点) -> efc730771
+窗口变更: 9 文件 +390/-13
+  cfc-web/.last_build_commit, package-lock.json, public/CHANGELOG*.md  (构建元数据)
+  docs/tdd/...(R169 报告/run/通知/blockers)
+  tests/e2e/_automation_reachability_result.json
+功能源码变更: 0(cfc-backend/src, cfc-frontend, cfc-web/src, cfc-langgraph 全部无 diff)
+```
+
+## 3. 执行明细
+
+### 3.1 后端全量
+
+```
+cd cfc-backend && mvn -o -B clean test       # 14:15 -> 15:00,45m
+surefire XML: 363 类 / 3057 例 / 3030 过 / 17F + 6E / 4S = 99.12%
+COMPILATION ERROR: 0 ; *.dump: 0 ; BUILD FAILURE(仅测试层)
+@SpringBootTest 上下文启动 28 次
+```
+
+12 个红类(23 红例):EnergySystemFlowTest 6 / GrowthPublicEndpointMappingTest 5E / DimensionRegistryMapContractTest 3 / GuideModuleIntegrationTest 1E / OrchestrationEngineTest 1 / OrchestrationIntegrationTest 1 / MembershipPaySwitchTest 1 / PaymentServiceORDRoutingTest 1 / UserServiceWechatLoginContractTest 1 / UserServiceWechatLoginTest 1 / TaskReviewScP11ContractTest 1 / TeamProfitCronContractTest 1
+→ 与 R169 **逐类逐例一致(零漂移第 17 次)**。
+
+### 3.2 三端 Jest + 根级
+
+```
+cfc-web      : 17 suites / 116 tests 全过,23.5s
+cfc-frontend : 42 suites / 624 = 609 过 / 3 红 / 12 跳,32.2s
+               (3 红均为 S-cfc-132/133 预期 RED:health-plan-summary / consignee-edit 模板锚点)
+tests (根级) : 2 suites / 23 = 7 过 / 16 红,18.2s(全 ECONNREFUSED / fetch failed 9082)
+```
+
+### 3.3 浏览器 E2E(每节点单任务)
+
+```
+189  opencode shell -> npx playwright test -c playwright.config.live.js --project=admin-desktop
+     节点 HEAD 8524533c8 ; spec blob 4703d73fb7647b5ec00fedcaab3e51854bb1a6fb = 本机 HEAD
+     5 passed (1.4m) EXIT=0
+本机 frontend-mobile : 6 passed (1.0m)
+本机 shop-api        : 4 passed (8.3s)
+本机 health(health.js): 13 passed (6.3s)
+本机 dx-bridge       : 2 skipped(/api/health/data-sources/bridge/* 线上 404,B-DX-1 第 23 轮)
+```
+
+### 3.4 线上 API 非功能层(本轮新增入闭环)
+
+```
+api-smoke                : 4 passed (9.8s)
+nonfunc-security         : 首跑 9 过 / 2 红 -> 修正 2 处用例缺陷 -> 11 passed (10.1s)
+nonfunc-perf-compat      : 首跑 4 过 / 1 红 / 2 跳 -> 按需求对齐改 skip -> 4 过 / 3 跳
+traceability-p94-p95-c26 : 6 passed / 4 skipped (8.3s)
+                           (该文件此前 git status ??(B-162-1 类),本轮先验证可执行再入库,
+                             避免把「从未执行的游离用例」当资产提交)
+```
+
+API 层合计 **32 例 = 25 过 / 0 红 / 7 跳**。
+
+三处用例缺陷与处置:
+
+| TC | 症状 | 根因 | 处置 |
+|---|---|---|---|
+| TC-A-N05 | `code=400 手机号和验证码不能为空` | 用登录接口当特权入口 | 探针改 `/api/admin/families/overview`(匿名 401) |
+| TC-P-N11 | 线上 404 | 路径 `/api/assessment/...` 不存在(真实 `/api/health/report/upload`)+ 用 HTTP 码判业务拒绝 | 路径修正 + 改判 `code≠200` 且 `HTTP<500` 且 `data.reportId` 为空 |
+| TC-P-N02/X-N01 | `code=46003 会员专享` | S-cfc-163(dc5522d09)聊天仅会员可见,用例未同步 | 探针识别门控 → `test.skip` + 登记 G-170-1(缺会员夹具) |
+
+### 3.5 langgraph@251
+
+```
+POST /api/shell?location[directory]=/sc-data/cfc/cfc-langgraph
+  setsid nohup bash -c 'cd ... && .venv/bin/python -m pytest -q --ignore=tests/qna --ignore=tests/test_chat.py > /tmp/r150_pytest.log'
+节点 HEAD efc730771 = 本机
+结果: 5 failed, 46 passed, 8 warnings in 129.43s
+日志: RateLimitError/429 x7 (AgnesAI_error x7) ; 401 x3 (api key ****ab12 invalid)
+       EXIT= 未落盘(B-126 第 5 轮复现)
+live 探针: /health 200 3.9ms ; /api/v1/analyze 200 8.66s 1131B ; /api/v1/chat 000 90s 0B
+对照生产: https://ai.etotem.com.cn/api/v1/chat 200
+```
+
+### 3.6 线上站点与鉴权
+
+```
+GET  http://cfc.iwintrue.com/admin     -> 200 (0.16s)
+GET  http://cfc.iwintrue.com/frontend  -> 200 (0.03s)
+POST 匿名 6 端点 -> 401 x6
+  /api/mind/fortune, /api/energy/overview, /api/health/plan/list,
+  /api/huawei-health/status, /api/xiaomi-health/status, /api/admin/families/overview
+GET  /api/health/data-sources/bridge/{list,sync-status} -> 404
+```
+
+## 4. SRS / 场景闸门
+
+```
+git diff --name-only 8524533c8..HEAD -- docs/superpowers/specs docs/superpowers/api/API_REFERENCE.md tests/test-cases
+  -> (空)  窗口零变更
+specs 201 份 ; 台账 15 份 + README ; SC 153 ; TC 唯一 367
+```
+
+→ 零新增用例(G12 合规);本轮对台账的改动仅为「补追溯矩阵 §10 锚点双向核对」与 3 处用例修正。
+
+## 5. 度量
+
+| 指标 | 数值 | 口径 |
+|---|---|---|
+| 场景→用例映射 | 89/153 = **58.2%** | specs 内 `SC-*` ∩ 台账 `SC-*` |
+| 唯一 TC | 367 | `tests/test-cases/*.md` |
+| 可执行锚点 | 161 | 测试源码 `TC-*` 字面(排除 generated) |
+| 双向命中 | 76 → **20.7%** | G9 |
+| 孤儿锚点 | **85** | 锚点 − 台账(新增反向指标) |
+| 台账空转 | **291** | 台账 − 锚点 |
+| 异常流语义 | 592 处 | 台账内「异常/失败/拒绝/越权/超时/回滚」 |
+
+## 6. 处置与产出
+
+- 修正测试资产 3 处:`tests/e2e/api/nonfunc-security.spec.js`(TC-A-N05 / TC-P-N11)、`tests/e2e/api/nonfunc-perf-compat.spec.js`(TC-P-N02/X-N01)。
+- 追溯矩阵新增 `tests/TRACEABILITY-MATRIX.md` §10(锚点双向核对 + 孤儿清单 + 建议动作)。
+- 报告 `docs/tdd/orchestration/report-test-2026-11-12-R170.md`;本记录;研发通知 `docs/tdd/notice-2026-11-12-R170-研发通知.md`;`blockers.md` 追加 R170 段。
+- 通知通道:Agent Mail `GetMe` → `not_bound`(明确「请勿重试」)→ 以仓库通知 + 推送落地。
+
+## 7. 临时文件(不入库)
+
+`_r170_backend.log`、`_r170_tc.txt`、`_r170_an.txt`、`_r170_orph.txt`、`_r170_only.txt`、`_r170_sc*.txt`

+ 114 - 0
docs/tdd/notice-2026-11-12-R170-研发通知.md

@@ -0,0 +1,114 @@
+# 研发交接通知 · R170(2026-11-12)
+
+**致:** 研发负责人 / 后端(数据与 Schema)/ 运维(AI 节点)/ CI 维护
+**来自:** 测试负责人(自动化测试闭环)
+**结论:** **不通过(承袭 R169;结构改善、指标略降)**
+**基线:** `cfclub @ 8524533c8 → efc730771`(窗口零功能源码变更)
+
+---
+
+## 一、本轮新增 / 变化(仅这些需要你们动作)
+
+### 1. 【新增 P2】`activities.approved_count` 回填 SQL 恒失败 → 该列永久为 0
+
+- **位置**:`cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java:2261`
+- **实证**:本轮全量 45 分钟、28 次上下文启动,**28/28 全部失败**
+  `SQLSyntaxErrorException: Unknown column 'a.id' in 'where clause'`
+- **语句**:
+  ```sql
+  UPDATE activities a SET a.approved_count = (
+    SELECT cnt FROM (SELECT COUNT(*) AS cnt FROM activity_registrations r
+                     WHERE r.activity_id = a.id AND r.status = 'approved') t)
+  ```
+  MySQL 不允许 `SET a.col` 带别名赋值,派生表内也不能引用被更新表的别名列。
+- **影响**:该列唯一写入点即此语句(创建时 `ActivityService:188` 置 0、审批时不递增)→ **持久化列恒 0**。
+  当前前端 `activity-detail.vue` 读的是 `ActivityRegistrationService:327` 动态计算的 `approvedCount`,故**暂未爆雷**;
+  但任何按 `activities.approved_count` 做的统计 / 排序 / 看板 / 报表都会失真。
+- **建议修法**:
+  ```sql
+  UPDATE activities a
+    JOIN (SELECT activity_id, COUNT(*) cnt FROM activity_registrations
+          WHERE status='approved' GROUP BY activity_id) t
+      ON t.activity_id = a.id
+    SET a.approved_count = t.cnt
+  ```
+  并把该语句从共用 `try` 块拆出、失败改 `log.error`(当前 `warn` + 继续打印「数据表检查完成」= 永久静默)。
+- **验收**:启动日志 `grep -c "Unknown column 'a.id'"` = 0,且
+  `SELECT COUNT(*) FROM activities a WHERE a.approved_count <> (SELECT COUNT(*) FROM activity_registrations r WHERE r.activity_id=a.id AND r.status='approved')` = 0。
+
+### 2. 【P0 仍未修,第 6 轮】CI 排除清单双向失真
+
+- `BACKEND_OBSERVED_CLASSES`(`.gitlab-ci.yml:61`,33 类)vs 实测红类(**12 类**):
+  - **漏拦 5 个当前红类** → `backend-blocking-junit` **必然失败**:
+    `GrowthPublicEndpointMappingTest`、`UserServiceWechatLoginContractTest`、`UserServiceWechatLoginTest`、`TaskReviewScP11ContractTest`、`TeamProfitCronContractTest`
+  - **26 个已转绿类仍被排除** → 硬门禁覆盖被人为缩小
+- 建议:短期把 5 类补进清单;长期加「红类集合 ↔ 清单集合双向差集必须为空」的 CI 断言。
+
+### 3. 【P1 仍未修,第 6 轮】`nutrition_age_dosage` 建表 SQL 无效
+
+- `schema.sql:5345-5357`:列清单无 `product_id`,却在 `:5356` 建 `INDEX idx_product (product_id)`
+  → 本轮 **28/28** 上下文全部 `Key column 'product_id' doesn't exist in table`,**该表从未创建成功**。
+- 附带:实体 `NutritionAgeDosage` 有 `updateTime`,DDL 无 `update_time`(建表修好后会立刻 `Unknown column`)。
+
+### 4. 【P0 恶化】langgraph@251 上游 429 加重,且 `chat` 端点挂死
+
+- pytest:**46/51**(R169 为 48/51)。5 红 = `test_auto_tag` 3(`assert 500==200`)+ `test_intent` 2。
+- 日志:`RateLimitError 429 (AgnesAI_error)` ×7、`401 api key ****ab12 invalid` ×3。
+- live:`/api/v1/analyze` **200 / 8.66s**(已恢复),`/api/v1/chat` **000 / 90s / 0 字节**(仍挂死);`/health` 200 / 3.9ms。
+- 对照生产 `https://ai.etotem.com.cn/api/v1/chat` = 200 → 仅 251 节点被掐。
+- **研发侧请修**:`intent_classifier.py:62` 捕获异常后默认 `chat` 的兜底,会把 429 伪装成「分类错误」→ 改为抛出可识别错误。
+
+### 5. 【测试侧已自修,同步口径】3 处用例自身缺陷(不是产品缺陷)
+
+| TC | 原症状 | 真实根因 | 处置 |
+|---|---|---|---|
+| `TC-A-N05` | 恒红 `code=400 手机号和验证码不能为空` | 拿**登录接口**当「特权入口」 | 探针改 `/api/admin/families/overview`(匿名 401)→ 绿 |
+| `TC-P-N11` | 恒红(404) | 路径 `/api/assessment/report/upload` **不存在**(真实 `/api/health/report/upload`)+ 用 HTTP 码判业务拒绝 | 路径修正 + 改判 `code≠200`、`HTTP<500`、`data.reportId` 为空 → 绿 |
+| `TC-P-N02/X-N01` | 恒红 `code=46003 会员专享` | **S-cfc-163(dc5522d09)聊天仅会员可见** 落地后用例未同步 | 识别门控 → 显式 skip(不假绿)+ 缺口 G-170-1 |
+
+> 结论:本轮**产品侧新增缺陷仅 1 个**(第 1 条),其余红例均为存量已知 + 环境(429 / 9082 未起)+ 预期 RED(S-cfc-132/133,3 例)。
+
+---
+
+## 二、存量红灯台账(与 R169 逐类逐例一致,零漂移第 17 次)
+
+| 类 | 红例 | 归因 |
+|---|---|---|
+| `EnergySystemFlowTest` | 6 | 存量(装置/契约待裁决) |
+| `DimensionRegistryMapContractTest` | 3 | 契约待裁决(P-9) |
+| `OrchestrationEngineTest` / `OrchestrationIntegrationTest` | 2 | 存量 |
+| `MembershipPaySwitchTest` / `PaymentServiceORDRoutingTest` | 2 | 存量 |
+| `UserServiceWechatLoginTest` / `...ContractTest` | 2 | 存量(且 CI 漏拦) |
+| `GuideModuleIntegrationTest` | 1E | 平台费截断(数据) |
+| `TaskReviewScP11ContractTest` | 1 | S-cfc-151 剩余:跨家庭/匿名审批仍可发分 |
+| `TeamProfitCronContractTest` | 1 | 存量(且 CI 漏拦) |
+| `GrowthPublicEndpointMappingTest` | 5E | **未入库文件**(`git status ??`),B-162-1 |
+
+前端 3 红 = S-cfc-132/133 预期 RED。根级 16 红 = 本机 9082 未监听(环境型,B-36 第 56 轮)。
+
+---
+
+## 三、需要你们确认/决策的点
+
+1. `activities.approved_count` 是否仍需要作为**持久化统计列**?(若改为纯动态计算,请删除该列与迁移,测试侧改为断言 `ActivityRegistrationService:327` 口径)
+2. CI 排除清单是否按「双向差集脚本」重构?(否则 G11 永远 ❌)
+3. 251 节点 LLM Key 是否可换独立配额?(G7 卡在 429)
+4. `GrowthPublicEndpointMappingTest`(5E,176s 超时型)是否入库?长期 `??` 状态会让「零漂移」口径失去纯度。
+
+---
+
+## 四、本轮实测数字(供核对)
+
+| 套件 | 例数 | 过 | 红 | 跳 |
+|---|---|---|---|---|
+| 后端 JUnit | 3057 | 3030 | 23 | 4 |
+| 管理端 Jest | 116 | 116 | 0 | 0 |
+| 前端 Jest | 624 | 609 | 3 | 12 |
+| langgraph@251 | 51 | 46 | 5 | 0 |
+| 浏览器 E2E | 30 | 28 | 0 | 2 |
+| 线上 API(新增入闭环) | 32 | 25 | 0 | 7 |
+| 根级黑盒 | 23 | 7 | 16 | 0 |
+| **合计** | **3933** | **3861(98.17%)** | **47** | **21** |
+
+报告全文:`docs/tdd/orchestration/report-test-2026-11-12-R170.md`
+执行记录:`docs/tdd/automation-run-2026-11-12-R170.md`

+ 355 - 0
docs/tdd/orchestration/report-test-2026-11-12-R170.md

@@ -0,0 +1,355 @@
+# 测试负责人工作汇报(R170 轮)
+
+> 本汇报按《测试负责人工作汇报模板》填写。R170 为**全量实跑轮**:三节点派发(189 admin 浏览器 / 251 langgraph pytest / 本机串行后端全量 + Jest + 浏览器 + **新增 API 契约层**),所有维度均为本轮新实测。
+> 对比基线 R169(13:50):窗口 `8524533c8..efc730771` 仅 cfc-web 构建元数据(`package-lock`/CHANGELOG/`.last_build_commit`)与研发 docs,**零功能源码变更** → 后端结论可比。
+> 本轮最大增量:① 首次把 **API 非功能层(32 例:冒烟 / 安全 / 性能兼容 / 追溯契约)** 纳入常规闭环;② 修正 **3 处用例自身缺陷**(2 例假红转绿、1 例按新需求改为显式 skip);③ 新发现 **B-170-1**(`activities.approved_count` 回填 SQL 恒失败,28/28 上下文全灭)。
+
+---
+
+## 基本信息
+
+| 项目 | 内容 |
+|------|------|
+| 汇报人 | 测试负责人(自动化测试闭环任务) |
+| 汇报周期 | 2026年11月12日 14:11 ~ 15:20(第 170 轮) |
+| 基线 | `cfclub @ 8524533c8`(R169 结论点)→ `efc730771`(`pull --rebase --autostash` 后) |
+| 窗口内需求变更 | **零功能源码变更**:仅 cfc-web 版本号/CHANGELOG + `docs/tdd`(9 文件 +390/−13) |
+| 负责模块/单元 | 全站回归:cfc-backend JUnit 全量 · 管理端 admin@189 · 小程序 H5/shop/health/dx@本机 · cfc-langgraph@251 · 前端/Web Jest · 根级黑盒 · **线上 API 非功能层** |
+
+---
+
+## 一、本轮工作数据汇总
+
+### 1.1 测试用例建设
+
+| 指标 | 数量 | 填写说明 |
+|------|------|----------|
+| **本轮新增测试用例数\*** | **0 个**(新增**执行**32 例为既有资产首次入闭环,非新建) | SRS/场景闸门窗口零变更(`git diff 8524533c8..HEAD -- docs/superpowers/specs tests/test-cases` = 空)→ G12 合规,无需因变更更新用例 |
+| **本轮验证执行测试数\*** | **3933 个**(全部本轮实跑) | 后端 JUnit 3057 + 前端 Jest 624 + 管理端 Jest 116 + langgraph 51 + 浏览器 30(28 过 + 2 跳)+ **线上 API 32(25 过 + 7 跳)** + 根级黑盒 23 |
+| **用例通过率** | **98.17%**(3861/3933;剔除 skip **98.80%**) | 红 47 / 跳 25,明细见 §5.2 |
+| **用例台账** | 15 份台账 + README(`tests/test-cases/`) | 唯一 TC 编号 367;本轮为台账补 **§10 锚点双向核对**(见 §3.4) |
+
+### 1.2 会话与任务分发
+
+| 指标 | 数量 | 填写说明 |
+|------|------|----------|
+| **分派任务数\*** | **3 个** | R170-1 → `opencode-189`(admin-desktop **5/5 EXIT=0**,1.4m);R170-2 → `opencode-251`(langgraph pytest **46/51**,129.43s);R170-3 → 本机串行(后端全量 45m + 三套 Jest + 本机浏览器 + API 层) |
+| **恢复智能体会话数\*** | 0 个 | 189 首派即 5/5(B-153-1 销号后第 10 轮);251 首派即跑完 |
+| **当前在用会话数** | 0 个 | 全部收尾,无残留浏览器任务(符合「每节点仅一任务」约束) |
+
+### 1.2b 各会话当前工作状态列表
+
+| 会话标签 | 会话 ID/地址 | 当前状态 | 当前任务 | 静默/空闲时长 | 工作目录 | 备注 |
+|----------|--------------|----------|----------|---------------|----------|------|
+| `opencode-189` | 192.168.16.189:4090 | done | admin-desktop UI E2E | 已完成(1.4m) | `C:\code\cfc\tests` | **5/5 EXIT=0**;节点 HEAD `8524533c8`、spec blob `4703d73fb…` **与本机 HEAD 完全一致** → 跨节点可比 |
+| `opencode-251` | 192.168.16.251:4096 | done | langgraph pytest 51 例 | 已完成(129.43s) | `/sc-data/cfc/cfc-langgraph` | **46 过 / 5 红**;节点 HEAD `efc730771` = 本机;`EXIT=` 未落盘(B-126 第 5 轮复现) |
+| `local-browser` | 本机进程 | done | fe / shop / health / dx | 已完成(1.0m + 8.3s + 6.3s + 跳过) | `d:\cfc\tests` | fe **6/6**、shop **4/4**、health **13/13**、dx **2 skip**(B-DX-1 端点 404) |
+| `local-api` | 本机进程 | done | 线上 API 冒烟/安全/性能兼容 | 已完成(9.8s + 10.1s + 15.1s) | `d:\cfc\tests` | api-smoke **4/4**、security **11/11**(修正后)、perf-compat **4 过 / 3 跳** |
+| `local-mvn` | 本机进程 | done | cfc-backend 全量 | 已完成(**45m**,14:15→15:00) | `d:\cfc\cfc-backend` | 363 类 / 3057 例 / 3030 过 / 17F+6E+4S = **99.12%**;编译 0 错、dump 0 |
+| `local-jest` | 本机进程 | done | cfc-frontend + cfc-web + 根级 | 已完成(32.2s + 23.5s + 18.2s) | `cfc-frontend` / `cfc-web` / `tests` | fe 609/3/12、web 116/116、根级 7/16(9082 未起,B-36 第 56 轮) |
+
+### 1.3 问题处置
+
+| 指标 | 数量 | 填写说明 |
+|------|------|----------|
+| **新测试发现问题数\*** | **1 个(产品)+ 4 个(测试资产/度量)** | 产品:**B-170-1** `activities.approved_count` 回填 SQL 恒失败(P2 数据一致性);测试资产:B-170-2(2 处用例缺陷,已自修)、B-170-3(用例未随会员门控更新,已改 skip)、B-170-4(锚点双向失真)、B-170-5(251 chat 挂死) |
+| **已解决测试相关问题数\*** | **3 个** | ① `TC-A-N05` 探针选错(拿登录接口当特权入口)→ 改 `/api/admin/families/overview` → **绿**;② `TC-P-N11` 路径错(`/api/assessment` → 真实 `/api/health`)+口径错(用 HTTP 码判业务拒绝)→ 改业务码判据 → **绿**;③ `TC-P-N02/X-N01` 未随 S-cfc-163 更新 → **假红转显式 skip**(不假绿) |
+| **待解决测试相关问题数** | 7 个(P0×1 / P1×4 / P2×2) | ① B-142-2 / B-170-5 langgraph 上游 429 与 chat 挂死(P0);② B-152-1/2 CI 清单双向失真(P0/P1);③ B-156-1 建表 SQL 无效(P1);④ B-162-1 未入库测试文件(P1);⑤ S-cfc-151 剩余 1 红(P1);⑥ B-170-1 approved_count 回填(P2);⑦ B-36 根级 16 红(P2,环境型) |
+
+### 1.4 本轮 git 提交与推送证据(真理证据)
+
+| 提交 sha | 关联任务/能力键 | 阶段 | 触及文件类型 | 目标分支 | 已推送远端 | 备注 |
+|----------|-----------------|------|--------------|----------|------------|------|
+| 见 §7 | 全站巡检 R170 | 汇报 | 测试文档(报告/run/通知/blockers)+ 测试资产(3 处用例修正 + 追溯矩阵附录) | cfclub | ✅ 已推送 origin/cfclub | **结果有变化**(API 层 +32 例、3 处用例修正、后端零漂移第 17 次、lg 3→5 红、新增 B-170-1)→ 按规则提交并通知研发 |
+
+### 1.5 状态机阶段分布(全局进度)
+
+| 阶段 | 键数 | 备注 |
+|------|------|------|
+| UNTRACKED | 0 | 需求侧 ⏳ 0 键(窗口零新增规格) |
+| RED | 0 | 测试侧本轮零新建红单测(3 处为既有用例修正而非新键) |
+| YELLOW | 63 | 研发侧口径(承袭 R169) |
+| GREEN | 8 | 已实现待部署归集 |
+| DONE | 15 | 已归档 |
+| 合计 | 86 | 本轮零流转(无新契约键入池) |
+
+---
+
+## 二、历史审查记录(阶段 -1 记忆召回)
+
+| 项 | 内容 |
+|---|---|
+| 上次审查 | **R169(2026-11-12 13:50)**,结论「不通过(承袭,正向改善)」 |
+| 遗留缺口 | 后端 23 红(存量 17 + 预期 RED 6)· langgraph 3 红(429)· 根级 16 红(9082 未起)· CI 清单漏拦 · G9 锚点 18.5% · B-156-1 / B-162-1 未修 |
+| 本次是否改善 | **是**:后端 23 红逐例持平(第 17 次零漂移)→ 无回归;langgraph **3→5 红(恶化,上游 429)**;API 层新增 32 例(25 过);G9 口径更正为 **20.7%**(环比 +2.2pp 属口径细化而非真实提升) |
+| 教训应用 | ① 沿用「Maven 工程根在 `cfc-backend`」;② 沿用「远程 kick 分离 + 单次读日志」;③ 本轮**新增**教训:判定用例红之前必须先问「是用例错还是产品错」——本轮 3 处红中 3 处全是用例错,产品 0 新增缺陷 |
+
+> 本次为该对象**第 170 次**审查,有历史记录;上述内容仅作对照,**本次结论全部来自本轮实测**。
+
+---
+
+## 三、项目画像与覆盖矩阵
+
+### 3.1 项目画像(承袭并本轮核验)
+
+| 维度 | 内容 |
+|---|---|
+| 业务定位 | 家庭教育「五维能量」(身/心/智/行/富)测评 + 成长规划 + 商城 + 健康报告解析的微信小程序 + Web 管理端全栈系统 |
+| 技术栈 | 后端 Spring Boot 2.7.18 + MyBatis-Plus + Java 8(端口 9082)· 小程序 uni-app Vue2 · 管理端 Vue2 + Element UI(8082)· AI 服务 FastAPI + LangGraph + ChromaDB(9000) |
+| 模块结构 | `cfc-backend` / `cfc-frontend` / `cfc-web` / `cfc-langgraph` / `dan/danshop` / `tests`(分层测试) |
+| 关键路径 | 登录(微信/direct-register)→ 家庭与成员 → 五维测评/能量 → 成长规划(规划师)→ 商城订单 → 健康报告上传解析 → AI 对话(**S-cfc-163 起仅会员可见**) |
+| 测试现状 | 后端 363 测试类 / 3057 例;前端 Jest 624;管理端 Jest 116;浏览器 E2E 30;API 契约层 32;langgraph pytest 51;根级黑盒 23 |
+| 质量目标 | 全量通过率 ≥98%、浏览器 E2E 100%、langgraph 100%、锚点覆盖率 ≥80%、CI 硬门禁可用 |
+| 风险点 | LLM 上游配额(429)· 数据库迁移静默失败 · CI 排除清单失真 · 用例/台账命名双轨 |
+
+### 3.2 20 类测试覆盖矩阵(全类不省略)
+
+| # | 测试类型 | 状态 | 本轮证据 |
+|---|---|---|---|
+| 1 | 单元测试 | ✅ 已发现 | 后端 363 类 / 3057 例(surefire XML 全产出);前端 Jest 624;管理端 116 |
+| 2 | 测试覆盖率 | ⚠️ 部分 | cfc-web 有 `--coverage` 配置且历轮记录 Lines 9.95%;**后端 JaCoCo 未配置** → 后端行覆盖仍无法量化(延续缺口 G-170-3) |
+| 3 | 变异测试 | ❌ 未发现 | 仓库无 PIT/Stryker 配置;本轮未实测(离线档无工具) |
+| 4 | 集成测试 | ✅ 已发现 | 后端 `integration/**`(含 `AIChatControllerGateTest` 18 例、`GrowthPublicEndpointMappingTest` 5E);根级 `tests/integration` 16 红均因 9082 未起 |
+| 5 | 契约测试 | ✅ 已发现 | 后端 `*ContractTest` 多组:`DimensionRegistryMapContractTest`(3 红) / `TaskReviewScP11ContractTest`(1 红) / `ServiceRoleApplyBindInviteContractTest`(6/6) / `MembershipChatMemberGateContractTest`(6/6) / `HealthDataSourceBridgeContractTest`(16/16) |
+| 6 | 接口 API 测试 | ✅ 已发现(**本轮扩容**) | 线上 API 层 32 例:api-smoke 4/4、security 11/11、perf-compat 4 过 3 跳、traceability 6 过 4 跳 |
+| 7 | 系统 / 端到端 | ✅ 已发现 | 浏览器 28/28(189 admin 5 + 本机 fe 6 / shop 4 / health 13)+ 2 skip(dx,端点 404) |
+| 8 | 验收 UAT | ⚠️ 待核实 | 无独立 UAT 用例库;以 `tests/test-cases/*.md` 15 份台账近似验收口径,未与业务方逐条签署 |
+| 9 | BDD 规格 | ❌ 未发现 | 无 `.feature` / Cucumber 资产 |
+| 10 | 冒烟测试 | ✅ 已发现 | `api-smoke.spec.js` 4/4(自注册链路 / 成员列表 / 能量概览 / 越权 403)+ 根级冒烟 7/23 过 |
+| 11 | 回归测试 | ✅ 已发现 | 本轮全量回归 3933 例;后端与 R169 **逐类逐例零漂移(第 17 次)** |
+| 12 | 探索性测试 | ⚠️ 部分 | 本轮以「安全/健壮性探针」形式执行(畸形上传、空参登录、并发、UA 兼容),未形成独立探索性会话记录 |
+| 13 | 性能测试 | ⚠️ 部分 | 仅冒烟级:家庭列表 **72ms**、20 并发无 5xx(`429=0` 说明限流器未部署);**无真实压测**(execute 档需联网授权,未执行) |
+| 14 | 可靠性测试 | ⚠️ 部分 | 观察到 `DatabaseInitializer` 迁移 28 次静默失败(B-170-1/B-156-1);无故障注入 / 长稳测试 |
+| 15 | 安全测试(SAST/DAST/依赖) | ✅ 已发现(部分) | DAST 类 11 例全绿(IDOR 403、越权提权拒绝、XSS 不回显、上传被拒);**SAST / 依赖漏洞扫描未执行**(需联网,未授权) |
+| 16 | 兼容性测试 | ✅ 已发现 | 多端 UA 一致性 1 例通过;浏览器仅 Chromium(无 Firefox/WebKit 矩阵) |
+| 17 | 可用性测试 | ❌ 未发现 | 无可用性量表 / 用户任务完成率采集 |
+| 18 | 无障碍测试 | ❌ 未发现 | 无 axe / WCAG 断点;小程序 TabBar 语义未验证 |
+| 19 | QA 流程 | ⚠️ 部分 | 有 `blockers.md` / `automation-status.md` / 台账;**CI 硬门禁因排除清单失真而恒红(G11)** |
+| 20 | 质量指标 | ⚠️ 部分 | 通过率/红例/跳例可取;**锚点率与场景映射两套口径长期并存(B-124/B-170-4)** |
+
+**结论**:20 类中 ✅ 已发现 8 类、⚠️ 部分 8 类、❌ 未发现 4 类(变异、BDD、可用性、无障碍)。「未发现」仅表示扫描未命中,**不等于项目未做**。
+
+### 3.3 本轮实测数据总表
+
+| 套件 | 命令/节点 | 例数 | 过 | 红 | 跳 | 耗时 |
+|---|---|---|---|---|---|---|
+| 后端 JUnit | `cd cfc-backend && mvn -o -B clean test`(本机) | 3057 | 3030 | **23**(17F+6E) | 4 | **45m** |
+| 管理端 Jest | `cfc-web && npx jest` | 116 | 116 | 0 | 0 | 23.5s |
+| 前端 Jest | `cfc-frontend && npx jest` | 624 | 609 | 3 | 12 | 32.2s |
+| langgraph | 251 `POST /api/shell` → pytest | 51 | 46 | **5** | 0 | 129.4s |
+| 浏览器 E2E | 189 + 本机 | 30 | 28 | 0 | 2 | 1.4m + 1.0m |
+| 线上 API | `playwright.config.api.js` | 32 | 25 | 0 | 7 | 43.5s |
+| 根级黑盒 | `tests && npx jest` | 23 | 7 | **16** | 0 | 18.2s |
+| **合计** | — | **3933** | **3861** | **47** | **21** | **≈48m** |
+
+> 通过率 **98.17%**;剔除 skip **98.80%**。环比 R169(3901/3838=98.38%):+32 例(API 层)、+23 过、红 45→47(langgraph 3→5)。
+
+### 3.4 可执行锚点双向核对(本轮新增口径)
+
+| 指标 | 数值 |
+|---|---|
+| 台账唯一 TC 编号 | 367 |
+| 可执行锚点唯一编号 | 161 |
+| 双向命中 | **76** |
+| 孤儿锚点(执行有、台账无) | **85** |
+| 台账空转(有编号、无执行) | **291** |
+| 严格锚点覆盖率(G9) | **76/367 = 20.7%** |
+| 锚点可追溯率(新增 G9-b) | **76/161 = 47.2%** |
+| 场景→用例映射(G14) | **89/153 = 58.2%** |
+
+**根因**:可执行资产并存两套命名——`TC-<域>-U<NN>`(UI/用户态,如 `TC-A-U01`、`TC-H-U02`)与 `TC-<域>-<标签>-<NN>`(如 `TC-LANDSCAPE-01`),台账只登记 `TC-<域>-<NN>` 单套 → 凡走 UI/契约命名的用例全部落不到台账。**这不是 85 个孤立缺漏,而是命名规范未双向对齐**。明细已写入 `tests/TRACEABILITY-MATRIX.md` §10。
+
+---
+
+## 四、测试方案与执行档
+
+| 项 | 内容 |
+|---|---|
+| 目标 | 全量用例通过 + 覆盖 SRS/场景(含异常流);本轮聚焦「用例本身是否可信」与「需求变更后用例是否同步」 |
+| 范围 | cfc-backend 全量 · cfc-web · cfc-frontend · tests(根级 + e2e-live + e2e/api)· cfc-langgraph@251 |
+| 覆盖维度 | 功能(单元/集成/契约/API/E2E)、非功能(安全/性能/兼容)、流程与度量 |
+| **执行档** | **review(离线档,默认)**——未安装依赖、未启停服务、未联网装包、未改生产代码 |
+| 未授权未执行 | 依赖漏洞扫描 / 真实压测 / SAST 商业扫描(需联网,本轮无显式联网授权) |
+| 排除项 | 微信真机、小程序审核链路、支付真实扣款(涉资金,需人工介入) |
+| 本机环境探针 | JDK `1.8.0_25` ✅ · Maven `3.6.1` ✅ · Node `v22.22.2` ✅ · D 盘可用 **87G** ✅ · 9082 **未监听**(→ B-36 环境红) |
+| 节点探针 | 251:4096 **200** / 251:9000 `/health` **ok** · 189:4090 **200** · 204:4090 **000**(B-95 第 30 轮) |
+
+> 自动化无人值守运行,按既定默认档位(review 离线)执行;未越过任何需联网/启停服务的边界。
+
+---
+
+## 五、域结论表
+
+| 审查维度 | 负责域 | 结论 | 关键证据 |
+|---|---|---|---|
+| 单元·组件 | 单元域 | ⚠️ 有条件通过 | 3057 例 99.12%;12 红类中 11 类为存量已知;**GrowthPublicEndpointMappingTest 5E 仍为未入库文件**(B-162-1) |
+| 集成·接口·契约 | 集成域 | ✅ 通过 | 契约类多组全绿(HB 16/16、ServiceRole 6/6、MembershipChat 6/6);API 层 32 例 25 过 7 跳、**0 红** |
+| 系统·验收·BDD | 系统域 | ⚠️ 部分 | E2E 28/28;无 BDD 资产;UAT 未签署 |
+| 测试保障(冒烟/回归/探索) | 保障域 | ✅ 通过 | 冒烟 4/4;全量回归零漂移第 17 次;探索性以安全探针形式落地 3 处用例缺陷发现 |
+| 性能·可靠性 | 性能域 | ⚠️ 未实测(降级为设计) | 仅冒烟(72ms / 20 并发无 5xx);真实压测未授权未执行;**限流器未部署(20 次爆发 429=0)** |
+| 安全 | 安全域 | ✅ 通过(DAST 面) | 匿名 6 端点全 401;IDOR 403;越权提权被拒;XSS 不回显;畸形上传业务拒绝且不落库 |
+| 兼容·体验·无障碍 | 兼容域 | ⚠️ 部分 | UA 一致性通过;仅 Chromium 单引擎;无障碍 0 资产 |
+| 流程·度量·代码 | 流程域 | ❌ 不通过 | CI 排除清单**漏拦 5 个当前红类**(`backend-blocking-junit` 必红)+ **26 个已转绿类仍被排除**;锚点覆盖率 20.7% |
+
+---
+
+## 六、五要素问题清单
+
+### 问题 1(P2·数据一致性,**本轮新发现 B-170-1**)
+
+| 要素 | 内容 |
+|---|---|
+| ① 问题描述 | 后端每次启动,`DatabaseInitializer` 回填 `activities.approved_count` 的 UPDATE 语句**必然语法失败**,本轮全量 45 分钟内 28 次上下文启动 **28/28 全失败**(`SQLSyntaxErrorException: Unknown column 'a.id' in 'where clause'`)。该列唯一写入点即此语句,创建活动时置 0(`ActivityService:188`)、审批时不递增 → **持久化列恒为 0**。 |
+| ② 测试 bug 点 | `cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java:2261`:`UPDATE activities a SET a.approved_count = (SELECT cnt FROM (SELECT COUNT(*) … WHERE r.activity_id = a.id …) t)` —— MySQL 不允许 `SET a.col` 带别名赋值,且派生表内不能引用被更新表的别名列。异常被 `catch (Exception e) { log.warn(...) }` 吞掉(:2263-2265),日志仍继续打印「数据表检查完成」。 |
+| ③ 最优修改方案 | 改为 JOIN 更新:<br>`UPDATE activities a JOIN (SELECT activity_id, COUNT(*) cnt FROM activity_registrations WHERE status='approved' GROUP BY activity_id) t ON t.activity_id = a.id SET a.approved_count = t.cnt`<br>(MySQL 允许 JOIN 形式带别名);并把该语句从 `try` 块中拆出单独 try-catch,失败时 `log.error` 而非 `warn`,避免与其他 DDL 混在一次事务性判断里。 |
+| ④ 修改目的 | 让存量活动的报名人数统计可被回填,避免任何按 `activities.approved_count` 做的统计/排序/看板失真;同时让迁移失败在日志层级可被监控捕获(当前 `warn` 级别 + 无告警 = 永久静默)。 |
+| ⑤ 修改后能否闭环 | **能**。复测方式:`cd cfc-backend && mvn -o -B -Dtest=DatabaseInitializer*Test test` + 全量启动日志 `grep -c "Unknown column 'a.id'"` 应为 **0**;再断言 `SELECT COUNT(*) FROM activities WHERE approved_count <> (SELECT COUNT(*) FROM activity_registrations r WHERE r.activity_id=activities.id AND r.status='approved')` 为 0。通过标准:两者同时成立。 |
+
+### 问题 2(P1·测试资产,**已自修 B-170-2a**)
+
+| 要素 | 内容 |
+|---|---|
+| ① 问题描述 | 安全用例 `TC-A-N05` 恒红:把 `/api/admin-auth/login`(**登录入口,本就应匿名可访问**)当「特权接口」探测,空参返回 `code=400 手机号和验证码不能为空` → 断言 401/403 失败。属**用例设计错误**,长期被记成「后台鉴权缺口」。 |
+| ② 测试 bug 点 | `tests/e2e/api/nonfunc-security.spec.js:75-78`(原 `apiPost('/api/admin-auth/login', {}, null)`)。 |
+| ③ 最优修改方案 | 探针改为真实特权面 `/api/admin/families/overview`(S-cfc-143/144 收口后匿名应 401)。已于本轮实施。 |
+| ④ 修改目的 | 让「后台鉴权」断言对准真正的受保护资源,消除恒红假缺陷对门禁的噪声污染。 |
+| ⑤ 修改后能否闭环 | **已闭环**:`IDOR`/`TC-A-N05` 一组复跑 **11/11 全绿**(原 9/11);直连探证 `POST /api/admin/families/overview` 匿名 = `HTTP 401 + code 401 请先登录`。 |
+
+### 问题 3(P1·测试资产,**已自修 B-170-2b**)
+
+| 要素 | 内容 |
+|---|---|
+| ① 问题描述 | 上传安全用例 `TC-P-N11` 恒红,且**红的原因不是安全问题**:① 路径 `/api/assessment/report/upload` 线上不存在(恒 404,真实路由由 `HealthReportController` 类级 `@RequestMapping("/api/health")` 决定);② 判据用「HTTP 非 200」判「被拒」,而本项目统一 `Result{code}` + **HTTP 200 承载业务码**。 |
+| ② 测试 bug 点 | `tests/e2e/api/nonfunc-security.spec.js:121-132`(路径 + `expect(status).not.toBe(200)` + 4xx 白名单)。 |
+| ③ 最优修改方案 | 路径改 `/api/health/report/upload`;判据改为「HTTP < 500 且 业务 `code ≠ 200`」,并断言 `data.reportId` 为空(被拒不得落库)。已于本轮实施。实测行为:非 multipart → `code=400 请求格式错误`;伪 PDF → `code=500 PDF解析失败`;两者 HTTP 均 200、均未落库。 |
+| ④ 修改目的 | 让安全断言与项目响应约定一致,避免「端点写错」被误读为「上传未被拦截」的安全缺口。 |
+| ⑤ 修改后能否闭环 | **已闭环**:该例复跑绿;并额外取证「畸形请求不触发 HTTP 5xx」。 |
+
+### 问题 4(P1·需求对齐,**已改 skip + 缺口登记 B-170-3**)
+
+| 要素 | 内容 |
+|---|---|
+| ① 问题描述 | 性能用例 `TC-P-N02/X-N01`(并发不串数据)恒红:非会员调用 `/api/ai/health-coach/send` 返回 `code=46003「该功能为会员专享」`。根因是 **S-cfc-163「聊天仅会员可见」**(`dc5522d09`)落地后契约变更,用例未同步更新 → **假红**。 |
+| ② 测试 bug 点 | `tests/e2e/api/nonfunc-perf-compat.spec.js:92-107`(断言 `code===200`,未处理会员门控)。 |
+| ③ 最优修改方案 | 先发 1 次探针,若 `code=46003` 则 `test.skip()` 并写明「会员门控生效,缺会员夹具」;否则维持原断言。已于本轮实施。 |
+| ④ 修改目的 | 满足「SRS/场景变更必须同步用例」的要求;同时**不以改断言方式制造假绿**——门控真实存在,只是当前夹具无法构造会员会话。 |
+| ⑤ 修改后能否闭环 | **部分闭环**:假红消除(perf-compat 4 过 / 3 跳 / 0 红);真正闭环需补「会员夹具」→ 登记 **G-170-1**,闭环方式:测试侧新增 `registerMember()` 夹具(开通会员或直改会员态测试账号)后取消 skip。 |
+
+### 问题 5(P0·环境,**延续 B-142-2 / 新 B-170-5**)
+
+| 要素 | 内容 |
+|---|---|
+| ① 问题描述 | langgraph@251 本轮 **46/51**(R169 48/51,恶化 2);同时 live `POST /api/v1/chat` **90s 零字节(000)**、`/api/v1/analyze` **200 / 8.66s / 1131B**(已恢复)。日志实证 **7× `RateLimitError 429 (AgnesAI_error)` + 3× `401 api key ****ab12 invalid`**。对照生产 `https://ai.etotem.com.cn/api/v1/chat` = **200** → 仅 251 节点 LLM 上游被掐。 |
+| ② 测试 bug 点 | 5 红:`test_auto_tag_content_relevance` / `test_auto_tag_short_content` / `test_auto_tag_empty_content`(断言 `500==200`)+ `test_classify_analysis` / `test_classify_recommend`(`intent_classifier.py:62` 捕获异常后**静默兜底 chat**,把 429 伪装成「分类错误」)。 |
+| ③ 最优修改方案 | 运维侧:为 251 配置独立 API Key / 提升配额,或加本地限流+排队;研发侧:`intent_classifier.py:62` 改为**向上抛出可识别错误**(如 `LLMUnavailable`)而非默认 `chat`,让 429 在断言层可见。 |
+| ④ 修改目的 | 让 langgraph 51 例回到 51/51,并消除「429 伪装成分类错误」的诊断噪声。 |
+| ⑤ 修改后能否闭环 | **能**。复测:251 `pytest -q --ignore=tests/qna --ignore=tests/test_chat.py` 连续 **2 轮 51/51** 才销号(当前观察 0/2;R167 起共 4 轮未达)。另需修 B-126:kick 串用单引号包 `bash -c '…; echo EXIT=$? >> LOG'`,否则 `EXIT=` 仍不落盘。 |
+
+### 问题 6(P0/P1·流程,**延续 B-152-1/2 第 6 轮未修**)
+
+| 要素 | 内容 |
+|---|---|
+| ① 问题描述 | `.gitlab-ci.yml:61` 的 `BACKEND_OBSERVED_CLASSES`(33 类)与实测红类(**12 类**)双向失真:**漏拦 5 个当前红类**(`GrowthPublicEndpointMappingTest`、`UserServiceWechatLoginContractTest`、`UserServiceWechatLoginTest`、`TaskReviewScP11ContractTest`、`TeamProfitCronContractTest`)→ `backend-blocking-junit` **必然失败**;同时 **26 个已转绿类仍被排除** → 硬门禁覆盖被人为缩小。 |
+| ② 测试 bug 点 | `.gitlab-ci.yml:61` 静态清单(无自动同步机制)。 |
+| ③ 最优修改方案 | ① 立即把 5 个漏拦类加入清单(短期止血);② CI 增一条「红类集合 ↔ 清单集合双向差集必须为空」的断言脚本(长期治本),差值非空直接 fail 并打印两侧差集。 |
+| ④ 修改目的 | 让 CI 硬门禁恢复「能红能绿」的真实约束力;当前状态是「必红」→ 团队会直接忽略该 job,门禁形同虚设。 |
+| ⑤ 修改后能否闭环 | **能**。复测:推送后观察 `backend-blocking-junit` 由 fail → pass(或仅剩预期 RED 时 fail 且日志只含预期类);差集脚本输出两侧均为空。 |
+
+### 问题 7(P1·数据/Schema,**延续 B-156-1 第 6 轮未修**)
+
+| 要素 | 内容 |
+|---|---|
+| ① 问题描述 | `schema.sql:5345-5357` `CREATE TABLE nutrition_age_dosage` 列清单无 `product_id`,却在 `:5356` 声明 `INDEX idx_product (product_id)` → 建表**恒失败**(本轮 28/28 上下文全部 `Key column 'product_id' doesn't exist in table`),`DatabaseInitializer` catch 后仍打印「数据表检查完成」→ **该表从未创建成功**。 |
+| ② 测试 bug 点 | `cfc-backend/src/main/resources/schema.sql:5356`;实体 `NutritionAgeDosage.java` 另有 `updateTime` 而 DDL 无 `update_time`(建表修好后会立刻 `Unknown column`)。 |
+| ③ 最优修改方案 | 二选一:① 补列 `product_id BIGINT NOT NULL` + 同步实体;② 若该索引本不必要,删 `INDEX idx_product (product_id)`。同时补 `update_time DATETIME`。并给 `DatabaseInitializer` 加「建表失败 → 汇总报告 + 非零退出/告警」。 |
+| ④ 修改目的 | 消除「表不存在但系统认为已就绪」的隐患;营养按年龄剂量功能一旦上线即会 `Table doesn't exist`。 |
+| ⑤ 修改后能否闭环 | **能**。复测:全量启动日志 `grep -c "Key column 'product_id' doesn't exist"` = **0**,且 `SHOW CREATE TABLE nutrition_age_dosage` 有返回;再跑一次全量后端 0 新增红。 |
+
+---
+
+## 七、覆盖缺口清单
+
+| 编号 | 缺口类型 | 现状 | 风险 | 建议测试设计 | 优先级 | 闭环方式 |
+|---|---|---|---|---|---|---|
+| G-170-1 | 夹具缺口 | 无「会员账号」夹具 | AI 教练(S-cfc-163 后仅会员可用)并发/隔离/正常链路无法验证,3 例只能 skip | 新增 `registerMember()`:直改会员态或调用开通接口 | **P0** | 夹具落地后取消 skip,perf-compat 恢复到 7/7 |
+| G-170-2 | 锚点覆盖率 | 76/367 = 20.7%(G9-b 47.2%) | 79% 台账用例无执行证据 | 给台账加「锚点别名列」,把 85 孤儿按语义归并 | **P1** | 覆盖率 ≥80% 且孤儿 = 0 |
+| G-170-3 | 后端覆盖率 | 无 JaCoCo | 无法量化后端行/分支覆盖,G1 只能靠通过率 | pom 加 `jacoco-maven-plugin`,`mvn test` 出报告 | **P1** | 报告产出且行覆盖 ≥60% |
+| G-170-4 | 变异测试 | 无 PIT/Stryker | 用例「通过但断言弱」不可见 | 先对 `*ContractTest` 覆盖的 3 个核心 Service 做 PIT 试点 | P2 | 变异得分 ≥50% |
+| G-170-5 | 真实压测 | 仅 20 并发冒烟,且 `429=0`(限流器未部署) | 活动名额/秒杀场景无容量证据 | k6/JMeter 场景脚本(下单、报名、AI 对话),需联网授权 | P2 | execute 档授权后执行 |
+| G-170-6 | 安全扫描 | 仅 DAST 面 11 例 | 依赖漏洞、SAST 未覆盖 | OWASP Dependency-Check + Semgrep(需联网) | P1 | 授权后执行并归档报告 |
+| G-170-7 | 无障碍 | 0 资产 | 小程序/管理端无 WCAG 证据 | axe-core 注入 e2e-live,先管 5 个核心页 | P3 | 关键页 0 critical |
+| G-170-8 | 环境型红 | 根级 16 红(9082 未监听) | 掩盖真实回归 | 测试前自动拉起 `cfc-backend`(探活 → 缺失则启) | P2 | 根级由 7/23 → 23/23 |
+
+---
+
+## 八、门禁结论
+
+| 门禁 | 阈值 | 实测 | 判定 |
+|---|---|---|---|
+| G1 全量通过率 | ≥98% | **98.17%**(剔 skip 98.80%) | ✅ |
+| G2 红例总数 | =0(预期 RED 除外) | **47**(后端 23 / 根级 16 / lg 5 / fe 3) | ❌ |
+| G3 前端 Jest | 红 ⊆ 预期 RED | 609 过 / 3 红(全 S-cfc-132/133 预期 RED)/ 12 跳 | ✅ |
+| G4 管理端 Jest | 100% | **116/116** | ✅ |
+| G5 编译与完整性 | 0 编译错 / 0 dump | 0 / 0(surefire 红仅测试层) | ✅ |
+| G6 浏览器 E2E | 100% 执行项 | **28/28**(+ dx 2 skip,不假绿) | ✅ |
+| G7 langgraph | 100% | **46/51 = 90.2%** | ❌ |
+| G8 鉴权契约 | 匿名受保护端点全 401 | **6/6 = 401** | ✅ |
+| G9 锚点覆盖率 | ≥80% | **20.7%** | ❌ |
+| G10 多节点派发 | 每节点单任务成功 | 189 5/5 + 251 51 例 + 本机 —— **三节点全通** | ✅ |
+| G11 CI 硬门禁 | 可用(能红能绿) | 漏拦 5 红类 + 26 陈旧类 → **必红** | ❌ |
+| G12 SRS 对齐 | 变更即更新用例 | 窗口零变更 → 0 新增用例 | ✅ |
+| G13 存量红漂移 | 0 漂移 | 17 例逐类逐例一致(**第 17 次**) | ⚠️ 零漂移但未清零 |
+| G14 场景→用例映射 | ≥80% | **58.2%**(89/153) | ❌ |
+
+### 综合结论:**不通过(承袭,结构改善 / 指标略降)**
+
+- **正向**:① 首次把 API 非功能层 32 例纳入闭环,**0 红**;② 修正 3 处用例自身缺陷(2 假红转绿 + 1 需求对齐 skip),使「红」更可信;③ 后端 3057 例与 R169 **逐类逐例零漂移(第 17 次)** → 证明 R169 采信期无漏检;④ 新增 B-170-1 产品缺陷 + 锚点双向核对口径(85 孤儿首次可见)。
+- **负向**:langgraph 3→5 红(上游 429 恶化)、通过率 98.38%→98.17%(分母 +22、lg 多 2 红)、G11 仍必红。
+- **复评条件**:G-170-1 会员夹具落地 + 251 连续 2 轮 51/51 + CI 双向差集脚本落地 + 后端存量 17 红清零 → 可升「有条件通过」。
+
+---
+
+## 九、附录
+
+### 9.1 复现命令
+
+```bash
+# 后端全量(工程根在 cfc-backend,不是仓库根)
+cd cfc-backend && JAVA_HOME=/d/jdk1.8.0_25 /d/maven/apache-maven-3.6.1/bin/mvn -o -B clean test
+
+# 三端 Jest
+cd cfc-web && npx jest
+cd cfc-frontend && npx jest
+cd tests && npx jest
+
+# 浏览器(必须在 tests/ 内跑,防 @playwright/test 双版本冲突)
+cd tests && npx playwright test --config=playwright.config.live.js --project=frontend-mobile
+cd tests && npx playwright test --config=playwright.config.live.js --project=shop-api
+cd tests && npx playwright test --config=playwright.config.health.js
+cd tests && npx playwright test --config=playwright.config.live.js --project=dx-bridge
+
+# 线上 API 非功能层
+cd tests && npx playwright test e2e/api/api-smoke.spec.js e2e/api/nonfunc-security.spec.js -c playwright.config.api.js --project=desktop
+cd tests && npx playwright test e2e/api/nonfunc-perf-compat.spec.js -c playwright.config.api.js --project=desktop
+
+# 251 langgraph(kick 分离 + 单次读日志)
+MSYS_NO_PATHCONV=1 node docs/tdd/tools/dispatch_251_pytest.cjs kick
+MSYS_NO_PATHCONV=1 node docs/tdd/tools/dispatch_251_pytest.cjs read
+
+# 189 admin E2E
+MSYS_NO_PATHCONV=1 node docs/tdd/tools/dispatch_189_admin.cjs
+```
+
+### 9.2 本轮慢类(耗时占全量 45m 的 15%)
+
+| 类 | 耗时 |
+|---|---|
+| `GrowthPublicEndpointMappingTest` | 176s(5E,超时型;且仍是未入库文件) |
+| `UserServiceFamilyNameLinkTest` | 154s |
+| `GuideApplicationServiceTest` | 150s |
+| `GuidePackageServiceTest` | 139s |
+
+### 9.3 日志噪声与稳定性观测
+
+- `cfc-scheduler` 线程行数 **8255 / 46357(17.8%)**;`SQLSyntaxErrorException` **980 次**,其中 `Key column 'product_id' doesn't exist` 与 `Unknown column 'a.id'` 各 **28 次**(= 上下文启动次数),`Duplicate key name` 系列各 28 次(幂等 index 重复,属正常但应由 `ensureIndex` 吞掉而非抛到日志)。
+- `dump` 文件 **0** 个 → 无 JVM crash。

+ 28 - 0
docs/tdd/test/blockers.md

@@ -1035,3 +1035,31 @@
 | **B-153-1-Update(第 9 轮销号维持)** | 189 admin U05 前置偶发 | 测试(观察项) | **维持销号**。连续 9 轮首轮即 5/5(本轮 1.2m)。 | 测试(R169 实派) | 2026-11-12 13:50 | 已销号 |
 | **B-167-1-Update(观察 2/3)** | 189 admin `account-roles` 502 | 环境/网络(P2) | **观察**。本轮首轮即 5/5(无 502)→ 连续 2 轮首跑 5/5,剩 1 轮观察。 | 测试(R169 实派) | 2026-11-12 13:50 | 观察中 2/3 |
 | **R169 门禁** | 全量 3901 / 3838 | 测试 | **不通过(承袭,正向改善)**。✅G1 98.38%(剔 skip 98.84%)/G3 609-624/G4 116-116/G5 编译(dump 0)/G6 28-28/G8 5-5(189 首绿)/G10 双节点(189 5/5 + 251 48/51)/G12 对齐零新增;❌G2 45 红(后端 23 + 根级 16 + lg 3 + fe 3)/G7 48-51(94.1%)/G9 锚点 18.5%/G11 CI 必红/G14;⚠️G13 存量 17 零漂移(第 16 次)。变化 = 后端 22F→17F、S-cfc-147 闭环、S-cfc-151 收窄、lg 4→3 红(全正向)。 | 测试(R169 闭环) | 2026-11-12 13:50 | 已记录 |
+
+---
+
+## R170 段(2026-11-12 15:20,新增)
+
+> **R170 口径(全量实跑轮,三节点派发 + API 层首次入闭环)**:189 admin **5/5 EXIT=0**(1.4m,spec blob `4703d73fb` = 本机)+ 本机 fe 6/shop 4/health 13 + dx 2 skip;cfc-web **116/116**;cfc-frontend 609/3/12(3 全 S-cfc-132/133 预期 RED);根级 7/16(B-36 第 56 轮);**langgraph@251 51 例 = 46 过 / 5 红(129.43s,全 AgnesAI 429)**;**后端全量实跑 45m00s(HEAD efc730771):3057 例 = 3030 过 / 23 红(17F+6E)/ 4 跳(99.12%)**;**新增线上 API 非功能层 22 例(19 过 / 3 跳 / 0 红)**。
+> **结论**:**不通过(承袭,结构改善 / 指标略降)**。变化项:① **API 层 22 例首次入闭环且 0 红**;② **3 处用例自身缺陷修正**(TC-A-N05 探针错 / TC-P-N11 路径+口径错 / TC-P-N02 未随 S-cfc-163 更新)→ 2 假红转绿 + 1 改 skip;③ 后端 23 红逐类逐例持平(**零漂移第 17 次**);④ langgraph 3→5 红(上游 429 恶化);⑤ **新发现 B-170-1**;⑥ 首次做锚点**双向**核对(85 孤儿可见)。全量 3933/3861 = 98.17%。
+
+| 编号 | 关联键/对象 | 类型 | 描述 | 经办 | 登记时间 | 状态 |
+|---|---|---|---|---|---|---|
+| **B-170-1** | `activities.approved_count` 回填 | 研发(P2 数据一致性) | **新增**。`DatabaseInitializer.java:2261` 的 `UPDATE activities a SET a.approved_count = (SELECT … WHERE r.activity_id = a.id …)` 语法非法(MySQL 不允许 `SET a.col` 带别名,派生表不可引用被更新表别名)→ 本轮 **28/28 次上下文启动全失败**(`Unknown column 'a.id'`)。该列唯一写入点即此语句 → **持久化列恒 0**(创建时 `ActivityService:188` 置 0、审批不递增)。消费侧 `ActivityRegistrationService:327` 为动态计算,前端暂不受影响,但任何按该列统计/排序的报表失真。 | 测试(R170 全量实测) | 2026-11-12 15:20 | 待处理 |
+| **B-170-2(已自修)** | API 安全用例 2 处缺陷 | 测试(P1 资产) | **已闭环**。① `TC-A-N05` 拿 `/api/admin-auth/login`(登录入口)当特权探针 → 改 `/api/admin/families/overview`(匿名 401);② `TC-P-N11` 路径 `/api/assessment/report/upload` 不存在(真实 `/api/health/report/upload`)+ 用 HTTP 码判业务拒绝(本项目 HTTP 200 承载业务码)→ 改判 `code≠200` 且 `HTTP<500` 且 `data.reportId` 空。修正后 security **11/11 全绿**(原 9/11)。 | 测试(R170 实修) | 2026-11-12 15:20 | **已解除** |
+| **B-170-3(已转 skip)** | `TC-P-N02/X-N01` 未随 S-cfc-163 更新 | 测试(P1 需求对齐) | **假红消除**。非会员调 `/api/ai/health-coach/send` 返 `code=46003 会员专享`(S-cfc-163/dc5522d09 落地)→ 原断言 `code===200` 恒红。改为探针识别门控后 `test.skip`(**不假绿**)→ perf-compat 4 过/3 跳/0 红。真正闭环需会员夹具 → **G-170-1**。 | 测试(R170 实修) | 2026-11-12 15:20 | 部分闭环(待夹具) |
+| **B-170-4** | 锚点台账双向失真 | 测试(P2 度量) | **新增**。台账唯一 TC **367** vs 可执行锚点 **161** vs 双向命中 **76** → 严格覆盖率 **20.7%**;**孤儿锚点 85**(执行有/台账无,占锚点 52.8%)、台账空转 **291**。根因:并存 `TC-<域>-U<NN>`(UI)与 `TC-<域>-<标签>-<NN>` 两套命名,台账只登记单套。明细已写入 `tests/TRACEABILITY-MATRIX.md` §10(含 85 孤儿分组清单)。 | 测试(R170 实测) | 2026-11-12 15:20 | 待处理(P2) |
+| **B-170-5** | 251 `chat` 端点挂死 | 运维/AI(P0) | **新增/反转**。live `POST /api/v1/chat` **000 / 90s / 0 字节**;同窗口 `/api/v1/analyze` **200 / 8.66s / 1131B**(R167 曾两会挂死,本轮 analyze 恢复)、`/health` 200/3.9ms;对照生产 `ai.etotem.com.cn` chat = 200 → 仅 251 节点上游被掐(429 ×7 + 401 ×3)。 | 测试(R170 实测) | 2026-11-12 15:20 | 待处理(归 B-142-2) |
+| **B-142-2-Update(第 12 轮,恶化)** | 251 LLM 上游 429 | 运维/AI(P0) | **恶化 3→5 红(51 例 46 过)**(R169 为 48 过)。5 红 = `test_auto_tag` 3(`assert 500==200`)+ `test_intent` 2;日志 `429 AgnesAI free limit` ×7 + `401 ****ab12 invalid` ×3。**连续 2 轮 51/51 才销号(观察 0/6)**。 | 测试(R170 实派) | 2026-11-12 15:20 | 待处理(P0) |
+| **B-152-1/2-Update(第 6 轮未修)** | CI 排除清单双向失真 | 研发(P0/P1 CI) | **延续**。清单 33 类 vs 实测红类 **12**:**漏拦 5 类**(GrowthPublicEndpointMappingTest / UserServiceWechatLoginTest / UserServiceWechatLoginContractTest / TaskReviewScP11ContractTest / TeamProfitCronContractTest)→ `backend-blocking-junit` 必红;**26 类已转绿仍排除**(R169 记漏 6,DanAssessment 转绿后收窄为 5)。 | 测试(R170 复核) | 2026-11-12 15:20 | 待处理(P0) |
+| **B-156-1-Update(第 6 轮未修)** | `nutrition_age_dosage` 建表 SQL 无效 | 研发(P1 Schema) | **延续**。`schema.sql:5345-5357` 无 `product_id` 列却建 `INDEX idx_product (product_id)` → 本轮 **28/28** 上下文 `Key column 'product_id' doesn't exist`,建表失败被静默 catch。 | 测试(R170 全量) | 2026-11-12 15:20 | 待处理 |
+| **B-162-1-Update(第 5 轮)** | 后端未入库测试文件 | 测试(P1 治理) | **延续**。`GrowthPublicEndpointMappingTest`(5E,**176s 超时型**)仍 `git status ??`;`JwtInterceptorGrowthPolicyTest`(0 红)/`ArticleTagsContractTest`(0 红) 同属未入库。 | 测试(R170 巡检) | 2026-11-12 15:20 | 待归口 |
+| **B-126-Update(第 5 轮复现)** | 251 `EXIT=` 未落盘 | 测试(P2 工具) | **复现**。`/tmp/r150_pytest.log` 无 `PYTEST_EXIT=` 行 → 退出码不可判(kick 串里 `$?` 被外层展开)。修法:单引号包 `bash -c '…; echo EXIT=$? >> LOG'`。 | 测试(R170 实派) | 2026-11-12 15:20 | 待修 |
+| **B-36-Update(第 56 轮零漂移)** | 根级黑盒门控 | 测试(P3) | **零漂移**。23 例 7 过 / 16 红(9082 未监听,fetch failed,实跑)。 | 测试(R170 实测) | 2026-11-12 15:20 | 待处理(环境型) |
+| **B-95-Update(第 30 轮)** | `204:4090` 失联 | 运维(P2) | **延续**。204:4090 = 000;189:4090 = 200、251:4096 = 200。 | 运维 | 2026-11-12 15:20 | 待处理 |
+| **B-DX-1-Update(第 23 轮)** | dx-bridge 端点 404 | 部署(P2) | **延续**。dx 2 例 skip(不假绿)。 | 部署/运维 | 2026-11-12 15:20 | 待处理 |
+| **B-153-1-Update(第 10 轮销号维持)** | 189 admin U05 前置偶发 | 测试(观察项) | **维持销号**。连续 10 轮首轮即 5/5(本轮 1.4m EXIT=0)。 | 测试(R170 实派) | 2026-11-12 15:20 | 已销号 |
+| **B-167-1-Update(观察 3/3,销号)** | 189 admin `account-roles` 502 | 环境/网络(P2) | **销号**。连续 3 轮首跑 5/5(无 502),达「连续 3 轮」条件。 | 测试(R170 实派) | 2026-11-12 15:20 | **已销号** |
+| **G-170-1** | 缺会员夹具 | 测试(P0 夹具) | **新增**。S-cfc-163 后 AI 教练仅会员可用,无会员夹具 → 并发/隔离链路 3 例只能 skip。建议新增 `registerMember()`。 | 测试(R170) | 2026-11-12 15:20 | 待补 |
+| **G-170-2/3/4/5/6/7/8** | 锚点 / JaCoCo / 变异 / 压测 / 安全扫描 / 无障碍 / 环境型红 | 测试(P1~P3) | 见报告 §七 覆盖缺口清单(G-170-2 锚点≥80%、G-170-3 后端 JaCoCo、G-170-4 PIT、G-170-5 k6、G-170-6 OWASP DC+Semgrep、G-170-7 axe、G-170-8 自动拉起 9082)。 | 测试(R170 方案) | 2026-11-12 15:20 | 待排期 |
+| **R170 门禁** | 全量 3933 / 3861 | 测试 | **不通过(承袭)**。✅G1 98.17%(剔 skip 98.80%)/G3 609-624/G4 116-116/G5 编译 0 错 dump 0/G6 28-28/G8 6-6 全 401/G10 三节点(189 5/5 + 251 51 例 + 本机)/G12 SRS 零变更;❌G2 47 红(后端 23 + 根级 16 + lg 5 + fe 3)/G7 46-51(90.2%)/G9 20.7%/G11 CI 必红/G14 58.2%;⚠️G13 存量 17 零漂移(第 17 次)。 | 测试(R170 闭环) | 2026-11-12 15:20 | 已记录 |

+ 69 - 0
tests/TRACEABILITY-MATRIX.md

@@ -175,3 +175,72 @@ L2 语义层需比对 TC「所属场景」列(归一 `SC-?([A-Z]+)-0*(\d+)`)
 | `npx playwright test e2e/api/{api-smoke,nonfunc-security,nonfunc-perf-compat,butler-fallback,human-butler-workorder}.spec.js -c playwright.config.api.js --project=desktop` | 24 passed / 3 failed / 5 skipped |
 
 去重合计 **42 个用例:30 passed / 3 failed / 9 skipped**。SC-P-94 / SC-P-95 三类流 6 条全部真实通过;SC-C-26 / SC-A-27 / SC-P-96 因配置接口 404/500 与远程无前端 SPA 未实测,登记为 4 个 `fixme`。因缺「推 `origin/cfclub` + 工作区干净」两项判据,**本轮不更新 `sc-key-map.md` 跑通状态列**(仍 15/241),需单独授权 git 流程后入册(G-9)。
+
+---
+
+## 10. R170 锚点双向核对(2026-11-12,实测口径)
+
+> **目的**:回答「用例台账 ↔ 可执行测试资产」是否**双向**一致。历轮只报单向(台账 TC 有多少被执行锚点覆盖),
+> 导致「**执行了但台账没登记**」的孤儿锚点长期不可见。本轮首次做双向差集。
+
+> 扫描范围:`cfc-backend/src/test/**/*.java`、`cfc-frontend/__tests__`、`cfc-web/src/__tests__`、
+> `tests/e2e-live`、`tests/e2e`(排除 `tests/e2e/api/generated`)、`cfc-langgraph/tests`;台账 `tests/test-cases/*.md`。
+
+| 指标 | 数值 | 说明 |
+|---|---|---|
+| 台账唯一 TC 编号 | **367** | `tests/test-cases/*.md` 表首列 |
+| 可执行锚点唯一编号 | **161** | 测试源码中出现的 `TC-xxx` 字面 |
+| 双向命中 | **76** | 台账 ∩ 锚点 |
+| **孤儿锚点(执行有、台账无)** | **85** | 锚点 − 台账 → 追溯断链,见 §10.1 |
+| 台账无锚点(有编号、无执行) | **291** | 台账 − 锚点,见 §10.2 分组 |
+| 严格锚点覆盖率 | **76/367 = 20.7%** | G9 口径(排除 generated 虚高) |
+| 锚点可追溯率 | **76/161 = 47.2%** | 新增反向指标 G9-b |
+
+### 10.1 孤儿锚点清单(85 个,按前缀分组)
+
+| 前缀组 | 数量 | 编号 | 归属 |
+|---|---|---|---|
+| `TC-A-U*` | 6 | `TC-A-U01` `TC-A-U02` `TC-A-U03` `TC-A-U04` `TC-A-U05` `TC-A-U06` | `tests/e2e-live/admin-live.spec.js`(管理端 UI 5+1 例) |
+| `TC-A5*` | 5 | `TC-A5-01` `TC-A5-02` `TC-A5-03` `TC-A5-04` `TC-A5-05` | 后端/前端 UI 五维相关用例 |
+| `TC-C10*` | 6 | `TC-C10-01` `TC-C10-02` `TC-C10-03` `TC-C10-04` `TC-C10-05` `TC-C10-06` | 历史编号段(未回填台账) |
+| `TC-C16*` | 5 | `TC-C16-01` `TC-C16-02` `TC-C16-03` `TC-C16-04` `TC-C16-05` | 历史编号段(未回填台账) |
+| `TC-DX-U*` | 2 | `TC-DX-U01` `TC-DX-U02` | `tests/e2e-live/dx-health-bridge-auth.spec.js`(D/X 桥接 2 例,404 跳过) |
+| `TC-F-G*` | 1 | `TC-F-G01` | `tests/e2e-live/frontend-live.spec.js`(游客态 1 例) |
+| `TC-F-U*` | 6 | `TC-F-U01` `TC-F-U02` `TC-F-U03` `TC-F-U04` `TC-F-U05` `TC-F-U06` | `tests/e2e-live/frontend-live.spec.js`(小程序 H5 6 例) |
+| `TC-FA*` | 7 | `TC-FA-18` `TC-FA-19` `TC-FA-20` `TC-FA-21` `TC-FA-22` `TC-FA-23` `TC-FA-24` | 前端签到/家庭相关 Jest |
+| `TC-GA-M*` | 5 | `TC-GA-M01` `TC-GA-M02` `TC-GA-M03` `TC-GA-M04` `TC-GA-M05` | 成长档案 M 类 |
+| `TC-GA-P*` | 5 | `TC-GA-P01` `TC-GA-P02` `TC-GA-P03` `TC-GA-P04` `TC-GA-P05` | 成长档案 P 类 |
+| `TC-GRID*` | 4 | `TC-GRID-01` `TC-GRID-02` `TC-GRID-03` `TC-GRID-04` | 五维沙盘 grid 用例 |
+| `TC-H-U*` | 9 | `TC-H-U02` `TC-H-U03` `TC-H-U04` `TC-H-U05` `TC-H-U06` `TC-H-U07` `TC-H-U08` `TC-H-U09` `TC-H-U10` | `tests/e2e-live/health-contract.spec.js`(健康 9 例) |
+| `TC-HP-U*` | 3 | `TC-HP-U01` `TC-HP-U02` `TC-HP-U03` | `tests/e2e-live/health-contract.spec.js`(健康方案 3 例) |
+| `TC-LANDSCAPE*` | 8 | `TC-LANDSCAPE-01` `TC-LANDSCAPE-02` `TC-LANDSCAPE-03` `TC-LANDSCAPE-04` `TC-LANDSCAPE-05` `TC-LANDSCAPE-06` `TC-LANDSCAPE-07` `TC-LANDSCAPE-08` | 横屏/布局兼容用例 |
+| `TC-MG-U*` | 6 | `TC-MG-U02` `TC-MG-U03` `TC-MG-U04` `TC-MG-U05` `TC-MG-U06` `TC-MG-U07` | `cfc-frontend/__tests__/utils/membershipGate.test.js`(会员门控 6 例) |
+| `TC-S-U*` | 4 | `TC-S-U01` `TC-S-U02` `TC-S-U03` `TC-S-U04` | `tests/e2e-live/shop-sort-api.spec.js`(商城排序 4 例) |
+| `TC-SIG*` | 3 | `TC-SIG-02` `TC-SIG-03` `TC-SIG-04` | 电子签名相关用例 |
+
+**根因(P1 治理)**:可执行资产使用 **`TC-<域>-U<NN>`(UI/用户态)** 与 **`TC-<域>-<语义标签>-<NN>`** 两套命名,
+台账只登记 `TC-<域>-<NN>` 单套 → 凡走 UI/契约命名的用例全部落不到台账。这不是 85 个孤立缺漏,而是**命名规范未双向对齐**。
+
+### 10.2 台账无锚点(291 个)前缀分布
+
+| 前缀 | 无锚点数 | 风险等级 |
+|---|---|---|
+| `TC-P-*` | 93 | 中(平台/后台流程,多为 Web 管理端未自动化) |
+| `TC-T-*` | 39 | 高(团队/分销域,无执行证据) |
+| `TC-X-*` | 35 | 中 |
+| `TC-A-*` | 33 | 低(同域 U 系列已在执行,编号体系不同) |
+| `TC-C-*` | 27 | 中 |
+| `TC-S-*` | 17 | 低(U 系列已覆盖排序契约) |
+| `TC-HB-*` | 16 | 低(桥接 16 例由契约测试覆盖,编号不同) |
+| `TC-B-*` | 11 | 中 |
+| `TC-H-*` | 6 | 低 |
+| `TC-PA-*` | 6 | 中 |
+| `TC-DV-*` | 4 | 中 |
+| `TC-G-*` | 2 | 中 |
+| `TC-SIG2D-*` | 2 | 中(二维签名相关,无执行锚点) |
+
+### 10.3 建议动作
+
+1. **(测试侧,本轮可执行)** 在台账新增「锚点别名列」,把 §10.1 的 85 个孤儿按语义挂到既有 TC 编号,或为其补 `TC-<域>-<NN>` 正式编号 → 把锚点覆盖率从 20.7% 抬到 47.2% 量级。
+2. **(测试侧)** 对 §10.2 的 `TC-T-*`(39)、`TC-X-*`(35)补执行锚点,属最高风险空转区。
+3. **(研发侧)** 新写测试必须带台账编号,CI 增一条「新增 `TC-` 字面必须能在台账命中」的静态断言(与 B-152 双向差集同源)。

+ 11 - 0
tests/e2e/api/nonfunc-perf-compat.spec.js

@@ -91,6 +91,17 @@ test('[TC-A-N04/X-N02] 限流熔断稳定性:爆发请求下系统不死(不
 
 test('[TC-P-N02/X-N01] 并发不串数据:并行 AI 请求各自独立响应', async ({ request }) => {
   const token = await register(request, 'parent');
+
+  // R170 需求变更对齐(S-cfc-163「聊天仅会员可见」,dc5522d09):
+  //   该变更把 AI 教练能力收敛为会员专享,非会员直连返回 code=46003
+  //   「该功能为会员专享,请先开通会员」。因此**非会员夹具无法构造并发会话**,
+  //   原断言 `code===200` 会恒假红(产品行为正确、用例未随需求更新)。
+  //   处置:显式识别门控并 skip(不静默放行、不改断言假绿),
+  //   同时登记夹具缺口「缺会员账号夹具」→ G-170-1。
+  const probe = await apiPost(request, '/api/ai/health-coach/send', { query: '并发探针' }, token);
+  test.skip(probe.json && probe.json.code === 46003,
+    'AI 教练受会员门控(S-cfc-163 / code=46003):缺会员夹具,并发隔离待补测');
+
   const queries = ['帮我制定运动计划', '推荐儿童食谱', '如何改善睡眠', '英语启蒙建议', '亲子沟通技巧'];
   const calls = queries.map((q) => apiPost(request, '/api/ai/health-coach/send', { query: q }, token));
   const res = await Promise.all(calls);

+ 33 - 7
tests/e2e/api/nonfunc-security.spec.js

@@ -71,9 +71,12 @@ test.describe('非功能/安全接口冒烟', () => {
   });
 
   // —— 后台鉴权(TC-A-N05):非管理员访问后台接口被拒(401/403)——
-  // 注:当前远端 /api/admin/* 未部署(404),改用真实存在的 /api/admin-auth/login 作为特权入口探针
+  // R170 修正:原探针用 `/api/admin-auth/login`(**登录入口,本就应匿名可访问**),
+  //   空参返回 code=400「手机号和验证码不能为空」是正确行为 → 拿登录接口当「特权入口」
+  //   属用例设计错误(恒红假缺陷)。改用真实特权面 `/api/admin/families/overview`
+  //   (S-cfc-143/144 收口后匿名应 401)。
   test('[TC-A-N05] 非管理员访问后台接口被拒(401/403)', async ({ request }) => {
-    const r = await apiPost(request, '/api/admin-auth/login', {}, null);
+    const r = await apiPost(request, '/api/admin/families/overview', {}, null);
     expect([401, 403].includes(r.code), `后台接口未授权应 401/403,实际 code=${r.code} msg=${r.message}`).toBeTruthy();
   });
 
@@ -118,16 +121,39 @@ test.describe('非功能/安全接口冒烟', () => {
   });
 
   // —— 上传恶意/非 PDF 文件被拒(TC-P-N11 / TC-X-N08)——
+  // R170 修正两处用例缺陷:
+  //   ① 路径错误:`/api/assessment/...` 不存在(线上恒 404),真实路由由
+  //      `HealthReportController` 的类级 `@RequestMapping("/api/health")` 决定 →
+  //      `/api/health/report/upload`。
+  //   ② 判定口径错误:本项目统一 `Result{code,message,data}` + **HTTP 200 承载业务码**,
+  //      用「HTTP 非 200」判「被拒」恒假红;正确判据是 **业务 code ≠ 200**(拒绝落库)
+  //      且 HTTP 不出现 5xx(不发生未捕获异常)。
+  //   实测(R170):非 multipart → code=400「请求格式错误,请使用文件表单上传」;
+  //      multipart 但内容非 PDF → code=500「PDF解析失败…」→ 两者均为业务拒绝,符合契约。
   test('[TC-P-N11] 报告上传非 PDF/恶意内容被拒(非 200)', async ({ request }) => {
     const ctx = await getParent(request);
-    const res = await request.post('/api/assessment/report/upload', {
+
+    // ① 非 multipart 直传 → 业务拒绝(code≠200),HTTP 不 5xx
+    const r1 = await request.post('/api/health/report/upload', {
       data: 'PK\x03\x04not-a-real-pdf',
       headers: { 'Content-Type': 'application/octet-stream', Authorization: 'Bearer ' + ctx.token },
       timeout: 60000,
     });
-    const status = res.status();
-    // 安全合规行为:拒绝为 4xx(400/401/403/405/413/415/422)。非 200 即视为未成功落库。
-    expect(status, `报告上传非 PDF 应被拒(4xx),实际 HTTP ${status}`).not.toBe(200);
-    expect([400, 401, 403, 405, 413, 415, 422].includes(status)).toBeTruthy();
+    const s1 = r1.status();
+    const b1 = await r1.json().catch(() => ({}));
+    expect(s1, `上传畸形请求不应触发 HTTP 5xx,实际 ${s1}`).toBeLessThan(500);
+    expect(b1.code, `非表单上传应被业务拒绝(code≠200),实际 HTTP ${s1} code=${b1.code} msg=${b1.message}`).not.toBe(200);
+
+    // ② multipart 但内容非 PDF → 业务拒绝(不落库),HTTP 不 5xx
+    const r2 = await request.post('/api/health/report/upload', {
+      multipart: { file: { name: 'evil.pdf', mimeType: 'application/pdf', buffer: Buffer.from('PK\x03\x04not-a-real-pdf') } },
+      headers: { Authorization: 'Bearer ' + ctx.token },
+      timeout: 60000,
+    });
+    const s2 = r2.status();
+    const b2 = await r2.json().catch(() => ({}));
+    expect(s2, `伪 PDF 上传不应触发 HTTP 5xx,实际 ${s2}`).toBeLessThan(500);
+    expect(b2.code, `伪 PDF 应被业务拒绝(code≠200),实际 HTTP ${s2} code=${b2.code} msg=${b2.message}`).not.toBe(200);
+    expect(b2.data && b2.data.reportId, '被拒的上传不得产生报告记录').toBeFalsy();
   });
 });

+ 121 - 0
tests/e2e/api/traceability-p94-p95-c26.spec.js

@@ -0,0 +1,121 @@
+/**
+ * 需求↔SC↔TC 定向三类流执行证据(2026-11-10,execute 档)
+ * ================================================================
+ * 对应补写用例:TC-P-94*、TC-P-95*(tests/test-cases/parent.md「成就与成长历史专项」)
+ *              TC-C-26*、TC-A-27-S*(child.md / admin.md 睡眠配置专项)
+ * 主表:tests/TRACEABILITY-MATRIX.md
+ *
+ * 运行:
+ *   cd d:/cfc/tests && npx playwright test e2e/api/traceability-p94-p95-c26.spec.js \
+ *     -c playwright.config.api.js --project=desktop
+ *
+ * 环境事实(2026-11-10 实测):
+ *   - 远程 http://cfc.iwintrue.com 仅部署后端 API,未托管前端 SPA;
+ *     故 SC-P-96(五列布局)等纯 UI 断言无法在此规格执行,标「未实测」。
+ *   - 账号由 /api/auth/direct-register 自注册,不依赖任何硬编码凭据;
+ *     全程不打印 token / 手机号,不写业务数据(仅只读查询类 POST)。
+ *   - 接口实际契约已探活确认,断言均基于实测可判定行为,未臆造字段。
+ */
+const { test, expect } = require('@playwright/test');
+const { registerRole, apiPost } = require('../helpers/api');
+
+let ctx;
+let memberId;
+
+test.beforeAll(async ({ request }) => {
+  ctx = await registerRole(request, 'parent', 'QA-TRACE');
+  const r = await apiPost(request, '/api/family/member/list', {}, ctx.token);
+  expect(r.code, '成员列表应可用').toBe(200);
+  expect(Array.isArray(r.data) && r.data.length > 0, '应至少有一个家庭成员').toBeTruthy();
+  memberId = r.data[0].memberId;
+});
+
+// ---------------------------------------------------------------
+// SC-P-94 · 我的成就「完成任务」→ 历史完成任务页面
+// 接口:POST /api/tasks/history(MyBatis-Plus Page)
+// ---------------------------------------------------------------
+test.describe('SC-P-94 历史完成任务三类流', () => {
+  test('TC-P-94 正常流:分页结构完整且仅返回已完成记录', async ({ request }) => {
+    const r = await apiPost(request, '/api/tasks/history', { memberId, page: 1, size: 10 }, ctx.token);
+    expect(r.code).toBe(200);
+    expect(r.data, '应返回分页对象').toBeTruthy();
+    expect(Array.isArray(r.data.records), 'records 应为数组').toBeTruthy();
+    expect(r.data).toHaveProperty('total');
+    expect(r.data).toHaveProperty('pages');
+    expect(r.data).toHaveProperty('current');
+    // 新注册账号无历史任务,空态为合法结果(对应文档用例「暂无已完成任务」)
+    expect(r.data.total).toBeGreaterThanOrEqual(r.data.records.length);
+  });
+
+  test('TC-P-94-E 异常流:非法分页/非法 category 不 5xx 且结构稳定', async ({ request }) => {
+    const bad = [
+      { memberId, page: -1, size: 10 },
+      { memberId, page: 999999, size: 10 },
+      { memberId, page: 1, size: 10, category: '__INVALID__' },
+    ];
+    for (const body of bad) {
+      const r = await apiPost(request, '/api/tasks/history', body, ctx.token);
+      expect(r.code, `非法参数 ${JSON.stringify(body)} 不应返回 5xx`).toBe(200);
+      expect(Array.isArray(r.data.records), '分页结构应完整').toBeTruthy();
+    }
+    // 实测:实现未对非法分页/category 返回 400,而是归一处理返回空页。
+    // 文档用例允许「400 或归一到首页」,故按「不 5xx + 结构稳定」判定通过;
+    // 参数校验强度是否需加强 → 标「需核实」,见主矩阵 G-7。
+  });
+
+  test('TC-P-94-P 权限流:传非本家庭 memberId 不返回他人任务数据', async ({ request }) => {
+    const r = await apiPost(request, '/api/tasks/history', { memberId: 999999999, page: 1, size: 10 }, ctx.token);
+    expect([200, 403, 401]).toContain(r.code);
+    if (r.code === 200) {
+      expect(r.data.records, '不得返回非本家庭任务').toHaveLength(0);
+    }
+    // 实测:实现按当前登录态过滤,返回 200 + 空列表(未返回 403)。
+    // 断言按「不泄露他人数据」落地;「必须 403」契约需在造跨家庭夹具后复测 → 主矩阵 G-7。
+  });
+});
+
+// ---------------------------------------------------------------
+// SC-P-95 · 我的成就「成长记录」→ 成长历史记录页
+// 接口:POST /api/health/checkin/list(今日成长打卡记录流)
+// ---------------------------------------------------------------
+test.describe('SC-P-95 成长历史记录三类流', () => {
+  test('TC-P-95 正常流:打卡记录流返回可遍历数组', async ({ request }) => {
+    const r = await apiPost(request, '/api/health/checkin/list', { memberId, page: 1, size: 10 }, ctx.token);
+    expect(r.code).toBe(200);
+    expect(Array.isArray(r.data), '打卡记录流应为数组(空数组为合法空态)').toBeTruthy();
+  });
+
+  test('TC-P-95-E 异常流:非法筛选与非法分页不 5xx', async ({ request }) => {
+    const bad = [
+      { memberId, page: -1, size: 10 },
+      { memberId, page: 1, size: 10, type: '__INVALID__' },
+      { memberId, startTime: '2099-01-01', endTime: '2020-01-01' },
+    ];
+    for (const body of bad) {
+      const r = await apiPost(request, '/api/health/checkin/list', body, ctx.token);
+      expect(r.code, `非法参数 ${JSON.stringify(body)} 不应返回 5xx`).toBe(200);
+      expect(Array.isArray(r.data)).toBeTruthy();
+    }
+  });
+
+  test('TC-P-95-P 权限流:非本家庭 memberId 不返回他人打卡记录', async ({ request }) => {
+    const r = await apiPost(request, '/api/health/checkin/list', { memberId: 999999999, page: 1, size: 10 }, ctx.token);
+    expect([200, 403, 401]).toContain(r.code);
+    if (r.code === 200) {
+      expect(r.data, '不得返回非本家庭打卡记录').toHaveLength(0);
+    }
+  });
+});
+
+// ---------------------------------------------------------------
+// 契约缺口(真实探活结果,登记为 fixme,不伪造通过)
+// ---------------------------------------------------------------
+test.describe('契约缺口待修复(2026-11-10 实测)', () => {
+  test.fixme('TC-P-95:/api/growth/record/list 带/不带 memberId 均返回 code=500「服务器内部错误」,成长档案记录聚合口径未落地', async () => {});
+
+  test.fixme('TC-C-26:睡眠选项配置读取契约未落地 —— /api/health/sleep/tags 返回 404;/api/config/public/value 带 key=sleep.quality.tags 返回 code=500', async () => {});
+
+  test.fixme('TC-A-27-S:后台维护契约未落地 —— /api/admin/sleep/tags 返回 404;需确认是否改由 /api/admin/config/* 通用配置承载', async () => {});
+
+  test.fixme('SC-P-96 / 前端页面类断言:远程环境未托管前端 SPA,纯 UI 与页面跳转断言需本地/真机环境执行', async () => {});
+});