Skip to content

fix(desktop): 工作目录缺失时恢复目录并继续对话 - #4060

Merged
MagicLizi merged 9 commits into
mainfrom
dash/fix-workdir-missing-recovery
Sep 8, 2026
Merged

fix(desktop): 工作目录缺失时恢复目录并继续对话#4060
MagicLizi merged 9 commits into
mainfrom
dash/fix-workdir-missing-recovery

Conversation

@dashhuang

@dashhuang dashhuang commented Sep 7, 2026

Copy link
Copy Markdown
Member

这次改了什么

摘要

普通本地工作目录被删除后,发送消息会被 WORKDIR_MISSING 拦住。现在在原位置重建缺失目录,让原任务继续接收消息,无需先选择文件夹。发给 AI 的消息会附带一次环境说明:仅重建了目录,原文件尚未恢复;是否查找文件、恢复项目或询问用户由 AI 根据当前请求决定。

同时补齐存活任务的旧目录兜底:数据库已保存有效的新目录时,按该目录恢复原生会话,保留原生历史后再发送。

同路径重建后,Claude Code / Pi 也沿现有恢复路径刷新原生进程,避免继续使用指向已删除目录的 cwd。发现相似名称的路径时,把候选位置交给 AI 核实,不自动切换项目。

检测到文件系统不可用或已观测的设备身份改变时,使用 Cindy 现有对话工作目录继续交流,不在断开的挂载点下创建替身目录。AI 收到原目录、临时工作位置及文件未恢复的说明;磁盘重连后不自动切回或迁移产物。该例外已获需求方确认。

恢复时保留调用方原始 userPrompt、Maker Memory 与 reasoning 显示偏好,项目/Orca 内容仍由原启动流程生成。关闭旧实例前复用 Bot 资源预检,实际启动继续统一装配 Bot 身份与资源。Pi 的 slash 输入保留在开头,由 Pi 决定是否执行;恢复说明留给后续普通消息。

变更类型

  • feat 新功能
  • fix 缺陷修复
  • refactor / perf 重构或性能优化
  • docs / test / chore 文档、测试或工程维护
  • 其他:

范围

  • 关联 Issue / 需求:工作目录缺失不应阻断对话,恢复操作由 AI 与用户在对话中处理。
  • 本 PR 包含:普通目录原址重建、不可用文件系统的对话目录兜底、旧目录的 DB 兜底、发给 AI 的恢复说明及回归测试。
  • 明确不包含:恢复已删除文件、SSH 远端目录重建、新增目录选择界面、改变托管 Git worktree 的快照恢复规则。
  • 用户可见变化:普通本地目录缺失且可重建时,消息直接继续发送;聊天中的用户原文不变。
  • 是否存在 breaking change:无。

UI 变化

不涉及。

  • 引用的设计规范:不涉及,无 Renderer、组件或本地化文案改动。

怎么验证的

自动验证

env -u VITE_CINDY_AUTH_REGION -u NODE_ENV -u NODE_DEBUG pnpm test:unit:related
结果:通过,覆盖 Desktop、maker-core related、lizi-mcps 与 orca-workflow;包括设备消失/替身目录、同卷目录删除、不可用错误/超时、fallback 清理与重建、三种引擎恢复发送,以及真实 Maker 的有效 cwd。

pnpm --filter desktop run --if-present typecheck
结果:通过。

pnpm --filter @cindy/maker-core run --if-present typecheck
结果:正常退出;该包未定义独立 typecheck script。

git diff --check
结果:通过。

手工验证

未运行客户端手工交互。

未执行的验证

未运行 macOS/Windows 客户端实机端到端验证、手机或 SSH 实机验证;本次为后端恢复改动,使用文件系统与发送事务测试验证。完整单测由 CI 执行。

风险

风险分类

  • 无已知风险
  • SQLite / migration
  • system prompt
  • 协议兼容
  • 权限 / 安全 / 用户数据
  • 存量插件兼容(批准状态 / 指纹 / manifest 校验 / 安装布局 / 包格式)
  • 原生层 / fingerprint / OTA
  • 跨平台差异
  • 其他:恢复说明暂存在进程内存中。

影响与回滚

  • 仅在确认 ENOENT 后原址重建普通目录,不覆盖已存在的文件;权限错误、非目录路径和托管 Git worktree 恢复失败仍保留原处理。
  • 初始及恢复探测、mkdir、realpath、相似目录读取复用既有有界 utility-process 池,单次操作上限 5 秒;mkdir 超时或挂载不可用时沿同一个 fallback 继续,不重试原址写入。超时前可能已创建部分父目录,不承诺文件系统回滚。支持不可用 I/O/网络错误、缺失的 Windows drive/share root、macOS /Volumes 根及已观测的文件系统设备变化。文件系统身份在 bootstrap 与成功探测时记录到现有内存条目,不新增落盘记录。识别限制:进程启动前已断开的任意自定义挂载点(包括隐藏在悬空符号链接后的挂载)、同设备 bind mount,无法只凭 ENOENT 与设备号可靠识别;不声称完整覆盖这些情况。
  • fallback bootstrap 用有界探测过滤运行时目录授权,但不将临时不可用造成的子集写回持久授权。可写授权仍以 SQLite 为准,路径解析无法确认时保守排除可写根;正常 bootstrap 仍保留原有持久收窄规则。内部 Main–utility 消息不向 Renderer/Device Link 开放文件操作。
  • 临时工作位置复用 owner-scoped dialogue workspace,避免系统 temp 回收导致新产物丢失;未自动删除或迁移其中生成的文件。运行实例报告实际启动 cwd,原 DB 项目目录保持不变。目录路由与说明仅在进程内存中保留;显式切换/清空/归档/删除/切账号沿既有清理,发送成功仅消费说明,不使运行实例切回原目录。
  • 恢复说明只进入模型消息,不修改系统提示词或用户显示内容;发送被接受后才消费,未接受则保留供重试。应用在发送成功前退出会丢失这条尚未发送的说明,此限制已与需求方确认。
  • DB 目录兜底在关闭旧运行实例前核对原生恢复信息,避免用新会话替代历史;验证失败不会提前关闭旧实例。
  • 启动偏好仅由当前 Session 持有不可变副本,替代原 userPrompt 单字段;恢复只补充发送参数缺省的启动偏好。权限、目录授权、plan/thinking 等可变控制继续从当前权威状态读取,不缓存整份历史启动参数。Bot 资源预检失败发生在关闭旧实例前,重试仍走同一入口,无新增缓存、持久化或重试机制。
  • Claude Code / Pi 的同路径恢复复用待发送说明作为刷新依据;说明在 /compact 或拒绝发送后仍保留,因此下一次发送可能再次刷新运行时。没有新增持久状态或重试机制。
  • 同一目录的其他存活本地任务也保留恢复说明,重建后用 realpath 识别别名,各自在下次发送时刷新 Claude Code / Pi 运行时,不中断其他任务正在执行的消息。说明绑定各任务自身的目录路径,切换目录后丢弃;每个任务独立消费或清理。Device Link / 手机由执行端共用发送事务受益;SSH 不使用本机文件系统重建远端目录。
  • 回滚 / 降级方式:回退本 PR,恢复原目录缺失错误处理。已重建目录不自动删除。
  • 存量插件影响:无。
  • 远程适配:SSH 保持远端路径规则;Device Link/手机复用被控端发送事务,无新界面或对外 IPC。恢复只影响当前任务的原生实例,不重连 relay、不影响其他控制端链路;因此不涉及连接级多 peer 恢复。

提交前检查

  • 已 review 完整 diff
  • 每个 commit 都带 DCO 签名(git commit -s,见 DCO
  • UI 改动已在「UI 变化」注明引用的设计规范章节(不涉及 UI 则跳过)
  • 未提交凭证、令牌或授权文件
  • 已补充必要文档(行为、边界及回滚说明见本 PR)
  • 已确认测试结果或说明未执行原因

@dashhuang
dashhuang requested a review from a team as a code owner September 7, 2026 12:46
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-08T11:42:06.426017Z e967fea New commits
🔒 Security Review Completed 2026-09-08T11:44:06.672941Z e967fea New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@greptile-apps

greptile-apps Bot commented Sep 7, 2026

Copy link
Copy Markdown

Greptile Summary

本 PR 在普通本地工作目录缺失时原址重建目录,并将一次性恢复说明仅注入模型侧消息;同时为仍存活但持有旧 cwd 的 runtime 增加基于 DB 工作目录及原生会话历史的重建路径。

  • 区分普通目录、SSH 会话和托管 Git worktree 的恢复策略。
  • 在关闭旧 runtime 前从 DB 对齐执行字段和原生会话 ID。
  • 新增目录恢复、DB cwd 兜底及恢复说明消费语义的单元测试。
  • 当前恢复说明状态尚未接入会话关闭和删除生命周期的清理。

Confidence Score: 4/5

该 PR 整体可合并,但建议补齐恢复提示在会话关闭、清除和删除时的清理,以避免长期状态残留及旧提示串入同 ID 新实例。

核心目录恢复、DB 执行字段对账和消息接受边界处理均有明确保护;剩余问题是模块级恢复提示 Map 缺少生命周期清理,属于非阻塞但应修复的状态管理问题。

Files Needing Attention: apps/desktop/src/main/maker-ipc/workingDirectoryRecovery.ts, apps/desktop/src/main/maker-ipc/register.ts

Important Files Changed

Filename Overview
apps/desktop/src/main/maker-ipc/workingDirectoryRecovery.ts 新增普通本地目录重建及一次性模型提示状态;该状态缺少会话生命周期清理。
apps/desktop/src/main/maker-ipc/makerSendTransaction.ts 为存活会话增加 DB cwd 重建,并在 wire payload 中注入和按 accepted 语义消费恢复说明。
apps/desktop/src/main/maker-ipc/register.ts 接入目录恢复服务并区分托管 worktree、远端会话和普通本地目录,但未将恢复状态接入关闭清理。
apps/desktop/src/main/maker-ipc/tests/workingDirectoryRecovery.test.ts 覆盖 ENOENT 重建、非目录路径、权限错误和提示消费,但缺少会话关闭清理场景。
apps/desktop/src/main/maker-ipc/tests/makerSendTransaction.test.ts 覆盖 DB 工作目录兜底、历史恢复、SSH 排除以及恢复提示的 accepted 消费语义。

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[发送消息] --> B{当前 runtime 的 cwd 存在?}
  B -->|是| G[发送给 provider]
  B -->|否且 DB 有不同目录| C[验证 DB 工作目录]
  C -->|有效| D[从 DB 对齐模型和原生会话 ID]
  D --> E[关闭旧 runtime 并用 DB cwd 重建]
  E --> G
  C -->|无效| H[返回 WORKDIR_MISSING]
  B -->|普通本地目录且 ENOENT| F[原址递归重建目录并记录恢复说明]
  F --> G
  G --> I{provider accepted?}
  I -->|是| J[消费恢复说明]
  I -->|否| K[保留说明供重试]
Loading
Prompt To Fix All With AI
### Issue 1
apps/desktop/src/main/maker-ipc/workingDirectoryRecovery.ts:8
`pending` 仅在消息被 provider 接受后通过 `consume` 删除;会话的 close、clear、delete 和 runtime replacement 流程都不会清除此状态。如果目录重建后的消息未被接受,而会话随后关闭,该记录会一直留在模块级 Map 中,长期运行时会随不同 sessionId 持续增长;同 ID 的新实例还可能收到包含旧目录的恢复说明。建议提供按 sessionId 清理的方法,并接入现有的会话清理生命周期。

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "fix(desktop): 工作目录缺失时恢复目录并继续对话" | Re-trigger Greptile

Comment thread apps/desktop/src/main/maker-ipc/workingDirectoryRecovery.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c299f0ff96

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/main/maker-ipc/makerSendTransaction.ts Outdated
Comment thread apps/desktop/src/main/maker-ipc/makerSendTransaction.ts Outdated
@MagicLizi

Copy link
Copy Markdown
Contributor

@dashhuang 👋 这个 PR 还有 3 条 review conversation 没 resolve(apps/desktop/src/main/maker-ipc/workingDirectoryRecovery.ts / apps/desktop/src/main/maker-ipc/makerSendTransaction.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。

如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。

@MagicLizi MagicLizi added status:awaiting-bot-review 等外部审查机器人表态(review-pr 自动维护,仅展示) touches:core 改动碰到架构核心路径(review-pr 自动维护,仅展示) labels Sep 7, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 28d828c712

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/main/maker-ipc/register.ts Outdated
@MagicLizi MagicLizi added status:ci-failed CI 失败(review-pr 自动维护,仅展示) and removed status:awaiting-bot-review 等外部审查机器人表态(review-pr 自动维护,仅展示) labels Sep 7, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 68b8938356

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/main/maker-ipc/makerSendTransaction.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4d503f87d7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/main/maker-ipc/workingDirectoryRecovery.ts
Comment thread apps/desktop/src/main/maker-ipc/makerSendTransaction.ts Outdated
Comment thread apps/desktop/src/main/maker-ipc/workingDirectoryRecovery.ts Outdated
@MagicLizi MagicLizi added status:awaiting-bot-review 等外部审查机器人表态(review-pr 自动维护,仅展示) and removed status:ci-failed CI 失败(review-pr 自动维护,仅展示) labels Sep 7, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 29fcd143a3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/main/maker-ipc/makerSendTransaction.ts
Comment thread apps/desktop/src/main/maker-ipc/makerSendTransaction.ts
Comment thread apps/desktop/src/main/maker-ipc/register.ts Outdated
@dashhuang

Copy link
Copy Markdown
Member Author

本轮收敛检查点(对应原生命令、恢复选项与目录别名):

  • 恢复说明只由 workingDirectoryRecovery 管理,按任务独立消费。重建成功后识别物理别名,保留各任务自身路径;不同目录的旧说明不得被扫描覆盖。
  • 成功 mkdir 才产生说明;现存目录探测、失败不伪造说明。clear/discard/目录切换发生在 IO 等待期间时,迟到结果不得重新激活该条目;已消费的条目也不得被旧操作覆盖。
  • 原生命令保持原始消息前缀。Claude /compact 与 Pi slash 输入不消费恢复说明;普通消息被接受才消费,拒绝发送则保留。
  • 重建前保留调用方 userPrompt,不能以数据库缺字段为由丢弃;捕获点在项目/Orca 注入前,实际创建 Session 时保存为只读字段,重复/并发 bootstrap 复用实例不能覆盖原值。
  • 本轮测试覆盖:真实 parent symlink 别名、无关目录的 pending note、别名解析期间清理、Pi 命令(含附件)后普通消息、无 createOpts 时原始 userPrompt 透传。

没有新增持久化、独立注册表或重试系统。挂载盘断开时的产品行为仍待需求方确认,本轮没有改变该边界。

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fa9607ed78

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/main/maker-ipc/makerSendTransaction.ts
Comment thread apps/desktop/src/main/maker-ipc/makerSendTransaction.ts Outdated
@MagicLizi

Copy link
Copy Markdown
Contributor

@dashhuang 👋 这个 PR 还有 3 条 review conversation 没 resolve(apps/desktop/src/main/maker-ipc/workingDirectoryRecovery.ts / apps/desktop/src/main/maker-ipc/makerSendTransaction.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。

如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。

@dashhuang

Copy link
Copy Markdown
Member Author

启动配置恢复族的收敛检查点(16b10d023):

  • 原有 userPrompt 单字段归并为当前 Session 的只读启动偏好:原始 userPrompt、Maker Memory、reasoning 显示。副本在生成项目/Orca 内容前捕获,实际实例持有;并发或重复启动不能覆盖已存在实例的偏好。
  • 无 createOpts 和部分 createOpts 共用缺省补齐规则;显式 false 保留。权限、目录授权、plan/thinking 等可变控制仍走当前权威状态,不回放完整旧启动参数。
  • cwd 恢复在关闭旧实例前复用 Bot 资源预检。实际启动原本就通过 hydrateBotProfileRuntime 装配 Bot 身份与资源;本次补齐失败发生在旧实例关闭前的顺序约束。
  • 回归覆盖无参数、部分参数、显式覆盖、外部对象修改及复用实例、预检失败后重试。最终相关单测及 Desktop typecheck 通过;maker-core 无独立 typecheck script。未运行客户端实机端到端验证。

无新增注册表、持久化、锁或重试机制。挂载盘断开时的产品行为仍待需求方确认,本轮不改变该边界。

@MagicLizi

Copy link
Copy Markdown
Contributor

@dashhuang 👋 这个 PR 还有 1 条 review conversation 没 resolve(apps/desktop/src/main/maker-ipc/workingDirectoryRecovery.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。

如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。

@MagicLizi MagicLizi removed the status:awaiting-bot-review 等外部审查机器人表态(review-pr 自动维护,仅展示) label Sep 7, 2026
@dashhuang

Copy link
Copy Markdown
Member Author

挂载不可用恢复的收敛检查点:

  • 需求方已确认:磁盘/网络盘不可用时使用临时对话目录继续交流,并由 AI 说明和沟通;不采用再次阻断或目录选择界面。
  • 普通目录仅在 ENOENT 且没有不可用文件系统证据时原址重建。已观测设备身份改变、不可用错误/探测超时、缺失 drive/share root、macOS /Volumes 不可用根进入现有 dialogue workspace。
  • 文件系统探测复用现有有界 utility-process 池。现有 per-session 内存条目持有观测设备、fallback 与一次性说明;消费说明不清除 fallback,清空/归档/删除/切账号沿现有清理。无新增持久化、锁或重试系统。
  • 首次使用 fallback 时所有原生引擎均沿既有恢复流程换 cwd;Session 报告实际启动目录而非旧 DB 项目目录。原生历史、启动偏好与当前授权继续走统一恢复路径。后续普通发送不反复切回或重启。
  • AI 被明确告知原目录与文件未恢复/未复制、产物留在临时工作位置,磁盘重连不自动迁移或切回。实际文件放在既有 owner-scoped dialogue workspace,避免系统 temp 回收产物。
  • 识别限制明确保留:没有此前观测的任意自定义挂载点、同设备 bind mount、冷启动时隐藏在悬空链接后的挂载,不能只凭 ENOENT 可靠判定;未引入持久化挂载清单,也不声称覆盖全部情况。

测试覆盖设备消失/底层替身目录、同卷子目录删除、I/O 与超时错误、分配期间清理、/Volumes 根与系统盘别名、三种引擎改 cwd 后再发送、真实 Maker 对旧 DB 行的运行目录投影。验证结果以 PR 正文为准;未进行真实磁盘拔插或 SMB 断线端到端测试。

@MagicLizi MagicLizi added status:ci-failed CI 失败(review-pr 自动维护,仅展示) status:ci-running CI 还在跑(review-pr 自动维护,仅展示) and removed status:ci-failed CI 失败(review-pr 自动维护,仅展示) status:ci-running CI 还在跑(review-pr 自动维护,仅展示) labels Sep 7, 2026

@MagicLizi MagicLizi 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.

独立审查未通过(P1×2)。当前 head e139f365c0ea2b9245f0ba06effa7ae6ea974e37 不能合。

  • [P1] apps/desktop/src/main/maker-ipc/workingDirectoryRecovery.ts:112 / apps/desktop/src/main/maker-ipc/register.ts:1054 — stat 已改走有界 utility-process,但原址恢复的 mkdir(..., { recursive: true }) 仍在 Electron main 里直接 fsp.mkdir。探测得到 ENOENT 的网络盘/UNC 父路径仍可能在 mkdir 上卡住 libuv,主进程无法取消。请把 mkdir 也走可杀进程/超时,超时则走现有 fallback。

  • [P1] apps/desktop/src/main/maker-ipc/makerSendTransaction.ts:686 → bootstrapSession → prepareDirectoryGrantsForBootstrap — 文件系统不可用时本 PR 会强制 reason: 'workdir' 重建 runtime。prepareDirectoryGrantsForBootstrap 用新 cwd 再 stat extra/writable;挂载上的授权目录会变成 not-exist,随后 persist 把 SQLite 里的 extra_dirs/writable_dirs 写空。盘回来也不会恢复。unavailable/fallback 重建时不要把这次失败当成授权撤销。

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
…oundaries

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
…cwd recovery

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
… is unavailable

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
@dashhuang
dashhuang force-pushed the dash/fix-workdir-missing-recovery branch from e139f36 to e967fea Compare September 8, 2026 11:32
@dashhuang

Copy link
Copy Markdown
Member Author

针对 #4060 (review) 的两项 P1,已在 e967feaf7 修复,归并为两个失败族:

  1. 目录恢复的文件操作必须有界。 复用既有 workdir utility-process 池执行 mkdir、realpath 和相似路径读取,沿用原有 5 秒期限、队列与进程终止规则,不增加另一套 worker/重试机制。原址 mkdir 在 stat 之后失败或超时,也进入同一个 fallback 转移;权限错误仍不会假报恢复。超时写操作不会重试,但操作在超时前可能已创建部分父目录,不承诺文件系统回滚。
  2. 临时不可用不是撤销持久授权。 fallback 状态唯一由现有 recovery Map 持有,消费 wire note 后仍有效。fallback bootstrap 继续从 SQLite 读取可写授权、过滤运行时不可用目录,并禁止将这份临时子集写回 extra_dirs/writable_dirs。可写/只读 realpath 无法确认时保守排除可写授权,避免别名绕过。正常 bootstrap 保持原有持久收窄行为。

对称路径:成功→原址说明;stat/诊断/mkdir 不可用或超时→既有 fallback;普通权限错误→原失败;worker 超时→终止、忽略迟到响应、确认 exit 后释放槽;cleanup 先完成→迟到 IO 不重建内存路由;fallback note 消费→保留路由与授权保护;显式换目录→丢弃原路由。fallback 目录自身不可用仍按既有失败处理,不递归创建 fallback。

回归覆盖 mkdir 超时/不可用/权限错误、不同操作不共用 stat 结果、worker 迟到响应与后续正常探测、真实目录创建与错误、fallback 授权过滤/恢复,以及只读或可写别名解析超时。Main–utility 协议仅内部使用,不新增 Renderer/Device Link 能力。没有新增持久化、挂载清单、锁或重试系统。

验证:同步主干后,根 pnpm test:unit:related 全部通过,Desktop typecheck 通过;maker-core 未定义独立 typecheck,--if-present 正常退出。git diff --check 通过,rebase 的逐提交 range-diff 全部等价。未运行真实断盘/UNC 卡死的客户端端到端测试。

@MagicLizi MagicLizi 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.

Code Review — PR #4060: fix(desktop): 工作目录缺失时恢复目录并继续对话

Tier: heavy | Head: e967fea | Reviewer: magiclizi (auto-review)

Summary

This PR adds working directory recovery for ordinary local directories. When a conversation's working directory is missing (deleted or filesystem unavailable), Cindy now recreates it in-place (for normal ENOENT) or falls back to a temporary dialogue workspace (for unavailable filesystems). A one-time recovery note is injected into the wire message to inform the AI, without modifying the user display or system prompt.

Architecture

  • workingDirectoryRecovery.ts (164 lines): Stateless per-session Map tracking recovery state bound to (sessionId, workingDir). Proper lifecycle cleanup via discard (session close), clear (owner boundary), and consume (message accepted). Notes are compare-and-set to prevent stale consumption.
  • Bounded IO: All filesystem operations (stat, mkdir, realpath, readdir) run in the existing utility-process pool with 5s timeouts. No new worker or retry mechanism.
  • Grant protection: Fallback bootstrap uses injectable statDirectory/realpathDirectory with preservePersistedGrants flag. When canonicalization times out, conservatively returns empty grants. Never writes temporary unavailability back to persistent extra_dirs/writable_dirs.
  • Native runtime refresh: After same-path recovery, Claude Code/Pi sessions are rehydrated through the existing rehydrateActiveSession path. Bot resources are pre-checked before closing the old instance.
  • Startup preferences: hostStartupPreferences (userPrompt, makerMemoryEnabled, displayReasoning) are captured once at Session creation as a frozen readonly snapshot, surviving recovery without new DB fields.

Security & Safety (heavy tier)

  1. No credentials, tokens, or PII in diff — stage 1 security scan passed clean (0 hard/0 soft hits).
  2. No new IPC channels exposed to Renderer or Device Link. The utility-process protocol extensions (mkdir, realpath, similar) are internal Main<->utility data; resolved paths never reach controllers.
  3. No new dependencies added.
  4. No data exfiltration vectors — recovery module operates only on local filesystem paths.
  5. Conservative grant handling — timeout or unavailability results in empty grants, never expanded permissions.
  6. Dependency audit: N/A (no new deps). Semgrep: not executed (no base SHA provided to scope scan to new code only).

Findings

0 P0, 0 P1 found.

The implementation is well-structured with proper boundary handling:

  • Managed Git worktrees are excluded from recovery (they keep their own restore path).
  • SSH sessions skip local directory fallback.
  • Permission errors (EACCES) and non-directory paths (ENOTDIR) are not treated as ENOENT.
  • TOCTOU between stat and mkdir is handled: mkdir failure returns false (no false recovery claim).
  • Cleanup races (discard/clear during pending IO) are guarded by identity checks on the Map entry.
  • Pi slash commands and Claude Code /compact bypass the note prepend.
  • Session workDir now reports actual runtime cwd, not stale DB value — tested explicitly.

Test Coverage

  • 320 new lines in workingDirectoryRecovery.test.ts covering real filesystem operations, concurrent IO, lifecycle cleanup, alias resolution, mount unavailability, and fallback.
  • 259 new lines in makerSendTransaction.test.ts covering DB fallback, same-path refresh, Pi/Claude command bypass, note acceptance semantics, Bot preflight failure/retry, and SSH exclusion.
  • Additional tests for grant protection, utility-process operations, and maker-core startup preferences.

Verification

  • pnpm test:unit:related — author reports pass (Desktop, maker-core, lizi-mcps, orca-workflow).
  • pnpm --filter desktop run typecheck — author reports pass.
  • CI (client-ci) — green on current head.
  • Security scan (stage 1): pass. Dependency audit: N/A. Semgrep: not executed (no base SHA; not a scan pass claim).
  • No client E2E or real disk-unplug testing; explicitly documented as out of scope.

Bot Settle Notice

copilot-pull-request-reviewer and chatgpt-codex-connector timed out (433+ min > 20 min limit). Their opinions may arrive after merge.

Conclusion

Pass — 0 P0/P1 findings. Clean review with comprehensive test coverage, conservative security posture, and proper lifecycle management. Ready for approve and merge.

@MagicLizi
MagicLizi merged commit 6678fd3 into main Sep 8, 2026
19 checks passed
yuchenlin added a commit to yuchenlin/cindy that referenced this pull request Sep 8, 2026
…d-harness

Keep grok-build as AgentKind in checkWorkDirExists while taking
makecindy#4060 working-directory recovery.

Signed-off-by: Bill Yuchen Lin <10104354+yuchenlin@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

touches:core 改动碰到架构核心路径(review-pr 自动维护,仅展示)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants