feat(skills): интерактивный контракт (wiki:2660) — выпил claim/TTL/heartbeat из mappa-* скилов (#1040)

- mappa-task-work 1.0.0→1.1.0: «взять таску» = task_update(status=active, owner=<сессия>, version) + 409-retry; release = status=ready; close с version; heartbeat убран (живость из session live-ingest); MCP-таблица и loop-mode обновлены
- mappa-knowledge 1.0.0→1.1.0: wiki_create карв-аут, wiki_update version+409; лиз-секция убрана
- mappa-delegation 1.0.0→1.1.0: task_create карв-аут, close/update version+409
- mappa-brainstorm-promote 1.2.0→1.3.0: create карв-аут, 422 busy → 409-семантика
- mappa-messaging 1.0.0→1.1.0: task_create/close карв-аут + version+409
- mappa-closing-ritual: task_close version+409 (упоминания)

Откат claim-механики из скилов; контракт = wiki:2660.
This commit is contained in:
2026-08-25 00:27:25 +03:00
parent aa80e9faa5
commit ae45a51420
6 changed files with 109 additions and 106 deletions

View File

@@ -1,12 +1,12 @@
---
name: mappa-brainstorm-promote
author: ours
version: 1.2.0
version: 1.3.0
description: >
Finalize a matured brainstorm buffer on the boss's desk
(~/projects/.workshop/.brainstorm/): ask routing (workshop-meta → local
.wiki/concepts/, domain → target project wiki via knowledge_ingest,
mappa-service → mappa-борд под лизом, skill → claude-skills skeleton),
mappa-service → mappa-борд (карв-аут), skill → claude-skills skeleton),
extract action-items into target .tasks, create pointers + review umbrella
for impl tasks, archive the buffer. Старое имя — триггер-синоним:
workshop-promote-brainstorm. Location-agnostic: fires from ANY folder; all
@@ -27,7 +27,7 @@ description: >
- **workshop-meta** → локальный `.wiki/concepts/` (методология самой зоны).
- **domain** → глобал через `mcp__projects-meta__knowledge_ingest` в `~/projects/<proj>/.wiki/`.
- **mappa-service** → сервисные борды (mappa, .common, …): таски/вики-сущности через `mcp__mappa__task_create`/`wiki_create` **под лизом** (`task_claim_next`); pointers-таска НЕ нужна, если спека уже в вики проекта (w:NNNN); review-umbrella — сервисная таска; covering-письмо в инбокс цели.
- **mappa-service** → сервисные борды (mappa, .common, …): таски/вики-сущности через `mcp__mappa__task_create`/`wiki_create` (карв-аут, wiki:2660); review-umbrella — сервисная таска; covering-письмо в инбокс цели.
- **skill** → `~/projects/claude-skills/skills/<name>/SKILL.md` (только шапка + пустой каркас тела, локальный коммит без push/install/build-hermes).
Буфер уезжает в `.archive/`. Action-items уходят тасками в target-проект. **Всегда, при любом маршруте, в воркшоп-вики остаётся summary-страница.**
@@ -63,7 +63,7 @@ description: >
│ │ │ │
│ ▼ ▼ ▼
│ knowledge_ingest mcp__mappa__wiki_create dialog: description (trigger contract)
│ │ (под лизом)
│ │ (карв-аут, wiki:2660)
│ │ │ ▼
│ │ │ preview + confirm
│ │ │ │
@@ -118,10 +118,9 @@ description: >
- **Определить канал борда:** есть ли у проекта файловая доска `.tasks/STATUS.md` (file channel) или борд живёт в mappa-сущностях (service channel — сервисные проекты: mappa, .common, …). Проверка: файл `.tasks/STATUS.md` в чек-ауте (file) против `mcp__mappa__task_list(project=<proj>)` / `entity_search` (service). Если файловой доски нет, а mappa-сущности есть → **service channel**.
4. **Если domain + service channel (mappa-борд, fold-in 1):**
- **Лиз:** `mcp__mappa__task_claim_next(project=<target>, owner=<своя>)``token`.
- **Спека → вики-сущность:** `mcp__mappa__wiki_create(project, slug, body, claim_token)` (или `wiki_update`, если страница уже есть). Если спека уже в вики проекта (w:NNNN) — не дублировать, описание импл-тасок ссылается на неё.
- **Спека → вики-сущность:** `mcp__mappa__wiki_create(project, slug, body)` (или `wiki_update(project, id, version)`, если страница уже есть). Если спека уже в вики проекта (w:NNNN) — не дублировать, описание импл-тасок ссылается на неё.
- **Pointers-таска НЕ нужна**, если спека уже в вики проекта — дыра pointers закрыта инлайн (описание импл-тасок прямо ссылается на спеку).
- **Импл-таски:** `mcp__mappa__task_create(project, slug, title, description, status='ready', claim_token)`**ПОСЛЕДОВАТЕЛЬНО, не батчем** (см. NB в шаге 7).
- **Импл-таски:** `mcp__mappa__task_create(project, slug, title, description, status='ready')` (карв-аут, wiki:2660)**ПОСЛЕДОВАТЕЛЬНО, не батчем** (см. NB в шаге 7).
- **Review-umbrella:** сервисная таска `mcp__mappa__task_create(status='blocked', blocker=<номера импл-тасок>)`.
- **Covering-письмо:** `mcp__mappa__inbox_send(project=<target>, from=<своя>, subject='Промоушен <topic>: таски <#N>…', body=<перечень>).` — канон mappa-delegation: таска на борде не пингует живую сессию.
@@ -367,7 +366,7 @@ description: >
- Если `tasks_create` review-таски упала — сообщить пользователю, **продолжить** к шагу 11 (архивация буфера). Review-таску можно создать вручную позже из `.archive/<date>-<topic>.md`.
- **domain (service channel):** review-umbrella — **сервисная таска**: `mcp__mappa__task_create(project=<target>, slug=<topic>-review, status='blocked', blocker=<номера импл-тасок через запятую>, description=<шаблон domain выше, спека = w:NNNN в вики проекта>, claim_token)` под тем же лизом. Pointers-таска отсутствует (спека в вики).
- **domain (service channel):** review-umbrella — **сервисная таска**: `mcp__mappa__task_create(project=<target>, slug=<topic>-review, status='blocked', blocker=<номера импл-тасок через запятую>, description=<шаблон domain выше, спека = w:NNNN в вики проекта>)`. Pointers-таска отсутствует (спека в вики).
**Covering-письмо в инбокс цели (оба канала; канон mappa-delegation).** После создания тасок — `inbox_send` получателю-проекту: таска на борде не пингует живую сессию, письмо = пинг + контекст. File channel: `mcp__mappa__inbox_send(project=<target>, from=<своя>, subject='[event: created] <topic> — <N> тасок', body=<перечень: #N slug> )`. Service channel: то же, но `from` = своя папка (или сервисный адрес) и тело ссылается на w:NNNN-спеку.
@@ -424,7 +423,7 @@ description: >
- `mcp__projects-meta` недоступен → abort до записей.
- Целевой проект (для domain) не найден в `meta_status` → abort.
- `knowledge_ingest` упал → abort до `tasks_create` и `git mv`. Буфер остаётся.
- **Service channel:** лиз занят (422 busy от `task_claim_next`) → abort или retry после освобождения; не писать без лиза.
- **Service channel:** create — карв-аут (wiki:2660), 422 busy больше не бывает; 409 на `wiki_update` (version) → retry со свежей версией.
- **Service channel:** `task_create` упал (гонка счётчика/переходный период) → **повторить последовательно**, не батчем; при стабильном отказе — abort до архивации.
- **Service channel:** спека уже существует в вики проекта (w:NNNN) → не дублировать `wiki_create`, ссылаться на неё.
- **domain:** `tasks_create` для `[<topic>-pointers]` упал → **abort** до content-тасок и до review-таски. Без pointers оставшиеся таски бесполезны (агент будет угадывать). Сообщить пользователю; буфер оставить на месте для retry.
@@ -440,7 +439,7 @@ description: >
- **Всегда (любой маршрут):** создаёт summary-страницу `.wiki/concepts/<topic>.md` в `.workshop/` + добавляет строку в `index.md`.
- **workshop-meta:** summary IS контент (шаг 6 записывает полное тело; шаг 9 дополняет секцию «задачи/ссылки» или пропускается если уже полный).
- **domain:** создаёт запись в target-wiki через MCP (`mcp__projects-meta__knowledge_ingest`).
- **domain (service channel):** создаёт спека-сущность в вики mappa-проекта (`mcp__mappa__wiki_create` под лизом); импл-таски + review-umbrella — сервисные таски (`mcp__mappa__task_create` под лизом); covering-письмо в инбокс цели (`inbox_send`). Pointers-таска НЕ создаётся (спека уже в вики).
- **domain (service channel):** создаёт спека-сущность в вики mappa-проекта (`mcp__mappa__wiki_create` — карв-аут); импл-таски + review-umbrella — сервисные таски (`mcp__mappa__task_create` — карв-аут); covering-письмо в инбокс цели (`inbox_send`). Pointers-таска НЕ создаётся (спека уже в вики).
- **domain:** создаёт также **mandatory pre-impl** таску `[<topic>-pointers]` в target — pre-filled блок текста для `.wiki/CLAUDE.md` Domain conventions (ссылки на global wiki slug + workshop archive trace + local overview.md). Без неё последующий импл-агент попадает в дыру: where_stopped one-liner + пустой Domain conventions stub.
- **skill:** создаёт `~/projects/claude-skills/skills/<name>/SKILL.md` — **только шапка + пустой каркас**. Локальный коммит в `claude-skills/`. **Без** установки, push, или build-hermes — это всё в созданных baseline-тасках.
- Создаёт N тасок в target `.tasks/` через MCP.
@@ -460,6 +459,6 @@ description: >
- **Skill:** не бампить `version` после первой записи (это работа владельца `claude-skills/`).
- **Skill:** не промоутить в существующий скил (см. Failure modes — abort).
- **Skill:** не пропускать обязательный preview + confirm перед `Write` — действие выходит за пределы мастерской, изменяет соседний репозиторий.
- **Service channel:** не писать в mappa-борд без лиза (422 busy); не батчить `task_create` (гонка sha-CAS счётчика, инцидент 2026-08-24: 6/7 упали) — только последовательно.
- **Service channel:** не батчить `task_create` (гонка счётчика, инцидент 2026-08-24: 6/7 упали) — только последовательно; `wiki_update`/`task_close` со свежей version (409 → retry).
- **Service channel:** не плодить pointers-таску, если спека уже в вики проекта (w:NNNN) — описание импл-тасок ссылается на неё инлайн.
- **Service channel:** не забывать covering-письмо в инбокс цели — таска на борде не пингует живую сессию.

View File

@@ -78,13 +78,13 @@ Forward-looking, не timeline: handoff = связка новых вещей к
### 5. Propose wiki-ingest (не писать!)
Если за сессию появилось durable-знание — **ПРЕДЛОЖИТЬ** ingest
(`mappa-knowledge`: mappa wiki_create/update под лизом), перечислив кандидатов.
(`mappa-knowledge`: mappa wiki_create/update — version+409, wiki:2660), перечислив кандидатов.
Ничего не писать без подтверждения.
### 6. Propose task-board closes (не закрывать!)
Если есть задачи, выглядящие закрытыми — **ПРЕДЛОЖИТЬ** закрытия
(`mappa-task-work`: task_close под лизом). Уважать ralph-loop: verifier-задачи
(`mappa-task-work`: task_close — version+409). Уважать ralph-loop: verifier-задачи
закрывать только через verifier (attempt/harness-record).
### 7. Формат предложения — один блок

View File

@@ -1,7 +1,7 @@
---
name: mappa-delegation
author: ours
version: 1.0.0
version: 1.1.0
description: >
Цикл делегирования задачи другому агенту/проекту: pre-flight gate →
шаблон тела → dry-run preview → confirm → covering-письмо в инбокс
@@ -44,11 +44,10 @@ steering-loop поля (notify/weight), парную review-таску для im
Номер задаче присваивает сервер (`tasks_create` из счётчика agenda/task-counter) — постановщик номер не придумывает и не резервирует. Возвращённый `#n` из preview/confirm — машинный ключ задачи: им ссылаются блокеры, письма, decision-trail.
> **Лизинговая модель (#1054).** Мутации тасок гейтятся лизом проекта
> (`mcp__mappa__task_claim_next` → token для `task_create`; file channel —
> sha-CAS через Gitea). `#1054 lease-model-generalization` (create-без-лиза как
> принцип, claim на таску) — **опционально**: контракт этого скила работает на
> текущих тулах; перейти на create-без-лиза, когда #1054 имплементится.
> **Интерактивный контракт (wiki:2660).** `task_create` — карв-аут, лиз НЕ
> нужен (create = INSERT, входной контур). `task_close`/`task_update` — optimistic
> version+409 (конфликт → свежая version из `task_get` → retry). File channel —
> sha-CAS через Gitea (projects-meta), там свои правила.
## Steps (цикл)
@@ -133,8 +132,7 @@ mcp__mappa__inbox_send(
)
```
(Мутация тасок гейтится лизом проекта — `task_claim_next`; доставка письма —
карв-аут, лиза не требует.)
(`task_create` — карв-аут (wiki:2660), доставка письма — карв-аут.)
Причина: таска на борде **не пингует живую сессию** получателя. Поллер
подхватит по `Weight`/`Notify`, но живая интерактивная сессия узнаёт только
@@ -177,7 +175,7 @@ task» — общий случай (шаг 7 — его частность дл
## Side effects
- Создаёт таску в target-проекте через `tasks_create` (file channel — Gitea commit; service channel — mappa-сущность под лизом).
- Создаёт таску в target-проекте через `tasks_create` (file channel — Gitea commit; service channel — mappa-сущность карв-аутом, wiki:2660).
- Опционально создаёт парную review-таску (status=blocked).
- Covering-письмо в инбокс получателя (кросс-проектная делегация).

View File

@@ -1,7 +1,7 @@
---
name: mappa-knowledge
author: ours
version: 1.0.0
version: 1.1.0
description: >
Цикл работы со знаниями проекта в Mappa (Karpathy LLM Wiki, канал =
mappa-сущности): ingest → query → lint + граф-слой для
@@ -11,7 +11,7 @@ description: >
«что связывает X и Y», «как связаны», «путь между X и Y», «what connects
X and Y», «что ссылается на X», «backlinks of X», «сироты», «битые ссылки»,
«orphan pages». Wiki = сущности type=wiki (чтение — карв-аут лиза; запись —
под лизом проекта, решение 19). Реляционные вопросы — через graph_* (BFS на
version+409, wiki:2660). Реляционные вопросы — через graph_* (BFS на
стороне сервиса), guarded failure-mode: одна страница и стоп, без
многохоповых цепочек чтением. Skip для одно-страничных контентных вопросов.
---
@@ -25,7 +25,7 @@ query / lint) + **граф-слой** для реляционных и стру
сущностями читают через граф (graph_*).
Канал — Mappa (`mcp__mappa__*`), НЕ файлы. Страница — сущность `type=wiki`
(`wiki:N`); чтение — карв-аут лиза, запись — под лизом проекта (решение 19).
(`wiki:N`); чтение — карв-аут, запись — version+409 (wiki:2660).
Файлового `.wiki/` больше нет; `setup-wiki` умер (нечего настраивать).
## Когда использовать
@@ -66,19 +66,17 @@ README/ADR (не персистентная база знаний), проект
|---|---|---|
| Чтение страницы | `mcp__mappa__wiki_get(project?, slug)` | чтение — карв-аут (решение 19) |
| Поиск страниц | `mcp__mappa__entity_search(q, type='wiki', project?, scope?, limit)` | ILIKE по body/title |
| Лиз для записи | `mcp__mappa__task_claim_next(project, owner)` | → `token`; таска может отсутствовать |
| Продление лиза | `mcp__mappa__task_heartbeat(project, claim_token)` | долгие ingest-циклы |
| Создать страницу | `mcp__mappa__wiki_create(project, slug, body, claim_token)` | под лизом |
| Обновить страницу | `mcp__mappa__wiki_update(project, id, title?, body?, claim_token)` | под лизом; id — internal |
| Создать страницу | `mcp__mappa__wiki_create(project, slug, body)` | карв-аут (wiki:2660), лиз не нужен |
| Обновить страницу | `mcp__mappa__wiki_update(project, id, title?, body?, version)` | version+409; id — internal |
| Путь между сущностями | `mcp__mappa__graph_path({from, to})` | кратчайшая цепочка, BFS |
| Соседи / исходящие | `mcp__mappa__graph_neighbors({id})` | рёбра узла с резолвом целей |
| Входящие ссылки | `mcp__mappa__graph_backlinks({id})` | кто ссылается на узел |
| Здоровье графа | `mcp__mappa__graph_stats()` | nodes/edges/components |
**Запись всегда под лизом.** Мутации wiki гейтятся лизом проекта: без
валидного `claim_token` — 422 busy. Лиз экспирится по TTL (дефолт 600s);
закрыть вручную нечем (кроме `admin_release_lease` для залипших) — пиши,
затем отпусти (не держи лиз на время чтения/размышлений).
**Запись — version+409 (wiki:2660 interactive contract).** `wiki_create`
карв-аут (лиз не нужен); `wiki_update` несёт `version` из последнего чтения,
конфликт → 409 → `wiki_get` заново → retry. Долгих лизов/TTL/heartbeat больше
нет — живость владельца из session live-ingest.
**Рефы и id (#1037/#1028).** Публичная поверхность несёт per-type реф полным
именем первым полем: `ref: "wiki:3"` (решение 20, конвенция #1028), `num`
@@ -99,7 +97,7 @@ per-type рефы полными именами (`[[task:N]]`/`[[inbox:N]]`).
3. Создай `sources/<slug>` — одну страницу-резюме на источник (~50150 строк;
ссылку на raw клади в frontmatter `raw_path` + `ingested:`).
4. Для каждой затронутой страницы:
- есть → обнови (`wiki_update(project, id, body, claim_token)`). **Противоречия
- есть → обнови (`wiki_update(project, id, body, version)`). **Противоречия
помечай явно** блоком `> **Противоречие:** источник A говорит X, источник B — Y`.
Не затирай молча.
- нет → создай (`wiki_create`).
@@ -115,9 +113,8 @@ per-type рефы полными именами (`[[task:N]]`/`[[inbox:N]]`).
**Один ingest может затронуть 1015 страниц. Это нормально — для того LLM и нужны.**
Порядок записи: сначала лиз (`task_claim_next`), затем все wiki-мутации одним
циклом (не бери лиз на чтение), затем отпусти (лиз живёт TTL — просто закончи
писать; heartbeat только если цикл реально долгий).
Порядок записи: все wiki-мутации одним циклом; `wiki_update`со свежей
`version` из чтения (409 → retry). Создание новых страниц — карв-аут.
### Query — вопрос по вики
@@ -234,7 +231,7 @@ level/since/component/entity, retention 14d). Ручную `log`-страниц
- **Не-ASCII слаги.** Ломают grep и кросс-платформенность. Транслитерируй.
- **Пропущенные противоречия в lint.** Ценность вики — во вскрытых напряжениях,
а не в ложном консенсусе.
- **Запись без лиза.** Wiki-мутации без `claim_token` → 422 busy.
- **Запись — version+409 (wiki:2660).** `wiki_create` — карв-аут; `wiki_update` со свежей `version` (конфликт → 409 → retry).
- **Держать лиз на чтение/раздумья.** Лиз — на время записи. Чтение — карв-аут.
- **Реляционный вопрос чтением одной страницы.** Это тот самый 0%-recall
провал — зови graph_*.

View File

@@ -1,7 +1,7 @@
---
name: mappa-messaging
author: ours
version: 1.0.0
version: 1.1.0
description: >
Цикл межсессионной почты через Mappa: SEND (inbox_send) → RECEIVE
(inbox_monitor) → POLICY (peer ≠ authority). Адрес = имя папки проекта из
@@ -156,8 +156,8 @@ mcp__mappa__inbox_send(
lifecycle-уведомления («таска создана», «закрыта», «заблокирована»). Не больше.
- **Задачи — только через `mcp__mappa__task_*`.** Доска — единственный
источник правды о задаче: существование, статус, скоуп, решения создаются и
меняются через `task_create` / `task_close` — никогда не «решаются» внутри
письма. (Мутации тасок гейтятся лизом проекта — `task_claim_next`.)
меняются через `task_create` / `task_close` (карв-аут + version+409, wiki:2660)
— никогда не «решаются» внутри письма.
Следствие: **если это не на доске — это не задача и не решение, это разговор.**
Значимый дизайн-выбор должен лечь на доску (или в вики), инбокс лишь указывает

View File

@@ -1,29 +1,37 @@
---
name: mappa-task-work
author: ours
version: 1.0.0
version: 1.1.0
description: >
Центральный цикл работы с тасками в Mappa: ориентация → выбор работы
(claim, priority/due) → исполнение → сдача (close + review-umbrella) +
loop-mode «поработай очередь». Борд = сущности mappa (решения 14/15/19/20);
мутации под лизом. Поглощает using-tasks + task-format + task-loop
(loop-mode ВНУТРИ) + priority-due-раздел (старые имена — триггер-синонимы).
Триггеры: «что на досках», «возьми таску», «какой статус», «update status»,
«pause», «switch to X», «где мы остановились», «work the queue», «поработай
очередь», «прогони доску». Приоритет = территория человека: агенты ставят
P0-P2/дедлайн только при создании, дефолт P1; просрочка → notify, без
авто-бампа. НЕ про делегирование (→ mappa-delegation), НЕ про доску-обзор
(→ ops/using-system-snapshot).
(owner, priority/due) → исполнение → сдача (close + review-umbrella) +
loop-mode «поработай очередь». Борд = сущности mappa (решения 14/15/20);
мутации — version+409 (wiki:2660 interactive contract), «взять таску» =
status→active + owner, живность владельца из session live-ingest. Поглощает
using-tasks + task-format + task-loop (loop-mode ВНУТРИ) + priority-due-раздел
(старые имена — триггер-синонимы). Триггеры: «что на досках», «возьми таску»,
«какой статус», «update status», «pause», «switch to X», «где мы остановились»,
«work the queue», «поработай очередь», «прогони доску». Приоритет = территория
человека: агенты ставят P0-P2/дедлайн только при создании, дефолт P1;
просрочка → notify, без авто-бампа. НЕ про делегирование (→ mappa-delegation),
НЕ про доску-обзор (→ ops/using-system-snapshot).
---
# mappa-task-work
Центральный цикл работы с задачами: **ориентация → выбор работы → исполнение →
сдача**. Борд — сущности mappa (`type=task`, `t:N`): чтение — карв-аут лиза,
**любая мутацияпод лизом проекта** (решение 19). Скилл = цикл, не тул:
одна механика на выбор/исполнение/сдачу, плюс **loop-mode** («поработай
сдача**. Борд — сущности mappa (`type=task`, `task:N`): чтение — карв-аут,
**мутацииoptimistic concurrency** (wiki:2660 interactive contract):
version+409, конфликт → retry со свежей version из `task_get`. Скилл = цикл,
не тул: одна механика на выбор/исполнение/сдачу, плюс **loop-mode** («поработай
очередь») внутри — отдельный скил не создаётся.
> **Интерактивный контракт (wiki:2660).** claim/TTL/heartbeat выпилены:
> поллер не клиент mappa (живёт на файловых досках projects-meta), клиент —
> интерактивные сессии. «Взять таску» = `task_update(status=active, owner=<сессия>)`
> с верной version; конфликт (уже взято) → 409 → выбор другой таски. Живость
> владельца — из session live-ingest (сессия `pi` пишется сама), не таймер.
> **Переходное (file channel).** Пока поллер/кэш читают файловые борды
> (`.tasks/STATUS.md`), legacy-канал живёт: блоки в файле обязаны строгому
> формату (см. «Формат таски» ниже), мутации — через `mcp__projects-meta__tasks_*`
@@ -43,27 +51,29 @@ description: >
| Операция | Тул | Примечание |
|---|---|---|
| Взять следующую ready-таску | `mcp__mappa__task_claim_next(project, owner)` | атомарно: лиз + таска; → `{ok, token, task}` |
| Продлить лиз | `mcp__mappa__task_heartbeat(project, claim_token)` | долгие таски |
| Создать таску | `mcp__mappa__task_create(project, slug, title?, description?, status?, priority?, due?, claim_token)` | под лизом; per-type номер (решение 20) |
| Закрыть таску | `mcp__mappa__task_close(project, id, claim_token)` | под лизом |
| Прочитать таску | `mcp__mappa__entity_get(id)` | id internal из search/claim |
| Взять таску | `mcp__mappa__task_update(project, id, status='active', owner=<сессия>, version)` | «взять» = conditional update; 409 → уже взята |
| Сбросить/отдать | `mcp__mappa__task_update(project, id, status='ready', owner='', version)` | release = обычный update |
| Создать таску | `mcp__mappa__task_create(project, slug, title?, description?, status?, priority?, due?)` | карв-аут, лиз не нужен (wiki:2660) |
| Закрыть таску | `mcp__mappa__task_close(project, id, version)` | version+409 (конфликт → retry) |
| Прочитать таску | `mcp__mappa__entity_get(id)` | id internal из search; несёт version |
| Список борда | `mcp__mappa__entity_search(q, type='task', project=<имя>, limit)` | все статусы |
| Дерево parent_of | `mcp__mappa__graph_tree(root, depth?, fields?, limit?)` | зонтики/иерархия |
| Связанные сущности | `mcp__mappa__graph_neighbors/backlinks(id)` | рефы к таске |
| Просрочка | `mcp__mappa__admin_overdue_scan(project?)` | P2-джоба: notify в инбокс, без мутаций |
| Уведомление при закрытии | `mcp__mappa__inbox_send(project=<notify>, from=<своя>, subject, body)` | письмо комиссионеру |
**Лиз = лок на запись (решение 19).** Одна строка leases на проект: если другой
агент держит лиз — `task_claim_next` вернёт **422 busy**. Это серверный аналог
старого `.tasks/.lock`: проверять «а не поллер ли работает» руками не нужно —
сам claim скажет. Чтения лиза не требуют.
**Взятие таски = version+409, не лиз.** Каждая таска несёт `version`; conditional
update (`status=active, owner=<сессия>, version=N`) проходит, только если никто
не изменил её после твоего чтения. Конфликт → **409**`task_get` заново →
выбирай другую. Конкуренции «проект занят» нет: два агента берут разные таски
свободно. Живость — из сессии: пока твоя pi-сессия активна (end-state≠clean),
твои 🔴 видны как «в работе».
**Рефы и id (#1037/#1028).** Таски наружу несут `ref: "t:N"` полным именем
первым полем (`task:N`, конвенция #1028), `num` следом, глобальный `id`
internal (последним, для addressing в тулах). Ссылайся на таску
`[[task:N]]`/`task:N` в прозе (слаг/имя первым, реф как якорь: «таска
`mappa-task-work` (task:1062)»), никогда `#<глобальный id>`.
**Рефы и id (#1037/#1028).** Таски наружу несут `ref: "task:N"` полным именем
первым полем (конвенция #1028), `num` следом, глобальный `id` internal
(последним, для addressing в тулах). Ссылайся на таску `[[task:N]]`/`task:N`
в прозе (слаг/имя первым, реф как якорь: «таска `mappa-task-work` (task:1062)»),
никогда `#<глобальный id>`.
## Статусы (эмодзи для презентации)
@@ -91,16 +101,19 @@ internal (последним, для addressing в тулах). Ссылайся
4. Подтверди одним предложением: «Мы в середине X, следующий шаг — Y».
5. Спроси, верен ли план, перед действиями.
### Фаза 1 — Выбор работы (claim, priority/due)
### Фаза 1 — Выбор работы (owner, priority/due)
1. `mcp__mappa__task_claim_next(project, owner)` — атомарно: лиз + следующая
ready-таска. `owner` = `<machine>:<runtime>:<session>`.
Порядок выдачи: **P0-пул первый, внутри по дедлайну (просроченные первыми),
потом P1, потом P2**; отсутствующий priority = P1 (task-priority-due).
1. Собери кандидатов: `entity_search(q, type='task', project=<имя>)`
готовые ⚪ ready. Порядок приоритета: **P0-пул первый, внутри по дедлайну
(просроченные первыми), потом P1, потом P2**; отсутствующий priority = P1
(сортировка — клиентская, wiki:2660).
2. **Локально-первая рекомендация** — борд cwd первым; кросс-проект — футонота
(`Cross-project: N 🔴 active — см. tasks_aggregate`) только если N>0 и в cwd
нет активной 🔴. Кросс-проектные ургенты — информация, не драйвер «что делать здесь».
3. **Priority/Due — территория человека (раздел task-priority-due):**
3. **Взять таску:** `task_update(project, id, status='active', owner='<machine>:<pi>:<session>', version=<из чтения>)`.
- 200 → таска твоя (🔴, owner=твоя сессия).
- **409** → кто-то уже взял (version устарела) → `entity_get` → бери следующую.
4. **Priority/Due — территория человека (раздел task-priority-due):**
- Агент ставит `priority`/`due` **только при создании** таски (явные параметры
или строки `**Priority:** P0|P1|P2` / `**Due:** yyyy-mm-dd` в description).
Отсутствует → дефолт P1, без дедлайна.
@@ -114,9 +127,10 @@ internal (последним, для addressing в тулах). Ссылайся
- **Одна активная таска** 🔴 на проект. Не параллель.
- Читай description + per-task файл (`<slug>.md`, где есть) до старта.
- Долгие таски: `task_heartbeat(project, claim_token)` (лиз по TTL, дефолт 600s).
- Heartbeat не нужен: живость = живая pi-сессия (session-sync пишет её в mappa;
stale-детект — по сессии владельца, не таймер на таске).
- **`session_break` gate** (из task-loop): если в description таски есть маркер
`session_break` — после close НЕ клейми следующую: печатай
`session_break` — после close НЕ бери следующую: печатай
`🔚 SESSION BOUNDARY …` и останавливайся (домен-свитч / milestone / тяжёлая инфра).
### Фаза 3 — Сдача (close + review-umbrella)
@@ -125,7 +139,8 @@ internal (последним, для addressing в тулах). Ссылайся
каждого — evidence: тест в диффе, артефакт, ссылка на дизайн. Нет evidence на
критерий → спроси user'а «закрывать или подождать coverage'а».
2. Resolve/drop открытые вопросы.
3. `task_close(project, id, claim_token)` → статус `done`.
3. `task_close(project, id, version=<из чтения>)` → статус `done`.
**409 на конфликт**`entity_get` → повтори close со свежей version.
4. **Notify-письмо (кросс-проектные таски).** Если таска пришла из другого
проекта (в description/meta есть `from:`/`notify:`) — `inbox_send`
комиссионеру: `project=<notify>`, `subject="[event: closed] <slug>"`,
@@ -143,7 +158,7 @@ internal (последним, для addressing в тулах). Ссылайся
1. Текущая 🔴 → `task_close` если завершена (см. Фазу 3), иначе пометь
`status=paused` (owner остаётся; «where stopped» — в description или handoff).
2. **Инбокс-свип** на границе тасок (`inbox_monitor`).
3. Возьми следующую: `task_claim_next` (лиз + таска). Прежняя остаётся 🟡.
3. Возьми следующую: `task_update(status=active, owner=<сессия>)`. Прежняя остаётся 🟡.
4. Подтверди ориентацию перед стартом.
> **Never lose Where I stopped** — критичное поле: в description (последний
@@ -155,23 +170,23 @@ internal (последним, для addressing в тулах). Ссылайся
## Loop-mode — «поработай очередь»
Один триггер-сёрфейс: «поработай очередь» / «work the queue» / «прогони доску»
→ этот режим. Work the board **в этой сессии**: claim → работа → close → claim,
→ этот режим. Work the board **в этой сессии**: взять → работа → close → взять,
пока очередь не пуста или user не сказал стоп. **Интерактивный цикл, не демон.**
```
claim_next(project, owner) → пусто? → STOP «борд пуст» (не поллить)
таска
search(ready) → пусто? → STOP «борд пуст» (не поллить)
↓ кандидат
task_update(status=active, owner=<сессия>, version) → 409? → следующий кандидат
↓ взято
работа в этой сессии (read description + <slug>.md)
завершена? нет → park: blocked (внешний) | paused (возобновляемо) → claim_next
завершена? нет → park: blocked (внешний) | paused (возобновляемо) → дальше
↓ да
consult_policy (из claim): human-only/strict-human → STOP перед close/commit, спросить user
↓ auto
pre-close coverage check → task_close
pre-close coverage check → task_close(version)
session_break на таске? → да: печатай 🔚 SESSION BOUNDARY, STOP
↓ нет
claim_next
следующий кандидат
```
- **Пустая очередь — естественный стоп, не wait-loop.** Нет `CronCreate`, нет
@@ -181,12 +196,8 @@ claim_next …
- **Не завершаемая таска:** внешний блокер → `status=blocked` + blocker
(конкретный факт + что нужно); прервал ты (бюджет/стоп) → `status=paused` +
where_stopped. Одна упавшая таска не останавливает цикл — паркуй и дальше.
- **Heartbeat:** claim живёт ~10 мин — если таска дольше ~8 мин, `task_heartbeat`
периодически.
- **Consult-гейт:** `auto` → автопилот до close; `human-only`/`strict-human`
работай, затем **STOP перед close/commit** и спроси user. Push никогда не
автоматический (project-discipline Rule 4: commit freely, push по явному
гранту).
- **409 на взятие** → таска только что взята другим (сессия жива) → следующий
кандидат; 409 на close → свежая version из `entity_get`, повтор.
---
@@ -194,8 +205,8 @@ claim_next …
### Primary: mappa task_create
Создание задач — **через тул, не руками** (решение 20): сначала лиз
(`task_claim_next`) → `task_create`. Номер `t:N` назначает сервер — не выдумывай.
Создание задач — **через тул, не руками** (решение 20): карв-аут, лиз не нужен
(wiki:2660). Номер `task:N` назначает сервер — не выдумывай.
```
mcp__mappa__task_create(
@@ -205,8 +216,7 @@ mcp__mappa__task_create(
description: <markdown>, // тело; [[refs]] → рёбра (решение 4)
status: ready | active | paused | blocked | done, // по умолчанию ready
priority: P0 | P1 | P2, // только при создании; отсутствует → P1
due: yyyy-mm-dd, // только при создании; отсутствует = нет
claim_token: <токен лиза проекта> // мутация под лизом (решение 19)
due: yyyy-mm-dd // только при создании; отсутствует = нет
)
```
@@ -241,9 +251,7 @@ Slug-правила: короткий, lowercase, kebab-case, латиница.
Поля, которые разбирает поллер: `**Weight:**` (cheap-ok | needs-claude |
needs-human — **обязателен** для авто-взятия), `**Notify:**` (<owner>/<repo>),
`**Requirements:**`, `**Runtime allowed:**`, `**Consult policy:**`, `**Blocker:**`
(только на 🔵), `**Priority:**`/`**Due:**` (как выше). `**Owner:**/`**Claim
token:**/`**Claim expires at:**` — claim-штамп, пишет и чистит поллер; залипший
штамп на ⚪ блокирует поллер.
(только на 🔵), `**Priority:**`/`**Due:**` (как выше).
**Weight — поле, решающее взятие:** без `**Weight:**` поллер паркует в 🔵
(`no backend for weight_tier: unknown`). Обычный код → `needs-claude`;
@@ -253,10 +261,10 @@ token:**/`**Claim expires at:**` — claim-штамп, пишет и чисти
## Failure modes
- **422 busy** на `task_claim_next` → лиз держит другой агент; не параллель,
retry позже или спроси user.
- **409 на взятие** → таска уже взята (живая сессия владельца) → следующий
кандидат; не «бейся о замок».
- **task_close на незавершённую** → никогда. Park (blocked/paused).
- **claim истёк (zombie)** → не бросай claimed-таску: park/close по факту.
- **409 на close** → конфликт version → `entity_get` → повтори со свежей version.
- **notify не указан (legacy)** → без него boss не узнает о завершении.
- **weight не указан (legacy)** → поллер паркует (no backend for weight_tier).
- **update Priority/Due после создания** → сервер отклоняет; паркуй вопрос
@@ -264,11 +272,12 @@ token:**/`**Claim expires at:**` — claim-штамп, пишет и чисти
## What NOT to do
- **Не выдумывай номера** — `t:N` назначает сервер (решение 20).
- **Лиз-дисциплина:** мутации — только под лизом; 422 busy = кто-то другой пишет.
- **Не выдумывай номера** — `task:N` назначает сервер (решение 20).
- **Version-дисциплина:** мутации несут version из последнего чтения; 409 →
перечитай, не перезаписывай вслепую.
- **Одна активная таска** — только одна 🔴 на проект.
- **Never close без coverage check** — evidence на каждый acceptance criterion.
- **Не закрывай незавершённое** — park, не close; не оставляй claimed-таску.
- **Не закрывай незавершённое** — park, не close; не оставляй взятое.
- **Не бампай priority/due после создания** — территория человека.
- **Не «решай» задачи письмом/в чате** — борд — единственный источник правды
(канон mappa-messaging: «если это не на доске — это не задача»).