首页
/ Headroom 贡献指南全解:从 PR 工作流、Real behavior proof 到本地开发环境与架构原则

Headroom 贡献指南全解:从 PR 工作流、Real behavior proof 到本地开发环境与架构原则

2026-09-06 23:56:13作者:毕习沙Eudora

Headroom 是一个在工具输出、日志、文件与 RAG 块到达 LLM 之前对其进行压缩的上下文优化层,代码库由 Python SDK、Rust 工作区(crates/)与配套代理、MCP 服务器组成。本文基于仓库根目录的 CONTRIBUTING.md 完整展开:先讲清"哪类贡献应该走哪条路径",再逐条拆解 bug 修复的证据要求、新功能规范(spec)与供应链审查清单;最后结合仓库中的真实配置文件(.pre-commit-config.yaml.commitlintrc.jsonMakefile.github/PULL_REQUEST_TEMPLATE.md)讲透本地开发环境搭建、git hooks 机制与 Ruff/pytest 代码规范,帮助你在提交 PR 前把验证工作做在本地做完,而不是交给评审人。

一、贡献入口:哪类改动走哪条路径

CONTRIBUTING.md 开篇就定调:这些规则存在是因为"跳过它们被坑过",而不是官僚主义。参与贡献即表示同意 CODE_OF_CONDUCT.md。文档用一张表明确了不同贡献类型的入口:

类型 应该怎么做
缺陷或小修复 直接开 PR(必须带复现步骤 + 测试)
新功能 / 架构级改动 先开 issue 或在 Discord 里问,拿到维护者认可后再动手
纯重构 不要提。除非维护者明确要求、且作为某个具体修复的一部分
只为修复已知 main 分支失败而改测试/CI 不要提,团队已在跟踪
新增依赖或版本升级 PR 中必须附书面理由(见第四节)
提问 去 Discord #help 频道

此外有一条硬性限制:每位作者的开放 PR 上限为 10 个,先把已开的 PR 合掉再开新的。

这条分流表的意图很明确:维护者的精力优先花在"有复现、有测试的缺陷修复"上,而对方向未对齐的功能开发、无收益的重构、重复的 CI 修补直接关门。读源码结构可以看到,仓库的 PR 模板 ​.github/PULL_REQUEST_TEMPLATE.md 把这套要求固化成了勾选清单——从"Testing"的实测输出粘贴框,到"Real Behavior Proof"的四要素,再到"Review Readiness"的自评审框,PR 模板本身就是贡献规则的机器可读版本。

二、两条指导原则:验证是你的事,供应链是真威胁

文档"Guiding principles"一节给出两条总纲:

  1. 验证是作者的工作,不是评审人的工作(Verification is the author's job, not the reviewer's)。
  2. 供应链是真实威胁——任何依赖变更都要经过人工审查,每一次,没有例外。

第 2 条在仓库里能读到具体的工程背景:pyproject.toml 对核心依赖做了大量带注释的版本锁定与排除,例如 ast-grep-cli>=0.30.0,!=0.44.1(0.44.1 是一个携带恶意二进制的供应链污染构建),[tool.uv.constraint-dependencies] 里逐项列出针对传递依赖的 CVE 最低版本约束(pygments、gitpython、lxml-html-clean、cryptography 等)。这些注释本身就是"依赖变更必须写明理由"这条规则在日常维护中的落地形态。

三、Bug 修复与"Real behavior proof"

3.1 缺陷 PR 的两个必备件

每个 bug-fix PR 必须包含:

  1. 一个复现——最小代码、失败的测试,或明确的复现步骤;
  2. 一个在修复前失败、修复后通过的测试(单元、集成或 e2e 均可)。

如果确实无法编写测试,必须在 PR 中明确说明,并解释你实际是如何验证的。

3.2 每个外部 PR 都要求"真实行为证明"

原文的表述很直白:"We can't merge what we can't verify."(我们无法合并无法验证的东西)。PR 正文必须包含一个 Real behavior proof 小节,覆盖四个要点:

  • 测试所用的环境(操作系统、Python 版本、配置、provider/模型);
  • 打补丁之后运行过的确切命令或步骤
  • 修复后的证据 + 观察到的结果
  • 你没有测试什么

证据的合格线也有明确区分:

  • ✅ 算数的:截图、录屏、终端输出、复制的实时输出、关联的工件、脱敏后的运行日志;
  • ❌ 单独不算数的:单元测试、mock、快照、lint、类型检查、绿色 CI——"它们只能证明测试通过了,不能证明功能是真的在工作"。测试当然要写,但不能替代真实运行证据。

缺少这一节的 PR 可能被自动关闭(autoclosed)。

对应地,.github/PULL_REQUEST_TEMPLATE.md 中已经把这一节模板化:

## Real Behavior Proof
- Environment:
- Exact command / steps:
- Observed result:
- Not tested:

模板还额外要求"Testing"一节里粘贴真实命令输出(pytestruff check .mypy headroom 的勾选与输出),以及一段"Runtime Rollout Safety"清单(功能是否受 rollout 通道管理、默认行为是否改变、kill switch 与回滚路径)——后者对代理这类会拦截真实流量的组件尤为重要。

四、新功能:先规范后代码

功能开发在写代码之前必须走完三步:

  1. 开一个 feature-request issue(或在 Discord 提出);
  2. 实现之前先拿到核心维护者的认可(一个 👍);
  3. 附一份简短的 spec,至少覆盖:
    • API 表面(公共函数、配置项、CLI 参数);
    • 对现有行为的改变
    • 用户故事——Given / When / Then,黄金路径 + 一个边缘用例;
    • 失败模式(Failure modes);
    • 恢复 / 韧性(Recovery / resilience);
    • 安全考量

文档的收尾一句话值得记住:"Short and concrete beats long."(短而具体胜过长而空泛)。这套 spec 结构与代码库中可见的架构文档风格一致,例如 RUST_DEV.md 对 Rust 重写各阶段的阻塞项、已知回归、后端选择(InMemoryCcrStore / SqliteCcrStore / RedisCcrStore)都采用"状态 + 证据(测试文件路径)+ 处置结论"的写法,正是"具体、可验证"规范的示范。

五、依赖与供应链审查清单

人工维护者会审查每一个依赖变更。新增或升级包的 PR 必须回答四个问题:

  • 为什么是这个包(而不是自己实现 / 用已有依赖);
  • 谁在维护它(活跃度、发布节奏、安全记录);
  • 安装面(传递依赖、原生代码、安装期/运行期是否需要网络);
  • 为什么是这个版本——被允许的理由只有三种:bug 修复、安全补丁、必须的新功能。纯粹为了版本好看而做的升级(cosmetic bump)会被直接关闭。

六、PR 工作流:从分支到标题的完整链路

6.1 七步流程

  1. Fork 仓库,从 main 拉分支;
  2. 安装 Node 18+,然后执行 uv sync --extra dev,再执行 make install-git-hooks——这一步会把仓库的预提交检查装到每个 commit 上、把 commitlint 装到每条 commit message 上、把 ci-precheck 装到每次 push 上;
  3. 一个 PR 只做一个逻辑变更;
  4. 补测试;
  5. 本地跑:uv run pytestuv run ruff check .uv run ruff format .
  6. 不要手改 CHANGELOG.md——release-please 会根据你的 Conventional Commit PR 标题自动生成它,所以一个清晰的 fix(...) / feat(...) 标题本身就是你的 changelog 条目;CI 中有守卫会拒绝手动编辑;
  7. 开 PR:清晰的描述 + Real behavior proof + 所需的 spec/理由,并在"Review Readiness"勾选完成前保持 draft 状态。

6.2 标题与 commit message 格式

PR 标题遵循 Conventional Commits:feat:fix:docs:test:refactor:。commit message 格式在本地由仓库的 commit-msg hook 强制,CI 中再次校验。

仓库里的 .commitlintrc.json 定义了完整的规则:继承 @commitlint/config-conventionaltype-enum 允许的 13 种类型为 buildchorecidocsdepsfeatfixparityperfrefactorrevertstyletest(注意 parity 是 Headroom 特有的类型,对应 Rust 移植中的 Python 行为对齐工作);同时放开了 body-max-line-lengthfooter-leading-blanksubject-case 三项默认约束。

评审合入的条件:CI 全绿、一位维护者 review、覆盖率持平或提升

6.3 本地 git hooks:把 CI 搬进你的终端

make install-git-hooks 背后是 scripts/install-git-hooks.sh,它安装三层钩子:

  1. pre-commit:每次提交时运行 .pre-commit-config.yaml 中的检查;
  2. commit-msg:每次提交时运行 commitlint 校验 Conventional Commit 格式;
  3. pre-push:每次 push 前运行 make ci-precheck,即与 GitHub Actions 完全相同的门禁。

脚本注释里记录了引入 pre-push 的动机:某次 push 连中五个 CI 失败(cargo fmt 漂移、多余的 x86_64-apple-darwin wheel、两个 CI 分支缺少 Rust 扩展、commitlint 警告被当错误处理),而 pre-commit 钩子的引入则是因为有 PR 在 ruff 钩子从未安装给贡献者的情况下带着导入顺序违规合入了主干。

.pre-commit-config.yaml 中的具体钩子包括:

  • sync-plugin-versionsscripts/sync-plugin-versions.py,提交时同步各插件清单中的版本号);
  • verify-ruff-version(校验 Ruff 版本与锁文件一致,防止本地/CI 版本漂移);
  • commitlint(commit-msg 阶段);
  • check-merge-conflict(带 --assume-in-merge,连 rebase 残留冲突标记也能抓到);
  • ruff(--fix)与 ruff-format,均为 v0.16.3,排除 experiments/ 目录;
  • mypy v1.14.1,pass_filenames: false 整库跑 mypy headroom

Makefile 中的 ci-precheck 目标则聚合了三类本地门禁:

make ci-precheck          # 全部 CI 门禁(rust + python + commitlint)
make ci-precheck-rust     # cargo fmt --check + clippy + cargo test --workspace
make ci-precheck-python   # 构建 Rust 扩展后,跑 smart_crusher 受影响的一组 pytest 文件
make ci-precheck-commitlint  # npx commitlint --from origin/main --to HEAD

设计意图写在 Makefile 注释里:如果 make ci-precheck 是绿的,git push 就不会在 CI 变红。其中 ci-precheck-python 会先执行 scripts/build_rust_extension.sh,因为 SmartCrusher 等转换器会硬导入 PyO3 编译出的 headroom._core。pre-push 钩子内置了逃生通道 git push --no-verify,但脚本明确标注"每一次跳过都是对 CI 翻车的一次掷骰子"。

七、开发环境搭建

7.1 标准本地环境

文档给出的初始化序列:

git clone https://github.com/headroomlabs-ai/headroom.git
cd headroom
python -m venv .venv && source .venv/bin/activate
node --version  # Node 18+ required for commitlint hooks
python -m pip install --upgrade pip
python -m pip install -e ".[dev,relevance,proxy]"
python -m pytest

两个适用前提值得注意:

  • Node 18+ 是硬要求,因为 commitlint 钩子依赖 npx
  • Headroom 使用 pyproject.toml 中声明的 maturin 构建后端build-backend = "maturin"),一个 wheel 同时包含 headroom/ 下的 Python 源码和 Rust 编译产物 headroom/_core.so(由 crates/headroom-py 的 cdylib 注入)。老版本 pip 可能因为找不到 setup.py 而失败可编辑安装——先升级 pip,或改用 uv sync --extra dev

extras 的选择与验证目标相关:dev 提供 pytest/ruff/mypy 等开发依赖,relevance 提供 fastembed 语义相关性栈,proxy 提供 FastAPI/uvicorn/MCP 等代理服务器依赖(见 pyproject.toml[project.optional-dependencies])。

7.2 Dev Containers

仓库为 VS Code / Codespaces 提供两套配置:

  • .devcontainer/devcontainer.json——基础环境:文档标注为 Python 3.12、uv、Node.js、gh。对照配置文件实际内容,容器还固定了 Node 20 与 Rust 1.95.0(带 rustfmt/clippy 组件,与 rust-toolchain.toml 的 stable 版本钉选一致),并把代理端口 8787 设为自动转发的转发端口;
  • .devcontainer/memory-stack/devcontainer.json——通过 docker compose 额外拉起 Qdrant + Neo4j 两个 sidecar,转发 8787(代理)、6333/6334(Qdrant REST/gRPC)、7474/7687(Neo4j Browser/Bolt);按文档说明,在容器内连接时使用 qdrant:6333neo4j://neo4j:7687

容器内日常命令统一为:uv run ruff check .uv run pytest 等。

7.3 Rust 侧的常用命令

本仓库是 Python + Rust 双栈,贡献涉及 crates/ 时应熟悉 RUST_DEV.mdMakefile 中的目标:

目标 作用
make test cargo test --workspace
make test-parity 运行 headroom-parityparity-run,对 tests/parity/fixtures 下的 Python 录制输出做 Rust 行为比对
make bench cargo bench --workspace
make build-proxy release 构建 headroom-proxy 并 strip,打印产物体积
make build-wheel maturin build --release -m crates/headroom-py/Cargo.toml
make fmt / make lint cargo fmt --all / cargo fmt --check + cargo clippy --workspace -- -D warnings

default-members 会把 headroom-py 排除在裸 cargo test --workspace 之外,避免 PyO3 cdylib 在无 Python 宿主时被当作可执行目标运行。

八、可选的自动化 Review

仓库内置了 .github/copilot-instructions.md,让维护者可以按需启用 GitHub Copilot 代码评审,而不必为每个 PR 增加工作流计费噪音。文档给出的操作路径是:在仓库的 Settings → Rules → Rulesets → Automatically request Copilot code review 中开启或关闭,并建议除非维护者明确需要额外的评审流量,否则保持关闭

九、编码标准与架构原则

9.1 编码标准

  • Ruff 统一负责 lint 与 format,行宽 100,PEP 8。pyproject.toml[tool.ruff]target-version 钉在 py310,lint 规则集为 E/W/F/I/B/C4/UP(pycodestyle、pyflakes、isort、bugbear、comprehensions、pyupgrade),并把 headroom 声明为 known-first-party;
  • 公共函数必须带类型注解,docstring 用 Google 风格。pyproject.toml[tool.mypy] 全局开启 disallow_untyped_defs,仅对 handler mixin、动态导入较多的模块(headroom.proxy.handlers.*tokenizers.*providers.litellm 等)做了按模块豁免;
  • 覆盖新行为 + 边缘用例,新增代码追求 >80% 覆盖率
  • Python 3.10+requires-python = ">=3.10",classifiers 覆盖到 3.14),可选功能一律放在 extras 后面,核心安装保持轻量。

9.2 架构原则

文档最后两条原则是对代码风格要求之上更硬的约束:

  • 安全优先:绝不丢弃 user/assistant 内容、绝不破坏 tool call/response 配对、畸形内容原样透传、宁可漏报(false negative)也不误伤;
  • 性能:transform 在 P99 < 50ms,可选依赖懒加载,先 profile 再优化。

这两条与测试目录的命名可以直接对应上验证方式:tests/test_compress_passthrough.py 验证"压缩失败时透传"、tests/test_lossless_*.py 系列验证无损优先的压缩策略、tests/test_compression_safety_rails.py 验证安全护栏——"绝不破坏 tool call 配对"这类原则在仓库里都有对应的回归测试承接。

十、贡献者如何被署名

贡献者会在三处获得署名:CHANGELOG、GitHub contributors 页面、release notes。而由于 changelog 由 release-please 从 Conventional Commit 标题生成,你的 PR 标题就是最终面向所有用户的署名载体——把 fix(...) / feat(...) 标题写清楚,既是格式要求,也是内容要求。


要点回顾:Headroom 的贡献流程以"验证是作者的责任"为纲——bug 修复要带复现和翻转测试,外部 PR 要带 Real behavior proof(环境、确切命令、证据、未测项),新功能要带六要素 spec,依赖变更要带供应链四问;本地侧则用 make install-git-hooks 把 ruff/mypy/commitlint/ci-precheck 三层门禁装进每个 commit 与 push,配合 Dev Container(Python + Node 20 + Rust 1.95,可选 Qdrant/Neo4j sidecar)即可获得与 CI 一致的开发体验。按这套流程执行,你的 PR 在提交时就已经通过了评审人将要做的大部分验证。

登录后查看全文
热门项目推荐
相关项目推荐