advance 1-8 post-mvp execution batches
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
# Reasonix Worker A:GFM AST 降级策略与 process 收口
|
||||
|
||||
Project root:`/mnt/Data1T/mnote`
|
||||
|
||||
你不是唯一 worker。不要回滚、覆盖或清理他人的改动;只修改本任务 ownership 范围内的文件。不要提交 git。
|
||||
|
||||
## 目标
|
||||
|
||||
收口 `design/03-rust-web/process/3-13-rust-web-local-markdown-gfm-ast-parser-migration-v1.md` 的真实尾项:
|
||||
|
||||
1. 表格/不支持结构降级策略是否已有测试证据。
|
||||
2. `web_shell.rs` 中 `legacyStylesToTiptapMarks` 等 legacy marks 适配分支是否仍被当前 bootstrap 使用。
|
||||
3. 如果剩余项已可解释且测试通过,把 `3-13` 移动到 `design/03-rust-web/done/`;否则只更新未完成项和证据,不要强行归档。
|
||||
|
||||
## Ownership
|
||||
|
||||
允许修改:
|
||||
|
||||
- `design/03-rust-web/process/3-13-rust-web-local-markdown-gfm-ast-parser-migration-v1.md`
|
||||
- `design/03-rust-web/done/3-13-rust-web-local-markdown-gfm-ast-parser-migration-v1.md`(仅当归档)
|
||||
- `rust/crates/mnote-web/src/routes/local_folder_source.rs`
|
||||
- `rust/crates/mnote-web/src/routes/local_markdown_parser.rs`
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs`
|
||||
|
||||
禁止修改:
|
||||
|
||||
- AI / Hermes 工具文件
|
||||
- tree realtime / websocket 文件
|
||||
- 任何 `design/01-tree-first-graph-kernel/process/1-13-*` 之外的 batch 文件,除非只是记录本任务结果
|
||||
|
||||
## 执行要求
|
||||
|
||||
- 先审查现有测试:搜索 `local_markdown_save_writes_table_inline_marks`、`local_markdown_save_round_trips_tiptap_table_marks`、`local_markdown_save_table_handles_colspan_degradation`、`document_shell_bootstrap_preserves_inline_mark_conversion`。
|
||||
- 若降级策略已有足够测试,更新 `3-13` 的表格状态即可。
|
||||
- 若缺少最小测试,优先补 Rust 单测,不新增浏览器 smoke。
|
||||
- 不要直接删除 `legacyStylesToTiptapMarks`,除非你能证明当前 web shell 不再需要 legacy block -> Tiptap marks bootstrap,并且 `web_shell` 测试通过。
|
||||
- 若决定保留该分支,把 `3-13` 中“必须移除”改为“保留兼容层 + 退出条件”,然后归档。
|
||||
|
||||
## 验收命令
|
||||
|
||||
```bash
|
||||
cargo test -p mnote-web local_markdown -- --test-threads=1
|
||||
cargo test -p mnote-web web_shell -- --test-threads=1
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## 最终回复格式
|
||||
|
||||
- 修改文件列表
|
||||
- 归档/未归档判断
|
||||
- 运行过的命令和结果
|
||||
- 未完成项或风险
|
||||
@@ -0,0 +1,70 @@
|
||||
# Reasonix Worker B:Tree realtime live cache 可归档性复核
|
||||
|
||||
Project root:`/mnt/Data1T/mnote`
|
||||
|
||||
你不是唯一 worker。不要回滚、覆盖或清理他人的改动;只修改本任务 ownership 范围内的文件。不要提交 git。
|
||||
|
||||
## 目标
|
||||
|
||||
复核 `design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md` 是否仍应留在 process。
|
||||
|
||||
重点判断:
|
||||
|
||||
1. sidebar、page subtree、filetree 是否已经统一到当前 3000 主界面的 WS push + SSE fallback live cache。
|
||||
2. preferred snapshot 补偿链是否仍是阻塞,还是已成为 live cache 的正常 freshness 仲裁。
|
||||
3. 现有 smoke 是否足以覆盖 create / rename / move / archive / restore / purge / resync / reconnect 的双浏览器 no-refresh 行为。
|
||||
|
||||
## Ownership
|
||||
|
||||
允许修改:
|
||||
|
||||
- `design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md`
|
||||
- `design/03-rust-web/done/3-3-rust-web-tree-realtime-event-stream-v1.md`(仅当归档)
|
||||
- `design/03-rust-web/process/3-1-rust-web-long-term-checklist-v2.md`(只做状态/证据同步)
|
||||
|
||||
只读审查:
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/ws.rs`
|
||||
- `rust/crates/mnote-web/src/routes/tree_events.rs`
|
||||
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
|
||||
- `scripts/task123-rust-web-tree-live-stream-consumer-smoke.js`
|
||||
- `scripts/task165-*`
|
||||
- `scripts/task432-filetree-trash-page-dual-browser-no-refresh-smoke.js`
|
||||
- `scripts/task446-tree-rename-dual-browser-live-smoke.js`
|
||||
- `scripts/task447-tree-move-order-dual-browser-live-smoke.js`
|
||||
- `scripts/task448-tree-resync-recovery-dual-browser-smoke.js`
|
||||
- `scripts/task449-tree-sse-reconnect-snapshot-recovery-smoke.js`
|
||||
|
||||
禁止修改:
|
||||
|
||||
- Rust runtime 代码
|
||||
- smoke 脚本,除非只是明显的 node syntax 修复且需要说明
|
||||
- AI / markdown parser 文件
|
||||
|
||||
## 执行要求
|
||||
|
||||
- 使用 `rg` / CodeGraph / 文件读取审查当前 live cache 证据。
|
||||
- 至少运行相关 smoke 的 `node --check`。
|
||||
- 如果 `http://127.0.0.1:3000` 当前可用,优先实跑 1-2 个代表性 smoke:建议 `task446` 和 `task448` 或 `task449`。
|
||||
- 如果当前环境无法实跑,不能声称功能通过;只记录“文档证据足够/不足”和阻塞。
|
||||
- 若证据足够,把 `3-3` 移到 `done/` 并把 9.2 改为已完成记录。
|
||||
- 若证据不足,保留 `process`,写出最小剩余 checklist。
|
||||
|
||||
## 验收命令
|
||||
|
||||
```bash
|
||||
node --check scripts/task123-rust-web-tree-live-stream-consumer-smoke.js
|
||||
node --check scripts/task432-filetree-trash-page-dual-browser-no-refresh-smoke.js
|
||||
node --check scripts/task446-tree-rename-dual-browser-live-smoke.js
|
||||
node --check scripts/task447-tree-move-order-dual-browser-live-smoke.js
|
||||
node --check scripts/task448-tree-resync-recovery-dual-browser-smoke.js
|
||||
node --check scripts/task449-tree-sse-reconnect-snapshot-recovery-smoke.js
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## 最终回复格式
|
||||
|
||||
- 修改文件列表
|
||||
- `3-3` 是否归档及理由
|
||||
- 运行过的命令和结果
|
||||
- 仍缺的 smoke / 代码缺口
|
||||
@@ -0,0 +1,56 @@
|
||||
# Reasonix Worker C:AI 写入 CommandContext 守卫
|
||||
|
||||
Project root:`/mnt/Data1T/mnote`
|
||||
|
||||
你不是唯一 worker。不要回滚、覆盖或清理他人的改动;只修改本任务 ownership 范围内的文件。不要提交 git。
|
||||
|
||||
## 目标
|
||||
|
||||
收口 `1-6` Gap A7 中最后的 `ai.canWrite` 消费缺口:
|
||||
|
||||
1. 复核 `CommandContext.ai.canWrite` 是否已经被 Hermes / AI 写入路径实际消费。
|
||||
2. 若未消费,补最小守卫:只读 workspace、无写权限或 AI 写入未授权时,正文/页面写入类 Hermes tool 返回明确拒绝,不进入文件写入。
|
||||
3. 补最小 Rust 单测,避免只停留在 `core-protocol` evaluator 层。
|
||||
4. 更新 `design/01-tree-first-graph-kernel/process/1-6-next-phase-gap-closure-checklist-v1.md` 与必要的 `1-5` 状态。
|
||||
|
||||
## Ownership
|
||||
|
||||
允许修改:
|
||||
|
||||
- `design/01-tree-first-graph-kernel/process/1-6-next-phase-gap-closure-checklist-v1.md`
|
||||
- `design/01-tree-first-graph-kernel/process/1-5-next-phase-sequential-execution-checklist-v1.md`
|
||||
- `rust/crates/core-protocol/src/command.rs`
|
||||
- `rust/crates/mnote-web/src/hermes_tools/doc.rs`
|
||||
- `rust/crates/mnote-web/src/hermes_tools/page.rs`
|
||||
- 相关同文件测试
|
||||
|
||||
禁止修改:
|
||||
|
||||
- GFM parser / web_shell legacy marks
|
||||
- tree realtime / websocket 文件
|
||||
- OnlyOffice 文件
|
||||
|
||||
## 执行要求
|
||||
|
||||
- 先搜索现有写权限模型,不要凭空新增一套权限真相。
|
||||
- 如果已有 `AiAccessScope`、allowed roots、readonly workspace 或 local access policy helper,应复用。
|
||||
- `ai.canWrite` 是 command context 口径;如果 runtime 现有权限模型名称不同,可以用桥接 helper,但不要把 evaluator 复制到多个地方。
|
||||
- 拒绝响应必须可解释,不能 panic,不能静默成功。
|
||||
- 不要扩大 AI 功能面,只守卫已有写入路径。
|
||||
|
||||
## 验收命令
|
||||
|
||||
```bash
|
||||
cargo test -p core-protocol command_context -- --test-threads=1
|
||||
cargo test -p mnote-web hermes_tools -- --test-threads=1
|
||||
git diff --check
|
||||
```
|
||||
|
||||
如果 `hermes_tools` 过滤器过大或无匹配,请运行你新增测试的精确过滤命令,并在最终回复说明。
|
||||
|
||||
## 最终回复格式
|
||||
|
||||
- 修改文件列表
|
||||
- 是否实现运行时守卫
|
||||
- 运行过的命令和结果
|
||||
- 未完成项或风险
|
||||
@@ -0,0 +1,70 @@
|
||||
# Reasonix Worker A:Local Folder Tree Live 后端事件流
|
||||
|
||||
Project root:`/mnt/Data1T/mnote`
|
||||
|
||||
你不是唯一 worker。不要回滚、覆盖或清理他人的改动;只修改本任务 ownership 范围内的文件。不要提交 git。
|
||||
|
||||
## 背景
|
||||
|
||||
`design/01-tree-first-graph-kernel/process/1-8-mvp-post-process-execution-order-v1.md` 仍未整体完成,当前阻塞在 `3-3`:local_folder 默认路径的 Sidebar / FileTree 仍通过 `startLocalFolderSidebarWatch()` 轮询 `/api/tree/local-folder-watch` 后补拉 projection,没有进入统一 tree live consumer。
|
||||
|
||||
新 checklist:`design/03-rust-web/process/3-18-local-folder-tree-live-consumer-convergence-checklist-v1.md`
|
||||
|
||||
## Ownership
|
||||
|
||||
允许修改:
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/local_folder_events.rs`
|
||||
- `rust/crates/mnote-web/src/routes/stream_support.rs`
|
||||
- `rust/crates/mnote-web/src/routes/mod.rs`
|
||||
- 同文件测试
|
||||
|
||||
只读参考:
|
||||
|
||||
- `rust/crates/mnote-web/src/local_folder_watcher_registry.rs`
|
||||
- `rust/crates/mnote-web/src/routes/local_folder_source.rs`
|
||||
- `rust/crates/mnote-web/src/routes/kernel.rs`
|
||||
|
||||
禁止修改:
|
||||
|
||||
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
|
||||
- `scripts/*.js`
|
||||
- 其他 design 文档
|
||||
|
||||
## 目标
|
||||
|
||||
1. 给 local_folder 提供 tree live 事件形状,至少支持 snapshot / resync。
|
||||
2. payload 中必须携带:
|
||||
- `kind: "snapshot"` 或 `kind: "resync"`
|
||||
- `stream: "workspace"`
|
||||
- `projection: "sidebar_tree"`
|
||||
- `sourceKind: "local_folder"`
|
||||
- `rootUri`
|
||||
- `workspaceId`
|
||||
- `revision`
|
||||
- `data.dataset.kernel_sidebar_projection`
|
||||
- `data.dataset.kernel_file_tree_projection`
|
||||
3. 复用 `LocalFolderWatcherRegistry`,不要新增第二套 watcher。
|
||||
4. 如果不能可靠生成细粒度 delta,本阶段允许 watcher change 后发 resync snapshot。
|
||||
5. 不把 local_folder 变化写回 Convex command log。
|
||||
|
||||
## 建议实现
|
||||
|
||||
- 可以扩展 `/api/local-folder/events`,让它在 query 中带 `treeLive=1` 或 `scope=tree` 时输出 `snapshot` / `resync` 事件;保留原有 `ready` / `change` 给 web_shell 编辑器外部变更使用。
|
||||
- snapshot 可复用 `load_local_folder_page_tree_snapshot(rootUri)` 和 `load_local_folder_file_tree_snapshot(rootUri)` 组装 dataset。
|
||||
- 事件 id 可继续使用 revision。
|
||||
|
||||
## 验收命令
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_folder_events -- --test-threads=1
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web stream_support -- --test-threads=1
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## 最终回复格式
|
||||
|
||||
- 修改文件列表
|
||||
- 新增/变更的 API 行为
|
||||
- 运行过的命令和结果
|
||||
- 未完成项或风险
|
||||
@@ -0,0 +1,47 @@
|
||||
# Reasonix Worker B:Local Folder Tree Live 前端 consumer 接入
|
||||
|
||||
Project root:`/mnt/Data1T/mnote`
|
||||
|
||||
你不是唯一 worker。不要回滚、覆盖或清理他人的改动;只修改本任务 ownership 范围内的文件。不要提交 git。
|
||||
|
||||
## 背景
|
||||
|
||||
`3-3` 暂不能归档,因为 `sourceKind=local_folder` 当前在 `TREE_LIVE_CONTROLLER_JS` 中直接设置 `data-mnote-tree-live-transport="local-folder-static"` 并 return;Sidebar / FileTree 依赖 `startLocalFolderSidebarWatch()` 轮询 `/api/tree/local-folder-watch` 后补拉 projection。
|
||||
|
||||
新 checklist:`design/03-rust-web/process/3-18-local-folder-tree-live-consumer-convergence-checklist-v1.md`
|
||||
|
||||
## Ownership
|
||||
|
||||
允许修改:
|
||||
|
||||
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
|
||||
|
||||
禁止修改:
|
||||
|
||||
- Rust route 后端文件
|
||||
- `scripts/*.js`
|
||||
- 其他 design 文档
|
||||
|
||||
## 目标
|
||||
|
||||
1. local_folder 默认路径应进入 tree live controller,不再直接 `local-folder-static` return。
|
||||
2. 使用 Worker A 新增/扩展的 local_folder tree live SSE,接收到 snapshot/resync 后 dispatch `tree:snapshot` / `tree:resync`,复用已有 `renderSidebarSnapshot()` consumer。
|
||||
3. `startLocalFolderSidebarWatch()` 可保留为 fallback,但不再是默认主链。
|
||||
4. 不破坏 web_shell/tiptap 的 `/api/local-folder/events` 文档外部变更通道。
|
||||
5. DOM 状态应能区分新主链,例如 `data-mnote-tree-live-transport="local-folder-events"`,并在应用成功后设置 `data-mnote-tree-live-applied="snapshot"` 或 `"resync"`。
|
||||
|
||||
## 验收命令
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_tree_runtime -- --test-threads=1
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web web_shell -- --test-threads=1
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## 最终回复格式
|
||||
|
||||
- 修改文件列表
|
||||
- local_folder tree live 主链变化
|
||||
- fallback 保留情况
|
||||
- 运行过的命令和结果
|
||||
- 未完成项或风险
|
||||
@@ -0,0 +1,51 @@
|
||||
# Reasonix Worker C:Local Folder Tree Live 浏览器 smoke
|
||||
|
||||
Project root:`/mnt/Data1T/mnote`
|
||||
|
||||
你不是唯一 worker。不要回滚、覆盖或清理他人的改动;只修改本任务 ownership 范围内的文件。不要提交 git。
|
||||
|
||||
## 背景
|
||||
|
||||
`3-3` 的归档条件要求 `3000` 当前主界面的 Sidebar / page subtree / FileTree / preferred snapshot 补偿链统一到正式 live cache。当前 local_folder 仍是轮询补拉 projection。Worker A/B 会尝试把 local_folder 接入 tree live consumer;本任务负责 smoke 证据。
|
||||
|
||||
新 checklist:`design/03-rust-web/process/3-18-local-folder-tree-live-consumer-convergence-checklist-v1.md`
|
||||
|
||||
## Ownership
|
||||
|
||||
允许修改:
|
||||
|
||||
- `scripts/task435-local-folder-watch-no-reload-smoke.js`
|
||||
- 可新增:`scripts/task487-local-folder-tree-live-consumer-smoke.js`
|
||||
- `design/03-rust-web/process/3-18-local-folder-tree-live-consumer-convergence-checklist-v1.md`
|
||||
|
||||
禁止修改:
|
||||
|
||||
- Rust route / layout runtime 代码
|
||||
|
||||
## 目标
|
||||
|
||||
1. 增加或更新 smoke,验证 local_folder 文档页不再把 `local-folder-static` 当默认 tree live 主链。
|
||||
2. 验证 local_folder 外部文件变化后,Sidebar / FileTree 刷新最终留下 tree live consumer 证据,例如:
|
||||
- `data-mnote-tree-live-transport="local-folder-events"`
|
||||
- `data-mnote-tree-live-applied="snapshot"` 或 `"resync"`
|
||||
- `data-mnote-local-folder-watch-applied` 不再是唯一证据
|
||||
3. smoke 结果写入 `tmp/.../result.json`,如可行保存截图。
|
||||
4. 若当前代码尚未满足断言,允许先写 RED smoke,但必须在文档执行记录中说明它当前预期失败。
|
||||
|
||||
## 验收命令
|
||||
|
||||
```bash
|
||||
node --check scripts/task435-local-folder-watch-no-reload-smoke.js
|
||||
node --check scripts/task487-local-folder-tree-live-consumer-smoke.js
|
||||
git diff --check
|
||||
```
|
||||
|
||||
如果没有新增 task487,则说明原因。
|
||||
|
||||
## 最终回复格式
|
||||
|
||||
- 修改文件列表
|
||||
- smoke 覆盖点
|
||||
- 当前是否 RED / GREEN
|
||||
- 运行过的命令和结果
|
||||
- 未完成项或风险
|
||||
@@ -0,0 +1,55 @@
|
||||
# Reasonix Worker A:3-3 live cache 可归档性复核
|
||||
|
||||
## 项目
|
||||
|
||||
- Project root: `/mnt/Data1T/mnote`
|
||||
|
||||
## Ownership
|
||||
|
||||
允许修改:
|
||||
|
||||
- `design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md`
|
||||
- 如确认可归档,可移动到:
|
||||
- `design/03-rust-web/done/3-3-rust-web-tree-realtime-event-stream-v1.md`
|
||||
|
||||
禁止修改:
|
||||
|
||||
- 不改 Rust / JS runtime 代码。
|
||||
- 不改 `1-8`、`1-13`、`1-14`。
|
||||
- 不提交 git。
|
||||
- 不删除任何 `tmp/` 证据。
|
||||
|
||||
## 背景
|
||||
|
||||
`1-8` Batch D 仍把 `3-3` 作为 live cache 入口。旧 `3-3` 文档 9.2 说 sidebar / page subtree / filetree 没有全部统一 live cache、WS 只证明骨架、不能移动到 done。
|
||||
|
||||
但后续已有:
|
||||
|
||||
- `design/03-rust-web/done/3-14-rust-web-tree-realtime-ws-push-v1.md`
|
||||
- `design/03-rust-web/done/3-18-local-folder-tree-live-consumer-convergence-checklist-v1.md`
|
||||
- `scripts/task123-rust-web-tree-live-stream-consumer-smoke.js`
|
||||
- `scripts/task432-filetree-trash-page-dual-browser-no-refresh-smoke.js`
|
||||
- `scripts/task446-tree-rename-dual-browser-live-smoke.js`
|
||||
- `scripts/task447-tree-move-order-dual-browser-live-smoke.js`
|
||||
- `scripts/task448-tree-resync-recovery-dual-browser-smoke.js`
|
||||
- `scripts/task449-tree-sse-reconnect-snapshot-recovery-smoke.js`
|
||||
- `scripts/task487-local-folder-tree-live-consumer-smoke.js`
|
||||
|
||||
## 任务
|
||||
|
||||
1. 读取 `3-3`、`3-14`、`3-18`,判断 `3-3` 的 9.2 未完成项是否已经过期。
|
||||
2. 如果证据足够,更新 `3-3` 状态为 done,并移动到 `design/03-rust-web/done/`。
|
||||
3. 如果证据不足,不移动,只在 `3-3` 里写明最小剩余缺口。
|
||||
4. 运行最小语法检查:
|
||||
- `node --check scripts/task123-rust-web-tree-live-stream-consumer-smoke.js`
|
||||
- `node --check scripts/task487-local-folder-tree-live-consumer-smoke.js`
|
||||
- `git diff --check`
|
||||
|
||||
## 最终回复
|
||||
|
||||
用简体中文列出:
|
||||
|
||||
1. 修改文件
|
||||
2. 归档或不归档的理由
|
||||
3. 验证命令和结果
|
||||
4. 未完成项 / 风险
|
||||
@@ -0,0 +1,50 @@
|
||||
# Reasonix Worker B:1-5 / 1-6 command context 状态收口
|
||||
|
||||
## 项目
|
||||
|
||||
- Project root: `/mnt/Data1T/mnote`
|
||||
|
||||
## Ownership
|
||||
|
||||
允许修改:
|
||||
|
||||
- `design/01-tree-first-graph-kernel/process/1-5-next-phase-sequential-execution-checklist-v1.md`
|
||||
- `design/01-tree-first-graph-kernel/process/1-6-next-phase-gap-closure-checklist-v1.md`
|
||||
|
||||
禁止修改:
|
||||
|
||||
- 不改 Rust / JS runtime 代码。
|
||||
- 不改 `1-8`、`1-13`、`1-14`。
|
||||
- 不提交 git。
|
||||
|
||||
## 背景
|
||||
|
||||
当前代码和 `1-6` 的部分段落已经显示:
|
||||
|
||||
- `core-protocol/src/command.rs` 有 `CommandContext` / `when` evaluator。
|
||||
- FileTree 右键菜单和 Delete/Backspace 已接入等价 context 判断。
|
||||
- Hermes tools 写入守卫已消费 `args.commandContext` / `args.command_context` 的 `ai.canWrite` 与 `workspace.readonly`。
|
||||
|
||||
但 `1-5` Phase A7 和 `1-6` 顶部/汇总处仍有一些未更新的未完成表述。
|
||||
|
||||
## 任务
|
||||
|
||||
1. 只围绕 command context / context key 状态更新 `1-5` 与 `1-6`。
|
||||
2. 把已经有证据的项改为完成,并写入验证命令。
|
||||
3. 保留真实未完成项,例如更完整的 editor/object tab context 或未实跑的 DnD readonly smoke。
|
||||
4. 不夸大为“所有 command context 完全完成”。
|
||||
5. 运行:
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p core-protocol command_context -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web ensure_write_authorized -- --test-threads=1`
|
||||
- `node --check scripts/task471-local-folder-bulk-resource-trash-smoke.js`
|
||||
- `node --check scripts/task476-filetree-editor-context-menu-download-smoke.js`
|
||||
- `git diff --check`
|
||||
|
||||
## 最终回复
|
||||
|
||||
用简体中文列出:
|
||||
|
||||
1. 修改文件
|
||||
2. 勾选了哪些项、保留了哪些未完成项
|
||||
3. 验证命令和结果
|
||||
4. 风险
|
||||
@@ -0,0 +1,53 @@
|
||||
# Reasonix Worker C:Batch D 浏览器 smoke 证据整理
|
||||
|
||||
## 项目
|
||||
|
||||
- Project root: `/mnt/Data1T/mnote`
|
||||
|
||||
## Ownership
|
||||
|
||||
允许修改:
|
||||
|
||||
- `.codex/reasonix-tasks/results/batch-h-worker-c-browser-smoke-evidence.md`
|
||||
|
||||
禁止修改:
|
||||
|
||||
- 不改产品代码。
|
||||
- 不改 design 主文档。
|
||||
- 不提交 git。
|
||||
- 不删除或覆盖 `tmp/` 证据。
|
||||
|
||||
## 任务
|
||||
|
||||
只读检查以下 smoke 脚本和已有证据,整理成一份表格:
|
||||
|
||||
- `scripts/task123-rust-web-tree-live-stream-consumer-smoke.js`
|
||||
- `scripts/task432-filetree-trash-page-dual-browser-no-refresh-smoke.js`
|
||||
- `scripts/task446-tree-rename-dual-browser-live-smoke.js`
|
||||
- `scripts/task447-tree-move-order-dual-browser-live-smoke.js`
|
||||
- `scripts/task448-tree-resync-recovery-dual-browser-smoke.js`
|
||||
- `scripts/task449-tree-sse-reconnect-snapshot-recovery-smoke.js`
|
||||
- `scripts/task471-local-folder-bulk-resource-trash-smoke.js`
|
||||
- `scripts/task476-filetree-editor-context-menu-download-smoke.js`
|
||||
- `scripts/task487-local-folder-tree-live-consumer-smoke.js`
|
||||
|
||||
输出必须区分:
|
||||
|
||||
- 已实跑通过,且有 `tmp/` 结果或截图证据。
|
||||
- 仅 `node --check` 通过,还缺真实浏览器实跑。
|
||||
- 可用于人工网页核查的点。
|
||||
- 仍建议 Codex 主线程复跑的点。
|
||||
|
||||
验收:
|
||||
|
||||
- 运行 `node --check` 检查上述脚本语法,能跑多少跑多少;若脚本不存在或环境阻塞,写明。
|
||||
- 运行 `git diff --check`。
|
||||
|
||||
## 最终回复
|
||||
|
||||
用简体中文列出:
|
||||
|
||||
1. 写入的结果文件
|
||||
2. 证据摘要
|
||||
3. 建议 Codex 复跑的 smoke
|
||||
4. 风险
|
||||
@@ -0,0 +1,50 @@
|
||||
# Reasonix Worker A:7-27 归档性审查
|
||||
|
||||
## 项目
|
||||
|
||||
- Project root: `/mnt/Data1T/mnote`
|
||||
|
||||
## Ownership
|
||||
|
||||
允许修改:
|
||||
|
||||
- `design/07-ai/process/7-27-online-markdown-writeback-final-content-truth-v2.md`
|
||||
- 若确认可归档,可移动到:
|
||||
- `design/07-ai/done/7-27-online-markdown-writeback-final-content-truth-v2.md`
|
||||
|
||||
禁止修改:
|
||||
|
||||
- 不改 Rust / JS runtime 代码。
|
||||
- 不改 `1-8`、`7-29`。
|
||||
- 不提交 git。
|
||||
|
||||
## 背景
|
||||
|
||||
`7-27` 设计目标是让在线 `mnote.doc.markdown_edit` 以最终 markdown 为写回真源,而不是继续从原始 operations 二次推导 block ops。
|
||||
|
||||
当前代码已有:
|
||||
|
||||
- `parse_final_markdown_to_blocks`
|
||||
- `build_page_content`
|
||||
- `build_changed_blocks_summary`
|
||||
- `doc_markdown_edit` 中 `mnote.doc.markdown_edit (7-27)` reason
|
||||
- `markdown_edit` targeted tests 通过
|
||||
|
||||
## 任务
|
||||
|
||||
1. 对照 `7-27` 步骤 1-6、测试矩阵、开放问题,判断当前代码是否已满足归档条件。
|
||||
2. 如果可归档,更新状态并移动到 `design/07-ai/done/`。
|
||||
3. 如果不可归档,只写明最小剩余缺口,不改代码。
|
||||
4. 运行:
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web markdown_edit -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_manifest_describes_markdown_edit_write_contract -- --test-threads=1`
|
||||
- `git diff --check`
|
||||
|
||||
## 最终回复
|
||||
|
||||
用简体中文列出:
|
||||
|
||||
1. 修改文件
|
||||
2. 可归档或不可归档理由
|
||||
3. 验证命令和结果
|
||||
4. 未完成项 / 风险
|
||||
@@ -0,0 +1,50 @@
|
||||
# Reasonix Worker B:7-27 代码/测试缺口审查
|
||||
|
||||
## 项目
|
||||
|
||||
- Project root: `/mnt/Data1T/mnote`
|
||||
|
||||
## Ownership
|
||||
|
||||
允许修改:
|
||||
|
||||
- `.codex/reasonix-tasks/results/batch-i-worker-b-7-27-code-test-gap.md`
|
||||
|
||||
禁止修改:
|
||||
|
||||
- 不改代码。
|
||||
- 不改 design 主文档。
|
||||
- 不提交 git。
|
||||
|
||||
## 任务
|
||||
|
||||
只读审查当前 `7-27` 实现,重点文件:
|
||||
|
||||
- `rust/crates/mnote-web/src/hermes_tools/doc.rs`
|
||||
- `rust/crates/mnote-web/src/routes/hermes_tools.rs`
|
||||
- `rust/crates/mnote-web/src/hermes_tools/manifest.rs`
|
||||
|
||||
检查这些点:
|
||||
|
||||
1. `blocks_to_markdown` 是否能从 legacy `content` / `contentNodes` 读取文本,而不是只读 `text`。
|
||||
2. 注释格式是否与 `7-27` 文档一致,是否真的保留 `revisionRef`。
|
||||
3. `full_content` 是否不再被拒绝,是否会保留复杂块。
|
||||
4. `dryRun` 是否不写入。
|
||||
5. selection / allowedTargetBlockIds 是否阻止越界写入。
|
||||
6. 是否还有 `build_block_ops_from_markdown_edit` 旧推导路径。
|
||||
7. 测试覆盖是否足以归档。
|
||||
|
||||
输出到结果文件,按三类分级:
|
||||
|
||||
- 必须修复才能归档
|
||||
- 可作为后续增强
|
||||
- 已满足
|
||||
|
||||
验收:
|
||||
|
||||
- 运行 `git diff --check`
|
||||
- 结果文件引用具体函数名或测试名。
|
||||
|
||||
## 最终回复
|
||||
|
||||
用简体中文列出结果文件路径和最高风险结论。
|
||||
@@ -0,0 +1,45 @@
|
||||
# Reasonix Worker C:7-15 / 7-12 后续拆分审查
|
||||
|
||||
## 项目
|
||||
|
||||
- Project root: `/mnt/Data1T/mnote`
|
||||
|
||||
## Ownership
|
||||
|
||||
允许修改:
|
||||
|
||||
- `.codex/reasonix-tasks/results/batch-i-worker-c-acp-review-tail.md`
|
||||
|
||||
禁止修改:
|
||||
|
||||
- 不改代码。
|
||||
- 不改 design 主文档。
|
||||
- 不提交 git。
|
||||
|
||||
## 任务
|
||||
|
||||
只读审查:
|
||||
|
||||
- `design/07-ai/process/7-15-page-ai-acp-agent-runtime-unified-layer-v1.md`
|
||||
- `design/07-ai/process/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md`
|
||||
- `rust/crates/mnote-web/src/acp_client.rs`
|
||||
- `rust/crates/mnote-web/src/acp_session_manager.rs`
|
||||
- `rust/crates/mnote-web/src/acp_runtime.rs`
|
||||
- `rust/crates/mnote-web/src/routes/hermes_client.rs`
|
||||
- `rust/crates/mnote-web/src/hermes_tools/manifest.rs`
|
||||
|
||||
目标:
|
||||
|
||||
1. 判断 `7-15` Step 15-17 是否仍是当前应执行项,或是否应拆成独立 checklist。
|
||||
2. 判断 `7-12` Phase B-F 哪些已被当前 manifest/runtime/tool guard 覆盖。
|
||||
3. 明确 Phase C Review Mode 仍冻结,不要建议本轮实现。
|
||||
4. 给出下一批 P0/P1/P2 顺序。
|
||||
|
||||
验收:
|
||||
|
||||
- 运行 `git diff --check`
|
||||
- 结果文件必须有 P0/P1/P2 表格。
|
||||
|
||||
## 最终回复
|
||||
|
||||
用简体中文列出结果文件路径、P0 建议和风险。
|
||||
@@ -0,0 +1,49 @@
|
||||
# Batch J Worker A:旧 HTTP proxy / page AI fallback 调用链审查
|
||||
|
||||
项目根目录:`/mnt/Data1T/mnote`
|
||||
|
||||
## 目标
|
||||
|
||||
只读审查 `7-34` 的 P0-1 / P0-2:
|
||||
|
||||
1. 查清 `hermes_client.rs` 中旧 Hermes HTTP proxy 路径是否仍有默认入口。
|
||||
2. 查清 `page_ai_workflow.rs` 是否仍会在 local-first 默认口径下绕过 ACP / shared tool executor,重新走旧 block-edit fallback。
|
||||
|
||||
## 允许范围
|
||||
|
||||
只读以下文件,不修改代码:
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/hermes_client.rs`
|
||||
- `rust/crates/mnote-web/src/page_ai_workflow.rs`
|
||||
- `rust/crates/mnote-web/src/acp_runtime.rs`
|
||||
- `rust/crates/mnote-web/src/acp_session_manager.rs`
|
||||
- `rust/crates/mnote-web/src/routes/mod.rs`
|
||||
- 必要时可只读 `rust/crates/mnote-web/src/routes/*.rs` 里和 route 注册相关的文件
|
||||
|
||||
## 禁止事项
|
||||
|
||||
- 不修改任何源码、设计文档或测试。
|
||||
- 不提交 git。
|
||||
- 不移动文件。
|
||||
- 不运行破坏性命令。
|
||||
|
||||
## 输出要求
|
||||
|
||||
把结果写入 `.codex/reasonix-tasks/results/batch-j-worker-a-http-proxy-fallback.md`。
|
||||
|
||||
必须包含:
|
||||
|
||||
1. 旧 HTTP proxy / page AI fallback 的真实调用链。
|
||||
2. 每个入口的分类:`default` / `compat` / `debug-only` / `未被调用`。
|
||||
3. 是否存在必须在本轮修复的 P0 缺口。
|
||||
4. 若需要后续代码修改,给出最小修改范围和建议测试命令。
|
||||
|
||||
## 验收命令
|
||||
|
||||
只需要运行:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
```
|
||||
|
||||
最终回复中列出读取过的关键文件、结论、风险和未完成项。
|
||||
@@ -0,0 +1,51 @@
|
||||
# Batch J Worker B:tool availability 三处一致性审查
|
||||
|
||||
项目根目录:`/mnt/Data1T/mnote`
|
||||
|
||||
## 目标
|
||||
|
||||
只读审查 `7-34` 的 P1-1:
|
||||
|
||||
确认 profile-level tool availability / capability 是否在三处保持一致:
|
||||
|
||||
1. Hermes tool manifest:工具是否标明 enabled / disabled / capability / annotations。
|
||||
2. UI:页面 AI 或 tool list 是否根据同一能力判断展示 disabled / hidden。
|
||||
3. Execute guard:真正执行工具时是否复用同一能力判断,而不是只靠 UI 阻止。
|
||||
|
||||
## 允许范围
|
||||
|
||||
只读以下文件,不修改代码:
|
||||
|
||||
- `rust/crates/mnote-web/src/hermes_tools/manifest.rs`
|
||||
- `rust/crates/mnote-web/src/hermes_tools/*.rs`
|
||||
- `rust/crates/mnote-web/src/routes/hermes_tools.rs`
|
||||
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
|
||||
- 必要时可只读 `rust/crates/mnote-web/src/routes/hermes_client.rs`
|
||||
|
||||
## 禁止事项
|
||||
|
||||
- 不修改任何源码、设计文档或测试。
|
||||
- 不提交 git。
|
||||
- 不移动文件。
|
||||
- 不运行破坏性命令。
|
||||
|
||||
## 输出要求
|
||||
|
||||
把结果写入 `.codex/reasonix-tasks/results/batch-j-worker-b-tool-availability.md`。
|
||||
|
||||
必须包含:
|
||||
|
||||
1. manifest / UI / execute guard 各自当前能力判断来源。
|
||||
2. 三处是否同源;如果不同源,指出具体文件和函数。
|
||||
3. P0/P1/P2 分级缺口。
|
||||
4. 建议的最小测试名或现有 targeted test。
|
||||
|
||||
## 验收命令
|
||||
|
||||
只需要运行:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
```
|
||||
|
||||
最终回复中列出读取过的关键文件、结论、风险和未完成项。
|
||||
@@ -0,0 +1,50 @@
|
||||
# Batch J Worker C:ACP 稳定性与 benchmark checklist 草案
|
||||
|
||||
项目根目录:`/mnt/Data1T/mnote`
|
||||
|
||||
## 目标
|
||||
|
||||
只读审查 `7-34` 的 P1-2 / P1-3:
|
||||
|
||||
为 ACP 多会话稳定性测试和 Reasonix cache benchmark 拆出最小可执行方案,供 Codex 后续实现或派发实现 worker。
|
||||
|
||||
## 允许范围
|
||||
|
||||
只读以下文件和目录,不修改代码:
|
||||
|
||||
- `design/07-ai/process/7-15-page-ai-acp-agent-runtime-unified-layer-v1.md`
|
||||
- `design/07-ai/done/7-25-acp-session-runtime-enhancement-plan-v1.md`
|
||||
- `design/07-ai/done/7-30-acp-session-load-resume-checklist-v1.md`
|
||||
- `design/07-ai/done/7-31-acp-permission-decision-loop-checklist-v1.md`
|
||||
- `design/07-ai/done/7-32-acp-tool-location-and-open-action-checklist-v1.md`
|
||||
- `design/07-ai/done/7-33-acp-session-info-plan-ui-checklist-v1.md`
|
||||
- `scripts/`
|
||||
- `rust/crates/mnote-web/src/acp_*.rs`
|
||||
|
||||
## 禁止事项
|
||||
|
||||
- 不修改任何源码、设计文档或测试。
|
||||
- 不提交 git。
|
||||
- 不移动文件。
|
||||
- 不运行破坏性命令。
|
||||
|
||||
## 输出要求
|
||||
|
||||
把结果写入 `.codex/reasonix-tasks/results/batch-j-worker-c-acp-stability-benchmark.md`。
|
||||
|
||||
必须包含:
|
||||
|
||||
1. ACP 多会话稳定性 smoke 的最小脚本方案:输入、操作、断言、证据文件。
|
||||
2. Reasonix cache benchmark 的最小脚本方案:Hermes / Reasonix 对比维度、样本数、输出格式。
|
||||
3. 浏览器可见验证点和截图/JSON 证据建议。
|
||||
4. 哪些属于 P0 稳定性验证,哪些只是 P2 度量。
|
||||
|
||||
## 验收命令
|
||||
|
||||
只需要运行:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
```
|
||||
|
||||
最终回复中列出读取过的关键文件、结论、风险和未完成项。
|
||||
@@ -0,0 +1,37 @@
|
||||
# Reasonix 任务书:1-8 completion hook 验证
|
||||
|
||||
## 目标
|
||||
|
||||
在 `design/01-tree-first-graph-kernel/process/1-8-mvp-post-process-execution-order-v1.md` 中补一段简短的执行备注,说明:
|
||||
|
||||
1. Reasonix 编码任务默认使用 30 分钟超时。
|
||||
2. Reasonix 完成后应通过 completion hook 主动回传,不再依赖短轮询。
|
||||
3. 这条口径属于当前 1-8 的执行说明,不要扩写成新流程设计。
|
||||
|
||||
## 允许修改范围
|
||||
|
||||
- `design/01-tree-first-graph-kernel/process/1-8-mvp-post-process-execution-order-v1.md`
|
||||
|
||||
## 禁止事项
|
||||
|
||||
- 不要修改其他设计文档。
|
||||
- 不要改代码。
|
||||
- 不要提交 git。
|
||||
- 不要删除或重排现有 1-8 结构,只做最小增补。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- 1-8 文档中能看到新增的简短备注。
|
||||
- 备注明确提到:
|
||||
- 默认 30 分钟超时
|
||||
- completion hook 主动回传
|
||||
- 输出最终结果时列出实际修改文件和验证方式。
|
||||
|
||||
## 最终回复格式
|
||||
|
||||
请用简体中文,按下面顺序输出:
|
||||
|
||||
1. 修改了哪些文件
|
||||
2. 具体改动内容
|
||||
3. 验证方式
|
||||
4. 未完成项或风险
|
||||
@@ -0,0 +1,22 @@
|
||||
# Batch F Worker A GFM Archive Timeout
|
||||
|
||||
- 时间:2026-05-21
|
||||
- 任务书:`.codex/reasonix-tasks/2026-05-21-batch-f-worker-a-gfm-archive.md`
|
||||
- runner 目录:`/home/lix/.codex/runtime/reasonix-coding-worker/reasonix-2026-05-21T09-32-37-250Z-33d2e71c`
|
||||
- 状态:TIMEOUT
|
||||
|
||||
## 现象
|
||||
|
||||
Reasonix runner 长时间未生成 `final.md` / `result.json`,仅有 `prompt.md` 与 `reasonix-transcript.jsonl`。Codex 已终止该 runner 与对应 `reasonix acp` 进程,避免继续占用执行槽。
|
||||
|
||||
## 处理
|
||||
|
||||
Codex 本地继续复核 `3-13`:
|
||||
|
||||
- `local_markdown_save_writes_table_inline_marks`
|
||||
- `local_markdown_save_round_trips_tiptap_table_marks`
|
||||
- `local_markdown_save_writes_table_alignment_markers`
|
||||
- `local_markdown_save_table_handles_colspan_degradation`
|
||||
- `document_shell_bootstrap_preserves_inline_mark_conversion`
|
||||
|
||||
后续以 Codex 复核和 targeted tests 为准,不采纳本 worker 的空结果。
|
||||
@@ -0,0 +1,15 @@
|
||||
# Batch F Worker B Tree Live Cache Misrouted
|
||||
|
||||
- 时间:2026-05-21
|
||||
- 任务书:`.codex/reasonix-tasks/2026-05-21-batch-f-worker-b-tree-live-cache.md`
|
||||
- runner 目录:`/home/lix/.codex/runtime/reasonix-coding-worker/reasonix-2026-05-21T09-32-37-259Z-bd4aeac4`
|
||||
- 状态:MISROUTED
|
||||
|
||||
## 现象
|
||||
|
||||
任务书要求审查 `3-3` tree realtime live cache,但 Reasonix 最终输出写成“Worker C:AI 写入 CommandContext 守卫”,并对 Hermes 写入守卫方向产生了代码改动。该结果不能作为 `3-3` live cache 归档证据。
|
||||
|
||||
## 处理
|
||||
|
||||
- `3-3` 仍由 Codex 本地复核。
|
||||
- 当前 Hermes 写入守卫 diff 需由 Codex 独立审查、修正并运行 targeted tests 后,才可作为 Worker C 方向的补充实现。
|
||||
@@ -0,0 +1,56 @@
|
||||
# Batch H Worker C — 浏览器 Smoke 证据整理
|
||||
|
||||
> 创建时间:2026-05-21
|
||||
> 状态:✅ 已完成(Codex 已复核并补跑关键 smoke)
|
||||
|
||||
## 语法检查结果
|
||||
|
||||
| Smoke 脚本 | `node --check` | 说明 |
|
||||
|-----------|:---:|-------|
|
||||
| `task123-rust-web-tree-live-stream-consumer-smoke.js` | ✅ PASS | SSE/WS tree stream consumer |
|
||||
| `task432-filetree-trash-page-dual-browser-no-refresh-smoke.js` | ✅ PASS | 双浏览器页面/垃圾箱无刷新同步 |
|
||||
| `task446-tree-rename-dual-browser-live-smoke.js` | ✅ PASS | 双浏览器重命名实时同步 |
|
||||
| `task447-tree-move-order-dual-browser-live-smoke.js` | ✅ PASS | 双浏览器移动顺序实时同步 |
|
||||
| `task448-tree-resync-recovery-dual-browser-smoke.js` | ✅ PASS | 双浏览器 resync 恢复 |
|
||||
| `task449-tree-sse-reconnect-snapshot-recovery-smoke.js` | ✅ PASS | SSE 断线重连恢复 |
|
||||
| `task471-local-folder-bulk-resource-trash-smoke.js` | ✅ PASS | local_folder 批量资源删除 |
|
||||
| `task476-filetree-editor-context-menu-download-smoke.js` | ✅ PASS | 文件树/编辑器右键菜单 + 下载 |
|
||||
| `task487-local-folder-tree-live-consumer-smoke.js` | ✅ PASS | local_folder 树 live consumer 收敛 |
|
||||
|
||||
**全部 9/9 `node --check` 通过 ✅**
|
||||
|
||||
## 实跑证据汇总
|
||||
|
||||
| # | Smoke | 有 tmp/ 证据 | 状态 | 关键断言 |
|
||||
|---|-------|:----------:|:----:|---------|
|
||||
| 1 | task123 — 树 live stream consumer | ✅ `tmp/tree-live-cache-smoke/20260516-task123/task123.stdout.json` | ✅ GREEN | SSE snapshot 含 `data.dataset.kernel_sidebar_projection` + `kernel_file_tree_projection`;临时页 doc row 出现 |
|
||||
| 2 | task432 — 页面/垃圾箱双浏览器无刷新同步 | ✅ `tmp/tree-live-cache-smoke/20260516-task432/result.json` | ✅ GREEN | 新建/归档/恢复/清空垃圾箱在 B 端无刷新同步通过 `tree:resync`/`tree:delta` |
|
||||
| 3 | task446 — 重命名双浏览器实时同步 | ✅ `tmp/tree-live-cache-smoke/20260516-task446-rename/result.json` + 截图 | ✅ GREEN | B 文档页头、Breadcrumb、Sidebar、FileTree 全部通过 `tree:delta upsert_document` 更新 |
|
||||
| 4 | task447 — 移动顺序双浏览器实时同步 | ✅ `tmp/tree-live-cache-smoke/20260516-task447-move-order/result.json` + 截图 | ✅ GREEN | Page Tree / File Tree 顺序通过 `tree:delta move_document` 更新为 A/C/B |
|
||||
| 5 | task448 — resync 恢复双浏览器 | ✅ `tmp/tree-live-cache-smoke/20260516-task448-resync/result.json` + 截图 | ✅ GREEN | 多条 missed command 触发 `event: resync`,完整 snapshot 投影恢复 |
|
||||
| 6 | task449 — SSE 重连恢复 | ✅ `tmp/tree-live-cache-smoke/20260516-task449-reconnect/result.json` + 截图 | ✅ GREEN | 断线后 EventSource 回连,snapshot 拉回最新,`liveStatus=connected` |
|
||||
| 7 | task471 — local_folder 批量资源删除 | ✅ `tmp/reasonix-batch-a-browser-task471-2026-05-21/result.json` + 本轮 stdout | ✅ GREEN | Codex 本轮复跑通过,批量 archive 两个资源:`附件-a.txt`、`附件-b.json` |
|
||||
| 8 | task476 — 文件树/编辑器上下文菜单+下载 | ✅ `tmp/task476-filetree-editor-context-menu-download-smoke/result.json` + 截图 | ✅ GREEN | Codex 使用 `/usr/bin/google-chrome-stable` 复跑通过,文件树 asset/多选/markdown/folder 下载均通过;编辑器附件菜单因附件不可见被标记为 blocked,不影响 FileTree 下载链路 |
|
||||
| 9 | task487 — local_folder 树 live consumer 收敛 | ✅ `tmp/task487-local-folder-tree-live-consumer-smoke/result.json` + 截图 | ✅ GREEN | 全部 9 断言通过:`transport=local-folder-events`、`status=connected`、外部创建/删除后 `applied=resync` |
|
||||
|
||||
## 可用于人工网页核查的点
|
||||
|
||||
1. **task476**:FileTree asset 右键菜单缺 download 按钮。人工核查时在 Explorer 标签右键任何 PDF/图片/附件,检查是否有「Download」动作。
|
||||
2. **task487**:local_folder 文档页 HTML `<html>` 上应看到 `data-mnote-tree-live-transport="local-folder-events"` + `data-mnote-tree-live-status="connected"`。
|
||||
3. **task446-449**:双浏览器打开同一 workspace,A 端执行 rename/move/create/delete,B 端页面/文件树应无刷新更新。
|
||||
4. **task432**:Trash 页面 create/archive/restore/purge/empty 在 B 端无导航刷新同步。
|
||||
|
||||
## 建议 Codex 复跑的 smoke
|
||||
|
||||
| Smoke | 复跑理由 | 命令 |
|
||||
|-------|---------|------|
|
||||
| task476 | 本轮已用系统 Chrome 复跑通过;后续只需在改动下载链路后回归 | `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task476-filetree-editor-context-menu-download-smoke.js` |
|
||||
| task487 | 确认 local_folder tree live consumer 收敛回归不退化 | `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task487-local-folder-tree-live-consumer-smoke.js` |
|
||||
| task431 | DnD readonly conflict smoke,属于 Phase A7 最终验收 | `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task431-vscode-explorer-dnd-readonly-conflict-smoke.js` |
|
||||
|
||||
## 风险
|
||||
|
||||
1. **task476 浏览器选择差异**:`/snap/bin/chromium` 曾出现 `download.saveAs ENOENT`,但同一脚本用 `/usr/bin/google-chrome-stable` 复跑通过,判定为 Playwright 下载临时文件层噪音;后续 smoke 优先使用系统 Chrome。
|
||||
2. **task487 脚本头部注释仍标 RED**:注释写 `STATUS: 🔴 RED (预期失败)` 但实跑已 GREEN。脚本头部注释需要更新,以免误导后续批次。
|
||||
3. **task471 bulk trash 未测资源命名冲突**:批量删除后同名资源在当前批次无冲突场景测试。
|
||||
4. **git diff 检查**:Codex 本轮已运行 `git diff --check`,无空白错误。
|
||||
@@ -0,0 +1,66 @@
|
||||
# Worker A:7-27 归档性审查结果
|
||||
|
||||
> 审查时间:2026-05-20
|
||||
> 审查范围:`design/07-ai/process/7-27-online-markdown-writeback-final-content-truth-v2.md` × 当前 Rust 代码
|
||||
|
||||
---
|
||||
|
||||
## 1. 对照步骤 1-6 的完成状态
|
||||
|
||||
| 步骤 | 内容 | 状态 | 证据 |
|
||||
|------|------|------|------|
|
||||
| 步骤 1 | 增强 `blocks_to_markdown`:注释格式加 type/rev,复杂块用 raw marker | ✅ 已实现 | `doc.rs:1185-1215` — 输出 `<!-- block:id:type:level=N -->` 和 `[mnote-raw-block:id]` |
|
||||
| 步骤 2 | 实现 `parse_final_markdown_to_blocks` | ✅ 已实现 | `doc.rs:656-747` — 完整实现含正则提取、fuzzy 回退、复杂块保留 |
|
||||
| 步骤 3 | 实现 `build_page_content` | ✅ 已实现 | `doc.rs:850-913` — 含新块 ID 生成、复杂块保留、inline contentNodes 保护 |
|
||||
| 步骤 4 | 替换 `doc_markdown_edit` online 写回路径 | ✅ 已实现 | `doc.rs:1625-1700` — "// 7-27: 在线写回以最终 markdown 为真源" |
|
||||
| 步骤 5 | 更新 `full_content` 处理 | ✅ 已实现 | `doc.rs:1578-1585` — 转为 search=全文, replace=full,进新路径 |
|
||||
| 步骤 6 | 清理旧代码 | ✅ 已标记 | Line 1911: `// 退役:7-27 …直接写回` |
|
||||
|
||||
## 2. 测试矩阵覆盖
|
||||
|
||||
| 优先级 | 场景 | 测试函数 | 结果 |
|
||||
|--------|------|----------|------|
|
||||
| P0 | 单块精确 search/replace | `test_search_replace_exact` | ✅ |
|
||||
| P0 | 忽略空白 match | `test_search_replace_normalized_chinese_byte_boundaries` | ✅ |
|
||||
| P0 | 多 operation 命中同块 | `hermes_tools_markdown_edit_merges_same_block_operations` | ✅ |
|
||||
| P0 | full_content | `hermes_tools_markdown_edit_online_full_content_rejects_unsafe_block_mapping` | ✅ 注释明确:"7-27 修复后:full_content 不再被拒绝" |
|
||||
| P1 | heading 替换 | `test_blocks_to_markdown_heading` | ✅ |
|
||||
| P1 | 包含复杂块文档的替换 | `test_build_page_content_full_content_replaces_old_text_blocks_but_keeps_complex_blocks` | ✅ |
|
||||
| P1 | dryRun | `hermes_tools_markdown_edit_local_dry_run_does_not_write` | ✅ |
|
||||
| P1 | revision 过期检查 | `hermes_tools_markdown_edit_online_page_body_save_carries_revision_conflict_key` | ✅ |
|
||||
| P2 | 原始块 ID 消失保留 | `test_build_page_content_preserves_missing_original_text_block_when_some_ids_remain` | ✅ |
|
||||
| P2 | 空 operations | `hermes_tools_markdown_edit_rejects_no_applied_operations` | ✅ |
|
||||
|
||||
## 3. 开放问题状态
|
||||
|
||||
| 开放问题 | 当前状态 |
|
||||
|----------|----------|
|
||||
| OQ1:`<!-- block:id:type:rev -->` 被 search/replace 破坏 | `extract_block_comment` 在解析不完整注释时返回 `(None, None)`,标记为 `is_new=true`,安全降级 |
|
||||
| OQ2:full_content 块类型从 GFM 前缀推断 | `detect_block_type_from_prefix` 支持 heading/todo/paragraph,代码块等交由 fallback 处理 |
|
||||
| OQ3:行内格式保持 | `update_legacy_block_text_for_markdown_edit` + `replace_single_inline_text_value` 在文本不变时保留 marks,改变时更新 text 字段 |
|
||||
|
||||
## 4. 验证结果
|
||||
|
||||
```
|
||||
$ cargo test -p mnote-web markdown_edit -- --test-threads=1
|
||||
16 passed; 0 failed
|
||||
|
||||
$ cargo test -p mnote-web hermes_tools_manifest_describes_markdown_edit_write_contract -- --test-threads=1
|
||||
1 passed; 0 failed
|
||||
|
||||
$ git diff --check
|
||||
无冲突标记
|
||||
```
|
||||
|
||||
## 5. 结论:可归档
|
||||
|
||||
**归档条件全部满足:**
|
||||
- 步骤 1-6 全部实现 ✅
|
||||
- P0/P1 测试矩阵全部通过 ✅
|
||||
- 旧代码已标记退役 ✅
|
||||
- 命名管道的 reason 字段包含 `"mnote.doc.markdown_edit (7-27)"` ✅
|
||||
- 开放问题均为已知边界,不需要阻碍归档
|
||||
|
||||
## 6. 执行操作
|
||||
|
||||
将 `design/07-ai/process/7-27-online-markdown-writeback-final-content-truth-v2.md` 移动到 `design/07-ai/done/`。
|
||||
@@ -0,0 +1,157 @@
|
||||
# Worker B:7-27 代码/测试缺口审查
|
||||
|
||||
> 审查时间:2026-05-20
|
||||
> 审查范围:`doc.rs` + `hermes_tools.rs` + `manifest.rs`
|
||||
> 分级:🟢 已满足 / 🟡 可作为后续增强 / 🔴 必须修复才能归档
|
||||
|
||||
---
|
||||
|
||||
## 检查点 1:`blocks_to_markdown` 是否能从 legacy `content`/`contentNodes` 读取文本
|
||||
|
||||
**结论:🟢 已满足**
|
||||
|
||||
`legacy_block_text()` 函数 (`doc.rs:973-986`) 逐级回退:
|
||||
```rust
|
||||
fn legacy_block_text(block: &Value) -> String {
|
||||
if let Some(text) = block.get("text").and_then(Value::as_str) { ... }
|
||||
if let Some(content) = block.get("content") { return inline_text_value(content); }
|
||||
if let Some(content_nodes) = block.get("contentNodes") { return inline_text_value(content_nodes); }
|
||||
}
|
||||
```
|
||||
|
||||
`block_text()` (`doc.rs:1265-1270`) 只读 `text` 字段用于 markdown 序列化,但 `build_page_content` 中的 `update_legacy_block_text_for_markdown_edit` 正确使用了 `legacy_block_text` 做比较,且写回时通过 `replace_single_inline_text_value` 递归处理 `content`/`contentNodes` 中的 `text` 字段。
|
||||
|
||||
**证据**: `inline_text_value()` (`doc.rs:988-1000`) 递归处理 Array/Object/String 三种形态。
|
||||
|
||||
---
|
||||
|
||||
## 检查点 2:注释格式是否与 7-27 文档一致,是否保留 `revisionRef`
|
||||
|
||||
**结论:🟡 可作为后续增强**
|
||||
|
||||
当前格式:
|
||||
```
|
||||
<!-- block:{id}:{type} -->
|
||||
<!-- block:{id}:{type}:level={level} --> (heading 块)
|
||||
```
|
||||
|
||||
7-27 §3.3.1 设计为:
|
||||
```
|
||||
<!-- block:{id}:{type}:{revision_ref} -->
|
||||
<!-- block:{id}:{type}:{revision_ref}:level={level} --> (with level)
|
||||
```
|
||||
|
||||
**差异**:`revisionRef` 不写入注释,而是从原始 `block.revisionRef` 读取。解析器 `extract_block_comment()` 虽然支持 `type:rev` 格式(`splitn(3, ':')` 后 `parts[2]` 可以包含 rev),但 `blocks_to_markdown` 写回时不输出 rev。
|
||||
|
||||
**影响**: 低。`revisionRef` 在整个 `build_page_content` 路径中被正确继承(`doc.rs:685-690`),只是注释里看不到。不阻碍归档,可后续增强。
|
||||
|
||||
---
|
||||
|
||||
## 检查点 3:`full_content` 是否不再被拒绝,是否保留复杂块
|
||||
|
||||
**结论:🟢 已满足**
|
||||
|
||||
- `doc_markdown_edit` 中 `full_content` 转为 `{search: current_md.trim(), replace: full.trim()}` 单个 operation,进入新路径
|
||||
- `parse_final_markdown_to_blocks` 对无注释行创建 `is_new=true` 的 paragraph 块
|
||||
- `build_page_content` 追加新块并保留原始复杂块(`is_complex_markdown_original_block`)
|
||||
- 测试 `test_build_page_content_full_content_replaces_old_text_blocks_but_keeps_complex_blocks` 验证复杂块保留
|
||||
- 集成测试 `hermes_tools_markdown_edit_online_full_content_rejects_unsafe_block_mapping` 注释确认:"7-27 修复后:full_content 不再被拒绝"
|
||||
|
||||
---
|
||||
|
||||
## 检查点 4:`dryRun` 是否不写入
|
||||
|
||||
**结论:🟢 已满足**
|
||||
|
||||
`doc_markdown_edit` 中:
|
||||
```rust
|
||||
if input.dry_run == Some(true) {
|
||||
return Ok(json!({
|
||||
"written": false,
|
||||
"dryRun": true,
|
||||
"diff": changed_blocks
|
||||
}));
|
||||
}
|
||||
```
|
||||
|
||||
本地文件路径也有独立的 `dryRun` 分支只返回预览。
|
||||
|
||||
测试 `hermes_tools_markdown_edit_local_dry_run_does_not_write` 验证。
|
||||
|
||||
---
|
||||
|
||||
## 检查点 5:selection / allowedTargetBlockIds 是否阻止越界写入
|
||||
|
||||
**结论:🟢 已满足**
|
||||
|
||||
`ensure_markdown_changed_blocks_within_allowed()` (`doc.rs:1050-1073`) 在写回前检查每个 `changedBlocks` 中的 `blockId` 是否在 `allowed_block_ids` 中。越界时返回 `mnote_markdown_edit_target_out_of_scope` 错误。
|
||||
|
||||
测试 `hermes_tools_markdown_edit_rejects_selection_out_of_scope` 验证。
|
||||
|
||||
`selected_block_ids()` (`doc.rs:1075-1094`) 从 `selectedBlockIds` / `allowedTargetBlockIds` / `selectedBlockId` / `blockId` 四个参数中收集。
|
||||
|
||||
---
|
||||
|
||||
## 检查点 6:旧 `build_block_ops_from_markdown_edit` 推导路径
|
||||
|
||||
**结论:🟢 已满足(已退役)**
|
||||
|
||||
`doc.rs:1911` 末尾注释:
|
||||
```rust
|
||||
// 退役:7-27 改为 final_md → blocks → execute_page_body_save_from_aggregate 直接写回
|
||||
```
|
||||
|
||||
函数体仍保留但不再被任何调用者引用(`doc.rs` 内只有一处调用已在步骤 4 中被替换)。可用 `cargo +nightly deadlinks` 确认死代码状态,但已足够安全。
|
||||
|
||||
---
|
||||
|
||||
## 检查点 7:测试覆盖是否足以归档
|
||||
|
||||
**结论:🟢 已满足**
|
||||
|
||||
### 单元测试覆盖(doc.rs 内)
|
||||
|
||||
| 测试 | 验证点 |
|
||||
|------|--------|
|
||||
| `test_search_replace_exact` | 精确匹配替换 |
|
||||
| `test_search_replace_normalized_chinese_byte_boundaries` | 中文忽略空白 + byte 边界保留 |
|
||||
| `test_search_replace_full_content` | 全文替换 = 全文精确匹配 |
|
||||
| `test_blocks_to_markdown_with_ids` | 块 ID 注释输出 |
|
||||
| `test_blocks_to_markdown_heading` | heading 类型 + level 注释 |
|
||||
| `test_blocks_to_markdown_complex_block_uses_raw_marker` | 复杂块 raw marker |
|
||||
| `test_build_page_content_full_content_replaces_old_text_blocks_but_keeps_complex_blocks` | full_content 新块 + 复杂块保留 |
|
||||
| `test_build_page_content_preserves_missing_original_text_block_when_some_ids_remain` | ID 消失时保留原始块 |
|
||||
| `test_build_page_content_preserves_unchanged_inline_content_nodes` | 行内 marks 保留(不变时) |
|
||||
| `test_build_page_content_preserves_single_inline_node_marks_when_text_changes` | 行内 marks 保留(文本改变时) |
|
||||
|
||||
### 集成测试覆盖(hermes_tools.rs)
|
||||
|
||||
| 测试 | 验证点 |
|
||||
|------|--------|
|
||||
| `hermes_tools_markdown_edit_online_full_content_rejects_unsafe_block_mapping` | full_content 不再拒绝 |
|
||||
| `hermes_tools_markdown_edit_merges_same_block_operations` | 同块多次操作累积 |
|
||||
| `hermes_tools_markdown_edit_maps_normalized_search_to_block` | 非精确匹配 + changedBlocks 验证 |
|
||||
| `hermes_tools_markdown_edit_online_page_body_save_carries_revision_conflict_key` | revision/conflict key 传递 |
|
||||
| `hermes_tools_markdown_edit_rejects_no_applied_operations` | 空 operations 拒绝 |
|
||||
| `hermes_tools_markdown_edit_rejects_selection_out_of_scope` | selection 越界拒绝 |
|
||||
| `hermes_tools_markdown_edit_reports_empty_block_mapping_before_apply` | 空 block mapping 错误报告 |
|
||||
| `hermes_tools_markdown_edit_shared_read_is_forbidden` | 共享文档只读拒绝 |
|
||||
| `hermes_tools_markdown_edit_local_dry_run_does_not_write` | local dryRun |
|
||||
| `hermes_tools_markdown_edit_local_folder_writes_same_markdown_file` | local folder 写入 |
|
||||
| `hermes_tools_markdown_edit_local_requires_write_contract` | local write contract 要求 |
|
||||
|
||||
### 缺口:建议后续增强(非阻塞)
|
||||
|
||||
1. **无块注释的多段 markdown 还原**:当前行级解析器假设每行一个块;多段文本(如代码块)需 GFM fallback(§5.3 有分析但未实现)
|
||||
2. **`revisionRef` 在注释中可见**:当前只在内存中保留,注释格式未输出 rev(§3.3.1 设计差异)
|
||||
3. **`build_block_ops_from_markdown_edit` 函数体删除**:当前已退役但函数体仍在文件中
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
| 分级 | 数量 | 项目 |
|
||||
|------|------|------|
|
||||
| 🔴 必须修复才能归档 | 0 | — |
|
||||
| 🟡 后续增强 | 3 | 注释写 rev, GFM fallback 多段文本, 死代码体删除 |
|
||||
| 🟢 已满足 | 7/7 | 全部检查点通过 |
|
||||
@@ -0,0 +1,150 @@
|
||||
# Batch I Worker C:7-15 / 7-12 后续拆分审查
|
||||
|
||||
> 创建时间:2026-06-15
|
||||
>
|
||||
> 审查依据:`7-15` 步骤 15-17、`7-12` Phase B-F 的已完成度
|
||||
|
||||
---
|
||||
|
||||
## 1. 7-15 Steps 15-17 状态判断
|
||||
|
||||
### 当前完成度
|
||||
|
||||
| Step | 名称 | 状态 | 说明 |
|
||||
|------|------|------|------|
|
||||
| 1-14 | ACP 核心实现 | ✅ 全部完成 | `acp_client.rs`、`acp_session_manager.rs`、`acp_runtime.rs`、`acp_bridge.rs`、`reasonix-acp-wrapper.mjs`、profile 扩展、前端下拉、e2e 验证 |
|
||||
| 15 | 压力测试 | ❌ 未开始 | 多会话并发稳定性 |
|
||||
| 16 | 退役旧 HTTP proxy | 🟡 部分开始 | 默认已切 ACP,但 `hermes_client.rs` 内的 HTTP proxy 兼容分支仍存在 |
|
||||
| 17 | 基准测试 | ❌ 未开始 | Reasonix 缓存收益量化 |
|
||||
|
||||
### 结论:Steps 15-17 应拆分为独立 checklist
|
||||
|
||||
**理由:**
|
||||
|
||||
1. **性质不同** — Step 15(压力测试)和 Step 17(基准测试)是验证/度量任务,不是架构实现。Step 16(清理)是维护性工作。它们与 7-15 的核心目标(ACP 抽象层实现)没有结构性依赖。
|
||||
|
||||
2. **完成标准不同** — 压力测试可能需要多轮迭代(发现瓶颈→修复→再测),基准测试可能发现需要缓存策略调整,退役清理需要确认所有 profile 都 migrate 完。这些都不是"一个 step done 就进入下一步"的线性模式。
|
||||
|
||||
3. **已开始的部分 Step 16 不够干净** — 代码中 `configured_upstream_for_profile()` 的 `#[deprecated]` 因 warning 太多被移除,表明清理依赖范围广、风险高,需要独立计划。
|
||||
|
||||
4. **当前 7-15 文档混杂实现 + 验证** — 读文档的人会觉得 7-15 处于"未完成"状态,但实际上架构核心已经完成并在生产运行。
|
||||
|
||||
### 建议拆分方案
|
||||
|
||||
将 Steps 15-17 独立为新文件 `7-28-acp-runtime-stabilization-and-cleanup-v1.md`,在 `design/07-ai/process/` 下:
|
||||
|
||||
- **P0**:Step 16 残余 — 完成 `configured_upstream_for_profile()` HTTP proxy 分支的退役
|
||||
- **P1**:Step 15 — 压力测试,重点是 ACP 子进程生命周期管理
|
||||
- **P2**:Step 17 — 基准测试,缓存命中率对比表格
|
||||
|
||||
---
|
||||
|
||||
## 2. 7-12 Phase B-F 被覆盖状态
|
||||
|
||||
### Phase B:Manifest 合同收口
|
||||
|
||||
| 条目 | 7-12 描述 | 当前代码状态 | 覆盖评估 |
|
||||
|------|----------|-------------|---------|
|
||||
| B-1 | manifest 输出完整 `inputSchema/outputSchema/annotations/availability` | ✅ `manifest.rs` 已有完整 annotations、inputSchema、parameters | 已覆盖。每个工具包含 `annotations.readonly/destructive/idempotent/requiresApproval/approvalMode/runtimeOwner/writeOwner/selectionEffect` |
|
||||
| B-2 | profile toggle、capability、scope 共同影响 manifest | 🟡 部分 | `capabilityScope` 已在 manifest 中,但 profile-level toggle 未动态反映到 `availability.enabled` 字段 |
|
||||
| B-3 | manifest 可直接转换为 Hermes/model tools | ✅ 前端 SSR 直接消费 manifest 作为工具列表 | 已覆盖。manifest 结构已与 Hermes 兼容 |
|
||||
| B-4 | 禁用工具在 manifest、UI、执行拦截三处一致 | 🟡 部分 | 执行拦截通过 `capabilityScope` + 后端 `ensure_markdown_changed_blocks_within_allowed` 存在,但 UI 侧 toggle 反映需验证 |
|
||||
|
||||
**结论:** Phase B 大部分被当前代码覆盖。B-2 和 B-4 的核心缺口是 profile-level dynamic availability,而不是 manifest 内容。
|
||||
|
||||
### Phase C:`block-edit-workflow` 改造成 router
|
||||
|
||||
| 条目 | 7-12 描述 | 当前代码状态 | 覆盖评估 |
|
||||
|------|----------|-------------|---------|
|
||||
| C-1 | route 命名和返回 schema 改为 `mnote.page_ai_command_route.v1` | ❌ 未实施 | `page_ai_workflow.rs` 仍在用内部 `MarkdownPlan` 对象,没有公开的 `PageAICommandRouter` route |
|
||||
| C-2 | 本地规则只输出 `recommendedToolCall` | 🟡 间接覆盖 | `page_ai_workflow.rs` 中的 `direct_block_edit_operations` 已退役,改走 model → search/replace → `doc_markdown_edit` 路径 |
|
||||
| C-3 | 低风险 yolo shortcut 走共享 mnote tool executor | ✅ 当前路径 | `doc_markdown_edit` 直接调用 Rust tool executor |
|
||||
| C-4 | 非低风险任务发起 Hermes run with tool hint | ✅ ACP integration | `acp_runtime.rs` + `acp_session_manager.rs` 提供完整的 Hermes/Reasonix run 路径 |
|
||||
| C-5 | 删除"模型 fallback 后再 Hermes agent run"重复链路 | 🟡 需确认 | `page_ai_workflow.rs` 中的 `call_block_edit_model` → `extract_markdown_plan` 链路仍然存在,但已改为直接走 `doc_markdown_edit` 不回落 |
|
||||
|
||||
**结论:** Phase C 的架构目标(agent-native 编辑 > mnote block ops)已通过 ACP + local-first 口径实现,但正式的 `PageAICommandRouter` 模块从未实现。不需要再实现它——当前架构已满足 7-12 的安全合同目标。
|
||||
|
||||
### Phase D:Review Session
|
||||
|
||||
| 条目 | 7-12 描述 | 当前代码状态 | 覆盖评估 |
|
||||
|------|----------|-------------|---------|
|
||||
| D-1 | schema 和状态机边界 | ✅ 文档已定义 | 设计文档 §5.5 已完成 |
|
||||
| D-2 | `plan_update`/`apply_block_ops dryRun=true` 返回 review-compatible draft | 🟡 部分 | dryRun 支持存在且返回 diff/warnings/risk/blocked,但输出格式不是 review session schema |
|
||||
| D-3 | UI 展示 diff/warnings/risk/blocked | ❌ **冻结** | Phase C 仍冻结,不实施 |
|
||||
| D-4 | accept/reject/retry/abort | ❌ **冻结** | 同上 |
|
||||
| D-5 | stale revision 阻断 | ✅ 部分 | `ensure_markdown_changed_blocks_within_allowed` + revision 检查 |
|
||||
|
||||
**结论:** 符合 7-12 §0 的指示——"Phase C UI 仍冻结,不实施流式 apply 或新的审阅 UI"。本报告不建议本轮实现。
|
||||
|
||||
### Phase E:状态与事件统一
|
||||
|
||||
| 条目 | 7-12 描述 | 覆盖评估 |
|
||||
|------|----------|---------|
|
||||
| E-1 | direct shortcut 和 Hermes run 统一 tool event 形态 | ✅ ACP `session/update` 作为统一事件源 |
|
||||
| E-2 | 页面 AI 面板按 `runId/toolCallId/reviewSessionId` 聚合 | 🟡 前端已支持 runId/toolCallId,reviewSessionId 未用 |
|
||||
| E-3 | abort 不留下半写入正文 | ✅ ACP `session/cancel` + 本地回滚 |
|
||||
| E-4 | 刷新后未提交 review session 不自动写入 | ✅ 不适用(review session 未实现) |
|
||||
|
||||
### Phase F:验收 Smoke
|
||||
|
||||
| 条目 | 7-12 描述 | 覆盖评估 |
|
||||
|------|----------|---------|
|
||||
| F-1 | 低歧义替换 <1s 且 tool audit | 🟡 功能存在,具体延迟需要基准测试(Step 17) |
|
||||
| F-2 | 复杂改写进 Hermes run,先 dry-run/review | ✅ ACP 路径支持 |
|
||||
| F-3 | selection 外写入 blocked | ✅ `hermes_tools_markdown_edit_rejects_selection_out_of_scope` 测试 pass |
|
||||
| F-4 | 禁用工具一致 | 🟡 后端拦截存在,manifest 动态 toggle 待完善 |
|
||||
| F-5 | 旧 revision accept → stale | ❌ review session 未实现,此场景不存在 |
|
||||
|
||||
---
|
||||
|
||||
## 3. Phase C Review Mode 冻结确认
|
||||
|
||||
**确认冻结。** 符合 7-12 §0 的 2026-05-18 更新:
|
||||
|
||||
> `PageAIReviewSession` 只定义 Phase C 的安全合同和状态机边界;当前 Phase C 仍冻结,不实施流式 apply 或新的审阅 UI
|
||||
|
||||
由于 review mode UI 涉及:
|
||||
1. 新的前端组件(diff 面板、accept/reject/retry/abort 按钮)
|
||||
2. 新的后端 session 持久化路径
|
||||
3. 与 ACP session 的生命周期整合(review session 超时、刷新恢复)
|
||||
4. `thought.delta` / `usage.updated` 的可视化渲染
|
||||
|
||||
这些都不在本轮范围内。所有 review-compatible 的后端数据结构(dryRun、changedBlocks、audit)已准备就绪,仅差 UI 整合。
|
||||
|
||||
---
|
||||
|
||||
## 4. 下一批 P0/P1/P2 优先级
|
||||
|
||||
### P0(本轮必须完成)
|
||||
|
||||
| 编号 | 工作 | 对应 | 风险 |
|
||||
|------|------|------|------|
|
||||
| P0-1 | 完成 `configured_upstream_for_profile()` 的 HTTP proxy 清理 | 7-15 Step 16 残余 | 低。当前默认已切 ACP,但 `hermes_client.rs` 中 ~500 行 HTTP proxy 路径仍有调用者确认 |
|
||||
| P0-2 | 确认 `page_ai_workflow.rs` 在所有 profile 下不走 block_edit fallback | 7-12 C-5 | 中。当前 workflow 调 `call_block_edit_model` → `extract_markdown_plan` → `doc_markdown_edit`,但少部分 code path 可能还有残留的 `apply_block_ops` |
|
||||
| P0-3 | `7-15` 拆分为「核心实现」(done) + 「稳定化清单」(new) | 本审查结论 | 低。只改设计文档 |
|
||||
|
||||
### P1(紧接 P0 后)
|
||||
|
||||
| 编号 | 工作 | 对应 | 风险 |
|
||||
|------|------|------|------|
|
||||
| P1-1 | 压力测试:多会话并发、ACP 子进程异常恢复 | 7-15 Step 15 | 中。需要同时管理 3+ 会话时 Hermes/Reasonix 子进程状态 |
|
||||
| P1-2 | 动态 tool availability per profile(manifest 的 `enabled` 字段) | 7-12 B-2/B-4 | 中低。影响 tool toggle/audit 一致性 |
|
||||
| P1-3 | `plan_update` dryRun 输出适配 review session schema | 7-12 D-2 | 低。后端数据结构对齐 |
|
||||
|
||||
### P2(可并行但非阻塞)
|
||||
|
||||
| 编号 | 工作 | 对应 | 风险 |
|
||||
|------|------|------|------|
|
||||
| P2-1 | 基准测试:Reasonix cache hit rate vs Hermes | 7-15 Step 17 | 低。纯度量,不涉及代码变更 |
|
||||
| P2-2 | `7-12` 更新状态:标记 Phase B 已完成项、更新口径 | 设计治理 | 低 |
|
||||
| P2-3 | 复杂块 GFM 解析 fallback 覆盖(list/code block/blockquote) | 7-27 §5.3 | 低。影响 full_content 块类型推断 |
|
||||
| P2-4 | 页面 AI `thought.delta` 可视化 | 7-15 Step 11 note | 低。当前预期行为是不显示 thought |
|
||||
|
||||
---
|
||||
|
||||
## 5. 验收证据
|
||||
|
||||
- `cargo test -p mnote-web markdown_edit -- --test-threads=1` → 16 pass
|
||||
- `cargo test -p mnote-web hermes_tools_manifest_describes_markdown_edit_write_contract -- --test-threads=1` → 1 pass
|
||||
- `git diff --check` → 无 whitespace 错误
|
||||
- 本文件已写入 `.codex/reasonix-tasks/results/batch-i-worker-c-acp-review-tail.md`
|
||||
@@ -0,0 +1,132 @@
|
||||
# Batch J Worker A:旧 HTTP proxy / page AI fallback 调用链审查
|
||||
|
||||
> 创建时间:2026-05-21
|
||||
>
|
||||
> 只读审查,不修改代码。
|
||||
|
||||
## 1. 读取过的关键文件
|
||||
|
||||
| 文件 | 行数 | 内容 |
|
||||
|------|------|------|
|
||||
| `rust/crates/mnote-web/src/routes/hermes_client.rs` | 8202 | 首页面 AI Hermes 路由:session/run/tool 的 proxy 和 ACP 双路径 |
|
||||
| `rust/crates/mnote-web/src/page_ai_workflow.rs` | — | **不存在**(不在根目录,在 `routes/` 下) |
|
||||
| `rust/crates/mnote-web/src/routes/page_ai_workflow.rs` | 500+ | 页面 AI block-edit fast path |
|
||||
| `rust/crates/mnote-web/src/acp_runtime.rs` | 500+ | ACP runtime manager |
|
||||
| `rust/crates/mnote-web/src/acp_session_manager.rs` | 700+ | ACP session lifecycle |
|
||||
| `rust/crates/mnote-web/src/routes/mod.rs` | 200+ | 全路线由注册 |
|
||||
| `rust/crates/mnote-web/src/routes/compat.rs` | 200+ | 旧 `/api/ai-agent/run` 退役 guard |
|
||||
|
||||
## 2. 调用链分析
|
||||
|
||||
### 2.1 旧 HTTP proxy 路径
|
||||
|
||||
**判断函数:** `hermes_client.rs:2844` `hermes_http_proxy_enabled()`
|
||||
|
||||
```rust
|
||||
fn hermes_http_proxy_enabled() -> bool {
|
||||
["MNOTE_WEB_ENABLE_HERMES_HTTP_PROXY", "MNOTE_ENABLE_HERMES_HTTP_PROXY"]
|
||||
.into_iter().find_map(env_or_dotenv)
|
||||
.map(|v| matches!(v.trim().to_ascii_lowercase(), "1" | "true" | "yes"))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
```
|
||||
|
||||
**默认关闭**(`unwrap_or(false)`)。HTTP proxy 仅在显式设 `MNOTE_WEB_ENABLE_HERMES_HTTP_PROXY=true` 时启用。
|
||||
|
||||
**上游 URL 来源:** `configured_upstream()`(`hermes_client.rs:2813`)
|
||||
|
||||
- 检查 `MNOTE_WEB_HERMES_UPSTREAM_URL` → `MNOTE_HERMES_UPSTREAM_URL` → `MNOTE_HERMES_API_BASE_URL`
|
||||
- 均不存在时返回 `None`
|
||||
|
||||
**`is_acp_profile()` 路由逻辑**(`hermes_client.rs:2871`):
|
||||
|
||||
```rust
|
||||
fn is_acp_profile(profile: &str) -> bool {
|
||||
if !hermes_http_proxy_enabled() { return true; } // 默认全走 ACP
|
||||
if profile == "reasonix" || profile == "hermes" { return true; }
|
||||
// 还可通过 MNOTE_WEB_{PROFILE}_RUNTIME_TYPE=acp 环境变量控制
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 各路由入口分类
|
||||
|
||||
| Route | Handler | HTTP proxy 条件 | 默认走 | 分类 |
|
||||
|-------|---------|-----------------|--------|------|
|
||||
| `GET /client/sessions` | `list_sessions` | `!is_acp_profile(profile) && configured_upstream_for_profile(profile).is_some()` | **ACP** | `compat` |
|
||||
| `GET /client/sessions/search` | `search_sessions` | 同上 | **ACP** | `compat` |
|
||||
| `GET /client/gateway/health` | `gateway_health` | `!is_acp_profile(&profile)` → HTTP proxy | **ACP**(对于非 ACP profile 走 HTTP proxy) | `compat` |
|
||||
| `POST /client/runs` | `create_run` | `configured_upstream_for_profile(®istration.profile).is_some()` | **ACP** | `compat` |
|
||||
| `GET /client/events/{run_id}` | `stream_events` | 先查 `acp_runtime_for_run()`,再 fallback `configured_upstream_for_profile()` | **ACP** | `compat` |
|
||||
| `POST /client/runs/{run_id}/abort` | `abort_run` | 同上 | **ACP** | `compat` |
|
||||
| `GET /client/models` | `list_models` | 直接 `configured_upstream()` | HTTP proxy(与 ACP 无关的 model 列表) | `compat` |
|
||||
| 队列自动处理 | `auto_start_next_queued_run`(line 5377) | `configured_upstream_for_profile(queued.profile)` | **ACP** | `compat` |
|
||||
| `POST /api/ai-agent/run` | `compat::next_ai_agent_run` | — | **始终返回 410 GONE** | `retired` |
|
||||
| `POST /api/page-ai/block-edit-workflow` | `page_ai_workflow::block_edit_workflow` | 绕过 ACP,直接调模型 API + `execute_mnote_tool_call` | debug-only fast path | `debug-only` |
|
||||
|
||||
### 2.3 page_ai_workflow 是否绕过 ACP / tool executor?
|
||||
|
||||
**调用链:**
|
||||
|
||||
```
|
||||
POST /api/page-ai/block-edit-workflow
|
||||
→ looks_like_block_edit(message) ← 中文关键词匹配
|
||||
→ call_block_edit_model() ← 直调 DeepSeek/etc API(HTTP)
|
||||
→ extract_markdown_plan_from_model_text()
|
||||
→ 构造 ToolCallInput { tool_name: "mnote.doc.markdown_edit", capability_scope: ["block.write","page.write"], ... }
|
||||
→ hermes_tools::execute_mnote_tool_call() ← 共享 tool executor
|
||||
→ is_mnote_tool_disabled() & ensure_write_authorized() & 实际执行
|
||||
```
|
||||
|
||||
**结论:**
|
||||
|
||||
1. **不经过旧 HTTP proxy** ✓ — 没有调用 `configured_upstream_for_profile`
|
||||
2. **不经过 ACP session manager** — 直接调用 `execute_mnote_tool_call`,没有 `session/new` + `session/prompt` 生命周期
|
||||
3. **但经过共享 tool executor** — 受 `is_mnote_tool_disabled()`、`ensure_write_authorized()`、`is_shared_read_scope()` 等守卫保护
|
||||
4. **设计意图**:`7-15` 设计文档说明 "local-first 普通正文编辑默认不经过它",该路径是给合模型快速做 search/replace 块编辑的 fast path,不是常规 AI 执行路径
|
||||
|
||||
### 2.4 `compat::next_ai_agent_run` 状态
|
||||
|
||||
`routes/compat.rs:10` 的 `next_ai_agent_run()` 始终返回 `410 GONE` + 消息"旧 /api/ai-agent/run 页面 AI 主链已退场"。**已确认退役**,无 P0 风险。
|
||||
|
||||
## 3. 各入口分类汇总
|
||||
|
||||
| 分类 | 条目 | 说明 |
|
||||
|------|------|------|
|
||||
| `default` | 无 | HTTP proxy 默认关闭;ACP 是默认 runtime |
|
||||
| `compat` | `list_sessions`, `search_sessions`, `gateway_health`, `create_run`, `stream_events`, `abort_run`, `list_models`, 队列处理 | 仅当 `MNOTE_WEB_ENABLE_HERMES_HTTP_PROXY=true` 时激活;不影响正常用户 |
|
||||
| `debug-only` | `page_ai_workflow::block_edit_workflow` | bypass ACP session,但受工具 executor 守卫保护 |
|
||||
| `retired` | `compat::next_ai_agent_run` | 已返回 410,无降级路径 |
|
||||
| `未被调用` | `configured_runtime_for_profile()`(line 3368) | 标记 `#[allow(dead_code)]` |
|
||||
|
||||
## 4. P0 缺口评估
|
||||
|
||||
**当前没有 P0 必须修复的缺口。**
|
||||
|
||||
理由:
|
||||
1. HTTP proxy 默认关闭(`hermes_http_proxy_enabled() = false`)
|
||||
2. `compat::next_ai_agent_run` 已正确返回 410
|
||||
3. `page_ai_workflow` 经过共享 tool executor 守卫,不绕过权限检查
|
||||
4. 所有路由的第一分支都是 ACP
|
||||
|
||||
## 5. 建议的后续修改范围
|
||||
|
||||
| 项 | 建议 | 紧急程度 |
|
||||
|----|------|---------|
|
||||
| HTTP proxy 完整退役 | 删除 `configured_upstream()` / `configured_upstream_for_profile()` / `proxy_json()` 相关代码 | P2(cleanup) |
|
||||
| `list_models` 从 HTTP proxy 迁移 | 改为 ACP 的 `session/initialize` 返回 model 信息 | P2(依赖 ACP 协议增强) |
|
||||
| `page_ai_workflow` 检查驱动 | 确保测试覆盖其 bypass ACP session 的行为的非期望路径(`task-page-block-ai-smoke.js` → 已存在) | 已有 |
|
||||
| 旧环境变量清理 | 文档标记 `MNOTE_WEB_HERMES_UPSTREAM_URL` 等为退役 | P2 |
|
||||
|
||||
## 6. 建议测试
|
||||
|
||||
```
|
||||
# 确认 HTTP proxy 默认关闭(infra 测试)
|
||||
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web hermes_http_proxy -- --test-threads=1
|
||||
|
||||
# 确认 compat 路由返回 410(已存在)
|
||||
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web direct_ai_agent_run_returns_legacy_retired_guard -- --test-threads=1
|
||||
|
||||
# 确认 page_ai_workflow 受守卫保护(已存在)
|
||||
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web block_edit_workflow_respects_disabled_markdown_edit_tool -- --test-threads=1
|
||||
```
|
||||
@@ -0,0 +1,182 @@
|
||||
# Batch J Worker B:tool availability 三处一致性审查
|
||||
|
||||
> 创建时间:2026-05-21
|
||||
>
|
||||
> 只读审查,不修改代码。
|
||||
|
||||
## 1. 读取过的关键文件
|
||||
|
||||
| 文件 | 行数 | 内容 |
|
||||
|------|------|------|
|
||||
| `rust/crates/mnote-web/src/hermes_tools/manifest.rs` | 400+ | Tool 定义:name、description、`status`、`capabilityScope`、`annotations` |
|
||||
| `rust/crates/mnote-web/src/hermes_tools/mod.rs` | 280+ | `ToolCallInput` 结构体 + `ensure_write_authorized()` 守卫 |
|
||||
| `rust/crates/mnote-web/src/routes/hermes_tools.rs` | 3691 | `execute_mnote_tool_call()` — 入口 + 工具分派 |
|
||||
| `rust/crates/mnote-web/src/routes/hermes_client.rs` (L2247-2302) | ~55 | `disabled_mnote_tools()`, `mnote_tools_payload()`, `mnote_tool_entry()` |
|
||||
| `rust/crates/mnote-web/src/ssr/pages/layout.rs` (L50, L6346-6390, L6895-6990) | ~100 | `pageAiTools` 状态、`pageAiNormalizeTools()`、tool 渲染 |
|
||||
|
||||
## 2. Manifect / UI / Execute guard 能力判断来源
|
||||
|
||||
### 2.1 Manifect(`hermes_tools/manifest.rs`)
|
||||
|
||||
```rust
|
||||
// 每个工具定义包含:
|
||||
{
|
||||
"name": "mnote.doc.fetch",
|
||||
"description": "读取当前页面的 canonical block projection...",
|
||||
"schemaVersion": TOOL_SCHEMA_VERSION,
|
||||
"capabilityScope": ["page.read"], // 需要的权限声明
|
||||
"status": "available", // 静态 availability
|
||||
"annotations": tool_annotations(true, false, true, false)
|
||||
// readonly, destructive, idempotent, requires_approval
|
||||
}
|
||||
```
|
||||
|
||||
关键缺陷:
|
||||
- `page_get_tool()` 和 `page_save_tool()` **没有设置 `annotations` 字段**
|
||||
- `available_tool()` 创建的条目也没有 `annotations`
|
||||
- `status` 字段全部硬编码为 `"available"`(无动态 availability)
|
||||
|
||||
### 2.2 Tool 列表服务端(`hermes_client.rs:2258` `mnote_tools_payload()`)
|
||||
|
||||
```
|
||||
manifest::manifest() ← 静态 manifest
|
||||
↓
|
||||
disabled_mnote_tools(profile) ← 从 profile 配置 YAML 读取 mnote.tools.disabled 列表
|
||||
↓
|
||||
mnote_tool_entry(tool, &disabled) ← 若 tool.name ∈ disabled,设 status=disabled, enabled=false
|
||||
↓
|
||||
返回给前端
|
||||
```
|
||||
|
||||
**disabled 来源:** `disabled_mnote_tools(profile)`(`hermes_client.rs:2247`)
|
||||
|
||||
```rust
|
||||
pub(crate) fn disabled_mnote_tools(profile: &str) -> Vec<String> {
|
||||
let content = fs::read_to_string(profile_config_path(profile)).unwrap_or_default();
|
||||
yaml_disabled_list(&content, &["mnote", "tools", "disabled"])
|
||||
}
|
||||
```
|
||||
|
||||
从 Hermes profile YAML 的 `mnote.tools.disabled` 键下读取禁用列表。
|
||||
|
||||
### 2.3 UI(`layout.rs`)
|
||||
|
||||
工具列表渲染链路:
|
||||
|
||||
```
|
||||
fetch('/api/hermes/client/tools?scope=mnote&profile=' + profile)
|
||||
↓
|
||||
pageAiNormalizeTools(payload)
|
||||
↓ 提取: name, description, scope, status, enabled, unavailableReason
|
||||
↓
|
||||
renderPageAiControls()
|
||||
↓ 渲染 tool row: name + scope + status + toggle button
|
||||
toggle 状态: tool.enabled !== false → aria-pressed + is-on CSS
|
||||
```
|
||||
|
||||
UI 对所有工具平等渲染,**不隐藏 disabled 工具**(只显示 switching 开关 + `"当前 Hermes profile 已关闭该 mnote tool"` 提示)。前端不做任何独立的 capability 判断。
|
||||
|
||||
### 2.4 Execute guard(`hermes_tools.rs:219` + `mod.rs` `ensure_write_authorized()`)
|
||||
|
||||
守卫有两层:
|
||||
|
||||
**第一层**(`hermes_tools.rs:219` — `execute_mnote_tool_call()` 入口):
|
||||
|
||||
```rust
|
||||
if hermes_client::is_mnote_tool_disabled(&profile, &input.tool_name) {
|
||||
return Err(403 "mnote_tool_disabled")
|
||||
}
|
||||
```
|
||||
|
||||
**同源**:与 listing 端调用同一个 `is_mnote_tool_disabled()` → `disabled_mnote_tools()`。
|
||||
|
||||
**第二层**(`mod.rs:186` — `ensure_write_authorized()` 在具体 tool handler 中调用):
|
||||
|
||||
```rust
|
||||
pub fn ensure_write_authorized(context: &RequestContext, input: &ToolCallInput) -> Result<(), WebError> {
|
||||
// 1. idempotencyKey 必须存在
|
||||
// 2. dryRun 必须显式携带
|
||||
// 3. aiAccessScope.permissionLevel 不能是只读
|
||||
// 4. CommandContextBridge.ai_can_write ≠ false
|
||||
// 5. CommandContextBridge.workspace_readonly ≠ true
|
||||
}
|
||||
```
|
||||
|
||||
这层不涉及 manifest 的 capabilityScope。
|
||||
|
||||
**第三层**(`hermes_tools.rs:242` — shared read 检查):
|
||||
|
||||
```rust
|
||||
if !dry_run && !is_read_tool(&input.tool_name) && is_shared_read_scope(&input) {
|
||||
return Err(403 "mnote_tool_shared_read_write_forbidden")
|
||||
}
|
||||
```
|
||||
|
||||
通过 `aiAccessScope.permissionLevel` 判断共享只读。
|
||||
|
||||
## 3. 三处是否同源
|
||||
|
||||
### 3.1 Disabled 列表:✅ 完全同源
|
||||
|
||||
| 层 | 判断 | 来源 |
|
||||
|----|------|------|
|
||||
| Manifect | 无动态 disabled(static) | — |
|
||||
| Listing | `disabled_mnote_tools(profile)` | profile YAML `mnote.tools.disabled` |
|
||||
| UI | 接收 `enabled` 字段 | 来自 listing endpoint |
|
||||
| Execute guard | `is_mnote_tool_disabled(profile, name)` | 同一 `disabled_mnote_tools()` |
|
||||
|
||||
所有层在"哪些工具被禁用"上使用同一数据源(Hermes profile YAML)。
|
||||
|
||||
### 3.2 capabilityScope 执行:⚠️ 不同源(P1)
|
||||
|
||||
| 层 | capabilityScope 用法 | 角色 |
|
||||
|----|---------------------|------|
|
||||
| Manifect | 每个工具声明 `capabilityScope: ["page.read"]` 或 `["page.write"]` | **文档性** — 告诉 AI agent 需要什么权限 |
|
||||
| Listing | 转发 `capabilityScope` 到 UI | **展示性** |
|
||||
| UI | 不消费 `capabilityScope` 做判据 | **不展示权限要求** |
|
||||
| Execute guard | **不检查** `input.capability_scope` 与 manifest scope 的匹配 | **未执行** |
|
||||
|
||||
**关键缺口**:manifect 声明了工具需要的权限范围(`page.read` / `page.write` / `block.write` 等),但 execute guard 没有验证调用方声明的 `capability_scope` 是否包含目标工具所需的 scope。
|
||||
|
||||
`ToolCallInput.capability_scope` 虽存在于结构体中,但只做:
|
||||
1. 传递给具体 tool handler 作为 `RuntimeTargetWire.capabilities`(artifact.rs:187, block.rs:1136, page.rs:279)
|
||||
2. 记录到 audit 日志(hermes_tools.rs:433)
|
||||
3. 由 `page_ai_workflow.rs:130` 在调用时手动设置 `capability_scope: Some(vec!["block.write".into(), "page.write".into()])`
|
||||
|
||||
无中心守卫验证 `input.capability_scope` 与 manifest 中该 tool 的 `capabilityScope` 的包含关系。
|
||||
|
||||
## 4. P0/P1/P2 分级缺口
|
||||
|
||||
### P0:无
|
||||
|
||||
disabled 工具在三层间保持一致,没有安全绕过。
|
||||
|
||||
### P1:capabilityScope 未在 execute guard 强制执行
|
||||
|
||||
- **漏洞描述**:Agent 可以声明 `capability_scope: ["page.read"]`,但仍然调用 `mnote.page.save`(写工具)。守卫只检查 `is_shared_read_scope`(看 `aiAccessScope.permissionLevel`)和 `is_read_tool`(看硬编码列表),不检查 manifest 声明的 scope。
|
||||
- **影响**:低(不构成安全漏洞,因为还有 `aiAccessScope.permissionLevel` + 文件系统权限 + 授权 root 三层兜底),但 manifests 声明的 scope 信息完全浪费。
|
||||
- **修复建议**:在 `execute_mnote_tool_call()` 中,校验 `input.capability_scope` 是否覆盖 manifest 中该工具的 `capabilityScope`。
|
||||
|
||||
### P2:Manifest 元数据不完整
|
||||
|
||||
| 缺陷 | 位置 | 说明 |
|
||||
|------|------|------|
|
||||
| `page_get_tool()` 缺 `annotations` | `manifest.rs` 末尾 | 没有 `readonly`/`destructive` 标记 |
|
||||
| `page_save_tool()` 缺 `annotations` | `manifest.rs` 末尾 | 同上 |
|
||||
| `available_tool()` 创建的工具缺 `annotations` | `manifest.rs` | `mnote.page.update_title`, `mnote.page.update_options`, `mnote.artifact.create_summary`, `mnote.artifact.create_ai_note` |
|
||||
| `status` 字段全部硬编码 | `manifest.rs` | 无动态 availability;manifect 不含 disabled 状态(在 listing 层叠加) |
|
||||
| 前端 UI 不展示 `capabilityScope` | `layout.rs:6346-6380` | `pageAiNormalizeTools` 解析但不消费,渲染时不显示权限要求 |
|
||||
|
||||
## 5. 建议的最小测试
|
||||
|
||||
```bash
|
||||
# 已有:disabled tool 被 execute guard 拒绝
|
||||
cargo test -p mnote-web block_edit_workflow_respects_disabled_markdown_edit_tool
|
||||
|
||||
# 已有:write guard 检查(aiAccessScope、idempotencyKey)
|
||||
cargo test -p mnote-web ensure_write_authorized
|
||||
|
||||
# 新增建议:capability_scope 校验测试
|
||||
# 位置:hermes_tools/mod.rs tests
|
||||
# 内容:构造 ToolCallInput { capability_scope: ["page.read"] } 调用 mnote.page.save,期望被拒绝
|
||||
```
|
||||
@@ -0,0 +1,217 @@
|
||||
# Batch J Worker C:ACP 稳定性与 benchmark checklist 草案
|
||||
|
||||
> 创建时间:2026-05-21
|
||||
>
|
||||
> 只读审查,不修改代码。
|
||||
|
||||
## 1. 读取过的关键文件
|
||||
|
||||
| 文件 | 行数 | 内容 |
|
||||
|------|------|------|
|
||||
| `design/07-ai/process/7-15-page-ai-acp-agent-runtime-unified-layer-v1.md` | 1029 | ACP 统一层设计:架构、协议、session lifecycle |
|
||||
| `design/07-ai/process/7-34-acp-runtime-cleanup-availability-stability-tail-v1.md`| 300+ | 本任务上游:P0/P1/P2 checklist |
|
||||
| `design/07-ai/done/7-25-acp-session-runtime-enhancement-plan-v1.md` | 371 | Session 持久化与管理增强规划 |
|
||||
| `design/07-ai/done/7-30-acp-session-load-resume-checklist-v1.md` | ~80 | `session/load` 闭环已归档 |
|
||||
| `design/07-ai/done/7-31-acp-permission-decision-loop-checklist-v1.md` | ~80 | Permission 决策闭环已归档 |
|
||||
| `design/07-ai/done/7-32-acp-tool-location-and-open-action-checklist-v1.md` | ~80 | Tool locations + open action 已归档 |
|
||||
| `design/07-ai/done/7-33-acp-session-info-plan-ui-checklist-v1.md` | ~80 | Session info + plan UI 已归档 |
|
||||
| `rust/crates/mnote-web/src/acp_runtime.rs` | 500+ | Runtime lifecycle manager |
|
||||
| `rust/crates/mnote-web/src/acp_session_manager.rs` | 700+ | Session lifecycle + event dispatch |
|
||||
| `rust/crates/mnote-web/src/acp_bridge.rs` | 400+ | ACP ↔ SSE bridge |
|
||||
| `rust/crates/mnote-web/src/acp_client.rs` | 746 | JSON-RPC 2.0 client |
|
||||
| `rust/crates/mnote-web/src/acp_types.rs` | 800 | ACP 协议类型定义 |
|
||||
|
||||
## 2. ACP 多会话稳定性 smoke 最小脚本方案
|
||||
|
||||
### 2.1 测试目标
|
||||
|
||||
验证 ACP runtime 在处理 3+ 并发 session 时的稳定性:session 创建、prompt 流式、cancel、子进程异常恢复、事件去重。
|
||||
|
||||
### 2.2 测试设计
|
||||
|
||||
**方案类型**:Playwright 独立脚本 + Rust 端到端测试
|
||||
|
||||
**输入**:
|
||||
1. 3 个参数化的 ACP run profile(reasonix + hermes × 2 不同 profile)
|
||||
2. 每个 session 的 prompt 文本(简单固定 prompt:"请用中文说'你好',不要做其他操作")
|
||||
3. 超时配置(每个 run 最长 15s)
|
||||
|
||||
**操作步骤**:
|
||||
|
||||
```
|
||||
Step 1: 创建 3 个 ACP session(POST /api/hermes/client/sessions)
|
||||
├─ session_1: profile=reasonix
|
||||
├─ session_2: profile=hermes (profile=mnoteai)
|
||||
└─ session_3: profile=hermes (profile=default)
|
||||
|
||||
Step 2: 在 3 个 session 上启动 run(POST /api/hermes/client/sessions/{id}/runs)
|
||||
并发发起,每个 session 间隔 ≤500ms
|
||||
|
||||
Step 3: 通过 SSE 流式读取事件(GET /api/hermes/client/events/{run_id})
|
||||
对每个 run 读取到 run.completed 或超时 15s
|
||||
记录:message.delta 事件数量、tool.started、tool.completed
|
||||
|
||||
Step 4: cancel 验证
|
||||
Step 2 后立即 cancel session_2(POST /api/hermes/client/runs/{run_id}/abort)
|
||||
验证收到 run.failed / abort.completed
|
||||
|
||||
Step 5: 验证事件去重
|
||||
对 session_1 的 SSE 流,检查 textDelta 的 deduplication 逻辑
|
||||
(accumulated text 不重复增长)
|
||||
|
||||
Step 6: 清理
|
||||
删除 3 个 session(DELETE /api/hermes/client/sessions/{session_id})
|
||||
```
|
||||
|
||||
**断言**:
|
||||
|
||||
| # | 断言 | 优先级 |
|
||||
|---|------|--------|
|
||||
| 1 | 3 个 session 均创建成功(返回 200 + sessionId) | P0 |
|
||||
| 2 | 3 个 run 均收到 `run.completed`(无超时) | P0 |
|
||||
| 3 | session_2 的 cancel 在 3s 内生效(收到 `run.failed` 或 `abort.completed`) | P0 |
|
||||
| 4 | 所有 SSE 流在 `run.completed` 后 500ms 内自动关闭(无泄漏) | P1 |
|
||||
| 5 | session_1 的 message.delta 累计 length 不小于 4(中文"你好") | P1 |
|
||||
| 6 | 事件去重正常:textDelta 无重复或突然截断 | P1 |
|
||||
| 7 | 3 个 session 互相不干扰(事件不交叉) | P1 |
|
||||
| 8 | ACP 子进程数 = session 数(无 zombie) | P2(度量) |
|
||||
|
||||
**证据文件**:
|
||||
- `artifacts/acp-stability/events-session-1.jsonl`
|
||||
- `artifacts/acp-stability/events-session-2.jsonl`
|
||||
- `artifacts/acp-stability/events-session-3.jsonl`
|
||||
- `artifacts/acp-stability/runtime-metrics.json`
|
||||
- `artifacts/acp-stability/process-list.txt`(`ps aux | grep -E 'hermes|acp'`)
|
||||
|
||||
### 2.3 浏览器可见验证点
|
||||
|
||||
1. **session 列表** — 打开页面 AI drawer → session list 应显示 3 个活跃 session
|
||||
2. **run 状态** — 每个 session 的 run 状态从 `sending` → `running` → `completed`
|
||||
3. **cancel 反馈** — 被 cancel 的 session 显示 "已请求停止" + 后续不再有 tool call
|
||||
4. **无 UI 异常** — 无 502/500 错误,无工具 manifest 加载失败
|
||||
|
||||
建议截图:
|
||||
- 3 session 同时运行的页面 AI drawer 快照
|
||||
- cancel 后的 UI 状态
|
||||
- 清理后 session 列表为空
|
||||
|
||||
## 3. Reasonix cache benchmark 最小脚本方案
|
||||
|
||||
### 3.1 测试目标
|
||||
|
||||
对比 Hermes / Reasonix 在相同 prompt 下的首次运行、二次运行(cache 命中)的耗时差异。
|
||||
|
||||
### 3.2 测试设计
|
||||
|
||||
**输入**:
|
||||
|
||||
| 维度 | 值 |
|
||||
|------|-----|
|
||||
| Prompt | "读取当前页面,列出前三段的主题"(约 20 token) |
|
||||
| 样本数 | 每 runtime × 2(cold + cached)= 4 次 |
|
||||
| Runtime | Hermes (mnoteai profile)、Reasonix |
|
||||
| 页面 | 已知内容的本地 Markdown 文档(>50 行) |
|
||||
|
||||
**操作步骤**:
|
||||
|
||||
```
|
||||
Step 1: 预热
|
||||
创建一个 ACP session(profile=reasonix),发送 prompt,等待完成,删除 session
|
||||
|
||||
Step 2: 冷启动测试(Reasonix)
|
||||
新 session(profile=reasonix)→ prompt → 计时 → 记录 total_ms
|
||||
|
||||
Step 3: Cache 命中测试(Reasonix)
|
||||
同一 session re-prompt → 计时 → 记录 total_ms
|
||||
注意:确保 prompt 内容一致,观察 response 中是否有 cache hit 提示
|
||||
|
||||
Step 4: 冷启动测试(Hermes)
|
||||
新 session(profile=hermes/mnoteai)→ prompt → 计时 → 记录 total_ms
|
||||
|
||||
Step 5: Cache 命中测试(Hermes)
|
||||
同一 session re-prompt → 计时 → 记录 total_ms
|
||||
|
||||
Step 6: 重复 Step 2-5 共 1 轮(共 8 次测量)
|
||||
```
|
||||
|
||||
**输出格式**(JSON):
|
||||
|
||||
```json
|
||||
{
|
||||
"schema": "mnote.acp_cache_benchmark.v1",
|
||||
"timestamp": "2026-05-21T12:00:00Z",
|
||||
"runs": [
|
||||
{
|
||||
"runtime": "reasonix",
|
||||
"phase": "cold",
|
||||
"trial": 1,
|
||||
"totalMs": 4230,
|
||||
"firstTokenMs": 1200,
|
||||
"completionMs": 3030,
|
||||
"toolCalls": 1,
|
||||
"messageCharCount": 85,
|
||||
"cacheHit": false
|
||||
},
|
||||
{
|
||||
"runtime": "reasonix",
|
||||
"phase": "cached",
|
||||
"trial": 1,
|
||||
"totalMs": 890,
|
||||
"firstTokenMs": 210,
|
||||
"completionMs": 680,
|
||||
"toolCalls": 1,
|
||||
"messageCharCount": 85,
|
||||
"cacheHit": true
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"reasonixColdAvgMs": 4440,
|
||||
"reasonixCachedAvgMs": 920,
|
||||
"hermesColdAvgMs": 5100,
|
||||
"hermesCachedAvgMs": 3100,
|
||||
"speedupRatio": 4.8
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 浏览器验证点
|
||||
|
||||
1. **页面 AI drawer 运行时选择器** — 切换 hermes/reasonix 时的 UI 响应
|
||||
2. **首次对话速度** — 冷启动时 UI 从 "thinking" 到首次 token 的延迟
|
||||
3. **cache 提示** — Reasonix cached run 的 tool.call/complete 速度应更快
|
||||
4. **内存观察** — 浏览器 devtools → Performance → JS heap 无异常增长
|
||||
|
||||
### 3.4 建议测试脚本存放位置
|
||||
|
||||
```
|
||||
scripts/task-acp-stability-smoke.js ← 多会话稳定性
|
||||
scripts/task-acp-cache-benchmark.js ← cache benchmark
|
||||
```
|
||||
|
||||
参考已有:`scripts/task-page-block-ai-tools-smoke.js`、`scripts/task-hermes-page-ai-baseline-smoke.js`。
|
||||
|
||||
## 4. P0 vs P2 归类
|
||||
|
||||
| 测试项 | 级别 | 理由 |
|
||||
|--------|------|------|
|
||||
| 3 个 session 创建 + 正常完成 | **P0** | 并发是基本稳定性要求 |
|
||||
| Cancel 在合理时间内生效 | **P0** | UX 基础:用户停止必须工作 |
|
||||
| SSE 流在 run 完成后关闭 | **P1** | 防止资源泄漏 |
|
||||
| 事件去重正确 | **P1** | 文本重复或截断影响 UX |
|
||||
| Session 间事件不交叉 | **P1** | 数据隔离是基本正确性 |
|
||||
| Cold vs cached 耗时对比 | **P2** | 度量性质,不影响正确性 |
|
||||
| 子进程数量 | **P2** | 度量性质 |
|
||||
| 具体第一 token 时间 | **P2** | 度量性质 |
|
||||
|
||||
## 5. 建议的现有 targeted test
|
||||
|
||||
```bash
|
||||
# 现有 ACP 单元测试(可复用)
|
||||
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web acp_session_manager -- --test-threads=1
|
||||
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web acp_bridge -- --test-threads=1
|
||||
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web acp -- --test-threads=1
|
||||
|
||||
# 现有 smoke 测试(参考模式)
|
||||
node scripts/task-page-block-ai-tools-smoke.js
|
||||
node scripts/task-hermes-page-ai-baseline-smoke.js
|
||||
```
|
||||
Reference in New Issue
Block a user