- Backfill `created` frontmatter on 266 active notes (git date or mtime) - Normalize `status` to 4 values: draft/active/done/archived (11 notes) - Active/进行中/needs-review → active - archive → archived - 待执行/conditional → draft - 完成/accepted → done - Declare clipper boundary: 04_Archive/Inbox-Clippings/** exempt from migration - Update depth rule: max 3 → max 4 levels (new notes only) - Add metadata-converge.mjs script for reproducibility
21 KiB
created, updated, type, status, tags
| created | updated | type | status | tags | |||
|---|---|---|---|---|---|---|---|
| 2026-09-25 | 2026-09-26 | plan | active |
|
重构方案 2026-09-25
完成后删除本文件。 不再往
06_Metadata/里加计划文档 —— 见「五、文档瘦身」。 依据:Compass 对照评审 + 本机实测(2026-09-25)。
定义完成(Definition of Done)
每一条都能一条命令验证。达不到就不算完成。
| # | 条件 | 验证命令 | 期望 |
|---|---|---|---|
| 1 | 没有「已忽略但被跟踪」的文件 | git ls-files -z | git check-ignore -z --no-index --stdin | tr '\0' '\n' | wc -l |
0 |
| 2 | 高置信密钥零命中 | node .scripts/verify-vault.mjs |
退出码 0 |
| 3 | 根目录只剩 PARA + 支持目录 | ls -1 |
非 dotfiles 只有 00–06、契约三件套、package.json、一个 lockfile(点文件用 ls -A 对照清单抽查) |
| 4 | 契约只有一份 | git ls-files | grep -c 'CLAUDE\.md$' |
1 |
| 5 | 元数据收敛 | 活笔记(00–03,排除 00_Inbox/Clippings/)frontmatter |
每篇含 created;status ∈ 4 值;内容性键(title/source/date/author/published/description/aliases)不删 |
| 6 | 06_Metadata 瘦身 |
git ls-files '06_Metadata/**' | wc -l |
≤ 14 |
| 7 | 深度基线守恒 | git -c core.quotePath=false ls-files '*.md' | grep -v '^04_Archive/' | awk -F/ 'NF>=6' | wc -l |
≤ 40(基线 40,2026-09-26 实测;≤4 层新规则只约束新笔记,存量不压平) |
零、诊断
这个仓库同时是四样东西,抢一个根目录:
| 身份 | 证据 | 现状 |
|---|---|---|
| A 知识库 | PARA,704 篇 md | 主体,但 90 天只提交 27 次 |
| B 代码仓库 | .scripts/memory/(pgvector + uv + schema.sql)、specs/、initdb/、compose.yml、两个 lockfile |
最后动于 2026-04-13 |
| C 工作运行手册 | 01_Projects/Work/(含雇主凭据),最后动于 2026-09-23 |
最活跃的目录 |
| D Obsidian 分发 | .obsidian/ 108 个文件、39 MB 插件 JS |
对一个私人仓库无意义 |
真正的病:文档代替动作
06_Metadata/ 42 个文件里,有 4 份工作流文档(WORKFLOWS / WORKFLOW_SIMPLIFICATION_ANALYSIS / WORKFLOW_TRANSITION_GUIDE / ULTRA_SIMPLE_WORKFLOW)、2 份评审报告、IMPROVEMENT_PLAN、FIX_TASK_BREAKDOWN、REFACTOR_SUMMARY。
二月写了评审 → 写了改进计划 → 写了任务拆解 → 写了重构总结 —— 密钥还在 git 里。政策也被写了三遍:.claude/project-instructions.md 和 .claude/memory/instructions/safety.md 几乎逐字重复,AGENTS.md 是另一套(JS 代码规范)。
所以本方案的产出形式是清单和脚本,不是又一份散文。执行完删掉本文件。
一处更正
上一轮我说「定时备份会把取消跟踪的文件重新加回来」—— 这是错的。实测:
crontab -l为空,无 systemd timer- obsidian-git
autoSaveInterval: 0、autoPushInterval: 0、autoBackupAfterFileChange: false git add .和 obsidian-git 都遵守.gitignore
所以 git rm --cached + ignore 规则是永久有效的。文件在 git 里,唯一原因是它们在 ignore 规则写下来之前就被提交过,之后没人取消跟踪。
好消息:止血比我说的一小时更便宜,而且不需要改任何自动化。
自动化里唯一真实存在的是 web clipper:00_Inbox/Clippings/ 里已经有 2025 和 2026 两代新 clippings,说明它按 YYYY/MM/ 往 inbox 写。这个要改。
阶段 0 · 轮换密钥(只有你能做)
按「攻击者能拿到什么」排序,不按文件类型。
| 顺序 | 目标 | 在哪 | 时间 |
|---|---|---|---|
| 1 | Vaultwarden admin token + SMTP 密码 | 01_Projects/Infrastructure/Services/vaultwarden.md |
20 min |
| 2 | AWS access key、GitHub PAT、GitLab token | 03_Resources/Community/Matrix Server.md、01_Projects/Personal-Tech/Development/Github.md、03_Resources/Development/DevOps/Gitlab/rest api.md |
30 min |
| 3 | 工作/政府项目运行手册密码 | 01_Projects/Work/Government-Projects/(Virtual-Data-Center/prod.md、Municipal-Development-Reform/production.md+deploy.md、Industry-Info/Deployment.md) |
60 min + 通报决定 |
| 4 | OpenRouter + DeepSeek | 商家后台 | 5 min |
| 5 | Local REST API key + 证书 | 插件设置里重新生成 | 10 min |
第 1 条优先,因为它是密码管理器本身,守着其他所有条目。第 3 条带一个非技术问题:要不要向雇主通报,先决定再动。
轮换完再确认:06_Metadata/SECURITY_ROTATION_LOG_2026-02.md 里"notes 侧已完成"那行与事实不符,改成实际状态,勾掉历史清理的复选框(它们本来就该是未勾选的)。
阶段 1 · 止血:让泄漏无法复发
事实
75 个文件被跟踪,同时又被 .gitignore 覆盖:
| 数量 | 类别 |
|---|---|
| 26 | .obsidian/plugins/*/manifest.json |
| 17 | .obsidian/plugins/*/data.json |
| 14 | .claude/sessions/*.jsonl |
| 6 | .scripts/memory/__pycache__/*.pyc |
| 6 | .memory-changes-*.txt |
| 6 | .obsidian/{types,graph,daily-notes,core-plugins,community-plugins,backlink}.json |
另有 3 个路径根本没被任何规则覆盖:
.qmd/index.sqlite-wal
.qmd/index.sqlite-shm
.memory-index.lock
步骤
-
标签存档点(回滚用)
git tag pre-restructure-2026-09-25 && git push origin pre-restructure-2026-09-25 -
补 3 条 ignore 规则 →
.qmd/、.memory-index.lock、*.sqlite-wal、*.sqlite-shm -
摘掉全部 75 + 3 个文件
git ls-files -z | git check-ignore -z --no-index --stdin | xargs -0 git rm --cached git rm --cached .qmd/index.sqlite-wal .qmd/index.sqlite-shm .memory-index.lock--cached只动索引,工作区文件保留。 -
删掉文件名黑名单式 ignore 现在
.gitignore里有**/*password*.md、**/*secret*.md、**/*token*.md。这批密钥所在的文件叫prod.md、deployment.md、Matrix.md—— 黑名单一条都没拦住,还误伤正常笔记。改用内容扫描,见第 5 步。 -
写
.scripts/verify-vault.mjs(仓库脚本目录是.scripts/,根目录无scripts/),接进 CI,与 lint 并列// .scripts/verify-vault.mjs — 只报路径与行号,绝不打印匹配到的内容 import { execSync } from 'node:child_process' import { readFileSync } from 'node:fs' const PATTERNS = [ ['openrouter', /sk-or-v1-[A-Za-z0-9]{32,}/], ['openai', /sk-[A-Za-z0-9]{32,}/], ['aws', /AKIA[0-9A-Z]{16}/], ['github-pat', /gh[pousr]_[A-Za-z0-9]{36,}/], ['gitlab-pat', /glpat-[A-Za-z0-9_-]{20,}/], ['private-key', /-----BEGIN [A-Z ]*PRIVATE KEY-----/], ['slack', /xox[baprs]-[A-Za-z0-9-]{10,}/], ] // 路径限定规则:.obsidian/** 下 64 位 hex 即报 —— 覆盖 local-rest-api // 那类无前缀 apiKey(本次泄漏类型之一,通用模式抓不到) let fail = 0 // 1. 已忽略但被跟踪(-z 变体,免疫 quotePath 对非 ASCII 路径的转义) const trackedIgnored = execSync( 'git ls-files -z | git check-ignore -z --no-index --stdin', { encoding: 'utf8' }, ).split('\0').filter(Boolean) if (trackedIgnored.length) { fail = 1 console.error(`✗ ${trackedIgnored.length} 个已忽略文件仍被跟踪:`) trackedIgnored.forEach((f) => console.error(` ${f}`)) } // 2. 内容扫描 —— 只扫 git 跟踪的文件(git ls-files),不扫工作区 // 未跟踪文件:否则摘除索引后仍在磁盘上的 data.json(真密钥) // 会让「退出码 0」永远达不到 const files = execSync('git ls-files "*.md" "*.json" "*.yml" "*.yaml"', { encoding: 'utf8', }).trim().split('\n').filter(Boolean) for (const f of files) { let body try { body = readFileSync(f, 'utf8') } catch { continue } for (const [name, re] of PATTERNS) { const m = re.exec(body) if (m) { fail = 1 const line = body.slice(0, m.index).split('\n').length console.error(`✗ ${name} 命中 ${f}:${line}`) } } } console.log(fail ? 'verifier 失败' : '✓ verifier 通过') process.exit(fail) -
改
.github/workflows/lint.yml,加一步node .scripts/verify-vault.mjs(在 lint 之前) -
改 clipper 输出目录/删→ 已移出(审核 2026-09-26,止血只管 git 密钥):clipper 目录 → 阶段 2;FIRST_RUN/SessionStart → 阶段 5FIRST_RUN+ SessionStart hook
时间:我 40 分钟,你验证 10 分钟。
阶段 2 · 分区:清空根目录
目标根目录
00_Inbox/ 01_Projects/ 02_Areas/ 03_Resources/
04_Archive/ 05_Attachments/ 06_Metadata/
AGENTS.md CLAUDE.md README.md # 契约三件套
.obsidian/ .claude/ .github/ .config/ .scripts/ .codex/ .zcode/
.gitignore .prettierignore # 点文件按现状保留
package.json pnpm-lock.yaml # 只留一个 lockfile
迁移表
| 现在 | 处置 | 理由 |
|---|---|---|
.scripts/memory/(pgvector 服务) |
先判定死活(见下),死则删,活则迁出仓库 | 最后动于 2026-04-13 |
compose.yml、initdb/ |
随 .scripts/memory/ 一起走 |
是同一套服务 |
specs/001-opencode-client/ |
删或迁出 | 一个规格,最后动于 2026-01-31 |
memory_eval/ |
删 | 与 .qmd/.scripts/memory 三者重复 |
package-lock.json 和 pnpm-lock.yaml |
删 package-lock.json |
二选一 |
未命名.md、未命名 1.md、Untitled Kanban.md |
删(空文件) | 根目录散件 |
install.sh、LICENSE |
删(人工确认) | 上游 claudesidian 残留("Claudesidian Setup Script";MIT © Noah Brier / Alephic),与阶段 5 去残留一致 |
b02685b5772985d358ab7092c07d5058.png |
删或移 05_Attachments/ |
无意义文件名 |
chinese-to-english-mapping.csv |
移 .scripts/ |
是脚本的输入数据 |
| web clipper 输出目录(浏览器扩展设置,非仓库文件) | 改到 04_Archive/Inbox-Clippings/,修 2024/11/2023/ 自嵌套;剪一篇测试文章验证落盘 04_Archive/Inbox-Clippings/YYYY/MM/ |
从阶段 1 移入(审核 2026-09-26) |
.failed_images.txt、.upgrade-checklist.md |
删 | 临时文件 |
.zcode/、.codex/ |
留在根(点目录) | 工具配置 |
判定 .scripts/memory 死活
crontab -l 2>/dev/null | grep -i memory
ls -la .memory-changes-*.txt | tail -1
grep -rn "install-hook" ~/.claude/settings.json .claude/settings.json 2>/dev/null
最后一份 .memory-changes-* 是 2026-04 的、crontab 为空 → 死代码,直接删。
如果还挂在某个 hook 上 → 迁到独立仓库,别留在笔记库里。
时间:我 30 分钟,你确认删除清单 15 分钟。
阶段 3 · 元数据收敛
关键发现:不是三套 schema,是一套 + 一个被隔离的导入格式
352 篇有 frontmatter(50.0%)里,174 篇在 04_Archive,全是 clippings。
| 键 | 篇数 | 实际归属 |
|---|---|---|
date / page-title / url |
192 / 178 / 178 | clipper,几乎全在 04_Archive |
created / tags / type / status / updated |
129 / 128 / 91 / 67 / 54 | 活笔记,已经是一套自洽的五键 |
title / modified |
87 / 5 | 混合 / 基本没用 |
03_Resources 121 个文件里只有 10 篇有 frontmatter —— 活笔记的元数据比想象中干净得多。
步骤
- 宣布边界,不是迁移:
04_Archive/Inbox-Clippings/**与00_Inbox/Clippings/**永久使用 clipper schema(date/page-title/url),永不迁移 —— 04_Archive 之外00_Inbox/Clippings/还有 10 篇 2026 年新剪藏,边界必须同时覆盖两处。一步砍掉一半"混乱"。 - 活笔记补
created(机检口径:PARA 00–03 排除00_Inbox/Clippings/,共 367 篇,101 篇已有,266 篇待补):是补齐不是重写;不新增其他必备键,不删内容性键(title/description/source/date/author/published/aliases是内容事实)。脚本先 dry-run 输出 diff,可回滚。 status收敛到 4 值:draft/active/done/archived。 现状有 14 种:active(51)、archive(4),外加review、done、needs-review、accepted、conditional、reading、evergreen、draft、Active、待执行、进行中、完成各一。写成脚本映射,可回滚。- 修深度违规:
.claude/memory/instructions/organization.md白纸黑字写着「Flat structure over deep nesting (max 3 levels)」,实际 247 个文件在 5 层、75 个在 7 层。要么改规则到 4 层,要么压平 clippings。选改规则,只约束新笔记 —— 200 篇 clippings 压平不值得;活跃区现存 40 个 ≥6 层文件(LLM_Evaluation 引用树、Work ref)保留不动,属「704 篇不迁移」,验收按基线 40 守恒。 模板去重→ 移入阶段 4(审核 2026-09-26,与 06_Metadata 瘦身是同一批文件操作,归一处)。
时间:写脚本 1 小时 + 脚本跑 5 分钟,可回滚。
阶段 4 · 文档瘦身
06_Metadata 42 → ≤14。
| 保留 | 数量 |
|---|---|
Templates/:Daily Note Template、Project Template、quick-note、book-note(均被 .obsidian 接线)+ meeting、person(待接线,人工确认是否还要) |
6 |
Reference/:PARA_METHOD.md、GIT_WORKFLOW.md(重写)、TROUBLESHOOTING.md |
3 |
SECURITY_ROTATION_LOG_2026-02.md(改到与实际一致) |
1 |
| 本文件(完成即删) | 1 |
归档到 04_Archive/ 或删除 |
理由 |
|---|---|
daily-note.md、project.md → 并入被接线的 Daily Note Template.md / Project Template.md 后删 |
重复对,接线的那份留 |
Research Note Template.md |
未接线未用,删(人工确认) |
4 份工作流文档 → 合 1 份进 AGENTS.md |
互不一致 |
2 份 REVIEW_REPORT_* |
历史记录,进 04_Archive |
IMPROVEMENT_PLAN、FIX_TASK_BREAKDOWN、REFACTOR_SUMMARY |
已执行或已作废 |
CHANGELOG、CONTRIBUTING、CLAUDE-BOOTSTRAP、claudian-phase1-complete、claudian-token-optimization-testing |
上游模板残留 |
CLAUDE.md.archived-2026-01-07 |
归档文件本身该归档 |
Hermes * Template.md(3 份) |
未在使用 |
DATAVIEW_VALIDATION、WORKFLOW_SIMPLIFICATION_ANALYSIS、WORKFLOW_TRANSITION_GUIDE、ULTRA_SIMPLE_WORKFLOW、INBOX_TRIAGE |
一次性过程文档 |
模板去重(从阶段 3 移入):保留被 .obsidian 接线的那份 —— daily-notes.json 接线 Daily Note Template.md,插件接线 Project Template.md/quick-note.md/book-note.md;删任何模板前先查 daily-notes.json / plugins/*/data.json。
时间:我 45 分钟,你逐条确认删除。
阶段 5 · 一份 agent 契约
事实
AGENTS.md是 TypeScript/Python 代码规范(2026-01-06)- 15 个
CLAUDE.md,包括.obsidian/plugins/claudian/和specs/.../contracts/里的 .claude/project-instructions.md与.claude/memory/instructions/safety.md逐字重复.claude/settings.json:deny和ask都是空的,allow里有Bash(git add:*)、Bash(find:*)、Edit(.obsidian/plugins/quickadd/data.json)、Edit(04_Archive/Projects/Airport/Chengdu/Office Test Env.md)- 16 个
.claude/commands/*.md,没有一个带risk或writes字段
目标
-
AGENTS.md= 唯一的操作手册:目录表 + 每个目录允许什么操作 + 属性前缀 + 读后写 + 改前问 + 绝不重写归档。 JS/Python 规范挪到.config/README.md或一节「scripts」。 -
CLAUDE.md和GEMINI.md= 一行指针:See AGENTS.md。 -
删掉 14 个 stub
CLAUDE.md(含插件目录里的)。 -
合并
project-instructions.md+.claude/memory/instructions/*.md→AGENTS.md,删原件。 -
权限表(照 Compass 的目录表,不是照搬内容):
目录 agent 可以 00_Inbox/自由追加 01_Projects/、03_Resources/按要求编辑 02_Areas/编辑前问 04_Archive/、.obsidian/、06_Metadata/Templates/不重写 任何 .env、data.json、凭据文件永不读写 -
settings.json收紧:deny加:Edit(.obsidian/plugins/*/data.json)(只锁含密钥的插件配置)、Edit(04_Archive/**)、Bash(git push:*)、Bash(git add .:*)ask加:Edit(.obsidian/**)(其余 .obsidian 编辑弹窗确认,与权限表「.obsidian 不重写」对齐 —— 密钥硬锁、其余兜底)- 删掉 allow 里那两个无理由的
Edit(...)条目
-
命令加元数据:每个
.claude/commands/*.md顶部加writes:和risk:,让一个按钮不能悄悄变成删除。 -
改前门:
README.md一句 claudesidian 都不保留,改成描述这个仓库;package.json的name/version更新;/init-bootstrap、clone URL、upgrade命令全部下线或归档。 -
**(从阶段 1 移入)**删
FIRST_RUN,改.claude/settings.json的 SessionStart hook(现在会往每个会话灌 "Welcome to Claudesidian")。
时间:1 小时。
六、顺序、时间与回滚
顺序(依赖关系)
0 轮换 ──► 1 止血 ──► 2 分区 ──► 3 元数据 ──► 4 文档 ──► 5 契约 ──► 6 历史重写
你 我/你 我/你 脚本 我/你 我 维护窗口
阶段 1 必须在最前(除轮换外):verifier 装上之后,后面每一步都不会再把新密钥提交进去。
阶段 6 必须最后:等目录定下来,filter-repo 只需跑一次。
时间
| 阶段 | 你的时间 | 我的时间 |
|---|---|---|
| 0 轮换 | 2–3 小时 | — |
| 1 止血 | 10 min | 40 min |
| 2 分区 | 15 min | 30 min |
| 3 元数据 | 5 min | 1 小时 |
| 4 文档 | 20 min | 45 min |
| 5 契约 | 10 min | 1 小时 |
| 6 历史重写 | 1 小时 | 辅助 |
| 合计 | 约 4 小时 | 约 4 小时 |
历史重写(阶段 6,单独维护窗口)
- 先做全量异地备份:
git clone --mirror到外置盘。这一步不可跳过 —— 笔记库可能只有这一份。 git filter-repo --replace-text或--path移除阶段 1 的路径- force push,然后每个克隆重新拉取(
git fetch --all && git reset --hard origin/main或重新 clone) - 提醒:任何旧的 fork、备份、CI 缓存都还留着旧对象
- 回滚:如果 filter-repo 结果异常(密钥未清除或笔记丢失),从异地 mirror 重新 clone 恢复,验证干净后替换当前仓库;mirror 在轮换确认完成前不可销毁
回滚
- 阶段 1–5 全部可逆:
git tag pre-restructure-2026-09-25是还原点 - 阶段 1 的
git rm --cached不删工作区文件 - 阶段 3 的 frontmatter 改写用脚本做,脚本先 dry-run 输出 diff
- 阶段 6 不可逆 → 先备份
七、不要做
- 不要现在拆成两个 git 仓库。 历史里还有密钥,同时动两件事风险叠加。等 verifier 稳定跑一个月。
- 不要搬 Compass 的生活主题文件夹、wheel-of-life 属性、Bible reading 工作区。那是它的工作,不是这个库的。
- 不要一次上 16 个 agent job 和 DataviewJS 仪表盘。 Compass 自己的指南说整套系统做了几年,一个周末抄完必然失败。
- 不要加新插件。 27 个装了,10 个开着;Tasks / QuickAdd / Calendar / Kanban / Copilot 装了没开。要么开要么删,装了不开只是噪音。
- 不要再往
06_Metadata/写计划文档然后不执行。 这是这个库最主要的失败模式 —— 已经有 4 份工作流文档和 2 份评审报告为证。
八、关于"岛屿"
数据:604 条 wikilink / 704 篇(0.86/篇)。活笔记 427 篇里 77% 零入链;704 篇里 573 篇零出链。
但分布是双峰的:73 篇有 ≥3 条出链,且几乎全在 01_Projects/Personal-Tech/LLM_Evaluation(48 个文件,含 roadmap、worksheet、practice 模板)。
结论:链接不是"太少",是只发生在一个项目里。真正在运转的循环是 LLM_Evaluation,不是 inbox→note。
所以:先承认已有的循环,再决定要不要新循环。 评审建议你上一个 inbox→链接笔记的每日循环 —— 但没检查已有的那个。上第二个循环不会让第一个变好。
补充:[[README]] 被引用 42 次,是"最高入链目标" —— 但那是十几个同名 README 的计数糊在一起。同名笔记会污染任何链接健康指标,做度量时先按路径区分。
九、下一步
只做一件事:阶段 0 的第 1 条 —— 轮换 Vaultwarden 的 admin token 和 SMTP 密码。
它是密码管理器本身。它守着列表上其他每一条。20 分钟。
做完告诉我,我立刻执行阶段 1(摘除 78 个文件 + 装 verifier + 接 CI)。