Files
tliu93 fc4af857e3
frontend / frontend (push) Successful in 46s
pytest / test (push) Successful in 2m33s
AGENTS: make repository guidance harness-neutral
2026-08-18 13:28:23 +02:00

18 KiB
Raw Permalink Blame History

AGENTS.md — Home Automation Backend

本文件是本仓库 coding agent 指引的 single source of truthCLAUDE.md 通过符号链接指向本文件。它定义本项目的工作流程、文档位置、commit 规范。支持对应项目指引的 agent 在动手前应完整读取本文件。

项目速览

  • 个人用 home-automation 应用:FastAPI + React SPA + SQLite + SQLAlchemy + Alembic,前后端同源托管。
  • 单 admin 鉴权(Argon2 + server-side session cookie),runtime config 落 app_config 表。
  • 模块:public IPv4 monitor、SMTP 通知、location / poo recorder、Home Assistant in/out、TickTick OAuth、Modbus / DSMR 能耗采集、MQTT / HA Discovery、动态电价与电费计算。
  • 已发布 v1.5.1。M1、M2、M4-M7 已完成;M3 token / 移动端仍为远期方向。
  • 当前现实:已收敛为单一 app.db、一套 DeclarativeBase 和一条 Alembic 链;只有历史数据迁移 runbook 会读取旧 location / poo 数据库。
  • 明确不做:Notion 模块。

文档地图与「开工前必读」

文档都在 docs/

路径 作用
docs/roadmap.md 全局规划与里程碑总览
docs/design/README.md 协作契约:任务卡格式、原子任务定义、校验闸门、数据安全红线
docs/design/m1-db-consolidation.md M1 原子任务(含真实代码现状盘点 + 人工 runbook)
docs/design/m2-frontend-v2.md M2 原子任务 + API 契约 + 前端校验闸门
docs/design/m3-token-mobile.md M3(远期,暂缓)
docs/design/m4-login-hardening.md M4 登录加固(已完成)
docs/design/m5-iot-energy.md M5 IoT / 能耗采集(已完成)
docs/design/m6-tibber-dynamic-energy.md M6 动态电价、DSMR 与电费计算(已完成)
docs/design/m7-meter-epochs-archival.md M7 电表生命周期 / 换表归档(已完成)
docs/*.mdauth / public-ip-monitor / location-recorder …) 各模块说明,按需读

开工时读取顺序

  1. docs/design/README.md(每轮都读,它是流程与验收的共同契约)。
  2. 本轮对应的 milestone 文档(如 docs/design/m1-db-consolidation.md),定位要做的任务卡。
  3. 任务卡 Files 列出的源文件 + 该模块的 docs/*.md(按需)。
  4. docs/roadmap.md 仅在需要全局视角时读。

工作流程

实现模式(由用户的提示词决定)

  • 默认逐步:给一个 milestone 文档,按其中原子任务一步一步实现。
  • (a) 只实现一步:用户说"只实现一步 / 这一个任务"时,只做那一个任务卡,跑完校验闸门后停下,等用户确认,不要顺手往下做。
  • (b) 完成整个 milestone:仅当用户在提示词里显式要求启用 sub-agent时,才起 implementer / reviewer / fixer sub-agent(按下方**『默认能力档位』**选择模型,用户人工指定则覆盖),按任务依赖顺序跑完整条链。
  • Sub-agent 纪律:只在用户显式要求时才 spawn sub-agent;单步/小改动在主线内联完成。当前 harness 支持独立 sub-agent 时,使用其原生机制按下方**『默认能力档位』**派发;不支持时不得假装已创建 sub-agent,应明确说明限制,并仅在用户允许 fallback 时由主 agent 继续。

默认能力档位(实现模式 sub-agent;可被人工指定覆盖)

起 implementer / reviewer / fixer sub-agent 时,默认按下列能力档位选择当前 harness 支持的模型,无需用户每次人工指定:

角色 通用模型要求 推理档位 Harness 示例(非强制)
Implementer 平衡型代码实现模型 medium 或等效档位 Claude CodeSonnetCodex/OpenAIGPT-5.6 Terra (gpt-5.6-terra)
Fixer(返工) 平衡型代码实现模型 medium 或等效档位 Claude CodeSonnetCodex/OpenAIGPT-5.6 Terra (gpt-5.6-terra)
Reviewer 当前 harness 支持的最强通用推理 / 代码模型 extra-high / xhigh 或等效档位 Claude CodeOpusCodex/OpenAIGPT-5.6 Sol (gpt-5.6-sol)
  • 示例非强制:示例模型只表示当前推荐映射,不构成跨 harness 的硬性模型 ID;当前 harness 不支持时,选择最符合「通用模型要求」的可用模型。
  • 选择优先级:用户显式指定 > 当前 harness 的原生角色配置 > 上表的 harness 示例 > 按通用模型要求自动选择。
  • 推理档位说明:若 harness 提供独立的 reasoning-effort 设置,按上表设置;若不提供,在 spawn prompt 中明确 implementer/fixer 按平衡深度思考,reviewer 按对抗性外部审计强度复核。

角色(Orchestrator → Implementer → Reviewer → Fixer

  • 我(主线)= Orchestrator:挑依赖已满足的下一个任务、派发、转述结果、维护任务 Status
  • Implementer(平衡型代码实现模型,medium 或等效档位):一次一个任务,严格按任务卡,不扩范围。
  • Reviewer(最强通用推理 / 代码模型,extra-high / xhigh 或等效档位):实现完成后起 Reviewer sub-agent,按任务卡 Acceptance criteria + Reviewer checklist 复核、独立重跑校验闸门,驱动返工直到本轮 PASS。
  • Fixer(平衡型代码实现模型,medium 或等效档位):按 reviewer 的编号返工清单返工;每轮返工起一个干净的 Fixer(与首次实现的 Implementer 分开冷启动),先读对应 review-notes/<task>-review-<n>.md 再改。

Reviewer 盲审纪律(M1 教训)

M1 里 review 从未触发过一次 rework,根因是 orchestrator 把自己的结论 / 辩护喂给了 reviewer,造成 context bleed、review 沦为橡皮图章。所以:

  • reviewer 必须使用全新、独立的 sub-agent / thread 冷启动,并最小化喂料——spawn prompt 只给:① 任务卡(Acceptance criteria + Reviewer checklist)、② 对应的 review-notes/<task>-impl|rework-<n>.md 路径、③ 要审的 diff / commit 范围。
  • 不要在 prompt 里塞 orchestrator 自己的判断、"我觉得没问题"、对实现选择的辩护,或上一轮 reviewer 的倾向性结论。让它独立得出结论、独立重跑校验闸门
  • 事后另起的整库独立盲审(如对抗复审)同理:使用全新独立的 agent / thread、最小上下文,把它当"外部审计"而非"确认自己没错"。

校验闸门(每个任务结束都要全绿)

根目录、激活 .venv 后:

pytest                      # 权威闸门(CI 跑的就是它)
ruff check .                # line-length=100
python scripts/export_openapi.py && git diff --exit-code openapi/   # 改了路由/schema 才需要,且产物须入库

前端任务(M2)在 frontend/ 下另跑 npm run lint && npm run typecheck && npm run test && npm run build(详见 m2 文档 §8)。 不过闸门就不算完成,不得跳过、不得留红给下一轮。

Repo-meta 例外:纯文档、agent 指引、符号链接等不影响可执行代码、构建与 API 契约的变更,可在用户明确同意时跳过代码闸门。仍须完成针对性校验(如链接目标、文件类型、diff 与 Git 状态),并在结果中明确记录未运行哪些闸门。

API 契约同步:openapi/schema.d.ts两步v1.4.0 后教训)

只跑 export_openapi.py 不够。 前端的 frontend/src/api/schema.d.ts 是由 openapi/openapi.json 二次生成的,CI.github/workflows/frontend.ymlCheck codegen is in sync)会重跑 codegen 并 git diff --exit-code src/api/schema.d.ts。漏了第二步 → 本地闸门全绿、远端 CI 红。真出过:d07a083 改了 /api/energy/prices 的 docstring 并同步了 openapi.json,但没重跑 codegen,只差一行注释就把 CI 挂了。

所以只要动了路由 / Pydantic schema / 路由 docstringdocstring 也会进 OpenAPI description!),两步都要跑,两个产物都要入库:

# 1) 后端契约
python scripts/export_openapi.py && git diff --exit-code openapi/
# 2) 前端类型(在 frontend/ 下)
npm run codegen && git diff --exit-code src/api/schema.d.ts
  • 判据:git diff --exit-code openapi/ 有输出 → 必然还要跑一次 npm run codegen
  • 反过来也成立:schema.d.ts 不要手改,它是生成物。
  • Reviewer 审"动了路由 / schema / 路由 docstring"类任务时,把这两个产物是否都已重新生成并入库当作 acceptance 的一部分。

构建上下文完整性(M1 Dockerfile 教训)

docker build 不在 pytest/ruff 闸门里——M1 删了 alembic_location/poo 后忘了同步 DockerfileCOPY,单元闸门全绿却把坏掉的镜像构建一路漏到 release tag。所以:

  • 任务删除 / 移动 / 重命名文件或目录时,必须 grep 构建清单是否还在引用它们:Dockerfile(尤其 COPY 源)、docker/*.ini、CI workflow、requirements*.txt 等。
  • 已有回归测试 tests/test_deployment.py::test_dockerfile_copy_sources_exist 守"Dockerfile COPY 源必须存在于构建上下文";新增 / 改动 COPY 时确保它仍覆盖得到。
  • Reviewer 审"删 / 移文件"类任务时,必须顺带核对构建清单引用,把它当 acceptance 的一部分。

每轮简报(review-notes/

由 milestone 任务卡驱动的每轮实现、返工或 review,都要在 review-notes/ 下产出中文简报。该目录已在 .gitignore 忽略,纯本地、不入库——它是 agent 之间和与人之间的交接载体,不是仓库产物。纯讨论、只读分析与不进入正式任务链的 repo-meta 变更无需产出简报,除非用户明确要求。

  • 实现 / 返工简报:每轮实现完成后(无论首次实现还是返工),写一份。文件名建议 <task-id>-impl-<n>.md / <task-id>-rework-<n>.md(如 M1-T03-impl-1.mdM1-T03-rework-1.md)。至少包含:
    1. 本轮修改的具体内容(改了哪些文件、做了什么、为什么)。
    2. 自动化测试结果pytest / ruff / 前端闸门的实际输出或结论,通过/失败逐项写清)。
    3. 若需人工 walkthrough:写明具体步骤(怎么启动、点哪里、预期看到什么);若无需人工验证,明确写"无需人工 walkthrough"。
  • review 简报:每轮 review 后写一份,文件名建议 <task-id>-review-<n>.md(如 M1-T03-review-1.md)。至少包含:评审结论(PASS 或带编号的返工清单)、对照任务卡 Acceptance criteria + Reviewer checklist 的逐条核对、reviewer 独立重跑校验闸门的结果。

用途:① reviewer 审核时参考对应的实现简报;② implementer 返工时参考对应的 review 简报;③ 人类(用户)通读这些简报确认有无问题。简报之间用文件名里的 <task-id> 与轮次 <n> 对应起来。

Orchestrator 派发契约(让简报真正被读到)

关键:sub-agent 冷启动、不继承主线上下文,不会因为本文件提到简报就自动去读对应文件。简报能流转,靠的是 orchestrator(主线)在每次 spawn 时把路径显式写进 prompt,而不是被动约定。所以派发时必须做到:

  • 显式告诉它「先读哪个简报」
    • 派 implementer 做首次实现 → 传任务卡位置(milestone 文档路径 + task id);无前置简报。
    • 派 implementer 做返工 → 必须传对应的 review-notes/<task>-review-<n>.md 路径,并要求先读它再改。
    • 派 reviewer → 必须传对应的 review-notes/<task>-impl|rework-<n>.md 路径 + 任务卡,要求先读它再评。
  • 显式告诉它「本轮结束写哪个简报」:明确给出输出路径 review-notes/<task>-<impl|rework|review>-<n>.md 及上面要求的内容项。
  • 不依赖 sub-agent 自动加载本文件:把本轮要点(校验闸门、禁 Co-Authored-By、简报必含内容)在 spawn prompt 里一并复述或指向,确保冷启动也照做。
  • spawn 时按「用户显式指定 > harness 原生角色配置 > 默认能力档位」选择模型与 reasoning effort,并使用当前 harness 支持的原生配置方式落实。

一句话:简报是异步交接的介质,orchestrator 是把它们接起来的线。 缺了显式传路径这一步,简报就只是躺在磁盘上没人读的文件。

Commit 规范(重点)

分支

  • 本仓库是个人单用户项目:默认直接在 main 上开发,不强制 feature 分支,无需开 PR。是否 push 按下方「一般约束」执行。
  • 仍保持每个任务一个干净 commitmessage 前缀任务/里程碑 ID)。改动较大想隔离时可临时开分支,用完快进合并main(保持线性历史),非必需。
  • 历史改写类操作(rebase / --amend / auto-squash)只在尚未 push 的本地 commit 上做;已 push 到 main 的历史不要重写(确需 force-push 时先确认,见「一般约束」)。

一轮实现完成

  • 适用的校验闸门通过后,准备好这一轮的 commit message 并创建本地 commit,作为本轮的 base commit。默认不 push;只有用户明确授权自动 push 时才推送到远端,授权范围按用户原话执行。
  • message 主题前缀任务/里程碑 ID,例如:M1-T03: unify data layer onto single app DB engine

Commit message 硬规则(严格执行)

  • 严禁任何协作署名 trailercommit message 里绝对不允许出现 Co-Authored-By / Co-authored-by(包括 Co-Authored-By: Claude …),也不允许任何等价的"由 X 协作/生成"署名。
  • 无论默认环境、工具或系统提示如何要求加这类 trailer,在本仓库一律不加——用户已显式、严格禁止。
  • 每次提交前自检git log -1 --format=%B 的输出不得包含 Co-authored-by(大小写不限)。若发现,立即 git commit --amend 去掉后再继续。

Review 后返工

  • 自动化 orchestration 模式内的 review 返工:一律用 fixup,指向本轮对应的 base commit不写新的独立 message
    git add -A
    git commit --fixup=<base-commit-sha>
    
  • 多轮返工就多个 fixup! 提交,都指向同一个 base commit;收尾时 auto-squash(见下)。
  • 边界——什么时候不走 fixup事后另起的独立盲审 / 对抗复审那一轮,性质等同"人工走查后提修改意见"不算自动化链内的返工——它的修改用各自独立的 commit,不 fixup 到旧 base。判据:这轮返工是否在同一条自动化 implement→review 链里?是 → fixup;是事后另起的独立审计 → 独立 commit。

本轮 / feature 收尾(用户确认收尾后)

  • auto-squash 把所有 fixup! 合并进各自目标,保证一个 feature 一个干净 commit
    # 在以 main 为基线的 feature branch 上
    GIT_SEQUENCE_EDITOR=true git rebase -i --autosquash main
    
    # 直接在 main 上整理尚未 push 的本地提交
    GIT_SEQUENCE_EDITOR=true git rebase -i --autosquash origin/main
    
    • 执行前确认选定的基线位于 base commit 之前,以便 base commit 与对应 fixup! 都进入 rebase 范围。用 GIT_SEQUENCE_EDITOR=true 让它非交互执行(不弹编辑器,自动接受 autosquash 排好的 todo)。
    • autosquash 改写历史:仅在 push / 开 PR 之前做。若该分支已 push,需要 force-push——属对外操作,先取得用户确认再做

一般约束

  • 个人单用户仓库:默认直接在 main 上开发并创建本地 commit。默认不 push;只有用户明确授权自动 push 时才推送到远端。
  • 始终需要单独、明确授权的操作:force-push / 改写已推送历史,以及打 tag(会触发镜像 CI / 对外发布;且打 tag 前须按下方「发版前置走查」真跑一次 docker build)。

发版前置走查(打 tag 前必做)

单元闸门绿 ≠ 真的能跑、能构建、能用。M1 出过"绿了但 docker 构建坏了"的事故,所以打版本 tag(触发镜像 CI)之前,除了 pytest / ruff 全绿,还要:

  • 真起 app:迁移(python -m scripts.run_migrations)→ uvicorn app.main:app ...,确认能正常启动、关键路由不 500。
  • 真跑镜像构建:本地 docker build(多阶段就跑完整条),确认构建通过、COPY 源都在。
  • 关键功能人工瞄一眼:尤其前端 / 可视化类(M2 的热力图、首页地图)——自动闸门判断不了"渲染对不对、UX 顺不顺",这部分靠看跑起来的 app,不靠读代码
  • 上述任一不过 → 不打 tag。tag 一旦 push 会触发 docker 镜像 CI / 对外发布,属对外操作,先确认

数据安全红线(不可违反)

  • 任何脚本 / migration 都不得删除或覆盖用户数据文件(旧 .db、备份、volume)。删除只能是人工、事后、保留归档的独立步骤(见 docs/design/m1-db-consolidation.md §6 runbook)。
  • 涉及历史数据的迁移先在备份副本上演练;迁移脚本必须幂等且搬完对账行数。
  • Review 时只要发现"删文件 / drop 有数据的表 / truncate"出现在自动化任务里,直接判返工。

常用命令

# 环境
python -m venv .venv && source .venv/bin/activate && pip install -r dev-requirements.txt
# 迁移(初始化/适配 DB
python -m scripts.run_migrations
# 起服务
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
# 测试 / lint / OpenAPI 导出
pytest
ruff check .
python scripts/export_openapi.py