Includes: - cli.py path fix (parents[1]) for config/catalog resolution - Library cleanup feature design docs (TODO.md, mockup) - Audit + bug-queue markdowns from May 2026 reliability pass - .gitignore expanded for transient artifacts
6.4 KiB
Bug Report — Python CLI — audit-snapshot-2026-05-24T15-55Z.md
Snapshot: audit-snapshot-2026-05-24T15-55Z.md Required-reading docs read: AGENTS.md / TODO.md / CACHE_CONTRACT.md (at D:\DEV\Extensions\Production\rclone-jav\docs\CACHE_CONTRACT.md) / bug-audit-plan.md / project memory Auditor agent: fresh Explore agent (chunk 1 auditor) Verifier agents: fresh Explore agents per candidate, blind context, stricter contract-check prompt
This file contains CONFIRMED + PARTIAL findings only. Candidate scratch lives in bugs-candidates-python.md. REFUTED / NEEDS-INFO candidates stay in scratch with verifier response appended.
Chunk 1 calibration note: Moderate verification yielded 1 confirmed bug with 75% rejection rate (3/4 REFUTED). Auditor's recurring weakness: flagging f["key"] direct access as KeyError risk without checking the contract that guarantees the dict shape upstream (rclone lsjson schema, cache.json schema enforced by load_cache validation + CACHE_CONTRACT.md). Stricter verifier prompt with required contract-check caught all 3 false positives. Light candidates were NOT verified per audit-plan stop condition (>30% rejection → halt L verification). The Python auditor likely shares a similar pattern-matching weakness on L candidates — revisit only if needed. See bugs-candidates-python.md for unverified L list (C-5, C-6, C-7, C-8, C-9).
Severe (S)
(none flagged by auditor in this chunk)
Moderate (M)
M-1 — save_config lacks Windows file-locking retry that save_cache has
- File:
D:\DEV\Project\rclone-jav\rcjav\cli.py:186-189(save_config), with comparison atrcjav/cache.py:142-147(save_cache) - Symptom (one sentence): When a user runs
--savewhile config.json is briefly locked by antivirus, Windows Search indexer, or any reader,os.replace(tmp, CONFIG_PATH)raises uncaught PermissionError and the user sees a Python traceback — config write fails.save_cachefor the same os.replace pattern has explicit PermissionError + 0.5s retry;save_configdoes not. - Why it's a bug: Asymmetric protection.
save_cache(cache.py:142-147):try: os.replace(tmp, CACHE_PATH) except PermissionError: time.sleep(0.5); os.replace(tmp, CACHE_PATH)save_config(cli.py:186-189):Single call site at cli.py:465 insidetmp.write_text(json.dumps(cfg, indent=2), encoding="utf-8") os.replace(tmp, CONFIG_PATH)--saveflag handler, NOT wrapped in try/except. Outer exception handler at cli.py:1000-1004 catches only KeyboardInterrupt. PermissionError propagates uncaught → Python traceback to user. On Windows with active AV (Defender, Avast, etc.), file-lock-during-replace is common. - Reproduction:
- Input: user runs
python rc-jav.py --save --target cq:JAVwhile config.json is being read by another process (AV scan, Windows Search indexer reindexing, manual file open in editor) - Expected: write retries briefly + succeeds, OR clear "config write failed, retry" message
- Actual: PermissionError raised from os.replace, uncaught, Python prints traceback
PermissionError: [WinError 32] The process cannot access the file because it is being used by another process. tmp file may remain on disk. Config not persisted. User confused.
- Input: user runs
- Suggested fix sketch: copy save_cache's pattern verbatim — wrap os.replace in try/except PermissionError with 0.5s sleep + single retry
- Verifier agent: fresh Explore, blind context, stricter prompt
- Verifier verdict: CONFIRMED
- Verifier confidence: high (95%)
- Contract refs verifier read: save_cache implementation as comparison; outer exception handler scope
- Mirror check needed in: any other
os.replacecallsite inrcjav/package that writes user-visible config/state (search foros.replacein rcjav/ — only save_cache and save_config currently) - Status: fixed
- Fix:
D:\DEV\Project\rclone-jav\rcjav\cli.py:186-194— wrappedos.replace(tmp, CONFIG_PATH)in same try/except PermissionError + time.sleep(0.5) + retry pattern that save_cache uses (rcjav/cache.py:142-147). Now symmetric: both writers handle transient Windows file locks identically. Single retry (not infinite) — persistent locks still bubble PermissionError to caller, matching save_cache behavior.timealready imported in cli.py:14 — no new import needed. No manifest bump — CLI repo only, no extension files touched. Python syntax verified viapy_compile. Smoke-tested in isolation: (1) normal write produces correct file; (2) first os.replace raises PermissionError then succeeds on retry — final state correct, 0.5s sleep observed (2 calls, elapsed 0.50s); (3) persistent PermissionError on both attempts → bubbles up to caller (2 attempts, matches save_cache). Mirror check resolved: only save_cache and save_config use os.replace in rcjav/; both now have retry.
Light (L)
(none promoted — chunk 1 L verification skipped per stop condition)
Needs Input (N)
(none promoted)
False Positives (discarded)
-
rcjav/rclone_io.py:66— flagged as Moderate "rclone KeyError on Path". REFUTED. rclone lsjson output contract guaranteesPathfield on every item per official docs. Directitem["Path"]access is appropriate fail-fast for contract violation. Lines 77-78's.get()pattern for Size/ModTime is defensive over-engineering for those fields, NOT evidence Path needs the same. -
rcjav/library.py:257— flagged as Moderate "library cache KeyError on path". REFUTED via 3 converging facts: (1) CACHE_CONTRACT.md mandatespathkey on every file entry, (2)load_cache()(cache.py:67-106) validates schema before find_library_issues runs — non-conformant caches get wiped via_fresh_cache(), (3) FileEntry dataclass + every cache write site explicitly emitspath. The.get()pattern at cli.py:526 (--reextract) is defensive because that path reads cache.json directly without re-validation; library.py operates on already-validated data. -
rcjav/library.py:328-330— flagged as Moderate "rename_file KeyError on path/jav_id". REFUTED.fcomes only from cache entries (remote_data.get("files", [])), which are contract-guaranteed to havepath. Caller scalar args (old_rel_path,new_rel_path) are strings, not dicts. Line 330'sor f["jav_id"]fallback is forextract_idreturning None, NOT for missing key — correct design. Auditor conflated scalar caller args with iterated dict entries.