diff --git a/.claude/skills/d8d-dev/modes/journey/journey-definition.md b/.claude/skills/d8d-dev/modes/journey/journey-definition.md index 50470da..34b8d7b 100644 --- a/.claude/skills/d8d-dev/modes/journey/journey-definition.md +++ b/.claude/skills/d8d-dev/modes/journey/journey-definition.md @@ -14,6 +14,12 @@ 3. 为每个 Epic 生成/重写 epic-N-journeys.md - 严格遵循旅程真实性规则(⛔ 见 ../../rules/journey-authenticity-rules.md) - 使用增强版旅程格式 + - ⛔ R6 自包含强制:前置条件禁止包含任何业务数据依赖(已有/存在/包含 + 业务数据) + 如果旅程涉及需要前置业务数据的操作(如报名需先有组局),必须在用户视角步骤中 + 包含完整的数据创建 UI 操作序列(使用标准前置步骤模板,见规则文件 R6 节) + - ⛔ 每个 Epic 可由独立 teammate 生成(并行模式见步骤 3P) + - ⛔ 标准前置步骤模板: _bmad-output/implementation-artifacts/journey-prerequisite-templates.md + 使用方法见 ../../rules/journey-authenticity-rules.md R6 节"标准前置步骤模板" ↓ 3.2 生成后自检(⛔ 强制) **Read fully and follow: ../../rules/journey-authenticity-rules.md** @@ -21,6 +27,37 @@ - 按规则 R1-R5 逐条校验 - 违规 → 自动修复 → 重新校验(max 3x) ↓ +3P. 并行生成模式(⛔ 可选 — 支持 teammate 时启用) + 当检测到 Agent/teammate 工具可用时,可使用并行模式加速生成: + + a. 主 agent 为每个 Epic 创建独立 Task: + - 主题: "生成 Epic {N} 用户旅程" + - 描述包含: PRD 片段 + Epic/Story 定义路径 + 规则文件路径 + 源码文件路径 + + b. 按批次分派 teammate(每批最多 3 个并行): + Batch 1: Epic 1, 2, 5(基础功能,无前置依赖) + Batch 2: Epic 3, 4, 6(依赖 Batch 1 产出的数据结构) + Batch 3: Epic 7, 8, 9(依赖前面所有 Epic 的数据) + + c. 每个 teammate 的任务 prompt 必须包含: + 1. 规则文件路径: ../../rules/journey-authenticity-rules.md(⛔ 必须 Read fully and follow) + 2. 标准前置步骤模板: _bmad-output/implementation-artifacts/journey-prerequisite-templates.md + ⛔ R6 自包含原则:前置条件禁止包含业务数据依赖,必须使用模板中的步骤内联 + 3. 对应 Epic/Story 定义文件路径 + 4. 对应页面源码文件路径列表 + 5. 入口配置信息 + 6. 增强版旅程格式模板 + + d. 每个 teammate 生成后必须自行执行步骤 3.2 自检(max 3x) + + e. 主 agent 收集所有生成结果 → 统一执行步骤 3.5(覆盖度评估) + ⚠️ 覆盖度评估必须由主 agent 统一执行(需要读取所有旅程文件) + + ⛔ 并行模式注意事项: + - 不同 teammate 生成的旅程中引用同一标准模板时,步骤内容必须完全一致 + - 主 agent 在步骤 3.5 后检查跨 Epic 一致性 + - 不一致 → 主 agent 自动修复(以第一个生成结果的版本为准) + ↓ 3.5 覆盖度评估(FR 追踪矩阵交叉检查): a. 读取 `03-epics-and-stories.md` 的 "FR 覆盖追踪矩阵" 表格 b. 读取所有 `epic-N-journeys.md` 中每条旅程涉及的技术验证步骤 diff --git a/.claude/skills/d8d-dev/modes/journey/journey-execution.md b/.claude/skills/d8d-dev/modes/journey/journey-execution.md index d80aef1..301b067 100644 --- a/.claude/skills/d8d-dev/modes/journey/journey-execution.md +++ b/.claude/skills/d8d-dev/modes/journey/journey-execution.md @@ -85,7 +85,7 @@ **⛔ 如果以上回退链全部失败且源码无 console.log → FAIL #14 → AUTO-FIX** -(详见 ../../rules/auto-fix-engine.md AUTO-FIX #14) +(详见 ../../rules/auto-fix/workflows.md AUTO-FIX #14) **规则 E4: 数据断言必须使用操作前后的实际值** ``` @@ -162,7 +162,7 @@ curl 只用于补充数据验证(如确认数据库状态),**永远不能 ## ⛔ 真实用户完成规则(Real User Completion Rule) -**Read fully and follow: ../../rules/auto-fix-engine.md 第 3 节"真实用户完成规则"** +**Read fully and follow: ../../rules/auto-fix/diagnostics.md"真实用户完成规则"** - 核心原则:browser_type/click 失败 = Bug → AUTO-FIX,禁止 evaluate/run_code 绕过 - browser_type 失败后必须执行 FAIL #6 诊断子步骤(含完整修复流程) @@ -197,7 +197,7 @@ curl 只用于补充数据验证(如确认数据库状态),**永远不能 - browser_snapshot → 验证起始页面(⛔ 禁止用 curl GET 替代) - ⛔ **零交互检查**: 如果 snapshot 显示页面有文字但无任何 button/input/a/form: - 标记步骤 FAIL(用户无法从此页面继续操作) - - `browser_evaluate` 执行 FAIL #5 诊断(检查 interactiveCount / frameworkMounted — 见 ../../rules/auto-fix-engine.md) + - `browser_evaluate` 执行 FAIL #5 诊断(检查 interactiveCount / frameworkMounted — 见 ../../rules/auto-fix/diagnostics.md) - 按诊断结果分流 → AUTO-FIX 或 AUTO-IMPLEMENT - ⛔ **起始页视觉验证**: browser_take_screenshot → filename: "tests/e2e/screenshots/journey-epic-{N}-{M}-step0.png", type: "png" + analyze_image - 循环每个用户步骤(步骤编号 S = 1, 2, 3...): @@ -210,7 +210,7 @@ curl 只用于补充数据验证(如确认数据库状态),**永远不能 1. browser_take_screenshot → filename: "tests/e2e/screenshots/journey-epic-{N}-{M}-step{S}.png", type: "png" 2. analyze_image → 使用 visual-verification.md 中的标准提示词 3. 结果记录到 pipeline-state.yaml 的 verification_detail(含 analyze_image_executed: true) - 4. 视觉验证 FAIL → 按分流规则处理(见 ../../rules/auto-fix-engine.md + ../../rules/iron-laws.md 检测即阻断表) + 4. 视觉验证 FAIL → 按分流规则处理(见 ../../rules/auto-fix/engine.md + ../../rules/iron-laws.md 检测即阻断表) 5. ⛔ **步骤级跟踪**(每个用户步骤完成后强制执行): a. 更新 verification-checklist.yaml 对应 step 的 status b. 追加 step_results 到 pipeline-state.yaml 的 user_journeys: @@ -424,14 +424,14 @@ curl 只用于补充数据验证(如确认数据库状态),**永远不能 4. 修复阶段(⛔ 以下任一条件满足时必须执行,不得跳过): - **Read fully and follow: ../../rules/auto-fix-engine.md** + **Read fully and follow: ../../rules/auto-fix/engine.md** (包含: PASS/FAIL 判定标准 → FAIL 分类表 → 真实用户完成规则 → AUTO-FIX/IMPLEMENT/DATA-FIX 流程 → GATE-CHECK) **触发条件(⛔ 明确化 — 以下任一即触发)**: - `steps_failed > 0`(功能断言失败) - `constraint_mismatches` 非空(R7 约束不一致检测到 — 即使 steps_passed == steps_executed) - 跨轮次累计的视觉问题 ≥ 1(emoji_as_icons / english_enum / broken_images 等 — 检测到即修复) - - 前一次验证的 `pending_fixes` 中有未修复项(⛔ 即使是其他 EPIC 范围的问题,也必须在本轮修复 — 参见 ../../rules/auto-fix-engine.md "已知问题不豁免"规则) + - 前一次验证的 `pending_fixes` 中有未修复项(⛔ 即使是其他 EPIC 范围的问题,也必须在本轮修复 — 参见 ../../rules/auto-fix/classification.md "已知问题不豁免"规则) - `verification_detail` 中任一检查项为 FOUND 且非设计系统主观视觉评价(WARN) **⛔ 禁止的"记录但不修复"模式**: diff --git a/.claude/skills/d8d-dev/modes/journey/journey-validation.md b/.claude/skills/d8d-dev/modes/journey/journey-validation.md index 49cfc56..355cb17 100644 --- a/.claude/skills/d8d-dev/modes/journey/journey-validation.md +++ b/.claude/skills/d8d-dev/modes/journey/journey-validation.md @@ -16,7 +16,15 @@ ↓ 1.5 执行前校验与自动修复(⛔ 强制) **Read fully and follow: ../../rules/journey-authenticity-rules.md** - - 按统一规则 R1-R5 + R7 校验所有旅程文件 + - 按统一规则 R1-R9 校验所有旅程文件 + - ⛔ R6 自包含严格校验(前置条件零数据依赖): + a. 正则匹配所有前置条件中的数据依赖模式: + `已有|存在|包含|至少有` + `组局|订单|收益|提现|场地|通知|评价|数据|用户|视频|封面` + b. 发现违规 → 自动修复(使用标准前置步骤模板): + - 读取 _bmad-output/implementation-artifacts/journey-prerequisite-templates.md + - 将违规前置条件转化为前置 UI 操作步骤(从对应模板复制步骤内联) + - 更新前置条件为仅保留用户状态("用户未登录"/"用户已登录") + c. 3 次自动修复仍失败 → 阻断验证,输出详细报告 - R7 约束一致性校验:对含"可选/可跳过/选填"关键词的步骤,grep 对应源码验证逻辑 - 不一致 → 自动修复旅程定义(使其准确反映代码)→ 重新校验 - 违规 → 自动修复旅程文件 → 重新校验(max 3x) @@ -99,12 +107,21 @@ - 旅程: {旅程名称} - 文件: {旅程文件路径} - 前置条件: {前置条件描述} + - ⛔ 本旅程完全自包含(R6 合规),从用户原始状态开始,不依赖其他旅程产出 + - 标准前置步骤模板: _bmad-output/implementation-artifacts/journey-prerequisite-templates.md 严格按照 journey-execution.md 执行模板执行完整验证流程。 ``` ↓ 1.10 启动逐个任务验证(每条旅程独立 agent) - 对**每个任务**启动一个**独立的 agent**,按照 `./journey-execution.md` 执行模板执行验证 + - 独立 agent 执行模型(⛔ 严格 R6 自包含 — 每个 teammate 完全独立): + - 每个 teammate 拿到一条完全自包含的旅程 + - 从用户原始状态开始执行(browser_navigate 到自然入口) + - 不依赖其他旅程的产出数据(所有数据通过自身 UI 操作创建) + - 失败不影响其他旅程 + - 无需与其他 teammate 协调或等待 + - 对**每个任务**启动一个**独立的 agent**,按照 `./journey-execution.md` 执行模板执行验证 - 独立 agent 避免上下文污染,执行更精准 ⛔ **强制规则:禁止在主 agent 中一次性串行执行多条旅程** diff --git a/.claude/skills/d8d-dev/modes/validation-mode.md b/.claude/skills/d8d-dev/modes/validation-mode.md index 9abe4e2..fa39d86 100644 --- a/.claude/skills/d8d-dev/modes/validation-mode.md +++ b/.claude/skills/d8d-dev/modes/validation-mode.md @@ -2,7 +2,7 @@ > 参见 ../rules/e2e-testing.md 了解 Playwright MCP 测试序列详情。 > 参见 ../rules/iron-laws.md 了解四维验证检查清单。 -> 参见 ../rules/auto-fix-engine.md 了解统一修复引擎(FAIL分类标准 + 修复流程 + GATE-CHECK)。 +> 参见 ../rules/auto-fix/engine.md 了解统一修复引擎(FAIL分类标准 + 修复流程 + GATE-CHECK)。 ## 续跑逻辑 @@ -177,7 +177,7 @@ Phase 0V: 批量创建 Task + 读取 Story 规格文档 > 跨 Epic 的用户旅程验证已独立为「旅程验证模式」,不在此模式中执行。 > 回归验证模式只负责逐 Story 功能点验证。完成所有 Story 后建议进入旅程验证模式。 - > AUTO-FIX/AUTO-IMPLEMENT/AUTO-DATA-FIX 的完整分类标准和修复流程见 ../rules/auto-fix-engine.md + > AUTO-FIX/AUTO-IMPLEMENT/AUTO-DATA-FIX 的完整分类标准和修复流程见 ../rules/auto-fix/engine.md ``` ### 与完整模式的区别 @@ -320,7 +320,7 @@ echo "localhost_fallback=$LOCALHOST_COUNT inline_api=$INLINE_COUNT" **⛔ AUTO-FIX/IMPLEMENT 失败阻断**:源码三重检查 AUTO-FIX/AUTO-IMPLEMENT 重试 3 次后仍 FAIL → Story 标记为 `failed`,记录到 `error_log`,继续下一个 Story。不得反复重试或跳过。 -> 完整的 AUTO-FIX/IMPLEMENT/DATA-FIX 流程和重试上限见 ../rules/auto-fix-engine.md 第 4 节 +> 完整的 AUTO-FIX/IMPLEMENT/DATA-FIX 流程和重试上限见 ../rules/auto-fix/workflows.md **检查 4: 未实现功能占位符(所有前端强制)** ```bash @@ -353,7 +353,7 @@ echo "placeholder_functions=$PLACEHOLDER_COUNT" ``` **判定**: -- `interactiveCount === 0` 且 `hasContent === true` → **FAIL** → 触发 FAIL #5 诊断子步骤(见 auto-fix-engine.md) +- `interactiveCount === 0` 且 `hasContent === true` → **FAIL** → 触发 FAIL #5 诊断子步骤(见 ../rules/auto-fix/diagnostics.md) - `interactiveCount > 0` → **PASS** → 继续正常 E2E 流程 **检查 6: UI 硬编码英文枚举值(所有前端 Story 强制)** diff --git a/.claude/skills/d8d-dev/phases/phase-04-test.md b/.claude/skills/d8d-dev/phases/phase-04-test.md index 36c3440..295fb0d 100644 --- a/.claude/skills/d8d-dev/phases/phase-04-test.md +++ b/.claude/skills/d8d-dev/phases/phase-04-test.md @@ -1,6 +1,6 @@ # Phase 4: TEST(测试) -> 参见 ../rules/auto-fix-engine.md 了解统一修复引擎(FAIL分类标准 + 修复流程 + GATE-CHECK)。 +> 参见 ../rules/auto-fix/engine.md 了解统一修复引擎(FAIL分类标准 + 修复流程 + GATE-CHECK)。 > 参见 ../rules/e2e-testing.md 了解 Playwright MCP 测试序列详情。 @@ -177,5 +177,5 @@ REVIEW attempt < 3? 继续下一个 Story ``` -> 完整的 AUTO-FIX/IMPLEMENT/DATA-FIX 分类标准和修复流程见 ../rules/auto-fix-engine.md 第 2-4 节 +> 完整的 AUTO-FIX/IMPLEMENT/DATA-FIX 分类标准见 ../rules/auto-fix/classification.md,修复流程见 ../rules/auto-fix/workflows.md diff --git a/.claude/skills/d8d-dev/phases/phase-05-review.md b/.claude/skills/d8d-dev/phases/phase-05-review.md index ba9f8b0..b55f299 100644 --- a/.claude/skills/d8d-dev/phases/phase-05-review.md +++ b/.claude/skills/d8d-dev/phases/phase-05-review.md @@ -1,6 +1,6 @@ # Phase 5-7: REVIEW + EPIC-DONE + COMPLETE -> 参见 ../rules/auto-fix-engine.md 了解统一修复引擎(FAIL分类标准 + 修复流程 + GATE-CHECK)。 +> 参见 ../rules/auto-fix/engine.md 了解统一修复引擎(FAIL分类标准 + 修复流程 + GATE-CHECK)。 > 参见 ./iron-laws.md 了解质量关卡规则。 @@ -156,5 +156,5 @@ echo "journey_results=$JOURNEY_COUNT" - 阻断: 仅在 d8d-iterate 编排模式下生效;d8d-dev 独立运行时记录为 WARN - 修复: 调用 d8d-prototype --incremental 更新原型 -> 完整的 FAIL 分类标准、GATE-CHECK 和修复流程见 ../rules/auto-fix-engine.md +> 完整的 FAIL 分类标准、GATE-CHECK 和修复流程见 ../rules/auto-fix/engine.md diff --git a/.claude/skills/d8d-dev/reference/platform-uni-app.md b/.claude/skills/d8d-dev/reference/platform-uni-app.md index 93e005f..25849cf 100644 --- a/.claude/skills/d8d-dev/reference/platform-uni-app.md +++ b/.claude/skills/d8d-dev/reference/platform-uni-app.md @@ -1,7 +1,7 @@ # Uni-app 微信小程序平台参考 > **⛔ 主流程文件统一引用此文件**:所有 uni-app/微信小程序的平台特定内容集中于此。 -> 主流程文件(auto-fix-engine / iron-laws / journey-mode / visual-verification / validation-mode / e2e-testing / phase-03-implement)中不再内联平台特定方案,改为引用本文件对应章节。 +> 主流程文件(auto-fix/engine / iron-laws / journey-mode / visual-verification / validation-mode / e2e-testing / phase-03-implement)中不再内联平台特定方案,改为引用本文件对应章节。 --- @@ -397,3 +397,131 @@ onPullDownRefresh(() => { uni.stopPullDownRefresh(); // ← 新增 } ``` + +--- + +## 8. H5 不兼容组件替换方案(⛔ 强制) + +### 问题 + +uni-app 原生组件(``/``/``/``/`` 等)在 H5 环境下可能出现: +- 不渲染(DOM 中存在但宽高为 0) +- 不可交互(点击无响应) +- 样式异常(默认浏览器样式覆盖 Neon Pulse 设计) + +微信小程序正常但 H5 不正常的组件,**不得标记为"环境限制"跳过**。必须用跨平台兼容的纯 view + CSS 方案替换。 + +### ⛔ 核心原则 + +- **禁止**使用 uni-app 原生表单组件(``/``/``/``/``) +- **必须**使用纯 `` + CSS + `@tap` 实现等效功能 +- 这样做:小程序和 H5 都能正常渲染,无需任何平台判断 + +### 不兼容组件清单 + +| uni-app 原生组件 | H5 问题 | 替换方案 | +|------------------|---------|---------| +| `` | 不渲染或样式异常 | view + Iconify check 图标 | +| `` | 不渲染或样式异常 | view + CSS 圆点 | +| `` | 样式与设计系统冲突 | view + CSS 滑动开关 | +| `` | 样式不可控 | view + CSS 进度条 + @touchmove | +| `` | 原生弹窗与 H5 不兼容 | 自定义弹窗组件 | + +### 检测方法(⛔ 旅程验证步骤级强制) + +```bash +# 扫描所有 .vue 文件中的原生表单组件 +grep -rn ' + + + + + + +``` + +```css +.custom-checkbox { + width: 18px; height: 18px; min-width: 18px; + border-radius: 4px; + border: 1.5px solid rgba(255, 255, 255, 0.2); + background: rgba(255, 255, 255, 0.05); + display: flex; align-items: center; justify-content: center; + transition: all 0.2s ease; +} +.custom-checkbox.checked { + background: linear-gradient(135deg, #ffb3b5, #ff5167); + border-color: #ff5167; +} +.check-icon { width: 12px; height: 12px; color: #000; } +``` + +#### radio → view + CSS 圆点 + +```vue + + + + + + + +``` + +```css +.custom-radio { + width: 18px; height: 18px; min-width: 18px; + border-radius: 50%; + border: 1.5px solid rgba(255, 255, 255, 0.2); + background: rgba(255, 255, 255, 0.05); + display: flex; align-items: center; justify-content: center; + transition: all 0.2s ease; +} +.custom-radio.checked { border-color: #ff5167; } +.radio-dot { + width: 10px; height: 10px; border-radius: 50%; + background: linear-gradient(135deg, #ffb3b5, #ff5167); +} +``` + +#### switch → view + CSS 滑动 + +```vue + + + + + + + +``` + +```css +.custom-switch { + width: 44px; height: 24px; border-radius: 12px; + background: rgba(255, 255, 255, 0.1); + position: relative; transition: background 0.2s; +} +.custom-switch.on { background: linear-gradient(135deg, #ffb3b5, #ff5167); } +.switch-thumb { + width: 20px; height: 20px; border-radius: 50%; + background: #fff; position: absolute; top: 2px; left: 2px; + transition: transform 0.2s; +} +.custom-switch.on .switch-thumb { transform: translateX(20px); } +``` + +### 关联修复规则 + +- 触发 FAIL 分类: ../rules/auto-fix/classification.md #23 +- 修复模板: ../rules/auto-fix/workflows.md AUTO-FIX #23 章节 +- ⛔ 禁止标记为"环境限制"跳过 diff --git a/.claude/skills/d8d-dev/rules/auto-fix-engine.md b/.claude/skills/d8d-dev/rules/auto-fix-engine.md deleted file mode 100644 index 3dd5dca..0000000 --- a/.claude/skills/d8d-dev/rules/auto-fix-engine.md +++ /dev/null @@ -1,661 +0,0 @@ -# 统一修复引擎(Auto-Fix Engine) - -> **⛔ 所有模式共享**:完整模式、回归验证模式、旅程验证模式均引用此文件。任何模式不得另起炉灶重新定义修复机制。 - ---- - -## 1. 步骤完成判定标准(⛔ 每个 step 执行后强制判定) - -**PASS**: `browser_snapshot` 显示的内容 **完全包含** 旅程/验证定义中该步骤"用户看到 X"的预期状态。 -- 例: 预期"看到已登录状态(头像、昵称)" → snapshot 中有实际昵称文字(非占位符、非"加载中...") → PASS - -**FAIL**: 以下任一 = FAIL -1. snapshot 内容与预期不符(如预期"已登录"但看到"加载中...") -2. `browser_click` / `browser_type` 无法找到目标元素(元素不存在或不可交互) -3. `browser_wait_for` 超时(页面未按预期响应) -4. snapshot 显示错误状态(错误提示、空白页、404、500) -5. 数据断言不通过(操作前后数据状态无变化或变化不符预期) - -**⛔ 禁止判定为 PASS 的情况**: -- `"加载中..."` / `"请稍候"` / 空白页 -- 元素存在但内容为空 / 占位符 -- `"暂未开放"` / `"开发中"` / `"功能开发中"` 提示 -- 不做 snapshot 直接假设操作成功 -- 用 `browser_evaluate` 绕过不可交互的元素后声称"操作完成" - ---- - -## 2. FAIL 分类标准(⛔ 所有模式共享) - -| # | FAIL 特征 | 分类 | 修复类型 | 示例 | -|---|-----------|------|---------|------| -| 1 | API 返回 404 且路由文件不存在 | 未实现 | AUTO-IMPLEMENT | 路由缺失 | -| 2 | API 返回 500 / 错误状态码 | Bug | AUTO-FIX | 空 body 返回 500 | -| 3 | 前端组件/按钮未渲染 | 未实现 | AUTO-IMPLEMENT | 登录入口按钮缺失 | -| 4 | 页面跳转到错误位置 | Bug | AUTO-FIX | 导航逻辑错误 | -| 5 | 页面停留在非功能态("加载中"/空白/错误态/有内容但零交互元素) | Bug | AUTO-FIX | hydration 失败 / 渲染 Bug / fetchProfile 空 catch / 响应结构不匹配 | -| 6 | 可交互元素无法通过 Playwright 操作 | Bug | AUTO-FIX | uni-input 编译后 browser_type 失败 | -| 7 | 功能提示"暂未开放"/"开发中"/"暂未实现" | 未实现 | AUTO-IMPLEMENT | 手机号登录只有占位符 | -| 8 | 测试数据过期/状态不对 | 数据问题 | AUTO-DATA-FIX | 收益冻结期不可提现 | -| 9 | 已有数据阻止测试(重复评价等) | 数据问题 | AUTO-DATA-FIX | 删除旧评价或换测试用户 | -| 10 | 正常业务规则正确拒绝无效操作 | 可接受 | 仅记录 | 最低提现金额限制 | -| 11 | 旅程定义或源码中发现 Mock 登录且手机号登录未实现 | 未实现 | AUTO-IMPLEMENT | Mock 登录不是真实用户流程,必须实现手机号+验证码登录 | -| 12 | 操作被前端表单校验拦截(Toast/提示阻止提交)| 环境限制/校验 | ENV-ADAPT | 按 Toast 内容分流:缺少必填→补充填写;文件上传→browser_file_upload;环境限制→代码添加 dev 替代 | -| 13 | 表单校验失败但页面无任何提示(无 Toast/无错误文字)| Bug(UX 缺陷)| AUTO-FIX | 用户不知道为什么操作无效 → 添加 Toast 或内联错误提示 | -| 14 | 即时使用数据(验证码等)仅通过 Toast 显示,无 console.log 或自动填充,下一步骤无法获取 | Bug(数据丢失风险)| AUTO-FIX | sendCode() 用 Toast 显示验证码但无 console.log,流程卡死 | -| 15 | UI 直接输出英文枚举值(如 APPROVED/REJECTED/PENDING/PAID/CANCELLED 等)而非本地化中文标签 | Bug(UI 本地化缺失)| AUTO-FIX | 任何前端页面(管理后台/小程序/H5)状态字段显示英文枚举而非中文 | -| 16 | 约束声明与代码行为不一致(Constraint Mismatch): 旅程描述/UI 文案说"可选/可跳过/选填"但代码实际必填,或 UI 标记"选填"但接口必填 | 约束不一致 | JOURNEY-FIX / AUTO-FIX | 旅程说"可跳过上传证件"但代码 `if (!idCardFront)` Toast "请上传所有证件照片" | -| 17 | 前端代码 CSS/style 含 `url("data:image/svg+xml")` 或 `url('data:image/svg+xml')`(SVG data URI 充当图标/背景/装饰) | Bug(平台不兼容) | AUTO-IMPLEMENT | 微信小程序不支持 SVG data URI。⛔ 必须按 platform-uni-app.md §1 完整安装 Iconify + tailwindcss-icons + weapp-tailwindcss,将所有 SVG data URI 替换为 Iconify 类名。**禁止用另一种 SVG data URI 替代原来的** | -| 18 | 页面同时有系统导航栏(pages.json navigationBarTitleText 非 custom)和自定义 fixed header,导致双层标题栏 | Bug(双重导航栏) | AUTO-FIX | 截图中顶部出现两层标题(系统导航+自定义头部),参见 ../reference/platform-uni-app.md §3 | -| 19 | Tab 页面下拉刷新方式不一致(scroll-view refresher / onPullDownRefresh / 无 混用),或使用列表级 scroll-view refresher | Bug(UX 不一致) | AUTO-FIX | 首页用 scroll-view refresher,其他 Tab 无刷新,参见 ../reference/platform-uni-app.md §7 | -| 20 | API route handler 无 try-catch 包裹,导致未处理异常直接返回原始 500 且无日志 | Bug(健壮性) | AUTO-FIX | Admin 提现审批 PUT 返回 500 但状态仍更新(异常被吞) | -| 21 | uni-app 页面使用 `onMounted` 获取数据,但该页面是 `uni.navigateBack()` 的返回目标,导致回退时数据不刷新 | Bug(数据陈旧) | AUTO-FIX | 提现后返回收益页余额未刷新(onMounted 不再触发) | -| 22 | Playwright MCP 浏览器崩溃/超时(browser_snapshot/browser_click 等操作返回错误或无响应)| 环境问题 | AUTO-RETRY | 浏览器进程崩溃、MCP 连接超时、页面 unresponsive | -| **99** | **以上都不匹配的任何 FAIL** | **默认: Bug** | **AUTO-FIX** | **兜底规则,确保无遗漏** | -| **V1** | 页面主题/配色与原型不一致(深色→浅色、主色调偏差) | 视觉差异 | VISUAL-FIX | 原型深色主题但实际浅色;按钮颜色不一致 | -| **V2** | 页面布局结构与原型不同(顶部导航↔侧边栏、网格↔列表、卡片↔表格) | 视觉差异 | VISUAL-FIX | 原型顶部水平导航但实际左侧边栏;原型卡片网格但实际列表 | -| **V3** | 品牌标识与原型不同(Logo 图标、标题文字、副标题) | 视觉差异 | VISUAL-FIX | 原型闪电图标+"VividSpark"但实际 "V" 字母+"Welcome back" | -| **V4** | 原型存在的 UI 组件在实际页面中缺失(Remember me、FAQ、过滤器、标签页等) | 视觉差异 | VISUAL-FIX | 原型有 FAQ 区域但实际缺失;原型有 Remember me 但实际无 | -| **V5** | 按钮/输入框/卡片样式与原型不一致(padding、圆角、边框、字号等) | 视觉差异 | VISUAL-FIX | 原型圆角 16px 但实际 8px;原型按钮 padding 16/32 但实际 12/24 | - -### "可接受"严格限定(⛔ 仅以下 2 种,其余一律必须修复) - -1. **系统按设计正确拒绝了无效操作**(如未选协议点登录 → "请先同意用户协议"),且不影响后续步骤 -2. **业务规则正确阻止了当前数据状态下的操作**(如余额不足不可提现),且该规则不应为测试目的修改 - -**除以上 2 种外,任何 FAIL 不得标记为"可接受"。功能缺失、代码错误、交互故障均必须修复。** - -### ⛔ "已知问题不豁免"规则(⛔ 强制 — 跨轮次生效) - -**核心原则**: "已知" ≠ "已修复"。任何在之前的验证轮次中被发现但未实际修复的问题,在后续轮次再次检测到时,**必须**立即触发修复,不得以任何理由跳过。 - -### ⛔ "已修复标记不可信"规则(⛔ 强制 — 交互缺陷审计表专用) - -**核心原则**: 旅程文件中交互缺陷审计表的"✅ 已修复"标记**不等同于实际验证通过**。 - -1. 验证执行时遇到"✅ 已修复"的缺陷,**必须重新验证该修复是否正确**: - - 读取标记对应的源码文件,确认修复代码存在 - - 如果涉及跳转:验证跳转目标是否正确(不只是"有 handler") - - 如果涉及数据:验证数据流是否正确(不只是"不报错") - -2. **禁止行为**: - - ❌ 看到"✅ 已修复"就跳过该交互元素的验证 - - ❌ 以"上次已修复"为由跳过实际 UI 交互测试 - - ❌ 仅检查 handler 是否存在而不验证 handler 的行为是否正确 - -3. **典型案例(V19 漏检教训)**: - - 审计标记:"报名派对按钮 | 无 handler | ✅ 已修复" - - 实际修复:添加了 handleSignUp 函数 - - 但修复错误:用了 `event-detail` 而非 `venue-detail`(venue.id ≠ event.id) - - 验证 agent 信任"已修复"标记 → 漏检错误修复 - -**判定流程**: -``` -检测到 FAIL → 查分类表确定修复类型 - ↓ -读取 pipeline-state.yaml 前一次验证结果 - ↓ -该问题是否在上一轮已被记录(pending_fixes / verification_detail 中存在)? - ├── 是(跨轮次未修复)→ ⛔ 提升优先级,立即执行修复(不得再次"仅记录") - └── 否(新问题)→ 按正常分类触发修复 -``` - -**⛔ 禁止行为**: -- ❌ 以"已知/遗留/历史/V3遗留"为由跳过修复 -- ❌ 只记录到 `pending_fixes` 而不执行实际修复动作 -- ❌ 将修复推迟到"旅程完成后"或"下次验证" -- ❌ 标记"已记录"作为修复动作("已记录"不是修复) -- ❌ 以"非当前 EPIC/旅程范围"为由跳过 pending_fixes 中的问题(范围外 ≠ 豁免) - -**跨轮次问题升级规则**: -- 第 1 轮检测到 → 正常修复流程 -- 第 2 轮仍未修复 → **强制修复**(不得延迟,不得降级为 WARN) -- 第 3+ 轮仍未修复 → **阻断当前旅程**(标记 FAIL + 输出修复失败报告) - -### 关键判断流程 - -- API 404 → `grep` 检查路由文件是否存在 → 存在 = Bug(#2) → 不存在 = 未实现(#1) -- 页面显示"暂未开放"/"开发中"/"暂未实现" → 一律未实现(#7) → AUTO-IMPLEMENT -- 元素可见但不可操作(`browser_type` 失败) → Bug(#6) → ⛔ **先诊断**(`browser_evaluate` 检查 DOM/CSS) → 按诊断结果分流修复(禁止直接用 `browser_run_code` 绕过) -- 页面卡在"加载中..."/空白 → Bug(#5) → AUTO-FIX(调查根因:空 catch、响应结构不匹配、状态管理错误) -- 页面有静态内容但零交互元素(有文字但无 button/input/a/form)→ `browser_evaluate` 执行 FAIL #5 诊断 → 按诊断结果分流(见 FAIL #5 诊断子步骤) -- 操作后页面无变化(无跳转/无反馈)→ browser_snapshot 检查 Toast → 按 Toast 内容分流(#12) → ENV-ADAPT - - 有 Toast/错误提示 → 读取内容 → 按 #12 分流(补充填写 / browser_file_upload / ENV-ADAPT) - - ⛔ 无任何提示 → Bug(UX 缺陷:校验失败但用户看不到原因)→ AUTO-FIX(添加 Toast/错误提示) -- 即时使用数据(验证码)仅通过 Toast 显示且源码无 console.log → Bug(#14) → AUTO-FIX(添加 console.log + 自动填充) -- UI 显示英文枚举值(APPROVED/REJECTED/PENDING/PAID/CANCELLED 等)→ Bug(#15) → AUTO-FIX(添加中文映射函数,适用于所有前端:管理后台 statusLabel / 小程序 computed 映射) -- 前端代码含 `data:image/svg+xml`(SVG data URI)→ `grep -rn "data:image/svg+xml" app/mini-program/src/` 扫描所有 `.vue` 文件 → 发现 → 平台不兼容(#17) → AUTO-IMPLEMENT(⛔ 必须按 platform-uni-app.md §1 完整安装 Iconify + 替换所有 SVG data URI,**禁止用另一种 SVG data URI 替代原来的**) -- Tab 页面下拉刷新方式不一致(scroll-view refresher / onPullDownRefresh / 无 混用)→ Bug(#19) → AUTO-FIX(参见 ../reference/platform-uni-app.md §7 统一为 onPullDownRefresh) -- 无法归类的 FAIL → 默认 Bug(#99) → AUTO-FIX -- API 返回 500 + `Read` 源码发现 handler 无 try-catch → Bug(#20) → AUTO-FIX(添加 try-catch 包裹 + console.error + JSON 500 响应) -- `uni.navigateBack()` 返回后 `browser_snapshot` 数据未刷新 + 源码用 `onMounted` 非 `onShow` → Bug(#21) → AUTO-FIX(替换 `onMounted` 为 `onShow`) - ---- - -## 3. 真实用户完成规则(⛔ 旅程验证模式强制) - -**核心原则**: 旅程中的每个用户步骤,必须是一个**仅使用鼠标/键盘/触摸屏的真实用户**能够完成的方式。 - -**具体规则**: -- `browser_type` 在可见 input 上失败 → Bug(#6) → AUTO-FIX -- `browser_click` 在可见按钮上失败 → Bug(#6) → AUTO-FIX -- 禁止用 `browser_evaluate` / `browser_run_code` 绕过不可交互的元素 -- "需要 evaluate 才能操作" 本身就是 Bug → AUTO-FIX - -**`browser_run_code` 仅允许场景**: -- 检测布局溢出等非用户交互操作 -- 读取 localStorage / cookie 等数据状态验证 -- 复杂的拖拽操作(`browser_drag` 不可用时) - -**⛔ 如果 `browser_type` 在可见 input 上失败**: -1. 标记该步骤 FAIL -2. 分类: Bug(#6 前端交互问题) -3. ⛔ **诊断**: `browser_evaluate` 检查 DOM 结构和计算样式(见下方 FAIL #6 诊断子步骤) -4. 根据诊断结果选择修复策略(CSS Bug / 可见性 Bug / 事件 Bug / 框架编译 Bug) -5. 触发 AUTO-FIX: 修复具体根因 -6. 修复后重试 `browser_type` -7. 仍失败 → 标记 journey-step-fail,继续下一步骤 - -**⛔ FAIL #5 诊断子步骤(页面有内容但零交互元素时强制执行)**: - -**触发条件**: `browser_snapshot` 显示页面有静态内容(标题/文字),但没有 button、input、a、form 等可交互元素。 - -1. `browser_evaluate` 检查页面状态: - ```javascript - () => { - const interactive = document.querySelectorAll('button, input, a, form, [role="button"], [role="link"]'); - const hasContent = document.body.innerText.trim().length > 0; - // 检测框架挂载状态 - const hasNextRoot = !!document.getElementById('__next'); - const hasReactRoot = !!document.querySelector('[data-reactroot]'); - const hasVueApp = !!document.querySelector('[data-v-app]') || !!document.querySelector('#app')?.__vue_app__; - const frameworkMounted = hasNextRoot || hasReactRoot || hasVueApp; - // 检测 JS 是否加载 - const scriptTags = document.querySelectorAll('script[src]').length; - return { - interactiveCount: interactive.length, - hasContent, - frameworkMounted, - hasNextRoot, hasReactRoot, hasVueApp, - scriptTags, - bodyHTMLLength: document.body.innerHTML.length, - bodyText: document.body.innerText.substring(0, 300) - }; - } - ``` - -2. 根据诊断结果分流: - - `interactiveCount === 0 && frameworkMounted === true` → **框架已挂载,当前页面源码就是无交互的展示页** - → `grep/Read` 检查当前页面源码:源码无交互组件 → **功能缺失** → AUTO-IMPLEMENT(添加 redirect/导航逻辑) - → 源码有交互组件但未渲染 → **渲染 Bug(#5)** → AUTO-FIX - - `interactiveCount === 0 && frameworkMounted === false && scriptTags > 0` → **JS 加载但框架未挂载** → **Hydration 失败 Bug(#5)** → AUTO-FIX(检查控制台错误、检查构建产物) - - `interactiveCount === 0 && !hasContent` → **空白页** → Bug(#5) → AUTO-FIX(检查加载逻辑) - -3. 修复后重测: - - AUTO-IMPLEMENT 添加 redirect 后 → `browser_navigate` 重试 → 应自动跳转到功能页 → `browser_snapshot` 确认有交互元素 - - AUTO-FIX 修复 hydration/渲染后 → `browser_navigate` 重试 → `browser_snapshot` 确认交互元素已渲染 - -**⛔ FAIL #6 诊断子步骤(`browser_type`/`browser_click` 失败时强制执行)**: - -> uni-app/小程序编译特性诊断(如 uni-input height:0px)详见 **../reference/platform-uni-app.md 第 3.1 节**。 - -1. `browser_evaluate` 检查目标元素的 DOM 结构和计算样式: - ```javascript - (el) => { - const tag = el.tagName?.toLowerCase(); - const computed = getComputedStyle(el); - const info = { - tag, height: computed.height, width: computed.width, - opacity: computed.opacity, display: computed.display, - innerInputs: [] - }; - el.querySelectorAll('input').forEach(i => { - const c = getComputedStyle(i); - info.innerInputs.push({ - height: c.height, width: c.width, - opacity: c.opacity, pointerEvents: c.pointerEvents - }); - }); - return info; - } - ``` - -2. 根据诊断结果分流: - - `innerInputs[0].height === '0px'` → **框架编译 CSS Bug** → AUTO-FIX: 给 input 或包裹容器添加显式 `height` - - `opacity === '0'` 或 `pointerEvents === 'none'` → **可见性 Bug** → AUTO-FIX: 修复 CSS 可见性 - - 无 `innerInputs`(非框架编译)→ **原生交互 Bug** → AUTO-FIX: 修复事件绑定或 DOM 结构 - - 诊断结果正常但 `browser_type` 仍失败 → **Playwright 适配** → 允许 `browser_run_code` + `force: true`(仅限此场景,且须记录适配原因) - ---- - -## 4. 修复流程 - -### AUTO-FIX(代码 Bug,max 2 次) - -**适用**: API 错误、逻辑错误、配置错误、渲染异常、交互故障、空 catch 块、响应结构不匹配 - -**流程**: -1. 读取失败原因,定位源文件和行号(`grep` / `Read`) -2. ⛔ 如果是 FAIL #6(交互失败)→ 先执行诊断子步骤(见上方),按诊断结果选择修复策略 -3. `Edit` 最小改动修复 -4. `Bash` 重启受影响服务(如有必要) -5. 重测失败步骤 → PASS 继续 / FAIL 重试 - -**示例**: -- 退款 API 无 body 返回 500 → `Edit` 添加 body 解析容错 -- `fetchProfile` 空 catch 吞噬错误 → `Edit` 添加错误日志 + null 处理 -- API 响应结构与前端期望不匹配 → `Edit` 修正数据提取逻辑 -- input 元素不可交互 → `browser_evaluate` 诊断 → 按根因修复(见 FAIL #6 诊断子步骤) -- uni-app input `height: 0px` → `Edit` 给 `.input-field` 添加显式 `height: 44px` -- 管理后台首页缺少 redirect → 源码检查发现 page.tsx 是纯展示 → AUTO-IMPLEMENT: 添加 `redirect("/login")` -- React hydration 失败 → `browser_evaluate` 确认框架未挂载 → AUTO-FIX: 检查构建产物和 `use client` 指令 -- 任何前端页面状态字段显示英文枚举 → `Edit` 添加中文映射函数(管理后台: statusLabel / 小程序: computed 属性)→ 替换直接输出为映射函数调用 - -### AUTO-IMPLEMENT(功能缺失,max 2 次) - -**适用**: 路由缺失、组件未渲染、功能占位符("暂未开放")、后端接口未实现 - -**流程**: -1. 读取 `phase-03-implement.md` 对应方案(如存在) -2. `Write` 新文件 / `Edit` 补充实现 -3. `Bash` 安装依赖(如需)+ 重启服务 -4. 重测失败步骤 → PASS 继续 / FAIL 重试 - -**示例**: -- 个人页无登录入口 → `Edit` 添加登录按钮 -- 手机号登录"暂未开放" → `AUTO-IMPLEMENT` 实现完整手机号+验证码登录流程 -- 后端路由缺失 → `Write` 新 `route.ts` + `handler` -- 图标方案未实施 → 按方案安装 Tailwind + Iconify - -### AUTO-RETRY(Playwright 崩溃专用,max 3 次) - -**适用**: Playwright MCP 浏览器崩溃、超时、无响应(FAIL #22) - -**流程**: -``` -Playwright 操作返回错误/超时: - ↓ -1. 诊断: 确认是 MCP 超时/浏览器崩溃(而非业务逻辑错误) - - 特征: browser_snapshot/browser_click 返回 "timeout" / "disconnected" / 无响应 - - 非特征: 页面显示业务错误(404/500/Toast)→ 走正常 FAIL 分类 - ↓ -2. 恢复: 等待 10 秒 → 重新 browser_navigate 到当前页面 - ↓ -3. 重试: 从崩溃的步骤重新执行(不从头开始) - - 读取 verification-checklist.yaml 找到当前步骤 - - 重试当前步骤(max 3x) - ↓ -4. 重试耗尽: 仍崩溃 → 标记 journey-step-crash + 记录 crash_log - - ⛔ 禁止标记 PARTIAL PASS 跳过 - - 必须记录崩溃的具体错误信息 -``` - -**⛔ 禁止行为**: -- ❌ Playwright 崩溃后标记 PARTIAL PASS 跳过 UI 验证 -- ❌ 以"浏览器环境不稳定"为由跳过交互验证 -- ❌ 不重试直接标记 crash 并继续 -- ❌ 将业务逻辑错误(Toast/400/500)误分类为 Playwright 崩溃 - -**Emoji → Iconify 替换方案(检测到 emoji 充当功能图标时使用 ⛔ 强制)**: - -⛔ **完整安装配置、使用方式、禁止方案列表见 ../reference/platform-uni-app.md 第 1 节。禁止自行发明替代方案。** - -适用条件: icon/btn 元素内含 emoji 字符(如 🔍🔔💬🪪💳📷🏠📱) - -执行步骤: -1. **Read fully and follow: ../reference/platform-uni-app.md 第 1 节**(⛔ 不可跳过 — 含 Admin/Mini-program 两条路径完整安装步骤) -2. **替换源码**: 将所有 emoji 字符替换为 Iconify 类名(如 🔍 → `class="i-lucide-search"`) -3. **Bash** 安装依赖 + 重新构建 -4. **重测** 验证图标渲染正常 - -⛔ **禁止的替代方案**(完整列表见 ../reference/platform-uni-app.md 第 1 节"禁止的替代方案") - -**手机号+验证码登录实现方案(#11 触发时使用)**: - -当旅程验证发现 Mock 登录(非真实用户流程)且手机号登录未实现时,按以下方案 AUTO-IMPLEMENT: - -1. **后端: POST /api/auth/send-code** - - 接收 `{phone}` - - `NODE_ENV=development`: 生成 6 位随机验证码,存 Redis `sms:${phone}` TTL 300s,**直接返回** `{code: "123456"}` — 这是标准开发实践,不是 Mock - - `NODE_ENV=production`: 调用短信服务商 API 发送验证码,返回 `{success: true}` -2. **后端: POST /api/auth/login** — 增强已有接口 - - 接收 `{phone, code}` - - 从 Redis 取出验证码比对 - - 匹配 → 查找/创建用户 → 返回 JWT(复用已有登录逻辑) -3. **前端: 启用 login/index.vue 已有的手机号表单**(HTML 已存在,仅需实现 JS) - - `sendCode()`: 调用 `POST /api/auth/send-code`,开发环境将返回的 code 自动填入输入框 - - `handlePhoneLogin()`: 调用 `POST /api/auth/login {phone, code}`,成功后跳转首页 -4. **清理**: 删除 Mock 登录按钮(`v-if="isDev" class="mock-btn"`)和 `handleMockLogin()` 函数 - -### AUTO-FIX #14: 即时使用数据持久化(max 1 次) - -**适用**: FAIL #14 — 即时使用数据(验证码等)仅通过 Toast 显示,无 console.log,下一步骤无法获取 - -**为什么这是 Bug**: 下一步骤需要的数据(如验证码)仅通过 Toast 传递,3秒后消失。Playwright 无法回溯,流程卡死。 -注意:仅适用于**下一步骤立即需要**的数据。订单号、支付结果等后续页面自然显示的不在此列。 - -**流程**: -1. 读取失败页面源码,定位 `uni.showToast` 传递即时使用数据的位置 -2. 确认数据变量名(如 `res.code`) -3. 在 `uni.showToast` 调用**前**添加 `console.log('[DEV] 验证码:', res.code)` -4. 对验证码场景:添加自动填充 `if (res.code) { code.value = res.code }` -5. Toast 保留作为辅助反馈,不删除 -6. 重测失败步骤 → PASS 继续 / FAIL 标记 journey-step-fail - -**修复模板**: -```javascript -// 修复前: -const msg = res.code ? `验证码已发送:${res.code}(开发环境)` : "验证码已发送"; -uni.showToast({ title: msg, icon: "none" }); - -// 修复后: -console.log('[DEV] 验证码:', res.code); -if (res.code) { code.value = res.code; } -uni.showToast({ title: "验证码已发送", icon: "success" }); -``` - -### AUTO-FIX #18: 双重导航栏修复(max 1 次) - -**适用**: FAIL #18 — 页面在 pages.json 有 `navigationBarTitleText`(非 custom)且页面源码有 `position:fixed` 的自定义 header - -**为什么这是 Bug**: 系统导航栏与自定义头部同时显示,用户看到两层标题(如"首页"+"NEON PULSE"),浪费屏幕空间且视觉混乱。uni-app 小程序项目应统一使用 `navigationStyle: "custom"`。 - -**流程**: -1. `Read` pages.json → 找到该页面的 style 配置 -2. `Edit` pages.json → 将该页面 style 改为 `{ "navigationStyle": "custom" }`(移除或保留 navigationBarTitleText 均可,custom 模式下不显示) -3. `Read` 页面源码 → 找到自定义 header 的 CSS(通常为 `position: fixed; top: 0` 的元素) -4. `Edit` 页面源码 CSS: - - header 添加 `padding-top: env(safe-area-inset-top, 20px)` - - header height 改为 `calc(原高度 + env(safe-area-inset-top, 20px))` - - content margin-top 与新 header height 一致(含 safe-area) -5. 重测 → 截图确认只有一层标题 - -**修复示例**: -```css -/* 修复前(首页 header 与系统导航栏重叠): */ -.header { position: fixed; top: 0; left: 0; right: 0; height: 64px; } -.content { margin-top: 64px; } - -/* 修复后(统一使用自定义导航栏): */ -.header { - position: fixed; top: 0; left: 0; right: 0; - height: calc(64px + env(safe-area-inset-top, 20px)); - padding-top: env(safe-area-inset-top, 20px); -} -.content { - margin-top: calc(64px + env(safe-area-inset-top, 20px)); -} -``` - -```jsonc -// pages.json 修复: -// 修复前: -{ "path": "pages/home/index", "style": { "navigationBarTitleText": "首页" } } -// 修复后: -{ "path": "pages/home/index", "style": { "navigationStyle": "custom" } } -``` - -### AUTO-FIX #19: 下拉刷新方式统一(max 1 次) - -**适用**: FAIL #19 — Tab 页面下拉刷新方式不一致(scroll-view refresher / onPullDownRefresh / 无 混用) - -**⛔ 详细修复步骤见 ../reference/platform-uni-app.md §7** - -**流程**: -1. `Edit` pages.json → globalStyle 添加 `"enablePullDownRefresh": true` -2. 对 LIST_LEVEL 页面(scroll-view refresher): - - 移除 scroll-view 的 `refresher-enabled` / `:refresher-triggered` / `@refresherrefresh` - - 移除 `refreshing` ref 和 `onRefresh` 函数 - - 添加 `onPullDownRefresh` 生命周期(from `@dcloudio/uni-app`) - - 数据加载 `finally` 中调用 `uni.stopPullDownRefresh()` -3. 对 NONE 页面:添加 `onPullDownRefresh` + 对应数据刷新逻辑 + `uni.stopPullDownRefresh()` -4. 重测 → 所有 Tab 页面整页下拉刷新效果一致 - -### AUTO-FIX #20: API route try-catch 包裹(max 1 次) - -**适用**: API endpoint 返回 500,`Read` 源码发现 handler 函数无 try-catch 包裹 - -**为什么这是 Bug**: 未处理异常直接返回原始 500 且无日志,无法诊断根因。常见的二次操作(如审批后创建通知)失败会吞噬前面的成功状态。 - -**检测方法**: API 返回 500 时,`Read` 对应 route.ts 源码,检查 `export async function POST/PUT/DELETE/GET` 内是否有 try-catch - -**修复流程**: -1. `Read` 失败的 route.ts 文件 -2. 确认 handler 无 try-catch 包裹 -3. 在 requireAdmin/鉴权检查之后,将剩余逻辑包裹在 try-catch 中 -4. catch 块:`console.error("Xxx error:", error)` + `NextResponse.json({error: "处理失败"}, {status: 500})` -5. 参考模式:同项目中已有 try-catch 的 route(如 admin auth login) - -**修复模板**: -```typescript -// 修复前: -export async function PUT(req, { params }) { - const result = requireAdmin(req); - if ("error" in result) return result.error; - const body = await req.json(); // ← 无 try-catch,异常直接变 500 - // ... 业务逻辑 ... -} - -// 修复后: -export async function PUT(req, { params }) { - const result = requireAdmin(req); - if ("error" in result) return result.error; - try { - const body = await req.json(); - // ... 业务逻辑不变 ... - } catch (error) { - console.error("Xxx error:", error); - return NextResponse.json({ error: "处理失败" }, { status: 500 }); - } -} -``` - -### AUTO-FIX #21: uni-app 页面 onMounted→onShow 替换(max 1 次) - -**适用**: 旅程中 `uni.navigateBack()` 返回目标页面后,页面数据未刷新 - -**为什么这是 Bug**: `onMounted` 仅在组件首次创建时触发。当用户从子页面 `navigateBack()` 返回时,父页面组件仍在导航栈中未被销毁,`onMounted` 不会再次触发。uni-app 的 `onShow` 在每次页面显示时都会触发,是正确的数据刷新时机。 - -**触发条件**(三条件全部满足): -- 条件 A: 旅程中使用 `uni.navigateBack()` 返回到该页面 -- 条件 B: 返回后 `browser_snapshot` 显示数据未更新(如余额、列表数量未变化) -- 条件 C: `Read` 页面源码确认数据获取在 `onMounted` 而非 `onShow` 中 - -**修复流程**: -1. `Read` 页面 `.vue` 文件 -2. 修改 import:将 `import { ref, onMounted } from "vue"` 中 `onMounted` 移除(若 vue 导入不再需要 onMounted) -3. 添加 `import { onShow } from "@dcloudio/uni-app"`(新 import 行) -4. 将 `onMounted(() => { fetchXxx() })` 替换为 `onShow(() => { fetchXxx() })` -5. 参考模式:项目中已有正确使用 `onShow` 的页面(如 profile/index.vue) - -**修复模板**: -```typescript -// 修复前: -import { ref, onMounted } from "vue"; -// ... -onMounted(() => { fetchEarnings(); }); - -// 修复后: -import { ref } from "vue"; -import { onShow } from "@dcloudio/uni-app"; -// ... -onShow(() => { fetchEarnings(); }); -``` - -### AUTO-FIX/JOURNEY-FIX #16: 约束一致性修复(max 1 次) - -**适用**: FAIL #16 — 旅程描述/UI 文案说"可选"但代码实际必填,或 UI 标记"选填"但接口必填 - -**检测触发条件**(双条件同时满足): -- 条件 1:声明方说"可选"(旅程描述含"可跳过/可选/选填/skip/optional/使用占位图" 或 页面文本含"(选填)/(可选)") -- 条件 2:代码实际"必填"(Toast 含"请上传/请填写/请选择/不能为空" 或 源码有 `if (!field)` 验证) - -**为什么这是问题**: 用户被"可选"承诺误导,但操作时被强制校验拦截。真实用户体验受损。 - -**流程**: -1. `Read` 对应页面源码 → 定位必填验证逻辑(`if (!field)` / `required` / `validate`) -2. 判断正确设计意图: - - PRD/UX 规范说可选 → AUTO-FIX 代码(移除必填验证或改为可选提交) - - PRD/UX 规范说必填 → JOURNEY-FIX 旅程定义(更新描述为必填)+ AUTO-FIX UI 文案(移除"选填"标记) - - 无明确 PRD 依据 → JOURNEY-FIX(旅程定义改为反映代码行为)+ 记录 `constraint_mismatch` 到 pending_fixes -3. 修复后重测该步骤 - -**代码修复示例**(设计意图=可选时): -```javascript -// 修复前(强制必填): -if (!form.idCardFront || !form.idCardBack || !form.idCardHolding) { - uni.showToast({ title: "请上传所有证件照片", icon: "none" }); - return; -} - -// 修复后(改为可选): -// 移除强制校验,允许不上传照片直接提交 -``` - -**旅程修复示例**(设计意图=必填时): -```markdown -// 修复前: -9. 用户第二步上传身份证照片(可跳过或使用占位图)→ 点击"下一步" - -// 修复后: -9. 用户第二步上传身份证照片(必填:身份证正面、反面、手持照片)→ 点击"下一步" -``` - -### AUTO-DATA-FIX(数据状态,max 1 次) - -**适用**: 测试数据过期、冻结期收益不可用、关联数据缺失 - -**流程**: -1. `Bash` 直接操作数据库或 API 修复数据状态 -2. 重测失败步骤 - -**示例**: -- 收益冻结期不可提现 → `Bash`: `psql UPDATE earnings SET status='AVAILABLE'` -- 组局状态不正确 → API: `PUT /api/admin/events/[id]` 修正状态 -- 测试用户数据缺失 → API: `POST` 创建缺失的关联记录 - -### ENV-ADAPT(环境限制,max 1 次) - -**适用**: 操作被前端表单校验拦截(#12),Toast 提示了具体原因 - -**流程**: -1. `browser_snapshot` 读取页面 Toast/错误提示内容 -2. 根据 Toast 内容分流: - - "请输入/请填写/不能为空" → 找到对应字段 → `browser_type` 补充填写 → 重试操作 - - "请上传/请选择图片/请选择文件" → `browser_click` 触发文件选择 → `browser_file_upload` 上传测试文件 → 重试操作 - - "暂未开放/开发中" → 分类为 AUTO-IMPLEMENT → 走 auto-implement 流程 -3. 重试仍失败 → 标记 journey-step-fail + Toast 内容 - -**⛔ 重要:表单校验失败但页面无任何提示 → Bug(#13) → AUTO-FIX(添加 Toast/错误提示)** - -**示例**: -- 局头申请要求上传证件照 → Toast "请上传所有证件照片" → `browser_file_upload` 上传测试图片 → 重试提交 -- 注册表单缺少必填项 → Toast "请输入手机号" → `browser_type` 填写手机号 → 重试提交 -- 表单提交无反应且无 Toast → Bug(#13) → AUTO-FIX: 添加 Toast 提示 - -### 修复失败处理 - -- 重试达到上限仍 FAIL → 标记该步骤 `journey-step-fail` -- 记录: `{journey, step, fail_reason, fix_type, fix_attempted, fix_result}` -- 继续执行下一个步骤(不阻断整条旅程) -- 旅程完成后统计: `steps_passed / steps_total` → 决定整体 PASS/FAIL - ---- - -## 5. GATE-CHECK(⛔ 修复完成后、记录阶段之前执行) - -``` -GATE-CHECK: -├── 所有交互使用真实用户操作(无 browser_run_code 绕过 input/button) ← 真实用户完成规则 -├── browser_take_screenshot 已执行 ← 证据检查 -├── ⛔ analyze_image 已执行(所有截图都有对应 AI 视觉分析结果) ← 视觉验证检查 -│ 验证:pipeline-state 中 analyze_image_executed === true -│ 未执行 → 不得标记完成,必须回退执行视觉验证(见 ./visual-verification.md) -├── browser_network_requests 已执行 ← 网络检查 -├── failed_steps 数量 = 0 ← 断言检查 -├── 即时使用数据有 console.log 或自动填充(无 #14 违规) ← 数据可追溯检查 -├── auto_fix 对所有 FAIL 已触发 ← 修复检查 -├── ⛔ verification_detail 中每个 FAIL 项都有对应的 auto_fix 记录 ← FAIL-修复交叉验证 -│ 逐一比对 verification_detail 与 auto_fix 记录: -│ english_enum_check.found = FOUND → auto_fix 必须包含 #15 修复记录 -│ icons_check.emoji_as_icons = FOUND → auto_fix 必须包含 Iconify 替换修复记录 -│ icons_check.tabbar_icons_visible = FAIL → auto_fix 必须包含 TabBar 修复记录 -│ 缺少对应记录 → 不得标记完成,必须回退执行修复 -└── ⛔ 视觉验证 FAIL 的修复在内联关卡已执行(非推迟到步骤 4) ← 内联修复检查 - -结果判定: -├── 全部 PASS → 继续记录阶段,status: pass -├── FAIL + 修复成功 → 继续记录阶段,status: pass(含修复记录) -└── FAIL + 修复失败/未触发 → status: fail(⛔ 不得记录为 pass) -``` - -### ⛔ 严重违规行为 - -- 检测到 FAIL 但没有执行分类判定 -- 分类判定为 Bug/未实现但没有触发修复 -- 修复失败但没有记录 `auto_fix_result` -- `status: pass` 但 `failed_steps > 0` 且未全部修复成功 -- 用 `browser_evaluate` 绕过不可交互的 input/button 后标记步骤 PASS -- 把"加载中..."/"暂未开放"判定为 PASS -- 任何 FAIL 标记为"可接受"但不符合上述严格限定的 2 种情况 -- **用 SVG data URI / CSS background-image 替代 Iconify 图标方案**(微信小程序不支持 SVG data URI,必须用 @egoist/tailwindcss-icons + Iconify) - ---- - -## 5.5 步骤级状态更新(⛔ AUTO-FIX/AUTO-IMPLEMENT 完成后强制执行) - -**修复完成后,必须同步更新步骤级跟踪文件,确保修复证据完整可追溯。** - -``` -AUTO-FIX/AUTO-IMPLEMENT 执行后: - 1. 更新 verification-checklist.yaml: - - 找到当前 step → status 改为 "fixed"(修复后待重测)或 "pass"(重测通过后) - - 如果重测通过 → status 改为 "pass" - - 2. 更新 pipeline-state.yaml step_results: - - 在当前步骤的 auto_fixes 数组中追加修复记录: - ```yaml - auto_fixes: - - fix_id: "FIX-{NNN}" - issue_id: "ISSUE-{NNN}" # 关联触发修复的问题 - fix_type: "AUTO-FIX|AUTO-IMPLEMENT|AUTO-DATA-FIX" - description: "{修复描述}" - files_changed: [ "{文件路径}" ] - retry_count: {N} - result: "pass|fail" - fixed_at: "{ISO timestamp}" - screenshot_after_fix: "{修复后重测截图路径}" - ``` - - 3. 更新步骤级 GATE: - - 重新检查 G5: all_issues_resolved - - 修复通过 → step_gate 改为 "pass" - - 修复失败且重试耗尽 → step_gate 改为 "fail",步骤标记 failed - - 4. 如重测需要重新截图和 analyze_image: - - browser_take_screenshot → 保存为新截图(原截图保留) - - analyze_image → 更新 analyze_image_result - - 更新 step_results 的 screenshot_path 和 analyze_image_result -``` - -**⛔ 禁止行为**: -- ❌ 修复后不更新 verification-checklist.yaml 的 step status -- ❌ 修复后不追加 auto_fix 记录到 step_results -- ❌ 修复后不重新检查步骤级 GATE -- ❌ 修复失败但 step_gate 仍标记为 "pass" - ---- - -## 6. 各模式引用方式 - -以下文件必须引用本引擎,删除各自的重复定义: - -| 文件 | 引用位置 | -|------|---------| -| `modes/journey/journey-execution.md` | 执行模板步骤 4(修复阶段)→ `Read fully and follow: ./auto-fix-engine.md` | -| `validation-mode.md` | AUTO-FIX/AUTO-IMPLEMENT 分支 → `Read fully and follow: ./auto-fix-engine.md` | -| `phase-04-test.md` | Phase 4.5 AUTO-FIX 循环 → 引用第 4 节修复流程 | -| `phase-05-review.md` | 质量关卡 FAIL 处理 → 引用第 2 节分类标准 | -| `visual-diff-mode.md` | Phase VD-3 逐页修复 → VISUAL-FIX (V1-V5) 分类引用 | -| `e2e-testing.md` | 步骤 9.5 判定规则 → 引用第 2 节分类标准 | -| `iron-laws.md` | 检测即阻断规则 → 引用第 2 节分类标准 + 第 5 节 GATE-CHECK | diff --git a/.claude/skills/d8d-dev/rules/auto-fix/classification.md b/.claude/skills/d8d-dev/rules/auto-fix/classification.md new file mode 100644 index 0000000..33c18f8 --- /dev/null +++ b/.claude/skills/d8d-dev/rules/auto-fix/classification.md @@ -0,0 +1,112 @@ +# FAIL 分类标准 + +> **⛔ 所有模式共享** — 引用自 `./engine.md`。检测到 FAIL 后,必须先按此表分类,再触发对应修复流程(见 `./workflows.md`)。 + +--- + +## 2. FAIL 分类标准(⛔ 所有模式共享) + +| # | FAIL 特征 | 分类 | 修复类型 | 示例 | +|---|-----------|------|---------|------| +| 1 | API 返回 404 且路由文件不存在 | 未实现 | AUTO-IMPLEMENT | 路由缺失 | +| 2 | API 返回 500 / 错误状态码 | Bug | AUTO-FIX | 空 body 返回 500 | +| 3 | 前端组件/按钮未渲染 | 未实现 | AUTO-IMPLEMENT | 登录入口按钮缺失 | +| 4 | 页面跳转到错误位置 | Bug | AUTO-FIX | 导航逻辑错误 | +| 5 | 页面停留在非功能态("加载中"/空白/错误态/有内容但零交互元素) | Bug | AUTO-FIX | hydration 失败 / 渲染 Bug / fetchProfile 空 catch / 响应结构不匹配 | +| 6 | 可交互元素无法通过 Playwright 操作 | Bug | AUTO-FIX | uni-input 编译后 browser_type 失败 | +| 7 | 功能提示"暂未开放"/"开发中"/"暂未实现" | 未实现 | AUTO-IMPLEMENT | 手机号登录只有占位符 | +| 8 | 测试数据过期/状态不对 | 数据问题 | AUTO-DATA-FIX | 收益冻结期不可提现 | +| 9 | 已有数据阻止测试(重复评价等) | 数据问题 | AUTO-DATA-FIX | 删除旧评价或换测试用户 | +| 10 | 正常业务规则正确拒绝无效操作 | 可接受 | 仅记录 | 最低提现金额限制 | +| 11 | 旅程定义或源码中发现 Mock 登录且手机号登录未实现 | 未实现 | AUTO-IMPLEMENT | Mock 登录不是真实用户流程,必须实现手机号+验证码登录 | +| 12 | 操作被前端表单校验拦截(Toast/提示阻止提交)| 环境限制/校验 | ENV-ADAPT | 按 Toast 内容分流:缺少必填→补充填写;文件上传→browser_file_upload;环境限制→代码添加 dev 替代 | +| 13 | 表单校验失败但页面无任何提示(无 Toast/无错误文字)| Bug(UX 缺陷)| AUTO-FIX | 用户不知道为什么操作无效 → 添加 Toast 或内联错误提示 | +| 14 | 即时使用数据(验证码等)仅通过 Toast 显示,无 console.log 或自动填充,下一步骤无法获取 | Bug(数据丢失风险)| AUTO-FIX | sendCode() 用 Toast 显示验证码但无 console.log,流程卡死 | +| 15 | UI 直接输出英文枚举值(如 APPROVED/REJECTED/PENDING/PAID/CANCELLED 等)而非本地化中文标签 | Bug(UI 本地化缺失)| AUTO-FIX | 任何前端页面(管理后台/小程序/H5)状态字段显示英文枚举而非中文 | +| 16 | 约束声明与代码行为不一致(Constraint Mismatch): 旅程描述/UI 文案说"可选/可跳过/选填"但代码实际必填,或 UI 标记"选填"但接口必填 | 约束不一致 | JOURNEY-FIX / AUTO-FIX | 旅程说"可跳过上传证件"但代码 `if (!idCardFront)` Toast "请上传所有证件照片" | +| 17 | 前端代码 CSS/style 含 `url("data:image/svg+xml")` 或 `url('data:image/svg+xml')`(SVG data URI 充当图标/背景/装饰) | Bug(平台不兼容) | AUTO-IMPLEMENT | 微信小程序不支持 SVG data URI。⛔ 必须按 platform-uni-app.md §1 完整安装 Iconify + tailwindcss-icons + weapp-tailwindcss,将所有 SVG data URI 替换为 Iconify 类名。**禁止用另一种 SVG data URI 替代原来的** | +| 18 | 页面同时有系统导航栏(pages.json navigationBarTitleText 非 custom)和自定义 fixed header,导致双层标题栏 | Bug(双重导航栏) | AUTO-FIX | 截图中顶部出现两层标题(系统导航+自定义头部),参见 ../reference/platform-uni-app.md §3 | +| 19 | Tab 页面下拉刷新方式不一致(scroll-view refresher / onPullDownRefresh / 无 混用),或使用列表级 scroll-view refresher | Bug(UX 不一致) | AUTO-FIX | 首页用 scroll-view refresher,其他 Tab 无刷新,参见 ../reference/platform-uni-app.md §7 | +| 20 | API route handler 无 try-catch 包裹,导致未处理异常直接返回原始 500 且无日志 | Bug(健壮性) | AUTO-FIX | Admin 提现审批 PUT 返回 500 但状态仍更新(异常被吞) | +| 21 | uni-app 页面使用 `onMounted` 获取数据,但该页面是 `uni.navigateBack()` 的返回目标,导致回退时数据不刷新 | Bug(数据陈旧) | AUTO-FIX | 提现后返回收益页余额未刷新(onMounted 不再触发) | +| 22 | Playwright MCP 浏览器崩溃/超时(browser_snapshot/browser_click 等操作返回错误或无响应)| 环境问题 | AUTO-RETRY | 浏览器进程崩溃、MCP 连接超时、页面 unresponsive | +| 23 | uni-app 原生组件在 H5 环境下不渲染/不可交互(``/``/``/``/`` 等)| Bug(平台不兼容) | AUTO-FIX | H5 环境下 `` 不渲染,`` 无法点击。⛔ 必须替换为纯 view + CSS 自定义组件,参见 ../reference/platform-uni-app.md §8 | +| **99** | **以上都不匹配的任何 FAIL** | **默认: Bug** | **AUTO-FIX** | **兜底规则,确保无遗漏** | +| **V1** | 页面主题/配色与原型不一致(深色→浅色、主色调偏差) | 视觉差异 | VISUAL-FIX | 原型深色主题但实际浅色;按钮颜色不一致 | +| **V2** | 页面布局结构与原型不同(顶部导航↔侧边栏、网格↔列表、卡片↔表格) | 视觉差异 | VISUAL-FIX | 原型顶部水平导航但实际左侧边栏;原型卡片网格但实际列表 | +| **V3** | 品牌标识与原型不同(Logo 图标、标题文字、副标题) | 视觉差异 | VISUAL-FIX | 原型闪电图标+"VividSpark"但实际 "V" 字母+"Welcome back" | +| **V4** | 原型存在的 UI 组件在实际页面中缺失(Remember me、FAQ、过滤器、标签页等) | 视觉差异 | VISUAL-FIX | 原型有 FAQ 区域但实际缺失;原型有 Remember me 但实际无 | +| **V5** | 按钮/输入框/卡片样式与原型不一致(padding、圆角、边框、字号等) | 视觉差异 | VISUAL-FIX | 原型圆角 16px 但实际 8px;原型按钮 padding 16/32 但实际 12/24 | + +### "可接受"严格限定(⛔ 仅以下 2 种,其余一律必须修复) + +1. **系统按设计正确拒绝了无效操作**(如未选协议点登录 → "请先同意用户协议"),且不影响后续步骤 +2. **业务规则正确阻止了当前数据状态下的操作**(如余额不足不可提现),且该规则不应为测试目的修改 + +**除以上 2 种外,任何 FAIL 不得标记为"可接受"。功能缺失、代码错误、交互故障均必须修复。** + +### ⛔ "已知问题不豁免"规则(⛔ 强制 — 跨轮次生效) + +**核心原则**: "已知" ≠ "已修复"。任何在之前的验证轮次中被发现但未实际修复的问题,在后续轮次再次检测到时,**必须**立即触发修复,不得以任何理由跳过。 + +### ⛔ "已修复标记不可信"规则(⛔ 强制 — 交互缺陷审计表专用) + +**核心原则**: 旅程文件中交互缺陷审计表的"✅ 已修复"标记**不等同于实际验证通过**。 + +1. 验证执行时遇到"✅ 已修复"的缺陷,**必须重新验证该修复是否正确**: + - 读取标记对应的源码文件,确认修复代码存在 + - 如果涉及跳转:验证跳转目标是否正确(不只是"有 handler") + - 如果涉及数据:验证数据流是否正确(不只是"不报错") + +2. **禁止行为**: + - ❌ 看到"✅ 已修复"就跳过该交互元素的验证 + - ❌ 以"上次已修复"为由跳过实际 UI 交互测试 + - ❌ 仅检查 handler 是否存在而不验证 handler 的行为是否正确 + +3. **典型案例(V19 漏检教训)**: + - 审计标记:"报名派对按钮 | 无 handler | ✅ 已修复" + - 实际修复:添加了 handleSignUp 函数 + - 但修复错误:用了 `event-detail` 而非 `venue-detail`(venue.id ≠ event.id) + - 验证 agent 信任"已修复"标记 → 漏检错误修复 + +**判定流程**: +``` +检测到 FAIL → 查分类表确定修复类型 + ↓ +读取 pipeline-state.yaml 前一次验证结果 + ↓ +该问题是否在上一轮已被记录(pending_fixes / verification_detail 中存在)? + ├── 是(跨轮次未修复)→ ⛔ 提升优先级,立即执行修复(不得再次"仅记录") + └── 否(新问题)→ 按正常分类触发修复 +``` + +**⛔ 禁止行为**: +- ❌ 以"已知/遗留/历史/V3遗留"为由跳过修复 +- ❌ 只记录到 `pending_fixes` 而不执行实际修复动作 +- ❌ 将修复推迟到"旅程完成后"或"下次验证" +- ❌ 标记"已记录"作为修复动作("已记录"不是修复) +- ❌ 以"非当前 EPIC/旅程范围"为由跳过 pending_fixes 中的问题(范围外 ≠ 豁免) + +**跨轮次问题升级规则**: +- 第 1 轮检测到 → 正常修复流程 +- 第 2 轮仍未修复 → **强制修复**(不得延迟,不得降级为 WARN) +- 第 3+ 轮仍未修复 → **阻断当前旅程**(标记 FAIL + 输出修复失败报告) + +### 关键判断流程 + +- API 404 → `grep` 检查路由文件是否存在 → 存在 = Bug(#2) → 不存在 = 未实现(#1) +- 页面显示"暂未开放"/"开发中"/"暂未实现" → 一律未实现(#7) → AUTO-IMPLEMENT +- 元素可见但不可操作(`browser_type` 失败) → Bug(#6) → ⛔ **先诊断**(`browser_evaluate` 检查 DOM/CSS) → 按诊断结果分流修复(禁止直接用 `browser_run_code` 绕过) +- 页面卡在"加载中..."/空白 → Bug(#5) → AUTO-FIX(调查根因:空 catch、响应结构不匹配、状态管理错误) +- 页面有静态内容但零交互元素(有文字但无 button/input/a/form)→ `browser_evaluate` 执行 FAIL #5 诊断 → 按诊断结果分流(见 diagnostics.md FAIL #5 诊断子步骤) +- 操作后页面无变化(无跳转/无反馈)→ browser_snapshot 检查 Toast → 按 Toast 内容分流(#12) → ENV-ADAPT + - 有 Toast/错误提示 → 读取内容 → 按 #12 分流(补充填写 / browser_file_upload / ENV-ADAPT) + - ⛔ 无任何提示 → Bug(UX 缺陷:校验失败但用户看不到原因)→ AUTO-FIX(添加 Toast/错误提示) +- 即时使用数据(验证码)仅通过 Toast 显示且源码无 console.log → Bug(#14) → AUTO-FIX(添加 console.log + 自动填充) +- UI 显示英文枚举值(APPROVED/REJECTED/PENDING/PAID/CANCELLED 等)→ Bug(#15) → AUTO-FIX(添加中文映射函数,适用于所有前端:管理后台 statusLabel / 小程序 computed 映射) +- 前端代码含 `data:image/svg+xml`(SVG data URI)→ `grep -rn "data:image/svg+xml" app/mini-program/src/` 扫描所有 `.vue` 文件 → 发现 → 平台不兼容(#17) → AUTO-IMPLEMENT(⛔ 必须按 platform-uni-app.md §1 完整安装 Iconify + 替换所有 SVG data URI,**禁止用另一种 SVG data URI 替代原来的**) +- Tab 页面下拉刷新方式不一致(scroll-view refresher / onPullDownRefresh / 无 混用)→ Bug(#19) → AUTO-FIX(参见 ../reference/platform-uni-app.md §7 统一为 onPullDownRefresh) +- uni-app 原生组件 H5 不渲染 → `browser_snapshot` 显示目标区域空白/缺失,`Read` 源码确认使用了 ``/``/``/``/`` 等 uni-app 原生组件 → Bug(#23) → AUTO-FIX(⛔ 替换为纯 view + CSS 自定义组件,参见 ../reference/platform-uni-app.md §8。**禁止标记为"环境限制"跳过修复**) +- 无法归类的 FAIL → 默认 Bug(#99) → AUTO-FIX +- API 返回 500 + `Read` 源码发现 handler 无 try-catch → Bug(#20) → AUTO-FIX(添加 try-catch 包裹 + console.error + JSON 500 响应) +- `uni.navigateBack()` 返回后 `browser_snapshot` 数据未刷新 + 源码用 `onMounted` 非 `onShow` → Bug(#21) → AUTO-FIX(替换 `onMounted` 为 `onShow`) diff --git a/.claude/skills/d8d-dev/rules/auto-fix/diagnostics.md b/.claude/skills/d8d-dev/rules/auto-fix/diagnostics.md new file mode 100644 index 0000000..a190b08 --- /dev/null +++ b/.claude/skills/d8d-dev/rules/auto-fix/diagnostics.md @@ -0,0 +1,99 @@ +# 诊断流程 + +> **⛔ 所有模式共享** — 引用自 `./engine.md`。当步骤 FAIL 需要深入诊断时,按此流程执行。 + +--- + +## 3. 真实用户完成规则(⛔ 旅程验证模式强制) + +**核心原则**: 旅程中的每个用户步骤,必须是一个**仅使用鼠标/键盘/触摸屏的真实用户**能够完成的方式。 + +**具体规则**: +- `browser_type` 在可见 input 上失败 → Bug(#6) → AUTO-FIX +- `browser_click` 在可见按钮上失败 → Bug(#6) → AUTO-FIX +- 禁止用 `browser_evaluate` / `browser_run_code` 绕过不可交互的元素 +- "需要 evaluate 才能操作" 本身就是 Bug → AUTO-FIX + +**`browser_run_code` 仅允许场景**: +- 检测布局溢出等非用户交互操作 +- 读取 localStorage / cookie 等数据状态验证 +- 复杂的拖拽操作(`browser_drag` 不可用时) + +**⛔ 如果 `browser_type` 在可见 input 上失败**: +1. 标记该步骤 FAIL +2. 分类: Bug(#6 前端交互问题) +3. ⛔ **诊断**: `browser_evaluate` 检查 DOM 结构和计算样式(见下方 FAIL #6 诊断子步骤) +4. 根据诊断结果选择修复策略(CSS Bug / 可见性 Bug / 事件 Bug / 框架编译 Bug) +5. 触发 AUTO-FIX: 修复具体根因 +6. 修复后重试 `browser_type` +7. 仍失败 → 标记 journey-step-fail,继续下一步骤 + +**⛔ FAIL #5 诊断子步骤(页面有内容但零交互元素时强制执行)**: + +**触发条件**: `browser_snapshot` 显示页面有静态内容(标题/文字),但没有 button、input、a、form 等可交互元素。 + +1. `browser_evaluate` 检查页面状态: + ```javascript + () => { + const interactive = document.querySelectorAll('button, input, a, form, [role="button"], [role="link"]'); + const hasContent = document.body.innerText.trim().length > 0; + // 检测框架挂载状态 + const hasNextRoot = !!document.getElementById('__next'); + const hasReactRoot = !!document.querySelector('[data-reactroot]'); + const hasVueApp = !!document.querySelector('[data-v-app]') || !!document.querySelector('#app')?.__vue_app__; + const frameworkMounted = hasNextRoot || hasReactRoot || hasVueApp; + // 检测 JS 是否加载 + const scriptTags = document.querySelectorAll('script[src]').length; + return { + interactiveCount: interactive.length, + hasContent, + frameworkMounted, + hasNextRoot, hasReactRoot, hasVueApp, + scriptTags, + bodyHTMLLength: document.body.innerHTML.length, + bodyText: document.body.innerText.substring(0, 300) + }; + } + ``` + +2. 根据诊断结果分流: + - `interactiveCount === 0 && frameworkMounted === true` → **框架已挂载,当前页面源码就是无交互的展示页** + → `grep/Read` 检查当前页面源码:源码无交互组件 → **功能缺失** → AUTO-IMPLEMENT(添加 redirect/导航逻辑) + → 源码有交互组件但未渲染 → **渲染 Bug(#5)** → AUTO-FIX + - `interactiveCount === 0 && frameworkMounted === false && scriptTags > 0` → **JS 加载但框架未挂载** → **Hydration 失败 Bug(#5)** → AUTO-FIX(检查控制台错误、检查构建产物) + - `interactiveCount === 0 && !hasContent` → **空白页** → Bug(#5) → AUTO-FIX(检查加载逻辑) + +3. 修复后重测: + - AUTO-IMPLEMENT 添加 redirect 后 → `browser_navigate` 重试 → 应自动跳转到功能页 → `browser_snapshot` 确认有交互元素 + - AUTO-FIX 修复 hydration/渲染后 → `browser_navigate` 重试 → `browser_snapshot` 确认交互元素已渲染 + +**⛔ FAIL #6 诊断子步骤(`browser_type`/`browser_click` 失败时强制执行)**: + +> uni-app/小程序编译特性诊断(如 uni-input height:0px)详见 **../reference/platform-uni-app.md 第 3.1 节**。 + +1. `browser_evaluate` 检查目标元素的 DOM 结构和计算样式: + ```javascript + (el) => { + const tag = el.tagName?.toLowerCase(); + const computed = getComputedStyle(el); + const info = { + tag, height: computed.height, width: computed.width, + opacity: computed.opacity, display: computed.display, + innerInputs: [] + }; + el.querySelectorAll('input').forEach(i => { + const c = getComputedStyle(i); + info.innerInputs.push({ + height: c.height, width: c.width, + opacity: c.opacity, pointerEvents: c.pointerEvents + }); + }); + return info; + } + ``` + +2. 根据诊断结果分流: + - `innerInputs[0].height === '0px'` → **框架编译 CSS Bug** → AUTO-FIX: 给 input 或包裹容器添加显式 `height` + - `opacity === '0'` 或 `pointerEvents === 'none'` → **可见性 Bug** → AUTO-FIX: 修复 CSS 可见性 + - 无 `innerInputs`(非框架编译)→ **原生交互 Bug** → AUTO-FIX: 修复事件绑定或 DOM 结构 + - 诊断结果正常但 `browser_type` 仍失败 → **Playwright 适配** → 允许 `browser_run_code` + `force: true`(仅限此场景,且须记录适配原因) diff --git a/.claude/skills/d8d-dev/rules/auto-fix/engine.md b/.claude/skills/d8d-dev/rules/auto-fix/engine.md new file mode 100644 index 0000000..1e99b21 --- /dev/null +++ b/.claude/skills/d8d-dev/rules/auto-fix/engine.md @@ -0,0 +1,142 @@ +# 统一修复引擎(Auto-Fix Engine) + +> **⛔ 所有模式共享**:完整模式、回归验证模式、旅程验证模式均引用此文件。任何模式不得另起炉灶重新定义修复机制。 + +--- + +## 1. 步骤完成判定标准(⛔ 每个 step 执行后强制判定) + +**PASS**: `browser_snapshot` 显示的内容 **完全包含** 旅程/验证定义中该步骤"用户看到 X"的预期状态。 +- 例: 预期"看到已登录状态(头像、昵称)" → snapshot 中有实际昵称文字(非占位符、非"加载中...") → PASS + +**FAIL**: 以下任一 = FAIL +1. snapshot 内容与预期不符(如预期"已登录"但看到"加载中...") +2. `browser_click` / `browser_type` 无法找到目标元素(元素不存在或不可交互) +3. `browser_wait_for` 超时(页面未按预期响应) +4. snapshot 显示错误状态(错误提示、空白页、404、500) +5. 数据断言不通过(操作前后数据状态无变化或变化不符预期) + +**⛔ 禁止判定为 PASS 的情况**: +- `"加载中..."` / `"请稍候"` / 空白页 +- 元素存在但内容为空 / 占位符 +- `"暂未开放"` / `"开发中"` / `"功能开发中"` 提示 +- 不做 snapshot 直接假设操作成功 +- 用 `browser_evaluate` 绕过不可交互的元素后声称"操作完成" + +--- + +## 2. FAIL 分类标准 + +> ⛔ **Read fully and follow: ./classification.md** — 包含 23 种 FAIL 分类(#1-#23, #99, V1-V5)、"可接受"严格限定、"已知问题不豁免"规则、关键判断流程。 + +--- + +## 3. 真实用户完成规则与诊断流程 + +> ⛔ **Read fully and follow: ./diagnostics.md** — 包含真实用户完成规则、FAIL #5 诊断子步骤(页面有内容但零交互)、FAIL #6 诊断子步骤(browser_type/click 失败)。 + +--- + +## 4. 修复流程 + +> ⛔ **Read fully and follow: ./workflows.md** — 包含 AUTO-FIX、AUTO-IMPLEMENT、AUTO-RETRY、AUTO-DATA-FIX、ENV-ADAPT 全部修复模板。 + +--- + +## 5. GATE-CHECK(⛔ 修复完成后、记录阶段之前执行) + +``` +GATE-CHECK: +├── 所有交互使用真实用户操作(无 browser_run_code 绕过 input/button) ← 真实用户完成规则 +├── browser_take_screenshot 已执行 ← 证据检查 +├── ⛔ analyze_image 已执行(所有截图都有对应 AI 视觉分析结果) ← 视觉验证检查 +│ 验证:pipeline-state 中 analyze_image_executed === true +│ 未执行 → 不得标记完成,必须回退执行视觉验证(见 ./visual-verification.md) +├── browser_network_requests 已执行 ← 网络检查 +├── failed_steps 数量 = 0 ← 断言检查 +├── 即时使用数据有 console.log 或自动填充(无 #14 违规) ← 数据可追溯检查 +├── auto_fix 对所有 FAIL 已触发 ← 修复检查 +├── ⛔ verification_detail 中每个 FAIL 项都有对应的 auto_fix 记录 ← FAIL-修复交叉验证 +│ 逐一比对 verification_detail 与 auto_fix 记录: +│ english_enum_check.found = FOUND → auto_fix 必须包含 #15 修复记录 +│ icons_check.emoji_as_icons = FOUND → auto_fix 必须包含 Iconify 替换修复记录 +│ icons_check.tabbar_icons_visible = FAIL → auto_fix 必须包含 TabBar 修复记录 +│ 缺少对应记录 → 不得标记完成,必须回退执行修复 +└── ⛔ 视觉验证 FAIL 的修复在内联关卡已执行(非推迟到步骤 4) ← 内联修复检查 + +结果判定: +├── 全部 PASS → 继续记录阶段,status: pass +├── FAIL + 修复成功 → 继续记录阶段,status: pass(含修复记录) +└── FAIL + 修复失败/未触发 → status: fail(⛔ 不得记录为 pass) +``` + +### ⛔ 严重违规行为 + +- 检测到 FAIL 但没有执行分类判定 +- 分类判定为 Bug/未实现但没有触发修复 +- 修复失败但没有记录 `auto_fix_result` +- `status: pass` 但 `failed_steps > 0` 且未全部修复成功 +- 用 `browser_evaluate` 绕过不可交互的 input/button 后标记步骤 PASS +- 把"加载中..."/"暂未开放"判定为 PASS +- 任何 FAIL 标记为"可接受"但不符合上述严格限定的 2 种情况 +- **用 SVG data URI / CSS background-image 替代 Iconify 图标方案**(微信小程序不支持 SVG data URI,必须用 @egoist/tailwindcss-icons + Iconify) + +--- + +## 5.5 步骤级状态更新(⛔ AUTO-FIX/AUTO-IMPLEMENT 完成后强制执行) + +**修复完成后,必须同步更新步骤级跟踪文件,确保修复证据完整可追溯。** + +``` +AUTO-FIX/AUTO-IMPLEMENT 执行后: + 1. 更新 verification-checklist.yaml: + - 找到当前 step → status 改为 "fixed"(修复后待重测)或 "pass"(重测通过后) + - 如果重测通过 → status 改为 "pass" + + 2. 更新 pipeline-state.yaml step_results: + - 在当前步骤的 auto_fixes 数组中追加修复记录: + ```yaml + auto_fixes: + - fix_id: "FIX-{NNN}" + issue_id: "ISSUE-{NNN}" # 关联触发修复的问题 + fix_type: "AUTO-FIX|AUTO-IMPLEMENT|AUTO-DATA-FIX" + description: "{修复描述}" + files_changed: [ "{文件路径}" ] + retry_count: {N} + result: "pass|fail" + fixed_at: "{ISO timestamp}" + screenshot_after_fix: "{修复后重测截图路径}" + ``` + + 3. 更新步骤级 GATE: + - 重新检查 G5: all_issues_resolved + - 修复通过 → step_gate 改为 "pass" + - 修复失败且重试耗尽 → step_gate 改为 "fail",步骤标记 failed + + 4. 如重测需要重新截图和 analyze_image: + - browser_take_screenshot → 保存为新截图(原截图保留) + - analyze_image → 更新 analyze_image_result + - 更新 step_results 的 screenshot_path 和 analyze_image_result +``` + +**⛔ 禁止行为**: +- ❌ 修复后不更新 verification-checklist.yaml 的 step status +- ❌ 修复后不追加 auto_fix 记录到 step_results +- ❌ 修复后不重新检查步骤级 GATE +- ❌ 修复失败但 step_gate 仍标记为 "pass" + +--- + +## 6. 各模式引用方式 + +以下文件必须引用本引擎,删除各自的重复定义: + +| 文件 | 引用位置 | +|------|---------| +| `modes/journey/journey-execution.md` | 执行模板步骤 4(修复阶段)→ `Read fully and follow: ../../rules/auto-fix/engine.md` | +| `validation-mode.md` | AUTO-FIX/AUTO-IMPLEMENT 分支 → `Read fully and follow: ../rules/auto-fix/engine.md` | +| `phase-04-test.md` | Phase 4.5 AUTO-FIX 循环 → 引用 classification.md 第 2 节分类标准 | +| `phase-05-review.md` | 质量关卡 FAIL 处理 → 引用 classification.md 第 2 节分类标准 | +| `visual-diff-mode.md` | Phase VD-3 逐页修复 → VISUAL-FIX (V1-V5) 分类引用 classification.md | +| `e2e-testing.md` | 步骤 9.5 判定规则 → 引用 classification.md 第 2 节分类标准 | +| `iron-laws.md` | 检测即阻断规则 → 引用 classification.md 第 2 节分类标准 + engine.md 第 5 节 GATE-CHECK | diff --git a/.claude/skills/d8d-dev/rules/auto-fix/workflows.md b/.claude/skills/d8d-dev/rules/auto-fix/workflows.md new file mode 100644 index 0000000..adc6959 --- /dev/null +++ b/.claude/skills/d8d-dev/rules/auto-fix/workflows.md @@ -0,0 +1,437 @@ +# 修复流程模板 + +> **⛔ 所有模式共享** — 引用自 `./engine.md`。修复前必须先完成 `./classification.md` 中的分类判定。 + +--- + +## 4. 修复流程 + +### AUTO-FIX(代码 Bug,max 2 次) + +**适用**: API 错误、逻辑错误、配置错误、渲染异常、交互故障、空 catch 块、响应结构不匹配 + +**流程**: +1. 读取失败原因,定位源文件和行号(`grep` / `Read`) +2. ⛔ 如果是 FAIL #6(交互失败)→ 先执行诊断子步骤(见 `./diagnostics.md`),按诊断结果选择修复策略 +3. `Edit` 最小改动修复 +4. `Bash` 重启受影响服务(如有必要) +5. 重测失败步骤 → PASS 继续 / FAIL 重试 + +**示例**: +- 退款 API 无 body 返回 500 → `Edit` 添加 body 解析容错 +- `fetchProfile` 空 catch 吞噬错误 → `Edit` 添加错误日志 + null 处理 +- API 响应结构与前端期望不匹配 → `Edit` 修正数据提取逻辑 +- input 元素不可交互 → `browser_evaluate` 诊断 → 按根因修复(见 diagnostics.md FAIL #6 诊断子步骤) +- uni-app input `height: 0px` → `Edit` 给 `.input-field` 添加显式 `height: 44px` +- 管理后台首页缺少 redirect → 源码检查发现 page.tsx 是纯展示 → AUTO-IMPLEMENT: 添加 `redirect("/login")` +- React hydration 失败 → `browser_evaluate` 确认框架未挂载 → AUTO-FIX: 检查构建产物和 `use client` 指令 +- 任何前端页面状态字段显示英文枚举 → `Edit` 添加中文映射函数(管理后台: statusLabel / 小程序: computed 属性)→ 替换直接输出为映射函数调用 + +### AUTO-IMPLEMENT(功能缺失,max 2 次) + +**适用**: 路由缺失、组件未渲染、功能占位符("暂未开放")、后端接口未实现 + +**流程**: +1. 读取 `phase-03-implement.md` 对应方案(如存在) +2. `Write` 新文件 / `Edit` 补充实现 +3. `Bash` 安装依赖(如需)+ 重启服务 +4. 重测失败步骤 → PASS 继续 / FAIL 重试 + +**示例**: +- 个人页无登录入口 → `Edit` 添加登录按钮 +- 手机号登录"暂未开放" → `AUTO-IMPLEMENT` 实现完整手机号+验证码登录流程 +- 后端路由缺失 → `Write` 新 `route.ts` + `handler` +- 图标方案未实施 → 按方案安装 Tailwind + Iconify + +### AUTO-RETRY(Playwright 崩溃专用,max 3 次) + +**适用**: Playwright MCP 浏览器崩溃、超时、无响应(FAIL #22) + +**流程**: +``` +Playwright 操作返回错误/超时: + ↓ +1. 诊断: 确认是 MCP 超时/浏览器崩溃(而非业务逻辑错误) + - 特征: browser_snapshot/browser_click 返回 "timeout" / "disconnected" / 无响应 + - 非特征: 页面显示业务错误(404/500/Toast)→ 走正常 FAIL 分类 + ↓ +2. 恢复: 等待 10 秒 → 重新 browser_navigate 到当前页面 + ↓ +3. 重试: 从崩溃的步骤重新执行(不从头开始) + - 读取 verification-checklist.yaml 找到当前步骤 + - 重试当前步骤(max 3x) + ↓ +4. 重试耗尽: 仍崩溃 → 标记 journey-step-crash + 记录 crash_log + - ⛔ 禁止标记 PARTIAL PASS 跳过 + - 必须记录崩溃的具体错误信息 +``` + +**⛔ 禁止行为**: +- ❌ Playwright 崩溃后标记 PARTIAL PASS 跳过 UI 验证 +- ❌ 以"浏览器环境不稳定"为由跳过交互验证 +- ❌ 不重试直接标记 crash 并继续 +- ❌ 将业务逻辑错误(Toast/400/500)误分类为 Playwright 崩溃 + +**Emoji → Iconify 替换方案(检测到 emoji 充当功能图标时使用 ⛔ 强制)**: + +⛔ **完整安装配置、使用方式、禁止方案列表见 ../reference/platform-uni-app.md 第 1 节。禁止自行发明替代方案。** + +适用条件: icon/btn 元素内含 emoji 字符(如 🔍🔔💬🪪💳📷🏠📱) + +执行步骤: +1. **Read fully and follow: ../reference/platform-uni-app.md 第 1 节**(⛔ 不可跳过 — 含 Admin/Mini-program 两条路径完整安装步骤) +2. **替换源码**: 将所有 emoji 字符替换为 Iconify 类名(如 🔍 → `class="i-lucide-search"`) +3. **Bash** 安装依赖 + 重新构建 +4. **重测** 验证图标渲染正常 + +⛔ **禁止的替代方案**(完整列表见 ../reference/platform-uni-app.md 第 1 节"禁止的替代方案") + +**手机号+验证码登录实现方案(#11 触发时使用)**: + +当旅程验证发现 Mock 登录(非真实用户流程)且手机号登录未实现时,按以下方案 AUTO-IMPLEMENT: + +1. **后端: POST /api/auth/send-code** + - 接收 `{phone}` + - `NODE_ENV=development`: 生成 6 位随机验证码,存 Redis `sms:${phone}` TTL 300s,**直接返回** `{code: "123456"}` — 这是标准开发实践,不是 Mock + - `NODE_ENV=production`: 调用短信服务商 API 发送验证码,返回 `{success: true}` +2. **后端: POST /api/auth/login** — 增强已有接口 + - 接收 `{phone, code}` + - 从 Redis 取出验证码比对 + - 匹配 → 查找/创建用户 → 返回 JWT(复用已有登录逻辑) +3. **前端: 启用 login/index.vue 已有的手机号表单**(HTML 已存在,仅需实现 JS) + - `sendCode()`: 调用 `POST /api/auth/send-code`,开发环境将返回的 code 自动填入输入框 + - `handlePhoneLogin()`: 调用 `POST /api/auth/login {phone, code}`,成功后跳转首页 +4. **清理**: 删除 Mock 登录按钮(`v-if="isDev" class="mock-btn"`)和 `handleMockLogin()` 函数 + +### AUTO-FIX #14: 即时使用数据持久化(max 1 次) + +**适用**: FAIL #14 — 即时使用数据(验证码等)仅通过 Toast 显示,无 console.log,下一步骤无法获取 + +**为什么这是 Bug**: 下一步骤需要的数据(如验证码)仅通过 Toast 传递,3秒后消失。Playwright 无法回溯,流程卡死。 +注意:仅适用于**下一步骤立即需要**的数据。订单号、支付结果等后续页面自然显示的不在此列。 + +**流程**: +1. 读取失败页面源码,定位 `uni.showToast` 传递即时使用数据的位置 +2. 确认数据变量名(如 `res.code`) +3. 在 `uni.showToast` 调用**前**添加 `console.log('[DEV] 验证码:', res.code)` +4. 对验证码场景:添加自动填充 `if (res.code) { code.value = res.code }` +5. Toast 保留作为辅助反馈,不删除 +6. 重测失败步骤 → PASS 继续 / FAIL 标记 journey-step-fail + +**修复模板**: +```javascript +// 修复前: +const msg = res.code ? `验证码已发送:${res.code}(开发环境)` : "验证码已发送"; +uni.showToast({ title: msg, icon: "none" }); + +// 修复后: +console.log('[DEV] 验证码:', res.code); +if (res.code) { code.value = res.code; } +uni.showToast({ title: "验证码已发送", icon: "success" }); +``` + +### AUTO-FIX #18: 双重导航栏修复(max 1 次) + +**适用**: FAIL #18 — 页面在 pages.json 有 `navigationBarTitleText`(非 custom)且页面源码有 `position:fixed` 的自定义 header + +**为什么这是 Bug**: 系统导航栏与自定义头部同时显示,用户看到两层标题(如"首页"+"NEON PULSE"),浪费屏幕空间且视觉混乱。uni-app 小程序项目应统一使用 `navigationStyle: "custom"`。 + +**流程**: +1. `Read` pages.json → 找到该页面的 style 配置 +2. `Edit` pages.json → 将该页面 style 改为 `{ "navigationStyle": "custom" }`(移除或保留 navigationBarTitleText 均可,custom 模式下不显示) +3. `Read` 页面源码 → 找到自定义 header 的 CSS(通常为 `position: fixed; top: 0` 的元素) +4. `Edit` 页面源码 CSS: + - header 添加 `padding-top: env(safe-area-inset-top, 20px)` + - header height 改为 `calc(原高度 + env(safe-area-inset-top, 20px))` + - content margin-top 与新 header height 一致(含 safe-area) +5. 重测 → 截图确认只有一层标题 + +**修复示例**: +```css +/* 修复前(首页 header 与系统导航栏重叠): */ +.header { position: fixed; top: 0; left: 0; right: 0; height: 64px; } +.content { margin-top: 64px; } + +/* 修复后(统一使用自定义导航栏): */ +.header { + position: fixed; top: 0; left: 0; right: 0; + height: calc(64px + env(safe-area-inset-top, 20px)); + padding-top: env(safe-area-inset-top, 20px); +} +.content { + margin-top: calc(64px + env(safe-area-inset-top, 20px)); +} +``` + +```jsonc +// pages.json 修复: +// 修复前: +{ "path": "pages/home/index", "style": { "navigationBarTitleText": "首页" } } +// 修复后: +{ "path": "pages/home/index", "style": { "navigationStyle": "custom" } } +``` + +### AUTO-FIX #19: 下拉刷新方式统一(max 1 次) + +**适用**: FAIL #19 — Tab 页面下拉刷新方式不一致(scroll-view refresher / onPullDownRefresh / 无 混用) + +**⛔ 详细修复步骤见 ../reference/platform-uni-app.md §7** + +**流程**: +1. `Edit` pages.json → globalStyle 添加 `"enablePullDownRefresh": true` +2. 对 LIST_LEVEL 页面(scroll-view refresher): + - 移除 scroll-view 的 `refresher-enabled` / `:refresher-triggered` / `@refresherrefresh` + - 移除 `refreshing` ref 和 `onRefresh` 函数 + - 添加 `onPullDownRefresh` 生命周期(from `@dcloudio/uni-app`) + - 数据加载 `finally` 中调用 `uni.stopPullDownRefresh()` +3. 对 NONE 页面:添加 `onPullDownRefresh` + 对应数据刷新逻辑 + `uni.stopPullDownRefresh()` +4. 重测 → 所有 Tab 页面整页下拉刷新效果一致 + +### AUTO-FIX #20: API route try-catch 包裹(max 1 次) + +**适用**: API endpoint 返回 500,`Read` 源码发现 handler 函数无 try-catch 包裹 + +**为什么这是 Bug**: 未处理异常直接返回原始 500 且无日志,无法诊断根因。常见的二次操作(如审批后创建通知)失败会吞噬前面的成功状态。 + +**检测方法**: API 返回 500 时,`Read` 对应 route.ts 源码,检查 `export async function POST/PUT/DELETE/GET` 内是否有 try-catch + +**修复流程**: +1. `Read` 失败的 route.ts 文件 +2. 确认 handler 无 try-catch 包裹 +3. 在 requireAdmin/鉴权检查之后,将剩余逻辑包裹在 try-catch 中 +4. catch 块:`console.error("Xxx error:", error)` + `NextResponse.json({error: "处理失败"}, {status: 500})` +5. 参考模式:同项目中已有 try-catch 的 route(如 admin auth login) + +**修复模板**: +```typescript +// 修复前: +export async function PUT(req, { params }) { + const result = requireAdmin(req); + if ("error" in result) return result.error; + const body = await req.json(); // ← 无 try-catch,异常直接变 500 + // ... 业务逻辑 ... +} + +// 修复后: +export async function PUT(req, { params }) { + const result = requireAdmin(req); + if ("error" in result) return result.error; + try { + const body = await req.json(); + // ... 业务逻辑不变 ... + } catch (error) { + console.error("Xxx error:", error); + return NextResponse.json({ error: "处理失败" }, { status: 500 }); + } +} +``` + +### AUTO-FIX #21: uni-app 页面 onMounted→onShow 替换(max 1 次) + +**适用**: 旅程中 `uni.navigateBack()` 返回目标页面后,页面数据未刷新 + +**为什么这是 Bug**: `onMounted` 仅在组件首次创建时触发。当用户从子页面 `navigateBack()` 返回时,父页面组件仍在导航栈中未被销毁,`onMounted` 不会再次触发。uni-app 的 `onShow` 在每次页面显示时都会触发,是正确的数据刷新时机。 + +**触发条件**(三条件全部满足): +- 条件 A: 旅程中使用 `uni.navigateBack()` 返回到该页面 +- 条件 B: 返回后 `browser_snapshot` 显示数据未更新(如余额、列表数量未变化) +- 条件 C: `Read` 页面源码确认数据获取在 `onMounted` 而非 `onShow` 中 + +**修复流程**: +1. `Read` 页面 `.vue` 文件 +2. 修改 import:将 `import { ref, onMounted } from "vue"` 中 `onMounted` 移除(若 vue 导入不再需要 onMounted) +3. 添加 `import { onShow } from "@dcloudio/uni-app"`(新 import 行) +4. 将 `onMounted(() => { fetchXxx() })` 替换为 `onShow(() => { fetchXxx() })` +5. 参考模式:项目中已有正确使用 `onShow` 的页面(如 profile/index.vue) + +**修复模板**: +```typescript +// 修复前: +import { ref, onMounted } from "vue"; +// ... +onMounted(() => { fetchEarnings(); }); + +// 修复后: +import { ref } from "vue"; +import { onShow } from "@dcloudio/uni-app"; +// ... +onShow(() => { fetchEarnings(); }); +``` + +### AUTO-FIX #23: uni-app 原生组件 H5 跨平台替换(max 1 次) + +**适用**: FAIL #23 — uni-app 原生组件在 H5 环境下不渲染或不可交互 + +**为什么这是 Bug(而非"环境限制")**: uni-app 项目需要同时运行在微信小程序和 H5(测试环境)。任何平台下组件不可用都意味着**跨平台兼容性缺陷**。正确的做法是在代码层面做跨平台适配,而非标记"环境限制"跳过。 + +**检测触发条件**(三条件任一满足): +- 条件 A:`browser_snapshot` 中目标交互区域空白/缺失,但 `Read` 源码确认该区域有 uni-app 原生组件(``/``/``/``/``) +- 条件 B:`browser_click` 在 ``/`` 等元素上失败,`browser_evaluate` 确认 DOM 中该元素存在但渲染尺寸异常(width/height=0 或 display=none) +- 条件 C:`grep` 源码扫描发现使用了已知 H5 不兼容组件,且该组件对用户流程必需 + +**⛔ 禁止行为**: +- ❌ 标记为"环境限制"/"已知问题"跳过修复 +- ❌ 仅在源码添加注释说明"H5 不可用" +- ❌ 用 `browser_evaluate` 绕过不可交互组件后声称"操作完成" +- ❌ 用 `browser_run_code` 直接操作 DOM 状态 + +**流程**: +1. `Read` 页面源码 → 定位原生组件的使用位置和绑定逻辑 +2. 根据 `../reference/platform-uni-app.md §8` 的替换方案,用纯 view + CSS 自定义组件替换 +3. 保持原有的 `v-model`/`@tap` 逻辑不变(仅替换模板和样式) +4. 重测失败步骤 → PASS 继续 + +**替换模板(checkbox)**: +```vue + + + + + + + +``` +```css +.custom-checkbox { + width: 18px; height: 18px; min-width: 18px; + border-radius: 4px; + border: 1.5px solid rgba(255, 255, 255, 0.2); + background: rgba(255, 255, 255, 0.05); + display: flex; align-items: center; justify-content: center; + transition: all 0.2s ease; +} +.custom-checkbox.checked { + background: linear-gradient(135deg, #ffb3b5, #ff5167); + border-color: #ff5167; +} +.check-icon { width: 12px; height: 12px; color: #000; } +``` + +**替换模板(radio)**: +```vue + + + + + + + +``` +```css +.custom-radio { + width: 18px; height: 18px; min-width: 18px; + border-radius: 50%; + border: 1.5px solid rgba(255, 255, 255, 0.2); + background: rgba(255, 255, 255, 0.05); + display: flex; align-items: center; justify-content: center; + transition: all 0.2s ease; +} +.custom-radio.checked { border-color: #ff5167; } +.radio-dot { + width: 10px; height: 10px; border-radius: 50%; + background: linear-gradient(135deg, #ffb3b5, #ff5167); +} +``` + +**替换模板(switch)**: +```vue + + + + + + + +``` +```css +.custom-switch { + width: 44px; height: 24px; border-radius: 12px; + background: rgba(255, 255, 255, 0.1); + position: relative; transition: background 0.2s; +} +.custom-switch.on { background: linear-gradient(135deg, #ffb3b5, #ff5167); } +.switch-thumb { + width: 20px; height: 20px; border-radius: 50%; + background: #fff; position: absolute; top: 2px; left: 2px; + transition: transform 0.2s; +} +.custom-switch.on .switch-thumb { transform: translateX(20px); } +``` + +### AUTO-FIX #16: 约束不一致修复(max 1 次) + +**适用**: FAIL #16 — 旅程描述/UI 文案说"可选"但代码实际必填,或 UI 标记"选填"但接口必填 + +**检测触发条件**(双条件同时满足): +- 条件 1:声明方说"可选"(旅程描述含"可跳过/可选/选填/skip/optional/使用占位图" 或 页面文本含"(选填)/(可选)") +- 条件 2:代码实际"必填"(Toast 含"请上传/请填写/请选择/不能为空" 或 源码有 `if (!field)` 验证) + +**为什么这是问题**: 用户被"可选"承诺误导,但操作时被强制校验拦截。真实用户体验受损。 + +**流程**: +1. `Read` 对应页面源码 → 定位必填验证逻辑(`if (!field)` / `required` / `validate`) +2. 判断正确设计意图: + - PRD/UX 规范说可选 → AUTO-FIX 代码(移除必填验证或改为可选提交) + - PRD/UX 规范说必填 → JOURNEY-FIX 旅程定义(更新描述为必填)+ AUTO-FIX UI 文案(移除"选填"标记) + - 无明确 PRD 依据 → JOURNEY-FIX(旅程定义改为反映代码行为)+ 记录 `constraint_mismatch` 到 pending_fixes +3. 修复后重测该步骤 + +**代码修复示例**(设计意图=可选时): +```javascript +// 修复前(强制必填): +if (!form.idCardFront || !form.idCardBack || !form.idCardHolding) { + uni.showToast({ title: "请上传所有证件照片", icon: "none" }); + return; +} + +// 修复后(改为可选): +// 移除强制校验,允许不上传照片直接提交 +``` + +**旅程修复示例**(设计意图=必填时): +```markdown +// 修复前: +9. 用户第二步上传身份证照片(可跳过或使用占位图)→ 点击"下一步" + +// 修复后: +9. 用户第二步上传身份证照片(必填:身份证正面、反面、手持照片)→ 点击"下一步" +``` + +### AUTO-DATA-FIX(数据状态,max 1 次) + +**适用**: 测试数据过期、冻结期收益不可用、关联数据缺失 + +**流程**: +1. `Bash` 直接操作数据库或 API 修复数据状态 +2. 重测失败步骤 + +**示例**: +- 收益冻结期不可提现 → `Bash`: `psql UPDATE earnings SET status='AVAILABLE'` +- 组局状态不正确 → API: `PUT /api/admin/events/[id]` 修正状态 +- 测试用户数据缺失 → API: `POST` 创建缺失的关联记录 + +### ENV-ADAPT(环境限制,max 1 次) + +**适用**: 操作被前端表单校验拦截(#12),Toast 提示了具体原因 + +**流程**: +1. `browser_snapshot` 读取页面 Toast/错误提示内容 +2. 根据 Toast 内容分流: + - "请输入/请填写/不能为空" → 找到对应字段 → `browser_type` 补充填写 → 重试操作 + - "请上传/请选择图片/请选择文件" → `browser_click` 触发文件选择 → `browser_file_upload` 上传测试文件 → 重试操作 + - "暂未开放/开发中" → 分类为 AUTO-IMPLEMENT → 走 auto-implement 流程 +3. 重试仍失败 → 标记 journey-step-fail + Toast 内容 + +**⛔ 重要:表单校验失败但页面无任何提示 → Bug(#13) → AUTO-FIX(添加 Toast/错误提示)** + +**示例**: +- 局头申请要求上传证件照 → Toast "请上传所有证件照片" → `browser_file_upload` 上传测试图片 → 重试提交 +- 注册表单缺少必填项 → Toast "请输入手机号" → `browser_type` 填写手机号 → 重试提交 +- 表单提交无反应且无 Toast → Bug(#13) → AUTO-FIX: 添加 Toast 提示 + +### 修复失败处理 + +- 重试达到上限仍 FAIL → 标记该步骤 `journey-step-fail` +- 记录: `{journey, step, fail_reason, fix_type, fix_attempted, fix_result}` +- 继续执行下一个步骤(不阻断整条旅程) +- 旅程完成后统计: `steps_passed / steps_total` → 决定整体 PASS/FAIL diff --git a/.claude/skills/d8d-dev/rules/e2e-testing.md b/.claude/skills/d8d-dev/rules/e2e-testing.md index a6f8486..5e918a7 100644 --- a/.claude/skills/d8d-dev/rules/e2e-testing.md +++ b/.claude/skills/d8d-dev/rules/e2e-testing.md @@ -1,6 +1,6 @@ # E2E 测试 -> 参见 ./auto-fix-engine.md 了解统一修复引擎(FAIL分类标准 + 修复流程 + GATE-CHECK)。 +> 参见 ./auto-fix/engine.md 了解统一修复引擎(FAIL分类标准 + 修复流程 + GATE-CHECK)。 ## E2E 测试覆盖矩阵 @@ -123,7 +123,7 @@ echo "inline_api=$INLINE_COUNT" 4. 修改引用(替换 emoji、更新 pages.json) 5. 重启服务 → 重新执行步骤 9.5 验证 -> 完整的 AUTO-FIX/IMPLEMENT/DATA-FIX 分类标准和修复流程见 ./auto-fix-engine.md +> 完整的 AUTO-FIX/IMPLEMENT/DATA-FIX 分类标准和修复流程见 ./auto-fix/engine.md ### 增强 analyze_image 提示词(步骤 8 详解) diff --git a/.claude/skills/d8d-dev/rules/iron-laws.md b/.claude/skills/d8d-dev/rules/iron-laws.md index 39df46e..dc8dfc2 100644 --- a/.claude/skills/d8d-dev/rules/iron-laws.md +++ b/.claude/skills/d8d-dev/rules/iron-laws.md @@ -1,6 +1,6 @@ # 铁律 -> 参见 ./auto-fix-engine.md 了解统一修复引擎(FAIL分类标准 + 修复流程 + GATE-CHECK)。 +> 参见 ./auto-fix/engine.md 了解统一修复引擎(FAIL分类标准 + 修复流程 + GATE-CHECK)。 ## 铁律:Epic 间零暂停自动继续 @@ -238,8 +238,8 @@ Epic N+1 完成 | 约束声明与代码行为不一致 | 旅程描述含"可选/可跳过/选填"或页面 UI 含"(选填)/(可选)"标记,但代码实际有必填验证(Toast 含"请上传/请填写/不能为空"或源码 `if (!field)` 拦截) | 约束不一致(#16) | JOURNEY-FIX: 修复旅程定义 / AUTO-FIX: 修复代码或 UI 文案,使声明与行为一致 | | analyze_image 视觉验证未执行 | pipeline-state 无 `analyze_image_called: true` 或 verification_detail.design_check 缺失 | 阻断 | 必须回退执行 analyze_image 后才能标记完成(见 ./visual-verification.md) | | 双重导航栏 | 页面在 pages.json 中有 `navigationBarTitleText`(非 custom)且页面源码含 `position:fixed` 的自定义 header 元素,导致系统导航栏与自定义头部同时显示(截图中可见两层标题) | Bug(#18) | AUTO-FIX: pages.json 该页面添加 `navigationStyle: "custom"` + 页面 header CSS 添加 safe-area padding(参见 ../reference/platform-uni-app.md §3) | -| API route 无 try-catch | API 返回 500 + `Read` 源码发现 handler 函数无 try-catch 包裹(异常直接返回原始 500 且无日志) | Bug(#20) | AUTO-FIX: 添加 try-catch 包裹 + console.error + JSON 500 响应(参见 ./auto-fix-engine.md #20) | -| 页面 navigateBack 后数据陈旧 | `uni.navigateBack()` 返回目标页后 `browser_snapshot` 数据未更新 + 源码用 `onMounted` 非 `onShow` | Bug(#21) | AUTO-FIX: 替换 `onMounted` 为 `onShow`(from `@dcloudio/uni-app`,参见 ./auto-fix-engine.md #21) | +| API route 无 try-catch | API 返回 500 + `Read` 源码发现 handler 函数无 try-catch 包裹(异常直接返回原始 500 且无日志) | Bug(#20) | AUTO-FIX: 添加 try-catch 包裹 + console.error + JSON 500 响应(参见 ./auto-fix/workflows.md #20) | +| 页面 navigateBack 后数据陈旧 | `uni.navigateBack()` 返回目标页后 `browser_snapshot` 数据未更新 + 源码用 `onMounted` 非 `onShow` | Bug(#21) | AUTO-FIX: 替换 `onMounted` 为 `onShow`(from `@dcloudio/uni-app`,参见 ./auto-fix/workflows.md #21) | | 交互元素状态未变化 | browser_click 点击 checkbox/radio/toggle 后 browser_snapshot 确认 checked/selected 状态未改变 | Bug(#22) | AUTO-FIX: 修复事件绑定或状态更新逻辑 | | 按钮永久 disabled | 旅程定义中要求点击的按钮(如 Next/Submit/Publish)处于 disabled 状态且无法通过前置操作解除 | Bug(#23) | AUTO-FIX: 修复 disabled 条件或连接正确的状态源 | | API 成功但 UI 未更新 | curl/API 返回成功但 browser_snapshot + browser_take_screenshot 确认界面无变化(如封面预览未刷新、列表未新增) | Bug(#24) | AUTO-FIX: 添加 refetch/状态同步/乐观更新 | @@ -247,7 +247,7 @@ Epic N+1 完成 | 已修复标记盲信 | 旅程文件交互缺陷审计表标记"✅ 已修复",验证时不重新测试实际行为 | 漏检风险 | ⛔ 必须重新验证修复是否正确(不只是检查 handler 是否存在) | | 漏识别 uni-app 交互 | 交互元素识别只覆盖 React(onClick)不覆盖 uni-app(@tap) | 覆盖漏洞 | 交互元素识别必须同时支持 onClick + @tap/@click/bindtap | -> 完整的 FAIL 分类标准(含兜底规则 #99)和修复流程见 ./auto-fix-engine.md 第 2-4 节 +> 完整的 FAIL 分类标准(含兜底规则 #99)见 ./auto-fix/classification.md,修复流程见 ./auto-fix/workflows.md **违反此规则的行为**: - ❌ 检测到溢出后写"需注意"然后标记 done diff --git a/.claude/skills/d8d-dev/rules/journey-authenticity-rules.md b/.claude/skills/d8d-dev/rules/journey-authenticity-rules.md index dc08fc5..c03d80d 100644 --- a/.claude/skills/d8d-dev/rules/journey-authenticity-rules.md +++ b/.claude/skills/d8d-dev/rules/journey-authenticity-rules.md @@ -179,6 +179,30 @@ grep -rn "import.meta.env.DEV\|process.env.NODE_ENV\|isDev" app/mini-program/src - 然后进入原有旅程步骤 ``` +### 标准前置步骤模板(⛔ 机制描述 — 项目特定模板见模板文件) + +当旅程需要其他 Epic 产出的数据时,不得在前置条件中声明数据依赖,而必须使用标准前置步骤模板将完整 UI 操作步骤内联到旅程中。 + +**机制**: +1. 为项目中常见的跨 Epic 前置操作定义标准步骤模板(如登录、认证、创建核心实体等) +2. 模板文件路径:`_bmad-output/implementation-artifacts/journey-prerequisite-templates.md` +3. 如果模板文件不存在 → 从源码自动提取并生成(见下方生成规则) +4. 不同旅程引用同一模板时,步骤内容必须完全一致(复制粘贴,不做修改) + +**模板生成规则**(首次生成或源码变更后重新生成): +1. 读取各核心页面的源码(登录页、认证页、创建页等) +2. 提取每个页面的可交互元素(按钮、输入框、选择器等) +3. 按自然导航顺序组装操作步骤 +4. 每步包含:用户操作描述 + 技术验证 browser_* 操作 + 预期结果 +5. 标注环境限制(⚠️ H5 环境适配、开发环境特殊行为等) +6. 模板间允许组合(如"局头认证" = "登录模板" + "认证步骤") + +**模板引用规则**: +- 直接内联完整步骤到旅程中(不使用"[引用]"缩写,确保每条旅程自包含) +- 模板中的占位符(如 `{自然入口 URL}`)需替换为实际值 +- ⚠️ 标注和环境适配信息必须保留 +- 技术验证步骤中的 browser_* 操作描述必须保留 + ### 例外场景(允许数据依赖) 只有以下场景允许依赖预先存在的数据: diff --git a/.claude/skills/d8d-dev/rules/visual-verification.md b/.claude/skills/d8d-dev/rules/visual-verification.md index 538d677..b61994b 100644 --- a/.claude/skills/d8d-dev/rules/visual-verification.md +++ b/.claude/skills/d8d-dev/rules/visual-verification.md @@ -41,7 +41,7 @@ ### 第二层:GATE-CHECK 验证(闭环保障) -`auto-fix-engine.md` GATE-CHECK 增加: +`./auto-fix/engine.md` GATE-CHECK 增加: ``` ├── ⛔ analyze_image 已执行(所有截图都有对应视觉分析结果) @@ -284,7 +284,7 @@ step_results: | 设计系统客观指标不合规(背景色/主色调/卡片样式/边框规则/按钮渐变等可从源码验证项) | Bug | AUTO-FIX: 修复 CSS/class,参照 _bmad-output/04-ui-ux-design.md 设计规范 | — | | 设计系统主观视觉评价不合规(发光效果强度/动画质感等无法从源码客观验证的维度) | 改进建议 | WARN | — | -> **注意**:视觉检查发现的问题分流后,走 `auto-fix-engine.md` 标准修复流程(AUTO-FIX/AUTO-IMPLEMENT),不在此文件定义修复细节。 +> **注意**:视觉检查发现的问题分流后,走 `./auto-fix/engine.md` 标准修复流程(AUTO-FIX/AUTO-IMPLEMENT),不在此文件定义修复细节。 ### 跨步骤累计检测规则(⛔ 强制)