Files
ycode/docs/协作/项目工作空间与后台活动-实施方案-01.md
T

329 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 项目、工作空间与后台活动实施方案 01
日期:2026-10-21。代码核对基线:`590d7ae2fafc2c6b378e316be2f3daab5a98831f`。
状态:**方案已落地,尚未实施;足以进入技术验证和分阶段开发,不代表 SDK 能力或产品验收已经通过。** 本文收敛已讨论的《YCode 项目与工作空间生命周期方案 v2》。不复用 B05 编号,不替代既有协作任务;后续实施编号由协作计划分配。
## 1. 已确认的产品规则
1. 一个任务保留同一段主会话,可以从主空间切到新 worktree,再回到主空间。用户不需要另外添加工作区、另开对话或手动结束 Run。
2. **当前绑定可以变,历史执行归属不能变。** 每次 Run 保存实际空间、环境、cwd、模型和权限快照;旧消息、工具记录和后台活动不会随当前绑定迁移。
3. worktree 是工作空间的来源类型,不是另一层导航。主工作副本与 linked worktree 对应空间在执行层面平级,不要求目录物理嵌套。
4. 侧栏只做项目折叠、任务平铺,不展开 Session、Run 和进程树。空间管理放在项目菜单或独立入口。
5. 用户明确要求“创建 worktree 并在里面继续”时,宿主负责创建、登记、切换;仅要求创建时不自动切换。
6. 只管理 YCode 启动并纳入管理的命令、子 agent 及其受管后代,不扫描、接管或结束外部命令。
7. 子 agent 与后台进程共用输入区域上方的独立活动条,用户和 agent 都能查询状态、查看日志、请求结束。
8. 切换不迁移、不自动结束旧活动;清理空间前,才必须核实活动退出、成果保全和其他引用。
9. 外部删除 worktree 经确认后,原会话自动回到已验证的主空间。离线、权限错误、超时不等于删除;主空间也不可用则暂停。
10. 自动回退不意味着自动重放工具、自动合并或认定交付完成。合并、删除工作副本、删除分支、任务归档是独立动作。
**切换闭环**解决后续调用确实进入新空间、旧活动可管、历史不漂移;**清理闭环**解决成果已处理、受管占用已结束、引用已处置,再安全删除。二者不能合成一个“完成”按钮而省略检查。
## 2. 概念与生命周期
| 实体 | 含义 | 关系与限制 |
|---|---|---|
| Project 项目 | 长期目标、资料、默认配置与任务集合 | 可关联多个空间,普通办公项目可以无 Git |
| Workspace 工作空间 | 某环境中的一份文件工作副本 | 稳定 ID、单根目录、单环境;根目录不是 OS 安全沙箱 |
| Environment 执行环境 | 机器、连接状态、运行时与能力 | 首期只登记本机;手机/网页未来可只是控制入口 |
| Task 任务 | 目标、验收与交付 | 首期一个任务一段主会话;完成回复不等于目标验收 |
| Session 会话 | 消息、工具记录与执行历史 | 当前绑定可变;切换仍打开原对话 |
| Run 执行 | 一次受控执行与配置快照 | 首期每个 Run 的空间不可变;允许同一会话透明续接 |
| Activity 后台活动 | 受管子 agent、进程或 shell 的生命周期 | 永远绑定启动时的 Run、环境和空间 |
```text
项目
├─ 关联空间:主空间 / worktree A / worktree B
└─ 任务(侧栏平铺)
└─ 主会话
├─ 当前绑定 → 一个空间,带修订号
├─ Run 1 → 主空间的不可变快照
├─ Run 2 → worktree A 的不可变快照
└─ 后台活动 → 各自启动时的空间,可跨越绑定切换继续运行
```
空间可以被多个项目引用,共享的是实际文件,不产生隔离;同一路径在不同环境中不是同一空间。空间创建者、当前引用者、控制活动的权限必须分别建模,创建者不因此取得删除其他会话占用空间的权利。
正常链路:讨论 → 创建并切换 worktree → 执行和管理后台活动 → 交付/验收 → 明确完成合并、PR 或补丁等交付 → 返回主空间 → 停止并核实旧活动 → 保全检查 → 清理副本 → 保留历史。验收退回沿原任务继续新 Run;Run 失败不回滚文件;任务归档不自动杀进程或删空间。普通聊天不强制走验收流。
## 3. 当前代码事实与待验证项
以下为基线源码/本地声明核对,不是新功能交付证明;路径均相对仓库根目录。
| 位置 | 当前事实 | 本次需要补齐 |
|---|---|---|
| `src/shared/workspaces.ts`、`src/shared/projects.ts` | 空间记录以 id/name/root 为中心,项目可关联空间 | 来源、环境、仓库身份、主空间关系与可用性 |
| `src/main/` 的项目、空间、会话存储及 `src/main/session-factory.ts` | 现有会话保存 projectId/workspaceId/cwd 等上下文,session-factory 拒绝项目与上下文空间不一致 | 可变当前绑定和不可变 Run 快照分离;迁移不可只取全局选中空间 |
| `src/main/git.ts` | 现有 Git 分支/HEAD 查询不是 worktree 生命周期管理器 | 受管创建、验证、清理与成果检查 |
| `src/main/agent/claude/claude-kernel.ts` | Bash/PowerShell 交给 SDK;tool_use/tool_result 形成工具记录 | 工具返回不等于后台进程退出;接入受管任务生命周期 |
| `src/shared/types.ts` 中 ToolCallRecord | running/ok/error 等描述工具调用 | 不能直接充当进程注册表 |
| `src/main/terminal-host.ts` | 内存 terminalsMap,PTY 输出、shell 退出、原始输入与 kill | shell 生命周期不覆盖每条命令或脱离 shell 的后代 |
| `src/renderer/src/components/ChatPane.tsx` | composeComposer 未传入任务活动,已有 floatingSurface/queuePanel | 独立 activitySlot,不挤入互斥授权或队列浮层 |
| `src/renderer/src/workbuddy/composer/input-composition.ts` | 有任务位置接口,尚未形成持久活动接线 | 复用位置思路而不是宣称已有完整活动栏 |
| `src/renderer/src/workbuddy/composer/ComposerControls.tsx` | 授权/浮层/队列布局,onStop 对应整个 Run | 单活动停止不能复用整轮取消 |
| `src/renderer/src/workbuddy/conversation/SpecializedBlocks.tsx` 的 TaskBlock | 消息内子 agent 工具记录 | 不是独立、持续可控的后台活动栏 |
本地安装的 Claude Agent SDK **0.3.304** 类型声明(`node_modules/@anthropic-ai/claude-agent-sdk/sdk.d.ts`)包含 `task_started`、`task_progress`、`task_notification`、`background_tasks_changed` 及 Query 的 `stopTask`、`backgroundTasks`。参考声明区域约 6181–6288、3769、3273 行;版本变化应以实际类型名定位。
注意:Query 的 `backgroundTasks(toolUseId?)` 是将前台任务转为后台的操作,不是列表查询;任务列表观测来自 `background_tasks_changed` 等事件,不能因名称相似误调用。
这些声明只证明接口存在,**不证明**当前内核已消费它们,不证明所有 CLI 版本覆盖一致,也不证明任务快照跨 SDK 重启持久化。SDK taskId 是 SDK 任务身份,不是 OS PID,更不保证其全部后代退出。cwd 重绑定、会话 resume、工具边界续接、旧 controller 的后台任务可控性必须做真实 SDK 技术验证。
本方案没有执行原版 WorkBuddy 采集,也不形成新的原版行为结论。后续原版视觉/行为核验仍遵守 AGENTS.md 的 REA 要求。
## 4. 最小数据契约(拟议,非已有接口)
先使用现有本地存储风格和宿主服务,不预先建设远程调度平台。以下类型表达字段职责,落地时以实际序列化与项目类型约定调整,不把路径或类型草案冒充现有实现。
```ts
type ID = string;
type WorkspaceAvailability = 'available' | 'missing' | 'unavailable' | 'unknown';
interface WorkspaceBinding {
workspaceId: ID;
revision: number; // 每次成功变更递增,队列/工具校验使用
}
interface WorkspaceSpec {
id: ID;
environmentId: ID;
root: string;
source: 'directory' | 'git-primary' | 'git-worktree';
repositoryIdentity?: string; // 宿主核实,不能仅依赖 remote URL
primaryWorkspaceId?: ID;
creatorSessionId?: ID;
availability: WorkspaceAvailability;
}
interface SessionWorkspaceState {
sessionId: ID;
projectId: ID;
binding: WorkspaceBinding;
}
interface ExecutionSnapshot {
workspaceId: ID;
bindingRevision: number;
environmentId: ID;
cwd: string;
workspaceIdentity: string;
modelSnapshotRef: ID;
permissionSnapshotRef: ID;
}
interface RunWorkspaceRecord {
runId: ID;
sessionId: ID;
execution: Readonly<ExecutionSnapshot>;
continuationOfRunId?: ID;
switchOperationId?: ID;
}
interface QueuedWorkspaceRef {
queueItemId: ID;
executionTarget: WorkspaceBinding; // 入队捕获,不是发送时随意取最新空间
status: 'queued' | 'blocked-workspace-change' | 'dispatched' | 'cancelled';
// 权限快照沿用原规则,不因重新绑定空间而放宽
}
type ActivityState = 'starting' | 'running' | 'stopping' |
'exited' | 'stopped' | 'failed' | 'unknown';
interface ManagedActivity {
id: ID;
kind: 'agent' | 'process' | 'shell';
ownerSessionId: ID;
taskId: ID;
runId: ID;
execution: Readonly<ExecutionSnapshot>;
parentActivityId?: ID;
state: ActivityState;
readiness: 'not-applicable' | 'pending' | 'ready' | 'failed' | 'unknown';
trackingCoverage: 'owned-tree' | 'sdk-task-only' | 'shell-only' | 'unknown';
controllerId: ID;
sdkTaskId?: string;
sdkTaskRunId?: string; // SDK 提供的本次任务执行身份,区分 resume/复用
instanceGeneration: number; // controller/activity 实例代次
processIdentity?: { pid: number; startedAt: string; instanceId: string };
logRef?: ID;
updatedAt: string;
exit?: { code?: number; signal?: string; reason: string; observedAt: string };
}
interface WorkspaceOperation {
id: ID; // 幂等键;结果和恢复日志按此关联
sessionId: ID;
from: WorkspaceBinding;
targetWorkspaceId: ID;
stage: 'preparing' | 'prepared' | 'committed' | 'completed' |
'failed' | 'reconcile-required';
continuationRunId?: ID;
errorCode?: string;
}
```
`readiness` 与存活状态分开:running+pending 不能显示“已就绪”;已退出不能残留绿色 ready。退出需要可信证据。SDK 任务没有 OS 身份时不填造 PID;其 coverage 只声明 SDK 所能证明的范围。持久化 PID/时间仅辅助核验,不替代可靠句柄、控制器身份或运行时证明。
绑定的单一事实来源由宿主管理,会话/任务展示引用它,不各存一套会互相覆盖的 currentWorkspaceId。历史快照与活动执行快照只追加,不因重命名、切换或清理重写。日志不保存凭据或无关环境秘密。
## 5. 空间操作工具与返回契约
建议统一宿主服务,UI 和 agent 共用;工具名称是暂定职责,不是新增已发布 API。
| 拟议操作 | 主要输入 | 成功结果与限制 |
|---|---|---|
| `workspace.createWorktree` | sessionId、expectedRevision、基准引用、分支、目标路径、continueHere、operationId | 已验证空间 ID、创建事实;continueHere=true 才进入切换事务 |
| `workspace.switch` | sessionId、targetWorkspaceId、expectedRevision、operationId | 提交后绑定、续接 Run ID;不能只更新 UI |
| `workspace.returnToPrimary` | sessionId、expectedRevision、operationId | 核实主空间后走同一 switch;不猜测 cwd 或默认分支 |
| `workspace.inspectCleanup` | workspaceId | 阻塞项、成果/引用/活动检查摘要和短期检查版本;不是删除授权 |
| `workspace.cleanup` | workspaceId、检查版本、operationId、明确清理意图 | 删除副本的实际结果;执行前重新核实,不默认删分支 |
| `activity.list/status` | 当前会话范围、可选 activityId | 受授权活动、状态来源、观测时间与覆盖范围 |
| `activity.readLogs` | activityId、游标、大小上限 | 分页日志,不借路径参数读任意文件 |
| `activity.stop` | activityId、operationId | 停止已受理或终态;受理不等于已结束 |
工具由宿主重新校验会话、项目关联、环境能力、空间身份、路径冲突与权限。工具请求不能通过伪造 sessionId 或任意 PID 扩大范围;同一项目不自动获得其他会话活动的控制权。已明确授权管理的共享活动才可访问。
错误至少区分:`STALE_BINDING`、`TARGET_UNAVAILABLE`、`WORKSPACE_IDENTITY_CHANGED`、`FOREGROUND_BUSY`、`PENDING_APPROVAL`、`CONTROLLER_RETENTION_REQUIRED`、`TRACKING_UNKNOWN`、`UNRESOLVED_CHANGES`、`OTHER_REFERENCES`、`RECONCILE_REQUIRED`。错误带真实阶段、可恢复动作和已产生副作用,不以“操作失败”掩盖已创建目录,也不自动重试未知副作用。
重复 operationId 查询/返回原操作结果,不重复创建 worktree、发消息或停止无关活动。未完成记录先对账;不同参数复用同一 ID 应拒绝。
## 6. 切换状态机与 Run 边界
### 6.1 首期保守路径
**首期选用同一会话内透明续接新 Run,不假定 SDK 在现有 Run 内可靠改变 cwd。** SDK 技术验证若证明安全的执行片段方案,再单独评审;不把它作为首期交付的隐藏前提。
```text
请求切换
→ 锁定会话调度并核对 binding revision
→ 等待前台工具到安全边界 / 明确取消且确认终止
→ 核实目标、权限、旧 controller 可保留性
→ 准备新执行上下文与操作日志
→ 原子提交绑定和续接意图
→ 在原会话建立引用新快照的新 Run
→ 核实实际执行目录后放行后续工具
```
agent 调用切换工具时,该控制调用本身不能被纳入“等待所有前台工具结束”,否则会等待自己形成死锁。宿主先登记切换请求和阻止其他新派发;控制调用在旧上下文中恰好返回一次“已受理、待边界提交”,随后在可验证的工具边界执行提交与续接。受理不冒充切换成功,最终结果由 operationId 关联的宿主事件报告。旧 SDK 在控制工具响应入账前不得销毁;如何捕获这个边界属于阶段 A 的必验项。
用户无需点“结束 Run”。宿主负责边界、旧调用结果入账和透明续接;技术上的旧 Run 收敛不等于结束产品任务。不能假定所有 SDK 工具绝对串行:前台调用、已知并发调用、待处理授权都纳入调度检查,后台活动另行管理。
续接不得重复追加用户消息、重复工具结果或重新执行已完成工具。旧 Run 记为受控交接而不是伪造一次任务成功;新 Run 引用 continuationOfRunId,保留前后因果。新模型/权限的解析继续遵守现有消息快照规则,空间切换不变相提升权限。
关键门禁:如果关闭旧 SDK controller 会丢失仍运行的任务状态/stopTask 能力,**保留旧 controller 直到其受管任务结束,或阻止本次切换并说明需先停止哪些活动**。不能为“切换成功”关闭 controller,再把旧活动标记完成。宿主直接管理的进程可以独立于 Run/controller 生命周期,但必须验证这种独立性。
提交成功但新 Run 启动失败时,绑定保留已提交事实、会话暂停并报告失败,或通过新的受控操作回到原有效空间;不能后台偷偷回滚而让日志/UI 矛盾。只有新上下文已验证,才允许后续执行。
### 6.2 队列和待处理授权
- 每条队列消息入队捕获 workspaceId 和 bindingRevision,权限快照保持现有语义。
- 切换成功后,尚未派发且 revision 不匹配的消息进入 `blocked-workspace-change`;**不自动移到新空间、不悄悄留在旧空间执行**。
- 用户或拥有本会话管理能力的 agent 可明确选择重新绑定到已验证空间、保留旧目标待以后恢复,或取消。重新绑定产生审计事件和新的目标修订,不放宽原权限,不把旧已派发消息改成新消息。
- 外部删除触发自动回退也遵守上述规则:当前会话回主空间,不代表旧队列自动在主空间执行。
- 第一期有待处理授权时先阻断切换,允许用户完成/拒绝或显式取消旧请求;确认取消后再切换。不得把“允许”按钮的回调应用到另一个 Run/cwd。
- 异常删除或恢复使旧授权失去执行上下文时,旧请求失效,取消其尚未执行的调用。新 Run 真要执行相应动作,重新评估并重新请求授权,不能沿用一次性旧同意。
- 迟到的授权回复必须用 requestId、runId、workspaceId/revision 校验并拒绝过期结果;消息选择问答也不得驱动已失效的旧 Run。
### 6.3 原子性与故障恢复
不把 Git 创建目录、写存储和 SDK 启动假装成一笔可回滚数据库事务。使用最小持久化操作日志,加会话/空间锁和修订号校验;原子替换绑定记录时同时保留 operationId,跨文件结果通过日志对账。
| 中断位置 | 恢复规则 |
|---|---|
| 创建前失败 | 保留原绑定,记录失败 |
| Git 创建成功,登记或切换失败 | 保留新副本,报告其路径和未接管/待恢复状态,不擅自删除 |
| prepared 未 committed | 核实真实目录与 controller,再选择继续或终止;不能仅凭日志声称完成 |
| committed 后未确认新 Run | 保留已提交事实,禁止自动重放,恢复调度或标记 reconcile-required |
| 进程/任务状态无法核实 | 活动为 unknown,阻止相关清理;不重复启动可能仍活着的命令 |
| 清理时崩溃 | 对比 Git worktree 元数据、目录、登记与历史引用,记录实际残留,不自动 force/prune |
重启后未完成 Run、活动和操作先进入对账;原子 rename/日志仅改善一致性,不是跨进程无条件成功保证。任何恢复都不回滚已经发生的文件写入、发布或外部副作用。
## 7. 后台活动的真实生命周期
宿主统一 Activity Registry 作为 UI 和 agent 的事实来源。SDK 任务、宿主进程、PTY shell 使用各自 adapter,保留观测来源和覆盖范围,不把所有“工具调用成功”转换成“进程退出”。
```text
starting → running → exited / failed
└────→ failed
starting / running → stopping → stopped(有退出确认)
任意非终态 → unknown(断连、失去身份或跟踪)
unknown → 经可信对账恢复 running / stopping / 明确终态
```
自然退出与用户请求停止分开;收到停止请求只进入 stopping。停止超时保留 stopping 或 unknown,不移除活动。重复事件、乱序通知按 controllerId/taskId、SDK 的任务执行 run_id(如有)及实例代次、事件顺序/已核实终态归并,不能让旧 incarnation 的迟到进度覆盖新执行或退出结果。SDK 版本缺少足够执行身份、无法区分 resume/复用时转 unknown/对账,不能猜测合并。就绪探测是可选端口/健康检测,必须属于本活动且有明确目标,不因端口被其他程序占用就认定本服务 ready。
进程控制要求:
- 保存受管启动关系、句柄/启动身份,避免 PID 复用导致结束其他进程;代理端只接受 activityId。
- 停止 agent 不证明它的子进程退出;分别核实受管后代。宿主管理进程应验证 Windows 与其他目标平台的进程树策略。
- SDK background task 列表是该 controller 的任务快照,重启后空列表不能证明旧进程不存在。SDK-only coverage 不自动提升为 owned-tree。
- PTY shell 退出不证明其中所有命令退出。任意手输 shell 命令或脱离父进程的后台命令,要么通过受控启动协议纳管,要么明确其覆盖未知;不得靠日志猜测进程身份。
- 第一阶段覆盖宿主受管启动和经验证的 SDK task,禁止承诺“知道系统所有命令”。未覆盖的 YCode 启动方式会阻止其空间自动清理,而不是扫描外部命令补齐。
- 会话切换后旧活动继续显示来源空间;新空间启动同类服务仍可能有端口、数据库、凭据和共享 Git 状态冲突,worktree 不是完整运行隔离。
## 8. UI 落点与不变项
```text
▼ YCode
日常开发 主空间
开发登录功能 登录空间
修复加载问题 修复空间
后台活动 2(可折叠)
[子 agent] 审查登录实现 运行中 登录空间 [详情] [结束]
[进程] pnpm dev 已就绪 主空间 [日志] [结束]
─────────────────────────────────────────────
原输入区域 / 独占的授权或问答区域
```
新增独立 activitySlot,沿用原有间距、字体和样式变量,不新增复杂导航。活动多时折叠计数,详情和日志按需打开;退出活动保留可查记录,不无限占用输入区。
活动条与 floatingSurface、queueSlot、approvalSlot 分开:授权/选择卡独占输入区时,普通输入框仍隐藏;紧凑活动条可以保留,但不能遮挡选项或让输入框重新出现。发消息、整轮停止、队列和授权状态机不因活动条改变。单活动结束调用 activity.stop,不调用现有取消整个 Run 的 onStop。
侧栏任务 ID 与选中项不因切换改变,仅更新当前空间的轻量标记。活动显示启动空间,历史工具显示执行空间,不能都读取当前绑定渲染。正常切换无需额外长提示;阻塞、未知状态和失败必须在已有反馈位置明确说明。
## 9. 外部删除与清理门禁
打开会话、发消息、派发工具前校验空间,文件通知只做提前提示。确认删除需结合环境在线可访问、路径身份与 Git worktree 信息;目录暂时不可访问、权限错误、超时只能 unavailable/unknown。目录存在但被替换为另一仓库属于 identity-changed,不静默接管。
确认删除 → 阻止旧空间派发 → 收敛旧前台结果/停止状态 → 核实已登记主空间身份与权限 → 同一会话按切换事务自动回退 → 检查主空间现状再继续。旧后台活动仍保留可控;旧队列阻塞,旧授权失效;未知写入/发布不自动重放。主空间无效时暂停,不能随意选择全局空间。
清理只接受明确清理意图,使用空间级排他门禁阻止新受管执行,并在真正删除前重新校验:
1. 该空间的前台、后台、子 agent 及受管后代已确认终态,无 unknown 或覆盖缺口。
2. 未提交修改和未跟踪文件已有明确保全/处理结果;目录干净不是合并证明。
3. 未合并提交已有明确交付保全证据;合并路径须先确认目标分支合并成功,再清理,不能把清理操作变成隐式 merge。PR/补丁路径核实保全策略,不伪称已合并。
4. 其他任务/会话引用已显式处置;创建者不能越过其他使用者。历史引用保留,不是阻止所有清理的永久活动锁。
5. 需要返回的当前会话有有效主空间;当前绑定已迁移或在同一受控操作中完成迁移。
6. 删除前核对空间身份、检查版本和无新增调用,确保没有检查后又启动活动的竞态。
默认不 force 删除、不级联删分支、不删除历史。已有旧活动允许先切回主空间,但标记清理受阻。删除失败保留登记/残留状态,不宣称已完成。没有成果证据的外部删除只记异常与损失风险,不能记为成功交付。
## 10. 迁移与分阶段实施门禁
既有会话按已保存 workspaceId/cwd 迁移绑定,不能一律绑定当前全局空间。cwd 与登记不匹配时核对身份,无法核实则不可用;保留原始值供恢复。既有 Run 无可靠快照时标记历史来源未知,不回填一个“看起来正确”的空间。首期环境登记 local,不提前实现远程连接。备份/迁移版本与回退兼容策略随实际存储改动评审。
| 阶段 | 交付范围 | 进入下一阶段的门禁 |
|---|---|---|
| A 技术验证 | SDK 0.3.304 cwd/resume/工具边界;task 生命周期;controller 保留;Windows 受管进程身份与停止 | 记录真实可行路径及不支持项。若不能保留旧活动控制,采用切换阻断,不以 mock 通过替代 |
| B 核心绑定 | 最小类型、不可变快照、会话绑定、操作日志、队列/授权校验、创建/切换/返回 | 同一会话新 Run 真实 cwd 生效;故障与重复操作不重放、不误路由 |
| C 活动管理 | 注册表、SDK/进程 adapter、按活动查询/日志/停止、controller 生命周期 | 旧空间活动仍可管;终态可信;身份复用、重启未知、越权均正确处理 |
| D UI 集成 | 简单侧栏、独立活动条、轻量空间标记、单活动结束 | 授权/问答独占和原发消息/回合状态不回归,无额外导航树 |
| E 恢复与清理 | 确认删除回退、空间锁、成果/引用/覆盖门禁、迁移 | 故障注入和 Windows 真实验收通过,才开放受管自动清理 |
A 可用隔离 spike 验证而不改产品默认行为;后续分阶段交付不将未完成的能力暴露成成功按钮。新能力保留回退开关,关闭时不销毁已登记活动或历史;正在运行的活动仍须可观察和停止。
## 11. 验收清单与证据要求
| 场景 | 必须断言 | 验证层级 |
|---|---|---|
| 创建并继续/仅创建 | 前者登记并切换原会话,后者不改变绑定;不重复建目录 | 宿主集成 + 真实 Git |
| 当前绑定切换 | 新 Run 实际 cwd/仓库身份正确,旧 Run 快照不变 | 真实 SDK,不只 mock |
| 工具边界续接 | 旧工具仅执行一次,结果仅记一次,新工具进入新空间 | mock 故障注入 + 真实 SDK |
| 排队与授权 | 旧 revision 阻塞、重新绑定显式;迟到允许失效,新空间重新评估 | 状态测试 + UI |
| 旧后台活动 | 切换后仍显示旧空间、可查询/停止;关闭 controller 不丢管理 | SDK/宿主真实进程 |
| 存活/就绪/停止 | 活着未 ready、stop 请求未退出、超时 unknown 均不伪终态 | 受控长驻程序 + 平台测试 |
| PID/身份与权限 | PID 复用不误杀,任意 PID/其他会话活动操作被拒 | adapter 单测 + 平台集成 |
| 外部删除/不可用 | 确认删除才回退;离线/权限错误不回退;主空间无效暂停 | 文件/Git 集成 + 故障注入 |
| 崩溃恢复 | prepared/committed/删除中断均对账,不重放副作用、不假完成 | 持久化故障注入 |
| 清理门禁 | 脏文件、未跟踪文件、未保全提交、其他引用、unknown 均阻断 | 真实 Git fixture |
| 单活动结束 | 仅停止指定受管活动,不取消整轮或触及外部命令 | UI + 宿主集成 |
| 布局回归 | 授权/问答时不显示普通输入框,队列/活动互不遮挡,侧栏不增层 | YCode 截图/交互验收 |
| 迁移 | 原 cwd 保留,不匹配与旧历史未知如实呈现 | 存储 fixture |
真实 SDK、真实 Windows 进程与真实 UI 验收必须分别记录环境、版本、命令/操作和结果;mock 或类型检查不能证明 cwd 重绑定与进程树管理可靠。本文仅提交设计文档,未运行上述产品验收、未改动业务源码,后续交付必须逐项给出实际证据和仍未覆盖的范围。