来源:round-65 主机侧 CI 关键路径实测(operator 明确要求「加速,别把时间浪费在等 PR」)。证据取自两次已完成的真实 run,非估算:
- Go-only PR:run
33674961074(chore/round-64-wave,HEAD=4a7af8c6 前一轮),19:42:55 → 19:51:31 = 8m36s
- Frontend-only PR:run
33676372418(fix/desktop-menu-i18n-forward),19:57:12 → 20:05:07 = 7m55s
仓库 TokenDanceLab/AgentHub = PUBLIC(gh repo view --json isPrivate = false)⇒ Actions 分钟不计费,用并行度换墙钟时间是纯收益,不需要在「省分钟」和「省等待」之间取舍。
关键路径实测拆解(Go-only PR,8m36s)
| 阶段 |
起→止 |
耗时 |
是否在关键路径 |
排队 + Detect changed paths |
19:42:55 → 19:43:41 |
46s |
✅(job 自身只跑 6s,其余是 runner 启动/排队) |
go-hub-test (2) |
19:43:44 → 19:48:35 |
4m51s |
✅ |
go-hub-test (1) |
19:43:44 → 19:46:27 |
2m43s |
❌(早 2m08s 结束,白等) |
go-hub(lint+ratchet+coverage merge+gosec+vet+staticcheck+依赖方向门) |
19:48:38 → 19:51:23 |
2m45s |
✅(串行挂在 test 之后) |
backend-required |
19:51:27 → 19:51:31 |
4s |
✅ |
并行侧(同一时刻起跑、都早于关键路径结束):go-edge-test 两 shard ≤2m52s、go-edge 1m34s、windows-go ≤2m45s、backend-integration 3m06s、backend-e2e ≤1m17s、docker 1m59s、govulncheck ≤29s。即:8m36s 里有 5m30s 完全由「hub 测试最慢 shard + hub 串行尾巴」两段决定,其余 20 多个 job 全程在等它俩。
关键路径实测拆解(Frontend-only PR,7m55s)
| 阶段 |
耗时 |
排队 + changes |
~40s(job 自身 7s) |
Frontend coverage baseline (@agenthub/workbench) 19:57:29 → 20:05:00 |
7m31s ← 唯一长杆 |
frontend-desktop (1/2) |
3m43s / 3m36s |
Native Windows frontend (desktop/web) |
4m10s / 3m40s |
Frontend (web) / Visual QA / stubbed E2E |
≤1m46s |
frontend-required |
3s |
Go 侧全部按 path filter 正确跳过(go-edge/go-hub/windows-go/backend-required 各 2-6s 报绿),说明路径过滤本身是健康的,问题只在两条长杆。
切片 1(收益最大、风险最低):把 go-hub / go-edge 的静态检查从「测试之后」搬到「与测试并行」
现状 checks.yml:270-271 go-hub: needs: [changes, go-hub-test],job 内顺序是:
Lint(golangci --timeout=5m) → verify-hub-lint-ratchet.py → ratchet 自测 → download-artifact(shard 覆盖率) → merge-coverprofiles.py → Coverage check (>=40%) → gosec → go vet → staticcheck。
其中只有 download/merge/coverage 三步真的需要 shard 产物;lint、ratchet、gosec、vet、staticcheck 一步都不需要,却被 needs 强行排在 4m51s 之后。
修法:拆成
go-hub-static(needs: changes,与 go-hub-test 同时起跑)= checkout + setup-go + Lint + ratchet + ratchet 自测 + gosec + vet + staticcheck;
go-hub(保留同名 required check,needs: [changes, go-hub-test, go-hub-static])= 只做 download-artifact + merge + coverage 门 + 两个 fail-closed/skip 兜底 step,并断言 go-hub-static.result == success。
go-edge 同构处理(尾巴 1m34s,含 verify-orchestrator-deps.py + 自测)。
预期:Go PR 关键路径 46s + max(4m51s, ~2m45s) + ~25s ≈ 5m50s,净省 ~2m45s(-32%)。
切片 2:go-hub-test / go-edge-test 的 shard 轮询不均衡(注释自称 balanced,实测不 balanced)
checks.yml:213-215 注释原文:「packages from go list ./... are split round-robin (NR%2) — balanced and disjoint」。实测 hub:shard2 4m51s vs shard1 2m43s,差 2m08s ⇒ disjoint 成立、balanced 被推翻(与 #2246「注释声称被活代码推翻」同族,此处是被 CI 实测推翻)。
修法(择一,需实测后定):
matrix.shard: [1,2,3] + NR % 3,同步改 merge-coverprofiles.py 调用处的 shard 文件名列表(当前硬编码 shard-1.out shard-2.out,见 checks.yml:334-339);
- 或保留 2 shard,但把已知重包显式钉到轻 shard(需要一个可复核的成本表,不接受拍脑袋)。
预期:hub 测试段 4m51s → ~3m20s,叠加切片 1 后 Go PR ≈ 4m30s(-49%)。
切片 3:frontend 长杆 @agenthub/workbench coverage baseline 7m31s
它是 frontend PR 的唯一长杆(第二名 3m43s)。需先定位耗时构成(vitest 全量 + coverage 转换 + 是否重复 build),再决定:按 test file 分 shard / 降低 coverage instrument 范围 / 复用已构建产物。本切片要求先出「耗时构成实测」再改,不接受未测量的优化。
验收(不可伪造)
- 改动前后各取一次同类型真实 run(Go-only 一次、frontend-only 一次),在 issue 里贴
gh run view <id> --json jobs 的关键路径表(job 名 / startedAt / completedAt),证明 Go-only 总时长从 8m36s 降到 ≤6m00s(只做切片 1)或 ≤5m00s(切片 1+2)。
- 7 个 required check 名一字不改:
validate / go-edge / go-hub / windows-go / windows-frontend / backend-required / frontend-required;gh api .../branches/master/protection 前后 diff 为空(不需要也不允许动 branch protection)。
- 三个兜底语义必须保留并各自有证据:path filter 未选中 → 报绿 no-op;
changes job 自身失败 → fail-closed 报红;!cancelled() 让 skip 不阻塞保护。
- 门禁全绿:
python scripts/verify/verify-doc-ssot.py、bash scripts/verify/verify-commit-messages.sh、git diff --check;checks.yml 若被 docs/architecture/github-actions-ci-cd-policy.md 引用为 SSOT,同步更新该文档(verify-doc-ssot.py 会抓)。
负向约束
enforce_admins: true:一旦 needs 图写错导致某个 required check 永不报结果,所有 PR 都会被永久阻塞且管理员无法绕过。因此本切片只能以 PR 形式验证,禁止直接 push master;PR 上必须先看到 7 个 check 全部报出结果再合并。
- 改
.github/workflows/checks.yml 会让 changes 的所有 filter 命中(desktop/web/shell/go/mobile/frontend/design_css 都把该文件列入),⇒ 该 PR 会跑全矩阵(实测两条关键路径并行,预计 ~9-10min,不是叠加)。这是一次性成本,但必须单独成 PR,不要和实现批混在一起,避免把 Go-only 快批拖成全矩阵。
- 不得为了提速删门(coverage 阈值、ratchet、gosec、staticcheck、Windows 合同、path filter fail-closed 一个都不能少);只允许改执行顺序与并行度。
- 不得改
concurrency 组语义(cancel-in-progress: true 是「连推取消旧 run」的既有节流约定)。
依赖 / 排期
来源:round-65 主机侧 CI 关键路径实测(operator 明确要求「加速,别把时间浪费在等 PR」)。证据取自两次已完成的真实 run,非估算:
33674961074(chore/round-64-wave,HEAD=4a7af8c6前一轮),19:42:55 → 19:51:31= 8m36s33676372418(fix/desktop-menu-i18n-forward),19:57:12 → 20:05:07= 7m55s仓库
TokenDanceLab/AgentHub= PUBLIC(gh repo view --json isPrivate=false)⇒ Actions 分钟不计费,用并行度换墙钟时间是纯收益,不需要在「省分钟」和「省等待」之间取舍。关键路径实测拆解(Go-only PR,8m36s)
Detect changed pathsgo-hub-test (2)go-hub-test (1)go-hub(lint+ratchet+coverage merge+gosec+vet+staticcheck+依赖方向门)backend-required并行侧(同一时刻起跑、都早于关键路径结束):
go-edge-test两 shard ≤2m52s、go-edge1m34s、windows-go ≤2m45s、backend-integration 3m06s、backend-e2e ≤1m17s、docker 1m59s、govulncheck ≤29s。即:8m36s 里有 5m30s 完全由「hub 测试最慢 shard + hub 串行尾巴」两段决定,其余 20 多个 job 全程在等它俩。关键路径实测拆解(Frontend-only PR,7m55s)
changesFrontend coverage baseline (@agenthub/workbench)19:57:29 → 20:05:00frontend-desktop (1/2)Native Windows frontend (desktop/web)Frontend (web)/ Visual QA / stubbed E2Efrontend-requiredGo 侧全部按 path filter 正确跳过(
go-edge/go-hub/windows-go/backend-required各 2-6s 报绿),说明路径过滤本身是健康的,问题只在两条长杆。切片 1(收益最大、风险最低):把
go-hub/go-edge的静态检查从「测试之后」搬到「与测试并行」现状
checks.yml:270-271go-hub: needs: [changes, go-hub-test],job 内顺序是:Lint(golangci --timeout=5m)→verify-hub-lint-ratchet.py→ ratchet 自测 →download-artifact(shard 覆盖率) →merge-coverprofiles.py→Coverage check (>=40%)→ gosec →go vet→staticcheck。其中只有 download/merge/coverage 三步真的需要 shard 产物;lint、ratchet、gosec、vet、staticcheck 一步都不需要,却被
needs强行排在 4m51s 之后。修法:拆成
go-hub-static(needs: changes,与go-hub-test同时起跑)= checkout + setup-go + Lint + ratchet + ratchet 自测 + gosec + vet + staticcheck;go-hub(保留同名 required check,needs: [changes, go-hub-test, go-hub-static])= 只做 download-artifact + merge + coverage 门 + 两个 fail-closed/skip 兜底 step,并断言go-hub-static.result == success。go-edge同构处理(尾巴 1m34s,含verify-orchestrator-deps.py+ 自测)。预期:Go PR 关键路径
46s + max(4m51s, ~2m45s) + ~25s ≈ 5m50s,净省 ~2m45s(-32%)。切片 2:
go-hub-test/go-edge-test的 shard 轮询不均衡(注释自称 balanced,实测不 balanced)checks.yml:213-215注释原文:「packages fromgo list ./...are split round-robin (NR%2) — balanced and disjoint」。实测 hub:shard2 4m51s vs shard1 2m43s,差 2m08s ⇒ disjoint 成立、balanced 被推翻(与 #2246「注释声称被活代码推翻」同族,此处是被 CI 实测推翻)。修法(择一,需实测后定):
matrix.shard: [1,2,3]+NR % 3,同步改merge-coverprofiles.py调用处的 shard 文件名列表(当前硬编码shard-1.out shard-2.out,见checks.yml:334-339);预期:hub 测试段 4m51s → ~3m20s,叠加切片 1 后 Go PR ≈ 4m30s(-49%)。
切片 3:frontend 长杆
@agenthub/workbenchcoverage baseline 7m31s它是 frontend PR 的唯一长杆(第二名 3m43s)。需先定位耗时构成(vitest 全量 + coverage 转换 + 是否重复 build),再决定:按 test file 分 shard / 降低 coverage instrument 范围 / 复用已构建产物。本切片要求先出「耗时构成实测」再改,不接受未测量的优化。
验收(不可伪造)
gh run view <id> --json jobs的关键路径表(job 名 / startedAt / completedAt),证明 Go-only 总时长从 8m36s 降到 ≤6m00s(只做切片 1)或 ≤5m00s(切片 1+2)。validate/go-edge/go-hub/windows-go/windows-frontend/backend-required/frontend-required;gh api .../branches/master/protection前后 diff 为空(不需要也不允许动 branch protection)。changesjob 自身失败 → fail-closed 报红;!cancelled()让 skip 不阻塞保护。python scripts/verify/verify-doc-ssot.py、bash scripts/verify/verify-commit-messages.sh、git diff --check;checks.yml 若被docs/architecture/github-actions-ci-cd-policy.md引用为 SSOT,同步更新该文档(verify-doc-ssot.py会抓)。负向约束
enforce_admins: true:一旦needs图写错导致某个 required check 永不报结果,所有 PR 都会被永久阻塞且管理员无法绕过。因此本切片只能以 PR 形式验证,禁止直接 push master;PR 上必须先看到 7 个 check 全部报出结果再合并。.github/workflows/checks.yml会让changes的所有 filter 命中(desktop/web/shell/go/mobile/frontend/design_css 都把该文件列入),⇒ 该 PR 会跑全矩阵(实测两条关键路径并行,预计 ~9-10min,不是叠加)。这是一次性成本,但必须单独成 PR,不要和实现批混在一起,避免把 Go-only 快批拖成全矩阵。concurrency组语义(cancel-in-progress: true是「连推取消旧 run」的既有节流约定)。依赖 / 排期
edge-server/**、hub-server/internal/app/**、.github/workflows/release.yml的 3 行 ldflags)。