PRD: Git-интеграция и автоматизация PR/Release в prd-wizard
Problem Statement
Сейчас whilly --prd-wizard создаёт PRD-файл и плавно переходит к выполнению плана задач, но полностью игнорирует git-состояние проекта. Пользователю приходится вручную:
- инициализировать git, если репо ещё нет;
- решать, что делать с незакоммиченными изменениями перед началом работы;
- создавать ветку под фичу;
- коммитить результат каждой выполненной задачи;
- пушить и открывать MR/PR в GitHub или GitLab;
- бампить версию в
whilly/__init__.pyиpyproject.toml; - создавать release, тег и публиковать пакет на PyPI.
Это рутина, которая занимает время и часто забывается, особенно после длительной автономной работы Whilly. Также отсутствует механизм самовосстановления Whilly при крэше его собственного кода — пользователь упирается в трейсбэк и должен лечить руками.
Objectives
- Превратить
prd-wizardв полноценный «git-aware» режим, который сам проводит пользователя через все этапы git-workflow. - Сделать настройку one-shot: единожды ответил на серию вопросов — все следующие запуски в этом проекте работают молча по сохранённой политике.
- Дать полный override-стек: env vars и CLI-флаги переопределяют конфиг без перезаписи; команда
--git-configредактирует политику интерактивно. - Обеспечить безопасную и идемпотентную автоматизацию вплоть до публикации в PyPI с умной классификацией ошибок.
- Реализовать «маску на себя»: при internal crash Whilly сам открывает issue, лечит себя через отдельный Claude-агент, после успешного фикса возвращается к выполнению исходного плана.
Target Users
- Соло-разработчики, использующие Whilly для автоматизации мелких/средних фич — хотят «один раз настроить и забыть».
- MTS DevOps команды, работающие через
gitlab.services.mts.ru(glab) — нужен MR-флоу + сохранение токена. - Open-source мейнтейнеры Whilly-подобных пакетов на GitHub (
gh) — нужен полный pipeline вплоть до GitHub Release + PyPI upload.
Requirements
Functional Requirements
FR-1. Триггер в начале whilly --prd-wizard
- Перед интерактивным сбором PRD Whilly запускает git-preflight.
- Все вопросы preflight задаются только при первом запуске в данной директории. Ответы сохраняются в
.whilly/git.yaml. При последующих запусках preflight молча применяет сохранённую политику, если нет override через env/flag.
FR-2. Случай: git-репозиторий не инициализирован
- Whilly спрашивает:
Initialize git repo? (y/n). - На
y: выполнитьgit init, создать.gitignore(если отсутствует) с дефолтным шаблоном для Python (.venv/,__pycache__/,*.pyc,whilly_logs/,.whilly_state.json,.whilly_workspaces/,.whilly_worktrees/), сделать initial commitchore: initial commit, продолжить wizard. - На
n: продолжить wizard без git-интеграции (записатьgit.enabled: falseв.whilly/git.yaml).
FR-3. Случай: git инициализирован + есть незакоммиченные изменения
- Whilly показывает
git statusи спрашивает:Commit all changes before wizard? (y/n/skip). y:git add -A && git commit -m "chore: pre-wizard snapshot", продолжить.n: прервать запуск с exit code != 0.skip: продолжить wizard без коммита (изменения остаются в working tree).
FR-4. Выбор провайдера (только при первом запуске)
- Whilly спрашивает:
Which git provider to use for MR/PR? (gh / glab / none). gh: использовать GitHub CLI (gh pr create,gh pr merge,gh release create).glab: использовать GitLab CLI или GitLab API (gitlab.services.mts.ruпо умолчанию для MTS env, конфигурируемо).none: не открывать PR — Whilly останавливается наgit pushветки.- Сохранить в
.whilly/git.yamlключgit.provider.
FR-5. Создание ветки под фичу
- Whilly автоматически создаёт ветку
feature/prd-{slug}отmain(fallbackmaster), где{slug}— slug PRD-файла, тот же что вPRD-{slug}.md. - Если ветка уже существует — переключиться в неё с предупреждением.
- Wizard и весь дальнейший план задач работают в этой ветке.
FR-6. Per-task auto-commit во время плана
- После каждого
task doneWhilly выполняет:git add {task.key_files}(только файлы изkey_files, чтобы не подмешать чужой шум) илиgit add -A, еслиkey_filesпуст.git commit -m "feat({task.id}): {task.description первая строка}".
- Если в worktree-режиме (
WHILLY_WORKTREE=1или--worktree) — коммит идёт в worktree-ветку, потомcherry-pickв основную фича-ветку (как уже делаетworktree_runner.WorktreeManager). - Если коммит-команда падает (например, нет изменений, hook fail) — записать в лог, не блокировать прогресс плана.
FR-7. Version bump (по явному флагу)
- По умолчанию никакого автобампа.
- Флаг
--bump=patch|minor|major(или envWHILLY_BUMP=patch) — после последнегоtask done:- изменить версию в
whilly/__init__.pyиpyproject.tomlсинхронно; - сделать commit
chore: bump version to X.Y.Z; - применяется только если
git.enabled=true.
- изменить версию в
FR-8. PR/MR creation
- При первом запуске спросить (если
provider != none):Auto-create MR/PR after plan? (y/n). Сохранить вgit.auto_pr. - Если
auto_pr=trueи план завершён успешно (все задачиdoneилиskipped):git push -u origin {branch};- сформировать тело PR из PRD-файла (
PRD-{slug}.md) + краткой сводки выполненных задач; gh pr create --base main --head {branch} --title "..." --body-file ...илиglab mr create ....
- Сохранить URL созданного PR в state и вывести в финальный отчёт.
FR-9. Auto-release pipeline
- При первом запуске спросить (если
auto_pr=true):Auto-release after PR merge? (y/n). Сохранить вgit.auto_release. - Если
auto_release=trueи--bumpуказан, после создания PR Whilly:gh pr merge --auto --squash(илиglab mr merge --when-pipeline-succeeds).- Polling CI до зелёного статуса с timeout (по умолчанию 1800 сек, конфигурируемо).
- После merge:
git checkout main && git pull && git tag vX.Y.Z && git push --tags. gh release create vX.Y.Z --notes-from-tag(илиglab release create).python -m build && twine upload dist/*(PyPI publish).
- Все шаги — полностью автоматические без подтверждений, если
auto_release=true.
FR-10. Обработка ошибок (retry + idempotent + classification)
- Идемпотентность: каждый шаг (push, PR create, tag, release, PyPI upload) проверяет «уже сделано?» — если да, skip без ошибки. Это позволяет безопасно перезапускать Whilly с
--resume. - Retry с backoff: 3 попытки с экспоненциальной задержкой (5/15/45s) на network/transient errors.
- Классификация ошибок (определяется по сообщению/exit code):
auth_error(gh:HTTP 401, glab:unauthorized, twine:403 Forbidden) → fail-fast, лог:❌ Credentials missing/invalid for {tool}. Run '{tool} auth login' and retry. Whilly cannot continue.Записать вwhilly_logs/whilly_events.jsonl, exit code 4 (новый —auth required).timeout_error→ удвоить timeout, retry до 3 раз, потом fail-fast.network_error(DNS, connection refused) → retry с backoff.merge_conflict→ fail-fast с подсказкойResolve conflicts manually and re-run with --resume.ci_failure→ fail-fast, оставить PR открытым, exit с подсказкой.unknown_error→ лог + fail-fast.
FR-11. Self-heal Whilly («маска на себя»)
- При internal crash в самом Whilly (Python traceback не из task-агента, а из
whilly/*.py):- Поймать exception на верхнем уровне
cli.run_plan. - Открыть issue в GitHub-репо Whilly (
gh issue create --repo {whilly_repo} --title "..." --body "{traceback + context}"). - Запустить отдельный Claude-агент на репозитории Whilly (определить путь через
Path(whilly.__file__).parent.parent) с промптом:Fix this Whilly bug. Context: {traceback}. Issue: {issue_url}. After fix, run 'pytest -q' and 'ruff check'. Open MR with title 'fix: ...' and exit when MR is created.. - Дождаться появления fix-MR (polling).
- Дождаться green CI на fix-MR.
- Auto-merge fix-MR.
- Pull новую версию Whilly (если работает в pip-installed mode —
pip install -Uиз git, иначеgit pullв репо). - Возобновить выполнение исходного плана через
--resume(state уже сохранён в.whilly_state.json).
- Поймать exception на верхнем уровне
- Принцип «сначала маску на себя»: пока Whilly не починен, исходный план не продолжается.
- Защита от бесконечной рекурсии: счётчик self-heal попыток в state, max 2 за один запуск, потом fail-fast с сообщением
Self-heal failed twice. Manual intervention required..
FR-12. Override-стек (env > flag > file)
- Файл
.whilly/git.yaml— источник дефолтов:git: enabled: true provider: gh # gh | glab | none base_branch: main branch_template: "feature/prd-{slug}" auto_pr: true auto_release: false auto_commit_per_task: true self_heal: true - Env vars (читаются
WhillyConfig.from_env()):WHILLY_GIT_ENABLED=0|1WHILLY_GIT_PROVIDER=gh|glab|noneWHILLY_AUTO_PR=0|1WHILLY_AUTO_RELEASE=0|1WHILLY_AUTO_COMMIT=0|1WHILLY_BUMP=patch|minor|majorWHILLY_SELF_HEAL=0|1WHILLY_BASE_BRANCH=main
- CLI флаги:
--no-pr/--pr--no-release/--release--no-auto-commit/--auto-commit--no-self-heal/--self-heal--bump=patch|minor|major--git-provider=gh|glab|none--base-branch=main
- Команда
whilly --git-config— интерактивно перепрожить вопросы preflight и перезаписать.whilly/git.yaml. - Команда
whilly --git-reset— удалить.whilly/git.yaml, в следующий запуск preflight снова спросит. - Приоритет: CLI flag > env var > file > built-in default.
Non-Functional Requirements
- Совместимость: Python 3.10+, как и весь Whilly. Зависит от наличия
git, опциональноghилиglabнаPATH. - Безопасность: PyPI publish использует
twineс токеном из env (TWINE_PASSWORD) или~/.pypirc— никогда не запрашивать токен интерактивно и не сохранять в.whilly/git.yaml. - Производительность: per-task commit не должен блокировать loop > 2 сек на средний commit (
git add+git commit). - Совместимость с worktree: интегрироваться с существующим
worktree_runner.WorktreeManager— коммиты идут в worktree-ветку, cherry-pick в основную сохраняется. - Совместимость с
--headless: в headless-режиме все интерактивные вопросы preflight fail-fast с сообщениемgit config not set, run 'whilly --git-config' interactively first or pass WHILLY_GIT_* env vars. - Логирование: все git-операции пишут структурные события в
whilly_logs/whilly_events.jsonl(event: git.commit | git.push | git.pr_create | git.release | git.error). - Atomic config writes:
.whilly/git.yamlпишется черезtempfile + os.replace(какTaskManager), чтобы не было битого YAML при крэше.
Technical Constraints
ghCLI должен быть установлен и аутентифицирован для GitHub-провайдера. При отсутствии — fall back на режим «push only, no PR» с предупреждением.glabCLI илиpython-gitlabдля GitLab. MTS env: использовать паттерн из~/.claude/CLAUDE.md(glab config get token -h gitlab.services.mts.ru).- Stdin/TTY detection: preflight только в TTY-режиме. В headless — error + exit, как описано выше.
- Не модифицировать
whilly_logs/ИЛИ.whilly_state.jsonв commit’ах — добавить в.gitignoreесли их там нет. pyproject.tomlversion bump должен использоватьtomllib(Python 3.11+) илиtomli(3.10), чтобы парсить корректно — не regex.- Self-heal Claude-агент должен запускаться через тот же
agent_runnerчто и обычные задачи, чтобы не дублировать subprocess-логику.
Success Criteria
- Метрика 1 (UX): время от
whilly --prd-wizardдо открытого MR/PR при первом запуске на чистом репо ≤ 2 минуты + время выполнения плана. При втором запуске — preflight ≤ 5 секунд (без вопросов). - Метрика 2 (надёжность): на тестовом репо последовательность
prd-wizard → план из 5 задач → auto-pr → auto-release → PyPIотрабатывает успешно в ≥ 95% запусков без ручного вмешательства (тестируется в CI сghпротив test-org). - Метрика 3 (self-heal): искусственно введённый bug в
whilly/cli.pyприводит к: открытому issue + созданному fix-MR + возобновлению исходного плана. Покрыто отдельным e2e-тестом. - Метрика 4 (override):
WHILLY_AUTO_PR=0 whilly --prd-wizardотключает PR creation без модификации.whilly/git.yaml. Проверяется unit-тестом. - Метрика 5 (idempotency): повторный запуск
whilly --resumeпосле успешного auto-release не создаёт дубликат tag/release/PyPI upload. Проверяется e2e-тестом.
Out of Scope
- Поддержка других git-хостингов (Bitbucket, Gitea, Codeberg) — только GitHub и GitLab в первой итерации.
- Auto-merge без
--bump: если--bumpне указан, auto-release вообще не запускается, даже еслиauto_release=trueв конфиге. - Conventional commits parsing для авто-определения bump уровня — отвергнуто на этапе сбора требований.
- PyPI без token / interactive auth — токен только из env /
.pypirc. - Rollback при failure — отвергнуто, выбран
idempotent + retry + classification. - Изменение существующих non-git компонентов Whilly (TUI, batch planning, decomposer) — всё остаётся как есть, новая фича — слой поверх.
- Multi-PR на один плана — план = одна ветка = один PR. Если нужно разбить — пользователь делит план вручную.
Risks and Assumptions
Risks
| Risk | Severity | Mitigation |
|---|---|---|
| Self-heal зацикливается на нечинимом баге | High | Счётчик max 2 self-heal за запуск, после — fail-fast |
| Self-heal Claude-агент создаёт broken fix который мержится автоматически | High | Auto-merge в self-heal только после green CI; если CI красный → fail-fast |
gh pr merge --auto плохо работает с repository rules / required reviewers | Medium | Документировать ограничение; fallback на ручной merge с подсказкой |
twine upload опубликует пакет с битой версией | High | Проверка: версия из pyproject.toml не существует в PyPI до publish |
| Per-task commit ломает CI (промежуточный код не собирается) | Medium | По умолчанию auto_commit_per_task=true для feature branch; main защищён branch protection |
Конфликт с existing branch feature/prd-{slug} (старый незаконченный план) | Medium | При коллизии — спросить «reuse / new with suffix -N», в headless — fail |
| Headless-режим теряет gracefulness preflight | Low | В headless требовать WHILLY_GIT_* env заранее, иначе exit 5 (новый код) |
Assumptions
- Пользователь имеет
git,gh/glabустановленные и аутентифицированные до запуска Whilly. Whilly не делаетgh auth loginза пользователя. - Default branch проекта называется
mainилиmaster— иные имена черезWHILLY_BASE_BRANCH=develop. - PyPI token хранится в env или
~/.pypircпользователем заранее. - Whilly-репозиторий для self-heal доступен на GitHub под известным URL (вычисляется из
setup.py/pyproject.tomlили хардкодится в константу). .whilly/директория добавлена в.gitignoreглобально — иначе локальный конфиг попадёт в коммит.- Claude CLI (
claude) присутствует на PATH (илиCLAUDE_BINуказывает на него) для self-heal агента.
Timeline
Декомпозиция на milestones (детальная нарезка задач — следующим шагом через prd-to-tasks):
- M1 — Git preflight + config (~1 день)
.whilly/git.yamlschema + atomic read/write.- Preflight функция: detect git, ask init, ask dirty, save config.
- Override-стек (env + flag).
whilly --git-config/--git-resetкоманды.
- M2 — Branch + per-task commit (~1 день)
- Auto-create
feature/prd-{slug}branch. - Hook в
task done→ commit. - Интеграция с
worktree_runner.
- Auto-create
- M3 — PR creation (gh + glab) (~1.5 дня)
- Adapter pattern для двух провайдеров.
- PR body builder из PRD + summary.
- Idempotent push + create.
- M4 — Version bump + auto-release (~2 дня)
--bumpflag, синхронный bump в обоих файлах черезtomllib.- Auto-merge polling.
- Tag + release + PyPI upload pipeline.
- Idempotency checks на каждом шаге.
- M5 — Error classification + retry (~1 день)
- Error classifier (auth/timeout/network/merge_conflict/ci/unknown).
- Retry с backoff.
- Новые exit codes (4, 5).
- M6 — Self-heal (~2 дня)
- Top-level exception catcher.
- Issue creation.
- Self-heal Claude-агент launcher.
- Polling fix-MR + CI + auto-merge.
- Resume исходного плана.
- Anti-recursion counter.
- M7 — Tests + docs (~1 день)
- Unit-тесты на classifier, config, override-стек.
- E2E-тесты на test-репо (mock gh/glab).
- Обновить
README.md,docs/Whilly-Usage.md,docs/Whilly-Interfaces-and-Tasks.md.
Total estimate: ~9.5 рабочих дней для одного исполнителя. Возможна параллелизация M3 (PR) и M4 (release) после M2.