From 3ecb2aa7f8b8a63bbe39eee612f796ce833888a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AD=94=E6=B4=92=E6=B4=92?= <140176187+mosasa15@users.noreply.github.com> Date: Fri, 24 Apr 2026 16:27:33 +0800 Subject: [PATCH 1/4] docs(noname): add near-term task cards --- ...55\350\267\257\347\272\277\345\233\276.md" | 1 + ...73\345\212\241\346\270\205\345\215\225.md" | 1 + ...00\345\217\221\346\216\222\346\234\237.md" | 2 + ...73\345\212\241\345\215\241\347\211\207.md" | 207 ++++++++++++++++++ .../README.md" | 4 +- 5 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 "docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/06-\350\277\221\346\234\237\345\274\200\345\217\221\344\273\273\345\212\241\345\215\241\347\211\207.md" diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/02-\345\220\216\347\273\255\350\267\257\347\272\277\345\233\276.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/02-\345\220\216\347\273\255\350\267\257\347\272\277\345\233\276.md" index 47b69c8..8e7d442 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/02-\345\220\216\347\273\255\350\267\257\347\272\277\345\233\276.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/02-\345\220\216\347\273\255\350\267\257\347\272\277\345\233\276.md" @@ -161,3 +161,4 @@ - 评估下一层受控输出是否具备试点条件 更细的分工、交接目标、红线与文档要求,见 [未来两周开发排期](./04-未来两周开发排期.md)。 +基于最新项目评估收敛出的执行卡片,见 [近期开发任务卡片](./06-近期开发任务卡片.md)。 diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/03-\345\215\217\344\275\234\344\273\273\345\212\241\346\270\205\345\215\225.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/03-\345\215\217\344\275\234\344\273\273\345\212\241\346\270\205\345\215\225.md" index ee3785e..45a34f8 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/03-\345\215\217\344\275\234\344\273\273\345\212\241\346\270\205\345\215\225.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/03-\345\215\217\344\275\234\344\273\273\345\212\241\346\270\205\345\215\225.md" @@ -136,3 +136,4 @@ - 不允许为了赶进度直接扩大 `NoName` 权限边界 更细的两周内并行安排,见 [未来两周开发排期](./04-未来两周开发排期.md)。 +如果需要基于最新评估直接开工,而不是沿用旧标题拆分,优先看 [近期开发任务卡片](./06-近期开发任务卡片.md)。 diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/04-\346\234\252\346\235\245\344\270\244\345\221\250\345\274\200\345\217\221\346\216\222\346\234\237.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/04-\346\234\252\346\235\245\344\270\244\345\221\250\345\274\200\345\217\221\346\216\222\346\234\237.md" index ab998b2..d955404 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/04-\346\234\252\346\235\245\344\270\244\345\221\250\345\274\200\345\217\221\346\216\222\346\234\237.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/04-\346\234\252\346\235\245\344\270\244\345\221\250\345\274\200\345\217\221\346\216\222\346\234\237.md" @@ -4,6 +4,8 @@ 适用范围:当前 `main` 分支 排期目标:在不扩大 `NoName` 权限边界的前提下,完成主链收口、协作提效与文档稳定化 +补充说明:基于 2026-04-24 最新项目评估收敛出的直接执行项,见 [近期开发任务卡片](./06-近期开发任务卡片.md)。 + ## 1. 排期原则 本排期按两条线并行推进: diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/06-\350\277\221\346\234\237\345\274\200\345\217\221\344\273\273\345\212\241\345\215\241\347\211\207.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/06-\350\277\221\346\234\237\345\274\200\345\217\221\344\273\273\345\212\241\345\215\241\347\211\207.md" new file mode 100644 index 0000000..85ea3f7 --- /dev/null +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/06-\350\277\221\346\234\237\345\274\200\345\217\221\344\273\273\345\212\241\345\215\241\347\211\207.md" @@ -0,0 +1,207 @@ +# 近期开发任务卡片 + +更新时间:2026-04-24 +适用范围:当前 `main` 分支 +用途:把最新项目评估收敛成可直接执行、可交接、可验证的任务卡片 + +## 1. 使用规则 + +- 本文件是当前阶段的执行基线,优先级高于旧卡片标题和口头同步。 +- 任务状态变化后,至少同步更新: + - [现状评估](./01-现状评估.md) + - [后续路线图](./02-后续路线图.md) + - [未来两周开发排期](./04-未来两周开发排期.md) +- 如果任务被拆分,必须补充“本轮只做哪一层、下一层留给谁”。 +- 如果任务直接影响协作者接线,也要同步更新 [协作任务清单](./03-协作任务清单.md)。 + +## 2. 任务卡片 + +### 卡片 C1:清理前端测试环境污染 + +- 优先级: + - `P0` +- 建议负责人: + - 主开发线 +- 任务目标: + - 让 Vitest 不再把 `.review/`、额外 worktree 和临时副本当作当前仓库测试一起执行。 +- 建议修改范围: + - `vitest.config.ts` + - `package.json` + - `docs/项目文档/04-质量与发布/01-测试与验证.md` + - 必要时最小更新 `docs/项目文档/01-总览/02-运行与开发.md` +- 交接目标: + - `npm run test:web-core` 和 `npm run test -- --run ...` 只命中当前仓库测试文件 + - 测试输出不再出现 `.review/` 路径 + - 文档明确说明本地 review worktree 的处理方式 +- 禁止红线: + - 不直接删除现有 `.review/` 工作树 + - 不重做整套前端测试框架 + - 不为了“看起来干净”而降低现有测试覆盖 +- 最小验证: + - `npm run test:web-core` + - `npm run test -- --run src/platform/webRuntime.test.ts src/stores/__tests__/gameStore.test.ts src/components/__tests__/AgentTracePanel.test.ts src/components/__tests__/NoNameDebugConsole.test.ts` +- 作用效果: + - 恢复前端测试结果的可信度 + - 降低 review / 本地开发 / CI 三者之间的验证偏差 + - 让后续 NoName UI 与 Web mock 调整更容易判断真假回归 + +### 卡片 C2:修复章节压缩对 compaction summary 的回吃风险 + +- 优先级: + - `P0` +- 建议负责人: + - 协作者优先,主开发线审合并 +- 任务目标: + - 避免章节 compaction 在重复执行时,把之前写回的 compaction summary 再次当作 structured note 输入,形成递归沉淀。 +- 建议修改范围: + - `src-tauri/src/noname_memory_manager.rs` + - 必要时最小改动 `src-tauri/src/noname_memory_compaction.rs` + - 对应测试文件 +- 交接目标: + - `build_chapter_compaction_input` 能稳定排除不应重复沉淀的 summary 型 note + - 至少新增 1 个回归测试,覆盖“先写回 summary,再重复 compact 同章”的场景 + - 保持 active / resolved / archived 的既有边界不被破坏 +- 禁止红线: + - 不借机重写整套 compaction 结构 + - 不在本任务里顺手接入 runtime 章节推进主链 + - 不扩大 notes 的自动写回范围 +- 最小验证: + - `cargo test noname_memory_manager -- --nocapture` + - `cargo test noname_memory_compaction -- --nocapture` +- 作用效果: + - 解除当前 chapter compaction 合并阻塞 + - 防止章节摘要被重复压缩后逐轮失真 + - 为后续 notes 与章节收束接线提供可靠输入边界 + +### 卡片 C3:明确章节收束时 notes 与 compaction 的调用顺序 + +- 优先级: + - `P1` +- 建议负责人: + - 主开发线 +- 任务目标: + - 收敛“章节收束时先 compact、先 archive、还是先写回 summary”的顺序,避免 notes 生命周期和 compaction 结果打架。 +- 建议修改范围: + - `src-tauri/src/noname_memory_manager.rs` + - 必要时最小改动 `src-tauri/src/noname_runtime.rs` + - `docs/项目文档/02-架构/02-NoName-代理系统.md` + - `docs/项目文档/03-规划/01-现状评估.md` + - `docs/项目文档/03-规划/02-后续路线图.md` +- 交接目标: + - 形成一条明确的章节收束顺序说明 + - 如果落代码,只新增最小 orchestration helper,不直接扩张 runtime 复杂度 + - 文档能说明“何时 compact、何时 archive、何时 upsert summary” +- 禁止红线: + - 不跳过人工 review / second guardrail 体系去碰最终剧情状态 + - 不把大段章节收束逻辑重新堆回 `tauri_commands.rs` + - 不在没有顺序说明的情况下直接接入正式章节推进流程 +- 最小验证: + - `cargo test noname_memory_manager -- --nocapture` + - `cargo test noname_runtime -- --nocapture` + - 文档自查,无失效引用 +- 作用效果: + - 让 structured notes、compaction summary 和章节归档形成稳定顺序 + - 降低后续章节推进接线时的重复设计成本 + - 避免“功能都在,但调用顺序不稳定”导致的隐性回归 + +### 卡片 C4:继续收口 `tauri_commands.rs` 中剩余的低风险 NoName 编排 + +- 优先级: + - `P1` +- 建议负责人: + - 主开发线 +- 任务目标: + - 在 A2 已阶段性完成的基础上,继续把低风险、重复性的 NoName orchestration 从命令层往模块内收。 +- 建议修改范围: + - `src-tauri/src/tauri_commands.rs` + - `src-tauri/src/noname_apply.rs` + - 视情况最小改动 `src-tauri/src/noname_runtime.rs` +- 交接目标: + - 再减少一段 reviewed apply 或 low-risk apply 的命令层重复壳逻辑 + - 新下沉逻辑必须有直接单测,不只依赖命令集成测 + - 命令层继续只保留参数接收、runtime/engine 调用和错误转换 +- 禁止红线: + - 不新增新的人工 apply scope + - 不扩张 second guardrail 语义 + - 不因为“顺手”把生成主链大改成一次性重构 +- 最小验证: + - `cargo test noname_ -- --nocapture` +- 作用效果: + - 持续降低主链冲突概率 + - 提高 reviewed apply 逻辑的可测试性和可维护性 + - 为后续高风险接口评估保留清晰的受控边界 + +### 卡片 C5:建立 `webRuntime` 与后端字段漂移的守卫式验证 + +- 优先级: + - `P1` +- 建议负责人: + - 主开发线 +- 任务目标: + - 让 Web mock 在 review / lifecycle / noteContext 等关键字段上持续贴近后端,而不是只做一次性同步。 +- 建议修改范围: + - `src/platform/webRuntime.ts` + - `src/platform/webRuntime.test.ts` + - `src/types/game.ts` + - 必要时最小更新 `src/stores/gameStore.ts` +- 交接目标: + - 至少补一组针对 `controlledOutputReviews`、`policyForbiddenScopes`、`noteContext` 或 lifecycle 摘要的守卫测试 + - 前端调试台能继续消费后端当前真实语义,不依赖过时 mock 假设 + - 测试命令在排除 `.review/` 后结果清晰可读 +- 禁止红线: + - 不重做整个 Web runtime + - 不自行发明后端尚不存在的新字段 + - 不把 UI 需求反向写成后端约束 +- 最小验证: + - `npm run test:web-core` + - `npm run test -- --run src/platform/webRuntime.test.ts src/stores/__tests__/gameStore.test.ts` +- 作用效果: + - 降低前后端语义漂移带来的伪回归 + - 让 Trace 面板和调试台读到的 NoName 数据更可信 + - 为后续安全输出草稿层探索保留稳定前端底座 + +### 卡片 C6:为“安全输出草稿层”补一层轻量探针,而不是直接落正式逻辑 + +- 优先级: + - `P2` +- 建议负责人: + - 主开发线 +- 任务目标: + - 在不扩大权限边界的前提下,为下一层安全输出接口补一层文档化、可测试的草稿探针。 +- 建议修改范围: + - `docs/项目文档/03-规划/05-下一层安全输出接口试点评估.md` + - 必要时只补最小 utility / test probe,不新增正式后端命令 +- 交接目标: + - 形成一份更具体的草稿结构或状态机探针 + - 能明确说明“drafted / reviewed / guardrailAllowed / manuallyApplied”各自证据来源 + - 不让后续开发者把评估文档误读成“已经可以直接试点正式 apply” +- 禁止红线: + - 不新增正式 apply scope + - 不直接落最终剧情、世界硬事实或 NPC 隐藏意图写入 + - 不绕过 reviewed apply 的人工确认链 +- 最小验证: + - 文档自查 + - 如补探针测试:`cargo test noname_ -- --nocapture` 或对应最小测试集 +- 作用效果: + - 把“下一层安全输出”从概念讨论变成受控设计对象 + - 降低后续试点时误扩权的风险 + - 为下一阶段研发提供统一术语和边界 + +## 3. 文档管理动作 + +### 新任务落地时至少同步 + +- [现状评估](./01-现状评估.md) +- [后续路线图](./02-后续路线图.md) +- [未来两周开发排期](./04-未来两周开发排期.md) + +### 如果任务状态变化明显,还应同步 + +- [协作任务清单](./03-协作任务清单.md) +- [测试与验证](../04-质量与发布/01-测试与验证.md) + +### 当前建议的更新节奏 + +1. 任务启动前:确认卡片仍有效,是否已被最新 PR 覆盖 +2. 任务提交后:补“当前状态 / 已验证” +3. 任务被否决或拆分后:补“为什么不合并 / 拆到哪一张卡片” diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/README.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/README.md" index 1c9a436..b94b298 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/README.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/README.md" @@ -1,6 +1,6 @@ # Nobody 项目文档 -更新时间:2026-04-20 +更新时间:2026-04-24 适用分支:`main` ## 1. 这套文档解决什么问题 @@ -21,6 +21,7 @@ 5. [后续路线图](./03-规划/02-后续路线图.md) 6. [协作任务清单](./03-规划/03-协作任务清单.md) 7. [未来两周开发排期](./03-规划/04-未来两周开发排期.md) +8. [近期开发任务卡片](./03-规划/06-近期开发任务卡片.md) ## 3. 目录结构 @@ -41,6 +42,7 @@ - [后续路线图](./03-规划/02-后续路线图.md) - [协作任务清单](./03-规划/03-协作任务清单.md) - [未来两周开发排期](./03-规划/04-未来两周开发排期.md) +- [近期开发任务卡片](./03-规划/06-近期开发任务卡片.md) ### `04-质量与发布` From cdf6a3e0fb49b1366f127b9f3aa5218d700355f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AD=94=E6=B4=92=E6=B4=92?= <140176187+mosasa15@users.noreply.github.com> Date: Fri, 24 Apr 2026 16:54:52 +0800 Subject: [PATCH 2/4] test(vitest): exclude review worktrees from discovery --- ...0\350\241\214\344\270\216\345\274\200\345\217\221.md" | 3 ++- ...-\347\216\260\347\212\266\350\257\204\344\274\260.md" | 3 ++- ...6\347\273\255\350\267\257\347\272\277\345\233\276.md" | 7 +++++++ ...0\345\274\200\345\217\221\346\216\222\346\234\237.md" | 2 ++ ...1\344\273\273\345\212\241\345\215\241\347\211\207.md" | 8 ++++++++ ...3\350\257\225\344\270\216\351\252\214\350\257\201.md" | 9 ++++++--- vitest.config.ts | 9 ++++++++- 7 files changed, 35 insertions(+), 6 deletions(-) diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/01-\346\200\273\350\247\210/02-\350\277\220\350\241\214\344\270\216\345\274\200\345\217\221.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/01-\346\200\273\350\247\210/02-\350\277\220\350\241\214\344\270\216\345\274\200\345\217\221.md" index 03f6807..f516a81 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/01-\346\200\273\350\247\210/02-\350\277\220\350\241\214\344\270\216\345\274\200\345\217\221.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/01-\346\200\273\350\247\210/02-\350\277\220\350\241\214\344\270\216\345\274\200\345\217\221.md" @@ -69,6 +69,7 @@ npm run test:web-core ## 5. 当前已知注意事项 -- 仓库根目录下若存在 `.review/` 工作树,会污染 Vitest 的测试发现范围 +- 当前 `vitest.config.ts` 已把前端测试发现范围限制在 `src/`,并显式排除 `.review/` +- 如果后续新增前端测试目录,记得同步更新 Vitest 配置 - `tauri_commands.rs` 仍然较大,修改前应优先确认是否可以在独立模块中完成 - `.kiro/` 中有大量历史资料,仅供追溯,不应直接当作当前设计基线 diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/01-\347\216\260\347\212\266\350\257\204\344\274\260.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/01-\347\216\260\347\212\266\350\257\204\344\274\260.md" index 36e5c6d..3c6179a 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/01-\347\216\260\347\212\266\350\257\204\344\274\260.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/01-\347\216\260\347\212\266\350\257\204\344\274\260.md" @@ -52,7 +52,8 @@ ### 3.4 验证环境污染 -本地若保留 `.review/` 工作树,会让 Vitest 把非当前仓库测试也一起执行,影响结果可信度。 +这一风险已经完成第一轮收口:Vitest 的前端测试发现范围已收紧到 `src/`,并显式排除了 `.review/`。 +当前仍需注意的是:如果后续新增前端测试目录,必须同步更新 `vitest.config.ts`,否则测试发现范围会再次漂移。 ### 3.5 记忆检索与 notes 串联仍未完成 diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/02-\345\220\216\347\273\255\350\267\257\347\272\277\345\233\276.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/02-\345\220\216\347\273\255\350\267\257\347\272\277\345\233\276.md" index 8e7d442..3676e2c 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/02-\345\220\216\347\273\255\350\267\257\347\272\277\345\233\276.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/02-\345\220\216\347\273\255\350\267\257\347\272\277\345\233\276.md" @@ -20,6 +20,13 @@ - 清理本地工作树或在测试配置中显式排除 - 在测试文档中补充说明 +当前状态: + +- 第一轮已完成 +- `vitest.config.ts` 已将前端测试发现范围限制在 `src/`,并显式排除 `.review/` +- `npm run test:web-core` 与前端 NoName 定向测试已恢复只命中当前仓库测试文件 +- 测试文档与运行文档已补充这一基线说明 + ### 任务 2:重建正式文档基线 - 目标:用当前中文文档替代旧版漂移文档 diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/04-\346\234\252\346\235\245\344\270\244\345\221\250\345\274\200\345\217\221\346\216\222\346\234\237.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/04-\346\234\252\346\235\245\344\270\244\345\221\250\345\274\200\345\217\221\346\216\222\346\234\237.md" index d955404..eff7ea1 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/04-\346\234\252\346\235\245\344\270\244\345\221\250\345\274\200\345\217\221\346\216\222\346\234\237.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/04-\346\234\252\346\235\245\344\270\244\345\221\250\345\274\200\345\217\221\346\216\222\346\234\237.md" @@ -6,6 +6,8 @@ 补充说明:基于 2026-04-24 最新项目评估收敛出的直接执行项,见 [近期开发任务卡片](./06-近期开发任务卡片.md)。 +额外更新:卡片 `C1` 已完成第一轮收口,Vitest 已限制在 `src/` 下发现测试并显式排除 `.review/`。 + ## 1. 排期原则 本排期按两条线并行推进: diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/06-\350\277\221\346\234\237\345\274\200\345\217\221\344\273\273\345\212\241\345\215\241\347\211\207.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/06-\350\277\221\346\234\237\345\274\200\345\217\221\344\273\273\345\212\241\345\215\241\347\211\207.md" index 85ea3f7..0eb41a6 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/06-\350\277\221\346\234\237\345\274\200\345\217\221\344\273\273\345\212\241\345\215\241\347\211\207.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/06-\350\277\221\346\234\237\345\274\200\345\217\221\344\273\273\345\212\241\345\215\241\347\211\207.md" @@ -45,6 +45,14 @@ - 降低 review / 本地开发 / CI 三者之间的验证偏差 - 让后续 NoName UI 与 Web mock 调整更容易判断真假回归 +当前状态: + +- 第一轮已完成 +- `vitest.config.ts` 已把前端测试发现范围收紧到 `src/`,并显式排除 `.review/` +- `npm run test:web-core` 已恢复只命中当前仓库测试 +- NoName 前端定向测试已恢复只命中 `src/` 下的 4 个目标文件 +- `docs/项目文档/04-质量与发布/01-测试与验证.md` 与 `docs/项目文档/01-总览/02-运行与开发.md` 已同步更新 + ### 卡片 C2:修复章节压缩对 compaction summary 的回吃风险 - 优先级: diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/04-\350\264\250\351\207\217\344\270\216\345\217\221\345\270\203/01-\346\265\213\350\257\225\344\270\216\351\252\214\350\257\201.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/04-\350\264\250\351\207\217\344\270\216\345\217\221\345\270\203/01-\346\265\213\350\257\225\344\270\216\351\252\214\350\257\201.md" index 7d03d82..98ca805 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/04-\350\264\250\351\207\217\344\270\216\345\217\221\345\270\203/01-\346\265\213\350\257\225\344\270\216\351\252\214\350\257\201.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/04-\350\264\250\351\207\217\344\270\216\345\217\221\345\270\203/01-\346\265\213\350\257\225\344\270\216\351\252\214\350\257\201.md" @@ -40,12 +40,15 @@ npm run build ### `.review/` 污染测试发现 -如果仓库根目录中存在 `.review/` 工作树,Vitest 会把其中的测试文件也当作当前仓库测试一起执行。 +如果仓库根目录中存在 `.review/` 工作树,Vitest 过去会把其中的测试文件也当作当前仓库测试一起执行。 + +当前基线已经在 `vitest.config.ts` 中把测试发现收紧到 `src/`,并显式排除了 `.review/`。 处理建议: -- 不再需要时清理 `.review/` -- 或在测试配置中显式排除 `.review/` +- 日常前端测试默认直接使用当前推荐命令即可 +- 如果后续新增前端测试目录,记得同步更新 `vitest.config.ts` +- 不再需要时仍建议清理 `.review/`,避免干扰人工检索与磁盘占用 ## 4. 回归建议 diff --git a/vitest.config.ts b/vitest.config.ts index 446cec8..88ca1f8 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from 'vitest/config'; +import { configDefaults, defineConfig } from 'vitest/config'; import vue from '@vitejs/plugin-vue'; export default defineConfig({ @@ -7,5 +7,12 @@ export default defineConfig({ environment: 'jsdom', globals: true, clearMocks: true, + // Frontend tests live under src/; keeping discovery here avoids .review worktrees + // and other local review copies being picked up as part of the current repo run. + include: ['src/**/*.{test,spec}.{ts,tsx}'], + exclude: [ + ...configDefaults.exclude, + '.review/**', + ], }, }); From 92305d9cddf7b219ec6c9123b86f082471b0b27a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AD=94=E6=B4=92=E6=B4=92?= <140176187+mosasa15@users.noreply.github.com> Date: Fri, 24 Apr 2026 17:20:13 +0800 Subject: [PATCH 3/4] feat(noname): bridge chapter compaction through memory manager --- ...60\347\212\266\350\257\204\344\274\260.md" | 2 +- ...55\350\267\257\347\272\277\345\233\276.md" | 3 +- ...00\345\217\221\346\216\222\346\234\237.md" | 4 +- ...73\345\212\241\345\215\241\347\211\207.md" | 8 + src-tauri/src/noname_memory_manager.rs | 231 +++++++++++++++++- src-tauri/src/noname_memory_store.rs | 39 +++ 6 files changed, 280 insertions(+), 7 deletions(-) diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/01-\347\216\260\347\212\266\350\257\204\344\274\260.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/01-\347\216\260\347\212\266\350\257\204\344\274\260.md" index 3c6179a..4f795f2 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/01-\347\216\260\347\212\266\350\257\204\344\274\260.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/01-\347\216\260\347\212\266\350\257\204\344\274\260.md" @@ -57,7 +57,7 @@ ### 3.5 记忆检索与 notes 串联仍未完成 -检索排序解释性、notes 生命周期和 notes/retrieval 串联第一轮已经具备;note-aware retrieval 已接入 context builder,使 active notes 扩展出的召回结果能够进入角色上下文包。当前剩余缺口主要在 structured notes 与 compaction、章节收束及更高层 runtime 编排的贯通,后续需要继续验证 notes 如何参与长期记忆沉淀,而不是只参与单次上下文构建。 +检索排序解释性、notes 生命周期和 notes/retrieval 串联第一轮已经具备;note-aware retrieval 已接入 context builder,使 active notes 扩展出的召回结果能够进入角色上下文包。章节 compaction 输入的第一轮收口也已完成:当前章节 episodic events、非 `archived` notes 会进入章节压缩,而旧的 `compact-chapter-*` summary 已被排除,避免重复压缩时发生 summary 回吃。当前剩余缺口主要在章节收束顺序与更高层 runtime 编排的贯通,后续需要继续验证 notes 如何稳定参与长期记忆沉淀,而不是只参与单次上下文构建。 ### 3.6 下一层安全输出仍不能扩大权限边界 diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/02-\345\220\216\347\273\255\350\267\257\347\272\277\345\233\276.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/02-\345\220\216\347\273\255\350\267\257\347\272\277\345\233\276.md" index 3676e2c..c6a8831 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/02-\345\220\216\347\273\255\350\267\257\347\272\277\345\233\276.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/02-\345\220\216\347\273\255\350\267\257\347\272\277\345\233\276.md" @@ -99,7 +99,8 @@ - 章节收束时 resolved 自动归档的既有行为已保留 - 与 retrieval 的第一轮串联已完成:active notes 可作为检索上下文桥,archived notes 不参与扩展召回 - 与 context builder 的串联已完成第一轮:角色上下文构建会消费 note-aware retrieval,并在 `source_stats` 中暴露 `noteContext` -- 与 compaction、章节收束和更高层 runtime 编排的更深层串联尚未完成,后续继续推进 +- 与 compaction 的第一轮串联已完成:`NoNameMemoryManager` 已集中构造章节压缩输入,当前章节 events 与非归档、非 summary notes 会进入 compaction,旧的 chapter summary 不会被重复压缩 +- 与章节收束顺序和更高层 runtime 编排的更深层串联尚未完成,后续继续推进 ### 任务 6.5:优化角色上下文差异度 diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/04-\346\234\252\346\235\245\344\270\244\345\221\250\345\274\200\345\217\221\346\216\222\346\234\237.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/04-\346\234\252\346\235\245\344\270\244\345\221\250\345\274\200\345\217\221\346\216\222\346\234\237.md" index eff7ea1..ff5e29a 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/04-\346\234\252\346\235\245\344\270\244\345\221\250\345\274\200\345\217\221\346\216\222\346\234\237.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/04-\346\234\252\346\235\245\344\270\244\345\221\250\345\274\200\345\217\221\346\216\222\346\234\237.md" @@ -1,12 +1,12 @@ # 未来两周开发排期 -更新时间:2026-04-20 +更新时间:2026-04-24 适用范围:当前 `main` 分支 排期目标:在不扩大 `NoName` 权限边界的前提下,完成主链收口、协作提效与文档稳定化 补充说明:基于 2026-04-24 最新项目评估收敛出的直接执行项,见 [近期开发任务卡片](./06-近期开发任务卡片.md)。 -额外更新:卡片 `C1` 已完成第一轮收口,Vitest 已限制在 `src/` 下发现测试并显式排除 `.review/`。 +额外更新:卡片 `C1`、`C2` 已完成第一轮收口;Vitest 已限制在 `src/` 下发现测试并显式排除 `.review/`,章节压缩输入也已收口为“当前章节事件 + 非归档、非 summary notes”。 ## 1. 排期原则 diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/06-\350\277\221\346\234\237\345\274\200\345\217\221\344\273\273\345\212\241\345\215\241\347\211\207.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/06-\350\277\221\346\234\237\345\274\200\345\217\221\344\273\273\345\212\241\345\215\241\347\211\207.md" index 0eb41a6..d7d58d6 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/06-\350\277\221\346\234\237\345\274\200\345\217\221\344\273\273\345\212\241\345\215\241\347\211\207.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/06-\350\277\221\346\234\237\345\274\200\345\217\221\344\273\273\345\212\241\345\215\241\347\211\207.md" @@ -81,6 +81,14 @@ - 防止章节摘要被重复压缩后逐轮失真 - 为后续 notes 与章节收束接线提供可靠输入边界 +当前状态: + +- 第一轮已完成 +- `NoNameMemoryManager` 已新增 `build_chapter_compaction_input` 与 `compact_chapter_from_memory`,集中承接章节压缩输入构造 +- 章节压缩输入现已只消费当前章节的 episodic events,以及非 `archived`、非 `compact-chapter-*` summary 的 narrative notes +- 已补充“按章节筛选 episodic memory”与“先写回 summary,再重复 compact 同章”两类回归测试 +- 当前已确认旧的 chapter summary 不会在后续重复压缩时被再次回吃;后续重点转向 `C3`,明确章节收束的调用顺序 + ### 卡片 C3:明确章节收束时 notes 与 compaction 的调用顺序 - 优先级: diff --git a/src-tauri/src/noname_memory_manager.rs b/src-tauri/src/noname_memory_manager.rs index 6571e82..d50c77a 100644 --- a/src-tauri/src/noname_memory_manager.rs +++ b/src-tauri/src/noname_memory_manager.rs @@ -8,8 +8,8 @@ use crate::noname_memory_retrieval::{ }; use crate::noname_memory_store::NoNameMemoryStore; use crate::noname_memory_types::{ - NoNameEpisodicMemoryItem, NoNameNarrativeMemoryItem, NoNameSemanticMemoryItem, - NoNameWorkingMemoryItem, + NoNameEpisodicMemoryItem, NoNameNarrativeMemoryItem, NoNameNarrativeStatus, + NoNameSemanticMemoryItem, NoNameWorkingMemoryItem, }; use crate::noname_note_store::{ NoNameChapterNoteReview, NoNameNoteLifecycleAction, NoNameNoteLifecycleResult, NoNameNoteStore, @@ -183,6 +183,45 @@ impl NoNameMemoryManager { NoNameMemoryCompactionService::new().compact_chapter(input) } + pub fn build_chapter_compaction_input( + &self, + chapter_id: impl Into, + chapter_index: u32, + title: impl Into, + created_at: u64, + ) -> NoNameChapterCompactionInput { + let notes = self + .notes + .list_by_chapter(chapter_index) + .into_iter() + .filter(note_feeds_chapter_compaction) + .collect(); + + NoNameChapterCompactionInput { + chapter_id: chapter_id.into(), + chapter_index, + title: title.into(), + events: self.store.episodic_by_chapter(chapter_index), + notes, + created_at, + } + } + + pub fn compact_chapter_from_memory( + &self, + chapter_id: impl Into, + chapter_index: u32, + title: impl Into, + created_at: u64, + ) -> NoNameCompactionSummary { + self.compact_chapter_memory(self.build_chapter_compaction_input( + chapter_id, + chapter_index, + title, + created_at, + )) + } + pub fn compact_trace_memory( &self, input: NoNameTraceCompactionInput, @@ -382,11 +421,22 @@ fn first_non_empty(values: &[&str]) -> Option { .map(str::to_string) } +fn note_feeds_chapter_compaction(note: &NoNameNarrativeMemoryItem) -> bool { + note.status != NoNameNarrativeStatus::Archived && !is_chapter_compaction_summary_note(note) +} + +fn is_chapter_compaction_summary_note(note: &NoNameNarrativeMemoryItem) -> bool { + note.note_id.starts_with("compact-chapter-") +} + #[cfg(test)] mod tests { use super::*; use crate::memory_layers::{ChapterSummary, MemoryEntry, WorldFact}; - use crate::noname_memory_types::{NoNameNarrativeNoteType, NoNameNarrativeStatus}; + use crate::noname_memory_compaction::NoNameCompactionKind; + use crate::noname_memory_types::{ + NoNameMemoryImportance, NoNameNarrativeNoteType, NoNameNarrativeStatus, + }; use crate::noname_types::NoNameRole; #[test] @@ -455,6 +505,181 @@ mod tests { assert_eq!(active_notes[0].summary, summary.summary); } + #[test] + fn manager_builds_chapter_compaction_input_from_current_memory() { + let mut manager = NoNameMemoryManager::new(); + manager.push_episodic_memory(NoNameEpisodicMemoryItem { + memory_id: "event-1".to_string(), + event_type: "scene".to_string(), + timestamp: 1, + chapter_index: 1, + location_id: Some("mountain_gate".to_string()), + actors: vec!["Player".to_string(), "Elder Qinghe".to_string()], + summary: "The ward flared while Elder Qinghe guarded the gate.".to_string(), + detail_ref: None, + importance: NoNameMemoryImportance::High, + }); + manager.push_episodic_memory(NoNameEpisodicMemoryItem { + memory_id: "event-other".to_string(), + event_type: "scene".to_string(), + timestamp: 2, + chapter_index: 2, + location_id: Some("spirit_mine".to_string()), + actors: vec!["Miner".to_string()], + summary: "A later chapter happens elsewhere.".to_string(), + detail_ref: None, + importance: NoNameMemoryImportance::Low, + }); + manager.upsert_narrative_memory(NoNameNarrativeMemoryItem { + note_id: "goal-1".to_string(), + chapter_index: 1, + arc_id: None, + note_type: NoNameNarrativeNoteType::Goal, + title: "Hold Gate".to_string(), + summary: "Keep the mountain gate secure.".to_string(), + status: NoNameNarrativeStatus::Active, + related_entities: vec!["Player".to_string()], + updated_at: 1, + }); + manager.upsert_narrative_memory(NoNameNarrativeMemoryItem { + note_id: "thread-1".to_string(), + chapter_index: 1, + arc_id: None, + note_type: NoNameNarrativeNoteType::UnresolvedThread, + title: "Saboteur Trail".to_string(), + summary: "The saboteur clue was resolved by Qinghe's confession.".to_string(), + status: NoNameNarrativeStatus::Resolved, + related_entities: vec!["Elder Qinghe".to_string()], + updated_at: 2, + }); + manager.upsert_narrative_memory(NoNameNarrativeMemoryItem { + note_id: "old-archived".to_string(), + chapter_index: 1, + arc_id: None, + note_type: NoNameNarrativeNoteType::Conflict, + title: "Old Conflict".to_string(), + summary: "This archived conflict should not be compacted again.".to_string(), + status: NoNameNarrativeStatus::Archived, + related_entities: vec!["Old Rival".to_string()], + updated_at: 3, + }); + manager.upsert_compaction_summary(&NoNameCompactionSummary { + summary_id: "compact-chapter-chapter-1".to_string(), + kind: NoNameCompactionKind::Chapter, + title: "Gate Crisis".to_string(), + summary: "Existing chapter summary".to_string(), + chapter_index: Some(1), + source_ids: vec!["event-1".to_string()], + key_entities: vec!["Player".to_string()], + locations: vec!["mountain_gate".to_string()], + goals: vec!["Hold Gate: Keep the mountain gate secure.".to_string()], + conflicts: Vec::new(), + unresolved_threads: Vec::new(), + relationships: Vec::new(), + diagnostics: vec!["events_compacted=1".to_string()], + estimated_tokens: 16, + created_at: 4, + }); + + let input = manager.build_chapter_compaction_input("chapter-1", 1, "Gate Crisis", 10); + + assert_eq!(input.chapter_id, "chapter-1"); + assert_eq!(input.chapter_index, 1); + assert_eq!(input.title, "Gate Crisis"); + assert_eq!(input.events.len(), 1); + assert_eq!(input.events[0].memory_id, "event-1"); + assert_eq!(input.notes.len(), 2); + assert!(input.notes.iter().any(|note| note.note_id == "goal-1")); + assert!(input.notes.iter().any(|note| note.note_id == "thread-1")); + assert!(!input + .notes + .iter() + .any(|note| note.note_id == "old-archived")); + assert!(!input + .notes + .iter() + .any(|note| note.note_id == "compact-chapter-chapter-1")); + } + + #[test] + fn manager_compacts_chapter_from_memory_without_reusing_prior_chapter_summary() { + let mut manager = NoNameMemoryManager::new(); + manager.push_episodic_memory(NoNameEpisodicMemoryItem { + memory_id: "event-ward".to_string(), + event_type: "scene".to_string(), + timestamp: 1, + chapter_index: 3, + location_id: Some("mountain_gate".to_string()), + actors: vec!["Player".to_string(), "Elder Qinghe".to_string()], + summary: "The ward collapsed during the night watch.".to_string(), + detail_ref: None, + importance: NoNameMemoryImportance::High, + }); + manager.upsert_narrative_memory(NoNameNarrativeMemoryItem { + note_id: "conflict-ward".to_string(), + chapter_index: 3, + arc_id: None, + note_type: NoNameNarrativeNoteType::Conflict, + title: "Ward Collapse".to_string(), + summary: "The mountain gate ward is failing.".to_string(), + status: NoNameNarrativeStatus::Active, + related_entities: vec!["Mountain Gate".to_string()], + updated_at: 3, + }); + manager.upsert_narrative_memory(NoNameNarrativeMemoryItem { + note_id: "thread-saboteur".to_string(), + chapter_index: 3, + arc_id: None, + note_type: NoNameNarrativeNoteType::UnresolvedThread, + title: "Hidden Saboteur".to_string(), + summary: "The saboteur behind the ward remains unidentified.".to_string(), + status: NoNameNarrativeStatus::Active, + related_entities: vec!["Elder Qinghe".to_string()], + updated_at: 4, + }); + + let first_summary = manager.compact_chapter_from_memory("chapter-3", 3, "Night Ward", 20); + manager.upsert_compaction_summary(&first_summary); + + let input_after_upsert = + manager.build_chapter_compaction_input("chapter-3", 3, "Night Ward", 21); + assert!(!input_after_upsert + .notes + .iter() + .any(|note| note.note_id == first_summary.summary_id)); + + let second_summary = manager.compact_chapter_from_memory("chapter-3", 3, "Night Ward", 22); + + assert_eq!(second_summary.chapter_index, Some(3)); + assert!(second_summary + .source_ids + .iter() + .any(|item| item == "event-ward")); + assert!(second_summary + .source_ids + .iter() + .any(|item| item == "conflict-ward")); + assert!(!second_summary + .source_ids + .iter() + .any(|item| item == &first_summary.summary_id)); + assert!(second_summary + .conflicts + .iter() + .any(|item| { item.contains("Ward Collapse") && item.contains("ward is failing") })); + assert!(second_summary.unresolved_threads.iter().any(|item| { + item.contains("Hidden Saboteur") && item.contains("remains unidentified") + })); + assert!(second_summary + .diagnostics + .iter() + .any(|item| item == "events_compacted=1")); + assert!(second_summary + .diagnostics + .iter() + .any(|item| item == "notes_compacted=2")); + } + #[test] fn manager_keeps_note_lifecycle_in_sync_with_retrieval_store() { let mut manager = NoNameMemoryManager::new(); diff --git a/src-tauri/src/noname_memory_store.rs b/src-tauri/src/noname_memory_store.rs index a8541fa..ba770d7 100644 --- a/src-tauri/src/noname_memory_store.rs +++ b/src-tauri/src/noname_memory_store.rs @@ -64,6 +64,14 @@ impl NoNameMemoryStore { &self.episodic } + pub fn episodic_by_chapter(&self, chapter_index: u32) -> Vec { + self.episodic + .iter() + .filter(|item| item.chapter_index == chapter_index) + .cloned() + .collect() + } + pub fn semantic(&self) -> &[NoNameSemanticMemoryItem] { &self.semantic } @@ -237,4 +245,35 @@ mod tests { assert_eq!(report.explanations[0].item_id, "event-1"); assert_eq!(report.explanations[0].rank, 1); } + + #[test] + fn episodic_by_chapter_filters_other_chapters() { + let mut store = NoNameMemoryStore::new(); + store.push_episodic(NoNameEpisodicMemoryItem { + memory_id: "event-1".to_string(), + event_type: "scene".to_string(), + timestamp: 1, + chapter_index: 1, + location_id: None, + actors: vec!["player".to_string()], + summary: "chapter one".to_string(), + detail_ref: None, + importance: NoNameMemoryImportance::Low, + }); + store.push_episodic(NoNameEpisodicMemoryItem { + memory_id: "event-2".to_string(), + event_type: "scene".to_string(), + timestamp: 2, + chapter_index: 2, + location_id: None, + actors: vec!["player".to_string()], + summary: "chapter two".to_string(), + detail_ref: None, + importance: NoNameMemoryImportance::Low, + }); + + let chapter_one = store.episodic_by_chapter(1); + assert_eq!(chapter_one.len(), 1); + assert_eq!(chapter_one[0].memory_id, "event-1"); + } } From 0de53ce74ba36681581f9021ad20b46d2ac48cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AD=94=E6=B4=92=E6=B4=92?= <140176187+mosasa15@users.noreply.github.com> Date: Fri, 24 Apr 2026 18:07:51 +0800 Subject: [PATCH 4/4] feat(noname): stabilize chapter closure ordering --- ...43\347\220\206\347\263\273\347\273\237.md" | 13 ++ ...60\347\212\266\350\257\204\344\274\260.md" | 2 +- ...55\350\267\257\347\272\277\345\233\276.md" | 3 +- ...00\345\217\221\346\216\222\346\234\237.md" | 2 +- ...73\345\212\241\345\215\241\347\211\207.md" | 7 ++ src-tauri/src/noname_memory_manager.rs | 111 ++++++++++++++++++ 6 files changed, 135 insertions(+), 3 deletions(-) diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/02-\346\236\266\346\236\204/02-NoName-\344\273\243\347\220\206\347\263\273\347\273\237.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/02-\346\236\266\346\236\204/02-NoName-\344\273\243\347\220\206\347\263\273\347\273\237.md" index 0764871..4af9138 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/02-\346\236\266\346\236\204/02-NoName-\344\273\243\347\220\206\347\263\273\347\273\237.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/02-\346\236\266\346\236\204/02-NoName-\344\273\243\347\220\206\347\263\273\347\273\237.md" @@ -97,6 +97,19 @@ 旧版协作文档把多个已落地模块标成“未开始”,已不适合作为当前任务基线。 +### 5.4 记忆沉淀顺序需要统一复用 + +`NoNameMemoryManager` 现在已经形成章节收束的最小顺序约定: + +1. 先基于当前章节 events 和尚未归档的 notes 做 chapter compaction +2. 再执行章节 note 整理,把 `resolved` notes 归档 +3. 最后把 chapter summary 作为新的 narrative note 写回 + +这样可以同时保证两件事: + +- 已解决但仍有价值的 note 会进入本次章节摘要 +- 新写回的 chapter summary 不会污染同轮 note review,也不会在后续章节压缩里被再次回吃 + ## 6. 当前结论 `NoName` 已经完成 `V1` 骨架并进入 reviewed apply runtime 收口阶段。 diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/01-\347\216\260\347\212\266\350\257\204\344\274\260.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/01-\347\216\260\347\212\266\350\257\204\344\274\260.md" index 4f795f2..701665c 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/01-\347\216\260\347\212\266\350\257\204\344\274\260.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/01-\347\216\260\347\212\266\350\257\204\344\274\260.md" @@ -57,7 +57,7 @@ ### 3.5 记忆检索与 notes 串联仍未完成 -检索排序解释性、notes 生命周期和 notes/retrieval 串联第一轮已经具备;note-aware retrieval 已接入 context builder,使 active notes 扩展出的召回结果能够进入角色上下文包。章节 compaction 输入的第一轮收口也已完成:当前章节 episodic events、非 `archived` notes 会进入章节压缩,而旧的 `compact-chapter-*` summary 已被排除,避免重复压缩时发生 summary 回吃。当前剩余缺口主要在章节收束顺序与更高层 runtime 编排的贯通,后续需要继续验证 notes 如何稳定参与长期记忆沉淀,而不是只参与单次上下文构建。 +检索排序解释性、notes 生命周期和 notes/retrieval 串联第一轮已经具备;note-aware retrieval 已接入 context builder,使 active notes 扩展出的召回结果能够进入角色上下文包。章节 compaction 输入的第一轮收口也已完成:当前章节 episodic events、非 `archived` notes 会进入章节压缩,而旧的 `compact-chapter-*` summary 已被排除,避免重复压缩时发生 summary 回吃。章节收束顺序也已在 `NoNameMemoryManager` 固定为“先 compact,再 archive resolved notes,最后写回 chapter summary”,避免 resolved note 丢出摘要,也避免新 summary 污染同轮 note review。当前剩余缺口主要在更高层 runtime 编排的贯通,后续需要继续验证 notes 如何稳定参与长期记忆沉淀,而不是只参与单次上下文构建。 ### 3.6 下一层安全输出仍不能扩大权限边界 diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/02-\345\220\216\347\273\255\350\267\257\347\272\277\345\233\276.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/02-\345\220\216\347\273\255\350\267\257\347\272\277\345\233\276.md" index c6a8831..e32c595 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/02-\345\220\216\347\273\255\350\267\257\347\272\277\345\233\276.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/02-\345\220\216\347\273\255\350\267\257\347\272\277\345\233\276.md" @@ -100,7 +100,8 @@ - 与 retrieval 的第一轮串联已完成:active notes 可作为检索上下文桥,archived notes 不参与扩展召回 - 与 context builder 的串联已完成第一轮:角色上下文构建会消费 note-aware retrieval,并在 `source_stats` 中暴露 `noteContext` - 与 compaction 的第一轮串联已完成:`NoNameMemoryManager` 已集中构造章节压缩输入,当前章节 events 与非归档、非 summary notes 会进入 compaction,旧的 chapter summary 不会被重复压缩 -- 与章节收束顺序和更高层 runtime 编排的更深层串联尚未完成,后续继续推进 +- 与章节收束顺序的第一轮串联已完成:`close_chapter_memory` 已固定“先 compact,再 organize/archive resolved notes,最后 upsert summary” +- 与更高层 runtime 编排的更深层串联尚未完成,后续继续推进 ### 任务 6.5:优化角色上下文差异度 diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/04-\346\234\252\346\235\245\344\270\244\345\221\250\345\274\200\345\217\221\346\216\222\346\234\237.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/04-\346\234\252\346\235\245\344\270\244\345\221\250\345\274\200\345\217\221\346\216\222\346\234\237.md" index ff5e29a..b63daf9 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/04-\346\234\252\346\235\245\344\270\244\345\221\250\345\274\200\345\217\221\346\216\222\346\234\237.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/04-\346\234\252\346\235\245\344\270\244\345\221\250\345\274\200\345\217\221\346\216\222\346\234\237.md" @@ -6,7 +6,7 @@ 补充说明:基于 2026-04-24 最新项目评估收敛出的直接执行项,见 [近期开发任务卡片](./06-近期开发任务卡片.md)。 -额外更新:卡片 `C1`、`C2` 已完成第一轮收口;Vitest 已限制在 `src/` 下发现测试并显式排除 `.review/`,章节压缩输入也已收口为“当前章节事件 + 非归档、非 summary notes”。 +额外更新:卡片 `C1`、`C2`、`C3` 已完成第一轮收口;Vitest 已限制在 `src/` 下发现测试并显式排除 `.review/`,章节压缩输入已收口为“当前章节事件 + 非归档、非 summary notes”,章节收束顺序也已固定为“先 compact,再 archive,最后 upsert summary”。 ## 1. 排期原则 diff --git "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/06-\350\277\221\346\234\237\345\274\200\345\217\221\344\273\273\345\212\241\345\215\241\347\211\207.md" "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/06-\350\277\221\346\234\237\345\274\200\345\217\221\344\273\273\345\212\241\345\215\241\347\211\207.md" index d7d58d6..448d03c 100644 --- "a/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/06-\350\277\221\346\234\237\345\274\200\345\217\221\344\273\273\345\212\241\345\215\241\347\211\207.md" +++ "b/docs/\351\241\271\347\233\256\346\226\207\346\241\243/03-\350\247\204\345\210\222/06-\350\277\221\346\234\237\345\274\200\345\217\221\344\273\273\345\212\241\345\215\241\347\211\207.md" @@ -120,6 +120,13 @@ - 降低后续章节推进接线时的重复设计成本 - 避免“功能都在,但调用顺序不稳定”导致的隐性回归 +当前状态: + +- 第一轮已完成 +- `NoNameMemoryManager` 已新增 `close_chapter_memory`,把章节收束顺序固定为“先 compact,再 organize/archive resolved notes,最后 upsert chapter summary” +- 当前顺序约束已经补入回归测试:resolved notes 会进入本次章节摘要,但本次 note review 不会把新写回的 summary 误记为 carried-forward active note +- 本轮未把章节收束 helper 直接接入正式 runtime 章节推进流程;后续如要接线,应复用该 helper,而不是重新拼装顺序 + ### 卡片 C4:继续收口 `tauri_commands.rs` 中剩余的低风险 NoName 编排 - 优先级: diff --git a/src-tauri/src/noname_memory_manager.rs b/src-tauri/src/noname_memory_manager.rs index d50c77a..5f5ec43 100644 --- a/src-tauri/src/noname_memory_manager.rs +++ b/src-tauri/src/noname_memory_manager.rs @@ -38,6 +38,12 @@ pub struct NoNameNoteAugmentedRetrievalReport { pub explanations: Vec, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct NoNameChapterClosureResult { + pub note_review: NoNameChapterNoteReview, + pub summary: NoNameCompactionSummary, +} + impl Default for NoNameMemoryManager { fn default() -> Self { Self::new() @@ -222,6 +228,25 @@ impl NoNameMemoryManager { )) } + pub fn close_chapter_memory( + &mut self, + chapter_id: impl Into, + chapter_index: u32, + title: impl Into, + closed_at: u64, + ) -> NoNameChapterClosureResult { + // Compact first so resolved notes are still available to the summary, + // then archive them and finally persist the chapter summary as a fresh note. + let summary = self.compact_chapter_from_memory(chapter_id, chapter_index, title, closed_at); + let note_review = self.organize_chapter_notes(chapter_index, closed_at); + self.upsert_compaction_summary(&summary); + + NoNameChapterClosureResult { + note_review, + summary, + } + } + pub fn compact_trace_memory( &self, input: NoNameTraceCompactionInput, @@ -680,6 +705,92 @@ mod tests { .any(|item| item == "notes_compacted=2")); } + #[test] + fn close_chapter_memory_compacts_before_archiving_and_stores_summary_after_review() { + let mut manager = NoNameMemoryManager::new(); + manager.push_episodic_memory(NoNameEpisodicMemoryItem { + memory_id: "event-finale".to_string(), + event_type: "scene".to_string(), + timestamp: 7, + chapter_index: 4, + location_id: Some("summit_hall".to_string()), + actors: vec!["Player".to_string(), "Elder Qinghe".to_string()], + summary: "The summit hall rite ended with the ward restored.".to_string(), + detail_ref: None, + importance: NoNameMemoryImportance::High, + }); + manager.upsert_narrative_memory(NoNameNarrativeMemoryItem { + note_id: "resolved-thread".to_string(), + chapter_index: 4, + arc_id: None, + note_type: NoNameNarrativeNoteType::UnresolvedThread, + title: "Ward Saboteur".to_string(), + summary: "Elder Qinghe confessed before the rite concluded.".to_string(), + status: NoNameNarrativeStatus::Resolved, + related_entities: vec!["Elder Qinghe".to_string()], + updated_at: 6, + }); + manager.upsert_narrative_memory(NoNameNarrativeMemoryItem { + note_id: "carry-forward".to_string(), + chapter_index: 4, + arc_id: None, + note_type: NoNameNarrativeNoteType::Goal, + title: "Rebuild Trust".to_string(), + summary: "The sect still needs to rebuild trust after the sabotage.".to_string(), + status: NoNameNarrativeStatus::Active, + related_entities: vec!["Player".to_string()], + updated_at: 6, + }); + + let closure = manager.close_chapter_memory("chapter-4", 4, "Summit Rite", 8); + + assert_eq!(closure.note_review.chapter_index, 4); + assert_eq!(closure.note_review.archived_from_resolved_count, 1); + assert!(closure + .note_review + .archived_note_ids + .iter() + .any(|note_id| note_id == "resolved-thread")); + assert!(closure + .note_review + .active_note_ids + .iter() + .any(|note_id| note_id == "carry-forward")); + assert!(!closure + .note_review + .active_note_ids + .iter() + .any(|note_id| note_id == &closure.summary.summary_id)); + assert!(closure + .summary + .source_ids + .iter() + .any(|item| item == "resolved-thread")); + assert!(closure.summary.summary.contains("Ward Saboteur")); + assert!(closure + .summary + .summary + .contains("confessed before the rite concluded")); + assert!(!closure + .summary + .unresolved_threads + .iter() + .any(|item| item.contains("Ward Saboteur"))); + + let chapter_notes = manager.notes_by_chapter(4); + let resolved_thread = chapter_notes + .iter() + .find(|note| note.note_id == "resolved-thread") + .expect("resolved note should remain stored"); + assert_eq!(resolved_thread.status, NoNameNarrativeStatus::Archived); + + let stored_summary = chapter_notes + .iter() + .find(|note| note.note_id == closure.summary.summary_id) + .expect("chapter summary should be stored after closure"); + assert_eq!(stored_summary.status, NoNameNarrativeStatus::Active); + } + #[test] fn manager_keeps_note_lifecycle_in_sync_with_retrieval_store() { let mut manager = NoNameMemoryManager::new();