---
phase: 04-onboarding-validation
plan: '01'
type: execute
wave: 1
depends_on: []
files_modified:
  - pipeline/worker/scripts/literature_pipeline.py
autonomous: true
requirements:
  - ONBD-03
  - DEEP-01

must_haves:
  truths:
    - User can see which papers are ready for deep reading (OCR done, analyze=true)
    - User can see which papers are blocked (analyze=true but OCR not done)
    - User can see which papers are waiting for OCR (do_ocr=true, ocr_status=pending/processing)
    - User can see fix instructions in verbose mode
  artifacts:
    - path: "pipeline/worker/scripts/literature_pipeline.py"
      provides: "Enhanced deep-reading output with three states"
      contains: "def run_deep_reading"
    - path: "paperforge/cli.py"
      provides: "deep-reading command parser + --verbose flag"
      contains: "deep-reading"
  key_links:
    - from: "cli.py"
      to: "literature_pipeline.py"
      via: "run_deep_reading(vault)"
      pattern: "run_deep_reading"
    - from: "literature_pipeline.py"
      to: "ocr/.*/meta.json"
      via: "read_json(meta_path)"
      pattern: "ocr_status"
---

<objective>
Enhance `paperforge deep-reading` to output three distinct states (ready/blocked/waiting OCR) and add `--verbose` mode that shows fix instructions per blocked paper.
</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/phases/04-onboarding-validation/04-CONTEXT.md
@.planning/STATE.md
@.planning/ROADMAP.md
@.planning/REQUIREMENTS.md
@pipeline/worker/scripts/literature_pipeline.py (lines 2746-2815 for run_deep_reading)
@paperforge/cli.py (lines 71-72, 166-171 for deep-reading dispatch)
</context>

<tasks>

<task type="auto">
  <name>Task 1: Add --verbose flag to deep-reading parser</name>
  <files>paperforge/cli.py</files>
  <read_first>
    - paperforge/cli.py (lines 38-88 for build_parser, lines 166-178 for dispatch)
  </read_first>
  <action>
    In paperforge/cli.py, add `--verbose` flag to the deep-reading subparser.

    In build_parser(), find the `deep-reading` parser (around line 72) and add:
    ```python
    p_dr = sub.add_parser("deep-reading", help="Check deep-reading queue status")
    p_dr.add_argument(
        "--verbose", "-v", action="store_true",
        help="Show fix instructions for blocked papers"
    )
    ```

    In main(), update the dispatch_map to pass args:
    ```python
    dispatch_map = {
        "status": run_status,
        "selection-sync": run_selection_sync,
        "index-refresh": run_index_refresh,
        "deep-reading": run_deep_reading,
    }
    ```

    Update main() to unpack verbose argument before calling run_deep_reading:
    ```python
    if args.command == "deep-reading":
        return run_deep_reading(vault, verbose=getattr(args, "verbose", False))
    ```
  </action>
  <verify>
    <automated>grep -n "add_argument.*verbose" paperforge/cli.py && grep -n "run_deep_reading.*verbose" paperforge/cli.py</automated>
  </verify>
  <done>deep-reading parser accepts --verbose/-v flag and passes it to run_deep_reading</done>
</task>

<task type="auto">
  <name>Task 2: Enhance run_deep_reading to three-state output + verbose fix instructions</name>
  <files>pipeline/worker/scripts/literature_pipeline.py</files>
  <read_first>
    - pipeline/worker/scripts/literature_pipeline.py (lines 2746-2815 for run_deep_reading current implementation)
    - paperforge/config.py (for paperforge_paths signature)
  </read_first>
  <action>
    Modify run_deep_reading in pipeline/worker/scripts/literature_pipeline.py:

    1. Change function signature to:
    ```python
    def run_deep_reading(vault: Path, verbose: bool = False) -> int:
    ```

    2. In the pending_queue building section (lines 2786-2794), add `do_ocr` tracking:
    ```python
    pending_queue.append({
        'zotero_key': key,
        'domain': domain,
        'title': item.get('title', ''),
        'ocr_status': ocr_status,
        'do_ocr': is_do_ocr,  # add this
    })
    ```

    3. Add is_do_ocr detection after is_analyze (around line 2772):
    ```python
    do_ocr_match = re.search('^do_ocr:\\s*(true|false)$', record_text, re.MULTILINE)
    is_do_ocr = do_ocr_match and do_ocr_match.group(1) == 'true'
    ```

    4. Restructure the report output (lines 2795-2811) to produce three sections:
    ```python
    ready = [q for q in pending_queue if q['ocr_status'] == 'done']
    waiting_ocr = [q for q in pending_queue if q['do_ocr'] and q['ocr_status'] in ('pending', 'processing')]
    blocked = [q for q in pending_queue if q['analyze'] and not q['do_ocr']]  # analyze but no ocr requested

    # Actually blocked means analyze=true but ocr not done
    ready = [q for q in pending_queue if q['ocr_status'] == 'done']
    waiting_ocr = [q for q in pending_queue if q['do_ocr'] and q['ocr_status'] in ('pending', 'processing')]
    blocked = [q for q in pending_queue if q['ocr_status'] not in ('done', '') and not q.get('do_ocr')]
    ```

    Wait - re-read the spec from CONTEXT.md Area 3:
    - 就绪 (ready): `ocr_status = done`
    - 阻塞 (blocked): `analyze = true` but `ocr_status != done`
    - 等待 OCR (waiting): `do_ocr = true` and `ocr_status = pending/processing`

    So the correct logic is:
    ```python
    ready = [q for q in pending_queue if q['ocr_status'] == 'done']
    waiting_ocr = [q for q in pending_queue if q['do_ocr'] and q['ocr_status'] in ('pending', 'processing')]
    blocked = [q for q in pending_queue if q['is_analyze'] and q['ocr_status'] not in ('done', '')]
    # where is_analyze means analyze=true
    ```

    Actually since we only add to pending_queue when is_analyze=true (line 2785: `if is_analyze and correct_status == 'pending'`), blocked = all non-ready in pending_queue.

    But we need to distinguish blocking from waiting:
    - blocked = analyze=true + do_ocr=false (user hasn't requested OCR)
    - waiting = do_ocr=true + ocr_status=pending/processing

    Actually re-reading the spec more carefully:
    - "阻塞: analyze = true 但 ocr_status != done" — user wants deep reading but OCR not done
    - "等待 OCR: do_ocr = true 且 ocr_status = pending/processing" — OCR requested but not done

    These can overlap: if do_ocr=true AND analyze=true AND ocr_status=pending, it's BOTH waiting and blocked (waiting for OCR which blocks deep reading).

    Simplest: show three groups:
    - 就绪 (ready): ocr_status == 'done'
    - 等待 OCR (waiting): do_ocr == true and ocr_status in ('pending', 'processing')
    - 阻塞 (blocked): analyze == true and ocr_status not in ('done', '') and NOT (do_ocr == true and ocr_status in ('pending', 'processing'))

    5. When verbose=True, for each blocked paper show fix instructions based on ocr_status:
    - If ocr_status is empty/missing: "Run `paperforge ocr run` to start OCR"
    - If ocr_status == 'pending': "OCR is queued — run `paperforge ocr run` if not already running"
    - If ocr_status == 'processing': "OCR in progress — wait for completion"
    - If ocr_status == 'failed': Read meta.json error field for fix suggestion, else "OCR failed — check meta.json for details, then re-run `paperforge ocr run`"

    6. Print sections:
    ```
    ## 待精读队列
    [if ready:] ## 就绪 (N 篇) — OCR 已完成，可直接 /LD-deep
    [if waiting:] ## 等待 OCR (N 篇)
    [if blocked:] ## 阻塞 (N 篇) — 需要先完成 OCR
    [if verbose and blocked:] ### 修复指令
    ```
  </action>
  <verify>
    <automated>grep -n "def run_deep_reading" pipeline/worker/scripts/literature_pipeline.py && grep -n "verbose" pipeline/worker/scripts/literature_pipeline.py | head -5</automated>
  </verify>
  <done>run_deep_reading outputs three distinct states and --verbose shows fix instructions</done>
</task>

<task type="auto">
  <name>Task 3: Verify existing deep-reading tests pass</name>
  <files>tests/test_cli_worker_dispatch.py</files>
  <read_first>
    - tests/test_cli_worker_dispatch.py (lines 108-118 for deep-reading test)
  </read_first>
  <action>
    Verify the existing test for deep-reading dispatch still passes. The test mocks run_deep_reading and checks it receives the vault argument. Since we added a verbose=False default parameter, the existing call pattern `run_deep_reading(vault)` will still work.

    Run: pytest tests/test_cli_worker_dispatch.py::test_deep_reading_dispatch -v
  </action>
  <verify>
    <automated>pytest tests/test_cli_worker_dispatch.py::test_deep_reading_dispatch -v 2>&1 | tail -10</automated>
  </verify>
  <done>deep-reading dispatch test passes</done>
</task>

</tasks>

<verification>
- `paperforge deep-reading --help` shows --verbose option
- `paperforge deep-reading` output has three sections (就绪/等待 OCR/阻塞)
- `paperforge deep-reading --verbose` shows fix instructions for blocked papers
</verification>

<success_criteria>
paperforge deep-reading outputs three distinct states (ready/waiting/blocked) and --verbose flag shows fix instructions
</success_criteria>

<output>
After completion, create `.planning/phases/04-onboarding-validation/04-01-SUMMARY.md`
</output>
