> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blockx.chaintable.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 贡献流程

> 向 BlockX 提交改动的工作方式：spec 先行、三条设计原则、代码约定速查、commit 与 PR 规范、AI agent 协作约定。

BlockX 是一个设计文档先行（spec-first）的仓库：`docs/specs/` 是行为语义的来源真相，改行为先改 spec，再改代码，两者在同一个 PR 里落地。本页把 `AGENTS.md`、`docs/specs/code-style.md` 和 CI 配置整合成一份贡献者清单；`AGENTS.md` 是这些约定的权威版本。

## 三条设计原则

三条原则同时约束 spec 和代码。展开说明与代码示例见 [设计原则与代码约定](/architecture/design-principles)。

**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。

## 提交一个改动的流程

<Steps>
  <Step title="读对应 spec">
    找到 `docs/specs/` 中定义该子系统的文件（如 `worker.md`、`io-subsystem.md`、`call-execution-subsystem.md`），确认你要改的行为当前的定义，理解它的设计意图。
  </Step>

  <Step title="改行为语义就先改 spec">
    协议、生命周期、超时、错误码语义有变化时，先在同一个 PR 里更新 spec，再写代码。`api/` 下的结构体不能偷偷成为新的协议真相。
  </Step>

  <Step title="写 core 逻辑与表驱动测试">
    在 `internal/<module>/core/` 里改状态机、幂等判定、阶段推进；测试放在同目录 `*_test.go`，覆盖 spec 里写的 happy path 与失败 / 超时路径。
  </Step>

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

  <Step title="本地验证">
    ```bash theme={null}
    go fmt ./...
    go vet ./...
    go test ./...
    uv sync --project python
    PYTHONPATH=python uv run --project python python -m unittest discover -s python/tests
    ```

    `make test` 会依次跑 fmt 检查、`go vet` 与带覆盖率的单测。
  </Step>

  <Step title="跑相关 E2E">
    按改动范围选：`go test -v -timeout 300s ./cmd/worker/...`（worker 进程 E2E）、`./cmd/bundle_worker/...`、`./cmd/syncinvoker/...`、`./e2e/contract/...`、`./e2e/system/...`。这些都要先 `uv sync --project python`。
  </Step>

  <Step title="提 PR">
    目标分支 `dev`。PR 描述写清目的、涉及的 spec / 模块路径、跑过的验证命令和后续工作（见下文）。
  </Step>
</Steps>

## 代码约定速查

以下条目从 `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 直接依赖具体类型（`WorkerCore`、`CoordinatorCore`），不为 core 抽 interface；没有第二个实现或测试替身需求前不提前抽接口。
7. 不引入 `pkg/utils`、`common`、`shared` 这类没有边界的汇总目录。
8. 导出名避免 `Manager`、`Util`、`Data`、`Info` 这类弱语义后缀；状态、事件、命令直接反映领域语义，如 `WorkerCore.HandleTaskPhaseFinished`、`PhaseTransitionResult`（`internal/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.x`、`dev-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 retryable`、`deploy: 支持从 S3 pointer 加载 worker.env`），关键是范围前缀清楚、摘要具体，不要只写 `fix`。

**PR 描述至少包含：**

* 目的：解决什么问题。
* 影响的 spec 与模块路径（如 `docs/specs/worker.md`、`internal/worker/core/`）。
* 跑过的验证命令（贴出实际命令）。
* 后续工作或已知未覆盖的场景。
* 行为有变化时，链接对应 spec 并说明兼容性影响。

<Warning>
  **不要 force-push 已开 PR 或已共享的分支。** PR 打开后只追加 commit 并正常 push。`git push -f`、`--force`、`--force-with-lease` 会重写历史，打断 GitHub 上 reviewer 评论与代码的关联，也会让已 fetch 该分支的同事出问题。唯一例外是从未推送过的纯本地分支，或团队明确约定合并前 squash。
</Warning>

## 测试要求

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

## 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.md`、`docs/specs/python-test-organization.md`、`docs/specs/system-e2e-testing.md` — 测试组织约定。
* `README.md` — 三条设计原则与仓库结构。

站内：

<Columns cols={2}>
  <Card title="设计原则" href="/architecture/design-principles">
    Sans-IO、Scoped Resource Context、Occam's Razor 的展开说明与代码示例。
  </Card>

  <Card title="仓库结构" href="/architecture/repository-layout">
    `cmd/`、`internal/`、`api/`、`python/` 各放什么。
  </Card>

  <Card title="测试" href="/development/testing">
    单测、E2E、perf 测试的组织与命令。
  </Card>

  <Card title="本地开发环境" href="/development/getting-started">
    Go、uv、etcd 与依赖 stub 的准备。
  </Card>
</Columns>
