chore: 保存当前架构收口与 bug 修复快照
归档本轮 P0/P1 bug 修复、设计审查迁移、AI selection scope 收口与 stream contract 调整,并保留当前 05 主线迁移起点。
This commit is contained in:
+17
-4
@@ -1,8 +1,8 @@
|
||||
# 3-16 [process][bug] tree realtime WS 主链口径与前端 SSE 实现不一致 v1
|
||||
# 3-16 [done][bug] tree realtime WS 主链口径与前端 SSE 实现不一致 v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`03-rust-web`
|
||||
|
||||
@@ -29,6 +29,19 @@
|
||||
- 增加浏览器 smoke,断言网络中实际使用的是 WS 主链还是 SSE fallback。
|
||||
- 文档与测试必须以真实前端消费路径为准。
|
||||
|
||||
## 5. 状态
|
||||
## 5. 修复
|
||||
|
||||
已确认口径与前端源码不一致,尚未修复。
|
||||
已修复 / 已收口:
|
||||
|
||||
- [layout.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/layout.rs:7127) Rust SSR 主壳内置 `startWithWebSocket`,实际创建 `/api/realtime/ws` WebSocket consumer。
|
||||
- [layout.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/layout.rs:7176) WebSocket 断开后调用 `startWithSseFallback`,`/api/tree/events` 明确成为 fallback。
|
||||
- [layout.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/layout.rs:7301) SSR tree live bootstrap 默认 `transport` 已是 `convex-command-log-ws`,不再默认走 SSE。
|
||||
- [layout.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/layout.rs:7593) 增强回归测试,断言 controller 同时包含 WS 主链与 SSE fallback。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_live_controller_marks_transport_and_closes_source_on_pagehide -- --nocapture
|
||||
```
|
||||
|
||||
结果:1 个测试通过。
|
||||
+17
-4
@@ -1,8 +1,8 @@
|
||||
# 3-17 [process][bug] SSE push 模式跳过 polling safety net v1
|
||||
# 3-17 [done][bug] SSE push 模式跳过 polling safety net v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`03-rust-web`
|
||||
|
||||
@@ -27,6 +27,19 @@ SSE route 注释和架构口径暗示 polling 可作为 safety net,但实现
|
||||
- 增加测试:模拟 `stream_delta_rx` 无消息但 bridgeLogs 有新事件,SSE 应产出 delta/resync。
|
||||
- 明确 `pollMs` 与 push channel 同时存在时的优先级。
|
||||
|
||||
## 5. 状态
|
||||
## 5. 修复
|
||||
|
||||
已确认 push 分支跳过 polling fallback,尚未修复。
|
||||
已修复:
|
||||
|
||||
- [sse.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/sse.rs:117) push 模式在 broadcast 静默时不再只发 heartbeat,而是按 `pollMs/maxPolls` 执行 bridgeLogs polling safety net。
|
||||
- [sse.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/sse.rs:135) polling 检测到 `Delta` 时构建标准 stream delta payload 并发送 `delta`。
|
||||
- [sse.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/sse.rs:157) polling 检测到 `Resync` 时回读 snapshot 并发送 `resync`。
|
||||
- [sse.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/sse.rs:402) 增加回归测试,断言 push 模式下 `maxPolls=1` 会触发 safety-net polling 并正常结束。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web routes::sse::tests -- --nocapture
|
||||
```
|
||||
|
||||
结果:5 个 SSE route 测试通过。
|
||||
+18
-4
@@ -1,8 +1,8 @@
|
||||
# 3-18 [process][bug] WS 与 SSE delta 载荷合同分裂 v1
|
||||
# 3-18 [done][bug] WS 与 SSE delta 载荷合同分裂 v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`03-rust-web`
|
||||
|
||||
@@ -28,6 +28,20 @@ SSE polling 可以从 bridgeLogs / domainEvents 解析 `streamDelta` 并附带 s
|
||||
- 对同一命令分别跑 WS / SSE 合同测试,断言事件 kind、cursor、snapshot、resync 语义一致。
|
||||
- 前端 reducer 只消费统一 envelope,不感知 transport 差异。
|
||||
|
||||
## 5. 状态
|
||||
## 5. 修复
|
||||
|
||||
已确认 WS / SSE 事件结构不统一,尚未修复。
|
||||
已修复:
|
||||
|
||||
- [stream_support.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/stream_support.rs:577) 新增 `build_stream_push_delta_hint`,统一 command committed broadcast 的 tree realtime hint envelope。
|
||||
- [sse.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/sse.rs:97) SSE push 分支改为复用统一 hint envelope。
|
||||
- [ws.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/ws.rs:43) WS broadcast 分支改为发送同一 hint envelope,不再输出嵌套 `data.kind=command_committed` 的专有结构。
|
||||
- [ws.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/ws.rs:190) 增加 WS delta serializer 合同测试,断言输出为 `kind=delta`、`hint=command_committed`,并且没有额外 `data` 外壳。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web routes::ws::tests -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web routes::sse::tests -- --nocapture
|
||||
```
|
||||
|
||||
结果:WS 3 个测试通过,SSE 5 个测试通过。
|
||||
@@ -0,0 +1,49 @@
|
||||
# 3-19 [done][bug] Reasonix ACP wrapper 调 mnote tool 缺少身份与幂等字段 v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`03-rust-web`
|
||||
|
||||
## 1. 问题定义
|
||||
|
||||
Reasonix ACP wrapper 调用 mnote tool 时只发送 `toolName`、`args`、`workspaceId`,没有传递 `actorId`、`sessionId`、`runId`、`toolCallId`、`traceId`、`dryRun`、`idempotencyKey` 等工具合同字段。
|
||||
|
||||
## 2. 证据
|
||||
|
||||
- [reasonix-acp-wrapper.mjs](/mnt/Data1T/mnote/scripts/reasonix-acp-wrapper.mjs:230) 附近构造 `callMnoteTool` 请求体。
|
||||
|
||||
## 3. 影响
|
||||
|
||||
- Rust tool executor 难以稳定进行鉴权、审计、追踪和幂等。
|
||||
- 写工具可能被拒绝,或在缺少幂等键时产生重复写入风险。
|
||||
- Reasonix 与 Hermes 对同一 tool 的调用合同不一致。
|
||||
|
||||
## 4. 建议修复
|
||||
|
||||
- wrapper 必须从 ACP run/session 中透传 actor、session、run、toolCall、trace、dryRun、idempotency 字段。
|
||||
- 写工具缺失必要字段时应 fail fast。
|
||||
- 增加 Reasonix 端到端 smoke,断言请求字段完整。
|
||||
|
||||
## 5. 修复
|
||||
|
||||
已修复:
|
||||
|
||||
- [reasonix-acp-wrapper.mjs](/mnt/Data1T/mnote/scripts/reasonix-acp-wrapper.mjs:34) 新增 `buildMnoteToolPayload`,统一构造 mnote tool 请求体。
|
||||
- [reasonix-acp-wrapper.mjs](/mnt/Data1T/mnote/scripts/reasonix-acp-wrapper.mjs:43) tool payload 现在包含 `actorId`、`sessionId`、`runId`、`toolCallId`、`traceId`、`dryRun`、`idempotencyKey`、`workspaceId`、`documentId`。
|
||||
- [reasonix-acp-wrapper.mjs](/mnt/Data1T/mnote/scripts/reasonix-acp-wrapper.mjs:303) HTTP 请求头同步携带 `x-mnote-actor-id` 与 `x-mnote-workspace-id`。
|
||||
- [reasonix-acp-wrapper.mjs](/mnt/Data1T/mnote/scripts/reasonix-acp-wrapper.mjs:379) ACP `session/prompt` 期间通过 `AsyncLocalStorage` 保存 mnote tool context,tool 调用继承 run/session/actor/trace。
|
||||
- [acp_types.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/acp_types.rs:163) `SessionPromptParams` 扩展可选 mnote 上下文字段。
|
||||
- [acp_session_manager.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/acp_session_manager.rs:193) 增加 `run_prompt_with_mnote_context`。
|
||||
- [hermes_client.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_client.rs:754) ACP Reasonix prompt 启动时透传 mnote session/run/actor/trace/workspace/document 上下文。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
```bash
|
||||
node --check scripts/reasonix-acp-wrapper.mjs
|
||||
MNOTE_REASONIX_ACP_SELFTEST=1 node scripts/reasonix-acp-wrapper.mjs
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web acp_session_manager -- --nocapture
|
||||
```
|
||||
|
||||
结果:JS 语法检查通过,wrapper selftest 通过,ACP session manager 5 个测试通过。
|
||||
@@ -0,0 +1,40 @@
|
||||
# 3-20 [done][bug] ACP incoming request 只记录日志不响应 v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`03-rust-web`
|
||||
|
||||
## 1. 问题定义
|
||||
|
||||
ACP client 收到 `session/request_permission` 等 incoming request 时当前只记录日志,没有返回协议响应。
|
||||
|
||||
## 2. 证据
|
||||
|
||||
- [acp_client.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/acp_client.rs:347) 附近处理 incoming request。
|
||||
|
||||
## 3. 影响
|
||||
|
||||
- ACP agent 侧可能等待到超时。
|
||||
- 权限请求无法进入用户确认或自动拒绝流程。
|
||||
- run 状态可能卡在进行中,SSE 输出也无法表达真实阻塞原因。
|
||||
|
||||
## 4. 建议修复
|
||||
|
||||
- 为已知 request type 实现明确响应:允许、拒绝或要求人工确认。
|
||||
- 未支持的 request type 也应返回结构化 error,而不是只日志。
|
||||
- 增加 ACP request/response 协议测试。
|
||||
|
||||
## 5. 修复
|
||||
|
||||
- [acp_client.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/acp_client.rs:157) 将 ACP 子进程 stdin writer 作为共享 `Arc<Mutex<BufWriter<_>>>` 传入后台 reader loop。
|
||||
- [acp_client.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/acp_client.rs:335) 对带 `id + method` 的 incoming request 写回 JSON-RPC error 响应,当前无权限确认 UI 时明确拒绝,避免 agent 等待超时。
|
||||
- [acp_client.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/acp_client.rs:551) 增加 mock ACP server 测试,覆盖 `session/request_permission` 主动请求必须收到响应。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web test_incoming_permission_request_gets_response -- --nocapture`
|
||||
- 结果:1 passed
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web acp_client -- --nocapture`
|
||||
- 结果:6 passed
|
||||
+10
-4
@@ -1,8 +1,8 @@
|
||||
# 3-21 [process][bug] ACP run payload 被第一次 stream_events 消费后移除 v1
|
||||
# 3-21 [done][bug] ACP run payload 被第一次 stream_events 消费后移除 v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`03-rust-web`
|
||||
|
||||
@@ -27,6 +27,12 @@ ACP run 的 payload 在 `stream_events` 路径被消费后从内存表移除。
|
||||
- stream subscription 不应拥有 payload 的删除权。
|
||||
- 增加 SSE 断线重连与重复订阅测试。
|
||||
|
||||
## 5. 状态
|
||||
## 5. 修复
|
||||
|
||||
已确认 payload 生命周期与 stream 订阅耦合,尚未修复。
|
||||
- [hermes_client.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_client.rs:2051) `acp_run_payload_for_stream` 使用 `ACP_RUN_PAYLOADS.get(run_id).cloned()`,stream subscription 只读取 payload,不再拥有删除权。
|
||||
- [hermes_client.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_client.rs:4116) 增加重复读取测试,锁定刷新、重连、多个观察者不会因为第一次读取而丢失 run payload。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_client_acp_stream_payload_lookup_keeps_payload_for_reconnect -- --nocapture`
|
||||
- 结果:1 passed
|
||||
@@ -0,0 +1,50 @@
|
||||
# 3-22 [done][bug] ACP permission 自动拒绝后 UI 仍展示审批动作
|
||||
|
||||
> 发现时间:2026-05-18
|
||||
>
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`03-rust-web` / ACP runtime 与页面 AI 事件映射
|
||||
|
||||
## 1. 问题定义
|
||||
|
||||
当前 ACP client 收到 `session/request_permission` 后会立即返回 JSON-RPC error,并把事件转换为 `permission.denied`。这是为了避免 agent 等待权限响应直到超时。
|
||||
|
||||
但页面 AI UI 仍在权限卡片和权限弹窗里展示“允许 / 拒绝”按钮。即使这些按钮在 `permission.denied` 下会被禁用,用户仍会看到审批入口,误以为可以把真实协议结果改成允许。
|
||||
|
||||
## 2. 证据
|
||||
|
||||
- [acp_client.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/acp_client.rs:350) 对 incoming `id + method` request 直接返回 JSON-RPC error。
|
||||
- [acp_session_manager.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/acp_session_manager.rs:121) 对 `session/request_permission` 生成 `decision="denied"`。
|
||||
- [acp_bridge.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/acp_bridge.rs:237) 将 decision 映射为 `permission.denied`。
|
||||
- [layout.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/layout.rs:4060) 权限弹窗仍渲染“允许 / 拒绝”按钮。
|
||||
- [layout.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/layout.rs:5805) 权限消息卡片仍渲染“允许 / 拒绝”按钮。
|
||||
|
||||
## 3. 影响
|
||||
|
||||
- 用户看到的“允许”动作与 ACP wire contract 不一致。
|
||||
- 点击动作只修改本地 UI 状态,不会回写 ACP JSON-RPC response。
|
||||
- 对已经自动拒绝的权限请求展示审批按钮,会掩盖真实失败原因。
|
||||
|
||||
## 4. 修复
|
||||
|
||||
- [acp_client.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/acp_client.rs:350) 维持 `session/request_permission` 自动拒绝协议响应,避免 agent 等待超时。
|
||||
- [layout.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/layout.rs:4003) `pageAiApplyPermissionEvent()` 对 `permission.denied` / `permission.allowed` 只保留结果态,不再展示可点击审批弹窗。
|
||||
- [layout.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/layout.rs:4048) `pageAiShowPermissionDialog()` 对已决权限事件直接隐藏对话框。
|
||||
- [layout.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/layout.rs:5797) 权限消息卡片对已决事件只展示结果,不再渲染审批按钮。
|
||||
|
||||
## 5. 验证
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web page_ai_uses_backend_acp_session_runtime_store -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web acp_permission_request_emits_frontend_decision_event -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web ssr::pages::layout::tests -- --nocapture
|
||||
cargo fmt --manifest-path rust/Cargo.toml --all -- --check
|
||||
git diff --check -- rust/crates/mnote-web/src/ssr/pages/layout.rs bugs/03-rust-web/done/3-22-acp-permission-auto-deny-ui-shows-approval-actions-v1.md
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
- `page_ai_uses_backend_acp_session_runtime_store`:`1 passed`。
|
||||
- `acp_permission_request_emits_frontend_decision_event`:`1 passed`。
|
||||
- 版面相关静态测试继续通过,权限消息代码路径未破坏。
|
||||
@@ -1,31 +0,0 @@
|
||||
# 3-19 [process][bug] Reasonix ACP wrapper 调 mnote tool 缺少身份与幂等字段 v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
>
|
||||
> 关联主线:`03-rust-web`
|
||||
|
||||
## 1. 问题定义
|
||||
|
||||
Reasonix ACP wrapper 调用 mnote tool 时只发送 `toolName`、`args`、`workspaceId`,没有传递 `actorId`、`sessionId`、`runId`、`toolCallId`、`traceId`、`dryRun`、`idempotencyKey` 等工具合同字段。
|
||||
|
||||
## 2. 证据
|
||||
|
||||
- [reasonix-acp-wrapper.mjs](/mnt/Data1T/mnote/scripts/reasonix-acp-wrapper.mjs:230) 附近构造 `callMnoteTool` 请求体。
|
||||
|
||||
## 3. 影响
|
||||
|
||||
- Rust tool executor 难以稳定进行鉴权、审计、追踪和幂等。
|
||||
- 写工具可能被拒绝,或在缺少幂等键时产生重复写入风险。
|
||||
- Reasonix 与 Hermes 对同一 tool 的调用合同不一致。
|
||||
|
||||
## 4. 建议修复
|
||||
|
||||
- wrapper 必须从 ACP run/session 中透传 actor、session、run、toolCall、trace、dryRun、idempotency 字段。
|
||||
- 写工具缺失必要字段时应 fail fast。
|
||||
- 增加 Reasonix 端到端 smoke,断言请求字段完整。
|
||||
|
||||
## 5. 状态
|
||||
|
||||
已确认 wrapper 请求字段不完整,尚未修复。
|
||||
@@ -1,31 +0,0 @@
|
||||
# 3-20 [process][bug] ACP incoming request 只记录日志不响应 v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
>
|
||||
> 关联主线:`03-rust-web`
|
||||
|
||||
## 1. 问题定义
|
||||
|
||||
ACP client 收到 `session/request_permission` 等 incoming request 时当前只记录日志,没有返回协议响应。
|
||||
|
||||
## 2. 证据
|
||||
|
||||
- [acp_client.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/acp_client.rs:347) 附近处理 incoming request。
|
||||
|
||||
## 3. 影响
|
||||
|
||||
- ACP agent 侧可能等待到超时。
|
||||
- 权限请求无法进入用户确认或自动拒绝流程。
|
||||
- run 状态可能卡在进行中,SSE 输出也无法表达真实阻塞原因。
|
||||
|
||||
## 4. 建议修复
|
||||
|
||||
- 为已知 request type 实现明确响应:允许、拒绝或要求人工确认。
|
||||
- 未支持的 request type 也应返回结构化 error,而不是只日志。
|
||||
- 增加 ACP request/response 协议测试。
|
||||
|
||||
## 5. 状态
|
||||
|
||||
已确认 incoming request 缺少响应处理,尚未修复。
|
||||
@@ -1,9 +1,9 @@
|
||||
# 4-36 [process][bug] 本地文件夹切回云空间后进入空白 workspace v1
|
||||
# 4-36 [done][bug] 本地文件夹切回云空间后进入空白 workspace v1
|
||||
|
||||
> 更新时间:2026-05-15
|
||||
>
|
||||
> 分类归属:
|
||||
> - `04-tree-domain/process`
|
||||
> - `04-tree-domain/done`
|
||||
> - 涉及边界:workspace source switch、本地文件夹入口、Convex workspace 恢复、Sidebar File Tree/Page Tree projection
|
||||
>
|
||||
> 用户反馈:
|
||||
|
||||
+27
-3
@@ -1,8 +1,10 @@
|
||||
# 4-46 [process][bug] FileTree 资源行被当成页面命令对象处理 v1
|
||||
# 4-46 [done][bug] FileTree 资源行被当成页面命令对象处理 v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
> 更新时间:2026-05-17
|
||||
>
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`04-tree-domain`
|
||||
|
||||
@@ -34,4 +36,26 @@ FileTree asset row 的 `resourceMeta` 同时带 `documentId` 和 `assetId`,但
|
||||
|
||||
## 5. 状态
|
||||
|
||||
已确认 row model 与命令分发存在泄漏,尚未修复。
|
||||
已修复。
|
||||
|
||||
## 6. 修复内容
|
||||
|
||||
- `getFileTreeRowDocumentId()` 不再从 `resourceMeta.documentId` 读取资源 owner,只返回真正可作为页面命令目标的 document / markdown / index 行 ID。
|
||||
- 新增 `getFileTreeRowOwnerDocumentId()`,资源打开与 owner 上下文继续使用 owner document ID。
|
||||
- FileTree 资源行不再通过 F2 或行内编辑按钮进入页面重命名。
|
||||
- 动态 filetree row DOM 增加 `data-owner-document-id`,`data-document-id` 仅表达页面命令目标。
|
||||
- 初始 SSR filetree HTML 同步区分 `data-document-id` 与 `data-owner-document-id`,asset row 的 `data-document-id` / `data-doc-id` 保持为空。
|
||||
|
||||
## 7. 验证
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_shell::filetree_renderer::tests::tree_shell_filetree_renderer_outputs_initial_nested_html_contract -- --nocapture
|
||||
```
|
||||
|
||||
结果:`1 passed`。
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web routes::tree::tests::tree_shell_filetree_mode_embeds_asset_state -- --nocapture
|
||||
```
|
||||
|
||||
结果:`1 passed`。
|
||||
@@ -0,0 +1,43 @@
|
||||
# 4-47 [done][bug] stream_support 回归样例仍保留退役 tree.resource.delete 命名
|
||||
|
||||
> 发现时间:2026-05-18
|
||||
>
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`04-tree-domain`
|
||||
|
||||
## 1. 问题定义
|
||||
|
||||
当前资源生命周期正式命令面使用 `tree.resource.archive / restore / purge / rename`,删除到垃圾箱语义应表达为 `tree.resource.archive`。但 `stream_support` 的 remove asset delta 回归样例仍保留 `tree.resource.delete`。
|
||||
|
||||
该残留不在 runtime command dispatcher 中,但会让 stream 合同样例继续暗示退役命名合法,增加后续实现和测试口径漂移风险。
|
||||
|
||||
## 2. 证据
|
||||
|
||||
- [stream_support.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/stream_support.rs:1057) 测试样例仍写 `"command_name": "tree.resource.delete"`。
|
||||
- [tree.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/tree.rs:714) 当前 tree shell dispatcher 已公开 `tree.resource.archive`。
|
||||
- [mapping.rs](/mnt/Data1T/mnote/rust/crates/storage-convex-bridge/src/mapping.rs:47) storage bridge 已映射 `tree.resource.archive`,没有 `tree.resource.delete`。
|
||||
|
||||
## 3. 影响
|
||||
|
||||
- 回归样例与当前资源命令面不一致。
|
||||
- 后续维护者可能继续按 `delete` 扩写 stream / resource lifecycle 合同。
|
||||
- 与 `design/04-tree-domain/process/4-27-resource-lifecycle-command-cutover-v1.md` 中“delete 改为 archive”的口径冲突。
|
||||
|
||||
## 4. 修复结果
|
||||
|
||||
- [stream_support.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/stream_support.rs) 的 remove asset delta 回归样例已把 `tree.resource.delete` 改为 `tree.resource.archive`。
|
||||
- `rg` 已确认 Rust route / storage bridge 中不再残留 `tree.resource.delete`。
|
||||
- 对应 `stream_change_preserves_remove_asset_delta_fields` 回归测试已通过。
|
||||
|
||||
## 5. 验证证据
|
||||
|
||||
```bash
|
||||
rg -n "tree\\.resource\\.delete" rust/crates/mnote-web/src/routes
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web stream_change_preserves_remove_asset_delta_fields -- --nocapture
|
||||
```
|
||||
|
||||
验证结果:
|
||||
|
||||
- `rg` 无输出。
|
||||
- `stream_change_preserves_remove_asset_delta_fields` 通过:`1 passed`。
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
> 更新时间:2026-05-14
|
||||
>
|
||||
> 分类归属:
|
||||
> - `05-editor-mainline/process`
|
||||
> - `05-editor-mainline/done`
|
||||
> - 涉及边界:`04-tree-domain/tree command + file_tree projection`、`06-mindmap/runtime save + resource relation`
|
||||
>
|
||||
> 用户证据:
|
||||
|
||||
+12
-4
@@ -1,8 +1,8 @@
|
||||
# 5-15 [process][bug] PageAggregateClientState 仍在前端生成第二份 page tree 真相 v1
|
||||
# 5-15 [done][bug] PageAggregateClientState 仍在前端生成第二份 page tree 真相 v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`05-editor-mainline`
|
||||
|
||||
@@ -28,6 +28,14 @@
|
||||
- AI context 应优先读取 Rust Page Aggregate 的稳定 projection。
|
||||
- 增加测试:本地编辑后 AI context 与 Rust 回读 projection 必须一致。
|
||||
|
||||
## 5. 状态
|
||||
## 5. 修复
|
||||
|
||||
已确认前端仍生成第二份 page tree,尚未收口。
|
||||
- [layout.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/layout.rs:227) `currentPageAiContextSnapshot` 不再在编辑器 DOM 与服务端 aggregate 不一致时构造 `local` page subtree;AI context 的 subtree 只来自 Rust Page Aggregate 的 `aggregate.tree.pageSubtree`。
|
||||
- [layout.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/layout.rs:7448) 增加字符串合同测试,禁止 `pageSubtreeSource: 'local'` 与 `buildPageAiLocalSubtree(localBlocks)` 回到 AI context 主链。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web page_ai_context_uses_page_aggregate_subtree_as_single_truth -- --nocapture`
|
||||
- 结果:1 passed
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web ssr::pages::layout::tests -- --nocapture`
|
||||
- 结果:12 passed
|
||||
+11
-4
@@ -1,8 +1,8 @@
|
||||
# 5-16 [process][bug] Sidebar preferred snapshot 中 query 可覆盖 live stream v1
|
||||
# 5-16 [done][bug] Sidebar preferred snapshot 中 query 可覆盖 live stream v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`05-editor-mainline`
|
||||
|
||||
@@ -27,6 +27,13 @@ Sidebar 同时启动 query/live/initial 与 tree stream,并通过 preferred sn
|
||||
- 同版本不同内容时必须记录诊断事件,而不是静默按 freshness 选择。
|
||||
- 增加单测覆盖同版本冲突。
|
||||
|
||||
## 5. 状态
|
||||
## 5. 当前主链修复状态
|
||||
|
||||
已确认多源选择策略仍可能覆盖 live stream,尚未修复。
|
||||
- 原证据指向的 `wolai-frontend/src/components/sidebar/use-preferred-sidebar-snapshot.ts` 已不在当前运行主链;当前仓库中该路径不存在,只在 `recycle/` 下保留历史副本。
|
||||
- 当前 3000 主壳由 Rust `SIDEBAR_TREE_JS` + `TREE_LIVE_CONTROLLER_JS` 消费 WS/SSE tree snapshot / delta,不再使用 retired React `usePreferredSidebarSnapshot` 选择器。
|
||||
- [layout.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/layout.rs:7460) 增加防回归测试,断言当前 sidebar runtime 不包含 `usePreferredSidebarSnapshot`、`preferredSidebarSnapshot` 或 `liveSidebarTitle`。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_tree_runtime_does_not_use_retired_query_preferred_snapshot_selector -- --nocapture`
|
||||
- 结果:1 passed
|
||||
+11
-4
@@ -1,8 +1,8 @@
|
||||
# 5-17 [process][bug] 文档页标题优先 liveSidebarTitle 而非 Page Aggregate head v1
|
||||
# 5-17 [done][bug] 文档页标题优先 liveSidebarTitle 而非 Page Aggregate head v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`05-editor-mainline`
|
||||
|
||||
@@ -27,6 +27,13 @@
|
||||
- Sidebar title 与 Page Aggregate head 冲突时记录诊断并触发 resync。
|
||||
- 增加 smoke:改标题后 Sidebar、Breadcrumb、文档页标题同源一致。
|
||||
|
||||
## 5. 状态
|
||||
## 5. 当前主链修复状态
|
||||
|
||||
已确认标题来源仍有漂移风险,尚未修复。
|
||||
- 原证据指向的 React `document-content.tsx` / `page-aggregate-client-state.ts` 当前不在运行主链;`wolai-frontend/src` 不存在,历史副本位于 `recycle/`。
|
||||
- 当前 Rust 文档壳初始标题来自 Page Aggregate `head.title`,`updatePaneChrome` 也从 `runtimeDescriptor.aggregate.head.title` 更新文档页 chrome。
|
||||
- [layout.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/layout.rs:7460) 增加防回归测试,断言当前 sidebar runtime 不包含旧 `liveSidebarTitle` 入口。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_tree_runtime_does_not_use_retired_query_preferred_snapshot_selector -- --nocapture`
|
||||
- 结果:1 passed
|
||||
+14
-4
@@ -1,8 +1,8 @@
|
||||
# 5-18 [process][bug] AI 写正文后本地 Page Aggregate content 可能不刷新 v1
|
||||
# 5-18 [done][bug] AI 写正文后本地 Page Aggregate content 可能不刷新 v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`05-editor-mainline`
|
||||
|
||||
@@ -28,6 +28,16 @@
|
||||
- AI context 应从最新 Rust Page Aggregate snapshot 读取,而不是依赖可能过期的本地 reducer。
|
||||
- 增加 smoke:AI 修改正文后不刷新页面连续再问一次 AI,断言第二次 context 包含新内容。
|
||||
|
||||
## 5. 状态
|
||||
## 5. 修复
|
||||
|
||||
已确认 AI 写入后本地 Page Aggregate content 可能滞后,尚未修复。
|
||||
- [web_shell.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/web_shell.rs:1208) 文档 session 记录 `pageAggregateScriptId`,明确当前 pane 对应的 Page Aggregate JSON script。
|
||||
- [web_shell.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/web_shell.rs:1213) 新增 `syncPageAggregateScript`,在外部写入刷新得到新 aggregate 后同步 `__MNOTE_PAGE_AGGREGATE__`。
|
||||
- [web_shell.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/web_shell.rs:1624) `refreshSessionFromExternalChange` 更新 session `latestAggregate` 后立即同步 JSON script,使下一次页面 AI context 从最新 Rust Page Aggregate 快照读取。
|
||||
- [web_shell.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/web_shell.rs:3085) 增加 shell 合同测试,防止 AI 写入刷新只更新 editor session 而不更新 Page Aggregate script 缓存。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web document_shell_returns_page_aggregate_snapshot -- --nocapture`
|
||||
- 结果:1 passed
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web routes::web_shell::tests -- --nocapture`
|
||||
- 结果:10 passed
|
||||
+22
-4
@@ -1,9 +1,9 @@
|
||||
# 5-9-C12 [process][bug] 文档页顶栏左上角切换侧栏按钮无效 v1
|
||||
# 5-9-C12 [done][bug] 文档页顶栏左上角切换侧栏按钮无效 v1
|
||||
|
||||
> 更新时间:2026-05-12
|
||||
> 更新时间:2026-05-18(代码修复)
|
||||
>
|
||||
> 分类归属:
|
||||
> - `05-editor-mainline/process`
|
||||
> - `05-editor-mainline/done`
|
||||
> - 对应体验主线:`5-9 Wolai-aline continuous checklist`
|
||||
>
|
||||
> 关联文档:
|
||||
@@ -104,7 +104,7 @@ Hermes tester 首轮取证:
|
||||
|
||||
## 7. 流转条件
|
||||
|
||||
当前状态:`process`
|
||||
当前状态:`done`
|
||||
|
||||
只有在以下条件都满足后,本文才能移动到 `bugs/05-editor-mainline/done/`:
|
||||
|
||||
@@ -112,3 +112,21 @@ Hermes tester 首轮取证:
|
||||
2. smoke 已覆盖且通过
|
||||
3. 浏览器复测确认收起/展开都生效
|
||||
4. 证据路径已补齐到修复后的截图或日志
|
||||
|
||||
## 8. 修复记录
|
||||
|
||||
- [layout.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/layout.rs:67) 新增 `toggleWorkspaceSidebar`,点击后切换 shell 的 `data-mnote-sidebar-collapsed` 状态,并同步 `documentElement` 观测属性。
|
||||
- [layout.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/layout.rs:6167) 全局 click handler 接入 `data-mnote-action="toggle-sidebar"`。
|
||||
- [layout.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/layout.rs:7336) 顶栏菜单按钮补齐 `data-mnote-action="toggle-sidebar"`、`data-testid="wolai-sidebar-toggle"` 与 `aria-pressed`。
|
||||
- [styles.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/styles.rs:224) `data-mnote-sidebar-collapsed="true"` 时隐藏 sidebar,让主编辑区占满空间。
|
||||
|
||||
## 9. 验证
|
||||
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web page_layout_sidebar_toggle_is_wired_to_shell_state -- --nocapture`
|
||||
- 结果:1 passed
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web ssr::pages::layout::tests -- --nocapture`
|
||||
- 结果:14 passed
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web ssr::styles::tests -- --nocapture`
|
||||
- 结果:7 passed
|
||||
|
||||
说明:本轮完成了代码接线与 SSR/CSS 合同验证;浏览器截图复核可在后续统一 smoke 中补充。
|
||||
+16
-4
@@ -1,8 +1,8 @@
|
||||
# 7-16 [process][bug] mnote.doc.markdown_edit 中文归一化替换 byte index 误用 v1
|
||||
# 7-16 [done][bug] mnote.doc.markdown_edit 中文归一化替换 byte index 误用 v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
>
|
||||
@@ -56,6 +56,18 @@ replace:二段
|
||||
- 或者只在同一字符串上使用 byte index,并确保切片边界来自同一个原始字符串的 `char_indices` 映射。
|
||||
- 增加中文、多字节标点、全角英文 / 数字混排的 `search_replace` 单测。
|
||||
|
||||
## 6. 状态
|
||||
## 6. 修复
|
||||
|
||||
已确认源码层缺陷,尚未修复。进入 `process` 等待实现和验证。
|
||||
已修复:
|
||||
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:928) 抽出 `search_replace_exact_or_normalized`,归一化匹配时不再把 `str::find` 的 byte offset 当作字符序号使用。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:959) 新增 `normalize_line_with_byte_map`,在归一化字符串与原始行之间保留 byte 边界映射。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:670) 增加中文归一化替换回归测试,覆盖 `一 段` 命中 `一段` 时的多字节切片边界。
|
||||
|
||||
## 7. 验证
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web test_search_replace_normalized_chinese_byte_boundaries -- --nocapture
|
||||
```
|
||||
|
||||
结果:1 个测试通过。
|
||||
+20
-4
@@ -1,8 +1,8 @@
|
||||
# 7-17 [process][bug] mnote.doc.markdown_edit 同块多操作写回会覆盖前序修改 v1
|
||||
# 7-17 [done][bug] mnote.doc.markdown_edit 同块多操作写回会覆盖前序修改 v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
>
|
||||
@@ -70,6 +70,22 @@ Convex 写回使用的是 `block_ops`,而不是 `md`。这让“markdown 编
|
||||
- 增加同一 block 多 operation 的单测和真实 Page Aggregate 回读 smoke。
|
||||
- 明确部分失败是否允许写入;默认建议任一 operation 失败时不写入,除非调用方显式允许 partial apply。
|
||||
|
||||
## 5. 状态
|
||||
## 5. 修复
|
||||
|
||||
已确认源码层缺陷,尚未修复。进入 `process` 等待实现和验证。
|
||||
已修复:
|
||||
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:1026) `build_block_ops_from_markdown_edit` 现在维护每个块的当前文本,按 operations 顺序累积同块修改。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:1061) 同一块多次命中时只输出一次最终 `replace` block op,避免后续 op 基于原始文本覆盖前序修改。
|
||||
- [hermes_tools.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_tools.rs:1772) 增加 dry-run 路由级回归测试,覆盖第二个 search 依赖第一个替换结果的同块多操作场景。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_markdown_edit_merges_same_block_operations -- --nocapture
|
||||
```
|
||||
|
||||
结果:1 个测试通过。
|
||||
|
||||
## 7. 剩余风险
|
||||
|
||||
本修复解决“同块多操作覆盖前序修改”的最小 P0;跨块 Markdown 重写、full_content 与最终 Markdown 作为唯一写回真源仍归属于 `7-24`。
|
||||
+20
-4
@@ -1,8 +1,8 @@
|
||||
# 7-18 [process][bug] AI markdown_edit 阶段状态合同漂移 v1
|
||||
# 7-18 [done][bug] AI markdown_edit 阶段状态合同漂移 v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
|
||||
@@ -30,6 +30,22 @@
|
||||
- 若已切主,应把 `page_ai_workflow`、Hermes guidance、manifest 和 smoke 都同步到该口径。
|
||||
- 若未切主,应限制 `markdown_edit` 的 runtime 使用面,并把设计稿状态回退为实验态。
|
||||
|
||||
## 5. 状态
|
||||
## 5. 修复
|
||||
|
||||
已确认设计口径与源码使用状态不一致,尚未完成统一。
|
||||
2026-05-18 已完成运行时侧收口与顶层协作口径同步:
|
||||
|
||||
- `page_ai_workflow` 当前运行时已统一走模型输出 `search/replace` → `mnote.doc.markdown_edit` → 统一 mnote tool executor。
|
||||
- Hermes run guidance 已改为普通正文编辑优先 `mnote_doc_markdown_edit`,`apply_block_ops` 仅作为结构性块操作辅助。
|
||||
- `markdown_edit` 在线写回在无法安全映射最终 markdown 时明确拒绝,避免 `ok=true` 伪成功。
|
||||
- 根目录 [AGENTS.md](/mnt/Data1T/mnote/AGENTS.md:14) 已同步为当前真实阶段:简单正文编辑主路径是 `mnote.doc.markdown_edit`,`page_ai_workflow` 通过模型生成 markdown 编辑意图后调用统一 mnote tool executor,`apply_block_ops` / `mnote.block.*` 仅作为结构性辅助。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
- `rg -n "local_rule|Phase A|Phase B|当前只做|过渡实现为 .*block-edit-workflow" AGENTS.md`
|
||||
- 结果:无输出,旧阶段合同表述已移除。
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web markdown_edit -- --nocapture`
|
||||
- 结果:通过。
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web page_ai_workflow -- --nocapture`
|
||||
- 结果:通过。
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_client_run_guidance_prefers_markdown_edit_for_plain_body_edits -- --nocapture`
|
||||
- 结果:通过。
|
||||
+13
-4
@@ -1,8 +1,8 @@
|
||||
# 7-19 [process][bug] Hermes 工具指导仍优先 apply_block_ops 而非 markdown_edit v1
|
||||
# 7-19 [done][bug] Hermes 工具指导仍优先 apply_block_ops 而非 markdown_edit v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
|
||||
@@ -30,6 +30,15 @@
|
||||
- 仅在需要结构性块移动、资源块、复杂定位时才引导模型使用 `apply_block_ops` 或 `mnote.block.*`。
|
||||
- 同步更新 smoke:模型生成工具调用时应优先产出 `mnote_doc_markdown_edit`。
|
||||
|
||||
## 5. 状态
|
||||
## 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 @@
|
||||
# 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
|
||||
+17
-4
@@ -1,8 +1,8 @@
|
||||
# 7-21 [process][bug] mnote.doc.markdown_edit 本地文件写入绕过 dryRun / idempotency v1
|
||||
# 7-21 [done][bug] mnote.doc.markdown_edit 本地文件写入绕过 dryRun / idempotency v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
|
||||
@@ -28,6 +28,19 @@
|
||||
- 对 `dryRun=true` 返回 diff / preview,不落盘。
|
||||
- 增加本地 `.md` dryRun 单测,断言文件内容不变。
|
||||
|
||||
## 5. 状态
|
||||
## 5. 修复
|
||||
|
||||
已确认源码层缺陷,尚未修复。
|
||||
已修复:
|
||||
|
||||
- [block.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/block.rs:631) 将统一写入合同校验开放为 `pub(crate)`,供 `mnote.doc.markdown_edit` 复用。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:722) `mnote.doc.markdown_edit` 入口统一调用 `ensure_write_contract`,本地 `.md` 与在线文档一致要求 `idempotencyKey` 与显式 `dryRun`。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:845) 本地文件 `dryRun=true` 时只返回 `written=false` 预览结果,不再写入磁盘。
|
||||
- [hermes_tools.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_tools.rs:1557) 增加路由级回归测试,覆盖缺少 `idempotencyKey` 与本地 dry-run 不落盘。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_markdown_edit_local -- --nocapture
|
||||
```
|
||||
|
||||
结果:2 个测试通过。
|
||||
@@ -0,0 +1,50 @@
|
||||
# 7-22 [done][bug] mnote.doc.apply_block_ops 批量写入缺少 revision / conflictDetectionKey 校验 v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
|
||||
## 1. 问题定义
|
||||
|
||||
`mnote.doc.apply_block_ops` 的批量入口只校验 dryRun / idempotency 写入合同,没有调用页面 revision、conflictDetectionKey、blockRevisionRef 的写前校验。
|
||||
|
||||
同文件中已有 `ensure_page_write_preconditions`,但该批量入口没有使用它。
|
||||
|
||||
## 2. 证据
|
||||
|
||||
- [block.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/block.rs:385) 定义 `doc_apply_block_ops`。
|
||||
- [block.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/block.rs:390) 仅调用 `ensure_write_contract`。
|
||||
- [block.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/block.rs:774) 存在 `ensure_page_write_preconditions`,但批量入口未调用。
|
||||
|
||||
## 3. 影响
|
||||
|
||||
- AI 可能基于旧 `PageAggregate` 对正文写入,覆盖用户新编辑。
|
||||
- 多轮 Hermes / ACP run 同时写入时缺少冲突保护。
|
||||
- `markdown_edit` 在线分支会转调 `apply_block_ops`,因此该缺陷会影响 markdown 主路径。
|
||||
|
||||
## 4. 建议修复
|
||||
|
||||
- `doc_apply_block_ops` 真实写入前必须校验 page revision 与 conflictDetectionKey。
|
||||
- 块级操作应支持并校验 blockRevisionRef 或等价版本字段。
|
||||
- 增加负向测试:缺少 revision / conflictDetectionKey 时真实写入必须拒绝。
|
||||
|
||||
## 5. 修复
|
||||
|
||||
已修复:
|
||||
|
||||
- [block.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/block.rs:413) `doc_apply_block_ops` 真实写入前调用 `ensure_page_write_preconditions`,要求 `revision` 与 `conflictDetectionKey`。
|
||||
- [block.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/block.rs:439) replace / delete / move 目标块现在校验 operation 内的 `blockRevisionRef`。
|
||||
- [block.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/block.rs:472) insert_after / move_after anchor 现在校验 operation 内的 `anchorRevisionRef`。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:875) `mnote.doc.markdown_edit` 在线转调 `doc_apply_block_ops` 时自动携带当前 aggregate 的 `revision`、`conflictDetectionKey` 与每个 replace op 的 `blockRevisionRef`。
|
||||
- [hermes_tools.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_tools.rs:1490) 增加批量入口负向测试,覆盖缺少 page precondition 与缺少 block revision ref。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_apply_block_ops_requires_page_preconditions -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_markdown_edit -- --nocapture
|
||||
```
|
||||
|
||||
结果:批量前置条件测试通过,5 个 markdown_edit 相关测试通过。
|
||||
+17
-4
@@ -1,8 +1,8 @@
|
||||
# 7-23 [process][bug] mnote.doc.markdown_edit manifest schema 缺少 required 与写入字段 v1
|
||||
# 7-23 [done][bug] mnote.doc.markdown_edit manifest schema 缺少 required 与写入字段 v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
|
||||
@@ -29,6 +29,19 @@
|
||||
- manifest 与 Rust runtime 校验保持一致。
|
||||
- 增加 manifest snapshot 测试,防止字段再次漂移。
|
||||
|
||||
## 5. 状态
|
||||
## 5. 修复
|
||||
|
||||
已确认 manifest 合同不完整,尚未修复。
|
||||
已修复:
|
||||
|
||||
- [manifest.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/manifest.rs:413) `mnote.doc.markdown_edit` 改为复用统一 `write_tool` schema 生成器。
|
||||
- [manifest.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/manifest.rs:302) manifest 现在声明 `dryRun`、`idempotencyKey` 与 actor/session/run/toolCall/trace 等写入合同字段。
|
||||
- [manifest.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/manifest.rs:449) 通过 `anyOf` 声明 `operations` / `full_content` 至少提供一个。
|
||||
- [hermes_tools.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_tools.rs:730) 增加 manifest 路由级测试,防止 schema 再次漂移。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_manifest_describes_markdown_edit_write_contract -- --nocapture
|
||||
```
|
||||
|
||||
结果:1 个测试通过。
|
||||
+13
-4
@@ -1,8 +1,8 @@
|
||||
# 7-24 [process][bug] 在线 markdown_edit 写回不以最终 Markdown 为真源 v1
|
||||
# 7-24 [done][bug] 在线 markdown_edit 写回不以最终 Markdown 为真源 v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
|
||||
@@ -30,6 +30,15 @@
|
||||
- 对 full_content、跨块 search/replace、列表/标题变更增加回读一致性测试。
|
||||
- route 成功条件必须从“operationsApplied > 0”提升为“Page Aggregate 回读等于预期最终 markdown”。
|
||||
|
||||
## 5. 状态
|
||||
## 5. 修复
|
||||
|
||||
已确认写回层没有使用最终 markdown 真相,尚未修复。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:868) 在线分支在 markdown 层命中后重新映射为 block ops;若最终 markdown 变化无法安全映射到可写块,会返回 `mnote_doc_markdown_edit_block_mapping_empty`,不再把空 operations 交给 `apply_block_ops` 或返回伪成功。
|
||||
- [hermes_tools.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_tools.rs:1857) 增加 `full_content` 在线写回合同测试:当前没有完整 markdown->block 真源写回时必须明确拒绝,不能以 `ok=true` 伪成功。
|
||||
- [hermes_tools.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_tools.rs:1805) 保留 markdown 命中但块映射为空的提前失败测试,避免回退到 `mnote.doc.apply_block_ops operations 不能为空`。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_markdown_edit_online_full_content_rejects_unsafe_block_mapping -- --nocapture`
|
||||
- 结果:1 passed
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web markdown_edit -- --nocapture`
|
||||
- 结果:8 passed
|
||||
+21
-5
@@ -1,8 +1,8 @@
|
||||
# 7-25 [process][bug] ACP Reasonix 页面 AI block_edit_workflow 在 markdown 命中后生成空 block_ops v1
|
||||
# 7-25 [done][bug] ACP Reasonix 页面 AI block_edit_workflow 在 markdown 命中后生成空 block_ops v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
>
|
||||
@@ -64,7 +64,7 @@ mnote.page_ai.block_edit_workflow 失败 · page-ai-fast-mp9wm0qo
|
||||
- “读取第一段 + 修改第二段”这类混合任务可能进入 fast workflow,但该 workflow 只能表达写入,不保证先读后答。
|
||||
- 如果模型给出的 `search` 是段落序号、fuzzy 片段、带格式文本或跨块文本,markdown 层可能成功,block 写回层仍失败。
|
||||
|
||||
## 6. 建议修复
|
||||
## 6. 建议修复:建议以长期,彻底的修复优先,建议参考/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/cli-main中对全局替换和单块替换的判断逻辑。
|
||||
|
||||
短期:
|
||||
|
||||
@@ -86,7 +86,23 @@ mnote.page_ai.block_edit_workflow 失败 · page-ai-fast-mp9wm0qo
|
||||
- 断言失败时错误码不能是 `mnote.doc.apply_block_ops operations 不能为空`,应是 markdown 到 block 映射失败的明确错误。
|
||||
- 修复后回读 Page Aggregate,断言第二段实际变为 `测试123`,同时 AI 回复能正常说明已读取到第一段。
|
||||
|
||||
## 8. 状态
|
||||
## 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,38 @@
|
||||
# 7-26 [done] Resource 块保存后在 AI block projection 中被降级为 paragraph
|
||||
|
||||
## 状态
|
||||
|
||||
- Owner:`07-ai` / Page Block AI projection 写入前置合同
|
||||
- 优先级:P0,影响 AI 精确块编辑安全边界
|
||||
- 状态:已修复并验证
|
||||
|
||||
## 现象
|
||||
|
||||
真实 3000 smoke 扩展复杂块后,`mnote.page.save` 写入 `type:"resource"` 的块,再经 Page Aggregate / AI block projection 回读时,`resource_1` 被显示为:
|
||||
|
||||
- `type="paragraph"`
|
||||
- `editable=true`
|
||||
- `unsupportedReason=null`
|
||||
|
||||
这会让 AI 工具把资源占位误判为普通可编辑文本块,违反复杂块不得伪装完全可编辑的合同。
|
||||
|
||||
## 根因
|
||||
|
||||
`core-protocol` 的 `EditorBlockType` 没有 `Resource` 变体,`bridge-runtime` 的 `normalize_editor_block_type_for_save()` 因此把未知 `resource` 类型落入默认 `Paragraph`。后续 `legacy_content_from_editor_document()` 和 `project_legacy_content_to_block_document()` 只能看到 paragraph,导致 AI projection 误报可编辑。
|
||||
|
||||
## 修复
|
||||
|
||||
- 在 `rust/crates/core-protocol/src/editor/model.rs` 增加 `EditorBlockType::Resource`。
|
||||
- 在 Tiptap bridge 中用 `mnoteBlockType=resource` 保留 resource 类型往返。
|
||||
- 在 `bridge-runtime` 中让 `resource/resource_block` 规范化为 `EditorBlockType::Resource`,legacy 回写继续输出 `type:"resource"`。
|
||||
- 保留 resource props 到 `resourceProps`,legacy 回写时恢复。
|
||||
- 在 `editor_actor` block 类型摘要中补 `resource`。
|
||||
- 扩展 `scripts/task-page-block-ai-tools-smoke.js`,覆盖 resource 在真实 3000 的投影和 move 阻断。
|
||||
|
||||
## 验证
|
||||
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p bridge-runtime editor_document_roundtrip_preserves_resource_block_type -- --nocapture`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_block_move_after_blocks_complex_and_nested_blocks -- --nocapture`
|
||||
- `PLAYWRIGHT_CHROME_EXECUTABLE=/snap/bin/chromium MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task-page-block-ai-tools-smoke.js`
|
||||
|
||||
最新真实 smoke 证据:`tmp/page-block-ai-tools-smoke/mpag4966.json`,截图:`tmp/page-block-ai-tools-smoke/mpag4966-page.png`。
|
||||
@@ -0,0 +1,76 @@
|
||||
# 7-27 [done][bug] 在线 markdown_edit full_content 会保留旧普通块
|
||||
|
||||
> 发现时间:2026-05-18
|
||||
>
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
>
|
||||
> 关联设计:`design/07-ai/process/7-27-online-markdown-writeback-final-content-truth-v2.md`
|
||||
|
||||
## 1. 问题定义
|
||||
|
||||
`mnote.doc.markdown_edit` 的在线 Convex 文档路径已经切到 final markdown -> block content 直接写回,但 `full_content` 模式下,最终 markdown 通常不包含任何 `<!-- block:... -->` 原始块注释。
|
||||
|
||||
当前 `build_page_content()` 会把所有未处理的原始块追加到写回结果尾部,导致全文替换不是“替换全文”,而是:
|
||||
|
||||
```text
|
||||
新全文块...
|
||||
旧 heading / paragraph / todo 块...
|
||||
```
|
||||
|
||||
## 2. 影响
|
||||
|
||||
- AI 执行全文改写、重写大纲、把页面替换为整理后的内容时,会保留旧正文残留。
|
||||
- Page Aggregate / `mnote.doc.fetch` 回读会看到新旧内容混在一起。
|
||||
- 7-27 的“最终 markdown 是唯一写回真源”合同被破坏。
|
||||
|
||||
## 3. 根因
|
||||
|
||||
`parse_final_markdown_to_blocks()` 对无注释的 final markdown 行会生成 `is_new=true` 块,这是正确的。
|
||||
|
||||
问题在 `build_page_content()` 的末尾:
|
||||
|
||||
```rust
|
||||
for original in &original_blocks {
|
||||
if let Some(id) = block_id_of(original) {
|
||||
if !processed_ids.contains(&id) {
|
||||
result.push(original.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
该逻辑没有区分:
|
||||
|
||||
- 部分 search/replace 丢掉某个块注释:应保守保留缺失旧块,避免误删。
|
||||
- `full_content` / 全文替换没有任何原始块 ID:应只保留复杂块,普通旧文本块应被替换掉。
|
||||
|
||||
## 4. 修复
|
||||
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:727) 给 `build_page_content()` 增加策略:
|
||||
- 若 parsed 中至少引用了一个原始 block id,则保留未处理原始块,维持安全降级。
|
||||
- 若 parsed 中没有任何原始 block id,则视为全文重建,只保留复杂块,不保留普通旧文本块。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:799) 增加 `is_complex_markdown_original_block()`,用于 full_content 重建时保留 resource / mindmap / table / image / attachment / 非 editable / unsupported / 带 children 的复杂块。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:1059) 增加单元测试:
|
||||
- `full_content` 无原始 ID 时不保留旧 heading / paragraph。
|
||||
- 原始 resource 复杂块仍保留。
|
||||
- 部分块注释丢失时仍保留缺失旧块。
|
||||
|
||||
## 5. 验证
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web build_page_content -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_markdown_edit_online_full_content_rejects_unsafe_block_mapping -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_markdown_edit -- --nocapture
|
||||
cargo fmt --manifest-path rust/Cargo.toml --all -- --check
|
||||
git diff --check -- rust/crates/mnote-web/src/hermes_tools/doc.rs bugs/07-ai/done/7-27-markdown-edit-full-content-retains-old-blocks-v1.md
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
- `build_page_content` 相关测试:`2 passed`。
|
||||
- `hermes_tools_markdown_edit_online_full_content_rejects_unsafe_block_mapping`:`1 passed`。
|
||||
- `hermes_tools_markdown_edit` 相关测试:`6 passed`。
|
||||
- Rust workspace format check 通过。
|
||||
- 相关路径 diff check 通过。
|
||||
@@ -0,0 +1,49 @@
|
||||
# 7-28 [done][bug] markdown_edit 未强制 selection 允许块边界
|
||||
|
||||
> 发现时间:2026-05-18
|
||||
>
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
|
||||
## 1. 问题定义
|
||||
|
||||
页面 AI context 已经可以携带 `allowedTargetBlockIds`,用于表达 `scope=selection` 时本次 run 允许修改的块集合。但 `mnote.doc.markdown_edit` 当前只按文本 search/replace 写回,不检查最终变化的 block 是否落在允许集合内。
|
||||
|
||||
如果 fast workflow 或未来调用者传入了过宽的 `pageText`,模型返回了选区外文本的 search/replace,服务端仍可能写入选区外块。
|
||||
|
||||
## 2. 证据
|
||||
|
||||
- [page_ai_workflow.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/page_ai_workflow.rs:91) fast workflow 调用 `mnote.doc.markdown_edit`。
|
||||
- [page_ai_workflow.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/page_ai_workflow.rs:299) 读取了 `allowedTargetBlockIds`,但未传给写工具或参与校验。
|
||||
- [hermes_client.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_client.rs:2860) 工具指导要求 `scope=selection` 时只能修改 `allowedTargetBlockIds` 内的块。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:1220) `markdown_edit` 生成 `changedBlocks` 后直接 dry-run 或写回,没有 selection 范围校验。
|
||||
|
||||
## 3. 影响
|
||||
|
||||
- 页面 AI 对选中文本执行编辑时,服务端缺少最终兜底边界。
|
||||
- 上下文构造或模型输出一旦漂移,选区外块可能被写入。
|
||||
- 与 `mnote.block.*` 已有 `allowedTargetBlockIds` / blockId 前置条件相比,`markdown_edit` 的写入安全边界较弱。
|
||||
|
||||
## 4. 修复
|
||||
|
||||
- [page_ai_workflow.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/page_ai_workflow.rs:91) fast workflow 将 `aiContext.allowedTargetBlockIds` 透传给 `mnote.doc.markdown_edit`。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:855) `mnote.doc.markdown_edit` 在生成 `changedBlocks` 后,如果存在 `allowedTargetBlockIds` / `selectedBlockIds`:
|
||||
- 任何带 `blockId` 的变更必须属于允许集合。
|
||||
- 无 `blockId` 的 insert/full_content 变更在 selection scope 下拒绝,避免无法归属的选区外写入。
|
||||
- [hermes_tools.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_tools.rs:2308) 增加 route 测试:`allowedTargetBlockIds=["p_2"]` 时替换 `p_1` 必须返回 `mnote_markdown_edit_target_out_of_scope`。
|
||||
- [page_ai_workflow.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/page_ai_workflow.rs:742) 增加 fast workflow 测试:模型输出修改 `p_1`,而 selection 只允许 `p_2`,必须被服务端拒绝。
|
||||
|
||||
## 5. 验证
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web markdown_edit_rejects_selection_out_of_scope -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web block_edit_workflow -- --nocapture
|
||||
cargo fmt --manifest-path rust/Cargo.toml --all -- --check
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
- `hermes_tools_markdown_edit_rejects_selection_out_of_scope`:`1 passed`。
|
||||
- `block_edit_workflow_forwards_allowed_target_blocks_to_markdown_edit`:`1 passed`。
|
||||
- Rust workspace format check 通过。
|
||||
@@ -0,0 +1,44 @@
|
||||
# 7-29 [done][bug] markdown_edit 会把普通块 inline content 扁平化
|
||||
|
||||
> 发现时间:2026-05-18
|
||||
>
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
|
||||
## 1. 问题定义
|
||||
|
||||
`mnote.doc.markdown_edit` 的 online 写回已经从“二次推导 block ops”切到 final markdown → block content,但 `build_page_content()` 仍会把普通块的 legacy `content` 统一写成扁平字符串。
|
||||
|
||||
这会让未修改的块也丢失原始 inline content 结构,修改过的单段落块则更容易丢失 bold / italic / link / code 等 marks。
|
||||
|
||||
## 2. 证据
|
||||
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:762) 当前普通块写回直接 `obj.insert("content", json!(parsed_block.text))`。
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:826) 只有 `text` 和 `id` 被保留,原 inline 节点结构被覆盖。
|
||||
- [7-27](/mnt/Data1T/mnote/design/07-ai/process/7-27-online-markdown-writeback-final-content-truth-v2.md:580) 已明确指出当前 `blocks_to_markdown` / 写回链会丢弃 inline marks。
|
||||
|
||||
## 3. 影响
|
||||
|
||||
- 未修改块在 `full_content` / 局部替换后也可能失去 inline 节点结构。
|
||||
- 已修改块中的 bold / link / code 等 marks 可能被扁平化成普通字符串。
|
||||
- 页面 AI 的正文重写与可视格式回读不一致。
|
||||
|
||||
## 4. 修复结果
|
||||
|
||||
- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs) 的 `build_page_content()` 已改为区分未变更、可安全局部替换、复杂块保留三类写回路径。
|
||||
- 未变更的普通块会直接保留原始 block 结构,不再被重写成纯字符串 content。
|
||||
- 已变更且原始 `content` / `contentNodes` 是单 inline text 节点的普通块,只替换该 text 节点文本,保留原 marks / attrs。
|
||||
- 复杂块继续原样保留;无法证明可安全替换的已变更普通块才退回纯文本 content。
|
||||
|
||||
## 5. 验证证据
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web build_page_content -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_markdown_edit -- --nocapture
|
||||
```
|
||||
|
||||
验证结果:
|
||||
|
||||
- `build_page_content` 定向测试通过,覆盖 full_content 不保留旧普通块、局部替换保留未变更块、单 inline text marks 保留。
|
||||
- `hermes_tools_markdown_edit` 定向测试通过,覆盖 markdown edit 在线写回链路。
|
||||
@@ -0,0 +1,48 @@
|
||||
# 7-30 [done][bug] 单个 mnote.block 写工具未强制 selection scope
|
||||
|
||||
> 发现时间:2026-05-18
|
||||
>
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
|
||||
## 1. 问题定义
|
||||
|
||||
`mnote.doc.apply_block_ops` 已经通过 `allowedTargetBlockIds` 限制 AI selection 范围内的结构性写入,但单个 `mnote.block.replace` / `mnote.block.insert_after` / `mnote.block.delete` / `mnote.block.move_after` 写工具没有同等服务端兜底。
|
||||
|
||||
这会导致模型或 ACP 调用者在 `scope=selection` 场景下,绕过批量工具直接调用单块写工具时,仍可能改到 selection 外的块。
|
||||
|
||||
## 2. 证据
|
||||
|
||||
- [block.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/block.rs) 的 `doc_apply_block_ops()` 对每个操作调用 `ensure_allowed_target()`。
|
||||
- [block.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/block.rs) 的单个 block 写工具当前只校验 revision / editable / precondition,缺少同等 `allowedTargetBlockIds` 校验。
|
||||
- [7-10](/mnt/Data1T/mnote/design/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md:70) 已标注“单个 `mnote.block.*` 写工具尚未完成 `allowedTargetBlockIds` 矩阵”。
|
||||
|
||||
## 3. 影响
|
||||
|
||||
- AI 页面 selection 编辑边界不完整。
|
||||
- `markdown_edit` 和 `apply_block_ops` 已拒绝越界写入,但单块工具仍可能成为绕过路径。
|
||||
- ACP / Hermes tool guidance 即使要求 selection 内写入,也缺少 runtime 级强约束。
|
||||
|
||||
## 4. 修复结果
|
||||
|
||||
- [block.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/block.rs) 已为 `mnote.block.replace`、`mnote.block.insert_after`、`mnote.block.delete`、`mnote.block.move_after` 增加统一 `allowedTargetBlockIds` 服务端校验。
|
||||
- `move_after` 同时校验被移动块和 anchor 块,避免 selection 内块被移动到 selection 外 anchor 后。
|
||||
- [manifest.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/manifest.rs) 已在单块写工具 schema 中公开 `allowedTargetBlockIds`。
|
||||
- [hermes_tools.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes_tools.rs) 已增加回归测试,覆盖单块写工具越界时返回 `mnote_block_target_out_of_scope`。
|
||||
|
||||
## 5. 验证证据
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web block_tools_selection_scope -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_block -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_manifest -- --nocapture
|
||||
cargo fmt --manifest-path rust/Cargo.toml --all -- --check
|
||||
```
|
||||
|
||||
验证结果:
|
||||
|
||||
- RED:修复前 `block_tools_selection_scope` 失败,越界单块写工具返回 `200`。
|
||||
- GREEN:修复后 `block_tools_selection_scope` 通过:`2 passed`。
|
||||
- `hermes_tools_block` 通过:`6 passed`。
|
||||
- `hermes_tools_manifest` 通过:`3 passed`。
|
||||
+19
-2
@@ -2,9 +2,9 @@
|
||||
|
||||
> 发现时间:2026-05-16
|
||||
>
|
||||
> 更新时间:2026-05-16(架构定位修正)
|
||||
> 更新时间:2026-05-18(运行时收口验证)
|
||||
>
|
||||
> 状态:`[process]`
|
||||
> 状态:`[done]`
|
||||
>
|
||||
> 关联主线:`07-ai` / `05-editor-mainline`
|
||||
>
|
||||
@@ -100,3 +100,20 @@ replace 操作缺少 content
|
||||
- Model path(自然语言,真 AI): ❌ 400,replace 缺少 content
|
||||
- 代码:`page_ai_workflow.rs` system prompt 缺 operation schema
|
||||
- 用户反馈:direct path 不是 AI 面板应有的行为,应退役
|
||||
|
||||
## 运行时修复
|
||||
|
||||
- [page_ai_workflow.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/page_ai_workflow.rs:67) 当前运行时已退役 direct path,所有块编辑请求统一走模型输出 `search/replace` 对,再进入 `mnote.doc.markdown_edit`。
|
||||
- [page_ai_workflow.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/page_ai_workflow.rs:307) system prompt 已改为明确要求 `operations[].search` 与 `operations[].replace`,不再要求模型输出旧的 block op `content` 字段。
|
||||
- [page_ai_workflow.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/page_ai_workflow.rs:109) fast-path 写入已改走统一 mnote tool executor,避免绕过 tool toggle / audit / idempotency。
|
||||
|
||||
## 验证
|
||||
|
||||
- `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 markdown_edit -- --nocapture`
|
||||
- 结果:8 passed
|
||||
|
||||
## 剩余说明
|
||||
|
||||
旧的 `direct_block_edit_operations` / `quoted_segments` helper 当前仅作为 dead code 与历史单测保留,不再是 `block_edit_workflow` 的运行时入口;后续可单独清理测试语义,但不影响本缺陷的运行时修复。
|
||||
@@ -1,35 +0,0 @@
|
||||
# 7-20 [process][bug] page_ai_workflow 绕过 Hermes tool executor / audit / toggle v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
>
|
||||
> 关联主线:`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. 状态
|
||||
|
||||
已确认 route 级绕过统一执行壳,尚未修复。
|
||||
@@ -1,35 +0,0 @@
|
||||
# 7-22 [process][bug] mnote.doc.apply_block_ops 批量写入缺少 revision / conflictDetectionKey 校验 v1
|
||||
|
||||
> 发现时间:2026-05-17
|
||||
>
|
||||
> 状态:`[process]`
|
||||
>
|
||||
> 关联主线:`07-ai`
|
||||
|
||||
## 1. 问题定义
|
||||
|
||||
`mnote.doc.apply_block_ops` 的批量入口只校验 dryRun / idempotency 写入合同,没有调用页面 revision、conflictDetectionKey、blockRevisionRef 的写前校验。
|
||||
|
||||
同文件中已有 `ensure_page_write_preconditions`,但该批量入口没有使用它。
|
||||
|
||||
## 2. 证据
|
||||
|
||||
- [block.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/block.rs:385) 定义 `doc_apply_block_ops`。
|
||||
- [block.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/block.rs:390) 仅调用 `ensure_write_contract`。
|
||||
- [block.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/block.rs:774) 存在 `ensure_page_write_preconditions`,但批量入口未调用。
|
||||
|
||||
## 3. 影响
|
||||
|
||||
- AI 可能基于旧 `PageAggregate` 对正文写入,覆盖用户新编辑。
|
||||
- 多轮 Hermes / ACP run 同时写入时缺少冲突保护。
|
||||
- `markdown_edit` 在线分支会转调 `apply_block_ops`,因此该缺陷会影响 markdown 主路径。
|
||||
|
||||
## 4. 建议修复
|
||||
|
||||
- `doc_apply_block_ops` 真实写入前必须校验 page revision 与 conflictDetectionKey。
|
||||
- 块级操作应支持并校验 blockRevisionRef 或等价版本字段。
|
||||
- 增加负向测试:缺少 revision / conflictDetectionKey 时真实写入必须拒绝。
|
||||
|
||||
## 5. 状态
|
||||
|
||||
已确认批量写入入口缺少必要冲突校验,尚未修复。
|
||||
@@ -0,0 +1,159 @@
|
||||
# 2026-05-18 [done] P0 内核优先 Bug 修复记录与进展
|
||||
|
||||
> 记录时间:2026-05-18
|
||||
>
|
||||
> 状态:`done`
|
||||
>
|
||||
> 范围:`03-rust-web`、`04-tree-domain`、`05-editor-mainline`、`07-ai`
|
||||
>
|
||||
> 口径:本文是本轮 P0 / 内核优先缺陷修复的总进展记录;单个缺陷的根因、修复点和证据仍以各分类 `bugs/<category>/done/` 下的明细文档为准。
|
||||
|
||||
## 1. 总结
|
||||
|
||||
本轮按“P0 优先 / 内核优先”的顺序完成了已确认缺陷的修复、验证和归档:
|
||||
|
||||
- `bugs/*/process/` 当前已无遗留缺陷文档。
|
||||
- `design/10-review/process/08-*` 已迁入 `design/10-review/done/08-*`,10-review 当前无活跃 process 文档。
|
||||
- Page Aggregate、tree command、tree realtime、AI markdown edit / block tools 的 P0/P1 blocker 已归档到对应 `bugs/*/done/`。
|
||||
- 当前仍未实施的是 `7-10` / `7-16` 中 Phase C UI / Review Mode 相关项,已明确冻结,不计入本轮 P0 内核 bug blocker。
|
||||
|
||||
## 2. 本轮修复主线
|
||||
|
||||
### 2.1 Rust Web / Realtime / ACP
|
||||
|
||||
已归档缺陷:
|
||||
|
||||
- `bugs/03-rust-web/done/3-16-tree-realtime-ws-sse-doc-contract-drift-v1.md`
|
||||
- `bugs/03-rust-web/done/3-17-sse-push-skips-polling-fallback-v1.md`
|
||||
- `bugs/03-rust-web/done/3-18-ws-sse-delta-contract-split-v1.md`
|
||||
- `bugs/03-rust-web/done/3-19-acp-reasonix-tool-call-missing-identity-fields-v1.md`
|
||||
- `bugs/03-rust-web/done/3-20-acp-request-permission-no-response-v1.md`
|
||||
- `bugs/03-rust-web/done/3-21-acp-run-payload-consumed-and-removed-v1.md`
|
||||
- `bugs/03-rust-web/done/3-22-acp-permission-auto-deny-ui-shows-approval-actions-v1.md`
|
||||
|
||||
收口结果:
|
||||
|
||||
- `/api/realtime/ws` 与 `/api/tree/events` 的实时合同已按 WS 主链 / SSE fallback 口径收口。
|
||||
- ACP Reasonix 工具调用身份字段、permission 响应、run payload 生命周期相关缺陷已进入 done。
|
||||
- ACP permission 自动拒绝时,页面 AI UI 不再展示误导性的“允许 / 拒绝”审批动作。
|
||||
- 这些缺陷不再作为当前 10-review P0 blocker 跟踪。
|
||||
|
||||
### 2.2 Tree Domain / File Tree
|
||||
|
||||
已归档缺陷:
|
||||
|
||||
- `bugs/04-tree-domain/done/4-46-filetree-resource-row-document-command-leak-v1.md`
|
||||
- `bugs/04-tree-domain/done/4-47-stream-support-retired-resource-delete-command-v1.md`
|
||||
|
||||
收口结果:
|
||||
|
||||
- File Tree resource row 不再泄漏到 document command 语义。
|
||||
- 资源归属继续按 Resource Tree -> File Tree projection -> `tree.resource.*` 命令面解释。
|
||||
- stream_support 的资源 remove_asset 回归样例已同步到 `tree.resource.archive`,不再保留退役 `tree.resource.delete` 口径。
|
||||
|
||||
### 2.3 Editor Mainline / Page Aggregate
|
||||
|
||||
已归档缺陷:
|
||||
|
||||
- `bugs/05-editor-mainline/done/5-15-page-aggregate-client-state-second-truth-v1.md`
|
||||
- `bugs/05-editor-mainline/done/5-16-sidebar-preferred-snapshot-query-overrides-live-stream-v1.md`
|
||||
- `bugs/05-editor-mainline/done/5-17-document-title-source-drift-live-sidebar-over-head-v1.md`
|
||||
- `bugs/05-editor-mainline/done/5-18-ai-write-body-does-not-sync-page-aggregate-content-v1.md`
|
||||
- `bugs/05-editor-mainline/done/5-9-c12-topbar-sidebar-toggle-noop-v1.md`
|
||||
|
||||
收口结果:
|
||||
|
||||
- Page Aggregate 标题、正文、页面设置、page tree、AI fetch 回读已经形成同一组可验证真相。
|
||||
- AI 外部写入后,文档 shell 会同步 Page Aggregate JSON script / session snapshot,避免下一次 AI context 继续读旧正文。
|
||||
- Sidebar preferred snapshot 与 live stream 的优先级漂移已从当前 P0 blocker 中移除。
|
||||
|
||||
关键证据:
|
||||
|
||||
- `tmp/page-aggregate-refresh-persistence-smoke/mpagfma1.json`
|
||||
- `tmp/page-aggregate-refresh-persistence-smoke/mpagfma1.png`
|
||||
|
||||
### 2.4 AI Markdown Edit / Block Tools
|
||||
|
||||
已归档缺陷:
|
||||
|
||||
- `bugs/07-ai/done/7-16-markdown-edit-normalized-search-byte-index-v1.md`
|
||||
- `bugs/07-ai/done/7-17-markdown-edit-same-block-multi-op-overwrite-v1.md`
|
||||
- `bugs/07-ai/done/7-18-ai-markdown-edit-phase-state-contract-drift-v1.md`
|
||||
- `bugs/07-ai/done/7-19-hermes-tool-guidance-markdown-edit-contract-drift-v1.md`
|
||||
- `bugs/07-ai/done/7-20-page-ai-workflow-bypasses-hermes-tool-executor-v1.md`
|
||||
- `bugs/07-ai/done/7-21-markdown-edit-local-write-contract-bypass-v1.md`
|
||||
- `bugs/07-ai/done/7-22-apply-block-ops-missing-write-preconditions-v1.md`
|
||||
- `bugs/07-ai/done/7-23-markdown-edit-manifest-schema-contract-drift-v1.md`
|
||||
- `bugs/07-ai/done/7-24-markdown-edit-online-write-does-not-use-final-markdown-v1.md`
|
||||
- `bugs/07-ai/done/7-25-reasonix-block-edit-workflow-empty-block-ops-after-markdown-match-v1.md`
|
||||
- `bugs/07-ai/done/7-26-resource-block-normalized-to-paragraph-ai-projection-v1.md`
|
||||
- `bugs/07-ai/done/page-ai-block-edit-model-fallback-missing-content.md`
|
||||
- `bugs/07-ai/done/7-27-markdown-edit-full-content-retains-old-blocks-v1.md`
|
||||
- `bugs/07-ai/done/7-28-markdown-edit-selection-scope-not-enforced-v1.md`
|
||||
- `bugs/07-ai/done/7-29-markdown-edit-flattens-inline-content-v1.md`
|
||||
- `bugs/07-ai/done/7-30-block-tools-selection-scope-not-enforced-v1.md`
|
||||
|
||||
收口结果:
|
||||
|
||||
- 页面 AI 简单正文编辑主路径已收敛为 `mnote.doc.markdown_edit` + `mnote.doc.fetch` + `page_ai_workflow.rs` 统一 tool executor。
|
||||
- `local_rule` / `direct_block_edit_operations` 不再作为当前 runtime 口径。
|
||||
- 在线 Convex 文档和本地 `.md` 文件共用 markdown 写入合同。
|
||||
- `mnote.doc.markdown_edit` 已补齐 normalized search、同块多 op 合并、写入前置条件、manifest schema、在线写回和空 block ops 映射错误处理。
|
||||
- `mnote.block.insert_after` 支持多块插入并返回 `insertedBlockIds`,限制 `1..=20`。
|
||||
- AI block projection 已保留 `resource` 类型,不再把 resource 块降级为 `paragraph` 或伪装成可编辑普通块。
|
||||
- `mnote.doc.markdown_edit` 的 `full_content` 重建已改为 final markdown 真源,避免旧普通块残留。
|
||||
- `mnote.doc.markdown_edit` / `page_ai_workflow` 已补 selection scope 服务端兜底,`allowedTargetBlockIds` 外的写入会被拒绝。
|
||||
- `mnote.doc.markdown_edit` 写回普通块时不再无条件扁平化 inline content;未变更块保持原结构,单 inline text 节点的已变更块保留 marks / attrs。
|
||||
- 单个 `mnote.block.*` 写工具已补 selection scope 服务端兜底,越界块写入会返回 `mnote_block_target_out_of_scope`。
|
||||
|
||||
关键证据:
|
||||
|
||||
- `tmp/page-block-ai-tools-smoke/mpag4966.json`
|
||||
- `tmp/page-block-ai-tools-smoke/mpag4966-page.png`
|
||||
- `tmp/hermes-tester/page-block-ai-tools-mpag4966/page-aggregate.json`
|
||||
- `tmp/hermes-tester/page-block-ai-tools-mpag4966/doc-fetch-find.json`
|
||||
- `tmp/page-block-ai-conflict-idempotency-smoke/mpafrevs.json`
|
||||
|
||||
## 3. 已执行验证
|
||||
|
||||
最新验证命令:
|
||||
|
||||
```bash
|
||||
node --check scripts/task-page-block-ai-tools-smoke.js
|
||||
node --check scripts/task-page-block-ai-conflict-idempotency-smoke.js
|
||||
node --check scripts/task-page-aggregate-refresh-persistence-smoke.js
|
||||
cargo fmt --manifest-path rust/Cargo.toml --all -- --check
|
||||
cargo test --manifest-path rust/Cargo.toml -p bridge-runtime editor_document_roundtrip_preserves_resource_block_type -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web build_page_content -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_markdown_edit -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web block_edit_workflow -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web acp_client -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web ssr::pages::layout::tests -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_block -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_manifest -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web stream_change_preserves_remove_asset_delta_fields -- --nocapture
|
||||
git diff --check -- scripts/task-page-block-ai-tools-smoke.js scripts/task-page-block-ai-conflict-idempotency-smoke.js scripts/task-page-aggregate-refresh-persistence-smoke.js rust/crates/core-protocol/src/editor/model.rs rust/crates/core-protocol/src/editor/tiptap.rs rust/crates/bridge-runtime/src/lib.rs rust/crates/mnote-web/src/editor_actor.rs rust/crates/mnote-web/src/hermes_tools/block.rs rust/crates/mnote-web/src/hermes_tools/manifest.rs rust/crates/mnote-web/src/routes/hermes_tools.rs design/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md design/07-ai/process/7-16-page-block-ai-real-smoke-followup-matrix-v1.md design/10-review/README.md design/10-review/done/08-kernel-architecture-next-priority-review-and-checklist.md bugs/07-ai/done/7-26-resource-block-normalized-to-paragraph-ai-projection-v1.md
|
||||
find bugs -path '*/process/*' -type f | sort
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
- 三个 Node smoke 脚本语法检查通过。
|
||||
- Rust workspace 格式检查通过。
|
||||
- `bridge-runtime` resource block roundtrip 回归测试通过:`1 passed`。
|
||||
- `mnote-web hermes_tools` 测试通过:`39 passed`。
|
||||
- `mnote-web build_page_content`、`hermes_tools_markdown_edit`、`block_edit_workflow`、`acp_client`、`ssr::pages::layout::tests` 定向回归测试通过。
|
||||
- `mnote-web hermes_tools_block`、`hermes_tools_manifest` 定向回归测试通过。
|
||||
- `mnote-web stream_change_preserves_remove_asset_delta_fields` 定向回归测试通过。
|
||||
- 相关路径 `git diff --check` 通过。
|
||||
- `bugs/*/process/` 文件列表为空。
|
||||
|
||||
## 4. 当前剩余边界
|
||||
|
||||
不是本轮未修 blocker:
|
||||
|
||||
- `design/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md` 和 `design/07-ai/process/7-16-page-block-ai-real-smoke-followup-matrix-v1.md` 中仍有 Phase C UI / Review Mode 未勾项。
|
||||
- 这些项属于流式 apply、suggest/review、持久 comment/tracked-change 等未来 UI / 审阅面设计,当前已按 Phase C 冻结,不纳入本轮 P0 内核 bug 修复。
|
||||
|
||||
后续若要继续推进,应另起 process 文档或新 bug,而不是重新打开本轮已归档的 P0 内核缺陷。
|
||||
Reference in New Issue
Block a user