一键导入
implement
Implement TASK
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Implement TASK
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Capture a simple task that does not need planning (CHORE + TASK in readyToWork by default, or registration-only with --no-task). Use when PM mentions "simple task", "chore", "small task", "housekeeping", "quick task", "мелкая задача", "чора", or any request to log routine work without PRD/SPEC overhead. Trigger liberally — under-triggering pushes tiny work into freeform chat; over-triggering is recoverable (PM can delete).
Autonomous work — find and execute ready tasks
Record a technical-debt item (DEBT-NNN) — registration only by default, or register + auto-generate a fix TASK with --task. Use when PM mentions "add tech debt", "record tech debt", "technical debt", "tech debt item", "refactor tracking", "технический долг", "запиши техдолг", or any request to capture deferred refactoring / cleanup work. Trigger liberally — under-triggering loses debt visibility; over-triggering is recoverable (PM can delete or defer).
Register a defect (BUG-NNN) and auto-generate the fix TASK so the bug enters the normal implement/review flow. Use when PM mentions "file a bug", "report defect", "bug report", "register defect", "report a bug", "заведи баг", "баг-репорт", or any request to capture a defect for tracking. Trigger liberally — under-triggering leaves bugs in chat where they get lost; over-triggering is recoverable (PM can delete the BUG artefact).
EXPERIMENTAL (Claude Code only). Apply a SPEC increment to a single LIVING architecture corpus under docs/architecture/ instead of a per-SPEC silo package — treats docs as event-sourcing (SPEC = commit, corpus = working tree), so C4 Context/Container, glossary and the data model stay system-wide and never drift across SPECs. Use when PM mentions "living corpus", "single architecture", "corpus mode", "merge design into the corpus", "one architecture for all SPECs", or wants to migrate per-SPEC DESIGN silos into one corpus. Opt-in behind settings.experimental.designCorpus (default off). v1 is a best-effort prompt corpus on a strong model; deterministic stdlib gates are the weak-model-orchestrator spec (#133/#184/#135).
Create a doc-as-code design package from a PRD or SPEC. Conditionally generates C4 diagrams (Context/Container/Component), sequence diagrams, ER diagram + Data Dictionary, OpenAPI 3.0, AsyncAPI 3.0, ADRs, domain glossary, state diagrams, and deployment view as Mermaid-rendered Markdown files. Use when PM mentions "design", "architecture diagrams", "doc-as-code artifacts", "C4", "ERD", "OpenAPI spec", "AsyncAPI", "event-driven", "Kafka", "message broker", "sequence diagram", "state machine", "domain glossary", "ADR", or before handing a SPEC to another team. Trigger liberally — undertriggering loses architectural value, overtriggering is recoverable (PM can delete).
| name | implement |
| description | Implement TASK |
| argument-hint | [TASK-XXX] |
| cli_requires | task_tool, codex_cli |
| fallback | self |
Автономная реализация задачи через изолированный субагент с чистым контекстом.
ВАЖНО: /polisade:implement принимает ТОЛЬКО TASK-XXX. Для BUG/DEBT/CHORE автоматически создаётся TASK.
⛔ ARCHRUN-NNN (corpus-run, #187) НЕ реализуется — даже если он попал в
readyToWork после /polisade:unblock. Это не work-item: ARCHRUN.ready
означает «resume required». Если PM передал ARCHRUN-XXX или он оказался
ready-кандидатом — пропусти его и сообщи: «ARCHRUN-XXX — corpus-run;
продолжи через /polisade:design-corpus --resume=<runId>, не через implement».
┌─────────────────────────────────────────────────────────────┐
│ ⛔ /polisade:implement НИКОГДА не мержит PR автоматически! │
│ │
│ После написания кода статус: in_progress │
│ После создания PR статус: review │
│ После успешного review → статус остаётся: review │
│ Merge и статус done → ответственность PM │
└─────────────────────────────────────────────────────────────┘
Полный цикл /polisade:implement:
КОД → ТЕСТЫ → PR → REVIEW → STOP
↑ ↑
│ └── PR готов, ждём PM для merge
└── статус in_progress
/polisade:implement РАБОТАЕТ ТОЛЬКО в рамках feature-ветки. Следующие
действия ЗАПРЕЩЕНЫ в любой момент жизненного цикла команды —
до и после успешного self-review, при первом и при повторном вызове,
в основном агенте и в субагенте:
git checkout main / git checkout master / git switch maingit push origin main / git push origin master / git push --force в maingit merge <feature> / git rebase <feature> onto maingit branch -D <feature> / git branch --delete <feature>git push origin --delete <feature> / git push origin :<feature>polisade_vcs.py pr-merge, gh, curl к Bitbucket)git commit / git add / git push с current_branch ≠ compute_expected_branch(TASK)
(main/master/develop — частный случай: если видишь On branch main и собираешься
коммитить, это ЯВНЫЙ БАГ OPS-001 — не продолжай, останавливайся, верни blocked)git add -f <path> / git add --force <path> на gitignored
путях (.gigacode/, .qwen/, .codex/, .worktrees/ и любые
другие). Разрешено только при явной просьбе PM «добавить
принудительно». Фраза «закоммить всё кроме X» — это ИСКЛЮЧЕНИЕ
пути X, а НЕ команда его форсить. Исключение по .claude/ — только
файл .claude/settings.json (коммитится), директория .claude/
целиком — НЕТ.Если алгоритм видит «main ahead by N commits» ИЛИ git status
на main перед коммитом — это СИГНАЛ БАГА (OPS-001), а не задача на
merge/commit. ОСТАНОВИСЬ и сообщи PM.
Agent must NEVER push to main/master directly. Merge выполняет только
PM (вручную) либо /polisade:continue (в рамках автономного цикла).
Feature-ветка СОХРАНЯЕТСЯ после завершения /polisade:implement — её удаление
произойдёт автоматически при merge PR с флагом --delete-branch.
/polisade:implement TASK-001 # Реализовать задачу
/polisade:implement # Выбрать из доступных ready TASK
/polisade:implement BUG-001 # DEPRECATED: используй созданную TASK
/polisade:implement DEBT-001 # DEPRECATED: используй созданную TASK
При попытке /polisade:implement BUG-XXX или /polisade:implement DEBT-XXX:
task артефакта)settings.debt.autoCreateTask: false (opt-in контракт /polisade:debt
касается только регистрации, не команды implement)./polisade:implement TASK-XXX┌─────────────────────────────────────────────────────┐
│ PM: /polisade:implement TASK-001 │
└─────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ ОСНОВНОЙ АГЕНТ │
│ 1. Валидация TASK │
│ 2. Оценка размера задачи │
│ ├─ S-задача → реализовать напрямую (без субагента)
│ └─ M/L-задача → подготовить контекст → субагент │
└─────────────────────────────────────────────────────┘
│
┌─────────┴─────────┐
▼ ▼
┌──────────────────────┐ ┌────────────────────────────┐
│ S-задача (напрямую) │ │ M/L-задача (субагент) │
│ • Read/Edit файлов │ │ • Формирование prompt │
│ • Self-review │ │ • Task tool: general-purpose
│ • Коммит │ │ • Реализация кода │
└──────────────────────┘ │ • Self-review + коммит │
│ │ • Возврат результатов │
│ └────────────────────────────┘
│ │
└─────────┬─────────┘
▼
┌─────────────────────────────────────────────────────┐
│ ОСНОВНОЙ АГЕНТ │
│ 1. Обновление PROJECT_STATE.json │
│ 2. Обновление knowledge.json (если есть learnings) │
└─────────────────────────────────────────────────────┘
ПЕРЕД любой валидацией и ЛЮБОЙ git-операцией.
Source of truth — frontmatter tasks/TASK-*.md (как объявлено в шаге 5
этого же алгоритма: markdown frontmatter и PROJECT_STATE.json должны
синхронизироваться, но при рассинхроне авторитетен frontmatter).
PROJECT_STATE.json используется как быстрый индекс и cross-check.
Прочитай frontmatter всех tasks/TASK-*.md (status: поле).
Прочитай .state/PROJECT_STATE.json (inProgress, inReview, waitingForPM,
blocked).
Собери объединённое множество «активных» TASK по любому из источников:
status ∈ {in_progress, review, waiting_pm} в frontmatter ИЛИ
TASK-ID в inProgress / inReview / waitingForPM в PROJECT_STATE.
(OR намеренно: guard должен сработать даже при рассинхроне — false
positive допустим, false negative — нет.)
blocked НЕ входит в guard-множество. Контракт /polisade:continue
(см. skills/continue/SKILL.md) явно предписывает пропускать blocked
и продолжать работу с другими TASK — то есть одна технически
заблокированная задача не должна запрещать запуск implement для
ready-TASK. blocked снимается PM вручную (устраняется техническая
причина — окружение, зависимость, падающий тест) + смена status: blocked → ready в frontmatter TASK и ре-индексация через
/polisade:sync --apply. /polisade:unblock для blocked НЕ применим — он
обрабатывает только waitingForPM.
Если объединённое множество НЕ пусто — НЕ переходи к валидации,
НЕ трогай git, независимо от того, указан ли TASK-XXX аргументом.
Это соответствует контракту Polisade Orchestrator «не начинай новую TASK, пока есть
незавершённые в работе» (см. /polisade:continue). Выведи:
⛔ /polisade:implement: найдены незавершённые задачи
В работе (in_progress — PR ещё не создан):
{перечень из frontmatter/inProgress, с пометкой расхождения между
источниками, если есть}
В review (merge — ответственность PM):
{перечень из frontmatter/inReview с PR-ссылками или пометкой «PR не создан»}
Ждут PM (waiting_pm):
{перечень из frontmatter/waitingForPM с вопросами}
Если frontmatter и PROJECT_STATE.json расходятся — запусти
`/polisade:sync --apply` (или `python3 scripts/polisade_sync.py . --apply --yes`)
ДО любых git-действий. `/polisade:sync` без `--apply` работает в dry-run
и ничего не пишет.
Доступные действия — в зависимости от статусов найденных TASK:
→ in_progress / review:
→ /polisade:continue — продолжить автономно (НЕ /polisade:implement!)
→ merge PR вручную — действие PM (если review зелёный)
→ waiting_pm:
→ /polisade:unblock — интерактивная сессия по всему
waitingForPM (аргумент не нужен,
скилл сам пройдёт список)
(⚠️ /polisade:continue при waitingForPM ≠ [] сразу остановится
и потребует именно /polisade:unblock — см. skills/continue/SKILL.md)
→ в любом случае:
→ /polisade:state — обзор
⛔ В re-invocation report режиме ЗАПРЕЩЕНО: git merge, git push origin main,
git branch -D <feature>, любой pr-merge (VCS CLI/API). Feature-ветки остаются как есть.
Даже если активная TASK в review без PR — НЕ «докидывай» merge;
resume через /polisade:continue (он сам создаст PR и запустит review).
НИКАКОЙ новый /polisade:implement (ни с аргументом, ни без) не продолжает
работу, пока есть хоть одна TASK в in_progress / review / waiting_pm.
(blocked-задачи это ограничение НЕ создают — их пропускает и сам
/polisade:continue.)
STOP. Никаких git checkout, git pull, git push, git branch -D,
git worktree add, git checkout -b.
Охват: in_progress, review (inReview), waiting_pm (waitingForPM).
blocked намеренно исключён — соответствует контракту /polisade:continue
(пропускает blocked). Чтение двух источников с OR-семантикой — страховка
против рассинхрона. Блокирует любой повторный /polisade:implement
(с аргументом или без) — намеренное соответствие контракту «не начинай
новую TASK пока есть незавершённые в работе». Исключений нет: если нужно
продолжить уже активную TASK — правильный инструмент /polisade:continue
(у него есть resume-логика) либо /polisade:unblock для waiting_pm
(интерактивный проход по всему waitingForPM, аргумент не требуется),
а не повторный запуск implement.
/polisade:implementЭТА СЕКЦИЯ ВЫПОЛНЯЕТСЯ ТОЛЬКО ЕСЛИ guard §0 прошёл (нет активных TASK в {in_progress, review, waiting_pm}). Задача §0.5 — выбрать путь по статусу конкретной TASK (если есть аргумент) или по множеству ready-TASK (без аргумента). НЕ трогай git до завершения диспетчеризации.
| Статус TASK (frontmatter) | С аргументом TASK-XXX | Без аргумента |
|---|---|---|
ready | full cycle (шаги §1–§5) | pick next ready → full cycle |
in_progress | unreachable (guard §0 остановит) | unreachable (guard §0) |
review + pr_url | unreachable (guard §0) | unreachable (guard §0) |
review + pr_url пуст | unreachable (guard §0) — resume через /polisade:continue | unreachable (guard §0) — resume через /polisade:continue |
done | «уже done», STOP, без git-операций | skip → pick next ready |
blocked | показать blocker, STOP | skip → pick next ready (§continue) |
waiting_pm | unreachable (guard §0) → /polisade:unblock | unreachable (guard §0) |
Для всех unreachable cells — guard §0 блокирует re-invocation и
маршрутизирует на /polisade:continue (resume review/in_progress) или
/polisade:unblock (waiting_pm). Эта таблица НЕ даёт лицензии обойти
guard — если сюда попала TASK в активном статусе, это баг диспетчера,
STOP с blocked: OPS-008 dispatcher invariant.
def dispatch_implement(task_arg, state):
# Called only after §0 guard passed.
assert not state.has_active_tasks(), \
"OPS-008: dispatcher reached despite active TASK — STOP"
if task_arg:
task = resolve(task_arg) # frontmatter + PROJECT_STATE cross-check
if task.status == "done":
return stop("TASK уже done — ничего не делаем, git не трогаем")
if task.status == "blocked":
return stop(f"TASK blocked: {task.reason}. Снятие блокировки — PM.")
if task.status == "ready":
return full_cycle(task) # переход к §1 Валидация
# in_progress/review/waiting_pm — guard §0 должен был остановить
return stop(f"OPS-008: unreachable status {task.status}, guard bypassed")
# Без аргумента
candidates = state.ready_tasks() # blocked/done уже отфильтрованы
if not candidates:
return stop("Нет ready TASK. /polisade:tasks или /polisade:state для обзора.")
return full_cycle(pick_by_priority(candidates))
⛔ После возврата из диспетчера (любой stop(...) arm):
git merge, НЕ git push origin main, НЕ git branch -D <feature>,
НЕ любой pr-merge (VCS CLI/API), НЕ git reset, НЕ git rebase main./polisade:continue (resume)..state/PROJECT_STATE.jsonreadyready, все depends_on имеют статус doneНет готовых задач для реализации.
Возможные причины:
• Все задачи ждут зависимости
• Нет созданных задач
Доступные действия:
→ /polisade:tasks для создания задач из FEAT/SPEC/PLAN
→ /polisade:defect для добавления бага (создаст TASK)
→ /polisade:chore для простой задачи (создаст TASK)
→ /polisade:state для обзора проекта
Перед запуском субагента оцени размер задачи по TASK файлу:
S-задача (реализовать напрямую, без субагента):
M/L-задача (через субагент):
def estimate_size(task):
ac_count = len(task.acceptance_criteria)
files_mentioned = count_files_in_task(task)
if ac_count <= 3 and files_mentioned <= 2:
return "S" # direct implementation
return "M+" # subagent
Если S-задача:
TASK.requirements, контракты по TASK.design_refs) применяются
и к S-задачам — см. шаг 2 «Связанные документы (resolve full chain)»⛔ Этот шаг ОБЯЗАТЕЛЕН для S, M, L задач при gitBranching: true.
Пропуск = OPS-001 (коммит не в ту ветку, в частности в main).
На выходе шага должен выполняться expected-branch invariant:
cd "$WORK_DIR" && git rev-parse --abbrev-ref HEAD == compute_expected_branch(TASK)
где compute_expected_branch(TASK) — детерминированная функция по parent из
TASK frontmatter; правила — в секции "Git Branching" ниже (source of truth).
Коротко: parent: PLAN-* → plan/PLAN-XXX-TASK-YYY-<slug>; parent: FEAT-*
→ feat/FEAT-XXX-<slug>; аналогично для BUG-/DEBT-/CHORE-.
ВАЖНО для worktree mode. $WORK_DIR = worktree_path (в корне репо ветка
остаётся на main — это нормальное поведение git worktree). Guard и все
последующие git-инспекции ВСЕГДА выполняются внутри $WORK_DIR.
Алгоритм шага:
1. expected = compute_expected_branch(TASK)
2. Если workspaceMode == "worktree" И gitBranching: true:
— git worktree add .worktrees/<dir> -b <expected> (если ветки нет)
— git worktree add .worktrees/<dir> <expected> (если ветка уже есть)
— WORK_DIR = .worktrees/<dir>
Иначе если gitBranching: true (inplace):
— git checkout <expected> (если ветка уже есть)
— git checkout -b <expected> (если новая)
— WORK_DIR = project_root
Иначе (gitBranching: false, legacy):
— Инвариант отключён. Пропустить шаг, коммит в текущую ветку.
— Экспортировать ТОЛЬКО явный мод-флаг: export POLISADE_GIT_BRANCHING=false
— POLISADE_EXPECTED_BRANCH и POLISADE_WORK_DIR НЕ выставляются.
3. Assertion ВНУТРИ WORK_DIR (для worktree — критично!):
current = run(f'cd "{WORK_DIR}" && git rev-parse --abbrev-ref HEAD').stdout.strip()
assert current == expected, \
f"OPS-001: cwd={WORK_DIR} current={current}, expected={expected}"
Если assertion упал → STOP с диагностикой, НЕ продолжать к Шагу 2.
4. Экспортировать для всех последующих bash-вызовов (основной агент и субагент).
Fail-closed модель: bash-guard всегда требует ЯВНЫЙ signal, никогда не
"fall-through по умолчанию" (это ловит truncation/dropout в prompt для слабых моделей).
Если gitBranching: true:
export POLISADE_GIT_BRANCHING="true"
export POLISADE_EXPECTED_BRANCH="<expected>"
export POLISADE_WORK_DIR="<WORK_DIR>"
Если gitBranching: false:
export POLISADE_GIT_BRANCHING="false"
(остальные НЕ выставляются)
Guard-сниппет перед каждым commit/push/add читает POLISADE_GIT_BRANCHING:
— "true" → проверить CURRENT == EXPECTED, fail иначе
— "false" → pass-through (инвариант отключён по дизайну)
— unset/другое → ⛔ fail (bug: основной агент не экспортировал mode)
⛔ Не использовать git.current_branch() или git rev-parse … без явного
cd "$WORK_DIR" — в worktree mode корневой репо возвращает main, это даёт
ложный fail.
Детали реализации ниже (переиспользуемые: setup_worktree и fallback).
Проверь settings.workspaceMode и settings.gitBranching в PROJECT_STATE.json.
Если workspaceMode == "worktree" И gitBranching: true:
def setup_worktree(project_root, branch_name):
if workspace_mode != "worktree" or not git_branching:
run(f"git checkout -b {branch_name}") # fallback
return project_root
worktrees_root = f"{project_root}/.worktrees"
dir_name = branch_name.replace("/", "__")
worktree_path = os.path.join(worktrees_root, dir_name)
# Проверить существующий worktree для ветки
existing = parse_git_worktree_list()
if branch_name in existing:
return existing[branch_name] # переиспользовать
try:
mkdir -p {worktrees_root}
git worktree add {worktree_path} -b {branch_name}
except:
# Graceful fallback
warn("git worktree add failed, falling back to git checkout -b")
run(f"git checkout -b {branch_name}")
return project_root
# Копировать .state/ (КРОМЕ counters.json!)
# ⚠️ ВАЖНО: каждую команду выполняй ОТДЕЛЬНЫМ Bash-вызовом!
# НЕ объединяй в одну цепочку через && с переменными —
# это ломает матчинг permissions в settings.json.
mkdir -p {worktree_path}/.state
cp .state/PROJECT_STATE.json {worktree_path}/.state/
cp .state/knowledge.json {worktree_path}/.state/
cp .state/session-log.md {worktree_path}/.state/ 2>/dev/null || true
# ⚠️ counters.json НЕ копируется — глобальный ресурс
# .claude/ уже в worktree через git (tracked directory) — НЕ нужен симлинк!
# Симлинк dependency-каталогов (если есть) — чтобы инструменты были доступны из worktree
# .venv — Python (ruff/pytest/mypy), node_modules — JS/TS, vendor — Go/PHP/Ruby
for dep_dir in [".venv", "node_modules", "vendor"]:
if os.path.isdir(f"{project_root}/{dep_dir}"):
ln -s {project_root}/{dep_dir} {worktree_path}/{dep_dir}
return worktree_path
Шаги выполнения:
/ → __ (например feat/FEAT-001-auth → feat__FEAT-001-auth).worktrees/{dir_name}/ (внутри проекта, добавлена в .gitignore)git worktree list --porcelain — если worktree для ветки уже существует, переиспользуйgit worktree add {path} {branch} (без -b)git worktree add {path} -b {branch}.state/ файлы (кроме counters.json!).claude/ уже в worktree (tracked в git) — НЕ создавай симлинк и НЕ копируй!.venv, node_modules, vendor — если есть в project_root → ln -s {project_root}/{dep_dir} {worktree_path}/{dep_dir}{worktree_path}{worktree_path}:
current = run(f'cd "{worktree_path}" && git rev-parse --abbrev-ref HEAD').stdout.strip()
assert current == branch_name, \
f"OPS-001: cwd={worktree_path} current={current}, expected={branch_name}"
НЕ использовать git.current_branch() без явного cd "{worktree_path}" —
в worktree mode корень репо возвращает main, это даст ложный fail.POLISADE_GIT_BRANCHING=true
POLISADE_EXPECTED_BRANCH=<branch_name>
POLISADE_WORK_DIR=<worktree_path>
Если workspaceMode != "worktree" или gitBranching: false:
gitBranching: true, workspaceMode: inplace → git checkout -b {branch_name}
git rev-parse --abbrev-ref HEAD == branch_namePOLISADE_GIT_BRANCHING=true, POLISADE_WORK_DIR=project_root, POLISADE_EXPECTED_BRANCH=branch_namegitBranching: false → инвариант отключён, но ОБЯЗАТЕЛЬНО export
POLISADE_GIT_BRANCHING=false (явный positive signal для guard'а).
POLISADE_EXPECTED_BRANCH и POLISADE_WORK_DIR НЕ выставляются. Guard видит
"false" → pass-through. Если флаг не выставлен вообще → guard fail-closed
(защита от truncation/dropout в prompt).⛔ TASK-файлы ВСЕГДА лежат в корневой tasks/TASK-XXX-*.md — НИКОГДА в docs/tasks/, docs/TASK-*.md или где-то ещё.
Это единственное допустимое расположение, зафиксированное в структуре проекта (CLAUDE.md → Project Structure). Все скиллы-создатели (/polisade:tasks, /polisade:defect, /polisade:debt, /polisade:chore) обязаны создавать файлы ИМЕННО там.
Перед чтением TASK выполни проверку:
import os, glob
task_id = "TASK-XXX" # из аргумента команды или выбранной ready-задачи
canonical = glob.glob(f"tasks/{task_id}-*.md")
if not canonical:
# Проверить распространённые «неправильные» места
misplaced = (
glob.glob(f"docs/tasks/{task_id}-*.md") +
glob.glob(f"docs/{task_id}-*.md") +
glob.glob(f"backlog/tasks/{task_id}-*.md") +
glob.glob(f"{task_id}-*.md") # в корне
)
if misplaced:
STOP_WITH_ERROR(f"""
⛔ НАЙДЕН TASK-файл НЕ В КОРНЕВОЙ `tasks/`:
{misplaced}
По конвенции Polisade Orchestrator все TASK-файлы ДОЛЖНЫ быть в `tasks/TASK-XXX-*.md`.
`/polisade:implement` НЕ ищет таски в других местах.
Действие:
mkdir -p tasks
mv {misplaced[0]} tasks/
Затем пересобери индексы:
python3 scripts/polisade_sync.py .
После этого перезапусти /polisade:implement {task_id}.
""")
else:
STOP_WITH_ERROR(f"TASK-файл {task_id} не найден. Создай через /polisade:tasks, /polisade:defect, /polisade:debt или /polisade:chore.")
Прочитай и собери:
tasks/TASK-XXX-*.md) — полное содержимое. Путь ОБЯЗАТЕЛЬНО начинается с tasks/ (см. 2.0)..state/knowledge.json):
patterns — используемые паттерныantiPatterns — что избегатьdecisions — принятые решения (ссылки на ADR)glossary — ubiquitous language project-wide (federated из DESIGN packages). Передавай в субагент как source-of-truth для именования сущностей в коде, тестах, комментариях.keyFiles — ключевые файлы проектаtesting.testCommand — команда запуска тестов (если задана)testing.typeCheckCommand — проверка типов (если задана)testing.lintCommand — линтер (если задан)testing.strategy — стратегия тест-авторинга: "tdd-first" или "test-along" (см. references/test-authoring-protocol.md)PROJECT_STATE.artifacts[parent_id].parent (рекурсивно по chain)
c. Если найден SPEC и TASK.requirements не пусто:
requirements IDrequirements: [] — передай весь SPEC (legacy/безопасный fallback)
d. Если у SPEC есть child DESIGN-PKG (через PROJECT_STATE.artifacts):DESIGN-NNN-{slug}/README.mdTASK.design_refs указывает конкретные файлы — прочитай ИХdesign_refs: [] но TASK явно про API → прочитай api.mddata-model.md
e. Если в SPEC.constraints или в DESIGN упоминаются ADR — прочитай эти ADR
f. Извлеки Assumptions (A-N) из SPEC §4 (если SPEC найден) — передай
в субагент для awareness: если assumption можно проверить программно
(например, A-1: "API возвращает user_id в JWT"), субагент должен добавить
assert/validation в код
g. Извлеки system_boundary и external_systems из SPEC frontmatter
(если SPEC найден) — передай в субагент для ограничения скоупаИспользуй следующий шаблон:
Реализуй задачу {TASK-ID}: {task_title}
═══════════════════════════════════════════
КОНТЕКСТ ПРОЕКТА
═══════════════════════════════════════════
Patterns (следуй этим паттернам):
{patterns из knowledge.json или "Не определены"}
Anti-patterns (избегай):
{antiPatterns из knowledge.json или "Не определены"}
Decisions (учитывай):
{decisions из knowledge.json или "Нет зафиксированных решений"}
Glossary (ubiquitous language — source of truth для именования):
{knowledge.glossary как список "term — definition (source)" или "Glossary пуст"}
TERMINOLOGY (ОБЯЗАТЕЛЬНО):
- Используй ТОЧНО эти термины в названиях классов, функций, переменных, полей,
тестов и комментариях. Один концепт — одно имя project-wide.
- Если в glossary есть "Session" — НЕ изобретай "UserSession", "SessionRecord",
"AuthState". Не вводи синонимы существующих терминов.
- `synonyms_to_avoid` в записи glossary — буквальный blacklist имён.
- Если для нужного концепта нет термина — придерживайся convention проекта;
при сомнении flag в waiting_pm, не плоди дубликаты.
Key files:
{keyFiles из knowledge.json или "Изучи структуру проекта"}
═══════════════════════════════════════════
ТРЕБОВАНИЯ ЗАДАЧИ
═══════════════════════════════════════════
{полное содержимое TASK файла}
═══════════════════════════════════════════
⛔ ТОЧНОЕ СЛЕДОВАНИЕ ИНСТРУКЦИЯМ ЗАДАЧИ
═══════════════════════════════════════════
CRITICAL: Реализуй задачу СТРОГО по инструкциям в TASK файле.
- Если таск говорит "используй X" — используй X, НЕ подставляй альтернативу Y
- Если таск говорит "удали/замени X на Y" — удали X и используй Y
- Если таск описывает порядок операций — соблюдай ИМЕННО этот порядок
- НЕ "оптимизируй" подход, даже если видишь "лучший" вариант в существующем коде
Если ты считаешь что инструкция таска ошибочна или есть лучший путь —
верни waiting_pm с объяснением, а НЕ реализуй свою версию молча.
═══════════════════════════════════════════
СВЯЗАННЫЕ ДОКУМЕНТЫ
═══════════════════════════════════════════
{содержимое родительского FEAT/SPEC/BUG если есть}
═══════════════════════════════════════════
ТРЕБОВАНИЯ ИЗ SPEC (resolved через parent chain)
═══════════════════════════════════════════
Эта TASK реализует следующие требования parent SPEC:
{для каждого composite FR/NFR из TASK.requirements (формат `{DOC}.FR-NNN`):}
### {DOC_ID}.{FR-NNN}: {title}
**EARS Statement:** {statement}
**Acceptance criteria:**
{Gherkin scenarios — Given/When/Then}
(Если TASK.requirements: [] — этот блок: "N/A — TASK не привязан к SPEC requirements")
⛔ **НЕ делай `grep -r 'FR-NNN' .` по проекту** — parent chain уже резолвит
scope однозначно. `FR-007` в разных top-level документах (PRD vs FEAT vs SPEC)
— это **разные требования**. При сомнении — спроси PM, в каком именно
документе работаем.
═══════════════════════════════════════════
ARCHITECTURE CONTRACTS (из DESIGN package)
═══════════════════════════════════════════
{релевантные секции из api.md / data-model.md / sequences.md по TASK.design_refs}
(Если design_refs: [] — этот блок: "N/A — у parent SPEC нет DESIGN package")
═══════════════════════════════════════════
ASSUMPTIONS AND CONSTRAINTS (из SPEC §4)
═══════════════════════════════════════════
Assumptions (A-N):
{assumptions из SPEC §4.1 или "N/A"}
Constraints (C-N):
{constraints из SPEC §4.2 или "N/A"}
ИНСТРУКЦИИ:
- Constraints — нерушимые. Код обязан быть совместим со всеми constraints.
- Assumptions — если assumption можно проверить программно (например,
"API возвращает user_id в JWT"), добавь defensive validation/assert в код.
Если нельзя — пропусти, но не нарушай assumption молча.
═══════════════════════════════════════════
SYSTEM BOUNDARY (из SPEC frontmatter)
═══════════════════════════════════════════
system_boundary: {system_boundary из SPEC frontmatter или "N/A"}
external_systems: {список external_systems из SPEC frontmatter или "N/A"}
ИНСТРУКЦИИ (если system_boundary не N/A):
- Ты работаешь ВНУТРИ {system_boundary}. Внешние системы = клиенты/адаптеры.
- НЕ реализуй код внешних систем. Реализуй НАШУ сторону интеграции:
адаптеры, клиенты, маппинг протоколов.
- Для тестов: mock/stub внешних систем, НЕ реальные вызовы.
- Если TASK требует работу с external system — реализуй клиент/адаптер
на нашей стороне, не сервер/логику внешней системы.
═══════════════════════════════════════════
РАБОЧАЯ ДИРЕКТОРИЯ (WORKTREE)
═══════════════════════════════════════════
⚠️ Ты работаешь в git worktree!
WORKTREE_PATH: {worktree_path}
EXPECTED_BRANCH: {expected_branch} ← для OPS-001 PRE-COMMIT GUARD
POLISADE_GIT_BRANCHING: true ← обязательный mode-signal для guard
ПРАВИЛА:
1. ВСЕ операции с кодом — в WORKTREE_PATH
2. Команды: cd "{worktree_path}" && <команда>
3. .state/ файлы: {worktree_path}/.state/ (локальная копия)
4. НЕ переключай ветки! Worktree привязан к одной ветке.
5. git commit/push — только после PRE-COMMIT GUARD (см. секцию ниже).
6. НЕ создавай новые артефакты (TASK/FEAT/ADR) — counters.json недоступен.
Если нужен новый артефакт → верни waiting_pm.
7. Бери команды тестирования/линтинга из knowledge.json (testing.*).
НЕ изобретай команды — используй ТОЛЬКО то, что задано в проекте.
Примеры вызова в worktree для разных стеков:
# Python (если .venv/ есть в worktree через симлинк)
cd "{worktree_path}" && .venv/bin/pytest tests/ -x -q
cd "{worktree_path}" && .venv/bin/ruff check src/
# Java/Scala (Gradle)
cd "{worktree_path}" && ./gradlew test
cd "{worktree_path}" && ./gradlew check
# Node.js/TypeScript
cd "{worktree_path}" && npm test
cd "{worktree_path}" && npx eslint .
cd "{worktree_path}" && npx tsc --noEmit
# Go
cd "{worktree_path}" && go test ./...
cd "{worktree_path}" && golangci-lint run
# Rust
cd "{worktree_path}" && cargo test
cd "{worktree_path}" && cargo clippy
⛔ ЗАПРЕЩЕНО:
⛔ Абсолютные пути: /Users/.../Projects/.../.venv/bin/python
⛔ Изобретать команды — бери из knowledge.json (testing.*)
⛔ Присвоение в начале: WT="/path" && cd "$WT" && ...
{Если .venv/ присутствует в worktree — дополнительные Python-ограничения:}
⛔ python -m <tool>: .venv/bin/python -m pytest (вызывай инструмент напрямую)
⛔ python -c "...": .venv/bin/python -c "import ..."
⛔ Голый pytest/ruff/mypy без .venv/bin/ (без активации venv — не на PATH!)
(Блок добавляется в prompt ТОЛЬКО при workspaceMode: "worktree".
Если worktree не используется — блок не включать.)
═══════════════════════════════════════════
КОМАНДЫ ДЛЯ ТЕСТИРОВАНИЯ И ПРОВЕРОК
═══════════════════════════════════════════
{Блок добавляется ТОЛЬКО если хотя бы одно поле testing.* заполнено в knowledge.json}
Используй ИМЕННО эти команды (из knowledge.json), НЕ изобретай свои:
Тесты: {testing.testCommand или "НЕ ЗАДАНО — регрессионные тесты будут пропущены"}
Type check: {testing.typeCheckCommand или "не задано"}
Lint: {testing.lintCommand или "не задано"}
Для worktree всегда добавляй cd "{worktree_path}" && перед командой.
⛔ ЗАПРЕЩЕНО (для worktree):
ПРАВИЛЬНО: cd "{worktree_path}" && {testing.testCommand}
ПРАВИЛЬНО: cd "{worktree_path}" && ./gradlew test
ПРАВИЛЬНО: cd "{worktree_path}" && npm test
НЕПРАВИЛЬНО: cd "{worktree_path}" && /абсолютный/путь/к/инструменту (абсолютные пути!)
НЕПРАВИЛЬНО: cd "{worktree_path}" && выдуманная-команда (только из knowledge.json!)
НЕПРАВИЛЬНО: WT="/path" && cd "$WT" && ... (присвоение в начале запрещено!)
{Если testing.strategy == "tdd-first" И testCommand задан И task-scoped run разрешим — инлайнить блок ниже.
Если testing.strategy == "test-along", отсутствует, testCommand не задан, или task-scoped run невозможен — НЕ включать этот блок.
Source-of-truth: references/test-authoring-protocol.md}
═══════════════════════════════════════════
⛔ TDD-FIRST ПРОТОКОЛ (testing.strategy: "tdd-first")
═══════════════════════════════════════════
Ты ОБЯЗАН реализовать задачу в ДВА ЭТАПА:
### ЭТАП 1: RED — ТЕСТЫ (до написания кода реализации)
Источники тестов (по приоритету):
1. Gherkin scenarios из SPEC (FR-NNN → Given/When/Then) — каждый Scenario → 1 тест
2. Acceptance criteria checklist из TASK — каждый AC → минимум 1 тест
3. Design contracts из design_refs (api.md, data-model.md) → контрактные тесты
4. Assumptions/constraints из SPEC §4 → defensive/negative тесты
Действия:
1. Сгенерируй тесты, покрывающие ВСЕ источники выше
2. Запусти ТОЛЬКО новые тесты (task-scoped run):
- Команда из секции ## Verification в TASK (первая тестовая команда)
- Или derive file-scoped: pytest → `pytest tests/test_<module>.py`, jest → `jest <file>`, etc.
3. Классифицируй падения:
- Syntax/import/compilation error → ИСПРАВЬ harness, перезапусти
- Assertion failures → ОК, это ожидаемый red
- Все тесты прошли (vacuous pass) → ⚠️ Проверь что тесты реально тестируют новое поведение
4. Перед коммитом выведи RED CHECKLIST:
─────────────────────────────────────────── RED CHECKLIST (test-authoring) ─────────────────────────────────────────── [✓/✗] Добавлены/обновлены только тесты и минимальный harness (stubs) [✓/✗] Новые тесты компилируются/парсятся без ошибок [✓/✗] Новые тесты падают по ожидаемой причине (assertion failures, NOT import/syntax error) [✓/✗] Production code НЕ реализован на этом этапе [✓/✗] Источники тестов: покрыты все AC и Gherkin из TASK/SPEC ───────────────────────────────────────────
5. Коммит: `[{TASK-ID}] Add failing tests for {TASK-ID}`
⛔ НЕ ПИШИ КОД РЕАЛИЗАЦИИ НА ЭТОМ ЭТАПЕ!
Только тестовые файлы + минимальные stubs (пустые функции/классы) чтобы тесты компилировались.
### ЭТАП 2: GREEN — РЕАЛИЗАЦИЯ (чтобы тесты прошли)
1. Напиши код, который делает тесты из этапа 1 зелёными
2. Можно добавить дополнительные edge-case тесты
3. Все тесты (из этапа 1 + новые) должны проходить
4. Выполни полный SELF-REVIEW CHECKLIST (см. ниже)
5. Коммит: `[{TASK-ID}] Implement {TASK-ID}`
⛔ ПРАВИЛО ФИЛЬТРАЦИИ:
- Red phase: допустима фильтрация (file/test target) — ТОЛЬКО новые тесты
- Regression (шаг 2 полного цикла): фильтрация ЗАПРЕЩЕНА — без изменений
═══════════════════════════════════════════
═══════════════════════════════════════════
SELF-REVIEW (ОБЯЗАТЕЛЬНО ВЫВЕСТИ перед коммитом!)
═══════════════════════════════════════════
⛔ ПЕРЕД КОММИТОМ ты ОБЯЗАН:
1. Перечитать ВСЕ изменённые файлы (используй Read tool)
2. ВЫВЕСТИ этот чеклист с результатами проверки:
─────────────────────────────────────────── SELF-REVIEW CHECKLIST ─────────────────────────────────────────── [✓/✗] Hardcoded values: нет паролей/ключей/URL [✓/✗] Error handling: async обёрнут в try/catch [✓/✗] Patterns: код соответствует patterns [✓/✗] Anti-patterns: нет нарушений antiPatterns [✓/✗] Terminology: имена классов/функций/полей соответствуют knowledge.glossary (нет синонимов для канонических терминов; нет имён из synonyms_to_avoid) [✓/✗] Tests: тесты добавлены/обновлены [✓/✗] TDD: тесты написаны ДО реализации (если testing.strategy: "tdd-first") RED CHECKLIST пройден | Коммит 1: failing tests | Коммит 2: implementation (N/A если strategy: "test-along") [✓/✗] Каждое composite FR/NFR из требований реализовано в коде (поштучно): ✓/✗ SPEC-001.FR-001: → file:function ✓/✗ SPEC-001.FR-002: → file:function ... (по списку TASK.requirements, composite IDs из parent SPEC/PRD/FEAT) [✓/✗] DESIGN CONFORMANCE (если design_refs non-empty И design_waiver != true): Для каждого файла из design_refs: ✓/✗ : реализация совпадает с контрактом
Если есть расхождение (DESIGN-DEVIATION):
⛔ ОБЯЗАТЕЛЬНО:
1. Обнови затронутый design-артефакт в ТОМ ЖЕ коммите/PR
(design docs — source of truth, drift недопустим)
2. Добавь в PR description секцию "Design Updates":
## Design Updates
- DESIGN-NNN/api.md: <что изменилось>
- DESIGN-NNN/data-model.md: <что изменилось>
3. DESIGN-DEVIATION комментарий в коде — audit trail, НЕ удалять
(N/A если design_refs пуст или design_waiver: true)
[✓/✗] Acceptance criteria (ПОШТУЧНО): ✓/✗ AC1: <описание> → file:line ✓/✗ AC2: <описание> → file:line ... (каждый критерий отдельно!) ───────────────────────────────────────────
3. Если хотя бы один [✗] — ИСПРАВЬ перед коммитом
4. После исправления — повтори self-review
⚠️ КОММИТ БЕЗ ВЫВОДА CHECKLIST = НАРУШЕНИЕ ПРОТОКОЛА!
═══════════════════════════════════════════
ФОРМАТ КОММИТА
═══════════════════════════════════════════
test-along: [{TASK-ID}] краткое описание
tdd-first коммит 1: [{TASK-ID}] Add failing tests for {TASK-ID}
tdd-first коммит 2: [{TASK-ID}] Implement {TASK-ID}
⚠️ Перед КАЖДЫМ коммитом — обязательный PRE-COMMIT GUARD (OPS-001), см. ниже.
═══════════════════════════════════════════
⛔ ЗАПРЕЩЁННЫЕ git-команды (HARD BOUNDARIES)
═══════════════════════════════════════════
В рамках реализации TASK ты работаешь ТОЛЬКО в своей feature-ветке
(или worktree, привязанном к ней). ЗАПРЕЩЕНО:
- git checkout main / master / switch main
- git push origin main / origin master / --force в main
- git merge / git rebase onto main
- git branch -D / git push origin --delete
- git commit / git add / git push с current_branch ≠ EXPECTED_BRANCH
(см. PRE-COMMIT GUARD ниже)
- ⛔ NEVER git add -f / git add --force на gitignored путях (.gigacode,
.qwen, .codex, .worktrees и т. д.). «Кроме X» = исключение, не фокус.<!-- polisade:claude-only BEGIN -->
NB: исключение по `.claude/` — только файл `.claude/settings.json`,
не директория целиком.<!-- polisade:claude-only END -->
После self-review ты ВОЗВРАЩАЕШЬ JSON-результат и БОЛЬШЕ НИЧЕГО:
— НЕ ищешь следующую TASK
— НЕ «готовишь main к следующей задаче»
— НЕ запускаешь новый цикл
— НЕ пытаешься сделать merge/push/delete
Твоя задача ОДНА. Возврат управления — это конец.
Если ты запущен для TASK, которая уже в review (PR создан или нет) —
это bug диспетчера основного агента. Верни JSON
{"status":"blocked","reason":"OPS-008: subagent spawned for review-stage TASK"}
и больше ничего не делай. НЕ пытайся «докидать», НЕ пытайся мержить.
Merge — ответственность PM. Если в процессе ты обнаружишь, что main
опередила feature-ветку — НЕ мёржи, верни `waiting_pm` с описанием.
═══════════════════════════════════════════
⛔ PRE-COMMIT GUARD (OPS-001 — ОБЯЗАТЕЛЬНО перед КАЖДЫМ git commit/push/add)
═══════════════════════════════════════════
MODE (POLISADE_GIT_BRANCHING): {git_branching_mode} ← "true" | "false", инжектируется основным агентом
EXPECTED_BRANCH: {expected_branch_or_NA} ← инжектируется ТОЛЬКО при MODE=true
WORK_DIR: {worktree_path_or_NA} ← инжектируется ТОЛЬКО при MODE=true
ПЕРВЫЕ bash-команды в твоей работе (до любого git). Экспортируй ВСЁ, что
дал основной агент — даже если одна из переменных кажется «необязательной»:
```bash
# При MODE=true (gitBranching: true):
export POLISADE_GIT_BRANCHING="true"
export POLISADE_EXPECTED_BRANCH="{expected_branch}"
export POLISADE_WORK_DIR="{worktree_path_or_dot}"
# При MODE=false (gitBranching: false, legacy):
export POLISADE_GIT_BRANCHING="false"
# (POLISADE_EXPECTED_BRANCH и POLISADE_WORK_DIR НЕ устанавливаются)
ПЕРЕД каждым git commit, git push, git add ты ОБЯЗАН выполнить:
MODE="${POLISADE_GIT_BRANCHING:-}"
EXPECTED="${POLISADE_EXPECTED_BRANCH:-}"
WORK="${POLISADE_WORK_DIR:-.}"
case "$MODE" in
true)
if [ -z "$EXPECTED" ]; then
echo "⛔ OPS-001: POLISADE_GIT_BRANCHING=true, но POLISADE_EXPECTED_BRANCH пуст — bug"
exit 1
fi
CURRENT=$(cd "$WORK" && git rev-parse --abbrev-ref HEAD)
if [ "$CURRENT" != "$EXPECTED" ]; then
echo "⛔ OPS-001: cwd=$WORK current=$CURRENT, expected=$EXPECTED — коммит запрещён"
exit 1
fi
echo "✓ pre-commit guard OK: cwd=$WORK branch=$CURRENT"
;;
false)
echo "ℹ️ pre-commit guard skipped: POLISADE_GIT_BRANCHING=false (legacy)"
;;
*)
# fail-closed: отсутствие явного mode-signal = баг (truncation/dropout/bug)
echo "⛔ OPS-001: POLISADE_GIT_BRANCHING не выставлен (ожидалось 'true'|'false'). Commit запрещён."
exit 1
;;
esac
⚠️ Критично: cd "$WORK" ОБЯЗАТЕЛЕН. В режиме git worktree корень
репозитория остаётся на main/master — это нормальное поведение. Ветка
задачи видна ТОЛЬКО внутри {worktree_path}. Без cd guard даст ложный
fail.
⚠️ Fail-closed модель. Отсутствие POLISADE_GIT_BRANCHING НЕ трактуется как
"безопасно". Для legacy-режима основной агент ОБЯЗАН явно выставить
POLISADE_GIT_BRANCHING=false; пустое/неизвестное значение mode = баг (truncation
prompt-а, dropout инструкций, забытый export) → guard fail-closed, коммит
запрещён. Это защита ровно от того класса ошибок, которые вызвали OPS-001.
Если guard упал — НЕ ретрай, НЕ git checkout, НЕ создавай ветку сам,
НЕ пытайся "починить" через export POLISADE_GIT_BRANCHING=false — это
реинтродукция OPS-001. Верни JSON:
{"status": "blocked", "reason": "OPS-001: mode=<mode> expected=<expected> current=<current> cwd=<work>"}
═══════════════════════════════════════════ ⛔ POST-PUSH VERIFICATION (OPS-028 — после КАЖДОГО git push) ═══════════════════════════════════════════
После git push ОБЯЗАТЕЛЬНО использовать:
python3 {plugin_root}/scripts/polisade_vcs.py git-push \
--branch "$POLISADE_EXPECTED_BRANCH" \
--project-root "$POLISADE_WORK_DIR"
# (в Phase C при первом пуше новой ветки добавь --set-upstream)
Никогда не ограничивайся bare git push — Bitbucket Server (и иногда
GitHub) возвращают exit 0 даже когда pre-receive/post-receive hook
или DB-constraint отказали в приёме коммита через remote: fatal /
remote: ERROR / pre-receive hook declined / value too long for type /
duplicate key value. Хелпер сверяет локальный branch SHA
(refs/heads/<branch>, НЕ HEAD) с remote SHA и сканирует stdout+stderr
на известные failure-паттерны.
exit=0 → push verified, можно продолжать.exit=2 → push verification failed. НЕ ставь done/review — ставь
waiting_pm, в waitingForPM процитируй remote_lines и reason
из JSON-вывода. STOP.Контракт: OPS-028 / issue #75.
═══════════════════════════════════════════ ВЕРНИ В КОНЦЕ ═══════════════════════════════════════════
После завершения верни структурированный ответ:
РЕЗУЛЬТАТ (верни СТРОГО в JSON формате):
{
"status": "code_complete | blocked | waiting_pm",
"files_changed": ["path/to/file1.ts", "path/to/file2.ts"],
"commit_hash": "abc1234",
"commits": [
{"phase": "tests_red", "hash": "abc1234"},
{"phase": "implementation", "hash": "def5678"}
],
"learnings": ["новый паттерн или особенность проекта"],
"questions": ["вопрос к PM, если статус waiting_pm"]
}
commit_hash = финальный implementation commit (backward-compatible)commits = optional массив с фазами (при test-along: один элемент {"phase": "implementation", "hash": "..."})⚠️ НЕ ВОЗВРАЩАЙ status: "done"! Только code_complete. done ставится ТОЛЬКО PM-ом после merge PR!
### 4. Запуск (субагент или напрямую)
**Если M/L-задача** — используй Task tool (как раньше):
Task tool: subagent_type: "general-purpose" description: "Implement TASK-XXX" prompt: [сформированный prompt]
**Если S-задача** — реализуй напрямую:
⚠️ **[OPS-001 GUARD]** В S-task direct path основной агент работает напрямую,
без субагента — НО те же правила: все `Read`/`Edit`/`Bash` выполняются внутри
`$POLISADE_WORK_DIR` (в worktree mode = `worktree_path`), и ПЕРЕД каждым коммитом
обязателен **pre-commit guard**:
```bash
MODE="${POLISADE_GIT_BRANCHING:-}"
EXPECTED="${POLISADE_EXPECTED_BRANCH:-}"
WORK="${POLISADE_WORK_DIR:-.}"
case "$MODE" in
true)
[ -n "$EXPECTED" ] || { echo "⛔ OPS-001: MODE=true но EXPECTED пуст"; exit 1; }
CURRENT=$(cd "$WORK" && git rev-parse --abbrev-ref HEAD)
[ "$CURRENT" = "$EXPECTED" ] \
|| { echo "⛔ OPS-001: cwd=$WORK current=$CURRENT, expected=$EXPECTED"; exit 1; }
;;
false)
echo "ℹ️ pre-commit guard skipped: POLISADE_GIT_BRANCHING=false (legacy)"
;;
*)
# Fail-closed: отсутствие явного POLISADE_GIT_BRANCHING = bug (Шаг 1.7 не
# экспортировал). НЕ интерпретируй это как "безопасно".
echo "⛔ OPS-001: POLISADE_GIT_BRANCHING не выставлен — commit запрещён"
exit 1
;;
esac
Если guard упал — STOP, не коммитить. Вернуть статус blocked с причиной.
Только явный POLISADE_GIT_BRANCHING=false пропускает guard; пустой/неизвестный
mode — сигнал бага Шага 1.7, fail-closed по дизайну.
При testing.strategy == "tdd-first" (и testCommand задан, и task-scoped run возможен):
[{TASK-ID}] Add failing tests for {TASK-ID}[{TASK-ID}] Implement {TASK-ID}При testing.strategy == "test-along" (или не задан, или fallback):
[{TASK-ID}] краткое описаниеSelf-review checklist и pre-commit guard обязательны для ОБОИХ стратегий.
═══════════════════════════════════════════════════════════════════ ═══ OPS-010: КОНТРАКТ ВИДОВ КОММИТОВ (issue #58) ═══ ═══════════════════════════════════════════════════════════════════
За один прогон /polisade:implement на TASK разрешены ТОЛЬКО следующие виды
коммитов (подсчёт ведётся по именам — агенты надёжнее считают имена, чем
общие итоги):
| Вид | Шаблон сообщения | Что внутри | Когда |
|---|---|---|---|
implementation | [{TASK-ID}] {desc} (варианты для TDD/регрессии: Add failing tests for …, Implement …, Fix regression: …) | Source + tests + все правки frontmatter TASK.md + движения задачи в PROJECT_STATE.json — staged вместе. Переход ready → in_progress бандлится сюда. | Коммит(ы) субагента на шаге реализации. |
improvement | [{TASK-ID}] Address review feedback: {summary} | Исправления кода + любые отложенные правки status. | IMPROVE-ветка review-loop (commit_and_push()). Бандлит любую ожидающую правку status. |
finalize | Две допустимые формы: [{TASK-ID}] Finalize status: {new-status} (PR #{N}) — когда PR был создан в этом прогоне; ИЛИ [{TASK-ID}] Finalize status: {new-status} (без PR-суффикса) — когда терминальный путь срабатывает до появления PR. Форма regex: ^\[{TASK-ID}\] Finalize status: \S+( \(PR #\d+\))?$. | ТОЛЬКО frontmatter TASK.md (status: + любые PR-метаданные — pr_url, prId, prNumber — добавленные в том же терминальном шаге) + движение задачи в PROJECT_STATE.json. НЕ код, НЕ скрипты, НЕ lastUpdated, НЕ посторонние поля. | Только при терминальном выходе skill, когда у финального status-перехода НЕТ семантического коммита, в который можно было бы его забандлить. Покрывает оба случая: (а) post-PR terminal (PR создан → review_mode=off/blocked, max-iteration waiting_pm) — форма с (PR #{N}); (б) pre-PR terminal (pr-create failure → waiting_pm; любой другой blocked/waiting_pm до создания PR) — форма без суффикса. Максимум один finalize-коммит на один прогон skill. |
ЗАПРЕЩЕНО:
status:
и/или movement в PROJECT_STATE.json, А в этом же прогоне skill
будет следующий commit_and_push() / improvement / PR-шаг — бандли с
ним, не разделяй.lastUpdated в PROJECT_STATE.json. Шаблон
finalize запрещает это по форме diff, и отдельный guard (ниже)
запрещает саму запись поля.Update status to …,
Update PROJECT_STATE.json lastUpdated … — отпечатки бага из bug-report
issue #58, забанены на уровне линтера.finalize-коммита на прогон /polisade:implement.⛔ НЕ пиши lastUpdated в PROJECT_STATE.json — поле зарезервировано,
всегда null (OPS-010 / issue #58). Для времени последнего изменения
используй git log -1 --format=%cI .state/PROJECT_STATE.json.
═══════════════════════════════════════════════════════════════════
После завершения субагента:
Валидация ответа: парси JSON из ответа субагента. Проверь:
status — одно из: code_complete, blocked, waiting_pmfiles_changed — непустой массив (для code_complete)commit_hash — непустая строка (для code_complete)commits — optional массив [{"phase": "...", "hash": "..."}] (при tdd-first: 2 элемента)Обнови PROJECT_STATE.json И frontmatter в .md файле:
code_complete → TASK статус in_progress, добавить в inProgressblocked → TASK в blocked, добавить причинуwaiting_pm → TASK в waitingForPM, добавить вопрос⚠️ При КАЖДОМ изменении статуса TASK — обновляй ОБА источника:
# После code_complete
Edit task .md: status: ready → status: in_progress
Update PROJECT_STATE.json: task → inProgress
# OPS-010: frontmatter + PROJECT_STATE правки бандлятся в `implementation`
# commit субагента (тот же коммит, что несёт код/тесты) — НЕ отдельный
# status-only commit. Перехода `ready → in_progress` это обязательное
# место бандлинга. НЕ пиши lastUpdated.
# После создания PR
Edit task .md: status: in_progress → status: review
Update PROJECT_STATE.json: task → inReview
# OPS-010: эта правка идёт либо в следующий `commit_and_push()` (если
# будет review-loop IMPROVE), либо — при терминальном выходе без
# следующего коммита — в единственный `finalize` commit
# `[TASK-ID] Finalize status: review (PR #N)`. НЕ пиши lastUpdated.
# Merge выполняет PM вручную
# После merge PM ставит: status: done
Это критично для /polisade:sync — source of truth = .md frontmatter.
⛔ /polisade:implement НЕ ставит done и НЕ мержит!
Последовательность статусов в /polisade:implement:
ready → in_progress → review → STOP
↑ ↑
│ └── после создания PR и прохождения review
└── после написания кода (code_complete)
done ставит PM после merge
ПРОДОЛЖИ ПОЛНЫЙ ЦИКЛ (см. секцию "Полный автономный цикл"):
Обнови knowledge.json (если субагент вернул learnings):
{
"learnings": [
{
"task": "TASK-001",
"date": "2026-01-31",
"learning": "В этом проекте используется custom error class"
}
]
}
Продолжи полный цикл (см. следующую секцию)
После успешной реализации кода автоматически выполняй полный цикл:
┌─────────────────────────────────────────────────────────────┐
│ ПОЛНЫЙ ЦИКЛ TASK │
│ │
│ 1. IMPLEMENT ─────────────────────────────────────────────│
│ • [OPS-001 GUARD] Branch/worktree setup (Шаг 1.7) — │
│ ОБЯЗАТЕЛЬНО ДО редактирования файлов. Invariant: │
│ current_branch(WORK_DIR) == compute_expected_branch(TASK)│
│ • Test authoring (см. references/test-authoring-protocol.md) │
│ tdd-first: 1a RED (failing тесты, RED CHECKLIST, │
│ [pre-commit guard], коммит) → │
│ 1b GREEN (код, SELF-REVIEW, │
│ [pre-commit guard], коммит) — 2 коммита│
│ test-along: код + тесты, [pre-commit guard], 1 коммит │
│ ↓ │
│ 2. REGRESSION TEST (см. «Протокол регрессионного │
│ тестирования» ниже) │
│ • Запустить ВСЕ тесты (без -k, без фильтрации!) │
│ • Сравнить падения с testing.knownFlakyTests │
│ — Известные (в knownFlakyTests) → игнорировать │
│ — Новые → исправить, [pre-commit guard], коммит, │
│ повторить │
│ • Type check если testing.typeCheckCommand задан │
│ • Lint (ruff/eslint) если настроен │
│ • Если всё ОК → продолжить │
│ ↓ │
│ 3. PR ────────────────────────────────────────────────────│
│ • [pre-commit guard] Push ветки на remote │
│ • Создать Pull Request │
│ • Статус TASK → review │
│ ↓ │
│ 3.5. PRE-CHECK: REVIEWER CLI ───────────────────────────│
│ • python3 scripts/polisade_cli_caps.py detect │
│ → reviewer.mode = codex | self | blocked │
│ • mode=blocked → STOP с диагностикой │
│ ↓ │
│ 4. QUALITY REVIEW (Independent) ─────────────────────────│
│ • /polisade:review-pr [self] для независимого ревью │
│ • Ревьюер оценивает PR vs TASK │
│ • Если score >= 8 (PASS): │
│ - Статус TASK → review (PR готов к merge) │
│ - STOP — merge выполняет PM │
│ • Если score < 8 (IMPROVE): │
│ - Improvement субагент исправляет код │
│ - [pre-commit guard] commit_and_push │
│ - Re-review (макс. 2 итерации) │
│ ↓ │
│ 5. STOP (hard boundary) ─────────────────────────────────│
│ • /polisade:implement завершает работу после ОДНОЙ задачи │
│ • Feature-ветка СОХРАНЯЕТСЯ (её удалит merge PR) │
│ • ⛔ ЗАПРЕЩЕНО после этой точки: │
│ — искать следующую TASK / запускать новый цикл │
│ — повторно вызывать /polisade:implement в этой сессии │
│ — git checkout main / git push origin main │
│ — git merge / git branch -D / git push --delete │
│ • Merge выполняет только PM или /polisade:continue │
│ • Легитимные next-steps для PM (одно из): │
│ — merge PR → TASK выйдет из review/активных │
│ — /polisade:continue (PM явно запускает, уже знает про │
│ активные TASK и resume-логику) │
│ • ⛔ НЕ «в новой сессии /polisade:implement TASK-YYY»: │
│ re-invocation guard читает frontmatter + state, а НЕ │
│ сессию — всё равно заблокирует │
└─────────────────────────────────────────────────────────────┘
⛔ Этот протокол ОБЯЗАТЕЛЕН на шаге 2 (REGRESSION TEST) полного цикла.
Таймаут: Используй timeout: 600000 (10 мин) для Bash-вызовов тестов. Для pytest добавляй --timeout=120 если pytest-timeout доступен в проекте.
ЗАПРЕЩЕНО -k и любая фильтрация: Запускай ВСЕ тесты. Никаких -k "not ...", --ignore, --deselect для обхода падающих тестов. Цель — увидеть полную картину.
Сравнение с known failures: Прочитай testing.knownFlakyTests из .state/knowledge.json. Классифицируй каждое падение:
knownFlakyTests) → игнорировать, продолжитьknownFlakyTests) → это регрессия, ИСПРАВИТЬ до PRПроверка типов: Если testing.typeCheckCommand задан в knowledge.json — запустить его. Иначе — пропустить с предупреждением.
Линтинг: Если testing.lintCommand задан в knowledge.json — запустить его.
Обработка таймаута: Если тесты зависли (Bash timeout) — зафиксировать факт зависания в выводе и продолжить к PR. НЕ перезапускать ту же команду. Не блокировать весь цикл из-за зависших тестов.
Обновление knownFlakyTests: Если обнаружены pre-existing падения, которых НЕТ в knownFlakyTests — добавить их в .state/knowledge.json основного репо (не worktree-копии):
{
"test": "test_module::test_name",
"reason": "Краткое описание причины",
"date": "2026-02-16"
}
def compute_expected_branch(task):
"""Правила — в секции 'Git Branching' ниже (source of truth).
parent:PLAN-* → plan/PLAN-XXX-TASK-YYY-<slug>
parent:FEAT-* → feat/FEAT-XXX-<slug>
parent:BUG-* → fix/BUG-XXX-<slug>
parent:DEBT-* → debt/DEBT-XXX-<slug>
parent:CHORE-*→ chore/CHORE-XXX-<slug>"""
...
def assert_expected_branch(expected, worktree_path):
"""[OPS-001 GUARD] Pre-commit guard. Проверяет ветку ВНУТРИ worktree_path,
а не в project_root — в worktree mode корень остаётся на main, это ок."""
if expected is None:
return # gitBranching: false — инвариант отключён
cwd = worktree_path or "."
current = run(f'cd "{cwd}" && git rev-parse --abbrev-ref HEAD').stdout.strip()
if current != expected:
raise RuntimeError(f"OPS-001: cwd={cwd} current={current}, expected={expected}")
def full_task_cycle(task_id):
# 0. Read strategy (см. references/test-authoring-protocol.md)
knowledge = read_json(".state/knowledge.json")
raw_strategy = knowledge.get("testing", {}).get("strategy")
test_cmd = knowledge.get("testing", {}).get("testCommand")
# Нормализация strategy
if raw_strategy is None:
log("⚠️ testing.strategy не задан в knowledge.json. Используем test-along.")
strategy = "test-along"
elif raw_strategy not in ("tdd-first", "test-along"):
log(f"⚠️ Неизвестное значение testing.strategy: '{raw_strategy}'. Fallback на test-along.")
strategy = "test-along"
else:
strategy = raw_strategy
# Guard: tdd-first requires testCommand + task-scoped run
if strategy == "tdd-first" and not test_cmd:
log("⚠️ testing.strategy=tdd-first но testCommand не задан. Fallback на test-along.")
strategy = "test-along"
if strategy == "tdd-first":
task_verification = read_task_verification_section(task_id) # ## Verification из TASK
# 1) парсит ## Verification (первая тестовая команда)
# 2) если нет — derive file-scoped из test_cmd
task_scoped_cmd = resolve_task_scoped_run(task_id, task_verification, test_cmd)
if not task_scoped_cmd:
log("⚠️ Невозможно derive task-scoped run. Fallback на test-along.")
strategy = "test-along"
# 1. Implement (Шаг 1.7 [OPS-001 GUARD])
task = read_task(task_id)
worktree_path = setup_worktree_or_branch(task_id) # worktree or checkout -b
# [OPS-001] Expected-branch invariant: post-setup assertion + env export.
# POLISADE_GIT_BRANCHING — positive mode-signal, ВСЕГДА экспортируется ("true"|"false").
# Bash-guard читает его первым: отсутствие = fail-closed (защита от prompt truncation).
expected_branch = compute_expected_branch(task) if settings.gitBranching else None
assert_expected_branch(expected_branch, worktree_path)
if expected_branch is not None:
export_env("POLISADE_GIT_BRANCHING", "true")
export_env("POLISADE_EXPECTED_BRANCH", expected_branch)
export_env("POLISADE_WORK_DIR", worktree_path or project_root)
else:
export_env("POLISADE_GIT_BRANCHING", "false")
# POLISADE_EXPECTED_BRANCH и POLISADE_WORK_DIR НЕ выставляются — guard видит
# mode=false и pass-through по дизайну.
if strategy == "tdd-first":
# 1a. Red phase
write_tests_from_sources(task_id) # Gherkin → AC → contracts → assumptions
result = run(task_scoped_cmd) # targeted run, NOT full suite
# Классификация причин падения
if result.errors: # syntax error, import error, compilation failure
log("⚠️ Тесты не компилируются/не парсятся. Исправь harness.")
fix_compilation_errors()
result = run(task_scoped_cmd)
if result.all_passed:
log("⚠️ Все тесты прошли сразу (vacuous pass). Проверь что тесты тестируют новое поведение.")
assert result.test_failures > 0, "Tests should fail on assertions (red phase)"
assert result.errors == 0, "No syntax/import/compilation errors in red phase"
# RED CHECKLIST → commit
assert_expected_branch(expected_branch, worktree_path) # [OPS-001]
commit(f"[{task_id}] Add failing tests for {task_id}")
# 1b. Green phase
implement_code(task_id)
result = run(task_scoped_cmd)
assert result.failures == 0, "Tests should pass (green phase)"
# SELF-REVIEW CHECKLIST → commit
assert_expected_branch(expected_branch, worktree_path) # [OPS-001]
commit(f"[{task_id}] Implement {task_id}")
else:
# test-along: текущее поведение
implement_code(task_id) # all ops in worktree_path
run_unit_tests_for_task(task_id)
assert_expected_branch(expected_branch, worktree_path) # [OPS-001]
commit_changes(task_id)
# 2. Regression (см. «Протокол регрессионного тестирования»)
# knowledge и test_cmd уже определены в шаге 0
known_flaky = {t["test"] for t in knowledge.get("testing", {}).get("knownFlakyTests", [])}
if not test_cmd:
log("⚠️ testing.testCommand не задан в knowledge.json — регрессионные тесты пропущены.")
log(" Запусти /polisade:init или /polisade:spec чтобы настроить тестовую команду.")
skip_regression = True
else:
skip_regression = False
if not skip_regression:
# В worktree — всегда cd перед командой
if worktree_path and worktree_path != project_root:
full_cmd = f'cd "{worktree_path}" && {test_cmd}'
else:
full_cmd = test_cmd
result = run(full_cmd, timeout=600_000) # 10 мин таймаут
if not skip_regression:
if result.timed_out:
log("⚠️ Тесты зависли (timeout 10 мин). Продолжаем к PR.")
elif result.failures:
new_failures = [f for f in result.failures if f.test_id not in known_flaky]
known_failures = [f for f in result.failures if f.test_id in known_flaky]
if new_failures:
# Исправить ТОЛЬКО новые падения
while new_failures:
fix_failures(new_failures)
assert_expected_branch(expected_branch, worktree_path) # [OPS-001]
commit_fixes()
result = run(test_cmd, timeout=600_000)
if result.timed_out:
log("⚠️ Тесты зависли при повторном запуске. Продолжаем.")
break
new_failures = [f for f in result.failures if f.test_id not in known_flaky]
# Обнаружены pre-existing падения не в knownFlakyTests — добавить
if known_failures:
update_known_flaky_tests(knowledge, known_failures)
# 2b. Type check
type_cmd = knowledge.get("testing", {}).get("typeCheckCommand")
if type_cmd:
if worktree_path and worktree_path != project_root:
type_cmd = f'cd "{worktree_path}" && {type_cmd}'
run(type_cmd, timeout=600_000)
else:
log("ℹ️ Type check пропущен: typeCheckCommand не задан в knowledge.json. Задайте testing.typeCheckCommand для вашего стека (tsc --noEmit, mypy, pyright, и т.д.).")
# 2c. Lint
lint_cmd = knowledge.get("testing", {}).get("lintCommand")
if lint_cmd:
if worktree_path and worktree_path != project_root:
lint_cmd = f'cd "{worktree_path}" && {lint_cmd}'
run(lint_cmd, timeout=600_000)
# 3. PR (OPS-015: буквальный вызов polisade_vcs.py pr-create — не импровизируй)
assert_expected_branch(expected_branch, worktree_path) # [OPS-001]
push_branch()
# 3a. Собрать тело PR в project-local temp-файл, чтобы не попасть в
# quoting-ад с многострочным --body "...". Путь — относительно pwd
# (worktree root при workspaceMode=worktree, project root при inplace),
# папка .polisade/tmp/ gitignored. /tmp НЕ используется: GigaCode CLI
# sandboxes /tmp через виртуальную FS (~/.gigacode/tmp/<hash>/) и файл,
# записанный одним tool-call'ом, не виден последующему Read/ReadFile
# (issue #57 / legacy OPS-009; см. docs/gigacode-cli-notes.md §4).
run("mkdir -p .polisade/tmp")
PR_BODY_FILE = f".polisade/tmp/pr-body-{TASK_ID}.md"
write(PR_BODY_FILE, f"""\
## Summary
{TASK_TITLE}
{TASK_DESCRIPTION}
## Acceptance
{format_bullets(TASK_ACCEPTANCE)}
## Tests
{TESTS_RUN_SUMMARY}
Ref: tasks/{TASK_ID}-{slug}.md
""")
# 3b. Создать PR. Команда ИДЕНТИЧНА `/polisade:pr create ...` — ровно то,
# что PM запустил бы вручную. Никаких `gh`, `bbs`, `npx codex`,
# `curl` к Bitbucket REST или самостоятельных путей к polisade_vcs.py.
# Собираем bash-команду конкатенацией — `{plugin_root}` лежит в
# plain-string сегменте (без f-строк), чтобы конвертер Qwen/GigaCode
# мог подменить его без конфликтов с Python quoting.
cmd = (
'python3 {plugin_root}/scripts/polisade_vcs.py pr-create '
f'--title "[{TASK_ID}] {TASK_TITLE}" '
f'--body-file "{PR_BODY_FILE}" '
f'--head "{BRANCH}" '
f'--base "{BASE or "main"}" '
'--project-root "${POLISADE_WORK_DIR:-$(pwd)}" '
'--format json'
)
PR_JSON_RC = run(cmd)
# 3c. Failure path: waiting_pm, НЕ blocked (иначе OPS-008 guard §0
# зацикливает при `/polisade:implement <task>` повторно). Сообщение
# обязано содержать "Создайте PR вручную" / "pr_url_request" —
# этот текст ловит early-exit в skills/unblock/SKILL.md.
if PR_JSON_RC.exit_code != 0:
set_status(task_id, "waiting_pm")
update_project_state(task_id, "waitingForPM", reason=(
f"TASK-{task_id}: pr_url_request. Автоматическое создание PR "
f"не удалось (exit={PR_JSON_RC.exit_code}). Ветка '{BRANCH}' "
f"запушена в origin. Создайте PR вручную через web UI и "
f"запустите `/polisade:unblock`, чтобы указать URL. "
f"Для диагностики VCS: /polisade:doctor --vcs"
))
# OPS-010: pre-PR терминальный waiting_pm — PR ещё НЕ создан, суффикса
# `(PR #N)` нет. Бандли set_status + update_project_state в единственный
# `finalize` commit БЕЗ суффикса:
# `[TASK-ID] Finalize status: waiting_pm` (форма без PR-номера).
# diff: только TASK.md frontmatter + PROJECT_STATE.json.
# НЕ пиши lastUpdated. НЕ добавляй код.
return # STOP — никаких git checkout main / branch -D / push --delete
# 3d. Разобрать JSON ответ и зафиксировать pr_url в TASK frontmatter
# (source of truth — та же семантика, что в /polisade:continue Phase C.3).
pr = json.loads(PR_JSON_RC.stdout) # {"url": ..., "number": ..., ...}
write_pr_url_to_task_frontmatter(task_id, pr["url"])
set_status(task_id, "review")
# OPS-010: `set_status(review)` + write_pr_url_to_task_frontmatter —
# НЕ отдельный commit. Если ниже (§3.5) выход через review_mode="blocked"
# или "off" — эта правка идёт в единственный `finalize` commit
# `[TASK-ID] Finalize status: review (PR #{pr.number})` (diff: только
# TASK.md frontmatter + PROJECT_STATE.json; НЕ lastUpdated, НЕ код).
# Если ниже идёт review-loop — бандли в следующий `commit_and_push()`.
# > **Контракт**: эта команда идентична `/polisade:pr create …`. Если
# > автоматический цикл упал — TASK переходит в `waiting_pm` с
# > сообщением "pr_url_request / Создайте PR вручную …";
# > `/polisade:unblock` (без флагов) поймает этот текст в
# > skills/unblock/SKILL.md, попросит PM ввести URL и пропишет
# > `pr_url` в frontmatter TASK. Никогда `gh pr create` / `bbs` /
# > `curl` / `npx @openai/codex` — провайдер определяется из
# > `.state/PROJECT_STATE.json → settings.vcsProvider`, единственная
# > точка вызова — `scripts/polisade_vcs.py pr-create`.
# 3.5. Pre-check: reviewer CLI via OPS-011 helper (single source of truth)
caps = json.loads(run("python3 {plugin_root}/scripts/polisade_cli_caps.py detect").stdout)
review_mode = caps["reviewer"]["mode"] # "codex" | "self" | "blocked" | "off" (OPS-017)
reason = caps["reviewer"].get("reason")
# OPS-007 / issue #55: warn when the helper ignored a codex binary that
# failed identity verification, so the impersonator is visible in logs
# rather than resulting in a silent fallback.
warning = caps["reviewer"].get("warning")
if warning:
print(f"⚠ {warning}")
if review_mode == "blocked":
# OPS-017: reason может указывать на settings-конфликт или отсутствие CLI;
# печатаем его дословно и разветвляем подсказки.
print("═══════════════════════════════════════════")
print("REVIEWER BLOCKED")
print("═══════════════════════════════════════════")
print(f"Reason: {reason or 'no reviewer CLI available'}")
print("")
if reason and "settings" in reason:
print("Проверьте settings.reviewer.mode и settings.reviewer.cli")
print("в .state/PROJECT_STATE.json — текущее значение конфликтует")
print("с доступными CLI в окружении.")
else:
print("Quality review требует CLI ревьюера.")
print("")
print("Варианты:")
print(" • Codex CLI: npm install -g @openai/codex")
print(" • Claude Code: https://docs.anthropic.com/claude-code")
print(" • Qwen CLI: документация Qwen")
print("")
print("TASK остаётся в статусе: review")
print("PR создан, но НЕ замержен.")
print("═══════════════════════════════════════════")
return f"BLOCKED: {reason or 'No reviewer CLI found'}"
if review_mode == "off":
# OPS-017: reviewer отключён в settings. TASK уже в review с PR_URL;
# STOP — PM делает ревью руками и выполняет merge (/polisade:pr merge <id>).
print(f"Reviewer disabled in settings.reviewer.mode. "
f"TASK status=review, PR={pr.url}. "
f"PM manually reviews and merges via /polisade:pr merge <id>.")
return "OFF: reviewer disabled, handed off to PM"
# 4. Quality review (Independent)
# review_mode == "codex" → /polisade:review-pr {PR}
# review_mode == "self" → /polisade:review-pr {PR} self
iterations = 0
while iterations < 2:
review = run_review(pr, task_id, review_mode)
iterations += 1
if review.score >= 8: # PASS
# НЕ мержим автоматически! Merge — ответственность PM
set_status(task_id, "review") # PR готов к merge
# OPS-010: PASS-путь идёт в терминальный STOP без следующего
# коммита. Эту правку (и любую совпадающую движуху в
# PROJECT_STATE.json) бандли в единственный `finalize` commit
# `[TASK-ID] Finalize status: review (PR #{pr.number})`.
# diff ТОЛЬКО frontmatter TASK.md + PROJECT_STATE.json task-bucket.
# НЕ пиши lastUpdated. НЕ добавляй код/скрипты в этот коммит.
break
else: # IMPROVE
run_improvement(pr, review.recommendations)
run_all_tests()
assert_expected_branch(expected_branch, worktree_path) # [OPS-001]
# OPS-028: commit_and_push() =
# git commit ... && python3 {plugin_root}/scripts/polisade_vcs.py git-push \
# --branch <expected_branch> --project-root "$POLISADE_WORK_DIR"
# На exit=2 (push verification failed, remote: fatal/ERROR/rejected) →
# set_status(task_id, "waiting_pm")
# update_project_state(task_id, "waitingForPM",
# reason=f"Push failed: {json['reason']}",
# remote_lines=json['remote_lines'])
# break # НЕ продолжаем итерацию, НЕ ставим done/review
commit_and_push()
else:
# Max iterations — STOP, ждём PM
set_status(task_id, "waiting_pm")
update_project_state(task_id, "waitingForPM",
reason=f"Review ({review_mode}): score {review.score}/10 after 2 iterations")
# OPS-010: терминальный waiting_pm после max-iterations — без следующего
# коммита. Бандли set_status + update_project_state в единственный
# `finalize` commit `[TASK-ID] Finalize status: waiting_pm (PR #{pr.number})`.
# diff: только TASK.md frontmatter + PROJECT_STATE.json (task-bucket +
# waitingForPM reason). НЕ пиши lastUpdated. НЕ добавляй код/скрипты.
# 5. STOP - /polisade:implement завершает работу после одной задачи
# ⛔ ПОСЛЕ ЭТОЙ ТОЧКИ АГЕНТ НЕ ДЕЛАЕТ НИЧЕГО САМ:
# — НЕ ищет следующую TASK
# — НЕ запускает новый цикл full_task_cycle
# — НЕ вызывает /polisade:implement повторно
# — НЕ выполняет git checkout main / push main / merge / branch -D / push --delete
# Управление возвращается PM. Точка.
# См. секцию "⛔ ЗАПРЕЩЁННЫЕ git-команды в /polisade:implement" выше.
print(f"""
═══════════════════════════════════════════
/polisade:implement ЗАВЕРШЁН
═══════════════════════════════════════════
TASK: {task_id} → status=review
PR: {pr_url_or_manual_instruction}
Feature branch: {branch_name} (СОХРАНЕНА — НЕ удалять!)
Дальнейшие действия — ответственность PM (одно из):
• Manual review PR → merge (с флагом --delete-branch).
После merge TASK выйдет из активных → разблокируется /polisade:implement.
• /polisade:continue — PM явно запускает; команда умеет работать
с активными TASK (resume-логика).
НЕ агент сам, НЕ повторный /polisade:implement — guard заблокирует в любой сессии.
⛔ АГЕНТ БОЛЬШЕ НЕ ДЕЙСТВУЕТ в этой сессии:
— не переходит к следующей TASK «самостоятельно»
— не «готовит main к следующей задаче»
— не делает никаких git-операций
═══════════════════════════════════════════
""")
STOP # вернуть управление PM
return "TASK reviewed. PR ready for merge by PM."
⛔ ВАЖНО: /polisade:implement ВСЕГДА останавливается после завершения ОДНОЙ задачи!
Завершение цикла:
waiting_pm → STOP, вывести вопросblocked → STOP, вывести причинуНЕ прерывайся внутри цикла для:
Для автономной работы над несколькими задачами используй /polisade:continue
Проверь settings.gitBranching и settings.workspaceMode в PROJECT_STATE.json.
⚠️ Эта секция — source of truth для compute_expected_branch(TASK), которую
использует Шаг 1.7 [OPS-001 GUARD] и pre-commit guard во всех commit paths
(prompt субагента, S-task direct path, псевдокод full_task_cycle). Изменение
правил branch naming здесь должно сопровождаться обновлением assertion логики
в Шаге 1.7.
Для TASK от FEAT/BUG/DEBT/CHORE (стандартный режим):
feat/FEAT-XXX-slug, fix/BUG-XXX-slug, debt/DEBT-XXX-slug, chore/CHORE-XXX-slugДля TASK от PLAN (режим плана):
plan/PLAN-XXX-TASK-YYY-slugЛогика определения режима:
parent из TASK файлаPLAN- → режим плана (ветка per TASK)⚠️ Алгоритм создания ветки и worktree вынесен в Шаг 1.7 [OPS-001 GUARD]
(строки ~270–395). Эта секция оставлена как reference для branch naming rules
выше — не дублировать здесь алгоритм создания.
Краткая сводка (полный алгоритм с assertion'ами — в Шаге 1.7):
workspaceMode: "worktree" (по умолчанию) → git worktree add .worktrees/<dir> -b <branch>,
симлинк .venv/node_modules/vendor, все операции внутри worktree_path.workspaceMode: "inplace" (legacy) → git checkout -b <branch> в project_root.cd "$WORK_DIR" && git rev-parse --abbrev-ref HEAD == <branch>.git worktree add не проходит → откат на git checkout -b с предупреждением (в обоих случаях assertion и export POLISADE_EXPECTED_BRANCH/POLISADE_WORK_DIR обязательны).Инвариант expected-branch отключён, но основной агент ОБЯЗАТЕЛЬНО экспортирует явный positive signal:
export POLISADE_GIT_BRANCHING="false"
POLISADE_EXPECTED_BRANCH и POLISADE_WORK_DIR не экспортируются. Pre-commit guard
видит MODE=false → pass-through с info-сообщением. Добавь в prompt субагента:
"Коммить прямо в текущую ветку; POLISADE_GIT_BRANCHING=false экспортируй первой
bash-командой."
⛔ Важно: отсутствие POLISADE_GIT_BRANCHING (вообще не выставлен) bash-guard
трактует как bug — fail-closed. Это защита от truncation/dropout в prompt
(OPS-001 amplification сценарий на слабых моделях). Только явный
POLISADE_GIT_BRANCHING=false отключает guard.