React Doctor PR 诊断 Parity 实战:用 Daytona 评估器与确定性 NDJSON 对比检测 PR 引入的诊断回归
2026/9/14 6:18:46 网站建设 项目流程

React Doctor PR 诊断 Parity 实战:用 Daytona 评估器与确定性 NDJSON 对比检测 PR 引入的诊断回归

【免费下载链接】react-doctorYour agent writes bad React. This catches it项目地址: https://gitcode.com/GitHub_Trending/re/react-doctor

本篇指南完整讲解 React Doctor 仓库中run-parity技能(.agents/skills/run-parity/SKILL.md)所定义的「PR 诊断一致性」工作流:如何把 PR 的 base 与 head 两个版本在同一批真实仓库语料上分别跑出 React Doctor 诊断结果,写出确定性的 NDJSON 工件并逐条对比,从而判断一个 PR 是否新增或删除了任何诊断。读完后你将掌握完整的运行准备、配对沙箱评估、增量规则影响分析、基线缓存与 provenance 校验、诊断/性能双对比器,以及多 PR 矩阵批量评估的操作方式,并能读懂每个脚本(如 compare-parity.mjs)背后的实现约束。

什么是 PR 诊断 Parity,以及为什么需要它

React Doctor 是一个诊断 React 代码问题的检测器("Your agent writes bad React. This catches it")。检测器本身的规则实现会不断演进:一条规则逻辑的改动、一个共享工具的调整,都可能在成千上万个真实仓库上产生「多报 / 少报 / 换位置报」的差异。Parity(一致性)评估就是为这类改动兜底的门禁:

  • 运行同一语料、比较两个版本:把 PR 的 base 提交和 head 提交各跑一遍评估,得到两份「每仓库一条记录」的 NDJSON 工件,再做逐条诊断 diff;
  • 确定性优先:工件可复现、可缓存、可审计,比较器对输入做二次校验,任何不完整或非法的记录都会直接判为无效输入而不是被静默忽略;
  • 增量优先、全量兜底:能证明只影响少数规则的 PR 走「scoped(规则范围)」评估以节省 Daytona 配额,其余情况一律回退到全量对比(full parity)。

从源码结构看,整套工作流由两类组件构成:.agents/skills/run-parity/scripts/下的本地 Node/jq 脚本(对比器、校验器、影响分析器、Merkle 索引构建器),以及 packages/evals 下基于 Daytona 的评估器(packages/evals/package.json 中的evalmatrix-corpus-identitysource-hash三个 npm script)。

准备工作:解析 PR、固定提交与运行目录

前置条件

技能文档明确要求:

  • 环境变量DAYTONA_API_KEY已配置;
  • ghCLI 已完成 GitHub 认证;
  • PR 的 head 已经 push 到远端;
  • 未经许可不得 push 任何变更
  • 本仓库内安装依赖与运行脚本一律使用ni/nr(@antfu/ni 封装),不要直接写npm

解析 PR 并推导两侧仓库

gh pr view <pr-number-or-url> \ --json number,url,baseRefOid,headRefOid,headRepository,headRepositoryOwner

关键约束有三条:

  1. base 仓库从 PR 的 URL 推导,head 仓库从headRepositoryOwner.login+headRepository.name推导——fork PR 的 head 通常位于另一个仓库,必须分别指向正确的 repo URL;
  2. 必须使用返回的完整 commit hash(baseRefOid/headRefOid),而不是分支名。评估器内部也强制这一点:packages/evals/src/constants.ts 中PINNED_REPOSITORY_REF_PATTERN = /^[0-9a-f]{40}$/i,只接受 40 位提交哈希;
  3. 输入语料中的仓库引用同样必须 pin 到精确提交——validate-parity-input.jq 校验.repository.ref必须匹配^[0-9A-Fa-f]{40}$,未 pin 的仓库记录会被整批拒绝。这样做的目的正如技能文档所述:基线记录了已解析的仓库 hash,复用它作为 candidate 语料可以防止两次运行之间默认分支移动导致语料漂移。

创建运行目录

mkdir -p tmp/parity-pr-<number>-<head-short-sha>

该目录约定命名为tmp/parity-pr-<pr号>-<head短sha>运行结束后必须保留以便审计工件。评估前先在仓库根执行ni安装依赖。

双版本评估:baseline 与 candidate 命令

评估统一从packages/evals目录发起。语料默认是 packages/evals/repositories.json 中排名前 2000 的仓库(对应 constants.ts 的DEFAULT_CORPUS_REPOSITORY_COUNT = 2_000),初始并发 200(DEFAULT_CORPUS_CONCURRENCY = 200)。沙箱创建速率被硬性限制在 20(SANDBOX_CREATE_CONCURRENCY = 20)以免压垮 Daytona;评估器会清理失败资源并按 50、10 的并发重试失败项目(constants.ts 的EVALUATION_RETRY_CONCURRENCIES = [50, 10, 2])。

# 1) baseline:用 PR 的 base 提交跑全量诊断 nr --silent eval \ --react-doctor-repository <base-repository-url> \ --react-doctor-ref <baseRefOid> \ > <absolute-run-directory>/baseline.ndjson # 2) candidate:语料直接复用 baseline.ndjson(已 pin 仓库 hash),用 head 提交再跑一遍 nr --silent eval \ --repositories <absolute-run-directory>/baseline.ndjson \ --react-doctor-repository <head-repository-url> \ --react-doctor-ref <headRefOid> \ > <absolute-run-directory>/candidate.ndjson

两条命令都必须以退出码 0 结束且报告 100% 完成度,否则报告失败项目并停止对比。candidate 阶段用 baseline 的输出作为--repositories输入,而不是重新取默认语料——评估器会拒绝未 pin 的评估 NDJSON 输入。

评估器如何「钉死」检测器版本与规则集

在沙箱内,评估器通过git fetch --depth 1检出指定的 40 位提交,执行corepack enable+ni --frozen+turbo run build --filter=react-doctor,随后由MATERIALIZE_REACT_DOCTOR_EVALUATION_PROVENANCE_COMMAND(见 constants.ts)计算规则集哈希并写出 evaluation provenance 文件。这解释了技能文档中「评估器会给每条记录盖上精确的检测器提交、revision-local 规则/配置哈希、评估器源码哈希」的机制——其中configContract固定为"revision-local-rule-config-v1"EVALUATION_CONFIG_CONTRACT)。因此永远不要缓存一份没有这些 stamp 的旧运行结果。

scoped 评估还有一个值得注意的实现细节:规则配置生成逻辑会把所有未选中的 revision-local 规则置为"off",并且当规则范围内不含 security-scan 规则时,整体注入ignore: { tags: ["security-scan"] }跳过整树安全扫描路径(见 constants.ts 中EVALUATION_RULE_CONFIGURATION_SOURCE)。这正是技能文档所说「评估器把每个未选中的 revision-local 规则 stamp 为 off;范围中无 security-scan 规则时跳过整个整树扫描」的来源。

基线缓存:provenance 文件的创建与校验

全量 baseline 非常昂贵,因此技能允许跨 PR 共享不可变 baseline,但共享条件极严:

只有在完全相同的 React Doctor 提交、语料 manifest hash、评估器 schema、规则集/配置哈希下才能缓存成功基线;缓存基线在被复用前仍必须通过流式校验器。多个 PR 可以共享同一个不可变 baseline,但绝不能合并它们的 candidate head 或 delta。

创建 provenance 文件

正常的输入校验通过后,在 baseline 旁边创建 provenance 文件:

node .agents/skills/run-parity/scripts/baseline-cache-provenance.mjs create \ --baseline <baseline.ndjson> \ --corpus-manifest <corpus-manifest.json> \ --base-commit <full-base-commit> \ --repository <react-doctor-repository-url> \ --evaluator-source-hash <packages-evals-source-hash> \ --config-contract revision-local-rule-config-v1 \ --rule-set-hash <stamped-full-ruleset-hash>

其中--evaluator-source-hash的值从packages/evals目录执行nr --silent source-hash获得(对应 packages/evals/package.json 中的source-hashscript)。每次复用缓存前,都用verify子命令重跑一遍同一校验。从 baseline-cache-provenance.mjs 的实现看,verify是流式读取原始 NDJSON 字节的:它要求全量 baseline 的ruleKeys: [](即确属全量运行而非 scoped)、逐条检查每条记录的 producer 绑定,并独立要求「精确 pin 的语料项目集合」与 manifest 完全一致——任何一项不匹配都按 cache miss 处理。

命中缓存时的走法

  • cache hit:base 完全不进 Daytona。直接对已校验的缓存 baseline 跑上面的「candidate-only」命令,且不得传任何--paired-*参数
  • cache miss,或需要「全量 vs scoped」shadow 运行时:把 base 与 candidate 两个检测器放进同一个 Daytona 沙箱做配对评估(下一节)。

配对沙箱评估:同沙箱双检测器

配对(paired)模式下,一个沙箱同时容纳 base 与 treatment(candidate)两个 React Doctor 检出。从 constants.ts 中的PREPARE_PAIRED_REACT_DOCTOR_COMMANDS可以看到两侧各自git fetch --depth 1指定提交、分别turbo build、分别物化 provenance;SETUP_PAIRED_TARGET_REPOSITORY_COMMAND则显示目标仓库只 fetch 一次到一个 bare 对象库,再为 base/treatment 各建一个隔离 worktree。这与技能文档描述完全一致:

配对沙箱把每个目标仓库 fetch 一次进同一个对象库,然后在隔离的 base/treatment worktree 中用隔离的检测器安装、配置文件与报告路径扫描。一个项目对只有在两侧都成功后才会被写出,因此重试不会在任一输出中留下半成功的项目对。配对写入使用单一写入者队列,任一 sink 失败即回滚 baseline。

命令形式如下:

nr --silent eval \ --repositories <corpus-manifest-or-validated-input> \ --paired-baseline-output <absolute-new-baseline-path> \ --paired-base-react-doctor-repository <base-repository-url> \ --paired-base-react-doctor-ref <base-or-full-shadow-commit> \ --paired-base-rule <treatment-plugin/rule> \ --react-doctor-repository <treatment-repository-url> \ --react-doctor-ref <treatment-commit> \ --paired-execution sequential \ --rule <treatment-plugin/rule> \ > <absolute-run-directory>/candidate.ndjson

配套规则与硬约束:

  • --paired-baseline-output独占方式创建,永不覆盖已有工件;
  • 任何非零退出的配对评估都会使两份输出工件同时作废、不可复用——丢弃它们,而不是喂给比较器或缓存;
  • 性能对比必须两侧传相同的--paired-base-rule--rule值;全量对比则两个都省略。「全量 vs scoped」shadow 运行是有效的诊断证据,但不是性能证据。

沙箱规格、执行模式与超时预算

技能文档给出的规格与 constants.ts 常量一一对应:

项目源码常量
配对沙箱 CPU / 内存 / 磁盘4 核 / 8 GiB / 20 GiBPAIRED_SANDBOX_CPU_CORES/PAIRED_SANDBOX_MEMORY_GIB/PAIRED_SANDBOX_DISK_GIB
并行执行门槛沙箱 ≥4 核才并行PAIRED_SCAN_MINIMUM_PARALLEL_CPU_CORES = 4
配对默认并发50 个沙箱(低于 4 核沙箱的实测容量上限)DEFAULT_PAIRED_CORPUS_CONCURRENCY = 50
单次配对扫描上限5 分钟(300s),防止少数卡死沙箱吃掉整个 attempt 预算PAIRED_SANDBOX_SCAN_TIMEOUT_SECONDS = 300
  • --paired-execution auto(默认):仅在核数足够时并行;
  • --paired-execution sequential受控串行基准与下述性能对比必须用它,保证 base 与 candidate 共享同一沙箱且互不抢 CPU;
  • auto/parallel只能用于「纯诊断 parity」(计时证据将被丢弃)。

另外两种模式共享同一硬性 attempt 截止时间与完全一致的评估标签清理逻辑。普通(非配对)评估器保留其既有超时;对于带较短预算的评估,聚合重试保留时间被上限为 attempt 开始时剩余时间的 25%,确保至少 75% 的预算留给实际工作,而不是让快照构建吃掉整个首次 attempt 截止时间——对应 constants.ts 的EVALUATION_MAXIMUM_RETRY_RESERVE_RATIO = 0.25

多 PR 矩阵评估:treatment 描述符与共享 base

当多个 PR 共享同一个不可变 base 与同一语料时,使用可复现的矩阵 treatment 描述符。先固定语料身份与评估器哈希:

cd packages/evals nr --silent matrix-corpus-identity <absolute-corpus-manifest-path> nr --silent source-hash

每个描述符是一个不可变 JSON 文件,形状严格如下:

{ "schemaVersion": 1, "id": "pr-1234", "artifactDirectory": "/absolute/path/pr-1234", "reactDoctorRepository": "https://github.com/millionco/react-doctor.git", "reactDoctorCommit": "<40-character-head-commit>", "impactManifestPath": "/absolute/path/pr-1234-impact.json", "impactManifestSha256": "<sha256>", "group": { "baseReactDoctorRepository": "https://github.com/millionco/react-doctor.git", "baseReactDoctorCommit": "<40-character-base-commit>", "baseFullRuleSetHash": "<full-base-rule-set-sha256>", "baseArtifactPath": "/absolute/path/base-union-scoped.ndjson", "baselineOutputPath": "/absolute/cache/full-baseline.ndjson", "baselineProvenancePath": "/absolute/cache/full-baseline.provenance.json", "corpusManifestPath": "/absolute/path/corpus.json", "corpusManifestSha256": "<matrix-corpus-identity manifestSha256>", "corpusProjectSetSha256": "<matrix-corpus-identity projectSetSha256>", "evaluatorSourceHash": "<source-hash>", "configContract": "revision-local-rule-config-v1", "scanContract": "react-doctor-json-full-v1", "reportContract": "react-doctor-complete-report-v1", "projectRootPolicy": "manifest-root-dir-v1" } }

其中scanContract/reportContract/projectRootPolicy三个契约字符串在 constants.ts 中有常量定义(MATRIX_SCAN_CONTRACTMATRIX_REPORT_CONTRACTMATRIX_PROJECT_ROOT_POLICY)。描述符引用 impact manifest 时的校验链很长:它必须是find-impacted-rules.mjs的精确输出,其 hash、base commit、head commit、mode 与 candidate 规则键都会被重新验证;在 Daytona 启动前,矩阵运行器会 fetch 钉住的 base/head 提交、用当前版本的生成器重跑一遍,并要求字节级一致的 manifest 输出。所有参与重复运行的描述符必须具有完全相同的group对象、唯一的合法id、以及互不相同的 artifact 目录。

发起矩阵运行

nr --silent eval \ --matrix-treatment /absolute/path/pr-1234.json \ --matrix-treatment /absolute/path/pr-1235.json \ --matrix-wave-width 2

矩阵运行器的行为(可从 constants.ts 的常量交叉验证):

  • 已验证的全量 cache hit 让 base 留在 Daytona 之外;否则,只要任一 treatment 需要全量 parity 就扫一次完整 base,否则扫一次按排序后的增量规则范围并集
  • 一个目标 bare clone 供给各隔离 lane worktree;默认 2-lane 波次(DEFAULT_MATRIX_WAVE_WIDTH = 2)每沙箱 4 CPU / 8 GiB(MATRIX_CPU_CORES_PER_LANE = 2MATRIX_MEMORY_GIB_PER_LANE = 4),运行器在 400 CPU 总包络(MATRIX_MAXIMUM_CPU_CORES = 400)下推导并发,沙箱创建保持 20;
  • 重试保留已成功的(lane, project)结果,只以 50 → 10 → 2 的并发重试失败工作(前两级与普通评估的EVALUATION_RETRY_CONCURRENCIES对齐);
  • 每个 treatment 与其 candidate NDJSON、精确语料 manifest、描述符、impact manifest、规则、哈希、计数与 provenance原子性发布为一个自包含目录;
  • base 缺失时,成功的 treatment 被标记为blocked,而不是做独立的合并判断。

验证与对比已发布的 treatment 工件

把每个已发布的 treatment 目录视为自包含证据:对比之前先验证其 status、规范化相对路径、producer 绑定、字节长度、哈希、精确记录数、完整报告与语料项目元组。绝不要跟随 provenance 指向的共享缓存或 scoped-base 来源路径去取输入:

node .agents/skills/run-parity/scripts/verify-matrix-artifact.mjs \ <treatment-artifact-directory> # 增量 treatment:带上规则范围 node .agents/skills/run-parity/scripts/compare-parity.mjs \ --rules <treatment-artifact-directory>/rules.json \ <treatment-artifact-directory>/base.ndjson \ <treatment-artifact-directory>/candidate.ndjson \ > <treatment-artifact-directory>/parity.json # 全量 treatment node .agents/skills/run-parity/scripts/compare-parity.mjs \ <treatment-artifact-directory>/base.ndjson \ <treatment-artifact-directory>/candidate.ndjson \ > <treatment-artifact-directory>/parity.json

verify-matrix-artifact.mjs 的校验逻辑与上述「自包含证据」要求一一对应;注意矩阵 lane 是并发执行的,其工件仍属诊断证据——不要把矩阵工件喂给性能比较器。

增量 Parity:影响 manifest 与规则范围

对「只改规则」的 PR,可在 candidate 运行前构建保守的影响 manifest:

node .agents/skills/run-parity/scripts/find-impacted-rules.mjs \ <repository-root> <base-ref> <head-ref> <impact.json>

find-impacted-rules.mjs 的工作原理值得了解,因为它决定了「何时可以增量、何时必须全量」:

  1. 构建双向模块图:分别用git ls-tree拉取 base 与 head 两个版本下packages/oxlint-plugin-react-doctor/src的运行时源码树(RULES_ROOT 覆盖plugin/rules/plugin/constants/plugin/utils/三个增量运行时根),用 TypeScript 编译器 API 解析每个文件的运行时 import/export/require/动态import()边,建立依赖与反向依赖图;
  2. 规则键提取:在规则文件中定位defineRule(...)/defineRetiredRule(...)调用的id属性,拼出react-doctor/<id>形式的规则键;
  3. 反向传播:从变更文件出发沿反向依赖传播,收集被直接影响的规则;
  4. 诊断交互闭包candidateRuleKeys已自动包含已知诊断交互闭包(源码 中DIAGNOSTIC_INTERACTION_GROUPS,例如no-initialize-stateno-derived-state-effect一族、rules-of-hooksreact-hooks-js/hooks的配对);
  5. 保守回退:出现以下任一情况,manifest 的mode就是"full"——security-scan 规则变更;全局 runner/config/report/registry 变更;规则 ID 删除或重命名;未解析的运行时模块边;TS 解析失败;插件工具模块不经过任何已映射规则边界就触达宿主模块;甚至「没有任何运行时规则影响」(空增量范围永远不被构造)。

使用规则:只有 manifest 报告"mode": "incremental"时才走增量模式。把candidateRuleKeys原样写入 rules JSON,并在 candidate 命令中把每个键作为可重复的--rule参数传入:

nr --silent eval \ --repositories <validated-baseline-or-corpus-manifest> \ --react-doctor-repository <head-repository-url> \ --react-doctor-ref <headRefOid> \ --rule <plugin/rule> \ > <absolute-run-directory>/candidate-scoped.ndjson

Shadow 校验:增量结果必须能被全量复现

在增量 parity 积累足够的 shadow 历史、可以成为必选门禁之前,技能要求额外做一次全量 candidate:在与 scoped 运行完全相同的 head / 语料 / 并发策略下跑一次全量 candidate,并要求其规则过滤后的输出与 scoped candidate精确一致。同时报告实测 wall time 与项目级延迟——不要从样本外推加速比

输入校验:jq 流式验证器

candidate 运行前,逐条流式校验 baseline 的每一条记录。validate-parity-input.jq 会在不把整个 NDJSON 语料载入内存的情况下拒绝:

  • 未 pin(ref 非 40 位哈希)的仓库;
  • 评估错误记录(error字段);
  • 畸形报告(schema 校验,支持 v1/v2/v3 报告 schema 与full/diff/staged/baseline模式);
  • 不完整项目(v3 要求complete == trueskippedChecks为空、analyzedFileCount == analyzedFiles.length等)。
jq -e -n \ -f <repository-root>/.agents/skills/run-parity/scripts/validate-parity-input.jq \ <absolute-run-directory>/baseline.ndjson >/dev/null

jq 程序结尾的reduce还会用seen表检查项目键(org/name/ref/rootDir四元组的 JSON 序列化)不重复。若 baseline 命令退出码非零或该校验失败:检查其失败记录并停止

对比诊断:compare-parity.mjs 与退出码语义

从仓库根目录执行:

node .agents/skills/run-parity/scripts/compare-parity.mjs \ <run-directory>/baseline.ndjson \ <run-directory>/candidate.ndjson \ > <run-directory>/parity.json node .agents/skills/run-parity/scripts/compare-parity-performance.mjs \ <run-directory>/baseline.ndjson \ <run-directory>/candidate.ndjson \ > <run-directory>/performance-parity.json

compare-parity.mjs的退出码语义(源码 中PARITY_DIFFERENCE_EXIT_CODE = 1INVALID_INPUT_EXIT_CODE = 2):

退出码含义
0诊断完全一致
1对比成功,但存在诊断变化(added/removed)
2输入不完整或非法(含任一侧出现 skippedProjects 时的保守降级)

退出码1时,先检查受影响的源码位置再对变化定性(是真实修复、误报变化,还是行为回归)。

比较器的实现要点

阅读 compare-parity.mjs 可以印证技能文档中的每条工程承诺:

  • 双次校验与 fail closed:比较器对两侧输入重新执行与评估器等价的报告校验(validateReport,见 L313)。任何一侧含评估错误、畸形报告、缺失完成标记或残缺 legacy 报告,都会以「非法输入」状态退出;
  • 规范化诊断身份canonicalDiagnostic(L182)把诊断路径解析为「相对报告根目录」的规范化路径,统一 v1/v2/v3 报告 schema 下的身份,tags 排序、重复 occurrence 计数保留。因此重叠的 workspace 扫描不会虚增计数,schema 升级也不会被误报为诊断 churn;
  • 流式 + 临时目录暂存readRun逐行流式读取;baseline 记录按项目键的 SHA-256 写入系统临时目录(react-doctor-parity-*),大尺寸added/removed明细数组通过writeOutputArray增量写出;变化的诊断条目只在排序所需时间内驻留内存。因此临时盘与输出容量要与运行规模成比例
  • 候选侧更严diagnosticsByIdentity对 candidate 传rejectOutsideRuleScope = true(L472),scoped 模式下任何范围外的 candidate 诊断直接报错——即「scoped 比较器拒绝任意的范围外 candidate 诊断」。

增量(scoped)对比

对 scoped 运行,把全量 baseline 过滤到「同样的规则 + always-on 不变量范围」后再比:

node .agents/skills/run-parity/scripts/compare-parity.mjs \ --rules <rules.json> \ <baseline.ndjson> \ <candidate-scoped.ndjson> \ > <run-directory>/parity-scoped.json

「always-on 不变量范围」在 源码 中有明确定义:INVARIANT_DIAGNOSTIC_PLUGIN = "TS"(整个 TypeScript 插件范围TS/*)外加一份固定的INVARIANT_DIAGNOSTIC_RULE_KEYS列表(expo、pnpm-hardening、reduced-motion、RN 相关等约 20 个react-doctor/规则键)。scoped 比较器还会:

  • 要求精确的项目 / framework / analyzed-file 覆盖coverageFingerprint(L416)对 v3 报告的项目目录、packageRoot、framework、排序后的 analyzedFiles 做指纹,两侧指纹不一致的项目被记入skippedProjects并触发退出码 2;
  • 保留重复 occurrence 的多重性(unchanged取两侧最小 occurrence 数,多余部分分别记 added/removed);
  • 比较全部语义诊断字段,包括规范化后的 primary 与 related 路径。

性能对比:compare-parity-performance.mjs 的阈值体系

性能比较器(compare-parity-performance.mjs)要求完整的 v3 报告,且两侧的项目覆盖、规则范围(ruleKeys)、configContractevaluatorSourceHash逐项相等,任一不等即抛错退出 2。它只能在配对串行--paired-execution sequential)评估上运行,确保 base 与 candidate 共享同一 Daytona 沙箱、不互相争抢 CPU。退出码:0= 计时保持在固定的噪声容忍阈值内;1= 存在实质性项目级或聚合回归;2= 证据非法。

具体阈值全部是源码常量,且会原样写入输出的thresholds字段便于审计:

阈值说明
minimumBaselineElapsedMilliseconds1000 msbase 扫描不足 1 秒的项目被忽略
minimumProjectRegressionMilliseconds2000 ms单项目须至少慢 2 秒
maximumProjectRegressionRatio1.5且慢于 50%(ratio ≥ 1.5)才标记单个项目
minimumAggregateProjectCount10聚合回归至少需要 10 个合格项目
maximumAggregateRegressionRatio1.2且聚合 ratio ≥ 20%
minimumAggregateRegressionMs10000 ms且聚合新增延迟 ≥ 10 秒

输出的summary还包含 median 与 p95 的项目 ratio 分布(L211)。技能文档反复强调:归因前先看输出的原始测量数据

Merkle 索引:同一工件多次对比时的加速路径

当同一个 baseline 或 candidate 将被对比不止一次时,构建紧凑的 Merkle 索引(build-parity-index.mjs):

node .agents/skills/run-parity/scripts/build-parity-index.mjs \ --rules <rules.json> <baseline.ndjson> \ > <baseline.index.json> node .agents/skills/run-parity/scripts/build-parity-index.mjs \ --candidate --rules <rules.json> <candidate-scoped.ndjson> \ > <candidate.index.json> node .agents/skills/run-parity/scripts/compare-parity-indexes.mjs \ <baseline.index.json> <candidate.index.json> \ > <index-diff.json>

索引结构自底向上哈希:每条诊断身份 + occurrence 数构成叶节点 → 每个(项目, 规则)桶的semanticHash→ 每条规则跨项目的semanticHash→ 顶层wholeRunHashsemanticContract固定为compare-parity@1,算法 sha256)。--candidate标志启用与比较器一致的「范围外诊断即拒绝」策略。对比策略(见 compare-parity-indexes.mjs):根哈希相等立即停止;不同则先逐规则下钻,再进项目桶。空的项目/规则桶是显式表达的(空桶也有哈希,不会与缺失混淆);规则范围或覆盖元数据漂移会 fail closed。

验证比较器本身的变更

若你修改了上述任一脚本(比较器、性能比较器、影响分析器、索引或输入校验),从仓库根运行各自的测试:

node --test .agents/skills/run-parity/scripts/compare-parity.test.mjs node --test .agents/skills/run-parity/scripts/compare-parity-performance.test.mjs node --test .agents/skills/run-parity/scripts/find-impacted-rules.test.mjs node --test .agents/skills/run-parity/scripts/parity-index.test.mjs node --test .agents/skills/run-parity/scripts/validate-parity-input.test.mjs

这些测试文件与脚本同目录(如 compare-parity.test.mjs、find-impacted-rules.test.mjs),覆盖了 canonical 身份、覆盖指纹、规则范围拒绝、影响图构建与闭包等关键路径。

结果报告清单

一次 parity 运行的最终报告必须包含以下字段,缺一都会削弱结论的可审计性:

  • PR URL 与 base/head 两个完整 commit hash;
  • 对比项目数(compared)与跳过项目数(skipped),以及两侧项目总数;
  • 诊断总数(baseline/candidate)、added 与 removed 计数;
  • 最大的规则级 delta(rules.added/rules.removed排序后的头部条目);
  • 所有工件路径(baseline.ndjsoncandidate.ndjsonparity.jsonperformance-parity.json及 provenance 文件)。

小结:这套工作流的三条设计主线

  1. 不可变性:40 位提交 pin、语料 manifest 哈希、评估器源码哈希、规则集哈希、provenance 文件——每一层共享(baseline 缓存、矩阵 group)都以「字节级可复现」为代价换取「跨 PR 复用」的收益;
  2. fail closed:从 jq 输入校验、比较器的二次校验、配对输出的「任一非零即双工件作废」,到索引对比对范围漂移的保守失败,任何不确定的中间态都不进入 diff;
  3. 增量是优化,全量是真理:增量范围由保守的模块图影响分析自动推导,uncertainty 一律回退全量;并且在 shadow 历史足够之前,scoped 结果必须由同策略的全量运行精确复现才算可信。

【免费下载链接】react-doctorYour agent writes bad React. This catches it项目地址: https://gitcode.com/GitHub_Trending/re/react-doctor

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询