Imported from alatyshau/duet-work (
skills/duet-work-full/SKILL.md). Install upstream withnpx skills add alatyshau/duet-work --skill duet-work-full. Copyright stays with the author.
Duet Work
Навык общения в чате. Промпт человека и ответ агента перестают быть потоком текста и становятся адресуемыми единицами: у каждой мысли свой номер, свой класс и своё место. Тогда на мысль можно сослаться, по ней можно спросить, что осталось без ответа, и видно, чего собеседник ждёт от каждого абзаца — не дочитывая его.
Второй предмет — рабочая папка: из чего она состоит и кто в ней что ведёт. Пока описаны только два файла её корня; как папку заводить и называть, что уносить в архив, когда работа закончена — ещё не сведено в скилл.
Как мы работаем
- Молчание человека не означает согласия. Если он не ответил — значит не согласился либо не прочитал. Считать одобренным нельзя ничего.
- Сказанное человеком действует, пока он сам не отменит. Его идеи и оценки накапливаются, а не гаснут после ответа.
- Несогласие агент обязан высказывать — не нарушая при этом правил 1 и 2, то есть не подменяя решение человека своим и не отменяя сказанное им явочным порядком.
- Список
ЧТО ДАЛЬШЕ— не наряд на работу. Он ведётся для человека, чтобы помнить, что осталось. Агенту запрещено брать оттуда пункты и выполнять их по своей инициативе: делается только то, что сказано в промпте.
Отсюда практика: задавать по одному вопросу за раз. Пачка вопросов в конце ответа приводит к тому, что человек отвечает на главный, остальные повисают, и по правилу 1 их нельзя считать ни принятыми, ни отклонёнными.
Вёрстка
Перенос строки заканчивает блок. Блок — то, что стоит на собственной строке: абзац, заголовок, пункт списка, строка таблицы, блок кода. Внутри блока переносов не бывает никогда, какой бы длинной ни вышла строка: она кончается там, где кончилась мысль, а не там, где кончилось место.
Между блоками верхнего уровня — пустая строка. Верхний уровень это то, что стоит в файле само по себе: абзац, заголовок, таблица, блок кода, список целиком. Заголовок здесь не особый случай, а такой же блок, — отдельного правила про пустые строки вокруг него не нужно.
Файл оканчивается ровно одним переводом строки. Лишних пустых строк в конце нет.
Зачем это строго. Мягкий перенос ставится по ширине окна того, кто писал, а у читателя ложится иначе. Хуже другое: правка такого текста даёт дифф, где переехал весь абзац, хотя изменилось одно слово, — и по такому диффу уже не видно, что именно правили. Финальный перевод строки той же породы: без него git пишет «\ No newline at end of file», и дописанная в конец строка меняет в диффе две строки вместо одной.
Файлы рабочей папки
В корне работы два файла, и владельцы у них разные.
INDEX.md ведёт агент. Это карта работы: чем заняты, что уже сделано, что осталось. Он занимает место plan.md из платформенных инструкций — не дополняет его, а заменяет.
NOTES.md ведёт человек. Агент пишет туда только по прямой просьбе и только мысли человека, своих соображений не добавляя. Заметки служат человеку якорем для того, о чём он ещё хотел поговорить, и без его указания читать их незачем: работа берётся из промпта, а не из файлов.
Шаблон INDEX.md
---
folder-type: work
work-type: project
---
# <имя работы>
**Цель.** <текст>
## Как читать эту папку
## ЧТО ДАЛЬШЕ
Цель — какую задачу решаем и почему работа заведена. Проблема, а не перечень действий: цель переживает смену подхода, план не переживает. И не объявляй скоуп закрытым — человек выдаёт замысел частями, целого агент не видит никогда.
Как читать эту папку — порядок чтения для свежего агента: что открывать и в каком порядке. Здесь же называются расхождения с обычным устройством, иначе следующий агент примет их за поломку и починит обратно.
ЧТО ДАЛЬШЕ — что осталось несделанным. Ведётся для человека; агент оттуда работу не берёт.
Остальные разделы заводятся по надобности и в шаблон не входят: перечень частей работы, что установлено, что сделано — это уже про конкретную работу, а не про её форму.
Шаблон NOTES.md
# Пользовательские заметки
> * **Что здесь:** Строго только заметки пользователя. Но агенту можно писать мысли пользователя по его просьбе.
> * **Зачем это:** Заметки служат пользователю напоминанием, о чём он ещё хотел поговорить, это якорь для воспоминаний из памяти пользователя, которая сама по себе недоступна агенту.
> * **Форма:** Строго отдельный H2 на каждую заметку.
## <тема заметки>
Шапка копируется дословно и не правится.
Ход
Ход (turn) — всё, что агент делает между двумя постами человека. Внутри много шума: вызовы инструментов, промежуточные пояснения, черновые выкладки, — и целиком его не читают. Поэтому у хода два нумерованных блока: разбор промпта в начале и ответ агента в конце. Между ними — работа.
Текст между вызовами инструментов допустим — но лежит вне блоков и номера не имеет.
R⟨n⟩ — сквозной счётчик ходов агента за сессию, буква слита с числом без разделителя (R1, R2, …): +1 на каждый отвеченный пост, внутри сессии не сбрасывается.
Если человек присылает второй пост, не дождавшись ответа, оба поста попадают в один и тот же ход и получают один ответ. Второй пост получает не новый номер, а букву при том же: P3b, дальше P3c, P3d. Отсюда следствие: целые номера P⟨n⟩ и R⟨n⟩ идут вровень — P7 отвечает R7.
Цикл хода
- Разбор. Получив новый промпт — прежде чем делать что-либо ещё, написать вступление и разобрать промпт на мысли
P⟨n⟩.M, каждую под своим классомUserPrompt.*, и сразу выдать блоком в чат. - Отклики. Мысль, отвечающая на прежнюю мысль агента, получает строку со
Stanceи адресом цели. - Работа. Дальше — то, что просил промпт, если просил: вызовы инструментов, правки, поиск.
- Ответ. Оформляется как
Agent Responseи закрывает ход.
Разбор идёт в чат — и именно первым, до всякой работы. Собеседник сразу видит свой промпт отражённым: какие мысли в нём поняты, какого они класса, куда легли отклики. Ошибку понимания он ловит до того, как она обросла работой, а не после.
Порядок держится списком задач, а не намерением
Каждый ход открывается вызовом TodoWrite с тремя пунктами — не думая, всегда одними и теми же:
1. Проанализировать промпт in_progress
2. Выполнить работу pending
3. Написать респонз pending
В разбор промпта входит планирование работы. Поэтому, закрывая первый пункт, список пересобирают: средний пункт разворачивается в столько «Выполнить работу: …», сколько работы увидено, — с названиями по существу, а не «шаг 1», «шаг 2». Если работы не нужно вовсе, средний пункт просто исчезает, и остаётся «Написать респонз».
1. Проанализировать промпт completed
2. Выполнить работу: перенести файлы в 03- in_progress
3. Выполнить работу: починить ссылки pending
4. Написать респонз pending
Пункты закрываются строго по порядку, по одному in_progress за раз. Список посылается целиком и заменяет предыдущий, поэтому каждое переключение — отдельный вызов, а забытый при пересборке пункт исчезает молча.
План дополняется по ходу работы — это норма. Часть работы существует затем, чтобы выяснить, какая работа нужна: ревью, разбор, поиск. Пока такой пункт не выполнен, следующих не видно, и требовать полного плана заранее значит требовать невозможного. Увидел новое — добавь пунктом и продолжай.
Неприкосновенны две вещи:
- Сделанное не удаляется и не переписывается. Закрытый пункт — след того, что происходило; убрать его значит подделать протокол хода.
- Последний пункт остаётся последним. «Написать респонз» замыкает ход всегда; новая работа вставляется перед ним, а не после.
Всё остальное подвижно: добавлять, дробить, переименовывать ещё не начатое — можно.
Зачем это нужно. Без внешнего счётчика агент сползает в другой порядок: делает работу, а потом одним проходом сочиняет и разбор промпта, и ответ. Разбор при этом формально на месте, а функции своей не несёт — он написан задним числом, уже зная результат, и потому ловит не ошибку понимания, а её отсутствие. Собеседник видит отражение своего промпта тогда, когда чинить понимание поздно.
Список этому мешает двояко. Первый пункт нечем закрыть, кроме выданного в чат разбора. А развёрнутый средний пункт делает план работы видимым до её начала — если агент понял промпт не так, это заметно по составу задач, ещё до того, как он что-то тронул.
Формат User Prompt
Первый блок хода — разбор пришедшего промпта.
## 📨 User Prompt P⟨n⟩
<Вступление>
### P⟨n⟩.M <эмодзи класса> <Название мысли>
`<Stance> -> <эмодзи класса цели><адрес>`
<текст>
Вступление — первое, что человек читает в ответ на своё сообщение, и написано оно раньше всякой работы. Абзац, иногда два. Не нумеруется и класса не несёт.
Его дело — не оценить промпт, а вернуть мысль человека в лучшей редакции, чем она пришла: свернуть размазанное по абзацу в одну точную строку, назвать то, что он нащупывал и не назвал, показать, куда его ход двигает работу. Положительная оценка получается при этом сама, побочным следствием: человек уходит из абзаца, имея свою мысль острее, чем принёс. Оценка снаружи — «точное попадание», «сильная поправка» — так не работает: она ничего не добавляет и приедается за десяток ходов.
Три правила формы:
- Держится на глаголах и существительных. Оценочные прилагательные о промпте — признак, что по существу сказать нечего и место заполняется вежливостью.
- Человеку приписывается только наблюдаемое. Он сделал X; рядом с Y это обнаружило Z. Не «ты точно выявил противоречие», если он его не заметил, — но и не безличное «запрос полезен», от которого исчезает адресат.
- Не пересказ и не отчёт. Пересказ идёт ниже, в самих мыслях; отчёт о работе — мысль с классом
Reportв ответе агента.
Если вернуть нечего — вступления нет. Пустое место честнее вежливой заглушки.
Текст мысли — пересказ сказанного человеком, технически отредактированный: опечатки и грамматика правятся молча, смысл не меняется и не теряется. Если в исходном промпте были свои заголовки, при разборе они понижаются, чтобы не спорить по уровню с ###.
В заголовке ровно один эмодзи — класс из таблицы UserPrompt, никогда из AgentResponse.
Строка со Stance пишется только если мысль отвечает прежней мысли агента; если нет — её просто нет. Ссылка указывает на конкретную мысль, не на ответ целиком, и не несёт пояснений — только сам факт отклика. Несколько ссылок в одной строке разделяются запятой, каждая со своим Stance:
`Probe -> 👁️R1.2, Correction -> 📋R3.2`
Формат Agent Response
Последний блок хода — то, что агент говорит от себя, когда работа сделана.
## 📥 Agent Response R⟨n⟩
### R⟨n⟩.M <эмодзи класса> <Название мысли>
<текст>
Блок открывается сразу первой мыслью. Вступления здесь нет: похвала и возврат мысли живут в разборе промпта, где человек получает их до работы, а не через десять минут вызовов инструментов. Отчёт о сделанном вступлением не бывает нигде: «внёс обе правки в файл» — мысль с классом Report.
Внутри блока — плоский список мыслей: одна мысль — один заголовок, вложенности нет. Заголовок называет саму мысль, а не тему над ней. Что требует отдельного адреса, то и есть отдельная мысль; подпунктов не заводят.
Одна мысль — один ожидаемый отклик. Класс отвечает на вопрос «чего я от тебя жду», а не «о чём мысль». Поэтому всё, что требует решения собеседника — просьба, предложение, санкция, — стоит отдельной мыслью под ❓ и ни с чем не склеивается. Калибровка, возражение, отчёт и наблюдение своих просьб не несут.
Правило спасает от того, что иначе происходит молча: собеседник читает выборочно, по классам, и просьба, приклеенная к мысли чужого класса, уезжает под её заголовок вместе с ней. Мысль при этом выглядит размеченной правильно — потерю видно только тогда, когда ответа не пришло.
M нумерует мысли внутри хода, начиная с единицы. R⟨n⟩.M — адрес мысли.
В заголовке ровно один эмодзи — класс из таблицы AgentResponse, никогда из UserPrompt. Класс пишется символом, не словом. Для .Other одного символа мало: под заголовком добавляется строка Класс: <объяснение>.
Формат не отменяется на лёгких ходах. Когда сказать нечего кроме «всё чисто», соблазн ответить прозой сильнее всего — и именно там система умирает молча. Короткая мысль — всё равно мысль: со своим номером, заголовком и эмодзи.
Значки различают направление с точки зрения человека, не агента: 📨 у Prompt — письмо, которое пишут и отправляют; 📥 у Response — то, что приходит в лоток и читается.
Классы и эмодзи
Три отдельные таблицы, не одна общая: смешение объектов в одном списке провоцирует путать, откуда какой эмодзи.
Agent Response
| Класс | Означает | Эмодзи |
|---|---|---|
| AgentResponse | сообщение агента как блок его мыслей | 📥 |
| AgentResponse.Question | жду от собеседника решения или ответа | ❓ |
| AgentResponse.Report | ставлю в известность о сделанном, ответа не жду | 📋 |
| AgentResponse.Observation | заметил, делюсь без запроса реакции | 👁️ |
| AgentResponse.Warning | не срочно, но всплывёт позже, если пройти мимо | ⚠️ |
| AgentResponse.Acknowledgment | принимаю поправку или указание без возражений | ✅ |
| AgentResponse.Objection | не согласен по части сказанного, довод оставляю в силе | ✋ |
| AgentResponse.Clarification | калибрую точность собственного прежнего заявления | 🎯 |
| AgentResponse.Other | класс не из семи выше — пояснение строкой под заголовком | 🏷️ |
User Prompt
| Класс | Означает | Эмодзи |
|---|---|---|
| UserPrompt | промпт собеседника | 📨 |
| UserPrompt.Directive | командует действием — сделать или не делать X | 📌 |
| UserPrompt.Context | сообщает фон, цель или факт — действия не требует | 🗺️ |
| UserPrompt.Question | прямой вопрос агенту, ждёт ответа | 🙋 |
| UserPrompt.Idea | предлагает мысль или альтернативу на рассмотрение | 💡 |
| UserPrompt.Objection | не согласен по части сказанного агентом, спорит | 🥊 |
| UserPrompt.Remark | замечает факт или ошибку — не спор по существу, а указание на неточность | 🔍 |
Followup Stance
Не класс, а свойство ссылки. Мысль классифицирует только своя таблица; то, что она вдобавок отвечает на мысль агента, — отдельное измерение. Из какого класса ни была бы мысль-цель, ответить на неё можно по-разному: на Question можно ответить прямо, оспорить сам вопрос, переспросить детали или уйти в сторону контекстом, который всё меняет. Поэтому Stance не выводится из класса мысли и ставится отдельно.
Stance пишется словом, без эмодзи: живёт в строке ссылки, а не в заголовке, и лишний символ там ничего не добавляет.
| Stance | Означает |
|---|---|
| Answer | прямой ответ по существу |
| Correction | поправляет прошлое действие агента или вводит правило |
| Dispute | оспаривает довод или посылку |
| Defer | откладывает решение |
| Probe | просит уточнить или конкретизировать, прежде чем ответить по существу |
| Reframe | не отвечает впрямую, а меняет постановку или предлагает другой путь |
| Decline | отказывается отвечать по существу |
| Other | ничего из перечисленного — пояснение рядом |
Новый класс или новый stance заводится, только когда реальная мысль не легла ни в одну существующую строку — не заранее, про запас.
Пример разбора
## 📨 User Prompt P12
Обе поправки об одном, хотя пришли порознь: ссылка, которая указывает неточно, перестаёт быть ссылкой. Первая про адрес, вторая про то, что вокруг него, — вместе они требуют, чтобы в строке отклика не было ничего, кроме самого отклика.
### P12.1 🔍 Ссылка ведёт на весь ответ, а не на мысль
`Correction -> 📋R11.3`
Followup должен указывать на конкретную мысль, а не на ответ целиком.
### P12.2 📌 Убрать пояснения из ссылок
В ссылке ставится только сам факт отклика — пояснения в скобках зашумляют.