Lance 格式规范文档编写指南:从 PMC 投票门禁到可执行的规范写作范式
【免费下载链接】lanceOpen Lakehouse Format for Multimodal AI. Convert from Parquet in 2 lines of code for 100x faster random access, vector index, and data versioning. Compatible with Pandas, DuckDB, Polars, Pyarrow, and PyTorch with more integrations coming..项目地址: https://gitcode.com/GitHub_Trending/la/lance
docs/src/format/CLAUDE.md是 Lance 开源仓库中面向格式规范(Format Specification)文档维护者的写作与治理指南。它规定了docs/src/format/目录下格式规范文档的变更流程、写作风格与内容底线,并指向了由 CI 强制执行的format-spec-vote投票门禁。本文将逐条解读这份指南,并结合仓库中的 投票流程、门禁实现、门禁单元测试 以及 格式规范目录 下的真实规范文档,说明 Lance 如何把"格式即契约"这一理念落到文档治理与工程机制上。读完你将掌握:什么是 Lance 格式规范文档、如何合规地提出一次格式变更、规范文档应当满足哪些风格与内容标准,以及门禁背后的源码实现细节。
一、定位:Lance 格式规范文档是什么
在 Lance 仓库中,docs/src/format/目录承载的是格式规范(Format Specification),而非使用教程。它与 docs/src/guide/ 这类用户指南有明确分工:规范文档描述"数据在磁盘上如何组织、如何演进、如何保证兼容",是面向实现者与引擎开发者的契约;用户指南则给出可运行的代码示例。
从 格式规范索引 可以看到,Lance 被定义为一组分层互操作的规范栈,而非单一文件格式:
- 文件格式(file/index.md):以大页(page)存储列数据,针对随机访问优化,不使用 Parquet 式的 row group;
- 表格式(table/index.md):管理 fragment、manifest、删除、schema 演进与 ACID 提交;
- 索引格式(index/index.md):标量索引、向量索引、系统索引等冗余搜索结构;
- 目录/服务目录规范与统一命名空间接口:负责表的发现、注册与跨引擎协调。
CLAUDE.md正是这套规范栈的"元文档"——它不写格式本身,而是规定写格式文档的人应该怎么做。其内容分为三块:变更流程(Change Process)、写作风格(Style)、内容要求(Content)。
二、变更流程:PR 即提案,PMC 投票由 CI 强制
指南的第一条原则是:
Changes here require a PMC vote on the pull request, enforced by the
format-spec-voteCI gate.
即:任何对格式规范的修改都必须在 PR 上经过 PMC(项目管理委员会)投票,且这一要求由 CI 门禁结构性强制,而不是靠惯例自觉。这条规则同时存在于 docs/src/format/AGENTS.md 与 protos/AGENTS.md 中——因为格式规范的实体既包括docs/src/format/**的文档,也包括 protos/ 下的 protobuf 定义,两者都受同一门禁约束。
2.1 提案范围:一个 PR 只装一份契约
指南要求将规范变更放在独立的 PR中提交,该 PR 只包含三样东西:
- 规范文档本身的修改(
docs/src/format/**); - 配套的
protos/定义变更; - 仅足以让构建通过的最小库改动(例如匹配某个重命名的生成字段)。
具体实现必须放在后续 PR 中。理由在 投票流程 中讲得很透彻:投票的对象是格式——一个比任何具体实现都长寿的兼容性契约。如果 PR 同时携带 reader、writer 和测试改动,会把契约淹没在实现细节里,还让一次普通代码评审白白经历 72 小时投票期。讨论通过 PR 上的 review 评论进行,评审者可以针对规范的具体行发表意见;PR 可以先用 draft 形态开启,投票期从标记 ready for review 时才开始。
2.2 何时触发投票:format-change 标签
一个 PR 在满足以下任一条件时被判定为格式规范变更(由路径 labeler 自动打上format-change标签):
- 修改了 protobuf 定义(
protos/**/*.proto); - 修改了规范文档(
docs/src/format/**)。
门禁代码 ci/format_vote_gate.py 中的常量印证了这一点:FORMAT_LABEL = "format-change"、STATUS_CONTEXT = "format-spec-vote"。非格式 PR 会被直接放行并设置 success 状态("No format-spec change; vote not required."),不参与投票流程。
2.3 通过门槛:三个条件缺一不可
要合并一个format-changePR,必须同时满足(见 投票流程 与门禁decide_verdict的裁决优先级):
- 3 个绑定 +1 票:3 名 PMC 成员(排除提案者本人)approve 该 PR。只有最新 commit 上的 approval 才有效——新推送会使旧 approval 失效(因为提案内容变了)。
- 无否决:任何 PMC 成员的 "Request changes" review 都是一票否决(veto),会一直阻塞合并直到撤回。
- 最短投票期:自投票开启起满72 小时,周末不计入。投票期在 PR 同时满足"被打上
format-change标签"和"标记 ready for review"两个条件后开启,取两者较晚者;周末以 UTC 为界划分,门禁会在 PR 评论中给出 UTC 与太平洋时间的精确截止时刻。
另外,format-waived标签允许 PMC 成员对不改变格式的琐碎编辑(拼写、措辞、格式调整)免除投票。
三、门禁源码解析:format-spec-vote 如何工作
投票要求由 ci/format_vote_gate.py 结构性执行。该脚本通过 GitHub API 读取 PR 的 timeline 与 reviews,发布format-spec-vote提交状态(required status check)并维护一条带<!-- format-spec-vote-status -->标记的汇总评论。其核心逻辑由一组纯函数组成,全部在 ci/test_format_vote_gate.py 中有单测覆盖。
3.1 计票:tally_reviews
tally_reviews从有序的 review 列表中提取每位成员的"最新立场"(只统计 APPROVED / CHANGES_REQUESTED / DISMISSED 三种表态状态,COMMENTED/PENDING 忽略)。规则包括:
- 非 PMC 成员、PR 作者本人不计数;
- 每位成员以最近一次立场 review为准(先 approve 再 request changes 则算否决);
- approval 只有落在 head commit 上才有效,否则归入
stale_approvals; CHANGES_REQUESTED无论落在哪个 commit 都是一票否决。
对应测试如test_approvals_on_earlier_commit_are_stale、test_only_latest_review_per_member_counts直接验证了这些边界行为。
3.2 裁决:decide_verdict
decide_verdict按优先级返回阻塞条件:
veto > insufficient(批准数不足) > waiting_period(72小时未满) > pass一旦有否决票,其余条件都不再看;批准数不足与等待期也分别阻塞,只有三者全部满足才放行。
3.3 投票窗口:vote_opened_at 与 weekday_deadline
vote_opened_at取"打标签时刻"与"ready for review 时刻"的较晚者作为投票开启点——draft 期间的时间不计入。
weekday_deadline计算"排除了周末的 72 小时"截止时刻:它先把起点推进到最近的周一(_skip_weekend),再逐段扣除工作日直到剩余时间耗尽。周末边界固定以 UTC 定义(WEEKEND_TZ = timezone.utc),避免夏令时与各地时区带来的歧义;截止时间以 UTC 与太平洋时间双格式展示(_fmt_deadline),方便跨时区的 PMC 成员阅读。
3.4 状态机:draft、waived 与普通 PR
Gate.evaluate的完整判断链为:
- 非
format-changePR → 直接 success; - 被打上
format-waived且由 PMC 成员操作 → success("vote waived"); - draft PR → failure 但不发布评论(投票尚未开启,无需公布截止时间);
- 其余情况 → 读取 reviews 与 timeline,计算裁决,更新状态并 upsert 汇总评论。
PMC 名册在运行时从 docs/src/community/pmc.yaml 加载(_load_pmc)。门禁按 15 分钟周期重新评估所有 open 的format-changePR,评论也附带了可手动触发的 re-check 工作流入口。
四、写作风格:规范文档要"可执行"而非"可读就行"
CLAUDE.md对规范文档的风格提出三条硬性要求:
4.1 纯文本参考,不带代码示例
Keep format docs as concise, text-only reference — no code examples.
格式规范是简洁的纯文本参考,代码示例应放到用户指南(user guide)部分。规范文档的读者是引擎实现者,他们需要的是精确的契约描述,而不是一段可能过时的示例代码。
4.2 文件 schema 用 pyarrow 表达
Express file schemas as
pyarrowschema definitions, not markdown tables or informal text — pyarrow schemas are unambiguous and executable.
这一点很有意思:规范文档中的文件 schema 应写成pyarrow schema 定义,而不是 markdown 表格或非正式描述。原因在于 pyarrow schema 是无歧义且可执行的——它本身就是一段可以被 Python 直接解析的声明式代码,既避免了自然语言描述的模糊性,又能被工具链直接校验。反观 markdown 表格,列类型、可空性、嵌套结构都容易产生歧义。
4.3 语言无关的定义
Use language-agnostic definitions (JSON Schema, protobuf) — not language-specific code like Rust structs.
规范必须是语言无关的:用 JSON Schema、protobuf 这类跨语言定义来描述数据结构,而不是 Rust struct、Java class 等具体语言的实现。这与 Lance 多语言生态(Rust 核心 + Python/Java 绑定)直接相关——规范一旦绑定某种语言,其他语言的实现者就无法忠实复现。仓库 protos/ 下大量.proto文件正是这一原则的体现,例如IndexSection、IndexMetadata等消息的定义就是索引规范的权威载体(见 索引规范)。
五、内容底线:把机制和算法讲透
规范文档的内容要求比风格要求更关键,它直接决定了文档能否被实现为正确的代码。
5.1 schema 与数据演进必须写出具体机制
Explain schema/data evolution with concrete mechanics (field IDs, tombstones, data rewrites) — don't just name operations or defer to external specs.
文档不能只罗列操作名称或甩给外部规范,必须讲清楚具体机制:字段 ID 如何分配、tombstone(删除标记)如何记录、数据重写(data rewrite)何时发生。以 表格式规范 为例,它明确写出字段 ID 在初始建表时按深度优先顺序分配、新增字段后增量分配;schema 变更涉及的数据重写细节则在 schema.md 中展开。这种"讲机制"的要求,是为了防止规范只停留在概念层面,导致不同实现各自发挥、最终破坏兼容性。
5.2 算法必须完整描述
Describe all algorithms with full detail: parameters, precision, ordering, normalization bounds, and implementation steps — never reference an algorithm by name alone.
规范中提到任何算法,都不得只报算法名,必须写全:参数、精度、排序规则、归一化边界、实现步骤。这对向量索引、标量索引类文档尤为重要——例如 索引规范 中关于索引段的描述,会精确到fragment_bitmap、covering_fields、版本兼容检查(先查index_details中的类型 URL,再查version字段)这些可实现的细节,而不是笼统地说"用 B-tree 加速查询"。
5.3 索引文档的范式:bitmap.md
Index docs must include explicit file schemas and describe reader navigation (page type distinction, root/entry point location) — follow the pattern in
index/scalar/bitmap.md.
索引类规范文档被要求必须包含明确的文件 schema,并描述 reader 的导航方式(页面类型区分、根/入口点位置)。指南明确点名了范式文档:docs/src/format/index/scalar/bitmap.md。这份文档展示了完整的索引规范结构:
- 索引详情:用 protobuf 消息
BitmapIndexDetails定义(语言无关的载体); - 存储布局:单文件
bitmap_page_lookup.lance,明确列出列的keys(唯一值)与bitmaps(序列化的 RowAddrTreeMap)及其可空性; - 加速的查询类型:Equals / Range / IsIn / IsNull 分别对应的位图操作(查特定位图、范围取并集、值集取并集、取预计算 null 位图)。
keys列类型标注为{DataType}(占位符)正说明这是模板式的 schema 描述——实际类型由被索引列决定,留给实现者去填充,这正是"无歧义 + 可执行"的体现。
六、如何在仓库中实践这套规范
如果你要为 Lance 提交一次格式变更,仓库中实际需要参照的路径如下:
| 用途 | 仓库路径 |
|---|---|
| 规范文档根目录(受投票门禁保护) | docs/src/format/ |
| 本指南(编写规范文档前必读) | docs/src/format/CLAUDE.md 与 docs/src/format/AGENTS.md |
| 投票规则与门槛说明 | docs/src/community/voting.md |
| PMC 名册(决定谁有绑定投票权) | docs/src/community/pmc.yaml |
门禁实现(format-spec-vote状态检查) | ci/format_vote_gate.py |
门禁单元测试(可独立运行:pytest ci/test_format_vote_gate.py) | ci/test_format_vote_gate.py |
| protobuf 格式定义(随文档同步变更) | protos/table.proto 等 |
| 索引规范范式文档 | docs/src/format/index/scalar/bitmap.md |
提交时的正确姿势是:先开 draft PR 展示规范与 proto 变更,标记 ready for review 开启 72 小时投票期,等 3 名 PMC 成员在最新 commit 上 approve;期间任何新推送都会让既有 approval 作废,任何 PMC 成员的 "Request changes" 都是一票否决。琐碎的文字修正可请 PMC 打format-waived标签跳过投票,而格式变更的具体实现请放在后续 PR 中,让评审者始终聚焦于"契约"本身。
结语
docs/src/format/CLAUDE.md表面上只是一份写作指南,实际上它是 Lance 格式治理的最小纲领:用 CI 强制投票流程保证变更审慎,用可执行的 schema 定义消除文档歧义,用"讲机制、写全算法、带 reader 导航"的内容底线确保规范可被任何语言忠实实现。理解了这份指南,就等于理解了 Lance 如何在其多语言生态中长期维持一份稳定、可演进、可实现的存储格式契约。
【免费下载链接】lanceOpen Lakehouse Format for Multimodal AI. Convert from Parquet in 2 lines of code for 100x faster random access, vector index, and data versioning. Compatible with Pandas, DuckDB, Polars, Pyarrow, and PyTorch with more integrations coming..项目地址: https://gitcode.com/GitHub_Trending/la/lance
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考