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:
@@ -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-письмо в инбокс цели — таска на борде не пингует живую сессию.
|
||||
|
||||
@@ -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. Формат предложения — один блок
|
||||
|
||||
@@ -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-письмо в инбокс получателя (кросс-проектная делегация).
|
||||
|
||||
|
||||
@@ -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>` — одну страницу-резюме на источник (~50–150 строк;
|
||||
ссылку на 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 может затронуть 10–15 страниц. Это нормально — для того 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_*.
|
||||
|
||||
@@ -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)
|
||||
— никогда не «решаются» внутри письма.
|
||||
|
||||
Следствие: **если это не на доске — это не задача и не решение, это разговор.**
|
||||
Значимый дизайн-выбор должен лечь на доску (или в вики), инбокс лишь указывает
|
||||
|
||||
@@ -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: «если это не на доске — это не задача»).
|
||||
|
||||
Reference in New Issue
Block a user