tasks(using-yt-tools): close 3 nice-to-have findings 🟢

* empty-cache-dir-on-failure — fixed via `common@fc400b7` (defer mkdir
  in transcript.py + watch.py; 2 regression tests + 1 happy-path).
* warning-mojibake — fixed in same `common@fc400b7` (force_utf8_streams
  helper called from trio CLI entries; capsys-safe).
* frames-multiline-stdout — fixed via `claude-skills@b2c1a21` (spec
  describes per-CLI stdout shape; existing piping contract preserved
  rather than rewritten).

Cluster fully closed. Header _Updated_ note rewritten to reflect closure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-20 13:37:27 +03:00
parent b2c1a213b3
commit c7ee3d80d5

View File

@@ -1,5 +1,5 @@
# Task Board
_Updated: 2026-05-20 (using-yt-tools: 3 baseline 🟢; 3 finding fixes 🟢; skill-body-fill 🟢 [SKILL.md 0.2.0]; trigger-smoke-clean-session 🟢 [13/13]; review 🟢 by fresh-eyes subagent — Flow A/B/Failure-modes/NOT all PASS; 3 nice-to-have findings filed as ⚪ [empty-cache-dir-on-failure, frames-multiline-stdout, warning-mojibake])_
_Updated: 2026-05-20 (using-yt-tools cluster fully closed: 3 baseline 🟢; 3 finding fixes 🟢; skill-body-fill 🟢 [SKILL.md 0.2.0]; trigger-smoke-clean-session 🟢 [13/13]; review 🟢 by fresh-eyes subagent — Flow A/B/Failure-modes/NOT all PASS; all 3 nice-to-have findings 🟢 — empty-cache-dir-on-failure & warning-mojibake fixed in `common@fc400b7` [yt-tools 0.1.5]; frames-multiline-stdout spec-fixed in `claude-skills@b2c1a21` [SKILL.md 0.2.2])_
<!--
Canonical layout. One block per task. Per-task deep context lives in
@@ -189,66 +189,42 @@ Findings → follow-up tasks (`using-yt-tools-<gap>-fix`) через `tasks_crea
---
## [using-yt-tools-empty-cache-dir-on-failure] — `yt-transcript` создаёт `yt-cache/<vid>/` ДО фетча captions; при abort оставляет пустую папку, что противоречит Failure-modes интенту SKILL.md «никогда не оставляй полусостояние».
## 🟢 [using-yt-tools-empty-cache-dir-on-failure] — `yt-transcript` создаёт `yt-cache/<vid>/` ДО фетча captions; при abort оставляет пустую папку, что противоречит Failure-modes интенту SKILL.md «никогда не оставляй полусостояние».
**Reviewer observed** на broken-URL тесте (URL=`https://www.youtube.com/watch?v=AAAAAAAAAAAA`): exit 1 + stderr clean, но пустая `yt-cache/AAAAAAAAAAAA/` остаётся на FS.
**Fix-направления:**
- (a) defer mkdir до первого успешного write — проще логически, но требует review всех путей записи в `yt_tools/cache.py` + `transcript.py`;
- (b) cleanup-on-abort через atexit / try-finally в CLI entry — точечный диф, дешевле.
Затем — тест на «no empty dir после abort» в `tests/test_cache.py` (или новом `test_failure_modes.py`).
**Severity:** nice-to-have (cosmetic, не ломает workflow).
**Status:** ready
**Where I stopped:** (not started)
**Next action:** Решить fix-стратегию (a) vs (b). Имплементировать. Добавить regression-тест.
**Status:** done
**Closed by:** `OpeItcLoc03/common@fc400b7` — option (a) chosen: `out_dir.mkdir(parents=True, exist_ok=True)` deferred в `transcript.py` к месту прямо перед `out.write_text`; в `watch.py` — снято upfront, dir создаётся через `_ensure_source_mp4(...)`'s downstream mkdir после успешного `_fetch_snippets`. Regression: `tests/test_failure_modes.py::test_yt_transcript_no_empty_cache_dir_on_fetch_failure` + `::test_yt_watch_no_empty_cache_dir_on_snippets_failure` (mock `_fetch_snippets` raise → assert `yt-cache/<vid>/` не существует). Plus happy-path sanity `::test_yt_transcript_succeeds_creates_dir`. pytest 77/77 🟢. yt-tools bump 0.1.4→0.1.5 PATCH.
**Next action:** (none — kept until merged)
**Branch:** n/a
<!-- created-by: vitya@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-05-20 / via: local-file (gitea write-side 404 на tasks_create POST) / origin: [using-yt-tools-review] fresh-eyes subagent finding -->
<!-- closed-by: vitya@DESKTOP-NSEF0UK / 2026-05-20 / acceptance: no-empty-dir on abort ✅ (2 mock-fetch-fail tests); happy-path dir created ✅; yt-tools 0.1.5 ✅; pytest 77/77 ✅ -->
---
## [using-yt-tools-frames-multiline-stdout] — Spec/реальность mismatch в SKILL.md: «Last line каждого CLI's stdout — absolute path артефакта (CLI designed для single-line EOF output)» обещает чистый bare-path, а `yt-frames --timestamps T1,T2,T3` фактически выдаёт N строк формата `Wrote: <abspath>` (с префиксом).
## 🟢 [using-yt-tools-frames-multiline-stdout] — Spec/реальность mismatch в SKILL.md: «Last line каждого CLI's stdout — absolute path артефакта (CLI designed для single-line EOF output)» обещает чистый bare-path, а `yt-frames --timestamps T1,T2,T3` фактически выдаёт N строк формата `Wrote: <abspath>` (с префиксом).
**Проблема:** наивный «take last line as path» парс ломается на префиксе. Single-line EOF контракт работает для `yt-transcript` и `yt-frames` с одним таймкодом, но не для multi-timestamp.
**Решений два:**
- (a) **fix CLI** — emit bare paths matching contract (для multi-timestamp выводить N строк без `Wrote: ` префикса);
- (b) **fix spec** — описать multi-line case + `Wrote: ` префикс явно в SKILL.md.
Рекомендация ревьюера implicit: (a) чище — единый контракт «last line = path» через все три CLI, парсинг агента остаётся тривиальным. (b) дешевле — только edit SKILL.md, но размывает контракт.
Решение влияет на yt-tools 0.x semver (CLI change = MINOR bump; spec change = PATCH в claude-skills).
**Severity:** nice-to-have (работает на практике потому что агент знает имена кадров из таймкодов и Read'ит их напрямую, но spec wording misleading; будущий tooling «парсь stdout, бери EOF» сломается).
**Status:** ready
**Where I stopped:** (not started)
**Next action:** Выбрать (a) vs (b). Если (a) — fix `yt_tools/frames.py` printf, bump yt-tools версию, регрессионный тест. Если (b) — edit `skills/using-yt-tools/SKILL.md` Inputs/Outputs или Steps секцию, bump skill PATCH.
**Status:** done
**Closed by:** `claude-skills@b2c1a21` — option (b) chosen: fix spec, не код. Причина: `yt_tools/frames.py:12-13` docstring явно декларирует per-line `Wrote: <path>` дизайн «so callers can pipe/scrape without parsing summary», `tests/test_cli_smoke.py:81` уже enforce'ит `assert all(line.startswith("Wrote: "))`. Code-side change ломал бы намеренный piping-friendly контракт. SKILL.md Steps секция (L61) теперь разделяет stdout-контракт на per-CLI: `yt-transcript`/`yt-watch` — bare single-line path; `yt-frames` N строк `Wrote: <abs path>`, strip префикс «Wrote: » чтобы получить путь. Skill PATCH 0.2.1→0.2.2 (wording, без behavior change).
**Next action:** (none — kept until merged)
**Branch:** n/a
<!-- created-by: vitya@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-05-20 / via: local-file (gitea write-side 404 на tasks_create POST) / origin: [using-yt-tools-review] fresh-eyes subagent finding -->
<!-- closed-by: vitya@DESKTOP-NSEF0UK / 2026-05-20 / acceptance: spec per-CLI stdout shape documented ✅; existing piping contract preserved ✅; skill 0.2.2 ✅; install.ps1 picked up new version ✅ -->
---
## [using-yt-tools-warning-mojibake] — yt-dlp warnings на Windows console рендерятся с mojibake: `Video unavailable “ using minimal metadata` → `Video unavailable <20> using minimal metadata`.
## 🟢 [using-yt-tools-warning-mojibake] — yt-dlp warnings на Windows console рендерятся с mojibake: `Video unavailable “ using minimal metadata` → `Video unavailable <20> using minimal metadata`.
**Корень:** stderr encoding mismatch (cp1251 default Windows console vs UTF-8 source).
**Fix-направления:**
- `sys.stderr.reconfigure(encoding='utf-8', errors='replace')` в trio CLI-entry (`yt-transcript`, `yt-frames`, `yt-watch`) — программная починка;
- PYTHONIOENCODING=utf-8 hint в README Windows-блоке — пользовательская;
- filter warning sym↔ascii — workaround в `_warning_filter`.
Рекомендация: первый вариант (программная) — единый fix, не требует от user'а лишних шагов.
**Severity:** nice-to-have (cosmetic, сообщение остаётся читаемым).
**Status:** ready
**Where I stopped:** (not started)
**Next action:** Проверить, помогает ли `sys.stderr.reconfigure(encoding='utf-8', errors='replace')` в trio CLI-entry на проблемном кейсе. Если нет — добавить PYTHONIOENCODING=utf-8 в README Windows-note.
**Status:** done
**Closed by:** `OpeItcLoc03/common@fc400b7` — программная починка (первый вариант). Helper `force_utf8_streams()` в `yt_tools/core.py` делает `sys.stdout.reconfigure(encoding='utf-8', errors='replace')` + same for stderr; AttributeError/OSError swallowed для wrapped streams (pytest capsys, file redirects). Вызывается в `main()` всех трёх CLI: `yt-transcript`, `yt-frames`, `yt-watch`. pytest 77/77 🟢 (helper не ломает capsys). yt-tools bump 0.1.4→0.1.5 PATCH (одним коммитом с empty-cache-dir-on-failure).
**Next action:** (none — kept until merged)
**Branch:** n/a
<!-- created-by: vitya@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-05-20 / via: local-file (gitea write-side 404 на tasks_create POST) / origin: [using-yt-tools-review] fresh-eyes subagent finding -->
<!-- closed-by: vitya@DESKTOP-NSEF0UK / 2026-05-20 / acceptance: utf-8 reconfigure in trio CLI entries ✅; capsys-safe via AttributeError swallow ✅; pytest 77/77 ✅; yt-tools 0.1.5 ✅ -->
---