Gitea 开发规则手册:CLAUDE.md 与 AGENTS.md 中的 Agent 协作开发规范全解
Gitea 仓库根目录下的 CLAUDE.md 通过单行 @AGENTS.md 引用,把一份 20 条开发规则集中交给 AI Agent 与人类贡献者共同遵守。本篇以 AGENTS.md 规则为骨架逐条展开,结合 Makefile、tools/test-e2e.sh 等真实实现,给出可直接复制执行的验证、构建、Lint 与测试命令,帮助你在 Gitea 代码库中以符合上游惯例的方式完成从提交信息到测试落地的全流程工作。
一、CLAUDE.md:一行引用背后的“单一事实来源”
CLAUDE.md 的全部内容只有一行:
@AGENTS.md
这是 Agent 工具的 @ 文件引用语法——它不定义任何规则,而是把规则实体指向 AGENTS.md。从源码结构看,这种组织方式意味着:无论使用哪类 Agent 工具链,真正的行为约束都收敛在同一份文件中维护,避免了“每个 Agent 一份规则”的碎片化。AGENTS.md 共 20 条规则,可归为六类:验证优先的工作方式、PR 与提交规范、AI 归属声明、代码风格、构建与 Lint 命令链、测试哲学。下文逐一拆解,并给出对应的仓库内实现证据。
二、验证优先:make help 与 docs 目录
前两条规则确立了基本的信息获取纪律:
- Never assume, verify before claiming(绝不假设,断言前先验证)
- List development targets with
make help(用make help列出开发目标)- Read relevant developer documentation in the
docsfolder(阅读docs目录下相关开发者文档)
make help 的实际实现位于 Makefile#L183-L187:它用 awk 解析 Makefile 中形如 target: ## 说明 的注释生成目标清单,并额外手工补印了三个带 # 参数的动态目标——test-e2e、test-backend[#TestSpecificName]、test-integration[#TestSpecificName]。这说明 Gitea 的 Makefile 把“帮助文档即目标清单”作为约定,任何新增开发目标都应带上 ## 说明 注释以便被 make help 发现。
规则指向的开发者文档在当前仓库中确实齐备:
- docs/development.md:开发入门
- docs/testing.md:测试指南
- docs/guidelines-backend.md:后端规范
- docs/guidelines-frontend.md:前端规范
- docs/guidelines-refactoring.md:重构规范
- docs/release-management.md:发布管理
三、PR 描述与 Issue 引用风格
PR descriptions: minimal, only what and why, no task or file listings. Include screenshots for UI changes, before and after when modifying existing UI. Aim for less than 1000 characters
Reference issues and PRs by full URL, not by number
两条规则都服务于可检索性:PR 描述只写“做了什么、为什么”,不罗列任务清单或文件列表,控制在 1000 字符以内;UI 变更必须附截图,修改既有 UI 时须给出前后对比。引用 issue 或 PR 时要求完整 URL 而非裸编号——这样跨平台(含非 GitHub 托管环境)阅读时链接依然有效,也方便自动化工具解析。
四、提交规范与 AI 归属声明
这是与 Agent 协作最相关的一组规则:
Use Conventional Commits for commit messages and PR titles, plus Gitea's
enhancetype for user-facing enhancements
Add an
Assisted-by: AGENT_NAME:MODEL_VERSIONtrailer to commit messages, neverCo-Authored-ByorSigned-off-by
Attribute agent authorship on one trailing line in issue and PR comments, never as a PR description section
Never rewrite git history unless asked, update PRs with new commits and normal push
从当前仓库最近的提交历史可以直接看到 Conventional Commits 的落地形态,且确实大量使用 Gitea 自定义的 enhance 类型(区别于通用规范中的 feat,用于“面向用户的增强”):
enhance(actions): make workflow dispatch choice dropdown support search (#39154)
fix(web): populate the reason for "cannot commit to branch" in web editor commit form (#39155)
refactor(automerge): fix error handling, populate recent automerge tasks on restart (#39001)
chore(frontend): avoid loading CSS twice in vite dev mode (#39160)
fix(packages): preserve SemVer prerelease identifiers in Swift Registry (#39156)
feat: add deploy tokens (#37306)
ci(snap): pack snaps without an LXD container (#39152)
可见 type(scope): subject 是硬约束,enhance、fix、refactor、chore、feat、ci 等类型均真实出现。
关于 AI 归属:Agent 辅助的提交必须追加 trailer Assisted-by: AGENT_NAME:MODEL_VERSION(例如 Assisted-by: Claude:claude-sonnet 形式),且明确禁止使用 Co-Authored-By 或 Signed-off-by 表达 Agent 参与——这让人类署名与机器署名在 git log --format 输出中可被严格区分。在 issue/PR 评论中,Agent 身份只允许以“末尾一行”出现,不能占用 PR 描述正文的段落。最后一条强调:除非被要求,绝不重写 git 历史,PR 通过追加新 commit 加普通 push 更新,从而保住评审线程的连续性。
五、代码风格:注释、TypeScript、Go 模板与 i18n
五条风格规则覆盖了注释密度、前端语法、Go 语言特性、CSS 工具类与文件头:
1. 注释要“近乎没有”。
Comments: write almost none, short and preferably same-line, explaining why for a future reader. Never narrate code, the change or the prompt. Preserve existing ones that still apply. If you need to write a paragraph-long comment, rethink your implementation, it is likely too complicated
注释只解释“为什么”,禁止复述代码在做什么、改动是什么、提示词是什么;既有的仍然成立的注释要保留。需要写段落级注释时,应回退反思实现本身是否过于复杂——这是把“可理解性预算”压回实现逻辑而非文档侧。
2. 新增 .go 文件的版权头要带当前年份。
3. i18n 只改英文源文件。
In
options/locale, only editlocale_en-US.json, other locales are synced automatically
当前仓库 options/locale/ 下有 29 个语言文件,规则要求贡献者(含 Agent)只编辑 locale_en-US.json,其余语言由同步机制自动处理,避免多语言文件手改造成漂移。
4. TypeScript 用 ! 表达“必然存在”。
In TS, use
!instead of?./??when a value always exists
当某个值在语义上必然存在时,应使用非空断言 ! 而不是防御性地写 ?./??——后者会掩盖真实的不变量,让类型系统失去对“此处不可能为空”这一事实的表达。
5. Go 优先使用现代语言特性,CSS 优先 tw-* 工具类。
In Go, prefer to use modern language features wherever possible
Prefer
tw-*utilities over inlinestyleandflex-*helpers over per-childtw-ml-*/tw-mr-*margins, falling back totw-*where specificity requires!important
前端侧禁止内联 style,优先 Tailwind 的 tw-* 工具类;布局间距优先用 flex-* 的 gap 系列而非对子元素逐一设置 tw-ml-*/tw-mr-* 边距;确需提升优先级时才回落到 !important 写法。
六、构建与 Lint 命令链:fmt、tidy、generate-swagger 及四类 lint
Run
make fmtafter.goedits,make tidyaftergo.modedits,make generate-swaggerafter API changes, and lint what changed withmake lint-go,lint-js,lint-cssorlint-templates
这条“编辑类型 → 必跑命令”的映射是规则的心脏。逐条对照 Makefile 的实现可以看到每条命令的真实工作量:
make fmt(Makefile#L198-L207)做两件事:用 golangci-lint fmt 格式化 Go 代码;再用 sed 对 templates/ 下全部 .tmpl 文件做模板空白规整——去除 {{ 后与 }} 前的多余空白、( 后的多余空白(保留纯缩进行)。所以 Gitea 中“改 Go 文件”触发的格式化同时覆盖 Go 与模板两种语法。对应的 CI 门禁是 Makefile#L209-L216 的 fmt-check:重跑 fmt 后对 git diff 判空,有差异即失败。
make tidy(Makefile#L419-L428)不只是 go mod tidy:它先从 go.mod 解析出最低 Go 版本与 toolchain,执行 go mod tidy -compat=$(MIN_GO_VERSION);若 tidy 丢掉了 toolchain 指令,则用 go mod edit -toolchain 恢复(注释标明这是针对 Go 上游问题的 workaround);最后重新生成 go-licenses 文件。配套的 tidy-check(Makefile#L434-L441)对 go.mod、go.sum 与许可证清单做 diff 校验。
make generate-swagger(Makefile#L227-L242)用 go-swagger generate spec 从代码注释重新生成 Swagger 与 OpenAPI 3 规范,swagger-check 同样通过 diff 判定是否遗漏了重新生成——这正是规则要求“API 变更后必须跑”的原因:规范文件是生成物,必须与路由注释保持零漂移。
四个 lint 目标分别对应四类资产:
| 目标 | 实现 | 覆盖范围 |
|---|---|---|
lint-go |
Makefile#L331-L333 调用 tools/lint-go-all.go | Go 源码(lint-go-fix 加 --fix) |
lint-js |
Makefile#L293-L296 pnpm exec eslint + pnpm exec vue-tsc |
JS/TS 与 Vue 类型检查 |
lint-css |
Makefile#L303-L305 pnpm exec stylelint --max-warnings=0 |
CSS(零警告容忍) |
lint-templates |
Makefile#L353-L356 tools/lint-templates-svg.ts + djlint |
模板中的 SVG 与模板语法 |
规则强调“lint what changed”——只对本轮修改涉及的类别跑对应目标,避免全量 lint 的等待成本。所有目标还聚合在 checks-backend/checks-frontend 之下(Makefile#L266-L273):checks-backend 串起 tidy-check、swagger-check、openapi3-check、fmt-check、swagger-validate、security-check,构成与规则一一对应的自动门禁。
七、“修复根因”原则:禁止禁用 linter 或弱化测试
Fix the cause rather than disabling a linter or weakening a test. Where unavoidable, use the narrowest scope with a trailing comment giving the reason
规则把“消掉报错”与“消除问题”区分开:默认路径是修改代码本身;只有不可避免时,才允许在最窄作用域内豁免,且必须在行尾注释写明原因。这条约束直接约束了 Agent 面对 lint 报错时的行为模式——不允许生成 //nolint 或降低断言这类“以妥协换绿”的补丁。
八、单测怎么跑:Go、TS、e2e 三类命令
Run single tests with
go test -run '^TestName$' ./modulepath/for Go,pnpm exec vitest <path-filter>for TS andGITEA_TEST_E2E_FLAGS='<filepath>' make test-e2efor e2e
三类命令在仓库中各有精确落点:
Go 单测。 规则给出的 go test -run '^TestName$' ./modulepath/ 是标准裸命令;Makefile 额外提供了带参数的目标 test-backend#%(Makefile#L403-L406),把 . 替换为 / 后作为 -run 模式执行,且默认携带 -tags(如 sqlite 等 CGO 标签)。
TS 前端测试。 pnpm exec vitest <path-filter> 与 Makefile#L387-L389 的 test-frontend 目标一致(pnpm exec vitest),传路径过滤参数即可缩到单个文件。
e2e 测试。 GITEA_TEST_E2E_FLAGS='<filepath>' make test-e2e 的链路是:Makefile#L485-L487 的 test-e2e 目标依赖 playwright frontend backend 三个前置(先装浏览器、构建前端、构建后端),然后执行 ./tools/test-e2e.sh $(GITEA_TEST_E2E_FLAGS);tools/test-e2e.sh 的最后一行 pnpm exec playwright test "$@" 把该参数原样透传给 Playwright 作为文件过滤(tools/test-e2e.sh#L194)。脚本还揭示了 e2e 环境的完整搭建方式:用 mktemp -d 建隔离工作目录、随机空闲端口、sqlite 数据库、INSTALL_LOCK = true 跳过安装向导、关闭验证码,最后通过 gitea admin user create 命令创建带 --admin 的测试管理员(tools/test-e2e.sh#L167-L172)。集成测试则有对等的 test-integration#% 目标(Makefile#L461-L463),按 GITEA_TEST_DATABASE(sqlite/mysql/pgsql/mssql)编译运行。
九、测试哲学:最少、最快、确定性
Write the fewest, fastest tests covering the behavior, extending an existing one where possible. Prefer unit tests where logic is testable in isolation
Aim for sub-2s per integration test and sub-4s per e2e test. Wait on a deterministic condition rather than
sleep, and prefer semantic locators in e2e tests
四条要求可归纳为:数量上能扩展既有测试就不新建,能单元隔离就不写集成/端到端;速度上集成测试单条 2 秒内、e2e 单条 4 秒内;确定性上等待条件应基于可观察状态(如服务可访问、元素出现)而非固定 sleep;定位上 e2e 优先语义定位器(role、label 等)而非脆弱的选择器。
仓库实现对这套哲学有直接呼应。tools/test-e2e.sh#L174-L182 中的超时系数逻辑:本地机器取系数 1,CI 环境自动放大为 4 倍——即 e2e 用例的“4 秒预算”是按本机标准计时,CI 慢机器才乘以冗余。同一脚本中的服务就绪等待(tools/test-e2e.sh#L140-L157)也是“轮询 curl 直到可达 + 进程存活检查”的确定性等待模式,与 sleep 式等待形成对照。测试代码应遵循同样的原则。
十、速查表:规则到命令的映射
| 场景 | 规则要求 | 可执行命令 |
|---|---|---|
| 查看开发目标 | 以 make help 为准 |
make help |
| 修改 Go 代码后 | 格式化 | make fmt |
修改 go.mod 后 |
依赖整理 | make tidy |
| 修改 API 后 | 重新生成规范 | make generate-swagger |
| Lint 变更文件 | 按资产类别选择 | make lint-go / lint-js / lint-css / lint-templates |
| 跑单个 Go 测试 | 精确 -run |
go test -run '^TestName$' ./modulepath/ 或 make test-backend#TestName |
| 跑单个前端测试 | 路径过滤 | pnpm exec vitest <path-filter> |
| 跑单条 e2e | 文件过滤 | GITEA_TEST_E2E_FLAGS='<filepath>' make test-e2e |
| 提交信息 | Conventional Commits + enhance 类型 |
enhance(scope): subject + Assisted-by: AGENT:MODEL trailer |
| 更新 PR | 追加 commit,不重写历史 | 新 commit + 普通 push |
CLAUDE.md 到 AGENTS.md 的这套规则,本质是把 Gitea 上游评审中最常驳回的问题——规范漂移、lint 妥协、测试拖慢、Agent 归属不清——前置为可执行的约束清单,并让每一条都能落到 Makefile 目标或 tools/ 脚本中验证。遵循它,Agent 与人类贡献者在同一代码库中产出的提交在格式、风格与质量门槛上保持同一水准。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00