Skip to content

feat(core): inspect persisted conversation branches - #7185

Merged
doudouOUC merged 5 commits into
QwenLM:mainfrom
doudouOUC:agent/conversation-branch-inspection
Jul 19, 2026
Merged

feat(core): inspect persisted conversation branches#7185
doudouOUC merged 5 commits into
QwenLM:mainfrom
doudouOUC:agent/conversation-branch-inspection

Conversation

@doudouOUC

@doudouOUC doudouOUC commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Adds a read-only conversation topology inspector that identifies semantic leaves in persisted session trees, reports missing-parent, cycle, and conflicting-parent diagnostics, and produces deterministic summaries for each branch.

Normalizes metadata-only terminal records so custom-title and session-artifact writes do not appear as phantom conversation branches, while preserving recovery-significant system records such as rewind, compression, attribution, and file-history checkpoints.

Classifies rewind descendants and rewind siblings strictly as structural relationships. It does not select an active branch, mark a sibling abandoned, mutate the transcript, or change current resume, fork, daemon, ACP, or CLI behavior.

Why it's needed

Persisted sessions already form a tree through parent UUID links, but restart currently reconstructs one physically selected tail. Rewinds or concurrent writers can leave multiple valid sibling histories in the same JSONL file, so selecting only the physical tail can hide recoverable work. This PR provides the independent read-side foundation needed to enumerate and summarize those branches before a later write-side change adds exclusive ownership and durable branch selection.

Reviewer Test Plan

How to verify

  1. Run cd packages/core && npx vitest run src/utils/conversation-branches.test.ts src/utils/conversation-chain.test.ts; expect all 28 tests to pass.
  2. Confirm metadata-only title and artifact tails collapse without removing significant system checkpoints.
  3. Confirm both sides of a rewind fork remain visible: the rewind path is classified as a rewind descendant, while the sibling is classified structurally and is not discarded.
  4. Confirm the sanitized incident regression reduces three raw terminal records to two semantic conversation branches and that every returned leaf is accepted by the existing explicit reconstruction path.

Evidence (Before & After)

N/A — this PR adds a pure read-side core API and does not change user-visible behavior.

Tested on

OS Status
🍏 macOS
🪟 Windows N/A
🐧 Linux N/A

Environment (optional)

Node.js v22.22.3, local non-sandboxed unit tests.

Risk & Scope

  • Main risk or tradeoff: semantic leaf normalization intentionally uses a narrow allowlist; unknown and recovery-significant system records remain visible rather than being guessed away.
  • Not validated / out of scope: active-branch selection, writer ownership, persistence, and UI integration are intentionally deferred. Full-workspace typecheck is currently blocked by pre-existing Ink type export errors under the CLI selection code; core typecheck, lint, targeted tests, and the repository build pass.
  • Breaking changes / migration notes: none; the new API has no runtime consumer in this PR and existing behavior is unchanged.

Linked Issues

N/A

中文说明

本 PR 做了什么

新增纯读的会话拓扑检查能力,用于识别持久化会话树中的语义叶子、报告父节点缺失、父链循环和重复 UUID 父节点冲突,并为每个分支生成确定性摘要。

对仅包含元数据的终端记录进行归一化,避免自定义标题和会话 artifact 写入被误识别为虚假会话分支,同时保留 rewind、压缩、归因和文件历史检查点等对恢复有意义的 system 记录。

将 rewind descendant 和 rewind sibling 严格定义为结构关系。本 PR 不选择 active branch,不把 sibling 标记为 abandoned,不修改 transcript,也不改变现有 resume、fork、daemon、ACP 或 CLI 行为。

为什么需要

持久化会话已经通过父 UUID 链形成树结构,但重启恢复目前只重建一个按物理位置选出的尾部。rewind 或并发 writer 可能在同一 JSONL 文件中留下多个有效 sibling history,只选择物理尾部会隐藏仍可恢复的工作。本 PR 提供独立的读侧基础能力,用于枚举和摘要这些分支;后续写侧变更再补充独占所有权和持久化分支选择。

Reviewer 测试计划

如何验证

  1. 运行 cd packages/core && npx vitest run src/utils/conversation-branches.test.ts src/utils/conversation-chain.test.ts,预期 28 个测试全部通过。
  2. 确认仅包含标题和 artifact 的元数据尾部会折叠,同时不会移除重要的 system 检查点。
  3. 确认 rewind 分叉的两侧都保留:rewind 路径被分类为 rewind descendant,sibling 只做结构分类且不会被丢弃。
  4. 确认脱敏事故回归夹具会把三个原始 terminal 归一化为两个语义会话分支,并且每个返回叶子都能被现有显式重建链路接受。

证据(Before & After)

N/A——本 PR 新增纯读 core API,不改变用户可见行为。

测试平台

OS 状态
🍏 macOS
🪟 Windows N/A
🐧 Linux N/A

环境(可选)

Node.js v22.22.3,本地无 sandbox 单元测试。

风险与范围

  • 主要风险或取舍:语义叶子归一化刻意使用较窄的白名单;未知或对恢复有意义的 system 记录会保留,不做猜测性删除。
  • 未验证或范围外:active branch 选择、writer 所有权、持久化和 UI 接入均有意延后。全 workspace typecheck 当前被 CLI selection 代码中既有的 Ink 类型导出错误阻断;core typecheck、lint、定向测试和仓库 build 均通过。
  • 破坏性变更或迁移说明:无;新 API 在本 PR 中没有运行时消费者,现有行为不变。

关联 Issue

N/A

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@doudouOUC
doudouOUC force-pushed the agent/conversation-branch-inspection branch from 282f6ed to a632b8d Compare July 19, 2026 03:36
@github-actions

Copy link
Copy Markdown
Contributor

Please do not rebase or force-push to an active PR as it invalidates existing review comments. Note for future reference, the bots always squash all changes into a single commit automatically as part of the integration.

中文

请勿对活跃的 PR 执行 rebase 或 force-push,因为这会使已有的评审评论失效。另外,供日后参考:作为集成流程的一部分,机器人始终会自动将所有改动压缩(squash)为单个提交。

@doudouOUC
doudouOUC marked this pull request as ready for review July 19, 2026 04:23
@doudouOUC
doudouOUC enabled auto-merge July 19, 2026 04:23
@doudouOUC doudouOUC self-assigned this Jul 19, 2026
@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the PR, @doudouOUC!

Template looks good ✓ — all required sections present.

Problem: This is a feat PR adding read-side infrastructure for inspecting conversation branch topology in persisted session trees. No observed bug or reproduction — the PR is transparent that there is no runtime consumer and no user-visible behavior change. The motivation (rewinds or concurrent writers can leave multiple valid sibling histories that the current resume path hides) is plausible given prior rewind work (CHANGELOG: "preserve rewind parents after resume" #5923, "allow rewind after compressed history" #6358), but no linked issue demonstrates recoverable work is actually being lost today.

Direction: Session branch topology is a natural extension of the existing conversation-chain.ts and rewind handling. The CHANGELOG shows active investment in this area. However, this is infrastructure committed without the write-side consumer that would justify its existence. The design doc mentions a future write-side change for "exclusive ownership and durable branch selection," but that change isn't committed to here. Adding a public core API (exported from packages/core/src/index.ts) that nothing in the codebase calls is dead code risk.

Size: 548 production logic lines (conversation-branches.ts: 547, index.ts: 1), 498 test lines, 68 design doc lines. Touches core paths — flagging for maintainer awareness per the 500+ production line threshold on fork PRs.

Approach: The implementation is clean and well-structured. Since the last review, the author addressed all feedback: F1 (neutral-only roots exposed as phantom branches) is fixed and regression-tested, collapsed branch ordering follows physical terminal position, updatedAt uses the last collapsed physical leaf, and synthetic user filtering covers cron and mid_turn_user_message. wenshao independently verified the fix at runtime (5000-transcript fuzz, mutation testing, 32/32 runtime assertions). The scope feels right for a read-side foundation — no unnecessary abstractions, no scope creep.

The core question remains whether this infrastructure should land now as a foundation, or wait until the write-side consumer is ready so the API can be shaped by actual usage. Moving on to code review. 🔍

中文说明

感谢贡献,@doudouOUC

模板完整 ✓

问题: 这是一个 feat PR,为持久化会话树添加读侧拓扑检查基础设施。没有观测到的 bug 或复现——PR 明确说明没有运行时消费者,不改变用户可见行为。动机基于先前的 rewind 相关工作是合理的,但没有关联的 issue 证明当前存在实际的工作丢失。

方向: 会话分支拓扑是现有 conversation-chain.ts 和 rewind 处理的自然延伸。但这是在没有写侧消费者的情况下提交的基础设施。向 packages/core/src/index.ts 导出一个代码库中无处调用的公共 core API 存在死代码风险。

规模: 548 行生产逻辑代码,498 行测试,68 行设计文档。触及核心路径——按 500+ 生产行阈值标记需维护者关注。

方案: 实现干净且结构良好。自上次审查以来,作者解决了所有反馈:F1(纯元数据根被暴露为幽灵分支)已修复并有回归测试,折叠分支排序遵循物理终端位置,updatedAt 使用最后折叠的物理叶子,合成用户过滤覆盖了 cronmid_turn_user_message。wenshao 在运行时独立验证了修复(5000 条 transcript 模糊测试、变异测试、32/32 运行时断言)。

核心问题仍然是:这个基础设施应该现在作为基础落地,还是等到写侧消费者准备好后再根据实际使用来塑造 API。进入代码审查 🔍

Qwen Code · qwen3.7-max

Reviewed at 07290d8ce24b4c4f9c0bcf6deea715f6744c94a3 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Code Review

Independent proposal: To build a read-only branch inspector, I would: (1) index records by UUID with first-occurrence semantics, (2) build a children map, (3) find physical leaves, (4) collapse metadata-only terminals to their nearest non-metadata ancestor (omitting neutral-only chains with no conversation ancestor), (5) deduplicate the resulting antichain, (6) walk parent chains from each leaf to produce summaries, (7) classify branches relative to rewind records, (8) report structural diagnostics. This is what the PR does — the approach matches and I don't see a simpler path.

Findings: No correctness bugs, security issues, or regressions found. Changes since the last review pass:

  • F1 fix (0060e0d): collapseNeutralTail now returns null for neutral-only chains with no non-neutral ancestor, so metadata-only roots are omitted rather than reported as phantom ordinary branches. Regression tests cover all three neutral subtypes (custom_title, session_artifact_event, session_artifact_snapshot) as root records.
  • Collapsed ordering (119a2f6): Branch ordering now follows the physical terminal position of the collapsed leaf, not the semantic ancestor position. Test verifies this with out-of-order timestamps.
  • Synthetic user coverage (07290d8): The summary-hygiene regression now explicitly covers cron and mid_turn_user_message subtypes alongside notification and externalInputKind: 'notification'.

The underlying implementation remains clean: first-occurrence semantics matching conversation-chain.ts, narrow neutral-tail allowlist, DFS forest indexing for efficient ancestor checks, proper cycle detection, and deterministic text extraction that filters thoughts and tool arguments. No AGENTS.md violations (ESM, no any, proper type imports, kebab-case filename, tests collocated).

One latent concern noted by wenshao remains: the inspector indexes all records while buildOrderedUuidChain excludes artifact subtypes via isTranscriptConversationRecord, so the two use different record-eligibility predicates. The F1 fix handles the root case; a mid-chain artifact between two conversation records would still produce a branch the reconstruction path can't fully walk. This is theoretical (the current writer appends artifacts as tails, not mid-chain) and non-blocking given no runtime consumer.

Testing

Non-user-visible change (pure read-side API with no runtime consumer). tmux captures the test, typecheck, and lint execution as evidence.

github-runner@iZt4neqpisqczs6hsm7xn2Z:~/actions-runner-6/_work/qwen-code/qwen-code/.qwen/worktrees/triage$ cd packages/core && npx vitest run src/utils/conversation-branches.test.ts src/utils/conversation-chain.test.ts 2>&1

 RUN  v3.2.4 /home/github-runner/actions-runner-6/_work/qwen-code/qwen-code/.qwen/worktrees/triage/packages/core
      Coverage enabled with v8

 ✓ src/utils/conversation-chain.test.ts (8 tests) 4ms
 ✓ src/utils/conversation-branches.test.ts (20 tests) 8ms

 Test Files  2 passed (2)
      Tests  28 passed (28)
   Start at  23:41:10
   Duration  2.81s (transform 75ms, setup 31ms, collect 84ms, tests 12ms, environment 0ms, prepare 141ms)

github-runner@iZt4neqpisqczs6hsm7xn2Z:~/actions-runner-6/_work/qwen-code/qwen-code/.qwen/worktrees/triage/packages/core$ npx tsc --noEmit 2>&1; echo "TSC_EXIT=$?"
TSC_EXIT=0

github-runner@iZt4neqpisqczs6hsm7xn2Z:~/actions-runner-6/_work/qwen-code/qwen-code/.qwen/worktrees/triage/packages/core$ npx eslint packages/core/src/utils/conversation-branches.ts packages/core/src/utils/conversation-branches.test.ts 2>&1; echo "LINT_EXIT=$?"
LINT_EXIT=0

All 28 tests pass (20 new + 8 existing conversation-chain). Core typecheck clean. ESLint clean on both new files.

中文说明

代码审查

独立方案: 构建纯读分支检查器:(1) 按 UUID 索引记录(首次出现语义),(2) 构建子节点映射,(3) 找物理叶子,(4) 将仅元数据的终端记录折叠到最近非元数据祖先(省略无会话祖先的纯中性链),(5) 去重得到反链,(6) 从每个叶子走父链生成摘要,(7) 根据 rewind 记录分类分支,(8) 报告结构性诊断。PR 的做法与此一致。

发现: 未发现正确性 bug、安全问题或回归。自上次审查以来的变更:F1 修复(纯元数据根被省略而非报告为幽灵分支)、折叠排序遵循物理终端位置、合成用户过滤覆盖 cronmid_turn_user_message。无 AGENTS.md 违规。

wenshao 指出的一个潜在问题仍存在:inspector 索引全部记录,而 buildOrderedUuidChain 排除 artifact 子类型,两者使用不同的记录准入判定。F1 修复处理了根情况;链中间的 artifact 仍会产生重建路径无法完整遍历的分支。但这是理论性的(当前 writer 只在尾部追加 artifact),且无运行时消费者,不阻塞。

测试

非用户可见变更。tmux 捕获了测试、类型检查和 lint 执行作为证据。28 个测试全部通过。core typecheck 干净。ESLint 干净。

Qwen Code · qwen3.7-max

Reviewed at 07290d8ce24b4c4f9c0bcf6deea715f6744c94a3 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — clean code, thorough tests, all prior feedback addressed, F1 fixed and regression-tested. The remaining point is the product-direction question (no runtime consumer), which is a maintainer call, not a code-quality concern.

The code is solid. The implementation matches what I'd independently propose, and the three fix commits since the last review pass are all correct: neutral-only roots are now omitted (F1), collapsed branch ordering follows physical terminal position, and synthetic user filtering covers all known subtypes. 28 tests pass, typecheck and lint are clean.

wenshao's independent runtime verification adds significant confidence: 5000-transcript fuzz with the real writer semantics (active-tail advance, metadata tails, rewind re-rooting, concurrent-writer siblings), 32/32 runtime assertions against the compiled functions, and mutation testing proving the suite catches regressions. The cross-check invariant (every returned leaf reconstructs gap-free via buildOrderedUuidChain) held across all 5000 transcripts.

The reservation is unchanged from the last pass: this PR ships a 547-line public core API that nothing in the codebase calls. The PR is upfront about this. The author's rationale — validating a pure, deterministic read model independently of the write-side mutation and recovery policy — is reasonable, and the split does narrow the review surface. But the API surface is being designed without feedback from real usage, and if the write-side consumer lands differently than expected, the read-side may need reshaping.

Given the code quality, test coverage, wenshao's runtime verification, and the author's responsiveness to feedback, approving the code. The timing question (land now as a foundation vs. wait for the consumer) is for the maintainer — flagging for awareness per the 500+ production line threshold on fork PRs touching core paths.

中文说明

置信度:4/5 — 代码干净,测试充分,所有先前反馈已解决,F1 已修复并有回归测试。剩余一分是产品方向问题(无运行时消费者),这是维护者的判断,不是代码质量问题。

代码扎实。实现与我的独立方案一致,自上次审查以来的三个修复提交都正确:纯元数据根现在被省略(F1),折叠分支排序遵循物理终端位置,合成用户过滤覆盖所有已知子类型。28 个测试通过,typecheck 和 lint 干净。

wenshao 的独立运行时验证显著增强了信心:5000 条 transcript 模糊测试(使用真实 writer 语义),32/32 运行时断言,变异测试证明测试能捕获回归。交叉校验不变量在所有 5000 条 transcript 上成立。

顾虑与上次相同:本 PR 交付了一个 547 行的公共 core API,代码库中无处调用。作者的理由——独立于写侧变更验证纯确定性读模型——是合理的。但 API 表面设计没有来自真实使用的反馈。

鉴于代码质量、测试覆盖、wenshao 的运行时验证以及作者对反馈的积极响应,批准代码。时机问题(现在作为基础落地还是等消费者)由维护者判断——按 500+ 生产行阈值标记需维护者关注。

Qwen Code · qwen3.7-max

Reviewed at 07290d8ce24b4c4f9c0bcf6deea715f6744c94a3 · re-run with @qwen-code /triage

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Thanks — no code change from this review. The split is intentional: this PR validates a pure, deterministic read model against the existing persisted ChatRecord schema and a sanitized incident topology, while the later restart/selection change owns mutation and recovery policy. Keeping those concerns separate narrows the risk and lets the topology rules be reviewed without write-side behavior; the later consumer can adapt at the call boundary without changing persisted-data analysis. Maintainer guidance on whether to land this foundation separately is welcome.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — no blockers. Suggestions are inline.

— qwen3.7-max via Qwen Code /review

Comment thread packages/core/src/utils/conversation-branches.ts
Comment thread packages/core/src/utils/conversation-branches.ts
Comment thread packages/core/src/utils/conversation-branches.ts Outdated
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Addressed all three review suggestions in 69c2912aa9.

Feedback Action
tool_result count coverage Added a non-zero toolResult regression test.
Linear branch with no fork Added explicit branchPointUuid: null and first-user-summary assertions.
updatedAt after neutral-tail collapse Retained the last collapsed physical leaf for recency, added a regression test, and documented the contract.

Verification: full repository build passed; full workspace typecheck passed; 26 targeted conversation branch/chain tests passed.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Suggestions are inline.

— qwen3.7-max via Qwen Code /review

Comment thread packages/core/src/utils/conversation-branches.ts
@wenshao

wenshao commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Local runtime verification — PR #7185

I built and ran this PR locally against the real compiled code (not just vitest) as a merge reference. Net: the PR does what it claims — behavior is correct for every realistic transcript, and the tests have real teeth. One latent edge case is worth an optional follow‑up (F1 below); it is non‑blocking because nothing consumes this API yet.

PR 7185 verification summary

How I verified

  • Checked out PR head 69c2912aa in an isolated git worktree, then esbuild‑bundled conversation-branches.ts + conversation-chain.ts into real ESM and drove the compiled inspectConversationBranches / buildOrderedUuidChain directly (inspectConversationBranches only has a type‑only import, so the bundle is faithful runtime JS).
  • Env note: a full tsc --build OOMs in my sandbox, but vitest transpiles and runs the real TS source fine — type + behavior are exercised end‑to‑end. Not a PR issue.

Results

Check Result
Unit suite (both files) 26 passed (branches 18 + chain 8)
Runtime harness on the real compiled fn 32 / 32 assertions
Fuzz — 5000 faithful append‑only transcripts ✅ invariant holds (10,390 branches · 2,873 multi‑branch · 0 crashes)
Mutation testing ✅ suite catches regressions (3 / 5 / 1 fails)

Confirmed at runtime, beyond the test file:

  • Metadata‑only title/artifact tails collapse; significant system checkpoints (rewind / chat_compression / attribution_snapshot / file_history_snapshot / slash_command) stay visible.
  • Rewind fork keeps both sides — rewind-descendant vs rewind-sibling labelled correctly, neither discarded.
  • Sanitized‑incident: 3 raw terminals → 2 semantic branches.
  • Diagnostics: missing-parent · parent-cycle · conflicting-parent.
  • Summary hygiene: assistant thought parts and tool‑call args never leak into summaries; synthetic prompts (notification / cron / mid_turn_user_message / external‑input) are filtered; text is whitespace‑normalized and truncated at 200.
  • Cross‑check invariant — over 5000 transcripts generated with the real writer semantics (active‑tail advance, updateActiveTail:false metadata tails, rewind re‑rooting, concurrent‑writer siblings), every returned leaf reconstructs gap‑free via buildOrderedUuidChain and ends at that leaf, and the branch set is always an antichain.

Mutation testing (proves the suite isn't vacuous):

Mutation of conversation-branches.ts Tests failed
findBranchPoint > 1>= 1 3
collapseNeutralTail (disable neutral collapse) 5
updatedAt physicalLeaf → semanticLeaf (undo the review‑feedback fix) 1
baseline restored 18 passed

Minor: the reviewer test‑plan step 1 says "expect all 23 tests"; head actually has 26 (the review‑feedback commit added 3). Harmless — just bump the number.

Finding F1 — metadata‑only record with no non‑neutral ancestor → phantom "ordinary" branch (latent, non‑blocking)

collapseNeutralTail only collapses a neutral tail into a non‑neutral ancestor. When a neutral record has none — e.g. it is the session's first record (parentUuid = null) — it is returned as the leaf: the exact "phantom conversation branch" the PR says it prevents. For the two artifact subtypes it is worse: buildOrderedUuidChain cannot reconstruct that leaf (selectTranscriptLeaf rejects artifact records) → empty chain, which breaks the "every returned leaf is accepted by the existing reconstruction path" property.

Root record subtype inspectConversationBranches buildOrderedUuidChain
session_artifact_event phantom ordinary branch ❌ empty chain — cannot reconstruct
session_artifact_snapshot phantom ordinary branch ❌ empty chain — cannot reconstruct
custom_title phantom ordinary branch ✅ reconstructs ["meta"]

Minimal repro:

const recs = [{ uuid:'meta', parentUuid:null, sessionId:'s', timestamp:'2026-01-01T00:00:00.000Z',
                type:'system', subtype:'session_artifact_event', cwd:'/w', version:'0' }];
inspectConversationBranches(recs).branches;
//   => [{ leafUuid:'meta', classification:'ordinary', ... }]      // phantom branch
buildOrderedUuidChain(recs, { leafUuid:'meta', detectGaps:true }); // => { uuids:[], gaps:[] }  // cannot reconstruct

Reachability: the only writer that emits artifact records is ACP session/persist-artifact (acpAgent.ts); a client persisting an artifact before the first prompt would produce a root artifact. I did not prove that end‑to‑end, and there is no runtime consumer of inspectConversationBranches yet, so this stays latent. Root cause: the inspector indexes all records while buildOrderedUuidChain excludes artifact subtypes via isTranscriptConversationRecord, so the two use different record‑eligibility predicates. Suggested guard for the future write side: drop metadata‑only branches that lack a conversation ancestor, or share one eligibility predicate between the inspector and the reconstruction path so they can never disagree.

Recommendation

👍 Good to merge as the read‑side foundation. Treat F1 as a small follow‑up (or a cheap guard) before anything starts selecting/consuming these leaves.

🇨🇳 中文说明

本地真实构建验证 — PR #7185

作为合并参考,我在本地用真实编译产物(不仅是 vitest)构建并运行了本 PR。结论:PR 行为与描述一致 —— 对所有真实 transcript 都正确,且测试有实际约束力。 有一个潜在边界情况值得作为可选后续处理(下方 F1),但不阻塞合并,因为目前还没有任何运行时消费该 API。

验证方式

  • 在独立 git worktree 检出 PR head 69c2912aa,用 esbuild 把 conversation-branches.ts + conversation-chain.ts 打包为真实 ESM,直接驱动编译后的 inspectConversationBranches / buildOrderedUuidChaininspectConversationBranches 只有 type‑only import,打包产物即忠实的运行时 JS)。
  • 环境说明:完整 tsc --build 在我的 sandbox 里 OOM,但 vitest 能正常转译并运行真实 TS 源码,类型与行为已端到端覆盖 —— 与 PR 无关。

结果

检查项 结果
单测(两个文件) 26 通过(branches 18 + chain 8)
真实编译产物 runtime 夹具 32 / 32 断言
模糊测试 —— 5000 个忠实 append‑only transcript ✅ 不变量成立(10,390 分支 · 2,873 多分支 · 0 崩溃)
变异测试 ✅ 测试能抓到回归(3 / 5 / 1 失败)

在测试文件之外的运行时确认:

  • 仅含元数据的 title/artifact 尾部会折叠;重要 system 检查点(rewind / chat_compression / attribution_snapshot / file_history_snapshot / slash_command)保留可见。
  • rewind 分叉两侧都保留 —— rewind-descendantrewind-sibling 分类正确,两侧都不被丢弃。
  • 脱敏事故场景:3 个原始 terminal → 2 个语义分支。
  • 诊断:missing-parent · parent-cycle · conflicting-parent
  • 摘要卫生:assistant 的 thought 片段与 tool‑call 参数不会泄漏进摘要;合成 prompt(notification / cron / mid_turn_user_message / 外部输入)被过滤;文本做空白归一并在 200 处截断。
  • 交叉校验不变量 —— 在 5000 个按真实 writer 语义生成的 transcript(active‑tail 前进、updateActiveTail:false 元数据尾、rewind 重新定根、并发 writer sibling)上,每个返回的叶子都能通过 buildOrderedUuidChain 无 gap 重建并终止于该叶子,且分支集合始终是 antichain。

变异测试(证明测试非空洞):

conversation-branches.ts 的变异 失败测试数
findBranchPoint > 1>= 1 3
collapseNeutralTail(关闭中性折叠) 5
updatedAt physicalLeaf → semanticLeaf(回退 review 修复) 1
恢复 baseline 18 通过

小问题: reviewer 测试计划第 1 步写的是「23 个测试」,但 head 实际是 26 个(review‑feedback commit 新增了 3 个)。无害,更新下数字即可。

发现 F1 —— 无非中性祖先的纯元数据记录 → 幽灵 "ordinary" 分支(潜在,不阻塞)

collapseNeutralTail 只会把中性尾部折叠到非中性祖先。当中性记录没有这样的祖先时 —— 例如它是会话的第一条记录(parentUuid = null)—— 它会被当作叶子返回,正是 PR 声称要避免的「幽灵会话分支」。对两个 artifact 子类型更糟:buildOrderedUuidChain 无法重建该叶子(selectTranscriptLeaf 拒绝 artifact 记录)→ 空链,破坏了「每个返回叶子都能被现有重建链路接受」这一性质。

根记录子类型 inspectConversationBranches buildOrderedUuidChain
session_artifact_event 幽灵 ordinary 分支 ❌ 空链 —— 无法重建
session_artifact_snapshot 幽灵 ordinary 分支 ❌ 空链 —— 无法重建
custom_title 幽灵 ordinary 分支 ✅ 重建为 ["meta"]

最小复现:

const recs = [{ uuid:'meta', parentUuid:null, sessionId:'s', timestamp:'2026-01-01T00:00:00.000Z',
                type:'system', subtype:'session_artifact_event', cwd:'/w', version:'0' }];
inspectConversationBranches(recs).branches;
//   => [{ leafUuid:'meta', classification:'ordinary', ... }]      // 幽灵分支
buildOrderedUuidChain(recs, { leafUuid:'meta', detectGaps:true }); // => { uuids:[], gaps:[] }  // 无法重建

可达性: 唯一会写 artifact 记录的 writer 是 ACP session/persist-artifactacpAgent.ts);若客户端在首个 prompt 之前持久化 artifact,就会产生根 artifact。我没有端到端证明这一点,且目前没有任何运行时消费 inspectConversationBranches,所以仅为潜在问题。根因:inspector 索引全部记录,而 buildOrderedUuidChain 通过 isTranscriptConversationRecord 排除 artifact 子类型,两者使用了不同的记录准入判定。后续写侧的建议护栏:丢弃缺少会话祖先的纯元数据分支,或让 inspector 与重建链路共用同一准入判定,避免两者产生分歧。

建议

👍 作为读侧基础可以合并。把 F1 当作小的后续项(或加个便宜的护栏)即可 —— 在任何逻辑开始选择/消费这些叶子之前处理。


Verified locally on Linux, Node v22.22.2, against PR head 69c2912aa. Harness drives the real compiled functions; 5000‑transcript fuzz uses the real chatRecordingService active‑tail/rewind semantics.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — no blockers. Suggestions are inline.

— qwen3.8-max-preview via Qwen Code /review

Comment thread packages/core/src/utils/conversation-branches.ts
Comment thread packages/core/src/utils/conversation-branches.ts Outdated
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

@wenshao Thanks for the thorough runtime verification. I independently reproduced F1 on the current head and confirmed that an ACP artifact can be persisted before the first conversation record.

Item Action
F1: metadata-only root exposed as an ordinary branch Fixed in 0060e0de38. Neutral terminal chains with no known non-neutral conversation ancestor are now omitted.
Regression coverage Added coverage for root custom_title, session_artifact_event, and session_artifact_snapshot records.
Reviewer test-plan count Updated from 23 to 27 in both English and Chinese.

Verification: full repository build passed, workspace typecheck passed, and the targeted branch/chain suites passed 27/27.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Review follow-up summary for 119a2f673b:

Feedback Action
Neutral-only artifact/title roots could be reported as unreconstructable branches Fixed in 0060e0de38; such roots are omitted and covered by regression tests.
Collapsed branches were sorted by semantic ancestor position Fixed in 119a2f673b; ordering now follows the actual collapsed physical terminal position.

Verification: full repository build passed, workspace typecheck passed, and branch/chain unit suites passed 28/28. No feedback was rejected or deferred.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — no blockers. Suggestions are inline.

— qwen3.8-max-preview via Qwen Code /review

Comment thread packages/core/src/utils/conversation-branches.test.ts
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Review follow-up summary for 07290d8ce2:

Feedback Action
Synthetic user filtering lacked explicit coverage for cron and mid_turn_user_message Fixed by extending the existing summary-hygiene regression to cover both subtypes in the same linear branch.

Verification: full repository build passed, workspace typecheck passed, and branch/chain unit suites passed 28/28. No feedback was rejected or deferred.

@wenshao

wenshao commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

@doudouOUC
doudouOUC added this pull request to the merge queue Jul 19, 2026
Merged via the queue into QwenLM:main with commit 2998e6f Jul 19, 2026
62 of 63 checks passed
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Released in v0.20.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants