Repository navigation
fix(flowchat): recover aux pane tabs whose child session is missing - #3375
Open
czs914893385 wants to merge 2 commits into
Open
czs914893385 wants to merge 2 commits into
czs914893385 wants to merge 2 commits into
Conversation
…recovery
Brings the aux-pane child-session recovery work up to current main.
Conflict (only one file, src/web-ui/src/app/components/panels/base/FlexiblePanel.tsx,
case 'btw-session'): both sides appended a prop to <BtwSessionPanel>. Kept both - upstream's
resizeSuspended={resizeSuspended} (perf(web-ui) 6f2ec2b, the merged pane-resize PR GCWing#3371)
and this branch's four child-identity props (sessionKind / sessionTitle / parentToolCallId /
subagentType). No other file needed manual work; the pane-resize plumbing is untouched.
Contributor
Author
|
Refreshing the PR state: closing and reopening so GitHub recomputes the base it diffs against (it was showing the changes against a stale base). No code change — the branch is simply rebased onto current 刷新一下 PR 状态:先关后开,让 GitHub 重算 diff 的基线(之前对着一个陈旧基线在算)。代码没有变化,分支已基于当前 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Symptom
The auxiliary pane (the right-hand pane of the session scene) can hold a side-thread tab
(
btw-session) whose child session record is gone. Instead of recovering, the pane shows thegeneric empty copy —
暂未打开子会话/No side thread open— forever: the tab stays mounted,clicking it does nothing, and the only way out is to find and close the tab elsewhere in the
canvas. The state is reachable from ordinary user actions: deleting, archiving, or rolling back
the child session, or resetting the workspace, while the tab is still open.
Root cause
Confirmed, with the pre-fix line numbers on
main(aed8c001):buildBtwSessionPanelContent(
src/web-ui/src/flow_chat/services/btwSessionPane.ts:104) storeschildSessionId/parentSessionIdin the panel data and a duplicate key in the tab metadata(
btwSessionPane.ts:123). The child's identity — its kind, title,parentToolCallId,subagentType— is not part of the tab.record and loads its history is
ensureBtwSessionAvailableInternal(
btwSessionPane.ts:218), reached fromopenBtwSessionInAuxPane(btwSessionPane.ts:292).Nothing re-runs it later, so a record that disappears after the tab was opened is never
noticed.
void loadBtwSessionHistory({...}).catch(() => undefined)(btwSessionPane.ts:280-284) — andthe fire-and-forget entry point
ensureBtwSessionAvailable(btwSessionPane.ts:288) returnsnothing a caller could observe.
FlowChatStore.deleteSession(
FlowChatStore.ts:5368),removeSession(:5418) andremoveSessionsForWorkspace(
:5520) drop the record from the store; canvas tabs are unaffected.BtwSessionPanelContenthitsif (!childSessionId || !childSession)(BtwSessionPanel.tsx:1051) and rendersbtw.emptyThreadLabelwithdata-openbitfun-view="empty"(
BtwSessionPanel.tsx:1053,:1055). There is no retry, no close action, and no distinctionbetween "no tab is open" and "this tab's session is gone".
The fix
btwSessionHydration.ts(new) — a pure tracker that allows exactly one load attempt perabsence, keyed by the child id. It re-arms when the child id changes, when the record comes
back, or for an explicit user retry; it refuses to arm a pane that cannot start an attempt
(the MiniApp customize chat embeds this panel with a child id but no parent, so it must
explain the absence instead of loading forever), and it keeps an attempt that finished without
a usable record in the actionable state rather than looping.
btwSessionPane.ts—ensureBtwSessionAvailableAndSettleexposes the history-load promiseso a caller can render the failure;
openBtwSessionInAuxPanerecords the child identity in thetab (
BtwSessionPanelChildIdentity) and keeps its fire-and-forget semantics;hasBtwSessionAuxPaneTabtells an embedded pane whether a tab actually owns the child.BtwSessionPanel.tsx— a wrapper (useBtwSessionHydration) owns the recovery, so a tabthat is activated later still heals while an inactive tab keeps the folder's
"no subscription, no work" contract. A mounted child tab with no record now renders a loading
state (
btw.loadingThreadLabel) and, on failure, an actionable state(
btw.unavailableThreadLabel) with重新加载(only when this pane can start an attempt) and关闭标签(only when a tab really owns the child). Healing passes the recorded identity, so ahealed subagent child is recreated as
sessionKind: 'subagent'with its title,parentToolCallIdandsubagentTypeinstead of degrading into a plain side thread. Recoverynever hides a transcript that is already renderable.
btw.*keys inen-US,zh-CN,zh-TW.viewfacet gainsloadingandunavailable.How to reproduce
Before this PR (on
main):(a
btw-sessiontab).mounted.
暂未打开子会话, the tab does nothing when clicked, and there is noretry and no close action in the pane.
After this PR: the pane shows a loading state, the child record is recreated from the identity
the tab carries and its history reloads; if the load fails the pane shows
子会话加载失败with
重新加载, plus关闭标签when a canvas tab owns that child.BtwSessionPanel.missingSession.test.tsx(13 tests) and the new
btwSessionHydration.test.tscover the once-only guard, the retry, theclose-tab affordance, the restored subagent identity, and the "do not hide a renderable
transcript" rule;
btwSessionPane.navigation.test.tsandopenBtwSession.test.tscover theproduction wiring end to end.
Verification
Everything below ran on a fresh single-branch
git cloneofmain(
aed8c001a72c051ca74607e7eed8d84f6c2aee85) with only these 12 files overlaid — nopane-resize file is present — using node
v22.23.2and pnpm10.15.0.node scripts/i18n-audit.mjspnpm run lint:webpnpm --dir src/web-ui exec tsc --noEmit -p tsconfig.jsonpnpm --dir src/web-ui exec vitest run src/flow_chat/components/btw src/flow_chat/servicespnpm --dir src/web-ui exec vitest run src/app/components/panels src/app/scenes/session src/app/scenes/miniappspnpm --dir src/web-ui run test:run(default--maxWorkers=50%)window is not definedraised whileuseFlowChatVirtualizer.aim.test.tsxtore down). A control run of the same command on pristinemainat the same worker count also fails (1 failed file / 1 failed test, 970 passed), so this is environmental, not caused by this change.pnpm --dir src/web-ui exec vitest run --maxWorkers=4pnpm run build:webnode scripts/audit-appearance-contracts.mjstscneeds the generated api barrel (@/generated/api), which is produced bypnpm --dir src/web-ui run gen:typesand is gitignored; it was generated before type-checking.CI note: the
Frontend Buildjob stops atCheck repository hygiene, whoseproduct:identity:auditstep flagsdesign-system/apps/design-lab/src/pages/OverviewPage.tsx(retired product name). That failure is inherited from
main, not introduced here — the samecommand fails with the identical output on a pristine
maincheckout, and the CI run formainat this PR's base commit (
aed8c001) is already red. The other jobs are green(
Repository Object Sizes;Shell Deploy Scripts,Build Impact,Rust / CLI Validation).Relationship to the in-flight pane-resize PR #3371
BtwSessionPanel.tsxandFlexiblePanel.tsxare also modified by the in-flightperf(web-ui): stop re-measuring transcripts while a pane divider is dragged(#3371). This PRwas split out of that work to be independently reviewable and shippable:
resizeSuspended/measurementSuspendedline; the only
resizeSuspendedleft in these files is the pre-existingTerminalTabPanelprop that is already on
main);mainwithout any pane-resizefile, as tabulated above;
main+ perf(web-ui): stop re-measuring transcripts while a pane divider is dragged #3371's patch, then this branchmerged into it.
BtwSessionPanel.tsxauto-merges.FlexiblePanel.tsxhas exactly onetrivial conflict hunk, where both sides add props after the same line
(
displayTitle={content.data?.displayTitle}: ours adds the four identity props, perf(web-ui): stop re-measuring transcripts while a pane divider is dragged #3371 addsresizeSuspended={resizeSuspended}). Keeping both sides resolves it; the resolved merge leavesno conflict markers, touches exactly these 12 files relative to
main+ perf(web-ui): stop re-measuring transcripts while a pane divider is dragged #3371, and the mergedstate type-checks (exit 0) and passes both focused suites (59 files / 726 tests and
39 files / 210 tests).
Accepted residual
A heal that resolves while leaving a record with zero turns still renders the generic
empty-thread copy for that child. The tracker treats an absent record as the recoverable state,
and a deliberately kept empty shell record is not treated as a recovery target. This is recorded
as an accepted residual rather than papered over.
中文说明
问题现象
辅助面板(会话场景右侧面板)里可能挂着一个子会话标签页(
btw-session),而它对应的子会话记录已经不存在。此时面板会一直显示「暂未打开子会话」这个通用空状态:标签页还在,点击它没有任何反应,用户只能在画布别处找到并关闭标签页。常见触发方式:在标签页仍然打开时删除/归档/回滚该子会话,或重置所在工作区。根因(已确认,行号基于修复前的
main)buildBtwSessionPanelContent(btwSessionPane.ts:104)只在面板数据里保存childSessionId/parentSessionId,标签页元数据里只有去重键(:123),子会话的身份(类型、标题、parentToolCallId、subagentType)没有被保存。ensureBtwSessionAvailableInternal(:218),由openBtwSessionInAuxPane(:292)调用;之后没有任何逻辑会重新检查。void loadBtwSessionHistory({...}).catch(() => undefined)(:280-284)丢弃加载结果,而ensureBtwSessionAvailable(:288)也不返回任何可观察的东西。FlowChatStore.deleteSession(FlowChatStore.ts:5368)、removeSession(:5418)、removeSessionsForWorkspace(:5520)会删除记录,而画布标签页不受影响。if (!childSessionId || !childSession)(BtwSessionPanel.tsx:1051),渲染btw.emptyThreadLabel(:1053、:1055),既没有重试也没有关闭入口,也不区分「没有打开任何标签页」和「这个标签页的会话丢了」。修复内容
btwSessionHydration.ts:按子会话 id 记录状态的纯逻辑追踪器,每次「记录缺失」只允许一次加载尝试;id 变化、记录恢复或用户显式重试时才重新武装;对于无法发起加载的面板(例如只带子会话 id、没有父会话的 MiniApp 内嵌面板)不武装尝试,而是直接给出可解释的状态。btwSessionPane.ts:新增ensureBtwSessionAvailableAndSettle返回加载 Promise 以便面板渲染失败;openBtwSessionInAuxPane把子会话身份写进标签页(BtwSessionPanelChildIdentity),同时保持原有的「即发即忘」语义;新增hasBtwSessionAuxPaneTab判断当前子会话是否真的有面板标签页。BtwSessionPanel.tsx:由外层组件(useBtwSessionHydration)负责自愈,未激活的标签页仍然保持「不订阅、不做功」的约定,被激活后立即自愈。缺失记录的标签页先显示加载状态(btw.loadingThreadLabel),失败后显示可操作状态(btw.unavailableThreadLabel),按面板能力给出「重新加载」(可以发起加载时)和「关闭标签」(确实有标签页时)。自愈时会带上标签页记录的身份,使子代理子会话恢复为sessionKind: 'subagent'并保留标题、parentToolCallId、subagentType,而不会退化成普通子会话。已经可以渲染的会话内容不会被自愈状态遮挡。en-US、zh-CN、zh-TW各新增 4 个btw.*键。viewfacet 增加loading、unavailable。验证
全部在
main(aed8c001a72c051ca74607e7eed8d84f6c2aee85)的全新单分支克隆上、仅覆盖本 PR 的 12 个文件(不含任何 pane-resize 文件)完成,使用 nodev22.23.2与 pnpm10.15.0:i18n-audit0 警告;lint:web0 error / 10 个既有 warning;tsc --noEmit通过;flow_chat/components/btw+flow_chat/services59 文件 / 725 测试通过;app/components/panels+app/scenes/session+app/scenes/miniapps39 文件 / 210 测试通过;test:run(默认--maxWorkers=50%)测试全绿但在 jsdom 拆卸阶段出现 1 个环境性未捕获异常(原始main对照组同样失败),改用--maxWorkers=4后 exit 0(973 文件 / 10786 测试);build:web与audit-appearance-contracts均通过。补充说明:本 PR 的 CI
Frontend Build任务停在Check repository hygiene,其product:identity:audit步骤报design-system/apps/design-lab/src/pages/OverviewPage.tsx(retired product name)。该失败继承自main:在干净的main上执行同一命令会得到完全相同的报错,且本 PR 基线提交(aed8c001)在main上的 CI 本来就是红色。其余任务均通过(Repository Object Sizes;Shell Deploy Scripts、Build Impact、Rust / CLI Validation)。与在途 PR #3371 的关系
BtwSessionPanel.tsx与FlexiblePanel.tsx同时被在途的面板拖拽性能 PR #3371 修改。本 PR 已把自愈修复拆分出来独立提交:diff 中不含任何 #3371 的改动(没有新增resizeSuspended/measurementSuspended),可以在干净的main上独立编译与通过测试。未来合并在临时克隆中演练过:main加上 #3371 的补丁后再合并本分支,BtwSessionPanel.tsx自动合并成功,FlexiblePanel.tsx只有一处纯文本冲突(两边都在displayTitle={content.data?.displayTitle}之后插入 props),两边都保留即可解决;解决后的合并结果没有冲突标记,相对main+ #3371 只改动这 12 个文件,tsc与两个聚焦测试套件依然通过。已知残留
自愈「成功返回」但记录里没有任何轮次时,该子会话仍然显示通用的空会话文案。追踪器把「记录缺失」当作可恢复状态,而故意保留的空壳记录不视为恢复目标。这一点作为已接受的残留记录在案。