Skip to content

[Bug] 工作区路径被两个项目共用时,fork 出的会话 project_id 永久为 NULL,侧边栏项目下看不到该会话 #1252

Open

Description

@Aetik-yue

问题类别 · Category

UI / 界面体验 · UI / UX(列表归属 / project attribution)

涉及的 Agent 框架 · Agent framework

ZCode Agent(自研)

严重程度 · Severity

影响体验 · Major(会话功能正常,但在侧边栏项目下永久不可见)

复现频率 · Reproducibility

必现 · Always(同一条件下 9 次 fork 全部复现)

问题描述 · Description

当某个工作区路径同时是项目 A 的主根目录、又是项目 B 的附加目录时,在该工作区里 fork 出来的会话会永久丢失 project_id,导致它在左侧导航栏的「项目 → A」下看不到;因为置顶会话会从「任务」列表被剔除,一旦置顶就只剩下「已置顶」一个入口,取消置顶后连入口都没有。

本次现场(1 个父会话在 2 分钟内 fork 9 次):父会话 project_id 正常,9 个 fork 全是 NULL。

复现步骤 · Steps to reproduce

  1. 建两个项目指向同一路径:项目 A 把它作为主根目录,项目 B 再把它加为附加目录(本机现场:E:\oceanbase大赛 同时属于 seekdb 与 powercontext)。
  2. 在项目 A 里打开一个会话并正常对话(该会话 project_id = A)。
  3. 右键该会话 → 分叉 / fork,在新会话里继续对话。
  4. 查看左侧导航栏「项目 → A」。

期望表现 · Expected behavior

fork 出来的会话出现在「项目 → A」的会话列表里 —— 它继承父会话的工作区与项目归属。

实际表现 · Actual behavior

fork 会话不在任何项目下。tasks-index.sqlite 里该行 project_id IS NULL,只能落进侧边栏底部「任务」(无项目会话)分区;一旦被置顶,「任务」区也会因 !pinned 过滤而排除它,于是侧边栏除「已置顶」外没有任何入口。

根因 · Root cause

  1. CLI 侧创建的 fork 会话由 applyCliFacts → writeRecord 落库,INSERT 不写 project_id/host_id,随后调用 admitTaskIdentity(packages/services/src/session/taskIndexRepo.ts:869)。
  2. admitTaskIdentity 只用 uniqueRootProject() 决定归属(packages/services/src/project-storage/legacy-tasks.ts)。
  3. uniqueRootProject() 的语义是「同 host 唯一 root 匹配才归位」:LIMIT 2 后要求 length === 1,多个项目声明同一路径时返回 NULL(见 packages/services/src/project-storage/CONTRACT.md「同 host 唯一 root 匹配才归位,主要目录没有优先级」)。
  4. 本机 project_roots 中该路径有 2 行(A 主根 + B 附加目录)→ 返回 NULL。
  5. 侧边栏按 projectId === project.id 取项目下会话(projectTaskGroups 选择器),因此该 fork 永远不会出现在项目下;projectId === null 且 !pinned 才进「任务」区,置顶后又会被排除。

handoff_pending 的第 8 位(OWNERSHIP_DECIDED)已置位,admitTaskIdentity 只在 host_id IS NULL 时执行,所以这个 NULL 归属是一次性定死的,后续不会被重新判定。

证据 · Evidence

tasks-index.sqlite(ZCode 3.16.3.8992,Windows 11 26200)中同一父会话的 fork:

task_id                                pinned project_id  forked_from_task_id
session_3d31ebb7...  (父会话)          0      894f2577    -
sess_d9efba35...     (fork)            0      NULL        session_3d31ebb7...
sess_c995ac6c...     (fork)            0      NULL        session_3d31ebb7...
sess_0171ebdd...     (fork)            1      NULL        session_3d31ebb7...
... 共 9 个 fork 全部 project_id IS NULL

同一路径的项目归属:

project     name          is_primary
894f2577    seekdb        1
9926f3cf    powercontext  0   <-- 同一 root_path 的附加目录

环境 · Environment

  • ZCode 版本:3.16.3.8992(desktop,Electron)
  • 系统:Windows 11(10.0.26200),本地 workspace(非远端/WSL)
  • 工作区:E:\oceanbase大赛,同时注册于两个项目

补充 · Notes

  • 我已经在本地把受影响的 project_id 手工补上(tasks-index.sqlite),会话数据与 CLI 侧 db.sqlite 完全正常,只有侧边栏归属列受影响。
  • 已按上述根因提交 PR:让首次归位的 fork 在唯一 root 匹配失败时继承父任务的 project_id(不改变「主要目录没有优先级」的既有契约,非 fork 行为不变)。
  • 已经定死的历史行需要一次性修复(host_id 已写、project_id IS NULL 且 root 现在能唯一归属的行);如果你们希望由迁移统一处理,我可以再补一个 PR。

Activity

  1. github-actions commented on Oct 27, 2026

    @github-actions

    👋 感谢你的反馈,我们已经收到。

    • 维护者看到后会尽快回复你。
    • 状态保持为 status: 待评估,你可以随时补充信息。
    • 信息不全时我们会打上 needs: 更多信息 标签并 @ 你。

    👋 Thanks — we've received your issue.

    • A maintainer will get back to you as soon as we can.
    • Status stays at status: 待评估 (Triage); feel free to add context.
    • If we need more details, we'll add needs: 更多信息 and ping you.
  2. Aetik-yue commented on Oct 27, 2026

    @Aetik-yue
    Author

    补丁已准备好,供维护者参考(我这边调用 PR 创建接口被上游拒绝:REST POST /repos/zai-org/ZCode/pulls 返回 404,GraphQL CreatePullRequest 返回权限错误;compare 与分支读取都正常,应该是本账号没有建 PR 的权限)。

    一键对比 / 开 PR:zai-org/ZCode@main...Aetik-yue:fix/fork-project-attribution

    • 分支:Aetik-yue/ZCode:fix/fork-project-attribution
    • Commit:05d7cb3
    • 改动(4 文件):legacy-tasks.ts、contract.ts、CONTRACT.md、新增 packages/services/test/projectStorageForkAdmission.test.ts

    修复方式

    不改变 uniqueRootProject 的判定语义(避免和「主要目录没有优先级」的契约冲突),只在首次归位时给 fork 补一条继承:

    project_id = uniqueRootProject(host, rootPath)
                 ?? forkParentProject(host, forkedFromTaskId)

    forkParentProject 只读同一 host 下父任务已非空的 project_id,不跨 host 继承。

    场景 修改前 修改后
    非 fork,唯一 root / 共享 root 归位 / NULL 不变
    fork,唯一 root 归位 不变(root 优先)
    fork,共享 root,父任务有项目 NULL 继承父任务项目
    fork,父任务无项目 / 跨 host NULL 不变

    本地验证

    • pnpm exec vitest run packages/services/test/projectStorageForkAdmission.test.ts --project unit → 5 passed
    • pnpm typecheck → 退出码 0
    • pnpm lint → 退出码 0(改动文件无告警)

    需要确认

    本 PR 只影响之后的首次归位。已经被定死为 NULL 的历史行(host_id 已写、handoff_pending 已含 bit 8)不会被修复。如果你们希望启动迁移里补一次「root 现在能唯一归属则回填」的幂等修复,我可以再补一个 PR。

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions