| name | migrate |
| description | Upgrade PROJECT_STATE.json schema to current version |
/polisade:migrate — Schema Migration
Обновляет PROJECT_STATE.json и knowledge.json до текущей версии схемы. Добавляет недостающие поля, создаёт artifactIndex, устанавливает schemaVersion. Также добавляет testing.strategy в knowledge.json если отсутствует.
VCS bootstrap: если settings.vcsProvider отсутствует — добавляет "github". Если PM вручную переключил провайдер на bitbucket-server — создаёт .env.example (reference) и .env (stub для заполнения токенов) из plugin templates, добавляет некомментированную .env в .gitignore. Заполненный .env не перезаписывается (идемпотентность). ⚠️ Под GigaCode Filesystem Guard сам скрипт миграции может не запуститься (install-dir read-protected, #127) — тогда .env/.env.example автоматически не появятся; PM создаёт .env вручную из .env.example (cp .env.example .env) и заполняет токены (#131).
Использование
/polisade:migrate # Dry-run — показать что изменится
/polisade:migrate --apply # Показать diff и применить после подтверждения
Алгоритм
- Определить корень проекта.
- Запустить миграцию в режиме dry-run:
python3 {plugin_root}/scripts/polisade_migrate.py {project_root}
- Распарсить JSON-ответ, показать список миграций пользователю.
- Если пользователь подтверждает — применить:
python3 {plugin_root}/scripts/polisade_migrate.py {project_root} --apply --yes
Формат вывода
polisade_migrate.py всегда печатает один JSON-документ на stdout
(контракт OPS-108 — json.loads(stdout) обязан проходить). PM-friendly
сообщения и подтверждение интерактивного prompt'а уходят на stderr.
Полная таблица контрактов — docs/config-reference.md § Script JSON
output contracts.
Если схема актуальна
{
"status": "up_to_date",
"schemaVersion": 7,
"polisadeVersion": "3.0.0",
"touched_paths": [],
"stage_paths": []
}
Если нужна миграция (dry-run)
{
"status": "migration_needed",
"current_schema": 3,
"target_schema": 6,
"migrations": [
"Update schemaVersion: 3 → 7",
"Add settings.debt.autoCreateTask: true (preserve legacy auto-TASK behavior)"
],
"touched_paths": [".state/PROJECT_STATE.json"],
"stage_paths": [".state/PROJECT_STATE.json"],
"dry_run": true
}
После --apply --yes
{
"status": "applied",
"schemaVersion": 7,
"applied_count": 2,
"migrations": ["Update schemaVersion: 3 → 7", "..."],
"touched_paths": [".state/PROJECT_STATE.json"],
"stage_paths": [".state/PROJECT_STATE.json"]
}
touched_paths — всё, что миграция тронула (для информации и для
diff-сверки с git status --porcelain).
stage_paths — subset для git add: исключает пути, которые после
миграции попали под .gitignore (например .env при bitbucket bootstrap
оказывается в touched_paths, но НЕ в stage_paths, потому что та же
миграция добавила .env в .gitignore — попытка git add .env дала
бы rc=1).
Важно
- По умолчанию dry-run — не записывает ничего без
--apply
- Никогда не трогает
artifacts — только создаёт новый artifactIndex
- Безопасно запускать повторно — идемпотентная миграция
- После миграции
/polisade:doctor должен показывать pass для state_schema
После применения — закоммить и открыть PR
После /polisade:migrate --apply рабочее дерево обычно содержит изменения
(.state/PROJECT_STATE.json, иногда .gitignore, .env.example,
`.claude/settings.json`, `tasks/TASK-*.md` под OPS-026, и т. п.). PM в
корп-сессии (issue #108) после этого попросит «закоммить и сделай pr» —
агент должен пройти ровно по этому рецепту, без импровизации.
Контракт: ни одного $(...), бэктиков (command substitution в Bash) или
<(...) / >(...) в шелл-командах ниже. Корп-шелл (GigaCode CLI / codex
sandbox) режет их с сообщением «Command substitution using $(), ``, <(),
or >() is not allowed for security reasons», и весь рецепт обрывается.
WORK_DIR="${POLISADE_WORK_DIR:-.}"
git switch -c <branch>
git add <path1> <path2> ...
git commit -m "<skill>: <scoped summary>"
python3 {plugin_root}/scripts/polisade_vcs.py git-push \
--branch <branch> --set-upstream \
--project-root "$WORK_DIR"
mkdir -p .polisade/tmp
git log -1 --pretty=%B > .polisade/tmp/pr-body.md
python3 {plugin_root}/scripts/polisade_vcs.py pr-create \
--title "<skill>: <scoped summary>" \
--head <branch> --base main \
--body-file .polisade/tmp/pr-body.md \
--project-root "$WORK_DIR"
Why этот рецепт жёсткий:
polisade_vcs.py git-push верифицирует push (exit-code + pattern-scan +
SHA), bare git push — нет.
--body-file обходит ограничение корп-шелла на command substitution.
- Самодельный Python/curl в Bitbucket/GitHub REST API утекает токены из
.env мимо polisade_vcs.py и теряет provider-agnostic мост.
git status --porcelain — fallback, не primary: при параллельных
user-edits даёт лишние файлы.