P3.UA25R1.F3-doc-tabs-multi-tab-routing · doc tabs 多 tab 路由 + 子图 tab 主页 chain-mini-bar 复用
Worker:ClaudeA · 部门:前端 P3-xitune · 预计:1.5d · 优先级:P0 关键路径起点 · 状态:dispatched · isolation:🧵 file(同 worktree 同 branch · 与 F2-R1 文件正交可并行)
🔍 触发与解锁链
触发:用户 2026-06-18 17:14 拍板方案 A · 双连 dispatched F3-R1 + F2-R1 · 同 ClaudeA 自管串行节奏 · F3-R1 优先暴露 useSubgraphTabs API。
用户原话(verbatim · ADR-25-R1 §1.1 第 1+2 段):
"在主页面的 mini-node 中双击子图模块可以打开或者跳转到子图 tab 主页" "进入子图 tab 主页 → chain-mini-bar 显示子图内部 modules · 单击调音 / 双击悬浮窗与 Main tab 一致"
架构契约(ADR-25-R1 §3.3-R1 ① 输入/输出契约 · 业务契约 5 必填段):
- 契约 A · useSubgraphTabs composable(NEW):暴露 openSubgraphTab(defId) / closeSubgraphTab(defId) / activeTabId ref / subgraphTabs computed
- 契约 B · buildDocTabs 扩展:返回 [main, ...subgraphTabs.value.map(t => ({key:'subgraph-${defId}', label:t.name+'.subgraph', icon:'📦', dirty, closeable:true}))] · 主 tab 永居 [0]
- 契约 C · currentTabModules computed:activeDocTab='main' → linkStore.modules / activeDocTab='subgraph-${defId}' → linkStore.subgraphDefs[defId].nodes
- 契约 D · useChainMiniNodes 签名改造:接收 Ref<ModuleInstance[]>(子图 tab 时传 currentTabModules · 主 tab 时传 orderedModules · 实际就是统一传 currentTabModules)
- 契约 E · onMiniNodeDblclick 子图分支:console.log 占位 → 改 openSubgraphTab(node.subgraphMeta.defId)
- dirty 标记:复用 ADR-08-R1 R1.5(linkStore.subgraphDefs[defId].dirty)
- 5 类失败回退(ADR-25-R1 §3.3-R1 ③):defId 不存在 toast / 同 defId 重开 idempotent / 关闭 active tab 切回 main / 主 tab 拒关 closeable=false / 刷新丢 tab(状态不持久化)
解锁链(本任务 zombie 后):
- ✅ F2-R1 P3.UA25R1.F2-flow-readonly-dock-subgraph-display API 依赖满足(可消费 openSubgraphTab)
- ⏳ F4-R1 P_e2e.A25R1.F4-truth-e2e-subgraph-r1(blocked-by-F2-R1+F3-R1 · ClaudeC 0.5d → ADR-25 整体闭环 🏆)
任务定义(基于 ADR-25-R1 §3.3-R1 + §4.2)
子任务 ① · useSubgraphTabs composable 实装(0.4d)
Step 1.1:新建 frontend_vue3/src/stages/xitune/composables/useSubgraphTabs.ts(参考 useChainMiniNodes 风格):
interface SubgraphTab {
key: string // `subgraph-${defId}`
defId: string
name: string
icon: '📦'
dirty: boolean // 来自 linkStore.subgraphDefs[defId].dirty(ADR-08-R1 R1.5)
closeable: true
}
export function useSubgraphTabs() {
const linkStore = useLinkStore()
const _openTabs = ref<SubgraphTab[]>([]) // 已打开的子图 tab 数组
const activeTabId = ref<string>('main')
function openSubgraphTab(defId: string) {
const def = linkStore.subgraphDefs[defId]
if (!def) {
// ADR-25-R1 §3.3-R1 ③ 失败回退:def 不存在
// toast 由调用方处理(本 composable 仅返回 false)
console.warn('[F3-R1] openSubgraphTab: defId not found', defId)
return false
}
// idempotent:同 defId 不重开 · 切 active 即可
const key = `subgraph-${defId}`
if (!_openTabs.value.find(t => t.key === key)) {
_openTabs.value.push({ key, defId, name: def.name ?? defId, icon: '📦', dirty: def.dirty ?? false, closeable: true })
}
activeTabId.value = key
return true
}
function closeSubgraphTab(defId: string) {
const key = `subgraph-${defId}`
_openTabs.value = _openTabs.value.filter(t => t.key !== key)
if (activeTabId.value === key) {
activeTabId.value = 'main' // 关闭 active tab 时切回主 tab
}
}
const subgraphTabs = computed(() => _openTabs.value)
return { openSubgraphTab, closeSubgraphTab, activeTabId, subgraphTabs }
}
Step 1.2:导出类型 SubgraphTab interface(供 buildDocTabs 类型对齐)
子任务 ② · index.vue buildDocTabs 扩展(0.2d)
Step 2.1:修改 frontend_vue3/src/stages/xitune/index.vue L221-228 buildDocTabs():
- 在 setup 顶部 import + 实例化 const { openSubgraphTab, closeSubgraphTab, activeTabId: subgraphActiveTabId, subgraphTabs } = useSubgraphTabs()
- 改 buildDocTabs 返回:
function buildDocTabs(): DocTab[] {
const main = { key: 'main', label: name + '.xitune', icon: '🎚', dirty: false /* closeable 默认 false · shell 已支持 */ }
const subTabs = subgraphTabs.value.map(t => ({
key: t.key,
label: t.name + '.subgraph',
icon: t.icon,
dirty: t.dirty,
closeable: t.closeable,
}))
return [main, ...subTabs]
}
shellSlots.setDocTabs(buildDocTabs(), shellSlots.activeDocTab ?? 'main') 保留当前 active(避免切 tab 时被刷成 main)
- 添加 watch 监听 subgraphTabs 变化 → refreshDocTabs(子图 tab 增删时自动刷)+ watch subgraphActiveTabId → shellSlots.setDocTabs(buildDocTabs(), subgraphActiveTabId.value) 同步切 active
子任务 ③ · currentTabModules computed + useChainMiniNodes 改造(0.4d)
Step 3.1:在 index.vue setup 中(L391 useChainMiniNodes 调用之前)新增 computed:
const currentTabModules = computed<ModuleInstance[]>(() => {
const activeKey = shellSlots.activeDocTab ?? 'main'
if (activeKey === 'main') {
return orderedModules.value // 主链路 BFS 拉平(现有)
}
// 子图 tab:从 subgraphDefs 取 nodes
const tab = subgraphTabs.value.find(t => t.key === activeKey)
if (!tab) return orderedModules.value // fallback:防御性
const def = linkStore.subgraphDefs[tab.defId]
return def?.nodes ?? []
})
Step 3.2:修改 useChainMiniNodes 调用(L391-392):
- 把 useChainMiniNodes(orderedModules) 改为 useChainMiniNodes(currentTabModules)
- ⚠️ 检查 useChainMiniNodes.ts 签名:若现有签名是 useChainMiniNodes(modules: Ref<ModuleInstance[]>) 则无需改 composable;若是 useChainMiniNodes(modules: ComputedRef<ModuleInstance[]>) 也兼容(Vue ref/computed 互通)
- ⚠️ 不要修改 useChainMiniNodes.ts 内部逻辑(F1 d9a2e1c 资产 · ADR-25-R1 §2.2 部分保留矩阵)· 仅改调用入参
子任务 ④ · onMiniNodeDblclick 子图分支改造(0.1d)
Step 4.1:修改 index.vue L482-495 onMiniNodeDblclick:
- 删除 L487-493 console.log 占位(F1 d9a2e1c 占位代码)
- 改为:
if (node?.isSubgraph) {
if (node.subgraphMeta?.defId) {
const ok = openSubgraphTab(node.subgraphMeta.defId)
if (!ok) {
// ADR-25-R1 §3.3-R1 ③ defId 不存在 toast(用 alert 占位 · 后续可换 toast 组件)
console.warn('[F3-R1] 子图定义已不存在', node.subgraphMeta.defId)
}
}
return
}
floating.openDialog(instanceId, moduleId) // 普通模块保持现有
子任务 ⑤ · vitest +8 case + 类型/构建/全测试基线零回归(0.4d)
Step 5.1:新建 frontend_vue3/src/stages/xitune/composables/__tests__/useSubgraphTabs.spec.ts(参考 useChainMiniNodes.spec.ts 风格 · 8 case):
- T1 openSubgraphTab(defId) 新建 tab + 切 active
- T2 openSubgraphTab(同 defId) 重复 idempotent · 不重开 · 仅切 active
- T3 openSubgraphTab(不存在 defId) 返回 false + 不动 _openTabs
- T4 closeSubgraphTab 删 tab + 若是 active 切回 main
- T5 closeSubgraphTab 不是 active · activeTabId 不变
- T6 subgraphTabs computed 返回数组只读快照
- T7 dirty 字段从 linkStore.subgraphDefs[defId].dirty 派生
- T8 多 tab 顺序保持插入顺序(FIFO 不重排)
Step 5.2:vue-tsc --noEmit 0 errors · npm run build 0 errors · npm run test 全过(基线 + 8 新 case)
子任务 ⑥ · 浏览器实测端到端(0.0d 含在 ⑤ 验收时间)
Step 6.1:启动 backend(dotnet run)+ frontend(npm run dev)· 准备 fixture(xilink 工程含 1 子图 EQ_A · 主链路 1 EQ_A 实例)· 切到 xitune
Step 6.2:验收点(ADR-25-R1 §3.3-R1 ④ 用户操作流 · F2-R1 未 zombie 时部分操作走 stub):
- ☐ 主 tab 🎚 工程名.xitune(closeable=false)正常显示
- ☐ chain-mini-bar 双击 📦 EQ_A → doc tabs 加 📦 EQ_A.subgraph(可关闭) + 自动切 active
- ☐ 子图 tab 主页 chain-mini-bar 显示子图内部 modules(currentTabModules 切换生效)
- ☐ 子图 tab 内单击普通 module → activeInstanceId 切到子图内 instanceId · 主区右侧加载对应 InlinePanel
- ☐ 子图 tab 关闭 → 切回 main + activeInstanceId 恢复 main 之前选择
- ☐ 同 defId 双击 2 次 → 不重开(idempotent)+ 切 active
完整 prompt(直接复制粘贴 ClaudeA 终端)
[U-thread] P3.UA25R1.F3-doc-tabs-multi-tab-routing · ADR-25-R1 §3.3-R1(关键路径 · 1.5d · 暴露 API)
[部门] 前端 P3-xitune
[Worker CWD] d:/work/25_claude/workspace/AlgoDepartment/04_development/
[Occupies] P3.K-shared-xitune-doc-tabs + P3.K-shared-xitune-mini-bar
[优先级] P0(关键路径起点 · 1.5d · 暴露 useSubgraphTabs API 给 F2-R1 消费)
[ADR] docs/08-implementation/40-aios/ADR/ADR-AIOS-25-R1-flow-readonly-subgraph-and-doc-tabs.md(必读 §3.3-R1 + §4.2 F3-R1 fork 表 + §5 风险表)
[isolation] file(同 worktree 同 branch · 与 F2-R1 文件正交并行)
[API 暴露] useSubgraphTabs() composable · 供 F2-R1 onModuleDblClick + 本 fork onMiniNodeDblclick 共同消费
[参考文档绝对路径]
- 业务契约:ADR-25-R1 §3.3-R1 完整 5 必填段(① useSubgraphTabs API + buildDocTabs 扩展 + currentTabModules / ② 8 行收敛判据 / ③ 5 类失败回退 / ④ 6 步操作流 / ⑤ playwright e2e 模板)
- 用户 verbatim(ADR-25-R1 §1.1):"双击可以打开或跳转到子图 tab 主页 + chain-mini-bar 显示子图内部 modules · 单击调音 / 双击悬浮窗与 Main tab 一致"
- 范式 commits(worker 必读):
* 0bb4422 P3.UA25R1.F1 combo hotfix(同 ClaudeA 同 ADR-25-R1 · isolation: file · 完整 prompt 标本 278 行 · 8 段结构)
* d9a2e1c P3.A25.F1 useChainMiniNodes(本任务消费的 ChainMiniNode + isSubgraph + subgraphMeta 数据源)
* a5b52de P1.A21.F1 dock-host-generalize(同 ClaudeA 前端 · 完整 prompt 8 段 + commit 七元组标本)
[现有组件 grep 真值(.clinerules v3.0 ADR-23→25→25-R1 三连教训铁律 · 派发前已核查)]
① 待新建文件:
- frontend_vue3/src/stages/xitune/composables/useSubgraphTabs.ts(NEW · 参考 useChainMiniNodes.ts 风格)
- frontend_vue3/src/stages/xitune/composables/__tests__/useSubgraphTabs.spec.ts(NEW · +8 case)
② 待修文件 frontend_vue3/src/stages/xitune/index.vue:
- L221-228 buildDocTabs() 单 tab 现状 → 加 ...subgraphTabs.value.map(...)(主 tab 永居首位)
- L230-232 refreshDocTabs() 用 shellSlots.setDocTabs(buildDocTabs(), 'main') → 改保留当前 active
- L391-392 useChainMiniNodes(orderedModules) → 改 useChainMiniNodes(currentTabModules)
- L482-495 onMiniNodeDblclick 子图分支 console.log 占位 → 改 openSubgraphTab(node.subgraphMeta.defId)
- L494 普通模块 floating.openDialog 不动
③ 数据源(只读 · 不动):
- linkStore.subgraphDefs[defId]:含 .name / .nodes / .dirty(ADR-08-R1 R1.5 dirty 标记)
- useChainMiniNodes.ts ChainMiniNode 接口:含 .isSubgraph + .subgraphMeta.defId(F1 d9a2e1c 资产)
- shellSlots.activeDocTab / shellSlots.docTabs / shellSlots.setDocTabs(shell 框架已支持 closeable)
[文件正交策略](.clinerules v3.0 §isolation):
isolation: file · 同 worktree 同 branch · 改 1 + 新建 2 = 3 文件
与 F2-R1(改 FlowReadonlyDock.vue · 1.0d)文件正交可并行 · 但 F2-R1 onModuleDblClick 子图分支需调本 fork 暴露的 openSubgraphTab API · 建议本 fork 优先 commit useSubgraphTabs.ts(API 骨架)再继续其他改造 · F2-R1 可早期 import
[API 契约对外承诺(F2-R1 消费方依赖)]
export function useSubgraphTabs(): {
openSubgraphTab(defId: string): boolean // 返回 true=成功 / false=defId 不存在
closeSubgraphTab(defId: string): void
activeTabId: Ref<string> // 'main' | 'subgraph-${defId}'
subgraphTabs: ComputedRef<SubgraphTab[]>
}
- F2-R1 在 FlowReadonlyDock.vue setup 中需:
import { useSubgraphTabs } from '@/stages/xitune/composables/useSubgraphTabs'
const { openSubgraphTab } = useSubgraphTabs()
// onModuleDblClick 子图分支调 openSubgraphTab(mod.subgraphMeta?.defId)
【背景】
用户 2026-06-15 16:06 verbatim 三段拆解纠正 ADR-25 v0.1 · 16:14 ADR-25-R1 落盘 · 16:45 accept · F1.1-R1 0bb4422 已 zombie(单击不响应 + F2 文件清理)· 17:14 用户拍板方案 A 双连 dispatched F3-R1 + F2-R1。
本 fork F3-R1 是关键路径起点(1.5d · 优先级 P0)· 暴露 useSubgraphTabs composable 给 F2-R1 双击行为消费 · 同时改造 buildDocTabs 派生子图 tab + currentTabModules 切换 main / 子图 tab 内 chain-mini-bar 数据源。
教训承接(ADR-25-R1 §9.3):用户原话名词必须 grep 找具体文件+行号 → 本 prompt [现有组件 grep 真值] 段已落实 9 个真值锚点(L221-228 / L230-232 / L391-392 / L482-495 / linkStore.subgraphDefs / useChainMiniNodes / shellSlots / ChainMiniNode / dirty)。
【架构关键约束】
⚡ 严守 .clinerules v3.0 ADR-23→25→25-R1 三连教训铁律:用户原话"打开/跳转子图 tab 主页"必须解析为现有 shellSlots.docTabs + setDocTabs 框架(本 fork 完善 · 不新建)
📋 buildDocTabs 主 tab 永居 [0]:closeable=false(shell 默认行为)· 子图 tab closeable=true
📋 currentTabModules computed:activeKey='main' → orderedModules / activeKey='subgraph-${defId}' → subgraphDefs[defId].nodes
📋 useChainMiniNodes 签名兼容:本 fork 仅改调用入参(orderedModules → currentTabModules)· 不动 composable 内部逻辑(F1 d9a2e1c 资产)
📋 onMiniNodeDblclick 子图分支:console.log 占位 → openSubgraphTab(defId)· 普通模块 floating.openDialog 不动
📋 dirty 标记:复用 linkStore.subgraphDefs[defId].dirty(ADR-08-R1 R1.5)· 不新增 store 字段
📋 idempotent:同 defId openSubgraphTab 不重开 · 仅切 active(防御快速双击竞态)
🚫 严禁动 useChainMiniNodes.ts / useChainMiniNodes.spec.ts(F1 资产 · 仅消费)
🚫 严禁动 FlowReadonlyDock.vue(F2-R1 范围)
🚫 严禁动 linkStore.ts schema(仅消费 subgraphDefs · 不动 store)
🚫 严禁破坏 onMiniNodeClickWithDelay(F1.1-R1 0bb4422 已落地 isSubgraph 早 return · 本 fork 不动)
🚫 严禁删除主 tab(closeable=false 默认 · shell 已拒绝)
【执行步骤】
Step 0 · 文件注入真值核查(强制门槛 · ADR-25-R1 §9.3 三连教训承接)
- read frontend_vue3/src/stages/xitune/index.vue L221-232 / L391-400 / L460-500 · 确认本 prompt [现有组件 grep 真值] 9 个锚点对齐
- read frontend_vue3/src/stages/xitune/composables/useChainMiniNodes.ts 全文(F1 资产 · 派生 isSubgraph + subgraphMeta 数据源)
- read frontend_vue3/src/stores/linkStore.ts grep "subgraphDefs" 确认 schema(.name / .nodes / .dirty 三字段)
- read frontend_vue3/src/composables/useShellSlots(或类似)grep "setDocTabs|activeDocTab|DocTab" 确认 closeable 字段已支持
- read 标本 prompts/done/ADR-AIOS-25/P3.UA25R1.F1-mini-bar-click-no-response-on-subgraph--0bb4422.md(同 ClaudeA 同 ADR-25-R1 isolation:file · 8 段结构标本)
- 留 commit log:Step 0 五层核查记录
Step 1 · 新建 useSubgraphTabs composable 0.4d(子任务 ①)
- 新建 frontend_vue3/src/stages/xitune/composables/useSubgraphTabs.ts
- 实装 SubgraphTab interface + useSubgraphTabs() composable
- 暴露 4 项 API:openSubgraphTab(defId): boolean / closeSubgraphTab(defId) / activeTabId Ref / subgraphTabs ComputedRef
- idempotent 检查:同 defId 不重开 · 仅切 active
- 失败回退:defId 不存在 → 返回 false + console.warn(toast 由调用方处理)
- 关闭 active tab → 切回 'main'
Step 2 · index.vue buildDocTabs 扩展 0.2d(子任务 ②)
- 在 setup 顶部 import useSubgraphTabs + 实例化
- 改 L221-228 buildDocTabs:返回 [main, ...subgraphTabs.value.map(t => ({key, label, icon, dirty, closeable}))]
- 改 L230-232 refreshDocTabs:shellSlots.setDocTabs(buildDocTabs(), shellSlots.activeDocTab ?? 'main')
- watch subgraphTabs → refreshDocTabs(增删自动刷)
- watch activeTabId → shellSlots.setDocTabs(buildDocTabs(), activeTabId.value)(同步切 active)
Step 3 · currentTabModules computed + useChainMiniNodes 改造 0.4d(子任务 ③)
- 新增 currentTabModules computed(activeKey === 'main' ? orderedModules : subgraphDefs[defId].nodes)
- 改 L391-392 useChainMiniNodes(orderedModules) → useChainMiniNodes(currentTabModules)
- 不动 useChainMiniNodes.ts 内部
- 不动 sourceModules / nonSourceOrSingleSourceModules computed(基于 chainMiniNodes 派生 · 跟随 currentTabModules 自动响应)
Step 4 · onMiniNodeDblclick 子图分支改造 0.1d(子任务 ④)
- 改 L482-495 子图分支:console.log 占位 → openSubgraphTab(node.subgraphMeta.defId)
- 失败回退(defId 不存在):openSubgraphTab 返回 false + console.warn
- 普通模块 floating.openDialog 不动
Step 5 · vitest +8 case + 类型/构建 0.4d(子任务 ⑤)
- 新建 useSubgraphTabs.spec.ts · 8 case(T1 open / T2 idempotent / T3 not-found / T4 close+切回main / T5 close非active / T6 computed / T7 dirty 派生 / T8 FIFO 顺序)
- mock useLinkStore · 注入 subgraphDefs[mock-defId] = { name, nodes, dirty }
- vue-tsc --noEmit 0 errors
- npm run build 0 errors
- npm run test 全过(基线 + 8 新 case)
Step 6 · 浏览器实测 + commit
- 启动 backend(dotnet run)+ frontend(npm run dev)
- 准备 fixture:xilink 工程含 1 子图 EQ_A(3 modules)+ 主链路 1 EQ_A 实例 · 切到 xitune
- 验收点(ADR-25-R1 §3.3-R1 ④ · F2-R1 未 zombie 时双击主链路 📦 EQ_A 仍可触发本 fork 的 onMiniNodeDblclick → openSubgraphTab):
☐ 主 tab `🎚 工程名.xitune`(closeable=false)正常显示
☐ chain-mini-bar 双击 📦 EQ_A → doc tabs 加 `📦 EQ_A.subgraph(可关闭)` + 自动切 active
☐ 子图 tab 主页 chain-mini-bar 显示子图内部 3 个 modules(peq + gain + delay)
☐ 子图 tab 内单击 peq → activeInstanceId 切到子图内 instanceId · 主区加载 GEQTuningDialog inline
☐ 子图 tab 关闭 → 切回 main + activeInstanceId 恢复
☐ 同 defId 双击 2 次 → 不重开 + 切 active(idempotent)
- git add . && git commit -m "feat(xitune/doc-tabs): P3.UA25R1.F3 useSubgraphTabs composable + 多 tab 路由
用户 2026-06-18 17:14 拍板方案 A 双连 dispatched F3-R1 + F2-R1 · 17:15 start。
F3-R1 本任务(ADR-25-R1 §3.3-R1 + §4.2 关键路径起点):
① 新建 useSubgraphTabs composable · 暴露 openSubgraphTab/closeSubgraphTab/activeTabId/subgraphTabs API
② index.vue buildDocTabs 扩展 · 主 tab 永居 [0] + 子图 tab 派生(closeable=true · dirty 复用 ADR-08-R1)
③ currentTabModules computed · activeKey='main' → orderedModules / 'subgraph-${defId}' → subgraphDefs[defId].nodes
④ useChainMiniNodes 调用入参改 currentTabModules(F1 资产不动)
⑤ onMiniNodeDblclick 子图分支 · console.log 占位 → openSubgraphTab(defId)
⑥ vitest +8 case · vue-tsc + build + 全测试基线零回归
⑦ 暴露 API 给 F2-R1 消费(FlowReadonlyDock onModuleDblClick 子图分支同样调 openSubgraphTab)
解锁 F2-R1 API 依赖(可消费 openSubgraphTab)+ F4-R1 e2e
ADR-23→25→25-R1 三连教训承接:用户原话名词 grep 找具体文件+行号(本 commit 涉及 9 个真值锚点)
[step=6/6] [pid=P3] [uid=P3.UA25R1.F3-doc-tabs-multi-tab-routing] [type=feature] [isolation=file]
[occupies=P3.K-shared-xitune-doc-tabs+mini-bar] [files=3(2A+1M)] [ipc=none]
[adr=ADR-AIOS-25-R1 §3.3-R1 + §4.2 F3-R1 + 用户 2026-06-15 16:06 verbatim + 16:45 accept + 2026-06-18 17:14 双连 dispatched]"
【验收】
☐ Step 0 文件注入真值核查通过(read 9 个锚点对齐 + 5 层核查记录)
☐ Step 1 useSubgraphTabs composable 实装 · 4 API 全暴露 · idempotent + failure-fallback 落地
☐ Step 2 buildDocTabs 主 tab 永居 [0] · 子图 tab 派生正确 · refreshDocTabs 保留 active
☐ Step 3 currentTabModules computed 切换正确 · useChainMiniNodes 接收 currentTabModules · sourceModules 跟随响应
☐ Step 4 onMiniNodeDblclick 子图分支调 openSubgraphTab · 普通模块 floating.openDialog 零回归
☐ Step 5 vitest +8 case 全过 · vue-tsc + build 0 errors · 全测试基线零回归
☐ Step 6 浏览器实测 6 验收点全过(主 tab + 双击切 active + 子图内 mini-bar + 单击调音 + 关闭切回 + idempotent)
☐ commit message 含 7 元组 trailer + ADR-25-R1 §3.3-R1 + §4.2 引用 + 用户三段 timestamp
【禁止】
❌ 禁止跳过 Step 0 文件注入真值核查(ADR-25-R1 §9.3 三连教训 · 9 锚点必须逐一对齐)
❌ 禁止动 useChainMiniNodes.ts / useChainMiniNodes.spec.ts(F1 d9a2e1c 资产 · 仅改调用入参)
❌ 禁止动 FlowReadonlyDock.vue(F2-R1 范围)
❌ 禁止动 linkStore.ts schema 或 subgraphDefs 字段(仅消费)
❌ 禁止破坏 onMiniNodeClickWithDelay(F1.1-R1 0bb4422 资产 · 本 fork 不动 isSubgraph 早 return 分支)
❌ 禁止删除主 tab 或改 closeable=true(主 tab 必须不可关闭)
❌ 禁止持久化子图 tab 状态(刷新丢 tabs 是预期行为 · ADR-25-R1 §3.3-R1 ③)
❌ 禁止跳过 vitest +8 case(验收硬门槛)
❌ 禁止 commit 缺七元组 trailer(.clinerules v3.0 铁律)
❌ 禁止嵌入完整 SFC > 60 行 / TS interface > 5 行(.clinerules v3.0)· 用 diff 模式提交
解锁链(本任务 zombie 后)
- ✅ useSubgraphTabs composable API 暴露(F2-R1 可 import + 调用 openSubgraphTab)
- ✅ doc tabs 多 tab 路由落地(主 tab + N 子图 tab · 主 tab 永居 [0])
- ✅ 子图 tab 主页 chain-mini-bar 复用(currentTabModules 切换数据源)
- ✅ 用户 verbatim "双击打开/跳转子图 tab 主页" 落地
- ⏳ F2-R1 文件正交并行 zombie 后 → 主 tab 双击 + FlowReadonlyDock 双击双入口齐
- ⏳ F4-R1 e2e blocked-by-F2-R1+F3-R1 · ClaudeC 0.5d → ADR-25 整体闭环 🏆
风险评估
| 风险 | 缓解 |
|---|---|
useChainMiniNodes.ts 现有签名是 Ref<ModuleInstance[]> 还是 ComputedRef 不确定 |
Step 0 强制 read composable 全文确认 · 若签名只接受 Ref 则用 toRef/computed 转换 · 若兼容则直传 currentTabModules |
| linkStore.subgraphDefs[defId].dirty 字段是否实际存在(ADR-08-R1 R1.5 落地深度未知) | Step 0 强制 grep "subgraphDefs.*dirty" 在 linkStore.ts · 若字段未实装则 dirty=false 占位(不阻塞本 fork)+ commit log 标注 followup 等 ADR-08-R1 R1.5 真值核查 |
| shellSlots.setDocTabs 是否支持 activeKey 参数(切 active) | Step 0 强制 read shell composable + grep "setDocTabs" 函数签名 · 若不支持需先扩 shell 框架(超出本 fork 范围 · 升级到 ADR · 暂用 setDocTabs(tabs) + 单独 watch 触发 setActiveDocTab) |
| sourceModules / nonSourceOrSingleSourceModules 派生在子图 tab 切换后行为不一致 | 子图 tab 内通常无 source(子图是模块组合 · 非完整链路)· 该 computed 自然返回空数组 · 子图 tab chain-mini-bar 不显示源组(预期行为) |
| 子图 tab 内单击 module 切换 activeInstanceId · 但浮窗不在子图 tab 显示(隔离不到位) | activeInstanceId 是全局 ref · 切回 main tab 时仍指向子图内 instanceId(可能错乱)· 需 watch activeDocTab 切 main 时清 activeInstanceId(但用户期望"切回 main 时恢复之前选择" · 需缓存机制)· 本 fork 范围:暂用 watch activeDocTab → activeInstanceId.value = ''(简化)· 缓存机制留 followup |
| 快速双击同 defId(防抖竞态) | useSubgraphTabs.openSubgraphTab idempotent 检查(同 defId 不重开)+ Vue 响应式批处理 · 不会出现重复 tab |
| F2-R1 同时 dispatched 但 API 未就绪 | F2-R1 prompt 已说明依赖关系 · 建议 ClaudeA 先做本 fork(P0 关键路径起点)再接 F2-R1 · 或 F2-R1 worker 先 stub import { openSubgraphTab } from './stub' 待本 fork commit 后改 import 路径 |
| ClaudeA 排队膨胀(F8 ADR-15 0.5d + F3-R1 1.5d + F2-R1 1.0d 共 3.0d 串行) | 用户已知排期 · F2-R1 文件正交可并行(理论)· 实际单 ClaudeA 串行约 3.0d · 排期可控 |
历史
| 时间 | 事件 | hash |
|---|---|---|
| 2026-06-18 17:15 | dispatched(用户 17:14 拍板方案 A 双连 dispatched F3-R1 + F2-R1 · F3-R1 优先级 P0 关键路径起点 1.5d · 暴露 useSubgraphTabs API 给 F2-R1 消费 · 同 ClaudeA 自管串行节奏) | — |