pr-reply · v1.1.0 · 2026-10-03 · sha256 5c65e83657a23423
pr-reply v1.1.0A
Immutable. This exact content never changes and is served at /api/v1/blob/5c65e83657a23423.
---
name: pr-reply
description: "- ОТВЕТИТЬ РЕВЬЮЕРУ на НАШЕМ пул-реквесте в чужом репозитории: найти треды, где мяч у нас, отделить живого человека от бота, сделать ровно то,… Триггеры: “/pr-reply“, “/prr“, “ответь ревьюерам“, “кто ждёт нашего ответа“, “долги по ревью“, “мяч у нас“, “разгреби PR“, “answer the reviewers“, “who is waiting on us“, а также САМ в начале любой сессии про GitHub и при алярме pr_watch про новое ревью"
version: 1.1.0
---
# /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 «самый людный живой тред домена первым»), так что грабля лежит
ровно на приоритетной дорожке.
Лечение — брать последнюю страницу явно:
```bash
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-бис — если ревьюеры МЫ: проверять доказательство, а не поведение
Автор написал «поправил всё по вашим замечаниям». Прогнать его тесты и увидеть зелёное —
**недостаточно**: зелёный тест доказывает, что тест проходит, а не что защита работает.
Порядок:
1. Забрать ветку: `git fetch upstream pull/<N>/head:pr<N> && git checkout pr<N>`.
2. Прогнать так, как автор говорит, и назвать точный способ прогона в ответе (какие ячейки,
с сетью или без, какой python) — иначе вердикт невоспроизводим.
3. **Сломать по одной защите в РАБОЧЕМ коде, не трогая тесты, и убедиться, что тест краснеет.**
Не поймал — защита есть только на бумаге, и это находка ценнее любого стилевого замечания.
⚠️ Грабли метода (наступил 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](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.