# Provedenie Vetki Do Uborki

> «Вынеси WIP или старый commit в ветку», «сделай PR, не смешивая», «PR смержен, архивировать?», «убери branch clutter и remote branches», «пока шли тесты, main продвинулся».

- Skill: `kir-kopylov/provedenie-vetki-do-uborki` (Agent Skill, multi-file: 11 files)
- Install (CLI): `npx skillmds@latest add kir-kopylov/provedenie-vetki-do-uborki`
- Raw SKILL.md: https://api.skillmd.com/api/skills/kir-kopylov/provedenie-vetki-do-uborki/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: kir-kopylov (https://skillmd.com/u/kir-kopylov)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/kir-kopylov/provedenie-vetki-do-uborki

---


# Git PR Lifecycle Safeguard

## Запуск Навыка

При явном вызове или однозначном смысловом совпадении применяйте навык сразу. Перед первым шагом покажите ровно одну короткую контекстную строку (не более 30 слов) и продолжайте работу в том же ответе, не ожидая реакции:

Применяю **«Безопасное проведение изменений через Pull Request»**: <кратко назовите конкретную дополнительную процедуру или проверяемый результат для текущего запроса>; продолжаю без ожидания.

Не включайте в строку `author_github`, внутреннее имя папки или пересказ всего запроса. Не спрашивайте, применять ли навык.

Если одновременно подходят совместимые навыки, выберите минимальный набор и покажите одну общую строку. Если подходы ведут к несовместимым результатам и запрос не позволяет выбрать, спросите только о желаемом результате, не о разрешении применить навык.

Запуск навыка не расширяет полномочия. Выполните всю безопасную и уже разрешённую часть; запросите подтверждение только непосредственно перед ещё не разрешённым внешним или изменяющим действием. Не запрашивайте повторно уже данное разрешение и не дублируйте системное окно подтверждения.

## Обзор

Skill защищает цикл `WIP/old commit -> clean PR -> merge -> branch cleanup`. Он нужен не для любого git-вопроса, а когда есть риск смешать состояния: dirty tree, старый локальный commit, неправильная feature branch, stale remote branch, PR после merge, branch cleanup или remote branch clutter.

Главный принцип: сначала доказать реальное состояние через read-only проверку, затем менять repo только по явному и проверенному scope.

## Естественные Входы

Запускайте skill на фразы:

- «вынеси это в отдельную ветку»;
- «сохрани старый commit»;
- «сделай PR, но не смешивай»;
- «этот WIP не должен попасть в текущую ветку»;
- «PR смержен, что теперь?»;
- «убери branch clutter»;
- «можно архивировать после PR?»;
- «почисти remote branches»;
- «доведи локальную работу до clean PR»;
- «пока шли тесты, main продвинулся; перед commit ещё раз сверь базу и не потеряй WIP»;
- «после merge верни repo в чистое состояние».

## Общий Первый Шаг

Перед любым изменяющим действием выполните read-only reality check. Если доступен `sverka-git-pered-deystviem`, используйте его правила как нижний gate.

Минимальный снимок:

```bash
git -C <repo> status --short --branch --untracked-files=all
git -C <repo> diff --stat
git -C <repo> diff --cached --stat
git -C <repo> stash list --max-count=5
git -C <repo> branch -vv
```

Если работа касается push, PR, merge или remote cleanup, после локального снимка обновите refs и проверьте PR state:

```bash
git -C <repo> fetch --all --prune
git -C <repo> branch -r --merged origin/main
git -C <repo> branch -r --no-merged origin/main
```

Не называйте repo «чистым», если чистое только рабочее дерево, но активная ветка не та, upstream `[gone]`, есть локальный commit без PR или осталась remote branch с diff относительно `origin/main`.

## Явная Граница Одобрения Перед Публикацией

Обычный цикл ниже не добавляет отдельный вопрос об одобрении. Но если
пользователь явно потребовал сначала показать кандидата, действуйте так:

1. После окончательного staging успешно обновите remote refs и зафиксируйте:
   - выбранный до одобрения режим границы. По умолчанию `strict-base`:
     любой сдвиг base запрещает сам push. Режим `target-only` допустим,
     только если пользователь до одобрения явно согласился, что
     push защищается по target/destination, а base ограничивает только PR diff.
     Менять режим после одобрения нельзя;
   - имя remote, полные имена base/target refs и один фактический
     destination. Без сырого вывода в tool log получите списки из
     `git remote get-url --all "<remote>"` и
     `git remote get-url --push --all "<remote>"`. Для этой границы оба
     списка должны содержать ровно один и тот же URL, а к этой URL не
     должна применяться ни одна `url.*.insteadOf` или `url.*.pushInsteadOf`. Иначе не
     публикуйте: имя `origin` или уже раскрытая URL не исключают
     повторную подстановку адреса;
   - полные строки `<base-full-ref>` и `<target-full-ref>` должны различаться.
     Их равенство запрещает одобрение, `commit`, `push` и работу с PR: точный
     lease защищает значение указанного ref, но не доказывает, что target не
     является самой base-веткой;
   - destination должен быть без встроенного пароля или token,
     секретного query/fragment и приватного локального пути, чтобы его можно
     было безопасно показать пользователю и передать `git ls-remote` и `git push`.
     Иначе остановитесь до push и настройте credential-free destination;
   - OID staged tree из `git write-tree`;
   - OID base и target либо подтверждённое отсутствие target читайте
     напрямую с этого destination через
     `git ls-remote --heads "<destination>" "<base-full-ref>" "<target-full-ref>"`;
   - при существующем target OID HEAD должен точно совпадать с OID target; при
     отсутствии target OID HEAD должен точно совпадать с OID base. Иначе сначала
     пересоберите кандидата от точного удалённого состояния;
   - если существует `MERGE_HEAD`, он должен содержать ровно один OID,
     совпадающий с OID base. Дополнительные merge parents требуют пересборки;
   - OID текущего HEAD как первого будущего parent и, если существует
     `MERGE_HEAD`, все OID из него как остальных будущих parents;
   - ожидаемый merge-base base и будущего commit: для обычного commit это
     merge-base между base и HEAD, для merge с base среди `MERGE_HEAD` — OID
     base. Merge без base среди будущих parents этим правилом не публикуйте.
   - точный снимок будущих метаданных commit: сообщение побайтово, включая
     trailers, строки author и committer с name, email, date и timezone,
     режим подписи и полный ожидаемый набор headers с их значениями. Не
     подменяйте этот снимок текущими значениями Git config. Если значение
     подписи или другого header нельзя зафиксировать до одобрения, этот commit
     требует отдельного показа и нового одобрения после создания, но до push.
   Покажите пользователю все зафиксированные выше поля снимка, включая режим
   границы, remote, полные base/target refs и их OID, destination, staged tree,
   HEAD, будущих parents, merge-base и метаданные commit, а также полный
   кандидат PR через `git diff --cached <expected-merge-base-oid> --`; при существующем target
   также покажите изменение относительно него через
   `git diff --cached <target-oid> --`.
   Итоговый tree diff не доказывает отсутствие промежуточной неопубликованной
   истории и не заменяет точное совпадение HEAD с target или base.
   После полного показа считайте этот снимок единственным текущим кандидатом
   одобрения. Новый полный снимок сразу аннулирует прежний кандидат и его
   одобрение.
2. Получите явное одобрение этого снимка. Пользователь не обязан повторять
   все SHA, refs, destination и метаданные: фраза
   `одобряю последний показанный снимок` достаточна, только если существует
   ровно один текущий кандидат и после его показа не менялось ни одно поле.
   Такое одобрение относится ко всему снимку, а не только к staged tree.
   Ответ `да`, один OID staged tree, сокращённый SHA или ссылка на прежний
   снимок недостаточны. Непосредственно перед `commit` снова
   обновите remote refs и сравните все зафиксированные значения, включая полный
   список будущих parents, метаданные commit, режим границы, destination и
   отсутствие URL-подстановок, с одобренными.
   Ошибка обновления или любое изменение запрещает `commit`, `push`, создание и
   обновление PR до нового показа и одобрения.
   Только после успешного сравнения создайте commit из одобренных значений:
   явно передайте точные bytes сообщения, author/committer identities,
   timestamps/timezones, режим подписи и ожидаемые headers; не оставляйте их
   Git config или текущему времени.
3. Tree нового commit должен совпасть с одобренным tree, упорядоченный список
   его parents — с одобренным списком, а merge-base base и commit — с ожидаемым
   merge-base. До `push`, не выводя неожиданные raw metadata в tool log,
   прочитайте полное содержимое объекта через
   `git cat-file commit "<verified-commit-oid>"` и побайтово сравните его
   headers и сообщение с одобренным снимком. Любой дополнительный header или
   изменение author, committer, trailers, подписи либо сообщения, в том числе
   внесённое hook-ом, делает commit новым кандидатом: не публикуйте его до
   нового показа и одобрения. Перед `push` снова сравните destination и отсутствие
   URL-подстановок, перечитайте с него base/target и повторите проверки commit.
   Push выполняйте в явно зафиксированный destination и только с явным refspec
   `<verified-commit-oid>:refs/heads/<target>`. Сам push должен включать точное
   серверное условие target:

   ```bash
   # target существовал при одобрении
   git push "--force-with-lease=refs/heads/<target>:<approved-target-oid>" \
     "<approved-push-destination>" \
     "<verified-commit-oid>:refs/heads/<target>"

   # target отсутствовал при одобрении; пустой expect требует его отсутствия
   git push "--force-with-lease=refs/heads/<target>:" \
     "<approved-push-destination>" \
     "<verified-commit-oid>:refs/heads/<target>"
   ```

   Точный lease — только server-side CAS для обновляемого target ref;
   при совпадении expect он сам по себе может разрешить non-fast-forward.
   Поэтому прежние gates обязаны отдельно доказать нужную ancestry и
   fast-forward от одобренного target.

   Обычный `git push` не умеет атомарно сделать update target зависимым от
   неизменности base, который не обновляется. Повторный fetch или no-op
   refspec для base не создают server-side cross-ref CAS. Если одобрение запрещает
   любую публикацию при сдвиге base, остановитесь до push, пока не доказан
   такой серверный guard. Только в заранее одобренном режиме `target-only`
   сдвиг base после target push запрещает работу с PR и требует нового diff
   и одобрения, но не выдавайте это за server-side запрет самого push.
4. После `push`, но до работы с PR, прямым `git ls-remote` с одобренного
   destination снова прочитайте base/target: OID base должен остаться
   одобренным, а OID target — указывать ровно на этот commit.
   Создайте или обновите PR только для этих веток и прочитайте его обратно: имена
   head/base PR должны совпасть с ожидаемыми ветками, а OID head PR должен
   совпасть с проверенным OID target. OID base PR не заменяет повторную проверку
   живого OID удалённой base. Иначе снова покажите кандидата и получите новое
   одобрение.

## Режим 1: local-wip-to-clean-pr

Используйте, когда локальный WIP или старый commit нужно вынести в отдельный PR без смешения с текущей веткой.

Процесс:

1. Зафиксируйте исходный scope:
   - текущая ветка и upstream;
   - staged, unstaged, untracked;
   - нужные файлы или commit SHA;
   - есть ли чужие изменения в рабочем дереве.
   - если в одном intended-файле смешаны нужные и чужие hunks и их нельзя
     безопасно разделить, остановитесь до staging, stash и rebase и запросите
     один выбор точного состава патча.
2. Если есть WIP в текущем дереве, сохраните его безопасно:
   - общий `git stash push --include-untracked -m "<описание>"` допустим,
     только если весь status, включая untracked, доказанно относится к задаче;
     создание новой записи и глобальную чистоту после неё докажите по
     `references/late-base-gate.md`;
   - явный `git add <paths>` только после проверки scope, если пользователь подтвердил файлы;
   - не используйте общий stash или `git add .` при mixed worktree.
3. Создайте clean branch от актуального `origin/main`:
   - `git switch -c codex/<short-name> origin/main`;
   - для старого commit используйте `git cherry-pick <sha>`.
4. Разрешайте конфликты без потери текущих строк:
   - для `catalog.md` сохраняйте уже существующие skill rows и добавляйте новую строку рядом;
   - для registry/docs не откатывайте изменения, пришедшие в `origin/main`;
   - после ручного разрешения проверяйте отсутствие стандартных conflict-marker строк.
5. Доведите пакет до текущего repo contract:
   - `known-exceptions.yaml`;
   - секция `## Логирование Сбоев`;
   - examples и catalog row для `team-ready`;
   - отсутствие шаблонных заглушек.
6. Привяжите проверки к точному состоянию:
   - обновите refs и сохраните `tested_base_sha`;
   - разделите clean committed-only состояние и WIP; для WIP проверьте весь
     status, включая untracked, stage только доказанный intended scope и
     сохраните `tested_tree=$(git -C <repo> write-tree)`;
   - не продолжайте автоматически при mixed WIP;
   - выполните `git diff --check`, `git diff --cached --check`, полную suite и
     нужные живые пробы.
7. После тестов выполните поздний гейт базы до любого commit:
   - после каждого первоначального или повторного test run заново обновите
     refs, сравните `current_base_sha` с `tested_base_sha` и докажите через
     `merge-base --is-ancestor`, что база входит в `HEAD`;
   - только после успешных compare и ancestry, до commit, сохраните
     `verified_base_sha`; новый drift возвращает процесс к этому же gate;
   - при drift commit запрещён: clean committed-only переносите без stash, а
     WIP — только по fail-closed процедуре `references/late-base-gate.md`;
   - после переноса заново получите `tested_tree`, повторите полную suite,
     base-sensitive checks и затронутые живые пробы; для изменения Codex
     plugin явно пересчитайте semver относительно нового `origin/main`, сохранив
     выбранный до drift тип повышения (`patch`, `minor` или `major`); если тип
     не доказан, остановитесь для одного выбора до повторных тестов.
8. Сформируйте commit и свяжите его с тестами:
   - непосредственно до commit повторно сравните index с `tested_tree`,
     докажите отсутствие tracked изменений вне index, untracked и unmerged;
   - после commit сохраните `verified_head_sha` и докажите, что его tree в
     точности равен `tested_tree`, а staged, unstaged и untracked отсутствуют;
   - после доказанного commit удалите только созданный процессом
     recovery-stash: заново найдите его текущий selector по сохранённым object
     SHA и subject, непосредственно перед `stash drop` ещё раз сверьте SHA и
     не выполняйте cleanup без исключительного контроля над stash reflog;
     если точная идентичность или такой контроль не доказаны, сохраните stash
     и сообщите его SHA и команду восстановления. Исходные stashes не меняйте.
9. Непосредственно перед push выполните короткий гейт ещё раз:
   - после fetch база должна равняться `verified_base_sha` и входить в `HEAD`;
   - текущие HEAD и tree должны равняться `verified_head_sha` и `tested_tree`,
     а полный status — оставаться пустым;
   - любой drift возвращает процесс к переносу и повторным проверкам.
10. Push:
   - новую ветку публикуйте обычным `git push -u origin <branch>`;
   - перед переписыванием опубликованной ветки сохраните её remote SHA, а
     после rebase/amend используйте только явный lease
     `--force-with-lease=refs/heads/<branch>:<expected_remote_sha>` по правилам
     `references/late-base-gate.md`.
   Если действует явная граница одобрения, вместо этой сокращённой команды
   используйте явные destination, refspec и exact lease из раздела выше.
11. После push, но до PR, снова получите `origin/main` и точный remote head:
   - base drift возвращает процесс к rebase и повторным тестам;
   - remote head, не равный `verified_head_sha`, останавливает процесс;
   - оставшийся из-за заблокированного cleanup recovery-stash сохраняйте по
     object SHA и явно называйте способ восстановления.
12. Откройте PR - это стандартное завершение цикла `local-wip-to-clean-pr`, а не
   шаг, ожидающий отдельного запроса на публикацию:
   - title и body должны описывать scope, происхождение WIP и проверки;
   - затем проверьте `mergeable`, changed files и checks.
13. После открытия или обновления PR проверьте автоматическое review от
    `chatgpt-codex-connector`: если согласны с замечанием - почините и
    запушьте фикс; если не согласны или видите иначе - ответьте на
    комментарий с обоснованием на русском. Не оставляйте замечание бота без
    ответа ни в одну, ни в другую сторону. Это про обработку уже пришедшего
    отзыва, а не про подмену собственного review содержимого PR.

Если title или body изменены после выполнения PR-governance job, старый зелёный
job не подтверждает новые метаданные. Снова проверьте текущий текст локальным
governance script, если он есть в repo. Для утверждения именно о CI нужен новый
`pull_request` event с уже исправленными метаданными: простой rerun старого job
может использовать прежний event payload. Не создавайте бессодержательный
commit только ради нового события — разделяйте локальную проверку текущего
текста и устаревший CI-сигнал до следующего содержательного push или иного
штатного нового события.

## Режим 2: post-merge-branch-housekeeping

Используйте после merge или когда пользователь хочет убрать branch clutter.

Процесс:

1. Обновите refs:
   - `git fetch --all --prune`.
2. Проверьте, что PR действительно merged, а не только green:
   - PR `state`;
   - `mergedAt` или merge commit;
   - commit reachable из `origin/main`.
3. Верните локальный repo в ожидаемое состояние:
   - если worktree clean, переключитесь на `main`;
   - fast-forward local `main` до `origin/main`;
   - не продолжайте новую работу из старой feature branch.
4. Удалите локальные branches только если они merged в `origin/main`.
5. Для remote cleanup:
   - перечислите `git branch -r --merged origin/main`;
   - сверяйте с PR state, если branch name неочевиден;
   - удаляйте remote branch только если она доказанно merged или явно признана stale/discarded пользователем.
6. Unmerged remote branches не удаляйте автоматически:
   - покажите commit, diff, возраст, PR state;
   - классифицируйте `keep`, `delete after confirmation`, `rebuild as new PR`.
7. В конце покажите:
   - текущую ветку;
   - clean worktree;
   - локальные branches;
   - remote branches, которые остались, и почему.

## Правила Безопасности

Нельзя:

- использовать `git add .` при mixed worktree;
- создавать общий stash при недоказанном mixed WIP;
- продолжать commit или push после drift `origin/main` по результатам старых тестов;
- удалять recovery-stash по текущей позиции вместо записанного object SHA;
- удалять ветку, если commit не reachable из `origin/main` или merged PR;
- force-push без явного ожидаемого remote SHA в `--force-with-lease`;
- выполнять `git reset --hard`;
- считать `git branch -r` достаточным доказательством для удаления remote branch;
- продолжать новую работу из старой feature branch после merge;
- считать skipped publish job проблемой без проверки условия workflow;
- считать `200`, green check или merged PR доказательством локальной установки.

## Границы

Не используйте skill:

- для учебного вопроса без намерения менять repo;
- для CI-debug как отдельной задачи, если проблема уже локализована в логах GitHub Actions;
- для публикации PR, если пользователь просил только анализ или план;
- для destructive cleanup вне подтверждённого repo;
- чтобы заменить review содержимого PR.

Если есть несколько repo-кандидатов или пользователь не указал target repo, остановитесь после read-only поиска и попросите выбрать один repo.

## Опрос После Использования

Опрос задаётся один раз — после завершения цикла (PR влит и cleanup сделан) или явного стопа, не посреди рабочего цикла. Если пользователь уже ответил «пропустить» в этой сессии, не переспрашивайте.

```text
Опрос по skill:
1. Что в этом использовании provedenie-vetki-do-uborki было полезно?
2. Что стоит доработать в skill или его формате?
Можно ответить коротко или написать "пропустить".
```

Если пользователь ответил, сохраните санированную карточку в `~/.codex/skill-runs/provedenie-vetki-do-uborki/usage-feedback.jsonl` — лучше через bundled script:

```bash
python3 scripts/log_usage_feedback.py --liked "..." --improve "..." --outcome "..."
```

Script перед записью редактирует приватные пути, контакты и token-like строки и сохраняет `redaction_applied` и `redaction_types`. Если запись невозможна из-за sandbox, прав или отсутствия tools, не делайте вид, что лог сохранён: скажите об этом и покажите короткую JSONL-карточку для ручного сохранения. Raw-ответы, контакты, пути и секреты не коммитить.

## Логирование Сбоев

Перед выполнением прочитайте локальный `known-exceptions.yaml` как список уже известных случаев и применяйте подходящее `do_next_time` без нового поиска.

Если пользователь поправил skill, tool/API/browser упал, нарушен режим работы, пришлось искать workaround или skill сделал ложное предположение, запишите приватную карточку в `~/.codex/skill-runs/<skill-name>/exception-log.jsonl`.

Пишите факты: что skill хотел сделать, что сделал, где сломался, какая предпосылка была ложной и что сделать в следующий раз. Если поле неизвестно, пишите `unknown`. Raw logs не коммитить.

## Критерий Готовности

Работа завершена, когда:

- рабочее дерево clean или явно назван оставшийся intentional WIP;
- текущая ветка соответствует этапу: clean PR branch до merge, `main` после cleanup;
- PR создан или cleanup завершён;
- проверки названы явно;
- поздний гейт после тестов и короткий гейт перед push прошли на одном
  `verified_base_sha`, а опубликованный `verified_head_sha` содержит ровно
  проверенный `tested_tree`;
- созданный процессом recovery-stash удалён по object SHA либо сохранён с
  точной командой восстановления; исходные stashes не изменены;
- merged local branches удалены;
- remote branches удалены только если merged/closed и safe;
- оставшиеся unmerged branches перечислены с причиной keep/delete-later;
- пользователь понимает, что не было изменено и почему.

