Скилл = советник, не риск-аудитор. Показывает что юзер не видит → предлагает варианты → юзер выбирает → агент потом исполняет. Четыре фазы: pre-flight (синхронизация контекста), silent scan (3 параллельных помощника находят 5–8 дыр), live диалог (краткие решения по каждой дыре, расширение топа), запись (<plan-name>.md + история в history/).
Цель — лёгкая 10–15-минутная сессия, не тяжёлый аудит.
-
Прочитать контекст. Просканировать текущий разговор и доступные файлы (Read / Glob). Извлечь то что уже сказано — не спрашивать заново.
-
Найти пробелы. Сравнить с пятью минимумами. Записать что есть, что нет.
-
Задать максимум 2 вопроса за раз. Если не хватает 4 элементов — спросить 2, получить ответ, спросить ещё 2. Не делать «анкету» из 5 вопросов сразу.
-
Reference class — отдельный вопрос. «На что это похоже из уже сделанных проектов? Назови 1–2 примера — твоих или известных тебе». Если юзер говорит «не знаю» — двигаться дальше с пустым reference_class, помощники получат сигнал.
-
Горизонт оценки — выбор из меню.
Через сколько оценим что план провалился?
А) через 30 дней
Б) через 3 месяца
В) через 12 месяцев
Г) после первого пилота
Д) после запуска
Е) после привлечения первых N клиентов
Ж) иное (напиши своё)
-
Неясности → assumption. Если после 2 раундов вопросов что-то остаётся туманным — пометить как assumption и передать в Phase 2 в составе угла «Допущения». Не блокировать сессию.
-
Resolve plan-name. Спросить или вывести из контекста. Прогнать через CLI:
uv run scripts/cli.py slugify "<plan name from user>"
Запомнить slug — он будет именем файла.
-
Существует ли файл? Запустить:
uv run scripts/cli.py find docs/premortem --name "<plan_name>"
Если файл уже есть — это повторный запуск, перейти к секции «Rerun logic» ниже. Если нет — продолжать новый сценарий.
-
Классифицировать тип плана и выбрать углы. Запустить:
uv run scripts/cli.py classify "<plan_text>"
Получить JSON: {profile, angles, confidence, matched_keywords}.
Если confidence < 0.4 — fallback: задать одному LLM-помощнику вопрос «какие 6 углов лучше всего видят дыры этого плана» (одной строкой, без объяснений). Использовать его ответ.
Юзеру показать только список выбранных углов одной строкой:
Смотрю с углов: Клиент, Исполнитель, Противник, Допущения, Конкурент, Будущий поддерживающий.
-
Установить premortem-фрейм.
«Представим что к [горизонту] этот план провалился. Не "может провалиться" — провалился, факт. Что произошло?»
(См. references/frame-language.md.)
-
Запустить 3 параллельных помощника одним сообщением.
Каждый получает свой промпт из references/helper-prompt-template.md плюс назначенные углы (по 2 угла на помощника). Распределение: 6 углов на 3 помощника = (2,2,2). Один помощник получает «Будущий поддерживающий» с обязательным WYSIATI-микрошагом.
Модель: Opus 4.7 по умолчанию. Если юзер запустил скилл с флагом --model sonnet — Sonnet. Если флага нет, скилл может предложить выбор перед stage 2 одной строкой:
«Запускаю на Opus ($1–2). Sonnet будет дешевле ($0.50–1) — переключить?»
Параллельно: ВСЕ 3 в ОДНОМ сообщении с тремя Task tool calls.
-
Получить 6 сырых ответов (3 × до 2). Прогнать каждый через валидатор:
uv run scripts/cli.py validate-helper < helper_response.json
Retry policy. Максимум 3 attempts на помощника. Классификация ошибок:
| Тип ошибки | Retryable? | Сообщение для retry |
|---|
| Невалидный JSON / extra prose | да | «Верни ТОЛЬКО JSON, без преамбулы» |
| Schema fail (отсутствует поле) | да | «Не хватает поля X в дыре N» |
| maxItems >2 | да | «Максимум 2 дыры; верни лучшие 2» |
| Generic risks без привязки к плану | да | «Дыры слишком общие; привяжи к деталям плана» |
Empty holes ({"holes": []}) | НЕТ | принять как валидный пустой ответ |
| Low confidence на всех дырах | НЕТ | принять как есть, пометить assumption |
Если после 3 attempts всё ещё ошибка — отбросить ответ помощника, продолжить с тем что есть. Не чинить вручную.
-
Hash-дедуп. Прогнать все валидные дыры через:
uv run scripts/cli.py dedup < all_holes.json
Убирает явные дубли по нормализованному описанию (sha256). Оставшиеся дубли (одна дыра, разные слова) обработает шаг 6.
-
Семантический дедуп (LLM clustering). Если после hash-дедупа осталось >8 дыр — попросить LLM сгруппировать близкие в кластеры. Промпт:
Вот N сырых дыр от трёх помощников. Какие из них на самом деле ОДНА И ТА ЖЕ дыра,
видимая с разных углов? Верни JSON:
[
{
"merged": [индексы дыр объединённые в одну],
"title": "новое объединённое название",
"merged_from_angles": ["Клиент", "Допущения"], // углы-источники
"merged_descriptions": ["исходное описание 1", "исходное описание 2"]
},
...
]
ПРАВИЛА:
- Не теряй нюансы: если две дыры близки но имеют разный угол атаки — оставь обе.
- Если все 8+ — действительно разные дыры, верни пустой массив (ничего не объединять).
- Объединение допустимо только если ≥80% совпадение по сути.
Применить кластеры: для каждой объединённой дыры в Hole.merged_from записать ID исходных дыр (после назначения ID на шаге 8), в Hole.углы — все исходные углы, в описание — выбрать самое богатое исходное описание.
-
Лимит на 8. Если после семантического дедупа всё ещё >8 — ранжировать по важность × уверенность × novelty (где novelty = 1 для новых, 0.5 для похожих на ранее найденные при rerun) и взять топ-8. Не отбрасывать assumption-дыры приоритетно — они часто и есть самое ценное.
-
Назначить ID. Каждой уникальной дыре дать ID через:
uv run scripts/cli.py next-id --existing "H-001,H-002"
На первом запуске стартуем с H-001. Сохранить порядок: первая дыра → H-001, вторая → H-002, и т.д.
-
Сохранить промежуточный snapshot in-memory (объект Plan). На диск пока не пишем — это произойдёт в Phase 4.
-
Проверка: есть ли принятые дыры?
Если 0 принятых дыр (все ОТЛОЖЕНО / ОТВЕРГНУТО) — спросить юзера явно:
«Ты не принял ни одного решения. Возможны варианты:
А) сохранить как обзор без действий (юзер увидел дыры, решил пока ничего не делать)
Б) вернуться к 1–2 дырам и принять решения
В) пересмотреть — может я плохо предложил варианты
Что делаем?»
Если А — переходим к шагу 5 (session_recommendation), пропускаем топ и bias.
Если Б — возвращаемся к Phase 3 per-hole loop для выбранных юзером дыр.
Если В — перегенерируем варианты решений для названных дыр.
-
Топ 1–3. Скилл сам отбирает из ПРИНЯТЫХ по важность × обратимость × уверенность. Если ПРИНЯТЫХ < 3 — берём все.
-
Авто-расширение до полного режима. Для каждой дыры из топ 1–3 ИЛИ для любой ПРИНЯТОЙ дыры с (важность=высокая И уверенность ≠ предположение) — задать недостающие поля одним проходом:
- 2–3 первых шага (prevent)
- сигнал успеха (detect)
- стоп-условие
- что делать если уже случилось (limit damage)
- открытые вопросы
Пометить эти дыры режим: полный.
-
Bias-проверка топа (1 вопрос юзеру).
Скилл сам прогоняет топ через 6 биасов в системном промпте (см. references/bias-checklist.md), выбирает наиболее вероятный риск искажения и показывает юзеру:
«Какой биас мог сильнее всего исказить твой топ?
Я думаю — <биас>. Коррекция: <конкретное действие>.
Согласен или другой?»
Юзер подтверждает или меняет. Записывается одна строка в bias_проверка.
-
Session recommendation — явный шаг.
Скилл предлагает рекомендацию по сессии (один из 4 enum-значений) на основе паттерна принятых решений:
continue — большинство решений идут вперёд, нет дыр со статусом ОТЛОЖЕНО среди топа
reduce_stake — топ содержит решения вида «пилот / меньше / медленнее»
delay — есть ОТЛОЖЕНО в топе или явно сказано «доделать позиционирование / подождать»
abort — все или большинство критических дыр ОТВЕРГНУТЫ, или решения = «отказаться»
Скилл показывает свою предварительную рекомендацию + причину, юзер подтверждает или меняет:
«Моя рекомендация по сессии: delay — есть отложенные дыры в топе.
Ты подтверждаешь эту общую рекомендацию или предпочитаешь: continue / reduce_stake / abort?»
Записывается в Plan.session_recommendation.
-
Reverse премортем (условный). Запускается только если session_recommendation in {reduce_stake, delay, abort}. Если continue — пропускается.
«Прошёл [горизонт]. Не сделали (или сделали меньше). Оказалось зря — почему?»
LLM генерирует 3 сильные причины + одну строку «что перевешивает». Записывается в reverse_премортем.
Триггерится в Phase 1 шаг 8: файл уже существует.
-
Прочитать существующий файл. Скрипт plan_io.py read.
-
Показать сводку. Запустить:
uv run scripts/cli.py stats docs/premortem/<slug>.md
Юзеру:
Нашёл прошлый премортем по «<plan_name>» от <дата>.
<всего> дыр: <принято> ПРИНЯТО, <в_работе> В РАБОТЕ, <отложено> ОТЛОЖЕНО, <закрыто> ЗАКРЫТА.
Что хочешь сделать?
А) обновить статусы существующих дыр
Б) найти новые дыры
В) и то и другое
Г) начать заново под новый этап проекта
-
А или В: обновить статусы. Пройти по всем активным дырам (не ОТВЕРГНУТО / не ЗАКРЫТА), спросить «Что с H-NNN: ? Не изменилось / в работе / закрыто (опиши как) / отвергнуто (причина)». Обновить статус и история_статуса.
-
Б или В: новые дыры. Запустить Phase 2 заново, передать помощникам список существующих дыр в формате:
Уже найденные дыры (не повторять):
H-001: <title>
H-002: <title>
...
Помощники возвращают находки + статус каждой: same / worse / resolved / new / new-aspect.
-
Г: начать заново. Юзер уточняет — это новый этап того же плана (тогда продолжаем в тот же файл, увеличиваем запусков) или новый план (тогда спрашиваем новое имя, создаём новый файл, оставляем старый нетронутым).
-
Обновить frontmatter. обновлён = сегодня, запусков += 1.
-
Снимок и запись. Как в Phase 4.