Преглед изворни кода

merge(test): 测试负责人第83轮产物合入 - O-14 test_memory 装置修复 + S-cfc-68/71 用例补登记 + 第83轮汇报/运行报告/notice/B-61第5轮复现

E2E Test Bot пре 4 дана
родитељ
комит
39add0f31

+ 20 - 4
cfc-langgraph/tests/test_memory.py

@@ -2,16 +2,32 @@ import pytest
 from app.memory.store import MemoryManager
 
 
+class FakeEmbeddings:
+    """最小 Embeddings 桩(O-14 终裁 2026-11-08 09:1x)。
+
+    MemoryManager.__init__(embeddings) 已将 embeddings 改为必传
+    (向量版记忆,langchain_chroma.Chroma 替换已废弃 langchain.memory)。
+    本桩实现 langchain Embeddings 兼容接口,返回固定 4 维向量,
+    仅用于单测构造 MemoryManager,不触发真实嵌入。
+    """
+
+    def embed_documents(self, texts):
+        return [[0.0, 0.1, 0.2, 0.3] for _ in texts]
+
+    def embed_query(self, text):
+        return [0.0, 0.1, 0.2, 0.3]
+
+
 @pytest.mark.asyncio
 async def test_memory_manager_init():
-    mgr = MemoryManager()
+    mgr = MemoryManager(embeddings=FakeEmbeddings())
     assert mgr is not None
     assert mgr.memory_vectorstore is not None
 
 
 @pytest.mark.asyncio
 async def test_working_memory():
-    mgr = MemoryManager()
+    mgr = MemoryManager(embeddings=FakeEmbeddings())
     mem = mgr.get_working_memory()
     assert mem is not None
     assert mem.memory_key == "history"
@@ -19,7 +35,7 @@ async def test_working_memory():
 
 @pytest.mark.asyncio
 async def test_longterm_memory():
-    mgr = MemoryManager()
+    mgr = MemoryManager(embeddings=FakeEmbeddings())
     mem = mgr.get_longterm_memory(user_id=1)
     assert mem is not None
-    assert mem.memory_key == "long_term_memory"
+    assert mem.memory_key == "long_term_memory"

+ 134 - 0
docs/tdd/orchestration/report-test-2026-11-08-1030.md

@@ -0,0 +1,134 @@
+# 测试负责人工作汇报(第 83 轮 · 2026-11-08 10:30)
+
+> 来源:测试负责人闭环巡检(`review` 离线档,内网已授权 cfc.iwintrue.com 只读 E2E + 三节点派发回归)
+> 基线:`origin/cfclub` @ `0fde81bec`(本轮 worktree `test/r83` 基准,主工作树 0 已追踪改动/62 untracked 历史产物)
+> 详细报告:`docs/tdd/test/automation-run-2026-11-08-1030.md`
+
+---
+
+## 基本信息
+
+| 项目 | 内容 |
+|------|------|
+| 汇报人 | 陆谨(软件测试专家团主理人 · 测试负责人) |
+| 汇报周期 | 2026-11-08 09:30 ~ 10:30(第 83 轮) |
+| 负责模块/单元 | cfc-backend / cfc-frontend / cfc-web / cfc-langgraph / 内网部署 E2E(admin·frontend·langgraph·health) |
+
+---
+
+## 一、本轮工作数据汇总
+
+### 1.1 测试用例建设
+
+| 指标 | 数量 | 说明 |
+|------|------|------|
+| 本轮新增测试用例数* | 4 条 | TC-A-27(team.admin.manage 分润配置)/ TC-A-27-E / TC-A-27-P / TC-A-28(team.star.myView 星级看板)——S-cfc-68/71 新能力键已合入 cfclub(17b6d6bfb/2b73e20f6),原无测试用例,本轮按「SRS/场景变更须更新测试用例」补登记;另 O-14 终裁执行重写 langgraph test_memory 3 例装置(非新用例,见附录 A) |
+| 本轮验证执行测试数* | 67 例 | admin 5(189 节点)+ frontend 6(204 节点)+ health 契约 13(本机直跑)+ langgraph pytest 43(251 节点) |
+| 用例通过率 | 47/67 = 70.1% | 20 例红 = B-61 健康方案 3(连续第 5 轮复现)+ langgraph 17(C 类环境 14 + A 类装置 3,持平 R77/R78) |
+
+> 注:后端全量与 Jest 未重跑(mvn/Jest 环境时序成本高,承袭 R82:后端复用 R77 基线 95.07%,Jest 复用 R82 342+90 双绿,标未实测);详见 automation-run 报告 §1.4。
+
+### 1.2 会话与任务分发
+
+| 指标 | 数量 | 说明 |
+|------|------|------|
+| 分派任务数* | 3 个 | 189-admin(浏览器 E2E)/ 204-frontend(浏览器 E2E)/ 251-langgraph(pytest)——**首次三节点全部派发成功并产出**(每节点 1 个任务,符合节点约束) |
+| 恢复智能体会话数* | 0 个 | 无中断会话;189 首轮 prompt 404 经重试恢复为 200(瞬态) |
+| 当前在用会话数 | 0 个 | 三节点会话均 done;本机自动化会话已结束 |
+
+### 1.2b 各会话当前工作状态列表
+
+| 会话标签 | 会话 ID | 当前状态 | 当前任务 | 静默/空闲时长 | 工作目录 | 备注 |
+|----------|---------|----------|----------|---------------|----------|------|
+| 189-admin | ses_e47187681ffefMr2XVWQlHYTjg / ses_e47192d05ffeg1YwwPkJMTQGzc | done | admin-desktop live E2E(TC-A-U01~05) | — | 189 节点 C:\code\cfc | 5/5 全绿(登录+滑块+核心菜单,B-46 修复后连续 3 轮稳定) |
+| 204-frontend | ses_e4719259dffedyF7eA3gBJpTCY / ses_e47187016ffegelpgpcxDnQLUK | done | frontend-mobile live E2E(TC-F-U01~06) | — | 204 节点 d:\cfc | 6/6 全绿(游客态只读浏览链路) |
+| 251-langgraph | ses_e47186976ffekD3cG2Y0milovT / ses_e4719214fffeWMMdMt6sFakC8M | done | cfc-langgraph pytest 回归(43 例) | — | 251 节点 /sc-data/cfc | 26 过 17 红(C 类 14 + A 类装置 3),与 R77/R78 持平 |
+
+> 注:双会话为派发脚本首启(`&` 后台未存活)+ 受管后台重试各创建一次,两会话均执行并产出一致结果;重复会话无数据污染(只读任务)。
+
+### 1.3 问题处置
+
+| 指标 | 数量 | 说明 |
+|------|------|------|
+| 新测试发现问题数* | 0 个 | 本轮无新缺陷登记(B-61 为既有 P0 第 5 轮复现更新,见 blockers B-61-Update-83 与附录 C) |
+| 已解决测试相关问题数* | 1 个 | **B-56(远程派发通道受限)解除**:本轮 189/204/251 三节点 opencode 通道全部恢复可用并成功产出(此前连续多轮 agent 零产出/B-56 阻塞),测试侧自处置验证闭环,属本轮最大改善 |
+| 待解决测试相关问题数 | 7 项 | B-61(P0 连续第 5 轮)/B-54/B-59/B-60/B-62 及历史项;langgraph 环境 14 红(checkpointer 8 + 401 意图 4 + report_parse 2);O-14 装置 3 例已修复待 251 拉取复验 |
+
+### 1.4 Git 证据(已推送远端)
+
+| 提交 sha | 关联任务/能力键 | 阶段 | 触及文件类型 | 目标分支 | 已推送远端 | 备注 |
+|----------|-----------------|------|--------------|----------|------------|------|
+| 2537b4377 | O-14 / langgraph test_memory | 测试侧装置更新 | 测试(cfc-langgraph/tests/test_memory.py) | test/r83 | ✓ | O-14 终裁执行:3 例改传 `MemoryManager(embeddings=FakeEmbeddings())` 桩 |
+| cbfab7628 | S-cfc-68 / S-cfc-71 | 测试用例补登记 | 测试(tests/test-cases/admin.md) | test/r83 | 本轮合入后 ✓ | TC-A-27/27-E/27-P/28 四条例 |
+| 本报告+automation-run+notice+blockers | — | 报告/台账 | docs/tdd/* | cfclub | 本轮合入后 ✓ | 见 §1.4 交付记录 |
+
+### 1.5 状态机阶段分布
+
+| 阶段 | 键数 | 键 |
+|------|------|-----|
+| GREEN | 5 | admin.auth.login / dan.assessment.getActiveMaterial / guide.trainingPlan.getPlanDetail / health.report.createReport / health.report.upload(与 R82 一致,本地 .tdd/state.json) |
+| RED/YELLOW/UNTRACKED | 0 | 目标池 0 键 |
+
+### 1.6 数据源健康度
+
+| 数据源 | 读取键数 | 是否正常 | 备注 |
+|--------|----------|----------|------|
+| 需求侧(⏳ wish) | 0 | 正常 | 无新未实现键(S-cfc-68/71 已实现回收,不回池) |
+| 契约常量(contract) | 0 | 正常 | 标注用,不入池门槛 |
+| 测试用例引用(test_case_map) | 0 | 正常 | — |
+| 执行结果回填(case_results) | 0 | 正常 | — |
+
+---
+
+## 二、关键结论
+
+**门禁:⛔ 不通过(有条件)**——P0 B-61(健康方案鉴权)连续第 5 轮源码级未修;langgraph 17 红持续(环境 14 + 装置 3);后端全量/Jest 复用基线未实测;JaCoCo 覆盖率 N/A。
+
+**本轮实质改善**:① **B-56 解除**——三节点 opencode 派发通道全面恢复(189/204/251 均成功产出,首次实现「每节点 1 任务」的完整节点化回归);② admin 5/5、frontend 6/6、华为/小米健康契约 10/10 全绿;③ **O-14 执行落地**——test_memory 3 例 FakeEmbeddings 桩已重写推送(转绿待 251 拉取复验);④ **S-cfc-68/71 测试用例补登记**(TC-A-27/28 族),闭环「新能力键无用例」缺口。
+
+**最大杠杆(使绿路径)**:① 研发修 P0 B-61(`JwtInterceptor.LOGIN_REQUIRED_PATHS` 加 `/api/health/plan/` + 部署)→ 3 例转绿;② 251 节点拉取 cfclub(含 O-14 2537b4377 合入)后重跑 langgraph → test_memory 3 例装置红归零;③ 251 langgraph 运行环境修复(checkpointer 初始化 + API key 注入)→ 环境 14 红归零;④ 运维恢复 langgraph 9000 服务(curl 000,仍未恢复)。
+
+## 三、改进建议 / 下一步事项
+
+| 序号 | 事项 | 依赖/前置条件 | 预估 | 优先级 | 状态 |
+|------|------|---------------|------|--------|------|
+| 1 | 研发修 B-61 并部署(`LOGIN_REQUIRED_PATHS` + `/api/health/plan/`,`JwtInterceptor.java:87` 处) | 无 | 1 提交 | P0 | 待处理(连续第 5 轮) |
+| 2 | 251 拉取 cfclub 最新(含 O-14 test_memory 合入)并重跑 langgraph | 运维/251 侧 git pull | — | P1 | 待执行 |
+| 3 | 251 langgraph checkpointer/API key 环境修复(8+4 红) | 运维/研发 | — | P1 | 待处理 |
+| 4 | S-cfc-68/71 线上 E2E 复验(TC-A-27/28 落地可执行规格) | 测试侧下轮 | 1 提交 | P1 | 待启动 |
+| 5 | 运维恢复 251 langgraph 9000 服务 | 运维 | — | P2 | 待处理 |
+
+## 四、本轮阻塞与风险
+
+| 阻塞项 | 影响范围 | 阻塞类型 | 处置进展 | 需要协调 |
+|--------|----------|----------|----------|----------|
+| B-61 P0 健康方案鉴权(第 5 轮) | TC-HP-U01~03 + 匿名越权风险 | 研发 | 源码级未修(最新 HEAD `0fde81bec` 无 `/api/health/plan/`);65 轮派发 lan-204 在途未产出 | 研发负责人催办 |
+| langgraph 17 红(环境 14 + 装置 3) | lg 回归 | 测试+环境 | O-14 装置 3 例已修待 251 拉取;checkpointer/API key 属 251 环境 | 运维(9000 服务)+ 研发(checkpointer) |
+| 251 langgraph 9000 服务 closed | 线上 AI 服务 | 环境 | curl 000 持续 | 运维 |
+| B-60 后端全量 40min+ | CI 门禁落地 | 测试 | 5 慢类清单已登记,待下轮处置 | 测试侧 |
+| 主工作树 untracked 62 项 | 工作目录门禁 | 测试 | 历史测试产物(`_*`/`_artifacts_live/` 等),按纪律不代清理 | 主理人知悉 |
+
+## 附录 A:本轮新增/更新测试用例(按能力键)
+
+| 用例 ID | 能力键 | 目标单元 | 用例标题 | 登记 sha |
+|---------|--------|----------|----------|----------|
+| TC-A-27 | team.admin.manage | TeamProfitAdminController | 团队分润配置/成本价/流水查询/提现审核(正常流) | cbfab7628 |
+| TC-A-27-E | team.admin.manage | TeamProfitAdminController | 比例非法/并发审核/流水缺失(异常流) | cbfab7628 |
+| TC-A-27-P | team.admin.manage | TeamProfitAdminController | 非授权 403 / 匿名 401(权限流) | cbfab7628 |
+| TC-A-28 | team.star.myView | TeamStarController/StarScoreCalculator | 我的星级看板三维构成合计=totalScore 口径 | cbfab7628 |
+| test_memory × 3 | O-14(langgraph) | app.memory.store.MemoryManager | 改传 `embeddings=FakeEmbeddings()` 桩(装置更新) | 2537b4377 |
+
+## 附录 B:任务分派明细
+
+| 任务号 | 目标会话标签 | 分派时间 | 分派类型 | 当前状态 |
+|--------|--------------|----------|----------|----------|
+| 189-admin E2E | web-189(192.168.16.189:4090) | 09:58 | 新建 | done(5/5) |
+| 204-frontend E2E | web-204(192.168.16.204:4091) | 09:58 | 新建 | done(6/6) |
+| 251-langgraph pytest | junit(192.168.16.251:4096) | 09:58 | 新建 | done(26/43) |
+
+## 附录 C:新发现问题明细
+
+| 问题 ID | 能力键/任务号 | 问题类型 | 路由去向 | 登记时间 | 处置状态 |
+|---------|---------------|----------|----------|----------|----------|
+| B-61-Update-83 | /api/health/plan/* | 缺陷(鉴权缺口,P0 第 5 轮复现) | 研发 | 2026-11-08 10:2x | 待处理(第 5 轮复现登记,blockers.md) |
+| B-56 解除 | 远程派发通道 | 测试自处置 | 测试 | 2026-11-08 10:2x | 已解决(三节点通道恢复验证) |

+ 79 - 0
docs/tdd/test/automation-run-2026-11-08-1030.md

@@ -0,0 +1,79 @@
+# 自动化测试运行报告(第 83 轮 · 2026-11-08 10:30)
+
+> 基线:`origin/cfclub` @ `0fde81bec`(HEAD 本地已同步)
+> 执行方式:三节点派发(189-admin / 204-frontend / 251-langgraph)+ 本机直跑(health 契约)
+> 约束:每节点仅 1 个浏览器自动化任务;langgraph 测试分派 251 节点(按用户指令)
+
+---
+
+## 1. 执行矩阵
+
+| 套件 | 执行位置 | 方式 | 用例数 | PASS | FAIL | SKIP | 通过率 |
+|------|----------|------|--------|------|------|------|--------|
+| admin-desktop live E2E(`playwright.config.live.js` admin-desktop) | 192.168.16.189:4090 | 节点派发 | 5 | 5 | 0 | 0 | 100% |
+| frontend-mobile live E2E(`playwright.config.live.js` frontend-mobile) | 192.168.16.204:4091 | 节点派发 | 6 | 6 | 0 | 0 | 100% |
+| health 合约契约 E2E(`playwright.config.health.js`) | 本机直跑 | 本机 | 13 | 10 | 3 | 0 | 76.9% |
+| cfc-langgraph pytest | 192.168.16.251:4096 | 节点派发 | 43 | 26 | 17 | 0 | 60.5% |
+| **合计** | — | — | **67** | **47** | **20** | **0** | **70.1%** |
+
+> 未实测(承袭):后端全量 mvn(复用 R77 基线 95.07%/231 类 1847 例)、frontend/web Jest(复用 R82 342+90 双绿)、Playwright 非 live 套件(联网未授权)、JaCoCo/PIT/性能/SAST(无工具)。
+
+## 2. admin-desktop(189 节点)—— 5/5 全绿
+
+| 用例 | 标题 | 状态 | 耗时 |
+|------|------|------|------|
+| TC-A-U01 | 登录页可渲染(标题/账号/密码/登录按钮) | PASS | 3.3s |
+| TC-A-U02 | 滑块人机验证存在且可拖动通过 | PASS | 4.5s |
+| TC-A-U03 | 未登录访问后台受保护路由被拦截到登录页 | PASS | 4.4s |
+| TC-A-U04 | 管理员真实登录(滑块 + 凭据)进入后台主框架 | PASS | 10.8s |
+| TC-A-U05 | 核心菜单渲染/跳转 | PASS | — |
+
+> B-46 修复后连续 3 轮稳定(R81/R82/R83)。会话:ses_e47187681ffefMr2XVWQlHYTjg / ses_e47192d05ffeg1YwwPkJMTQGzc(两会话一致)。
+
+## 3. frontend-mobile(204 节点)—— 6/6 全绿
+
+| 用例 | 标题 | 状态 | 耗时 |
+|------|------|------|------|
+| TC-F-U01 | 首页可渲染(标题/落地路由/TabBar 四项) | PASS | 7.8s |
+| TC-F-U02 | 首页核心内容渲染(五维沙盘/三步引导) | PASS | 6.8s |
+| TC-F-U03 | TabBar 切换到「健康」正常跳转且无异常 | PASS | 13.2s |
+| TC-F-U04 | TabBar 切换到「成长」正常跳转 | PASS | 12.7s |
+| TC-F-U05 | 未登录访问「我的」出现登录/注册入口(鉴权引导) | PASS | 8.7s |
+| TC-F-U06 | 首屏关键资源加载无失败(无 requestfailed) | PASS | 6.2s |
+
+> 会话:ses_e4719259dffedyF7eA3gBJpTCY(完整 JSON 输出)。游客态只读浏览,零数据写入。
+
+## 4. health 合约契约(本机直跑)—— 10 过 3 红
+
+- **TC-H-U01~U10(华为/小米 10 端点)10/10 全绿**:未鉴权均 401/403 正确拦截 → B-50 维持已解除(部署含 `cc7b3e00b` 稳定生效)。
+- **TC-HP-U01/02/03(`/api/health/plan/{adjust,attribution,generate}`)3 例 FAIL**:匿名 POST 仍返回 **HTTP 200** 直通(应 401/403)。
+  - 源码复核:`JwtInterceptor.java:87 LOGIN_REQUIRED_PATHS`(最新 HEAD `0fde81bec`)**仍无 `/api/health/plan/`** → **源码级未修**,非部署滞后。
+  - → **B-61 P0 连续第 5 轮复现**(R78 首现、R79~R82 第 2~4 轮、R83 第 5 轮)。blockers.md 增补 B-61-Update-83。
+
+## 5. cfc-langgraph pytest(251 节点)—— 26 过 17 红(与 R77/R78 持平)
+
+| 失败分类 | 数量 | 用例 | 错误摘要 |
+|----------|------|------|----------|
+| C 类 · checkpointer 未初始化 | 8 | test_analyze×2、test_auto_tag×5、test_chat×1 | `Checkpointer requires configurable keys: thread_id` / chat 端点 500 |
+| C 类 · LLM API key 401 | 4 | test_intent×4 | `API key 无效(401)`,意图分类降级 default chat |
+| C 类 · 解析 400 | 2 | test_report_parse×2 | `assert 400 == 200` |
+| A 类 · 装置滞后(O-14) | 3 | test_memory×3 | `TypeError: MemoryManager.__init__() missing 'embeddings'` |
+
+> A 类 3 例即 O-14 终裁对象:测试侧已重写为 `MemoryManager(embeddings=FakeEmbeddings())`(2537b4377 推送 test/r83),251 节点 `/sc-data/cfc` 尚未拉取该修改,**本轮红属预期旧代码行为**;251 拉取后应转绿。
+> 251 langgraph 9000 FastAPI 服务本轮仍 closed(`curl 000`),pytest 为代码级直跑不受影响。
+
+## 6. 节点通道恢复(B-56 解除依据)
+
+| 节点 | 端口 | 会话创建 | prompt 下发 | assistant 产出 | 判定 |
+|------|------|----------|-------------|----------------|------|
+| 192.168.16.189 | 4090 | 200 | 200(首轮 404 瞬态,重试恢复) | ✓ 完整 JSON | 通道可用 |
+| 192.168.16.204 | 4091 | 200 | 200 | ✓ 完整 JSON | 通道可用 |
+| 192.168.16.251 | 4096 | 200 | 200 | ✓ 完整 JSON | 通道可用 |
+
+> 对比 R78:189/204 曾 agent 零产出(B-56)、251 9000/4090 closed → 本轮三节点全部恢复。B-56 确认解除。
+
+## 7. 结论与下一轮前置
+
+- 门禁:**不通过(有条件)**;红例 20 = B-61 3 + langgraph 17(C 类 14 + A 类装置 3,O-14 已修待拉取)。
+- 使绿顺序:① 研发修 B-61 + 部署 → HP-U01~03 转绿 ② 251 拉 cfclub 含 O-14 → test_memory 3 例归零 ③ 251 环境(checkpointer + API key)→ 环境 14 例归零 ④ 运维恢复 9000。
+- 下轮:S-cfc-68/71 线上 E2E 复验(TC-A-27/28 落地)+ Jest/后端回归 + B-61 复测。

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

@@ -76,4 +76,5 @@
 | B-61-Update(第 81 轮复现) | P0 健康方案鉴权缺口连续第 3 轮复现 | 研发 | **连续第 3 轮复现(2026-11-08 06:26)**。本轮 `playwright.config.health.js` 契约实测:TC-H-U01~U10 **10/10 全绿**(华/小米健康鉴权稳定拦截),但 TC-HP-U01/02/03(`/api/health/plan/{adjust,attribution,generate}` 匿名 POST)仍返回 **HTTP 200** 直通(应 401/403/404)。`JwtInterceptor.LOGIN_REQUIRED_PATHS` 修复未随任何部署轮次生效。与 B-61 主登记同因:opt-in 清单漏配 `/api/health/plan/`。闭环判据不变:修复+部署后 TC-HP-U01~03 转 401 即通过 | 测试负责人(第 81 轮复现登记) | 2026-11-08 06:26 | 待处理(P0,连续第 3 轮) |
 | B-62-Update(第 81 轮闭环确认) | S-cfc-35/S-cfc-38 birthday/gender 捕获 14 例 RED 全部转绿 | 研发(已实现) | **闭环确认(第 81 轮,2026-11-08 06:20)**。研发 04:53/04:56 提交实现(`4f40a8973` S-cfc-67 审批卡片出生日期/性别 + `2c65d98f7` S-cfc-66 加入申请表单必填项)后,本轮 frontend Jest **19 套件 / 343 例 / 342 通过 + 1 skip / 0 失败**:`join-form.required-info.test.js`(12 例)与 `family-members.joinRequestCardInfo.test.js`(2 例)**14 RED 全部转绿**,B-62 场景闭环。无通过用例转红(R79 基线 328 通过含净新增)。后续哨兵:frontend Jest 每轮 343 例全绿即维持闭环保守 | 测试负责人(第 81 轮确认) | 2026-11-08 06:28 | **已解除(14 RED 转绿,frontend 连绿)** |
 | B-61-Update(第 82 轮复现) | P0 健康方案鉴权缺口连续第 4 轮复现 | 研发 | **连续第 4 轮复现(2026-11-08 07:30)**。本轮 `playwright.config.health.js` 契约实测:TC-H-U01~U10 **10/10 全绿**(华/小米健康鉴权稳定拦截),但 TC-HP-U01/02/03(`/api/health/plan/{adjust,attribution,generate}` 匿名 POST)仍返回 **HTTP 200** 直通(应 401/403/404)。源码复核:`JwtInterceptor.java:87 LOGIN_REQUIRED_PATHS`(R82 HEAD b037fe71b)仍无 `/api/health/plan/` → 非部署滞后,**源代码级未修**。与 B-61 主登记同因:opt-in 清单漏配。闭环判据不变:修代码+部署后 TC-HP-U01~03 转 401 即通过。此为本轮最高优先级催办项(研发交接通知同步送达) | 测试负责人(第 82 轮复现登记) | 2026-11-08 07:32 | 待处理(P0,连续第 4 轮) |
+| B-61-Update(第 83 轮复现) | P0 健康方案鉴权缺口连续第 5 轮复现 | 研发 | **连续第 5 轮复现(2026-11-08 10:2x)**。本轮本机 `playwright.config.health.js` 契约实测:TC-H-U01~U10 **10/10 全绿**,TC-HP-U01/02/03(`/api/health/plan/{adjust,attribution,generate}` 匿名 POST)仍返回 **HTTP 200** 直通(应 401/403)。源码复核:`JwtInterceptor.java:87 LOGIN_REQUIRED_PATHS`(**最新 HEAD 0fde81bec**)仍无 `/api/health/plan/` → **源代码级未修**,非部署滞后。65 轮派发 lan-204 会话(ses_e477a6816ffe,D:\cfc)在途未产出,请研发侧重核会话状态或重派。闭环判据不变:修代码+部署后 TC-HP-U01~03 转 401/403 即通过(notice-83round 已送达) | 测试负责人(第 83 轮复现登记) | 2026-11-08 10:28 | 待处理(P0,连续第 5 轮) |
 | B-59-Update(第 82 轮观察) | admin U04/U05 首轮抖动复跑转绿;B-59 时序修复仍未落地 | 测试(自处置) | **第 82 轮(2026-11-08 07:18~07:35)**。admin-desktop 首轮 3/5(U04「身份自动带出」失败 + U05 依赖 U04 失败),独立复跑 5/5 全绿;shop 首轮 TC-S-U01 一次 502(上游瞬态,复跑 4/4 + curl 200×2)。首轮与 3 个 jest/lg 套件并行跑,机器负载高 → 佐证 B-59「固定 waitForTimeout(5000) 时序脆弱点」判断。修复(`waitForURL` 替换)仍未落地,**列入测试侧下轮自处置** | 测试负责人(第 82 轮实测登记) | 2026-11-08 07:36 | 待处理(下轮自处置) |

+ 36 - 0
docs/tdd/test/notice-2026-11-08-1030-83round.md

@@ -0,0 +1,36 @@
+# 研发交接通知(第 83 轮 · 2026-11-08 10:30)
+
+> 来源:测试负责人第 83 轮闭环巡检(`docs/tdd/test/automation-run-2026-11-08-1030.md`)
+> 通知对象:研发负责人(cfc-dev)
+> 测试结果有变化 → 本轮已提交推送至远程,按纪律通知研发。
+
+---
+
+## 一、本轮结论摘要(变化点)
+
+| 项 | 第 82 轮(07:47) | 第 83 轮(10:30) | 变化 |
+|----|------------------|-------------------|------|
+| admin E2E | 5/5 | 5/5 | 持平(稳定) |
+| frontend E2E | 6/6 | 6/6 | 持平(稳定) |
+| health 华为/小米 | 10/10 | 10/10 | 持平(B-50 维持解除) |
+| health 方案鉴权(B-61) | 3 红(第 4 轮) | 3 红(**第 5 轮**) | ⚠️ 连续复现升级 |
+| langgraph pytest | 26/43 | 26/43 | 持平(C 类 14 + A 类装置 3) |
+| 节点派发通道 | 受限(B-56) | **三节点全部恢复** | ✅ 重大改善 |
+| O-14 test_memory 装置 | 待终裁 | **已重写推送(2537b4377)** | ✅ 测试侧完成 |
+
+## 二、研发需处理(按优先级)
+
+1. **【P0 · 连续第 5 轮】B-61 健康方案鉴权**:`JwtInterceptor.LOGIN_REQUIRED_PATHS`(`JwtInterceptor.java:87`)增加 `"/api/health/plan/"`,与 `/api/huawei-health/`、`/api/xiaomi-health/` 同族;修复后部署(测试目标实例需含该提交)。闭环判据:TC-HP-U01/02/03 匿名 POST 由 200 转 401/403。65 轮派发 lan-204 会话在途未产出,建议优先核实其会话状态或重派。
+2. **【P1】251 节点 langgraph**:拉取 cfclub 最新(含 O-14 test_memory 合入 `2537b4377`)后重跑 pytest → test_memory 3 例装置红应归零;并检查 checkpointer 初始化(8 例)与 LLM API key 注入(4 例)环境问题。
+3. **【P1】S-cfc-68/71 测试复验**:已补登记 TC-A-27/28 用例(`tests/test-cases/admin.md`),待测试侧线上 E2E 复验(下轮);研发侧契约单测 `TeamStarMyViewContractTest` 24/24 绿已确认。
+4. **【P2】251 langgraph 9000 服务**:`curl http://192.168.16.251:9000/health` 仍 000(closed),请运维排期恢复。
+
+## 三、本轮测试侧产出(已推送)
+
+| 提交 sha | 内容 | 分支 |
+|----------|------|------|
+| 2537b4377 | O-14:test_memory 3 例 FakeEmbeddings 桩 | test/r83(本轮合入 cfclub) |
+| cbfab7628 | S-cfc-68/71 用例补登记(TC-A-27/27-E/27-P/28) | test/r83(本轮合入 cfclub) |
+| (本轮报告) | report-test-1030 + automation-run-1030 + notice-83round + blockers(B-61-Update-83) | cfclub |
+
+> 注:测试侧产物经 worktree `test/r83` 合入主分支 cfclub 后推送,请研发以 cfclub 最新为准。

+ 8 - 0
tests/.live-artifacts/health/.last-run.json

@@ -0,0 +1,8 @@
+{
+  "status": "failed",
+  "failedTests": [
+    "9ca3425f344ba4388632-2b45645a177d3468e055",
+    "9ca3425f344ba4388632-23db2565ec96e5b930b5",
+    "9ca3425f344ba4388632-b05a5b224955026c58b6"
+  ]
+}

+ 115 - 0
tests/.live-artifacts/health/health-contract-健康方案-·-鉴权契-4f7ba-01-403-·-HL-04-R4-健康方案对话式调整-health-api/error-context.md

@@ -0,0 +1,115 @@
+# Instructions
+
+- Following Playwright test failed.
+- Explain why, be concise, respect Playwright best practices.
+- Provide a snippet of code with the fix, if possible.
+
+# Test info
+
+- Name: health-contract.spec.js >> 健康方案 · 鉴权契约(§4.49.1/§4.49.2) >> TC-HP-U01 POST /api/health/plan/adjust 未鉴权应被拒绝(401/403) · HL-04/R4 健康方案对话式调整
+- Location: ..\..\_wt_r83\tests\e2e-live\health-contract.spec.js:74:5
+
+# Error details
+
+```
+Error: 未鉴权应被拒绝(401/403)/或端点未部署(404),实际 200
+
+expect(received).toContain(expected) // indexOf
+
+Expected value: 200
+Received array: [401, 403, 404]
+```
+
+# Test source
+
+```ts
+  1  | /**
+  2  |  * ================================================================
+  3  |  * 华为 / 小米运动健康接入 · 接口级鉴权契约 E2E · TC-H-U01 ~ TC-H-U10
+  4  |  * ================================================================
+  5  |  * 需求来源:docs/superpowers/api/API_REFERENCE.md
+  6  |  *   §4.43 华为运动健康接入(HuaweiHealthController  /api/huawei-health)
+  7  |  *   §4.44 小米运动健康云服务接口接入(XiaomiHealthController /api/xiaomi-health)
+  8  |  *   设计文档:docs/2026-11-06-华为运动健康开放平台接入方案.md(D1-D5 / X1-X5)
+  9  |  *
+  10 |  * 契约要点(来自 API_REFERENCE):
+  11 |  *   - 全部 @PostMapping,需 JWT(JwtInterceptor 拦截 /api/**,仅 8 个公开路径除外)。
+  12 |  *   - memberId 为空时取当前登录成员;令牌仅后端持有,不落小程序。
+  13 |  *
+  14 |  * 本契约验证「未携带 Authorization 时,10 个端点应被拒绝(401/403),不可 200 直通」。
+  15 |  * 只读:仅发送空 body 探测鉴权门禁,不写任何业务数据;凭据缺失属预期(验证拦截),
+  16 |  *      禁止在日志/报告中打印明文口令或令牌。
+  17 |  *
+  18 |  * 运行(隔离配置,避免干扰现有 15/15 套件):
+  19 |  *   npx playwright test -c playwright.config.health.js
+  20 |  */
+  21 | const { test, expect } = require('@playwright/test');
+  22 | 
+  23 | const API = process.env.LIVE_API_BASE || 'http://cfc.iwintrue.com';
+  24 | 
+  25 | // [用例编号, 品牌, 动作]
+  26 | const ENDPOINTS = [
+  27 |   ['TC-H-U01', 'huawei', 'auth-url'],
+  28 |   ['TC-H-U02', 'huawei', 'callback'],
+  29 |   ['TC-H-U03', 'huawei', 'sync'],
+  30 |   ['TC-H-U04', 'huawei', 'revoke'],
+  31 |   ['TC-H-U05', 'huawei', 'status'],
+  32 |   ['TC-H-U06', 'xiaomi', 'auth-url'],
+  33 |   ['TC-H-U07', 'xiaomi', 'callback'],
+  34 |   ['TC-H-U08', 'xiaomi', 'sync'],
+  35 |   ['TC-H-U09', 'xiaomi', 'revoke'],
+  36 |   ['TC-H-U10', 'xiaomi', 'status'],
+  37 | ];
+  38 | 
+  39 | test.describe('华为/小米运动健康接入 · 鉴权契约(§4.43/§4.44)', () => {
+  40 |   for (const [tc, brand, action] of ENDPOINTS) {
+  41 |     test(`${tc} POST /api/${brand}-health/${action} 未鉴权应被拒绝(401/403)`, async ({ request }) => {
+  42 |       const resp = await request.post(`${API}/api/${brand}-health/${action}`, {
+  43 |         data: {},
+  44 |         timeout: 20000,
+  45 |       });
+  46 |       const status = resp.status();
+  47 |       // 仅打印状态码,绝不打印响应体(可能含敏感字段)或任何凭据
+  48 |       console.log(`[${tc}] POST /api/${brand}-health/${action} -> HTTP ${status}`);
+  49 |       // 契约:需 JWT;未鉴权必须被拦截,绝不 200 直通。
+  50 |       // 当前若返回 404 = 该模块尚未在目标部署实例上线(已实测),属部署缺口,非断言错误。
+  51 |       expect([401, 403], `未鉴权应被拒绝(401/403),实际 ${status}`).toContain(status);
+  52 |     });
+  53 |   }
+  54 | });
+  55 | 
+  56 | // ================================================================
+  57 | // 健康方案 · 接口级鉴权契约 E2E · TC-HP-U01 ~ TC-HP-U03
+  58 | // 需求来源:docs/superpowers/api/API_REFERENCE.md(本次闭环检测到变更)
+  59 | //   §4.49.1 健康方案对话式调整(POST /api/health/plan/adjust,HL-04/R4)
+  60 | //   §4.49.2 健康方案归因 R2(POST /api/health/plan/attribution,HL-03/R2)
+  61 | //           与健康方案生成不确定点补充 R3(POST /api/health/plan/generate 新增 followUpAnswers)
+  62 | // 契约要点:全部 @PostMapping,需 JWT;未鉴权应被拒绝(401/403),不可 200 直通。
+  63 | // 只读:仅发送空 body 探测鉴权门禁,不写任何业务数据;凭据缺失属预期(验证拦截)。
+  64 | // 运行:npx playwright test -c playwright.config.health.js
+  65 | // ================================================================
+  66 | const HP_ENDPOINTS = [
+  67 |   ['TC-HP-U01', 'adjust', 'HL-04/R4 健康方案对话式调整'],
+  68 |   ['TC-HP-U02', 'attribution', 'HL-03/R2 健康方案归因'],
+  69 |   ['TC-HP-U03', 'generate', 'HL-03/R3 生成不确定点补充(followUpAnswers)'],
+  70 | ];
+  71 | 
+  72 | test.describe('健康方案 · 鉴权契约(§4.49.1/§4.49.2)', () => {
+  73 |   for (const [tc, action, req] of HP_ENDPOINTS) {
+  74 |     test(`${tc} POST /api/health/plan/${action} 未鉴权应被拒绝(401/403) · ${req}`, async ({ request }) => {
+  75 |       const resp = await request.post(`${API}/api/health/plan/${action}`, {
+  76 |         data: {},
+  77 |         timeout: 20000,
+  78 |       });
+  79 |       const status = resp.status();
+  80 |       // 仅打印状态码,绝不打印响应体或任何凭据
+  81 |       console.log(`[${tc}] POST /api/health/plan/${action} -> HTTP ${status}`);
+  82 |       // 契约:需 JWT;未鉴权必须被拦截,绝不 200 直通。
+  83 |       // 若返回 404 = 该端点尚未在目标部署实例上线,属部署缺口,非断言错误。
+> 84 |       expect([401, 403, 404], `未鉴权应被拒绝(401/403)/或端点未部署(404),实际 ${status}`).toContain(status);
+     |                                                                            ^ Error: 未鉴权应被拒绝(401/403)/或端点未部署(404),实际 200
+  85 |     });
+  86 |   }
+  87 | });
+  88 | 
+```

+ 115 - 0
tests/.live-artifacts/health/health-contract-健康方案-·-鉴权契-56a99-绝-401-403-·-HL-03-R2-健康方案归因-health-api/error-context.md

@@ -0,0 +1,115 @@
+# Instructions
+
+- Following Playwright test failed.
+- Explain why, be concise, respect Playwright best practices.
+- Provide a snippet of code with the fix, if possible.
+
+# Test info
+
+- Name: health-contract.spec.js >> 健康方案 · 鉴权契约(§4.49.1/§4.49.2) >> TC-HP-U02 POST /api/health/plan/attribution 未鉴权应被拒绝(401/403) · HL-03/R2 健康方案归因
+- Location: ..\..\_wt_r83\tests\e2e-live\health-contract.spec.js:74:5
+
+# Error details
+
+```
+Error: 未鉴权应被拒绝(401/403)/或端点未部署(404),实际 200
+
+expect(received).toContain(expected) // indexOf
+
+Expected value: 200
+Received array: [401, 403, 404]
+```
+
+# Test source
+
+```ts
+  1  | /**
+  2  |  * ================================================================
+  3  |  * 华为 / 小米运动健康接入 · 接口级鉴权契约 E2E · TC-H-U01 ~ TC-H-U10
+  4  |  * ================================================================
+  5  |  * 需求来源:docs/superpowers/api/API_REFERENCE.md
+  6  |  *   §4.43 华为运动健康接入(HuaweiHealthController  /api/huawei-health)
+  7  |  *   §4.44 小米运动健康云服务接口接入(XiaomiHealthController /api/xiaomi-health)
+  8  |  *   设计文档:docs/2026-11-06-华为运动健康开放平台接入方案.md(D1-D5 / X1-X5)
+  9  |  *
+  10 |  * 契约要点(来自 API_REFERENCE):
+  11 |  *   - 全部 @PostMapping,需 JWT(JwtInterceptor 拦截 /api/**,仅 8 个公开路径除外)。
+  12 |  *   - memberId 为空时取当前登录成员;令牌仅后端持有,不落小程序。
+  13 |  *
+  14 |  * 本契约验证「未携带 Authorization 时,10 个端点应被拒绝(401/403),不可 200 直通」。
+  15 |  * 只读:仅发送空 body 探测鉴权门禁,不写任何业务数据;凭据缺失属预期(验证拦截),
+  16 |  *      禁止在日志/报告中打印明文口令或令牌。
+  17 |  *
+  18 |  * 运行(隔离配置,避免干扰现有 15/15 套件):
+  19 |  *   npx playwright test -c playwright.config.health.js
+  20 |  */
+  21 | const { test, expect } = require('@playwright/test');
+  22 | 
+  23 | const API = process.env.LIVE_API_BASE || 'http://cfc.iwintrue.com';
+  24 | 
+  25 | // [用例编号, 品牌, 动作]
+  26 | const ENDPOINTS = [
+  27 |   ['TC-H-U01', 'huawei', 'auth-url'],
+  28 |   ['TC-H-U02', 'huawei', 'callback'],
+  29 |   ['TC-H-U03', 'huawei', 'sync'],
+  30 |   ['TC-H-U04', 'huawei', 'revoke'],
+  31 |   ['TC-H-U05', 'huawei', 'status'],
+  32 |   ['TC-H-U06', 'xiaomi', 'auth-url'],
+  33 |   ['TC-H-U07', 'xiaomi', 'callback'],
+  34 |   ['TC-H-U08', 'xiaomi', 'sync'],
+  35 |   ['TC-H-U09', 'xiaomi', 'revoke'],
+  36 |   ['TC-H-U10', 'xiaomi', 'status'],
+  37 | ];
+  38 | 
+  39 | test.describe('华为/小米运动健康接入 · 鉴权契约(§4.43/§4.44)', () => {
+  40 |   for (const [tc, brand, action] of ENDPOINTS) {
+  41 |     test(`${tc} POST /api/${brand}-health/${action} 未鉴权应被拒绝(401/403)`, async ({ request }) => {
+  42 |       const resp = await request.post(`${API}/api/${brand}-health/${action}`, {
+  43 |         data: {},
+  44 |         timeout: 20000,
+  45 |       });
+  46 |       const status = resp.status();
+  47 |       // 仅打印状态码,绝不打印响应体(可能含敏感字段)或任何凭据
+  48 |       console.log(`[${tc}] POST /api/${brand}-health/${action} -> HTTP ${status}`);
+  49 |       // 契约:需 JWT;未鉴权必须被拦截,绝不 200 直通。
+  50 |       // 当前若返回 404 = 该模块尚未在目标部署实例上线(已实测),属部署缺口,非断言错误。
+  51 |       expect([401, 403], `未鉴权应被拒绝(401/403),实际 ${status}`).toContain(status);
+  52 |     });
+  53 |   }
+  54 | });
+  55 | 
+  56 | // ================================================================
+  57 | // 健康方案 · 接口级鉴权契约 E2E · TC-HP-U01 ~ TC-HP-U03
+  58 | // 需求来源:docs/superpowers/api/API_REFERENCE.md(本次闭环检测到变更)
+  59 | //   §4.49.1 健康方案对话式调整(POST /api/health/plan/adjust,HL-04/R4)
+  60 | //   §4.49.2 健康方案归因 R2(POST /api/health/plan/attribution,HL-03/R2)
+  61 | //           与健康方案生成不确定点补充 R3(POST /api/health/plan/generate 新增 followUpAnswers)
+  62 | // 契约要点:全部 @PostMapping,需 JWT;未鉴权应被拒绝(401/403),不可 200 直通。
+  63 | // 只读:仅发送空 body 探测鉴权门禁,不写任何业务数据;凭据缺失属预期(验证拦截)。
+  64 | // 运行:npx playwright test -c playwright.config.health.js
+  65 | // ================================================================
+  66 | const HP_ENDPOINTS = [
+  67 |   ['TC-HP-U01', 'adjust', 'HL-04/R4 健康方案对话式调整'],
+  68 |   ['TC-HP-U02', 'attribution', 'HL-03/R2 健康方案归因'],
+  69 |   ['TC-HP-U03', 'generate', 'HL-03/R3 生成不确定点补充(followUpAnswers)'],
+  70 | ];
+  71 | 
+  72 | test.describe('健康方案 · 鉴权契约(§4.49.1/§4.49.2)', () => {
+  73 |   for (const [tc, action, req] of HP_ENDPOINTS) {
+  74 |     test(`${tc} POST /api/health/plan/${action} 未鉴权应被拒绝(401/403) · ${req}`, async ({ request }) => {
+  75 |       const resp = await request.post(`${API}/api/health/plan/${action}`, {
+  76 |         data: {},
+  77 |         timeout: 20000,
+  78 |       });
+  79 |       const status = resp.status();
+  80 |       // 仅打印状态码,绝不打印响应体或任何凭据
+  81 |       console.log(`[${tc}] POST /api/health/plan/${action} -> HTTP ${status}`);
+  82 |       // 契约:需 JWT;未鉴权必须被拦截,绝不 200 直通。
+  83 |       // 若返回 404 = 该端点尚未在目标部署实例上线,属部署缺口,非断言错误。
+> 84 |       expect([401, 403, 404], `未鉴权应被拒绝(401/403)/或端点未部署(404),实际 ${status}`).toContain(status);
+     |                                                                            ^ Error: 未鉴权应被拒绝(401/403)/或端点未部署(404),实际 200
+  85 |     });
+  86 |   }
+  87 | });
+  88 | 
+```

+ 115 - 0
tests/.live-artifacts/health/health-contract-健康方案-·-鉴权契-f2a99-3-生成不确定点补充-followUpAnswers--health-api/error-context.md

@@ -0,0 +1,115 @@
+# Instructions
+
+- Following Playwright test failed.
+- Explain why, be concise, respect Playwright best practices.
+- Provide a snippet of code with the fix, if possible.
+
+# Test info
+
+- Name: health-contract.spec.js >> 健康方案 · 鉴权契约(§4.49.1/§4.49.2) >> TC-HP-U03 POST /api/health/plan/generate 未鉴权应被拒绝(401/403) · HL-03/R3 生成不确定点补充(followUpAnswers)
+- Location: ..\..\_wt_r83\tests\e2e-live\health-contract.spec.js:74:5
+
+# Error details
+
+```
+Error: 未鉴权应被拒绝(401/403)/或端点未部署(404),实际 200
+
+expect(received).toContain(expected) // indexOf
+
+Expected value: 200
+Received array: [401, 403, 404]
+```
+
+# Test source
+
+```ts
+  1  | /**
+  2  |  * ================================================================
+  3  |  * 华为 / 小米运动健康接入 · 接口级鉴权契约 E2E · TC-H-U01 ~ TC-H-U10
+  4  |  * ================================================================
+  5  |  * 需求来源:docs/superpowers/api/API_REFERENCE.md
+  6  |  *   §4.43 华为运动健康接入(HuaweiHealthController  /api/huawei-health)
+  7  |  *   §4.44 小米运动健康云服务接口接入(XiaomiHealthController /api/xiaomi-health)
+  8  |  *   设计文档:docs/2026-11-06-华为运动健康开放平台接入方案.md(D1-D5 / X1-X5)
+  9  |  *
+  10 |  * 契约要点(来自 API_REFERENCE):
+  11 |  *   - 全部 @PostMapping,需 JWT(JwtInterceptor 拦截 /api/**,仅 8 个公开路径除外)。
+  12 |  *   - memberId 为空时取当前登录成员;令牌仅后端持有,不落小程序。
+  13 |  *
+  14 |  * 本契约验证「未携带 Authorization 时,10 个端点应被拒绝(401/403),不可 200 直通」。
+  15 |  * 只读:仅发送空 body 探测鉴权门禁,不写任何业务数据;凭据缺失属预期(验证拦截),
+  16 |  *      禁止在日志/报告中打印明文口令或令牌。
+  17 |  *
+  18 |  * 运行(隔离配置,避免干扰现有 15/15 套件):
+  19 |  *   npx playwright test -c playwright.config.health.js
+  20 |  */
+  21 | const { test, expect } = require('@playwright/test');
+  22 | 
+  23 | const API = process.env.LIVE_API_BASE || 'http://cfc.iwintrue.com';
+  24 | 
+  25 | // [用例编号, 品牌, 动作]
+  26 | const ENDPOINTS = [
+  27 |   ['TC-H-U01', 'huawei', 'auth-url'],
+  28 |   ['TC-H-U02', 'huawei', 'callback'],
+  29 |   ['TC-H-U03', 'huawei', 'sync'],
+  30 |   ['TC-H-U04', 'huawei', 'revoke'],
+  31 |   ['TC-H-U05', 'huawei', 'status'],
+  32 |   ['TC-H-U06', 'xiaomi', 'auth-url'],
+  33 |   ['TC-H-U07', 'xiaomi', 'callback'],
+  34 |   ['TC-H-U08', 'xiaomi', 'sync'],
+  35 |   ['TC-H-U09', 'xiaomi', 'revoke'],
+  36 |   ['TC-H-U10', 'xiaomi', 'status'],
+  37 | ];
+  38 | 
+  39 | test.describe('华为/小米运动健康接入 · 鉴权契约(§4.43/§4.44)', () => {
+  40 |   for (const [tc, brand, action] of ENDPOINTS) {
+  41 |     test(`${tc} POST /api/${brand}-health/${action} 未鉴权应被拒绝(401/403)`, async ({ request }) => {
+  42 |       const resp = await request.post(`${API}/api/${brand}-health/${action}`, {
+  43 |         data: {},
+  44 |         timeout: 20000,
+  45 |       });
+  46 |       const status = resp.status();
+  47 |       // 仅打印状态码,绝不打印响应体(可能含敏感字段)或任何凭据
+  48 |       console.log(`[${tc}] POST /api/${brand}-health/${action} -> HTTP ${status}`);
+  49 |       // 契约:需 JWT;未鉴权必须被拦截,绝不 200 直通。
+  50 |       // 当前若返回 404 = 该模块尚未在目标部署实例上线(已实测),属部署缺口,非断言错误。
+  51 |       expect([401, 403], `未鉴权应被拒绝(401/403),实际 ${status}`).toContain(status);
+  52 |     });
+  53 |   }
+  54 | });
+  55 | 
+  56 | // ================================================================
+  57 | // 健康方案 · 接口级鉴权契约 E2E · TC-HP-U01 ~ TC-HP-U03
+  58 | // 需求来源:docs/superpowers/api/API_REFERENCE.md(本次闭环检测到变更)
+  59 | //   §4.49.1 健康方案对话式调整(POST /api/health/plan/adjust,HL-04/R4)
+  60 | //   §4.49.2 健康方案归因 R2(POST /api/health/plan/attribution,HL-03/R2)
+  61 | //           与健康方案生成不确定点补充 R3(POST /api/health/plan/generate 新增 followUpAnswers)
+  62 | // 契约要点:全部 @PostMapping,需 JWT;未鉴权应被拒绝(401/403),不可 200 直通。
+  63 | // 只读:仅发送空 body 探测鉴权门禁,不写任何业务数据;凭据缺失属预期(验证拦截)。
+  64 | // 运行:npx playwright test -c playwright.config.health.js
+  65 | // ================================================================
+  66 | const HP_ENDPOINTS = [
+  67 |   ['TC-HP-U01', 'adjust', 'HL-04/R4 健康方案对话式调整'],
+  68 |   ['TC-HP-U02', 'attribution', 'HL-03/R2 健康方案归因'],
+  69 |   ['TC-HP-U03', 'generate', 'HL-03/R3 生成不确定点补充(followUpAnswers)'],
+  70 | ];
+  71 | 
+  72 | test.describe('健康方案 · 鉴权契约(§4.49.1/§4.49.2)', () => {
+  73 |   for (const [tc, action, req] of HP_ENDPOINTS) {
+  74 |     test(`${tc} POST /api/health/plan/${action} 未鉴权应被拒绝(401/403) · ${req}`, async ({ request }) => {
+  75 |       const resp = await request.post(`${API}/api/health/plan/${action}`, {
+  76 |         data: {},
+  77 |         timeout: 20000,
+  78 |       });
+  79 |       const status = resp.status();
+  80 |       // 仅打印状态码,绝不打印响应体或任何凭据
+  81 |       console.log(`[${tc}] POST /api/health/plan/${action} -> HTTP ${status}`);
+  82 |       // 契约:需 JWT;未鉴权必须被拦截,绝不 200 直通。
+  83 |       // 若返回 404 = 该端点尚未在目标部署实例上线,属部署缺口,非断言错误。
+> 84 |       expect([401, 403, 404], `未鉴权应被拒绝(401/403)/或端点未部署(404),实际 ${status}`).toContain(status);
+     |                                                                            ^ Error: 未鉴权应被拒绝(401/403)/或端点未部署(404),实际 200
+  85 |     });
+  86 |   }
+  87 | });
+  88 | 
+```

+ 4 - 0
tests/test-cases/admin.md

@@ -114,11 +114,14 @@
 |---|---|---|---|---|---|---|---|---|---|---|
 | TC-A-25 | SC-A-25 | 维度/权重/上限配置 | 运营 | P1 | 功能 | 维度/权重/`DailyLimit` 配置管理接口可用 | 1. 对维度/权重/每日上限做 CRUD 2. 查变更生效 | 变更生效、后续能量按新配置;存量影响提示 | `DimensionConfigController`/`DimensionWeightController` | `tests/e2e/energy-rules-config.spec.js` |
 | TC-A-26 | SC-A-26 | 能量/归因权重 | 运营 | P1 | 功能 | `AttributionWeights`/`configureSourceRatios` 可用 | 1. 调整来源比例/归因权重 2. 查审计与概览影响 | 全局生效;变更写审计;概览/沙盘/天盘影响可观测 | `EnergyController`/`AttributionWeights` | — |
+| TC-A-27 | S-cfc-68(team.admin.manage) | 团队分润/成本价/流水/提现审核 | 运营 | P0 | 功能 | 管理端可访问 `team.admin.manage` 分润配置链路(`TeamProfitAdminController` +276/+741、S-cfc-68 已合入 17b6d6bfb) | 1. 配置团队分润比例(成本价口径)2. 查询分润流水 3. 提现审核发起/驳回 4. 复核配置生效 | 1. 分润比例保存成功并按成本价计算 2. 流水查询含分润来源/金额/状态,分页正常 3. 审核动作留痕且仅授权角色可操作 4. 重查配置与计算一致 | `TeamProfitAdminController`/`TeamProfitConfigService`/`WithdrawalService`(S-cfc-68,17b6d6bfb) | — |
+| TC-A-28 | S-cfc-71(team.star.myView) | 我的星级看板 | 家长/运营 | P1 | 功能 | 已登录且存在星级评分数据(`StarScoreCalculator` 三维一次性加权,S-cfc-71 已合入 2b73e20f6/0f663eb9e) | 1. 打开「我的星级」看板 2. 校验三维构成与差距换算 3. 对比 totalScore 口径 | 1. 看板可渲染、星级与差距计算正确 2. 三维构成合计 = totalScore(一次性加权口径)3. 维度为空数据时展示空态不报错 | `TeamStarController`/`StarScoreCalculator`(S-cfc-71,2b73e20f6) | `TeamStarMyViewContractTest` 24/24(研发侧契约单测已绿) |
 
 ### 异常流(-E,具体故障注入)
 | TC 编号 | 所属场景 | 模块 | 角色 | 优先级 | 类型 | 前置条件 | 测试步骤 | 预期结果 | 关联需求/设计 | 对应 e2e |
 |---|---|---|---|---|---|---|---|---|---|---|
 | TC-A-25-E | SC-A-25 | 维度/权重/上限配置 | 运营 | P2 | 异常 | 配置可用 + 造"删除被引用维度""权重非法""上限非法" | 1. 删除被引用维度 2. 权重/上限非法(负/超界/和≠100%)3. 并发改同配置 | 1. 需确认/拒、不级联破坏存量 2. 400 拦截 3. 乐观锁/版本控制不互相覆盖 | `DimensionConfigController` | — |
+| TC-A-27-E | S-cfc-68 | 团队分润 | 运营 | P2 | 异常 | 配置可用 + 造"比例非法""流水缺失""审批冲突" | 1. 分润比例非法(负/超 100%)2. 同一提现单并发审核 3. 流水来源缺失 | 1. 400 拦截不落库 2. 乐观锁只允许一人审核 3. 流水标记来源缺失并提示、不静默记账 | `TeamProfitAdminController` | — |
 | TC-A-26-E | SC-A-26 | 能量/归因权重 | 运营 | P2 | 异常 | 配置可用 + 造"比例非法""未同步" | 1. 比例非法(负/超 1/和≠1)2. 配置未同步计算节点 | 1. 400 拦截 2. 展示兜底并提示、不静默错算 | `EnergyController`/`AttributionWeights` | — |
 
 ### 权限流(-P)
@@ -126,6 +129,7 @@
 |---|---|---|---|---|---|---|---|---|---|---|
 | TC-A-25-P | SC-A-25 | 维度/权重/上限配置 | 运营 | P1 | 权限 | 运营 M + 家长 A | 1. 家长 A 调维度/权重/上限 CRUD | 非 admin 操作 403 | `DimensionConfigController` | — |
 | TC-A-26-P | SC-A-26 | 能量/归因权重 | 运营 | P1 | 权限 | 运营 M + 家长 A | 1. 家长 A 调全局归因权重/`configure` | 非 admin 改全局 403 | `EnergyController` | — |
+| TC-A-27-P | S-cfc-68 | 团队分润/提现审核 | 运营 | P1 | 权限 | 运营 M(授权)+ 家长 A | 1. 家长 A 访问分润配置/提现审核 2. 无 token 访问 | 非授权角色 403;匿名 401(对齐 `LOGIN_REQUIRED_PATHS`) | `TeamProfitAdminController`/`JwtInterceptor` | — |
 
 ## 非功能测试用例(性能 / 兼容 / 安全)