verification-gate

v2026.09.24

代码改完后的分层验证执行与证据记录。完成 feature / 重大变更 / 创建 PR / 重构 / 声称「修完」前使用——按项目运行 build、unit、e2e、真机等代表性阶段并记录真实结果(编译过 ≠ 功能可用)。覆盖 Tauri 桌面 / Web / 服务 / Skill 四类分支,可把已执行事实转换为 production-engineering-audit staged receipt;完成等级与发布结论由 production-engineering-audit 裁决。不要用于:业务领域验证、Skill 质量审查(用 skill-lint)、纯文档变更、一次性脚本。

GitHub
Install command
npx skhub add cat-xierluo/verification-gate
Markdown
SKILL.md

Verification Gate(代码改完后的验证门禁)

本 skill 是「代码改完 → 声称完成 / 提交 PR」之间的验证执行与证据记录层。核心解决一个问题:编译过 ≠ 功能可用。

它负责真实运行适用阶段、观察功能终态并保留阶段事实;production-engineering-audit(PEA)是完成 层级的唯一裁决者。需要机器可消费的分层证据时,读取 references/staged-receipt.md,用确定性转换器生成 staged receipt, 再交给 PEA。不要在本 Skill 内另造 READY / RELEASED 判决。

直接教训:改完 reader worker 加载,typecheck / build / lint / 单测全过,就声称「修完」,实机却「文字层未知」(textLayerStatus 卡 unknown)崩——编译层根本抓不到运行时功能问题。这类坑的唯一解是 e2e(功能验证)+ 真机。

工作原则

  • 先编译层(1-4),再功能层(5-6):编译层是前置门禁,但不充分;功能层(e2e + 真机)才是完成线。
  • e2e/真机失败必须如实记录:5/6 不过就不能作为对应完成声明的通过证据,无论 1-4 多干净; 是否达到项目要求的完成层级交给 PEA 按项目契约裁决。
  • 断言功能结果,非「存在元素」:教用户写 e2e 时断言「canvas 像素非空 / textLayerStatus ≠ unknown / 点击后面板真的弹出」,而非「存在 canvas / 存在按钮」(防伪渲染 / 假成功)。
  • Bug 修复必须新增复现测试(回归规范):修完一个 bug,加一条能复现该 bug 的 e2e/单测,防止回归。
  • 套件悬挂 = 未通过:npm test 永不退出(summary 不打印 / 进程不退)按 P0 基础设施缺陷处理,诊断 playbook 见 references/lessons-from-practice.md 教训 12;用子集绕开只能是登记过任务的临时态——悬挂会掩盖真实失败(教训 11:修复当日暴露 3 条被藏数周的失败)。
  • 不采信生产者(worker/PM 自己)自报 PASS:跑实际命令,看实际输出。
  • 按项目类型选验证命令:Tauri 桌面 / Web / 服务 / Skill,命令不同(见 §项目类型分支)。

8 阶段验证

#阶段门禁完成线CI
1构建(build / cargo check)失败则停,不继续前置✅ 进 CI(PR 阻断项)
2类型检查(typecheck)关键错误清零前置✅ 进 CI(PR 阻断项)
3Lint(--max-warnings=0 + 依赖环)零警告 + 无依赖环前置✅ 进 CI(PR 阻断项)
4单元测试(vitest 分层)通过 + 覆盖率达标前置✅ 进 CI(PR 阻断项)
5e2e 功能验证(Playwright)功能结果断言(非存在元素)核心完成线✅ 进 CI(PR 阻断项,关键)
6真机验证(etv / build / staging)真实运行时行为核心完成线⚠️ 通常本地 / staging 跑(CI 难模拟真机,按需)
7安全扫描(密钥 / debug 日志)无泄露 / 无遗留 console.log前置✅ 进 CI(PR 阻断项)
8Diff 审查(范围 / 越界)变更合规,无 forbidden 文件前置✅ 进 CI(PR 阻断项)

CI 列说明:✅ = 建议在 CI 自动跑、失败即阻断 PR 合并;⚠️ = 真机环境 CI 一般难以模拟,放本地或独立 staging 流水线跑。本地手动跑时 8 阶段全跑,CI 跑 1-5 + 7-8(缺真机)。

阶段 1-4:编译层(前置门禁,不充分)

# 1 构建
npm run build 2>&1 | tail -20          # 前端
cd src-tauri && cargo check 2>&1 | tail # Tauri Rust

# 2 类型检查
npm run typecheck 2>&1 | tail

# 3 Lint(严格:零警告 + 依赖环)
npm run lint -- --max-warnings=0 2>&1 | tail
npx depcruise --config .dependency-cruiser.cjs src 2>&1 | tail  # 依赖环(如有)

# 4 单元测试(分层)
npm test 2>&1 | tail -50                # vitest(unit + integration + dom 分层)

门禁:任一失败 → 停,修复后再继续。但这 4 层全过 ≠ 功能可用。

阶段 5:e2e 功能验证(核心完成线)

这是「编译过 ≠ 功能可用」的唯一解。用 Playwright(或等价)驱动应用,断言功能结果。

npm run test:e2e -- tests/e2e/reader-renders.test.ts   # FaroPDF 实际路径示例
# 或项目既有:npm run verify:reader-e2e(chromium 真 Worker 功能门禁)

断言深度——不只「存在元素」,断言功能结果(见 references/assertion-depth.md)。

门禁事实:e2e 不过就记录 failed,不把该阶段当作 behavior-complete 证据;由 PEA 判断当前 声明是否被阻断。

阶段 6:真机验证(核心完成线)

dev server e2e 不够——真实运行时(Tauri WKWebView / Web build 产物 / 服务 staging)行为可能不同。

项目类型真机验证
Tauri 桌面npm run tauri build 产物实机(或 etv:WEBKIT_INSPECTOR_SERVER + tauri dev 真机 DOM + 截图)
Webnpm run build + vite preview(build 产物,非 dev server)
服务staging 环境真实请求

阶段通过条件:真机行为与 dev e2e 一致;prod-only 问题(worker/协议/路径)必须真机抓到。

阶段 7-8:安全 + Diff(前置)

# 7 安全
grep -rnE "sk-|api_key|password|token" --include="*.ts" --include="*.rs" src/ src-tauri/ 2>/dev/null | grep -viE "test|fixture|env|placeholder" | head
grep -rn "console.log" --include="*.ts" --include="*.tsx" src/ 2>/dev/null | head

# 8 Diff 审查
git diff --stat
git diff HEAD~1 --name-only   # 逐文件审:非预期变更 / 缺失错误处理 / forbidden 文件

项目类型分支(按项目选验证命令)

Tauri 桌面

npm run build && cd src-tauri && cargo check   # 1 构建
npm run typecheck                                # 2 类型
npm run lint                                     # 3 lint
npm test                                         # 4 单测
npm run test:e2e                                  # 5 e2e(打开 PDF 渲染 + textLayerStatus 断言;jsdom 层)
npm run verify:reader-e2e                          # 5+ chromium 真 Worker 门禁(dev server 自起自灭)
npm run verify:prod-render                         # 5+ 产物层门禁(vite preview build 产物 + 真实 UI + Retina DPR=2)
# 6 真机:npm run tauri build 产物实机 / etv(npm run etv:dev + etv:run,WKWebView 真机 DOM + 截图)
#    ⚠ 桌面 WebView 四层验证盲区(引擎代差/DPR/产物嵌入/dev≠打包)见教训 13:
#    chromium 全绿 ≠ WKWebView 过;真机 console(构建戳确认版本)是唯一决定性证据

真机验证做法见 references/e2e-practice.md(Tauri etv 段)。

Web

npm run build && npm run typecheck && npm run lint && npm test   # 1-4
npm run test:e2e                                                  # 5 Playwright(CI 也跑)
# 6 真机:npm run build + vite preview(build 产物)

服务

npm run build && npm run typecheck && npm run lint   # 1-3
npm test                                              # 4 unit + integration
# 5 integration test(HTTP 请求断言响应)
# 6 真机:staging 环境真实请求

Skill

引用 skill-lint(已有 Skill 创建质量审查)。本 skill 不重复 skill-lint 的职责。

触发时机

  • 完成 feature 或重大代码变更后
  • 创建 PR 之前(PR 合并的硬门禁)
  • 重构之后
  • 声称「修完」「behavior-complete」「done」之前(先产出阶段事实,再由 PEA 对完成层级裁决)
  • Bug 修复后(+ 新增复现测试)
  • release / 交付收尾、把 NOT_VERIFIED 清单「移交用户」之前——先分层收口(见 §NOT_VERIFIED 分层收口)

持续模式:长会话中每 15 分钟或重大变更后执行(心智检查点:完成函数后 / 完成组件后 / 切换任务前)。

本地验证 vs CI 门禁(必须搞清楚)

不需要 GitHub Actions 也能做完整验证。 本 skill 的 8 阶段本质是「一组要在代码声称完成前跑的命令 + 判定标准」,它在哪跑、谁来跑是独立的:

  • 本地验证:你(或 AI 代理)手动跑 build → typecheck → lint → test → test:e2e,看实际输出并记录阶段事实。即时、灵活,但靠自觉——容易「编译过了就声称修完」(正是本 skill 要防的坑)。
  • CI 门禁(GitHub Actions / GitLab CI / Gitea / Jenkins 等):把同一组命令写进流水线,在 push / 开 PR 时自动跑,失败就阻断 merge。它不改变「验证什么」,只是把门禁变成客观强制——没过门禁的代码合不进去。

关键认知:

  1. CI 不是「另一种验证」,是同一套验证的自动化载体。本地能过、CI 才能过;本地乱跳阶段,CI 会拦回来。
  2. 平台不限 GitHub Actions。可选:GitHub Actions、GitLab CI、Gitea Actions、Jenkins;本地也能用 act(本地跑 GitHub Actions)、lefthook / husky 的 pre-push hook 在提交前自动跑。
  3. 没有 CI 也能用本 skill——只要你在声称「修完 / 提 PR」前,本地跑完适用阶段、保存证据并交由 PEA 判定。CI 是「团队不用担心有人跳过门禁」的强化,不是前提。
  4. 真机(阶段 6)CI 一般难模拟:Tauri WKWebView、build 产物行为、staging 真实请求,通常放本地或独立 staging 流水线。CI 跑 1-5 + 7-8,真机缺口要在验证报告的「6 真机」栏记原因(NOT_RUN 需充分理由)。

落地建议:先本地把 8 阶段跑顺、阶段报告用熟;再把它搬进 CI(见 references/e2e-practice.md 的「CI 门禁」通用模板)。同一阶段在本地与 CI 的事实应一致,冲突时保留证据并排查环境差异,不手工覆盖红灯。

本地开发:哪些验证必要(按场景的最低清单)

8 阶段不是每次都要亲力亲为——编译层(1-4)工具链逼你跑,真正容易漏、也最该记住的是功能层(5-6),因为「编译过 ≠ 功能可用」。按场景取最低必要集:

日常改 bug / 写功能(本地循环中)

1 构建 → 2 类型 → 4 单测(改了逻辑就跑)→ 5 e2e(宣称修好前必跑)
  • 1-2 顺手跑(build / typecheck 一把过)。
  • 4 单测:纯逻辑改动必跑;只改样式可跳过。
  • 5 e2e 是这条清单的灵魂——改完宣称「修完」前必跑,断言功能真的出来(像素非空 / textLayerStatus ≠ unknown),这是编译层抓不到的坑的唯一解。

准备提 PR / 声称「修完」

日常清单 + 6 真机(至少 build 产物跑一遍)+ 7 安全(grep 自查)+ 8 Diff(看 git diff)
  • 6 真机:dev 跑得好,build 产物 / 真机可能崩(worker / 路径 / 协议差异)。
  • 7 安全:grep 密钥 / console.log,或交给 CI。
  • 8 Diff:人工看 git diff,确认只改了该改的,没越界 / 误删 / 含 forbidden 文件。

能自动化就别手动记着跑(pre-push hook / CI 挡)

  • 3 Lint:husky / lefthook pre-push 自动跑,或 CI 跑。
  • 7 安全扫描:CI 跑更稳,不靠自觉。

关键认知

  • 你本地必须亲力亲为的:1-2-4 确认代码层面 OK + 5 e2e 确认功能真的可用。
  • 提 PR 前补的:6 真机 + 8 看 diff。
  • 能自动化就别手动的:3 lint、7 安全。
  • 最低线不是 1-4:到你宣称「完成」那一步之前,5(及该场景下的 6)必须过。1-4 全过 ≠ 可以声称修完。

真实教训:worker 改完 typecheck / build / lint / 单测全过,实机 textLayerStatus 卡 unknown 崩——证明 1-4 全过 ≠ 功能可用,5/6 不过就是没修完。

NOT_VERIFIED 分层收口(release / 交付收尾)

功能挂了 NOT_VERIFIED 后,收尾时禁止整批移交用户——先逐项二分:

类别判据动作
Web 层可验功能面存在于浏览器 DOM:UI 渲染 / 交互 / localStorage 持久化 / 剪贴板(chromium grant 权限可断言真实内容)Agent 当场写 Playwright spec 验证,转正为回归 e2e(进 CI),并把 NOT_VERIFIED 口径收窄到实际剩余
真需真机依赖 OS / Tauri runtime / webview 特异行为:系统 API 注册、invoke 后端链路、WKWebView 观感保留 NOT_VERIFIED,精确到剩余粒度(「仅 WKWebView 剪贴板写入」,不是整条 feature)
  • 自检问句:「这份清单里有多少是我没跑 Playwright,而不是真验不了?」
  • 细节与案例见 references/lessons-from-practice.md 教训 10;className→CSS 真实渲染断言见 references/assertion-depth.md「视觉 / CSS 类」。

最终输出:阶段报告

| 阶段        | 结果                          |
|-------------|-------------------------------|
| 1 构建      | PASS / FAIL                   |
| 2 类型      | PASS / FAIL (X errors)        |
| 3 Lint      | PASS / FAIL (X warnings)      |
| 4 单测      | PASS / FAIL (X/Y, Z% cov)     |
| 5 e2e 功能  | PASS / FAIL (X specs)         |  ← 硬门禁
| 6 真机      | PASS / FAIL / NOT_RUN(记原因) |  ← 硬门禁
| 7 安全      | PASS / FAIL (X issues)        |
| 8 Diff      | X files, 范围合规/越界        |
| CI 门禁     | 1-5+7-8 全绿 / 有 job 红(阻断说明) |

表格适合人读,但不得附加本 Skill 自行裁定的 READY / RELEASED。需要机器消费时,把同一批真实阶段 事实写成 verification-gate-stage-report/v1,按 references/staged-receipt.md 转换,再由 PEA 根据 target、consumer、 候选 commit 与项目最低层级裁决。失败阶段同样必须进入回执,不能因为想生成绿灯而省略。

完成定义(落地)

代码改动声称「完成」必须:

  1. 1-4 编译层全过(前置)。
  2. 5 e2e 功能验证过(核心)。
  3. 6 真机验证过(核心,桌面/Web/服务按类型)。
  4. 7-8 安全 + Diff 合规。
  5. Bug 修复新增复现测试(回归)。
  6. 输出完整阶段事实和仓内 evidence,并把完成声明交给 PEA 裁决。

e2e/真机不过 = 对应阶段失败:不得把失败阶段伪装为通过或省略;是否阻断提交、合并或发布, 由 PEA 结合项目配置与声明层级判定。

依赖

系统依赖

依赖安装方式
node / npm项目自带
rust / cargo(Tauri 项目)macOS: brew install rust

项目依赖(按类型)

项目类型依赖用途
通用vitest / eslint / tsc单测 / lint / 类型
通用playwrighte2e 功能验证
Tauri 桌面@tauri-apps/clitauri build / dev 真机
通用(可选)dependency-cruiser依赖环检测(阶段 3)

参考文档(references/,按需读取)

  • references/eight-phases-rationale.md:想搞懂「为什么是 8 阶段、编译过为啥不等于能用」——各阶段证明/不证明什么、e2e+真机补什么盲区、反例。对应 §8 阶段验证 与 §本地开发:哪些验证必要。
  • references/assertion-depth.md:写 e2e 时——断言功能结果 vs 只断言存在元素(防伪渲染/假成功),跨渲染/状态/交互/API 四类案例。对应 §阶段 5。
  • references/e2e-practice.md:落地 e2e / 真机 / CI——Playwright spec 写法、fixture 矩阵、CI 通用模板(平台不限 GitHub Actions)、Tauri etv 真机。对应 §阶段 5/6 与 §本地 vs CI 门禁。
  • references/test-pyramid.md:组织单测 / verify 脚本 / 回归规范——vitest 分层、build 内嵌 verify、bug 修复必加复现测试、lint 严格。对应 §阶段 3/4。
  • references/lessons-from-practice.md:跑 skill 踩过的坑——pre-existing 失败判定、e2e 缺失、Tauri invoke 限制、快速模式 vs 全量、真机证据来源等,持续反哺。对应 §本地开发清单 的「快速模式」与 §阶段 4/5/6 边界。
  • references/staged-receipt.md:阶段已经真实执行后——输入契约、证据路径、HEAD 绑定、转换命令与 PEA 消费边界。对应 §最终输出。

Related Skills

维度verification-gateskill-lintproduction-engineering-audit
定位分层验证执行与事实记录Skill 创建/改造后的质量审查结构性浪费与完成声明裁决
对象Tauri/Web/服务/Skill 的真实阶段Skill 本身(SKILL.md/references/契约)项目配置、热路径与完成证据
关系生产 staged receipt,不裁定等级验证 Skill 结构合规消费 receipt,裁定证据支持上限

代码项目用 verification-gate 执行阶段,Skill 用 skill-lint 审结构;改 Skill 的代码脚本时两者都适用。 准备声称「已修复 / 已完成 / 可发布」时,再把 staged receipt 交给 production-engineering-audit;三者不 互相替代。

Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

Not specified

Source path

skills/verification-gate

Default branch

main

Latest commit

4071b59

Tree SHA

f189544