Back to skills
SKILL.md
Librarian
ASecurity记忆整理员:评估 notes.md 条目并按 D/U/R/A 提升到 MEMORY.md(含落点分层与同主题融合)、管理 MEMORY.md 容量候选、维护整理日志与 memory-index sidecar。用于 SessionStart 询问式开场卡用户选「处理」后(主通道)、`workframe-maintenance` 批处理工单的 notes 积压项、会话中出现 `memory_backlog` 维护信号时、MEMORY.md 接近容量上限需要评估降级候选时。典型触发:「整理一下记忆 / notes 太多了」。边界:只看记忆变动流水 → memory-log;纠正类条目由 correction-detection 直写高置信区,不经本 skill。
- 4 stars
- 0 votes
- 0 copies
- 3 views
- Added September 6, 2026
Security analysis
100/100Pro scans all 4 files and shows the line behind each finding
npx -y skills add ryanzhao1011/workframe --skill librarian --agent claude-codeAre you the author of Librarian?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/ryanzhao1011-librarian)---
name: librarian
description: '记忆整理员:评估 notes.md 条目并按 D/U/R/A 提升到 MEMORY.md(含落点分层与同主题融合)、管理 MEMORY.md 容量候选、维护整理日志与 memory-index sidecar。用于 SessionStart 询问式开场卡用户选「处理」后(主通道)、`workframe-maintenance` 批处理工单的 notes 积压项、会话中出现 `memory_backlog` 维护信号时、MEMORY.md 接近容量上限需要评估降级候选时。典型触发:「整理一下记忆 / notes 太多了」。边界:只看记忆变动流水 → memory-log;纠正类条目由 correction-detection 直写高置信区,不经本 skill。'
user-invocable: false
allowed-tools: [Read, Write, Edit, Glob, Grep, Bash, AskUserQuestion]
---
# Librarian 记忆整理技能
## 6 步整理流程
### 第 1 步:读取状态(动态角色发现 + shared 特殊角色)
**先处理 shared/(第 0 号"特殊角色")**:
```
Read .workframe/agent-memory/shared/MEMORY.md(跨角色权威事实;不存在则跳过)
Read .workframe/agent-memory/shared/notes.md(共享缓冲;不存在则跳过)
```
shared 层的整理规则与 role 层一致(D/U/R/A 评估、容量管理、快照保存),但写入条件更严:
- 提升到 `shared/MEMORY.md` 必须满足"影响 ≥2 个角色" + D/U/R/A ≥2 项(**主 Claude 不计入角色数**,理由见 2b 三问第 3 问)
- **单条压缩到 ≤200 字符**。shared 是**各 subagent 启动时全量注入**的——每加一句,此后每次委派都要付一次 token;判据是「这句删掉,另一个域的人会不会做错事」,答否就删。展开论证留在 `shared/notes.md` 或权威文档,MEMORY 只留结论
- **shared 变更在整理日志中单列**(与 role 层变更分开列,标明「本轮 shared 新增/修改 N 条」+ 逐条一行摘要),供用户事后复核。不逐条弹确认——已有「≥2 消费域 + D/U/R/A ≥2 + ≤200 字符」三道门槛,再加人工确认属于冗余打扰
- 冲突以 `shared/MEMORY.md` 为权威,role MEMORY 中的同类条目降级为 notes
**再动态发现项目中其他角色**:
```
用 Glob 扫描 .workframe/agent-memory/*/ 得到所有子目录,排除 shared
例如返回:[pm, dev, qa, prompt-eng](core 默认)
[pm, dev, qa, prompt-eng, ceo, designer, ...](项目级扩展)
```
对发现的每个角色 `<role>`,读取其记忆文件:
```
.workframe/agent-memory/<role>/MEMORY.md
.workframe/agent-memory/<role>/notes.md
```
统计每个 MEMORY.md 的当前字符数(预算:role ≤8000 字符 / shared ≤4000 字符——字符才反映注入开销,行数可被单行超长钻空)。
**为什么动态发现**:core plugin 提供 4 通用角色(pm/dev/qa/prompt-eng),项目可手动在 `.claude/agents/` 和 `.workframe/agent-memory/` 下新增项目级角色(如 ceo、designer、content-operator 等)。Librarian 必须覆盖所有角色,因此不能硬编码列表。
### 第 2 步:评估 notes.md + 决定落点
**2a. D/U/R/A 准入评估**
遍历每个角色的 notes.md 中的微反思条目,**跳过**下列两类已结案条目:
- 标注 `→ 已提升至 X` 的(本 SOP 自己提升过的)
- 标注 `→ 已被 {日期} 纠正取代` 的(`correction-detection` 第 3 步 supersede 时移进来的
历史留档)——它们是**被推翻的旧口径**,重新提升等于把用户纠正过的错误再送回高置信区
- **D**urability:30 天后还重要?
- **U**niqueness:目标落点中尚未记录?
- **R**etrievable:未来需要回忆?
- **A**uthority:来源可靠?
满足 ≥2 项 → 标记为"待提升"。
> **单人/小团队项目的判定口径**:用户当面拍板的偏好,Authority 已满分,**不要因为"是否对所有人普适"而压低评分**——这类知识库只服务当前用户,普适性不是准入条件。历史上大量高价值偏好因这条误判滞留在 notes(实测某项目 181 行 notes 里全是可执行规则,却因自设"需多次验证"门槛从未提升)。
**2b. 落点分层决策**
`MEMORY.md` 不是唯一落点。按知识性质选:
| 落点 | 判据 | 加载时机 | 预算 | 权限 |
|---|---|---|---|---|
| **机器闸**(断言 / 校验脚本 / hook) | 判定是**确定性**的、能从文件系统或代码现算出来 | 运行时触发 | 不占上下文 | **L2 用户审批**(代码变更) |
| **`context/both/`** | 每次会话必须遵守,**主会话与 subagent 都要** | 两个 hook 各注一次 | **每个字符付两遍**;实际上限由 sub 片决定 | **L2 用户审批** |
| **`context/main/`** | 每次会话必须遵守,**只有主会话要**(假定能问用户 / 能派发) | SessionStart | 主片余量 | **L2 用户审批** |
| **`context/sub/`** | 每次会话必须遵守,**只有 subagent 要** | SubagentStart | **余量最紧** | **L2 用户审批** |
| **`AGENTS.md`** | **项目自有**判据,两个 harness 都读 | Codex 原生 / CC 经桥接 | **无硬闸、但单位成本最高**:没有那种静默截断(项目可覆盖文档大小上限),**但 Codex 侧每个字符付两遍**(上下文里出现两次)。**别把「无硬上限」读成「免费」** | **L2 用户审批** |
| **项目 skill**(`.claude/skills/<name>/`) | **场景触发且成套**的操作方法——特定场景才需要、一到场景就要整套用(SOP / 口径集 / 模板 / 清单) | 场景命中按需加载 | 无硬上限(不占每会话预算) | **L2 用户审批**(core plugin skill 不直接改,走 self-iteration 提案) |
| **`<role>/MEMORY.md`** | 角色特有的高置信事实 | 该 subagent 被调度时 | 见 doctor 的 `memory_capacity` 检查 | L1 自主 |
| **`shared/MEMORY.md`** | 影响 ≥2 角色的权威事实 | 各 subagent 启动时 | 见 doctor 的 `memory_capacity` 检查 | L1 自主(写入条件更严);**agent / 主 Claude 不直写**——它们写 `shared/notes.md`,由本 skill 评估提升(对比上一行:role MEMORY 满足 D/U/R/A ≥2 可由角色直写,两行的「L1 自主」含义不同) |
| **auto-memory**(CC 官方主 Claude 记忆目录) | **主 Claude 层**内容:用户偏好 / 协作习惯 / 项目状态指针——消费者是主 Claude 而非某个角色 | 主会话启动(官方注入,subagent 不注入) | 见 doctor 的 `auto_memory` 检查 | 主 Claude 自维护;**librarian 不写**——识别到此类条目时在结果中建议归 auto-memory |
| **保留 notes** | 未定型 / 场景太具体 / 待验证 | 不加载 | — | — |
| **移出记忆体系** | 属**业务知识**(竞品分析、方案论证、领域事实)→ 应进需求文档 / spec,不是记忆 | — | — | L2 |
> **前三个 `context/*` 落点在「订阅方项目」里不可直接写。** `context/` 在插件内,而插件装在带版本号的缓存目录——订阅方项目对它**要么没有写权限,要么写进去后一次插件升级就静默消失**,后者更糟,因为它看起来成功了。
>
> **可选性判据必须是运行时可判的**,不能靠「你是不是框架维护者」这种自述:**判插件根是否可写**(第 0 步跑打包器时顺带取这一位)。插件根可写 → 五个落点都可选;只读 → **`context/*` 三个不可选**,`AGENTS.md` 与项目 skill 仍可选。
>
> **判到「该进 `context/`」但插件根只读时**:librarian **不写任何文件**,改为在**本项目**的 `promotion-candidates.md` 里落一条候选并标 `upstream: core`,摘要里写清「本条需上游框架采纳才能生效,本项目内无落点」。由用户决定是否向框架仓反馈。**不要写成「走 L2 提案」就完事**——L2 提案在本项目里审批,而变更要落在另一个仓,审批通过也改不了任何东西。
**先问一次(与下面三问并行,不是第四问)**:这条能不能变成**机器闸**(断言 / 校验 / hook)?能 → **优先落闸**——纪律靠模型每次记得住才生效,闸不用。**然后再走三问**决定纪律文本放哪:闸抓违例、纪律说该怎么做,两者互补不互斥,落了闸不等于不用写。
**四问定落点**(顺序二元判断,逐问淘汰):
1. **每次会话都必须遵守吗?** 是 → 进必载层,**转问 1b**
1b. **谁要遵守?**(本问是必载层内部的分流,退役前只有一个必载落点、没有这一问)
- 主会话与 subagent 都要 → `context/both/`(**注意双倍代价**)
- 只有主会话要 → `context/main/`
- 只有 subagent 要 → `context/sub/`
- 这是**项目自有**判据而非框架纪律 → `AGENTS.md`
2. 否——**特定场景触发、且是成套方法吗?**(≥3 条同主题相关做法,或含步骤/模板/清单结构)是 → 项目 skill。两形态:已有相关 skill → **增补该 skill**;没有 → 同主题攒满 3 条前**留 notes**,攒满后聚类新建(PRD 风格类沉淀直接增补项目 prd-style;先例:requirement-archiving 由实战沉淀)
3. 否——**一条就能说清的离散事实** → 先判**谁消费**:主 Claude(用户偏好 / 协作习惯 / 项目状态指针)→ 建议归 auto-memory(主 Claude 自维护,librarian 不代写);单个角色 → `<role>/MEMORY.md`;跨 ≥2 角色 → `shared/MEMORY.md`
> **主 Claude 不参与「≥2 角色」的计数**。它在 main-led 模式下可能直做任何域的活,恒为潜在读者——把它算进去,每条工种知识都自动凑够两个消费者,`shared/` 会被稀释成第二个大杂烩,而 shared 是**各 subagent 启动时全量注入**的,稀释的代价由每次委派承担。<br>计数只数**角色域**:这条工种知识,除了它自己那个域,还有哪个**角色**未来在具体决策/操作时要读它?答不出第二个 → 单角色域。<br>(主 Claude 的读取路径由主会话注入片里的直做纪律与 SessionStart 记忆地图保障,不靠挤进 shared。)
**第 0 步「先算预算」**(**只在判定要进必载层之后跑**,不是每条都跑——否则本 skill 变慢会被跳过):
- 跑 `inject-context.py --scope <target> --dry-run` 拿当前片的实际字符数与余量。**唯一事实源是打包器输出**,不是任何文档里写死的数
- **余量 < 待写内容 + 200 字符缓冲 → 不许直接写**:先做一轮「哪些已内化成默认行为、可以精简」的复盘。提升在必载层是**零和**的——装不下就得先挤掉别的
- 复盘挤不出空间 → 该条目**降级落点**(转第 2 问看能否进 skill),或列为 L2 候选让用户裁「挤掉哪一条」
- `both/` 写 N 个字符,主片与 sub 片各付 N;**`both/` 的实际预算上限由 sub 片决定,不由 main 片决定**
- **落点是 `AGENTS.md` 时打包器管不到它**,第 0 步改查两件:① 该项目文档大小上限的实际设置(默认值会截断,覆盖过才没有硬闸);② 按**两倍**计入 Codex 侧上下文账。**不查就写 = 把最贵的那个落点当成免费的**
**关键判断**:必载层是**注入**资产——放这里的知识对模型直接生效,可靠性最高,但每次会话都消耗 token,且**有硬上限**;skill 行是它的**制度化泄压阀**——场景性成套内容不进必载层、不占每会话 token。`MEMORY.md` 由 SubagentStart hook 注入对应 subagent(代码保证,仅角色被调度时加载)。**定型的、跨场景的纪律往必载层搬(先过第 0 步的余量);场景性的成套方法往 skill 搬;未定型的留 notes。**
L2 落点的提升**不自动执行**:把候选清单写入 `.workframe/state/promotion-candidates.md`(覆盖式重写,头部带生成时间;每条一行,固定格式 `- [ ] <scope> | <YYYY-MM-DD> | <一句话摘要> | 出处: <notes 条目> | 建议落点: <文件+章节>`——`- [ ]` 前缀是 maintenance_workorder.py 计数依赖,勿改),然后按调用场景分流:
- **常规调用 / `/core:maintenance-review`**:用 AskUserQuestion 当场请用户确认;用户跳过时清单保留在该文件,下次运行时先读它恢复候选再继续。用户确认后执行写入并把对应行改为 `- [x]`。
- **询问式触发(SessionStart 开场卡)/ `--maintenance` 批处理**:**不当场出卡**——只写入候选文件攒卡,在结果摘要里提示用户「有 N 条 L2 候选待拍板」。开场卡场景保持轻量(用户是来干正事的),批处理场景是 print 模式(没有交互卡可用)。
### 第 2.5 步:融合整理 SOP(写入前必须执行)
> **禁止无脑 append。**往任何目标文档写入前,按顺序执行以下检查。这是本 skill 最容易被跳过、也最影响质量的一步——直接追加会让目标文档不断膨胀、同义内容重复、口径互相矛盾。
> 写入侧已前置:correction-detection / auto-update 的写入路径按本 SOP 第 1-4 步做**写时**同主题检查;本步在批处理/提升场景兜底复查漏网。
1. **先读目标文档全文**——不能只读插入位置附近。不了解全貌就无法判断归属与重复
2. **定位归属章节**——找语义最匹配的现有章节;只有确认现有章节都不合适时才新建,并说明理由
3. **查重合并**——目标位置已有同义/近义内容时,**改写合并成一条**,不是追加第二条。合并时保留信息量更大的表述,补充缺失的细节
4. **查冲突**——新内容与现有内容矛盾时**停止写入**,向用户报告冲突点,不自行裁决谁对
5. **压缩表述**——notes 里的场景描述与论证**不搬**(留原处作为出处),只搬可执行规则;粒度跟随目标文档现有风格(一条一句 vs 一条一段)
6. **格式对齐**——列表 / 表格 / 标题层级跟随目标文档现有约定
7. **标注出处**——写明「YYYY-MM-DD 从 `<role>` notes 提升」,便于回溯原始论证。**跨 scope 搬迁**(auto-memory → 角色域,或改域)写「YYYY-MM-DD 从 `<来源>` 迁移」并 append `memory_migrated` 事件——两种标注**不可混用**:三账本 check A/A' 按字样分别要求 `memory_promoted` / `memory_migrated`,写错字样等于报错了另一种事件
8. **体积预算**——写入必载层前走 2b 的**第 0 步**拿打包器现算的余量,**不再用「约 150 行」这种软阈值**(它没有强制力,也与实际上限无关);余量不够先做一轮"哪些已内化成默认行为、可以精简"的复盘,复盘时场景性成套内容优先迁往项目 skill(按 2b 四问定落点,泄压不丢内容)
9. **notes 侧处理(归档制)**——提升/处理完成后,把该条目**整段移动**到同目录 `notes-archive.md`(追加到文件尾,条目标题后加标注 `→ 已提升至 <目标文件>(YYYY-MM-DD)` 或 `→ 用户拍板不提升(YYYY-MM-DD)`),并从 notes.md 删除该段。
- **为什么移动而不是原地标注**:notes.md 的语义是「待处理缓冲区」,`memory_backlog` 积压信号按它的内容量判定——已处理条目留在原地会让缓冲区只增不减、信号永不消解(与 M6 修复的 cadence 堆积同构)。archive 保留完整论证作为出处,不参与积压统计与后续评估
- **混合段允许条目级拆分**:一个日期段里部分子条目已处理、部分未处理时,只移已处理的子条目,段内留一行注记 `(本段部分条目已处理,见 notes-archive.md 同日条目)`
- `notes-archive.md` 首次创建时写文件头:`# <role> notes 归档区(已处理条目)` + 一行说明
- 移动 = 删除 + 写入,但内容零删减,归类为 **L1**(不需要 L2 审批)
10. **跨 scope 顺带检测**——上面几步已把目标文档全文读了一遍,这是**唯一天然的跨域视野**,顺手做一次不额外读文件的检查:本轮处理的条目,是否与**其他域**已有条目讲同一件事?
- 命中 → 出 **shared 候选**(进 `promotion-candidates.md`,走 L2 由用户拍板),**不自行搬**
- 判据仍是「另一个域未来在什么决策/操作时要读它」——**两个域碰巧都提过某个工具不算**,工具重叠不构成消费证据
- 这是**兜底**通道不是主通道:shared 候选的主发现路径是各角色收尾时的自反问(`agent-protocols` Step 2)。本步只捞那些当时没想到、事后从全文里才看出来的
- 检测不到不算失职——**宁可漏报也不要凑数**:shared 每多一条,此后每次 subagent 启动都要付一次 token
**自检**:写完后回读目标文档被修改的整个章节,确认 ① 没有同义重复 ② 没有前后矛盾 ③ 新旧内容的粒度和口吻一致。
### 第 3 步:容量管理 + memory-index sidecar 维护 + 量化衰减
**前置动作:快照保存(L1 变更前必须执行)**
执行任何 MEMORY.md 变更前,先将当前版本快照到:
```
logs/librarian-snapshots/{YYYY-MM-DD}/{HH-mm-ss}-{role}-MEMORY.md
```
文件名带秒级时间前缀(`HH-mm-ss`),避免同分钟内多次运行覆盖。每个角色最多保留 5 个快照(按时间排序),超出时删除最旧的快照。此快照机制确保 L1 变更可回滚。shared/ 也生成快照:`logs/librarian-snapshots/{YYYY-MM-DD}/{HH-mm-ss}-shared-MEMORY.md`。
**同时**为受影响的 sidecar entries 生成同目录快照(确保 rollback 时 MEMORY.md 与 memory-index.json 一致):
```
logs/librarian-snapshots/{YYYY-MM-DD}/{HH-mm-ss}-{role}-memory-index-entries.json
```
内容是从 `.workframe/state/memory-index.json` 中筛出 `scope=<role>`(或 `scope=shared` 处理 shared 角色时)的所有 entry 子集,格式:
```json
{"<entry-key>": {"scope":"...","created_at":"...","provenance":"...",...}}
```
若本次 librarian 运行未触及任何 sidecar entry(仅做 changelog 补漏 / metrics 重算),跳过 sidecar 快照。
**sidecar 维护**(`.workframe/state/memory-index.json`):
若 `memory-index.json` 不存在,先按 `templates/memory-index-template.json` 的最小结构初始化:
```json
{"__schema__":"workframe.memory-index.v2","entries":{}}
```
每条 MEMORY.md 写入/删除时同步维护 sidecar。**MEMORY.md 保持人类可读**,元数据放 sidecar:
```json
{
"entries": {
"shared:2026-04-10:发版前必须跑脱敏终检": {
"scope": "shared", // 实际角色名(pm/dev/…)或 shared——填具体值,不照抄占位符
"created_at": "2026-04-10",
"provenance": "user-decree", // 来源类型四选一,见下表;不打分、不写数字
"protected": true, // [纠正] 条目自动 true
"source": "[纠正]" // 枚举见下表;与 memory_promoted 事件的 source 同口径
}
}
}
```
**provenance 来源类型(归类判据——只归类,不打分,禁止写数字)**:
| 值 | 判据 | 赋值方 |
|---|---|---|
| `user-decree` | 用户明确纠正/拍板(`[纠正]` 条目) | correction-detection 固定写入,librarian 不产 |
| `user-confirmed` | 用户当面确认过 / 亲测实证 | 提升默认档 |
| `inferred` | 推断、单次观察,未经用户确认 | librarian 归类 |
| `external` | 外部转述、时效性强的信息(竞品动态 / 行业消息 / 他人说法) | librarian 归类 |
归类只需回答两个二元问题:①用户确认过吗?是 → `user-confirmed`;②否的话,信息源在项目外且有时效性吗?是 → `external`,否 → `inferred`。
**source 来源枚举**(与 `memory_promoted` / `memory_migrated` 事件的 `source` 字段同一套;doctor 的 `sidecar_health` 按此校验,写枚举外的值会被报出来):
| 值 | 含义 |
|---|---|
| `[纠正]` | correction-detection 写入的用户纠正条目 |
| `notes.md` | 从本域 notes.md 提升 |
| `shared/notes.md` | 从 shared 缓冲区提升 |
| `auto-memory` / `<role>/MEMORY.md` / `shared/MEMORY.md` | 跨 scope 搬迁的来源域 |
| `manual backfill` | 人工补录 |
| `librarian-promoted` | **历史值,只读不新写**——早期由 librarian 提升的条目用它。它答的是「谁做的」而非「从哪来」,与其余值不同轴;存量条目保持原样不回改,新写入一律用上面几种 |
Key 规则:`{scope}:{YYYY-MM-DD}:{条目前 20 字规范化}`。规范化三条**缺一不可**(与 skill `signal-intake` §5 同源,否则两边为同一条目算出不同 key、sidecar 出现重复项):**先删除全部空白字符**(空格 / 制表符 / 换行)**→ 再取前 20 字**(Unicode 字符数,非字节)→ 同日同前 20 字碰撞时追加序号后缀(`-2`、`-3`…)。
**顺序不可颠倒**(2026-08-16 统一,同 skill `signal-intake` §5):颠倒会让同一条目在两处算出不同 key。改序前的历史 key 不回改。
**事件写入(供 `/core:memory-log` 展示历史流水)**:
1. notes.md → MEMORY.md 提升成功后,先写 MEMORY.md 和 sidecar entry,再 append `.workframe/state/events.jsonl`:
```json
{"ts":"<ISO-8601>","type":"memory_promoted","scope":"<角色名或shared,填实际值>","role":"<role-if-any>","entry_key":"<memory-index-key>","summary":"<条目摘要/原文前80字>","source":"notes.md","protected":<sidecar.protected>,"provenance":"<sidecar.provenance>"}
```
落点为 skill / rule 文件时(L2 经用户批准后执行):**不写 sidecar**(sidecar 只索引 MEMORY 条目),事件仍写但**省略 entry_key**,落点文件路径写进 summary——与 correction-detection 第 4 步同规格。
2. MEMORY.md → notes.md 降级或容量腾挪经用户确认后,**必须在删除 sidecar entry 前** append `.workframe/state/events.jsonl`,把即将删除的元数据快照写入事件:
```json
{"ts":"<ISO-8601>","type":"memory_decayed","scope":"<角色名或shared,填实际值>","role":"<role-if-any>","entry_key":"<memory-index-key>","summary":"<降级条目摘要/原文前80字>","source":"<sidecar.source>","age_days":<按 created_at 现算的整数>,"provenance":"<sidecar.provenance>"}
```
3. **跨 scope 搬迁**(auto-memory → role/shared,或域判错后改域)——先在目标域写好条目与 sidecar entry,再 append `.workframe/state/events.jsonl`:
```json
{"ts":"<ISO-8601>","type":"memory_migrated","scope":"<迁入的目标域:角色名或shared>","role":"<role-if-any>","entry_key":"<目标域新建的 memory-index-key>","summary":"<条目摘要/原文前80字>","source":"<迁出方:auto-memory 或 <role>/MEMORY.md>","from_ref":"<源文件名或条目标识>","protected":<sidecar.protected>,"provenance":"<原样承接,不重新判定>"}
```
**与 `memory_promoted` 的分工**:提升是**同一 scope 内** notes → MEMORY(过 D/U/R/A);搬迁是**已合格条目跨 scope 移动**(不重判准入)。混用会污染 notes 陈化信号——消费方靠 `memory_promoted` 的时间戳判"这个域多久没消化 notes 了",而搬迁并没有消化任何 notes。
`provenance` **原样承接不重新判定**:搬迁不是新的信息来源,把 `user-decree` 在搬家途中降成 `inferred` 会让该条目失去衰减豁免。
**三个日期各有出处,别混**:
| 位置 | 取值 | 理由 |
|---|---|---|
| MEMORY 条目行首 | **经验发生日**(原条目的日期,搬迁不改) | 它回答「这条知识是什么时候的」,与搬家无关 |
| 出处标注 `(YYYY-MM-DD 从 … 迁移)` | **迁移日** | 它回答「什么时候搬的」 |
| sidecar `created_at` 与 `entry_key` 里的日期 | **优先承接原创建日**;源头查不到(如 auto-memory 只有 `modified`)时退用迁移日 | 衰减时钟按 `created_at` 走——重置它等于让一条两年前的老知识搬个家就「变年轻」,与 `provenance` 原样承接同理 |
两种取值三账本都认(`check C` 同时收行首日期与迁移日),但**同一条目内三处必须自洽**:`entry_key` 的日期段要与 `created_at` 相同。
`summary` 是历史快照字段,memory-log 优先展示事件里的 summary;不要依赖当前 sidecar 仍能查到已降级条目。
**量化衰减**(基于 sidecar 的 provenance 来源类型 + age 判断——归类不打分):
- `protected = false` 且 `provenance = external` → 生成**待审查降级候选**(外部转述/时效性信息,不论年龄;不自动执行——在下次 `/core:maintenance-review` 或维护批处理时由用户确认后执行;执行时按上面的时序先 append `memory_decayed`,再删除 sidecar entry)
- `protected = false` 且 `provenance = inferred` 且 `(today - created_at) > 180 天` → 同上生成待审查降级候选(长期未被重申的未确认推断)
- `user-confirmed` / `user-decree` 条目**永不自动进候选**——用户确认过的事实不因年龄过期,要变只能被新的确认/纠正 supersede
- **dormant 项目**:所有衰减动作**和** notes→MEMORY promotion **均冻结**(读 `activity-state.json` 的 `dormant` 字段,true 则本步全部跳过;`wake_up_pending=true` 时同等处理)。除非用户通过 `/core:maintenance-review` 明确确认,否则不改 MEMORY / notes
**容量管理流程**:
如果 MEMORY.md 已接近字符预算(role 8000 / shared 4000):
1. 识别低价值条目(从 sidecar 衰减信号识别:provenance=external / provenance=inferred 且 age>180 天 / protected=false 且非 [纠正])
2. 生成**容量整理候选**,写入本次 Librarian 日志和 `/core:maintenance-review` 待用户确认
3. 用户未确认前,不移动 MEMORY 条目、不删除 sidecar entry、不为了腾空间写入新条目
4. 用户确认后,才将候选条目移至 notes.md 底部(标注"已从 MEMORY.md 降级,source=<原 source>"),同步删除 sidecar entry,再写入待提升条目
**绝对禁止清理的内容**:
- 带 `[纠正]` 标记的条目 — **永不清理**(sidecar `protected=true`)
- 用户明确要求保留的条目(`protected=true`)
### 第 4 步:日志补漏
检查 `projects/changelog.md`(如存在)是否有缺失的记录:
- 对比 MEMORY.md / notes.md 的最近写入与 changelog 条目
- 补齐缺失的 changelog 记录
- 若项目未维护 changelog.md,跳过此步
### 第 5 步:刷新 `skill-metrics.yaml`(调用 deterministic 脚本)
**默认路径**:SessionEnd hook 会调用 `recompute_skill_metrics.py` 自动重算 `.workframe/state/skill-metrics.yaml`。如果本次 Librarian 需要立即刷新,调用插件内兜底命令(插件根路径从 `plugin-root.txt` 取,不依赖 PATH):
```bash
python "$(cat .workframe/state/plugin-root.txt)/bin/workframe-recompute-skill-metrics"
```
`plugin-root.txt` 由 SessionStart hook 每次会话刷新为当前插件根。若环境里 `workframe-recompute-skill-metrics` 恰好已在 PATH(CC plugin `bin/` 注入生效的环境),裸调等价。
**脚本职责**:
1. 读取 `.workframe/state/events.jsonl`(逐行 JSON 解析;跳过 `__schema__` 描述行和 malformed 行)
2. 按默认窗口 30 天过滤(`generated_at - ts ≤ 30d`),也可由调用方指定
3. 分类聚合:
- `skills[<name>]`: 按 `type=skill_used` 汇总 `invocations`(计数)、`successes`(`success=true` 计数)、`last_used`(最新 ts 的日期)
- `rules`: **整段空对象**——`recompute_skill_metrics.py` 直接写 `rules: {}`(`rule_triggered` 无 deterministic producer,不生成占位 entry;保留 `rules` 顶层 key 仅为 schema 结构兼容)。下游(check-iteration-trigger.py)不再消费此字段;见 `.workframe-meta/event-schema.json` 中 `reliability: removed_v0_2_1`
- `corrections_count`: `type=user_correction` 事件总数
- `blocks_count`: `type=task_blocked` 事件总数
- `proposal_failures_count`: `type=proposal_verified` 且 `signal_met=false` 的事件总数
4. 整个重写 `.workframe/state/skill-metrics.yaml`(不是 append),更新 `generated_at` 和 `window_days`
5. 不写入任何 skill/rule frontmatter — frontmatter 不再承担统计职责(Phase 0 已清理非官方字段 `usage_count` / `last_used` / `trigger_count`)
> 为什么 L1:脚本重算是 deterministic 的派生文件操作,不改动用户可读资产(MEMORY.md / notes.md / rules / skills 本体),失败影响面仅限 skill-metrics.yaml 自身,旧版本可随时从 events.jsonl 重新生成。
**下游消费者**:
- `self-iteration` 日常决策读 `skill-metrics.yaml`(无需解析全量 events)
- 审计/追因需要时读 `events.jsonl`
- `/core:audit` 等维护命令汇总展示
### 第 6 步:关闭积压信号 + 写整理日志
**关闭 `memory_backlog` 信号**(本次整理覆盖了触发该信号的 notes 时):
1. Read `.workframe/state/activity-state.json`,找 `pending_maintenance` 中
`kind == "memory_backlog"` 且 `status == "open"` 的条目,记下它们的 `id`
2. 用代码通道关闭(**不要自己改这个文件**)——它一并写 `pending_maintenance_dismissed` 事件:
```bash
python "$(cat .workframe/state/plugin-root.txt)/scripts/maintenance_workorder.py" \
--close-pm <PM-ID> [<PM-ID> ...]
```
> **为什么不自己写这份文件**:`activity-state.json` 同时装着 `session_counter` /
> `recent_drift_repairs` / 注入标记等一点点攒出来的状态。整份重写时漏掉任何一个都不会
> 报错,只会静默丢掉一段历史;而上面那个命令走文件锁 + 原子替换 + 三方合并,只动
> `pending_maintenance` 一处。同理,`--commit` 路径的记账也一律由代码提交。
> 归档制(第 2.5 步第 9 条)保证已处理条目移出 notes.md——若整理后 notes 仍超阈值(还有大量未处理条目),下次 Stop hook 会重新产生信号,这是**正确行为**,不是重复报警。
在 `logs/librarian/{YYYY-MM}/{YYYY-MM-DD}.md` 追加本次 Librarian 运行摘要:
```
## {日期} Librarian 整理
- 覆盖角色:{第 1 步发现的角色列表}
- 评估 notes.md 条目:{n} 条(跳过已标注已提升:{n} 条)
- 提升到 MEMORY.md:{n} 条
- 提升到 AGENTS.md / 项目 skill(L2,已确认):{n} 条;待确认清单:{n} 条
- 融合方式统计:新增章节 {n} / 合并进现有条目 {n} / 因冲突暂停 {n}
- 从 MEMORY.md 降级:{n} 条(仅统计用户已确认执行的降级;候选另列)
- 待审查降级/容量候选:{n} 条
- 快照保存:{n} 个角色
- skill-metrics.yaml 重算:window_days={N},events 条数={n},统计了 {m} 个 skill / {k} 个 rule
```
重大结构变更(如清理过期条目)同步追加到 `projects/changelog.md`(如维护)。
## 变更权限
- **L1 变更(自主执行)**:非冲突 notes.md → `MEMORY.md` / `shared/MEMORY.md` 条目提升、已处理条目整段移入同目录 `notes-archive.md`(归档制,见第 2.5 步第 9 条——**不是**原地加标注,那是被它取代的旧做法)、changelog 补漏、快照保存、`skill-metrics.yaml` 脚本重算
- **L1 候选(须用户在 `/core:maintenance-review` 确认后执行)**:MEMORY.md → notes.md 降级、容量不足时的腾挪整理
- **L2 变更(须用户审批)**:删除任何内容、**提升/增补到框架的注入源 `context/**`、`AGENTS.md` 或项目 skill(`.claude/skills/**`、`.agents/skills/**`)**、把业务知识移出记忆体系到需求文档、修改 `board.yaml` 任务结构、改 core plugin 内源文件(含 core skill——走 self-iteration 提案)
> L2 落点(`AGENTS.md` / 注入源 / skills)是主 Claude 直接消费的资产(前两者每会话必载、skill 场景加载),误写影响面大,因此**一律先出清单等用户确认**,不自主执行。清单形式:每条给「一句话摘要 + 出处 + 建议落点 + 建议插入的章节」,让用户一次过目即可决策。
Files in this skill
- SKILL.md
- eval-cases/01-promote-durable-fact.md
- eval-cases/02-preserve-correction.md
- eval-cases/03-regress-dormant-freeze.md
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.