# Параллельные сессии: координация — дизайн

**Дата:** 2026-05-18
**Автор:** Claude (Opus 4.7, brainstorming с Дмитрием, экономия 5%)
**Статус:** черновик → ревью заказчика → writing-plans
**Связано:** Pravila §12 / §14 (hard rules), memory `feedback_subagent_git_reliability.md`, Sprint 6 инцидент.

---

## 0. Контекст и проблема

При параллельной работе нескольких Claude-сессий на одном репозитории — что является ежедневной нормой проекта (на 18.05.2026 в `git worktree list` 16 активных worktrees) — наблюдаются два класса инцидентов:

**Класс A — субагенты теряются между worktree.**
Запущенный из контроллера субагент (особенно Haiku) путает рабочую директорию или ветку: коммитит в чужую ветку, делает orphan-коммит, не возвращает git-state в отчёте. Прецедент — Sprint 6: Haiku-субагенты угнали ветку параллельной сессии, инцидент устранён через `git reflog` + `reset`. Корневая причина — контроллер не верифицирует HEAD/branch после Task-инвокации.

**Класс B — нормативка/MEMORY дрейфует.**
Параллельные сессии правят один и тот же набор файлов (Pravila / CLAUDE.md / Tooling / PSR_v1 / MEMORY.md / `Открытые_вопросы_*.md`) и претендуют на один version-номер. Прецедент — 17.05.2026: ADR-006 D4 и C10 одновременно претендовали на Tooling v2.11, C10 пришлось переномеровать в v2.12 (видно в шапке CLAUDE.md). За неделю 18.05.2026 разделы A11/A3/A4/A6/C9/C10/discovery-interview — все 7 — трогали один набор из 4 нормативных файлов.

Обе проблемы — следствие одного: **параллельность без явного контракта на scope**.

## 1. Архитектура

Решение из 4 артефактов, без новых плагинов / MCP-серверов:

1. **Pravila §15** (новое hard-rule) — два пункта: §15.1 (субагенты + git), §15.2 (нормативка + pre-flight sync). Hard-rule = §9 «Отступления» не применяется (как §12 и §14).
2. **`docs/sessions/CURRENT.md`** — заявочный лог активных параллельных сессий.
3. **`.claude/hooks/subagent-prompt-prefix.mjs`** — PreToolUse-хук, инжектящий в каждый `Task`-prompt заголовок git-safety.
4. **Verify-протокол** в скиле `superpowers:subagent-driven-development` — pre-spawn + post-subagent чеклист контроллера.

Зависимости: артефакт 1 (Pravila §15) — корень всех правил; артефакты 2–4 ссылаются на §15.1 / §15.2 как enforcement floor.

## 2. Pravila §15 — текст обоих пунктов

### §15.1 Субагенты + git

> Git-коммит-задачи субагенту (любой `Task`-инвокейшн, чей prompt содержит `git commit`, `git push`, `git stage`, `git checkout`, `git switch`, `git merge`, `git rebase`, либо где явно ожидается коммит в результате) — **только модель Sonnet или Opus**, никогда Haiku. Контроллер, делегирующий git-операцию Haiku-субагенту — нарушение §15.1, фиксируется в feedback того же уровня, что §12.
>
> Исключение — read-only git-операции (`git log`, `git status`, `git diff`, `git rev-parse`, `git branch --show-current`, `git worktree list`) — разрешены любой модели.

### §15.2 Нормативные правки + pre-flight sync

> Любая правка файлов из списка «нормативка» (см. ниже) выполняется **только** на актуальной базе `origin/main`. Pre-flight обязателен: `git fetch origin && git log HEAD..origin/main --oneline` — если есть untracked commits на origin/main, ребейз/merge **до начала правки**, не после.
>
> Параллельная нормативная правка на устаревшей базе — нарушение §15.2. Признак нарушения: коммит правит файл, чья последняя версия на `origin/main` новее, чем версия в parent коммите правки.
>
> **Список «нормативка» — 8 позиций:**
>
> 1. `docs/Pravila_raboty_Claude_v1_1.md`
> 2. `CLAUDE.md`
> 3. `docs/Tooling_v8_3.md`
> 4. `docs/Plugin_stack_rules_v1.md`
> 5. `memory/MEMORY.md` (и все `memory/*.md`)
> 6. `docs/Открытые_вопросы_v8_3.md`
> 7. `docs/adr/*.md` (glob — collision на ADR-NNN номере = тот же класс, что version-bump нормативки)
> 8. `db/schema.sql` (параллельные миграции из разных сессий = реальный риск; запись в `db/CHANGELOG_schema.md` сама не защищает от version-base дрейфа)
>
> Расширение списка — отдельная правка §15.2, не «по ощущениям».

### §15.3 Cross-refs в других файлах

- **CLAUDE.md §1 priority chain** — упомянуть §15 рядом с §12 и §14 как hard-rules (footer-абзац после цепочки).
- **PSR_v1** — не правится (§15 не про координацию плагинов, а про координацию сессий).
- **Tooling** — не правится.

## 3. `docs/sessions/CURRENT.md` — заявочный лог

### 3.1 Формат

Один markdown-файл, append-only, парсимый. Запись на каждую активную параллельную сессию:

```markdown
## session: 2026-05-18-discovery-interview
- branch: worktree-discovery-interview
- worktree: c:/моя/проекты/портал crm/Документация-discovery
- started: 2026-05-18T14:30+03:00
- scope-files:
  - docs/Tooling_v8_3.md §4.30
  - docs/Pravila_raboty_Claude_v1_1.md §13.2
  - docs/Plugin_stack_rules_v1.md R10.1
  - CLAUDE.md §3.3 #55
  - docs/adr/009-*.md (new)
- version-claims:
  - Tooling: 2.12 → 2.13
  - Pravila: 1.25 → 1.26
  - PSR_v1: 3.11 → 3.12
  - CLAUDE.md: 2.12 → 2.13
- status: in-progress
- closes: docs/superpowers/plans/2026-05-18-discovery-interview-integration.md
```

### 3.2 Жизненный цикл записи

- **claim** — на старте сессии (до первой нормативной правки) контроллер добавляет запись + атомарный коммит `sessions: claim <topic>`. Отдельный коммит от plan-документа.
- **check** — pre-flight перед своей правкой: `Read docs/sessions/CURRENT.md` + scan записей со `status: in-progress`. Конфликт = пересечение `scope-files` ИЛИ `version-claims` с активной соседней сессией.
- **release** — на закрытии (push на main) контроллер меняет `status: closed-<commit-sha>`. Append-only-вариант (closed-записи остаются). Closed-записи переезжают в `docs/sessions/archive/YYYY-MM.md` через ≥30 дней после закрытия (ручная архивация раз в месяц, не cron).

### 3.3 Конфликт-резолюция

1. **Файловое пересечение** (две сессии правят один и тот же файл, не обязательно одну секцию) — **soft-warn**, продолжать можно, но pre-flight fetch перед каждым коммитом обязателен (§15.2).
2. **Section-пересечение** (одна и та же секция §X.Y) — **hard-stop**, координация с заказчиком до начала правки.
3. **Version-claim collision** (две сессии заявили один номер) — **hard-stop**, кто claim'нул раньше (по timestamp в CURRENT.md) — тот его и берёт; вторая сессия сдвигается на +1.

### 3.4 Что НЕ включаем (YAGNI)

- TTL / auto-expire записей — добавим, если файл реально засорится.
- Machine-readable JSON-формат — markdown достаточен, парсится `grep`/`sed`.
- Интеграция с GitHub Issues — отдельная тема, не блокер.
- Pre-commit hook на проверку CURRENT.md — добавим только если будут зафиксированы инциденты игнорирования.

### 3.5 Pre-flight snippet (для plan-документов и скила)

```bash
# Перед нормативной правкой:
git fetch origin && git log HEAD..origin/main --oneline
grep -A 20 'status: in-progress' docs/sessions/CURRENT.md
```

## 4. Хук `.claude/hooks/subagent-prompt-prefix.mjs`

### 4.1 Тип и matcher

Тип хука — **PreToolUse**, matcher `Task`. Перехватывает `tool_input.prompt`, дописывает в начало обязательный заголовок git-safety, возвращает модифицированный prompt через стандартный JSON-output API хук-системы Claude Code:

```json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "git-safety header injected (Pravila §15.1)",
    "updatedInput": { "prompt": "<header>\n\n<original prompt>" }
  }
}
```

Точный формат API подтвердить против реально работающих `tools/ruflo-queen-hook.mjs` / `tools/ruflo-recall-hook.mjs` на этапе имплементации.

### 4.2 Тело заголовка

```text
=== SUBAGENT GIT-SAFETY HEADER (Pravila §15.1, auto-injected) ===
Working directory (cwd): <absolute path>
Branch (git branch --show-current): <branch-name>
Parent commit (git rev-parse HEAD): <sha>
Worktree root (git rev-parse --show-toplevel): <path>

ОБЯЗАТЕЛЬНЫЕ ПРАВИЛА:
1. Все git-операции выполняй ТОЛЬКО внутри cwd выше. Если pwd ≠ cwd — STOP, верни ошибку.
2. ЗАПРЕЩЕНО: `git checkout <branch>`, `git switch <branch>`, `git checkout -b`, `git branch -m`, `git worktree add/remove`, `git push --force`, `git reset --hard`.
3. После КАЖДОГО `git commit` — выполни `git rev-parse HEAD` и `git branch --show-current`, выпиши результат в ответ. Это обязательная часть отчёта.
4. Если обнаружил, что находишься не в той ветке/worktree — STOP, не правь ничего, верни ошибку с raw output `git status` и `git branch --show-current`.
5. Если задача не требует git-операций (только Edit/Read/Grep) — этот блок информационный, follow rule 1.

=== END SUBAGENT GIT-SAFETY HEADER ===
<здесь — оригинальный prompt контроллера>
```

### 4.3 Резолюция переменных

Хук резолвит 4 значения сам в момент инжекта — не из контроллера. Резолв через стандартный Node child-process API c fixed-string `git`-командами (без подстановки user-input в shell — все аргументы статичны: `git rev-parse HEAD`, `git rev-parse --show-toplevel`, `git branch --show-current`, `pwd`/`process.cwd()`). Контроллер мог быть запущен в одной директории, но Task инвокируется по факту в ней же — хук берёт `process.cwd()` рантайма.

### 4.4 Какие модели затрагиваются

Заголовок инжектится для **всех** Task-инвокаций (универсально). §15.1 (Sonnet-only для git) — **не в хук**, а в Pravila + чеклист скила (Секция 5). Хук не знает, является ли задача git-задачей: анализ prompt'а — ненадёжный signal, пусть это решает контроллер.

### 4.5 Edge-cases

1. **Task без git** (только read/edit) — заголовок присутствует, безвреден (rule 5 нейтрализует).
2. **Контроллер уже включил похожий заголовок** — двойная защита, не проблема.
3. **Хук упал** (например, `git` не в PATH) — возвращает `{"continue": true}` без модификации prompt'а + логирует stderr. Не блокирующий: лучше Task без заголовка, чем сломанный Task.
4. **Несколько worktree в разных терминалах** — каждый запуск Claude Code имеет свой cwd, хук резолвит локально.

### 4.6 Объём и latency

~60 строк JS, паттерн один-в-один с `tools/ruflo-queen-hook.mjs` / `tools/ruflo-recall-hook.mjs`. Latency ~5–10 мс на Task (3 локальных `git`-вызова).

## 5. Verify-протокол в скиле `subagent-driven-development`

### 5.1 Где живёт

Добавочный чеклист-блок в скиле `superpowers:subagent-driven-development`:

- если скил вендорится в `.claude/skills/` — патчится напрямую;
- если только marketplace-скил без локальной копии — добавляем `references/git-safety-checklist.md` рядом + одну строку «обязательный verify-блок после каждого субагента» в проектную обёртку. Фактический способ решается при имплементации (см. §7 ограничения).

### 5.2 Pre-spawn чеклист (до Task-инвокации)

1. Резолвить и записать: cwd, branch, parent SHA, worktree root — те же 4 значения, что инжектит хук. Контроллер хранит их у себя для post-check.
2. Если задача git-коммит → выбрать model `sonnet` или `opus`. **Никогда** `haiku` (§15.1).
3. Если задача правит нормативку из списка §15.2 → pre-flight `git fetch && git log HEAD..origin/main --oneline`. Не пусто → ребейз до инвокации, не после.

### 5.3 Post-subagent чеклист (сразу после возврата)

1. `git rev-parse HEAD` — сравнить с pre-spawn parent SHA. Ожидание: либо равно (субагент не коммитил), либо отличается **ровно одним коммитом** на pre-spawn HEAD.
2. `git branch --show-current` — сравнить с pre-spawn branch. Не равно → **STOP**, разбор инцидента (Sprint 6 паттерн).
3. `git log -1 --format='%s%n%P'` — проверить subject (соответствует задаче?) + parent (родитель = pre-spawn HEAD?).
4. Если субагент коммитил несколько коммитов — не запрещено, но требует ручной проверки subject'ов.

### 5.4 Red-flag-список (любой = hard-stop разбор)

- branch ≠ ожидаемая;
- parent коммита ≠ pre-spawn HEAD (= висячий коммит / попадание на чужую ветку);
- HEAD двинулся, но субагент в отчёте об этом не упомянул;
- в diff'е есть файлы вне scope задачи.

### 5.5 Формат отчёта субагента

Субагент обязан выписать в конце ответа блок:

```
=== GIT REPORT ===
cwd: <pwd>
branch: <git branch --show-current>
HEAD: <git rev-parse HEAD>
HEAD^: <git rev-parse HEAD^>
status: <git status --short>
=== END GIT REPORT ===
```

Отсутствие блока = контроллер считает результат недостоверным и запускает 5.3-чеклист сам через Bash.

### 5.6 Соотношение с code-review

Двухстадийное review сохраняется (Pravila §4.5 / R10 PSR_v1): сильная модель делает code-review артефакта субагента. Git-safety-чеклист **не заменяет** code-review, он стоит **до** него (нет смысла ревьюить diff, если он не в той ветке).

## 6. Тесты, migration, контракт записи

### 6.1 Тесты самих защит

| Защита | Тест | Ожидание |
|---|---|---|
| Хук (inject) | `echo '{"tool_input":{"prompt":"X"}}' \| node .claude/hooks/subagent-prompt-prefix.mjs` | `updatedInput.prompt` начинается с `=== SUBAGENT GIT-SAFETY HEADER ===`; cwd/branch/HEAD — реальные |
| Хук — failure-mode | временно сломать (`mv .git → .git.bak`) → запустить хук | `{"continue":true}`, без модификации; Task не должен крашиться |
| Verify-протокол (5.3) | Task субагенту с prompt `"do nothing, run git status, return"` | в ответе субагента — блок `=== GIT REPORT ===`; контроллер сверяет HEAD до/после — должны быть равны |
| §15.1 enforcement | Ручной dry-run: попытаться делегировать `git commit` Haiku-субагенту | согласно чеклисту контроллер отказывается и перенаправляет Sonnet'у |
| §15.2 pre-flight | Симулировать diverged state: `git fetch` + сделать divergence → попытаться правнуть Pravila | pre-flight snippet ловит, требует rebase |
| CURRENT.md claim collision | Две параллельные сессии: добавить запись с тем же version-claim | вторая сессия должна вручную увидеть конфликт через grep `version-claims` |

Никакой автоматизации сверх этого (no pre-commit hook, no CI check) — YAGNI. Если CURRENT.md начнёт игнорироваться — добавим pre-commit grep отдельной правкой.

### 6.2 Migration существующих параллельных worktrees

На момент 18.05.2026 — 16 активных worktree (`git worktree list`). Шаги:

1. После merge §15 + хука + CURRENT.md на `main` — каждая активная worktree-сессия делает `git fetch` + ребейз на свежий `main` (получает §15 + хук).
2. Каждая активная сессия дописывает свою запись в CURRENT.md одним коммитом (`sessions: backfill claim <topic>`) — не блокирует работу, retro-claim.
3. Существующие version-collision'ы (A11 / C9 / A4 / discovery — последние 2 недели) **не пересматриваем**; §15 действует **с момента введения**, не ретроактивно.

### 6.3 Что НЕ делаем сейчас (явный YAGNI)

- Реестр зарезервированных версий — пропущен;
- pre-commit hook на CURRENT.md — пропущен;
- машинно-читаемый JSON-формат CURRENT.md — пропущен;
- запрет параллельных сессий — нет, параллельность — норма проекта;
- изменения PSR_v1 / Tooling — не требуются, §15 — внутри Pravila;
- subagent-без-git (вариант C из брейншторма) — отложен до повторного инцидента после A+B.

## 7. Ограничения / что не верифицировано в дизайне

- **Не верифицировал** через grep, что в Pravila нет пересекающихся правил под номером §15 — проверено в формате headings `## 13. ... / ## 14. ...`, §15 свободен. Подтверждено на этапе brainstorming.
- **Не верифицировал** API хук-системы Claude Code на актуальной версии CLI — формат `updatedInput` в PreToolUse для Task tool требует подтверждения против реально работающих `tools/ruflo-queen-hook.mjs` / `tools/ruflo-recall-hook.mjs` перед написанием хука. Задача имплементации, не дизайна.
- **Не верифицировал**, что `subagent-driven-development` скил вендорится в `.claude/skills/`. Может быть только маркетплейс-скил без локальной копии — фактический способ патчинга решится при имплементации (Секция 5.1).
- **Не верифицировал** Windows-специфику резолва `git`-команд через Node child-process в хуке — на Windows работало для `ruflo-queen-hook.mjs`, должно работать и здесь.

## 8. Acceptance criteria (для writing-plans)

Дизайн считается реализованным, когда:

1. Pravila §15 (15.1 + 15.2) добавлен, версия Pravila bumped, CHANGELOG entry есть.
2. `docs/sessions/CURRENT.md` создан, формат описан в README рядом, в нём есть retro-claim запись для всех активных worktrees на момент имплементации (Migration §6.2; на дату дизайна — 16 штук).
3. `.claude/hooks/subagent-prompt-prefix.mjs` написан, зарегистрирован в `.claude/settings.json`, прошёл тесты 6.1 (рукопись + failure-mode + e2e Task).
4. Скил `subagent-driven-development` патчен (5.1) — verify-протокол доступен контроллеру.
5. CLAUDE.md §1 cross-ref на §15 добавлен.
6. Pre-commit gitleaks + Pest/Vitest GREEN; gitleaks-full-history + lychee — перед push.
7. Регрессия Pest --parallel / Vitest — 0 регрессий vs baseline.
