sync: 拆分 auto-fix-engine 为子目录 + 新增 H5 组件替换/R6 自包含/并行生成模式 #53

Merged
d8dfun merged 1 commits from sync/d8d-skills-20260420-054757 into master 2026-04-20 13:52:37 +08:00
16 changed files with 1020 additions and 685 deletions

View File

@@ -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` 中每条旅程涉及的技术验证步骤

View File

@@ -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
- 跨轮次累计的视觉问题 ≥ 1emoji_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
**⛔ 禁止的"记录但不修复"模式**:

View File

@@ -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 中一次性串行执行多条旅程**

View File

@@ -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 强制)**

View File

@@ -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

View File

@@ -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

View File

@@ -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 章节
- ⛔ 禁止标记为"环境限制"跳过

View File

@@ -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/无错误文字)| BugUX 缺陷)| AUTO-FIX | 用户不知道为什么操作无效 → 添加 Toast 或内联错误提示 |
| 14 | 即时使用数据(验证码等)仅通过 Toast 显示,无 console.log 或自动填充,下一步骤无法获取 | Bug数据丢失风险| AUTO-FIX | sendCode() 用 Toast 显示验证码但无 console.log流程卡死 |
| 15 | UI 直接输出英文枚举值(如 APPROVED/REJECTED/PENDING/PAID/CANCELLED 等)而非本地化中文标签 | BugUI 本地化缺失)| 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
- ⛔ 无任何提示 → BugUX 缺陷:校验失败但用户看不到原因)→ 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代码 Bugmax 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-RETRYPlaywright 崩溃专用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 次)
**适用**: 操作被前端表单校验拦截(#12Toast 提示了具体原因
**流程**:
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 |

View 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/无错误文字)| BugUX 缺陷)| AUTO-FIX | 用户不知道为什么操作无效 → 添加 Toast 或内联错误提示 |
| 14 | 即时使用数据(验证码等)仅通过 Toast 显示,无 console.log 或自动填充,下一步骤无法获取 | Bug数据丢失风险| AUTO-FIX | sendCode() 用 Toast 显示验证码但无 console.log流程卡死 |
| 15 | UI 直接输出英文枚举值(如 APPROVED/REJECTED/PENDING/PAID/CANCELLED 等)而非本地化中文标签 | BugUI 本地化缺失)| 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
- ⛔ 无任何提示 → BugUX 缺陷:校验失败但用户看不到原因)→ 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`

View 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`(仅限此场景,且须记录适配原因)

View 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 |

View File

@@ -0,0 +1,437 @@
# 修复流程模板
> **⛔ 所有模式共享** — 引用自 `./engine.md`。修复前必须先完成 `./classification.md` 中的分类判定。
---
## 4. 修复流程
### AUTO-FIX代码 Bugmax 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-RETRYPlaywright 崩溃专用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 次)
**适用**: 操作被前端表单校验拦截(#12Toast 提示了具体原因
**流程**:
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

View File

@@ -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 详解)

View File

@@ -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

View File

@@ -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_* 操作描述必须保留
### 例外场景(允许数据依赖)
只有以下场景允许依赖预先存在的数据:

View File

@@ -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不在此文件定义修复细节。
### 跨步骤累计检测规则(⛔ 强制)