Skip to content

功能规格说明:记忆管理 ​

创建日期:2026-01-22

澄清 ​

2026-01-27 会议 ​

  • 问:如果任务涉及多个文件,路径特定的记忆规则应如何激活? → 答:并集:激活匹配任务中当前正在读取或修改的任何文件的所有记忆规则
  • 问:哪些工具交互或上下文信号应触发路径特定记忆规则的激活? → 答:上下文中的任意文件:当前加载在上下文窗口中(通过 Read、Grep 等)或正在写入的任何文件

用户场景与测试 (必填) ​

用户故事:保存项目特定记忆(优先级:P1) ​

作为用户,我希望代理保存项目特定的规则或上下文,以便它记住这个项目的信息。

为什么是这个优先级:这是核心功能,允许代理适应不同的项目需求和风格。

独立测试:可以要求代理记住项目特定规则(如"始终使用 pnpm")并验证它被保存到 AGENTS.md。

验收场景:

  1. 假设用户要求代理记住项目相关内容,当代理识别为项目规则时,则它必须被保存到当前目录的 AGENTS.md
  2. 假设信息已保存在 AGENTS.md 中,当代理被问到相关问题时,则它必须在响应中使用该信息

用户故事:保存全局用户记忆(优先级:P1) ​

作为用户,我希望代理保存跟随我跨所有项目的全局偏好,这样我不必重复它们。

为什么是这个优先级:对不同工作空间之间的个性化体验至关重要。

独立测试:可以要求代理记住全局偏好(如"我的名字是 Alice")并验证它被保存到全局记忆文件(如 ~/.wave/AGENTS.md)。

验收场景:

  1. 假设用户要求代理全局记住某些内容,当代理识别为用户偏好时,则它必须被保存到全局用户记忆文件
  2. 假设信息已保存在全局记忆中,当代理在任意项目中使用,则它必须能够访问该信息

用户故事:管理记忆(优先级:P2) ​

作为用户,我希望查看和删除已保存的记忆条目,以便保持我的上下文准确和最新。

为什么是这个优先级:防止记忆因过时或不正确的信息而变得杂乱。

独立测试:可以打开记忆管理 UI,找到条目并删除它。验证它从相应文件中被移除。

验收场景:

  1. 假设用户打开记忆管理 UI,则他们必须看到所有项目和用户记忆条目的列表
  2. 假设条目被选中,当用户选择删除它时,则它必须从存储文件中被移除

用户故事:自动记忆(优先级:P1) ​

作为用户,我希望代理自动跨会话记住重要信息,而不必手动保存。

为什么是这个优先级:显著减少摩擦并允许代理建立对项目和工作流的长期理解。

独立测试:可以执行涉及特定构建命令或架构决策的任务。开始新会话并通过检查系统提示或提出相关问题来验证代理知道该信息。

验收场景:

  1. 假设自动记忆已启用,当代理识别有价值的信息(构建命令、调试见解等)时,则它必须能够将其保存到项目的自动记忆目录
  2. 假设信息已保存在 MEMORY.md 中,当新会话开始时,则MEMORY.md 的前 200 行必须包含在系统提示中;当索引超过 200 行或超过 25000 字符时,则必须按两个上限一并截断(先按行、再按字符且在换行处切分,避免切断行),并附加一条写明触发原因(行数 / 字符数)与「单行索引应短于约 200 字符,细节移到主题文件」的警告
  3. 假设代理在 git 仓库中,则该仓库的所有工作树必须共享同一个自动记忆目录
  4. 假设代理需要写入其自动记忆,则它必须能够在无需手动批准的情况下执行(安全区域)

用户故事:记忆提示词对齐 Claude Code(优先级:P1) ​

作为用户,我希望代理的记忆提示词完整描述「记什么、不该记什么、怎么记、何时读、读到后怎么用」,使写入与读取遵循同一套口径,而不是主代理与后台提取子代理各说一套。

为什么是这个优先级:主代理提示词(packages/agent-sdk/src/prompts/autoMemory.ts)是自写的精简版,缺类型学、frontmatter 与索引写法、何时访问、陈旧核实、与 plan/task 的分工、目录已存在指引;提取 fork 提示词(prompts/autoMemoryExtraction.ts)则是照抄 Claude Code 的完整版,且 fork 复用主系统提示词(aiManager.ts runAutoMemoryFork 用主系统提示词构建请求,见「自动记忆完美 Fork 提取」场景 1)⇒ 同一个请求里同时含「要记 architecture / file paths / debugging insights」与「禁止记 architecture / file paths / debugging solutions」两条相反指令,产物格式也随之分叉(主代理写无 frontmatter 的裸 markdown,fork 写带 name/description/type 的文件)。

独立测试:构建系统提示,检查其记忆段的段落构成;触发一次提取 fork,比较其提示词与主提示词对「可保存内容」的约束是否互相矛盾。

验收场景:

  1. 假设自动记忆已启用,当构建主代理系统提示时,则记忆段必须包含四类记忆(user / feedback / project / reference)的类型学,每类给出 description、when_to_save、how_to_use 与示例
  2. 假设自动记忆已启用,当构建系统提示时,则记忆段必须包含「不应保存的内容」清单(可由当前项目状态推导的代码模式/约定/架构/文件路径、git 历史与谁改了什么、调试方案与修复配方、CLAUDE.md 已记录的内容、临时的会话内任务状态),并声明该清单在用户显式要求保存时同样适用(此时应先问其中「令人意外」或「不显然」的部分)
  3. 假设自动记忆已启用,当构建系统提示时,则记忆段必须包含两步保存流程:Step 1 用带 name/description/type frontmatter 的主题文件模板写记忆;Step 2 在 MEMORY.md 加一行指针;并声明 MEMORY.md 是索引不是记忆(每条一行、约 150 字符以内、无 frontmatter、不得把内容直接写进索引)
  4. 假设自动记忆已启用,当构建系统提示时,则记忆段必须包含「何时访问记忆」(记忆看起来相关或用户引用先前对话时;用户明确要求查看/回忆/记住时必须去读;用户表示忽略或不使用记忆时,按 MEMORY.md 为空处理——不应用、不引用、不比较、不提及记忆内容)
  5. 假设自动记忆已启用,当构建系统提示时,则记忆段必须包含「推荐之前先核实」的可信度规则(记忆里出现的函数/文件/flag 是写入当时的断言,可能已被改名或删除;若用户即将据此行动,须先核实存在)
  6. 假设自动记忆已启用,当构建系统提示时,则记忆段必须包含记忆与其他持久化形式的分工(需要在本会话内对齐方案时用 Plan;需要跟踪本会话的推进步骤时用 tasks;memory 只保留对未来会话仍有用的信息)
  7. 假设自动记忆已启用,当构建系统提示时,则记忆段必须说明记忆目录已存在、可直接用 Write 写入,不得先运行 mkdir 或检查其是否存在
  8. 假设主代理提示词与提取 fork 提示词在同一次 fork 请求中同时生效,当比较两者对「可保存内容」的约束时,则不得出现一条要求记录、另一条禁止记录的情形
  9. 假设自动记忆已启用,则主代理与提取 fork 写出的记忆文件必须遵循同一套 frontmatter 格式(name / description / type)

用户故事:自动记忆开关(优先级:P1) ​

作为用户,我希望关闭「自动记忆」后代理不再自动从对话中提取记忆、关闭前保存的记忆也按我的轮次设置触发,以便按需开关该后台行为。

为什么是这个优先级:2026-09 客户反馈插件端设置页关闭「开启自动记忆」后仍持续写入记忆——设置页开关此前只保存到各宿主自有配置(VS Code globalState/JetBrains 持久化/桌面 configStore),从未传到 agent 运行侧(SDK resolveAutoMemoryEnabled 只读 settings.json 与环境变量),实际等于开关无效。本故事把「宿主设置 → 运行中会话」的生效语义钉死,避免三端各自实现漂移。2026-09-10 用户拍板改造保存路径:这类用户偏好(自动记忆开关/频率)统一写入用户级 ~/.wave/settings.json 并经 SDK 实时重载生效,不再经 stdio initialize/updateConfig 当 AgentOptions 覆盖项下发(覆盖层会永久遮蔽 settings.json 的实时值,见 agent-config.md「设置实时重载」与「环境变量作用域与优先级」)。

独立测试:在宿主(desktop/VSCE/JetBrains)设置页「个性化 → 自动记忆规则」关闭开关并保存,随后在同一会话继续对话并新建会话,验证不再产生记忆提取(自动记忆目录/MEMORY.md 无新增、无提取 fork 调用);重新开启并设置频率后验证按轮次恢复提取。

验证分层(2026-09-10 追加拍板):场景 1–4 的「保存即生效、下一轮起停/恢复」→ 单测(SDK gate + 频率合并与计数)+ 真 host(packages/desktop/tests/integration,真 wave --stdio + 真 ~/.wave/settings.json:写文件后下一轮读到新值——真 host 上以 language 为可观测代表,autoMemoryEnabled/autoMemoryFrequency 与 language 走同一条写文件 + 实时重载路径);场景 5「关闭后不把自动记忆目录加入无批准安全区」与场景 1 的「关闭后目录写入被权限层拒绝」→ 单测覆盖白名单的幂等增删(LiveConfigManager.syncAutoMemorySafeZone);真 host 未覆盖:驱动权限层判定需要模型发出工具调用,而真 host 的本地假模型只回文本(可观测的替代口径是同一 ~/.wave/settings.json 已由真 host 断言写入、下一轮生效);场景 6 的优先级链(用户级 settings.json > 环境变量 > 默认)→ 单测。webview 保存路径(#2115 回归网:这两个字段全 optional,链路缺字段编译期不报错,只能靠测试盯住)→ 单测 packages/webview/tests/webview/settingsMemory.test.tsx(SettingsPage.onSave 载荷 + ChatApp 把 autoMemoryEnabled/autoMemoryFrequency 放进 updateConfiguration.configurationData)+ e2e packages/webview/e2e/desktop-settings-auto-memory-save.e2e.ts(真 bundle:点保存后从真 postMessage 报文里读这两个键,并按宿主回包回填开关/轮次);host → CLI 的 stdio 透传由对应透传测试负责(不在本组内重复)。逐条对照见 agent-config.md 边界说明「验证分层(单测 / e2e / 真 host)」。真 host 层挂在 main-only CI job(Linux real-host-e2e;Windows check-windows 同样跑真 host 与 desktop 单元套件)。

验收场景:

  1. 假设用户已在设置页把「开启自动记忆」关闭并保存,当该宿主后续继续对话或新建会话,则任何轮次结束都不得触发记忆提取(不写自动记忆文件、不发起提取 fork),无需重启会话或宿主
  2. 假设会话运行中(含正在生成)用户关闭自动记忆并保存,当保存完成,则从下一个对话轮次起停止提取;保存前已在途的提取放行完成、不截断写入
  3. 假设用户关闭自动记忆若干轮后再次开启并保存,当后续轮次结束,则恢复提取且触发计数从开启时刻重新累计(关闭期间的轮次不计入)
  4. 假设用户把触发轮次设为 N(1–100),当自动记忆开启时,则每满 N 个对话轮次触发一次提取;变更频率保存后按新值重新累计(与场景 3 同语义)
  5. 假设自动记忆关闭,当新建会话初始化时,则不再为该会话创建自动记忆目录、不把自动记忆目录加入无批准安全区(对齐运行时 gate 的同一判定来源)
  6. 假设存在多个设置来源,当解析自动记忆开关与频率时,则按 用户级 ~/.wave/settings.json(设置页保存的落点,经 SDK 实时重载在各会话下一轮开始时生效)> 环境变量 WAVE_DISABLE_AUTO_MEMORY/WAVE_AUTO_MEMORY_FREQUENCY > 默认(true/1)的优先级生效;设置页保存值写入该文件、是三端共享的同一份用户偏好(各宿主读写同一来源、互不覆盖),不得经 stdio 会话参数(AgentOptions 覆盖层)下发——该层只保留会话级/单次覆盖语义(见 agent-config.md「环境变量作用域与优先级」);改造前宿主私有存储(VSCE globalState / JB wave.xml / 桌面 wave-desktop.json)里的旧开关/频率值不迁移、不再被读取,升级后回落本场景的默认值(true/1),用户需在设置页重选一次(取舍见 agent-config.md 边界说明「升级用户的一次性迁移:不做」)

用户故事:自动记忆完美 Fork 提取(缓存共享)(优先级:P1) ​

作为用户,我希望自动记忆提取子代理复用主会话的请求前缀(相同的系统提示、相同的工具列表、相同的模型、相同的消息前缀),以便提取请求命中 prompt 缓存,降低 token 成本并加快提取速度。

为什么是这个优先级:当前提取子代理使用独立的系统提示、独立的工具列表和快速模型,与主会话请求前缀不一致,导致每次提取都无法命中缓存、重复处理完整历史。完美 Fork 使前缀一致,静态块命中缓存(Claude 精确前缀匹配,Qwen/GLM 向后扫描窗口)。

独立测试:可以启用自动记忆并配置缓存模型,完成一轮对话触发提取,检查提取请求与主会话最后请求的前缀一致,且 usage 中缓存读取 token 大于 0。

验收场景:

  1. 假设自动记忆已启用且触发提取,当提取 fork 构建请求时,则系统提示必须与主循环一致(相同的静态/动态分块,静态块带缓存标记)
  2. 假设提取 fork 构建请求,当检查工具列表和模型时,则必须与主循环一致(同一模型而非快速模型——模型是缓存键的一部分)
  3. 假设提取 fork 构建请求,当检查消息时,则消息前缀必须与主循环最后请求一致(含记忆注入的 system-reminder),提取提示词追加在末尾
  4. 假设配置了支持缓存的模型,当提取请求发出后,则usage 必须报告缓存读取 token(cache_read_input_tokens 或 cached_tokens)大于 0,证明前缀命中
  5. 假设提取 fork 需要阅读文件,当调用只读工具(Read/Grep/Glob、只读 Bash)时,则在本地执行并把结果回填到 fork 消息
  6. 假设提取 fork 需要写入记忆,当Write/Edit 路径在自动记忆目录内时,则允许执行;路径在目录外时,则拒绝并把拒绝结果回填给模型继续下一轮
  7. 假设提取 fork 调用目录外写入、Bash rm、MCP、Agent 等受限工具,当请求发出时,则必须被拒绝(与提取提示词一致)
  8. 假设提取 fork 运行期间,则不得写入主会话记录,不得影响主会话的消息、工具块或加载状态
  9. 假设配置了不支持缓存的模型,当提取请求发出时,则功能仍正常,仅失去缓存收益
  10. 假设主代理已读取若干文件并建立了读状态基线,当提取 fork 启动时,则 fork 必须从主代理克隆这份读状态(fork 内可直接 Edit 已读文件而无需先 Read),而非使用空表
  11. 假设提取 fork 在运行中读取或写入了文件,当 fork 结束后,则 fork 内新建/更新的读状态不得写回主会话的读状态,也不得影响主会话的读取去重与陈旧判定
  12. 假设提取提示词要预置「已有记忆清单」,当构建该清单时,则每条必须形如 - [<type>] <filename> (<ISO 时间戳>): <description>(type 与 description 取自各记忆文件的 frontmatter,缺失时留空),以便提取子代理无需先花一轮 ls 也能判断某条内容是否已存在
  13. 假设某个记忆文件的 frontmatter 用 YAML 块标量写 description(>/>-/|/|- 等),当构建已有记忆清单时,则该条的 description 必须是折叠/保留后的真实文本(不含 >、|、- 这类指示符),续行不得被解析成独立的 frontmatter 键,也不得让描述退化成 >-

用户故事:自动记忆提取并发保护(优先级:P1) ​

作为用户,我希望自动记忆提取不会并发运行,以便多个提取不会同时写入同一记忆文件导致更新丢失。

为什么是这个优先级:提取是后台任务,可能跨多个对话轮次运行。上一轮提取尚未完成时新一轮对话结束会再次触发提取,两个 fork 并发写 MEMORY.md 会互相覆盖。

独立测试:可以模拟慢速提取,在提取进行中触发新一轮对话结束,验证不会启动第二个并发提取;验证代理清理时等待进行中的提取完成。

验收场景:

  1. 假设一次提取正在进行,当新一轮对话结束触发提取时,则必须跳过本次提取,不得启动并发 fork
  2. 假设提取因进行中被跳过,当后续轮次条件满足时,则仍会再次触发提取,不丢失提取机会
  3. 假设主代理在本轮成功写入了记忆目录内的文件,当触发提取时,则跳过本次提取,避免与手动写入冲突(已有行为保留;被拒绝或失败的写入未实际更新记忆,不计入手动写入,仍需触发提取)
  4. 假设代理退出或清理时仍有提取在运行,当清理发生时,则等待进行中的提取完成后再退出,不截断写入

用户故事:外部文件改动回灌(变更通知)(优先级:P1) ​

作为用户,我希望主代理能感知到自己在别处(后台提取 fork、外部编辑器、其他会话)被改动过的已读文件,以便它不会基于过期的文件内容继续工作。

为什么是这个优先级:主代理与后台自动记忆提取 fork 并发运行。fork 可以改写主代理读过的记忆文件,用户也可能在编辑器里改文件;没有回灌时主代理对磁盘上的改动完全无感,会基于陈旧内容继续编辑或断言。对齐 Claude Code 的 changed_files 附件机制。

独立测试:主代理读取某文件后由另一进程修改它,触发下一轮请求,验证注入了一条包含路径与改动摘要的通知;再次触发无改动时验证不重复注入。

验收场景:

  1. 假设主代理完整读取过某文件,当该文件在磁盘上被外部写入者改为新内容、下一轮请求构建时,则必须向主代理注入一条变更通知,包含文件路径与改动摘要
  2. 假设文件的 mtime 变新但内容未变(touch、编辑器原地保存等),当检测时,则不得注入变更通知
  3. 假设读状态条目来自分页读取(提供了 offset 或 limit),当检测变更时,则该条目必须被跳过(部分内容无法给出可靠变更摘要)
  4. 假设主代理自己通过 Write/Edit 写入了文件,当下一轮检测时,则不得为主代理自己刚写的文件注入变更通知
  5. 假设某文件的变更已被通知过一次且此后未再变化,当后续轮次构建请求时,则不得重复注入同一变更通知
  6. 假设一轮内检测到多个文件变更,当注入通知时,则必须受上限约束(单条摘要截断 + 文件条数与总字节上限),被省略的部分不得无界增长上下文
  7. 假设后台提取 fork 在记忆目录内改写了主代理读过的记忆文件,当下一轮请求构建时,则主代理必须收到该文件的变更通知(对齐 CC 的 changed_files)

用户故事:记忆陈旧提醒(读取时标注新鲜度)(优先级:P1) ​

作为用户,我希望代理在读取记忆文件时被明确告知该记忆有多旧,以便它不会把陈旧的 file:line 引用或函数名当成当前事实断言。

为什么是这个优先级:记忆是写入那一刻的快照,代码演进后其中的行号与符号会失效;而带着 file:line 引用的陈旧断言反而显得比没有引用更权威。对齐 Claude Code 的 memdir/memoryAge.ts + FileReadTool 前置注记。

独立测试:把一个记忆文件的 mtime 调到多天前,让主代理 Read 它,验证工具结果前置了陈旧提醒且文案包含天数;把 mtime 设为今天/昨天验证不附加;读取非记忆文件验证不附加。

验收场景:

  1. 假设主代理通过 Read 读取自动记忆目录内的文件,当该文件 mtime 距今超过 1 天(天数按 Math.floor((现在 - mtime) / 86400000) 计算,记为 N),则该工具结果内容必须前置 <system-reminder>This memory is N days old. Memories are point-in-time observations, not live state — claims about code behavior or file:line citations may be outdated. Verify against current code before asserting as fact.</system-reminder>
  2. 假设记忆文件的 mtime 是今天或昨天(N ≤ 1),当读取时,则不得附加陈旧提醒
  3. 假设读取的文件不在自动记忆目录内,当读取时,则不得附加陈旧提醒
  4. 假设同一文件在同一 mtime 下被多次读取,则提醒文本必须逐字节相同(不得内联当前时间戳或其他每轮变化的量,以免破坏前缀缓存)
  5. 假设自动记忆被关闭,当读取记忆目录内的文件时,则不得附加陈旧提醒

用户故事:嵌套记忆注入(子目录的记忆文件)(优先级:P2) ​

作为开发者,我希望代理在读到某个目录下的文件时,自动看到该目录(或其最近祖先)中的 AGENTS.md / CLAUDE.md,以便子目录特有的约定在真正处理该子目录的文件时才进入上下文,而不是常驻在根记忆里。

为什么是这个优先级:把「某子包的构建方式」「某目录的特殊约定」放进根 AGENTS.md 会持续占用上下文且与当前任务无关;嵌套注入让它们按需生效。对齐 Claude Code 的 nested_memory 附件。

独立测试:在 packages/foo/AGENTS.md 写一条独特约定,让主代理读 packages/foo/src/a.ts,验证该约定作为 <system-reminder> 被注入;再读一次同目录文件验证不重复注入。

验收场景:

  1. 假设主代理通过 Read 成功读取某文件,当该文件所在目录或其某个在项目根之下的祖先目录存在 AGENTS.md(不存在时回退 CLAUDE.md)且该文件尚未被注入过,则必须在下一轮请求构建前把其内容作为 <system-reminder>Contents of <path>:\n\n<内容> 注入;触发面只包含 Read(不含 Grep/Glob),读取失败或被去重跳过的读取不计
  2. 假设同一嵌套记忆文件已被注入过,当后续再次读取该目录下的文件时,则不得重复注入;去重依据是会话级的已注入集合,不随读取状态缓存的驱逐而失效
  3. 假设该文件同时命中路径特定记忆规则(.wave/rules/ 的 paths),则两者都必须独立生效,嵌套注入不得取代路径规则注入
  4. 假设嵌套目录链上存在多个 AGENTS.md,则必须按「由远到近」(项目根方向 → 文件所在目录)的顺序注入,且不得只注入最内层的那一个;项目根自身的 AGENTS.md 不作为嵌套注入(它已作为项目记忆常驻)
  5. 假设读取的文件不在项目根之下(如用户级记忆目录、additionalDirectories 中的外部仓库),则不得触发任何嵌套注入
  6. 假设同一目录下 AGENTS.md 与 CLAUDE.md 同时存在,则只注入 AGENTS.md

用户故事:模块化记忆规则(优先级:P1) ​

作为开发者,我希望将项目指令组织到 .wave/rules/ 目录中的多个文件中,以便我可以维护聚焦的记忆规则文件而不是一个大的 AGENTS.md。

为什么是这个优先级:这是该功能的主要目标——启用记忆规则的模块化组织。

独立测试:可以在 .wave/rules/ 中创建多个 .md 文件并验证它们自动作为项目记忆加载,优先级与 AGENTS.md 相同。

验收场景:

  1. 假设项目有 .wave/rules/code-style.md 和 .wave/rules/testing.md,当Wave 启动时,则两个文件都应该作为项目记忆加载
  2. 假设.wave/rules/ 中有记忆规则,当Wave 执行任务时,则它应该遵守这些文件中的指令。无条件规则应与 AGENTS.md 一起出现在消息数组开头作为 <system-reminder>

用户故事:路径特定记忆规则(优先级:P1) ​

作为开发者,我希望使用 YAML frontmatter 将记忆规则限定到特定文件,以便记忆规则仅在 Wave 处理匹配指定模式的文件时生效。

为什么是这个优先级:对不同目录(如前端 vs 后端)有不同要求的大型项目至关重要。

独立测试:可以创建带有 paths 字段的记忆规则并验证它仅在代理处理匹配文件时活跃。

验收场景:

  1. 假设记忆规则的 paths 包含 "src/api/**/*.ts",当Wave 编辑 src/api/user.ts 时,则记忆规则应该活跃。匹配的条件规则应在 Read 工具触发时作为持久化 isMeta: true 用户消息注入到消息数组末尾,在所有工具结果之后
  2. 假设相同的记忆规则,当Wave 编辑 src/ui/Button.tsx 时,则记忆规则不应该活跃
  3. 假设没有 paths 字段的记忆规则,当Wave 编辑任何文件时,则记忆规则应该始终活跃。无条件规则应与 AGENTS.md 一起出现在消息数组开头作为 <system-reminder>
  4. 假设记忆规则的 frontmatter 用 YAML 块标量或缩进续行写多行值(如 paths: >- 后跟缩进路径、或 paths: 后跟块列表),当Wave 加载规则时,则 paths/priority 等字段必须解析成折叠/保留后的真实值(不含 >、|、- 这类指示符),续行不得被解析成独立的 frontmatter 键

用户故事:子目录中的记忆规则组织(优先级:P2) ​

作为团队负责人,我希望将记忆规则组织到 .wave/rules/ 的子目录中并使用符号链接跨项目共享记忆规则。

为什么是这个优先级:改善复杂设置的可维护性并支持跨项目标准执行。

独立测试:可以在 .wave/rules/ 中创建子目录和指向外部记忆规则文件的符号链接,然后验证它们被发现和加载。

验收场景:

  1. 假设.wave/rules/frontend/react.md 中有记忆规则,当Wave 加载记忆规则时,则它应该从子目录中发现并加载 react.md
  2. 假设符号链接 .wave/rules/shared 指向外部目录,当Wave 加载记忆规则时,则它应该解析符号链接并加载其中的记忆规则

用户故事:用户级模块化记忆规则(优先级:P2) ​

作为用户,我希望在 ~/.wave/rules/ 中定义跨所有项目生效的个人记忆规则。

为什么是这个优先级:允许用户全局维护个人偏好(如工作流、风格)。

独立测试:可以在 ~/.wave/rules/preferences.md 中创建记忆规则并验证它在任何项目中被加载。

验收场景:

  1. 假设~/.wave/rules/ 中有记忆规则,当Wave 在任何项目中启动时,则用户级记忆规则应该被加载
  2. 假设用户级和项目级记忆规则之间存在冲突,则项目级记忆规则应该优先

边界情况 ​

  • 缺失的存储文件:系统应该创建 AGENTS.md、全局记忆文件或自动记忆目录(如果不存在)
  • 重复条目:系统应该理想地防止或警告重复的记忆条目
  • 大型记忆文件:如果记忆文件增长过大,系统应该高效处理。对于 MEMORY.md,只加载前 200 行且不超过 25000 字符(行数上限挡不住超长单行,实测出现过 200 行内接近 200 KB 的索引),超出时截断并给出原因与「单行索引应短于约 200 字符」的警告
  • 记忆文件的新鲜度计算:陈旧提醒按文件 mtime 与当前时间之差计算;系统时钟回拨或文件来自其他时区时,负数天数一律按「今天」处理(不产生负值提醒)
  • 嵌套记忆的重复与循环:同一个嵌套记忆文件在一次会话中只注入一次;目录链遍历必须终止(不得因符号链接或异常路径循环反复注入)
  • 嵌套记忆文件不可读:某层目录的记忆文件存在但读取失败(权限、编码)时,跳过该文件继续处理其余目录,不得因单层失败而放弃整条目录链
  • 并发访问:处理多个代理实例可能尝试写入同一记忆文件的情况
  • Git 工作树:确保项目 ID 在同一仓库的不同工作树之间稳定
  • 循环符号链接:系统如何处理指向自身或创建循环的符号链接?(需求:检测并优雅处理)
  • 无效 YAML:系统如何处理格式错误的 frontmatter 的记忆规则文件?
  • 大括号展开:确保 {src,lib}/**/*.ts 等模式被正确展开
  • 空的规则目录:如果 .wave/rules/ 缺失或为空,系统不应报错

假设 ​

  • 用户对当前目录和全局 ~/.wave/ 目录有写入权限
  • AI 模型有足够的上下文窗口来包含组合后的记忆
  • Markdown 是人类可读性和易于编辑的首选格式
  • 记忆文件由主代理或提取 fork 按提示词约定写出,其 frontmatter(name/description/type)用于构建预置给提取 fork 的既有记忆清单,并供人按类型筛选;老文件缺 frontmatter 时按「无描述」降级处理,不影响可读性