sync: 拆分 auto-fix-engine 为子目录 + 新增 H5 组件替换/R6 自包含/并行生成模式
- 重构 auto-fix-engine.md → auto-fix/ 子目录(classification/diagnostics/engine/workflows) - 新增 §8 H5 不兼容组件替换方案(checkbox/radio/switch/slider/picker) - 新增 R6 自包含强制规则 + 标准前置步骤模板 - 新增并行生成模式(步骤 3P,支持 teammate 并行) - 更新所有文件中的路径引用
This commit is contained in:
@@ -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` 中每条旅程涉及的技术验证步骤
|
||||
|
||||
@@ -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)
|
||||
|
||||
**⛔ 禁止的"记录但不修复"模式**:
|
||||
|
||||
@@ -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 中一次性串行执行多条旅程**
|
||||
|
||||
@@ -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 强制)**
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 原生组件(`<checkbox>`/`<radio>`/`<switch>`/`<slider>`/`<picker>` 等)在 H5 环境下可能出现:
|
||||
- 不渲染(DOM 中存在但宽高为 0)
|
||||
- 不可交互(点击无响应)
|
||||
- 样式异常(默认浏览器样式覆盖 Neon Pulse 设计)
|
||||
|
||||
微信小程序正常但 H5 不正常的组件,**不得标记为"环境限制"跳过**。必须用跨平台兼容的纯 view + CSS 方案替换。
|
||||
|
||||
### ⛔ 核心原则
|
||||
|
||||
- **禁止**使用 uni-app 原生表单组件(`<checkbox>`/`<radio>`/`<switch>`/`<slider>`/`<picker>`)
|
||||
- **必须**使用纯 `<view>` + CSS + `@tap` 实现等效功能
|
||||
- 这样做:小程序和 H5 都能正常渲染,无需任何平台判断
|
||||
|
||||
### 不兼容组件清单
|
||||
|
||||
| uni-app 原生组件 | H5 问题 | 替换方案 |
|
||||
|------------------|---------|---------|
|
||||
| `<checkbox>` | 不渲染或样式异常 | view + Iconify check 图标 |
|
||||
| `<radio>` | 不渲染或样式异常 | view + CSS 圆点 |
|
||||
| `<switch>` | 样式与设计系统冲突 | view + CSS 滑动开关 |
|
||||
| `<slider>` | 样式不可控 | view + CSS 进度条 + @touchmove |
|
||||
| `<picker>` | 原生弹窗与 H5 不兼容 | 自定义弹窗组件 |
|
||||
|
||||
### 检测方法(⛔ 旅程验证步骤级强制)
|
||||
|
||||
```bash
|
||||
# 扫描所有 .vue 文件中的原生表单组件
|
||||
grep -rn '<checkbox\|<radio\|<switch\|<slider\|<picker' app/mini-program/src/ --include="*.vue"
|
||||
# 发现任何匹配 → 触发 AUTO-FIX #23
|
||||
```
|
||||
|
||||
### 替换模板
|
||||
|
||||
#### checkbox → view + Iconify check
|
||||
|
||||
```vue
|
||||
<!-- 修复前: -->
|
||||
<checkbox :checked="agreed" @tap="agreed = !agreed" color="#ffb3b5" />
|
||||
|
||||
<!-- 修复后: -->
|
||||
<view class="custom-checkbox" :class="{ checked: agreed }" @tap="agreed = !agreed">
|
||||
<view v-if="agreed" class="i-lucide-check check-icon"></view>
|
||||
</view>
|
||||
```
|
||||
|
||||
```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
|
||||
<!-- 修复前: -->
|
||||
<radio :checked="selected === 'A'" @tap="selected = 'A'" color="#ffb3b5" />
|
||||
|
||||
<!-- 修复后: -->
|
||||
<view class="custom-radio" :class="{ checked: selected === 'A' }" @tap="selected = 'A'">
|
||||
<view v-if="selected === 'A'" class="radio-dot"></view>
|
||||
</view>
|
||||
```
|
||||
|
||||
```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
|
||||
<!-- 修复前: -->
|
||||
<switch :checked="enabled" @change="enabled = !enabled" color="#ffb3b5" />
|
||||
|
||||
<!-- 修复后: -->
|
||||
<view class="custom-switch" :class="{ on: enabled }" @tap="enabled = !enabled">
|
||||
<view class="switch-thumb"></view>
|
||||
</view>
|
||||
```
|
||||
|
||||
```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 章节
|
||||
- ⛔ 禁止标记为"环境限制"跳过
|
||||
|
||||
@@ -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 |
|
||||
112
.claude/skills/d8d-dev/rules/auto-fix/classification.md
Normal file
112
.claude/skills/d8d-dev/rules/auto-fix/classification.md
Normal file
@@ -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 环境下不渲染/不可交互(`<checkbox>`/`<radio>`/`<switch>`/`<slider>`/`<picker>` 等)| Bug(平台不兼容) | AUTO-FIX | H5 环境下 `<checkbox>` 不渲染,`<radio>` 无法点击。⛔ 必须替换为纯 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` 源码确认使用了 `<checkbox>`/`<radio>`/`<switch>`/`<slider>`/`<picker>` 等 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`)
|
||||
99
.claude/skills/d8d-dev/rules/auto-fix/diagnostics.md
Normal file
99
.claude/skills/d8d-dev/rules/auto-fix/diagnostics.md
Normal file
@@ -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`(仅限此场景,且须记录适配原因)
|
||||
142
.claude/skills/d8d-dev/rules/auto-fix/engine.md
Normal file
142
.claude/skills/d8d-dev/rules/auto-fix/engine.md
Normal file
@@ -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 |
|
||||
437
.claude/skills/d8d-dev/rules/auto-fix/workflows.md
Normal file
437
.claude/skills/d8d-dev/rules/auto-fix/workflows.md
Normal file
@@ -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 原生组件(`<checkbox>`/`<radio>`/`<switch>`/`<slider>`/`<picker>`)
|
||||
- 条件 B:`browser_click` 在 `<checkbox>`/`<radio>` 等元素上失败,`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
|
||||
<!-- 修复前: -->
|
||||
<checkbox :checked="agreed" @tap="agreed = !agreed" color="#ffb3b5" />
|
||||
|
||||
<!-- 修复后: -->
|
||||
<view class="custom-checkbox" :class="{ checked: agreed }" @tap="agreed = !agreed">
|
||||
<view v-if="agreed" class="i-lucide-check check-icon"></view>
|
||||
</view>
|
||||
```
|
||||
```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
|
||||
<!-- 修复前: -->
|
||||
<radio :checked="selected === 'A'" @tap="selected = 'A'" color="#ffb3b5" />
|
||||
|
||||
<!-- 修复后: -->
|
||||
<view class="custom-radio" :class="{ checked: selected === 'A' }" @tap="selected = 'A'">
|
||||
<view v-if="selected === 'A'" class="radio-dot"></view>
|
||||
</view>
|
||||
```
|
||||
```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
|
||||
<!-- 修复前: -->
|
||||
<switch :checked="enabled" @change="enabled = !enabled" color="#ffb3b5" />
|
||||
|
||||
<!-- 修复后: -->
|
||||
<view class="custom-switch" :class="{ on: enabled }" @tap="enabled = !enabled">
|
||||
<view class="switch-thumb"></view>
|
||||
</view>
|
||||
```
|
||||
```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
|
||||
@@ -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 详解)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_* 操作描述必须保留
|
||||
|
||||
### 例外场景(允许数据依赖)
|
||||
|
||||
只有以下场景允许依赖预先存在的数据:
|
||||
|
||||
@@ -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),不在此文件定义修复细节。
|
||||
|
||||
### 跨步骤累计检测规则(⛔ 强制)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user