Repository navigation
[Bug] 工作区路径被两个项目共用时,fork 出的会话 project_id 永久为 NULL,侧边栏项目下看不到该会话 #1252
Copy link
Copy link
Open
Labels
priority: P2普通优先级普通优先级
Description
问题类别 · 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
- 建两个项目指向同一路径:项目 A 把它作为主根目录,项目 B 再把它加为附加目录(本机现场:
E:\oceanbase大赛同时属于seekdb与powercontext)。 - 在项目 A 里打开一个会话并正常对话(该会话
project_id= A)。 - 右键该会话 → 分叉 / fork,在新会话里继续对话。
- 查看左侧导航栏「项目 → A」。
期望表现 · Expected behavior
fork 出来的会话出现在「项目 → A」的会话列表里 —— 它继承父会话的工作区与项目归属。
实际表现 · Actual behavior
fork 会话不在任何项目下。tasks-index.sqlite 里该行 project_id IS NULL,只能落进侧边栏底部「任务」(无项目会话)分区;一旦被置顶,「任务」区也会因 !pinned 过滤而排除它,于是侧边栏除「已置顶」外没有任何入口。
根因 · Root cause
- CLI 侧创建的 fork 会话由
applyCliFacts→writeRecord落库,INSERT 不写project_id/host_id,随后调用admitTaskIdentity(packages/services/src/session/taskIndexRepo.ts:869)。 admitTaskIdentity只用uniqueRootProject()决定归属(packages/services/src/project-storage/legacy-tasks.ts)。uniqueRootProject()的语义是「同 host 唯一 root 匹配才归位」:LIMIT 2后要求length === 1,多个项目声明同一路径时返回NULL(见packages/services/src/project-storage/CONTRACT.md「同 host 唯一 root 匹配才归位,主要目录没有优先级」)。- 本机
project_roots中该路径有 2 行(A 主根 + B 附加目录)→ 返回NULL。 - 侧边栏按
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。
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
priority: P2普通优先级普通优先级
Activity
github-actions commentedon Oct 27, 2026
👋 感谢你的反馈,我们已经收到。
status: 待评估,你可以随时补充信息。needs: 更多信息标签并 @ 你。👋 Thanks — we've received your issue.
status: 待评估(Triage); feel free to add context.needs: 更多信息and ping you.Aetik-yue commentedon Oct 27, 2026
补丁已准备好,供维护者参考(我这边调用 PR 创建接口被上游拒绝:REST
POST /repos/zai-org/ZCode/pulls返回 404,GraphQLCreatePullRequest返回权限错误;compare与分支读取都正常,应该是本账号没有建 PR 的权限)。一键对比 / 开 PR:zai-org/ZCode@main...Aetik-yue:fix/fork-project-attribution
Aetik-yue/ZCode:fix/fork-project-attribution05d7cb3legacy-tasks.ts、contract.ts、CONTRACT.md、新增packages/services/test/projectStorageForkAdmission.test.ts修复方式
不改变
uniqueRootProject的判定语义(避免和「主要目录没有优先级」的契约冲突),只在首次归位时给 fork 补一条继承:forkParentProject只读同一 host 下父任务已非空的project_id,不跨 host 继承。本地验证
pnpm exec vitest run packages/services/test/projectStorageForkAdmission.test.ts --project unit→ 5 passedpnpm typecheck→ 退出码 0pnpm lint→ 退出码 0(改动文件无告警)需要确认
本 PR 只影响之后的首次归位。已经被定死为
NULL的历史行(host_id已写、handoff_pending已含 bit 8)不会被修复。如果你们希望启动迁移里补一次「root 现在能唯一归属则回填」的幂等修复,我可以再补一个 PR。