20 KiB
name, description
| name | description |
|---|---|
| phase-work | Берёт ровно одну фазу из среза в docs/phases/ и доводит её до конца — в своём git worktree и на своей ветке. Используй, когда просят «сделай фазу 25», «возьми фазу», «реализуй фазу», «work on the phase». Не для волны из нескольких агентов — /phase-orch, не для ревью — /phase-review, не для бага — /bug-work, не для задачи вне среза — /side-work. |
Работа над фазой
Один запуск — одна фаза. Скилл берёт её из docs/phases/, делает целиком, проверяет по её же
критериям приёмки и отдаёт готовую ветку. Ничего сверх взятой фазы он не трогает: именно это
позволяет запустить несколько агентов на один срез и не получить кашу.
Критерии приёмки уже написаны. У каждой фазы есть «Задачи», «Тесты, без которых фаза не закрыта» и
«Критерий готовности», у проекта — инварианты в AGENTS.md, у среза — дизайн-док. Выдумывать
нечего, надо сделать и доказать.
Какую фазу брать
Номер передали в аргументе — берёшь его, вопросов нет. Так пользователь и /phase-orch
раздают фазы: /phase-work 25. Ветка phase/<N>-* уже есть — это продолжение (зависшего
агента сменили), не гонка: работай в существующем worktree, не создавай вторую ветку и не
сбрасывай 🔄 на ⬜.
Номера нет — выбираешь сам. Волну «запусти троих» так не раздавай — это /phase-orch.
- Читаешь
docs/phases/README.md: находишь первый срез, где есть ⬜. - Внутри среза берёшь первую ⬜, у которой закрыты зависимости — они перечислены в шапке фазы.
- Проверяешь, что её никто не взял:
git branch --list "phase/<номер>-*"пусто.
Статус в README.md — первый источник правды, ветка — только арбитр гонки. 🔄 значит занято,
✅ значит сделано, ⬜ значит свободно. Ветка может существовать и при ✅ — её просто не убрали после
слияния; это не заявка, а мусор, см. «Кто убирает ветку».
Фаза 🔄 — чужая работа в процессе. Не трогай её, даже если кажется, что там застряли — кроме случая выше: номер передали и ветка уже есть, тебя посадили продолжить.
Как застолбить
Продолжение (номер дали и ветка phase/<N>-* уже есть) — этот блок
пропусти: ветку не создавай, 🔄 не сбрасывай, садись в существующий
worktree. «already exists» здесь не гонка.
Ветка — и есть заявка, потому что 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. Проверь то, что заявляешь. Гоняй только проект(ы) из Testing policy в AGENTS.md,
которые эта фаза трогает; пока пишешь — --filter на новый класс. Клиентский Vitest — только
если трогал src/HSchool.Client. HSchool.AppHost.Tests — только если трогал HTTP, сокет
или хост. Весь solution и «на всякий случай оба стека» не гонять. Не «должно работать», а
«прогнал вот этот проект, вот результат».
Что считать сделанным
Фаза закрыта, когда каждый пункт «Тестов, без которых фаза не закрыта» существует отдельным тестом и проходит, и когда выполнен «Критерий готовности». Не раньше.
✅ в README.md ставится последним коммитом ветки, и в этом коммите — только строка статуса.
Отдельным потому, что при слиянии нескольких веток строки статуса конфликтуют, а мелкий коммит
разруливается за секунду.
Не смог закрыть пункт — оставь ⬜ и напиши в отчёте, что именно не вышло и почему. Полработы с честной пометкой лучше, чем ✅ с дырой: следующее ревью её всё равно найдёт, только дороже.
Слить в main и убрать ветку
Фаза не считается сданной, пока лежит на ветке. Доводишь до main сам, тем же запуском.
Сливать можно только в основном дереве: main уже занят им, и во второй worktree его не
взять — git прямо откажет. А раз дерево одно на всех агентов, слияния надо выстроить в очередь.
1. Убери свой worktree. Пока он держит ветку, слияние и удаление невозможны.
git worktree remove --force <путь-в-scratchpad>/wt-phase-25
2. Возьми замок слияния — той же уловкой, что и заявку на фазу:
git branch merge/lock
Упало «already exists» — сливает другой агент (фаза, баг, ревью или side: замок общий).
Подожди и повтори; не лезь в дерево и не снимай чужой замок. Замок снимается
git branch -D merge/lock на всех путях, включая неудачные: забытый замок остановит остальных.
3. Проверь основное дерево, прежде чем трогать. Оно должно уже быть на main и чистым:
git -C <корень> branch --show-current
git -C <корень> status --porcelain
Не main (часто bug/… в IDE пользователя) — не checkout, ничего не сливай, сними
свой замок, отчёт. Непусто — там человек или сосед. То же: не сливать, снять замок, отчёт.
Чужие незакоммиченные правки и чужая ветка в основном дереве дороже твоей. Stash→merge —
не ты: это merge-агент /phase-orch, и только если пользователь сказал «сливай».
4. Слей явным коммитом слияния:
git -C <корень> merge --no-ff phase/25-golden-fixtures
--no-ff — чтобы фаза осталась видимой в истории одним куском, а не растворилась в линии.
5. Конфликты разрешай только те, что понимаешь. Почти всегда это строка статуса в
docs/phases/README.md — там обе стороны правы, надо оставить обе. Конфликт в коде, смысл
которого тебе неясен, — git merge --abort, снять замок, оставить ветку и написать в отчёте, с
чем именно она не сходится.
6. После слияния. Конфликт только в строке статуса или в доке — тесты не гонять, ничего не сошлось в коде. Был конфликт в коде — тот же узкий набор проектов, что в шаге 5, не весь solution. Две зелёные ветки в сумме бывают красными; ловить это должен затронутый проект, а не ритуал на восемь сборок.
Красно — откати слияние и оставь ветку жить:
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на solution «на всякий случай»: это восемь проектов и подъём хоста. Если всё же гонял и упало целиком с нативной ошибкой вSimulation.Tests— один повтор того же проекта; повторяется — в отчёт. - Dev-сервер не запускать. Нужно посмотреть на UI — пользуйся уже запущенным приложением пользователя.
Измеряй, а не рассуждай
Когда вопрос звучит как «а хорошо ли ложатся часы» или «а сколько выходит неполных семей» — не рассуждай о коде. Напиши временный тест, который печатает реальные числа, посмотри и удали его. Час размышлений о поведении алгоритма стоит дороже и ошибается чаще, чем один прогон с цифрами. Числа из такого замера — лучшее, что можно положить в отчёт.
Отчёт
Коротко, в конце:
- какая фаза взята и на какой ветке лежит;
- что сделано — по пунктам задач, без пересказа кода;
- какие тесты добавлены, списком по одной строке;
- чем подтверждено: какой проект прогнал и с каким результатом, числа замеров, если были; не «весь solution зелёный»;
- что осталось незакрытым и почему;
- что замечено за границами фазы;
- слита ли ветка в
mainи удалена ли — а если нет, то что помешало.
Итог запуска — фаза в main, ветка удалена, worktree убран, замок снят. Всё, что осталось от
работы, — это коммиты и отчёт.