# Session Housekeeping

> 自动审计并清理 Hermes 桌面端会话：基于三条不可约减事实驱动，每次跑完学习用户的审批偏好，迭代收敛规则，降低后续干预率。

- Skill: `yyyyyhhhhh0639/session-housekeeping` (Agent Skill, multi-file: 7 files)
- Install (CLI): `npx skillmds@latest add yyyyyhhhhh0639/session-housekeeping`
- Raw SKILL.md: https://api.skillmd.com/api/skills/yyyyyhhhhh0639/session-housekeeping/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: yyyyyhhhhh0639 (https://skillmd.com/u/yyyyyhhhhh0639)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/yyyyyhhhhh0639/session-housekeeping

---


# Session Housekeeping v2

## ⚠️ 前置检查：用户可能想要的不是 housekeeping，而是分组

**在启动 housekeeping 流程前，必须先做这件事：**

当用户表达整理会话、归类会话、分会话组、按主题分组等意图时：

### 步骤 1：确定用户想要的分组类型

| 用户意图 | 适用机制 | 说明 |
|---------|---------|------|
| 按代码仓库/文件夹归组 | ✅ Projects（路径前缀匹配） | 会话 cwd 需不同 |
| 按研究主题/概念分类 | ❌ Projects 不适用 → 用标题前缀重命名 | 同 cwd 时无法区分 |
| 清理无用的旧会话 | ✅ housekeeping | 走 L1-L3 全流程 |

### 步骤 2：如果用户要的是按主题分组，走标题前缀路线

**核心事实（用户纠正过此问题）：** Projects 的侧边栏分组靠 session.cwd 与 project_folders.path 的路径前缀匹配。当所有会话的 cwd 相同（如均为 C:\Users\<user>）时，Projects 无法按主题区分会话——无论创建多少个 Project，都会 0 条归入。

⚠️ **但是**：Tier 1（显式项目）**始终显示在侧边栏**，即使 0 条会话。所以创建无 folder 或路径不匹配的 Project 后，侧边栏 Projects 视图中**确实会出现项目条目**，只是展开后显示 "0 会话"。这不是"没生效"，而是"生效了但无匹配会话"。

**验证时必须检查两个层面：**
1. DB 层面：`projects.db` 有记录 ✓（我做的）
2. UI 层面：侧边栏是否在 Projects 模式？用户是否能看到项目卡片？→ **必须在桌面端 UI 中确认**，因为工具操作不会自动切换侧边栏模式

**正确做法：**
```bash
# 将会话标题加上 [主题] 前缀，在 Recents 视图达到视觉分组
hermes sessions rename <id1> "[Claude Code] 四种模式对比"
hermes sessions rename <id2> "[Pi Agent] 入门研究"
```

### 步骤 3：如果用户要的是按仓库/文件夹归组，走 Projects 路线

Hermes Desktop 侧边栏已有 Projects（项目/工作区）作为内置会话分组机制。

关键源码（project_tree.py L360-368）：
```python
class _FolderIndex:
    def match(self, target: str):
        segs = _segments(target or "")
        for end in range(len(segs), 0, -1):  # 最长前缀优先
            hit = self._by_path.get("/".join(segs[:end]))
            if hit:
                return hit
        return None, -1
```

**操作方式：**
```bash
# 创建项目——必须传 path，否则 folders 为空，无法匹配任何会话
hermes project create "My Project" C:\path\to\repo --use
```

**UI 创建路径（关键——仅 CLI 操作无法让 UI 感知）：**
```
step 1: 侧边栏顶部标题行末尾 ☰/📋 图标 → 切换到 Projects 分组视图
                          ↓
step 2: [+] 按钮弹出 Create Project 对话框
                          ↓
step 3: 输入名称 → 点 "Add folder" → 原生目录选择器选文件夹 → Create
```

**关键注意事项（用户纠正记录）：**
1. `project_create(name=...)` 不传 path 创建的项目**会出现在侧边栏**（Tier 1 始终显示）但 0 条归入 — 这不是"无效"，而是"路径不匹配"
2. 通过 tool 创建 Project 后，**前端不会自动切换到 Projects 分组模式** — 侧边栏仍在 Sessions 扁平列表模式，所以用户看不到变化。必须告知用户手动点击切换图标
3. 不能只检查 `project_list()` 或 `projects.db` 就声称"已生效" — **必须在桌面端 UI 中验证最终呈现**
4. 所有会话 cwd 相同时，路径前缀匹配**无法区分**不同主题——这不是"还能补救"，而是**机制层面的限制**

**验证方式（用户纠正过这一点的优先级）：**
- ✅ 检查 projects.db 和 project_list()（底层验证）
- **✅ 必须告知用户：侧边栏需要切换到 Projects 视图才能看到**（UI 层面验证的关键点）
- ✅ 检查 project_folders 表是否有记录
- ✅ 检查 path 与会话 cwd 的路径前缀匹配是否能命中

---

## 📊 会话取证分析

当需要深度分析某个会话的结构、行为模式、工具使用、子代理派遣模式时，加载 `references/session-forensic-analysis.md`。该参考文件包含：

- 获取会话完整数据的两种途径（`session_search` + Studio API）
- 超大 JSON 输出的分阶段 Python 解析策略
- 时间线构建、阶段划分、工具统计、delegate_task 详情提取的脚本模板
- 输出报告模板

触发词：`会话分析`、`session analysis`、`session forensics`、`时间线`、`timeline`、`会话结构`

---

## ⚠️ 术语映射——housekeeping 用词必须对齐 UI/CLI 可执行指令

housekeeping 报告中的操作建议必须使用以下术语，不要使用用户界面不存在的词汇：

| 报告中用词 | 用户操作方式 | 数据库变化 | 说明 |
|-----------|-------------|-----------|------|
| **删除** | `hermes sessions delete {id}` 或 TUI 按钮 | 记录永久消失 | 向用户报告时标注"需你手动执行"或"Agent 自动执行（已备份）" |
| **归档** | TUI 侧边栏"归档"按钮 | `archived=1` | **只报告，不自动操作**。用户有能力自己做。housekeeping 做归档会导致用户困惑。 |
| **标记为已结束** | ❌ 无用户操作方式，仅 Agent 能执行 | `ended_at=now()` | 报告时标注"Agent 自动执行（SQL 操作，无需你按任何按钮）" |
| **保留** | 什么都不做 | 不变 | 保持活跃列表中可见 |

**原则**：housekeeping 报告中永远不要出现"关掉"、"关闭"等没有对应 UI 按钮的词汇。你(Agent)能做的操作（设置 ended_at）称为"标记为已结束"。用户能做的操作（删除、归档）用用户实际可见的术语。

## 核心理念（三个不可约减事实）

| 事实 | 内容 | 推导 |
|------|------|------|
| **F1** | 人类注意力是稀缺的单线程资源 | 活跃会话数 ≤ N（N 初值 3，通过 L3 学习用户并行承受力浮动到 2-6） |
| **F2** | 信息检索价值与存储量成反比 | 无标题会话 = 0 容忍，删或命名 |
| **F3** | 知识有最优持久化层级 | **创建前**：已有同类会话 → 提醒用户继续而非新建。**清理时**：同类主题出现 ≥ 2 次且未沉淀 → 提示检查 Memory/Skill |

## 四层架构

| 层 | 功能 | 触发时机 |
|----|------|----------|
| **L0 创建前预防** | 新建会话前检查重复主题，拦截同类会话扩散 | 用户表达新问题时（可选的，L0 不阻塞正常使用） |
| **L1 操作流程** | 审计→分类→报告→执行→确认 | 每次 housekeeping 运行 |
| **L2 置信度分级** | 每条判断标注 HIGH/MEDIUM/LOW，匹配最佳交互深度 | 每次 housekeeping 运行 |
| **L3 学习回路** | 记录用户决策 → 更新偏好 → 降低下次干预 | 每次 housekeeping 运行 |

---

# L0：创建前预防——在新建会话前拦截重复主题（F3 前置检查）

**触发条件**：用户表达了一个新的问题/任务/目标，且可能在 Hermes 中新建会话来处理它。

## Step L0-1：提取用户新问题的主题关键词

从用户的最新输入中提取核心概念（2-5 个实词关键词）。排除停用词和通用对话锚点。

## Step L0-2：查询活跃会话的主题匹配

```bash
# 对每条有标题的活跃 TUI 会话，检查标题中是否包含 L0-1 中提取的关键词
# 模拟逻辑：
for keyword in $KEYWORDS; do
  sqlite3 /f/AI/Hermes/hermes/state.db "
    SELECT id, title, 
           (SELECT max(timestamp) FROM messages WHERE session_id=s.id) AS last_ts
    FROM sessions s
    WHERE ended_at IS NULL AND source='tui' AND title != '(无标题)'
      AND title LIKE '%$keyword%'
    ORDER BY last_ts DESC;
  "
done
done

> 为什么按 last_ts DESC 排序？ Hermes 默认列表按 started_at DESC（创建时间）排序，旧会话即使刚用过也会被新创建的压在底下。这是用户明有旧会话却新建的根本原因。L0 必须按最后活跃时间排序，否则匹配的会话可能在列表底部，用户仍看不到。

匹配规则：
- 标题包含任一关键词 → 弱匹配（候选）
- 标题包含 ≥ 2 个不同关键词 → 强匹配（高置信度推荐）
- 标题关键词相同（如"Claude Code"出现 2 次）→ 强匹配（属压缩链，附加说明）

## Step L0-3：如果找到匹配，提示用户

```
📎 发现已有活跃会话讨论相关主题：
   {id1} — "{title1}" ← 最后活动 {N} 小时前
   {id2} — "{title2}" ← 最后活动 {N} 小时前
   是否选择其中一条继续？(Y/id/n)
```

如果用户选择 `Y` 或 `id`：
- 执行 `/resume {id}` 或引导用户点击该会话
- 记录到 L3 偏好中

如果用户选择 `n`：
- 允许新建，但记录到 L3 的 topic_level.reported_topics

## Step L0-4：L3 记录

无论用户选择"继续旧会话"还是"新建"，记入 `references/preferences.json` 的 `topic_level.reported_topics`：

```json
{
  "topic_level": {
    "reported_topics": [
      {"query_keywords": ["Hermes", "桌面端", "会话"], "match": "Hermes桌面端继续对话方式", "user_choice": "continue"}
    ]
  }
}
```

---

**L0 与 L1-L3 的定位关系**：

```
housekeeping 完整生命周期（时间从左到右）：

  用户有想法 → L0（创建前预防）→ 用户使用会话 → L1（清理）
                    ↓                              ↓
              拦截重复主题                      清理遗留垃圾
                                               L2（分级）
                                               L3（学习）
                                                    ↓
                                          L0 下次更精准
```

L0 是 housekeeping 的心跳——在熵增之前逆熵。L1 是清理已发生的熵增。两者不可互相替代。

---

# L1：操作流程

## Step 0：确认范围

询问用户操作范围。
默认：全量审计 + 计划展示 + 用户确认后执行。

### 获取当前会话 ID

在执行任何操作前，必须先确定当前会话 ID，否则无法判断"当前会话"：

```bash
# 方式 A（推荐）：从 Hermes CLI 获取
hermes sessions list --limit 1 | grep -oP '\b\d{8}_\d{6}_[a-f0-9]{6,8}\b'

# 方式 B：从 state.db 按活跃 + 最后活跃时间推断（如果不清楚当前进程）
sqlite3 /f/AI/Hermes/hermes/state.db "
SELECT id FROM sessions 
WHERE ended_at IS NULL AND source='tui'
ORDER BY started_at DESC LIMIT 1;
"
```

获取到的 ID 存入变量 `$CURRENT_SESSION_ID`，所有需要排除当前会话的规则使用这个值。

### 加载历史偏好

保留历史回忆：通过 `scope_recall_context(query="session housekeeping preferences")` 或 `scope_recall_search(query="housekeeping-preference")` 加载之前跑 housekeeping 时记录的偏好，用于 L2 置信度判断。

## Step 1：读取当前状态

```bash
cd /f/AI/Hermes/hermes
sqlite3 state.db -header -column "
SELECT 
  s.id,
  COALESCE(s.title, '(无标题)') AS title,
  s.message_count,
  CASE WHEN s.ended_at IS NULL THEN 'active' ELSE 'ended' END AS status,
  COALESCE(s.end_reason, '') AS end_reason,
  s.source,
  s.archived,
  strftime('%Y-%m-%d', datetime(s.started_at, 'unixepoch')) AS start_date,
  COALESCE(
    (SELECT strftime('%Y-%m-%d %H:%M', datetime(m.timestamp, 'unixepoch'))
     FROM messages m 
     WHERE m.session_id = s.id 
     ORDER BY m.id DESC LIMIT 1),
    'unknown'
  ) AS last_active_time
FROM sessions s
ORDER BY 
  CASE WHEN s.ended_at IS NULL THEN 0 ELSE 1 END,
  s.started_at DESC;
"
```

## Step 2：分类每条会话——带置信度的决策矩阵

**规则匹配顺序**：规则 S（安全护栏）→ 规则 F（用户偏好覆盖）→ 规则 A → 规则 B → 规则 C → 规则 D → 规则 E

- 规则 S 和 F **永远优先于** A-E——先排除"不可动"和"已确认"的条目
- 每条会话命中第一条规则即停止匹配（优先级从高到低）
- 每条判断标注置信度，如果 L3 历史中有覆盖值，优先使用

### 规则 S：安全护栏（最高优先级——永不违反）
| 条件 | 行为 |
|------|------|
| 当前会话（Step 0 中获取的 $CURRENT_SESSION_ID） | **永不建议操作** |
| 有标题 + 活跃 + 非子代理 | **永远只报告，不自动执行** |
| 任何删除操作 | 执行前先 `hermes sessions export` 备份，备份失败则中止流程 |

### 规则 F：用户偏好覆盖（高于规则 A-E）
| 条件 | 操作 |
|------|------|
| 某条会话在上次 housekeeping 中被用户明确"保留"且写入 scope_recall | 跳过，不再建议任何操作 |
| 某条会话在上次 housekeeping 中被用户明确"删除"且写入 scope_recall | HIGH 置信度，自动包含在执行计划 |
| 用户曾对规则 X 的某条推荐做出过否决 | 该条规则在本轮降级：HIGH→MEDIUM，MEDIUM→LOW，LOW→询问时附"上次否决过同类"说明 |
| 用户对同一类判断给出过矛盾的选择（先保留后删除，或反之） | 该条规则降为 LOW，停止自动执行，直到用户再次明确指示 |

### 规则 A：子代理空壳
| 条件 | 置信度 | 操作 |
|------|--------|------|
| `source=subagent AND status=ended AND message_count ≤ 4` | **HIGH** | 自动删除，直接执行 |
| `source=subagent AND status=ended AND title='(无标题)' AND message_count > 4` | **HIGH** | 自动删除，直接执行 |

理由：`ended` 是客观事实，无标题 + 低消息量 → 无法被 session_search 检索到任何有意义内容。F2 推导。

### 规则 B：结束会话且无标题
| 条件 | 置信度 | 操作 |
|------|--------|------|
| `status=ended AND title='(无标题)' AND message_count ≤ 10` | **HIGH** | 自动删除，直接执行 |
| `status=ended AND title='(无标题)' AND message_count > 10` | **MEDIUM** | 列入计划，标注"建议删除"，说清原因 |

理由：有内容但无标题 → 用户从未命名它。**注意 F2 的"0 容忍"在此被弱化为 MEDIUM**，因为 >10 条消息的内容可能被 session_search 的内容字段命中。如果用户的审批记录中有"保留了有内容的无标题会话"的先例，维持 MEDIUM；如果从未保留，升级为 HIGH。

### 规则 C：活跃主子代理（subagent + active）
| 条件 | 置信度 | 操作 |
|------|--------|------|
| `source=subagent AND status=active AND title='(无标题)'` | **MEDIUM** | 列入计划，标注"建议删除或命名"，询问用户 |
| `source=subagent AND status=active AND title!='(无标题)'` | **LOW** | 列在报告中，但不主动建议操作 |

理由：活跃但无名的子代理 = 父会话可能已结束但残留。但无法判断父会话是否还在用。

注：`subagent + ended + title!='(无标题)'` 未被任何规则覆盖。这种情况极罕见，如果出现，报告但不操作。

### 规则 D：活跃会话降噪（F1 检查）
| 条件 | 置信度 | 操作 |
|------|--------|------|
| `活跃总数 > N` AND `该会话不是当前会话` AND `该会话有标题` AND `该会话距今 > 3 天无最后消息` | **LOW → MEDIUM（取决于之前用户的选择）** | 每个标注"建议标记为已结束"，附带最后活跃时间证据 |
| `活跃总数 > N` AND `该会话不是当前会话` AND `该会话 archived=1` | **MEDIUM** | 标注"建议标记为已结束（已归档、仍显示活跃）" |

**重要**：不再猜标题判断"完结"。改用**最后活跃时间**（来自 `messages` 表的 `timestamp`）。

**阈值 N 的确定**：默认 3（F1 推导）。如果 L3 历史中用户对"活跃超标"警告的忽略/否决次数 > 2，每忽略一次 N+=1，每否决一条 N+=1，上限 6。这反映用户的实际并行承受力。

### 规则 E：F3 检查（同类主题重复）
| 条件 | 置信度 | 操作 |
|------|--------|------|
| 活跃会话标题的**TF 关键词**出现 ≥ 2 次（如"Claude Code"出现 3 次） | **MEDIUM** | 在报告中标注"同类主题出现 N 次：是否已沉淀？" |
| 用户在上一次 housekeeping 中已确认某主题已沉淀 | **HIGH** | 跳过提示 |

**TF 关键词判定方法**：取活跃会话标题，按空格/标点/CJK 分词后，统计去除停用词的实词词频。出现 ≥ 2 次的词标记为"重复主题"。不依赖 session_search 的 FTS5（中文 FTS5 不可靠），直接用标题字符串做简单分词即可。具体操作：

```bash
sqlite3 /f/AI/Hermes/hermes/state.db "
SELECT title FROM sessions WHERE ended_at IS NULL;
" | tr ' ' '\n' | sort | uniq -c | sort -rn | head -10
```

## Step 3：生成带置信度的审计报告

```
═══════════════════════════════════════════
  会话审计报告 — {date}
═══════════════════════════════════════════

总记录: N 条
  活跃: N 条（当前: 1）
  已结束: N 条

=== 自动执行（HIGH 置信度，N 条）===
  ✓ {id} — 子代理空壳 → 删除
  ✓ {id} — 已结束无标题子代理 → 删除

=== 建议操作（MEDIUM 置信度，N 条）===
  ? {id} — 活跃无名子代理 → 删除 or 命名？
  ? {id} — 已结束无标题 ({n}条消息) → 建议删除？
  ? {id} — F3: "某主题"出现 2 次，是否已沉淀？

=== 仅供参考（LOW 置信度，N 条）===
  · {id} — {title} 最后活跃 {n} 天前，是否标记为已结束？

=== 保留（不操作，N 条）===
  · {id} — 当前会话
  · {id} — 用户偏好保留（上次确认过）

═══════════════════════════════════════════
  清理后预期: 活跃 N→?  总 N→?
═══════════════════════════════════════════
```

## Step 4：用户确认

- **HIGH 组**：不需要逐一确认，直接告知"将自动执行以下 N 项"
- **MEDIUM 组**：按**规则类别**批量展示（不要逐条单独问）。格式：
  ```
  规则 B（结束无标题会话，>10条消息）建议删除 3 条：
    ? id1 — {title?} 建议删除？
    ? id2 — {title?} 建议删除？
    ? id3 — {title?} 建议删除？
  全部同意？(Y/n) 或逐条回复 id1=n, id2=y ...
  ```
- **LOW 组**：按类别询问（"这 5 条长期未使用的会话都标记为已结束？"）
- 用户可以在**一条消息中**对多组做出决定

如果用户批量回复"全部同意"或"全部否"，直接跳转到 Step 5。

## Step 5：执行

```bash
# 1. 备份——如果失败则中止
hermes sessions export ~/housekeeping-backup-$(date +%Y%m%d_%H%M%S).jsonl
if [ $? -ne 0 ]; then echo "备份失败，中止"; exit 1; fi

# 2. 删除已确认的会话
sqlite3 /f/AI/Hermes/hermes/state.db "
DELETE FROM messages WHERE session_id IN ({id列表});
DELETE FROM sessions WHERE id IN ({id列表});
"

# 3. 标记为已结束（Agent SQL 操作，用户无感知）
sqlite3 /f/AI/Hermes/hermes/state.db "
UPDATE sessions SET ended_at=julianday('now'), end_reason='housekeeping' WHERE id IN ({id列表});
"

# 4. 归档（可选）
sqlite3 /f/AI/Hermes/hermes/state.db "
UPDATE sessions SET archived=1 WHERE id IN ({id列表});
"
```

## Step 6：执行后确认

重新运行 Step 1，展示清理后的状态，并逐一标注验证结果：

```diff
- [ ] 活跃 ≤ N（N=当前阈值，初始 3）
- [ ] 无无名会话（0 容忍）
- [ ] 备份文件路径确认
+ [✅] 活跃: 21→2 ✓（阈值 3）
+ [✅] 无无名会话: 0 条 ✓
+ [✅] 备份: housekeeping-backup-20260627.jsonl (1.2MB)
```

---

# L2：置信度分级机制

置信度是连接"操作清单"和"自动化"的桥梁。

| 级别 | 含义 | 对用户 | 如何升级 |
|------|------|--------|----------|
| **HIGH** | 这条判断不会错 | 自动执行，告知即可 | 用户从未否决过同类判断 + 累计运行 ≥ 2 次 |
| **MEDIUM** | 大概率正确，有例外可能 | 按规则类别批量询问，但给出推荐 | 用户累计批准 ≥ 2 次同类推荐 → 升 HIGH |
| **LOW** | 缺乏数据或高度依赖用户场景 | 作为开放式问题 | 用户给出明确选择 → 记入规则 → 下次升 MEDIUM |

---

# L3：学习回路（核心——可收敛的自改进系统）

## 数据存储方案

**三层存储，各司其职**：

| 存储层 | 内容 | 读写方式 | 用途 |
|--------|------|----------|------|
| `references/preferences.json` | 结构化偏好（规则计数、已沉淀主题、用户保留/删除列表） | `read_file` / `write_file` | **主要**偏好存储，程序可读，不依赖 SKILL.md 文本匹配 |
| `scope_recall_store`（memory target） | 本次运行的摘要记录（做了什么操作、用户改了什么） | `scope_recall_*` | 跨会话回忆，供代理在执行时参考历史上下文 |
| `SKILL.md` | 仅在"规则新增"时需要 patch | `skill_manage(action='patch')` | 极端情况下新增规则 |

**L3 不直接 patch SKILL.md 作为偏好学习的主要手段**。偏好写进 `references/preferences.json`，下一次 L3 直接读取该文件即可获得当前规则状态。

## 每次 housekeeping 运行结束后必须执行以下步骤：

### L3-1：读取当前偏好状态

```python
# 伪代码逻辑
prefs = read_file("skill:session-housekeeping/references/preferences.json")
# 解析 JSON 获取当前规则计数、保留/删除列表、已沉淀主题
```

### L3-2：记录用户本次审批结果

对每条会话的最终决定，记录两处：

**（A）写进 `references/preferences.json`（结构化数据）**

```json
{
  // session_level: 按会话 ID 记录
  "session_level": {
    "retain": [{"id": "...", "title": "...", "date": "2026-06-27"}],
    "delete": [{"id": "...", "date": "2026-06-27"}],
    "rename": {"session_id": "new title"}
  },
  // rule_level: 按规则记录计数
  "rule_level": {
    "rules": {
      "A": {
        "HIGH_count": 7,    // 被分配为 HIGH 且用户批准
        "override_retain_count": 0,  // 用户否决了删除、要求保留
        "override_delete_count": 1   // 用户要求删除但推荐是保留
      }
    }
  }
}
```

**（B）写入 `scope_recall_store`（跨会话回忆用途）**

将本次操作的摘要写入 scope_recall：

```python
scope_recall_store(
    target="memory",
    content="housekeeping: run #{n} completed. deleted {x} sessions, closed {y} active, retained {z}. user overrode rule {rule} on {id}.",
    entities=["session-housekeeping", "housekeeping-preference"],
    tags=["housekeeping"]
)
```

不需要为每条会话写一条——一次运行写一条摘要即可。

### L3-3：处理规则冲突

**规则 A 和 B（HIGH 置信度删除）被用户否决**：
→ 在 `preferences.json` 的 `session_level.retain` 中加入该会话 ID
→ 相关规则的 `override_retain_count += 1`
→ 如果 `override_retain_count >= 2`，该规则降级：下次不标记为 HIGH，改为 MEDIUM 并附"用户曾保留过同类"

**MEDIUM 推荐被用户连续同意（累计，不要求连续运行）**：
→ 相关规则的 `HIGH_count += 1`
→ 累计 >= 2 次（不是 3 次）→ L3 自动将对应条件升级为 HIGH：
  - 将 SKILL.md 中该条件的置信度从 MEDIUM 改为 HIGH
  - 同时更新 `preferences.json` 中的规则级别
  - 下次运行不再需要确认

**用户给出矛盾选择（同一规则第一轮批了、第二轮否了）**：
→ 写入 `preferences.json` 的 `feedback_contradictions` 条目
→ 如果同一个规则 + 条件出现 >= 2 次矛盾 → 该规则锁定为 LOW，**停止自动学习**，直到用户明确指示"信任此规则"

**阈值 N 的调整（规则 D 的活跃数阈值）**：
→ 每次用户手动忽略"活跃超标"警告，`active_threshold += 1`
→ 每次用户否定了关闭某会话的建议（理由不是"还在用"而是"就想留着"），`active_threshold += 0.5`
→ 上限 6，下限 2

### L3-4：持久化和汇报

```python
# 将更新后的 preferences.json 写回 skill
write_file("skill:session-housekeeping/references/preferences.json", json.dumps(prefs, indent=2, ensure_ascii=False))
```

**汇报给用户**（紧凑格式，不超过 3 行）：

```
📝 本次学习：规则 B 升级 HIGH（你批了 2 次同类推荐）。
  偏好记录：保留"B站搜索咨询"、新增"Claude Code 四种模式"已沉淀。
  当前规则状态：A=HIGH(7/0) B=HIGH(2/0) C=MEDIUM(1/0) D=MEDIUM(阈3)
```

---

## 触发方式

### 方式 A：自然语言

```
"管理会话" / "清理会话" / "跑 housekeeping"
```
当前 Hermes 持有关注 trigger 中的 user_intent。

### 方式 B：加载 Skill 后

```bash
/skill session-housekeeping
```
然后说："跑一下"

### 方式 C：Cron 定时任务（可选，建议 1-2 周一次）

```bash
cronjob(
  action='create',
  name='weekly-housekeeping',
  schedule='0 9 * * 1',
  prompt='Run session housekeeping on the Hermes desktop sessions.
          FIRST: load references/preferences.json to check for any 
          user overrides or contradictions on HIGH-confidence rules.
          
          EXECUTE: delete only items matching rule A (subagent shells)
          that have NOT been overridden by user preferences.
          
          DO NOT EXECUTE: any MEDIUM or LOW items automatically.
          REPORT them and leave them untouched — do not ask for confirmation
          because no user is present to respond.
          
          DO NOT touch current session or any active titled non-subagent session.',
  skills=['session-housekeeping'],
  deliver='origin'
)
```

**安全说明**：Cron 模式只执行规则 A（子代理空壳删除）且只删未被规则 F 覆盖的。所有 MEDIUM/LOW 项目仅报告、不自动执行、不询问（因为无人在线）。

## 运行前检查清单

- [ ] 是否加载了用户的历史 housekeeping 偏好（scope_recall_context）
- [ ] 是否确认了当前会话 ID（避免误删）
- [ ] HIGH 置信度规则是否有用户否决历史（若有，降级）
- [ ] 距离上次运行是否积累了足够的新决定（至少 1-2 条可学习）

