sync: auto-fix 规则按平台拆分 — uni-app/i18n 专属模板条件加载 #56

Merged
d8dfun merged 1 commits from sync/d8d-skills-20260421-064259 into master 2026-04-21 14:50:15 +08:00
10 changed files with 631 additions and 268 deletions

View File

@@ -199,6 +199,16 @@
- [ ] {具体可量化的断言1}
- [ ] {具体可量化的断言2}
- [ ] {全程使用自然导航路径}
### i18n 断言(可选 — 仅 i18n 项目填写)
> 如果项目使用了 i18n如 next-intl、next-i18next每个旅程步骤需额外定义以下断言。
> 非 i18n 项目留空或删除此区域即可。
| 步骤 | 预期 URL含 locale | 预期 html lang | 预期内容语言 | 预期 locale cookie |
|------|---------------------|---------------|-------------|-------------------|
| 1 | `/en/login` | `en` | English | `NEXT_LOCALE=en` |
| 2 | `/zh/dashboard` | `zh` | 中文 | `NEXT_LOCALE=zh` |
```
### 旅程来源优先级

View File

@@ -411,6 +411,33 @@ curl 只用于补充数据验证(如确认数据库状态),**永远不能
6. 修复失败 → 标记 auto_fix_result: "FAIL",记录原因
6. ⛔ **源码 SVG data URI 扫描**(视觉验证关卡内 — 每次 snapshot 后强制执行):
6.5. ⛔ **i18n 校验子步骤**(⛔ 条件激活 — 仅 i18n 项目,每次 snapshot 后强制执行):
前置检测:`glob src/i18n/**/*` 或 `next.config.*` 中含 i18n 配置。
项目无 i18n 配置 → 跳过以下所有步骤(单语言项目零影响)。
a. URL locale 检查:
`browser_evaluate` → `window.location.pathname`
→ 验证匹配 `/^\/(en|zh)\//`(或项目配置的 locale 列表)
→ FAIL(#25)URL 无 locale 前缀
b. html lang 检查:
`browser_evaluate` → `document.documentElement.lang`
→ 验证与 URL locale 一致
→ FAIL(#26)lang 属性不匹配
c. 内容语言检查:
`browser_evaluate` → `document.body.innerText.substring(0, 2000)`
→ URL `/zh/`:检查文本包含中文(`\u4e00-\u9fff`),无英文整句
→ URL `/en/`:检查文本无中文字符
→ FAIL(#27):内容语言不匹配
d. Cookie 检查:
`browser_evaluate` → `document.cookie`
→ 验证包含 NEXT_LOCALE或项目配置的 cookie 名)
→ 验证 cookie 值与 URL locale 一致
→ FAIL(#28)cookie 缺失或值不匹配
e. 记录结果到 verification_detail.i18n_check格式见 ../../rules/visual-verification.md
g. ⛔ **约束一致性检查**(每个用户步骤首次执行时 — R7 执行时强制检测):
触发条件(操作被拦截 + 声明方说"可选":
- 操作后 Toast 出现且含"请上传/请填写/请选择/不能为空/不能为空"
@@ -430,7 +457,8 @@ curl 只用于补充数据验证(如确认数据库状态),**永远不能
- ⛔ **资源加载状态检查**(新增 — 每条旅程必须执行):
- `browser_network_requests(static=true, filter="\\.(jpg|jpeg|png|gif|webp|svg|mp4|webm|mov)$")`
- 过滤图片/视频资源请求,检查 HTTP 状态码
- 任何 403/404 → 标记 `resource_status_check.found = FOUND` + 触发修复
- 任何 403/404 → 标记 `resource_status_check.found = FOUND` + 触发 AUTO-FIX #30见 ../../rules/auto-fix/workflows.md
- ⛔ **禁止行为**: 将 404/403 标记为"非阻断性"跳过修复。资源 404 是 Bug(#30),必须修复。
- 常见 403 原因MinIO 私有 bucket 未用 presigned URL、防盗链、CORS
- 常见 404 原因:资源路径错误、文件未上传
- 记录到 pipeline-state.yaml 的 `verification_detail.resource_status_check`

View File

@@ -522,6 +522,6 @@ grep -rn '<checkbox\|<radio\|<switch\|<slider\|<picker' app/mini-program/src/ --
### 关联修复规则
- 触发 FAIL 分类: ../rules/auto-fix/classification.md #23
- 修复模板: ../rules/auto-fix/workflows.md AUTO-FIX #23 章节
- 触发 FAIL 分类: ../rules/auto-fix/classification.md #23(→ 详细定义见 platforms/uni-app.md
- 修复模板: ../rules/auto-fix/platforms/uni-app.md AUTO-FIX #23 章节
- ⛔ 禁止标记为"环境限制"跳过

View File

@@ -6,37 +6,45 @@
## 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 |
> **平台专属条目**: 标记 `[uni-app]` 的见 `./platforms/uni-app.md`,标记 `[i18n]` 的见 `./platforms/nextjs-i18n.md`。
| # | 平台 | 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 | uni-app | 前端代码 CSS/style 含 SVG data URI充当图标/背景/装饰) | Bug(平台不兼容) | → 见 platforms/uni-app.md | 微信小程序不支持 SVG data URI |
| 18 | uni-app | 页面同时有系统导航栏和自定义 fixed header导致双层标题栏 | Bug(双重导航栏) | → 见 platforms/uni-app.md | 截图中顶部出现两层标题 |
| 19 | uni-app | Tab 页面下拉刷新方式不一致,或使用列表级 scroll-view refresher | Bug(UX 不一致) | → 见 platforms/uni-app.md | 首页用 scroll-view refresher其他 Tab 无刷新 |
| 20 | 通用 | API route handler 无 try-catch 包裹,导致未处理异常直接返回原始 500 且无日志 | Bug(健壮性) | AUTO-FIX | Admin 提现审批 PUT 返回 500 但状态仍更新(异常被吞) |
| 21 | uni-app | uni-app 页面使用 `onMounted` 获取数据,但该页面是 `uni.navigateBack()` 的返回目标,导致回退时数据不刷新 | Bug(数据陈旧) | → 见 platforms/uni-app.md | 提现后返回收益页余额未刷新 |
| 22 | 通用 | Playwright MCP 浏览器崩溃/超时browser_snapshot/browser_click 等操作返回错误或无响应)| 环境问题 | AUTO-RETRY | 浏览器进程崩溃、MCP 连接超时、页面 unresponsive |
| 23 | uni-app | uni-app 原生组件在 H5 环境下不渲染/不可交互 | Bug(平台不兼容) | → 见 platforms/uni-app.md | H5 环境下 `<checkbox>` 不渲染 |
| 25 | i18n | i18n 项目页面 URL 缺少 locale 前缀 | Bug(i18n) | → 见 platforms/nextjs-i18n.md | middleware locale 检测逻辑错误 |
| 26 | i18n | html lang 属性与 URL locale 不匹配 | Bug(i18n) | → 见 platforms/nextjs-i18n.md | layout.tsx 的 lang 属性未从 URL params 读取 |
| 27 | i18n | 页面内容语言与 URL locale 不匹配 | Bug(i18n) | → 见 platforms/nextjs-i18n.md | i18n 翻译文件缺失或加载错误 |
| 28 | i18n | NEXT_LOCALE Cookie 缺失或值与 URL locale 不匹配 | Bug(i18n) | → 见 platforms/nextjs-i18n.md | middleware 或 layout 未正确同步 cookie |
| 29 | 通用 | API 返回正确数据但前端显示值不匹配(数据映射/提取逻辑错误)| Bug(数据同步) | AUTO-FIX | API 返回 credits=3000 但 UI 显示 0 |
| 30 | 通用 | 静态资源 404图片/图标/CSS/JS 加载失败,`browser_network_requests` 确认)| Bug(资源缺失) | AUTO-FIX | sparklogo.png 路径错误或文件缺失 |
| **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 种,其余一律必须修复)
@@ -104,9 +112,23 @@
- ⛔ 无任何提示 → 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`
- `browser_evaluate` 抓取 API 响应与 UI 显示值不一致API 200 且数据正确但 UI 显示错误)→ Bug(#29) → AUTO-FIX见 workflows.md AUTO-FIX #29
- `browser_network_requests` 检测到静态资源 404 → Bug(#30) → AUTO-FIX见 workflows.md AUTO-FIX #30
**平台专属判断流程**(仅对应平台项目时执行):
- uni-app 项目 → 见 `./platforms/uni-app.md` 关键判断流程(#17/#18/#19/#21/#23
- i18n 项目 → 见 `./platforms/nextjs-i18n.md` 关键判断流程(#25/#26/#27/#28
### ⛔ #99 兜底规则强化(⛔ 强制)
**核心原则**: 任何由执行模板中定义的校验子步骤(含 i18n 校验 6.5 节的 FAIL产生的检测结果如果在分类表中找不到精确匹配的编号**必须**走 #99 兜底触发 AUTO-FIX。
**⛔ 禁止行为**:
- ❌ 将检测到的 FAIL 标记为"非阻断性"跳过修复
- ❌ 以"分类表中无对应编号"为由不触发修复
- ❌ 以"不影响核心流程"为由记录但不修复
- ❌ 将任何 FAIL 结果降级为 WARN/建议
**唯一例外**: 满足"可接受"两项严格限定之一(系统按设计正确拒绝 / 业务规则正确阻止),且不影响后续步骤。

View File

@@ -4,6 +4,23 @@
---
## 0. 平台检测与条件加载(⛔ 启动时执行一次)
修复引擎启动时,检测项目结构以决定加载哪些平台专属修复模板:
1. **检测 uni-app/小程序项目**:
- `[ -d "app/mini-program/src" ]``grep -rl "pages.json" . --include="*.json" -l | head -1`
- 检测到 → `PLATFORM_UNIAPP=true` → 额外 Read: `./platforms/uni-app.md`
2. **检测 i18n 项目**:
- `grep -rl "next-intl\|next-i18n\|\[locale\]" src/app/ src/middleware.ts 2>/dev/null | head -1`
- 或检查 `src/app/[locale]/` 目录是否存在
- 检测到 → `PLATFORM_I18N=true` → 额外 Read: `./platforms/nextjs-i18n.md`
3. **未检测到任何平台**: 仅加载通用修复模板classification.md + diagnostics.md + workflows.md
---
## 1. 步骤完成判定标准(⛔ 每个 step 执行后强制判定)
**PASS**: `browser_snapshot` 显示的内容 **完全包含** 旅程/验证定义中该步骤"用户看到 X"的预期状态。
@@ -27,7 +44,8 @@
## 2. FAIL 分类标准
> ⛔ **Read fully and follow: ./classification.md** — 包含 23 种 FAIL 分类(#1-#23, #99, V1-V5、"可接受"严格限定、"已知问题不豁免"规则、关键判断流程。
> ⛔ **Read fully and follow: ./classification.md** — 包含所有 FAIL 分类(#1-#30, #99, V1-V5、"可接受"严格限定、"已知问题不豁免"规则、关键判断流程。
> 平台专属条目(#17/#18/#19/#21/#23 标记 uni-app#25-#28 标记 i18n在分类表中保留编号但指向平台文件。
---
@@ -39,7 +57,8 @@
## 4. 修复流程
> ⛔ **Read fully and follow: ./workflows.md** — 包含 AUTO-FIX、AUTO-IMPLEMENT、AUTO-RETRY、AUTO-DATA-FIX、ENV-ADAPT 全部修复模板。
> ⛔ **Read fully and follow: ./workflows.md** — 包含 AUTO-FIX、AUTO-IMPLEMENT、AUTO-RETRY、AUTO-DATA-FIX、ENV-ADAPT 通用修复模板。
> ⛔ 平台专属修复模板条件加载: uni-app 项目 → `./platforms/uni-app.md`i18n 项目 → `./platforms/nextjs-i18n.md`。
---
@@ -140,3 +159,5 @@ AUTO-FIX/AUTO-IMPLEMENT 执行后:
| `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 |
| `auto-fix/platforms/uni-app.md` | uni-app 专属修复模板(#17/#18/#19/#21/#23)→ 由 engine.md 第 0 节条件加载 |
| `auto-fix/platforms/nextjs-i18n.md` | i18n 专属修复模板(#25-#28)→ 由 engine.md 第 0 节条件加载 |

View File

@@ -0,0 +1,148 @@
# Next.js i18n 专属 FAIL 分类与修复模板
> **⛔ 仅 i18n 项目加载** — 当 `engine.md` 平台检测确认项目使用 Next.js i18n 路由时读取此文件。
> 此文件包含 FAIL 分类表中 #25-#28 的完整定义和修复模板。
---
## 1. FAIL 分类i18n 专属)
| # | FAIL 特征 | 分类 | 修复类型 | 示例 |
|---|-----------|------|---------|------|
| 25 | i18n 项目页面 URL 缺少 locale 前缀(如应为 /en/xxx 或 /zh/xxx 但实际为 /xxx| Bug(i18n) | AUTO-FIX | middleware locale 检测逻辑错误 |
| 26 | html lang 属性与 URL locale 不匹配(如 URL 为 /zh/ 但 `<html lang="en">`| Bug(i18n) | AUTO-FIX | layout.tsx 的 lang 属性未从 URL params 读取 |
| 27 | 页面内容语言与 URL locale 不匹配(/zh/ 页面含英文整句,/en/ 页面含中文)| Bug(i18n) | AUTO-FIX | i18n 翻译文件缺失或加载错误 |
| 28 | NEXT_LOCALE Cookie 缺失或值与 URL locale 不匹配 | Bug(i18n) | AUTO-FIX | middleware 或 layout 未正确同步 cookie |
---
## 2. 关键判断流程i18n 专属)
- i18n 项目 `browser_evaluate` 检测到 html lang 与 URL locale 不匹配 → Bug(#26) → AUTO-FIX见下方 AUTO-FIX #26
- i18n 项目 `browser_evaluate` 检测到 NEXT_LOCALE cookie 缺失或值不匹配 → Bug(#28) → AUTO-FIX见下方 AUTO-FIX #28
- i18n 项目 URL 缺少 locale 前缀 → Bug(#25) → AUTO-FIX检查 middleware locale 检测逻辑)
- i18n 项目页面内容语言与 URL locale 不匹配 → Bug(#27) → AUTO-FIX检查翻译文件和加载逻辑
---
## 3. 修复模板
### AUTO-FIX #25: URL locale 前缀缺失max 2 次)
**适用**: FAIL #25 — i18n 项目页面 URL 缺少 locale 前缀
**为什么这是 Bug**: 缺少 locale 前缀会导致 middleware 无法正确检测语言、服务端渲染语言回退、SEO 无法正确索引多语言页面。
**检测触发条件**journey-execution.md 6.5a 子步骤):
- `browser_evaluate``window.location.pathname` 不以 `/en/``/zh/` 等语言前缀开头
- 例: 应该是 `/zh/dashboard` 但实际是 `/dashboard`
**流程**:
1. `Read` middleware 文件(通常 `src/middleware.ts`:
- 检查 locale 检测逻辑是否正确
- 检查重定向规则是否将无前缀 URL 重定向到带 locale 前缀的 URL
2. 修复路径(根据根因选择):
- **middleware 未检测 locale**: 添加 locale 检测逻辑(从 cookie/accept-language/header 读取)
- **middleware 重定向规则缺失**: 添加无前缀 URL → 带前缀 URL 的重定向
- **默认 locale 未设置**: 在 middleware 中设置默认 locale
3. `Edit` 修复 middleware
4. `Bash` 重启服务
5. 重测: `browser_navigate` → 确认 URL 自动添加 locale 前缀
### AUTO-FIX #26: html lang 属性同步max 2 次)
**适用**: FAIL #26 — html lang 属性与 URL locale 不匹配(如 URL 为 /zh/ 但 `<html lang="en">`
**为什么这是 Bug**: html lang 属性影响屏幕阅读器发音、浏览器翻译提示、SEO 语言信号。lang 属性不匹配会导致辅助功能错误和 SEO 降级。
**检测触发条件**journey-execution.md 6.5b 子步骤):
- `browser_evaluate``document.documentElement.lang` 返回值与 URL 中的 locale 不一致
- 例: URL `/zh/dashboard``document.documentElement.lang` = "en"
**流程**:
1. 从 URL 提取当前 locale: `browser_evaluate``window.location.pathname.match(/^\/(en|zh)\//)?.[1]`
2. `grep` 搜索 `<html``lang=``src/app/` 的 layout 文件中:
- `grep -rn 'lang=' src/app/layout.tsx src/app/\[locale\]/layout.tsx`
3. 根据项目结构选择修复路径:
- **Next.js App Router i18n 项目**: `src/app/[locale]/layout.tsx``<html lang={locale}>` — 确保从路由 params 读取 locale 而非硬编码
- **根 layout**: 如果根 layout 硬编码了 lang="en" → 改为从 cookies/headers 读取
4. `Edit` 修复 layout.tsx 的 lang 属性
5. `Bash` 重启服务(如需要)
6. 重测: `browser_navigate` → 重新检查 `document.documentElement.lang`
**修复模板Next.js [locale] layout**:
```tsx
// 修复前(根 layout 硬编码):
export default function RootLayout({ children }) {
return <html lang="en"><body>{children}</body></html>;
}
// 修复后(从 params 读取 locale:
export default function LocaleLayout({ children, params }) {
return <html lang={params.locale}><body>{children}</body></html>;
}
```
### AUTO-FIX #27: 页面内容语言修复max 2 次)
**适用**: FAIL #27 — 页面内容语言与 URL locale 不匹配
**为什么这是 Bug**: 用户选择了中文 URL 但页面显示英文内容,或反之。说明 i18n 翻译加载逻辑有问题。
**检测触发条件**journey-execution.md 6.5c 子步骤):
- `browser_evaluate` 抓取页面主要文本内容,检测语言与 URL locale 不一致
- 例: URL `/zh/dashboard` 但页面大段文字为英文
**流程**:
1. `browser_evaluate` 抓取页面文本,确认语言不匹配
2. 检查 i18n 配置:
- `Read` i18n 配置文件(如 `src/i18n.ts`, `next.config.js` i18n 部分)
- `Bash` 列出翻译文件: `find src/ -name "*.json" -path "*/locales/*" -o -path "*/messages/*"`
3. 修复路径(根据根因选择):
- **翻译文件缺失**: 创建或补充对应语言的翻译文件
- **翻译 key 缺失**: 在翻译文件中补充缺失的 key
- **翻译加载逻辑错误**: 修复 useTranslation / getTranslation 的 locale 参数传递
- **服务端渲染未使用正确 locale**: 检查 getStaticProps / getServerSideProps 的 locale 参数
4. `Edit` 修复相关文件
5. 重测: `browser_navigate` → 确认页面内容语言与 URL locale 一致
### AUTO-FIX #28: NEXT_LOCALE Cookie 同步max 2 次)
**适用**: FAIL #28 — NEXT_LOCALE Cookie 缺失或值与 URL locale 不匹配
**为什么这是 Bug**: Cookie 与 URL locale 不同步会导致刷新后语言回退、服务端渲染语言不一致、用户语言偏好丢失。
**检测触发条件**journey-execution.md 6.5d 子步骤):
- `browser_evaluate``document.cookie` 中 NEXT_LOCALE 值与 URL locale 不一致
- 例: URL `/zh/dashboard``NEXT_LOCALE=en`
**流程**:
1. 确认当前 URL locale 和 cookie 值:
- `browser_evaluate``JSON.stringify({url: window.location.pathname, cookie: document.cookie})`
2. `Read` middleware 文件(通常 `src/middleware.ts`:
- 检查是否在重定向/路由时设置 NEXT_LOCALE cookie
- 检查 cookie 的 domain/path/secure 设置
3. 修复路径(根据根因选择):
- **middleware 未设置 cookie**: 在 locale 检测逻辑后添加 `response.cookies.set('NEXT_LOCALE', locale)`
- **cookie 只在特定路径设置**: 扩大 cookie 设置的覆盖范围(所有含 locale 前缀的路径)
- **客户端切换语言未更新 cookie**: 在语言切换组件中添加 `document.cookie = 'NEXT_LOCALE=...'`
4. `Edit` 修复 middleware 或组件
5. `Bash` 重启服务
6. 重测: 清除 cookie → `browser_navigate` → 检查 cookie 值与 URL locale 一致
**修复模板middleware cookie 同步)**:
```typescript
// 在 middleware 的 response 创建后添加:
const response = NextResponse.redirect(newUrl);
response.cookies.set('NEXT_LOCALE', locale, {
path: '/',
maxAge: 60 * 60 * 24 * 365, // 1 year
sameSite: 'lax'
});
```
**修复模板(客户端语言切换时同步 cookie**:
```typescript
// 在语言切换组件的 onChange/onSelect handler 中添加:
document.cookie = `NEXT_LOCALE=${newLocale};path=/;max-age=${60*60*24*365};samesite=lax`;
```

View File

@@ -0,0 +1,232 @@
# uni-app / 小程序 专属 FAIL 分类与修复模板
> **⛔ 仅 uni-app/小程序项目加载** — 当 `engine.md` 平台检测确认项目包含 uni-app 时读取此文件。
> 此文件包含 FAIL 分类表中 #17/#18/#19/#21/#23 的完整定义和修复模板。
---
## 1. FAIL 分类uni-app 专属)
| # | FAIL 特征 | 分类 | 修复类型 | 示例 |
|---|-----------|------|---------|------|
| 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 |
| 21 | uni-app 页面使用 `onMounted` 获取数据,但该页面是 `uni.navigateBack()` 的返回目标,导致回退时数据不刷新 | Bug(数据陈旧) | AUTO-FIX | 提现后返回收益页余额未刷新onMounted 不再触发) |
| 23 | uni-app 原生组件在 H5 环境下不渲染/不可交互(`<checkbox>`/`<radio>`/`<switch>`/`<slider>`/`<picker>` 等)| Bug(平台不兼容) | AUTO-FIX | H5 环境下 `<checkbox>` 不渲染,`<radio>` 无法点击。⛔ 必须替换为纯 view + CSS 自定义组件,参见 ../reference/platform-uni-app.md §8 |
---
## 2. 关键判断流程uni-app 专属)
- 前端代码含 `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。**禁止标记为"环境限制"跳过修复**
- `uni.navigateBack()` 返回后 `browser_snapshot` 数据未刷新 + 源码用 `onMounted``onShow` → Bug(#21) → AUTO-FIX替换 `onMounted``onShow`
---
## 3. 修复模板
### AUTO-FIX #17: 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 节"禁止的替代方案"
### 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 #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); }
```

View File

@@ -72,19 +72,12 @@ Playwright 操作返回错误/超时:
- ❌ 不重试直接标记 crash 并继续
- ❌ 将业务逻辑错误Toast/400/500误分类为 Playwright 崩溃
**Emoji → Iconify 替换方案(检测到 emoji 充当功能图标时使用 ⛔ 强制)**:
### 平台专属修复模板(⛔ 条件加载)
**完整安装配置、使用方式、禁止方案列表见 ../reference/platform-uni-app.md 第 1 节。禁止自行发明替代方案。**
以下修复模板已迁移至平台专属文件,仅当 engine.md 平台检测确认对应平台时加载:
适用条件: 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 节"禁止的替代方案"
- **uni-app 项目** → Read fully: `./platforms/uni-app.md`(含 #17 Emoji→Iconify、#18 双重导航栏、#19 下拉刷新、#21 onShow、#23 原生组件
- **i18n 项目** → Read fully: `./platforms/nextjs-i18n.md`(含 #25 locale 前缀、#26 html lang、#27 内容语言、#28 Cookie 同步)
**手机号+验证码登录实现方案(#11 触发时使用)**:
@@ -130,63 +123,45 @@ if (res.code) { code.value = res.code; }
uni.showToast({ title: "验证码已发送", icon: "success" });
```
### AUTO-FIX #18: 双重导航栏修复max 1 次)
### AUTO-FIX #16: 约束不一致修复max 1 次)
**适用**: FAIL #18页面在 pages.json 有 `navigationBarTitleText`(非 custom且页面源码有 `position:fixed` 的自定义 header
**适用**: FAIL #16旅程描述/UI 文案说"可选"但代码实际必填,或 UI 标记"选填"但接口必填
**为什么这是 Bug**: 系统导航栏与自定义头部同时显示,用户看到两层标题(如"首页"+"NEON PULSE"浪费屏幕空间且视觉混乱。uni-app 小程序项目应统一使用 `navigationStyle: "custom"`
**检测触发条件**(双条件同时满足):
- 条件 1声明方说"可选"(旅程描述含"可跳过/可选/选填/skip/optional/使用占位图" 或 页面文本含"(选填)/(可选)"
- 条件 2代码实际"必填"Toast 含"请上传/请填写/请选择/不能为空" 或 源码有 `if (!field)` 验证)
**为什么这是问题**: 用户被"可选"承诺误导,但操作时被强制校验拦截。真实用户体验受损。
**流程**:
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. 重测 → 截图确认只有一层标题
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. 修复后重测该步骤
**修复示例**:
```css
/* 修复前(首页 header 与系统导航栏重叠): */
.header { position: fixed; top: 0; left: 0; right: 0; height: 64px; }
.content { margin-top: 64px; }
**代码修复示例**(设计意图=可选时):
```javascript
// 修复前(强制必填):
if (!form.idCardFront || !form.idCardBack || !form.idCardHolding) {
uni.showToast({ title: "请上传所有证件照片", icon: "none" });
return;
}
/* 修复后(统一使用自定义导航栏): */
.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 修复:
**旅程修复示例**(设计意图=必填时):
```markdown
// 修复前:
{ "path": "pages/home/index", "style": { "navigationBarTitleText": "首页" } }
9. 用户第二步上传身份证照片(可跳过或使用占位图)→ 点击"下一步"
// 修复后:
{ "path": "pages/home/index", "style": { "navigationStyle": "custom" } }
9. 用户第二步上传身份证照片(必填:身份证正面、反面、手持照片)→ 点击"下一步"
```
### 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 包裹
@@ -226,177 +201,6 @@ export async function PUT(req, { params }) {
}
```
### 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 次)
**适用**: 测试数据过期、冻结期收益不可用、关联数据缺失
@@ -435,3 +239,64 @@ if (!form.idCardFront || !form.idCardBack || !form.idCardHolding) {
- 记录: `{journey, step, fail_reason, fix_type, fix_attempted, fix_result}`
- 继续执行下一个步骤(不阻断整条旅程)
- 旅程完成后统计: `steps_passed / steps_total` → 决定整体 PASS/FAIL
### AUTO-FIX #29: 前端数据映射修复max 2 次)
**适用**: FAIL #29 — API 返回正确数据但前端显示值不匹配
**为什么这是 Bug**: 用户看到的数值与实际数据不一致,影响信任度和功能正确性(如积分余额显示 0 但实际有 3000
**检测触发条件**:
- `browser_evaluate` 抓取 API 响应值(如 `fetch('/api/v1/subscriptions').then(r=>r.json())`
- `browser_snapshot` 读取页面显示的值
- 两者不一致 → FAIL
**流程**:
1. `browser_evaluate` 抓取 API 实际响应,确认 API 数据正确
2. `browser_snapshot` 确认页面显示值与 API 不一致
3. `grep` 搜索显示该数据的组件源码:
- 例: `grep -rn "credits" src/components/ src/app/`
- 例: `grep -rn "Current credits" src/`
4. `Read` 组件源码,定位数据提取逻辑:
- 检查是否从 API 响应的正确字段读取
- 检查是否在 state 更新前渲染
- 检查是否有类型转换/默认值覆盖问题
5. `Edit` 修复数据映射逻辑
6. 重测: `browser_navigate` → 验证页面显示值与 API 一致
**常见根因与修复**:
- API 返回 `{subscription: {credits: 3000}}` 但组件读 `data.credits`(少了一层嵌套)→ 修正字段路径
- 组件用默认值 0 初始化 stateAPI 请求未完成时已渲染 → 添加 loading 状态或条件渲染
- API 响应字段名与组件期望不一致 → 修正字段名映射
### AUTO-FIX #30: 静态资源 404 修复max 2 次)
**适用**: FAIL #30 — 静态资源(图片/图标/CSS/JS返回 404
**为什么这是 Bug**: 缺失的资源会导致页面显示异常(破碎图标、样式缺失),影响用户体验和专业感。
**检测触发条件**journey-execution.md 第 3 步资源加载状态检查):
- `browser_network_requests(static=true, filter="\\.(jpg|jpeg|png|gif|webp|svg|ico)$")`
- 检测到任何资源的 HTTP 状态码为 404/403
**流程**:
1. 从 network 请求中获取 404 资源的 URL 路径
2. 判断资源类型:
- **应用内资源**logo、图标等: `grep` 搜索引用该资源的源码文件
- **第三方/CDN 资源**: 检查 URL 是否正确,是否需要更新 CDN 链接
3. 修复路径:
- **路径错误**(如 `/sparklogo.png` → 实际在 `/images/sparklogo.png`: `Edit` 修正引用路径
- **文件缺失**: 检查是否在构建时被排除 → 修复构建配置或补充文件
- **动态导入路径错误**: 修正 `import``require` 路径
4. `Edit` 修复引用路径或补充资源文件
5. 重测: `browser_navigate``browser_network_requests` 确认资源 200
**常见修复模式**:
```tsx
// 路径错误修复:
// 修复前: <img src="/sparklogo.png" />
// 修复后: <img src="/images/sparklogo.png" />
// 或使用 Next.js Image:
import sparkLogo from '@/public/images/sparklogo.png';
<Image src={sparkLogo} alt="Spark" />
```

View File

@@ -239,7 +239,7 @@ Epic N+1 完成
| 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/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 |
| 页面 navigateBack 后数据陈旧 | `uni.navigateBack()` 返回目标页后 `browser_snapshot` 数据未更新 + 源码用 `onMounted``onShow` | Bug(#21) | AUTO-FIX: 替换 `onMounted``onShow`from `@dcloudio/uni-app`,参见 ./auto-fix/platforms/uni-app.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/状态同步/乐观更新 |
@@ -249,6 +249,19 @@ Epic N+1 完成
| 已修复标记盲信 | 旅程文件交互缺陷审计表标记"✅ 已修复",验证时不重新测试实际行为 | 漏检风险 | ⛔ 必须重新验证修复是否正确(不只是检查 handler 是否存在) |
| 漏识别 uni-app 交互 | 交互元素识别只覆盖 React(onClick)不覆盖 uni-app(@tap) | 覆盖漏洞 | 交互元素识别必须同时支持 onClick + @tap/@click/bindtap |
### i18n 检测规则(⛔ 条件激活 — 仅 i18n 项目)
> **激活条件**:项目存在 `src/i18n/` 目录,或 `next.config.*` 含 `createNextIntlPlugin` / `i18n` 配置,或旅程定义包含 locale 相关断言。单语言项目完全跳过以下所有规则。
| 检测项 | FAIL 条件 | 分类 | 修复动作 |
|--------|----------|------|---------|
| URL 缺少 locale 前缀 | 页面 URL 不包含 `/en/``/zh/`(或项目配置的 locale 列表)前缀 | Bug(#25) | AUTO-FIX: 修复导航链接/middleware 重定向添加 locale 前缀 |
| 导航跳转丢失 locale | 从 `/zh/xxx` 页面点击导航后 URL 变为 `/yyy`(无 locale或 locale 切换 | Bug(#25) | AUTO-FIX: 修复 Link/router.push 添加 locale 前缀 |
| html lang 不一致 | `document.documentElement.lang` 与 URL 路径中的 locale 不匹配(如 `/zh/``lang="en"` | Bug(#26) | AUTO-FIX: 修复 layout.tsx 的 html lang 属性绑定 |
| 内容语言不一致 | URL 含 `/zh/` 但页面可见文本包含英文整句(非专有名词),或 URL 含 `/en/` 但包含中文字符 | Bug(#27) | AUTO-FIX: 替换硬编码文本为翻译键 |
| locale cookie 缺失 | 页面加载后 `document.cookie` 不包含 `NEXT_LOCALE`(或项目配置的 cookie 名) | Bug(#28) | AUTO-FIX: 在 middleware/layout 中设置 locale cookie |
| locale cookie 值不一致 | `NEXT_LOCALE` cookie 值与 URL 路径 locale 不一致 | Bug(#28) | AUTO-FIX: 修复 cookie 设置时机和值来源 |
> 完整的 FAIL 分类标准(含兜底规则 #99见 ./auto-fix/classification.md修复流程见 ./auto-fix/workflows.md
**违反此规则的行为**

View File

@@ -94,6 +94,17 @@
3. **英文枚举检测**
- 页面可见文本中无英文大写枚举值(如 APPROVED/REJECTED/PENDING/PAID 等)
3.8. **i18n 语言一致性检测**(⛔ 条件激活 — 仅 i18n 项目):
- 前置检测:`glob src/i18n/**/*` 或 `next.config.*` 中 i18n 配置存在 → 启用
- 项目无 i18n 配置 → 跳过以下所有检查
- `browser_evaluate` → `window.location.pathname` → 提取 locale如 `/zh/` → `zh`
- `browser_evaluate` → `document.documentElement.lang` → 验证与 URL locale 一致
- `browser_evaluate` → `document.body.innerText.substring(0, 2000)` → 语言检测:
- URL `/zh/` → 文本应包含中文Unicode `\u4e00-\u9fff`),不应有英文整句
- URL `/en/` → 文本应为英文,不应有中文字符
- `browser_evaluate` → `document.cookie` → 验证包含 NEXT_LOCALE
- FAIL → 按检测即阻断表 i18n 规则处理(#25-#28
3.5. **SVG data URI 源码扫描**(⛔ 强制 — 每次视觉验证关卡必须执行):
- `grep -rn "data:image/svg+xml" app/mini-program/src/`(或当前小程序源码目录)
- 发现任何匹配 → 标记 svg_data_uri_check.found = FOUND → 记录文件列表
@@ -225,6 +236,19 @@ verification_detail:
status: 403/404
type: "image/jpeg"
details: "描述"
i18n_check: # ⛔ 条件激活 — 仅 i18n 项目
enabled: true/false # 项目是否有 i18n 配置
locale_cookie_name: "NEXT_LOCALE" # 项目配置的 locale cookie 名
supported_locales: ["en", "zh"] # 项目支持的 locale 列表
url_locale: "zh" # 从 URL 提取的 locale
html_lang: "zh" # document.documentElement.lang
lang_match: true/false # html lang 与 URL locale 是否一致
content_language_match: true/false # 页面内容语言与 URL locale 是否一致
content_mismatch_samples: [] # 不匹配的文本样本
locale_cookie_present: true/false
locale_cookie_value: "zh"
cookie_match: true/false # cookie 值与 URL locale 是否一致
details: "描述"
```
**⛔ 步骤级关联**:上述 verification_detail 中发现的每个问题,必须同时记录到对应步骤的 `step_results[].issues_detected` 中(含 `step_id` 关联),确保问题可追溯到具体操作步骤: