ソースを参照

test(tdd): 第115轮测试闭环——后端全量2864例98.67%(红例48→34);S-cfc-130 bridgeWrite契约16/16闭环(B-112);189/251派发通道纠正后双双打通(B-113);DimensionScore夹具跟修21例转绿

E2E Test Bot 3 日 前
親
コミット
46cb414fd2

+ 2 - 4
cfc-backend/src/test/java/com/etotem/cfc/integration/service/DimensionScoreServiceTest.java

@@ -49,9 +49,6 @@ public class DimensionScoreServiceTest {
     @Mock
     @Mock
     private HealthReportService healthReportService;
     private HealthReportService healthReportService;
 
 
-    @Mock
-    private HealthDataSourceRecordService dataSourceRecordService;
-
     @Mock
     @Mock
     private com.etotem.cfc.mapper.FamilyMemberMapper familyMemberMapper;
     private com.etotem.cfc.mapper.FamilyMemberMapper familyMemberMapper;
 
 
@@ -77,7 +74,8 @@ public class DimensionScoreServiceTest {
         setField(service, "scoreService", scoreService);
         setField(service, "scoreService", scoreService);
         setField(service, "normMapper", normMapper);
         setField(service, "normMapper", normMapper);
         setField(service, "healthReportService", healthReportService);
         setField(service, "healthReportService", healthReportService);
-        setField(service, "dataSourceRecordService", dataSourceRecordService);
+        // S-cfc-130:DimensionScoreServiceImpl 的 dataSourceRecordService 死注入已解除,
+        // 该字段不再存在,夹具不再反射注入(否则 NoSuchFieldException 全类 21 例报错)
         setField(service, "familyMemberMapper", familyMemberMapper);
         setField(service, "familyMemberMapper", familyMemberMapper);
         setField(service, "danAssessmentResultMapper", danAssessmentResultMapper);
         setField(service, "danAssessmentResultMapper", danAssessmentResultMapper);
         setField(service, "dimensionEnergySyncService", dimensionEnergySyncService);
         setField(service, "dimensionEnergySyncService", dimensionEnergySyncService);

+ 295 - 0
docs/tdd/orchestration/report-test-2026-11-10-0416.md

@@ -0,0 +1,295 @@
+# 测试负责人闭环汇报 · 第 115 轮(2026-11-10 04:16)
+
+> 汇报轮次:CFC 闭环自动化测试 · 第 115 轮(测试负责人角色,自动化无人值守)
+> 执行档:**review(离线)** — 白名单内离线套件 + 已授权内网只读 E2E(`cfc.iwintrue.com`)+ 节点派发(251 / 189)
+> 仓库:`D:/cfc`;分支 `cfclub`;基线 HEAD `390a02963`(= `origin/cfclub`,跑前 `git pull --ff-only` 3 提交快进)
+> 执行窗口:03:05 → 04:40(后端全量 52:11 串行;其余套件错峰串行)
+> 团队:本轮为自动化闭环轮,八域结论由测试负责人**依据实测证据**汇总(未启用分布式成员会签),不代写任何成员未产出的结论
+
+---
+
+## 一、历史审查记录(阶段 -1 记忆召回)
+
+| 项 | 内容 |
+|---|---|
+| 上次审查 | 第 113 轮(2026-11-10 02:05),结论「有条件通过(延续)」 |
+| 历史遗留缺口 | B-114(HL-05 覆盖缺口)、B-112(S-cfc-129 bridgeWrite 未实现,14 例预期 RED)、B-113(189/251 派发通道)、B-69(mind 鉴权)、B-65/B-74/B-82/B-90(langgraph)、B-36(根级黑盒)、B-55(G10 派发) |
+| 本次是否改善 | **B-112 已闭环**(实现 `47d203981` 合入,契约 16/16 绿);**B-113 误判纠正并闭环**(251/189 双通道打通);B-114 后端侧维持闭环、前端/E2E 仍缺;B-69/B-65/B-90 未修 |
+| 声明 | 历史结论仅作对照,**本轮全部数字均为重新取证** |
+
+---
+
+## 二、项目画像(阶段 0 增量)
+
+| 维度 | 现状(本轮实测/只读扫描) |
+|---|---|
+| 业务定位 | 家庭教育成长平台:家长/孩子/规划师/供应商/运营五端 + 五维能量体系 + 健康报告解析 + 商城分佣 |
+| 技术栈 | `cfc-backend` Spring Boot 2.7.18 + MyBatis-Plus(JDK8,端口 9082);`cfc-frontend` uni-app 小程序;`cfc-web` Vue2+Element(8082);`cfc-langgraph` FastAPI+LangGraph+Chroma(9000);`dan/danshop` 独立商城(8888,方案外) |
+| 规模 | `@RestController` 363;`@PostMapping` 1965;测试文件 339;`@Test` 2854(`@Disabled` 4);Playwright spec 96 |
+| 关键路径 | 登录/JWT → 家庭与成员 → 五维测评与健康方案 → 健康报告上传解析 → 商城下单分佣 → 管理端审核 |
+| 本轮窗口变更 | **3 提交**:`47d203981`(S-cfc-130 bridgeWrite 实现,+199/−6 生产代码)、`2f46be297`/`390a02963`(docs + auto bump)。`docs/superpowers/specs` **零新增/零修改**(200 份) |
+| 质量目标(用户口径) | ① 全部用例通过;② 覆盖全部 SRS 与用户场景(含异常流);③ SRS/场景变更时更新用例 |
+
+---
+
+## 三、测试覆盖矩阵(20 类 · 三态)
+
+| # | 类型 | 状态 | 证据 |
+|---|------|------|------|
+| 1 | 单元测试 | 已发现 | 339 测试类、`@Test` 2854,本轮实跑 2864 例 |
+| 2 | 测试覆盖率 | 未发现 | `pom.xml` 无 jacoco(`grep -c jacoco` = 0),无覆盖率产物 |
+| 3 | 变异测试 | 未发现 | 无 pitest/mutation 配置 |
+| 4 | 集成测试 | 已发现 | `com.etotem.cfc.integration.**`(EnergySystem / GrowthRecordSync / GuideModule / DimensionScore 等) |
+| 5 | 契约测试 | 已发现 | DimensionRegistryMap / **HealthDataSourceBridge 16/16** / HealthPlanProductSelection 14/14 / TeamProfitCron / UserServiceWechatLogin / JwtInterceptor 31 例等 |
+| 6 | 接口 API 测试 | 已发现 | `tests/e2e-live/*.spec.js` 接口契约 17 例(shop 4 + health 13),线上实探 |
+| 7 | 系统/端到端 | 已发现 | 浏览器 **28/28**(admin 5 + frontend 6 + shop 4 + health 13);另 189 节点独立复跑 admin 5/5 |
+| 8 | 验收 UAT | 待核实 | 无 UAT 脚本/签署记录产物 |
+| 9 | BDD 规格 | 未发现 | 无 `.feature` |
+| 10 | 冒烟测试 | 已发现 | `tests/e2e/ci-smoke.spec.js`、`_smoke_browser.spec.js` |
+| 11 | 回归测试 | 已发现 | 本轮全量 3500 例串行回归(后端 52:11 + 其余) |
+| 12 | 探索性测试 | 未发现 | 无 charter/会话记录 |
+| 13 | 性能测试 | 未发现 | 无 k6/JMeter 脚本(属 execute 档,未授权联网) |
+| 14 | 可靠性测试 | 待核实 | 无故障注入/长稳;仅 B-90 挂起观测(251 chat 45s 零字节) |
+| 15 | 安全测试(SAST/DAST/SCA) | 未发现 | 无 semgrep/zap/依赖扫描产物;仅鉴权契约 13 例 + 线上 401 探针 |
+| 16 | 兼容性测试 | 未发现 | Playwright 仅 chromium;无多浏览器矩阵 |
+| 17 | 可用性测试 | 未发现 | 无可用性任务/量表 |
+| 18 | 无障碍测试 | 未发现 | 无 axe 扫描 |
+| 19 | 质量指标 | 已发现 | 本轮全量 98.06% / 后端 98.67% / E2E 100%,红例三层归因见 §5.3 |
+| 20 | QA 流程与代码审查 | 已发现 | `AGENTS.md` 核查:`@GetMapping` **仍 8 处**(违「统一 @PostMapping」);CI 仅 `mvn test-compile` + 前端 jest,**后端全量未入 CI** |
+
+---
+
+## 四、测试方案与执行档(阶段 2–3)
+
+| 项 | 内容 |
+|---|---|
+| 目标 | ① 全部用例通过 ② SRS/场景覆盖可量化 ③ 变更即更新用例 |
+| 范围 | 后端 JUnit 全量、前端/管理端 jest、根级 tests jest、langgraph pytest(派 251)、浏览器 E2E(本机 + 189) |
+| 排除项 | `dan/danshop`(独立商城,方案外)、`tests/test_chat.py`(走真实 LLM 无超时,套件不收敛 B-84)、`tests/qna`(收集期错误中断 B-15) |
+| 执行档 | **review**:不联网装依赖、不启停服务、不改生产代码;仅改**测试侧装置**(本轮 1 处,见 §5.4) |
+| 授权 | 用户指令已含内网 E2E 与节点派发授权;未申请 execute 档(压测/DAST 未执行,标注未实测) |
+
+---
+
+## 五、实测结果
+
+### 5.1 总账
+
+| 套件 | 用例 | 通过 | 红 | Skip | 通过率 | 备注 |
+|------|------|------|----|------|--------|------|
+| 后端 JUnit(cfc-backend) | 2864 | **2826** | 34 | 4 | **98.67%** | 339 类;`mvn -o -B clean test`,**52:11**,`BUILD FAILURE`(仅测试红) |
+| 前端 jest(cfc-frontend) | 426 | 423 | 0 | 3 | 100%(剔 skip) | 26 suites 全通过,51.4s |
+| 管理端 jest(cfc-web) | 116 | 116 | 0 | 0 | 100% | 17 suites,38.1s |
+| 根级 tests jest | 23 | 7 | 16 | 0 | 30.4% | B-36 环境门控,**零漂移** |
+| langgraph pytest **@251** | 43 | 32 | 11 | 0 | 74.4% | 79.05s,251 实派取回 |
+| **合计(不含 E2E)** | **3472** | **3404** | **61** | **7** | **98.04%** | — |
+| 浏览器 E2E(独立计数) | 28 | **28** | 0 | 0 | **100%** | 本机 15 live + 13 health;189 另跑 admin 5/5 |
+
+> **全量合计 3500 例 / 3432 过 = 98.06%**(含 E2E)。
+
+### 5.2 与上一实测基线对账(第 112 轮 337/2845/2793/98.17%)
+
+| 指标 | R112 | R115 | Δ |
+|---|---|---|---|
+| 后端类数 | 337 | 339 | **+2** |
+| 后端用例 | 2845 | 2864 | +19 |
+| 后端通过 | 2793 | 2826 | **+33** |
+| 后端通过率 | 98.17% | **98.67%** | **+0.50pp** |
+| 后端红例 | 48 | **34** | **−14** |
+| 全量 | 3453 / 3371(97.61%) | 3500 / 3432(**98.06%**) | +0.45pp |
+
+**Δ 归因(逐类核对,无意外退化)**:
+1. `HealthDataSourceBridgeContractTest` **14 红 → 16/16 全绿**(S-cfc-130 实现落地,B-112 闭环);
+2. `DimensionScoreServiceTest` **21 E → 21/21 全绿**(生产删字段导致夹具 `NoSuchFieldException`,本轮修夹具,见 §5.4);
+3. 其余 15 红类相对 R112 红类集合**无新增**(红类集合差集为空)。
+
+### 5.3 后端 34 红三层归因
+
+| 类别 | 例数 | 分布 | 定性 |
+|---|---|---|---|
+| **A 类 测试装置缺陷** | **16** | `CfCommissionServiceSettleTest` 8(档位查询未打桩 → `earnByRule` 未触发)、`ArticleAutoTagServiceTest` 4(`execute` 次数断言与实现调度口径不一致)、`GrowthRecordSyncFlowTest` 3(2E NPE 首帧在测试方法内 + 1F 断言)、`TaskPlanServiceTest` 1 | 测试侧可自修 |
+| **B 类 产品缺陷/契约未决** | **14** | `EnergySystemFlowTest` 6×500、`MindFortuneControllerTest` 1(B-69,401→400)、`OrchestrationEngineTest` 1、`OrchestrationIntegrationTest` 1、`MembershipPaySwitchTest` 1、`PaymentServiceORDRoutingTest` 1、`UserServiceWechatLoginContractTest` 1、`UserServiceWechatLoginTest` 1、`TeamProfitCronContractTest` 1 | 需研发处置 |
+| **C 类 数据/环境** | **1** | `GuideModuleIntegrationTest` 1E(`platform_fee` Data truncation) | 真库字段宽度 |
+| **契约待裁决** | **3** | `DimensionRegistryMapContractTest` 3(`map_unknownLegacy/empty/missing` 期望 `null`/`400`,实现返原值/`200`) | P-9 口径未决(第 4 轮) |
+
+### 5.4 本轮测试侧修复(1 处,A 类,已实测转绿)
+
+| 文件 | 变更 | 结果 |
+|---|---|---|
+| `cfc-backend/src/test/java/com/etotem/cfc/integration/service/DimensionScoreServiceTest.java` | 删除 `@Mock HealthDataSourceRecordService dataSourceRecordService` 字段 + `setField(service,"dataSourceRecordService",…)` 一行(S-cfc-130 已删除该生产字段 → 全类 21 例 `NoSuchFieldException`)+ 注释说明 | 定向复跑 **21/21 全绿**(4.98s) |
+
+> 纪律说明:只改**测试装置**以跟随生产重构,不改被测语义、不放宽断言(承 B-57 教训)。
+
+### 5.5 浏览器 E2E(28/28 全绿)
+
+| 套件 | 例数 | 结果 | 关键断言 |
+|------|------|------|----------|
+| admin-desktop(/admin,本机) | 5 | **5/5 ✅**(4.6/3.7/3.4/10.4s;U05 57.3s) | 真实登录 + 滑块通过 + 菜单可访问 |
+| admin-desktop(/admin,**189 节点**) | 5 | **5/5 ✅**(1.9m) | 独立节点复跑一致(首次成功派发 189) |
+| frontend-mobile(/frontend) | 6 | **6/6 ✅** | 首页/TabBar/未登录「我的」鉴权引导/零 requestfailed |
+| shop-api(商城排序契约) | 4 | **4/4 ✅**(61~170ms) | price asc/desc/默认/非法列白名单 |
+| health-contract | 13 | **13/13 ✅**(4.1s) | 华为 5 + 小米 5 + 健康方案 3 端点未鉴权全 **401**(B-61 维持) |
+
+### 5.6 节点派发(G10 首次双节点达成)
+
+| 节点 | 端口 | 状态 | 本轮结果 |
+|------|------|------|----------|
+| **251** | 9000 `/health` | 200(6ms) | — |
+| **251** | 4096 opencode | 200 | **实派 pytest 取回 43/32/11(79.05s)**——修正派发方式后成功(见 §5.7) |
+| **189** | 4090 opencode | 200 | **实派 admin-desktop 5/5(1.9m)**——首次成功 |
+| 204 | 4090 | 000 | 失联(B-95 第 5 轮),不影响本轮 |
+
+### 5.7 ★ 关键发现:B-113 为**误判**,派发通道本身可用(已闭环)
+
+- 依据 `openapi.json` 实证:`POST /api/session/{id}/shell` **设计上恒返 204(No Content)**,且 `GET /api/shell`(list)**只列 running**、命令秒退即从列表消失。
+- 旧派发器「先建 session shell → 轮询 `/api/shell` 列表找新 id」必然拿到 `NONE`,被连续多轮误判为「shell 建不出对象 / 通道退化」。
+- 正确入口:**`POST /api/shell?location[directory]=<dir>`**(body `{command,cwd}`)→ 返回 `Shell.Info.id` → `GET /api/shell/{id}/output`。
+- 长任务补充纪律:命令须**重定向到日志文件**,再另起 shell `tail` 读文件(shell 退出后对象会被回收,`/output` 返回 `ShellNotFoundError`)。
+- Git Bash 侧必须 `MSYS_NO_PATHCONV=1`,否则 `/sc-data/...` 会被改写成 `D:/workbuddy/.../sc-data/...`(本轮首次失败即此因)。
+- 工具落地:`docs/tdd/tools/dispatch_shell.cjs`(已入库)。
+
+### 5.8 线上可用性复测(只读探针)
+
+| 探针 | 结果 | 判定 |
+|---|---|---|
+| `POST /api/mind/fortune`(匿名) | **HTTP 200**,body `{"code":400,"message":"用户不存在"}`(1.43s) | ❌ **B-69 第 14 轮**:未入 `LOGIN_REQUIRED_PATHS` |
+| `POST /api/huawei-health|xiaomi-health/*`(匿名) | 全 401 | ✅ B-61/B-50 维持 |
+| `POST /api/health/plan/{adjust,attribution,generate}`(匿名) | 全 401 | ✅ |
+| `251:9000 POST /api/v1/chat` | **000 / 45.0s / 0B** | ❌ **B-90 第 7 次**:挂起(无超时/熔断) |
+| `251:9000 POST /api/v1/analyze` | 422(入参校验层快速拒绝) | ⚠️ 校验层正常;业务层挂起未复测(同 B-90 族) |
+
+### 5.9 SRS / 场景覆盖量化(口径写死在 `docs/tdd/tools/test_report_metrics.py`)
+
+| 指标 | 数值 |
+|---|---|
+| SRS 规格文件 | 200(本轮零变更) |
+| 用户场景 | 215;经 TC 映射 **155(72.1%)**;被 spec 字面引用 **32(14.9%)** |
+| 结构化 TC | **793**(异常流 477 = **60.2%**) |
+| TC 有测试资产锚点 | **86 / 793 = 10.8%** |
+| 测试资产扫描量 | 500 |
+
+**本轮用例更新(因 S-cfc-130 实现落地)**:新增 `tests/test-cases/health-datasource-bridge.md`,把规格 §3.1/§3.2/§4/§6 逐条落为 **TC-HB-01~16 + 3 条异常流设计**,并绑定已执行的 `HealthDataSourceBridgeContractTest` 16 个方法;同时如实登记 3 项未覆盖缺口(G-115-1~3)。
+
+---
+
+## 六、覆盖缺口清单
+
+| 缺口 | 类型 | 现状 | 风险 | 建议测试设计 | 优先级 | 闭环方式 |
+|---|---|---|---|---|---|---|
+| G-1 | 覆盖缺口 | TC 锚点 10.8%(86/793) | 「设计有用例、脚本无绑定」→ 回归靠人工 | 按 `tests/test-cases/*.md` 逐模块补 `@DisplayName`/spec 注释锚点,优先 P0 | P1 | 锚点率 ≥40% 后纳入门禁 |
+| G-2 | 覆盖缺口 | 桥接 E-2/E-3(同步→overview 读分、indicators 展示)无 MockMvc 用例 | 桥接「写进去」有证据、「读出来」无证据 | 补 2 例 MockMvc 契约 | P1 | 定向复跑全绿入台账 |
+| G-3 | 覆盖缺口 | 桥接 B-1/B-2 端点与前端绑定链路**未实现**(`cfc-frontend` 0 处 `huawei-health` 调用) | 用户侧 D/X 价值仍不可用 | 实现后补 3 例接口 + 3 条 Playwright | P2 | 实现落地后登记 |
+| G-4 | 覆盖缺口 | 根级黑盒 23 例 **16 红**(B-36) | 接口层黑盒长期失效 | 改为可注入登录态的 fixture,或降级为显式 skip | P1 | 0 红或全 skip 明示 |
+| G-5 | 工具缺口 | 覆盖率/变异/压测/SAST/依赖扫描全无 | 无法量化内部质量 | 引入 jacoco + pitest(离线可跑);k6/zap 需 execute 授权 | P2 | 产物入库后纳入门禁 |
+| G-6 | 流程缺口 | CI 仅 `mvn test-compile` + 前端 jest | 后端 2864 例不在 CI,回归靠定时任务 | CI 增 `mvn -o test` 作业(可先非阻塞) | P1 | pipeline 出现 `Tests run` 行 |
+| G-7 | 规范缺口 | `@GetMapping` 8 处(违 AGENTS.md 统一 `@PostMapping`) | 契约口径分裂 | 改 POST 或修订规范 | P2 | grep 归零 |
+| G-8 | 资产缺口 | `@Disabled` 4 例 + `tests/qna` 收集期错误 | 静默失效 | 修或删 | P2 | 收集期 0 error |
+
+---
+
+## 七、域结论表
+
+| 审查维度 | 负责专家 | 结论 | 关键证据 |
+|---|---|---|---|
+| 单元·组件级 | 苏雅(unit-component) | **不通过** | 2864 例 98.67%;34 红中 A 类 16(测试装置);覆盖率/变异未实测(无工具) |
+| 集成·接口级 | 柯砚(integration-interface) | **不通过** | 契约 16/16(桥接)+14/14(产品多选一)绿;`DimensionRegistryMapContractTest` 3 红(P-9 口径);接口黑盒 16 红(B-36) |
+| 系统·验收级 | 白樾(system-acceptance) | **通过(E2E 维度)** | 浏览器 28/28;UAT/BDD 未实测(无资产) |
+| 测试保障级 | 闻朔(test-assurance) | **有条件通过** | 冒烟/回归资产在;探索性无记录;回归全量可复现 |
+| 性能·可靠性 | 骆骋(performance-reliability) | **未实测** | 无压测工具/未授权联网;251 chat 45s 零字节属可靠性实证(B-90) |
+| 安全 | 关屿(security) | **未实测(仅鉴权面)** | 鉴权契约 13/13;`/api/mind/fortune` 匿名 200(B-69 第 14 轮);SAST/DAST/SCA 无工具 |
+| 兼容·体验 | 阮清(compatibility-experience) | **未实测** | 仅 chromium 单浏览器;无 axe/可用性任务 |
+| 流程·度量·代码 | 何序(process-metrics) | **不通过** | CI 无后端全量;TC 锚点 10.8%;`@GetMapping` 8 处违约定 |
+
+---
+
+## 八、五要素问题清单
+
+### P-1(P1 · A 类 · 16 例)测试装置缺陷致红,掩盖真实回归信号
+- **① 问题描述**:`CfCommissionServiceSettleTest` 8 例 `WantedButNotInvoked`、`ArticleAutoTagServiceTest` 4 例 `execute` 调用次数不符、`GrowthRecordSyncFlowTest` 3 例、`TaskPlanServiceTest` 1 例。
+- **② 测试 bug 点**:`CfCommissionServiceSettleTest#settle_F03a发放_自动创建感恩记录` 等 8 例(档位查询未打桩→`earnByRule` 未触发);`ArticleAutoTagServiceTest#toggleStatus_发布且标签为空_调度自动打标` 等 4 例(`threadPoolTaskExecutor.execute` 实测 2 次 vs 期望 1 次);`GrowthRecordSyncFlowTest#parentUploadsHealthReport_Success`(NPE 首帧在测试方法内,无生产帧)。
+- **③ 最优修改方案**:CfCommission 8 例补 `dimensionTierService`/档位查询桩;ArticleAutoTag 4 例把「未调度」断言改为 `verify(executor, times(n))` 并按生产实际调度口径对齐(或抽 `scheduleAutoTagIfNeeded` 单点计数);GrowthRecordSync 3 例子 `@BeforeEach` 补齐 mock 依赖返回。
+- **④ 修改目的**:把 16 例噪音从红例池剔除,使 G2 红例数可直接反映产品缺陷。
+- **⑤ 闭环**:定向复跑 4 类 0 红 → 全量 clean 复跑红例数由 34 降至 ≤18,且红类集合差集恰为这 4 类。
+
+### P-2(P0 · B 类 · 14 例)产品缺陷/契约行为不一致
+- **① 问题描述**:`EnergySystemFlowTest` 6 例 500/语义不符;`OrchestrationEngineTest`/`OrchestrationIntegrationTest` 各 1 例状态机期望不一致;`MembershipPaySwitchTest`、`PaymentServiceORDRoutingTest`、`UserServiceWechatLogin*` 2 例、`TeamProfitCronContractTest` 各 1 例断言不符。
+- **② 测试 bug 点**:`EnergySystemFlowTest#parentQueriesEnergyLogs_Success`(expected 200 but 500)等 6 例;`OrchestrationEngineTest#testOnTaskFailed_FoundNode_ThenMarkFailedAndEvaluateFlow`(expected "manual_fail" but null);`TeamProfitCronContractTest#refundWithinCooling_cancelAllRecordsFundsNotPaid`(cancelled=0)。
+- **③ 最优修改方案**:由研发按类逐个复现——EnergySystem 查 500 堆栈最内层(历史为 NullBean/依赖缺失);Orchestration 对齐 `task.status` 写入口径;TeamProfitCron 按 §8.2 补冷却期退款取消逻辑。
+- **④ 修改目的**:消除产品侧真实缺陷,使五维能量/编排/分佣三条主链路可信。
+- **⑤ 闭环**:每类定向复跑 0 红 + 全量红例下降;EnergySystem 需附 500 根因说明。
+
+### P-3(P1 · 契约 · 3 例)`DimensionRegistryMap` 契约口径未决(第 4 轮)
+- **① 问题描述**:`map_unknownLegacy/emptyLegacy` 期望 `data=null` 实返原值/空串;`map_missingLegacy` 期望 400 实返 200。
+- **② 测试 bug 点**:`DimensionRegistryMapContractTest#map_unknownLegacy_returnsNullData`、`#map_emptyLegacy_returnsNullData`、`#map_missingLegacy_returnsBadRequest`。
+- **③ 最优修改方案**:由主理人裁定「未知码是否映射为 null」并同步改契约或实现;若维持现实现,则改用例断言并在台账标注「口径变更」。
+- **④ 修改目的**:终止「测试与实现对同一契约各说一套」的长期挂账。
+- **⑤ 闭环**:裁定落地后 3 例转绿,`automation-status.md` 记录裁定条款。
+
+### P-4(P0 · 安全 · 第 14 轮)`/api/mind/**` 鉴权缺口
+- **① 问题描述**:线上匿名 `POST /api/mind/fortune` 返回 **HTTP 200** + `{"code":400,"message":"用户不存在"}`,未 401;后端 `MindFortuneControllerTest#testGetTodayFortuneWithoutAuth` 同源红(`$.code expected 401 but was 400`)。
+- **② 测试 bug 点**:`MindFortuneControllerTest#testGetTodayFortuneWithoutAuth`。
+- **③ 最优修改方案**:`JwtInterceptor.LOGIN_REQUIRED_PATHS` 增 `/api/mind/`(与 `/api/huawei-health/` 同法),并保持控制器内 `@RequestAttribute` 分支不变。
+- **④ 修改目的**:成员私有数据不得匿名称谓探测(B-69 连续 14 轮)。
+- **⑤ 闭环**:线上匿名 `POST /api/mind/fortune` 返回 401 + 单测转绿。
+
+### P-5(P0 · AI 服务 · 第 7 次)251 `chat` 挂起、`analyze` checkpointer 缺 `thread_id`
+- **① 问题描述**:`251:9000 POST /api/v1/chat` 45.0s 零字节(http=000);`test_analyze.py` 2 例 `assert 500 == 200`(`Checkpointer requires ... thread_id`)。
+- **② 测试 bug 点**:`tests/test_analyze.py::test_analyze_endpoint`、`::test_analyze_missing_report`;探针 `POST /api/v1/chat`。
+- **③ 最优修改方案**:① `app/analyze.py` ainvoke 传 `config={"configurable":{"thread_id":...}}` 或去掉 `MemorySaver()`;② 为 LLM/检索调用加 timeout + 熔断(挂起比 500 更差,会占满连接池)。
+- **④ 修改目的**:AI 主链路可用性与可观测性。
+- **⑤ 闭环**:251 实派 pytest analyze 2 例绿 + `POST /chat` 在 30s 内返回 2xx/4xx(非 000)。
+
+### P-6(P1 · 环境/凭据 · 第 16 轮)langgraph `test_auto_tag` 5 例 + `test_intent` 4 例
+- **① 问题描述**:`test_auto_tag.py` 5 例失败(251 侧 LLM 401);`test_intent.py` 4 例 `assert <Intent.CHAT> == <Intent.ANALYSIS/...>`(LLM 401 后静默降级 chat)。
+- **② 测试 bug 点**:`tests/test_auto_tag.py::test_auto_tag_endpoint_responds` 等 5 例;`tests/test_intent.py::test_classify_analysis` 等 4 例。
+- **③ 最优修改方案**:251 注入有效 LLM 凭据;`intent_classifier.py:62` 异常时不得静默降级(应标记 `UNKNOWN` 并上报)。
+- **④ 修改目的**:把「凭据缺失」与「分类错误」解耦,避免降级掩盖故障。
+- **⑤ 闭环**:251 实派 43 例 0 红。
+
+### P-7(P1 · 流程)后端全量不在 CI,TC 锚点仅 10.8%
+- **① 问题描述**:`.gitlab-ci.yml` 仅 `mvn test-compile` + 前端 jest;793 条 TC 中仅 86 条有资产锚点。
+- **② 测试 bug 点**:`.gitlab-ci.yml`(test 阶段无后端 `mvn test`);`tests/test-cases/*.md` 锚点缺失(如 `admin.md#TC-A-03` 等)。
+- **③ 最优修改方案**:新增 `backend-test` job(`mvn -o -B test -Dmaven.test.failure.ignore=true` 产出 surefire 为 artifact,先非阻塞);用例补 `@DisplayName("TC-XXX …")`。
+- **④ 修改目的**:让「回归」从事后定时任务变为提交即反馈。
+- **⑤ 闭环**:pipeline 出现 `Tests run: N` 且 artifact 可下载;锚点率月度提升至 ≥40%。
+
+---
+
+## 九、门禁结论
+
+**⛔ 不通过(有条件)** —— 质量基线本轮**明显抬升**(后端 98.17%→98.67%,红例 48→34,B-112/B-113 双闭环),但用户「全部用例通过」目标仍未达成。
+
+| 门禁 | 判据 | 实测 | 结论 |
+|---|---|---|---|
+| G1 全量通过率 | ≥98% | **98.06%**(后端 98.67%) | ✅ |
+| G2 红例数 | 0 | **61**(后端 34 + 根级 16 + langgraph 11) | ❌ |
+| G3 前端 jest | 全绿 | 423/426(3 skip,0 红) | ✅ |
+| G4 管理端 jest | 全绿 | 116/116 | ✅ |
+| G5 后端编译完整性 | 0 错、无沉默 | 编译 0 错;**339/339 类 XML 全产出**、沉默 0 | ✅ |
+| G6 浏览器 E2E | 全绿 | **28/28**(本机)+ 189 节点 5/5 | ✅ |
+| G7 langgraph | 0 红 | 32/43(**11 红**) | ❌ |
+| G8 鉴权契约 | 全绿 | health 13/13 + `/api/mind` **1 红** | ❌ |
+| G9 SRS 锚点 | ≥40% | **10.8%**(86/793) | ❌ |
+| G10 节点派发 | 双节点可派 | **251 ✅(pytest 实派取回)+ 189 ✅(admin 5/5)** | ✅ |
+| G11 CI 接入 | 含后端全量 | 仅 test-compile + 前端 jest | ❌ |
+| G12 测试资产入库 | 新增用例已入库 | 本轮新增 TC 文档 + 修夹具**待提交**(本次随报告提交) | ✅(提交后) |
+
+**放行建议**:维持「有条件通过」——可继续灰度,但 P-4(鉴权)、P-5(AI 挂起)应在下一次发布前闭环;P-1(16 例装置)建议本周内清理以恢复红例信号可信度。
+
+**复评条件**:G2 红例 ≤18(清 P-1 后)且 G8/G9 达标,可升「有条件通过(收紧)」。
+
+---
+
+## 十、本轮产出与通知
+
+| 项 | 路径 |
+|---|---|
+| 本报告 | `docs/tdd/orchestration/report-test-2026-11-10-0416.md` |
+| 用例台账(新增) | `tests/test-cases/health-datasource-bridge.md`(TC-HB-01~16 + 3 异常流 + 3 缺口) |
+| 测试装置修复 | `cfc-backend/src/test/java/.../DimensionScoreServiceTest.java`(21 例转绿) |
+| 派发工具(新增) | `docs/tdd/tools/dispatch_shell.cjs` |
+| 阻断库 | `docs/tdd/test/blockers.md`(B-112/B-113 闭环 + B-69/B-90/B-65/B-74/B-82 更新) |
+| 研发通知 | `docs/tdd/研发交接通知-2026-11-10-第115轮.md` |
+| 证据(未入库) | `_r115_backend.log`、`_r115_agg.json`/`_r115_agg2.json`、`_r115_251_pytest.log` |
+
+**通知通道**:Agent Mail 实测 `GetMe` → `{"_agentmail_meta":{"status":"not_bound"}}`(未开通,不可用)→ 以仓库通知文档(`docs/tdd/研发交接通知-2026-11-10-第115轮.md`)+ 推送 `origin/cfclub` 落地。

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

@@ -273,3 +273,18 @@
 | B-65-Update(第 113 轮 · 连续第 19 轮) | langgraph `/api/v1/analyze` checkpointer 缺 thread_id | 研发 | **连续第 19 轮未修(P0)**。本机 venv:`test_analyze.py` 2 例 `assert 500 == 200`,异常 `Checkpointer requires ... thread_id`(源码未变:`app/analyze.py:35` + `analysis_graph.py:96 MemorySaver()`) | 测试负责人(第 113 轮复现) | 2026-11-10 02:20 | 待处理(P0,第 19 轮) |
 | B-65-Update(第 113 轮 · 连续第 19 轮) | langgraph `/api/v1/analyze` checkpointer 缺 thread_id | 研发 | **连续第 19 轮未修(P0)**。本机 venv:`test_analyze.py` 2 例 `assert 500 == 200`,异常 `Checkpointer requires ... thread_id`(源码未变:`app/analyze.py:35` + `analysis_graph.py:96 MemorySaver()`) | 测试负责人(第 113 轮复现) | 2026-11-10 02:20 | 待处理(P0,第 19 轮) |
 | B-74-Update(第 113 轮 · 连续第 15 轮) | langgraph `test_auto_tag.py` 5 例红 | 研发/运维 | **连续第 15 轮**。本机 venv:5 例失败(本机 chromadb DLL 环境);251 侧同 5 例 LLM 401 需注入有效凭据 | 测试负责人(第 113 轮复现) | 2026-11-10 02:20 | 待处理(P1) |
 | B-74-Update(第 113 轮 · 连续第 15 轮) | langgraph `test_auto_tag.py` 5 例红 | 研发/运维 | **连续第 15 轮**。本机 venv:5 例失败(本机 chromadb DLL 环境);251 侧同 5 例 LLM 401 需注入有效凭据 | 测试负责人(第 113 轮复现) | 2026-11-10 02:20 | 待处理(P1) |
 | B-82-Update(第 113 轮 · 连续第 15 轮) | langgraph `test_intent.py` 4 例红(分类降级) | 研发 | **连续第 15 轮未修**。4 例 `assert <Intent.CHAT> == <Intent.ANALYSIS/RECOMMEND/MIND/HEALTH>`,日志 LLM 401 → 静默降级 chat | 测试负责人(第 113 轮复现) | 2026-11-10 02:20 | 待处理(P1) |
 | B-82-Update(第 113 轮 · 连续第 15 轮) | langgraph `test_intent.py` 4 例红(分类降级) | 研发 | **连续第 15 轮未修**。4 例 `assert <Intent.CHAT> == <Intent.ANALYSIS/RECOMMEND/MIND/HEALTH>`,日志 LLM 401 → 静默降级 chat | 测试负责人(第 113 轮复现) | 2026-11-10 02:20 | 待处理(P1) |
+
+<!-- ===== 第 115 轮(2026-11-10 04:16,本自动化)追加 ===== -->
+
+| B-112-Close(第 115 轮 · 闭环) | S-cfc-129 `health.dataSource.bridgeWrite` 未落地 | 研发 | **已闭环**。`47d203981`(02:42)合入 `HealthDataSourceBridgeService`(174 行)+ 华为/小米同步同事务接线 + `schema.sql` data_source 注释扩 `DEVICE` + `DimensionScoreServiceImpl` 死注入解除。全量实测 `HealthDataSourceBridgeContractTest` **16/16 全绿**(R112 为 14/16 红)→ §6 四项拍板(DEVICE / tier=2 / 触发点 A / 不加唯一索引)全部被契约锚定。闭环判据(契约 16/16 绿)达成;规格文件 `2026-11-08-dx-health-bridge.md` 亦已存在(R112「无规格」判定有误,本轮勘误) | 测试负责人(第 115 轮实测) | 2026-11-10 04:05 | **已闭环** |
+| B-113-Close(第 115 轮 · 误判纠正 + 闭环) | 189 shell 通道不可用 / 251 派发取回退化 | 运维/测试 | **已闭环(原判定为误判)**。`openapi.json` 实证:`POST /api/session/{id}/shell` **设计上恒返 204**,且 `GET /api/shell` **只列 running**、命令秒退即消失 → 旧派发器轮询列表必然 `NONE`。正确入口 `POST /api/shell?location[directory]=<dir>`(body `{command,cwd}`)→ 返回 `Shell.Info.id` → `GET /api/shell/{id}/output`;长任务须「重定向日志 + 另起 shell tail」防对象回收。**本轮实派成功**:251 pytest 43/32/11(79.05s)、189 admin-desktop **5/5**(1.9m,首次)。补充纪律:Git Bash 必带 `MSYS_NO_PATHCONV=1`,否则 `/sc-data` 被改写为本机路径。工具入库 `docs/tdd/tools/dispatch_shell.cjs` | 测试负责人(第 115 轮实派) | 2026-11-10 04:10 | **已闭环** |
+| B-115-New(第 115 轮 · A 类装置) | 生产删字段导致夹具 `NoSuchFieldException`(21 例) | 测试 | **已自处置**。S-cfc-130 移除 `DimensionScoreServiceImpl.dataSourceRecordService` 后,`DimensionScoreServiceTest` 仍反射注入该字段 → 全类 **21 E**。删夹具 `@Mock` 字段与 `setField` 一行(不改被测语义)→ 定向复跑 **21/21 绿**(4.98s)。教训:生产「死注入解除」类重构必须同步清理夹具反射注入,否则整类报错会被误读为产品大面积退化 | 测试负责人(第 115 轮实测) | 2026-11-10 03:52 | **已闭环** |
+| B-115-New2(第 115 轮 · 度量勘误) | R112「specs 无 bridgeWrite 规格」判定有误 | 测试 | **勘误**。`docs/superpowers/specs/2026-11-08-dx-health-bridge.md` 一直存在(含 §3.1 字段映射全表 / §4 SQL 契约 / §6 四项拍板),R112 的 grep 关键词未命中 `bridgeWrite` 字面 → 误判「验收无依据」。已在新用例文档 `tests/test-cases/health-datasource-bridge.md` 中把该规格逐条落 TC | 测试负责人(第 115 轮复核) | 2026-11-10 04:10 | **已勘误** |
+| B-69-Update(第 115 轮 · 连续第 14 轮) | `/api/mind/**` 鉴权缺口 | 研发 | **连续第 14 轮未修(P0)**。线上匿名 `POST /api/mind/fortune` → **HTTP 200** + `{"code":400,"message":"用户不存在"}`(1.43s);后端 `MindFortuneControllerTest#testGetTodayFortuneWithoutAuth` → `$.code expected 401 but was 400`。闭环判据不变:`JwtInterceptor` 增 `/api/mind/` | 测试负责人(第 115 轮实测) | 2026-11-10 04:12 | 待处理(P0,第 14 轮) |
+| B-90-Update(第 115 轮 · 第 7 次复证) | 251 `chat` 挂起 | 研发/运维 | **第 7 次复证(P0 未修)**。`POST 251:9000/api/v1/chat`(`query`+`user_id`)→ **000 / 45.0s / 0 字节**;`/api/v1/analyze` 同参 → **422**(入参校验层快速拒绝,业务层未穿透)。`251:9000/health` 200(6ms)。定性不变:LLM/检索无 timeout+熔断(B-84 同源) | 测试负责人(第 115 轮实测) | 2026-11-10 04:12 | 待处理(P0,第 7 次) |
+| B-65-Update(第 115 轮 · 连续第 20 轮) | langgraph `/api/v1/analyze` checkpointer 缺 thread_id | 研发 | **连续第 20 轮未修(P0)**。251 实派:`test_analyze.py` 2 例 `assert 500 == 200`,与既往逐例一致 | 测试负责人(第 115 轮复现) | 2026-11-10 04:08 | 待处理(P0,第 20 轮) |
+| B-74-Update(第 115 轮 · 连续第 16 轮) | langgraph `test_auto_tag.py` 5 例红 | 研发/运维 | **连续第 16 轮**。251 实派:5 例 `AssertionError`(LLM 401)。闭环判据不变 | 测试负责人(第 115 轮复现) | 2026-11-10 04:08 | 待处理(P1) |
+| B-82-Update(第 115 轮 · 连续第 16 轮) | langgraph `test_intent.py` 4 例红(静默降级) | 研发 | **连续第 16 轮未修**。4 例 `assert <Intent.CHAT> == <Intent.ANALYSIS/RECOMMEND/MIND/HEALTH>`;`intent_classifier.py:62` 异常即降级 chat | 测试负责人(第 115 轮复现) | 2026-11-10 04:08 | 待处理(P1) |
+| B-15-Update(第 115 轮 · 新形态) | `tests/qna` 收集期错误中断整轮 pytest | 测试/研发 | **复发(新形态)**。251 首派 `pytest tests --ignore=tests/test_chat.py` → `ERROR tests/qna/test_graph.py`(`ModuleNotFoundError: No module named 'src.qna'`)→ `Interrupted: 1 error during collection`,**0 例执行**。加 `--ignore=tests/qna` 后 43 例正常。闭环判据:删除死资产或补 `src/qna` 包 | 测试负责人(第 115 轮实派) | 2026-11-10 04:02 | 待处理(P1) |
+| B-95-Update(第 115 轮 · 第 5 轮失联) | `192.168.16.204` 节点失联 | 运维 | **第 5 轮失联**。`204:4090` → 000(2.43s)。本轮浏览器任务由本机 + 189 承担,不受影响 | 测试负责人(第 115 轮探活) | 2026-11-10 04:00 | 待处理(P2) |
+| B-36-Update(第 115 轮 · 零漂移) | 根级黑盒 23 例 16 红 | 测试 | **零漂移**。`cd tests && jest` → 23 例 7 绿 / 16 红(与 R112/R113 逐项一致),仍为环境门控/凭据缺失所致 | 测试负责人(第 115 轮实测) | 2026-11-10 03:55 | 待处理(P1) |

+ 64 - 0
docs/tdd/tools/dispatch_shell.cjs

@@ -0,0 +1,64 @@
+// opencode 直连 shell 派发器(R115 修正 B-113 误判:session 路由 204 是设计行为,不是失败)
+// 关键事实(openapi.json 实证):
+//   1. POST /api/shell?location[directory]=<dir>  ← 正确入口,直接返回 Shell.Info(含 id)
+//   2. POST /api/session/{id}/shell 恒返 204(无 body),且 shell 退出后即被 GC
+//      → 旧脚本轮询 /api/shell(list 只列 running)必然拿到 NONE,误判为「建不出对象」
+//   3. 长任务必须「重定向到日志文件 + 另起 shell 读文件」,否则 shell 对象被回收后
+//      /api/shell/{id}/output 返回 ShellNotFoundError
+// 用法(Git Bash 必须带 MSYS_NO_PATHCONV=1,否则 /sc-data 会被改写成本机路径):
+//   MSYS_NO_PATHCONV=1 BASE=http://192.168.16.251:4096 DIR=/sc-data/cfc/cfc-langgraph \
+//     CMD="pytest -q" LOG=/tmp/x.log node dispatch_shell.cjs
+const BASE = process.env.BASE;
+const DIR = process.env.DIR;
+const CMD = process.env.CMD;
+const LOG = process.env.LOG || '/tmp/dispatch_shell.log';
+const CAP_MS = parseInt(process.env.CAP_MS || '900000', 10);
+const AUTH = 'Basic ' + Buffer.from('opencode:' + (process.env.OPENCODE_SERVER_PASSWORD || 'IwinTrue@123')).toString('base64');
+const H = { Authorization: AUTH, 'Content-Type': 'application/json' };
+const LOC = '?location%5Bdirectory%5D=' + encodeURIComponent(DIR || '');
+
+async function j(url, opt) {
+  try {
+    const r = await fetch(url, Object.assign({ headers: H }, opt));
+    const t = await r.text();
+    try { return { status: r.status, json: JSON.parse(t) }; } catch { return { status: r.status, text: t }; }
+  } catch (e) { return { status: 0, err: e.message }; }
+}
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+
+async function run(cmd, capMs) {
+  const cr = await j(BASE + '/api/shell' + LOC, { method: 'POST', body: JSON.stringify({ command: cmd, cwd: DIR }) });
+  const id = cr.json && cr.json.data && cr.json.data.id;
+  if (!id) return { err: 'create failed ' + cr.status + ' ' + JSON.stringify(cr).slice(0, 200) };
+  const t0 = Date.now();
+  while (Date.now() - t0 < capMs) {
+    await sleep(3000);
+    const s = await j(BASE + '/api/shell/' + id + LOC);
+    const d = (s.json && s.json.data) || null;
+    if (!d) continue; // GC/竞态:继续等下一次
+    if (d.status && d.status !== 'running') {
+      const o = await j(BASE + '/api/shell/' + id + '/output' + LOC + '&limit=200000');
+      return { status: d.status, out: (o.json && o.json.data && o.json.data.output) || '' };
+    }
+  }
+  return { err: 'timeout ' + capMs + 'ms' };
+}
+
+(async () => {
+  // 1) 后台启动长任务并写盘(避免 shell 对象被回收)
+  const kick = await run('rm -f ' + LOG + '; ( ' + CMD + ' ) > ' + LOG + ' 2>&1; echo DONE_$? >> ' + LOG, 30000);
+  console.log('KICK', kick.err || kick.status);
+  // 2) 轮询日志文件
+  const t0 = Date.now();
+  let tail = '';
+  while (Date.now() - t0 < CAP_MS) {
+    await sleep(8000);
+    const r = await run('tail -60 ' + LOG, 45000);
+    if (r.err) { console.log('POLL_ERR', r.err); continue; }
+    tail = r.out || '';
+    if (/DONE_\d+/.test(tail)) break;
+  }
+  console.log('OUT_BEGIN');
+  console.log(tail);
+  console.log('OUT_END');
+})().catch(e => { console.error('ERR', e.message); process.exit(1); });

+ 69 - 0
docs/tdd/研发交接通知-2026-11-10-第115轮.md

@@ -0,0 +1,69 @@
+# 研发交接通知 · 第 115 轮(2026-11-10 04:16)
+
+> 发起:测试负责人(软件测试专家团 · 自动化闭环轮)
+> 基线:`cfclub` @ `390a02963`(= origin/cfclub)
+> 结论:**不通过(有条件)** — 全量 3500 例 / 3432 过 = **98.06%**;后端 2864 例 / 2826 过 = **98.67%**(较第 112 轮 98.17% **+0.50pp**,红例 48 → **34**)
+> 详细报告:`docs/tdd/orchestration/report-test-2026-11-10-0416.md`
+
+## 一、本轮已闭环(无需研发动作)
+
+| 项 | 说明 |
+|---|---|
+| **B-112(P0 契约未实现)→ 已闭环** | S-cfc-130 `47d203981` 落地后,`HealthDataSourceBridgeContractTest` **16/16 全绿**(此前 14 红)。§6 四项拍板全部被契约锚定:`data_source=DEVICE` / `tier=2` / 触发点 A(同步内事务)/ 不加唯一索引;小米与华为映射对称;`body_weight` 双维展开、未知码丢弃、同日读最新覆盖、不覆盖 REPORT 行均已验证。 |
+| **B-113(节点派发通道)→ 误判纠正 + 闭环** | 旧派发器走的 `POST /api/session/{id}/shell` **设计上恒返 204**,且 `/api/shell` 列表只列 running → 一直被误判为「建不出对象」。正确入口是 `POST /api/shell?location[directory]=<dir>`。本轮据此**实派成功**:251 pytest 43/32/11(79s)、189 admin-desktop **5/5**(1.9m)。工具已入库 `docs/tdd/tools/dispatch_shell.cjs`。 |
+| **B-114(HL-05 覆盖缺口)→ 后端侧维持闭环** | `HealthPlanProductSelectionContractTest` **14/14 绿**;前端 Jest 与 E2E 各 1 条仍缺。 |
+| **测试侧跟随重构(1 处)** | S-cfc-130 删除 `DimensionScoreServiceImpl.dataSourceRecordService` 后,`DimensionScoreServiceTest` 全类 21 例 `NoSuchFieldException`;已删夹具中的反射注入 → **21/21 全绿**。请后续若再动该 Service 依赖,同步改夹具。 |
+
+## 二、需要研发处置(按优先级)
+
+### P0-1 `/api/mind/**` 匿名可达(B-69 · 连续第 14 轮)
+- 实测:`POST http://cfc.iwintrue.com/api/mind/fortune` 匿名 → **HTTP 200** + `{"code":400,"message":"用户不存在"}`(1.43s)。
+- 单测:`MindFortuneControllerTest#testGetTodayFortuneWithoutAuth` → `$.code expected 401 but was 400`。
+- 修法:`JwtInterceptor.LOGIN_REQUIRED_PATHS` 增加 `/api/mind/`(与 `/api/huawei-health/` 同法)。
+- 验收:匿名 401;单测转绿。
+
+### P0-2 AI 服务可用性(B-90 第 7 次 + B-65 第 20 轮)
+- `POST 192.168.16.251:9000/api/v1/chat` → **000 / 45.0s / 0 字节**(长挂起,占连接池且无错误信号)。
+- `tests/test_analyze.py` 2 例 `assert 500 == 200`:`MemorySaver()` 存在但 ainvoke 未传 `configurable.thread_id`。
+- 修法:① analyze 传 `thread_id` 或移除 checkpointer;② 给 LLM/检索加 **timeout + 熔断**(挂起劣于 500)。
+- 验收:251 实派 pytest analyze 2 例绿;`/chat` 在 30s 内返回 2xx/4xx。
+
+### P1-1 产品缺陷 14 例(后端红例中 B 类)
+| 类 | 例数 | 现象 |
+|---|---|---|
+| `EnergySystemFlowTest` | 6 | 多数接口 **500**(能量日志/兑换/概览/任务授予) |
+| `OrchestrationEngineTest` / `OrchestrationIntegrationTest` | 2 | 失败节点未写 `manual_fail`;流程状态实为 `failed`(期望 in_progress/pending) |
+| `MembershipPaySwitchTest` | 1 | 虚拟支付开关缺配置时应返 null,实返订单对象 |
+| `PaymentServiceORDRoutingTest` | 1 | 未知前缀回调路由期望 SUCCESS 实 FAIL |
+| `UserServiceWechatLoginContractTest` / `UserServiceWechatLoginTest` | 2 | 首次登录未返回 jwt(null) |
+| `TeamProfitCronContractTest` | 1 | 冷却期退款后 `team_share_record` 未置 cancelled(§8.2) |
+| `GuideModuleIntegrationTest` | 1 | `platform_fee` **Data truncation**(真库字段宽度不足) |
+
+### P1-2 契约口径待裁定(P-9 · 第 4 轮)
+`DimensionRegistryMapContractTest` 3 例:未知/空 legacy 码期望 `data=null`,实现返原值/空串;缺失参数期望 400,实现返 200。请主理人裁定「未知码是否映射 null」,据此改实现或改用例(**不要再挂账**)。
+
+### P1-3 langgraph LLM 凭据与降级(B-74/B-82 · 第 16 轮)
+- `test_auto_tag.py` 5 例 → 251 侧 LLM **401**;
+- `test_intent.py` 4 例 → 401 后 `intent_classifier.py:62` **静默降级 chat**,导致断言失败。
+- 修法:251 注入有效凭据;异常时标记 `UNKNOWN` 并上报,不静默降级。
+
+### P2 工程卫生
+- `@GetMapping` **8 处**(`AGENTS.md` 要求统一 `@PostMapping`);
+- CI 无后端全量(`mvn test-compile` 之后直接前端 jest),建议加非阻塞 `backend-test` job 并上传 surefire artifact;
+- `@Disabled` 4 例、`tests/qna` 收集期 `ModuleNotFoundError: src.qna`(会使整轮 pytest 中断,本轮已 `--ignore`)。
+
+## 三、测试资产新增
+- `tests/test-cases/health-datasource-bridge.md`:把规格 `2026-11-08-dx-health-bridge.md` §3.1/§3.2/§4/§6 落为 **TC-HB-01~16** 并绑定已执行用例;另登记 3 项缺口(E-2/E-3 MockMvc 缺、B-1/B-2 端点未实现、前端 0 调用)。
+- 注意:`cfc-frontend` 全仓仍 **0 处** `huawei-health`/`xiaomi-health` 调用 → D/X 链路用户侧仍不可用(G-3)。
+
+## 四、复测方式
+```bash
+# 后端全量(JDK8 + Maven 3.6.1)
+cd cfc-backend && JAVA_HOME=D:/jdk1.8.0_25 /d/maven/apache-maven-3.6.1/bin/mvn -o -B clean test
+# 节点派发(Git Bash 必须 MSYS_NO_PATHCONV=1)
+MSYS_NO_PATHCONV=1 BASE=http://192.168.16.251:4096 DIR=/sc-data/cfc/cfc-langgraph \
+  CMD="cd /sc-data/cfc/cfc-langgraph && .venv/bin/python -m pytest tests --ignore=tests/test_chat.py --ignore=tests/qna -q" \
+  node docs/tdd/tools/dispatch_shell.cjs
+# 浏览器(tests/ 内执行,workers:1)
+cd tests && npx playwright test -c playwright.config.live.js && npx playwright test -c playwright.config.health.js
+```

+ 56 - 0
tests/test-cases/health-datasource-bridge.md

@@ -0,0 +1,56 @@
+# 测试用例 · D/X 域桥接(health.dataSource.bridgeWrite)
+
+> 关联 SRS:`docs/superpowers/specs/2026-11-08-dx-health-bridge.md`(GAP-A 契约,O-17 裁决,2026-11-08 合入 cfclub)。
+> 能力键:`health.dataSource.bridgeWrite`;任务编号 S-cfc-129(契约/RED)→ S-cfc-130(实现,`47d203981`)。
+> 实现基线:`HealthDataSourceBridgeService`(174 行)+ `HealthSyncServiceImpl`/`XiaomiHealthServiceImpl` 同事务接线 + `schema.sql` data_source 注释扩 `DEVICE` + `DimensionScoreServiceImpl` 死注入解除。
+> 派生自 `user-scenarios/` 的「家长绑定运动健康 → 同步 → 五维得分/档案可见」场景(D 域华为 / X 域小米)。
+> **状态**:实现已合入(2026-11-10 02:42),本文件由测试负责人在第 115 轮按 §3.1/§3.2/§4/§6 逐条落 TC 并绑定已执行用例。
+
+## 一、正常流用例(契约 §3.1 字段映射 / §3.2 消费接线)
+
+| TC 编号 | 所属场景 | 模块 | 角色 | 优先级 | 类型 | 前置条件 | 测试步骤 | 预期结果 | 关联需求/设计 | 对应 e2e |
+|---|---|---|---|---|---|---|---|---|---|---|
+| TC-HB-01 | D/X-1 | 桥接宿主 | 系统 | P0 | 契约 | 后端 classpath | 1. `Class.forName("com.etotem.cfc.service.HealthDataSourceBridgeService")` | 宿主类存在且可实例化(§3.2-1 建议独立桥接服务) | §3.2-1 | `HealthDataSourceBridgeContractTest#bridgeHost_classExists` |
+| TC-HB-02 | D/X-1 | 桥接宿主 | 系统 | P0 | 契约 | 同上 | 1. 反射查 `bridge` 方法签名 | 存在对外桥接方法,入参含 `HealthDataSourceRecord`(或批次 List) | §3.2-1 | `#bridgeHost_hasBridgeMethod` |
+| TC-HB-03 | D/X-2 | 华为同步 | 家长 | P0 | 功能 | mock `step` 数据点(`body.exercise`) | 1. 调 `bridge` 2. 校验落 `health_dimension_scores` | `member_id`/`dimension=exercise`/`score`=normalized.score/`percentile` 直取;`data_source=DEVICE`;`tier=2`;`raw_data`=整条 parsedResult | §3.1 全字段 | `#bridge_huaweiStep_fieldMappingConformToSpec` |
+| TC-HB-04 | D/X-3 | 小米同步 | 家长 | P0 | 功能 | mock 小米同步行 | 1. 调 `bridge` 2. 校验映射 | 与华为同构:`sourceType=xiaomi_health` → 同一 `DEVICE`/`tier=2` 口径,映射表完全一致 | §3.1 + §2.2 | `#bridge_xiaomiStep_sameDeviceMapping` |
+| TC-HB-05 | D/X-2 | 体重双维展开 | 家长 | P0 | 功能 | mock `body_weight` 行 | 1. 调 `bridge` | 一条 source 产出 **两行**:`nutrition` + `growth`,两行 `score` 相同 | §3.1 dimension 切分 | `#bridge_bodyWeight_expandTwoDimensions` |
+| TC-HB-06 | D/X-2 | 未知维度丢弃 | 家长 | P1 | 异常 | mock `dimension_codes="body.foo,body.sleep"` | 1. 调 `bridge` | 非白名单码被丢弃,仅写 `sleep`,不抛异常、不写非法 dimension | §3.1「未知码丢弃」 | `#bridge_unknownDimensionCodes_dropped` |
+| TC-HB-07 | D/X-2 | 混合合法非法 | 家长 | P1 | 异常 | mock 合法 + 非法码混排 | 1. 调 `bridge` | 合法码全部写入、非法码一律不写(部分成功语义) | §3.1 | `#bridge_mixedValidAndUnknown_writeOnlyValid` |
+| TC-HB-08 | D/X-2 | percentile 可空 | 家长 | P1 | 边界 | mock `normalized.percentile=null` | 1. 调 `bridge` | 仍写入得分行,`percentile` 落 NULL,不因空值中断整批 | §3.1「percentile 可空」 | `#bridge_percentileNull_stillWrites` |
+| TC-HB-09 | D/X-4 | 同日多源覆盖 | 家长 | P0 | 功能 | 同 member+dimension+assess_date 两条 source | 1. 先后桥接两条 | 读最新覆盖(created_at DESC LIMIT 1),最终得分=后一条 | §3.1 读最新覆盖 + §6 ④ 不加唯一索引 | `#bridge_sameDayMultipleRows_latestWins` |
+| TC-HB-10 | D/X-5 | 不覆盖报告行 | 家长 | P0 | 回归 | 已存在 `data_source=REPORT`/`tier=1` 行 | 1. 桥接同维度 DEVICE 行 | 按 tier/data_source 分层保留:REPORT 行有效性不被降低/消灭(防负向回归) | §3.1 写入语义 | `#bridge_mustNotOverwriteExistingReportRow` |
+| TC-HB-11 | D/X-2 | 华为同步接线 | 家长 | P0 | 集成 | `HealthSyncServiceImpl` | 1. 反射检查依赖与调用点 | 同步链路已注入桥接钩子(§6 ③ 触发点 A:同步内同事务) | §3.2-2 | `#huaweiSyncEngine_injectBridgeHook` |
+| TC-HB-12 | D/X-3 | 小米同步接线 | 家长 | P0 | 集成 | `XiaomiHealthServiceImpl` | 1. 反射检查依赖与调用点 | 同 TC-HB-11(两引擎对称) | §3.2-2 | `#xiaomiSyncEngine_injectBridgeHook` |
+| TC-HB-13 | D/X-1 | 死注入解除 | 系统 | P1 | 契约 | `DimensionScoreServiceImpl` | 1. 反射查 `dataSourceRecordService` 字段 | 字段已移除(死注入解除),不再是「注入但零调用」状态 | §3.2-1 | `#dimensionScoreServiceImpl_deadInjectionResolved` |
+
+## 二、SQL / 迁移契约用例(§4)
+
+| TC 编号 | 所属场景 | 模块 | 角色 | 优先级 | 类型 | 前置条件 | 测试步骤 | 预期结果 | 关联需求/设计 | 对应 e2e |
+|---|---|---|---|---|---|---|---|---|---|---|
+| TC-HB-14 | D/X-6 | schema 枚举 | 系统 | P0 | 契约 | `schema.sql` | 1. 读 `health_dimension_scores.data_source` 注释 | 注释取值枚举含 `DEVICE`(§6 ① 已拍板) | §4-1 + §6 ① | `#schema_dimensionScores_dataSourceCommentContainsDevice` |
+| TC-HB-15 | D/X-6 | 不加唯一索引 | 系统 | P0 | 契约 | 同上 | 1. 检查 DDL 索引 | `health_dimension_scores` 无 (member_id,dimension,assess_date) 唯一索引(§6 ④ 已拍板) | §4-2 + §6 ④ | `#schema_dimensionScores_noUniqueIndex` |
+
+## 三、权限流用例(§3.4)
+
+| TC 编号 | 所属场景 | 模块 | 角色 | 优先级 | 类型 | 前置条件 | 测试步骤 | 预期结果 | 关联需求/设计 | 对应 e2e |
+|---|---|---|---|---|---|---|---|---|---|---|
+| TC-HB-16 | D/X-7 | 鉴权归口 | 系统 | P1 | 权限 | 桥接为内部服务,无新增对外端点 | 1. 确认端点清单 | 本轮无新增 `bridge/*` 端点 → 权限维度「不适用」;D/X 既有端点鉴权由 TC-H-U01~U10 覆盖(线上 401 实测) | §3.4 | `#permissionFlow_notApplicable` + `e2e-live/health-contract.spec.js` |
+
+## 四、异常流用例(派生自正常流)
+
+| TC 编号 | 所属场景 | 模块 | 角色 | 优先级 | 类型 | 前置条件 | 测试步骤 | 预期结果 | 关联需求/设计 | 对应 e2e |
+|---|---|---|---|---|---|---|---|---|---|---|
+| TC-HB-03-E | D/X-2 | 映射码全非法 | 家长 | P2 | 异常 | `dimension_codes` 全非法 | 1. 调 `bridge` | 零写入但不抛异常,同步主链路不回滚已成功的 source 落库 | §3.1 | `#bridge_unknownDimensionCodes_dropped` |
+| TC-HB-08-E | D/X-2 | 归一化缺失 | 家长 | P2 | 异常 | `parsed_result` 无 `normalized` | 1. 调 `bridge` | 跳过该行(无得分不写),不产生 0 分脏行 | §3.1 | 未覆盖(缺口 G-115-1) |
+| TC-HB-09-E | D/X-4 | 并发同秒写入 | 家长 | P2 | 并发 | 同秒两条 source | 1. 并发桥接 | 读最新覆盖语义仍成立(created_at 相同时按既有序稳定返回),不出现双行读歧义 | §3.1 | 未覆盖(缺口 G-115-1) |
+
+## 五、覆盖缺口(本轮未覆盖,如实登记)
+
+| 缺口 | 规格条款 | 现状 | 建议补测 |
+|---|---|---|---|
+| G-115-1 | §5 E-1 的 E-2/E-3 | 桥接「同步 → `/api/dimension/overview` 读到新得分」(E-2)与「`/api/health/indicators/current` 可见 D/X 来源」(E-3)**无 MockMvc 级用例**;现有 16 例均为服务/反射/schema 级 | 补 2 个 MockMvc 契约:同步 mock → overview 新得分;indicators/current 含 source 字段 |
+| G-115-2 | §3.3 B-1/B-2(小程序设备数据端点) | 端点**未实现**,E-4 无对象可测 | 实现后补「登录 401 / 有 token 正常 / 分页 + source_type 过滤」3 例 |
+| G-115-3 | §3.3 B-3 + §5 E-5 | cfc-frontend 全仓仍 0 处 `huawei-health`/`xiaomi-health` 调用(2026-11-10 复验)→ 前端绑定/同步/状态卡链路零覆盖 | 前端落地后补 Playwright 3 条流(正常/异常/权限) |
+
+> 口径声明:本文件只登记「已实现且已有用例」的 16 条 + 3 条异常流设计;G-115-1~3 属未实现/未覆盖,**不得**据本文件宣称 E-2/E-3/E-4/E-5 已通过。