feat(m195): 导入管线取消令牌与原子回滚(取消不是失败)

M190 复核 §3 A-2:导入管线没有取消令牌,「取消导入」只改界面状态,后台仍跑完并写盘
(半包 / 覆盖风险,丢数据档)。本轮把取消做成管线级:

- core 新增 `import/cancel.rs`:`ImportCancel` 令牌(可 `from_flag` 接管既有
  `Arc<AtomicBool>`)+ 内部 `Stopped::{Cancelled, Failed}` 两条停止路线;
- 结果类型单列 `ImportStatus::Cancelled`(`error` 恒为 None、退出码 130、文案 key 独立),
  与 `Failed` 是两条路——UI 不弹红、脚本不会把「改主意」记成「装坏了」;
- 七个检查点:入口 → 解析后 → 补全后 → 开暂存目录前 → 提交前 → 合并态逐文件 → 登记前;
  解压 / 远端下载各逐条检查;单条下载在飞不可中断(fetcher 无取消参数)已在文档里说准;
- 取消后不留半包 / 不覆盖已有实例:暂存目录整个删、合并态 journal 逆序回滚(备份改回原名)、
  落盘后取消连内容一起回滚;`files / conflicts / missing / bytes_written` 清零,不留假账;
- presenter:同一个取消标记现在真交给 core(`with_cancel`),取消后不再「后台跑完并登记」;
  CLI 增 `cancelled → 130` 一档与三档人读名。

守门三条各带变异证明(见 mission 报告):删回滚 → 红、删登记前检查 → 红、把取消并进
`fail()` → 红。

没验:超大整合包的真实 IO 下取消响应延迟(最坏 = 当前一条文件读完/下完 + 一次回滚)、
单条下载在飞不可中断、macOS / Linux 未跑。
This commit is contained in:
AskaEth committed 2026-11-09 05:06:52 +08:00
1 parent 3116f43c9f
commit 1441a240fe
18 files changed
+1144 -189

No files matched your search

+14
View File
@@ -11,6 +11,11 @@
//! | `success` | `0` | 全部落地、无冲突 |
//! | `partial` | `3` | 有缺文件或有保留用户文件的冲突(实例可用但不完整) |
//! | `failed` | `1` | 致命失败,**已整包回滚** |
//! | `cancelled` | `130` | 用户取消(管线在检查点停手并回滚,**不是失败**) |
//!
//! `130`(= `128 + SIGINT`)是"被用户中断"的惯例码:CLI 这一轮没有取消入口(一次性命令,
//! Ctrl-C 直接杀进程),但 `ImportStatus::Cancelled` 是 core 的公开结果,脚本读 `--json` 时
//! 必须能把它与 `failed` 分开——所以退出码与文案在这里就位,接中断信号是下一步的事。
//!
//! `--json` 的信封在三种状态下**都是** `{"ok":true,"data":{…}}`(命令跑完了),分流看
//! `data.status` / `data.error.code` 或退出码;`ok:false` 只用于"命令没跑起来"(参数错、
@@ -208,6 +213,15 @@ fn status_label(report: &ImportReport) -> &'static str {
Some(RollbackStage::Incomplete) => "失败(回滚不完整)",
None => match report.status {
ImportStatus::Success => "成功",
// 取消**不是失败**:不说"失败",也不说"部分成功"(那会让人以为装了一半)。
// 但回滚不完整时也不许说"与导入前一致"——与失败路径同一套诚实口径。
ImportStatus::Cancelled => {
if report.rolled_back {
"已取消(目标目录与导入前一致)"
} else {
"已取消(回滚不完整,目标目录可能与导入前不同)"
}
}
_ => "部分成功",
},
}