Files
sanguo_moziplus_v2/docs/design/14-toolchain-skill-and-deploy.md
T
cfdaily 208c32d9cf
Deploy / ci (push) Waiting to run
Deploy / deploy (push) Blocked by required conditions
Deploy / notify-deploy-failure (push) Blocked by required conditions
auto-sync: 2026-06-08 12:55:14
2026-06-08 12:55:14 +08:00

21 KiB
Raw Blame History

工具链 Skill 设计 + 自动部署 + 端到端验证

状态: v1.1 — 仲达评审 M1/M2 修正 + S1-S4 采纳 作者: 庞统(副军师)🐦 评审: 司马懿(仲达)🗡️ 日期: 2026-06-08 前置: 13-toolchain-and-dev-workflow.md v2.2(事件中枢已闭环) 定位: 工具链从"设计完成"到"正式投入使用"的落地设计


§1. 目标

把工具链从设计态推进到可使用态:

  1. L1 TOOLS.md:每个 Agent 的 TOOLS.md 加入 Gitea API 操作模板,收到 Mail 后开箱即用
  2. L2 Skill 升级:7 个现有 Skill 对齐事件中枢 + CI 实际运行
  3. deploy.sh + deploy.yml:补完自动部署的实际脚本
  4. 端到端验证:用 sanguo/moziplus-v2 实验项目验证全链路

§2. 知识体系四层定位

来源: architecture-v3.0.md §10 BootstrapBuilder

定位 载体 Token 工具链职责
L0 铁律层 AGENTS.md / MEMORY.md ~500 不涉及
L1 角色层 SOUL.md + TOOLS.md ~2000 Gitea API 操作模板Agent 自带,开箱即用)
L2 引擎注入 BootstrapBuilder 注入 Skill 全文 ~1500 7 个 Skill v2(流程规范,Daemon 确定性注入)
L3 被动参考 Skill descriptionAgent 按需加载 按需 复用 L2 Skill,不新建

核心原则

  • L1 给操作手段("怎么做"):curl 命令模板、参数说明
  • L2 给流程规范("什么时候做什么"):审查清单、分支规范、测试策略
  • L3 是 L2 的被动触发版本,不需要单独维护

§3. L1TOOLS.md Gitea 操作模板

3.1 设计原则

  1. 开箱即用:收到 Mail 后直接复制粘贴 curl 命令即可执行,不需要查文档
  2. 按角色定制:不是每个 Agent 都需要全套 API,只给该角色需要的
  3. 统一格式:所有模板使用相同的环境变量约定

3.2 公共变量约定

每个 Agent 的 TOOLS.md 头部加入:

## Gitea 工具链
- **地址**: http://192.168.2.154:3000
- **组织**: sanguo
- **认证**: `Authorization: token $GITEA_TOKEN`(各 Agent 使用自己的 token
- **CI 管理界面**: http://192.168.2.154:3000/sanguo/{repo}/actions

3.3 按角色模板

A. 开发者(张飞/关羽/赵云)— PR 创建 + Merge

### 创建 PR
```bash
curl -X POST "http://192.168.2.154:3000/api/v1/repos/sanguo/{repo}/pulls" \
  -H "Authorization: token $GITEA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "head": "{branch}",
    "base": "main",
    "title": "{标题}"
  }'
# 返回 .number 即 PR 号

查看 PR diff

curl "http://192.168.2.154:3000/api/v1/repos/sanguo/{repo}/pulls/{pr_number}.diff" \
  -H "Authorization: token $GITEA_TOKEN"

Merge PRReview 通过后)

curl -X POST "http://192.168.2.154:3000/api/v1/repos/sanguo/{repo}/pulls/{pr_number}/merge" \
  -H "Authorization: token $GITEA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"Do": "merge", "merge_title_field": "Merge PR #{pr_number}"}'

查看 CI 状态

curl "http://192.168.2.154:3000/api/v1/repos/sanguo/{repo}/commits/{sha}/status" \
  -H "Authorization: token $GITEA_TOKEN"
# .state = "success" | "pending" | "failure" | "error"

#### B. 审查者(司马懿)— Review 操作

```markdown
### 读取 PR diff
```bash
curl "http://192.168.2.154:3000/api/v1/repos/sanguo/{repo}/pulls/{pr_number}.diff" \
  -H "Authorization: token $GITEA_TOKEN"

查看 PR 改动文件列表

curl "http://192.168.2.154:3000/api/v1/repos/sanguo/{repo}/pulls/{pr_number}/files" \
  -H "Authorization: token $GITEA_TOKEN"
# 每个文件有 .filename, .additions, .deletions, .changes

提交 Review

curl -X POST "http://192.168.2.154:3000/api/v1/repos/sanguo/{repo}/pulls/{pr_number}/reviews" \
  -H "Authorization: token $GITEA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "{审查意见}",
    "event": "APPROVE"
  }'
# event 可选: APPROVE | REQUEST_CHANGES | COMMENT

风险级别判定(自动 + 确认)

规则见 code-review Skill。改动者不能降级,只能维持或升级。


#### C. 协调者(庞统)— 全套管理

```markdown
### 创建 Issue + 指派
```bash
curl -X POST "http://192.168.2.154:3000/api/v1/repos/sanguo/{repo}/issues" \
  -H "Authorization: token $GITEA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "{标题}",
    "body": "{描述}",
    "assignees": ["{agent_id}"],
    "labels": [1, 2]
  }'
# labels 需用数字 ID,先 GET /repos/{owner}/{repo}/labels 查询

查询仓库 Labels

curl "http://192.168.2.154:3000/api/v1/repos/sanguo/{repo}/labels" \
  -H "Authorization: token $GITEA_TOKEN"

查询 PR 列表

curl "http://192.168.2.154:3000/api/v1/repos/sanguo/{repo}/pulls?state=open" \
  -H "Authorization: token $GITEA_TOKEN"

创建 Release

curl -X POST "http://192.168.2.154:3000/api/v1/repos/sanguo/{repo}/releases" \
  -H "Authorization: token $GITEA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "tag_name": "v{version}",
    "name": "v{version}",
    "body": "{changelog}",
    "target_commitish": "main"
  }'

关闭 Issue

curl -X PATCH "http://192.168.2.154:3000/api/v1/repos/sanguo/{repo}/issues/{issue_number}" \
  -H "Authorization: token $GITEA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"state": "closed"}'

#### D. 平台运维(姜维)— deploy + runner 管理

在开发者模板基础上追加:

```markdown
### 触发手动部署(需要 push 到 main 或手动 re-run
```bash
# Re-run 最近一次 workflow
curl -X POST "http://192.168.2.154:3000/api/v1/repos/sanguo/{repo}/actions/runs/{run_id}/rerun" \
  -H "Authorization: token $GITEA_TOKEN"

查看 deploy-history

cat ~/.sanguo_projects/{project}/data/deploy-history.jsonl

deploy.sh 规范

见 §5 deploy.sh 设计。所有项目必须遵循统一接口。


### 3.4 实施清单

| Agent | TOOLS.md 新增 | 预计行数 |
|-------|--------------|---------|
| zhangfei-dev | 开发者模板(PR/Merge/CI状态) | ~30 行 |
| guanyu-dev | 开发者模板 | ~30 行 |
| zhaoyun-data | 开发者模板 | ~30 行 |
| simayi-challenger | 审查者模板(diff/review/风险判定) | ~40 行 |
| pangtong-fujunshi | 协调者模板(全套管理) | ~50 行 |
| jiangwei-infra | 开发者 + 平台运维模板 | ~45 行 |

---

## §4. L2Skill 升级到 v2

### 4.1 升级原则

1. **不重写已有内容**,在现有 Skill 基础上追加/修改
2. **对齐事件中枢**:每个 Skill 说明在事件中枢链路中的位置
3. **对齐 CI 实际运行**ci.yml/deploy.yml 已有实际配置,Skill 要反映真实情况
4. **对齐 Gitea v1.23.4 限制**:不支持 failure()、concurrency、permissions 等

### 4.2 升级内容

#### git-workflow(小改动)

| 改动 | 说明 |
|------|------|
| 追加"事件中枢集成"节 | PR 创建 → Gitea Webhook → 中枢通知司马懿 → Review → 中枢通知作者 → Merge → deploy.yml 自动触发 |
| 追加"CI 自动触发"说明 | push 非 main 分支自动触发 ci.ymlpush main 自动触发 deploy.yml |
| 追加"分支感知"强化 | Agent spawn 后必须 `git branch --show-current`,确认分支正确 |

#### code-review(中等改动)

| 改动 | 说明 |
|------|------|
| 追加"事件中枢触发"节 | 收到 Mail → 读 PR diff → 审查 → 提交 Review → Webhook 自动通知作者 |
| 更新风险判定规则 | 对齐 §6.1 实际规则(按文件路径自动判定 + 只升不降) |
| 追加 Gitea Review API 操作 | curl 模板(和 L1 TOOLS.md 一致,这里放流程说明) |
| 追加审查结论格式 | APPROVE / REQUEST_CHANGES 的标准格式 |

#### testing-workflow(小改动)

| 改动 | 说明 |
|------|------|
| 追加"CI 集成"节 | UT 在 CI 自动跑(ci.yml test job)、coverage 在 deploy.yml ci job 跑 |
| 追加"E2E 触发方式" | 通过 e2e.yml 手动触发或 `RUN_INTEGRATION=1 pytest` 本地跑 |
| 追加"测试数据隔离" | CI 使用临时 venv + 临时 SQLite + 临时端口 |
| 追加"广播风暴禁止" | **禁止在 daemon 运行时跑含创建项目/Task/Mail 的测试**,否则会触发 Agent spawn 导致广播风暴。E2E 测试必须在 CI 隔离环境或 daemon 停止后跑 |

#### bugfix-workflow(小改动)

| 改动 | 说明 |
|------|------|
| 追加"事件中枢链路"节 | Bug Issue 创建/指派 → 中枢发 Mail → 修复 → PR → CI → Review → merge |
| 追加"CI 验证"步骤 | 修复后必须等 CI 通过再创建 PR |

#### hotfix-workflow(小改动)

| 改动 | 说明 |
|------|------|
| 追加"CI 自动跑"说明 | hotfix push main → deploy.yml 自动跑 CI + 部署 |
| 追加"失败自动创建 Issue" | deploy.yml notify-on-failure 已实现 |
| 更新 24h 复盘流程 | 复盘结论写到 Issue 评论中 |

#### ci-cd-ops(重写)

| 改动 | 说明 |
|------|------|
| 重写为 v2 | 对齐实际 ci.yml/deploy.yml 结构 |
| 新增 Gitea v1.23.4 限制清单 | 不支持 failure()/concurrency/permissions 等,workaround 方案 |
| 新增覆盖率渐进策略 | P1 只报告 → P2 40% 阈值 → P3 60% 阈值 |
| 新增 deploy.sh 规范 | 统一接口:--version / --source / --target / --health-check / --rollback |
| 新增 CI secret 配置 | CI_TOKEN 作为 repository secret 配置 |

#### release-workflow(中等改动)

| 改动 | 说明 |
|------|------|
| 追加"自动部署触发" | tag 创建 → deploy.yml 自动触发 |
| 追加 deploy-history.jsonl 规范 | 每次 deploy 记录 tag + commit + 时间戳 |
| 更新 schema 变更规范 | 向前兼容 Checklist(加列不加删、默认值、迁移脚本) |

### 4.3 不新建 Skill

现有 7 个 Skill 覆盖所有工具链流程。不需要为事件中枢、CI 操作新建 Skill——这些流程固化在 Mail 模板(§15.5)中,Skill 只提供共通知能。

### 4.4 实施清单

| Skill | 改动级别 | 预计改动行数 |
|-------|---------|------------|
| git-workflow | 小 | +15 行 |
| code-review | 中 | +40 行 |
| testing-workflow | 小 | +25 行(含广播风暴禁止约束) |
| bugfix-workflow | 小 | +15 行 |
| hotfix-workflow | 小 | +15 行 |
| ci-cd-ops | 重写 | ~120 行(原 114 行) |
| release-workflow | 中 | +30 行 |

---

## §5. deploy.sh + deploy.yml 补完

### 5.1 deploy.sh 统一接口

每个项目的 `scripts/deploy.sh` 必须遵循以下接口:

```bash
#!/bin/bash
# scripts/deploy.sh — 项目部署脚本
# 用法:
#   bash scripts/deploy.sh --version              # 显示当前版本
#   bash scripts/deploy.sh --source=DIR --target=DIR --health-check  # 部署
#   bash scripts/deploy.sh --rollback             # 回滚到上一版本

set -euo pipefail

SOURCE_DIR=""
TARGET_DIR=""
HEALTH_CHECK=false
ACTION="deploy"

for arg in "$@"; do
    case $arg in
        --version) ACTION="version" ;;
        --source=*) SOURCE_DIR="${arg#*=}" ;;
        --target=*) TARGET_DIR="${arg#*=}" ;;
        --health-check) HEALTH_CHECK=true ;;
        --rollback) ACTION="rollback" ;;
    esac
done

PROJECT_NAME="{project_name}"
DEPLOY_HISTORY="{target_dir}/data/deploy-history.jsonl"

version() {
    echo "${PROJECT_NAME} deploy version: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
}

deploy() {
    echo "=== Deploying ${PROJECT_NAME} ==="
    echo "Source: ${SOURCE_DIR}"
    echo "Target: ${TARGET_DIR}"

    # 1. 同步文件(排除不需要部署的)
    # ⚠️ --delete 会删除目标中源没有的文件,必须排除 data/(生产数据)
    rsync -av --delete \
        --exclude='.git' \
        --exclude='__pycache__' \
        --exclude='.venv' \
        --exclude='data' \
        --exclude='tests' \
        --exclude='docs' \
        --exclude='.gitea' \
        --exclude='node_modules' \
        "${SOURCE_DIR}/" "${TARGET_DIR}/"

    # 2. 安装依赖
    if [ -f "${TARGET_DIR}/pyproject.toml" ]; then
        cd "${TARGET_DIR}"
        python3 -m venv .venv
        .venv/bin/pip install --quiet -e ".[dev]" 2>/dev/null || \
        .venv/bin/pip install --quiet -e . 2>/dev/null || true
    fi

    # 3. 重启服务
    if command -v pm2 &>/dev/null; then
        pm2 restart ${PROJECT_NAME} 2>/dev/null || true
    fi

    # 4. 健康检查
    if [ "$HEALTH_CHECK" = true ]; then
        sleep 3
        curl -sf http://localhost:8083/api/health && echo " ✓" || {
            echo " ✗ Health check failed!"
            exit 1
        }
    fi

    # 5. 记录版本(rollback 时可通过 DEPLOY_OVERRIDE_COMMIT 覆盖)
    local commit_hash
    if [ -n "${DEPLOY_OVERRIDE_COMMIT:-}" ]; then
        commit_hash="${DEPLOY_OVERRIDE_COMMIT}"
    else
        commit_hash=$(cd "${SOURCE_DIR}" && git rev-parse --short HEAD 2>/dev/null || echo "unknown")
    fi
    local timestamp
    timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)
    echo "{\"timestamp\": \"${timestamp}\", \"commit\": \"${commit_hash}\", \"source\": \"${SOURCE_DIR}\"}" >> "${DEPLOY_HISTORY}"

    # 保留最近 10 条
    tail -10 "${DEPLOY_HISTORY}" > "${DEPLOY_HISTORY}.tmp" && mv "${DEPLOY_HISTORY}.tmp" "${DEPLOY_HISTORY}"

    echo "=== Deploy complete: ${commit_hash} at ${timestamp} ==="
}

rollback() {
    if [ ! -f "${DEPLOY_HISTORY}" ]; then
        echo "No deploy history, cannot rollback"
        exit 1
    fi

    # 读取倒数第二行的 commit(不依赖 SOURCE_DIR 的 git 状态)
    local prev_line
    prev_line=$(tail -2 "${DEPLOY_HISTORY}" | head -1)
    local prev_commit
    local prev_source
    prev_commit=$(echo "${prev_line}" | python3 -c "import sys,json; print(json.load(sys.stdin)['commit'])" 2>/dev/null)
    prev_source=$(echo "${prev_line}" | python3 -c "import sys,json; print(json.load(sys.stdin).get('source',''))" 2>/dev/null)

    if [ -z "${prev_commit}" ] || [ "${prev_commit}" = "unknown" ]; then
        echo "Cannot determine previous version"
        exit 1
    fi

    echo "=== Rolling back to ${prev_commit} ==="
    # checkout 到指定 commit,部署,然后回到 main
    cd "${SOURCE_DIR}"
    local current_branch
    current_branch=$(git branch --show-current)
    git checkout "${prev_commit}"
    # 部署时明确传 commit hash,不依赖 HEAD
    DEPLOY_OVERRIDE_COMMIT="${prev_commit}" deploy
    git checkout "${current_branch:-main}"
}

case $ACTION in
    version) version ;;
    deploy) deploy ;;
    rollback) rollback ;;
esac

5.2 deploy.yml 更新

替换当前 placeholder

  deploy:
    runs-on: macos-arm64
    needs: ci
    steps:
      - uses: actions/checkout@v4

      - name: Deploy
        env:
          CI_TOKEN: ${{ secrets.CI_TOKEN }}
        run: |
          bash scripts/deploy.sh --source="$GITHUB_WORKSPACE" \
            --target="$HOME/.sanguo_projects/sanguo_moziplus_v2" \
            --health-check

      - name: Rollback on failure
        if: always()
        env:
          CI_TOKEN: ${{ secrets.CI_TOKEN }}
        run: |
          STATUS=$(curl -sf \
            -H "Authorization: token $CI_TOKEN" \
            "${{ gitea.api_url }}/repos/${{ gitea.repository }}/commits/${{ gitea.sha }}/status" \
            | python3 -c "import sys,json; print(json.load(sys.stdin).get('state',''))" 2>/dev/null || echo "")
          if [ "$STATUS" != "success" ]; then
            echo "Deploy failed, rolling back..."
            bash scripts/deploy.sh --rollback || echo "Rollback failed, manual intervention needed"
          fi

5.3 实施清单

# 内容 文件 说明
D1 创建 deploy.sh moziplus-v2 scripts/deploy.sh 从模板创建,项目名填 sanguo_moziplus_v2
D2 更新 deploy.yml moziplus-v2 .gitea/workflows/deploy.yml 替换 placeholder
D3 同步到 Gitea push 到 moziplus-v2 触发 CI 验证

§6. 端到端验证:sanguo/moziplus-v2 实验项目

6.1 为什么用实验项目

  • moziplus-v2 主项目已有正式数据(黑板项目、Mail、Task),不适合做破坏性验证
  • sanguo/moziplus-v2 当前是空项目(只有 README.md + .gitea/workflows),可以随意实验

6.2 验证场景

场景 操作 预期结果
S1: CI 触发 push 分支到实验项目 ci.yml 自动跑 lint + test
S2: PR Review 流程 创建 PR → 中枢 Mail → 司马懿 Review → 中枢通知作者 全链路 Mail 通知
S3: CI 失败通知 push 含 lint 错误的代码 ci.yml 失败 → 写 PR 评论 → 中枢发 Mail
S4: 部署流程 merge PR → push main deploy.yml 自动跑 + 部署 + 健康检查
S5: Issue 指派 创建 Issue 并指派 中枢发 Mail 给被指派人
S6: 幂等验证 重复触发同一 delivery_id Webhook 第二次返回 200 duplicate,不创建重复 Mail

6.3 实验项目 CI 配置

从 moziplus-v2 的 ci.yml 精简,实验项目只需要基本验证:

name: CI
on:
  push:
    branches: ['**', '!main']
  pull_request:
    types: [opened, synchronize]

jobs:
  lint:
    runs-on: macos-arm64
    steps:
      - uses: actions/checkout@v4
      - run: echo "lint placeholder"

  test:
    runs-on: macos-arm64
    needs: lint
    steps:
      - uses: actions/checkout@v4
      - run: echo "test placeholder"

§7. 覆盖率渐进策略

7.1 P1 阶段(启用后 2 周)— 只报告不阻断

零改动。当前 deploy.yml 的 ci job 已包含 --cov=src --cov-report=term-missingpush main 时自动跑)。

注意:ci.yml(非 main 分支)没有 coverage 配置——快速门控不需要覆盖率,这是有意为之。

收集基线数据后决定 P2 阈值。

7.2 后续阶段(仅设计,暂不实施)

阶段 时间 策略 触发方式
P2 启用后 1 月 40% 阈值,低于警告不阻断 ci.yml 加 coverage threshold check
P3 启用后 2 月+ 60% 阈值,低于阻断 ci.yml 加 exit 1

§8. 前端展示

8.1 Gitea 自带管理界面

Gitea v1.23.4 自带完整的 CI 管理界面:

功能 URL
CI Runs 列表 http://192.168.2.154:3000/sanguo/{repo}/actions
单次 Run 日志 http://192.168.2.154:3000/sanguo/{repo}/actions/runs/{id}
PR CI Status PR 页面自动显示 CI 状态徽章
Webhook 管理 仓库 Settings → Webhooks

不需要自己做 CI 前端。

8.2 moziplus v2 前端展示

工具链事件的 Mail 通知已在 moziplus v2 前端展示(Mail 列表页)。

如果未来要加,唯一值得做的是:在 moziplus 前端加一个「工具链状态」面板,聚合展示各仓库最近 CI 状态。这是 P4,不阻塞使用。


§9. 实施路线

优先级 内容 耗时 前置
P1 L1 TOOLS.md6 个 Agent 2h
P1 L2 Skill 升级(7 个) 1d
P2 deploy.sh + deploy.yml 2h P1 Skill 升级(ci-cd-ops 定义了 deploy.sh 规范)
P3 sanguo/moziplus-v2 端到端验证 2h P1 + P2
P4 前端工具链状态面板 按需 不阻塞

§10. 前置条件 Checklist

工具链投入使用前必须确认:

# 条件 状态 谁确认
1 act-runner 已注册且 label = macos-arm64 CI 已跑过 24 次 已确认
2 Gitea repository secrets 已配置(CI_TOKEN ⚠️ 需确认 姜维
3 Gitea 组织级 Webhook 已启用(Hook ID=28 已确认 已确认
4 各 Agent 的 GITEA_TOKEN 环境变量 ⚠️ 待分配 庞统协调
5 main 分支保护规则(Review 才能 merge ⚠️ 需确认 姜维
6 禁止在 daemon 运行时跑全量 E2E 已警告司马懿 已确认

第 5 点很关键——如果 main 分支没有保护规则,开发者可以直接 push main 跳过 Review。


§11. 评审记录

v1.0 → v1.1 修订清单(仲达评审)

编号 类型 问题 修订内容
M1 必须修 rsync --delete 会删 data/ §5.1 --exclude 加 data + node_modules
M2 必须修 runs-on 与实际环境不一致 实际 ci.yml/deploy.yml 已用 macos-arm64,CI 已跑通,属仲达误判。文档 §6.3 已明确说明
S1 建议 rollback commit 获取有竞态 §5.1 rollback 改用 DEPLOY_OVERRIDE_COMMIT 显式传递,不依赖 git HEAD
S2 建议 pip install 缺 lock file 采纳但 P3,当前单一部署环境风险低
S3 建议 缺前置条件 checklist 新增 §10 前置条件 Checklist
S4 建议 验证场景补 S6 幂等 §6.2 新增 S6 幂等验证场景
评审 庞统模板缺关闭 Issue curl §3.3 C 节补关闭 Issue 模板
评审 testing-workflow 加广播风暴禁止 §4.2 testing-workflow 补充约束
评审 ci-cd-ops 行数估算措辞 修正:重写后预计 ~120 行(原 114 行)
评审 覆盖率 P1 前提需确认 §7.1 明确指出 deploy.yml 已有 coverageci.yml 有意不加