---
phase: 05-workflow-hardening-and-optional-plugin-shell
plan: '01'
type: execute
wave: 1
depends_on: []
files_modified:
  - tests/test_ocr_state_machine.py
  - tests/test_base_views.py
  - tests/test_base_preservation.py
  - tests/test_command_docs.py
autonomous: true
requirements:
  - REL-01
  - REL-03

must_haves:
  truths:
    - "OCR state transitions are tested end-to-end"
    - "Base rendering is tested with both default and custom paths"
    - "Command docs in AGENTS.md match actual CLI commands"
  artifacts:
    - path: "tests/test_ocr_state_machine.py"
      provides: "OCR job state transition tests (pending->processing->done/error/blocked)"
      min_lines: 60
    - path: "tests/test_base_views.py"
      provides: "Base view rendering with custom paths"
      min_lines: 50
    - path: "tests/test_base_preservation.py"
      provides: "User-edited .base files preserved on refresh"
      min_lines: 40
  key_links:
    - from: "tests/test_ocr_state_machine.py"
      to: "pipeline/worker/scripts/literature_pipeline.py"
      via: "run_ocr state transitions"
    - from: "tests/test_base_views.py"
      to: "paperforge/cli.py"
      via: "ensure_base_views rendering"
    - from: "AGENTS.md"
      to: "paperforge/cli.py"
      via: "command reference consistency"
---

<objective>
Fill test coverage gaps: OCR state machine, Base rendering with custom paths, and command documentation consistency. Addresses REL-01 (test coverage) and REL-03 (docs consistency).
</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/STATE.md
@.planning/ROADMAP.md
@.planning/REQUIREMENTS.md
@.planning/research/DEFECTS.md

# Interface contracts from existing code

From paperforge/cli.py (existing command surface):
```python
# Commands exposed:
# paperforge paths [--json]
# paperforge status
# paperforge selection-sync
# paperforge index-refresh
# paperforge ocr run / ocr doctor [--live]
# paperforge deep-reading [--verbose]
# paperforge base-refresh [--force]
# paperforge doctor
```

From pipeline/worker/scripts/literature_pipeline.py (OCR state machine):
```python
def run_ocr(vault: Path) -> int:
    # Job states: pending -> processing -> done/error/blocked
    # Reads ocr_queue.json, submits jobs, polls, updates state
def sync_ocr_queue(vault: Path, ...) -> int:
    # Syncs queue state with meta.json files
def cleanup_blocked_ocr_dirs(vault: Path) -> int:
    # Removes blocked directories
```

From paperforge/config.py (path resolver):
```python
def paperforge_paths(vault: Path, cfg: dict) -> dict[str, Path]:
    # Returns: vault, system, paperforge, exports, ocr, resources, literature, control, library_records, bases, worker_script, skill_dir, ld_deep_script
```
</context>

<tasks>

<task type="auto">
  <name>Task 1: OCR State Machine Tests</name>
<read_first>
  pipeline/worker/scripts/literature_pipeline.py (run_ocr, sync_ocr_queue, OCR_JOB_STATES if defined)
  tests/test_ocr_classify.py (existing OCR classification tests)
  tests/test_ocr_preflight.py (existing OCR preflight tests)
</read_first>
<files>tests/test_ocr_state_machine.py</files>
<action>
Create tests/test_ocr_state_machine.py with pytest fixtures and tests for:

1. **OCR job state transitions:**
   - Test: pending -> processing transition when job is submitted
   - Test: processing -> done transition when polling returns success
   - Test: processing -> error transition on API error response
   - Test: processing -> blocked transition when PDF unreadable

2. **Mock responses needed (use unittest.mock):**
   - Mock `requests.post` for job submission (return job_id)
   - Mock `requests.get` for polling (return various states)
   - Mock PDF file open for blocked transition

3. **Test structure:**
   - Use pytest tmp_path fixture for temporary vault
   - Mock network calls entirely — no real OCR API calls
   - Test state machine logic in isolation from network

4. **Coverage targets:**
   - At minimum: pending, processing, done, error, blocked states
   - sync_ocr_queue state reconciliation logic
   - cleanup_blocked_ocr_dirs behavior

**Example test structure:**
```python
def test_pending_to_processing_transition(tmp_path, mock_paddleocr_api):
    # Setup: create ocr-queue.json with pending job
    # Call: run_ocr(vault)
    # Assert: job state changed to processing

def test_processing_to_done_transition(tmp_path, mock_paddleocr_api):
    # Setup: ocr-queue.json with processing job
    # Mock polling returns done with result_url
    # Call: run_ocr(vault)
    # Assert: job state changed to done
```
</action>
<acceptance_criteria>
- tests/test_ocr_state_machine.py exists and runs without errors
- At least 4 test functions covering the major state transitions
- All mocks use unittest.mock patching — no real network calls
- pytest tests/ -k ocr_state passes with exit code 0
</acceptance_criteria>
<done>OCR state machine has comprehensive unit tests with mocked network calls</done>
</task>

<task type="auto">
  <name>Task 2: Base Rendering + Custom Path Tests</name>
<read_first>
  pipeline/worker/scripts/literature_pipeline.py (ensure_base_views function)
  tests/test_base_preservation.py (existing preservation tests)
  tests/test_base_views.py (existing base view tests)
  .planning/phases/03-obsidian-bases-config-aware/03-01-SUMMARY.md (what was built)
</read_first>
<files>
  tests/test_base_views.py
  tests/test_base_preservation.py
</files>
<action>
Extend existing base view tests to cover config-aware rendering with custom paths.

1. **tests/test_base_views.py — Add custom path rendering tests:**
   - Create tmp_path vault with custom paperforge.json (non-default directories)
   - Call ensure_base_views with custom cfg
   - Verify rendered Base files use the custom directory names in filter paths
   - Example: if custom control_dir is "MyControl", Base filters should reference "MyControl/library-records" not "LiteratureControl/library-records"

2. **tests/test_base_preservation.py — Extend user-edit preservation tests:**
   - Test that ensure_base_views with force=False does not overwrite user-edited sections
   - Test that force=True does a full regeneration
   - Test incremental merge behavior (existing user comments/data preserved)

3. **Base filter path verification:**
   - Read generated .base file
   - grep/parse for hardcoded paths vs config-derived paths
   - Assert: no hardcoded "03_Resources", "LiteratureControl", "Literature" strings in generated filters
</action>
<acceptance_criteria>
- tests/test_base_views.py has tests for custom path rendering that pass
- tests/test_base_preservation.py has tests for force/non-force behavior that pass
- Generated Base files with custom paths use the custom directory names
- No hardcoded default path strings in generated Base filter definitions
- pytest tests/ -k base passes with exit code 0
</acceptance_criteria>
<done>Base rendering works correctly with both default and custom directory configurations</done>
</task>

<task type="auto">
  <name>Task 3: Command Docs Consistency Check</name>
<read_first>
  AGENTS.md (user-facing command reference)
  paperforge/cli.py (actual command surface)
  docs/INSTALLATION.md (installation docs if exists)
  docs/README.md (setup docs if exists)
</read_first>
<files>AGENTS.md</files>
<action>
Verify and fix consistency between AGENTS.md command docs and the actual CLI implementation.

1. **Extract all commands from cli.py:**
   - Parse build_parser() subparsers to get all command names
   - Extract all subcommand structures (ocr run, ocr doctor, etc.)
   - Get all argument flags for each command

2. **Cross-reference with AGENTS.md:**
   - Find all `paperforge` command examples in AGENTS.md
   - Verify each command exists in cli.py
   - Verify each flag/argument exists in the parser
   - Verify behavior description matches actual implementation

3. **Fix inconsistencies:**
   - If AGENTS.md lists a command that doesn't exist in cli.py → remove from AGENTS.md or flag as issue
   - If cli.py has a command not in AGENTS.md → add to AGENTS.md
   - If flags differ → update AGENTS.md to match cli.py
   - Document any discrepancies found

4. **Output a consistency report:**
   - List of commands in AGENTS.md but not in CLI
   - List of commands in CLI but not in AGENTS.md
   - List of flag mismatches (if any)
   - Actions taken to resolve each
</action>
<acceptance_criteria>
- AGENTS.md commands match the actual cli.py command surface
- All subcommands (ocr run, ocr doctor, deep-reading --verbose) are documented
- No placeholder paths remain in documented commands (e.g., no `<system_dir>`)
- A consistency report is produced listing any fixes made
- The file AGENTS.md after fixes contains no broken command references
</acceptance_criteria>
<done>AGENTS.md command documentation is consistent with the actual CLI implementation</done>
</task>

</tasks>

<verification>
Run the full test suite:
```bash
pytest tests/ -v --tb=short
```

Expected: All tests pass including the new OCR state machine tests and base rendering tests.

Command docs consistency check:
```bash
# Verify AGENTS.md has no broken paperforge commands
# (manual verification by reading the consistency report produced)
```
</verification>

<success_criteria>
- [ ] tests/test_ocr_state_machine.py created with 4+ state transition tests
- [ ] tests/test_base_views.py extended with custom path rendering tests
- [ ] tests/test_base_preservation.py extended with force/non-force tests
- [ ] AGENTS.md command docs verified consistent with cli.py
- [ ] pytest tests/ -k "ocr_state or base" passes
- [ ] No placeholder paths remain in AGENTS.md documented commands
</success_criteria>

<output>
After completion, create `.planning/phases/05-workflow-hardening-and-optional-plugin-shell/05-01-SUMMARY.md`
</output>
