18 KiB
name, description
| name | description |
|---|---|
| phase-work | Берёт ровно одну фазу из среза в docs/phases/ и доводит её до конца — в своём git worktree и на своей ветке, чтобы над одним срезом можно было запустить несколько агентов параллельно. Используй, когда просят «сделай фазу 25», «возьми фазу», «реализуй фазу», «начни срез», «раздели срез между агентами», «work on the phase». Не для ревью уже написанного — там /phase-review, не для бага — /bug-work, и не для правки одного файла по просьбе. |
Работа над фазой
Один запуск — одна фаза. Скилл берёт её из docs/phases/, делает целиком, проверяет по её же
критериям приёмки и отдаёт готовую ветку. Ничего сверх взятой фазы он не трогает: именно это
позволяет запустить несколько агентов на один срез и не получить кашу.
Критерии приёмки уже написаны. У каждой фазы есть «Задачи», «Тесты, без которых фаза не закрыта» и
«Критерий готовности», у проекта — инварианты в AGENTS.md, у среза — дизайн-док. Выдумывать
нечего, надо сделать и доказать.
Какую фазу брать
Номер передали в аргументе — берёшь его, вопросов нет. Так пользователь раздаёт фазы агентам:
/phase-work 25, /phase-work 26 в соседних окнах.
Номера нет — выбираешь сам:
- Читаешь
docs/phases/README.md: находишь первый срез, где есть ⬜. - Внутри среза берёшь первую ⬜, у которой закрыты зависимости — они перечислены в шапке фазы.
- Проверяешь, что её никто не взял:
git branch --list "phase/<номер>-*"пусто.
Статус в README.md — первый источник правды, ветка — только арбитр гонки. 🔄 значит занято,
✅ значит сделано, ⬜ значит свободно. Ветка может существовать и при ✅ — её просто не убрали после
слияния; это не заявка, а мусор, см. «Кто убирает ветку».
Фаза 🔄 — чужая работа в процессе. Не трогай её, даже если кажется, что там застряли.
Как застолбить
Ветка — и есть заявка, потому что git не даст создать её дважды:
git branch phase/25-golden-fixtures
Команда упала с «already exists» — значит фазу взял другой агент между твоей проверкой и попыткой. Не спорь, вернись к выбору и возьми следующую.
Ветка создалась — фаза твоя. Сразу пометь её 🔄 в docs/phases/README.md в основном дереве,
отдельным коммитом, состоящим только из этой строки. Это единственная правка, которую ты делаешь
вне своего worktree, и она нужна по двум причинам: соседние агенты видят занятое, а пустая свежая
ветка ещё ничем не отличается от слитой — по ней одной понять, что фаза в работе, нельзя.
Пометить 🔄 надо до первой строчки кода, а не после.
Где работать
В своём worktree, а не в общем дереве. Иначе два агента правят одни файлы и роняют друг другу сборку.
git worktree add <путь-в-scratchpad>/wt-phase-25 phase/25-golden-fixtures
Путь — вне репозитория; каталог scratchpad этой сессии подходит. Worktree внутри репозитория
засоряет git status и попадёт в глаза следующему ревью.
Дальше вся работа там. В конце worktree удаляется (git worktree remove --force), ветка остаётся.
Что нужно знать про worktree:
- Первая сборка полная, минуту-две. Это нормально, а не сломанный кэш.
binиobjу каждого worktree свои, поэтому параллельныеdotnet testне мешают друг другу.- Тесты хоста поднимают сервер со своей папкой сейвов внутри worktree — соседний агент их не видит. Порты Aspire раздаёт сам, конфликтов нет.
node_modulesне копируется. Для клиентской фазы либоnpm ci, либо скопируйnode_modulesиз основного дерева — это быстрее и достаточно.
Порядок работы
1. Прочитай три документа, прежде чем писать код. Саму фазу; дизайн-док среза, ссылка на него
в шапке среза в README.md; инварианты и «Things that will bite you» в AGENTS.md. Дизайн
объясняет почему так, а не иначе — без него легко сделать формально по задачам и мимо смысла.
2. Начни с тестов из списка «Тесты, без которых фаза не закрыта». Они уже сформулированы —
превращай каждый пункт в тест. Проект для теста выбирает не удобство, а «Testing policy» в
AGENTS.md: генерация людей — в HSchool.People.Tests, планировщик — в HSchool.Schedule.Tests,
эндпоинты — в HSchool.AppHost.Tests. Тест генератора в тестах хоста найдёт следующее ревью.
3. Делай задачи по порядку и отмечай - [x] по мере готовности. Галочка ставится, когда есть
код и подтверждение, а не когда «в целом написано».
4. Правь документы тем же коммитом, что и код. Это правило проекта, а не вежливость:
- меняется раскладка сообщения —
ProtocolCodec.cs,protocol.tsиdocs/protocol.mdвместе, плюс бамп версии; - меняется HTTP —
docs/protocol.mdтем же коммитом; - решение разошлось с дизайн-доком — правится документ, и в отчёте это названо явно.
5. Проверь то, что заявляешь. Прогоняешь тесты своей фазы и полный прогон. Не «должно работать», а «прогнал, вот результат».
Что считать сделанным
Фаза закрыта, когда каждый пункт «Тестов, без которых фаза не закрыта» существует отдельным тестом и проходит, и когда выполнен «Критерий готовности». Не раньше.
✅ в README.md ставится последним коммитом ветки, и в этом коммите — только строка статуса.
Отдельным потому, что при слиянии нескольких веток строки статуса конфликтуют, а мелкий коммит
разруливается за секунду.
Не смог закрыть пункт — оставь ⬜ и напиши в отчёте, что именно не вышло и почему. Полработы с честной пометкой лучше, чем ✅ с дырой: следующее ревью её всё равно найдёт, только дороже.
Слить в main и убрать ветку
Фаза не считается сданной, пока лежит на ветке. Доводишь до main сам, тем же запуском.
Сливать можно только в основном дереве: main уже занят им, и во второй worktree его не
взять — git прямо откажет. А раз дерево одно на всех агентов, слияния надо выстроить в очередь.
1. Убери свой worktree. Пока он держит ветку, слияние и удаление невозможны.
git worktree remove --force <путь-в-scratchpad>/wt-phase-25
2. Возьми замок слияния — той же уловкой, что и заявку на фазу:
git branch merge/lock
Упало «already exists» — сливает другой агент. Подожди и повтори; не лезь в дерево, пока замок
чужой. Замок снимается git branch -D merge/lock на всех путях, включая неудачные: забытый
замок остановит остальных.
3. Проверь основное дерево, прежде чем трогать. Оно должно быть на main и чистым:
git -C <корень> status --porcelain
Непусто — там работает человек или соседний агент. Ничего не сливай, сними замок и скажи об этом в отчёте. Чужие незакоммиченные правки дороже твоей ветки.
4. Слей явным коммитом слияния:
git -C <корень> merge --no-ff phase/25-golden-fixtures
--no-ff — чтобы фаза осталась видимой в истории одним куском, а не растворилась в линии.
5. Конфликты разрешай только те, что понимаешь. Почти всегда это строка статуса в
docs/phases/README.md — там обе стороны правы, надо оставить обе. Конфликт в коде, смысл
которого тебе неясен, — git merge --abort, снять замок, оставить ветку и написать в отчёте, с
чем именно она не сходится.
6. Перепрогони тесты на main после слияния. Две зелёные ветки в сумме бывают красными:
каждая правила своё, а вместе получилось не то. Это единственный способ поймать такое.
Красно — откати слияние и оставь ветку жить:
git -C <корень> reset --hard ORIG_HEAD
7. Зелено — удали ветку и сними замок:
git -C <корень> branch -d phase/25-golden-fixtures
git branch -D merge/lock
-d, а не -D: git откажется удалять неслитое, и это последняя защита от потери работы.
Не пушь. Удалёнка есть, но отправка — решение пользователя; делай только если попросили прямо.
Осиротевшее. Ветку когда-то забыли удалить — уберёт следующий запуск, но только при двух
условиях сразу: фаза стоит ✅ в README.md и ветка слита в main.
git branch --merged main --list "phase/*" --format="%(refname:short)"
Оба условия обязательны, и вот почему: только что созданная ветка ещё не имеет коммитов, а значит
равна main и в этот список попадает. Удалять по одной «слитости» — снести чужую только что
взятую фазу. Статус ✅ и есть то, что отличает доделанное от начатого.
Каталоги умерших агентов подчищаются git worktree prune — записи о несуществующих worktree и так
мусор.
Границы
- Только своя фаза. Заметил проблему в соседней — строкой в отчёт, не в диф. Реализация, которая походя переписала полпроекта, невозможна к просмотру глазами.
- Сливаешь только свою ветку и только по порядку из «Слить в main и убрать ветку». Чужие ветки не сливаешь, даже если они выглядят готовыми.
- Не переставляй чужие статусы в
README.mdи не трогай чужие 🔄. - Не меняй молча версию протокола, форму сейва и публичное поведение API — даже когда это очевидно правильно. Если фаза этого требует, так и написано в её задачах.
- Общие файлы трогай минимально.
AGENTS.md,docs/protocol.md,README.mdправят все ветки сразу — чем точечнее правка, тем дешевле слияние.
Окружение: что укусит
MSB3021/MSB3027, «блокирует этот файл» — у пользователя запущено приложение, оно держит DLL. Процесс не убивать. В своём worktree сборка обычно проходит, там своиbin; если всё же упёрлось — проверь то, что можно без сборки, и скажи об этом в отчёте прямо.- Концы строк. В репозитории есть и CRLF, и LF.
sed -iпереворачивает файл целиком: правка в три строки превращается в диф на тысячу. Правь скриптом, который читает и пишет сnewlineпустой строкой, а потом смотриgit diff --stat— внезапные сотни строк там, где ты правил три, это оно. - Heredoc и экранирование. Файлы с табуляциями, юникод-экранами и кавычками приезжают через
heredoc покалеченными. Пиши такие файлы инструментом записи файла, а не
catв шелле. - Тесты хоста делят один сервер и одну папку сейвов, поэтому каждый начинает с очистки списка. Не пиши тест, который полагается на пустую базу или на конкретный id школы.
- Состав школы зависит от её id, пока не сделана фаза 26. Тест, требующий у первого ученика двух родителей или конкретную фамилию, зелёный у тебя и красный в CI. Проверяй свойство, а не совпадение.
- Полный
dotnet testизредка падает целиком с нативной ошибкой вSimulation.Tests. Один раз — перепрогони; повторяется — это находка для отчёта. - Dev-сервер не запускать. Нужно посмотреть на UI — пользуйся уже запущенным приложением пользователя.
Измеряй, а не рассуждай
Когда вопрос звучит как «а хорошо ли ложатся часы» или «а сколько выходит неполных семей» — не рассуждай о коде. Напиши временный тест, который печатает реальные числа, посмотри и удали его. Час размышлений о поведении алгоритма стоит дороже и ошибается чаще, чем один прогон с цифрами. Числа из такого замера — лучшее, что можно положить в отчёт.
Отчёт
Коротко, в конце:
- какая фаза взята и на какой ветке лежит;
- что сделано — по пунктам задач, без пересказа кода;
- какие тесты добавлены, списком по одной строке;
- чем подтверждено: что прогнал и с каким результатом, числа замеров, если были;
- что осталось незакрытым и почему;
- что замечено за границами фазы;
- слита ли ветка в
mainи удалена ли — а если нет, то что помешало.
Итог запуска — фаза в main, ветка удалена, worktree убран, замок снят. Всё, что осталось от
работы, — это коммиты и отчёт.