Skip to content

fix(desktop): archive child sessions when parent is deleted - #3151

Merged
Astro-Han merged 4 commits into
apache:mainfrom
YayoiNanoka:agent/archive-orphaned-subagent-sessions
Aug 18, 2026
Merged

fix(desktop): archive child sessions when parent is deleted#3151
Astro-Han merged 4 commits into
apache:mainfrom
YayoiNanoka:agent/archive-orphaned-subagent-sessions

Conversation

@YayoiNanoka

@YayoiNanoka YayoiNanoka commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Problem

Deleting a parent Session retires its revision family, but leaves ordinary linked subagent Sessions active. Once the parent is gone, the linked-session projection deliberately fails open and promotes those children to top-level rows. Deleting one swarm parent can therefore add up to 32 headless subagent rows to the main task list.

The children cannot be cascade-deleted because each Session owns its own transcript, artifacts, and outputs. Existing orphaned children also need the same treatment as newly created ones.

Solution

  • Resolve direct ordinary child revision families from the existing child-side subagentParent.parentSessionId relation, keeping that relation as the single source of truth.
  • Admit the parent removal and child archival as one stable retirement plan. A busy child blocks the operation instead of being force-retired.
  • Commit parent removal and child archival atomically in SQLite, so a failure cannot leave a partially retired family.
  • Keep already archived children in the stable admission plan without retiring them again, preserving their original archive timestamps and metadata versions.
  • Reconcile pre-existing ordinary orphan Sessions to archived state during Runtime Host recovery.
  • Keep orphaned children visible in the archive view and label them as Parent task deleted / 原父任务已删除.
  • Read the orphan label from the Runtime Host's projected subagent relation as well as local Session metadata.

Parent archival remains unchanged and does not archive ordinary children. Agent Graph operator retirement semantics also remain unchanged.

Validation

  • Storage metadata suite: 42 tests passed, including atomic rollback and preservation of already archived children.
  • Runtime Host retirement suite: 22 tests passed, including one child, a 32-child swarm, busy-child blocking, startup recovery, and admission guarding for already archived children.
  • Desktop archived-task projection suite: 8 tests passed, including orphan labelling.
  • Electron E2E: deleting a parent through the UI keeps its child out of the main task list and shows it in Archived tasks with the orphan label.
  • Storage, Runtime Host, and Desktop TypeScript checks passed.

Closes #1467

点击展开中文

问题

删除父 Session 时,目前只会清理它的 revision family,普通 subagent Session 仍然保持活跃。父 Session 消失后,关联 Session 的投影逻辑会按照既有的 fail-open 规则,把这些子 Session 提升为顶层任务。因此,删除一个 swarm 父任务后,主任务列表中最多可能突然出现 32 个 headless subagent。

这些子 Session 不能级联删除,因为每个 Session 都拥有独立的对话记录、产物和输出。修改前已经存在的孤儿 Session,也应该与新产生的孤儿得到相同处理。

解决方案

  • 继续以子 Session 上的 subagentParent.parentSessionId 作为唯一关系来源,反向找出父 Session 的直接普通子 Session 及其 revision family。
  • 将父 Session 删除与子 Session 归档纳入同一个稳定的 retirement plan;如果子 Session 正忙,则阻止本次删除,而不是强制归档。
  • 在 SQLite 中原子提交父 Session 删除和子 Session 归档,避免失败后出现只完成一半的状态。
  • 已归档的子 Session 仍参与稳定计划和 admission 保护,但不重复执行归档,从而保留原始归档时间和 metadata version。
  • Runtime Host 启动恢复时,将修改前已经存在的普通孤儿 Session 归档。
  • 孤儿子 Session 继续显示在归档页面,并标记为“原父任务已删除”。
  • 归档页面同时从 Runtime Host 投影后的 subagent 关系和本地 Session 元数据中读取孤儿标记。

父 Session 的普通归档行为保持不变,不会联动归档普通子 Session;Agent Graph operator 的原有清理语义也保持不变。

验证

  • Storage metadata 测试:42 项通过,包括原子回滚和已归档子 Session 状态保持。
  • Runtime Host retirement 测试:22 项通过,包括单个子 Session、32 个子 Session、忙碌子 Session 阻止删除、启动恢复,以及已归档子 Session 的 admission 保护。
  • Desktop 归档任务投影测试:8 项通过,包括孤儿标记。
  • Electron E2E:通过界面删除父任务后,子任务不会进入主任务列表,并会在已归档任务中显示“原父任务已删除”。
  • Storage、Runtime Host 和 Desktop 的 TypeScript 检查通过。

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2d7bac0e-772a-4ec4-a12a-0cf6e961cdc5

📥 Commits

Reviewing files that changed from the base of the PR and between 412bb53 and da27515.

📒 Files selected for processing (9)
  • apps/desktop/src/main/__tests__/task-catalog-rows.test.ts
  • apps/desktop/src/renderer/settings/task-catalog-rows.ts
  • packages/runtime-host/src/__tests__/session-retirement-coordinator.test.ts
  • packages/runtime-host/src/__tests__/session-revision-two-client-uds.test.ts
  • packages/runtime-host/src/server/session-retirement-coordinator.ts
  • packages/storage/src/__tests__/sqlite-session-metadata-store.test.ts
  • packages/storage/src/execution-stores.ts
  • packages/storage/src/session-store.ts
  • packages/storage/src/sqlite-session-metadata-store.ts
💤 Files with no reviewable changes (2)
  • apps/desktop/src/renderer/settings/task-catalog-rows.ts
  • apps/desktop/src/main/tests/task-catalog-rows.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/storage/src/execution-stores.ts
  • packages/storage/src/session-store.ts
  • packages/storage/src/sqlite-session-metadata-store.ts
  • packages/runtime-host/src/server/session-retirement-coordinator.ts

Included review availability: Your plan includes up to 3 reviews per rolling hour; 2 remain after this review.


📝 Walkthrough

Problem solved

When a parent Session is deleted, ordinary child subagent Sessions are archived instead of remaining as top-level tasks or being deleted. Archived children remain accessible and show Parent task deleted or 原父任务已删除.

The change also:

  • Archives pre-existing orphaned subagents during Runtime Host recovery.
  • Blocks parent deletion when a child is busy.
  • Preserves child transcripts, artifacts, outputs, and existing archive metadata.
  • Preserves Agent Graph operator retirement behavior.
  • Keeps unrelated orphaned and cyclic relations visible through fail-open behavior.

Design and source of truth

The PR extends the existing Session retirement and SQLite metadata paths. It does not create a parallel deletion or archival path.

removeSessionsVersioned now accepts an optional archive set. SQLite validates both sets and updates them in one transaction. Runtime Host resolves child revision families, checks busy state, and passes removal and archive plans through the existing storage facade. Desktop projection logic derives orphan status from the known Session set.

Scope and complexity

This is the smallest coherent solution shown by the diff. Atomic storage changes prevent partial parent removal. Runtime Host planning resolves child families, rejects busy children, and recovers pre-existing orphans. Desktop classification and locale changes support correct archive presentation.

The PR adds archive-plan handling, orphan recovery branches, archive labels, fixture options, and focused regression tests. These additions are necessary for atomicity, recovery, admission protection, and user-visible behavior. No obvious implementation or test code can be removed without weakening coverage.

Complexity delta

  • Authorities: No new retirement authority. Existing Session retirement and SQLite metadata remain authoritative.
  • States: The existing archived state gains explicit handling for orphaned child Sessions.
  • Branches: Runtime Host adds child-family planning, busy-child rejection, archive preservation, and recovery paths.
  • Configuration: E2E fixtures add an optional parentRemovalSessions setup path.
  • Public surface: removeSessionsVersioned and removeVersioned gain optional archive-session parameters.
  • Test maintenance: The PR adds storage, Runtime Host, desktop projection, and Electron E2E coverage.

The total maintenance complexity increases, but the increase is justified by the required atomic behavior and recovery guarantees. Coordination with #3074 remains a non-blocking integration risk because that change may redefine archive authority around isArchived.

Validation

Coverage includes:

  • SQLite atomic removal and archival, version conflicts, rollback, and archived-child metadata preservation.
  • Runtime Host child retirement, busy-child rejection, recovery, race handling, and larger child families.
  • Desktop orphan classification and archive labels.
  • Electron E2E parent deletion behavior.
  • Revision-authority conflict checks.
  • TypeScript checks, as reported in the PR objectives.

The final status of required checks is unverified because direct check results were not provided.

Review-relevant risks

  • The active task list and archive view now show deleted-parent children differently. The locale catalogs add user-visible text. Material user-interface changes require independent human review under repository policy.
  • SessionAuthorityStore.removeSessionsVersioned and SqliteSessionMetadataStore.removeVersioned change public method contracts. Material public-contract changes require independent human review under repository policy.
  • A revision copy committed after parent deletion may lack subagentParent and may later appear as an active top-level Session. This requires review of the copy and reconciliation paths.
  • The PR overlaps with #3074, which may change archive authority from status: 'archived' to isArchived. The merge order and compatibility require human review.
  • No security, licensing, release, or governance effect was identified in the current diff.
  • The person performing the merge must review the final diff. A maintainer makes the final determination.

Walkthrough

Parent session removal now archives linked subagent sessions instead of leaving them as active top-level tasks. Task settings identifies orphaned subagents with localized deleted-parent text. Storage, runtime, unit, and E2E tests cover the behavior.

Changes

Parent removal archival

Layer / File(s) Summary
Atomic removal and archival
packages/storage/...
Storage removal APIs accept archive targets, validate versions, archive children transactionally, and remove related authorities.
Runtime removal coordination
packages/runtime-host/...
The retirement coordinator discovers child families, stabilizes removal and archive plans, handles busy children and retries, and archives orphaned subagents during recovery.
Orphan task presentation
apps/desktop/src/renderer/settings/..., apps/desktop/src/main/__tests__/task-catalog-rows.test.ts
The task catalog detects missing parents and displays localized deleted-parent labels for archived subagents.
Parent removal end-to-end coverage
apps/desktop/e2e/..., packages/runtime-host/src/__tests__/session-revision-two-client-uds.test.ts
Fixtures seed linked parent and child sessions. Tests verify active removal, archived-child display, and revision conflicts for linked children.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: ⚪ Minimal · up to da275

The change archives ordinary child sessions when their parent is deleted and preserves them in the archive view; no actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant TasksSettingsPage
  participant SessionRetirementCoordinator
  participant SessionAuthorityStore
  participant SqliteSessionMetadataStore
  User->>TasksSettingsPage: Delete parent task
  TasksSettingsPage->>SessionRetirementCoordinator: Remove parent session
  SessionRetirementCoordinator->>SessionAuthorityStore: removeSessionsVersioned(removalSessions, archiveSessions)
  SessionAuthorityStore->>SqliteSessionMetadataStore: removeVersioned(removalSessions, archiveSessions)
  SqliteSessionMetadataStore-->>SessionAuthorityStore: Commit removal and archival
  SessionAuthorityStore-->>SessionRetirementCoordinator: Return retirement result
  SessionRetirementCoordinator-->>TasksSettingsPage: Removal completed
  TasksSettingsPage-->>User: Show child in archive with deleted-parent label
Loading

Possibly related issues

Possibly related PRs

Suggested reviewers: m4n5ter

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Ai Use Disclosure ⚠️ Warning The authored PR description selects neither AI-use declaration, and all three PR commits have no standalone Generated-by trailer. Select exactly one AI-use declaration. If generative tooling contributed, name the tool and scope, add trailers to affected commits, and ensure they survive squash or amend; see CONTRIBUTING.md.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: archiving child sessions when their parent is deleted.
Description check ✅ Passed The description clearly covers the problem, solution, validation, and linked issue, but it omits several template sections such as AI use and the checklist.
Linked Issues check ✅ Passed The changes satisfy #1467 by archiving child sessions atomically, handling orphan recovery, preserving archived metadata, and labeling archived children.
Out of Scope Changes check ✅ Passed The changes remain focused on parent-child session retirement, orphan presentation, storage behavior, and related test coverage.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@YayoiNanoka
YayoiNanoka marked this pull request as ready for review August 17, 2026 09:08
@hqhq1025
hqhq1025 requested a lite review from Copilot August 17, 2026 09:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Implements “archive ordinary subagent child sessions when their parent is deleted” end-to-end across Storage, Runtime Host retirement, and Desktop archived-task presentation, preventing orphaned subagent tasks from surfacing as top-level active rows after parent deletion.

Changes:

  • Extends the SQLite session metadata removal API to support an atomic “remove + archive” commit (with version conflict validation).
  • Updates Runtime Host session retirement to derive and admit a stable removal plan that includes direct ordinary subagent child revision families, plus startup recovery reconciliation for pre-existing orphans.
  • Enhances Desktop Archived Tasks UI to label orphaned subagent tasks (“Parent task deleted” / “原父任务已删除”), with added unit + E2E coverage.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/storage/src/sqlite-session-metadata-store.ts Adds atomic archival of additional sessions during versioned removal transactions.
packages/storage/src/session-store.ts Extends store interface and forwards archiveSessions to metadata removal.
packages/storage/src/execution-stores.ts Wires the updated removeSessionsVersioned signature through execution stores.
packages/storage/src/tests/sqlite-session-metadata-store.test.ts Adds coverage for atomic rollback and successful remove+archive behavior.
packages/runtime-host/src/server/session-retirement-coordinator.ts Derives stable removal plan including ordinary subagent children; reconciles orphan archives on recovery.
packages/runtime-host/src/tests/session-retirement-coordinator.test.ts Adds runtime-host tests for child archival, busy-child blocking, and recovery archiving.
apps/desktop/src/renderer/settings/tasks-settings-page.tsx Displays an orphan label in archived task rows when parent is missing.
apps/desktop/src/renderer/settings/task-catalog-rows.ts Adds helper to detect orphaned ordinary subagent tasks.
apps/desktop/src/renderer/locales/settings-tasks-copy.ts Adds localized copy for the “deleted parent” label.
apps/desktop/src/main/tests/task-catalog-rows.test.ts Adds unit test coverage for orphan-label detection logic.
apps/desktop/e2e/parent-session-deletion.spec.ts Adds E2E coverage validating child archival + orphan labeling after parent deletion.
apps/desktop/e2e/fixtures.ts Seeds parent/child sessions for the new E2E scenario.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/storage/src/sqlite-session-metadata-store.ts
Comment thread packages/runtime-host/src/server/session-retirement-coordinator.ts
Comment thread packages/storage/src/sqlite-session-metadata-store.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

@Astro-Han

Copy link
Copy Markdown
Contributor

Thanks for the fix — the core mechanism is solid: the parent-removal + child-archival commit is genuinely atomic (single transaction() with BEGIN IMMEDIATE, version-checked, rollback-tested), the busy-child gate has no race (admission lease covers all planned ids, gate serialized per session), archived children are genuinely protected from rewrite (guard set skips storage; skipNoop keeps timestamps/versions), startup reconciliation is idempotent, and the subagentParent.parentSessionId single-source-of-truth holds (desktop projection derives from the same header field). Tests are real (atomic rollback, 32-child swarm, busy-child, recovery, archived-guard all exercised). CI is green.

Conclusion: PASS with two P2s (both need coordination or an explicit deferral note).

P2-1 — undeclared coupling with #3074: the status === 'archived' dual-write collides with #3074's isArchived normalization. This PR's archive guards rely on dual writes (removeVersioned writes status: 'archived'; #readRemovalPlanSessionIds splits guards on header.status !== 'archived'; reconcile too), while #3074 (also open) removes 'archived' from SESSION_STATUSES, drops archivedAt, and its v25 migration normalizes legacy status: 'archived'active with isArchived as the sole authority — 6 overlapping files (sqlite-session-metadata-store.ts, session-store.ts, execution-stores.ts, session-retirement-coordinator.ts, plus two test files), neither PR declaring the other. If #3074 lands first, the archivePatch write is a type error (status union no longer has 'archived'); if this lands first, #3074's migration rewrites these statuses back to active and the guards silently stop working — violating this PR's own "preserve original archive timestamps/versions" promise (its does not rewrite an already archived linked Session test would fail under #3074's semantics). Please coordinate: switch the guards to isArchived-only (drop the status criterion and the status: 'archived' write), or declare the merge order explicitly.

P2-2 — a child in the preparing conversation-copy window escapes archival and can resurrect as an active top-level row. The removal plan and startup reconcile both exclude conversationCopy?.state === 'preparing' (#readRemovalPlanSessionIds / #reconcileOrphanedSubagentArchives), but revision copies carry no subagentParent (#createInput's common omits it). So a copy that commits after the parent deletion becomes an active session with no subagentParent — invisible to the orphan reconcile (which requires the field) and never auto-archived; collapseSessionRevisions then picks the fresh revision as family representative and the rail shows it as an active top-level row — exactly the fail-open this PR claims to eliminate, and it persists (not just until restart). No test covers it. Either inherit the parentage in the copy path, or have reconcile treat "family representative archived / parent deleted but member active" as an archive trigger; at minimum add a test pinning the behavior and document it as a known edge.

P3 (optional): the busy-error message doesn't say which child is busy (32-child swarm: any one pending interaction blocks the whole delete with session_busy and a generic deleteFailed toast — worth a diagnostic message with the session id); the orphan-label subagentParent.graph guard in the desktop is dead code (host projection doesn't carry graph, so the guard never fires and the new unit test covers an unreachable shape — either surface the graph flag in the projection or drop the guard); revised children lose the orphan label on the archive page (representative row is the revision without subagentParent — aggregate by family instead); deeper descendants (child-of-child) are neither archived nor reconciled (fine today since children can't spawn, but worth a note); #withStableRemovalPlan duplicates the admission/lane/retry machinery of #withStableFamily.


AI-assisted review disclosure: this review was produced with AI assistance (two pi review subagents on ollama-cloud/deepseek-v4-flash). Both verified the transaction boundary, busy-gate race-freedom, guard protection, reconcile idempotency, and test authenticity from the PR head source; P2-1 is from cross-checking #3074's head, P2-2 is a static trace of the revision-copy path (no triggered failure observed). Please weigh these findings with your own judgment.

中文摘要(AI 辅助审查)

结论:PASS(2 个 P2,需协调或显式延后)。核心机制正确:父删除+子归档真原子(单事务 BEGIN IMMEDIATE、版本校验、回滚测试)、busy 门控无竞态(admission lease 覆盖全部计划 id、gate 按 session 串行)、已归档子 Session 真受保护(guard 集跳过 storage、skipNoop 保留时间戳/版本)、启动恢复幂等、subagentParent 唯一关系来源成立;测试真实(原子回滚/32-child swarm/busy-child/恢复/已归档保护)。P2-1:与 #3074 未声明耦合——本 PR 的归档判据依赖 status==='archived' 双写,而 #3074 删除 'archived' 状态、v25 归一化到 active、isArchived 为唯一权威,6 个文件重叠;无论谁先合,另一方的归档保护语义或类型契约会破(若 #3074 先合是类型错误,若本 PR 先合 #3074 迁移会把 status 改写回 active 使 guard 静默失效,违反本 PR 自己"保留归档时间戳/版本"承诺)。建议改为 isArchived-only 判据或显式声明合并顺序。P2-2:preparing 窗口内的 conversation-copy 逃逸归档——删除后 commit 的副本无 subagentParent,恢复逻辑永远看不见它,被 collapseSessionRevisions 选为家族代表后以活跃顶层任务行复活(正是本 PR 声称消除的 fail-open,且持久),无测试覆盖;建议副本路径继承血缘或恢复时把"家族代表已归档但成员活跃"纳入归档触发,至少补测试并记录为已知边界。P3:busy 错误不指明哪个子会话忙(32 子 swarm 一个 pending 交互即阻塞整个删除)、孤儿标签 graph 守卫在桌面是死代码(host 投影不带 graph,新单测覆盖不可达形状)、修订版子 Session 归档页丢孤儿标签(需按家族聚合)、更深层后代不归档(当前产品子会话无法 spawn,可接受)、#withStableRemovalPlan 与 #withStableFamily 机制重复。

@YayoiNanoka

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review. I merged the latest main (including #3074) and addressed the actionable P2/P3 items in da2751535.

What changed:

  • P2 — refactor: make isArchived the sole session archive authority #3074 alignment: isArchived is now the only archive authority. Parent removal archives linked children atomically in the same SQLite transaction through setArchivedSync(..., true); it no longer writes status: "archived", archivedAt, or statusUpdatedAt. Already archived children remain true no-ops, including metadata revision and commit timestamp.
  • P2 — copy/removal race: linked child Sessions are already rejected by both branch and revision creation, while the removal plan is re-read after admission and retried if its family changes. I added regression coverage for both invariants, including a removal plan that changes between its initial read and admitted re-read.
  • P3 — busy diagnostics: session_busy messages now include the exact blocking Session ID.
  • P3 — orphan label guard: removed the dead renderer-side Agent Graph guard and its unreachable fixture; the host projection does not expose that graph metadata.
  • P3 — revised child behavior: added protocol-level coverage proving ordinary linked children cannot be branched or revised, so their parent relation cannot disappear through conversation copy.

I intentionally did not add recursive descendant traversal: ordinary linked child Sessions cannot spawn further subagents in the current runtime, so recursion would add an unsupported state model rather than protect a reachable case. I also kept the stable family and stable removal-plan helpers separate because their admitted sets and retry payloads are materially different.

Validation completed locally:

  • full workspace typecheck
  • all workspace builds
  • SQLite session metadata tests: 47 passed
  • Session retirement coordinator tests: 23 passed
  • two-client revision UDS test: 1 passed
  • desktop task catalog row tests: 8 passed

CI is running on the new head.

点击展开中文

感谢详细审查。已在提交 da2751535 中合入最新 main(包含 #3074),并处理了可执行的 P2/P3 问题。

本次修改:

  • P2 — 与 refactor: make isArchived the sole session archive authority #3074 对齐: 现在只以 isArchived 作为归档状态的唯一依据。删除父 Session 时,仍在同一个 SQLite 事务内通过 setArchivedSync(..., true) 原子归档关联子 Session;不再写入 status: "archived"archivedAtstatusUpdatedAt。已经归档的子 Session 会保持真正的 no-op,metadata revision 和 commit timestamp 都不会变化。
  • P2 — copy/remove 竞态: 当前代码已经禁止关联子 Session 创建 branch 或 revision;删除流程也会在取得 admission 后重新读取 removal plan,family 发生变化时自动重试。本次补充了这两条不变量的回归测试,包括 removal plan 在首次读取与 admission 后重读之间发生变化的场景。
  • P3 — busy 诊断: session_busy 错误现在会包含实际阻塞操作的 Session ID。
  • P3 — orphan 标签 guard: 删除了 renderer 中无效的 Agent Graph guard 及无法到达的测试 fixture,因为 host projection 并不会暴露这份 graph metadata。
  • P3 — 子 Session revision: 增加协议层测试,确认普通关联子 Session 不能创建 branch 或 revision,因此不会通过 conversation copy 丢失父子关系。

没有增加递归后代遍历:当前 runtime 不允许普通关联子 Session 再创建 subagent,递归处理的是目前不可到达的状态。也没有合并 stable family 与 stable removal-plan helper,因为两者持有的 admission 集合及重试数据不同,强行抽象反而会降低清晰度并增加并发正确性风险。

本地验证结果:

  • 全 workspace typecheck 通过
  • 全 workspace build 通过
  • SQLite session metadata:47 个测试通过
  • Session retirement coordinator:23 个测试通过
  • two-client revision UDS:1 个测试通过
  • desktop task catalog rows:8 个测试通过

新 head 的 CI 正在运行。

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for tightening this lifecycle boundary. I reviewed the current head with two independent code-review passes plus a read-only ollama-cloud/deepseek-v4-flash:high pass.

The problem is correctly defined: deleting an ordinary parent must not erase its child transcript, but the child must not remain active or orphaned. This implementation keeps isArchived as the single archive authority, re-resolves the removal plan under admission, and commits child archival plus parent deletion in one SQLite transaction. The already-archived guard and post-commit catalog refresh are necessary concurrency mechanics rather than parallel state.

I found no concrete P0–P2 issue on this head. The remaining opportunities are non-blocking test hardening: inject a storage failure after the child row write to prove rollback, exercise archive-set mutation during plan re-resolution, and pin the direct child-spawn/parent-removal race.

No local test suite was run during this review; conclusions are based on source and test inspection. Codex coordinated the independent passes and performed the final adjudication; the external model output was treated as unverified input until checked against the code.

中文摘要

感谢这次对生命周期边界的收敛。问题定义正确:删除普通父会话时不能删除子会话 transcript,但也不能让子会话继续处于活跃或孤儿状态。

当前实现以 isArchived 作为唯一归档状态,在 admission 下重新解析删除计划,并在同一个 SQLite transaction 中完成子会话归档与父会话删除。已归档 guard 和提交后的 catalog refresh 都是必要的并发机制,不是重复状态。

当前 head 未发现具体的 P0–P2 问题。剩余建议仅是非阻断测试加固:真实存储失败回滚、archive 集合变化、以及直接 child spawn 与 parent removal 的竞态。

本次未在本地运行测试套件;结论来自源码和测试检查。Codex 协调了独立审查并完成最终判断,外部模型输出在核对代码前均视为未验证输入。

@Astro-Han
Astro-Han merged commit eeb3ce3 into apache:main Aug 18, 2026
18 checks passed
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.

fix(desktop): archive child sessions whose parent was deleted

3 participants