---
phase: 46-index-path-resolution
plan: 002
type: execute
wave: 1
depends_on: []
files_modified:
  - paperforge/worker/base_views.py
  - paperforge/worker/discussion.py
autonomous: true
requirements:
  - PATH-05
  - PATH-06

must_haves:
  truths:
    - "Shipping .base templates contain zero ${LIBRARY_RECORDS} placeholders — the substitution key no longer exists in code"
    - "discussion.py path construction on Windows uses forward slashes throughout (pathlib handles them natively)"
  artifacts:
    - path: "paperforge/worker/base_views.py"
      provides: "Removed LIBRARY_RECORDS placeholder substitution — no .base template references it"
      contains: "LITERATURE.*CONTROL_DIR"  # only LITERATURE and CONTROL_DIR remain
    - path: "paperforge/worker/discussion.py"
      provides: "Unix-style path with no os.name-dependent backslash replacement"
      pattern: "ai_dir = vault_path / ai_path_str$"
  key_links:
    - from: "paperforge/worker/base_views.py:154 (removed)"
      to: "paperforge/config.py:331 library_records path"
      via: "paths.get('library_records') — was orphan substitution, no .base file uses ${LIBRARY_RECORDS}"
    - from: "paperforge/worker/discussion.py:266"
      to: "paperforge/worker/asset_index.py:338"
      via: "ai_path_str comes from canonical index entry 'ai_path' field — already uses forward slashes per PATH-01"
---

<objective>
Remove dead placeholder substitution `${LIBRARY_RECORDS}` from `base_views.py` and eliminate unnecessary Windows path backslash replacement in `discussion.py`.

**Purpose:** Two low-risk but correctness-critical cleanups: (1) No `.base` template references `${LIBRARY_RECORDS}` so the substitution code path is dead; removing it prevents future confusion. (2) `ai_path_str` from the canonical index already uses forward slashes (per PATH-01), and `pathlib.Path` on Windows handles forward slashes natively — the `replace("/","\\")` is an unnecessary no-op that masks path semantics.

**Output:**
- `base_views.py` — substitution dict has 2 entries (LITERATURE, CONTROL_DIR) instead of 3
- `discussion.py` — direct path concatenation without Windows-specific backslash replacement
</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/46-index-path-resolution/46-CONTEXT.md
@.planning/REQUIREMENTS.md

**Key codebase surfaces:**
- `paperforge/worker/base_views.py:140-167` — `substitute_config_placeholders()` replaces `${SCREAMING_SNAKE_CASE}` tokens in .base file content. Line 154 maps `LIBRARY_RECORDS` to `paths.get("library_records")`. No shipping `.base` template uses this token (verified by grep across the codebase — only this one line appears).
- `paperforge/worker/discussion.py:263-269` — `ai_path_str` from canonical index (already forward-slash normalized per PATH-01). Line 266 does `ai_path_str.replace("/", "\\") if os.name == "nt"` which is unnecessary since `pathlib.Path` on Windows handles `/` natively.

**Verification that no .base file uses ${LIBRARY_RECORDS}:**
```bash
# Run this before and after the change to confirm no breakage
rg '\$\{LIBRARY_RECORDS\}' paperforge/worker/ paperforge/ --include '*.base' 2>/dev/null || echo "No .base files in paperforge/"
# Also check installed vault .base files — they're generated at sync time, not shipping with templates
```
</context>

<tasks>

<task type="auto">
  <name>Task 1: Remove ${LIBRARY_RECORDS} placeholder substitution from base_views.py (PATH-05)</name>
  <files>paperforge/worker/base_views.py</files>
  <action>
    Remove the orphaned `LIBRARY_RECORDS` entry from the `substitutions` dict in `substitute_config_placeholders()` (line 154).

    The current code (lines 152-156):
    ```python
        substitutions = {
            "LITERATURE": paths.get("literature"),
            "LIBRARY_RECORDS": paths.get("library_records"),
            "CONTROL_DIR": paths.get("control"),
        }
    ```

    Change to:
    ```python
        substitutions = {
            "LITERATURE": paths.get("literature"),
            "CONTROL_DIR": paths.get("control"),
        }
    ```

    Rationale:
    - No shipping `.base` template (in `paperforge/` package or generated by `ensure_base_views()`) contains `${LIBRARY_RECORDS}` — confirmed by `rg '\$\{LIBRARY_RECORDS\}'` returning only this definition line.
    - The `library_records` path key remains in `paperforge_paths()` (it's still used by `sync.py:722` for v1.9 backward compat, removed in Phase 47). The path key is not being removed, only the unused substitution.
    - The substitution function's contract ("Unrecognized placeholders are left unchanged" per docstring line 150) is maintained — if any user-created `.base` file somehow has `${LIBRARY_RECORDS}`, it will remain as-is after this change.
  </action>
  <verify>
    <automated>
      $content = Get-Content "paperforge/worker/base_views.py" -Raw
      if ($content -match 'LIBRARY_RECORDS') {
        throw "FAIL: LIBRARY_RECORDS still present in base_views.py"
      }
      if ($content -notmatch 'LITERATURE.*CONTROL_DIR' -and $content -notmatch 'CONTROL_DIR') {
        throw "FAIL: CONTROL_DIR substitution should still exist"
      }
      if ($content -notmatch 'LITERATURE') {
        throw "FAIL: LITERATURE substitution should still exist"
      }
      Write-Output "[OK] LIBRARY_RECORDS substitution removed; LITERATURE and CONTROL_DIR preserved"
    </automated>
  </verify>
  <done>
    - `substitute_config_placeholders()` substitutions dict contains exactly 2 entries: `LITERATURE` and `CONTROL_DIR`
    - `rg '\$\{LIBRARY_RECORDS\}'` returns zero matches across the entire `paperforge/worker/` directory
    - Python syntax check: `python -c "import ast; ast.parse(open('paperforge/worker/base_views.py').read())"`
    - The `library_records` key in `paperforge_paths()` is unaffected (still exists at config.py line 331) — other consumers are not impacted
  </done>
</task>

<task type="auto">
  <name>Task 2: Remove unnecessary Windows path backslash replacement in discussion.py (PATH-06)</name>
  <files>paperforge/worker/discussion.py</files>
  <action>
    Simplify the `ai_dir` path construction at line 266 by removing the unnecessary `os.name`-dependent backslash replacement.

    Current code (lines 264-269):
    ```python
        ai_path_str = meta.get("ai_path", "")
        if ai_path_str:
            ai_dir = vault_path / ai_path_str.replace("/", "\\") if os.name == "nt" else vault_path / ai_path_str
        else:
            ai_dir = _build_ai_dir(vault_path, domain, zotero_key, paper_title)
    ```

    Change to:
    ```python
        ai_path_str = meta.get("ai_path", "")
        if ai_path_str:
            ai_dir = vault_path / ai_path_str
        else:
            ai_dir = _build_ai_dir(vault_path, domain, zotero_key, paper_title)
    ```

    Rationale:
    - `ai_path_str` comes from the canonical index entry's `"ai_path"` field, which is already forward-slash normalized (per PATH-01, the field is `str(workspace_dir.relative_to(vault)).replace("\\", "/") + "/"`).
    - `pathlib.Path` on Windows handles forward slashes natively — `Path(r"vault") / "Papers/domain/key - title/ai/"` works correctly on all platforms.
    - The `replace("/", "\\")` was a cargo-cult from early v1.6 code that assumed Windows APIs needed backslashes. This is false for `pathlib.Path` which normalizes separators automatically.
    - This also removes the `import os` dependency at the top of the file IF `os` is only used here. Check the file's other `os` references before removing the import.

    After the change, check if `os` is still used elsewhere in `discussion.py`. If `os` was imported at the top but only used here, remove the `import os` line as well. If it's used elsewhere (e.g., `os.environ`, `os.path.exists`), leave the import.
  </action>
  <verify>
    <automated>
      $content = Get-Content "paperforge/worker/discussion.py" -Raw
      if ($content -match 'replace\("/",\s*"\\\\"\)') {
        throw "FAIL: Backslash replace still present in discussion.py"
      }
      if ($content -match 'ai_dir = vault_path / ai_path_str\.replace') {
        throw "FAIL: Conditional path construction still has replace logic"
      }
      if ($content -notmatch 'ai_dir = vault_path / ai_path_str') {
        throw "FAIL: Expected simplified ai_dir assignment not found"
      }
      Write-Output "[OK] Unnecessary Windows path replacement removed"
    </automated>
  </verify>
  <done>
    - `ai_dir` is constructed as `vault_path / ai_path_str` (no conditional, no backslash replacement)
    - Python syntax check: `python -c "import ast; ast.parse(open('paperforge/worker/discussion.py').read())"`
    - If `os` import was only used for `os.name` in the removed code, the import is also removed (no dangling imports)
  </done>
</task>

</tasks>

<verification>
**Overall verification for this plan:**

1. Import validation — both modules load without errors:
   ```bash
   python -c "from paperforge.worker.base_views import substitute_config_placeholders; print('base_views.py OK')"
   python -c "from paperforge.worker.discussion import start_discussion_session; print('discussion.py OK')"
   ```

2. Static analysis — verify no residual references:
   ```bash
   # No LIBRARY_RECORDS anywhere in the worker package
   rg -n 'LIBRARY_RECORDS' paperforge/worker/
   
   # No forward-slash-to-backslash replace in discussion.py path construction
   rg -n 'replace.*/.*\\\\' paperforge/worker/discussion.py
   ```

3. Cross-reference check — `paperforge.config.paperforge_paths()` `library_records` key is NOT removed by this task (only the unused Base template substitution is removed). Verify it still exists:
   ```bash
   python -c "from paperforge.config import paperforge_paths; print('library_records' in paperforge_paths.__doc__ or 'library_records in paperforge_paths output')"
   ```
</verification>

<success_criteria>
1. `rg 'LIBRARY_RECORDS' paperforge/worker/base_views.py` returns no matches — dead substitution key removed.
2. `rg 'replace.*/.*\\\\' paperforge/worker/discussion.py` returns no matches — Windows path backslash replacement removed.
3. The `library_records` path key in `paperforge_paths()` is NOT removed (still exists in config.py:331 for remaining Phase 47 consumers).
4. Both modified files pass Python import validation.
</success_criteria>

<output>
After completion, create `.planning/phases/46-index-path-resolution/46-002-SUMMARY.md`
</output>
