Skip to main content
BlockX 是一个设计文档先行(spec-first)的仓库:docs/specs/ 是行为语义的来源真相,改行为先改 spec,再改代码,两者在同一个 PR 里落地。本页把 AGENTS.mddocs/specs/code-style.md 和 CI 配置整合成一份贡献者清单;AGENTS.md 是这些约定的权威版本。

三条设计原则

三条原则同时约束 spec 和代码。展开说明与代码示例见 设计原则与代码约定 Sans-IO 是默认规则。新子系统先定义纯内存 core:显式的输入事件、状态迁移和输出命令;RPC、UDS、存储、时钟、日志、指标都放到 adapter。判断标准是 core 能否在没有任何 IO 的情况下用表驱动测试完整回放。Worker、Coordinator、Dispatcher 都是这种形态。 Scoped Resource Context 用于以资源拥有权和回收为中心的子系统,典型是 IO 访问子系统。把 Worker 级共享资源和 task 级局部资源建成显式作用域(如 WorkerIOScope / TaskIOScope),quota、cache、singleflight、deadline 和清理都绑定到作用域生命周期,清理是 scope 退出的结构性属性,而不是”稍后再发一条释放命令”。当把一个模块硬拆成事件/命令流反而让所有权变模糊时,用这条。 Occam’s Razor 对两者都适用。不新增 struct、interface、错误码、队列、状态或服务,除非能说清楚”为什么现有 module / core / adapters / scope 边界装不下它”。如果一个行为既不能表达成一小组可枚举的状态、事件、命令,也不能表达成一小组 scope 的获取与释放规则,说明设计还太模糊,先回去改 spec。

提交一个改动的流程

1

读对应 spec

找到 docs/specs/ 中定义该子系统的文件(如 worker.mdio-subsystem.mdcall-execution-subsystem.md),确认你要改的行为当前的定义,理解它的设计意图。
2

改行为语义就先改 spec

协议、生命周期、超时、错误码语义有变化时,先在同一个 PR 里更新 spec,再写代码。api/ 下的结构体不能偷偷成为新的协议真相。
3

写 core 逻辑与表驱动测试

internal/<module>/core/ 里改状态机、幂等判定、阶段推进;测试放在同目录 *_test.go,覆盖 spec 里写的 happy path 与失败 / 超时路径。
4

接 adapter 与协议映射

internal/<module>/adapters/ 里把 core 的命令翻译成副作用,把外部回调重新包装成 core 事件。adapter 测试只覆盖协议映射、错误翻译和编排,不重复 core 的状态组合。
5

本地验证

make test 会依次跑 fmt 检查、go vet 与带覆盖率的单测。
6

跑相关 E2E

按改动范围选:go test -v -timeout 300s ./cmd/worker/...(worker 进程 E2E)、./cmd/bundle_worker/..../cmd/syncinvoker/..../e2e/contract/..../e2e/system/...。这些都要先 uv sync --project python
7

提 PR

目标分支 dev。PR 描述写清目的、涉及的 spec / 模块路径、跑过的验证命令和后续工作(见下文)。

代码约定速查

以下条目从 docs/specs/code-style.md 提炼,是 review 中最常被指出的问题。完整约束以该文件为准。
  1. cmd/ 只做装配与启动。不放领域判断、准入策略或协议分支;worker / coordinator 入口只是 app.Profile 清单,公共装配在 internal/*/app/
  2. core/ 不直接依赖 JSON-RPC、gRPC、etcd client、数据库 client、日志与指标实现。
  3. core/ 不自己取当前时间、生成随机值或启动 goroutine。时间、随机数由 adapter 作为输入传进来;并发属于 adapter。
  4. core/ 里尽量不用 context.Context。取消、超时、TTL 到期、deadline 推进建模成显式事件或输入字段。
  5. core/ 的输出是可枚举命令、决策结果或状态快照;adapter 负责执行,模式是”输入事件 → core 决策 → adapter 执行命令”,不让 core 持有反向 callback。
  6. interface 定义在真正的消费侧。adapter 调 core 直接依赖具体类型(WorkerCoreCoordinatorCore),不为 core 抽 interface;没有第二个实现或测试替身需求前不提前抽接口。
  7. 不引入 pkg/utilscommonshared 这类没有边界的汇总目录。
  8. 导出名避免 ManagerUtilDataInfo 这类弱语义后缀;状态、事件、命令直接反映领域语义,如 WorkerCore.HandleTaskPhaseFinishedPhaseTransitionResultinternal/worker/core/)。
  9. 需要多个布尔字段组合才能理解的对象状态,改成显式状态枚举;不滥用 map[string]any 与零散字符串字面量。
  10. 错误不靠字符串判断。稳定语义依赖错误码、枚举或命名结果类型;预期业务结果在 core 里建成显式结果,不滥用 error
  11. 先区分”准入 / 协议错误”与”已进入执行后的终态结果”,不要混成一套返回。
  12. api/ 只放跨模块共享的协议结构、消息包络和字段约定,不放 handler、存储模型或状态机;改协议优先加可选字段或新枚举值,不静默改既有字段含义。
  13. 时间语义集中建模。slot TTL、task deadline、结果保留窗口收敛在 Worker 模块及其 timer adapter,不在多个 adapter 里各自复制”超时后怎么判定”。
  14. scope-oriented 模块里,谁拥有资源谁就是真相来源;不让调度层、hook 层和 backend adapter 各自维护影子计数器。
  15. goroutine 的创建、退出和回收路径必须清楚,不能靠进程退出兜底。

Commit 与 PR 规范

分支模型。 日常开发合入 dev;CI(.github/workflows/test.yml)只在 PR 目标为 dev 或 push 到 dev 时运行。GitHub 上的默认分支是 main,但它明显落后于 dev(本地检查时 main 停在 2026-06 的 #335,dev 在 2026-08 的 #495),镜像 tag(v1.0.xdev-v1.0.x-n)都从 dev 打出。所以:从 dev 拉分支,PR 回 dev.github/ 下没有 PR / issue 模板,也没有 CODEOWNERS Commit 信息。 祈使语气、一个 commit 只做一件事(spec 更新、core 逻辑、adapter 接线、测试分开提)。格式用 模块: 摘要,例如 worker: tighten slot idempotency;仓库近期历史里中英文都有(io: keep idempotent BlockDB writes retryabledeploy: 支持从 S3 pointer 加载 worker.env),关键是范围前缀清楚、摘要具体,不要只写 fix PR 描述至少包含:
  • 目的:解决什么问题。
  • 影响的 spec 与模块路径(如 docs/specs/worker.mdinternal/worker/core/)。
  • 跑过的验证命令(贴出实际命令)。
  • 后续工作或已知未覆盖的场景。
  • 行为有变化时,链接对应 spec 并说明兼容性影响。
不要 force-push 已开 PR 或已共享的分支。 PR 打开后只追加 commit 并正常 push。git push -f--force--force-with-lease 会重写历史,打断 GitHub 上 reviewer 评论与代码的关联,也会让已 fetch 该分支的同事出问题。唯一例外是从未推送过的纯本地分支,或团队明确约定合并前 squash。

测试要求

Go 测试与实现放在同一目录(*_test.go),状态机、协议映射、core / adapter 边界优先用表驱动测试;新行为要同时覆盖 spec 里的成功路径和失败 / 超时路径;改协议或生命周期语义时,测试和 spec 在同一个变更里更新。CI 会跑 Go 单测、Python 单测、分 8 片的 Worker Process E2E、bundle worker / contract / system E2E 和 -short 的 Perf E2E。测试的目录组织、命令与 E2E 说明见 测试

AI agent 协作

  • AGENTS.md 是仓库级协作说明的权威文件:目录约定、构建 / 测试命令、编码风格、设计原则、测试与 PR 规范。人和 AI agent 都以它为准。
  • CLAUDE.md 只有几行,指向 AGENTS.md,避免两份文件漂移。
  • .claude/.gitignore 里,是本地工作目录,不入库。当前本地里放的是性能排查产物(pprof-blockx-worker-*profile-*pyspy-* 里的 pprof / py-spy 采样文件、optimization-priority.md 之类的分析笔记)和一个 worktrees/ 目录(供 agent 开 git worktree 用)。里面没有共享的 agent 配置、命令或 hook;不要把它当作团队约定的来源。
  • 让 agent 改代码时,把 AGENTS.md 和对应 spec 一起给它读;agent 产出的 PR 同样要满足上面的 commit / PR 规范和验证要求。

相关文档

blockx 仓库:
  • AGENTS.md — 仓库协作与开发规范的权威版本。
  • docs/specs/code-style.md — 模块边界、Sans-IO / Scoped Resource Context 的实现约束、错误与时间语义。
  • docs/specs/architecture.md — 系统架构与模块划分。
  • docs/specs/worker-test-organization.mddocs/specs/python-test-organization.mddocs/specs/system-e2e-testing.md — 测试组织约定。
  • README.md — 三条设计原则与仓库结构。
站内:

设计原则

Sans-IO、Scoped Resource Context、Occam’s Razor 的展开说明与代码示例。

仓库结构

cmd/internal/api/python/ 各放什么。

测试

单测、E2E、perf 测试的组织与命令。

本地开发环境

Go、uv、etcd 与依赖 stub 的准备。