首页
/ Home Assistant Core 的 AI 编码代理规范:CLAUDE.md 中的 Git、开发环境与测试标准详解

Home Assistant Core 的 AI 编码代理规范:CLAUDE.md 中的 Git、开发环境与测试标准详解

2026-09-04 11:21:16作者:袁立春Spencer

CLAUDE.md(它是指向 AGENTS.md 的符号链接)是 Home Assistant 核心仓库为 AI 编码代理与人类贡献者共同编写的“单一事实来源”式开发手册。本篇以该文档为骨架,逐条还原其规定的 Git/PR 纪律、基于 uv + prek 的本地开发环境、Python 3.14 语法约定、pytest 与 Syrupy 快照测试标准、代码质量实践,以及 Open Home Foundation 的 AI 政策边界;并结合仓库内真实的脚本、配置与源码实现给出可验证的依据,帮助你在提交任何 PR 之前,先理解 Home Assistant 对“可被维护者理解、可解释、可复现”的硬性要求。

文档定位:CLAUDE.md 是什么

仓库根目录下的 CLAUDE.md 是一个符号链接,实际内容是 AGENTS.md,标题为 “GitHub Copilot & Claude Code Instructions”。它面向两类读者:一是 Claude Code、GitHub Copilot 这类在本地仓库中执行编码任务的 AI 代理,二是需要与这些代理协同的人类贡献者。文档开篇即说明:

This repository contains the core of Home Assistant, a Python 3 based home automation application.

这意味着后续所有规范都围绕“Python 3 的 Home Assistant 自动化内核”这一语境展开。它不是泛泛的项目介绍,而是一份“如何在这个仓库里正确地写代码、跑测试、开 PR”的操作契约。文档由六节构成:Git 提交规范、Pull Requests、Development Commands、Python 语法说明、Testing、Good practices,最后以 AI policy 收束。下文按此脉络逐节展开,并在每节末尾给出仓库内的对应证据。

Git 提交纪律:PR 打开后不得重写历史

文档的第一条硬性规定是:

Do NOT amend, squash, or rebase commits that have already been pushed to the PR branch after the PR is opened - Reviewers need to follow the commit history, as well as see what changed since their last review.

即:一旦 PR 分支上的提交被推送到远端、PR 已经打开,就不得再对这些提交做 amend、squash 或 rebase。理由在文档中写明:审查者需要能够顺着提交历史往下走,也要能“看到自上次审查之后又改了什么”。在 Home Assistant 这种由大量维护者长期跟踪单个 PR 的贡献模式下,保留原始提交序列比保持提交历史“漂亮”更重要。因此,修正问题应当以追加新提交的方式进行,而不是改写既有提交。

Pull Requests:模板不可删减,未勾选框必须保留

文档对开 PR 的要求有两条:

  1. 必须使用仓库自带的 PR 模板 .github/PULL_REQUEST_TEMPLATE.md,且绝不能删除模板中的任何内容(NEVER REMOVE ANYTHING)。
  2. 不要删除未勾选的 checkbox —— 保留所有未勾选项,让审查者能看到“哪些选项没有被选中”。

仓库中的 .github/PULL_REQUEST_TEMPLATE.md 印证了这一点:模板包含 Breaking change、Proposed change、Type of change(一组单选 checkbox)、Additional information 以及一张长长的 Checklist。值得注意的是,模板的 Checklist 注释里已经明确把 AI 写进了流程规范:

AI tools are welcome, but contributors are responsible for fully understanding the code before submitting a PR.

并在 checklist 中要求勾选 “I understand the code I am submitting and can explain how it works.” 这与文档末尾的 AI 政策首尾呼应——模板本身就是一张“人类必须理解每一处改动”的声明表。因此“保留未勾选框”的约束并非格式洁癖,而是让维护者能据此判断贡献者是否逐项核对过(例如文档、测试、manifest 是否都处理了)。

开发命令:uv + script/setup + prek 的本地环境

用当前虚拟环境的 python3 跑测试

文档要求 “Run python3 in current virtual environment to ensure the correct Python version is used for testing.” 这是为了避免系统里多个 Python 解释器混用导致的版本错乱。仓库用 .python-version 固定了版本(当前为 3.14.5),pyproject.tomlrequires-python = ">=3.14.2",二者共同锁定了运行时解释器。

script/setup:进入新环境先跑它

文档规定:进入新的环境或 worktree 时,先运行 script/setup 来搭建带全部开发依赖(pylint、pre-commit hooks 等)的虚拟环境,这是提交前必需的。它还特别提示:如果 uv 报告找不到所需 Python 版本的下载,说明当前 uv 版本过旧,需要用官方安装脚本升级 uv 后重跑 script/setup

仓库里的 script/setup 证实了这条链路:它先检查/复制 .vscode/settings.json 模板、创建 config 目录、用 uv venv .venv(或 python3 -m venv)建虚拟环境并激活,随后调用 script/bootstrap、执行 prek install,再 hass --script ensure_config -c config 生成配置,最后向 configuration.yaml 追加 logger 配置块。而 script/bootstrap 负责真正解析依赖:

uv pip install \
  -e . \
  -r requirements_all.txt \
  -r requirements_test.txt \
  colorlog \
  --upgrade \
  --config-settings editable_mode=compat

python3 -m script.translations develop --all

即:以可编辑模式安装核心包,装齐 requirements_all.txtrequirements_test.txt(测试依赖),并顺手为所有集成生成英文翻译。最后一步 script.translations develop --all 正是下一节测试规范里“先重新生成翻译”这一要求的批量版本。

prek:提交前的 lint 与格式检查

文档要求:结束一段编码会话后,运行 uv run --no-sync prek run --all-files 检查 lint 与格式问题。prek 是 pre-commit 的 Rust 实现,仓库在 requirements_test.txt 中固定了 prek==0.2.28,钩子清单见 .pre-commit-config.yaml(含 ruff-pre-commitpre-commit-hooks 等)。.vscode/tasks.json 则把常用开发命令做成了可视化任务:Run Home Assistant Core-m homeassistant -c ./config)、PytestRuffprek run ruff-check --all-files)、PrekPylintCode Coverage 等。文档里 “.vscode/tasks.json contains useful commands used for development” 指的就是这些任务。

Python 3.14 语法说明:别把新特性当问题

这一节专门纠偏 AI 代理常见的“误报”。文档给出三条:

  • Home Assistant 官方最低支持 Python 3.14。不要把需要 Python 3.14 的语法或特性当成问题标出,也不要为旧版 Python 提变通方案。
  • Python 3.14 明确允许 except TypeA, TypeB:(不带括号),绝不能把它当成问题。
  • Python 3.14 惰性求值注解(PEP 649):注解中的前向引用不需要加引号——注解可以引用模块中后定义的名称,而无需引号,也无需 from __future__ import annotations。不要把“注解里的未加引号前向引用”当成问题。

从源码结构看,这一约定直接决定了 lint 与类型检查策略:仓库的 mypy.ini 体积极大(按模块细粒度控制),pylint/pluginsmypy_plugins 里还有定制插件。把这几条写进文档,是为了让 AI 代理在与人类维护者共享同一份“什么算合法”的判定时,不会反复为合法的新语法提无谓的修改建议。

测试标准:pytest、翻译再生成与 Syrupy 快照

用 uv 跑 pytest

文档要求用 uv run --no-sync pytest 运行测试。--no-sync 让 uv 跳过重新同步依赖、直接复用已建好的虚拟环境,避免每次跑测试都触发一次全量解析。

改了 strings.json 就要重新生成英文翻译

这是 Home Assistant 测试体系里一个容易被漏掉的坑。文档规定:修改某个集成的 strings.json 后,跑测试之前要先重新生成英文翻译文件:

python3 -m script.translations develop --integration <integration_name>

原因是:测试读取的是生成的 translations/en.json,而不是直接读 strings.json。仓库中 script/translations/develop.py 就是执行该命令的入口,script/bootstrap 里的 python3 -m script.translations develop --all 是它的“全量”形态。若跳过这一步,翻译相关断言会因为 en.json 过期而误判。

类型注解、具体类型与 usefixtures

文档对测试代码风格的要求可归纳为:

  • 所有测试函数参数都要有类型注解
  • 优先使用具体类型(如 HomeAssistantMockConfigEntry)而非 Any
  • 若 fixture 参数用不到,优先用 @pytest.mark.usefixtures 而非形参;
  • 避免在测试里写条件/分支,要么拆分测试,要么调整参数化覆盖所有情况;
  • 多个测试共享大部分代码时,用 pytest.mark.parametrize 合并为一个参数化测试,用 pytest.paramid 给用例命名。

Syrupy 快照:.ambr 优先于手写数据

文档明确:Home Assistant 使用 Syrupy 做快照测试,应“利用 .ambr 快照,而不是在 Python 代码里反复、穷举地生成测试数据”;同时,测试中硬编码 entity_id 是允许的,若同一 entity_id 重复出现则抽成常量。

仓库证据充分:requirements_test.txt 固定 syrupy==6.0.0tests/conftest.pyfrom syrupy.assertion import SnapshotAssertion,并引入定制扩展 from .syrupy import HomeAssistantSnapshotExtension;快照实体就存放在各集成测试目录下,例如 tests/components/acaia/snapshots/test_button.ambr。这意味着断言应指向快照文件,而不是在测试里逐字段拼出期望值。

代码质量实践:从 Quality Scale 到 admin service

以 Platinum/Gold 集成为范例

文档建议:Quality Scale 达到 Platinum 或 Gold 的集成,代表高标准的代码质量与可维护性,是“找某件事该怎么做”时的好起点;级别标注在集成的 manifest.json 里。仓库里大量 manifest 确实携带该字段,例如 homeassistant/components/acaia/manifest.json"quality_scale": "platinum",而 actiontecads 等为 legacyai_taskinternal

实体动作审查:别为已校验的输入再加防御

文档规定,审查实体动作(entity actions)时,不要对“已被 Home Assistant 的服务/动作 schema 与实体选择过滤器校验过”的输入字段额外建议防御性检查;只有当数据绕过这些校验器、或被转换成更不安全的形式时,才建议加守卫。这与下一条一脉相承:当校验已保证某个 dict key 存在时,优先用直接键访问 data["key"],而不是 .get("key"),让契约违反暴露出来,而不是被静默掩盖。

注释只解释 why,不解释 what

文档对注释的约束相当细:保持简短,一行说明“不显然的约束”或干脆不写;不要写只是复述下一行代码的注释(例如在 if self.initialized: 上方写 # Check if initialized);注释只解释 why(不显然的约束、意外行为、workaround),绝不解释 what;不要加“引用旧代码长什么样”来为改动辩护的注释;不要在函数内外加 # --- XYZ Triggers --- 这类分节/分隔注释(它们容易过时且误导)。例外:测试里解释某个函数调用或断言“为什么”的注释是允许的。

异常捕获要小

文档要求 try 块尽可能小:不要把大段代码包进一个 try,也不要捕获“本不该抛异常”的函数所抛的异常。这与前文“校验已保证 key 存在就直接访问”共同构成一个取向——让真正的错误尽早、原样地浮出来。

敏感服务要用 admin service 注册

文档规定:可能修改配置或有安全影响的敏感服务动作,应当要求 admin 用户,并用 async_register_admin_service 这个服务 helper 来注册(它会替你完成权限检查)。仓库里该 helper 定义在 homeassistant/helpers/service.py 第 997 行附近,其内部通过 _async_admin_handler 检查 user.is_admin,不满足则抛出 Unauthorized。因此“用 async_register_admin_service 而不是自己手写权限判断”是把安全检查收敛到一处、避免遗漏的关键实践。

AI 政策:人类必须在环

文档最后一节指向 AI_POLICY.md(Open Home Foundation AI Policy),并给出三条红线:不接受“自主(autonomous)”贡献——任何改动在提交前都必须由人类审查、理解并能解释;不要自主开 issue 或 PR;也不要在用户审查之前代用户发评论。

AI_POLICY.md 进一步细化:明确不允许用自主 agent 参与贡献,疑似自主创建的 PR/issue 会被关闭,绕过模板的贡献也会被处理;AI 可以用来帮你写,但不要让工具替你发未审查的内容;要能用自己的话解释 PR 描述与对问题的回答;若要在评论里引用与 AI 的交互,必须用引用块并披露,且附上你自己的评注。文末还声明:该政策的权威版本以官方发布页为准,出现差异时以发布版为准。

从整体结构看,CLAUDE.md/AGENTS.md 把“AI 可以做/不可以做什么”与“贡献代码/测试/PR 的具体标准”放在同一份文档里,目的正是让 AI 代理与人类维护者在同一套规则下协作:AI 负责提效,人类负责理解与担责。

要点速查

主题 规则 仓库证据
提交纪律 PR 打开后不得 amend/squash/rebase AGENTS.md
PR 模板 .github/PULL_REQUEST_TEMPLATE.md,不删任何内容/未勾选框 .github/PULL_REQUEST_TEMPLATE.md
环境 进新环境先跑 script/setup;用当前 venv 的 python3 script/setup.python-version
依赖安装 uv pip install -e . -r requirements_all.txt -r requirements_test.txt script/bootstrap
提交前检查 uv run --no-sync prek run --all-files .pre-commit-config.yaml.vscode/tasks.json
Python 版本 最低 3.14;except A, B: 合法;PEP 649 前向引用免引号 pyproject.tomlrequires-python = ">=3.14.2"
跑测试 uv run --no-sync pytest tests/conftest.py
翻译 strings.json 后先 python3 -m script.translations develop --integration <name> script/translations/develop.py
快照 用 Syrupy .ambr,别在代码里穷举数据 requirements_test.txtsyrupy==6.0.0
敏感服务 async_register_admin_service 注册 homeassistant/helpers/service.py
AI 政策 人类必须在环,禁止自主贡献 AI_POLICY.md

按这份手册行事,你的改动在提交前就应满足“本地测试通过、翻译已再生成、prek 检查干净、敏感服务已走 admin helper、每一处改动你能自己解释”这五条,这正是 Home Assistant 维护者判断一个 PR 是否“可合并”的核心依据。

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