# Phase 37: Frontmatter Rationalization — Plan

**Plan:** 37-01
**Type:** Backend refactoring
**Requirements:** FM-01, FM-02, FM-03, FM-04, FM-05, FM-06, FM-07
**Estimated tasks:** 6

## Task 1: Slim `frontmatter_note()` — remove workspace + OCR + debug fields

**Target:** `paperforge/worker/sync.py`, function `frontmatter_note()` (lines ~1598-1670)

1. Remove from generated frontmatter:
   - `type: article` (hardcoded, always "article")
   - `ocr_job_id`, `ocr_md_path`, `ocr_json_path` (OCR infrastructure)
   - `paper_root`, `main_note_path`, `fulltext_path`, `deep_reading_path`, `ai_path` (workspace paths — derivable)
   - `deep_reading_md_path` (derivable; kept only if deep-reading content exists, but stored in paper-meta.json)

2. Remove `collection_group`, `collections`, `collection_tags` (internal BBT metadata, not needed for Base views or user reading)

3. Keep:
   - Identity: `title`, `year`, `journal`, `authors` (as `first_author` single string from `entry.authors[0]` if available), `doi`, `pmid`, `zotero_key`, `domain`, `abstract`, `tags`
   - Workflow: `has_pdf`, `do_ocr`, `analyze`, `ocr_status`, `deep_reading_status`
   - Linkage: `pdf_path`
   - Collection context: `collection_path`

4. Change `authors` (list) to `first_author` (string) — Base views need scalar `first_author`

**Expected final frontmatter shape (16 fields):**
```yaml
---
title: "..."
year: 2024
journal: "..."
first_author: "Smith"
zotero_key: "TSTONE001"
domain: "骨科"
doi: "10.1016/..."
pmid: "12345678"
collection_path: "骨科"
abstract: |-
  ...
has_pdf: true
do_ocr: true
analyze: true
ocr_status: "done"
deep_reading_status: "pending"
pdf_path: "[[...]]"
tags:
  - 文献阅读
  - 骨科
---
```

## Task 2: Create `paper_meta.py` — per-workspace JSON writer

**New file:** `paperforge/worker/paper_meta.py`

1. Function: `write_paper_meta(workspace_dir: Path, entry: dict, migrated_from: dict = None) -> None`
2. Writes `<workspace_dir>/paper-meta.json` with:
   ```json
   {
     "paperforge_version": "1.9.0",
     "zotero_key": "TSTONE001",
     "ocr_job_id": "",
     "ocr_md_path": "[[...]]",
     "ocr_json_path": "...",
     "ocr_status": "done",
     "health": { "pdf_health": "healthy", ... },
     "maturity": { "level": 4, "level_name": "...", "checks": {...}, "blocking": null },
     "lifecycle": "fulltext_ready",
     "next_step": "ocr",
     "migrated_from": { "path": "...", "timestamp": "..." }
   }
   ```
3. Uses `write_json()` from `_utils.py` for atomic-safe writes
4. Idempotent: if paper-meta.json exists, merge (update mutable fields, preserve `migrated_from`)

## Task 3: Wire `paper-meta.json` into `_build_entry()` / `run_index_refresh()`

**Target:** `paperforge/worker/asset_index.py`, function `_build_entry()` (lines ~213-332)

1. After writing the formal note (after line ~330), call `write_paper_meta()` from `paper_meta.py`
2. Pass `workspace_dir` and `entry` dict
3. Works only when `workspace_dir` exists (workspace path); skip for flat-legacy path

**Target:** `paperforge/worker/sync.py`, function `run_index_refresh()` — ensure `write_paper_meta()` is importable

## Task 4: Add `do_ocr` and `analyze` to `_build_entry()` entry dict

**Target:** `paperforge/worker/asset_index.py`, function `_build_entry()`

1. The `_build_entry()` constructs the entry dict consumed by `frontmatter_note()`. Currently missing: `do_ocr`, `analyze`.
2. Read these from the library-record frontmatter if it exists, else default to:
   - `do_ocr`: `true` if `has_pdf` else `false` (same as current recommend_analyze logic)
   - `analyze`: `true` if `has_pdf` (auto_analyze_after_ocr behavior)
3. Add to entry dict: `entry["do_ocr"]`, `entry["analyze"]`, `entry["first_author"]`

## Task 5: Remove `library_record_markdown()`

**Target:** `paperforge/worker/sync.py`

1. Delete the `library_record_markdown()` function (lines ~618-677)
2. Remove its only call site in `run_selection_sync()` (line ~805) — replace with a log message or skip the library-record write entirely
3. Verify via `grep "library_record_markdown"` returns zero results

## Task 6: Update tests

1. `tests/test_asset_index.py` — update expected frontmatter fields to match slimmed set
2. `tests/test_migration.py` — verify paper-meta.json is created during migration
3. `tests/test_context.py` — if tests reference removed fields, update
4. Run full test suite: `pytest tests/ -q --tb=short`
5. Verify no regressions: 317+ tests pass

**Verification:**
- Run `paperforge sync` in sandbox vault → check generated formal note frontmatter has exactly the 16 fields
- Check `paper-meta.json` exists in workspace dir with correct fields
- `grep "library_record_markdown" paperforge/` returns zero results
- Full test suite passes
