MooTool Next Compose 的 Vault 外部冲突证据链实践:从 `prepare-vault-conflict-evidence.sh` 到产品主窗验收(DIFF-605)

原创2026-10-15 21:59:151,873 阅读
文章标签:开发工具桌面应用MCP 服务

MooTool Next Compose 的 Vault 外部冲突证据链实践:从 prepare-vault-conflict-evidence.sh 到产品主窗验收(DIFF-605)

MooTool Next Compose 是一套面向桌面端的多功能开发者工具箱(Compose Multiplatform 实现,next-compose 工程),其 Vault 功能为 JSON 数据与随手记提供本地文件库管理。当用户在编辑器内持有未保存的修改、而磁盘上的 Vault 文件被外部进程改写时,就会触发外部修改冲突——本文以 next-compose/docs/diff/605-vault-external-conflict-evidence-slice.md 为骨架,完整讲解该冲突场景的证据链构建:证据准备脚本如何生成隔离样本、呈现常量如何锁定路径与正文、自动化测试如何验证"监视器 → 冲突叠层"链路,以及产品主窗验收应当如何走查与登记。读完本文,你将掌握一套可复现的 Vault 外部冲突验收方法论:既能用脚本快速准备数据,也能用测试锁定行为,还能为最终产品窗截图保留清晰的手工走查步骤。

一、DIFF-605 定位:F04 §A 证据链的构成

DIFF-605 是 next-compose 工程内部一系列增量开发切片(DIFF)中的一环,其基线是 DIFF-604(Git merge §B / 帧 199),核心目标是为 F04 JSON Vault 外部冲突 补齐产品级证据链。整条链由三部分组成:

组成 角色 对应文件
VaultConflictProductEvidencePresentation 证据样本的路径、正文与匹配常量的单一事实源,对齐 prepare-vault-conflict-evidence.sh 产物 VaultConflictProductEvidencePresentation.kt
VaultConflictProductEvidenceFlowTest 锁定"监视器轮询 → 冲突叠层 Presentation 就绪"的自动化链路 VaultConflictProductEvidenceFlowTest.kt
VaultConflictProductEvidenceCaptureTest 输出证据帧 200-compose-vault-external-conflict-sample-reload-tab-focus.png(不重复 141 通用 reload 路径文案) VaultConflictProductEvidenceCaptureTest.kt

DIFF-605 明确"不重复 604":Git merge §B 与帧 199 属于另一条证据链,本文只聚焦 §A 的外部磁盘冲突场景。

二、证据准备脚本:隔离目录下的样本生成

prepare-vault-conflict-evidence.sh 是整个 §A 证据链的起点。它通过环境变量 MOOTOOL_COMPOSE_DATA_DIR 指向的隔离数据目录(由 product-evidence-common.sh 的 mootool_evidence_resolve_data_dir 解析:已设置则复用,未设置则 mktemp -d /tmp/mootool-compose-evidence-XXXX 新建),绝不污染本机默认的 Application Support 目录。脚本生成两类样本:

  • JSON 样本 data/vaults/json/sample.json,正文带 fixture: vault-external-conflict 标记;
  • 随手记样本 data/vaults/quick-note/sample-external.md(可选 §A 第二组 PNG),Markdown 前导元数据 title: Vault external conflict sample、syntax: text/markdown,正文含"随手记外部冲突走查"标记。

脚本末尾通过 mootool_evidence_assert_file 断言两个样本文件均已落盘,并输出后续手动走查所需的提示(包括步骤 3 的外部改写、步骤 3b/3c 的外部删除命令),同时打印以 # 开头的注释性提示。注意:脚本输出均为注释行加单行 export,不要 eval 整段输出(见 README-product-evidence.md 的明确警告)。

2.1 脚本关键产物一览

产物 路径 用途
sample.json $MOOTOOL_COMPOSE_DATA_DIR/data/vaults/json/sample.json §A JSON 外部冲突走查样本
sample-external.md $MOOTOOL_COMPOSE_DATA_DIR/data/vaults/quick-note/sample-external.md F01 随手记外部冲突样本(可选)
外部改写命令 — 步骤 3:printf '%s\n' '{"disk":true}' > …/sample.json
外部删除命令 — 步骤 3b:rm …/sample.json;步骤 3c:rm …/sample-external.md(均须先打开文件并保持脏编辑)

三、呈现常量:样本路径与正文的单一事实源

脚本写出的样本内容并非随意,而是与 VaultConflictProductEvidencePresentation.kt 中定义的常量一一对应,形成"脚本产物 = 测试断言"的等价链:

  • EVIDENCE_JSON_RELATIVE_PATH = "sample.json"、EVIDENCE_QUICKNOTE_RELATIVE_PATH = "sample-external.md";
  • JSON 初始正文含 "fixture": "vault-external-conflict"、"step": "open in app then edit without saving",匹配函数 matchesEvidenceInitialJson 以 marker 字符串命中判断;
  • 磁盘改写正文固定为 {"disk":true}(对应脚本注释中的 printf 示例);
  • 随手记初始正文含 随手记外部冲突走查 标记,磁盘改写正文为 disk rewrite from terminal;
  • matchesEvidenceWalkthroughPath 仅接受 sample.json / sample-external.md 两个相对路径。

这样设计的好处是:测试断言不再依赖脆弱的全文比较,而是以 fixture 标记做特征匹配;同时呈现常量本身就是"脚本应产出什么"的可执行文档,脚本若被改坏,测试立即失败。

四、自动化验证:监视器 → 冲突叠层的两条测试路径

4.1 Flow 测试:真实监视器链路

VaultConflictProductEvidenceFlowTest.kt 在临时目录中构建完整的应用容器(AppContainer + SettingsRepository + AppDatabase + HistoryRepository + SessionStore),随后:

  1. 用呈现常量创建 sample.json 并写入初始正文;
  2. 构造一个 JsonSession,把编辑器缓冲改成 dirty editor buffer(对应"打开后编辑不保存");
  3. 启动 VaultRevisionMonitor(轮询间隔 60ms)并 rebaseline() 建立基线;
  4. 通过 container.jsonVault.write 写入 EVIDENCE_JSON_DISK_REWRITE 模拟外部改写;
  5. 断言监视器在 3 秒内回调、VaultConflictState.relativePath == "sample.json"、editorText 保留脏缓冲、diskText 等于磁盘改写正文。

这里涉及两个底层组件:VaultRevisionMonitor(VaultRevisionMonitor.kt)以固定间隔对 Vault 根目录做文件快照指纹比对(SHA-256,超过 8 MiB 的文件仅记 size),通过 expectedHashes 区分"应用自身写入"与"外部变更";VaultConflictEngine.decide(VaultConflictEngine.kt)则根据"编辑器文本 vs 已保存文本 vs 磁盘文本"三元组归类出 Reload / Conflict / Deleted / Ignored 等变更类型。从测试结构可以推断:jsonVaultApplyExternalChange 是监视器回调进入会话层、最终产出 VaultConflictState 的关键接线函数。

4.2 Capture 测试:焦点帧证据输出

VaultConflictProductEvidenceCaptureTest.kt 是 Compose UI 测试:在 680×460 的测试窗口中渲染 VaultConflictDialog,把"重新加载"按钮挂上 FocusRequester,请求焦点后用 captureToImage 截取整窗,写入 docs/evidence/2026-09-15-inspector-screencapture/windows/200-compose-vault-external-conflict-sample-reload-tab-focus.png,并断言焦点环颜色像素(0x316DC0)不低于 8 个——用像素级断言锁定"焦点确实落在 reload 按钮上"。

需要强调的是:该帧是 Compose 测试帧(非产品主窗),只能佐证焦点与 UI 结构,不能代替产品主窗的 runDistributable 手工 PNG(results.md 中对此有专门说明)。

4.3 脚本等价链测试

ProductEvidencePrepScriptTest.kt 用 JUnit 的 ProcessBuilder 直接执行 prepare-vault-conflict-evidence.sh(注入 MOOTOOL_COMPOSE_DATA_DIR 指向临时目录),随后断言:

  • data/vaults/json/sample.json 存在且 matchesEvidenceInitialJson 命中;
  • data/vaults/quick-note/sample-external.md 存在且 matchesEvidenceInitialQuickNote 命中;
  • 脚本退出码为 0。

它把"路径与正文"的断言从 UI 层下沉到脚本层,与 Flow 测试共同构成 DIFF-605 的三段式验证。

五、冲突 UI 的展示规则(对齐 Electron 行为)

冲突叠层不是只弹一个对话框,其按钮与文案受 VaultConflictPresentation.kt 的统一规则驱动:

  • showReloadAction(deleted) = !deleted:外部删除时不显示"重新加载",主操作变为"另存副本"(saveCopyProminent(deleted));
  • hintMessageKey(deleted):区分"修改 vs 外部删除"两条说明文案;
  • buildUnifiedPreview:用 DiffEngine.compare 生成最多 4000 字符的统一 diff 预览;showDiffPreview(deleted) = !deleted,删除态只显示删除说明而不做 diff 预览;
  • conflictCopyName(VaultConflictEngine.kt):保存副本命名规则为 文件stem.local-<epochMs>.ext。

这些规则在 §A 走查的第 5 步(重新加载 / 保存副本 / 保留编辑三分支)中被逐一验证。

六、产品主窗手动验收走查(§A 步骤)

自动化链只能覆盖到叠层与焦点帧,最终产品窗验收仍需人工 GUI 走查(按 results.md §A 的步骤):

cd /path/to/next-compose
export MOOTOOL_COMPOSE_DATA_DIR="$(mktemp -d /tmp/mootool-compose-evidence-XXXX)"
./scripts/prepare-vault-conflict-evidence.sh   # 末尾自检 sample.json;勿 eval 整段输出
./gradlew :composeApp:runDistributable --offline
  1. Vault 根为 $MOOTOOL_COMPOSE_DATA_DIR/data/vaults/json/,启动主窗后打开 JSON,在 Vault 中打开 sample.json;
  2. 在编辑器内修改内容不要保存(状态栏应出现未保存 • 标记);
  3. 用 Finder/编辑器直接改写同一 sample.json,或另开终端执行 printf '%s\n' '{"disk":true}' > "$MOOTOOL_COMPOSE_DATA_DIR/data/vaults/json/sample.json";
  4. 等待监视器刷新(默认轮询间隔 400ms,测试态可缩短至 60ms,见 VaultRevisionMonitor 的 intervalMs),应弹出 外部修改冲突 对话框(重新加载 / 保存副本 / 保留编辑),状态栏可显示 vault.conflict.banner;
  5. 分别验证三条分支:重新加载恢复磁盘内容、保存副本生成 sample.local-<epoch>.json(不会覆盖原文件)、保留编辑关闭叠层且编辑器内容不变;
  6. 截图登记到 docs/evidence/2026-09-15-inspector-screencapture/windows/NNN-json-vault-external-conflict-product.png。

无 GUI 环境下,可先跑非交互冒烟脚本 verify-product-evidence-prep.sh(会校验 §A 的两个样本文件、Git merge/rebase 冲突仓库、IME 样本与 tray 基线),或直接执行测试 ./gradlew :composeApp:desktopTest --offline。

七、验证命令与全链路上下文

DIFF-605 的验证命令为:

cd /path/to/next-compose
./gradlew :composeApp:desktopTest --offline

要求在 JDK 21 环境下执行。从 results.md 的记录可知:DIFF-513 已跑通 verify-product-evidence-prep.sh 与 ProductEvidencePrepScriptTest(隔离目录下三套 prepare 脚本退出码为 0,merge 仓库 conflict.json 处于 U 未合并状态),但 runDistributable 与产品窗 PNG 仍需人工 GUI 执行,因此 acceptance.md 中 F04/F01 冲突 UI 仍标记为待验收。

需要留意 DIFF-605 的后续延伸:同样的证据链方法论被复用到 sample-external.md(DIFF-607,帧 202)、外部删除冲突(DIFF-610,帧 205;DIFF-611,帧 206),它们共享 VaultConflictProductEvidencePresentation 常量与 VaultConflictPresentation 规则。整体验收清单见 results.md,脚本总览见 README-product-evidence.md。

八、小结

DIFF-605 展示了一条完整的"证据链工程"路径:脚本负责可复现地准备隔离数据,Presentation 常量负责统一路径与正文口径,Flow/Capture/PrepScript 三层测试分别锁定行为、焦点帧与脚本产物,最后以产品主窗手工走查补齐真实 GUI 证据。对于任何需要在桌面应用中处理"编辑器内脏数据 vs 外部磁盘改写"的开发团队,这套"脚本生成 + 常量对齐 + 分层测试 + 人工走查"的组合都是值得借鉴的验收范式。

登录后查看全文
MooTool