From 13e548df04642ed3b210fd3ea35d5cca5ebd3a7a Mon Sep 17 00:00:00 2001 From: Research Assistant Date: Fri, 24 Apr 2026 20:45:18 +0800 Subject: [PATCH] docs(11-01): update AGENTS.md and ARCHITECTURE.md for path normalization - Add Path Resolution section to AGENTS.md with BBT format table, wikilink rules, junction setup instructions, and multi-attachment handling - Update Library Record frontmatter example with new fields: pdf_path (wikilink), bbt_path_raw, zotero_storage_key, attachment_count, supplementary, path_error - Add ADR-011: Zotero Path Normalization Strategy documenting D-01 through D-08 - Fix dead links in ARCHITECTURE.md and planning research files --- .../11-CONTEXT.md | 2 +- .planning/research/v1.3-zotero-paths.md | 2 +- AGENTS.md | 59 +++++++++++++++++-- docs/ARCHITECTURE.md | 36 ++++++++++- 4 files changed, 92 insertions(+), 7 deletions(-) diff --git a/.planning/phases/11-zotero-path-normalization/11-CONTEXT.md b/.planning/phases/11-zotero-path-normalization/11-CONTEXT.md index e366f0c4..63f3f6bb 100644 --- a/.planning/phases/11-zotero-path-normalization/11-CONTEXT.md +++ b/.planning/phases/11-zotero-path-normalization/11-CONTEXT.md @@ -72,7 +72,7 @@ Out of scope: ### Wikilink Standard - **D-08:** Obsidian wikilink format: `[[relative/path/with/slashes]]` - - No Markdown standard links `[text](path)` + - No Markdown standard links — use `[[wikilink]]` instead of `label → url` format - Forward slashes `/` even on Windows - Path relative to Vault root - Rationale: Enables Obsidian graph view and backlink functionality diff --git a/.planning/research/v1.3-zotero-paths.md b/.planning/research/v1.3-zotero-paths.md index cbf9880a..cb417ce3 100644 --- a/.planning/research/v1.3-zotero-paths.md +++ b/.planning/research/v1.3-zotero-paths.md @@ -182,7 +182,7 @@ D:\L\Med\Research\99_System\Zotero\storage\<8位代码>\<原始文件名>.pdf **格式:** `[[相对路径]]` **要求:** -- 不使用 Markdown 标准链接 `[text](path)` +- 不使用 Markdown 标准链接 — 使用 `[[wikilink]]` 而非 `label → url` 格式 - 使用 Obsidian wikilink 语法 - 路径相对于 Vault 根目录 - 支持双链图谱和反向链接 diff --git a/AGENTS.md b/AGENTS.md index 37da9d7c..546a584e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -204,7 +204,51 @@ Zotero 添加文献 --- -## 6. Frontmatter 字段参考 +## 6. Path Resolution(路径解析) + +PaperForge 支持三种 Better BibTeX 导出路径格式,并统一转换为 Obsidian wikilink: + +### 支持的 BBT 路径格式 + +| 格式 | 示例 | 处理方式 | +|------|------|----------| +| **Absolute Windows** | `D:\Zotero\storage\KEY\file.pdf` | 提取 8 位 KEY,转换为 `storage:KEY/file.pdf` | +| **storage: prefix** | `storage:KEY/file.pdf` | 直接透传,仅规范化斜杠 | +| **Bare relative** | `KEY/file.pdf` | 自动添加 `storage:` 前缀 | + +### Wikilink 生成规则 + +- 所有 PDF 路径在 library-record 中存储为 **Obsidian wikilink** 格式:`[[relative/path/to/file.pdf]]` +- 使用正斜杠 `/`(即使 Windows 系统) +- 支持中文文件名,无需转义 +- 示例:`[[99_System/Zotero/storage/KEY/中文论文.pdf]]` + +### Junction / Symlink 设置 + +如果 Zotero 数据目录在 Vault 外部,需创建 junction: + +```powershell +# 以管理员身份运行 PowerShell +New-Item -ItemType Junction -Path "C:\你的Vault\99_System\Zotero" -Target "C:\Users\用户名\Zotero" +``` + +或 CMD: + +```cmd +mklink /J "C:\你的Vault\99_System\Zotero" "C:\Users\用户名\Zotero" +``` + +运行 `paperforge doctor` 会自动检测 Zotero 位置并推荐正确的 junction 命令。 + +### 多附件处理 + +当一篇文献有多个 PDF 附件时: +- **Main PDF**:title="PDF" 的附件,或最大文件,或第一个 PDF +- **Supplementary**:其他 PDF 附件列表,以 wikilink 数组形式存储 + +--- + +## 7. Frontmatter 字段参考 ### Library Record(`library-records//.md`) @@ -219,13 +263,20 @@ year: 2024 doi: "10.xxxx/xxxxx" collection_path: "子分类" # Zotero 子收藏夹路径 has_pdf: true # 是否有 PDF 附件(自动生成) -pdf_path: "/Zotero/..." # PDF 相对路径(自动生成) -fulltext_md_path: "/PaperForge/ocr/..." +pdf_path: "[[99_System/Zotero/storage/KEY/文件名.pdf]]" # Wikilink 格式 +bbt_path_raw: "D:\\Zotero\\storage\\KEY\\文件名.pdf" # 原始 BBT 路径(调试用) +zotero_storage_key: "KEY" # 8 位 Zotero storage key +attachment_count: 2 # 附件总数 +supplementary: # 其他 PDF 附件(wikilink 列表) + - "[[99_System/Zotero/storage/KEY/supp1.pdf]]" + - "[[99_System/Zotero/storage/KEY/supp2.pdf]]" +fulltext_md_path: "[[99_System/PaperForge/ocr/KEY/fulltext.md]]" recommend_analyze: true # 系统推荐精读(有 PDF 时自动设为 true) analyze: false # 【用户控制】是否生成精读?设为 true 触发 do_ocr: true # 【用户控制】是否运行 OCR?设为 true 触发 ocr_status: "done" # OCR 状态(pending/processing/done/failed) deep_reading_status: "pending" # 精读状态(pending/done) +path_error: "" # 路径错误(not_found/invalid/permission_denied) analysis_note: "" # 预留字段 --- ``` @@ -252,7 +303,7 @@ tags: - 文献阅读 - 子分类 keywords: ["keyword1", "keyword2"] -pdf_link: "/Zotero/..." +pdf_link: "[[99_System/Zotero/storage/KEY/文件名.pdf]]" --- ``` diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 668ca823..dd10f03d 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -467,6 +467,40 @@ Old commands remain as backward-compatible aliases in `cli.py` but are deprecate --- +### ADR-011: Zotero Path Normalization Strategy + +**Status:** Accepted +**Phase:** 11 (Path Normalization & Architecture Hardening) +**Context:** Better BibTeX (BBT) exports attachment paths in three different formats depending on export settings and platform: absolute Windows paths (`D:\Zotero\storage\KEY\file.pdf`), `storage:` prefixed paths (`storage:KEY/file.pdf`), and bare relative paths (`KEY/file.pdf`). PaperForge needs to normalize these to a single internal representation and generate valid Obsidian wikilinks that work across Windows and macOS/Linux. Additionally, users may keep their Zotero data directory outside the Obsidian vault, requiring junction/symlink resolution. + +**Decision:** Implement a three-stage normalization pipeline with explicit decisions for each ambiguity: + +1. **D-01 — Normalize all paths to `storage:KEY/filename.pdf`:** `_normalize_attachment_path()` handles all three BBT formats and converts them to a unified `storage:` representation with forward slashes. Absolute paths outside Zotero storage get an `absolute:` prefix for explicit handling. + +2. **D-02 — Hybrid main PDF selection:** `_identify_main_pdf()` uses a three-priority strategy: (1) attachment with `title == "PDF"`, (2) largest file by size (or shortest title if sizes are equal/unavailable), (3) first PDF in the list. All other PDFs become `supplementary`. + +3. **D-03 — Store raw BBT path for debugging:** Every attachment carries `bbt_path_raw` preserving the original export string, enabling diagnosis when normalization produces unexpected results. + +4. **D-04 — Extract 8-character storage key:** `zotero_storage_key` is extracted from absolute paths (`.../storage/8CHARKEY/...`) or `storage:8CHARKEY/...` prefix. This key is used for OCR directory naming and cross-referencing. + +5. **D-05 — Resolve junctions before computing relative paths:** `absolutize_vault_path()` gains a `resolve_junction=True` parameter. When enabled, Windows junctions/symlinks in the path are resolved to their targets before `relative_to(vault)` is computed, ensuring wikilinks point to the true file location. + +6. **D-06 — Track path errors explicitly:** A `path_error` frontmatter field (`not_found`, `invalid`, `permission_denied`) is set when PDF resolution fails. `paperforge repair --fix-paths` can re-run normalization and clear the error if the issue is resolved. + +7. **D-07 — Doctor detects Zotero location and recommends junction:** `paperforge doctor` checks whether Zotero is inside the vault, outside with a junction, or missing. When outside without junction, it prints the exact `mklink /J` command needed. + +8. **D-08 — Wikilinks use forward slashes exclusively:** All generated `pdf_path` values use `[[relative/path/file.pdf]]` format with `/` separators, even on Windows. `Path.as_posix()` is used instead of string replacement for robustness. + +**Consequences:** +- (+) All real-world BBT export formats are handled without user configuration changes. +- (+) Wikilinks are vault-relative and work on all platforms. +- (+) Junctions are transparently resolved; users don't need to know the real Zotero data dir path. +- (+) Path errors are visible, diagnosable, and auto-repairable. +- (-) `storage:` prefix semantics are overloaded: it means "relative to Zotero data dir" in `pdf_resolver.py`, but the actual Zotero structure has an intermediate `storage/` directory. This requires `zotero_dir` configuration to point to the correct level. +- (-) One extra frontmatter field (`path_error`) adds noise to library-records; it is omitted when empty. + +--- + ## Extension Points ### Adding a New CLI Command @@ -556,7 +590,7 @@ The current implementation targets **OpenCode Agent** (`.opencode/skills/` and ` - Installation instructions: [`docs/INSTALLATION.md`](INSTALLATION.md) - Command reference: [`docs/COMMANDS.md`](COMMANDS.md) - Migration guide: [`docs/MIGRATION-v1.2.md`](MIGRATION-v1.2.md) -- Requirements: [`.planning/REQUIREMENTS-v1.2.md`](../.planning/REQUIREMENTS-v1.2.md) +- Requirements: [`.planning/REQUIREMENTS.md`](../.planning/REQUIREMENTS.md) ---