chore: trim dead code, modularize CLI, and archive stale docs
Extract review-plan, report, quarantine, state, and config handlers into commands/ with shared cli_helpers; remove unused exceptions and duplicate plan summary wrappers. Archive superseded review markdown, sync docs to 517-test baseline, and fix empty series titles when only a quality tag remains. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.5
Cursor
parent
5f0b531269
commit
79797644e1
+146
-45
@@ -10,7 +10,10 @@ from typing import Any
|
||||
|
||||
from vlm.models import ExecutionPlan, FileOperation, MovieIdentity, SeriesIdentity
|
||||
from vlm.review_display import display_path
|
||||
from vlm.utils import format_size, is_sample_path
|
||||
from vlm.utils import canonical_path_str, format_size, is_sample_path, utc_now
|
||||
|
||||
REVIEW_APPLIED_AT_KEY = "review_applied_at"
|
||||
REVIEW_CSV_PATH_KEY = "review_csv_path"
|
||||
|
||||
REVIEW_CSV_BASE_FIELDS = [
|
||||
"index",
|
||||
@@ -39,6 +42,14 @@ REVIEW_CSV_ALL_FIELDS = REVIEW_CSV_BASE_FIELDS + REVIEW_CSV_ENRICHED_FIELDS
|
||||
GROUP_BY_CHOICES = ("none", "reason", "title", "duplicate")
|
||||
|
||||
|
||||
def normalized_path_key(path_value: str | Path) -> str:
|
||||
"""Normalize path-like values for duplicate-group and lookup matching."""
|
||||
text = str(path_value).strip()
|
||||
if not text:
|
||||
return ""
|
||||
return canonical_path_str(Path(text.replace("\\", "/")))
|
||||
|
||||
|
||||
def _extract_season_episode(operation: FileOperation) -> tuple[int | None, int | None]:
|
||||
season: int | None = None
|
||||
episode: int | None = None
|
||||
@@ -156,10 +167,12 @@ def build_duplicate_path_maps(
|
||||
if not isinstance(quality_list, list):
|
||||
quality_list = []
|
||||
for file_path in dup.get("files", []) or []:
|
||||
path_key = str(file_path)
|
||||
path_key = normalized_path_key(file_path)
|
||||
if not path_key:
|
||||
continue
|
||||
path_to_group[path_key] = group_id
|
||||
for qc in quality_list:
|
||||
if isinstance(qc, dict) and str(qc.get("path")) == path_key:
|
||||
if isinstance(qc, dict) and normalized_path_key(qc.get("path", "")) == path_key:
|
||||
path_to_quality[path_key] = qc
|
||||
break
|
||||
|
||||
@@ -244,7 +257,7 @@ def enrich_review_row(
|
||||
enriched[key] = id_ctx[key]
|
||||
|
||||
if path_to_duplicate_group and source_path:
|
||||
gid = path_to_duplicate_group.get(source_path)
|
||||
gid = path_to_duplicate_group.get(normalized_path_key(source_path))
|
||||
if gid and not enriched.get("duplicate_group_id"):
|
||||
enriched["duplicate_group_id"] = gid
|
||||
|
||||
@@ -277,6 +290,43 @@ def enrich_review_rows(
|
||||
return result
|
||||
|
||||
|
||||
def _risk_flags_for_operation(
|
||||
op: FileOperation,
|
||||
*,
|
||||
season_threshold: int = 20,
|
||||
episode_threshold: int = 40,
|
||||
) -> list[str]:
|
||||
"""Return risk flag keys for an operation, or empty if not flagged."""
|
||||
if op.operation_type == "no-op" and op.reason.casefold().startswith("modified via manual review"):
|
||||
return []
|
||||
|
||||
flags: list[str] = []
|
||||
reason_l = op.reason.casefold()
|
||||
if "manual review" in reason_l:
|
||||
flags.append("manual_review")
|
||||
if is_sample_path(op.source_path):
|
||||
flags.append("sample_source")
|
||||
season, episode = _extract_season_episode(op)
|
||||
if season is not None and season >= season_threshold:
|
||||
flags.append("high_season")
|
||||
if episode is not None and episode >= episode_threshold:
|
||||
flags.append("high_episode")
|
||||
if op.has_conflict:
|
||||
flags.append("conflict")
|
||||
rc = op.review_context if hasattr(op, "review_context") else {}
|
||||
if isinstance(rc, dict) and rc.get("duplicate_group_id") and op.operation_type == "quarantine":
|
||||
if "duplicate" not in flags:
|
||||
flags.append("duplicate")
|
||||
elif "duplicate" in reason_l and "duplicate" not in flags:
|
||||
flags.append("duplicate")
|
||||
return flags
|
||||
|
||||
|
||||
def _is_actionable_high_risk(op: FileOperation, flags: list[str]) -> bool:
|
||||
"""True when review is required before confirmed execute (would modify files)."""
|
||||
return bool(flags) and op.operation_type != "no-op"
|
||||
|
||||
|
||||
def review_plan(
|
||||
plan: ExecutionPlan,
|
||||
season_threshold: int = 20,
|
||||
@@ -287,6 +337,7 @@ def review_plan(
|
||||
counters = {
|
||||
"total_operations": len(plan.operations),
|
||||
"high_risk_operations": 0,
|
||||
"review_export_rows": 0,
|
||||
"manual_review": 0,
|
||||
"sample_source": 0,
|
||||
"high_season": 0,
|
||||
@@ -295,46 +346,72 @@ def review_plan(
|
||||
}
|
||||
|
||||
for idx, op in enumerate(plan.operations, start=1):
|
||||
flags: list[str] = []
|
||||
reason_l = op.reason.casefold()
|
||||
if "manual review" in reason_l:
|
||||
flags.append("manual_review")
|
||||
flags = _risk_flags_for_operation(
|
||||
op,
|
||||
season_threshold=season_threshold,
|
||||
episode_threshold=episode_threshold,
|
||||
)
|
||||
if not flags:
|
||||
continue
|
||||
if "manual_review" in flags:
|
||||
counters["manual_review"] += 1
|
||||
if is_sample_path(op.source_path):
|
||||
flags.append("sample_source")
|
||||
if "sample_source" in flags:
|
||||
counters["sample_source"] += 1
|
||||
season, episode = _extract_season_episode(op)
|
||||
if season is not None and season >= season_threshold:
|
||||
flags.append("high_season")
|
||||
if "high_season" in flags:
|
||||
counters["high_season"] += 1
|
||||
if episode is not None and episode >= episode_threshold:
|
||||
flags.append("high_episode")
|
||||
if "high_episode" in flags:
|
||||
counters["high_episode"] += 1
|
||||
if op.has_conflict:
|
||||
flags.append("conflict")
|
||||
if "conflict" in flags:
|
||||
counters["conflicts"] += 1
|
||||
rc = op.review_context if hasattr(op, "review_context") else {}
|
||||
if isinstance(rc, dict) and rc.get("duplicate_group_id") and op.operation_type == "quarantine":
|
||||
if "duplicate" not in flags:
|
||||
flags.append("duplicate")
|
||||
elif "duplicate" in reason_l and "duplicate" not in flags:
|
||||
flags.append("duplicate")
|
||||
if flags:
|
||||
counters["review_export_rows"] += 1
|
||||
if _is_actionable_high_risk(op, flags):
|
||||
counters["high_risk_operations"] += 1
|
||||
rows.append(
|
||||
{
|
||||
"index": str(idx),
|
||||
"operation_type": op.operation_type,
|
||||
"risk_flags": "|".join(flags),
|
||||
"source_path": str(op.source_path),
|
||||
"destination_path": str(op.destination_path) if op.destination_path else "",
|
||||
"reason": op.reason,
|
||||
}
|
||||
)
|
||||
rows.append(
|
||||
{
|
||||
"index": str(idx),
|
||||
"operation_type": op.operation_type,
|
||||
"risk_flags": "|".join(flags),
|
||||
"source_path": str(op.source_path),
|
||||
"destination_path": str(op.destination_path) if op.destination_path else "",
|
||||
"reason": op.reason,
|
||||
}
|
||||
)
|
||||
|
||||
return rows, counters
|
||||
|
||||
|
||||
def _load_review_csv_by_index(csv_path: Path) -> dict[int, dict[str, str]]:
|
||||
"""Load manual review CSV rows keyed by 1-based operation index."""
|
||||
indexed: dict[int, dict[str, str]] = {}
|
||||
with open(csv_path, "r", encoding="utf-8", newline="") as fh:
|
||||
reader = csv.DictReader(fh)
|
||||
for row in reader:
|
||||
try:
|
||||
indexed[int(row["index"])] = row
|
||||
except (KeyError, ValueError, TypeError):
|
||||
continue
|
||||
return indexed
|
||||
|
||||
|
||||
def _actionable_high_risk_indices(
|
||||
plan: ExecutionPlan,
|
||||
*,
|
||||
season_threshold: int = 20,
|
||||
episode_threshold: int = 40,
|
||||
) -> list[int]:
|
||||
"""Return 1-based indices of operations that block execute until reviewed."""
|
||||
indices: list[int] = []
|
||||
for idx, op in enumerate(plan.operations, start=1):
|
||||
flags = _risk_flags_for_operation(
|
||||
op,
|
||||
season_threshold=season_threshold,
|
||||
episode_threshold=episode_threshold,
|
||||
)
|
||||
if _is_actionable_high_risk(op, flags):
|
||||
indices.append(idx)
|
||||
return indices
|
||||
|
||||
|
||||
def append_sample_safe_rows(
|
||||
plan: ExecutionPlan,
|
||||
rows: list[dict[str, str]],
|
||||
@@ -406,32 +483,56 @@ def check_review_requirements(
|
||||
season_threshold: int = 20,
|
||||
episode_threshold: int = 40,
|
||||
) -> list[str]:
|
||||
"""Return human-readable errors when review is required but missing or stale."""
|
||||
_, counters = review_plan(
|
||||
"""Return human-readable errors when review is required but missing or not applied."""
|
||||
actionable = _actionable_high_risk_indices(
|
||||
plan,
|
||||
season_threshold=season_threshold,
|
||||
episode_threshold=episode_threshold,
|
||||
)
|
||||
high_risk = counters.get("high_risk_operations", 0)
|
||||
if high_risk == 0:
|
||||
if not actionable:
|
||||
return []
|
||||
|
||||
errors: list[str] = []
|
||||
count = len(actionable)
|
||||
if not review_csv.is_file():
|
||||
errors.append(
|
||||
f"Review required: {high_risk} high-risk operation(s) but review CSV not found: {review_csv}. "
|
||||
f"Review required: {count} actionable high-risk operation(s) but review CSV not found: {review_csv}. "
|
||||
f"Run: vlm review-plan --input {plan_path}"
|
||||
)
|
||||
return errors
|
||||
|
||||
if not plan.metadata.get(REVIEW_APPLIED_AT_KEY):
|
||||
errors.append(
|
||||
f"Review required: {count} actionable high-risk operation(s) but plan was not updated via apply-review. "
|
||||
f"Run: vlm apply-review --plan {plan_path} --csv {review_csv}"
|
||||
)
|
||||
return errors
|
||||
|
||||
try:
|
||||
if review_csv.stat().st_mtime < plan_path.stat().st_mtime:
|
||||
errors.append(
|
||||
f"Review CSV is older than plan ({review_csv}). "
|
||||
f"Re-run review-plan and apply-review before execute --confirm."
|
||||
)
|
||||
csv_rows = _load_review_csv_by_index(review_csv)
|
||||
except OSError as exc:
|
||||
errors.append(f"Could not compare review CSV timestamps: {exc}")
|
||||
errors.append(f"Could not read review CSV: {exc}")
|
||||
return errors
|
||||
|
||||
missing = [idx for idx in actionable if idx not in csv_rows]
|
||||
if missing:
|
||||
preview = ", ".join(str(i) for i in missing[:8])
|
||||
suffix = f" (and {len(missing) - 8} more)" if len(missing) > 8 else ""
|
||||
errors.append(
|
||||
f"Review CSV missing rows for actionable operation index(es): {preview}{suffix}. "
|
||||
f"Re-run: vlm review-plan --input {plan_path}"
|
||||
)
|
||||
|
||||
recorded_csv = plan.metadata.get(REVIEW_CSV_PATH_KEY)
|
||||
if recorded_csv:
|
||||
try:
|
||||
if Path(recorded_csv).resolve() != review_csv.resolve():
|
||||
errors.append(
|
||||
f"Review CSV path does not match last apply-review ({recorded_csv}). "
|
||||
f"Re-run apply-review with --csv {review_csv}"
|
||||
)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
Reference in New Issue
Block a user