/pr-reply — ответить тем, кто уже согласился с нами разговаривать
Закон
Молчание в живом треде = провал, а не осторожность (CLAUDE.md §4.7). Отвечаю САМ: GitHub
выведен из-под гейта [коллега], «+» Антона не жду. Порог — 24 часа.
Живой ревьюер дороже нового PR. Человек, который написал на нашем PR, уже потратил своё
время — это самая тёплая дверь, какая у нас есть, и она закрывается от молчания, а не от
несогласия. Спорить с ним можно и нужно ([[objection-sparring-to-consensus]]), молчать — нет.
Шаг 1 — снять очередь (0 токенов, 0 своей сети)
python ~/.claude/scripts/pr_watch.py --digest
Секция 🔴 МЯЧ У НАС — это и есть очередь. Дальше глазами раздели её на три кучи:
| Кто говорил последним | Что делаем |
|---|
| живой человек | работаем сегодня, начиная с самого старого |
бот (codecov[bot], cubic-dev-ai[bot], review-notebook-app[bot], chatgpt-codex-connector) | чиним, если по делу; отвечать текстом не нужно |
| «нет снимка» | сторож эту позицию ещё не видел — прогнать pr_watch.py и вернуться |
⚠️ Digest видит только реестр — добери упоминания (замер 15.09)
gh api -X GET search/issues -f q='mentions:tonydzi updated:>=<вчера>' --jq '.items[].html_url'
По каждой ссылке: последний писал не мы и обращался к нам → в очередь; нет в реестре → pr_watch.py --add <url>.
15.09 так нашлись agno#10086 и langgraph#8764: ответ нам ждал 15–40ч, а в outbound_github.json тредов не было.
Ссылка даёт 404 через REST /issues/N → сперва GraphQL discussion(number:N): 15.09 все 6 «404» оказались discussions.
⚠️⚠️ Дайджест показывает НОЧНОЙ снимок — «🟢 активность от tonydzi» НЕ значит, что мяч у них (замер 20.09)
--digest делает 0 сетевых вызовов, поэтому дневной заход видит картину прошлой ночи. Тред, где
последним писали МЫ ночью, стоит в 🟢 с подписью «активность от tonydzi» — и остаётся там, даже если
инженер ответил нам сегодня утром: красным он станет только на следующем ночном тике, через сутки.
Замер 20.09: дайджест дал 1 настоящий долг (litellm#32642), а добор mentions:tonydzi дал ещё ДВА,
и оба были ответами живых инженеров на наши же комментарии того же дня — agno#10366 (18:26Z) и
langgraph#8039 (14:31Z); в дайджесте оба стояли зелёными «активность от tonydzi».
Правило: для КАЖДОГО треда из добора, обновлённого за 24ч, смотри последний коммент ФАКТОМ,
а не поле author из снимка. Зелёная строка с нашим именем — довод НЕ проверять только если
добор её не поднял.
⭐ КАК ДЕЛАТЬ ДОБОР ОДНИМ ВЫЗОВОМ (замер 22.09, 5-й случай класса подряд). Проверять последнего
комментатора по 40–60 позициям REST-парами — это ~110 сетевых вызовов; один GraphQL с алиасами даёт
то же за один. Шаблон: на каждую позицию aN: repository(owner:"o", name:"r"){ issueOrPublicRequest…},
внутри issueOrPullRequest(number:N) с двумя инлайн-фрагментами (... on Issue / ... on PullRequest),
в каждом comments(last:1){ nodes{ author{login} createdAt } }. comments(last:1) снимает всю возню
с пагинацией: GraphQL сам отдаёт ХВОСТ, грабля .[-1] первой страницы к нему не относится.
Два условия, иначе прибор соврёт: (1) gh api graphql возвращает ненулевой код при ЧАСТИЧНЫХ ошибках
(позиция оказалась discussion → «Could not resolve to an issue or pull request»), но data в stdout
при этом ЕСТЬ — парсь stdout, не выходи по returncode, иначе одна discussion гасит все 55 позиций;
(2) сравнивай логин с tonydzi явно — «наш последний» и «их последний» это и есть весь вердикт.
Замер 22.09: 4 красных дали 2 настоящих долга, а ещё 4 (claude-agent-sdk-python#1276, #1271,
mem0#6859, hermes-agent#88371) стояли зелёными или в «чужих тредах».
⛔ ВТОРАЯ ГРАБЛЯ ТОГО ЖЕ ПРИБОРА (замер 21.09): .[-1] БЕЗ ПАГИНАЦИИ ВРЁТ НА ДЛИННОМ ТРЕДЕ.
Эндпоинт GET /repos/{o}/{r}/issues/{N}/comments игнорирует sort и direction (в его контракте
их просто нет — только since, per_page, page), поэтому --jq '.[-1]...' отдаёт последний
коммент ПЕРВОЙ СТРАНИЦЫ, то есть №100, а не последний в треде. 21.09 так соврал gastownhall/beads#5877
(101 коммент): прибор показал «последним писал tonydzi», хотя реальный последний был от [аккаунт]
в тот же день, и тред чуть не уехал в «не долг». Чем людней тред — тем вероятнее ложь, а людные
треды мы берём ПЕРВЫМИ (§4.7 «самый людный живой тред домена первым»), так что грабля лежит
ровно на приоритетной дорожке.
Лечение — брать последнюю страницу явно:
cnt=$(gh api "repos/$repo/issues/$num" --jq '.comments')
page=$(( (cnt + 99) / 100 ))
gh api "repos/$repo/issues/$num/comments?per_page=100&page=$page" \
--jq '.[-1] | .user.login + " @ " + .created_at'
Готовый вариант на список тредов (считает страницу сам, печатает 2 последних коммента):
~/.claude/skills/pr-reply/gh_last_comment.sh <owner/repo#N> [...] (живёт рядом со скиллом —
единственным потребителем; в ~/.claude/scripts не кладём, там 21.09 висела чужая EXCLUSIVE-табличка
ON AIR на зоне fleet-code, и дверь onair_fleet_guard.py запись справедливо отбила).
⚠️ «Последний не мы» ≠ «ответ нам» (замер 14.09)
Digest ставит «ОТВЕТ ЗА НАМИ», если последним писал не tonydzi. В НАШЕМ PR это правда. В чужом
большом issue, где мы — один из 40 участников, это чаще посторонний, который пишет не нам.
Замер 14.09: из 53 позиций адресованы нам были 15; ещё ~25 — посторонние (anthropics/claude-code ×9 и
др.), 3 уже отвечены соседом, 6 ссылок 404. Прежде чем писать, прочитай последние 2–3 реплики: есть
@tonydzi, ответ на наш аргумент или просьба к автору/ревьюеру, которым были мы? Нет → не отвечаем,
ответ без новой ценности = спам (§1.5). Боты digest уже не считает (is_bot в pr_watch.py).
Leak-scan тела ответа: паттерны repo_release_leakscan.py (FAIL+WARN) по файлу --body-file, плюс
последняя строка обязана нести github.com/tonydzi.
⚠️ Сначала спроси: чей это PR
Реестр держит оба направления, и работа в них разная. Проверяется одной командой:
gh pr view <N> --repo <owner/repo> --json author --jq .author.login
| Автор PR | Кто мы | Что от нас ждут |
|---|
| мы | автор | сделать правку, которую попросил ревьюер → шаги 3–4 |
| посторонний | ревьюер | перепроверить их фикс и вынести вердикт → шаг 3-бис |
Замер 06.08: из трёх живых людей в очереди двое ждали нас как РЕВЬЮЕРОВ, а не как
авторов. Спутать эти роли — значит прийти в чужой тред с «поправил, посмотрите», где от
нас ждали проверки. Роль определяется автором PR, а не тем, в чьём репозитории он открыт.
⚠️ Очередь ровно так же свежа, как снимки сторожа. Дата снимка печатается в шапке digest —
если она вчерашняя, очередь тоже вчерашняя. Не выдавай старый список за сегодняшний.
🪦 Правило трёх дней (anton «+» 07.08)
Секцию 🪦 могилы digest выделяет сам (GRAVE_DAYS=3 в pr_watch.py): мяч у НИХ и
тишина >3 дней после нашего последнего хода. Замер по 54 нашим PR: все 8 мержей случились
за 0–3 дня, дальше — никогда. Поэтому: могилу не бампаем и не воскрешаем (один
вежливый бамп на PR разрешён только ПОКА тред живой, второй запрещён всегда — §seed 778).
Энергия из могил уходит живым тредам и ревью чужих PR. Могила ≠ провал: это нормальная
цена присутствия (§1.5), закрывать PR самим не нужно — пусть висит, вдруг проснутся сами.
⭐ Мяч у НИХ, но протух НАШ собственный claim — это новая ценность, а не бамп (замер 19.09)
Могила и «ждём их» запрещают бамп без новой ценности. Но наше опубликованное утверждение —
такой же скоропортящийся продукт, как и цифра ([[prichina-kak-claim]]): версия вышла, тег
переехал, баг уехал в релиз. Проверка дешёвая и делается ДО решения «молчим»: то, что мы в этом
треде утверждали про версии/релизы/состояние — всё ещё правда СЕГОДНЯ?
Протухло → пишем, даже если формально отвечают они, и первым абзацем правим себя, а не
повторяем просьбу. Замер, родивший правило: в google/adk-python#6941 мы 05.09 написали «ни один
опубликованный релиз не несёт bfeb04c» — 10.09 вышел v2.9.0, который его несёт, и баг из
«живёт на main» стал «стоит у пользователей». Проверка = две команды
(gh api repos/<repo>/compare/<tag>...<sha> --jq .status → behind значит содержится;
gh api repos/<repo>/releases --jq '.[0:5][] | "\(.tag_name) \(.published_at)"').
⚠️ Граница: это НЕ лазейка для бампа. Новая ценность = факт, который изменился в мире и меняет
приоритет для мейнтейнера; «прошла неделя» и «напоминаю» ценностью не являются. Перепроверять
на опубликованном артефакте, а не на чекауте: pip install <pkg>==<version> + контрольный кейс,
который ОБЯЗАН пройти — иначе красная строка ничего не доказывает.
Шаг 2 — прочитать, чего именно хотят
gh pr view <N> --repo <owner/repo> --json reviews,comments
gh api repos/<owner/repo>/pulls/<N>/comments --jq '.[] | "\(.user.login) | \(.path):\(.line) | \(.body)"'
Второй вызов обязателен: инлайн-комменты к строкам кода не видны в первом. Именно в них
обычно и лежит просьба (замер 06.08: go-sdk#1148, «let's remove this comment» — только там).
Прочитай просьбу буквально. «Убери этот комментарий» — значит убрать, а не сократить и не
переписать красивее. Мейнтейнер знает стиль своего репо лучше нас.
Шаг 3 — сделать то, что просили
Правка → прогон того, что репо просит прогнать (тесты/линт/генератор доков) → пуш в ту же
ветку. Если доки генерируются (//go:generate, weave, nbconvert), правь и исходник, и
сгенерированный файл — иначе ревьюер увидит рассинхрон и вернёт PR второй раз.
Не согласен — не молчи и не уступай молча: возражения по пунктам + «переубеди меня»
([[objection-sparring-to-consensus]]). Уступка без аргумента — такой же провал, как спор без него.
Шаг 3-бис — если ревьюеры МЫ: проверять доказательство, а не поведение
Автор написал «поправил всё по вашим замечаниям». Прогнать его тесты и увидеть зелёное —
недостаточно: зелёный тест доказывает, что тест проходит, а не что защита работает.
Порядок:
- Забрать ветку:
git fetch upstream pull/<N>/head:pr<N> && git checkout pr<N>.
- Прогнать так, как автор говорит, и назвать точный способ прогона в ответе (какие ячейки,
с сетью или без, какой python) — иначе вердикт невоспроизводим.
- Сломать по одной защите в РАБОЧЕМ коде, не трогая тесты, и убедиться, что тест краснеет.
Не поймал — защита есть только на бумаге, и это находка ценнее любого стилевого замечания.
⚠️ Грабли метода (наступил 06.08): мутация, которая переименовывает строку и в проверке, и в
тесте разом, «проходит» всегда — тест сравнивает переименованное с переименованным. Ломать
надо ПОВЕДЕНИЕ: убрать return refused, а не переименовать код ошибки. Первый прогон дал три
ложных «ПРОПУЩЕНА» именно из-за этого; сначала я усомнился в их коде, и был неправ.
⚠️ Вторые грабли: изолированный прогон пары ячеек не тянет импорты из ранних. NameError —
это скорее дефект твоей выборки, чем их notebook. Проверь, откуда символ, прежде чем писать
человеку, что у него сломано.
⚠️ Чужой РЕПРО (не PR) проверяй так же: у каждой строки вывода нужен контроль, который МОГ провалиться — негатив для «гейт вернул True» (гейт вообще умеет False?) и позитив для «операций 0» (у заведомо правильного случая они есть?). Замер 16.09 litellm#41395: оба вывода верны, но без контролей не доказаны; ContextVar-stash в цикле давал 0 и у родителя.
Вердикт формулируй как факт с цифрой («47 outcomes, exit 0, три мутации пойманы»), а не как
«выглядит хорошо». Закончил — так и скажи: «nothing further from me on this», чтобы автор не
ждал следующего круга.
Шаг 3-тер — ВОЗРАСТ ИСТОЧНИКА ЦИФРЫ (пережито 13.09.2026, класс claim-judged-against-current-file-not-run-date)
Цифра уезжает в ЧУЖОЙ тред → перед отправкой ответь на один вопрос: когда появилось то, по чему
я сужу? Судишь прогоны/логи/историю по ТЕКУЩЕМУ содержимому файла, настройки или поля — проверь,
что оно существовало на момент тех событий (ls бэкапов, mtime, git log, дата правки). Не проверил
— цифра не уезжает, уезжает формулировка «замерено с такой-то даты».
Замер, родивший правило: в anthropics/claude-code#66402 мы опубликовали «26 из 43 безлюдных
прогонов (60%) шли не на объявленной модели». Поле model: дописали в определения задач 10.09,
а под нож пошли прогоны 08–10.09 — объявления тогда просто не было. Честный замер дал 0 дрейфа и
обратный вывод. Отзыв: issuecomment-[id], строка в 00-System/Mycroft-Correction-Log.md.
Прибор с этим гвардом: ~/.claude/scripts/task_model_drift.py (+ регресс _test_task_model_drift.py).
⚠️ Усиление: чем ВЫГОДНЕЕ цифра нашему аргументу в чужом треде, тем строже проверка
([[self-serving-conclusion-needs-stronger-proof]]). Нашу ошибку поймало требование чужого инженера
сделать репро, а не наша собственная проверка — значит проверку надо делать ДО отправки.
Шаг 4 — ответить коротко
Форма, которая работает (замер: единственная, дававшая ответ живого человека):
- что сделал — фактом, без «спасибо за ревью, очень ценно»
- ссылка на коммит, если правка не однострочная
- ⭐ подпись-CTA (anton 10.09, supersedes прежнее «никакой подписи»): ОДНА строка со ссылкой
github.com/tonydzi, источник $OBSIDIAN_VAULT/08-Templates/github-touch-signature.md.
Carve-out против спама: на НАШЕМ PR в идущем диалоге достаточно ОДНОЙ подписи на тред —
уже подписывался в этом треде → в следующих репликах можно без неё. Первое касание чужого
треда (ревью/коммент) — подпись всегда. Канон: память cta-signature-every-github-touch;
сторож cta_signature_watch.py.
Голос Майкрофта (§3.3): пишу я → называюсь. Короткий технический ответ на чужом PR — цель
≤5 слов на само раскрытие, тон по каналу.
gh pr comment <N> --repo <owner/repo> --body "Done — <что именно>. <ссылка/деталь>."
⭐ ПОЧЕМУ ЗДЕСЬ КОММЕНТ, А НЕ РЕВЬЮ (граница правила, замер 05.09): это РЕПЛИКА в уже идущем диалоге — она комментом и подаётся. Но ПЕРВОЕ касание по ЧУЖОМУ PR идёт только через gh pr review <N> -R <owner/repo> --comment --body-file <файл>: gh pr comment не создаёт объект Review, и работа не видна ни в графе вкладов, ни в фильтре reviewed-by:tonydzi (в чужих PR за 30 дн: 127 комментов против 7 ревью = 5.2% видимых). У ISSUE ревью НЕ СУЩЕСТВУЕТ — там коммент нормален. Канон: Библия reglament-v-chuzhom-pr-pishem-revyu-a-ne-kommentariy + файл-константа $OBSIDIAN_VAULT/08-Templates/github-review-not-comment.md.
Шаг 5 — записать кредит автору совета
Совет постороннего человека — это вклад, и он именной (приказ Антона 06.08, скилл
/alpha-credit):
python ~/.claude/scripts/alpha_credit.py add --source gh --author "<login>" --handle "<login>" --url "<url PR>" --advice "<что подсказал>" --kind critique --consent public --ref "gh:<owner/repo>#<N>:<id>"
Шаг 5-бис — ответ инженера = контент-момент (anton 24.08: «сразу в контент-воронку»)
Живой инженер (мейнтейнер / посторонний контрибьютор, не бот) ответил в нашем треде или на
нашу лечилку → в ТОМ ЖЕ заходе черновик в воронку:
python [путь владельца] capture --title "<хук: кто ответил и что это значит>" --note "<суть + ссылка на тред>" --tier teaser
Draft-first: это черновик в triage, публикацию решает гейт воронки — здесь только захват.
⛔ Не захватываем: ответы ботов · чисто механические «done/merged» без истории · приватное.
Тир по весу: реплика = teaser; мерж нашего фикса или разговор с мейнтейнером Anthropic/OpenAI = medium.
Канон: [[everything-becomes-content]] + [[content-miner-reflex]] + реглумент reglament-pochinil-u-sebya-srazu-razday-miru (реакция мира на раздачу — вторая половина той же трубы).
Шаг 6 — закрыть петлю
Ответил → позиция уходит из «мяч у нас» сама на следующем тике сторожа. Проверять не надо;
надо проверить обратное: если через сутки она всё ещё там — ответ не доехал (не та ветка,
не тот PR, коммент ушёл в issue).
Границы
- Мерж чужого кода, юридическое (CLA/DCO), деньги, удаление чужого — Tier-2, к Антону.
- Массовые одинаковые комменты веером — запрещено: громкость ≠ спам (§1.5).
- Ответ боту текстом — мусор в чужом треде. Боту отвечают коммитом.
Как понять, что скилл работает
Считаем не число ответов, а возраст самого старого живого долга в digest. Работает —
если он держится ниже суток. Растёт — прибор есть, а рук нет, и это отдельный разговор,
а не повод отвечать быстрее.
⭐ Чёрный юмор обязателен (anton 10.09.2026, голосом)
Любой текст, который этот скилл отправляет ЖИВОМУ человеку наружу, несёт чёрную самоиронию — ровный служебный тон = дефект, объяснять надо не шутку, а её отсутствие. Строку берёшь через /mycroft-joke (полка (д) банка + строки 56-62 под холодное касание), приёмка — /taste-check P36. ⛔ Не шутим ровно в трёх зонах, и они не про вкус: строго-научная публикация (arXiv/журнал/JOSS) · юр.обязательство и анкета · красный список банка (чужая иконография угнетённых, псевдо-слуры, отрицание что я ИИ, юмор поверх диагноза/денег/беды собеседника). Одна шутка на тред. Канон: CLAUDE.md §3.3 §Поправка 10.09 + [[dark-humor-default-everywhere-external]].
<!--kit-footer-->
Like this skill? It is one of 100 in 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 (a second opinion from a panel of external models), claude-memory-tidy (stop your agent's memory from rotting), telegram-mcp-kit (your own Telegram over MCP in about 15 minutes).
Author: Anton Dziatkovskii, Palo Alto AI Research Lab. Telegram @tonydzi - WhatsApp +1 341 222 9178 - X @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.