Дополнительный источник: https://lzw.me/docs/opencodedocs/obra/superpowers/advanced/branch-completion/index.html
Зачем это нужно
Седьмой, завершающий шаг базового workflow (урок 17.5). Скилл finishing-a-development-branch активируется, когда реализация завершена, тесты проходят и пора решать, как интегрировать работу. Это короткий, но важный скилл: он закрывает цикл, открытый в уроке 17.7 (изоляция), — снимает изоляцию правильно: проверить, спросить, исполнить выбор, вычистить. Ошибки на этом шаге дороги: merge не в ту базу или удаление worktree с незакоммиченными файлами теряют работу.
Сквозная идея модуля здесь замыкается: все шаги оставляли за человеком право решать (HARD-GATE в уроке 17.6, выбор исполнения в 17.9, adjudикация находок в 17.11); финальное решение об интеграции — тоже решение человека, и скилл это фиксирует явно.
Основная идея
Формула скилла: «Verify tests → Detect environment → Present options → Execute choice → Clean up» — проверить тесты → определить окружение → предъявить опции → исполнить выбор → вычистить. Меню опций предъявляется ровно как написано — кратко, исчерпывающе, без импровизированных пунктов; решение об интеграции принимает человек. Скилл объявляется в начале: «I'm using the finishing-a-development-branch skill to complete this work».
Шаг 1: верифицировать тесты
Полный suite проекта (npm test / cargo test / pytest / go test ./...).
Падают — отчитаться о падениях и остановиться: меню появляется только после зеленого suite:
Tests failing (<N> failures). Must fix before completing:
[показать падения]
Проходят — к шагу 2. Смысл первого шага (из таблицы рационализаций): «зеленый прогон доказывает только то дерево, на котором он ran» — «тесты проходили раньше в сессии» не считается: прогон выполняется на дереве, которое вы собираетесь интегрировать.
Шаг 2: определить окружение
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
# зафиксировать сейчас, пока мы еще внутри workspace — шаг 5 меняет
# каталог до того, как очистке (шаг 6) понадобится это значение
WORKTREE_PATH=$(git rev-parse --show-toplevel)
Таблица скилла:
| Состояние | Меню | Очистка |
|---|---|---|
GIT_DIR == GIT_COMMON (обычное репо) |
стандартные 3 опции | worktree чистить нечего |
GIT_DIR != GIT_COMMON, именованная ветка |
стандартные 3 опции | по признаку владения (шаг 6) |
GIT_DIR != GIT_COMMON, detached HEAD |
сокращенные 2 опции (без merge) | управляется извне — оставить на месте |
Шаг 3: определить базовую ветку
Базовая ветка — то, от чего ответвилась работа; обычно названа в плане, разговоре или upstream ветки. Неизвестна — спросить: «This branch split from <лучшая догадка> — верно?» Подтверждение до merge обязательно: merge не в ту базу дорого отменять.
Шаг 4: предъявить опции
Обычное репо и worktree с именованной веткой — ровно эти 3 опции:
Implementation complete. What would you like to do?
1. Merge back to <base-branch> locally
2. Push and create a Pull Request
3. Keep the branch as-is (I'll handle it later)
Which option?
Detached HEAD — ровно эти 2:
Implementation complete. You're on a detached HEAD (externally managed workspace).
1. Push as new branch and create a Pull Request
2. Keep as-is (I'll handle it later)
Which option?
Правила меню (важные!): меню предъявляется дословно — кратко, с опциями из списка и без придумывания. Выбрасывание работы появляется только как ответ на явную просьбу партнера (раздел ниже). Подождать ответа: решение об интеграции — его. Отдельное правило детекции среды из урока 17.7 помнит о ветке: detached HEAD «управляется извне» — отсюда сокращенное меню без merge.
Шаг 5: исполнить выбор
Опция 1: merge локально
MAIN_ROOT=$(git -C "$(git rev-parse --git-common-dir)/.." rev-parse --show-toplevel)
cd "$MAIN_ROOT"
# merge сначала — успех проверить до удаления чего-либо
git checkout <base-branch>
git pull
git merge <feature-branch>
# верифицировать тесты на смерженном результате
<test command>
Тесты падают на результате — стоп, worktree и ветка остаются на месте, разбираться: ничего не запушено, merge локален и обратим. Результат зеленый — очистка worktree (шаг 6), затем удаление ветки:
git branch -d <feature-branch>
Опция 2: push и PR
git push -u origin <feature-branch>
# из detached HEAD — назвать новую ветку на remote:
# git push origin HEAD:refs/heads/<new-branch>
Затем PR/MR против <base-branch> инструментарием форджа — CLI при наличии, или URL создания, который печатает большинство форджей при push, — по шаблону и конвенциям репо; URL — партнеру. Worktree сохраняется: итерации по замечаниям PR происходят в нем.
Опция 3: оставить как есть
Отчет: Keeping branch <name>. Worktree preserved at <path>.
Если партнер просит выбросить работу
Путь существует только как ответ на явную просьбу выбросить работу. Подтверждение дословно:
This will permanently delete:
- Branch <name>
- All commits: <commit-list>
- Worktree at <path>
Type 'discard' to confirm.
Ждать именно слово discard. После подтверждения — смена каталога в MAIN_ROOT, очистка (шаг 6) и git branch -D <feature-branch>.
Шаг 6: очистка workspace
Выполняется для опции 1 и подтвержденного discard. Опции 2 и 3 worktree всегда сохраняют. Оба вызывающих уже сменили каталог в корень главного репо — удаление worktree обязано выполняться извне него — и используют GIT_DIR/GIT_COMMON/WORKTREE_PATH, зафиксированные в шаге 2 до смены каталога.
GIT_DIR == GIT_COMMON: обычное репо, чистить нечего; готово.WORKTREE_PATHвнутри.worktrees/илиworktrees/: worktree создал Superpowers — очистка наша:
git worktree remove "$WORKTREE_PATH"
git worktree prune # самозаживление: подчистить устаревшие регистрации
- Удаление отказано (
contains modified or untracked files): в worktree есть файлы, которых нет больше нигде — незакоммиченные планы, заметки, черновики. Никогда--forceпо собственной инициативе. Показать партнеру, что на кону:
git -C "$WORKTREE_PATH" status --porcelain -uall
Worktree removal refused — these files were never committed:
<список файлов>
1. Commit them to <branch> before cleanup
2. Move them into <main repo root>
3. Delete them (unrecoverable)
Which?
Исполнить выбор, затем удалить worktree.
- Иначе: workspace принадлежит среде хоста — оставить на месте; есть инструмент выхода из workspace (нативный тул каркаса) — использовать его.
Быстрая шпаргалка
| Опция | Merge | Push | Worktree | Ветка |
|---|---|---|---|---|
| 1. Merge локально | да | — | удаляется | удаляется |
| 2. PR | — | да | сохраняется | сохраняется |
| 3. Оставить | — | — | сохраняется | сохраняется |
| Discard (только явная просьба) | — | — | удаляется | удаляется (force) |
Отмазки
Выборка таблицы рационализаций скилла:
| Отмазка | Реальность |
|---|---|
| «Тесты проходили раньше в сессии» | Прогони suite на дереве, которое интегрируешь. Зеленый прогон доказывает только то дерево, на котором ran. |
| «Очевидно хочет merge» | Интеграция — решение партнера. Предъяви меню и жди. |
| «Похоже, закончил с фичей — предложу выбросить» | Меню исчерпывающе как написано. Discard — только когда партнер попросил теми словами. |
| «"Yeah, get rid of it" считается подтверждением» | Только напечатанное слово discard авторизует удаление. |
| «PR поднят — worktree теперь мусор» | Замечания PR правятся в этом worktree. Он остается до приземления работы. |
| «Другой worktree выглядит устаревшим — почищу и его» | Чистятся только worktree под .worktrees//worktrees/. Остальное принадлежит среде. |
«Removal refused — --force просто закончит очистку» |
Отказ значит: файлы существуют только там. --force уничтожает их навсегда. Покажи партнеру и спроси. |
| «Падение смерженного результата — наверняка flaky» | Падающий результат останавливает все. Ветка и worktree на месте, пока не разберетесь. |
| «База очевидно main» | Подтверди точку ответвления или спроси. Merge не в ту базу дорого отменять. |
| «Push отвергнут — force-push исправит» | Отвергнутый push значит: remote ушел вперед. Разберись; force-push — только по явной просьбе партнера. |
Практика
На учебной ветке (из практики урока 17.7) пройдите завершение целиком:
- Прогоните suite; зафиксируйте, зеленый ли он на дереве, которое интегрируете.
- Определите окружение командами шага 2; сверьте с ожиданием (worktree с именованной веткой).
- Предъявите меню дословно и «исполните» выбор опции 1: merge, тесты на результате, удаление ветки, очистка worktree.
- Сымитируйте отказ удаления: создайте в worktree незакоммиченный файл и убедитесь, что
git worktree removeотказывает; напишите три опции из скилла для этого случая.
Краткое резюме
finishing-a-development-branch закрывает цикл: верифицировать тесты на интегрируемом дереве → определить окружение и базовую ветку → предъявить меню дословно (3 опции, или 2 для detached HEAD) → исполнить выбор → вычистить по правилам владения.
- Меню — дословно, без импровизации; решение об интеграции — человека; discard — только по явной просьбе и слову
discard. - Merge: тесты прогоняются на смерженном результате; падение — стоп, все остается на месте.
- PR: worktree сохраняется для итераций по замечаниям.
- Отказ удаления worktree: файлы, которых нет нигде больше;
--forceпо своей инициативе запрещен — показать партнеру и спросить.
Вопросы для самопроверки
- Почему «тесты проходили раньше в сессии» не считается верификацией шага 1?
- Как окружение (обычное репо / worktree / detached HEAD) меняет меню опций и правила очистки?
- Почему подтверждение discard обязано быть напечатанным словом, а не «Yeah, get rid of it»?
- Что происходит при падении тестов на смерженном результате и почему это «дешевле», чем могло бы быть?
- Почему опция PR сохраняет worktree, а merge — нет?
- При отказе удаления worktree: какие три опции предъявляются и почему
--forceпо инициативе агента запрещен?
Что дальше
Семь шагов базового workflow пройдены. Следующая пара уроков — сквозные дисциплины качества: урок 17.13 разбирает systematic-debugging (4 фазы корневой причины) и verification-before-completion (доказательства прежде заявлений).