Skip to content

headless 恢复未覆盖磁盘工作目录已删除场景,PR #682 不宜直接合并 #687

Description

@kxn

背景

PR #682 试图修复一个 headless 会话恢复缺口:当历史线程关联的工作区目录在磁盘上已被移动或删除时,managed headless 启动仍会把旧目录作为 cmd.Dir 传给子进程,最终在 exec 阶段失败。

基于当前 master 的代码复核,问题仍然存在,但 PR #682 的修复方式不建议直接合并。

相关链接:

目标

把“线程/工作区元数据仍在,但实际工作目录已不存在”这一类 headless 恢复失败,收敛为明确、可解释、跨链路一致的领域错误,而不是把用户暴露给通用启动失败或 OS 原始报错。

完成标准

  • 普通 headless 恢复路径能明确区分“缺少可恢复目录元数据”和“目录元数据存在但磁盘目录已消失”两类失败。
  • auto-restore 路径也能落到明确的用户提示,而不是继续压回通用 headless_restore_start_failed
  • 不依赖启动失败后的猜测式错误改写来判断根因。
  • 不把权限、路径不可访问、网络盘异常等非缺失场景误报成“目录已被移动或删除”。
  • 补齐回归测试,覆盖普通恢复与 auto-restore 两条用户可见链路。

现状证据

当前 master 里的相关路径:

  • internal/app/daemon/app_headless.go:168-192
    • workDir := firstNonEmpty(command.WorkspaceKey, command.ThreadCWD)
    • a.startHeadless(... WorkDir: workDir ...)
    • 启动失败后直接 handleManagedHeadlessLaunchFailure(command, err, now)
  • internal/runtime/subprocess.go:48-53
    • cmd.Dir = opts.WorkDir
    • cmd.Start() 如果目录失效会直接返回底层错误
  • internal/core/orchestrator/service_snapshot_runtime.go:530-548
    • 普通恢复路径会把错误包装成 NoticeForProblem(...)
  • internal/core/orchestrator/service_surface_thread_selection.go:664-675
    • auto-restore 路径当前只识别 provider/profile/runtime 缺失,其他启动失败都会落回 headless_restore_start_failed
  • internal/core/orchestrator/service_surface_thread_attach.go:358-368
    • 当前已有 thread_cwd_missing 语义,但只覆盖“没有可恢复目录元数据”,不覆盖“目录元数据还在、但磁盘目录已不存在”

对 PR #682 的结论:

  • 它修的是一个真实缺口,不是过期问题。
  • 但它的实现是“启动失败后再 os.Stat(workDir) 猜根因”。
  • 只要 stat 失败,就把错误改写成 headless_workspace_dir_missing,这会把权限、网络盘、暂时不可访问等非缺失场景也误判进去。
  • 它没有把新错误码接入 auto-restore 的 notice 映射,auto-restore 最终仍会显示通用恢复失败。

已知约束 / 担心点

  • 这是 daemon / orchestrator / runtime 交界问题,不能只改一层文案。
  • 仓库已有 thread_cwd_missing 这类恢复领域错误;新方案应尽量和现有错误模型对齐,避免平行引入只在 daemon 层消费的新语义。
  • 如果最终引入新的恢复错误码,需要同时补普通恢复、auto-restore 和 sticky failure 相关行为。

执行决策

  • 是否拆分:暂不拆分。
  • 当前执行单元:本 issue 作为未拆分直做单元是可接受的,边界仍然集中在 headless 恢复目录缺失语义与用户提示一致性。
  • verifier 计划:预计需要独立 verifier pass;涉及 daemon/orchestrator 错误边界和用户可见恢复提示,不建议跳过。

建议范围

  1. 明确根因边界
    • 区分“目录元数据缺失”与“磁盘目录缺失”
    • 不要把“启动失败后目录 stat 不通”直接等价成“目录被删除”
  2. 把目录存在性校验前移到 headless 启动前
    • 在真正进入 cmd.Start() 之前形成明确的领域错误
    • 避免依赖平台文案或 OS 错误字符串
  3. 对齐现有恢复错误模型
    • 评估是扩展 thread_cwd_missing 语义,还是补一个新的、贯通 daemon/orchestrator 的恢复错误码
  4. 补全 auto-restore 用户提示
    • HeadlessRestoreLaunchFailureCode(...) / 对应 notice 映射需要覆盖该场景
  5. 补测试
    • 普通恢复路径
    • auto-restore 路径
    • 非目录缺失但 stat/访问失败的负例,避免误判

实现参考

  • internal/app/daemon/app_headless.go
  • internal/runtime/subprocess.go
  • internal/core/orchestrator/service_snapshot_runtime.go
  • internal/core/orchestrator/service_surface_thread_selection.go
  • internal/core/orchestrator/service_surface_thread_attach.go
  • PR fix(headless): explain missing workspace dir on switch failure #682 中的 explainMissingWorkspaceLaunchError(...) 和新增测试可作为问题样本,但不建议原样合并

当前 preferred direction:

  • 在 daemon 启动前形成真实领域错误,而不是启动失败后猜测性改写。
  • 新语义必须打通普通恢复和 auto-restore 两条链路。

检查参考

  • 手动 /use 或等价线程恢复路径中的 headless 恢复提示
  • 启动期 auto-restore / sticky failure 的 notice 映射
  • 现有 thread_cwd_missing 回归,避免把“无 cwd 元数据”和“cwd 已失效”混为一谈
  • Windows 上原始失败症状是否仍能稳定复现;如不能,也要用平台无关测试证明新边界

收尾参考

  • 如果最终引入新的恢复错误码或用户语义,回写 issue body 中的结论
  • 若错误模型或恢复状态机发生变化,检查是否需要同步 docs/general/remote-surface-state-machine.md
  • 若最终设计涉及 Feishu/notice 用户语义分层变化,检查是否需要补充对应文档或测试说明

补充说明

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:daemonDaemon server, admin API, and runtime controlarea:runtimeRuntime manager, process lifecycle, and persisted statebugSomething isn't workingstatus:needs-planTechnical investigation is sufficient, but the staged plan is not yet execution-ready

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions