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
This commit is contained in:
Research Assistant 2026-04-24 20:45:18 +08:00
parent 72cbdc3250
commit 13e548df04
4 changed files with 92 additions and 7 deletions

View file

@ -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

View file

@ -182,7 +182,7 @@ D:\L\Med\Research\99_System\Zotero\storage\<8位代码>\<原始文件名>.pdf
**格式:** `[[相对路径]]`
**要求:**
- 不使用 Markdown 标准链接 `[text](path)`
- 不使用 Markdown 标准链接 — 使用 `[[wikilink]]` 而非 `label → url` 格式
- 使用 Obsidian wikilink 语法
- 路径相对于 Vault 根目录
- 支持双链图谱和反向链接

View file

@ -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/<domain>/<key>.md`
@ -219,13 +263,20 @@ year: 2024
doi: "10.xxxx/xxxxx"
collection_path: "子分类" # Zotero 子收藏夹路径
has_pdf: true # 是否有 PDF 附件(自动生成)
pdf_path: "<system_dir>/Zotero/..." # PDF 相对路径(自动生成)
fulltext_md_path: "<system_dir>/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: "<system_dir>/Zotero/..."
pdf_link: "[[99_System/Zotero/storage/KEY/文件名.pdf]]"
---
```

View file

@ -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)
---