---
phase: 18-documentation-ux-polish
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - paperforge.json
  - paperforge/worker/ocr.py
  - CHANGELOG.md
  - CONTRIBUTING.md
autonomous: true
requirements:
  - DX-03
  - DX-04
  - UX-01

must_haves:
  truths:
    - "CHANGELOG.md exists at vault root documenting v1.0 through v1.4 in Keep a Changelog format"
    - "CONTRIBUTING.md exists at vault root with dev setup, hook install, test workflow, and conventions"
    - "Users can enable auto_analyze_after_ocr: true in paperforge.json, and after OCR completes, the paper's library-record automatically gets analyze: true"
  artifacts:
    - path: "paperforge.json"
      provides: "auto_analyze_after_ocr config field at top level"
      contains: "auto_analyze_after_ocr"
    - path: "paperforge/worker/ocr.py"
      provides: "auto_analyze hook after meta[\"ocr_status\"] = \"done\" at line 1498"
      min_lines: 1600
    - path: "CHANGELOG.md"
      provides: "Version history document"
      min_lines: 60
    - path: "CONTRIBUTING.md"
      provides: "Contributor onboarding document"
      min_lines: 80
  key_links:
    - from: "paperforge/worker/ocr.py:1498"
      to: "paperforge.json"
      via: "read_json(vault / 'paperforge.json')"
      pattern: "read_json\\(.*paperforge\\.json"
    - from: "paperforge/worker/ocr.py"
      to: "paths[\"library_records\"]"
      via: "rglob for <key>.md"
      pattern: "rglob\\(.*\\.md"
---

<objective>
Add the `auto_analyze_after_ocr` config option, create CHANGELOG.md and CONTRIBUTING.md at vault root.

Purpose: Satisfy three foundation requirements for v1.4 documentation and workflow streamlining. The config option is the only code change in Phase 18.
Output: paperforge.json and ocr.py modified; CHANGELOG.md and CONTRIBUTING.md created.
</objective>

<execution_context>
@C:/Users/Lin/.opencode/get-shit-done/workflows/execute-plan.md
@C:/Users/Lin/.opencode/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md

@paperforge.json
@paperforge/worker/ocr.py (lines 1485-1514, 1586-1592)
@paperforge/worker/_utils.py (lines 20-21 — read_json)
</context>

<tasks>

<task type="auto">
  <name>Task 1: Add auto_analyze_after_ocr config + OCR hook</name>
  <files>paperforge.json, paperforge/worker/ocr.py</files>
  <action>
    Per D-01, D-02, D-03 (UX-01):

    1. **paperforge.json** — Add `"auto_analyze_after_ocr": false` as a top-level key (after `"changelog_url"` on line 27, or at a natural position among settings). This is the default "opt-in" value — users must explicitly set it to `true`.

    2. **paperforge/worker/ocr.py** — After line 1498 (`meta["ocr_status"] = "done"`) and before line 1499 (`meta["ocr_finished_at"]`), insert the auto_analyze hook:

       ```python
       # Per D-01: auto_analyze_after_ocr opt-in workflow streamlining
       cfg_path = vault / "paperforge.json"
       if cfg_path.exists():
           try:
               pf_cfg = read_json(cfg_path)
               if pf_cfg.get("auto_analyze_after_ocr", False):
                   record_glob = list(paths["library_records"].rglob(f"{key}.md"))
                   if record_glob:
                       record_path = record_glob[0]
                       text = record_path.read_text(encoding="utf-8")
                       text = re.sub(
                           r"^analyze:.*$",
                           "analyze: true",
                           text,
                           count=1,
                           flags=re.MULTILINE,
                       )
                       record_path.write_text(text, encoding="utf-8")
           except Exception:
               logger.warning("auto_analyze_after_ocr: failed for %s", key, exc_info=True)
       ```

       Key design notes:
       - Do NOT add new imports — `read_json` is already imported from `_utils.py` (line 21-24), `re` is already imported (line 6).
       - Use the exact same frontmatter regex pattern pattern as deep_reading.py uses (`re.sub(r"^analyze:.*$", "analyze: true", text, count=1, flags=re.MULTILINE)`).
       - Wrap in `try/except` so a single failure does not abort the OCR batch.
       - This runs BEFORE `_sync.run_selection_sync(vault)` at line 1589, which does not overwrite user-controlled frontmatter fields like `analyze`.
  </action>
  <verify>
    <automated>python -c "import json; cfg=json.load(open('paperforge.json')); assert 'auto_analyze_after_ocr' in cfg; assert cfg['auto_analyze_after_ocr'] == False"</automated>
  </verify>
  <done>
    paperforge.json has `auto_analyze_after_ocr: false`. ocr.py line 1498 has the hook reading paperforge.json and setting analyze:true in library-record frontmatter. `re.sub` pattern matches the existing frontmatter convention. No new imports. `pytest tests/ -x` passes.
  </done>
</task>

<task type="auto">
  <name>Task 2: Create CHANGELOG.md</name>
  <files>CHANGELOG.md</files>
  <action>
    Per D-05 (DX-03):

    Create `CHANGELOG.md` at vault root in Keep a Changelog format (https://keepachangelog.com/en/1.1.0/).

    Structure:
    ```markdown
    # Changelog

    All notable changes to PaperForge Lite are documented in this file.

    The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

    ---

    ## [Unreleased]

    ### Added
    - `auto_analyze_after_ocr` config option in `paperforge.json` (opt-in, default `false`)

    ### Changed
    - ...

    ## [1.4.0] — 2026-04-27
    ...

    ## [1.3.0] — 2026-04-24
    ...

    ## [1.2.0] — 2026-04-24
    ...

    ## [1.1.0] — 2026-04-24
    ...

    ## [1.0.0] — 2026-04-23
    ...
    ```

    Content guidance:
    - **v1.0**: Initial MVP (CLI, Zotero sync, OCR, Base views, setup wizard, doctor)
    - **v1.1**: Sandbox hardening (setup wizard refinement, path resolution, repair command, smoke tests, rollback in prepare)
    - **v1.2**: Systematization (CLI unification, /pf-* namespace, package rename, shared command modules, architecture docs, migration guide)
    - **v1.3**: Path normalization & architecture (Zotero path normalization, multi-attachment support, module boundary cleanup, test dead zone elimination)
    - **v1.4**: Code health & UX (shared utilities extraction, structured logging, deep-reading queue merge, retry/progress, dead code removal, pre-commit hooks, docs & changelog)

    Use `Added`, `Changed`, `Fixed` subsections per version. Be concise — 3-8 bullet points per version is appropriate. Do NOT include every commit; summarize the user-facing and maintainer-facing changes.

    Do NOT update `paperforge.json` `changelog_url` — the existing GitHub Releases URL serves a different purpose (update checking, not local changelog browsing).
  </action>
  <verify>
    <automated>python -c "assert '## [Unreleased]' in open('CHANGELOG.md').read()"</automated>
  </verify>
  <done>
    CHANGELOG.md exists at vault root with Keep a Changelog format. Sections for v1.0 through v1.4 plus [Unreleased]. Each version has descriptive content.
  </done>
</task>

<task type="auto">
  <name>Task 3: Create CONTRIBUTING.md</name>
  <files>CONTRIBUTING.md</files>
  <action>
    Per D-06 (DX-04):

    Create `CONTRIBUTING.md` at vault root with these sections:

    ```markdown
    # Contributing to PaperForge Lite

    ## Development Setup

    - `pip install -e ".[test]"`  # editable install with test deps
    - Verify: `python -m paperforge --version`

    ## Pre-commit Hooks

    Before committing, run `pre-commit install` to activate hooks:
    ```
    pip install pre-commit
    pre-commit install
    ```

    Hooks configured (see `.pre-commit-config.yaml`):
    - Ruff lint + format
    - check-yaml, check-toml
    - end-of-file-fixer, trailing-whitespace
    - Consistency audit (detects duplicate utility functions)

    ## Test Workflow

    ```
    pytest tests/ -x          # Stop on first failure
    pytest tests/ -v          # Verbose
    pytest tests/ -k "ocr"    # Filter by keyword
    pytest tests/ --cov       # Coverage report
    ```

    All tests must pass before merging:
    - 203+ tests, 0 failures, 0 errors
    - Run `ruff check .` and `ruff format --check .` for lint

    ## Architecture Overview

    PaperForge Lite uses a **two-layer design**:
    - **Worker layer** (`paperforge/worker/`) — automated, deterministic, CLI-triggered
    - **Agent layer** (OpenCode Agent skills) — interactive, reasoning-driven, user-triggered

    Key files:
    - `paperforge/config.py` — Path resolution and config loading
    - `paperforge/worker/_utils.py` — **Leaf module** (imports only from stdlib and config.py, never from sibling workers)
    - `paperforge/worker/ocr.py` — OCR pipeline
    - `paperforge/worker/sync.py` — Zotero sync

    ## Code Conventions

    1. **No new `print()` calls** in worker modules — use `logger = logging.getLogger(__name__)` and `logger.info()`, `logger.debug()`, etc. `print()` is reserved for user-facing stdout output in CLI commands.
    2. **Import shared utilities from `_utils.py`** — do not copy-paste `read_json`, `write_json`, etc. into new modules.
    3. **`_utils.py` is a leaf module** — must never import from `paperforge.worker.*` or `paperforge.commands.*`.
    4. **Requirement IDs in commit messages** — link commits to requirements: `feat(UX-01): add auto_analyze_after_ocr`.
    5. **Pre-commit hooks** run on `git commit` — do not bypass them.
    6. **Forward slashes in paths** — use `Path.as_posix()` for all path-to-string conversions.
    ```

  </action>
  <verify>
    <automated>python -c "text = open('CONTRIBUTING.md').read(); assert 'Development Setup' in text; assert 'Pre-commit Hooks' in text; assert 'Architecture Overview' in text; assert 'Code Conventions' in text"</automated>
  </verify>
  <done>
    CONTRIBUTING.md exists at vault root with dev setup, pre-commit instructions, test workflow, architecture overview, and code conventions.
  </done>
</task>

</tasks>

<verification>
1. `python -c "import json; cfg=json.load(open('paperforge.json')); assert 'auto_analyze_after_ocr' in cfg"` — config field added
2. `grep -n "auto_analyze_after_ocr" paperforge/worker/ocr.py` — hook present
3. `python -c "assert '## [Unreleased]' in open('CHANGELOG.md').read()"` — changelog created
4. `python -c "text = open('CONTRIBUTING.md').read(); assert all(s in text for s in ['Development Setup', 'Pre-commit Hooks', 'Test Workflow', 'Architecture Overview', 'Code Conventions'])"` — contributing created
5. `pytest tests/ -x` — all existing tests pass
</verification>

<success_criteria>
1. paperforge.json has auto_analyze_after_ocr field (default false)
2. ocr.py has hook that reads config and sets analyze:true when flag enabled
3. CHANGELOG.md at vault root with Keep a Changelog format, v1.0-v1.4
4. CONTRIBUTING.md at vault root with 5 required sections
5. All 203 existing tests pass
</success_criteria>

<output>
After completion, create `.planning/phases/18-documentation-ux-polish/18-01-SUMMARY.md`
</output>
