# workhub 接入剧本 v1

> 给 AI 终端（Claude Code、Codex）的执行说明。人把这个链接交给你，你读完后按步骤把**当前项目**接入中央工作台 workhub。
> 新项目、已在建的项目都适用；已经接入过的项目，重新执行一遍就会按最新要求更新。
>
> - 中央地址：`https://workhub.qianba.cc`。如果你打开这份剧本用的地址与它不同（比如内网地址），以你实际打开的地址为准，下文的地址都相应替换。
> - 版本：v1，即中央当前的接入要求版本。接入后记在 `.workhub/lock.yaml` 的 `onboarding`；以后接入要求更新了，重新执行一遍就会按新版本更新项目。
> - 在项目仓库的根目录执行。命令以 bash 为例；Windows PowerShell 里用 `curl.exe`（`curl` 是 Invoke-WebRequest 的别名），环境变量写成 `$env:WORKHUB_KEY`。
> - 在 Windows 上用 Python 处理含中文的内容时加 `-X utf8`（如 `python -X utf8 script.py`），否则输出可能是乱码。

## 你要做成什么

接入完成后，项目仓库的标尺分支（main 或 master）上会有：

| 文件 | 作用 |
| --- | --- |
| `.workhub/progress.yaml` | 项目进度。中央从 Git 读取它，这是项目对外公开的全部内容 |
| `.workhub/loop.md` | Loop 视图（loop-engineering 流程图）的本地副本。所有接入项目都按它干活 |
| `.workhub/capabilities/` | 视图里用到的 agent 及其依赖的 skill，已解压 |
| `.workhub/standards.md` | 项目规范的副本：目录、设计文件、脚本、知识库怎么放 |
| `.workhub/layout.yaml` | 选择“映射”方式时才有：规范的各角色在本项目对应哪个目录 |
| `.workhub/lock.yaml` | 接入状态：接入要求版本、项目 ID、分支、视图版本、规范版本、已装能力。重跑剧本时据此判断要做什么 |
| `AGENTS.md` 里的 workhub 规则块 | 以后每次会话都要遵守的规则。Codex 直接读 `AGENTS.md` |
| `CLAUDE.md` 第一行的 `@AGENTS.md` | 让 Claude Code 读到同一份规则 |
| `.gitattributes` 里的一行 | 让 `.workhub/` 下的文件不做换行转换，避免签出后出现无意义的改动 |

中央 workhub 只从 Git 读取进度，不会向仓库提交任何东西。

## 原则

- **可重复执行**：每一步先检查是否已经做过，做过就跳过或更新，不重复登记。**任何一步改动了文件，最后都要执行第 7 步提交推送。**
- **密钥不落盘**：workhub 密钥、Git 凭据、密码箱内容都不写进仓库、提交信息、日志或 shell 历史可见的命令行参数。
- **关键动作先问人**：确认分支、登记项目、首次的进度文件、目录选映射还是改造、写入 AI 规则、安装 agent 和 skill、提交推送之前，先给人看并得到同意。
- **透明**：只用 git、curl 和常规命令；不下载并执行任何脚本。
- **请求体用文件发送**：JSON 里有中文、换行或私钥时，先用脚本（如 Python）生成 UTF-8 的 JSON 文件，再 `curl --data-binary @文件` 发送，发完删除。不要在命令行里手写这类 JSON。临时文件放在系统临时目录，不要放进仓库。

## 从哪一步开始

先做第 0 步，再按第 1 步的结果走：

| 情况 | 依次执行 |
| --- | --- |
| A. 中央没有登记这个仓库 | 2 → 3 → 4 → 5 → 6 → 7 → 8 |
| B. 已登记，但项目里没有 `.workhub/lock.yaml` | 2（分支以中央记录的为准）→ 4 → 5 → 6 → 7 → 8 |
| C. 已有 `.workhub/lock.yaml`（重跑） | 4（只检查）→ 5 → 6 →（前面有任何改动，或 `onboarding` 小于 1，就执行 7）→ 8 |

## 第 0 步：准备

1. 读取环境变量 `WORKHUB_KEY`。没有的话，请人提供本端的 workhub 密钥（`whk_` 开头）。建议人自己在终端设置环境变量后重启会话；如果人直接在对话里给出，只在本次会话的 shell 里设置（bash：`export WORKHUB_KEY=...`；PowerShell：`$env:WORKHUB_KEY="..."`），不写进任何文件。
2. 校验密钥：

```bash
curl -s -w "\n%{http_code}\n" https://workhub.qianba.cc/v1/whoami -H "Authorization: Bearer $WORKHUB_KEY"
# 200 → {"client": "build-server-02", ...}   401 表示密钥无效或已过期，请人确认
```

   AI 终端的每条命令往往在新的 shell 里执行，环境变量不会保留。最好请人在启动 AI 终端之前就设置好 `WORKHUB_KEY`；否则每条命令前都要重新设置。

3. 确认当前目录是 Git 仓库，并有远端：`git remote get-url origin`。
   - 没有远端的新项目：请人先在 Gitee、GitHub、Gogs 等创建仓库（或在 `gh` 已登录时用 `gh repo create`），设为 `origin` 并推送一次。
   - 远端是 `file://` 或本地路径：中央在另一台服务器上，通常访问不到，请人确认（只有中央和项目在同一台机器上的测试环境才能这样登记，此时 `git_credential` 随便填一个令牌即可）。

## 第 1 步：识别项目状态

1. 有 `.workhub/lock.yaml`：读出 `project_id`，`GET https://workhub.qianba.cc/v1/projects/<project_id>`。
   - 200：走情况 C。
   - 404：中央已没有这个项目。告诉人，人同意后删掉 `lock.yaml`，按下面第 2 条重新判断。
2. 没有 `lock.yaml`，按仓库地址查询：

```bash
curl -s -G https://workhub.qianba.cc/v1/projects/lookup --data-urlencode "repo_url=$(git remote get-url origin)" \
  -H "Authorization: Bearer $WORKHUB_KEY"
# 200 → 情况 B，记下 project_id 和 status_ref
# 404（not_registered）→ 情况 A
```

## 第 2 步：确认标尺分支

前期每个项目只用一个分支（main 或 master），中央也读这个分支。

1. 情况 B：用 lookup 返回的 `status_ref`。
2. 情况 A：查远端默认分支 `git ls-remote --symref origin HEAD`（输出 `ref: refs/heads/main	HEAD`），取不到就看有没有 `main` 或 `master`。
3. **问人确认**：“标尺分支用 `<分支>`，进度文件和 workhub 文件都提交到这个分支，可以吗？”之后的工作都在这个分支上做。

## 第 3 步：登记项目

中央需要一份**只读**凭据来拉取仓库。选一种，**告诉人你要怎么做**：

- **A. SSH 部署密钥（推荐）**。在系统临时目录（不在仓库内）生成一对密钥：

```bash
ssh-keygen -t ed25519 -N "" -C "workhub-readonly" -f "<临时目录>/workhub_deploy"
```

  把公钥 `workhub_deploy.pub` 加到仓库的“部署公钥 / Deploy keys”（只读）：
  - GitHub 且 `gh` 已登录：`gh repo deploy-key add "<临时目录>/workhub_deploy.pub" --title workhub-readonly`
  - 其他情况：把公钥内容给人，请人在 Gitee / GitHub / Gogs 的仓库设置里添加，完成后告诉你。

  仓库地址用 SSH 形式（如 `git@gitee.com:team/order.git`）。私钥全文放进请求体 JSON 时用脚本读取文件生成（换行要正确转义），登记完成后删除私钥、公钥和请求体文件。
- **B. 只读令牌**：请人提供一个只读的 HTTPS 访问令牌。仓库地址用 `https://` 形式。`username` 可选，Gitee 等要求填真实用户名时再填。

请求体（用脚本生成 UTF-8 文件 `<临时目录>/register.json`）：

```json
{
  "repo_url": "git@gitee.com:team/order.git",
  "status_ref": "main",
  "name": "订单服务",
  "git_credential": {"auth": "ssh_key", "private_key": "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----\n"}
}
```

`git_credential` 也可以是 `{"auth": "https_token", "token": "<令牌>"}`。`name` 用项目名称（不超过 40 字）。**问人确认后**登记：

```bash
curl -sX POST https://workhub.qianba.cc/v1/projects -H "Authorization: Bearer $WORKHUB_KEY" \
  -H "Content-Type: application/json; charset=utf-8" --data-binary @"<临时目录>/register.json"
# 201 → {"project_id": "prj_...", "status_ref": "main", "webhook_url": "...", ...}
```

- 409 `already_registered`：这个仓库已经登记过，响应里有 `project_id`，按情况 B 继续。
- 502：中央试拉失败（公钥还没加好、地址不对、分支不存在），按 `message` 处理后重试。
- `webhook_url` 只返回这一次：让登记响应直接输出到终端，把地址交给人，建议配到 Git 服务的 push webhook（不配的话中央每 5 分钟拉一次）。**不要写进仓库或任何文件。** 丢了可以由登记它的端调 `POST https://workhub.qianba.cc/v1/projects/<project_id>/webhook` 重新生成（旧地址失效）。

## 第 4 步：进度文件

按附录 A 的格式写 `.workhub/progress.yaml`（先建好 `.workhub/` 目录），`project_id` 用登记得到的。已有这个文件时（情况 C、D），只检查格式和 `project_id`，不改内容。

- **新项目**：`summary` 写项目要做什么、目前刚开始；`items` 列出已经开始的工作和接下来计划做的工作（`planned`）。
- **已在建的项目**：阅读 README、文档、近期提交（`git log --oneline -30`）、TODO 或任务清单，归纳出：
  - `summary`：一句话现状；
  - `phase`：当前阶段；
  - `progress`：整体进度，按已完成的工作占全部工作的比例粗估，拿不准就不写；
  - `items`：近期要做的、进行中、阻塞、刚完成或刚失败的工作（最多 20 条）。还没开始的计划写成 `planned`；阻塞的写清原因；
  - `milestones`：近期达成的里程碑，新的在前；
  - `updated_at`：写文件时的当前时间，带时区。

提交前校验（请求体同样用脚本生成）：

```bash
# <临时目录>/progress.json 的内容：{"project_id": "<project_id>", "content": "<progress.yaml 全文>"}
curl -sX POST https://workhub.qianba.cc/v1/progress/validate -H "Authorization: Bearer $WORKHUB_KEY" \
  -H "Content-Type: application/json; charset=utf-8" --data-binary @"<临时目录>/progress.json"
# → {"ok": true, ...} 或 {"ok": false, "error": "..."}
```

首次写入时**把内容给人确认**（这个文件对外网公开）。

## 第 5 步：项目规范、AI 规则和换行设置

项目规范规定干活的产出放在哪、怎么留存（目录、设计文件、脚本、知识库）；AI 规则块写进 `AGENTS.md`，让以后每次会话都按规范和 Loop 视图干活。两者在中央同属一个版本。

1. 下载规范：

```bash
curl -s -o "<临时目录>/standards.md" -D "<临时目录>/standards.headers" https://workhub.qianba.cc/standards.md
# 版本号在响应头 X-Standards-Version
```

   复制到 `.workhub/standards.md`。

2. **目录适配**（`lock.yaml` 里已经有 `standards_version` 的项目跳过这一条，除非规范的目录要求变了）：
   - **新项目**：按规范的默认位置建目录（`docs/design/`、`docs/knowledge/`、`scripts/` 等，空目录可以先不建，用到时再建），并按范本写一个 `docs/knowledge/index.md`。
   - **已在建的项目**：**问人选映射还是改造**。
     - 映射：读现有目录，按规范“已在建的项目”一节写 `.workhub/layout.yaml`，列出各角色在本项目对应的目录，给人确认。
     - 改造：对照规范末尾的范本，列出迁移计划（哪些文件移到哪里、要新建哪些文件），给人确认后执行。改造单独提交一次，不和接入的其他改动混在一起。

3. 写 AI 规则块（已填好项目 ID、分支、地址）：

```bash
curl -s "https://workhub.qianba.cc/standards/agents.md?project_id=<project_id>"
```

   - `AGENTS.md`：没有就新建，内容就是规则块。已有的话，找 `<!-- workhub:begin` 开头的行到 `<!-- workhub:end -->` 的一段，整段替换；没有这一段就在文件末尾空一行后追加。项目原有的内容不要改动。文件以换行结尾。
   - `CLAUDE.md`：没有就新建；第一行是 `@AGENTS.md`（已有这一行就不动）。Claude Code 通过它读取同一份规则，Codex 直接读 `AGENTS.md`。
   - 规则块有变化时，**把新旧差异给人看并确认**后再写入。

4. `.gitattributes` 里没有 `.workhub/** -text` 这一行就加上（没有这个文件就新建），避免换行转换让 `.workhub/` 下的文件在签出后出现无意义的改动。

内容与原来一致就算没有改动。

## 第 6 步：Loop 视图、agent 和 skill

所有接入项目都必须按 Loop 视图干活。视图和其中用到的 agent、skill 来自 workhub 能力库，由团队的各个端发布。它们会写进项目，以后的会话会按它们执行，所以**安装前要给人看**。

1. 下载视图到临时文件，检查状态码：

```bash
curl -s -o "<临时目录>/loop.md" -D "<临时目录>/loop.headers" -w "%{http_code}" https://workhub.qianba.cc/v1/loop/content
# 200 → 版本号在响应头 X-Loop-Version
# 404 → 中央还没有视图：跳过本步，lock.yaml 的 loop_version 记 0、capabilities 记 []
```

2. 取视图用到的全部 agent 和 skill（已去重）：

```bash
curl -s https://workhub.qianba.cc/v1/loop/resolve -H "Authorization: Bearer $WORKHUB_KEY"
# → {"loop_version": 2, "items": [{"id": "public/backend-dev", "type": "agent", "version": 1, "status": "active",
#     "content_sha256": "...", "content_path": "/v1/capabilities/public/backend-dev/versions/1/content"}, ...]}
# 410 → 视图引用了已下架的版本：停下来告诉人
```

3. 与 `lock.yaml` 对比，找出要新装或更新的项（`id`、`version`、`content_sha256` 有一个不同，或目录不存在）和要删除的项（lock 里有、清单里没有）。没有任何变化、视图版本也没变，就跳到第 5 条。
4. 把要新装或更新的包下载到临时目录，校验并只读查看：
   - 下载：`curl -s -o "<临时目录>/<名称>.zip" https://workhub.qianba.cc<content_path> -H "Authorization: Bearer $WORKHUB_KEY"`；
   - 校验 SHA-256 与 `content_sha256` 一致（`sha256sum`、`shasum -a 256`、`certutil -hashfile <文件> SHA256` 或 Python），不一致就停止并告诉人；
   - 列出包里的文件：`python -X utf8 -m zipfile -l <zip>`，读出 `manifest.yaml`。中央在发布时已经拒绝了路径穿越、绝对路径和符号链接，这里再看一眼文件名即可。

   **给人看并确认**：视图版本和更新说明（`GET https://workhub.qianba.cc/v1/loop` 的 `note`），要安装、更新、删除的能力清单，每个包的 `manifest.yaml` 和文件列表；`status` 为 `deprecated` 的一并告诉人。人同意后：
   - 解压到临时目录（`python -X utf8 -m zipfile -e <zip> <目录>`、PowerShell `Expand-Archive` 或 `unzip`），再替换到 `.workhub/capabilities/<id>/`（如 `.workhub/capabilities/public/backend-dev/`，先删除旧目录）；
   - 删除清单里已经没有的能力目录，以及因此变空的范围目录；
   - 把视图从临时文件复制到 `.workhub/loop.md`。

   人不同意就停在这里，不改动项目里的视图和能力。
5. 上报本项目在用的全部能力（清单里的每一项，包括 skill）。每次执行本步都上报一次：

```bash
# 请求体：{"project_id": "<project_id>", "items": [{"id": "public/backend-dev", "version": 1}, ...]}
curl -sX POST https://workhub.qianba.cc/v1/capabilities/installed -H "Authorization: Bearer $WORKHUB_KEY" \
  -H "Content-Type: application/json; charset=utf-8" --data-binary @"<临时目录>/installed.json"
```

## 第 7 步：写接入状态并提交

1. 写 `.workhub/lock.yaml`（`capabilities` 与 `.workhub/capabilities/` 下的目录一一对应，顺序与 `/v1/loop/resolve` 的清单一致；`sha256` 是 zip 包的 `content_sha256`，不是解压后文件的）：

```yaml
# 由 workhub 接入剧本维护，不要手改
onboarding: 1
project_id: prj_01JB8XQ2M4
status_ref: main
loop_version: 2
standards_version: 1
capabilities:
  - {id: public/backend-dev, version: 1, sha256: "<64 位>"}
```

2. **把改动清单给人确认**（`git status`、`git diff --stat`），确认没有把密钥、私钥、令牌、webhook 地址、密码箱内容加进去，然后在标尺分支上提交并推送：

```bash
git add .workhub AGENTS.md CLAUDE.md .gitattributes
git commit -m "接入 workhub（接入剧本 v1）"   # 首次接入
# 重跑时写明改了什么，如：git commit -m "更新 workhub 接入：Loop 视图 v2，新增 public/doc-writer"
# 你的工具自带的署名行可以照常附加
git push origin <标尺分支>
```

## 第 8 步：验收并汇报

```bash
curl -sX POST "https://workhub.qianba.cc/v1/projects/<project_id>/sync?wait=true" -H "Authorization: Bearer $WORKHUB_KEY"
# updated / file_unchanged / unchanged：成功
# parse_error：看 error，修正进度文件后回到第 7 步
# error：中央拉取失败，GET /v1/projects/<project_id>/sync-log 看原因
```

向人汇报：项目 ID、项目页面 `https://workhub.qianba.cc/projects/<project_id>`、视图版本、装了哪些能力、本次改动了什么，以及需要人处理的事（如配置 webhook；中央无法判断 webhook 是否已配置，只能问人）。

---

## 附录 A：进度文件 `.workhub/progress.yaml`

```yaml
schema: workhub/progress/v1
project_id: prj_01JB8XQ2M4        # 登记时分配，必须一致
name: 订单服务
summary: 退款功能开发中，预计本周完成联调   # 一句话现状
phase: 实现                        # 当前阶段，自由文本
progress: 60                       # 整体进度 0–100，可选
updated_at: 2026-10-20T16:00:00+08:00

items:                             # 计划中、进行中、阻塞或刚结束的工作，最多 20 条
  - id: refund                     # 项目内稳定标识
    title: 订单退款
    state: blocked                 # planned / running / blocked / done / failed / cancelled
    stage: 前后端实现
    progress: 60
    blocked_reason: 等待接口字段确认  # state 为 blocked 时必填
    updated_at: 2026-10-20T15:40:00+08:00

milestones:                        # 近期里程碑，最多 20 条，新的在前
  - date: 2026-10-18
    title: 退款接口设计评审通过
```

| 字段 | 必填 | 规则 |
| --- | --- | --- |
| schema | 是 | 固定为 `workhub/progress/v1` |
| project_id | 是 | 与登记返回的一致 |
| name | 是 | 不超过 40 字 |
| summary | 是 | 不超过 200 字 |
| phase | 否 | 不超过 20 字 |
| progress | 否 | 0–100 的整数 |
| updated_at | 是 | ISO 8601，带时区 |
| items[].id、title、state | 是 | id 为字母数字和 `_.-`；title 不超过 100 字 |
| items[].stage | 否 | 不超过 20 字 |
| items[].blocked_reason | blocked 时必填 | 不超过 200 字 |
| milestones[].date、title | 是 | date 为 YYYY-MM-DD |

文件不超过 64 KB；对外网公开，只写可以公开的概括；标尺分支禁止 force push 和删除。不合规时中央保留上一份合规内容，页面上显示解析错误。

## 附录 B：密码箱

一条凭据 = 标题和说明（公开）+ 内容（任意文本，加密）。默认私有（只有本端能取用），`public` 则所有端都能取用。只有创建它的端能修改和撤销。

```bash
# 存（请求体用脚本生成）：{"title": "订单服务测试环境管理员", "description": "测试后台登录", "project_id": "<project_id>", "visibility": "public", "content": "..."}
curl -sX POST https://workhub.qianba.cc/v1/secrets -H "Authorization: Bearer $WORKHUB_KEY" \
  -H "Content-Type: application/json; charset=utf-8" --data-binary @"<临时目录>/secret.json"
# 列出本端能取用的（只有公开字段），可加 &project_id=... 过滤
curl -s "https://workhub.qianba.cc/v1/secrets?mine=true" -H "Authorization: Bearer $WORKHUB_KEY"
# 一次取回多条内容（最多 50 条）
curl -sX POST https://workhub.qianba.cc/v1/secrets/values -H "Authorization: Bearer $WORKHUB_KEY" -H "Content-Type: application/json" \
  -d '{"ids": ["sec_xxxxxxxxxx", "sec_yyyyyyyyyy"]}'
```

创建者还可以 `PATCH /v1/secrets/{sid}`、`PUT /v1/secrets/{sid}/content`（版本加 1）、`POST /v1/secrets/{sid}/revoke`。`GET /v1/audit-log` 看本端的取用和本端凭据被谁取用。

## 附录 C：能力库

能力是 Skill 或 Agent 的 zip 包，根目录必须有 `manifest.yaml`。标识 `public/<名称>` 为公共能力，`<project_id>/<名称>` 为项目私有能力。

```bash
curl -s https://workhub.qianba.cc/v1/capabilities                                                   # 浏览
curl -s https://workhub.qianba.cc/v1/capabilities/public/backend-dev/versions/latest/resolve -H "Authorization: Bearer $WORKHUB_KEY"
# 推送自己封装的能力
curl -sX POST https://workhub.qianba.cc/v1/capabilities -H "Authorization: Bearer $WORKHUB_KEY" -H "Content-Type: application/json" \
  -d '{"id": "<project_id>/refund-checker", "type": "skill"}'
curl -sX POST https://workhub.qianba.cc/v1/capabilities/<project_id>/refund-checker/versions \
  -H "Authorization: Bearer $WORKHUB_KEY" -H "Content-Type: application/zip" --data-binary @refund-checker.zip
```

```yaml
# manifest.yaml
type: skill            # skill 或 agent
name: 退款校验
description: 校验退款规则
scenarios: [退款开发]   # 适用场景，可选
# agent 才有：依赖的 skill 及版本。公共能力只能依赖公共能力
# skills:
#   - {id: public/api-impl, version: 2}
```

版本发布后不可修改，单包不超过 10 MB。创建者可以废弃（`.../versions/{v}/deprecate`）或下架（`.../versions/{v}/yank`，需填原因）。

## 附录 D：Loop 视图

全局一张，md 文件，开头是 YAML 头：

```markdown
---
schema: workhub/loop/v1
name: 后端需求交付
entry: design
nodes:
  - id: design                        # 小写字母、数字、_、-；不能叫 done
    name: 接口设计
    agent: public/api-designer        # 默认用最新版；写 @版本号 锁定
    rules:
      - 输出接口文档，评审通过才进入下一步
    next:
      - to: implement
  - id: implement
    name: 编码实现
    agent: public/backend-dev
    next:
      - {to: done, when: 测试通过}                     # done 表示结束
      - {to: implement, when: 测试失败, max_loops: 3}   # 回到前面的节点就是 loop
---

## 说明

（自由文本）
```

并行：`to: [a, b]` 表示分叉，a、b 同时开始；各分支最后汇到同一个标了 `join: all` 的节点，所有分支完成后才开始它。分支里不能再分叉、不能回环、不能直接到 `done`。执行到分叉时，按视图正文的说明启动并行子任务。

要改进视图：基于最新版修改后发布，`base_version` 填下载时的版本号；期间有别的端发布了新版本会返回 409（`version_conflict`），先下载最新版覆盖本地再改。请求体 `{"base_version": 3, "content": "<整个 md 文件的内容>", "note": "更新说明"}` 用脚本生成：

```bash
curl -sX POST https://workhub.qianba.cc/v1/loop -H "Authorization: Bearer $WORKHUB_KEY" \
  -H "Content-Type: application/json; charset=utf-8" --data-binary @"<临时目录>/loop.json"
```

## 附录 E：错误

| 状态码 | 含义 |
| --- | --- |
| 400 / 422 | 参数不合法，`message` 里有原因 |
| 401 | 密钥无效、已删除或已过期 |
| 403 | 不是创建者，或取用别的端的私有凭据 |
| 404 | 不存在 |
| 409 | 仓库已登记（`already_registered`，带 `project_id`）；Loop 视图或接入要求的版本冲突（`version_conflict`） |
| 410 | 已撤销或已下架 |
| 429 | 请求过多 |
| 502 | 中央访问 Git 仓库失败 |

完整接口文档：https://workhub.qianba.cc/v1/docs

## 附录 F：维护接入要求

本剧本和首页的《接入一个项目》说明合在一起，是中央的“接入要求”，按版本维护，任何端都能发布新版本：

```bash
# 取当前版本的原文：{"version": 3, "guide": "<首页说明 md>", "playbook": "<本剧本 md>", "note": ..., ...}
curl -s https://workhub.qianba.cc/v1/onboarding
# 基于它修改后发布。请求体（用脚本生成）：{"base_version": 3, "guide": "...", "playbook": "...", "note": "更新说明"}
curl -sX POST https://workhub.qianba.cc/v1/onboarding -H "Authorization: Bearer $WORKHUB_KEY" \
  -H "Content-Type: application/json; charset=utf-8" --data-binary @"<临时目录>/onboarding.json"
```

- `base_version` 与中央当前版本不一致会返回 409（`version_conflict`）：先取最新版本，再修改后发布。
- 两个文件里可以用占位符 `{{ base_url }}`（中央地址）和 `{{ version }}`（版本号），中央返回时替换；要原样显示占位符本身，在前面加反斜杠（`\{{ base_url }}`）。
- 历史版本：`GET https://workhub.qianba.cc/v1/onboarding/versions`。要回退，就用旧版本的内容重新发布一版。

## 附录 G：维护项目规范

项目规范（`standards.md`，含对照范本）和 AI 规则块（`agents_rules.md`）同属一个版本，任何端都能发布新版本，规则与接入要求相同：

```bash
curl -s https://workhub.qianba.cc/v1/standards          # 当前版本原文：{"version": 2, "standards": "...", "agents_rules": "...", ...}
# 请求体：{"base_version": 2, "standards": "...", "agents_rules": "...", "note": "更新说明"}
curl -sX POST https://workhub.qianba.cc/v1/standards -H "Authorization: Bearer $WORKHUB_KEY" \
  -H "Content-Type: application/json; charset=utf-8" --data-binary @"<临时目录>/standards.json"
```

两个文件里可以用占位符 `{{ base_url }}`、`{{ version }}`；规则块另有 `{{ project_id }}`、`{{ branch }}`。
