> 执行细节(1 个章节 / 1 个代码块)已外置,执行前照读:references/implementation-details.md(grep: 实现细节)
记忆分层管家:双向治理注入截断,审计工作区记忆上提全局通用教训,同时防止全局记忆自身被撑爆。
用途
工作区 <workspace>/.workbuddy/memory/MEMORY.md 会随项目积累大量教训,但存在双向结构性问题:
- 通用教训被困在项目里:很多教训是全局通用技术教训(与具体项目无关),只躺在 workspace 文件 → 其他项目用不到。
- 工作区注入截断红线:workspace MEMORY.md 有硬性注入上限,按【字符】计——工作区 3000 / 用户级 4000(系统注入提示原文;旧的 5KB/12KB 是字节经验值,量纲不同、仅供提醒,见 PF-472),超限时尾部被静默截断,下次会话看不到后半段。
- 【v1.2.0 新增】全局记忆自身也会被撑爆:用户级
~/.workbuddy/MEMORY.md同样在每次会话被注入,且实测 22KB 文件已在注入时被砍尾(证据:注入块末尾出现(user memory truncated))。旧版技能只做 workspace→global 单向搬运,越搬 global 越大,问题只是挪了地方。v1.2.0 双向治理。
本技能把"散落记忆"分流:全局通用 → 上提用户级 ~/.workbuddy/MEMORY.md;项目专属 → 留在 workspace;并在 global 自身超阈值时就地压缩或拆子文件,让两层都保持在注入安全线内。
常见触发场景(细化)
- 用户主动问"记忆太多/被截断""哪些该升全局""工作区记忆该沉淀什么""记忆分层/蒸馏/整理/全局记忆瘦身"。
- 出现截断证据:注入块末尾
(user memory truncated),或某段记忆"上次还在、这次没了"(尾部静默截断)。 - 跨项目复用:A 项目学到的通用教训想让 B 项目也可见(上提全局)。
- 四向分流争议:拿不准"任务结论该写台账还是记忆"时,先过 Step 2.5 路由表。
- 非触发:纯问候、单次查询、无需动记忆的普通问答。
调用链
纯流程型技能,不调度其他技能。读两份 MEMORY.md + 当日日志,写用户级 MEMORY.md + workspace MEMORY.md + 当日日志。辅助脚本 scripts/scan_memory.py 做只读初审(分类 + 去重 + 阈值自检);支持全局索引模式自动深读 ~/.workbuddy/lessons/ 子文件全文做 [DUP] 深比对(--deep-read auto/on/off);scripts/prune_workspace.py 做工作区日志轮转的只读分析(资格/命名异常/覆盖率,见 Step 5.0);scripts/condense_workspace.py 做工作区 MEMORY.md 就地拆子文件(Mode B,对应全局 condense_global.py,见 Step 5.6)。
另加 scripts/guarded_memory_write.py:任何 MEMORY.md 改写的硬闸(字符红线 + 锚点保全 + 写后逐行核对,见 Step 5.7)。
工作流
Step 1 — 审计(读)
- 读工作区长期记忆:
<workspace>/.workbuddy/memory/MEMORY.md(<workspace>为当前工程根,如D:/QAXDownload)。 - 读用户级全局记忆:
~/.workbuddy/MEMORY.md(本机所有项目会话自动注入)。 - 读当日日志:
<workspace>/.workbuddy/memory/YYYY-MM-DD.md(不存在则先建)。 - 跑辅助脚本:
python scripts/scan_memory.py <workspace_mem> <global_mem> [--threshold N] [--global-threshold N] [--deep-read auto|on|off] --deep-read auto(默认):仅当检测到全局处于瘦索引模式(已拆子文件)时才深读;on总是读;off从不读([DUP] 仅基于索引标题,可能漏判)。
→ 列出工作区每块记忆、加权标注标题/正文命中"项目专属关键词"次数、报告工作区与全局各自的字节数 vs 阈值,并对全局超阈值给出自管理建议。- 自动化兜底巡检(
--guard模式):python scripts/scan_memory.py --guard --workspace-dir <ws根目录> [--pitfalls <路径>] [--daily-threshold 150] [--distill-lines 120] [--distill-days 30] [--pf-max-entries 80] [--pf-max-bytes 61440]只读扫:① 当日日志(行数/顶部主题索引/天数,标[WARN]缺索引/[DISTILL]待蒸馏/[ROTATE]待轮转);② 坑点库(PF 条数/字节,标[SPLIT])。输出待办清单,供周度 automation / AI 确认后执行(守脚本只读原则,绝不写文件)。 - ⚠️ 索引判定契约(PF-276,2026-08-31 实跑踩坑):
--guard判定"当日日志有索引"只认 H2——正则^##\s*.*主题索引(scan_memory.py:170)。自动化补索引脚本若用 H1(# 当日主题索引)插入,重跑 WARN 计数纹丝不动(白干)。正确姿势:补索引一律## 当日主题索引+ 每## 主题一行带行号;写完用--guard自身验证(WARN 计数下降才是有效,计数不变=契约没对上)。
Step 2 — 分类(判)
对每条工作区教训块判 GLOBAL(全局通用)vs PROJECT(项目专属):
- GLOBAL 信号:不引用任何具体项目技能名/Slug/版本号;讲的是通用工具/语言/平台行为——
Write/Edit 静默截断、grep 误命中、PowerShell 坑、Chart.js 回退、zip 编码乱码、destructive dry-run 守卫、自动化仅定时/一次性触发、契约字段名静默失败、门禁负向验证、一键冒烟、跨轮次先读真实版本号 等。 - PROJECT 信号:点名具体技能(
zfl-*)、SkillHub 已发布列表、sync-suite、监理套件、某技能特定版本/bug(如v3.11.2、99718)。 - PROJECT 信号补充(两类绝对不升格):① 已固化进某产物的结论——某教训已写进某个技能的
SKILL.md/references、或写进某文档/代码,读产物即得,再升全局是副本噪音;② 一次性结论——某次会话查到"X 在 Y 技能里"、某临时判断/定位结果,能重新发现或读产物即得。两者均只记当日日志(YYYY-MM-DD.md),不升全局。判据一句话:跨会话、反复适用的通用操作教训才升格;项目内已落盘产物与一次性结论,记日志即可。 - CHECK(需语义判断):标题点名项目物但正文是通用行为(如"### 22. 扫描 frontmatter 触发 schema 翻车与正解"正文是通用扫描方法论)。脚本仅给信号,最终由 AI 判。
- 结构段(STRUCTURAL):
## 用户习惯## 技能架构约定## 待办## 记忆维护纪律## 关联等是项目专属容器,不是教训条目,脚本明确标 STRUCTURAL、不列入上提候选(旧版曾误把## 用户习惯当 GLOBAL 候选,v1.2.0 修正)。 - 重复项 [DUP]:工作区教训若与全局已有条目同号/同义,脚本标
[DUP],不重复上提,只在全局对应条目下加"补强说明"(保证幂等,连跑不重复追加)。
Step 2.5 — 写入目标路由(四向分流)
Step 2 的 GLOBAL/PROJECT 只回答"升不升全局",没回答"任务结论往哪放"。实测踩坑(2026-09-25):把"T5c 已处置:watch_only + 反向回灌 + 提交 958a39c"这种任务处置结论写进项目 MEMORY.md,撑破 3000 红线两次(2996→3617→3198)。根因=混淆了三类性质不同的内容。每次写记忆前先过这道题:
| 内容性质 | 去向 | 形态约束 |
|---|---|---|
| 跨项目可复用的事实 / 约定 / 偏好 / 方法(如"密钥严禁进 git commit"、#1–#114 通用技术教训、R-L21「先注入故障见红」这类可迁移原则) | 全局 ~/.workbuddy/MEMORY.md |
薄索引:主题 + ≤20 字一句;详情在 lessons/ 子文件 |
| 本项目长期有效的架构 / 纪律 / 约定(如 R-L 系列、双环境部署、门禁纪律) | 项目 .workbuddy/memory/MEMORY.md |
同上,薄索引 |
| 任务状态 / 处置结论 / 今天做了什么(如"T5c 已处置"、某 PF 入库、某脚本升版) | 台账 待办清单_*.md + 当日 YYYY-MM-DD.md |
不受字符红线约束,可写细节;MEMORY.md 只留"✅ 已处置"状态行 + 指针 |
| 长叙事 / 证据 / 全量 dump(根修过程、见红取证、对比表) | 子文件(lessons_project.md / pitfalls/PF-NNN.md) | 由上面三类引用 |
铁律(回答"何时该沉淀进全局记忆"):
- 全局记忆没错,而且该用——它正是跨项目可复用知识的正确归宿(#1–#114、密钥铁律都该在全局)。纪律不是"别写全局",是"只把跨项目可复用的知识写全局,且永远薄索引"。
- 任务处置结论绝不进全局(也不进项目 MEMORY.md 正文)——它只服务于本任务追踪,进台账;MEMORY.md 留"✅ 已处置"状态行 + 指针即可。这是 PF-615 核心:处置细节写进红线上限文件 = 自相矛盾(必被截断)。
- 已有子文件的主题,索引只指指针、不复述论证链(PF-615:复述即膨胀,17228→3833 实证)。全局索引里
#1–#114等条目只留文件名 + 一句,完整链在子文件。 - 写前必跑
guarded_memory_write.py --check-only;薄索引形态 = 文件名自解释条目 ≤20 字、★ 条只留核心命题。
Step 3 — 截断检查(红线)
- 工作区 MEMORY.md 超 3000 字符(真红线)/ 5120 字节(提醒线)→ 强制上提全局通用教训以瘦身,否则下次注入被截断。
- 口径必须分清:注入上限按字符计(工作区 3000 / 全局 4000),而本技能脚本阈值按字节计(5120 / 12288)。两口径不一致会双向出错——纯 ASCII 内容时字节线偏松(5120 字节 = 5120 字符,已超 3000 字符却不报警),中文密集时偏严。判超限以字符红线为准:
guarded_memory_write.py --check-only一步给结论。 - 全局 MEMORY.md 超 4000 字符(真红线)/ 12288 字节(提醒线)→ 触发 Step 5.5 全局自管理(否则全局自身注入被截断)。
- 量纲纪律(PF-472):字符红线来自系统注入提示原文(工作区 3000 / 用户级 4000),不是估计值;字节阈值(
--threshold/--global-threshold)是经验提醒线。两者量纲不同,不可互推、不可互相替代——只判一个数就是漏报。
Step 4 — 上提(写全局)+ 体积护栏
- 将 GLOBAL 教训追加到用户级 MEMORY.md,续编为
#N(接全局现有最大号)。 - 每条自包含:改写跨引用(如"#14/#35具体化"→改写成描述性短语),因全局读者没有 workspace 编号上下文。
- 若全局无合适区块,新增
## 跨项目技术教训(续)段。 - 【体积护栏 v1.2.0】:追加前先估算增量字节,若
全局当前字节 + 增量 > --global-threshold,不盲塞——先执行 Step 5.5 给全局瘦身腾出空间,再把新教训以"编号+一句话"形式追加进索引,详细展开移入~/.workbuddy/lessons/子文件(见 Step 5.5)。 - 不重复全局已有条目;同义项在既有条目下加"补强说明"。
Step 5 — 压缩(写工作区)
- 重写 workspace MEMORY.md:仅保留 PROJECT 专属内容(架构约定 / 跨技能模式 / 待办 / 项目专属死脚本清理 / 动态发布状态)。
- GLOBAL 教训移除,改为一行指针:
通用技术教训已上提全局记忆 ~/.workbuddy/MEMORY.md。 - 保留"记忆维护纪律"段(含阈值与压缩四原则)。
Step 5.0 — 工作区日志轮转(受控删除,仅 >30天且已蒸馏)
工作区每日日志 YYYY-MM-DD.md 会随项目累积(本工作区已 15 个、累计 ~620KB)。系统记忆规则允许:超过 30 天的日志,先蒸馏进 MEMORY.md,再删旧文件。但删除是高危操作,必须走受控路径,禁止随手删。
- 资格线:日志 mtime 距今天 >30 天(阈值可配置
--prune-days,默认 30)。未达 → 一律不删(仍在活跃工作上下文)。 - 前置(蒸馏核对):该日志的耐久内容必须已进
MEMORY.md。不凭感觉判——用scripts/prune_workspace.py --dry-run做只读分析:列出 >30天日志、标 filename/mtime 不匹配异常、粗估MEMORY.md覆盖率(日志各段标题/关键词命中索引的比例)。覆盖率低 → 先 Step 5 把耐久项压进 MEMORY.md,再回来。 - 硬约束(与 Step 5.5 同范式):删除前必须先备份
cp <log> ~/.workbuddy/_trash_memory_<时间戳>_<日志名>.md,确认成功、且 AI 显式向用户确认后,才删。脚本只出提案,不自动删——删除由 AI 在用户确认后执行(personal_files_safety 要求 human-in-the-loop)。 - 命名异常检查(必做):filename 日期与 mtime 日期不一致(如本工作区
2026-07-17.md的 mtime 是 07-19、2026-07-14.md的 mtime 是 07-15,说明曾被改名/挪动)先标红。删前确认内容无错位,否则可能把 A 日工作记到 B 日文件、误删。 - 删除后:当日日志追加
## 工作区日志轮转段,记录删了哪些、备份路径、蒸馏去向。 - 注意:本步骤仅针对工作区
<workspace>/.workbuddy/memory/下的YYYY-MM-DD.md日报;MEMORY.md(已蒸馏的精华)与全局~/.workbuddy/MEMORY.md永不删。
Step 5.1 — 活跃日志顶部主题索引(防膨胀可读性)
Step 5.0 只管 >30 天日志的"轮转删除",但活跃日志(当天)本身会膨胀——实测单日可达 359 行、43 个 ## 主题,且 30 天规则对它在 30 天内完全不治理(每天都活跃则 30 天后才一次性蒸馏,蒸馏前全是全细节堆积)。须对活跃日志做轻量可读性治理,范式与 Mode B "总结但不失细节"同源。
- 顶部主题索引:每个
## 主题缩成一行(主题名 + 关键结论/行号),置于文件顶部## 当日主题索引块;正文细节原样保留。未来秒定位,不丢细节。 - 单日硬阈值(>150 行):活跃日日志 >150 行且顶部无索引块 → 强制补索引(写入前先收敛,不无限平铺)。
- 与 Step 5.0 分工:5.0 治"老日志淘汰",5.1 治"活跃日志可读性 + 收敛",互不冲突。
- 也可由周期 automation 兜底(见关联段):周度"工作区记忆蒸馏与防膨胀"扫 >120 行或 >30 天日志自动蒸馏 + 活跃日 >150 行强制补索引。
Step 5.5 — 全局自管理(防自身截断,v1.2.0 核心)
当全局 MEMORY.md 超 --global-threshold 时执行,两档任选(AI 按超出幅度与用户确认选):
- Mode A(原地压缩,低风险,适用略超阈值):保留单文件、仍全量注入。把每条教训压成「标题 + 一句话核心 + 关键关联」,删除冗余叙述/重复示例/过期日期;被他处引用的编号条目(如
#14)原样保留。压缩四原则:① 编号且被其他文件按号引用的条目原样保留;② 段落式教训压成单行;③ 删过期待办/重复叙述;④ 通用教训上提全局记忆,本文件只留项目专属。 - Mode B(拆子文件,适用远超阈值,如 >15KB):
~/.workbuddy/MEMORY.md仅留紧凑索引(编号 + 一句话),详细展开移入~/.workbuddy/lessons/按主题拆分(助手脚本scripts/condense_global.py自动完成,子文件命名见下): tech_rules.md(重要技术规则 14 条)user_prefs.md(用户偏好设置)cross_project_habits.md(跨项目工作习惯)lessons_core.md(经验教训 #1–#28 + 公共模块/收尾)design_patterns.md(可复用设计模式)lessons_extra.md(跨项目技术教训 #29–#53 + 补强说明)skillhub_publish.md(SkillHub 发布要点)- 索引顶部加一行:
> 详细展开见 ~/.workbuddy/lessons/(按需 Read) - 索引文件须 ≤ 阈值,确保每次注入完整可见;细节按需 Read 取回。
- 注意:子文件放
~/.workbuddy/lessons/(用户自有目录,非服务器缓存~/.workbuddy/memory/),不会被自动覆盖。 - 备份(强制):改写全局前先
cp ~/.workbuddy/MEMORY.md ~/.workbuddy/_trash_memory_<时间戳>.md,确认成功再动。助手脚本--apply模式会自动备份到~/.workbuddy/_trash_memory_<时间戳>.md。 - 发现约定(防孤儿化):拆子文件后,全局 MEMORY.md 索引顶部已写明"详细展开见 ~/.workbuddy/lessons/(按需 Read)"。未来会话/其它技能若需某条教训全文,须主动
Read ~/.workbuddy/lessons/<topic>.md——该目录不在自动注入范围内,不会被动可见。关键常驻规则(如"路径必须 portable")建议仍留在索引内联,勿全迁子文件。 - 助手脚本(已实现):重体力改写用
scripts/condense_global.py先生成"索引+子文件"提案,AI 核对后再落盘,降低手动重写 22KB 的出错率。 - 预览(只读,不改真实文件):
python scripts/condense_global.py --dry-run→ 提案写入~/.workbuddy/_preview_lessons/ - 落盘(先自动备份原文件):
python scripts/condense_global.py --apply→ 备份至~/.workbuddy/_trash_memory_<时间戳>.md,再写子文件 + 薄索引 - 脚本按顶层
##段自动分类,教训编号(### N.)在索引保留标题行,确保跨文件引用(如#20/#28)不丢。 - ⚠️ 薄索引态取源预警(PF-277,2026-08-31 周检实测):当前
MEMORY.md已是薄索引时,脚本护栏会从最近的全量备份取源(~/.workbuddy/_trash_memory_*.md,防空壳覆盖)——但不管备份新鲜度,周检场景上次 apply 可能已是数十天前 →--dry-run预览基于旧备份,若直接--apply会把当前索引整体回滚丢新条目。正确姿势:dry-run 必看[INFO] 检测到当前为薄索引,已从全量备份读取源行核对时间戳;超阈值且已薄索引时优先增量写~/.workbuddy/lessons/子文件 + 手动瘦索引,不依赖全量重建;确要重建先手动备份当前MEMORY.md+lessons/再 apply。
Mode B 全局索引格式示例(改写后的 ~/.workbuddy/MEMORY.md 仅留索引):
WorkBuddy 用户级本地记忆(索引)
重要技术规则
跨项目技术教训(续)
1. 隐性分层缺陷——"已有"≠"够用"
2. 自检分层三档(full/quick/skip)
53. Windows 路径分隔符须归一化
SkillHub 发布要点
Step 5.6 — 工作区就地拆子文件(Mode B)
当工作区 MEMORY.md 远超阈值(如 >15KB)且内容多为 PROJECT 专属(zfl- 跨技能教训、架构洞察、待办)无法靠 Step 5 上提瘦身时,执行就地 Mode B:
- 触发:
scan_memory.py报workspace >15KB且分类结果 GLOBAL 极少(内容基本 PROJECT)。此时 Step 5 压缩空间有限(PROJECT 本就该留工作区),须拆子文件。 - 手法(实跑验证):把详细全文下沉到工作区 memory 目录子文件
<workspace>/.workbuddy/memory/lessons_project.md,主MEMORY.md缩为薄索引(各##段标题 + 一句指针 + 顶部说明行)。 - 与全局 Mode B 平行:全局拆到
~/.workbuddy/lessons/(用户级、跨项目);工作区拆到<ws>/.workbuddy/memory/lessons_project.md(项目级、不跨项目)。两者都是"索引+子文件"模式,确保注入不截断。 - 助手脚本:
scripts/condense_workspace.py(对应全局condense_global.py): - 预览:
python scripts/condense_workspace.py --workspace <ws> --dry-run→ 提案写入<ws>/.workbuddy/memory/_preview_workspace_lessons/ - 落盘:
python scripts/condense_workspace.py --workspace <ws> --apply→ 先备份原文件到<ws>/.workbuddy/memory/_trash_memory_<ts>.md,再写lessons_project.md+ 薄索引 - 默认触发阈值 15KB(
--threshold可配);低于阈值提示用 Step 5 压缩即可,不强行拆。 - 索引格式:主
MEMORY.md顶部一行> 详细展开见.workbuddy/memory/lessons_project.md(按需 Read);各段仅留标题+摘要指针,详情在子文件。 - 引用完整性:跨段引用(如"见架构洞察段")在索引仍可见标题;详情 Read 子文件取回。
- 注意:
lessons_project.md位于工作区memory/目录(本地状态),不进技能发布包(技能打包只含技能目录本身),无泄漏风险。
Step 5.7 — 记忆改写硬闸(任何 MEMORY.md 改写必经)
Step 5 / 5.5 / 5.6 解决怎么压,本步解决压完凭什么算通过。2026-09-17 实证:为把一条新事实塞进工作区 MEMORY.md 而做压缩+新增同轮操作,自设闸算出 2960 字符(< 3000)从而落盘;同日复核该文件已达 3287 字符 / 5712 字节,超过 3000 字符注入上限——即为了塞一条新事实而静默破掉旧上限(与 PF-467 降版事故同族:改成功了 不等于 改对了)。
- 口径铁律(先分清口径再判超限):注入上限按字符计——工作区 3000 / 全局 4000(系统注入提示原文),这是真红线;而
scan_memory.py的 5120 / 12288 是字节治理线(提醒)。两口径不一致会双向出错:纯 ASCII 内容时字节线偏松(5120 字节 = 5120 字符,早已越过 3000 字符却报 OK = 漏报),中文密集时偏严(误报)。判超限一律以字符红线为准、且两个数一起看——只看一个数的门禁等于把口径当成了事实(PF-410 同族)。 - 三条闸(脚本已实现,勿用临时脚本替代):
python scripts/guarded_memory_write.py <mem_file> [--content new.md] [--protect TEXT]... [--apply]
1. 先算后写,超限即拒:字符数 > 红线 ⇒[BLOCK]且目标文件零副作用(连备份都不建)。禁止先写进去再看超没超。
2. 锚点保全:--protect声明的必保锚点(编号 / 待办 / 函数名 / 关键指针)缺失即 BLOCK——专治压过头把锚点压掉。
3. 写后逐行核对:与写前快照做行级 diff,输出消失行 / 新增行清单;落盘后读回校验 sha256(回执不算数,实读才算数)。 - 默认 dry-run:不加
--apply只预览。落盘时自动备份_bak_guarded_<时间戳>_<原名>到目标同目录;备份 sha256 与写前不一致即放弃写入。 - 空集不判通过:新内容为空 ⇒ BLOCK(与 PF-440/444 三态判定一致:没有内容不得读作内容合规)。
- 自证纪律:闸自身须可被证伪——
--selftest跑 9 例(N1–N4 四个负样本:超限 / 锚点缺失 / 空内容 / CRLF 目标·LF 口径贴限却 CRLF 超限 + P1 合规正样本 + C1–C4 四个反向对照:放大阈值后同一超限内容转 PASS、dry-run 不得落盘、CRLF 留足行尾余量转 PASS、LF 目标贴限转 PASS)。改闸后必跑;若负样本转绿、或反向对照仍 BLOCK,说明闸已恒真/恒假,改的是假门禁(PF-389 / #109)。 - 只读体检:
--check-only一步报 chars vs 红线 / bytes vs 治理线 / 需再精简 N 字符,作为 Step 3 截断检查的机器判据。 - 与既有步骤的关系:Step 5.5 / 5.6 的
condense_*.py负责怎么拆,其产物同样要过本步字符红线;本步是它们共同的下游闸,不替代它们。 - 自测口径须与守卫同源(2026-09-25 实证,PF-614 的实操面):凡自己估算候选稿字符数,必须按目标文件的行尾约定换算——目标为 CRLF 时每行多 1 个
\r⇒ 实际写入字符数 = 自测值 + 行数。本轮对 LF 候选稿直接len()得 3033,守卫按落盘形态报 3068,差值 34 恰等于行数,白跑两轮--contentdry-run 才定位。自测一律以--check-only的chars=为准、不自算;确需自算则先做与写盘相同的归一(replace("\r\n","\n")后按目标转回),否则读数系统性偏小、越贴限越容易误判"已达标"。 - 行尾基线(本技能自身):改写本技能文件必须保持其既有行尾——
SKILL.md与CHANGELOG.md为 CRLF,scripts/*.py为 LF。用\n拼接后直写会把 CRLF 文件变成混合行尾(注入 23 处 bare LF 需回修);读写一律newline="",必要时replace("\r\n","\n").replace("\n","\r\n")归一。
Step 5.8 — 索引引用核验(索引写了、文件真的在吗)
Step 5.7 管内容(字符红线),本步管指针。MEMORY.md 是"索引 + 短摘要"结构,条目形如「会话沉淀_xxx_2026_09_24_固化.md —— ★ 结论…」,它声明了一个文件存在。但索引写了、文件没建(或子文件被移走/改名)时,没有任何机制会发现:读记忆的人顺着索引去找、找不到,而缺失全程静默。这是 PF-454「声明≠接线」在记忆索引层的同类——不是门禁脚本缺调用点,而是记忆里的指针缺目标。
- 实证(2026-09-24):本轮为一条教训编写索引时,顺手在
~/.workbuddy/MEMORY.md挂了「会话沉淀_检查轮自身的假绿自查_2026_09_24_固化.md」条目,而该文件从未创建。查重时才发现:全库 48 条索引里 44 条真实存在、4 条属豁免,唯独新挂的那条是空指针——即在"沉淀假绿教训"的过程中,自己制造了一次假沉淀。 - 三档判据(中间档须显式存在):
1.[OK]索引与文件对得上;
2.[EXEMPT]泛指引(SKILL.md式概念指代,不构成"存在某文件"的断言)或技能内相对路径引用(落在skills/技能目录中,须递归搜skills/才能判)——每种豁免须带理由 + 复核条件,不得静默放过(否则豁免会退化成永久放行,PF-498 镜像面);
3.[MISS]两份搜索根均找不到 ⇒ 假沉淀,exit 1。 - 引用形态处理(2026-09-25 加固,PF-454 族):① 路径式引用(含
/,如pitfalls/PF-x.md)按声明路径严格核对,缺失即 MISS(此前被静默丢弃);② 裸PF-引用仅查pitfalls/,防lessons/根同名文件造成基名歧义假 OK。写法建议:PF 类索引直接写pitfalls/PF-NNN.md路径式最稳妥。 - 执行:
python scripts/check_index_refs.py [--mem <MEMORY.md>] [--lessons <dir>] [--skills <dir>] [--json] - 自证纪律:
--selftest跑 7 例(N1–N2 两个负样本:假索引 / 真假混合只报假的那条[精度] + P1 全存在正样本 + C1–C2 两个反向对照:①删掉真文件后同一索引须转 MISS ②不存在的技能内引用名不得被豁免 + F3/F4 两个加固对照:③裸PF-引用仅查pitfalls/、lessons/ 根同名文件不得假 OK ④路径式引用按声明路径严格核对)。反向对照是这里的命门——没有 ①,"全存在即 PASS"可能只是检查恒不报(与 Step 5.7 自证同一条纪律)。 - 与同族工具的边界(三者互不重叠,勿互替):
guarded_memory_write.py管写前(字符红线+锚点+写后核对)|zfl-skill-quality-checker --check-memory管超阈|check_index_refs.py管指针存在性——此前无覆盖。 - 接入时机:Step 4/5/5.5/5.6 任何一次改写 MEMORY.md 之后跑一次(编写新索引条目时尤其必要);结论
缺失: 无且RC=0才收尾。 - ⚠️ 作用域限制:该收尾判据只对「全局」索引成立,对「工作区」索引结构上必然假红(2026-09-25 实证,6 处 MISS 全属此类)。脚本只有两个搜索根——
--lessons(缺省取 MEM 同级lessons/)与--skills(~/.workbuddy/skills,DEFAULT_SKILLS),均不覆盖工作区<ws>/.workbuddy/memory/(check_index_refs.py:47/112/145)。故工作区索引里完全合法的相对/模板指针(lessons_project.md、memory/YYYY-MM-DD.md)一律被判[MISS] 「两份搜索根均未找到 ⇒ 假沉淀」——判词本身把"工具没搜那个根"误报成"假沉淀",正是 PF-633 同族(判据的口径没和目标的存放位置同源)。正解=对工作区索引把 MISS 降级为人工核对(Read确认目标实存即可收尾),绝不为了变绿而删真实指针——"为了让检查变绿而改证据"是本技能最该防的那一类(PF-644 正解③:不许绕过守卫)。修法(增第三搜索根--ws-memory)未做:属改共享工具、边界需授权,不顺手补。 本条是 ② 的权威口径——输出 → 完成标准与自验证两处"结论缺失: 无且RC=0"只适用于全局索引;工作区索引按上文降级为人工核对。
Step 6 — 记日志
- 向当日日志追加:
## 记忆分层重构+ 做了什么(上提 N 条、workspace XKB→YKB、全局自管理 Mode A/B、截断风险解除)。 - ⚠️ 并发写方在场时禁用 read-modify-write(2026-09-25 实证):日志虽 append-only,但另一会话正在同一
memory/写时(判据=该文件 mtime 近数分钟内变动、或末行带「并发披露」字样;PF-571 结论有保质期),Edit 的「读 → 匹配 → 整文件回写」会在窗口内静默丢弃他方刚追加的内容,文件越大窗口越宽(实测 109 KB)。改用 O_APPEND 二进制追加:open(log,'ab')写frag.encode('utf-8'),不走文本模式(免 Windows\n→\r\n翻译)。四件配套(缺一即退化成"看起来成功"):① 幂等哨兵=片段首行标题,已存在即 SKIP(exit 0);② 原前缀逐字节不变=after[:len(before)] == before;③ 本片段为精确后缀=after.endswith(frag_bytes)(②③不得互替为"行数变多"之类弱判据——那是 PF-635 的盲区形态);④ 并发交错字节单列披露=len(after)-len(before)-len(frag),>0 只披露不判失败(否则把并发误报成自身故障)。写前先备份、写后grep -c <哨兵>回读验证。
Step 7 — 跨工作区扩散(询问)
- 当前工作区处理完后,发现其它工作区记忆文件(排除当前工作区):
find ~/.workbuddy ~/WorkBuddy -path "*/.workbuddy/memory/MEMORY.md" 2>/dev/null
(~/WorkBuddy为本机工作区容器目录,Windows 大写;~/.workbuddy兜底。两处都不存在则跳过。注意:旧版误写为find ~/.workbuddy / ~/WorkBuddy(中间裸/会扫整块磁盘),v1.2.0 已修正。) - 用 AskUserQuestion 询问用户是否扫描其它工作区,选项:
- 全部扫描(推荐):对每个发现的工作区套用 Step 1–6(+ 必要时的 5.5),把困着的全局通用教训上提、工作区压缩。
- 仅扫描 >5KB 的:先做截断检查,只对超阈值(有截断风险)的工作区执行分层。
- 否,到此为止:结束。
- 若选"否"直接结束;若选"是",逐个工作区重复 Step 1–6(+5.5)。
- 历史工作区不注入独立当日日志(避免日期错乱),其分层动作统一记在当前工作区的当日日志里(一次汇总)。
OPEN 台账管理(跨会话连续性)
跨会话"已解决却挂起 / 下次主动问被忘"是真发生过的遗漏(如 R1 改完 bridge 仍未关台账)。本技能作为记忆治理本职,负责台账的读 + 回显 + 关闭端;写端由 zfl-task-summary 在任务收尾落账,单一真相源 = <workspace>/.workbuddy/memory/MEMORY.md 的 ## OPEN 决策/待办台账 / ## RESOLVED 台账 段。
台账格式(已在 workspace MEMORY.md 落地)
- 每条:
[OPEN]/[RESOLVED]+ 编号(O1/R1…)+ 日期 + 紧急度 + 逢会触发标记。 - 解决即关闭:OPEN 项解决后立即移到 RESOLVED 段并留痕防复发,绝不"已解决却挂着"。
- "下次主动问 zfl"类指令必须落 OPEN 台账并标"逢会触发",否则 100% 漏。
关闭步骤
OPEN 项被解决(代码改完 / 决策拍板 / 用户确认不做)时,当场标记 RESOLVED(移到 RESOLVED 段 + 一行留痕)。若用户在本轮指示了某 OPEN 项的处理,结束前确认已关闭。
跨项目通用决策
项目级决策留 workspace MEMORY.md;若属跨项目通用,按 Step 2 上提全局 ~/.workbuddy/MEMORY.md 同名段。
约束与错误处理
约束
- 任何 MEMORY.md 改写(Step 4/5/5.5/5.6 的产物、手工编辑、临时脚本)都必须过 scripts/guarded_memory_write.py 硬闸;超限即拒写盘,禁止先落盘再补救。
- 只读脚本 scan_memory.py 绝不写文件;任何写操作(Step 4/5/5.5)由 AI 在确认后执行。
- 全局记忆 ~/.workbuddy/MEMORY.md 属个人敏感文件,Step 5.5 Mode B 改写前必须先备份到 ~/.workbuddy/_trash_memory_<时间戳>/,确认成功再改。
- 工作区每日日志 YYYY-MM-DD.md 默认 append-only(只追加,不删除、不覆盖);唯一例外是 Step 5.0 受控轮转(>30天 且 已蒸馏进 MEMORY.md,且先备份 + 显式确认)。
- 不把工作区全部教训无脑复制到全局(污染全局、制造重复);不把项目专属动态状态("已发布列表""某技能当前版本")留在全局。
错误处理
- 脚本读不到文件 → 打印 [WARN] ... not found 并以退出码 1 结束,不抛栈。
- 全局超阈值但用户选"否" → 仅记日志告警,不强推 Step 5.5。
- 去重检测命中 [DUP] → 跳过追加,避免重复膨胀(幂等)。
- 阈值违规(如 --threshold 0)→ argparse 默认兜底,不中断流程。
示例
场景:工作区 MEMORY.md 11KB,含通用教训(dry-run 守卫/真实数据验收/契约字段名静默失败…)与项目专属(sync-suite 死脚本、监理套件架构)。
执行:Step 1 脚本报 workspace 11KB > 5KB [WARN];Step 2 把通用教训判 GLOBAL、sync-suite 判 PROJECT;Step 4 上提通用教训为全局 #29–#47;Step 5 工作区压到 4.5KB(仅留项目专属 + 一行指针);结果两层均解除截断。
全局自管理(Step 5.5):若全局已 22KB,Mode B 拆出 lessons_core.md(#1–28) / lessons_extra.md(#29–53) / tech_rules.md 等,索引压到 4KB,细节按需 Read。
可运行命令(示例段配套):
# 改写任何 MEMORY.md 前先体检字符红线
python scripts/guarded_memory_write.py <ws>/.workbuddy/memory/MEMORY.md --check-only
# 编写/调整索引条目后核验指针存在性(缺失:无 且 RC=0 才收尾)
python scripts/check_index_refs.py --mem ~/.workbuddy/MEMORY.md --lessons ~/.workbuddy/lessons --skills ~/.workbuddy/skills
输出
- stdout(脚本):两份文件字节数/KB + 阈值对比
[WARN]/[OK];逐块分类GLOBAL/PROJECT/CHECK/STRUCTURAL+ 标题/正文命中数 +[DUP]标记;去重与 global 自管理建议。 - 写入产物:更新后的
~/.workbuddy/MEMORY.md、<workspace>/.workbuddy/memory/MEMORY.md、<workspace>/.workbuddy/memory/YYYY-MM-DD.md;必要时~/.workbuddy/lessons/*.md(Mode B)。 - 结构约定:索引文件只含"编号+一句话";细节在子文件;日志追加"记忆分层重构"段。
- 完成标准(CREATE-R 成功判据):① 工作区与全局 MEMORY.md 均
--check-only通过(未超字符红线);② 改写后check_index_refs.py结论缺失: 无且RC=0;③ 本次新增/调整的教训已落账(台账或当日日志),无悬空索引。三项全满足才算收尾。 - 自验证(self-check,与 Step 5.7/5.8 自证纪律同源):收尾前必跑 ①
guarded_memory_write.py --check-only(两记忆均未超红线)②check_index_refs.py(结论缺失: 无且RC=0)③ 本次处置已落账;任一不满足则不算收尾——"比上次小"或"脚本没报错"都不算通过。
参考
scripts/guarded_memory_write.py --selftest:硬闸自证(4 负样本 + 1 正样本 + 4 反向对照,共 9 例),改闸后必跑。scripts/check_index_refs.py --selftest:索引引用存在性自证(2 负样本 + 1 正样本 + 4 反向对照,共 7 例),编写新索引条目后必跑;查当前状态直接python scripts/check_index_refs.py。zfl-skill-quality-checker --check-memory:独立扫描用户级/工作区 MEMORY.md 是否超注入截断阈值(默认 8000 字符),与本技能要解决的内存超限问题同源,发布前可互验。- 全局记忆「记忆维护纪律」段:阈值与压缩四原则的权威约定。
- 全局记忆「跨项目技术教训」各段:上提目标结构与续编规则。
配置项(阈值可配置)
| 参数 | 默认值 | 含义 | 调大/调小的时机 |
|---|---|---|---|
--limit-chars |
3000(工作区)/ 4000(全局) | 注入字符红线(真红线),guarded_memory_write.py 的硬闸值 |
依据=系统注入提示原文的 chars/session 限制。不得抬高:抬高=把超限判成合规,正是 2026-09-17 静默破上限的成因 |
--limit-bytes |
5120 / 12288 字节 | 字节治理线(提醒,不阻断),沿用 scan_memory.py |
仅供趋势提醒;判超限以 chars 为准。两口径不可混用(纯 ASCII 时字节线偏松会漏报) |
--threshold |
5120 字节(~5KB) | workspace 截断阈值 | 有意保持 5KB,不随全局上调:工作区超标多为真膨胀(系统会明确要求清理),调高阈值=掩盖而非治理 |
--global-threshold |
12288 字节(12KB) | 全局截断阈值 | 2026-09-14 由 5KB 上调。放宽提醒线必须同步收紧动作——condense_global.py --apply 一并降级为「严禁」(PF-402:该脚本会回退读旧备份,apply=静默回滚记忆);阈值只是提醒线,不是安全线 |
--prune-days |
30 天 | 工作区日志轮转资格线(Step 5.0) | 系统记忆规则"超 30 天蒸馏后删";若项目节奏慢、旧日志仍常回看→上调(如 60/90);但永不为 0(活跃日志不动) |
--daily-threshold |
150 行 | 当日日志缺索引硬阈值(--guard 模式) |
活跃日常超 150 行且无索引→调高;小项目不活跃→调低 |
--distill-lines |
120 行 | 当日日志待蒸馏行数阈值(--guard) |
活跃日膨胀快→调低提前蒸馏;慢项目→调高 |
--distill-days |
30 天 | 当日日志待轮转天数阈值(--guard) |
同 --prune-days 治理节奏 |
--pf-max-entries |
400 条 | 坑点库 PF 条数阈值(--guard) |
2026-09-14 由 80 上调:拆库后主表仅存摘要、每条约 270 字节,旧值要求每条 <163 字节,只能砍成纯标题、丧失查重价值反促漏记 |
--pf-max-bytes |
153600 字节(150KB) | 坑点库字节阈值(--guard) |
同上;实测 376 条约 103KB |
| 注 | — | 阈值必须写明依据(PF-410:无校准的「经验值」会在几次转手后退化成假门禁——改了数值却没人发现门禁早已失效)。分三线:工作区治理线 5KB < 告警线 8KB(zfl-skill-quality-checker --check-memory 默认 --memory-warn 8000,见其源码 quality_checker.py:412)< 真实截断线 10–15KB;全局为 12KB 提醒线(2026-09-14 上调,只作提醒不作保证,超阈应人工精简)。三者目的不同(提前治理 vs 检测告警 vs 实测红线),不冲突。最终判据仍是「注入后完整可见」。 |
— |
反模式(勿做)
- 勿把工作区全部教训无脑复制到全局(污染全局、制造重复)。
- 勿在全局保留项目专属动态状态("已发布列表""某技能当前版本"属 workspace 动态记录)。
- 勿把"已落盘产物"和"一次性结论"升全局——它们已固化进技能/文档或仅某次会话有效,能读产物或重查即得,升格纯属噪音且制造重复(与上方「PROJECT 信号补充」同源)。
- 勿删
<30天日志 / 未蒸馏日志(append-only 为默认纪律;仅 Step 5.0 受控轮转例外:>30天 且 已蒸馏 + 先备份 + 显式确认)。 - 勿忽略全局自身截断:只上提不瘦身,等于把截断问题从 workspace 搬到 global。
- 勿把比上次小当作通过:压缩的验收是字符红线(
guarded_memory_write.py --check-only报 BLOCK 就是没达标),不是比之前少了几百字。 - 勿在压缩时静默丢锚点:改完必须做行级 diff 核对消失行(本次实证:
--note待办与守卫函数名被压掉,靠 diff 才抓回)。 - 勿在
find路径里写裸/(会扫整块磁盘)。 - 勿让活跃日志无顶部索引无限膨胀:Step 5.0 仅治 >30 天日志,活跃日 >150 行无索引会难定位、抬高回溯成本;须按 Step 5.1 补"当日主题索引"。
- 勿只靠手动触发忘兜底:记忆撑大是慢变量(本技能 OPEN 台账即为此设),光靠用户触发易漏;须用周期 automation 兜底(周度"工作区记忆蒸馏与防膨胀"扫 >120 行或 >30 天日志自动蒸馏 + 补索引;巡检 automation 加 lessons/pitfalls 阈值检测)。
关联
- 全局记忆维护纪律见用户级 MEMORY.md「记忆维护纪律」段。
- 与
self-improving-agent(通用学习捕获,默认禁用)互补:本技能专注 workspace↔全局的分层分流与截断防护(双向)。 - 版本变更历史见同目录
CHANGELOG.md,不在正文渲染。 - lessons/ 专项知识库治理:本技能把
~/.workbuddy/lessons/当作全局 Mode B 子文件落点,但 lessons/ 内还有pitfalls.md(坑点库)、tech_rules.md等原生结构化知识库,各自也会撑大,其防撑大形态独立于 workspace/global 两层:坑点库pitfalls.md已设 >400 条或 >150KB 时精简主表摘要列(拆库形态 2026-08-24 已完成、详情外置pitfalls/PF-XXX.md,勿再提示拆库)、顶部 PF 索引表(秒定位)、单 PF ≤12 行纪律。本技能治理时应将 lessons/ 专项库的防撑大纳入视野(周度巡检 automation 已加"坑点库阈值自动检测"),但不把详情搬进本技能(避免与已落盘产物重复——见反模式"勿把已落盘产物升全局")。 - automation 兜底(治理模式,v1.3.3 固化进脚本):记忆治理从"手动纪律"升级为"周期 automation 兜底"——①周度"工作区记忆蒸馏与防膨胀"automation:扫
.workbuddy/memory/当日日志,>120 行或>30 天的先读再蒸馏进MEMORY.md长期条目、确认已记录后删旧,并对活跃日>150 行无索引强制补索引;②现有周度"跨工作区知识同步巡检"扩一步:统计pitfalls.md的 PF 条数(按表格行^\| PF-计数——### [PF-是拆库前老格式,用它会让计数恒为 0、阈值永不触发,见 PF-410)与字节,超400 条/150KB提示精简主表摘要。慢变量靠定时任务兜底,比纯手动纪律稳。v1.3.3 固化:上述两处巡检逻辑已写进scripts/scan_memory.py --guard——python scripts/scan_memory.py --guard --workspace-dir <ws>一次跑出当日日志治理清单([WARN]/[DISTILL]/[ROTATE])+ 坑点库阈值检测([SPLIT]),automation / AI 直接读其输出执行,避免"prompt 文本描述"与"代码实现"双份维护漂移。
自检与成功标准
- 成功标准:输出满足任务声明的全部要求,关键信息完整无遗漏,视为完成。
- 输出前自检:核对输出是否覆盖全部要求项、数据是否准确、格式是否符合规范。