mirror of
https://github.com/lllin000/PaperForge.git
synced 2026-07-22 17:00:23 +00:00
- Add named exports for testable functions (resolvePythonExecutable, getPluginVersion, etc.) - Update test imports from ../src/*.js to ../main.js - Fix repair action test: disabled flag was removed when ACTIONS was inlined
269 lines
11 KiB
Markdown
269 lines
11 KiB
Markdown
---
|
|
phase: pr-3-review
|
|
reviewed: 2026-05-08T12:00:00Z
|
|
depth: deep
|
|
files_reviewed: 21
|
|
files_reviewed_list:
|
|
- AGENTS.md
|
|
- README.md
|
|
- docs/INSTALLATION.md
|
|
- docs/setup-guide.md
|
|
- manifest.json
|
|
- paperforge/__init__.py
|
|
- paperforge/ocr_diagnostics.py
|
|
- paperforge/plugin/main.js
|
|
- paperforge/plugin/manifest.json
|
|
- paperforge/plugin/styles.css
|
|
- paperforge/plugin/versions.json
|
|
- paperforge/worker/status.py
|
|
- paperforge/worker/sync.py
|
|
- pyproject.toml
|
|
- scripts/bump.py
|
|
- tests/conftest.py
|
|
- tests/test_asset_index_integration.py
|
|
- tests/test_asset_state.py
|
|
- tests/test_command_docs.py
|
|
- tests/test_migration.py
|
|
- tests/test_plugin_install_bootstrap.py
|
|
findings:
|
|
critical: 0
|
|
warning: 6
|
|
info: 3
|
|
total: 9
|
|
status: issues_found
|
|
---
|
|
|
|
# PR #3: Code Review Report
|
|
|
|
**Reviewed:** 2026-05-08T12:00:00Z
|
|
**Depth:** deep (cross-file + call-chain + type consistency)
|
|
**Files Reviewed:** 21
|
|
**Status:** issues_found (6 WARNING, 3 INFO, 0 BLOCKER)
|
|
|
|
## Summary
|
|
|
|
Pull Request #3 ("milestone/v1.12-clean") delivers the "plugin runtime closure" feature set: interpreter override, consistent subprocess resolution via `resolvePythonExecutable()`, Dashboard workflow closure (OCR queue add/remove, once-per-session privacy warning, `/pf-deep` command copy with agent platform label), and stronger `doctor` diagnostics. It also consolidates the deep-reading data model to main-note-only and removes stale `docs/` files (INSTALLATION.md, setup-guide.md).
|
|
|
|
**Overall assessment: NEARLY MERGE READY.** The code is logically sound, the architectural design is clean, and tests pass (501 passed, 2 skipped — both pre-existing platform-specific skips unrelated to this PR). However, 6 WARNING-level issues should be addressed before merging. No BLOCKER-level issues were found.
|
|
|
|
---
|
|
|
|
## Checklist Results
|
|
|
|
| Item | Verdict |
|
|
|------|---------|
|
|
| **Diff clean?** | YES — No .planning artifacts, no unrelated changes, doc deletions are intentional |
|
|
| **Code logically sound for runtime closure?** | YES — Interpreter resolution, runtime health, Dashboard DASH-01/02/03 workflow closure all achieved |
|
|
| **Obvious bugs, regressions, missing error handling?** | 6 WARNING issues found (see below) |
|
|
| **Test suite sufficient?** | YES — 501 passed, 2 skipped (both pre-existing platform skips in `test_pdf_resolver.py`) |
|
|
| **Version alignment clean?** | YES — All three version sources aligned at `1.4.17rc3` |
|
|
| **Cross-file consistency?** | 1 issue: `versions.json` retroactively changed minAppVersion for an old release |
|
|
|
|
---
|
|
|
|
## Warnings
|
|
|
|
### WR-01: Hardcoded stale fallback version strings
|
|
|
|
**File:** `paperforge/plugin/main.js:1482`, `paperforge/plugin/main.js:1728`
|
|
**Issue:** Two places hardcode `'1.4.17rc2'` as a fallback when `manifest.version` is falsy:
|
|
|
|
```javascript
|
|
// Line 1482
|
|
const ver = this.plugin.manifest.version || '1.4.17rc2';
|
|
|
|
// Line 1728
|
|
const ver = this.manifest.version || '1.4.17rc2';
|
|
```
|
|
|
|
The version has been bumped to `1.4.17rc3` everywhere else, but these fallbacks remain at `rc2`. If `manifest.version` is ever undefined (corrupted settings, race condition during load), the wrong version tag would be passed to `pip install`. While unlikely in practice, this is a maintenance magnet that will inevitably be missed on future bumps.
|
|
|
|
**Fix:** Replace with a dynamic reference or a generic fallback:
|
|
|
|
```javascript
|
|
const ver = this.plugin.manifest.version;
|
|
// If ver is falsy, don't proceed with sync at all
|
|
if (!ver) {
|
|
new Notice('[!!] Cannot sync: plugin version unknown', 6000);
|
|
return;
|
|
}
|
|
```
|
|
|
|
Or at minimum, align with the current version:
|
|
|
|
```javascript
|
|
const ver = this.plugin.manifest.version || '1.4.17rc3';
|
|
```
|
|
|
|
---
|
|
|
|
### WR-02: versions.json retroactively changed minAppVersion for old release 1.4.3
|
|
|
|
**File:** `paperforge/plugin/versions.json:2`
|
|
**Issue:** The entry `"1.4.3": "1.0.0"` was changed to `"1.4.3": "1.9.0"`.
|
|
|
|
In Obsidian's plugin update mechanism, `versions.json` maps each plugin version to the minimum Obsidian version required **for that specific release**. Old entries must remain unchanged — they represent the requirements that were valid at the time of that release. Version `1.4.3` did NOT require Obsidian `1.9.0`.
|
|
|
|
Only the **new** version entry (`1.4.17rc3`) should map to `"1.9.0"`. The old `1.4.3` entry should stay at `"1.0.0"`.
|
|
|
|
```diff
|
|
{
|
|
- "1.4.3": "1.9.0",
|
|
+ "1.4.3": "1.0.0",
|
|
"1.4.17rc3": "1.9.0"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### WR-03: JS `resolvePythonExecutable` only has Windows-style venv paths
|
|
|
|
**File:** `paperforge/plugin/main.js:884-887`
|
|
**Issue:** The JavaScript `resolvePythonExecutable()` function hardcodes Windows-only venv paths:
|
|
|
|
```javascript
|
|
const venvCandidates = [
|
|
path.join(vaultPath, '.paperforge-test-venv', 'Scripts', 'python.exe'),
|
|
path.join(vaultPath, '.venv', 'Scripts', 'python.exe'),
|
|
path.join(vaultPath, 'venv', 'Scripts', 'python.exe'),
|
|
];
|
|
```
|
|
|
|
On macOS/Linux (where Obsidian/Electron also runs), virtualenv Python binaries live in `bin/python`, not `Scripts/python.exe`. The Python counterpart in `status.py` (`_resolve_plugin_interpreter`) correctly handles this with an `os.name == "nt"` check. The JavaScript version does not, meaning venv detection silently falls through on POSIX systems.
|
|
|
|
**Fix:** Add platform-aware venv paths:
|
|
|
|
```javascript
|
|
const isWin = process.platform === 'win32';
|
|
const venvCandidates = isWin ? [
|
|
path.join(vaultPath, '.paperforge-test-venv', 'Scripts', 'python.exe'),
|
|
path.join(vaultPath, '.venv', 'Scripts', 'python.exe'),
|
|
path.join(vaultPath, 'venv', 'Scripts', 'python.exe'),
|
|
] : [
|
|
path.join(vaultPath, '.paperforge-test-venv', 'bin', 'python'),
|
|
path.join(vaultPath, '.venv', 'bin', 'python'),
|
|
path.join(vaultPath, 'venv', 'bin', 'python'),
|
|
];
|
|
```
|
|
|
|
---
|
|
|
|
### WR-04: `execFileSync` in synchronous code path can block UI thread
|
|
|
|
**File:** `paperforge/plugin/main.js:897-914`
|
|
**Issue:** `resolvePythonExecutable()` uses `execFileSync` (synchronous) to test system candidates, with a 5-second timeout per candidate, up to 3 candidates. This function is called during **synchronous rendering** of both the Settings tab (`display()`) and the Dashboard (`_renderGlobalMode()`).
|
|
|
|
If Python is not properly installed and `py -3`, `python`, or `python3` each time out, the Obsidian render thread blocks for up to 15 seconds. This causes a visible UI freeze.
|
|
|
|
**Fix:** Two options:
|
|
|
|
1. **Async-first design**: Make `resolvePythonExecutable` async, cache the resolved interpreter, and use the cached value during rendering. The system candidate probe runs once asynchronously.
|
|
|
|
2. **Use `execFile` instead of `execFileSync`**: Replace the system candidate loop with an async approach:
|
|
|
|
```javascript
|
|
// Return cached result if already probed
|
|
if (resolvePythonExecutable._cache) return resolvePythonExecutable._cache;
|
|
|
|
// During synchronous rendering, skip the execFileSync probe entirely
|
|
// and let the async probe update the cache later.
|
|
```
|
|
|
|
---
|
|
|
|
### WR-05: Bare `Exception` catch in `_read_plugin_data`
|
|
|
|
**File:** `paperforge/worker/status.py:1963`
|
|
**Issue:** The `_read_plugin_data` function catches bare `Exception`, which can suppress `KeyboardInterrupt`, `MemoryError`, and other system-level exceptions:
|
|
|
|
```python
|
|
except (json.JSONDecodeError, OSError, Exception):
|
|
return {}
|
|
```
|
|
|
|
Since `OSError` already covers the `PermissionError`/`FileNotFoundError` cases, the bare `Exception` is redundant and dangerous.
|
|
|
|
**Fix:**
|
|
|
|
```python
|
|
except (json.JSONDecodeError, OSError):
|
|
return {}
|
|
```
|
|
|
|
---
|
|
|
|
### WR-06: Zotero data directory changed from optional to required without migration path
|
|
|
|
**Files:** `paperforge/plugin/main.js` (setup wizard validation, step 4 logic, summary page)
|
|
**Issue:** The Zotero data directory was previously optional (with placeholder text "可选,用于自动检测 PDF"). It is now required, with strict validation: not empty, exists, is a directory, and contains `storage/` subdirectory.
|
|
|
|
Existing users who completed setup with `zotero_data_dir` left empty will encounter setup wizard validation failures on their next reconfiguration visit. There is no migration logic to detect this state and prompt the user, nor an auto-discovery mechanism to fill in a likely path.
|
|
|
|
**Fix:** Add a one-time migration notice or auto-discovery:
|
|
|
|
```javascript
|
|
// On settings load, if zotero_data_dir is empty, attempt auto-discovery:
|
|
if (!s.zotero_data_dir) {
|
|
const candidates = [
|
|
path.join(os.homedir(), 'Zotero'),
|
|
path.join(os.homedir(), 'Zotero', 'storage'),
|
|
];
|
|
for (const c of candidates) {
|
|
if (fs.existsSync(path.join(c, 'storage'))) {
|
|
s.zotero_data_dir = c;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Info
|
|
|
|
### IN-01: Repetitive `require('fs')` calls in method bodies
|
|
|
|
**File:** `paperforge/plugin/main.js` — methods `_validatePythonOverride`, `_syncRuntime`, `_preCheck`, `_validateSetup`, etc.
|
|
**Issue:** `require('fs')`, `require('path')`, and `require('node:child_process')` are called at the top of individual methods instead of once at module scope. This is wasteful (Node caches `require` calls, so there's no runtime penalty) but is a readability/maintainability concern.
|
|
|
|
**Suggestion:** Move all `require()` calls to module scope at the top of the file.
|
|
|
|
---
|
|
|
|
### IN-02: `run_doctor()` return value semantics changed
|
|
|
|
**File:** `paperforge/worker/status.py` (near line 2290)
|
|
**Issue:** The return value of `run_doctor()` changed semantics:
|
|
|
|
- **Before:** returned `1` if `fix_map` had entries (any issue with a suggested fix, including warnings)
|
|
- **After:** returns `1 if has_fail else 0` (only hard failures)
|
|
|
|
Callers (CI scripts, `paperforge doctor` CLI handler) may rely on exit code `1` for any actionable issue. Verify that the CLI handler for `doctor` checks for warnings separately. If not, a warnings-only state would now produce exit code 0 when it previously produced 1.
|
|
|
|
---
|
|
|
|
### IN-03: `versions.json` missing trailing newline
|
|
|
|
**File:** `paperforge/plugin/versions.json`
|
|
**Issue:** The file ends without a trailing newline (`\n` at EOF). Minor POSIX compatibility concern — some tools (e.g., `diff`, `cat`) warn about missing trailing newlines.
|
|
|
|
---
|
|
|
|
## Conclusion
|
|
|
|
**Verdict: CONDITIONAL MERGE** — fix the 6 WARNING issues first.
|
|
|
|
The three most important fixes are:
|
|
1. **WR-01** — Fix hardcoded `'1.4.17rc2'` fallbacks (quick fix, prevents latent version drift)
|
|
2. **WR-02** — Restore `"1.4.3": "1.0.0"` in versions.json (data integrity for Obsidian update mechanism)
|
|
3. **WR-05** — Remove bare `Exception` catch in `_read_plugin_data` (defensive coding)
|
|
|
|
WR-03 and WR-04 are cross-platform correctness issues; WR-06 is a UX gap for existing users. None block the merge but should be documented as known limitations.
|
|
|
|
The structural design of the PR is sound. The interpreter resolution refactoring (returning `{path, source, extraArgs}` instead of a bare string) is cleanly applied across all call sites. The Dashboard workflow closure (DASH-01/02/03) is well-implemented with proper state management. The deep-reading model consolidation to main-note-only is consistent across JS plugin, Python backend, and tests.
|
|
|
|
---
|
|
|
|
_Reviewed: 2026-05-08T12:00:00Z_
|
|
_Reviewer: VT-OS/OPENCODE (gsd-code-reviewer)_
|
|
_Depth: deep_
|