Чистка спамных личных переписок перед повторным касанием: детерминированный план (какие НАШИ веерные рассылки и неотвеченные залпы стереть из диалога) → показ Антону → удаление ТОЛЬКО НАШИХ сообщений (revoke у обеих сторон) → свежий питч в очищенный тред. Триггеры: '/thread-clean', 'почисти переписку', 'вычисти спам из лички', 'чистка треда', 'убери наши рассылки', 'тред выжжен, почисти'
Installs into .claude/skills of the current project.
Are you the author of Thread Clean?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/tonydzi-thread-clean)
---
name: thread-clean
description: "Чистка спамных личных переписок перед повторным касанием: детерминированный план (какие НАШИ веерные рассылки и неотвеченные залпы стереть из диалога) → показ Антону → удаление ТОЛЬКО НАШИХ сообщений (revoke у обеих сторон) → свежий питч в очищенный тред. Триггеры: '/thread-clean', 'почисти переписку', 'вычисти спам из лички', 'чистка треда', 'убери наши рассылки', 'тред выжжен, почисти'"
version: 1.0.0
---
# /thread-clean — стереть свой спам, потом писать заново
## Зачем (принцип, anton 03.09.2026, ДВА голосовых)
Первый голосовой: «у человека память короткая… он забывает мгновенно всё, что ему написали.
Поэтому мы просто вычищаем всё неугодное у каждого лида, которому хотим что-то предложить,
и шлём человеку ещё раз то, что нам нужно».
⭐ Второй голосовой — УТОЧНЕНИЕ, отменяет «и его» из первого: «мы НЕ будем удалять сообщение
лида, будем удалять НАШЕ сообщение до последнего сообщения лида. Если он написал, что мы
спамеры — мы на это красиво шуткой ответим». То есть под нож идёт ТОЛЬКО наше; обиду лида
не стираем — снимаем шуткой в новом касании (§3.3 голос Майкрофта).
Практический смысл: тред, где висят 3-4 наших неотвеченных залпа подряд, убивает любой
следующий питч ещё до прочтения. Чистка возвращает диалог в состояние «последнее, что было
между нами — живой тёплый разговор», и новое письмо читается как первое касание за годы.
⚖️ Серое, не чёрное: удаляем СВОИ сообщения в СВОЕЙ личке штатной кнопкой платформы (revoke).
Копия всего остаётся в архиве `dm_messages.db` (1.8 млн сообщений на хабе) — CRM-история не
теряется, теряется только видимая собеседнику простыня.
⚖️ HARD-FLOOR (граница Claude): планировщик СТРОЮ и ПОКАЗЫВАЮ я (обратимо, 0 удалений). Сам
необратимый `delete_messages` — на прокоже прибора, который жмёт Антон / CRM-рутина, не
авто-Claude в потоке: перманентное удаление сообщений у Claude в запретном списке независимо
от «+». Это не тормоз стратегии (она зелёная), а тот же класс, что «пароль вводит человек».
## Шаг 1. RECALL — на кого замахнулись
Карточка CRM + история: `leads.db`, `chats.db`, прошлые касания в логе проекта.
Стоп-сигналы ДО чистки: явный отказ («stop», «not looking for…»), 🪦-метка, Tier-2-тема.
Отказ = не чистим и не пишем; чистка не отменяет [[declined-decisions]].
## Шаг 2. План (детерминированно, 0 LLM, ничего не удаляет)
Исполняется на ХАБЕ (`[машина флота]`): там лежит `tg_archive.db` (6 ГБ) — пиры его не держат.
Ключ диалога = numeric `dialog_id` (из `resolve_username` / `get_history`).
```bash
python ~/.claude/scripts/thread_clean_plan.py <dialog_id> --json plan.json
```
⚠️ Сверено с кодом 03.09 вечером: флагов `--handle` и ключа `slug` у прибора **нет** (раньше
этот раздел описывал их — сессия по такой инструкции спотыкалась). Живые флаги: `--source`,
`--wa-account`, `--db`, `--account`, `--blast-min`, `--also`, `--since`, `--json`; правда —
`--help`, а не этот файл.
Что печатает: каждое НАШЕ (`out=1`) сообщение с меткой
- `BLAST xN` — похожее наше исходящее ушло в N разных диалогов = веерная рассылка. Похожесть
считается **Жаккаром по словам** (порог 0.6) в окне ±120 дней, поэтому «gm [человек]…» и
«gm [человек]…» из одной рассылки узнаются как один шаблон. Замер 03.09: топ-веер = 6069
получателей, дальше 4852 и 4259.
- `UNANS` — после него собеседник не ответил ни разу.
- `MANUAL` — НАШЕ сообщение, добавленное флагом `--also <msg_id>`.
⚠️ ЖЕЛЕЗНЫЙ ИНВАРИАНТ ядра: сообщения ЛИДА (`out=0`) в план НЕ попадают НИКОГДА — ни авто,
ни через `--also`; попытка печатается как `⛔ ОТКАЗ по --also` и уезжает в
`plan["also_rejected"]`. Обиду («stop spamming me», «вы кинули нас») НЕ стираем: отвечаем
шуткой в новом касании. Стережёт тест `test_their_message_rejected_even_with_also`
(до 03.09 21:0x код пускал чужое, а тест закреплял старое поведение — ложный зелёный).
Прогон по большому треду — секунды–минуты (Жаккар по окну на каждое наше сообщение).
## Шаг 2-бис. WhatsApp — да, можно, но окно другое (замер 03.09.2026)
Прямой вопрос Антона из голосовой («в WhatsApp — не знаю, можно или нельзя, надо посмотреть»)
закрыт чтением кода MCP, а не догадкой:
```bash
python ~/.claude/scripts/thread_clean_plan.py <chat_jid> --source whatsapp --wa-account main
# chat_jid вида [id]@s.whatsapp.net; --wa-account main | wa2
```
- **План работает**: базы MCP лежат локально (`main` 134 405 сообщений / 18 831 наших /
10 498 чатов; `wa2` 14 570 / 2 282 / 7 842), схема отличается только именами колонок, поэтому
ядро одно, а различия живут в `SCHEMAS` внутри прибора. Боевой прогон 03.09 по треду
`[id]@s.whatsapp.net` дал 7 кандидатов из 22 сообщений.
- **Удаление**: `mcp__whatsapp__delete_message` шлёт протокольный revoke
(`{delete: {remoteJid, fromMe, id}}`) — стирает у обеих сторон и из локальной базы, и требует
двухфазного `confirmed=true`. Чужое сообщение в личке платформа не даёт удалить в принципе
(только админу в группе), то есть наш инвариант тут совпадает с ограничением WhatsApp.
- ⚠️ **Окно ревока НЕ замерено.** У Telegram лимита по возрасту нет (стёрли сообщение
3,5-летней давности), у WhatsApp «удалить у всех» исторически ограничено сроком, и Baileys
этот срок не проверяет — сервер просто откажет. Переносить телеграмный опыт нельзя
([[prichina-kak-claim]]): перед первой боевой чисткой в WA — канарейка на одном СТАРОМ
сообщении, и только потом пачка.
- ⚠️ В WhatsApp у Антона много **бытовых** тредов (автосервис, доставка). Там `UNANS` значит
«человек не ответил», а не «мы спамим» — без `BLAST` такой тред не чистим вообще.
## Шаг 3. Показать Антону и получить «+»
Удаление необратимо, поэтому план всегда идёт ему глазами: сколько всего сообщений, сколько
под нож, что именно останется (первая строка каждого выжившего), и одна строка «после чистки
последним в треде будет: <дата, чьё, текст>». Ждём «+» по КОНКРЕТНОМУ лиду или на список.
Standing-мандат на класс (например «все 🔥-треды грок-теста») действует, только если Антон
дал его явно и он записан в файл проекта; молчание мандатом не считается.
## Шаг 3-бис. ⚠️ ЧЕЙ АККАУНТ (замер 03.09.2026, чуть не стоил провала)
Один тред у лида может быть набит сообщениями с РАЗНЫХ наших аккаунтов. Замер: диалог
Парвеза [id] = 74 сообщения с `[рабочий аккаунт]` + 71 с `[рабочий аккаунт]`.
Правило: удалять сообщение можно ТОЛЬКО с того аккаунта, который его отправил. Прибор
печатает `[аккаунт]` у каждого кандидата и ругается, если аккаунтов больше одного. Нужного
аккаунта нет в рельсе → эта часть плана остаётся, и мы честно говорим, что тред почищен
наполовину; тихо «почистили» о половине не отчитываемся.
### Инвентарь аккаунтов (замер 03.09.2026, HP17)
Две независимые рельсы, у каждой свой список — сверять НАДО обе:
- **MCP** (демон `127.0.0.1:8765`, конфиг `[путь владельца]`, ключи
`TELEGRAM_SESSION_STRING[_<LABEL>]`): ⚠️ **безымянного ключа больше НЕТ** — замер 10.09.2026 на
хабе даёт четыре ЯВНЫХ ярлыка `[рабочий аккаунт]` · `[рабочий аккаунт]` · `[рабочий аккаунт]` · `TONYDZI`
(api_id у всех общий [id]). Псевдоним `[рабочий аккаунт]`→`default` из-за этого стал вести в
пустоту и живой аккаунт печатался как «сессии нет в .env» — починено резолвером
`resolve_account_label()` (точное имя раньше псевдонима), класс `alias-outlives-the-key`.
- **Telethon-рельса архива** (`[путь владельца]`, ключи `<ACC>_SESSION`):
`REFRESH`=@[рабочий аккаунт] · `[рабочий аккаунт]` · `TONYDZI` — все живые.
- 🔴 **ЗАПИСЬ «[рабочий аккаунт] МЁРТВ В ОБЕИХ» ОПРОВЕРГНУТА 10.09.2026.** Живой замер на хабе:
`is_user_authorized() == True`, `get_me().username == '[рабочий аккаунт]'` в обеих рельсах. Прежняя
строка (03.09, HP17, `AuthKeyUnregistered`) верна ТОЛЬКО для того дня и той машины. Мораль —
канонная: запрет это такой же claim, и он протухает ([[ban-is-a-claim-recheck-before-workaround]]).
⛔ Не строй «чистим наполовину» по памяти: перед каждой чисткой гоняй живую проверку ниже.
Как проверить живость всех разом (0 токенов, ничего не меняет): пройтись Telethon'ом по
ключам обоих `.env` и напечатать `is_user_authorized` + `get_me` — образец кода в истории
сессии 03.09; «в конфиге есть» ≠ «сессия жива» ([[prichina-kak-claim]]).
Перелогин мёртвого аккаунта: `[путь владельца] send <phone>` →
код приходит в приложение того аккаунта или SMS → `... code <код>` → (при 2FA) `... pass <пароль>`.
Код может забрать только человек, если аккаунт не залогинен ни в одной нашей рельсе, — это
истинный блок, а не повод молчать: одна строка Антону.
После правки `.env` демон надо ПЕРЕЗАПУСТИТЬ (иначе новый аккаунт не виден):
`Stop-Process` по `telegram-mcp\main.py` → запустить `~/.claude/scripts/telegram_mcp_sse.cmd`.
⚠️ Демон общий на флот: перезапуск на секунды роняет телегу у всех сессий, а СВОЯ сессия
теряет SSE-соединение до перезапуска клиента («Invalid request parameters» на любой вызов) —
проверять результат тогда напрямую Telethon'ом, а не MCP.
## Шаг 3-тер. ДВЕРЬ ИСПОЛНЕНИЯ (`thread_clean_apply.py`, построена 03.09.2026)
План строит `thread_clean_plan.py`, а исполняет `~/.claude/scripts/thread_clean_apply.py` —
раньше этого файла не было, и «удалить пачку» приходилось делать вызовами руками (бывшая
«Точка роста #1»). Рельса Telethon напрямую, не MCP: клиент MCP в сессии может лежать, а
демон общий на флот и его не трогают.
```bash
# сухой прогон (0 удалений, печатает что сделал бы)
python ~/.claude/scripts/thread_clean_apply.py --plan plan.json
# боевой — жмёт ЧЕЛОВЕК (у Claude перманентное удаление в запретном списке)
python ~/.claude/scripts/thread_clean_apply.py --plan plan.json --apply
```
Инвариант | Что делает | Откуда взялся
---|---|---
`out=True` живьём | перед каждым revoke перепроверяет, что сообщение НАШЕ | железный запрет скилла
`--min-age-days 14` | не трогает свежие | замер 03.09: в план попало наше «интро sui/aptos ещё нужно?» двухдневной давности — UNANS там значит «не успел ответить»
`--keep-last-ours` | сохраняет наш последний ответ, если иначе последним останется сообщение собеседника | замер 03.09: у Парвеза план стирал наш ответ, оставляя финальным «I dont think a collab would work tbh»
мёртвый аккаунт → пропуск вслух | печатает «тред чистится наполовину» | 87 из 169 кандидатов сидят на @[рабочий аккаунт], а он не авторизован с 30.08
сбой треда не роняет прогон | ловит исключение и идёт дальше | нерезолвнутый [человек] уносил с собой три следующих треда
`--handle <id>=@username` | резолв, когда numeric id нет в кэше сессии | Telethon кидает ValueError на id, которого не видел
журнал | `~/.claude/change_ledger/thread-clean-<HOST>.jsonl` | удаление необратимо — след обязателен
Сторож: `~/.claude/scripts/_test_thread_clean_apply.py` (6 проверок, 0 сети). Показан КРАСНЫМ
на трёх поломках ядра: снят фильтр возраста · снята защита последнего ответа · сломан алиас
аккаунта.
⚠️ Удаление = revoke у обеих сторон = необратимо = **Tier-2 класс E**: нужен «+» Антона через
`approval.py ask --cat delete --critical`, а не просто показ плана в чате.
## Шаг 3-кватер. КНОПКА ЧЕЛОВЕКА → РУКА CRM (`thread_clean_runner.py`, 04.09.2026)
Модель Антона дословно (03.09, голосом): «стирать будет скрип CRM-ки, ты просто нажимаешь
кнопку, даёшь CRM-ке знать, а она уже всё стирает». Цепочка собрана так, и в ней НЕТ звена,
где Claude стирает по собственному решению:
```
сессия Claude -> строит план + кладёт задание в очередь (обратимо, 0 удалений)
человек в 02 POLICE -> отвечает «+» на аск (ЭТО И ЕСТЬ КНОПКА)
approval_reply_tick.py -> слышит «+», ставит аску approved (каждые 15 мин, 0 LLM)
thread_clean_runner.py -> видит approved, зовёт …_apply --apply (каждые 20 мин, 0 LLM)
thread_clean_apply.py -> revoke у обеих сторон + журнал (Шаг 3-тер)
```
```bash
# поставить задание (ждёт «+», без него не выстрелит никогда)
python ~/.claude/scripts/thread_clean_runner.py --add --ask-id <id аска> --plan <plan.json> --handle [id]=[аккаунт]
python ~/.claude/scripts/thread_clean_runner.py --status # что стоит и почему
```
FAIL-CLOSED: запуск разрешён ровно при `status == 'approved'`. Нет аска в базе, `pending`,
`stale`, `expired`, битая БД, пропавший план — рутина пишет «жду: кнопка не нажата» и не
трогает ничего. Сторож: `_test_thread_clean_runner.py` (13 проверок; показан красным на
fail-open — «запускать всё, что не rejected»). Рот рутины:
`_thread_clean_runner_state.json`, зарегистрирован в `output_freshness.py` (`max_age_h: 3`),
потому что тихо снесённая задача = одобренная человеком чистка, которая не случится никогда.
## Шаг 4. Канарейка, потом пачка
✅ ПРОВЕРЕНО 03.09.2026: `mcp__telegram__delete_message` удаляет у ОБЕИХ сторон (revoke).
Доказательство: с `default` отправлено сообщение на `[рабочий аккаунт]` (у получателя msg 172847),
удалено с `default` (msg 1875552) → повторный `get_history` ГЛАЗАМИ ПОЛУЧАТЕЛЯ показал, что
сообщение исчезло и у него. Лимита по возрасту нет: боевая канарейка стёрла в треде Парвеза
сообщение msg 688220 от 2023-03-25 (3,5 года), пропало из истории.
Порядок всё равно осторожный:
1. Одно сообщение из плана → `get_history` → убедиться, что исчезло.
2. Дальше порциями, `get_history` после каждой порции, счётчик «было / стало».
3. Если в новом окружении revoke вдруг не сработает (чужой аккаунт, бот-сессия) — остановиться
и сказать Антону: чистить только у себя бессмысленно, у него всё остаётся.
Пере-проверять revoke каждый раз не нужно, но при смене аккаунта или рельсы (Telethon вместо
MCP) — проверка заново: это claim, а он протухает.
## Шаг 5. Верификация и лог
- `get_history` после чистки → счётчик «было N, стало M, последнее сообщение = …».
- Строка в лог касаний соответствующего проекта: дата · лид · сколько удалено · причины.
- Карточка CRM: пометка «тред очищен <дата>, причина: подготовка к <питч>».
## ⛔ ГЕЙТ ОДНОГО ГОЛОСА — до отправки проверь, не писал ли этому человеку другой наш аккаунт (03.09.2026)
```bash
python "[путь владельца]" --peer <tg_id> --account <с какого шлём>
```
`STOP` (exit 2) = этому человеку уже писали с другого нашего аккаунта в окне 72ч → НЕ шлём вторым
голосом, ведём тред тем аккаунтом, что уже там. `WARN` (exit 3) = оба источника слепы, шлём, но
вслух говорим, что не проверили. Замер-повод: лид Jiten Oswal, 28.08.2026 — за ОДИННАДЦАТЬ секунд
ему ушло четыре сообщения с @[рабочий аккаунт], @[рабочий аккаунт], @TonyDzi, @[рабочий аккаунт]; у двух последних это было
первое в жизни сообщение ему («my claude is waiting for yours», «check our room - gifts inside»).
С его стороны это неотличимо от скама с трёх номеров; лид молчит с 25.08.
Гейт стоит слоем 0 в `safe_send` (рутины) — эта строка закрывает вторую половину: ЖИВЫЕ сессии,
которые шлют через MCP мимо ledger. Прибор смотрит в ДВА источника (ledger + архив телеги), потому
что в ledger всего 14 событий за историю, а реальных касаний по одному лиду — 50.
## Шаг 6. Пауза, потом свежее касание
Не слать питч тем же вызовом, что и удаление: секунда между «исчезли 15 сообщений» и «привет!»
выглядит машинно, если человек в этот момент в чате. Разрыв хотя бы в десяток минут, лучше
следующий заход. Текст — по правилам канала ([[vip-leads-no-robot-text]], тир раскрытия).
## Стоп-краны
- ⛔ Только ЛИЧКИ. Группы, каналы, клубы (СОСТАВ, ClawEng) — не наша юрисдикция: там чужая
модерация и чужие свидетели.
- ⛔ Не удаляем содержательное: договорённости, цифры, обещания, чужие вопросы без ответа —
это материал CRM и наша репутация. Под нож идёт МУСОР НАШЕГО производства.
- ⛔ Не удаляем ничего в тредах, где есть деньги, обязательства, юридические темы (Tier-2) —
там переписка это доказательство.
- ⛔ Не чистим тред, чтобы скрыть свой факап от Антона или от команды.
- ⛔ Прямой вопрос собеседника «ты удалял сообщения?» — не отрицаем.
- ⛔ Чистка не даёт права на новый веер: одно точное письмо в правильную дверь (§1.5).
## Точки роста (Антону на доработку)
1. ✅ ЗАКРЫТО ПО-НАСТОЯЩЕМУ 10.09.2026 (03.09 было ЛОЖНЫМ ЗЕЛЁНЫМ): массовая рельса =
`thread_clean_apply.py` (Шаг 3-тер), Telethon пачкой + журнал в `change_ledger`.
⚠️ Что вскрылось на первом боевом прогоне (тред [аккаунт]): пара строитель→исполнитель
**никогда не работала сквозь**. `thread_clean_plan.py` печатает ОДИН диалог
`{dialog_id, candidates:[…]}`, а дверь читала список тредов с `peer_id`/`per_account`/
`date_raw` — и падала `KeyError: 'peer_id'` на любом реальном плане. Запись «закрыто»
стояла 7 суток, потому что сквозной прогон ни разу не делали, а тесты проверяли только
внутренние фильтры двери. Класс: `builder-executor-contract-drift`.
Починка: `normalize_plan()` на входе двери принимает ОБЕ формы (тесты
`test_builder_plan_is_consumable`, `test_already_normalized_plan_passes_through`).
Осталось на будущее: связать с `budget.py`, если чистка пойдёт десятками тредов за прогон.
1-бис. ✅ ЗАКРЫТО 10.09.2026: задание рутины несёт СВОИ флаги двери (`--extra`, белый список
`safe_extra()`). Повод там же: решение «оставить последним слово лида» принимается на этапе
ПЛАНА, а `thread_clean_runner.py` звал дверь жёстко без флагов — по кнопке человека уцелела
бы ровно та простыня, ради удаления которой чистка затевалась. Класс:
`решение-на-плане-теряется-в-рельсе`. Тесты `test_job_flags_reach_the_door`,
`test_unknown_flag_is_dropped` (очередь = внешний вход, в командную строку двери удаления
пускаем только известные флаги).
2. ⏸️ В БЭКЛОГ (оценено 03.09 по §3.5): предпосчитанная таблица отпечатков рассылок. Ускорит
план, но сейчас чистка идёт единицами тредов, а прогон занимает секунды–минуты — «кровь из
носу сейчас» не выполняется. Строим, когда чистка пойдёт десятками за прогон.
3. ⏸️ В БЭКЛОГ (там же): автометка «выжжен» в CRM по счётчику неотвеченных подряд. Правильная
идея, но у неё нет потребителя до тех пор, пока чистка не встанет рутиной на входе в аутрич;
сегодня её роль выполняет `/triage` + шкала теплоты ([[warmth-scale-lead-grading]]).
4. ✅ ЗАКРЫТО ЗАМЕРОМ 03.09 (см. «Замер порога» ниже): порог `--blast-min 3` больше не «с
потолка». Заодно вскрылось, что мерить его отпечатком НЕЛЬЗЯ — только боевым Жаккаром.
## Связанное
- Прибор: `~/.claude/scripts/thread_clean_plan.py` (docstring = паспорт), тест
`_test_thread_clean_plan.py` (7 проверок, красная на поломках ядра).
- Данные: `[путь владельца]` (архив Telegram, 6 ГБ; копия остаётся
после чистки), WhatsApp — `~/.local/share/whatsapp-mcp/store/whatsapp.db` (main) и
`~/.local/share/wa-wa2/whatsapp-mcp/store/whatsapp.db` (wa2), плюс `leads.db`, `chats.db`.
- Канон в домах: память [[thread-clean-before-recontact]] · Библия
`reglament-chistka-treda-pered-novym-kasaniem` · CLAUDE.md §9.11.
- Соседи: `/triage` (кого касаться), `/telegram-lead-outreach` (что писать), `/bold-followup`
(пинг без блока), `/pipeline`.
- Канон: [[cold-pr-into-silent-queue]] (4-е неотвеченное = спам), §1.5 (спам ≠ громкость),
§1.5-бис (пинг кому угодно), [[declined-decisions]], [[crm-lead-full-provenance]].
<!--kit-footer-->
---
**Like this skill?** It is one of 100 in [second-brain-starter-kit](https://github.com/tonydzi/second-brain-starter-kit): the second brain we built for ourselves and run every day at Palo Alto AI Research Lab. Install the whole set with `npx skills add tonydzi/second-brain-starter-kit`. Everything is open source and free, so take what you need.
Flagships worth a look on their own: [secondop-panel](https://github.com/tonydzi/secondop-panel) (a second opinion from a panel of external models), [claude-memory-tidy](https://github.com/tonydzi/claude-memory-tidy) (stop your agent's memory from rotting), [telegram-mcp-kit](https://github.com/tonydzi/telegram-mcp-kit) (your own Telegram over MCP in about 15 minutes).
Author: **Anton Dziatkovskii**, Palo Alto AI Research Lab. Telegram [@tonydzi](https://t.me/tonydzi) - WhatsApp [+1 341 222 9178](https://wa.me/13412229178) - X [@Tony_Stef_](https://x.com/Tony_Stef_)
**Engineers: want to test-drive this setup?** Message me. I hand out free starter seeds to engineers who test and report back, and custom skill requests are welcome.