背景
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 错误边界和用户可见恢复提示,不建议跳过。
建议范围
明确根因边界
区分“目录元数据缺失”与“磁盘目录缺失”
不要把“启动失败后目录 stat 不通”直接等价成“目录被删除”
把目录存在性校验前移到 headless 启动前
在真正进入 cmd.Start() 之前形成明确的领域错误
避免依赖平台文案或 OS 错误字符串
对齐现有恢复错误模型
评估是扩展 thread_cwd_missing 语义,还是补一个新的、贯通 daemon/orchestrator 的恢复错误码
补全 auto-restore 用户提示
HeadlessRestoreLaunchFailureCode(...) / 对应 notice 映射需要覆盖该场景
补测试
普通恢复路径
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 用户语义分层变化,检查是否需要补充对应文档或测试说明
补充说明
背景
PR #682 试图修复一个 headless 会话恢复缺口:当历史线程关联的工作区目录在磁盘上已被移动或删除时,managed headless 启动仍会把旧目录作为
cmd.Dir传给子进程,最终在exec阶段失败。基于当前
master的代码复核,问题仍然存在,但 PR #682 的修复方式不建议直接合并。相关链接:
目标
把“线程/工作区元数据仍在,但实际工作目录已不存在”这一类 headless 恢复失败,收敛为明确、可解释、跨链路一致的领域错误,而不是把用户暴露给通用启动失败或 OS 原始报错。
完成标准
headless_restore_start_failed。现状证据
当前
master里的相关路径:internal/app/daemon/app_headless.go:168-192workDir := firstNonEmpty(command.WorkspaceKey, command.ThreadCWD)a.startHeadless(... WorkDir: workDir ...)handleManagedHeadlessLaunchFailure(command, err, now)internal/runtime/subprocess.go:48-53cmd.Dir = opts.WorkDircmd.Start()如果目录失效会直接返回底层错误internal/core/orchestrator/service_snapshot_runtime.go:530-548NoticeForProblem(...)internal/core/orchestrator/service_surface_thread_selection.go:664-675headless_restore_start_failedinternal/core/orchestrator/service_surface_thread_attach.go:358-368thread_cwd_missing语义,但只覆盖“没有可恢复目录元数据”,不覆盖“目录元数据还在、但磁盘目录已不存在”对 PR #682 的结论:
os.Stat(workDir)猜根因”。stat失败,就把错误改写成headless_workspace_dir_missing,这会把权限、网络盘、暂时不可访问等非缺失场景也误判进去。已知约束 / 担心点
thread_cwd_missing这类恢复领域错误;新方案应尽量和现有错误模型对齐,避免平行引入只在 daemon 层消费的新语义。执行决策
建议范围
cmd.Start()之前形成明确的领域错误thread_cwd_missing语义,还是补一个新的、贯通 daemon/orchestrator 的恢复错误码HeadlessRestoreLaunchFailureCode(...)/ 对应 notice 映射需要覆盖该场景stat/访问失败的负例,避免误判实现参考
internal/app/daemon/app_headless.gointernal/runtime/subprocess.gointernal/core/orchestrator/service_snapshot_runtime.gointernal/core/orchestrator/service_surface_thread_selection.gointernal/core/orchestrator/service_surface_thread_attach.goexplainMissingWorkspaceLaunchError(...)和新增测试可作为问题样本,但不建议原样合并当前 preferred direction:
检查参考
/use或等价线程恢复路径中的 headless 恢复提示thread_cwd_missing回归,避免把“无 cwd 元数据”和“cwd 已失效”混为一谈收尾参考
docs/general/remote-surface-state-machine.md补充说明
master上按正确边界重做。