--- created: 2026-09-25 updated: 2026-09-26 type: plan status: active tags: [restructure, security, maintenance] --- # 重构方案 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 ``` ### 步骤 1. **标签存档点**(回滚用) ```bash git tag pre-restructure-2026-09-25 && git push origin pre-restructure-2026-09-25 ``` 2. **补 3 条 ignore 规则** → `.qmd/`、`.memory-index.lock`、`*.sqlite-wal`、`*.sqlite-shm` 3. **摘掉全部 75 + 3 个文件** ```bash 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` 只动索引,工作区文件保留。 4. **删掉文件名黑名单式 ignore** 现在 `.gitignore` 里有 `**/*password*.md`、`**/*secret*.md`、`**/*token*.md`。这批密钥所在的文件叫 `prod.md`、`deployment.md`、`Matrix.md` —— 黑名单一条都没拦住,还误伤正常笔记。改用**内容**扫描,见第 5 步。 5. **写 `.scripts/verify-vault.mjs`**(仓库脚本目录是 `.scripts/`,根目录无 `scripts/`),接进 CI,与 lint 并列 ```javascript // .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) ``` 6. **改 `.github/workflows/lint.yml`**,加一步 `node .scripts/verify-vault.mjs`(在 lint 之前) 7. ~~改 clipper 输出目录~~ / ~~删 `FIRST_RUN` + SessionStart hook~~ → **已移出**(审核 2026-09-26,止血只管 git 密钥):clipper 目录 → 阶段 2;FIRST_RUN/SessionStart → 阶段 5 **时间**:我 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` 死活 ```bash 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 —— 活笔记的元数据比想象中干净得多。 ### 步骤 1. **宣布边界,不是迁移**:`04_Archive/Inbox-Clippings/**` 与 `00_Inbox/Clippings/**` 永久使用 clipper schema(`date`/`page-title`/`url`),永不迁移 —— 04_Archive 之外 `00_Inbox/Clippings/` 还有 10 篇 2026 年新剪藏,边界必须同时覆盖两处。一步砍掉一半"混乱"。 2. **活笔记补 `created`**(机检口径:PARA 00–03 排除 `00_Inbox/Clippings/`,共 367 篇,101 篇已有,266 篇待补):是补齐不是重写;不新增其他必备键,不删内容性键(`title`/`description`/`source`/`date`/`author`/`published`/`aliases` 是内容事实)。脚本先 dry-run 输出 diff,可回滚。 3. **`status` 收敛到 4 值**:`draft` / `active` / `done` / `archived`。 现状有 14 种:`active`(51)、`archive`(4),外加 `review`、`done`、`needs-review`、`accepted`、`conditional`、`reading`、`evergreen`、`draft`、`Active`、`待执行`、`进行中`、`完成` 各一。写成脚本映射,可回滚。 4. **修深度违规**:`.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 守恒。 5. ~~模板去重~~ → **移入阶段 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` 字段 ### 目标 1. **`AGENTS.md` = 唯一的操作手册**:目录表 + 每个目录允许什么操作 + 属性前缀 + 读后写 + 改前问 + 绝不重写归档。 JS/Python 规范挪到 `.config/README.md` 或一节「scripts」。 2. **`CLAUDE.md`** 和 **`GEMINI.md`** = 一行指针:`See AGENTS.md`。 3. **删掉 14 个 stub `CLAUDE.md`**(含插件目录里的)。 4. **合并** `project-instructions.md` + `.claude/memory/instructions/*.md` → `AGENTS.md`,删原件。 5. **权限表**(照 Compass 的目录表,不是照搬内容): | 目录 | agent 可以 | | --- | --- | | `00_Inbox/` | 自由追加 | | `01_Projects/`、`03_Resources/` | 按要求编辑 | | `02_Areas/` | 编辑前问 | | `04_Archive/`、`.obsidian/`、`06_Metadata/Templates/` | 不重写 | | 任何 `.env`、`data.json`、凭据文件 | 永不读写 | 6. **`settings.json` 收紧**: - `deny` 加:`Edit(.obsidian/plugins/*/data.json)`(只锁含密钥的插件配置)、`Edit(04_Archive/**)`、`Bash(git push:*)`、`Bash(git add .:*)` - `ask` 加:`Edit(.obsidian/**)`(其余 .obsidian 编辑弹窗确认,与权限表「.obsidian 不重写」对齐 —— 密钥硬锁、其余兜底) - 删掉 allow 里那两个无理由的 `Edit(...)` 条目 7. **命令加元数据**:每个 `.claude/commands/*.md` 顶部加 `writes:` 和 `risk:`,让一个按钮不能悄悄变成删除。 8. **改前门**:`README.md` 一句 claudesidian 都不保留,改成描述**这个**仓库;`package.json` 的 `name` / `version` 更新;`/init-bootstrap`、clone URL、`upgrade` 命令全部下线或归档。 9. **(从阶段 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,单独维护窗口) 1. **先做全量异地备份**:`git clone --mirror` 到外置盘。这一步不可跳过 —— 笔记库可能只有这一份。 2. `git filter-repo --replace-text` 或 `--path` 移除阶段 1 的路径 3. force push,**然后每个克隆重新拉取**(`git fetch --all && git reset --hard origin/main` 或重新 clone) 4. 提醒:任何旧的 fork、备份、CI 缓存都还留着旧对象 5. **回滚**:如果 filter-repo 结果异常(密钥未清除或笔记丢失),从异地 mirror 重新 clone 恢复,验证干净后替换当前仓库;mirror 在轮换确认完成前不可销毁 ### 回滚 - 阶段 1–5 全部可逆:`git tag pre-restructure-2026-09-25` 是还原点 - 阶段 1 的 `git rm --cached` 不删工作区文件 - 阶段 3 的 frontmatter 改写用脚本做,脚本先 dry-run 输出 diff - 阶段 6 不可逆 → 先备份 --- ## 七、不要做 1. **不要现在拆成两个 git 仓库。** 历史里还有密钥,同时动两件事风险叠加。等 verifier 稳定跑一个月。 2. **不要搬 Compass 的生活主题文件夹**、wheel-of-life 属性、Bible reading 工作区。那是它的工作,不是这个库的。 3. **不要一次上 16 个 agent job 和 DataviewJS 仪表盘。** Compass 自己的指南说整套系统做了几年,一个周末抄完必然失败。 4. **不要加新插件。** 27 个装了,10 个开着;Tasks / QuickAdd / Calendar / Kanban / Copilot 装了没开。**要么开要么删**,装了不开只是噪音。 5. **不要再往 `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)。