Repository navigation
docs: 新增 fusion-api 与 fusion-ui 合并为 monorepo 的实施计划 - #83
Conversation
评估两仓合并可行性并给出分阶段方案。目标为新建 fusion 仓库, 两个应用分别下沉到 apps/api 与 apps/ui。 核心结论:代码层零耦合(505 个 .py 与 522 个 .ts/.tsx 无交集), 成本集中在约 3600 行的发布流水线。deploy.yml 的 2553 行中 deploy-dev 单 job 占 2339 行,其中四个步骤的内联 shell 合计 1757 行,当前不可本地执行也不可单测。 方案分五个 Task:先搬迁(CI 只改路径并加 paths 过滤), 再把 shell 抽到 ops/deploy 使其可测,之后才做参数化合并与 可复用 workflow。搬迁与重写不得在同一次变更内完成, 以保证发布故障可二分定位。同时修正合并后 "回滚到 SHA X" 语义失效的问题,镜像 tag 改为 <app>-<sha>。 Co-Authored-By: Claude <noreply@anthropic.com>
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
|
我支持把 但这份 plan 当前还不能作为执行合同,建议 Claude Code 按下面问题逐项复核并更新后再合并: P0:当前会阻断迁移或回退的问题
P1:方案论证和影响面需要修正
建议重排
请 Claude Code 不要只改文字结论,最好对每个 P0 给出当前文件/配置证据、修订后的具体步骤和可执行验收条件。 |
Co-Authored-By: Claude <noreply@anthropic.com>
基线刷新至 fusion-api@43cee73 / fusion-ui@77b7fc2,全部数字重算。 9 条评审意见全部受理,逐条给出核实结果与处理,见文末修订记录。 主要变更: - 新增 Task 0 承载新仓基建与恢复点,解决原 Task 1 要求真实发布 但 secrets/runner/Environment 到 Task 4 才就绪的顺序矛盾。采用 新增 runner 实例加应用标签,旧仓 runner 保留至 Task 5。 - 移除 event-level paths 过滤,改为始终触发加 changes job 加恒定 required gate,避免被跳过的 workflow 使 required check 永久 Pending,同时规避 changed-files 300 文件上限。 - 跨应用顺序改由顶层 orchestrator DAG 保证,concurrency 只防并发。 - 新增路径与状态清单,A 类为宿主机持久化状态(5 处 cd ~/project/fusion、4 处 storage/files bind mount、cost-sync unit 的 checkout 耦合),独立为 Task 2 受控 cutover;B/C 类为 25 处 API 测试与 12 处 UI 测试对根 .github 的硬引用,其中 UI 侧 filesUnder 会枚举整个 workflows 目录。 - 撤销 <app>-<sha> 镜像 tag 方案,原判断有误:两应用已使用不同 ACR repository,同 SHA 不产生歧义。改为新增 per-app 发布台账 解决 last deployed SHA 分叉。 - 真实发布与回滚收敛到 Task 边界,不在每个脚本搬运后执行。 - Task 5 补充平台元数据迁移边界与旧仓退役时点。 Co-Authored-By: Claude <noreply@anthropic.com>
|
9 条全部受理并已推送修订( P0 逐条核实P0-1 基线过期 — 成立。 实测 P0-2 Task 顺序矛盾 — 成立。 已新增 Task 0 承载新仓基建、Environment/branch policy 复制、runner 准备与 git bundle 恢复点。采纳"新增 runner 实例 + P0-3 concurrency 不保证顺序 — 成立。 改为顶层 orchestrator DAG P0-4 P0-5 宿主机路径与持久化数据 — 成立。 实测清单:
已独立为 Task 2「受控 cutover」,含备份、稳定 data 目录、软链接兼容、systemd 更新、上传文件切换前后读写校验,以及旧仓回退演练。 这里修正一处我自己的计数:初稿写"3 个 systemd unit 引用 这反而给出了更好的处理方式:仓库内已有解耦范式,Task 2 应把 P1 逐条核实P1-6 镜像 tag — 成立,我原判断有误。 实测 P1-7 相对路径 — 成立,且可量化。 已形成 path inventory 分 A/B/C 三类。B 类实测:
P1-8 每脚本发布回滚 — 成立。 改为每脚本 fixture/dry-run/contract test(覆盖正常路径、SHA 非法、回滚锚点缺失),真实发布与回滚只在 Task 边界执行,Task 3 末尾保留一次完整 API → UI 验收。 P1-9 平台元数据边界 — 成立。 Task 5 已补:复制项(Environment protection、branch rules、secrets/variables、webhooks)、仅旧仓保留项(Issues、PR、Actions run 历史、releases)、 Task 重排已采纳你建议的顺序:Task 0 基建 → Task 1 搬迁(只验 CI) → Task 2 受控 cutover → Task 3 抽 shell → Task 4 reusable workflow 与平台绑定 → Task 5 归档。 未独立核实的项P0-2 中关于 runner 为 repo-scoped、标签仅为通用值、 Generated by Claude Code |
|
第二轮复核结论: 另外,文末标记为“未独立核实”的 runner / Environment 信息,我已通过 GitHub API 复核,结论属实:
不过在真正执行前,还需要补齐以下第二轮问题: P0-10:跨仓 cutover 仍缺少真正的互斥机制与最终状态闭环GitHub concurrency group 只在同一个 repository 内协调,无法阻止旧 Task 2 应加入明确的 cutover runbook:
否则当前验收步骤可能以“旧仓重新发布成功”结束,最终运行态实际上停在旧仓版本。 P0-11:GitHub secrets 不能从旧仓直接复制GitHub API 只能列出 secret 名称和元数据,不能读回 secret 值;Task 0/Task 5 中的“复制 secrets”无法按字面执行。webhook secret 与 deploy key 私钥也有同类问题。 请改成:
官方说明:https://docs.github.com/en/rest/actions/secrets P0-12:持久化状态仍未真正脱离代码 checkoutTask 2 给出的示例 建议固定为仓库外路径,例如:
验收除单文件下载外,还应记录迁移前后文件数量、总字节数、权限/owner,并对文件清单生成校验摘要。 P0-13:Task 2 与 Task 4 的外部平台 cutover 边界仍需前移判定Task 2 切换 GitHub Actions 发布权,但 Vercel/Railway root directory 到 Task 4 才修改。如果这些平台当前仍跟踪旧仓,Task 2 完成后整体发布链并未真正切到新仓。 Task 0 应先产出平台使用状态清单:每个 Vercel/Railway 服务是否活跃、跟踪哪个 repo/branch、负责 dev 还是 production、是否自动部署。所有仍活跃且影响运行态的绑定必须纳入 Task 2 的同一次受控 cutover;只有不影响当前运行态或纯重构项才能留在 Task 4。 P0-14:git bundle 不保护当前未提交资产当前本地
官方格式说明:https://git-scm.com/docs/bundle-format P1-15:Task 0 不需要无改动地重复真实发布与回滚Task 0 只新增新仓基础设施,不修改旧仓 runner/workflow。此时要求两个旧仓各真实发布和回滚一次,风险和成本高于收益,也与“不要把 master 当单步试验场”的修订原则不一致。 建议 Task 0 只验证旧 runner 在线、旧 workflow 配置未变、最近成功记录与只读 smoke;真实旧仓回退演练集中到 Task 2 的 cutover runbook 中执行一次。 合并 PR 本身的发布边界当前 建议 Claude Code 将 P0-10~P0-14 写入 plan 的具体 Task、Global Constraints、验收与回退表,而不是只追加评审记录。修订后这份计划才具备完整的跨仓 cutover 闭环。 |
6 条意见全部受理,并按要求写入 Task、Global Constraints、验收与 回退表本身,不只追加评审记录。文档 195 行增至 258 行。 主要变更: - 跨仓互斥:GitHub concurrency 只在单 repository 内协调,无法阻止 三仓同时操作同一台 dev server。新增宿主机 flock 约束,Task 0 部署锁文件与获取脚本。 - Cutover 闭环:Task 2 新增 6 步 runbook,第 6 步回切收尾为强制项, 验镜像 ID、发布台账与部署所有权三项归属新仓,避免验收停在 旧仓重新发布成功而运行态仍在旧仓。 - Secrets:GitHub API 不能读回 secret 值,复制无法按字面执行。改为 名称清单导出加原始凭据源重新注入,不可得则轮换,禁止从日志或 运行环境反向导出,逐项登记来源与验证结果。 - 持久化路径:原方案示例仍位于新仓 checkout 根之下,与自身约束 矛盾。固定为 ~/.local/share/fusion/api/storage/files 与 ~/.config/fusion/runtime.env,验收增加文件数、总字节数、 权限 owner 与校验摘要四项比对。 - 平台绑定判定前移:Task 0 新增平台使用状态清单,活跃且影响运行态 的绑定并入 Task 2 同一次 cutover,Task 4 收窄为不影响运行态的项。 - 恢复点:git bundle 不含未跟踪文件,Task 0 增加 dirty 清单、 未提交资产独立归档与哈希、bundle verify 与还原验证。 - Task 0 移除无改动的真实发布与回滚,改为只读核验,旧仓回退演练 集中到 Task 2 runbook 执行一次。 - 新增本 PR 自身的合并边界章节:deploy.yml 无 paths 过滤,合并将 触发完整 API 发布,应按一次正式发布处理。 Co-Authored-By: Claude <noreply@anthropic.com>
|
6 条全部受理,已推送 先谢谢你把 runner / Environment / secrets 那几项复核了 —— 文档里"未独立核实"的标注已结清,runner 名称也补进了 Task 0。 P0-10 跨仓互斥与最终状态闭环 — 成立concurrency 只在单 repository 内协调这点是对的,我原方案默认了它能跨仓,是错的。
P0-11 secrets 不能复制 — 成立"复制 secrets"确实无法按字面执行。已改为:名称清单导出(repo 层与 Environment 层分别)→ 从密码管理器/主机安全配置/原始凭据源重新注入 → 原始值不可得的一律轮换 → 新仓只验存在性、权限边界与连通性,禁止输出值 → 逐项登记「来源、注入位置、验证结果、是否轮换」。webhook secret 与 deploy key 私钥同此处理。也写进了 Global Constraints,含"禁止从 workflow 日志或运行环境反向导出"。 P0-12 持久化状态仍在 checkout 内 — 成立,是我方案自相矛盾初稿示例
约束措辞也改成"必须迁出 checkout 树,而不只是换一个子目录"。验收增加文件数、总字节数、权限/owner、校验摘要四项比对,单文件下载验证保留为附加项。过渡软链接要求写明退役时间。 P0-13 平台 cutover 边界前移 — 成立Task 0 新增第 7 步平台使用状态清单(活跃性 / 跟踪 repo 与 branch / dev 还是 production / 是否自动部署),并据此判定每个绑定归 Task 2 还是 Task 4。活跃且影响运行态的并入 Task 2 同一次 cutover;Task 4 收窄为"不影响运行态的项",并注明本 Task 不得留有影响运行态的绑定。 P0-14 bundle 不含未提交资产 — 原理成立,但计数不适用于仓库当前状态
需要说明一处:"9 个未跟踪的 plan/spec 文件"在本会话的 checkout 里不成立。 在 P1-15 Task 0 不必重复发布回滚 — 成立Task 0 不动旧仓 runner/workflow,无改动发布确实风险大于收益。验收改为只读核验(runner 在线、workflow 配置未变、最近成功记录存在),真实旧仓回退演练收敛到 Task 2 runbook 第 5 步执行一次。 本 PR 自身的合并边界 — 已确认并单列成章节实测 文档新增「关于本 PR 自身的合并边界」章节,写明这不是操作层面无副作用的文档合并,应按一次正式发布处理,选合适时间窗口并单独记录 CI、部署与运行态结果。 这条也是对我的提醒:我不会去合这个 PR,合并时机由你决定。 Generated by Claude Code |
|
第三轮复核结论: 剩余问题已经收敛,但仍有两个执行级断点和三个需要在实施前锁定的契约: P0-16:Task 2 orchestrator 在 Task 4 之前没有可调用的部署单元当前顺序是:
这里缺少 Task 2 的实际调用路径。普通、disabled 的 workflow 不能直接作为同一 DAG 中带 建议在 Task 1 就生成两份逻辑原样、尚未参数化合并的 app-specific reusable workflow:
Task 2 顶层 orchestrator 通过 job-level 同时补齐新仓
P0-17:部署
|
5 条意见全部受理,写入 Task、约束、验收与回退表。文档 258 增至 303 行。 主要变更: - orchestrator 调用路径:实测两边 workflow 均不暴露 workflow_call, 普通 workflow 无法被 job-level uses 加 needs 调用,原顺序下 Task 2 的 DAG 无对象可调。Task 1 新增产出 _deploy-api.yml 与 _deploy-ui.yml 两份逻辑原样、仅暴露 workflow_call 的外壳, 只改调用形式不动部署逻辑,Task 4 再合并为参数化版本。 - 跨仓互斥:实测两个旧仓 workflow 的 flock 调用数均为 0,而计划 声明不改动旧仓,故宿主机放锁文件不产生互斥。约束中锁的范围 收窄为新仓与宿主机操作,跨仓互斥改由 drain 门禁承担。新增 disable 不等于停止一条。 - Task 2 runbook 第 2 步改为显式 drain:查询三仓 queued 与 in-progress run,等待或安全取消至归零并二次确认,每次 enable disable 切换前重跑,三仓不得同时 enabled。锁的两个 方案中选择严格 drain,理由与可推翻性写入正文。 - branch protection 分两步 bootstrap:两旧仓 required check 同名 PR container validation,新仓尚未产生该 check 且 GitHub 要求 近 7 天内成功运行过,直接复制会使 PR 永久 Pending。Task 0 只 复制非 check 类保护,gate 成功运行一次后再设 required。 - changes 与 gate 行为契约具体化:完整 Git diff 不依赖 300-file 过滤,定义五种触发情形的 base head,gate 用 always 并验证应 运行的 job 成功而非 skipped 即放行,UI job 不得因 needs 的 skipped 被级联跳过。验收增加两个反例用例。 - per-app 台账语义表:权威来源为运行容器 immutable image ref 加 image ID,台账仅为可恢复投影,存于 checkout 树外,锁内原子 替换,定义三种恢复路径,禁止把部署时变化的 manifest commit 回 master 以免递归触发发布。 Co-Authored-By: Claude <noreply@anthropic.com>
|
5 条全部受理,已推送 P0-16 orchestrator 无可调用对象 — 成立实测两边 workflow 均不暴露 Task 1 新增第 5 步:产出 回滚契约也补了(第 7 步):目标应用 P0-17 flock 没覆盖旧仓 — 成立,我的约束是空的实测两个旧仓 workflow 的 两个方案我选了方案 2(严格 drain),理由写进了正文:方案 1 要在 cutover 前夕改旧仓的部署 job,而旧仓正是失败时的唯一回退路径,动它会削弱兜底。所以:
runbook 第 2 步改为显式 drain:查询三仓 这个选择是可推翻的 —— 如果你要双保险,方案 1 可以作为独立前置 PR 追加,我在正文里也标了。 P1-18 branch protection bootstrap — 成立实测两个旧仓的 required check 同名 Task 0 第 3 步改为只复制非 check 类保护;Task 1 确定 gate 的精确 display name 并在成功运行一次后设 required;Task 4 仅在 job 名确实变化时迁移 context,并验证旧 context 已移除。 P1-19 changes / gate 契约 — 成立原计划只写了结构没定语义。Task 1 新增第 6 步,五条逐一写死。其中两条你点出的陷阱我加了专门的反例验收:
P1-20 台账语义 — 成立原文只有"新增 per-app 发布台账"几个字。已补一张语义表,采纳你的建议以运行态为准:
失败/自动回滚、手动回滚、宿主机丢失三种恢复路径也分别定义了。 风险表同步加了 5 行。三轮共 20 条意见现已全部落到 Task、约束、验收与回退表里。 Generated by Claude Code |
不做什么章节声称 app/ 与 src/ 下不产生 diff,但路径清单 B 类要求 修改 fusion-ui/src/scripts/buildAndDeployWorkflow.test.ts,该文件 位于 src/ 之下,两处自相矛盾。 改为限定业务模块不产生 diff,并显式列出该测试文件为唯一例外。 API 侧 5 个同类测试位于 test/ 与 app/ 平级,不涉及此例外。 Co-Authored-By: Claude <noreply@anthropic.com>
CI 失败说明:
|
原文把 storage/files 直接写成用户上传文件持久化并当作事实,但存储 后端有 local minio oss 三种,由宿主机 .env 的 STORAGE_BACKEND 决定, 不在仓库内,本计划无法确认生产实际取值。 改为条件式表述,并补充两侧依据:local 时 local_storage.py 的 upload 直接写原始字节、download 直接读回,是原件唯一副本,且 models.py 有两处 storage_key 非空列与 storage_backend storage_key 唯一约束硬引用,丢文件会留下指向空对象的 DB 行;minio oss 时对象 存于远端,本地目录重要性大幅下降。 同时指出 deploy.yml L1428 对挂载的断言是无条件的,挂载存在不能 证明后端为 local;L1036 至 L1042 存在从容器内 tar 抢救文件的兼容 路径,说明该数据曾只存在于容器内并出过问题。 Task 0 第 6 步增加 STORAGE_BACKEND 实际取值的判定,结果决定 Task 2 中该项迁移的分量;Task 2 的 2.1 相应改为按判定结果执行。 Co-Authored-By: Claude <noreply@anthropic.com>
原文称需修正 7 份跨仓 plan 的 ../fusion-ui/ 相对路径,把「提到 fusion-ui 的 plan」与「含 ../fusion-ui/ 相对路径的 plan」混为一谈。 实测含该模式的仅 2 份,其中一份是本计划自身,真实存量为 1 份。 同时补充 Task 5 的分诊要求。实测两套记录体系互不索引:台账按 领域加 commit SHA 追踪,71 处 SHA 引用,而 54 份 plan 中仅 4 份 被台账点名;checkbox 追踪已基本废弃,25 份有 box 但 0 勾选、 10 份无 box、仅 9 份全勾。单看一份 plan 无法判断死活,这正是 AGENTS.md 第 11 条要求先读台账再查 git log 的原因。 Task 5 改为迁移时须标注已完成 进行中 已废弃或按状态分目录, 否则等于把索引缺口原样搬进新仓。 Co-Authored-By: Claude <noreply@anthropic.com>
回应 PR #84 第一轮评审的两个 P1。 P1 之一:补删 docs/superpowers/plans/2026-08-31-run-skills-mvp.md。 原先以「台账无完成条目」为由保留,该判据错误。核实 master 历史, 64049e5(PR #76 引入 Run 级 Skills 运行时)已逐项实现该计划: app/ai/skills/registry.py、verified-research/1.0.0 skill、Run capability 路由、Trajectory API 与 skills 详情端点、agent loop wiring、previous run skill release 及配套测试;3ba19f9(PR #77) 与 22f8625(PR #79)为后续修正。保留它会恰好留下唯一一份 「已完成却显示未开始、且仍强制调用已弃用插件」的文档,与本 PR 目标相反。docs/superpowers/plans 至此为空,同步移除其 gitignore 白名单。 同时在执行台账补上 Run 级 Skills 运行时的已完成记录。台账缺这条 正是上述错误判据的成因。 P1 之二:确立与插件无关的稳定落位目录 docs/implementation-plans, 并将全部发现入口改为同时覆盖该目录与 docs/superpowers/specs: AGENTS.md 第 11 条、执行台账开头说明与检查清单第 3 条、 fusion-next-step skill。gitignore 增加对应白名单。 此前仅把发现入口收窄到 specs,会使 PR #83 的 monorepo 实施计划 合入后落在无人扫描的目录中。#83 需配套把文档移入新目录并修正 其对已删文件的引用,两者合并后需在最终树上重跑残留引用检查。 Co-Authored-By: Claude <noreply@anthropic.com>
回应 PR #84 评审的第二个 P1:#84 把发现入口收窄后,本计划若仍落在 docs/superpowers/plans 会落在无人扫描的目录,且其对已删文件的完整 路径引用会立即断链。 - 文件迁至 docs/implementation-plans/2026-08-31-monorepo-merge.md, 该目录由 #84 确立为与已弃用插件无关的稳定落位,并已纳入全部发现 入口。本分支不改 gitignore,其白名单随 #84 提供,因此本 PR 需在 #84 之后合并。 - 依据章节:对 2026-06-28-agent-run-continuation.md 的引用改为标注 该文件已由 #84 清理、原文存于 git 历史,并说明结论不受影响。 - 路径清单 C 类:含 ../fusion-ui/ 的唯一存量文件已随 #84 删除,本项 在新树上归零,只需对两个保留目录复查一次。 - Task 5 第 2 步重写:fusion-api 侧文档结构已由 #84 简化,本步改为 迁移 implementation-plans 与 specs,并指出 fusion-ui 侧尚未做过 同一轮清理、需先按同样口径核查再决定删除或迁移;同时要求 specs 脱离 superpowers 这一废弃插件名,且落位与发现入口必须一一对应、 在合并后的最终树上验证。 Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
概述
新增一份实施计划文档,评估把
fusion-api与fusion-ui合并为单一仓库的可行性并给出分阶段方案。只新增一个文件,不含任何代码改动:
目标仓库为新建的
HyxiaoGe/fusion,两个应用分别下沉到apps/api与apps/ui。基线
fusion-api@43cee73/fusion-ui@77b7fc2(2026-08-31 复核)。初版基于已过期的c5b0775,现已 merge master 并重算全部数字。核心结论
代码层零耦合,成本全在流水线。 516 个
.py(699 文件)与 524 个.ts/.tsx(636 文件),交叉计数为 0。无共享包、无 workspace 依赖、无交叉 import。两边.git分别 5.5M / 2.8M,merge --allow-unrelated-histories即可保留完整历史。deploy.yml的体量是内联 shell,不是 YAML 编排。 全文 2563 行中deploy-dev单个 job 占 2339 行(L189–L2527),其中四个步骤合计 1757 行(占该 job 的 75%):这套逻辑当前不可本地执行、不可单测,唯一验证方式是向 master 推送。
两条流水线的形状已被手工写了两遍。 逐字相同的步骤名 14 个(去重后),另有 3 对同职能不同名。
目录下沉不是纯路径替换。 path inventory 三类:
cd ~/project/fusion、4 处./fusion-api/storage/filesbind mount、${HOME}/project/fusion/.env、cost-syncsystemd unit 的 checkout 耦合。严重程度取决于宿主机.env中STORAGE_BACKEND的实际取值(local/minio/oss),Task 0 判定.github—— API 侧 5 个文件 25 处(含一处 CWD 相对,失效方式不同),UI 侧 12 处且filesUnder()会枚举整个 workflows 目录cache-dependency-path方案要点
六个 Task,顺序本身是方案的一部分:
STORAGE_BACKEND判定_deploy-api.yml/_deploy-ui.yml两份workflow_call外壳;changes/gate 与回滚契约。只验 PR CI 与镜像构建,不做发布ops/deploy/,配 fixture/dry-run/contract test_deploy-app.yml、per-app 发布台账语义、required context 迁移、平台绑定收尾关键约束:
~/.local/share/fusion/api/storage/files、运行时配置~/.config/fusion/runtime.envpaths:—— 被跳过的 workflow 不产出结论会使 required check 永久 Pending,且 changed-files 有 300 文件上限而首次搬迁涉及 1300+ 文件flock范围限于新仓与宿主机操作(实测两旧仓 workflow 的flock调用数为 0);跨仓互斥由 Task 2 的 drain 门禁承担disable不等于停止 —— 每次 enable/disable 切换前须 drain 至三仓在途 run 归零文档中显式排除的范围
apps/ui/src/scripts/buildAndDeployWorkflow.test.ts(B 类,需限定扫描范围)docker-compose.yml(api 侧有middleware/litellm_net/flyai三个网络)<app>-<sha>tag 方案已撤销与 #84 的配套调整(
7df9b1c)#84 清理了已弃用的 superpowers 遗留文档并确立
docs/implementation-plans/为插件无关的落位目录。本 PR 相应:docs/superpowers/plans/移入docs/implementation-plans/,与发现入口一致fusion-ui侧尚未做过同一轮清理,需先按同样口径核查deploy.yml为on.push.branches: [master]且无paths过滤。即便本 PR 只新增文档,合并仍会触发publish(构建推 ACR)+deploy-dev(alembic 迁移 + 容器重启)。文档中已单列「关于本 PR 自身的合并边界」章节。说明
🤖 Generated with Claude Code
https://claude.ai/code/session_01QR4stz9sbfVRS6xgriQ9kQ