Files
dl-organizer/docs/TECHNICAL_REVIEW.md
T

153 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Technical review: video-library-manager
## Metadata
| Field | Value |
|--------|--------|
| Repository path | `dl-organizer` (package `vlm`) |
| Verification date (UTC) | 2026-04-06 |
| Git revision verified | `ea21e15` |
| Test run | `uv run pytest -q`**496 passed** |
## Scope
- Source: `src/vlm/**/*.py`, `tests/`
- Config: `pyproject.toml`
- Assumption: single-user local library; artifacts semi-trusted; no formal threat model in repo.
## Executive summary
Architecture and tests are solid. Verified gaps: **no `library_root` check on move/rename source** in executor; **`by_quality` falls back to index 0 without surfacing reason**; **quarantine raises `ValueError` for unsupported category** while `execute_plan` has no per-operation try/except, so one bad op can abort the run; **duplicate join uses exact string path keys**; **`find` non-zero exit still consumes stdout**.
Weighted score (010): **8.0**.
## Score rubric
| Criterion | Score | Notes |
|-----------|-------|--------|
| Module boundaries / pipeline | 8.5 | Commands → domain modules; scan→parse→analyze→plan→execute is explicit. |
| Execution safety (filesystem) | 7.0 | Destination under root checked; **source not checked** for move/rename. |
| Planning / duplicate logic | 7.5 | Multiple strategies; string path identity; silent `by_quality` fallback; unknown `strategy` → keep index 0 (`duplicate_resolve.py:48`). |
| Data I/O & validation | 8.0 | `io.py` validates identities/analysis/plan shapes; validated objects remain dict-typed at boundaries (`type: ignore` in places). |
| Error handling consistency | 7.5 | Quarantine: `raise` vs `OperationResult` mismatch on category rejection. |
| Test signal | 8.5 | Broad `tests/`; path safety and resolver covered (`test_path_safety.py`, `test_duplicate_resolve.py`, etc.). |
| Dependencies | 9.0 | Runtime: `click`, `pyyaml` only (`pyproject.toml:79`). |
## Findings
### F1 [High] Move/rename: source not constrained to `library_root`
**Evidence:** `ExecutionEngine._perform_operation` checks destination with `is_within_root(..., self.config.library_root)` then calls `operation.source_path.rename(...)` without checking the source (`executor.py:354377`).
**Impact:** Malformed or hand-edited `plan.json` can rename **from any path the process can access** into the library tree. `QuarantineManager.quarantine_file` instead requires `file_path.relative_to(self.config.library_root)` (`quarantine.py:133137`).
**Recommendation:** For `move` / `rename`, require `is_within_root(operation.source_path, self.config.library_root)` (after `Path` resolution policy is defined). Fail with `OperationResult(success=False)`. Optional config gate for deliberate “import from outside,” default off. Add test alongside `tests/test_path_safety.py:76103`.
---
### F2 [Medium] `by_quality`: silent fallback to first item
**Evidence:** `choose_keep_index``if quality_comparison is None or len(quality_comparison) != len(items): return 0` (`duplicate_resolve.py:4042`).
**Impact:** Behavior equals **input order**, not quality, with no structured flag in plan metadata from this function alone.
**Recommendation:** Return `None` and skip auto-quarantine for that group, or require CLI/plan failure when `duplicate_keep == by_quality` and rows misaligned, or write explicit `metadata` / summary line when fallback occurs.
---
### F3 [Medium] Duplicate group ↔ identities join: exact path strings
**Evidence:** `path_to_index = {str(vf.path): i for ...}` and `indices = [path_to_index[p] for p in paths if p in path_to_index]` (`planner.py:108111`). Analysis `files` must match `str(VideoFile.path)` byte-for-byte.
**Impact:** Symlinks, differing normalization between analysis writer and identities loader, or OS case rules can **drop** files from duplicate resolution.
**Recommendation:** Normalize keys (e.g. `utils.canonical_path_str`) at both analysis emission and plan consumption; document contract; regression test if symlinks are in scope.
---
### F4 [LowMedium] Quarantine: `ValueError` vs `OperationResult`
**Evidence:** Unsupported category path does `raise ValueError(error_msg)` after logging (`quarantine.py:116129`). Other failure modes return `OperationResult`.
**Evidence:** `execute_plan` loop calls `execute_operation` with no try/except (`executor.py:110112`).
**Impact:** One invalid quarantine operation (e.g. manual plan) can **abort** the whole execute pass instead of recording a single failed result.
**Recommendation:** Return failed `OperationResult` for unsupported category; reserve exceptions for invariant violations only.
---
### F5 [Low] `find` non-zero exit: stdout still parsed
**Evidence:** On `process.returncode != 0`, code logs stderr if present, then always parses `stdout` into paths (`scanner.py:178192`).
**Impact:** Partial or stale listing possible without hard failure.
**Recommendation:** Document behavior; optionally fail if `returncode != 0` and empty result, or add `--strict-scan`.
---
### F6 [Low] Optional dependencies: `textual` in both `dev` and `tui`
**Evidence:** `pyproject.toml:1316` (`dev`), `1819` (`tui`).
**Impact:** Install surface ambiguity only.
**Recommendation:** Document that `dev` includes Textual for TUI-related tests, or slim `dev` if CI wants fewer deps.
---
### F7 [Informational] Validated JSON remains dict-typed at I/O edge
**Evidence:** `validate_plan_json` returns `dict` (`io.py:224251`); constructors may still bridge dict ↔ dataclass elsewhere.
**Impact:** Field drift between validator and `models.py` possible over time.
**Recommendation:** Single factory path: validated dict → domain objects for hot paths.
---
### F8 [Informational] Unknown `duplicate_keep` strategy string
**Evidence:** After strategy checks, `choose_keep_index` falls through to `return 0` (`duplicate_resolve.py:48`).
**Impact:** Config typo **keeps first duplicate** like `first_seen` without failing load.
**Recommendation:** Validate `duplicate_keep` in `config.py` / `validate_config` against an allowlist; reject unknown values.
## Verified strengths
| Claim | Evidence |
|--------|----------|
| `find` invoked without shell string | argv list: `subprocess.Popen(command, ...)` (`scanner.py:171172`, `164169`). |
| Path component sanitization | `sanitize_path_component` strips controls and separators (`utils.py:6575`); planner uses it before templates (`planner.py:301`, `419`). |
| Destination under library in planner | `is_within_root(destination, config.library_root)` (`planner.py:321`, `440`). |
| Quarantine manifest two-phase | Pending manifest write before `rename` (`quarantine.py:221268` region). |
| JSON artifact validation | `_validate_parsed_identities_json`, `_validate_analysis_json`, `validate_plan_json` (`io.py:157251`). |
| Parallel ffprobe | `ThreadPoolExecutor` when `include_video_metadata` and more than one path (`scanner.py:85101`). |
## Priority order
1. F1
2. F2
3. F4
4. F3
5. F8, F5, F6, F7
## Verification log (document vs codebase)
| Statement in this doc | Checked against |
|------------------------|-----------------|
| F1 source not under root guard | `executor.py:321377` |
| F2 by_quality fallback | `duplicate_resolve.py:4043` |
| F3 path_to_index | `planner.py:108111` |
| F4 raise + no try in loop | `quarantine.py:116129`, `executor.py:110112` |
| F5 find returncode | `scanner.py:178192` |
| F6 pyproject optional | `pyproject.toml:1220` |
| F7 validate_plan_json returns dict | `io.py:224251` |
| F8 unknown strategy | `duplicate_resolve.py:3648` |
| Strengths table | `scanner.py`, `utils.py`, `planner.py`, `quarantine.py`, `io.py` as cited |
| 496 tests passed | `uv run pytest -q` on 2026-04-06 |
This document is evidence-based against the paths above; behavior not re-listed here is **not** claimed verified.