Аудит дублирования (xhigh). Мелкие, но надёжно верифицированные дубли в v4-командах и в coverage-модулях. Низкий риск, быстрые победы.
Находки
6. _non_empty / _non_empty_option — байт-в-байт идентичны
commands/v4account.py:114 ↔ commands/v4finance.py:140. Тела совпадают полностью, отличается только имя:
def _non_empty(value, option_name):
normalized = (value or "").strip()
if not normalized:
raise click.UsageError(t("{option_name} must not be empty").format(option_name=option_name))
return normalized
→ Один валидатор в direct_cli/v4/ (рядом с money.py/emit.py).
7. Парсинг ID=VALUE спеков задвоен в 3 файлах
commands/v4account.py:310 (_parse_account_payments), v4finance.py:53 (_invoice_payments_param), v4adimage.py:69 (_set_param). ~35 строк boilerplate × 3: split на =, strip, int-валидация ID, проверка уникальности через set, сборка dict. Отличие только в именах полей.
→ parse_id_value_specs(specs, id_key, value_key, ...) в v4-хелперах.
9. 3 варианта парсинга списка ID
commands/v4tags.py:14 (_positive_ids_param), v4goals.py:12, v4finance.py:150 (_campaign_ids_param) — все вызывают parse_ids(), проверяют непустоту/положительность; отличаются обёрткой результата (list vs dict).
→ parse_positive_ids(raw, option_name, min_size, max_size).
10. Детекция captcha задвоена между coverage-модулями
wsdl_coverage.py:171,241 (inline-проверки в _assert_real_wsdl / _assert_real_xsd) ↔ reports_coverage.py:34 (кортеж _CAPTCHA_MARKERS). CLAUDE.md называет это инвариантом обоих модулей.
→ Один assert_not_captcha(url, content, markers). Учесть запрет CLAUDE.md на дублирование литералов — маркеры держать в одном месте (как URL в реестре).
Приёмка
Аудит дублирования (xhigh). Мелкие, но надёжно верифицированные дубли в v4-командах и в coverage-модулях. Низкий риск, быстрые победы.
Находки
6.
_non_empty/_non_empty_option— байт-в-байт идентичныcommands/v4account.py:114↔commands/v4finance.py:140. Тела совпадают полностью, отличается только имя:→ Один валидатор в
direct_cli/v4/(рядом сmoney.py/emit.py).7. Парсинг
ID=VALUEспеков задвоен в 3 файлахcommands/v4account.py:310(_parse_account_payments),v4finance.py:53(_invoice_payments_param),v4adimage.py:69(_set_param). ~35 строк boilerplate × 3: split на=, strip, int-валидация ID, проверка уникальности через set, сборка dict. Отличие только в именах полей.→
parse_id_value_specs(specs, id_key, value_key, ...)в v4-хелперах.9. 3 варианта парсинга списка ID
commands/v4tags.py:14(_positive_ids_param),v4goals.py:12,v4finance.py:150(_campaign_ids_param) — все вызываютparse_ids(), проверяют непустоту/положительность; отличаются обёрткой результата (list vs dict).→
parse_positive_ids(raw, option_name, min_size, max_size).10. Детекция captcha задвоена между coverage-модулями
wsdl_coverage.py:171,241(inline-проверки в_assert_real_wsdl/_assert_real_xsd) ↔reports_coverage.py:34(кортеж_CAPTCHA_MARKERS). CLAUDE.md называет это инвариантом обоих модулей.→ Один
assert_not_captcha(url, content, markers). Учесть запрет CLAUDE.md на дублирование литералов — маркеры держать в одном месте (как URL в реестре).Приёмка
test_*_cache_files_are_real_contentзелёные.pytestзелёный.