跳转到内容
OmniRoute source

Quality-Gate System — Critical Assessment, Catalog and Replication Playbook (中文 (简体))

总体评级:A− / “高级”。位列项目中的前约 5–10%。 该系统独立实现了多种 行业明确定义的模式——这是最有力的一致性信号(我们并非照抄检查清单,而是殊途同归地采用了正确实践)。

参考框架 我们的现状 评级
OWASP DSOMM(5 个级别、5 个维度) 稳居第 3 级,并在_测试强度_和_静态分析深度_方面达到第 4 级。大多数组织处于第 1–2 级。 L3→L4
OpenSSF Scorecard(18 项检查) 我们通过了 CI-Tests、Code-Review、Dependency-Update-Tool、Fuzzing、SAST、Signed-Releases(来源证明)、Token-Permissions、Vulnerabilities、Dangerous-Workflow。差距: main 的 Branch-Protection 处于关闭状态;部分 actions 未固定版本。 ~7–8/10
SLSA(4 个级别) npm publish --provenance + id-token: write + GitHub 托管构建 = L2,正接近 L3。要达到 L3+,还缺少经过加固的/密闭式构建器。 L2→L3
SonarQube “Clean as You Code” 理念完全一致:棘轮门禁保障_不回退_(新代码不会使指标恶化)。差异: Sonar 建议只设置少量条件;我们有约 46 项门禁(存在疲劳风险)。 一致,但有注意事项
Quality-Ratchet 模式 参考级实现:棘轮 + dedicatedGate + tightenSlack + --require-tighten + 优雅跳过。比大多数公开示例更为成熟。 典范
DORA 2024 在_稳定性_维度表现非常出色。风险:繁重的门禁可能会延长_交付周期_——已通过快速门禁拆分缓解,但仍存在覆盖缺口(参见第 2 部分)。 强(稳定性)
OWASP LLM Top 10 (2025) 我们通过运行时防护 + promptfoo(评估)+ garak(红队测试)覆盖了风险 #1(提示词注入)。采用的是行业标准工具。 已覆盖
变异测试 Stryker 每夜运行,阈值为 70/50,覆盖 8 个关键模块。行业共识为(现有代码 60% / 新代码 80%,每夜运行)——我们超越了这一标准。**差距:**分数尚未纳入棘轮机制。 接近完善

第 2 部分 — 批判性评估(优势 + 坦诚的不足)

Section titled “第 2 部分 — 批判性评估(优势 + 坦诚的不足)”
  1. 多指标棘轮引擎。 这是系统的核心。quality-baseline.json 中包含 24 项指标
    • 4 个专用基线,每个都有方向(up/down)、容差(eps)、余量 (tightenSlack)和 dedicatedGate 标志。已经修复的问题会一直保持修复状态——这是 对抗代码库熵增的良方。
  2. 供应链纵深防御。 SAST(CodeQL/Sonar)+ 密钥检测(启用 useDefault 的 gitleaks)
    • SCA(osv/npm-audit/Trivy/Dependabot)+ 许可证检查 + 锁文件检查 + SBOM + SLSA 来源证明 + Scorecard + 工作流加固(zizmor)。很少有代码库具备如此完整的防护体系。
  3. 对抗古德哈特定律的措施。 将覆盖率作为目标是一种经典的反模式 (“当一项指标成为目标时,它就不再是一项好指标”)。我们设置了相应的制衡机制: 变异测试(衡量测试是否能捕获缺陷,而不只是是否执行了某一行)、 check-test-masking(阻止通过弱化断言来使测试通过)、 每模块覆盖率下限(强制测试高风险代码,而不只是容易测试的部分),以及 check-pr-evidence(硬性规则 #18)。
  4. 反幻觉/一致性门禁。 这是一个少见且很有价值的类别:check-known-symbols、 check-fetch-targets、check-openapi-routes、check-docs-symbols 确保文档、规范和 字符串分派所指向的符号确实存在。能够捕获 lint/测试无法发现的“腐化”问题。
  5. 从建议性到阻断性的生命周期。 新门禁最初作为建议性检查引入(在成熟之前不阻止合并), 随后在周期结束时升级为阻断性检查。在不降低质量上限的前提下减少摩擦。
  6. 基础设施缺失时优雅跳过。 当二进制文件/网络失败时,扫描器(--ratchet)以 exit 0 退出——基础设施缺失不会阻止合法 PR。这是成熟的工程实践。
  7. 将文化编入规则。 硬性规则 + trust-but-verify + 过期允许列表 + 证据门禁, 将工程纪律转化为自动化验证。
  1. 🔴 快速门禁的拆分仍存在结构性漏洞。 quality.yml(PR→release/**) 现在会针对代码 PR 运行类型检查、快速确定性测试,以及建议性的生产构建, 但仍未运行 ci.yml 中完整的发布 PR 检查范围(覆盖率棘轮、 包制品、集成测试、E2E、SonarQube)。追求速度的动机是合理的,但门禁应该设置在 实际发生合并的位置(左移)。这是目前最大的待解决结构性问题。
  2. 🟠 门禁泛滥/疲劳风险。 约 46 个门禁 + 25 个作业,数量非常庞大。Sonar 本身也警告: 条件过多会导致“门禁疲劳”和优先级争论,并可能造成某些门禁被忽略。 DORA 警告称,繁重的门禁会增加交付周期。我们通过建议性分层和非绝对棘轮来缓解这一问题, 但目前缺少针对每个门禁的定期 ROI 审查(部分用于文档同步的微型门禁可以合并)。
  3. 🟠 变异分数尚未纳入棘轮。 对抗覆盖率指标博弈的最强措施目前仍是 建议性检查。这是价值最高的待办事项(而且已经完成了 90%)。
  4. 🟡 应在适当范围内升级为阻断性的建议项。 osv(vulnCount)和 oasdiff 尽管拥有冻结基线,却仍是建议性检查。将 osv 设为建议性检查是有道理的 (旧依赖中新出现的 CVE 可能会阻止一个不相关的 PR)——但可以采取折中方案 (仅阻止 CRITICAL 且可修复的问题,就像我们对 Trivy 所做的那样)。 oasdiff 保持建议性意味着破坏契约的变更仍可能通过。
  5. 🟡 运行时安全检查仅在夜间执行。 schemathesis/garak/promptfoo/chaos/k6 在夜间运行。 这是合理的决定(速度慢且需要在线服务器),但 PR 可能引入注入防护回归, 而这个问题只能在第二天夜间才会被发现。
  6. 🟡 main 上的分支保护处于关闭状态。 BRANCH_LOCK_TOKEN 会锁定 release 分支, 但 main 本身不受保护。这会被 Scorecard/DSOMM 扣分。需要所有者采取行动。
  7. 🟡 CodeQL 使用默认设置;semgrep 未纳入代码化配置。 默认设置可以正常工作 (0 条警报),但提交一个 codeql.yml 可以获得更强的控制能力;semgrep 通过外部云平台运行, 其配置未在仓库中进行版本管理。

第 3 部分 — 完整的质量检查点目录(可移植)

Section titled “第 3 部分 — 完整的质量检查点目录(可移植)”

以下 12 个类别以可复用的形式构成了“质量体系”。每个类别都列出了 目标(要保护什么)、我们使用的工具以及可在任何技术栈上复现的与工具无关的等效方案。

1. 风格与格式(确定性、快速)

Section titled “1. 风格与格式(确定性、快速)”
  • **OmniRoute:**通过 lint-staged 在预提交阶段运行 Prettier + ESLint,采用 2 空格缩进/双引号/每行 100 列。
  • **通用方案:**一个可自动修复的格式化工具 + 一个代码检查工具,在预提交阶段对暂存文件运行。
  • OmniRoute:typecheck:core(阻断性)+ typecheck:noimplicit:core(建议性)+ type-coverage 棘轮阈值 92.17% + 每个文件的 any 预算。
  • **通用方案:**在 CI 中执行严格类型检查 + 只升不降的类型覆盖率指标 + 每个文件的 any/逃生舱预算。
  • OmniRoute:2 个互不重叠的运行器(Node 原生 + vitest)、8 个分片、全局覆盖率 60/60/60/60 + 约 76% 的棘轮阈值 + 关键模块的 8 个逐模块最低阈值 + 每夜属性测试 + 每夜变异测试。
  • 通用方案:测试运行器 + 绝对覆盖率下限(防止归零)+ 覆盖率棘轮阈值(防止回退)+ 高风险代码的逐模块最低阈值(反古德哈特定律)+ 针对纯逻辑的基于属性的测试 + 每夜运行变异测试,将其作为衡量测试质量的真正指标。
  • OmniRoute:pr-test-policy(生产代码必须有测试)、check-test-masking(阻止弱化断言)、pr-evidence(成功声明必须附带证据块)、test-discovery(每个测试都必须被某个运行器收集)。
  • 通用方案:“新代码 ⇒ 新测试”门禁 + 断言删除/恒真表达式检测器 + 证据要求(TDD 或持续有效的测试)+ 保证不存在游离于匹配模式之外的孤立测试。

5. 复杂度与代码健康度(棘轮阈值)

Section titled “5. 复杂度与代码健康度(棘轮阈值)”
  • **OmniRoute:**ESLint 警告数(3769↓)、jscpd 重复率(5.72%↓)、圈复杂度+最大行数复杂度(1800↓)、sonarjs 认知复杂度(753↓)、knip 死代码/未使用导出(339↓)、逐文件大小(冻结,只能缩小)、循环依赖(自定义 Tarjan,阻断性)。
  • 通用方案:对每项健康度指标设置棘轮阈值(警告、重复、圈复杂度和认知复杂度、死代码、文件大小、导入循环)。方向始终是“不得回退”。
  • **OmniRoute:**CodeQL(告警棘轮阈值 = 0)、gitleaks([extend] useDefault=true——至关重要!)、SonarQube、自定义安全规则(public-creds、error-helper、route-guard-membership、route-validation)。
  • 通用方案:采用 SAST(CodeQL/Sonar/semgrep)并设置告警棘轮阈值 + 使用继承默认规则集的密钥扫描器(覆盖默认规则的自定义配置 = 产生盲区)+ 项目特定的“硬规则”安全门禁。
  • **OmniRoute:**osv-scanner + npm-audit + Trivy + Dependabot(SCA)、license-checker(SPDX 允许列表)、lockfile-lint(HTTPS+sha512+注册表)、check-deps 防 slopsquatting(允许列表 + 包龄 ≥72 小时)。
  • **通用方案:**多源 SCA + 许可证允许列表 + 锁文件完整性检查 + 带包龄/typosquatting 检查的依赖项允许列表 + 分组更新机器人。
  • **OmniRoute:**SBOM(CycloneDX + syft)、SLSA 来源证明(--provenance)、OpenSSF Scorecard(每周)、工作流加固(zizmor:artipacked→persist-credentials:false、缓存投毒、令牌权限)。
  • **通用方案:**发布时生成 SBOM + 已签名的来源证明(SLSA L2+)+ 定期运行 Scorecard + 加固所有工作流(最小权限令牌、非推送方检出时不持久化凭据、操作按 SHA 固定)。
  • **OmniRoute:**oasdiff(OpenAPI 破坏性变更)、schemathesis(每夜契约模糊测试)、openapi-coverage(已文档化路由的百分比,棘轮阈值 38.3%)、openapi-security-tiers(规范与路由守卫对比)。
  • **通用方案:**破坏性变更契约差异检查(oasdiff/buf)+ 基于规范的属性模糊测试(schemathesis)+ 只升不降的文档覆盖率 + 规范↔代码一致性。
  • **OmniRoute:**docs-sync(镜像版本)、docs-counts-sync(文档与代码中的数字)、env-doc-sync、doc-links、fabricated-docs、cli-i18n、i18n-ui-coverage(--threshold=65 + 棘轮阈值 80.1%)。
  • **通用方案:**同步文档与代码之间的版本/数量/环境变量(使用门禁,而非依赖信任)+ 验证内部链接 + 只升不降的国际化覆盖率。
  • **OmniRoute:**known-symbols(字符串分派 ⇒ 真实存在的符号)、provider-consistency、fetch-targets(客户端 fetch ⇒ 真实存在的路由)、docs-symbols、db-rules(硬规则 #2/#5)、migration-numbering。
  • **通用方案:**针对每一处“重复的事实来源”(注册表、字符串分派、跨层引用),设置门禁来证明两端相互匹配。这能捕获类型检查和测试无法发现的腐化问题。
  • **OmniRoute:**chaos(故障注入)、heap-growth(泄漏)、k6(浸泡测试)、promptfoo+garak(针对 LLM 的 OWASP LLM Top 10 红队测试)、3 条韧性法则(断路器/冷却期/锁定)。
  • 通用方案:识别你的领域中的故障模式,并为每一种模式设置门禁(即使仅每夜运行)。对于 AI 应用:注入攻击红队测试。对于分布式系统:混沌测试 + 泄漏测试 + 浸泡测试。

第 4 部分 — 适用于任何项目的复刻计划

Section titled “第 4 部分 — 适用于任何项目的复刻计划”

按阶段构建,每个阶段都能独立交付价值。不要试图一次完成全部 12 个类别 — 这恰恰会导致第 2 部分所警告的门禁疲劳。每个新门禁最初都以建议性模式运行, 稳定后再变为阻断性模式。

可复用的核心:「棘轮门禁的构成」

Section titled “可复用的核心:「棘轮门禁的构成」”

整个系统都围绕以下 3 文件模式运转。首先复制它:

  1. baseline.json — 冻结的指标值 + direction(up/down)+ eps(防波动)+ tightenSlack + dedicatedGate。
  2. collect-metrics.<ext> — 运行工具、提取数值,并写入 metrics.json。
  3. check-ratchet.<ext> — 将 metrics.json 与 baseline.json 进行比较;仅当退化超过 eps 时才 exit 1;如果缺少工具/基础设施,则 exit 0(优雅跳过);使用 --require-tighten 时,如果指标已经改善但未更新基线,则 exit 1(锁定改进成果)。

具备这些文件后,每个新指标(覆盖率、复杂度、警告、SAST 告警、包体积、变异测试分数……)都只需在基线中添加一行。

建立 CI;配置格式化器 + 代码检查器 + 类型检查 + 1 个测试运行器 + 绝对覆盖率下限 (例如 60%)。预提交钩子运行快速且可自动修复的检查。产出:任何 PR 都不会破坏基本质量要求。

阶段 1 — 棘轮引擎(第 2 周)— 一切的基础

Section titled “阶段 1 — 棘轮引擎(第 2 周)— 一切的基础”

实现上述 3 个文件。冻结以下项目的基线:警告、覆盖率、复杂度、重复代码、 死代码、文件大小。产出:从此以后,代码库只会不断改进。

阶段 2 — 静态分析深度(第 3 周)

Section titled “阶段 2 — 静态分析深度(第 3 周)”

配置 SAST(CodeQL/Sonar/semgrep)及告警棘轮;配置密钥扫描器(继承默认规则集); 配置 SCA(osv/Dependabot)+ 许可证允许列表 + lockfile-lint。产出:已知漏洞和 泄露的密钥无法通过门禁。

阶段 3 — 构建供应链(第 4 周)

Section titled “阶段 3 — 构建供应链(第 4 周)”

发布时生成 SBOM + 签名来源证明(SLSA L2)+ 定期运行 Scorecard + 强化工作流 (zizmor:最小化令牌权限、不持久化凭据、固定 actions 版本)。产出:发布内容可追溯且 防篡改。

阶段 4 — 测试强度(第 5–6 周)

Section titled “阶段 4 — 测试强度(第 5–6 周)”

如果有用,引入第 2 个测试运行器;为关键模块设置按模块划分的覆盖率下限(反古德哈特); 对纯逻辑使用基于属性的测试;每晚运行变异测试 → 获得第 1 个分数后,将 mutationScore 设为棘轮指标。产出:覆盖率不再是虚荣指标;测试可被证明能够捕获缺陷。

阶段 5 — 契约与动态测试(第 7 周)

Section titled “阶段 5 — 契约与动态测试(第 7 周)”

如果存在公共 API:使用 oasdiff(破坏性变更,阻断性)+ schemathesis(每晚模糊测试)。 根据领域需要,每晚运行 DAST/红队测试。产出:契约不会在无声无息中被破坏。

阶段 6 — 反幻觉与领域保障(第 8 周)

Section titled “阶段 6 — 反幻觉与领域保障(第 8 周)”

为项目中的每项「重复真相」设置一个一致性门禁。配置领域特定的失效模式 门禁(对于 AI:注入攻击红队测试)。产出:结构性腐化和领域故障都有安全网。

  • 每个新门禁都经历建议性→阻断性周期。
  • stale-allowlist:每项抑制都必须包含理由 + issue;过时的抑制会被检测出来。
  • evidence-gate:PR 中的成功声明必须提供证据(测试或活文档式测试)。
  • 每季度审查各门禁的 ROI(淘汰/削减无法带来回报的门禁——对抗疲劳)。
  • 将项目的硬性规则升级为可执行门禁。

贯穿各阶段的原则(不可妥协)

Section titled “贯穿各阶段的原则(不可妥协)”
  • 使用棘轮,而非绝对值。 门禁检查的是_不退化_,而不是固定数值(防止归零的下限除外)。
  • 绝对下限与棘轮并用。 下限防止崩塌;棘轮防止缓慢退化。
  • 在设计层面反古德哈特。 每个目标指标都需要制衡指标(覆盖率 ⇒ 变异测试 + 防掩盖;通过按模块设置下限,强制测试高难度代码)。
  • 优雅跳过。 缺少基础设施时绝不阻断;只有真正的退化才会阻断。
  • 对高成本指标使用 dedicatedGate。 需要外部二进制文件的指标应拥有独立脚本(支持跳过),置于同步运行的中央棘轮之外。
  • 在实际发生合并的位置设置门禁。 不要在快速门禁与实际合并之间留下缺口(这是从快速门禁拆分中吸取的教训)。
  • 阻断性门禁要少而精。 Sonar/DORA:条件过多 = 疲劳。相比阻断门禁之墙,应优先采用建议性门禁 + 棘轮。

第 5 部分 — 建议的改进(按优先级排序,保持兼容)

Section titled “第 5 部分 — 建议的改进(按优先级排序,保持兼容)”

P0 — 投资回报率最高,几乎准备就绪

  1. 变异分数棘轮机制(在第 1 次夜间 Stryker 运行产生数值后)。对抗覆盖率 Goodhart 效应的关键手段;已完成约 90%。
  2. 补上剩余的快速门禁漏洞 — 在一周的观察期后,将 quality.yml 的生产构建提升为阻断项,并继续把仅针对确定性发布 PR 的检查移入 PR→release 路径。
  3. 为 main 启用分支保护(所有者设置)— 提高 Scorecard 评分,补上 DSOMM 差距。

P1 — 价值较高 4. osv/oasdiff → 在正确范围内设为阻断项 — osv 仅阻断 CRITICAL+可修复项(采用类似 Trivy 的两步流程);oasdiff 阻断破坏性变更。 5. require-tighten → 设为阻断项(周期结束时)— 锁定指标改进成果。 6. 在 ci-summary 中逐项审查门禁的 ROI/耗时 — 找出并移除缓慢或低价值的门禁。

P2 — 收益递减 7. SLSA L3 — 如果希望从 L2 升级,则采用密封且可复现的构建器(GitHub SLSA generator)。 8. 提交 CodeQL 配置 + 对 semgrep 进行版本固定 — 提高可控性和可复现性。 9. 每个 PR 执行 DAST 冒烟测试 — 针对最高风险端点运行 schemathesis/promptfoo 的快速子集(而不只是夜间运行)。 10. 不稳定性仪表板 + DORA 指标 — 确保门禁不会侵蚀交付速度。


第 6 部分 — 具体的发布经验(第 9 阶段需要添加的门禁)

Section titled “第 6 部分 — 具体的发布经验(第 9 阶段需要添加的门禁)”

本节记录发布收尾过程中因缺少门禁而发生的真实事件, 并提供具体证据和建议的门禁。每一项都是第 5 部分的候选内容。

v3.8.27 的经验(2026-06-17)— “快速门禁漏洞”让确定性回归一直拖到发布日才暴露

Section titled “v3.8.27 的经验(2026-06-17)— “快速门禁漏洞”让确定性回归一直拖到发布日才暴露”

发生了什么。 在 v3.8.27 的 /generate-release 期间,发布 PR(release/v3.8.27 → main) 是整个集成周期中第一次执行完整的 ci.yml 矩阵。结果:同时出现 12 个失败 — 3 个确定性测试失败 + 约 9 个不稳定/环境问题。这些都不是线上产品回归,但 全部未被发现,因为周期 PR 通过**快速 QG (quality.yml)**进入 release/**,而该流程既不运行完整的单元测试套件,也不运行 pr-test-policy(测试掩盖检测), 也不运行完整的集成测试套件或 schema 一致性检查。3 个确定性问题如下:

  1. 测试因 UI 变更而过时 — permissions modal switch buttons declare button type: #4034 添加了第 4 个开关(仍保留 a11y type="button");测试中的 === 3 计数因此 过时。静态分析本应在 #4034 PR 中发现此问题。
  2. 测试因打包变更而过时 — findMissingArtifactPaths ... root runtime files: dist/http-method-guard.cjs 成为合理的必需路径;测试中的预期列表因此 过时。
  3. 有损模块化导致的偏差(最严重) — settings schemas accept ... unprefixed toggle:模块化后的 updateSettingsSchema(schemas/settings.ts,由 #3988 创建)与 规范版本(settingsSchemas.ts)出现偏差:45 个字段对 85 个字段 — 丢失 40 个 + 6 个存在差异(qdrant*)。它属于 死代码(运行时使用规范版本),因此没有线上影响,但只有手写的一致性测试 发现了它。#4030 恢复了 #3988/#3993 中另外 16 个类似的遗漏,但这个问题漏掉了。

建议的门禁(第 9 阶段):

  • G1 — 真正补上快速门禁漏洞(扩展 P0 #2)。 在 quality.yml(PR→release/**)中, 除 typecheck + 受影响的测试外,还应运行 pr-test-policy(测试掩盖检测)+ 完整的确定性 单元测试套件(或者至少运行速度快且稳定的静态/一致性测试文件)。 这样,过时测试和断言移除就能在引入它们的 PR 中被发现,而不是等到 发布日。集成/e2e 测试可以继续排除在外(缓慢/不稳定),但确定性测试层绝不能继续只 存在于 PR→main 中。
  • G2 — 模块化一致性门禁(新增,目前尚未覆盖)。 针对由模块化 barrel 重新导出的每个 symbol (src/shared/validation/schemas/*、providerRegistry modules 等),将其结构(z.object keys、registry entries)与规范 source 进行比较,并在出现偏差(字段丢失/多余)时失败。这本应在 #3988 PR 中直接发现 40 个字段的丢失。它将手写的一致性测试推广为通用机制(目前只有在有人 记得编写测试的地方才有此类测试)。成本很低:导入两者并比较 Object.keys(shape) 的差异。
  • G3 — 确定性的不稳定测试分诊(辅助措施)。 LiveWS-startup 以及 integration-combo/breaker 测试因 CI 中的服务器超时/级联问题(环境原因)而失败,并非逻辑问题。将这些测试标记为 known-flaky(隔离并关联 issue),使发布 PR 中的红色状态只代表真实信号,而不是由噪声 掩盖夹杂其中的确定性回归。

原则: 门禁必须在实际发生合并的位置运行(已在“跨领域原则”中说明)。v3.8.27 事件表明,这也适用于确定性测试层,而不仅仅是 lint/typecheck — 否则,过时测试和有损模块化积累的技术债只会在 PR→main 阶段以批量形式暴露, 而且是在最糟糕的时刻。



OmniRoute 源码 (a58000c7685f)

HagiCode

HagiCode 是一套智能体编码工作台:结构化工作流、多 Agent 并行执行与 Hero Dungeon 视图,把想法变成真正交付的软件。

让想法更快变成好用的软件,让智能编码更聪明、更高效,也更有趣。

HagiCode 浅色主题主界面截图
  • Smart结构化工作流将意图转化为从想法到交付的可执行路径。
  • Efficient多 Agent 工作流让调研、实现与审阅并行推进。
  • FunHero Dungeon 让长时间编码协作更直观、更有参与感。
访问 HagiCode