chore: land tree view-state, vault, Pi module split, and repo hygiene
Persist PageTree expand state via control-plane view-state and align chevron/DOM with restored expansion; keep Sidex-style shallow page-tree scan and drop the unused recursive scanner that only added cargo noise. Add password vault workbench routes/runtime/skill/CLI, split page_ai_pi into a module package, and retire Hermes/ACP/OpenHub recycle + root harness evidence from the index while gitignoring recycle and local diag dumps. Archive superseded design/bugs docs under old/, point architecture at ARCHITECTURE.md, and refresh smokes for Pi S1–S7, vault, and editor regressions so the working tree can stay clean.
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
# [recycle] 7-1 [done] 页面 AI provider=hermes 仍命中旧 run route 导致 502
|
||||
|
||||
## 现象
|
||||
|
||||
- 当前 Rust 3000 页面壳里,页面 AI 默认 provider 是 `hermes`。
|
||||
- 发送消息时仍请求旧 `/api/ai-agent/run`。
|
||||
- Rust compat route 对显式 `provider=hermes` 返回 `ai_provider_bridge_unavailable`,HTTP 状态为 502。
|
||||
|
||||
## 复现证据
|
||||
|
||||
- 页面壳默认 provider:`rust/crates/mnote-web/src/ssr/pages/layout.rs`
|
||||
- 页面 AI 发送旧 route:`fetch('/api/ai-agent/run')`
|
||||
- 旧 route 注册:`rust/crates/mnote-web/src/routes/mod.rs`
|
||||
- 502 来源:`rust/crates/mnote-web/src/routes/compat.rs`
|
||||
- 可复跑 smoke:`scripts/task-hermes-page-ai-baseline-smoke.js`
|
||||
|
||||
## 归类
|
||||
|
||||
- owner:`07-ai`
|
||||
- 根因:页面 AI UI 仍走旧 mnote-cli / compat 主链,而默认 provider 已切到 Hermes。
|
||||
- 修复方向:页面 AI 主链改为同源 `/api/hermes/client/*`,旧 `/api/ai-agent/run` 只保留 legacy guard。
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] 页面 AI 打开后创建或恢复 Hermes session。
|
||||
- [x] 发送消息不再请求 `/api/ai-agent/run`。
|
||||
- [x] 未配置 Hermes upstream 时返回稳定 `hermes_client_unconfigured`,不再出现旧 502。
|
||||
- [x] 配置 Hermes upstream 后 run/event stream 由 Hermes 返回。
|
||||
|
||||
## 完成证据
|
||||
|
||||
- 代码主链:`rust/crates/mnote-web/src/ssr/pages/layout.rs` 已改为 `/api/hermes/client/sessions`、`/api/hermes/client/runs`、`/api/hermes/client/events/{run_id}`。
|
||||
- 合同与路由:`rust/crates/mnote-web/src/routes/hermes_client.rs` 提供同源 Hermes client proxy。
|
||||
- 测试:`cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web hermes_ -- --nocapture`,14 passed。
|
||||
- smoke:`MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-smoke.js` 通过,并拦截旧 `/api/ai-agent/run` 证明新页面 AI 主链未调用旧 route。
|
||||
- 说明:2026-05-14 已在正式 `3000` 入口复跑 Hermes 页面 AI smoke 矩阵,旧 `/api/ai-agent/run` 只保留 410 retirement guard。
|
||||
@@ -0,0 +1,44 @@
|
||||
# [recycle] 7-19 [done][bug] Hermes 工具指导仍优先 apply_block_ops 而非 markdown_edit v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
|
||||
## 1. 问题定义
|
||||
|
||||
`mnote.doc.markdown_edit` 已被设计为普通页面正文编辑的主路径,但 Hermes run 的工具指导仍要求简单正文编辑优先调用 `mnote_doc_apply_block_ops`。
|
||||
|
||||
这导致模型策略与工具主线冲突。
|
||||
|
||||
## 2. 证据
|
||||
|
||||
- [hermes_client.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_client.rs:2351) 构造工具指导。
|
||||
- [hermes_client.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_client.rs:2357) 要求上下文足够时直接调用 `mnote_doc_apply_block_ops`。
|
||||
- [hermes_client.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_client.rs:2360) 明确“简单小段落编辑”优先用 `apply_block_ops`。
|
||||
|
||||
## 3. 影响
|
||||
|
||||
- Hermes 与 page AI fast-path 会走不同编辑工具。
|
||||
- markdown 级 search/replace 的收敛设计无法成为唯一验收口径。
|
||||
- `apply_block_ops` 的 revision / conflictDetectionKey 合同缺陷会被继续放大。
|
||||
|
||||
## 4. 建议修复
|
||||
|
||||
- 将普通正文 search/replace、局部段落替换、全文 markdown 替换统一引导到 `mnote_doc_markdown_edit`。
|
||||
- 仅在需要结构性块移动、资源块、复杂定位时才引导模型使用 `apply_block_ops` 或 `mnote.block.*`。
|
||||
- 同步更新 smoke:模型生成工具调用时应优先产出 `mnote_doc_markdown_edit`。
|
||||
|
||||
## 5. 修复
|
||||
|
||||
- [hermes_client.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_client.rs:2456) 将 Hermes run instructions 改为:普通正文 search/replace、局部段落替换、全文 markdown 替换优先调用 `mnote_doc_markdown_edit`。
|
||||
- [hermes_client.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_client.rs:2494) 将 `blockEditingToolOrder` 首位调整为 `mnote_doc_markdown_edit`,`mnote_doc_apply_block_ops` 仅保留给 markdown_edit 不能表达的结构性块操作。
|
||||
- [hermes_client.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_client.rs:4559) 增加 run guidance 合同测试,防止普通正文编辑重新退回 `apply_block_ops` 优先口径。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_client_run_guidance_prefers_markdown_edit_for_plain_body_edits -- --nocapture`
|
||||
- 结果:1 passed
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_client_run_body -- --nocapture`
|
||||
- 结果:2 passed
|
||||
@@ -0,0 +1,48 @@
|
||||
# [recycle] 7-20 [done][bug] page_ai_workflow 绕过 Hermes tool executor / audit / toggle v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
|
||||
## 1. 问题定义
|
||||
|
||||
`/api/page-ai/block-edit-workflow` 仍是独立模型调用链:直接读取 Hermes profile、调用 chat/completions、解析模型 JSON,并直接调用 Rust 工具函数。
|
||||
|
||||
它没有通过 `/api/hermes/tools/mnote/call` 的工具执行壳,因此绕过了 tool toggle、audit、统一幂等和统一调用追踪。
|
||||
|
||||
## 2. 证据
|
||||
|
||||
- [page_ai_workflow.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/page_ai_workflow.rs:69) 直接调用上游模型。
|
||||
- [page_ai_workflow.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/page_ai_workflow.rs:91) 在 route 内构造 `ToolCallInput`。
|
||||
- [page_ai_workflow.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/page_ai_workflow.rs:109) 直接调用 `doc::doc_markdown_edit`。
|
||||
|
||||
## 3. 影响
|
||||
|
||||
- 关闭或限制 mnote tool 时,该 fast-path 仍可能写入。
|
||||
- 工具调用审计与普通 Hermes run 不一致。
|
||||
- 幂等、dryRun、runId、toolCallId 等字段无法统一治理。
|
||||
|
||||
## 4. 建议修复
|
||||
|
||||
- 将 fast-path 的工具写入改为调用统一 tool executor。
|
||||
- fast-path 只负责 prompt / plan,不直接执行写入函数。
|
||||
- 增加 smoke:当对应 tool disabled 时,`page_ai_workflow` 不得绕过限制写入。
|
||||
|
||||
## 5. 修复
|
||||
|
||||
- [hermes_tools.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_tools.rs:62) 将统一 mnote tool 执行壳抽为 `execute_mnote_tool_call`,保留 profile disabled、audit、idempotency、auth/workspace 校验和统一结果包装。
|
||||
- [page_ai_workflow.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/page_ai_workflow.rs:109) `block_edit_workflow` 的写入阶段改为调用统一 executor,不再直接调用 `doc::doc_markdown_edit`。
|
||||
- [page_ai_workflow.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/page_ai_workflow.rs:657) 增加路由级测试:当 profile 禁用 `mnote.doc.markdown_edit` 时,页面 AI fast-path 必须返回 `mnote_tool_disabled`,不得绕过限制写入。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web block_edit_workflow_respects_disabled_markdown_edit_tool -- --nocapture`
|
||||
- 结果:1 passed
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web page_ai_workflow -- --nocapture`
|
||||
- 结果:3 passed
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_call_rejects_profile_disabled_tool -- --nocapture`
|
||||
- 结果:1 passed
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_markdown_edit_maps_normalized_search_to_block -- --nocapture`
|
||||
- 结果:1 passed
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
# [recycle] 7-25 [done][bug] ACP Reasonix 页面 AI block_edit_workflow 在 markdown 命中后生成空 block_ops v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
>
|
||||
> 关联缺陷:`7-24 在线 markdown_edit 写回不以最终 Markdown 为真源`
|
||||
|
||||
## 1. 用户可见症状
|
||||
|
||||
在页面 AI 中切换到 `ACP · Reasonix`,输入:
|
||||
|
||||
```text
|
||||
你
|
||||
检查你是否能读取到本页第一段,同时请修改第二段为:测试123
|
||||
```
|
||||
|
||||
工具调用失败:
|
||||
|
||||
```text
|
||||
mnote.page_ai.block_edit_workflow 失败 · page-ai-fast-mp9wm0qo
|
||||
结果 mnote.doc.apply_block_ops operations 不能为空
|
||||
```
|
||||
|
||||
## 2. 问题定义
|
||||
|
||||
`page_ai.block_edit_workflow` 当前已切到:
|
||||
|
||||
```text
|
||||
模型输出 search/replace
|
||||
-> mnote.doc.markdown_edit
|
||||
-> 在线文档再转换为 mnote.doc.apply_block_ops
|
||||
```
|
||||
|
||||
但在线写回层没有以最终 Markdown 为真源。它在 markdown 字符串中 search/replace 成功后,又用原始 `operations` 去原始 `blocks` 中反查目标块。如果反查不到块,就生成空 `block_ops`,随后仍调用 `mnote.doc.apply_block_ops`,最终抛出“operations 不能为空”。
|
||||
|
||||
## 3. 证据
|
||||
|
||||
- [page_ai_workflow.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/page_ai_workflow.rs:69) 调用模型生成 markdown operations。
|
||||
- [page_ai_workflow.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/page_ai_workflow.rs:91) 构造 `mnote.doc.markdown_edit` 输入。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:811) 在 `md` 字符串上执行 search/replace。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:857) 调用 `build_block_ops_from_markdown_edit`。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:989) 只用 `block.text.contains(search)` 反查 block。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:1008) 可能返回空 `block_ops`。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:878) 即使 `block_ops` 为空,也继续调用 `doc_apply_block_ops`。
|
||||
- [block.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/block.rs:405) 对空 operations 报错:`mnote.doc.apply_block_ops operations 不能为空`。
|
||||
|
||||
## 4. 根因判断
|
||||
|
||||
这条错误不是最终 root cause,只是下游暴露出来的症状。
|
||||
|
||||
真正根因是 markdown 层与 block ops 写回层存在二次定位:
|
||||
|
||||
1. `search_replace(&md, search, replace)` 可以通过精确、归一化或 fuzzy 匹配成功。
|
||||
2. `build_block_ops_from_markdown_edit` 却只支持 `block.text.contains(search)`。
|
||||
3. 两套匹配规则不一致时,markdown 层显示已命中,block 层却生成空 operations。
|
||||
4. 空 operations 没有在 `markdown_edit` 层转成语义化错误,而是继续传给 `apply_block_ops`。
|
||||
|
||||
## 5. 影响
|
||||
|
||||
- ACP Reasonix 的页面编辑会向用户暴露底层 `apply_block_ops` 错误,而不是说明“markdown 命中但无法映射到块”。
|
||||
- “读取第一段 + 修改第二段”这类混合任务可能进入 fast workflow,但该 workflow 只能表达写入,不保证先读后答。
|
||||
- 如果模型给出的 `search` 是段落序号、fuzzy 片段、带格式文本或跨块文本,markdown 层可能成功,block 写回层仍失败。
|
||||
|
||||
## 6. 建议修复:建议以长期,彻底的修复优先,建议参考/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/cli-main中对全局替换和单块替换的判断逻辑。
|
||||
|
||||
短期:
|
||||
|
||||
- `doc_markdown_edit` 在调用 `doc_apply_block_ops` 前,如果 `applied > 0` 但 `block_ops.is_empty()`,应返回明确错误,例如 `mnote_doc_markdown_edit_block_mapping_empty`,并包含 failed operation 的 search 摘要。
|
||||
- `page_ai_workflow` 应把该错误翻译为用户可理解的提示,不要暴露 `apply_block_ops operations 不能为空`。
|
||||
|
||||
中期:
|
||||
|
||||
- `build_block_ops_from_markdown_edit` 必须复用 `search_replace` 的定位结果,或让 `search_replace` 返回原始命中范围 / paragraph / block 映射。
|
||||
- 对“第一段/第二段”等序号型指令,应由模型输出对应段落原文作为 `search`,并增加回读校验。
|
||||
|
||||
长期:
|
||||
|
||||
- 按 `7-24` 收口:在线 `markdown_edit` 应以最终 Markdown 为写回真源,或明确限制只支持可安全映射的单块精确替换。
|
||||
|
||||
## 7. 复现 / 验证建议
|
||||
|
||||
- 浏览器 smoke:ACP Reasonix,页面含至少两段正文,输入“检查第一段并修改第二段为:测试123”。
|
||||
- 断言失败时错误码不能是 `mnote.doc.apply_block_ops operations 不能为空`,应是 markdown 到 block 映射失败的明确错误。
|
||||
- 修复后回读 Page Aggregate,断言第二段实际变为 `测试123`,同时 AI 回复能正常说明已读取到第一段。
|
||||
|
||||
## 8. 修复
|
||||
|
||||
已修复:
|
||||
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:862) 在 `mnote.doc.markdown_edit` 调用 `doc_apply_block_ops` 前拦截 `applied > 0 && block_ops.is_empty()`,返回 `mnote_doc_markdown_edit_block_mapping_empty`,不再泄露 `mnote.doc.apply_block_ops operations 不能为空`。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:928) 抽出 `search_replace_exact_or_normalized`,让 markdown 层与 block 映射层共享“精确 / 忽略空白 / 全半角归一化”规则。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:1038) 在线 block ops 构建不再用 `block.text.contains(search)`,改为对单块文本执行同一套安全匹配;fuzzy 仍只保留在 markdown 预处理层,避免写错块。
|
||||
- [hermes_tools.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_tools.rs:1670) 增加回归测试,覆盖空映射语义错误与忽略空白后正确映射到 `p_2`。
|
||||
|
||||
## 9. 验证
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_markdown_edit -- --nocapture
|
||||
```
|
||||
|
||||
结果:4 个相关测试通过。
|
||||
|
||||
## 10. 剩余风险
|
||||
|
||||
本修复解决单块 search/replace 映射和空 ops 下沉;跨块 Markdown 重写、完整最终 Markdown 作为写回真源仍归属于 `7-24`。
|
||||
@@ -0,0 +1,64 @@
|
||||
# [recycle] 7-31 [done][bug] 页面 AI block_edit_workflow 丢弃读取类回答摘要 v1
|
||||
|
||||
> 发现时间:2026-05-18
|
||||
>
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
|
||||
## 1. 用户可见症状
|
||||
|
||||
在页面 AI 使用 `ACP · Reasonix` 输入:
|
||||
|
||||
```text
|
||||
检查你是否能读取到本页第一段,同时请修改第二段为:测试123
|
||||
```
|
||||
|
||||
`block_edit_workflow` 即使完成写入,前端也只显示固定文案:
|
||||
|
||||
```text
|
||||
已通过页面 markdown 编辑快路径完成写入。
|
||||
```
|
||||
|
||||
用户要求中的“是否能读取到第一段”没有被回答。
|
||||
|
||||
## 2. 根因
|
||||
|
||||
`page_ai_workflow.rs` 的模型提示已经要求模型输出:
|
||||
|
||||
```json
|
||||
{"operations": [...], "summary": "..."}
|
||||
```
|
||||
|
||||
但路由只提取 `operations`,丢弃了 `summary`,并在成功响应中固定返回“已通过页面 markdown 编辑快路径完成写入。”。
|
||||
|
||||
这会让读写混合请求退化为纯写入反馈。
|
||||
|
||||
## 3. 修复
|
||||
|
||||
- 新增 `MarkdownEditPlan`,同时解析模型输出中的 `operations` 与 `summary`。
|
||||
- `block_edit_workflow` 成功响应优先返回模型 `summary`,缺失时才使用固定 fallback。
|
||||
- 系统提示明确要求:如果用户要求读取某段,`summary` 必须包含从 `page_text` 读取到的原文。
|
||||
- 新增回归测试 `block_edit_workflow_surfaces_model_summary_for_read_and_edit_request`,覆盖“读取第一段 + 修改第二段”的真实快路径响应。
|
||||
|
||||
## 4. 验证
|
||||
|
||||
RED:
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web block_edit_workflow_surfaces_model_summary_for_read_and_edit_request -- --nocapture
|
||||
```
|
||||
|
||||
旧实现失败,`payload.message` 不包含 `已读取第一段:第一段`。
|
||||
|
||||
GREEN:
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web page_ai_workflow -- --nocapture
|
||||
```
|
||||
|
||||
结果:`5 passed`。
|
||||
|
||||
## 5. 剩余边界
|
||||
|
||||
本修复只保证 fast workflow 成功响应不丢模型摘要;复杂多步推理、review session、流式 apply 仍属于 Phase C 冻结范围。
|
||||
@@ -0,0 +1,45 @@
|
||||
# [recycle] 7-34 [done][bug] 页面 AI ACP Runtime 默认值回跳 Hermes v1
|
||||
|
||||
> 发现时间:2026-05-18
|
||||
>
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
|
||||
## 1. 用户可见症状
|
||||
|
||||
页面 AI 面板中选择 `ACP · Reasonix` 后,会自动回跳到 `ACP · Hermes`。
|
||||
|
||||
用户期望:
|
||||
|
||||
- 默认 runtime 为 `ACP · Reasonix`
|
||||
- 下拉仍可手动切换到 `ACP · Hermes`
|
||||
|
||||
## 2. 根因
|
||||
|
||||
页面 AI 的历史 runtime 语义仍把空 `acpRuntime` 当成旧 `Hermes HTTP` 默认值。当前主线已经退役 HTTP Hermes,但前端 session/localStorage 恢复、后端空 runtime 推导、以及 `/profiles` 尚未返回时的下拉渲染仍可能产生空 runtime。
|
||||
|
||||
空 runtime 进入旧逻辑后会被解释成 Hermes 路径,导致用户刚选择 Reasonix 后又被历史 session 或空 select value 覆盖。
|
||||
|
||||
## 3. 修复
|
||||
|
||||
- 前端 `pageAiAcpRuntime` 初始值改为 `reasonix`。
|
||||
- localStorage 版本提升到 `3`,避免旧缓存把 active runtime 覆盖回 Hermes。
|
||||
- session 创建、持久化、停止 run、发送 run 默认都写入 `reasonix`。
|
||||
- 前端内置 `Reasonix / Hermes` 两个 ACP runtime fallback,`/profiles` 慢或失败时也不会渲染空 select。
|
||||
- 后端空 `acpRuntime` 默认归入 ACP 默认 runtime,默认值为 `reasonix`;旧 HTTP proxy 只保留在显式兼容开关后。
|
||||
|
||||
## 4. 验证
|
||||
|
||||
新增回归约束:
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web page_ai_acp_runtime_defaults_to_reasonix_and_keeps_hermes_switch -- --nocapture
|
||||
```
|
||||
|
||||
覆盖:
|
||||
|
||||
- `pageAiAcpRuntime` 默认是 `reasonix`
|
||||
- 下拉 options 固定包含 `reasonix` / `hermes`
|
||||
- 不再包含旧的 `默认 (Hermes HTTP)` 空选项
|
||||
- change handler 空值 fallback 到 `reasonix`
|
||||
Reference in New Issue
Block a user