refactor: consolidate skill docs, add anti-drift tests, and apply audit fixes

DLO-13: Restructure vlm-library-workflow skill as safety contract layer.
- Rewrite SKILL.md (69 lines): safety contract, execution threshold semantics,
  six-step high-risk loop, decision rules, phase skeleton
- Delete redundant references (cli-reference, workflow, command-recipes, dev-guide)
- Add triage.md (failure mapping + preflight) and dev-map.md (module→test mapping)
- Add tests/test_docs_consistency.py: 78 parametrized tests verifying documented
  vlm commands exist in CLI registry
- Add CSV path mismatch test to test_plan_review.py (4th safety gate path)
- Delete vlm-expert.skill (Gemini package, 7 months stale) and README Gemini section

DLO-2 audit fixes: rate limiter injection, symmetric quarantine categories,
review-plan safety gates, parser improvements, planner validation.

CLI modularization: commands/ directory with one module per command group.
This commit is contained in:
windyboy
2026-09-25 13:50:09 +08:00
parent c7a55190d7
commit dfa18ed405
35 changed files with 1116 additions and 621 deletions
+50
View File
@@ -290,6 +290,56 @@ def _is_video_file(file_path: Path, video_extensions: list[str]) -> bool:
return file_extension in [ext.lower() for ext in video_extensions]
DEFAULT_SIDECAR_EXTENSIONS = (".srt", ".ass", ".sub", ".idx", ".sup", ".nfo")
def find_sidecar_companions(
video_path: Path,
sidecar_extensions: tuple = DEFAULT_SIDECAR_EXTENSIONS,
) -> list[Path]:
"""Find sidecar files in the same directory that belong to a video file.
Conservative matching: a companion must share the video's exact stem,
optionally followed by dot-separated alphabetic suffix tokens (e.g.
language or track tags such as ``zh`` or ``en.forced``). Numeric or
otherwise non-alphabetic tokens are rejected so unrelated files are
never associated.
Args:
video_path: Path to the video file
sidecar_extensions: Sidecar extensions to consider (case-insensitive)
Returns:
Sorted list of companion paths (empty when none are found).
"""
parent = video_path.parent
try:
with os.scandir(parent) as it:
entries = [e for e in it if e.is_file(follow_symlinks=False)]
except OSError:
return []
stem = video_path.stem
exts = {ext.lower() for ext in sidecar_extensions}
companions: list[Path] = []
for entry in entries:
name = entry.name
suffix = Path(name).suffix.lower()
if suffix not in exts:
continue
base = name[: -len(suffix)]
if base == stem:
companions.append(Path(entry.path))
continue
if not base.startswith(stem + "."):
continue
tokens = base[len(stem) + 1:].split(".")
if tokens and all(token and token.isalpha() for token in tokens):
companions.append(Path(entry.path))
return sorted(companions, key=lambda p: p.name)
def _create_video_file(
file_path: Path,
library_root: Path,