Plate 编辑器基准实验室的 Evidence Kit 引导(Bootstrap):用证据契约与硬切规则取代旧版 App/Template 基准动物园
【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate
本文基于 Plate 仓库中benchmarks/editor/iterations/000-bootstrap-evidence.md(Evidence Harness Bootstrap,第 000 期迭代记录)展开,介绍 Plate 如何用一套"证据优先"(evidence-first)的 Evidence Kit 实验台(lab)替换掉旧的独立 Next/Vite 基准应用,确立目标自有的基准行归一化契约、来源配置、注册表控制平面与硬切(hard-cut)守护,并给出完整的验证命令。读完本文,你将掌握该实验台的目录骨架、行归一化契约的字段语义、硬切机制的实现位置、基准注册表的运作规则,以及从npm run check到npm run evidence:full的完整验证链路,从而能够在该实验台内新增或审查一条 Slate v2 vs Slate 的基准证据行。
背景与动机:为什么旧的"基准应用动物园"必须被替换
在本次引导之前,benchmarks/editor是一个独立的 Next/Vite 基准应用(app)与模板(template)集合,用于承载编辑器框架基准研究。该迭代记录(verdict: accepted)明确指出,这个形态存在结构性问题:
- 大量
apps、templates等应用脚手架占据了默认归属权,基准实验的"默认所有者"变成了浏览器应用壳,而不是证据产物本身; - 历史上的临时基准 JSON(
tmp/*benchmark*.json)散落在各包中,无法区分"当前有效的证据"与"一次性的历史输出"; - 在 Slate v2 与 Slate 还没有可靠的对比行之前,任何关于性能数字的宣称都缺乏可验证的形态。
因此本次引导的 Goal 非常明确:用更小、更"证据优先"的实验台替换benchmarks/editor,专门用于编辑器框架基准研究。其核心原则是:默认所有者是证据产物(evidence artifacts),而非浏览器应用脚手架。
这一点也在实验台 README(benchmarks/editor/README.md)中得到了复述:"This directory is an Evidence Kit lab for editor framework benchmark research. It replaces the old standalone Next/Vite benchmark app. The default owner is now evidence artifacts, not browser app scaffolding."
目标定义:一个"证据优先"的编辑器基准实验室
第 000 期记录把目标收敛为一句话:Replacebenchmarks/editorwith a smaller evidence-first lab for editor framework benchmark research。展开来看,这个目标包含三层含义:
- 缩小而非扩编:不再维护庞大的浏览器应用/模板矩阵,实验台只保留证据所需的骨架;
- 先证明自身,再输出数字:实验台必须先能证明"自己产出的证据形态是正确的",之后才允许任何人引用 Slate v2 vs Slate 的数字(Current Result 原文:"The lab can prove its own evidence shape before anyone starts claiming Slate v2 vs Slate numbers");
- 硬切守护:一旦旧应用/模板路径"复活",基准行必须失败(fail),从而阻止无意的回归。
实验台骨架全景:Evidence Kit scaffold 的组成
第 000 期记录列出了引导阶段已经实现的骨架(Implemented),对照 benchmarks/editor 目录可以逐一印证:
| 骨架组件 | 说明 | 仓库中的落点 |
|---|---|---|
| Fuzzer(模糊测试) | 随机生成基准行并校验归一化契约 | benchmarks/editor/test/fuzz/core-fuzz.mjs |
| Corpus(语料) | 固定种子用例,与生成用例一起覆盖契约 | benchmarks/editor/test/fixtures/corpus.json |
| Benchmark(基准行) | 核心/对比/富文本等基准行生成与检查 | benchmarks/editor/benchmarks 下多个*.mjs |
| Source fetchers(来源抓取器) | 抓取编辑器框架来源配置,供研究使用 | benchmarks/editor/benchmarks/fetch-editor-frameworks-research.mjs、fetch-source-pass-research.mjs |
| Package-boundary check(包边界检查) | 限制源码文件数、包体积、dry-run 打包体积 | benchmarks/editor/benchmarks/package-boundary-gates.mjs |
| Startup check(启动检查) | 校验包导入的 P95 耗时与导出数量上限 | benchmarks/editor/benchmarks/startup-import.mjs |
| Scope hash(范围哈希) | 由 evidence-kit 生成/更新基准作用域 | 通过npm run bench:scope(evidence-kit scope --update) |
| Perf docs(性能文档) | 生成 HTML 看板与可检索的 perf 文档 | benchmarks/editor/docs/perf(evidence.html、index.html、rich-text.html、slate-v2-internals.html) |
其中 fuzzer 是验证整个契约的"第一道门":它读取 corpus 中的固定用例,再以可复现的种子(默认0x5e_ed,即 24237)生成随机用例,逐条调用normalizeBenchmarkRow并做深度相等比较;此外它还会调用createEvidenceReadinessRows校验硬切就绪行、用构造的 Slate 对比产物校验三个活动面(slate-v2:dom-present、slate-v2:default-render-auto、slate)各产出一行归一化结果。如果契约被破坏,npm run fuzz会直接失败。
目标自有的行归一化契约(Target-owned Normalization Contract)
第 000 期记录强调了一个关键设计:基准行的归一化契约由目标(target)自己拥有,实现在 benchmarks/editor/src/index.mjs。这意味着"一行证据长什么样"不是由上层应用壳定义的,而是由证据实验台与目标仓库共同约定。
目标声明:editorTargets
export const editorTargets = Object.freeze([ { id: 'slate-v2', label: 'Slate v2', role: 'engine-and-react-runtime', sourcePath: '../../.tmp/slate-v2', evidenceOwner: 'scripts/benchmarks plus packages/slate*', }, { id: 'slate', label: 'Slate', role: 'legacy-baseline', sourcePath: '../../../slate', evidenceOwner: 'upstream package behavior and local clone', }, ]);从源码结构看,实验台当前只声明两个活动目标:
slate-v2,角色为"引擎 + React 运行时",本地源码路径指向.tmp/slate-v2(相对本实验台为../../.tmp/slate-v2),证据所有权归属于 Slate v2 自身的scripts/benchmarks与packages/slate*;slate,角色为"遗留基线"(legacy-baseline),本地源码路径指向../../../slate,证据所有权归属于上游包行为与本地克隆。
这两个目标同时出现在 benchmarks/editor/research/editor-frameworks-sources.json 的来源配置中,说明"来源配置"与"目标声明"是相互印证的同一事实。
行契约字段:normalizeBenchmarkRow
const normalized = { category: requireString(row.category, 'category'), fixture: requireString(row.fixture, 'fixture'), library: requireString(row.library, 'library'), status: requireString(row.status, 'status'), };一行归一化后的基准行至少包含四个必填字段,另有四个可选的度量字段:
| 字段 | 类型 | 语义 |
|---|---|---|
category | string(必填) | 基准类别,例如slate-react-huge-document-legacy-compare |
fixture | string(必填) | 场景/固件标识,例如5000-blocks/combined-selection/startupMs |
library | string(必填) | 被测实现,例如slate-v2:default-render-auto、slate |
status | string(必填) | 状态,例如ok、missing-source、missing-artifact、over-budget |
medianUs | number(可选) | 中位数耗时,统一换算为微秒 |
p95Us | number(可选) | P95 耗时,统一换算为微秒 |
ops | number(可选) | 采样次数 / 操作次数 |
bytes | number(可选) | 字节类指标(如堆增量) |
note | string(可选) | 可读上下文,附在结果 JSON 中 |
所有数值字段都会经过requireFiniteNumber校验,任何NaN/Infinity都会抛错——这是契约对"脏数据"的零容忍设计。normalizeBenchmarkResult则在行之上再包一层结果对象:name、generatedAt、node(Node 版本)与rows数组,并兼容rows与results两种负载键名。
毫秒到微秒的统一换算
时间类指标在写入结果前统一由msToUs转换为微秒并保留三位小数:
function msToUs(value) { return Number((requireFiniteNumber(value, 'milliseconds') * 1000).toFixed(3)); }度量字段的识别遵循命名约定:以Ms/Duration结尾的视为时间指标(写medianUs/p95Us),以Bytes/MB结尾或unit === 'bytes'的视为字节指标(写bytes),否则视为普通计数(写ops)。这保证了来自不同产物的指标最终都落到同一行形态上。
三个活动面的顺序与命名
export const slateLegacyCompareSurfaceOrder = Object.freeze([ 'v2DefaultRenderAuto', 'v2DomPresent', 'legacyChunkOn', ]);Slate 遗留对比产物中的三个活动面分别映射为:slate-v2:default-render-auto、slate-v2:dom-present与slate(chunk-on 基线),顺序固定,便于在结果 JSON 与文档看板中稳定呈现。
硬切(Hard-cut):旧路径回归即失败
第 000 期记录中"Hard-cut benchmark row that fails if old app/template paths return"是本引导最有特色的机制。它的实现分为两部分。
第一部分是硬编码的"陈腐表面路径"清单(benchmarks/editor/src/index.mjs 中的staleSurfacePaths):
export const staleSurfacePaths = Object.freeze([ 'apps', 'app', 'assets', 'components', 'data', 'lib/benchmark-types.ts', 'scripts/benchmark/run_contract_benchmarks.mjs', 'templates', 'tests/config', 'website', ]);第二部分是createEvidenceReadinessRows与findStaleSurfaces:只要这些路径中任何一个在实验台根目录重新出现,legacy-app-surface-removed这一行的状态就会从ok变为stale-surface,并在 note 中列出命中的路径。
三行就绪证据(evidence-readiness)分别为:
editor-framework-source-map:来源配置条数是否不少于目标数(sources.length >= editorTargets.length);local-editor-targets:本地目标源码根是否存在(knownTargets.length >= editorTargets.length);legacy-app-surface-removed:旧应用/模板路径是否已被清除(staleMatches.length === 0)。
这三行由 fuzzer 在每次运行时强制校验(if (readinessRows.length < 3) throw),也被npm run check与npm run evidence:full覆盖。也就是说,任何人只要把apps、templates、website之类的旧目录放回实验台,整条验证链就会红——这就是"硬切"的落地方式:不是靠人约定,而是靠基准行失败来强制执行。
来源配置:editor-frameworks-sources.json 与源码抓取
第 000 期记录提到 "Editor framework source config inresearch/editor-frameworks-sources.json"。该文件(benchmarks/editor/research/editor-frameworks-sources.json)当前声明了 2 个来源:
{ "version": 1, "topic": "editor-frameworks", "generatedBy": "@shapeshift-labs/evidence-kit", "sources": [ { "name": "slate-v2-package", "type": "file", "path": "../../.tmp/slate-v2/package.json", "fileName": "slate-v2-package.json", "why": "Slate v2 owns current deep Slate benchmark commands and artifact families." }, { "name": "slate-package", "type": "file", "path": "../../../slate/package.json", "fileName": "slate-package.json", "why": "Slate is the local baseline for Slate v2 compare lanes." } ] }src/index.mjs中的readResearchSources会校验每个来源的name与type为非空字符串,why字段记录了该来源被纳入研究的理由——Slate v2 拥有当前深度基准命令与产物家族,Slate 是对比道的本地基线。对应的抓取命令是npm run research:editor-frameworks:fetch与npm run research:source-pass:fetch,抓取产物用于研究目录(benchmarks/editor/research)下的证据源地图。
控制平面:benchmark-registry.json 的注册表规则
虽然第 000 期主要完成的是引导骨架,但后续第 003 期(benchmarks/editor/iterations/003-evidence-control-plane.md)将注册表确立为控制平面,其规则在第 000 期的决策中已经埋下伏笔:未来对比应进入本实验台,而不是恢复旧的 app/template 动物园。
注册表(benchmarks/editor/research/benchmark-registry.json)的核心政策是:
"policy": { "activeArtifactRule": "Only artifacts listed here are active benchmark evidence.", "discardRule": "Unregistered benchmark JSON files are ignored historical output." }即:只有注册在案的产物才是"活动证据";未被注册的旧tmp/*benchmark*.json一律视为被忽略的历史输出,除非为它添加注册表条目、目标自有适配器、fuzzer、corpus 用例、基准行或来源记录(README 的 Rule 一节)。注册表当前登记的产物家族包括:
- React 大型文档:
react-huge-document-legacy-compare(5,000 块、3 次迭代、20 次类型操作、combined-selection 道)、react-huge-document-overlays、react-huge-document-browser-trace、react-huge-document-slate-browser-trace; - React 局部性:
react-rerender-breadth; - React 打字:
react-active-typing-breakdown; - 核心当前:
core-normalization-current、core-query-ref-observation、core-node-transforms、core-text-selection、core-editor-store、core-refs-projection、core-transaction-current(可选); - 核心对比:
core-huge-document-compare、core-normalization-compare、core-observation-compare、core-rich-text-operations-compare、history-compare; - 剪贴板 / 协作 / issue 回放:
clipboard-large-payload、collab-readiness、issue-6038-transaction-execution、history-retained-memory(可选)。
每个产物条目都携带id、category、kind(slate-legacy-compare/current/compare/browser-trace/rows)、owner、family、cwd、实际运行命令(command)、产物路径(path)、required标志与decision(该基准要回答的问题)。required: true的产物缺失会被健康报告标记为missing-artifact,反之标记为optional-missing-artifact——这正是第 002 期记录(benchmarks/editor/iterations/002-rich-text-editor-evidence-matrix.md)中"红行有用"机制的基础:缺件、超预算、未注册都会显式暴露出来,而不是被静默吞掉。
验证命令:从 check 到 evidence:full
第 000 期记录给出了三条核心验证命令,全部在 benchmarks/editor 目录内执行:
npm run check npm run evidence:full npm run docs:perf:search -- editor benchmark结合 benchmarks/editor/package.json 的脚本定义,可以拆解出完整链路:
npm run check:对src/index.mjs、fuzzer、全部benchmarks/*.mjs逐个执行node --check语法检查,然后执行npm run evidence:full。这是"语法 + 全量验证"的总入口。npm run evidence:full:按序执行test:evidence:fuzzer 跑 200 个生成用例 + 包边界门禁(--bytes 200000 --files 32 --packBytes 1250000 --packFiles 96,即源码 ≤200KB / ≤32 文件、dry-run 打包 ≤1.25MB / ≤96 文件);fuzz:fuzzer 跑 1000 个生成用例并写入复现文件(--write-repro test/fixtures/repro-latest.json);bench:evidence:依次产出core-latest.json、slate-v2-legacy-latest.json、rich-text-editors-latest.json,再以--p95Ms 100 --exports 32检查启动导入,最后执行evidence:health;bench:startup:check、bench:package:gates;bench:scope(evidence-kit scope --update);docs:perf与docs:perf:check(生成并校验 HTML 看板);research:list。
npm run docs:perf:search -- editor benchmark:调用evidence-kit search,在生成的 perf 文档中检索关键词。例如要检索 Slate v2 与 Slate 的对比证据,可执行npm run docs:perf:search -- slate-v2 slate(见第 001 期记录 benchmarks/editor/iterations/001-slate-v2-legacy-evidence.md)。
bench:evidence的核心产物都落在 benchmarks/editor/benchmarks/results 目录,包括:
slate-v2-legacy-latest.json:第一个直接的 Slate v2 vs Slate 运行时对比结果(5,000 块工作负载);rich-text-editors-latest.json:综合基准矩阵,从活动注册表再生成;benchmark-health-latest.json:健康报告与排名后的下一步行动(next actions);core-latest.json、startup-import-latest.json、package-boundary-gates-latest.json、benchmark-scope-latest.json:核心行、启动检查、包边界门禁与作用域快照。
当前结果与决策:先证明形态,再谈数字
第 000 期记录对"当前结果"的表述非常克制:"The lab can prove its own evidence shape before anyone starts claiming Slate v2 vs Slate numbers."也就是说,引导阶段的验收标准不是某个性能数字,而是证据形态本身可证明——契约可校验、来源可追溯、硬切可执行、产物可再生成。
配套的 Decision 则划定了边界:
- 未来的编辑器对比应该在本实验台内添加来源可追溯的 Slate v2 vs Slate 基准行;
- 不应恢复被删除的 app/template 动物园,除非某条基准行证明了"浏览器目标应用才是正确的所有者"。
后续迭代印证了这一决策的落地方式:第 001 期把slate-v2-legacy对比产物作为第一条真实对比道,并刻意保留"混合结果"——部分选择密集行偏向 Slate chunking,就如实保留,因为"保留不舒适的行,而不是把基准变成营销"("the lane is useful because it preserves uncomfortable rows instead of turning the benchmark into marketing");第 002 期把综合矩阵作为当前富文本基准权威,并公开列出超预算红行(如cutTwoBlocksEditMsP50/cutTwoBlocksMsP50)与可选缺失产物(slate-transaction-benchmark.json、slate-history-retained-memory-benchmark.json);第 004 期(benchmarks/editor/iterations/004-clipboard-over-budget-investigation.md)则直接围绕剪贴板超预算行展开调查。这套"红行即线索"的做法正是证据优先实验台的核心价值。
延后事项与演进路径
第 000 期记录的 Deferred 部分明确了两项遗留工作:
- 更多的 Slate v2 vs Slate 浏览器交互证明(Additional Slate v2 vs Slate browser interaction proof)——当前对比道集中在巨大文档工作负载,浏览器交互面(selection、输入法、拖拽等)仍需补充;
- 将更多既有 Slate v2 基准产物规范化进本实验台——即按注册表规则逐一登记、归一化,而不是直接引用历史 JSON。
对想深入该实验台的读者,建议按以下顺序阅读:
- 实验台总览与命令清单:benchmarks/editor/README.md;
- 归一化契约与目标声明:benchmarks/editor/src/index.mjs;
- 活动注册表与控制平面:benchmarks/editor/research/benchmark-registry.json;
- 来源配置:benchmarks/editor/research/editor-frameworks-sources.json;
- 迭代记录:benchmarks/editor/iterations(000–004);
- 最新证据产物:benchmarks/editor/benchmarks/results;
- 生成的 HTML 看板:benchmarks/editor/docs/perf。
小结
第 000 期引导为 Plate 的编辑器基准研究确立了三个不可退让的基线:证据契约归一化(所有产物落到统一的category/fixture/library/status/medianUs/p95Us/ops/bytes行形态)、来源与注册表可追溯(只有注册在案的产物才算活动证据)、硬切守护(旧 app/template 路径一旦回归,验证立即失败)。在此之上,实验台先用npm run check/npm run evidence:full证明自身形态成立,再逐步吸纳 Slate v2 vs Slate 的对比道——无论结果是否"好看",都以红行如实呈现。这种"先证明形态、再输出数字"的纪律,正是富文本编辑器基准研究区别于营销型基准的关键所在。
【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考