Ver Fonte

docs(tdd): hub-orchestrator 替代迁移方案(分两步收编)— 能力矩阵盘点 + T1-T8 迁移任务 + 验收/回退

Agnes Agent há 4 dias atrás
pai
commit
a17220c6c0
1 ficheiros alterados com 159 adições e 0 exclusões
  1. 159 0
      docs/tdd/orchestration/hub-migration-2026-11-08.md

+ 159 - 0
docs/tdd/orchestration/hub-migration-2026-11-08.md

@@ -0,0 +1,159 @@
+# hub-orchestrator 替代迁移方案(分两步收编)
+
+**决策时间**:2026-11-08 15:xx(用户拍板「分两步收编」)
+**触发点**:用户「要完全使用 tdd-orchestrator 的机制替代 tools/xaf_agent_hub 机制」
+**背景**:CFC TDD 闭环当前双系统并存——`tdd-orchestrator` skill(编排层)+ `tools/xaf_agent_hub`(调度执行层)。本次迁移目标让 skill 成为唯一机制,hub 退役归档。
+
+---
+
+## 一、现状盘点(能力矩阵实证,grep / AST 验证)
+
+| 维度 | tdd-orchestrator(skill 侧) | xaf_agent_hub(当前执行层) |
+|---|---|---|
+| **测试发现/编目** | ✅ discover_tests.py / test_inventory.py | ❌ 无 |
+| **状态机 RED→YELLOW→GREEN** | ✅ --mark-red/yellow/green + state.json | ❌ 无 |
+| **账本** | ✅ `.workbuddy/tasks.json`(scheduler.py 原子写) | ⚠️ tasks_backlog.json(独立池,双源漂移风险) |
+| **部署链** | ✅ deploy_sessions + dispatch_deploy + finalize_deploy(production 确认门) | ⚠️ deploy_wait.py / wait_branch.py |
+| **发现问题** | ✅ --changes / --inspect / --clean-check | ❌ 无 |
+| **会话枚举/复用** | ✅ enumerate_sessions / find_idle_session / _alloc_with_quota_check | ✅ scan_instance / matcher.py / slot_lock |
+| **角色级模型链+冷却** | ✅ resolve_model_chain + agent_model_chains + model_cooldowns.json | ⚠️ 重合但语义不同(见下) |
+| **派发投递** | ⚠️ auto_dispatch → .workbuddy/dispatch.py → hub.py dispatch(**依赖 hub**) | ✅ scheduler.py 真实选路/投递 |
+| **实例级模型链候选 + 真实 provider 命中** | ❌ 缺(opencode_client 无 available_models;tdd_orchestrate.py 无 instance_model_chain) | ✅ 实例级候选列表 + available_models() 按节点 provider 命中 |
+| **多实例负载选路** | ❌ 固定 client 映射,无 `_pick_instance` | ✅ 按并发/运行中会话选最低负载可调度实例 |
+| **429 黑窗顺延(全家族至次日 08:00)** | ⚠️ model_rules 冷却→model_cooldowns.json,**非全家族黑窗逻辑** | ✅ mark_opencode_blackout + opencode_blackout_until + fix_free_limit |
+| **并发槽位锁** | ❌ 只有配额检查,无锁 | ✅ slot_lock.py |
+| **智能会话语义匹配** | ❌ 仅状态+时间判定 | ✅ matcher.py / _smart_candidates |
+| **运维守护(watchdog/loop_resume/check_agents/fill_slots)** | ❌ 无 | ✅ watchdog.py / loop_resume.py / fill_slots.py + 计划任务 XAFHubCheck_01..06 |
+| **会话恢复** | ❌ 无 | ✅ resume_interrupted.py / confirm_session.py |
+| **报告导出** | ❌ 无 | ✅ report.py HTML/CSV |
+
+**结论**:skill 已自给「编排层」(状态机/账本/部署/汇报/发现);但「真实派发链路末端仍依赖 hub」「调度基础设施(实例级/并发/黑窗/巡检)全部在 hub」。**不可一步删除 hub**。
+
+---
+
+## 二、迁移目标(分两步)
+
+### 第一步:能力移植(把 hub 独有调度能力移入 skill 体系)
+
+**落地位置**:
+- skill 本体:`C:\Users\liaoxg.DESKTOP-DGLN79N\.workbuddy\skills\tdd-orchestrator\scripts\`
+- 配套:`opencode-orchestrator/scripts/opencode_client.py`(skill 依赖)
+- 项目配置:`.tdd/config.json` / `machine.json`(机器级)
+- 分发层:`.workbuddy/dispatch.py`(适配器改造)
+
+**具体任务**:
+
+#### T1. 补 skill 实例级模型探测能力
+- `opencode_client.OpenCodeClient` 新增 `available_models()`(GET /model),返回该实例真实 provider 集合
+- `machine.json` 每个 client 增加 `model_chain`(实例级候选列表,按 hub config.json 的 `instances[].model_chain` 移植)
+- `tdd_orchestrate.py` 新增 `resolve_client_model_chain(cfg, client_name)`:候选按序取该实例真实可用第一个;429 跳过进冷却(沿用 model_rules);整链失败回落兜底。对齐 hub `scheduler.resolve_instance_model` 行为。
+
+#### T2. 黑窗语义统一到 skill cooldowns
+- 删除 `.workbuddy/dispatch.py` 对 `.model_state.json` 的依赖(hub 专有)
+- `resolve_model_chain` 支持"opencode 全家族 429 黑窗至次日 08:00"规则(已有 `model_rules[error_contains=="429"]→cooldown_until_next:"08:00"` 机制,需加"整族"语义,即一个 opencode 家族 hit 429 则家族内其他 model 同冷却)
+- `model_cooldowns.json` 扩展为 skill 事实源(覆盖 hub `.model_state.json` 职责)
+
+#### T3. 多实例负载选路
+- `auto_dispatch` 改走 skill 内部选路:先 `enumerate_sessions(machine_cfg, cfg)` 扫全部实例活跃会话,再按实例并发限额 (`instance_limit`) 与在途任务数选最优实例 → 传给 `_alloc_dedicated_session(client_name=...)`。
+- 等价替换 hub `_pick_instance`。
+
+#### T4. 槽位并发锁
+- 抽取 `slot_lock.py` 的 FFLock / LockBusyError 移植到 skill(或保留在项目 `.workbuddy/slot_lock.py` 共享),`enumerate_sessions` 后校验并发槽位再派发。
+
+#### T5. 派发链直连(核心)
+- `.workbuddy/dispatch.py` 重写:不再 `subprocess.run(hub.py dispatch ...)` 作为投递后端;改为直接 import skill 的 `OpenCodeClient` + 调用 `create_session / send_prompt` 完成投递。
+- 原 `scheduler.apply_instance_discipline`(discipline.py 纪律注入)保持不变(skill 调用 discipline.py 单一事实源)。
+
+#### T6. 填槽并入自动化
+- `fill_slots.py`(tasks_backlog.json 挑空闲位派)逻辑并入 skill 的 `--auto` 命令(auto_dispatch 已做 pending 派发,只需把 backlog pool 来源换成 `.workbuddy/tasks.json` 即可)。
+- 巡检 automation prompt 追加 "pool empty → --add-task 建新任务" 路径(与 hub fill_slots 对齐)。
+
+#### T7. 会话恢复
+- `resume_interrupted.py` / `confirm_session.py` 逻辑拆出为 skill 子命令 `--resume <tid>` + `--confirm <session_id>`。
+- 或直接合并到 `--inspect` 增强(识别 interrupted 会话 → 给出恢复建议)。
+
+#### T8. 巡检并入 WorkBuddy automation
+- 现有 `CF 研发负责人·闭环巡检(每时45分)`(id `5c32f42a-f237-4946-bedf-97dd1e2e0f66`)prompt 更新:去掉 `hub.py` / `setup_scheduled_tasks.py` / `XAFHubCheck_*` / `loop_resume.py` 等引用,改为纯 skill 命令(`tdd_orchestrate.py --auto / --status / --clean-check / --changes` + `python -m opencode_client.health` 探活)。
+- 停用 Windows 计划任务:`XAFHubCheck_01..06`(10 分钟错峰)+ `xaf_hub_watchdog`(进程看护)。停前先 kill 正在跑的 `loop_resume.py` 与 `watchdog.py` python 进程。
+
+### 第二步:hub 退役归档
+
+**前置条件**:T1–T8 全部落地并通过 N ≥ 3 日闭环稳定验证(至少覆盖一次 429 黑窗 + 一次 08:00 解禁 + 一次多实例并发)。
+
+**动作**:
+1. `tasks_backlog.json` 待办迁移:对照 `.workbuddy/tasks.json` 去重后重新 `--add-task` 重建(**双源漂移纪律**:以远端 `git show origin/cfclub:<path>` 为准比对,不整体覆盖)。
+2. **禁用** `workflow update` 相关 automation 调用 `setup_scheduled_tasks.py`。
+3. **归档** `E:/cfc/tools/xaf_agent_hub/` 目录 → `E:/cfc/docs/archive/xaf-agent-hub-legacy/`(保留 README 作参考,保留旧 logs 供审计)。
+4. `config.json`(入库共享)里 6 个实例的 `model_chain` + fallback 字段已在上次分层收口(commit `e692fb688`)移出共享,进入 machine.json —— 此次迁移确认无遗漏回退。
+5. **最终验收**:完整 TDD 闭环(S-cfc-N RED→YELLOW→GREEN→DONE)跑通且 git evidence 全部推 origin/cfclub,期间无一次因 skill 侧缺失能力而退回 hub 的例外。
+
+---
+
+## 三、验收标准(每步都有硬指标)
+
+### T1–T8 完成后硬验证
+- [ ] `git grep -n "hub.py\|xaf_agent_hub" .workbuddy/dispatch.py tdd_orchestrate.py` → **0 条引用**(除注释/文档说明外)
+- [ ] `python dispatch.py --task S-cfc-X --json --dry-run` 投递成功(输出包含「已投递」/ verified:true)
+- [ ] 模型黑窗时段(模拟 429)自动跳过并顺延下一档;下次检定时按 `model_cooldowns.json` 正确解冻
+- [ ] 多实例环境下(≥2 实例有会话),auto_dispatch 选到的实例为负载最低者(枚举输出验证)
+- [ ] 并发槽位满时派发拒绝并有清晰错误信息(LockBusyError)
+- [ ] WorkBuddy automation 巡检日志无 hub 引用,prompt 驱动全是 skill 命令
+- [ ] 现有 6 个 XAFHubCheck 计划任务 + xaf_hub_watchdog 已清除(`schtasks /Query` 核验)
+
+### 第二步归档前硬性收敛
+- [ ] 最近连续 3 次人工/自动巡检(≥72 小时)全程无退回 hub 的记录
+- [ ] SC 累计跑通 5/241 → 推进中,无倒退
+- [ ] 所有 hub `.log` 文件已归档保存(`out/*.log`、`fill_slots.log`、`agent_check.log` 等)
+
+---
+
+## 四、风险与缓释
+
+| 风险 | 影响面 | 缓释 |
+|---|---|---|
+| skill 是多项目共用(mes/CFC),instance_model_chain 变更影响 mes 实例 | mes 可能掉配置 | `resolve_client_model_chain` 加「client 级 model_chain 未配置 → 回退全局 agent_model_chains」防御分支 |
+| `model_cooldowns.json` 扩全家族语义可能与 mes 现有 model_rules 冲突 | mes 调度异常 | 仅在 CFC 的 machine.json 加 `cfc_specific_rules: true` 开关,默认关闭 |
+| 派发直连后纪律注入(discipline.py)路径变化 | 开发会话指令错配 | 保留 discipline.py 作为 skill 调用方,不动 discipline.py |
+| 计划任务清理期间(windows 重启等)巡检空窗 | 几小时内无人工巡检 | 保留 WorkBuddy automation(每时 45 分)兜底;cleanup 仅在 T8 完成后再做 |
+| 迁移阶段 skill/hub 双轨导致「双真相」 | 账本不一致 | **铁律**:迁移期双轨并行,只以 `.workbuddy/tasks.json`(skill)为准;tasks_backlog.json 只读,不做写入 |
+
+---
+
+## 五、回退方案
+
+**任一 T 无法落地时回退路径**:
+1. `.workbuddy/dispatch.py` 保留原始实现(备份 `dispatch.py.bak-*`),迁移期始终可切回 `subprocess(hub.py dispatch)`。
+2. skill 改动均在本机 `~/.workbuddy/skills/tdd-orchestrator/scripts/`,删除这些文件即还原(skill 重装不影响)。
+3. plan 任务暂不清理;T1–T8 落地后,先**暂停**(不动删)观察 24 小时,再按计划步骤关。
+
+---
+
+## 六、实施顺序建议
+
+按依赖优先级排序:
+
+```
+T1 (available_models) ──┐
+T2 (黑窗统一)   ─────────┤──► 核心派发直连(T5)
+T3 (负载选路)  ──────────┘
+      │
+      ▼
+T4 (槽位锁) → T6 (填槽) → T7 (会话恢复) → T8 (巡检并入)
+      │                              │
+      └────── 并行实施 ──────────────┘
+```
+
+T1/T2/T3 互不相干可并行(三组代码隔离在不同脚本);T5 必须等 T1/T2/T3 落定后才改写,否则回滚点清晰。
+
+---
+
+## 七、后续行动
+
+1. **本方案文档推送 origin/cfclub**(本次单文件提交,作为团队共享单一事实源)
+2. **请主理人裁决**:是否立即派发 opencode 会话实施 T1–T3(第一组,核心基础),还是先观察本轮稳定后再动
+3. **若立即实施**:
+   - 派发会话 A:实施 T1(opencode_client.available_models + tdd_orchestrate.py resolve_client_model_chain)
+   - 派发会话 B:实施 T2(model_cooldowns.json 扩全家族语义 + .workbuddy/dispatch.py 清理黑窗引用)
+   - 派发会话 C:实施 T3(auto_dispatch 接入负载选路)
+   - 核验会话:逐 T 独立复核 git diff + 实测模型选择
+   - 三组完成后再进入 T4–T8