Compare commits

...

8 Commits

8 changed files with 1239 additions and 1135 deletions

View File

@@ -1,247 +1,252 @@
---
name: mappa-brainstorm-promote
author: ours
version: 1.6.0
version: 1.7.0
description: >
Finalize a matured brainstorm buffer (mappa entity type=brainstorm, status=buffer):
read buffer → choose target project → promote via mcp__mappa__brainstorm_promote
(atomic buffer → wiki-страница в target + archive, решение 7) → extract
action-items into target tasks (mcp__mappa__task_create, карв-аут) → review
umbrella → covering letter. Общий механизм mappa, как task.create/wiki.create —
никакой workshop-специфики. Старое имя — триггер-синоним:
workshop-promote-brainstorm. Triggers (user): «промоутни брейнсторм»,
«finalize <topic>», «выкати в вики», «promote <topic>».
Finalize a matured brainstorm buffer (mappa entity type=brainstorm,
status=buffer): read buffer → choose target project → promote via
mcp__mappa__brainstorm_promote (atomic buffer → wiki-page in target +
archive, decision 7) → extract action-items into target tasks
(mcp__mappa__task_create, carve-out) → review umbrella → covering letter.
A general mappa mechanism, like task.create/wiki.create — no workshop
specifics. Old name — trigger-synonym: workshop-promote-brainstorm.
Triggers (bilingual): «промоутни брейнсторм», «finalize <topic>», «выкати в
вики», «promote <topic>», "promote the brainstorm", "finalize <topic>".
---
# mappa-brainstorm-promote
Финализация созревшего брейнсторм-буфера, который живёт **как mappa-сущность
типа `brainstorm`** (status=buffer). Это общий механизм mappa — ровно как
`task.create` или `wiki.create`: буфер существует в mappa, скил доводит его до
конца (промоут контента в вики + action-items тасками). Никакой
workshop-специфики: скил триггерится из любой папки, работает с brainstorm-
сущностями любого проекта.
Finalizing a matured brainstorm buffer that lives **as a mappa entity of type
`brainstorm`** (status=buffer). This is a general mappa mechanism — exactly
like `task.create` or `wiki.create`: the buffer exists in mappa, the skill
takes it to the end (promote the content into the wiki + action-items as
tasks). No workshop specifics: the skill triggers from any folder, works with
brainstorm entities of any project.
Процедура линейная (от чтения буфера до промоута и тасок), не цикл: запускается
явно на финальном буфере и доводит его до конца. В форкфлоу встаёт между
работой (`mappa-task-work`) и финишем (`mappa-closing-ritual`).
The procedure is linear (from reading the buffer to promotion and tasks), not
a loop: it's launched explicitly on the final buffer and takes it to the end.
In the forkflow it sits between work (`mappa-task-work`) and finish
(`mappa-closing-ritual`).
**Промоут контента — всегда через `mcp__mappa__brainstorm_promote`:**
атомарно создаёт wiki-страницу (slug из буфера, body сохраняется) в проекте из
вызова и переводит буфер в `archive` (номер/slug стабильны, решение 20; рёбра
parent_of, событие `brainstorm.promoted`). Никаких файловых каналов. Action-items
уходят тасками в target-проект через `mcp__mappa__task_create` (карв-аут без
лиза, #1054; последовательно, не батчем).
**Content promotion — always via `mcp__mappa__brainstorm_promote`:**
atomically creates a wiki page (slug from the buffer, body preserved) in the
project from the call and moves the buffer to `archive` (number/slug stable,
decision 20; parent_of edges, `brainstorm.promoted` event). No file channels.
Action-items go as tasks to the target project via `mcp__mappa__task_create`
(carve-out without a lease, #1054; sequentially, not batched).
## When to use
- «промоутни брейнсторм», «finalize <topic>», «выкати в вики», «promote
<topic>».
- Пользователь ссылается на brainstorm-сущность (brainstorm:N) или на тему
буфера, который созрел и готов к промоушену.
<topic>», "promote the brainstorm".
- The user references a brainstorm entity (brainstorm:N) or a buffer topic
that matured and is ready for promotion.
## Inputs
- Brainstorm-реф `brainstorm:N` или `<topic>` (slug/тема буфера) + project (если
буфер не в текущем проекте — спросить).
- Для skill-ветки дополнительно: `<name>` нового скила (если не указан —
спросить, предложить производное от topic).
- Brainstorm ref `brainstorm:N` or `<topic>` (buffer slug/topic) + project (if
the buffer is not in the current project — ask).
- For the skill branch additionally: `<name>` of the new skill (if not
specified — ask, propose a derivation from the topic).
## Decision flow
```
brainstorm-сущность в mappa (type=brainstorm, status=buffer)
brainstorm entity in mappa (type=brainstorm, status=buffer)
find + read (entity_search type=brainstorm → entity_get полный body)
find + read (entity_search type=brainstorm → entity_get full body)
ask: target project (куда промоутить)
ask: target project (where to promote)
├── обычный проект → brainstorm_promote(project=<target>)
│ → wiki-страница (спека) в вики target
├── ordinary project → brainstorm_promote(project=<target>)
│ → wiki page (spec) in the target wiki
└── skill → dialog: description (trigger contract)
→ preview + confirm
→ mkdir + Write SKILL.md (каркас) в ~/projects/skills/
→ mkdir + Write SKILL.md (skeleton) in ~/projects/skills/
→ git add + commit (local, no push/install)
parse action-items from buffer body
parse action-items from the buffer body
for each: mcp__mappa__task_create (ПОСЛЕДОВАТЕЛЬНО, не батчем)
for each: mcp__mappa__task_create (SEQUENTIALLY, not batched)
review-umbrella: mcp__mappa__task_create (blocked, blocker=impl#)
covering-письмо: mcp__mappa__inbox_send (канон mappa-delegation)
covering letter: mcp__mappa__inbox_send (mappa-delegation canon)
финальный отчёт (wiki:NNNN — спека, brainstorm:N — архив, таски)
final report (wiki:NNNN — spec, brainstorm:N — archive, tasks)
```
## Steps
1. **Найти буфер в mappa.** `mcp__mappa__entity_search(type='brainstorm',
project=<проект>, q=<topic>)` → в результатах brainstorm:N + **internal id**.
Прочитать полный буфер: `mcp__mappa__entity_get(id=<internal id>)` — тело =
running record (frontmatter + раунды).
1. **Find the buffer in mappa.** `mcp__mappa__entity_search(type='brainstorm',
project=<project>, q=<topic>)` → in the results brainstorm:N + **internal
id**. Read the full buffer: `mcp__mappa__entity_get(id=<internal id>)` —
body = running record (frontmatter + rounds).
Если буфера нет в mappa — создать brainstorm-сущность через
`mcp__mappa__brainstorm_create` (или HTTP `POST /entities` type=brainstorm,
контракт решения 7/#1054). Не изобретать файловые буферы.
If the buffer is not in mappa — create a brainstorm entity via
`mcp__mappa__brainstorm_create` (or HTTP `POST /entities` type=brainstorm,
contract decision 7/#1054). Don't invent file buffers.
2. **Показать summary буфера (≤2 абзаца).**
2. **Show the buffer summary (≤2 paragraphs).**
3. **Спросить target-проект** — куда промоутить контент. По умолчанию — проект,
где буфер живёт (брейншторм ведут там, где тема релевантна). Проверить, что
проект существует в mappa: `mcp__mappa__entity_search` type=project
(или `mcp__mappa__entity_search` с q=<имя проекта>). Если нет — abort с
сообщением.
3. **Ask the target project** — where to promote the content. Default — the
project where the buffer lives (brainstorms are run where the topic is
relevant). Verify the project exists in mappa:
`mcp__mappa__entity_search` type=project (or `mcp__mappa__entity_search`
with q=<project name>). If not — abort with a message.
4. **Если target = skill (пользователь хочет оформить как скил):**
- Спросить `<name>` нового скила (если не указан) — валидный slug
4. **If target = skill (the user wants it as a skill):**
- Ask `<name>` of the new skill (if not specified) — a valid slug
(`[a-z0-9-]+`).
- Валидация (порядок важен): сначала проверить, что `~/projects/skills/`
сам репозиторий существует. Если нет — **abort** с сообщением «клонируй
skills через update-skills или вручную».
- Затем: `~/projects/skills/skills/<name>/` НЕ должна существовать.
Если существует — **abort** с сообщением «скил `<name>` уже существует,
обновляйся обычным маршрутом в `~/projects/skills/`, этот скил не для
апдейтов».
- Двухпроходной каркас: диалог по `description` (триггер-контракт активации:
минимум 2-3 фразы, пары русский/английский; что делает; антипаттерны) →
preview + confirm → `Write` каркаса (шапка + 6 пустых секций) → локальный
`git commit` в `~/projects/skills/`. **Без** install.sh, push,
build-hermes — это в baseline-тасках шага 7. Тело каркаса дописывается
вторым проходом глазами (вне scope этого скила).
- Validation (order matters): first check that `~/projects/skills/` itself
is a repository. If not — **abort** with the message "clone skills via
update-skills or manually".
- Then: `~/projects/skills/skills/<name>/` must NOT exist. If it exists —
**abort** with the message "skill `<name>` already exists, update through
the normal route in `~/projects/skills/`, this skill is not for updates".
- Two-pass skeleton: dialog on `description` (activation trigger contract:
minimum 2-3 phrases, Russian/English pairs; what it does; antipatterns) →
preview + confirm → `Write` of the skeleton (header + 6 empty sections) →
local `git commit` in `~/projects/skills/`. **Without** install.sh, push,
build-hermes — those are in the baseline tasks of step 7. The body of the
skeleton is written in a second pass by eye (outside this skill's scope).
5. **Промоут контента (всегда через `brainstorm_promote`, решение 7):**
5. **Content promotion (always via `brainstorm_promote`, decision 7):**
`mcp__mappa__brainstorm_promote(project=<target>, brainstorm_id=<internal id>)`
- Атомарно: buffer → wiki-страница (slug из буфера, body сохраняется, рёбра
parent_of buffer→wiki и refs→buffer) + буфер → `archive` + значимое событие
`brainstorm.promoted`.
- **Frontmatter-summary (wiki:2661):** убедиться, что в теле буфера есть
`summary:` одной строкой в frontmatter — карточки `wiki.search` читают его.
Если нет — дописать через `mcp__mappa__brainstorm_update` (PATCH
/brainstorm/:id, title/body/status, optimistic version+409) перед промоутом.
- Повторный промоут архивированного буфера → ошибка (one-shot, идемпотентно
через статус). Сверить `brainstorm_id` (internal) из шага 1.
- Если `brainstorm_promote` упал (конфликт версии, 409) → retry со свежим
internal id; при стабильном отказе — abort до создания тасок.
- Atomically: buffer → wiki page (slug from the buffer, body preserved,
parent_of buffer→wiki edges and refs→buffer) + buffer → `archive` +
significant `brainstorm.promoted` event.
- **Frontmatter-summary (wiki:2661):** make sure the buffer body has
`summary:` as one line in the frontmatter — `wiki.search` cards read it.
If missing — append via `mcp__mappa__brainstorm_update` (PATCH
/brainstorm/:id, title/body/status, optimistic version+409) before the
promotion.
- Re-promoting an archived buffer → error (one-shot, idempotent via
status). Cross-check `brainstorm_id` (internal) from step 1.
- If `brainstorm_promote` failed (version conflict, 409) → retry with the
fresh internal id; on a stable failure — abort before creating tasks.
6. **Парсинг action-items:** regex по строкам вида `- [ ] ...` в теле буфера,
секции после `## Следующие шаги`/`## TODO`/`## Next steps`/
`## Action items`. Показать список, дать редактировать/удалять/добавлять.
Если 0 action-items — продолжить, не блокировать.
6. **Action-items parsing:** regex over lines like `- [ ] ...` in the buffer
body, sections after `## Следующие шаги`/`## TODO`/`## Next steps`/
`## Action items`. Show the list, allow editing/removing/adding. If 0
action-items — continue, don't block.
7. **Создание тасок:**
7. **Task creation:**
> **NB:** таски создавать **ПОСЛЕДОВАТЕЛЬНО**, не батчем. Один
> `task_create` → дождаться ответа → следующий.
> **NB:** create tasks **SEQUENTIALLY**, not batched. One `task_create` →
> wait for the response → the next one.
- **Обычный target:** для каждого action-item —
- **Ordinary target:** for each action-item —
`mcp__mappa__task_create(project=<target>, slug=<kebab>, title, description,
status='ready')`. Create — карв-аут, лиз не нужен (wiki:2660/#1054).
Описание импл-таски ссылается на спеку (wiki:NNNN из шага 5).
- **Skill:** три baseline-таски в `project='skills'`:
- `[<name>-install]` — запустить `install.sh` в `~/projects/skills/`,
проверить активацию в новой сессии.
- `[<name>-hermes-mapping]` — запись в `~/projects/skills/hermes/mapping.yaml`
(режим `auto` для стилевых, `pending` если трогает тулы/окружение).
- `[<name>-test-trigger]` — прогнать триггер-фразы из description:
активируется на своих, не активируется на 2-3 близких чужих.
Плюс content-таски из буфера (если были) — тоже в `project='skills'`,
status='ready')`. Create — carve-out, no lease needed (wiki:2660/#1054).
The impl task description references the spec (wiki:NNNN from step 5).
- **Skill:** three baseline tasks in `project='skills'`:
- `[<name>-install]` — run `install.sh` in `~/projects/skills/`,
verify activation in a new session.
- `[<name>-hermes-mapping]` — a record in
`~/projects/skills/hermes/mapping.yaml` (mode `auto` for style ones,
`pending` if it touches tools/environment).
- `[<name>-test-trigger]` — run the trigger phrases from the description:
activates on its own, doesn't activate on 2-3 close foreign ones.
Plus content tasks from the buffer (if any) — also in `project='skills'`,
slug-prefix `<name>-`.
- Если N-я таска упала — продолжить остальные, в конце сообщить какие
созданы / какие нет. Запомнить slug'и для review-umbrella.
- If the N-th task failed — continue the rest, report at the end which were
created / which weren't. Remember the slugs for the review-umbrella.
8. **Review-umbrella (для target с импл-тасками и для skill — всегда):**
8. **Review-umbrella (for a target with impl tasks, and for skill — always):**
`mcp__mappa__task_create(project=<target>, slug=<topic>-review,
status='blocked', blocker=<номера импл-тасок через запятую>, description=<чек-лист>)`
status='blocked', blocker=<impl task numbers separated by commas>, description=<checklist>)`
- **Кто делает:** не имплементер. Следующая сессия в этом проекте (другая
модель / другой день / другой агент) с чистым контекстом. «Я только что
это написал» bias = главный риск.
- Чек-лист: прочитать спеку (wiki:NNNN из шага 5), `git log` shipped-коммитов,
для каждой импл-таски прогнать тесты и сверить с acceptance criteria,
findings → follow-up tasks через `task_create`.
- Закрытие: все findings зафайлены ИЛИ ревьюер подтвердил «нет findings»
в close-note.
- Если review-таска упала — сообщить, **продолжить** к шагу 9 (промоут уже
сделан, буфер в archive).
- **Who does it:** not the implementer. The next session in this project (a
different model / different day / different agent) with a clean context.
The "I just wrote this" bias is the main risk.
- Checklist: read the spec (wiki:NNNN from step 5), `git log` of the
shipped commits, for each impl task run the tests and cross-check with
acceptance criteria, findings → follow-up tasks via `task_create`.
- Closing: all findings filed OR the reviewer confirmed "no findings" in
the close-note.
- If the review task failed — report, **continue** to step 9 (the promotion
is already done, the buffer is in archive).
9. **Covering-письмо в инбокс цели (канон mappa-delegation).** Таска на борде
не пингует живую сессию, письмо = пинг + контекст:
9. **Covering letter to the target's inbox (mappa-delegation canon).** A task
on the board doesn't ping a live session, a letter = ping + context:
`mcp__mappa__inbox_send(project=<target>, from=<своя папка>, subject='Промоушен
<topic>: таски <#N…>', body=<перечень + wiki:NNNN спека>)`
`mcp__mappa__inbox_send(project=<target>, from=<your folder>, subject='Promotion
<topic>: tasks <#N…>', body=<list + wiki:NNNN spec>)`
10. **Финальный отчёт пользователю:**
- Куда промочено: `wiki:NNNN` (спека в вики target).
- Архив: `brainstorm:N` (status=archive, номер стабилен).
- Какие таски созданы (ref, title, проект).
- **Для skill:** напомнить про второй проход «доведём `<name>`».
10. **Final report to the user:**
- Where it was promoted: `wiki:NNNN` (spec in the target wiki).
- Archive: `brainstorm:N` (status=archive, number stable).
- Which tasks were created (ref, title, project).
- **For skill:** remind about the second pass "let's flesh out `<name>`".
## Failure modes
- Буфер не найден в mappa (нет brainstorm-сущности) → abort, сообщить: создать
через `brainstorm_create` (шаг 1) или HTTP POST /entities.
- `entity_search`/`entity_get` упал (API-ошибка, не пустой результат) → abort
с текстом ошибки; не создавать буфер по догадке.
- Target-проект не существует в mappa → abort до промоута.
- `brainstorm_promote` упал (409 версия / стабильный отказ) → retry со свежим
internal id; при повторном отказе — abort до создания тасок. Буфер остаётся
в buffer — ретраится позже.
- Буфер уже `archive` (повторный вызов) → abort: промоут one-shot,
идемпотентность через статус (решение 7).
- `task_create` упал на N-й content-таске → продолжить остальные, сообщить
partial. Промоут уже сделан — буфер не откатывается.
- `task_create` review-umbrella упал → не блокировать, сообщить пользователю
(создать вручную из шага 8).
- `inbox_send` (covering-письмо) упал → промоут и таски не откатываются;
сообщить пользователю, письмо можно отправить позже (промоут уже виден
в графе/инбоксе цели).
- **Skill:** `~/projects/skills/` не существует → abort.
- **Skill:** `~/projects/skills/skills/<name>/` уже существует → abort.
- **Skill:** пользователь не подтвердил preview → abort, состояние не меняется.
- **Skill:** локальный `git commit` в `~/projects/skills/` упал → файл остаётся,
сообщить что коммит нужно сделать руками; промоут буфера не блокируется.
- Buffer not found in mappa (no brainstorm entity) → abort, report: create via
`brainstorm_create` (step 1) or HTTP POST /entities.
- `entity_search`/`entity_get` failed (API error, not an empty result) → abort
with the error text; don't create a buffer by guess.
- Target project doesn't exist in mappa → abort before promotion.
- `brainstorm_promote` failed (409 version / stable refusal) → retry with the
fresh internal id; on a repeated failure — abort before creating tasks. The
buffer stays in buffer — retried later.
- Buffer already `archive` (repeated call) → abort: promotion is one-shot,
idempotence via status (decision 7).
- `task_create` failed on the N-th content task → continue the rest, report
partial. The promotion is already done — the buffer is not rolled back.
- `task_create` review-umbrella failed → don't block, report to the user
(create manually from step 8).
- `inbox_send` (covering letter) failed → promotion and tasks are not rolled
back; report to the user, the letter can be sent later (the promotion is
already visible in the graph/inbox of the target).
- **Skill:** `~/projects/skills/` doesn't exist → abort.
- **Skill:** `~/projects/skills/skills/<name>/` already exists → abort.
- **Skill:** user didn't confirm the preview → abort, state unchanged.
- **Skill:** local `git commit` in `~/projects/skills/` failed → the file
stays, report that the commit needs to be done by hand; the buffer promotion
is not blocked.
## Side effects
- **Всегда:** `brainstorm_promote` — атомарно wiki-страница в target + буфер →
`archive` + рёбра parent_of (wiki→buffer, refs→buffer) + событие
`brainstorm.promoted`.
- **Обычный target:** спека-страница в вики целевого проекта (с
frontmatter-summary, wiki:2661) + импл-таски + review-umbrella + covering-письмо.
- **Skill:** каркас `~/projects/skills/skills/<name>/SKILL.md` (только шапка +
пустой 6-секционный каркас) + локальный коммит в `~/projects/skills/`.
**Без** install.sh, push, build-hermes — это в baseline-тасках.
- Создаёт N тасок в target через `mcp__mappa__task_create` (карв-аут).
- Создаёт review-umbrella таску (status=blocked, blocker=impl#).
- Отправляет covering-письмо в инбокс target.
- **Always:** `brainstorm_promote` — atomically wiki page in the target +
buffer → `archive` + parent_of edges (wiki→buffer, refs→buffer) +
`brainstorm.promoted` event.
- **Ordinary target:** spec page in the target project's wiki (with
frontmatter-summary, wiki:2661) + impl tasks + review-umbrella + covering letter.
- **Skill:** skeleton `~/projects/skills/skills/<name>/SKILL.md` (only header +
empty 6-section skeleton) + local commit in `~/projects/skills/`.
**Without** install.sh, push, build-hermes — those are in the baseline tasks.
- Creates N tasks in the target via `mcp__mappa__task_create` (carve-out).
- Creates a review-umbrella task (status=blocked, blocker=impl#).
- Sends a covering letter to the target's inbox.
## What NOT to do
- **Не использовать файловые каналы** — буфер живёт в mappa brainstorm-сущности,
никаких `.brainstorm/`/`.archive/` записей.
- **Не использовать `mcp__projects-meta__tasks_create` / `knowledge_ingest` /
`knowledge_promote`** — файловые каналы выпилены. Таски
`mcp__mappa__task_create`, вики — `brainstorm_promote` (контент) +
`wiki_create`/`wiki_update` (доп. страницы).
- Не делать `git mv` буфера в архив — промоут архивирует сам.
- Не удалять буфер вместо промоута — теряется граф-история (parent_of, refs).
- Не батчить `task_create` (гонка; инцидент 2026-08-24: 6/7 упали) — только
последовательно.
- Не забывать covering-письмо — таска на борде не пингует живую сессию.
- **Skill:** не переформатировать тело буфера в каркас автоматически — тело
дописывается вторым проходом глазами.
- **Skill:** не запускать `install.sh`, не делать push, не править
`hermes/mapping.yaml` — это baseline-таски.
- **Skill:** не промоутить в существующий скил (abort).
- **Don't use file channels** — the buffer lives in a mappa brainstorm entity,
no `.brainstorm/`/`.archive/` records.
- **Don't use `mcp__projects-meta__tasks_create` / `knowledge_ingest` /
`knowledge_promote`** — file channels are removed. Tasks
`mcp__mappa__task_create`, wiki — `brainstorm_promote` (content) +
`wiki_create`/`wiki_update` (extra pages).
- Don't `git mv` the buffer into the archive — the promotion archives it itself.
- Don't delete the buffer instead of promoting — the graph history is lost
(parent_of, refs).
- Don't batch `task_create` (race; incident 2026-08-24: 6/7 failed) — only
sequentially.
- Don't forget the covering letter — a task on the board doesn't ping a live session.
- **Skill:** don't automatically reformat the buffer body into the skeleton —
the body is written in a second pass by eye.
- **Skill:** don't run `install.sh`, don't push, don't edit
`hermes/mapping.yaml` — those are baseline tasks.
- **Skill:** don't promote into an existing skill (abort).

View File

@@ -1,148 +1,152 @@
---
name: mappa-closing-ritual
author: ours
version: 1.2.0
version: 1.3.0
description: >
Финиш-фаза форкфлоу: handoff write (mcp__mappa__handoff_write, версия h:N)
+ PROPOSE wiki-ingest + PROPOSE закрытий тасок. Мутации — только после
подтверждения пользователя. Ad-hoc: mode=light — явный вопрос «Сделать
handoff?» в конце сессии (НЕ автоматический sweep), решение за человеком.
Старое имя — триггер-синоним: session-handoff (write-часть). Чтение на
старте — mappa-session-orient. Триггеры: «завершаем сессию», «сворачиваемся»,
«закругляемся», «wrap up session», «end session», «we're done for now».
Анти-триггеры (task-зона, не session-end): «закрываем эту таску», «pause»,
«отбой», «разбегаемся».
Finish phase of the forkflow: handoff write (mcp__mappa__handoff_write,
version h:N) + PROPOSE wiki-ingest + PROPOSE task closes. Mutations — only
after user confirmation. Ad-hoc: mode=light — an explicit question "Write
handoff?" at the end of the session (NOT an automatic sweep), the decision
is the human's. Old name — trigger-synonym: session-handoff (write part).
Reading at start — mappa-session-orient. Triggers (bilingual): «завершаем
сессию», «сворачиваемся», «закругляемся», "wrap up session", "end session",
"we're done for now". Anti-triggers (task-zone, not session-end):
«закрываем эту таску», «pause», «отбой», «разбегаемся».
---
# mappa-closing-ritual
Финиш-фаза цикла агента: **записать handoff → предложить wiki-ingest →
предложить закрытия тасок**. Старт ≠ финиш: чтение handoff на старте
`mappa-session-orient`; здесь — write-путь с процедурой и подтверждением.
Finish phase of the agent cycle: **write handoff → propose wiki-ingest →
propose task closes**. Start ≠ finish: reading the handoff at start
`mappa-session-orient`; here — the write path with procedure and confirmation.
Канал — **handoff-сущность Mappa** (`mcp__mappa__handoff_write`, тип `h:`,
per-project): поля `session_id`/`date`/`status`/`summary`/`open_treks[]`/
`ask_user[]`/`guards[]`/`recent_commits[]`. Каждый write = **новая версия**
(append-only, versioned-история). Файлового `.tasks/NEXT_SESSION.md` больше нет.
Channel — the **Mappa handoff entity** (`mcp__mappa__handoff_write`, type
`h:`, per-project): fields `session_id`/`date`/`status`/`summary`/
`open_treks[]`/`ask_user[]`/`guards[]`/`recent_commits[]`. Each write = a **new
version** (append-only, versioned history). The file-based
`.tasks/NEXT_SESSION.md` no longer exists.
## Когда использовать
## When to use
- Session-end фразы: «завершаем сессию», «сворачиваемся», «закругляемся»,
«wrap up session», «end session», «we're done for now».
- Ad-hoc-сессия без трека/таски в конце: **mode=light**явный вопрос
«Сделать handoff?» (не автоматический sweep), решение за человеком.
- AGENTS.md проекта содержит trigger-строку `session handoff: read on start, write on end`.
- Session-end phrases: «завершаем сессию», «сворачиваемся», «закругляемся»,
"wrap up session", "end session", "we're done for now".
- Ad-hoc session without a track/task at the end: **mode=light**an explicit
question "Write handoff?" (not an automatic sweep), the human decides.
- The project's AGENTS.md contains the trigger line
`session handoff: read on start, write on end`.
**Skip (task-зона, не session-end):** «закрываем эту таску» (task close →
**Skip (task-zone, not session-end):** «закрываем эту таску» (task close →
`mappa-task-work`), «pause», «приостанови» (task-pause), «отбой», «разбегаемся»
(слишком broad), «сейчас завершу одну задачу и тогда поговорим» (частичное
завершение). При неоднозначности — **ASK**: «закрываем сессию или таску?»
(too broad), "let me finish one task first, then we'll talk" (partial finish).
On ambiguity — **ASK**: "are we closing the session or a task?"
## Steps
### 1. Scope check
Это текущий проект (cwd). Никаких global мутаций, никаких других проектов.
This is the current project (cwd). No global mutations, no other projects.
### 2. Mid-task capture
Если есть 🔴 active таска проекта (борд mappa / `.tasks/`) — захватить в summary:
If there is a 🔴 active task of the project (mappa board / `.tasks/`) — capture
into summary:
```
left mid-task: <slug>
where_stopped: <одна строка>
where_stopped: <one line>
```
Без борда — писать handoff без mid-task секции, не блокировать.
No board — write the handoff without the mid-task section, don't block.
### 3. Compose content (поля handoff)
### 3. Compose content (handoff fields)
- `session_id``<ISO дата>` или идентификатор сессии;
- `status``active` (работа продолжается) / `paused` (заморожено) / `done` (завершено);
- `summary`связка: где остановились, mid-task, ключевые решения;
- `open_treks`массив открытых треков (готовность + entry-point);
- `ask_user` — pending решения / ожидаемые разрешения;
- `guards`«не делать» (preemptive guards);
- `recent_commits` — 35 последних коммитов (`<slug>: <subject>`).
- `session_id``<ISO date>` or session identifier;
- `status``active` (work continues) / `paused` (frozen) / `done` (finished);
- `summary`the link: where we stopped, mid-task, key decisions;
- `open_treks`array of open tracks (readiness + entry-point);
- `ask_user` — pending decisions / expected resolutions;
- `guards`"don't do" (preemptive guards);
- `recent_commits` — 35 latest commits (`<slug>: <subject>`).
Forward-looking, не timeline: handoff = связка новых вещей конкретно для
следующего разворота, не overview всего проекта. Борд mappa / вики остаются
авторитетными для своего scope'ане дублировать их в handoff.
Forward-looking, not a timeline: handoff = a link of new things specifically
for the next turn, not an overview of the whole project. The mappa board / wiki
remain authoritative for their own scope — don't duplicate them in the handoff.
### 4. Append
`mcp__mappa__handoff_write(project=<имя>, session_id, status, summary, open_treks?, ask_user?, guards?, recent_commits?)`сервис создаёт новую версию `h:N` (предыдущие остаются; чтение последней — `entity_search(type='handoff', project, limit=1)`). Поля-массивы передавать как `[]`, если пусто (следующий агент видит: пусто, не забыто). Пишется без лиза (как инбокс).
`mcp__mappa__handoff_write(project=<name>, session_id, status, summary, open_treks?, ask_user?, guards?, recent_commits?)`the service creates a new `h:N` version (previous ones remain; reading the latest — `entity_search(type='handoff', project, limit=1)`). Pass array fields as `[]` when empty (the next agent sees: empty, not forgotten). Written without a lease (like inbox).
> **Confirm gate:** handoff write — мутация. В mode=light (ad-hoc) и при
> неоднозначности — сначала явный вопрос «Сделать handoff?», только после «да»
> **Confirm gate:** handoff write is a mutation. In mode=light (ad-hoc) and on
> ambiguity — first an explicit question "Write handoff?", only after "yes"
> write.
### 5. Propose wiki-ingest (не писать!)
### 5. Propose wiki-ingest (don't write!)
Если за сессию появилось durable-знание — **ПРЕДЛОЖИТЬ** ingest
(`mappa-knowledge`: mappa wiki create — карв-аут, update — version+409, wiki:2660), перечислив кандидатов.
Ничего не писать без подтверждения.
If durable knowledge appeared during the session — **PROPOSE** the ingest
(`mappa-knowledge`: mappa wiki create — carve-out, update — version+409,
wiki:2660), listing the candidates. Don't write anything without confirmation.
### 6. Propose task-board closes (не закрывать!)
### 6. Propose task-board closes (don't close!)
Если есть задачи, выглядящие закрытыми — **ПРЕДЛОЖИТЬ** закрытия
(`mappa-task-work`: task_close с version+409, wiki:2660). Уважать ralph-loop: verifier-задачи
закрывать только через verifier (attempt/harness-record).
If there are tasks that look closed — **PROPOSE** the closes
(`mappa-task-work`: task_close with version+409, wiki:2660). Respect ralph-loop:
verifier tasks close only via the verifier (attempt/harness-record).
### 7. Формат предложения — один блок
### 7. Proposal format — one block
```
Ритуал закрытия:
(а) заингестить X в вики?
(б) закрыть Y?
(в) ничего.
Closing ritual:
(a) ingest X into the wiki?
(b) close Y?
(c) nothing.
```
Ждать ответа. Отказ = пропуск (не настаивать, не повторять в этой сессии).
Wait for the answer. Refusal = skip (don't insist, don't repeat in this session).
---
## mode=light (ad-hoc-сессии)
## mode=light (ad-hoc sessions)
Ad-hoc-сессия (без таски/трека, но могут рождаться артефакты): след в mappa
пишется всегда (контракт сервиса — session live-ingest), но **структурирование —
по явному вопросу**: в конце сессии спроси «Сделать handoff?» (решение за
человеком). НЕ автоматический sweep: без «да» — ничего не писать, след
остаётся в mappa как есть.
Ad-hoc session (no task/track, but artifacts may be born): the trace in mappa
is always written (service contract — session live-ingest), but
**structuring happens only on an explicit question**: at the end of the
session ask "Write handoff?" (the human decides). NOT an automatic sweep:
without "yes" — write nothing, the trace stays in mappa as is.
## Failure modes
- **Secret detected.** Контент матчит паттерны секретов (`AKIA...`, `sk-...`,
- **Secret detected.** Content matches secret patterns (`AKIA...`, `sk-...`,
`ghp_...`, `ssh-rsa`, `BEGIN PRIVATE KEY`, `password=`/`token=`) → **abort
write**. Сообщить user'у с указанием подозрительной строки.
- **Неоднозначная фраза** → ASK «закрываем сессию или таску?», не угадывать.
- **Mid-task без борда** → handoff без mid-task секции, не блокировать.
- **User отказал на предложения ритуала** → пропустить, не настаивать.
- **Проект не в mappa** → silent exit (первая сессия).
write**. Tell the user with the suspicious line indicated.
- **Ambiguous phrase** → ASK "are we closing the session or a task?", don't guess.
- **Mid-task without a board** → handoff without the mid-task section, don't block.
- **User refused the ritual proposals** → skip, don't insist.
- **Project not in mappa** → silent exit (first session).
## Side effects
- Пишет handoff-сущность проекта (append-only, versioned-история). Никаких
файлов, никаких git-коммитов за handoff.
- Ритуал **предлагает** wiki-ingest и закрытия тасок — но НЕ пишет их без «да».
- Никаких global мутаций, никаких других проектов.
- Writes the project's handoff entity (append-only, versioned history). No
files, no git commits for the handoff.
- The ritual **proposes** wiki-ingest and task closes — but does NOT write
them without "yes".
- No global mutations, no other projects.
## What NOT to do
- **Не auto-execute** — каждая мутация (handoff write / wiki-ingest / task
close) только после явного «да» пользователя.
- **Не гонять ритуал на substantive commit.** Только session-end фраза (или
явный запрос user'а).
- **Не sweep автоматически в ad-hoc** — mode=light: явный вопрос «Сделать
handoff?», решение за человеком.
- **Не писать секреты** в handoff. Матч secret-patterns → abort.
- **Не писать handoff на каждый commit** — только финиш.
- **Не дублировать борд / вики** — handoff = forward-looking связка.
- **Не cross-project** — per-project scope.
- **Не считать handoff авторитетным** на стороне читателя — это рекомендация
прошлой сессии, не директива; user может override.
- **No auto-execute** — every mutation (handoff write / wiki-ingest / task
close) only after an explicit "yes" from the user.
- **Don't run the ritual on a substantive commit.** Only a session-end phrase
(or an explicit user request).
- **Don't sweep automatically in ad-hoc** — mode=light: an explicit question
"Write handoff?", the human decides.
- **Don't write secrets** into the handoff. Secret-pattern match → abort.
- **Don't write a handoff on every commit** — only at the finish.
- **Don't duplicate the board / wiki** — handoff = forward-looking link.
- **No cross-project** — per-project scope.
- **Don't treat the handoff as authoritative** on the reader's side — it's the
previous session's recommendation, not a directive; the user can override.
## Reference
- Чтение handoff на старте: `mappa-session-orient`.
- Закрытия тасок: `mappa-task-work` (pre-close coverage, notify-письмо).
- Reading the handoff at start: `mappa-session-orient`.
- Task closes: `mappa-task-work` (pre-close coverage, notify letter).
- Wiki-ingest: `mappa-knowledge`.
- Письма: `mappa-messaging`. Делегирование: `mappa-delegation`.
- Session live-ingest (след в mappa пишется всегда): `concepts/session-live-ingest` (wiki:2604).
- Letters: `mappa-messaging`. Delegation: `mappa-delegation`.
- Session live-ingest (the trace in mappa is always written): `concepts/session-live-ingest` (wiki:2604).

View File

@@ -1,209 +1,261 @@
---
name: mappa-delegation
author: ours
version: 1.2.0
version: 1.3.0
description: >
Цикл делегирования задачи другому агенту/проекту: pre-flight gate →
шаблон тела → dry-run preview → confirm → covering-письмо в инбокс
получателя → парная review-таска для impl. Каждая кросс-проектная делегация
— пара: tasks_create + письмо (event: created) — таска на борде не пингует
живую сессию. Старое имя — триггер-синоним: delegate-task. Триггеры:
«делегировать таску», «delegate task», «создать задачу на агента»,
«поставить задачу агенту», «tasks_create для». НЕ применимо: self-assigned
таски на своей доске («создать задачу себе» → mappa-task-work), работа
своими руками, workshop-внутренние таски.
The cycle of delegating a task to another agent/project: pre-flight gate →
body template → dry-run preview → confirm → covering letter to the
recipient's inbox → paired review task for impl. Every cross-project
delegation is a pair: tasks_create + letter (event: created) — a task on the
board does not ping a live session. Old name — trigger-synonym:
delegate-task. Triggers (bilingual): «делегировать таску», «создать задачу
на агента», «поставить задачу агенту», «tasks_create для», "delegate task",
"create a task for an agent", "assign a task to an agent". NOT applicable:
self-assigned tasks on your own board («создать задачу себе» →
mappa-task-work), doing work yourself, workshop-internal tasks.
---
# mappa-delegation
Унифицированный **цикл постановки задач на агентов**: от pre-flight гейта до
covering-письма получателю. Гарантирует, что каждая делегированная задача
содержит: обязательные скилы (императивный invoke), pre-flight разрешения,
steering-loop поля (notify/weight), парную review-таску для impl — и что
получатель реально узнаёт о задаче (письмо, не только борд).
Unified **cycle of assigning tasks to agents**: from the pre-flight gate to
the covering letter to the recipient. Guarantees that every delegated task
carries: mandatory skills (imperative invoke), pre-flight permissions,
steering-loop fields (notify/weight), a paired review task for impl — and that
the recipient actually learns about the task (letter, not just the board).
## When to use
Перед каждым вызовом `tasks_create` для другого проекта или агента.
Before every `tasks_create` call for another project or agent.
**Активируется:** «делегировать таску», «delegate task», «создать задачу на агента», «поставить задачу агенту», «tasks_create для».
**Activates:** «делегировать таску», «создать задачу на агента», «поставить задачу агенту», «tasks_create для», "delegate task", "create a task for an agent".
**Не применяется:**
- Работа которую выполняешь сам в текущей сессии.
- Self-assigned таски на своей доске («создать задачу себе», «task for myself», «поставить себе задачу») → `mappa-task-work`, не делегирование. Дизамбигуатор: «на агента»/«агенту»/«в проект X» = делегирование; «себе»/«myself» = своя доска.
- Workshop-internal таски (`.workshop/.tasks/` — workshop-meta, не делегирование).
- `tasks_create` с `target=agenda` (cross-project agenda — не делегирование агенту).
**Not applicable:**
- Work you do yourself in the current session.
- Self-assigned tasks on your own board («создать задачу себе», "task for myself") → `mappa-task-work`, not delegation. Disambiguator: «на агента»/«агенту»/«в проект X» = delegation; «себе»/"myself" = your own board.
- Workshop-internal tasks (`.workshop/.tasks/` — workshop-meta, not delegation).
- `tasks_create` with `target=agenda` (cross-project agenda — not delegation to an agent).
## Inputs
- `target_project` — qualified `<owner>/<repo>` (обязательно)
- `target_project` — qualified `<owner>/<repo>` (required)
- `slug` — kebab-case latin
- Краткое описание задачи (цель + acceptance criteria)
- Short task description (goal + acceptance criteria)
- `weight``cheap-ok | needs-claude | needs-human`
- `notify`slug проекта-комиссионера (кому писать inbox при close/park)
- `notify`commissioning project slug (who gets the inbox letter on close/park)
Номер задаче присваивает сервер (`tasks_create` из счётчика agenda/task-counter) — постановщик номер не придумывает и не резервирует. Возвращённый `#n` из preview/confirm — машинный ключ задачи: им ссылаются блокеры, письма, decision-trail.
The task number is assigned by the server (`tasks_create` from the
agenda/task-counter) — the assigner neither invents nor reserves it. The
returned `#n` from preview/confirm is the task's machine key: blockers,
letters, and the decision trail reference it.
> **Контракт (interactive, wiki:2660).** `task_create` — **карв-аут без
> лиза** (create-без-лиза как принцип); update/close — version+409 (409 →
> re-GET → retry). file channel — sha-CAS через Gitea. Claim/TTL нет
> «взять таску» = conditional update по version (поллер вне mappa).
> **Contract (interactive, wiki:2660).** `task_create` — **carve-out without a
> lease** (create-without-lease as a principle); update/close — version+409
> (409 → re-GET → retry). file channel — sha-CAS via Gitea. No claim/TTL —
> "take a task" = conditional update by version (poller outside mappa).
## Steps (цикл)
## Steps (the cycle)
### 1. Pre-flight gate (6 вопросов пользователю)
### 1. Pre-flight gate (6 questions to the user)
Спросить **до** составления тела задачи:
Ask **before** composing the task body:
0. **Критическая инфраструктура?** — задача меняет: поллер/агент-раннер, MCP серверы (projects-meta, interns), механизм claim/close/heartbeat, deploy-инфру (traefik, docker, systemd), CI/CD пайплайны, git hooks.
- Если **да**`weight: needs-human` принудительно, без обсуждения. Объяснить пользователю почему.
- Если **нет** → идти дальше.
1. **Интерны — разрешены?** (да/нет, per задача)
2. **Автопуш — разрешён?** (да/нет, per задача)
3. **Контекстные скилы сверх дефолтов?** — предложить по содержанию задачи (например `claude-api` для работы с Anthropic SDK, `frontend-design` для UI, `using-interns` если интерны разрешены), пользователь утверждает.
4. **notify — кому докладывать о завершении/затыке?** (slug проекта; обычно `.workshop` или `OpeItcLoc03/workshop`)
5. **Session-break после этой задачи?** — нужен ли разрыв сессии после её закрытия (domain-switch, milestone, heavy infra)?
- Если **да** → проставить `session_break` в теле задачи (см. шаблон): `true` или строка-hint с названием следующего трека. `mappa-task-work` остановится после close и предложит завершить сессию, не клеймя следующую задачу.
- Если **нет** → поле не добавлять (дефолт — агент продолжает цикл).
0. **Critical infrastructure?** — the task changes: poller/agent-runner, MCP
servers (projects-meta, interns), claim/close/heartbeat mechanics, deploy
infra (traefik, docker, systemd), CI/CD pipelines, git hooks.
- If **yes** → force `weight: needs-human`, no discussion. Explain to the
user why.
- If **no** → continue.
1. **Interns — allowed?** (yes/no, per task)
2. **Auto-push — allowed?** (yes/no, per task)
3. **Contextual skills beyond defaults?** — propose per task content (e.g.
`claude-api` for Anthropic SDK work, `frontend-design` for UI,
`using-interns` if interns are allowed); the user approves.
4. **notify — who gets the completion/block report?** (project slug; usually
`.workshop` or `OpeItcLoc03/workshop`)
5. **Session-break after this task?** — is a session break needed after it
closes (domain-switch, milestone, heavy infra)?
- If **yes** → set `session_break` in the task body (see template): `true`
or a string-hint with the next track's name. `mappa-task-work` will stop
after close and propose ending the session, without claiming the next task.
- If **no** → don't add the field (default — the agent continues the cycle).
### 2. Составить тело задачи по шаблону
### 2. Compose the task body per template
Секции строго по порядку:
Sections strictly in order:
```
<Цель — одно-два предложения. Acceptance criteria если есть.>
**Спека:** <path к design-решению или .brainstorm/…> — обязательно для задач
из дизайна/решения: импл читает дизайн, не угадывает
<Goal — one or two sentences. Acceptance criteria if any.>
**Spec:** <path to the design solution or .brainstorm/…> — mandatory for tasks
from design/decision: the impl reads the design, doesn't guess
## Обязательные скилы — вызвать до начала работы
## Mandatory skills — invoke before starting work
- invoke `tdd-criteria` — до написания кода
- invoke `mappa-task-work` — для управления статусом задачи
- invoke `project-discipline` — дисциплина коммитов/пушей
- invoke `mappa-knowledge` после закрытия — заингесть .wiki/concepts/<slug>.md
[если кросс-проектная: - invoke `using-projects-meta` — cross-project tasks/wiki]
[контекстные скилы из шага 1.3]
- invoke `tdd-criteria` — before writing code
- invoke `mappa-task-work` — for task status management
- invoke `project-discipline` — commit/push discipline
- invoke `mappa-knowledge` after closing — ingest .wiki/concepts/<slug>.md
[if cross-project: - invoke `using-projects-meta` — cross-project tasks/wiki]
[contextual skills from step 1.3]
**TDD:** да | нет — <причина>
**Разрешения:** интерны: да/нет | автопуш: да/нет
**TDD:** yes | no — <reason>
**Permissions:** interns: yes/no | auto-push: yes/no
**weight:** cheap-ok | needs-claude | needs-human
**notify:** <commissioning-project-slug>
[**allow_upgrade:** true/false]
[**session_break:** true | "<следующий трек / hint>"] # optional — mappa-task-work остановится после close, не клеймит следующую задачу
[**session_break:** true | "<next track / hint>"] # optional — mappa-task-work stops after close, doesn't claim the next task
```
**Когда ставить `session_break`** (опционально; по умолчанию НЕ ставить — это маркер реальной границы, не дефолт). Три случая:
**When to set `session_break`** (optional; by default DON'T set it — it marks
a real boundary, not a default). Three cases:
1. **Смена домена / репо** — задача завершает один трек перед переходом на несвязанный.
2. **Milestone-задача** — последняя в группе sub-tasks одной фичи.
3. **Тяжёлая инфра-задача** — shared checkout, migrations, deploy — где разумно остановиться и проверить состояние.
1. **Domain / repo switch** — the task finishes one track before moving to an
unrelated one.
2. **Milestone task** — the last in a group of sub-tasks of one feature.
3. **Heavy infra task** — shared checkout, migrations, deploy — where it's
reasonable to stop and check the state.
Значение: `true` (следующий трек = «см. STATUS.md») либо строка-hint с названием следующего трека. Потребитель — `mappa-task-work`: после close печатает `🔚 SESSION BOUNDARY …` и останавливается, не клеймя следующую задачу. Дизайн: `.wiki/concepts/delegate-task-session-break.md`.
Value: `true` (next track = "see STATUS.md") or a string-hint with the next
track's name. Consumer — `mappa-task-work`: after close it prints
`🔚 SESSION BOUNDARY …` and stops, without claiming the next task. Design:
`.wiki/concepts/delegate-task-session-break.md`.
**Этапные цепочки (staged breakdown):** если решение бьётся на этапы
(1 → 1b → 3), создавай каждый этап отдельной таской со `status: blocked` +
`blocker: <номера-предшественников> (#n1, #n2 — номера, не слаги; номер =
машинный ключ)`. Доска показывает порядок, поллер не возьмёт зависимую
работу раньше времени. Таски в один репо создавай последовательно, не
параллельно (иначе sha-lock конфликт — см. Failure modes).
**Staged breakdown:** if the solution splits into stages (1 → 1b → 3), create
each stage as a separate task with `status: blocked` + `blocker:
<predecessor numbers> (#n1, #n2 — numbers, not slugs; the number = machine
key)`. The board shows the order, the poller won't take dependent work early.
Create tasks in one repo sequentially, not in parallel (otherwise sha-lock
conflict — see Failure modes).
**Почему `invoke` а не триггер-фраза:** AGENTS.md ненадёжен (уплывает при compression, слабые модели игнорируют). Тело задачи читается активно — императив `invoke` это прямая команда, не пассивный матчинг.
**Why `invoke` and not a trigger phrase:** AGENTS.md is unreliable (drifts
under compression, weak models ignore it). The task body is read actively —
the imperative `invoke` is a direct command, not passive matching.
### 3. Dry-run preview
`tasks_create(confirm=false)`показать пользователю preview до реального коммита.
`tasks_create(confirm=false)`show the user the preview before the real commit.
### 4. Подтверждение и создание
### 4. Confirmation and creation
После OK пользователя: `tasks_create(confirm=true)`.
After the user's OK: `tasks_create(confirm=true)`.
### 5. Сопроводительное письмо — обязательно при кросс-проектной делегации
### 5. Covering letter — mandatory for cross-project delegation
После создания **каждая кросс-проектная делегация** дублируется письмом в
инбокс получателя (канон — `mappa-messaging`: канал Mappa, адрес из адресной
книги `~/projects/.wiki/concepts/projects-address-book.md`):
After creation, **every cross-project delegation** is duplicated by a letter to
the recipient's inbox (canon — `mappa-messaging`: Mappa channel, address from
the address book `~/projects/.wiki/concepts/projects-address-book.md`):
```
mcp__mappa__inbox_send(
project: <адрес-получателя>, # имя папки, из адресной книги
from: <своя-папка>,
project: <recipient address>, # folder name, from the address book
from: <your folder>,
subject: "[event: created] #n slug",
body: "1-2 строки — что за задача, почему, slug; «разбери и возьми»"
body: "1-2 lines — what the task is, why, slug; «sort it out and take it»"
)
```
(Мутации update/close — version-based (409 → retry); доставка письма
карв-аут, лиза не требует.)
(update/close mutations — version-based (409 → retry); letter delivery
carve-out, requires no lease.)
Причина: таска на борде **не пингует живую сессию** получателя. Поллер
подхватит по `Weight`/`Notify`, но живая интерактивная сессия узнаёт только
через inbox-монитор — т.е. через письмо. Правило «task + letter, не только
task» — общий случай (шаг 7 — его частность для downstream-задач).
Reason: a task on the board **does not ping the recipient's live session**.
The poller will pick it up by `Weight`/`Notify`, but a live interactive
session learns only through the inbox monitor — i.e. through the letter. The
rule "task + letter, not just task" — is the general case (step 7 is its
particular case for downstream tasks).
Пропуск: self-assigned задачи на своей доске; `target=agenda` (общая доска,
конкретного получателя нет — steering-loop через `Notify`).
Skip: self-assigned tasks on your own board; `target=agenda` (shared board,
no concrete recipient — steering-loop via `Notify`).
### 6. Парная review-таска (только для impl-задач)
### 6. Paired review task (impl tasks only)
Если задача имплементационная — создать парную `<slug>-review` (status=blocked, blocker=`#n` — номер impl-таски). Пропустить для: pointers-тасок, ops-тасок, research-тасок, любых non-impl.
If the task is implementation — create the paired `<slug>-review`
(status=blocked, blocker=`#n` — the impl task number). Skip for: pointer
tasks, ops tasks, research tasks, any non-impl.
**`weight` review-таски — наследовать от impl-таски, но не ниже `needs-claude`** (проставлять явно при `tasks_create`):
**`weight` of the review task — inherit from the impl task, but never below
`needs-claude`** (set explicitly at `tasks_create`):
- impl `needs-human` → review `needs-human` (критично-инфраструктурное изменение нельзя ревьюить слабым tier'ом — ревью наследует строгость impl).
- impl `needs-human` → review `needs-human` (a critical-infra change cannot
be reviewed by a weak tier — the review inherits the impl's strictness).
- impl `needs-claude` → review `needs-claude`.
- impl `cheap-ok` → review `needs-claude` (флор: review дисциплинарно-критична, см. What NOT to do — cheap-ok сюда не опускать).
- impl `cheap-ok` → review `needs-claude` (floor: review is
discipline-critical, see What NOT to do — don't drop to cheap-ok).
Без явного `weight` поллер не маршрутизирует review-таску (reconciler её пропускает) — поэтому проставлять всегда, даже когда impl и review совпадают по tier'у.
Without an explicit `weight` the poller won't route the review task (the
reconciler skips it) — so always set it, even when impl and review are at the
same tier.
### 7. Downstream-задача для ЖИВОЙ сессии → требовать task + inbox-письмо
### 7. Downstream task for a LIVE session → require task + inbox letter
Если тело задачи **поручает агенту самому создать downstream-задачу** для другого проекта, где работает **живая интерактивная сессия** (напр. прог сам ставит deploy-таску админу), — в ТЗ **явно потребуй И `tasks_create`, И inbox-письмо** тому проекту (`mcp__mappa__inbox_send(project=<target>, from=<своя>, subject="[event: created] #n slug", ...)`).
If the task body **instructs the agent to create a downstream task itself** for
another project where a **live interactive session** is working (e.g. the
programmer sets a deploy task for the admin) — in the spec **explicitly require
BOTH `tasks_create` AND the inbox letter** to that project
(`mcp__mappa__inbox_send(project=<target>, from=<yours>, subject="[event: created] #n slug", ...)`).
Причина: таска на борде живую сессию **НЕ пингует**. Поллер подхватит по `Weight`/`Notify`, но живая интерактивная сессия узнаёт только через inbox-монитор / Stop-хук — т.е. через письмо. ТЗ, требующее лишь `tasks_create`, оставляет downstream-таску висеть незамеченной, и кто-то доделывает пинг руками.
Reason: a task on the board does **NOT** ping the live session. The poller
will pick it up by `Weight`/`Notify`, but a live interactive session learns
only through the inbox monitor / Stop-hook — i.e. through the letter. A spec
that requires only `tasks_create` leaves the downstream task hanging unnoticed,
and someone finishes the ping by hand.
Правило: poller-driven таргет`Weight`/`Notify` обязательны; live-сессия → inbox-письмо обязательно; **не уверен, поллер или живой — требуй ОБА.** Это же правило применяй, когда пингуешь пира сам: task + letter, не только task.
Rule: poller-driven target`Weight`/`Notify` mandatory; live session → inbox
letter mandatory; **not sure poller or live — require BOTH.** Apply the same
rule when you ping a peer yourself: task + letter, not just task.
## Failure modes
- **Пользователь отказывает на pre-flight** → abort, задачу не создавать.
- **Пользователь отклоняет dry-run preview** → abort.
- **notify не указан** → переспросить, не пропускать молча. Без notify steering-loop не замыкается.
- **weight не указан** → переспросить. Без weight поллер не знает кому отдать задачу.
- **tasks_create упал** → различить: **PushRejected** (sha-lock конфликт —
репо уехало между preview и confirm; бывает при параллельном создании в один
репо) → **retry**: повторить confirm — сервер перечитает актуальный base_sha.
Другие ошибки → сообщить пользователю, не делать retry без явного запроса.
- **User refuses the pre-flight** → abort, don't create the task.
- **User rejects the dry-run preview** → abort.
- **notify not specified** → re-ask, don't skip silently. Without notify the
steering-loop doesn't close.
- **weight not specified** → re-ask. Without weight the poller doesn't know who
to give the task to.
- **tasks_create failed** → distinguish: **PushRejected** (sha-lock conflict —
the repo moved between preview and confirm; happens on parallel creation into
one repo) → **retry**: repeat the confirm — the server re-reads the actual
base_sha. Other errors → tell the user, don't retry without an explicit request.
## Side effects
- Создаёт таску в target-проекте через `tasks_create` (file channel — Gitea commit; service channel — mappa-сущность, create = карв-аут без лиза wiki:2660).
- Опционально создаёт парную review-таску (status=blocked).
- Covering-письмо в инбокс получателя (кросс-проектная делегация).
- Creates a task in the target project via `tasks_create` (file channel —
Gitea commit; service channel — mappa entity, create = carve-out without a
lease wiki:2660).
- Optionally creates the paired review task (status=blocked).
- Covering letter to the recipient's inbox (cross-project delegation).
## What NOT to do
- Не пропускать pre-flight gate — даже если кажется что всё очевидно.
- Не использовать пассивные триггер-фразы вместо `invoke` — «tdd-criteria» в тексте слабее чем «invoke `tdd-criteria`».
- Не пропускать `notify` — без него boss не узнает о завершении.
- Не пропускать `weight` — без него fleet routing слеп.
- Не создавать review-таску для pointers/ops/research задач — только для impl.
- Не создавать review-таску без `weight` — reconciler/поллер её пропустит. Наследовать от impl, флор `needs-claude` (см. Step 6).
- Не назначать `weight: cheap-ok` для задач где дисциплина критична (review, security, schema migration) — слабые модели могут игнорировать invoke-инструкции.
- Не назначать `weight: needs-claude` или `cheap-ok` задачам, меняющим критическую инфраструктуру (поллер, MCP серверы, deploy, CI/CD) — только `needs-human`.
- Не ставить `session_break` рутинно на каждую задачу — это маркер реальной границы (domain-switch / milestone / heavy infra), не дефолт; иначе `mappa-task-work` рвёт сессию после каждого close.
- **Не создавать задачи из дизайна/решения без `**Спека:**`-ссылки** —
импл-агент угадывает пороги/скоуп вместо чтения дизайна.
- **Не создавать несколько тасок в один репо параллельно** — sha-lock
конфликты (PushRejected); сериализуй confirm'ы.
- **Не делегировать кросс-проектную задачу без сопроводительного письма** в
инбокс получателя (шаг 5, Mappa `inbox_send`). `tasks_create` в чужой борд
живую сессию не пингует — task без letter остаётся незамеченной до
поллера/руки.
- **Не поручать агенту создать downstream-таску для живой сессии без парного inbox-письма** (см. Step 7). `tasks_create` в чужой борд живую сессию не пингует — ТЗ обязано требовать И таску, И письмо, иначе downstream-таска висит незамеченной.
- Don't skip the pre-flight gate — even if everything seems obvious.
- Don't use passive trigger phrases instead of `invoke` — "tdd-criteria" in
text is weaker than "invoke `tdd-criteria`".
- Don't skip `notify` — without it the boss won't learn about completion.
- Don't skip `weight` — without it fleet routing is blind.
- Don't create a review task for pointer/ops/research tasks — impl only.
- Don't create a review task without `weight` — the reconciler/poller will
skip it. Inherit from impl, floor `needs-claude` (see Step 6).
- Don't assign `weight: cheap-ok` where discipline is critical (review,
security, schema migration) — weak models may ignore invoke instructions.
- Don't assign `weight: needs-claude` or `cheap-ok` to tasks changing critical
infrastructure (poller, MCP servers, deploy, CI/CD) — only `needs-human`.
- Don't set `session_break` routinely on every task — it marks a real
boundary (domain-switch / milestone / heavy infra), not a default; otherwise
`mappa-task-work` breaks the session after every close.
- **Don't create tasks from design/decision without a `**Spec:**` reference**
— the impl agent guesses thresholds/scope instead of reading the design.
- **Don't create several tasks in one repo in parallel** — sha-lock conflicts
(PushRejected); serialize the confirms.
- **Don't delegate a cross-project task without the covering letter** to the
recipient's inbox (step 5, Mappa `inbox_send`). `tasks_create` into a foreign
board doesn't ping the live session — a task without a letter stays unnoticed
until the poller/hand.
- **Don't instruct the agent to create a downstream task for a live session
without the paired inbox letter** (see Step 7). `tasks_create` into a foreign
board doesn't ping the live session — the spec must require BOTH the task
and the letter, otherwise the downstream task hangs unnoticed.
## Reference
- Письма: `mappa-messaging` (канон inbox_send, адресная книга).
- Задачи/борд: `mappa-task-work`.
- Знание: `mappa-knowledge` (wiki после закрытия).
- Промоушен: `mappa-brainstorm-promote` (review-umbrella через него же).
- Letters: `mappa-messaging` (inbox_send canon, address book).
- Tasks/board: `mappa-task-work`.
- Knowledge: `mappa-knowledge` (wiki after closing).
- Promotion: `mappa-brainstorm-promote` (review-umbrella through it too).

View File

@@ -1,193 +1,195 @@
---
name: mappa-knowledge
author: ours
version: 1.4.1
version: 1.5.0
description: >
Цикл работы со знаниями проекта в Mappa (Karpathy LLM Wiki, канал =
mappa-сущности): ingest → query → lint + граф-слой для
реляционных/структурных вопросов. Поглощает using-wiki + using-wiki-graph
(старые имена — триггер-синонимы). Триггеры: «заингесть», «обнови вики»,
«запроси вики», «проверь вики», «use project wiki», «query the wiki»,
«что связывает X и Y», «как связаны», «путь между X и Y», «what connects
X and Y», «что ссылается на X», «backlinks of X», «сироты», «битые ссылки»,
«orphan pages». Wiki = сущности type=wiki (чтение — карв-аут; create —
карв-аут, update — version+409; контракт wiki:2660). Реляционные вопросы — через graph_* (BFS на
стороне сервиса), guarded failure-mode: одна страница и стоп, без
многохоповых цепочек чтением. Skip для одно-страничных контентных вопросов.
The cycle of working with a project's knowledge in Mappa (Karpathy LLM Wiki,
channel = mappa entities): ingest → query → lint + a graph layer for
relational/structural questions. Absorbs using-wiki + using-wiki-graph (old
names — trigger-synonyms). Triggers (bilingual): «заингесть», «обнови вики»,
«запроси вики», «проверь вики», "use project wiki", "query the wiki",
«что связывает X и Y», «как связаны», «путь между X и Y», "what connects
X and Y", «что ссылается на X», «backlinks of X», «сироты», «битые ссылки»,
"orphan pages". Wiki = entities type=wiki (read — carve-out; create —
carve-out, update — version+409; contract wiki:2660). Relational questions —
via graph_* (BFS server-side), guarded failure-mode: one page and stop,
no multi-hop chains by reading. Skip for single-page content questions.
---
# mappa-knowledge
Единый цикл работы со знаниями проекта в **Mappa**: три операции (ingest /
query / lint) + **граф-слой** для реляционных и структурных вопросов. Скилл =
цикл, не тул: знание **компилируется один раз и держится актуальным**
(ingest), к нему обращаются (query), его проверяют (lint), а связи между
сущностями читают через граф (graph_*).
The single cycle of working with a project's knowledge in **Mappa**: three
operations (ingest / query / lint) + a **graph layer** for relational and
structural questions. The skill is a cycle, not a tool: knowledge is
**compiled once and kept current** (ingest), queried (query), checked (lint),
and the links between entities are read through the graph (graph_*).
Канал — Mappa (`mcp__mappa__*`), НЕ файлы. Страница — сущность `type=wiki`
(`wiki:N`); чтение — карв-аут; **create — карв-аут без лиза; update —
optimistic concurrency (version+409 → retry)** (контракт wiki:2660, v0.12.0).
Файлового `.wiki/` больше нет; `setup-wiki` умер (нечего настраивать).
Channel — Mappa (`mcp__mappa__*`), NOT files. A page is an entity `type=wiki`
(`wiki:N`); read — carve-out; **create — carve-out without a lease; update —
optimistic concurrency (version+409 → retry)** (contract wiki:2660, v0.12.0).
The file-based `.wiki/` no longer exists; `setup-wiki` is dead (nothing to set up).
## Когда использовать
## When to use
- Заингестить документ/источник в вики («заингесть X», «обнови вики»).
- Ответить из вики / проверить вики («запроси вики», «проверь вики», lint).
- Реляционный/структурный вопрос («что связывает X и Y», «backlinks», «сироты») — граф-слой.
- Модифицировать любую страницу — форматы ниже обязательны; конвенции проекта
живут в `AGENTS`-сущности (legacy — `CLAUDE`-указатель).
- Ingest a document/source into the wiki («заингесть X», «обнови вики»).
- Answer from the wiki / check the wiki («запроси вики», «проверь вики», lint).
- Relational/structural question («что связывает X и Y», «backlinks», «сироты») — the graph layer.
- Modify any page — formats below are mandatory; project conventions live in
the `AGENTS` entity (legacy — `CLAUDE` pointer).
**НЕ для:** разовых вопросов по коду (обычное чтение файлов), однофайловых
README/ADR (не персистентная база знаний), проекта без вики в mappa.
**NOT for:** one-off code questions (normal file reading), single-file
README/ADR (not a persistent knowledge base), a project without a wiki in mappa.
## Три слоя (не смешивать)
## Three layers (don't mix)
1. **Raw-источники**`summaries/<slug>` страницы. Иммутабельны: читай, не
редактируй (единственное исключение — блок-цитата `> Status` по явной
просьбе пользователя).
2. **Вики**остальные страницы (entities/concepts/packages/contradictions/open-questions/overview).
3. **Схема** — сущности `AGENTS` (канон, slug `AGENTS`) + `CLAUDE` (legacy-
указатель «Canon is AGENTS»). Читай `AGENTS` первой; она перекрывает этот
скил при конфликте.
1. **Raw sources**`summaries/<slug>` pages. Immutable: read, don't edit
(the only exception — the `> Status` blockquote on an explicit user request).
2. **Wiki** — the other pages (entities/concepts/packages/contradictions/open-questions/overview).
3. **Schema**the `AGENTS` entity (canon, slug `AGENTS`) + `CLAUDE` (legacy
pointer "Canon is AGENTS"). Read `AGENTS` first; it overrides this skill on
conflict.
## Первый шаг любой операции
## First step of any operation
1. `mcp__mappa__wiki_get(project, 'AGENTS')`если есть, читай (канон; если
нет`wiki_get(project, 'CLAUDE')`, легаси-указатель).
2. `mcp__mappa__wiki_get(project, 'index')`каталог; найди нужные страницы.
(Каталог по умолчанию`entity_search`, решение 1; `index` — опора ориентации.)
3. Только потом действуй.
1. `mcp__mappa__wiki_get(project, 'AGENTS')`if present, read it (canon; if
not`wiki_get(project, 'CLAUDE')`, the legacy pointer).
2. `mcp__mappa__wiki_get(project, 'index')`the catalog; find the needed
pages. (Default catalog`entity_search`, decision 1; `index` is an
orientation aid.)
3. Only then act.
Если `AGENTS`/`CLAUDE` нет — вики либо новая, либо неухоженная: не
импровизируй структуру, первый ingest создаёт `AGENTS` (+ `CLAUDE`-указатель).
If `AGENTS`/`CLAUDE` is missing — the wiki is either new or unmaintained:
don't improvise the structure, the first ingest creates `AGENTS`
(+ `CLAUDE` pointer).
## MCP-поверхность
## MCP surface
| Операция | Тул | Примечание |
| Operation | Tool | Note |
|---|---|---|
| Чтение страницы | `mcp__mappa__wiki_get(project?, slug)` | чтение — карв-аут |
| Поиск страниц | `mcp__mappa__entity_search(q, type='wiki', project?, scope?, limit)` | ILIKE по body/title (полные тела) |
| Карточный поиск | `mcp__mappa__wiki.search(q, scope?, project?, projects?, limit?)` | карточки {ref, project, slug, title, summary, snippet, related} — без тел (wiki:2661) |
| Создать страницу | `mcp__mappa__wiki_create(project, slug, body)` | **карв-аут без лиза** |
| Обновить страницу | `mcp__mappa__wiki_update(project, id, title?, body?, version)` | **version-based**: конфликт → 409 → retry со свежей version из wiki_get |
| Путь между сущностями | `mcp__mappa__graph_path({from, to})` | кратчайшая цепочка, BFS |
| Соседи / исходящие | `mcp__mappa__graph_neighbors({id})` | рёбра узла с резолвом целей |
| Входящие ссылки | `mcp__mappa__graph_backlinks({id})` | кто ссылается на узел |
| Здоровье графа | `mcp__mappa__graph_stats()` | nodes/edges/components |
| Read a page | `mcp__mappa__wiki_get(project?, slug)` | read — carve-out |
| Search pages | `mcp__mappa__entity_search(q, type='wiki', project?, scope?, limit)` | ILIKE over body/title (full bodies) |
| Card search | `mcp__mappa__wiki.search(q, scope?, project?, projects?, limit?)` | cards {ref, project, slug, title, summary, snippet, related} — without bodies (wiki:2661) |
| Create a page | `mcp__mappa__wiki_create(project, slug, body)` | **carve-out without a lease** |
| Update a page | `mcp__mappa__wiki_update(project, id, title?, body?, version)` | **version-based**: conflict → 409 → retry with the fresh version from wiki_get |
| Path between entities | `mcp__mappa__graph_path({from, to})` | shortest chain, BFS |
| Neighbors / outgoing | `mcp__mappa__graph_neighbors({id})` | node edges with target resolution |
| Incoming links | `mcp__mappa__graph_backlinks({id})` | who references the node |
| Graph health | `mcp__mappa__graph_stats()` | nodes/edges/components |
**Запись — карв-аут (create) / version-based (update), без лиза (interactive
contract, wiki:2660).** `wiki_create` не требует claim_token; `wiki_update`
принимает ожидаемую `version` (свежую из `wiki_get`) — конфликт → 409 →
**Writing — carve-out (create) / version-based (update), no lease (interactive
contract, wiki:2660).** `wiki_create` requires no claim_token; `wiki_update`
takes the expected `version` (fresh from `wiki_get`) — conflict → 409 →
re-GET → retry.
**Frontmatter-summary (wiki:2661, карточный поиск).** При create/update/
promote пиши `summary:`ОДНУ строку-суть в frontmatter страницы (`---\ntitle: …\nsummary: одна строка\n---`). Карточки `wiki.search` читают его
(без summary карточка беднее — fallback только сниппет). Информацию-дубликат
в body не вставляй: summary компилируется один раз, в frontmatter.
**Frontmatter-summary (wiki:2661, card search).** On create/update/promote
write `summary:`ONE essence line in the page frontmatter (`---\ntitle: …\nsummary: one line\n---`). The `wiki.search` cards read it (without summary the
card is poorer — snippet fallback only). Don't insert duplicate info into the
body: the summary is compiled once, in the frontmatter.
**Рефы и id (#1037/#1028).** Публичная поверхность несёт per-type реф полным
именем первым полем: `ref: "wiki:3"` (решение 20, конвенция #1028), `num`
следом, глобальный `id` — internal (последним). Для `wiki_update` нужен
internal `id`из ответа `wiki_get`/`entity_search`. В прозе — слаг/имя
первым, реф как якорь: «спека `concepts/session-live-ingest` (wiki:2604)».
В теле страниц — викилинки по слагу (`[[concepts/foo]]`, решение 4) или
per-type рефы полными именами (`[[task:N]]`/`[[inbox:N]]`).
**Refs and ids (#1037/#1028).** The public surface carries the per-type ref by
full name as the first field: `ref: "wiki:3"` (decision 20, convention #1028),
`num` next, the global `id` — internal (last). `wiki_update` needs the
internal `id`from the `wiki_get`/`entity_search` response. In prose —
slug/name first, ref as anchor: "the spec `concepts/session-live-ingest`
(wiki:2604)". In page bodies — wikilinks by slug (`[[concepts/foo]]`,
decision 4) or per-type refs by full names (`[[task:N]]`/`[[inbox:N]]`).
---
## Цикл: три операции
## The cycle: three operations
### Ingest — «заингесть X»
1. Прочитай источник полностью.
2. Извлеки: entities, concepts, packages, кросс-резы.
3. Создай `summaries/<slug>`одну страницу-резюме на источник (~50150 строк;
ссылку на raw клади в frontmatter `raw_path` + `ingested:`).
4. Для каждой затронутой страницы:
- есть → обнови (`wiki_update(project, id, body, version)` — version свежая
из `wiki_get`; 409 → re-GET → retry). **Противоречия помечай явно** блоком
`> **Противоречие:** источник A говорит X, источник B — Y`.
Не затирай молча.
- нет → создай (`wiki_create`, карв-аут).
5. Обнови `index` (каталог: одна строка на страницу) — опционально; каталог
по умолчанию — `entity_search` (решение 1).
6. Отчитайся пользователю: что создано, что обновлено, какие противоречия.
Первый ingest новой вики: создай `AGENTS` (канон) + `CLAUDE` (указатель).
1. Read the source completely.
2. Extract: entities, concepts, packages, cross-results.
3. Create `summaries/<slug>`one summary page per source (~50150 lines;
put the raw link in frontmatter `raw_path` + `ingested:`).
4. For every affected page:
- exists → update (`wiki_update(project, id, body, version)` — version
fresh from `wiki_get`; 409 → re-GET → retry). **Mark contradictions
explicitly** with a `> **Contradiction:** source A says X, source B — Y`
block. Don't overwrite silently.
- missing → create (`wiki_create`, carve-out).
5. Update `index` (catalog: one line per page) — optional; the default catalog
is `entity_search` (decision 1).
6. Report to the user: what was created, what updated, which contradictions.
First ingest of a new wiki: create `AGENTS` (canon) + `CLAUDE` (pointer).
**Оп-лог — автоматический.** Каждая write-операция уже пишется сервисом в
таблицу `logs` (component=тип сущности, message=slug+operation; смотреть
`mcp__mappa__admin_logs`). Ручную `log`-страницу НЕ веди — это дубль,
аудит-след живёт в сервисе (решение 12, ратификация 2026-08-24).
**Op-log — automatic.** Every write operation is already logged by the service
into the `logs` table (component=entity type, message=slug+operation; to view
`mcp__mappa__admin_logs`). Don't maintain a manual `log` page — it's a
duplicate, the audit trail lives in the service (decision 12, ratified
2026-08-24).
**Один ingest может затронуть 1015 страниц. Это нормально — для того LLM и нужны.**
**One ingest can touch 1015 pages. That's normal — that's what LLMs are for.**
Порядок записи: все wiki-мутации одним циклом; create — карв-аут, update — с
version (свежей из `wiki_get`); 409 → re-GET → retry. Лиз/claim для записи НЕ
нужен (wiki:2660).
Write order: all wiki mutations in one cycle; create — carve-out, update — with
version (fresh from `wiki_get`); 409 → re-GET → retry. No lease/claim needed
for writing (wiki:2660).
### Query — вопрос по вики
### Query — a question to the wiki
1. Читай `index` сначала, затем углубляйся в страницы (`wiki_get` по слагу).
2. Отвечай с цитатами-викилинками: `[[concepts/foo]]` (рёбра создаются при
записи, решение 4).
3. **Компаундируй вики.** Если ответ — реальный синтез (сравнение, анализ,
новая связь) — спроси пользователя: «Сохранить как страницу wiki?» Хорошие
вопросы становятся страницами в `concepts/`.
1. Read `index` first, then dig into pages (`wiki_get` by slug).
2. Answer with quote-wikilinks: `[[concepts/foo]]` (edges are created on
write, decision 4).
3. **Compounding the wiki.** If the answer is a real synthesis (comparison,
analysis, new link) — ask the user: "Save as a wiki page?" Good questions
become pages in `concepts/`.
**Реляционные/структурные вопросы — не читай, а зови граф** (следующая секция):
связи образуют граф, который LLM не обходит надёжно чтением.
**Relational/structural questions — don't read, call the graph** (next
section): links form a graph that an LLM doesn't traverse reliably by reading.
### Lint — «проверь вики»
Ищи:
- **Противоречия** между страницами.
- **Сирот** — страницы без входящих ссылок: `graph_backlinks(id)` (id из
`wiki_get`) → нет входящих рёбер = сирота.
- **Stale-claims** — `updated_at` страницы старше источника, который она резюмирует.
- **Потерянные сущности** — понятия из текста без своей страницы
(`entity_search` по имени → пусто).
- **Пустые/TODO-секции.**
Look for:
- **Contradictions** between pages.
- **Orphans** — pages without incoming links: `graph_backlinks(id)` (id from
`wiki_get`) → no incoming edges = orphan.
- **Stale-claims** — a page's `updated_at` older than the source it summarizes.
- **Lost entities** — concepts from the text without their own page
(`entity_search` by name → empty).
- **Empty/TODO sections.**
Отчёт — панч-лист. Ничего не удаляй автоматически.
Report — a punch list. Don't delete anything automatically.
---
## Граф-слой (реляционные/структурные вопросы)
## Graph layer (relational/structural questions)
**Stop and call the graph.** На реляционный/структурный вопрос о вики или
любых сущностях mappa (таски, письма, сессии) **не отвечай, прочитав одну
страницу** — это 0%-recall провал, ради которого существует граф-слой.
Сервис ходит по рёбрам детерминированно (BFS) и возвращает ответ в
нескольких строках; контекст не засоряется.
**Stop and call the graph.** On a relational/structural question about the wiki
or any mappa entities (tasks, letters, sessions) **don't answer after reading
one page** — that's the 0%-recall failure the graph layer exists for. The
service walks the edges deterministically (BFS) and returns the answer in a few
lines; context doesn't get polluted.
Формы вопроса → тул:
Question form → tool:
| Вопрос | Тул |
| Question | Tool |
|---|---|
| relational — «что связывает X и Y», «путь между», "what connects", "shortest path" | `graph_path({from, to})` |
| neighbourhood — «соседи X», "neighbours of X" | `graph_neighbors({id})` |
| incoming — «кто ссылается на X», «backlinks», "what links to X" | `graph_backlinks({id})` |
| health — «сироты», «битые ссылки», «здоровье вики», "orphan pages" | `graph_stats()` + `graph_backlinks(id)` |
**Адресация: slug → internal id.** Резолвь `id` через `wiki_get`/`entity_search`
(последнее поле ответа; `ref`/`num` — для показа). Ответы graph несут per-type
refs полными именами (`task:N`/`inbox:N`/`wiki:N`, конвенция #1028) — реферируй
по ним, не по id. Пустой `path` = связи реально нет — так и скажи; не выдумывай
цепочку из текстовой близости.
**Addressing: slug → internal id.** Resolve `id` via `wiki_get`/`entity_search`
(the last response field; `ref`/`num` — for display). Graph responses carry
per-type refs by full names (`task:N`/`inbox:N`/`wiki:N`, convention #1028) —
reference them, not ids. An empty `path` = the link genuinely doesn't exist —
say so; don't invent a chain from textual proximity.
**Precondition — граф реально связан.** Если сомневаешься — сначала
`graph_stats()`: `edges` ≈ 0 ⇒ граф пуст, отвечай чтением. (Слаги без
[[линков]] рёбер не создают; сироты — норма для разреженных вики.)
**Precondition — the graph is actually connected.** If unsure — first
`graph_stats()`: `edges` ≈ 0 ⇒ empty graph, answer by reading. (Slugs without
[[links]] create no edges; orphans are normal for sparse wikis.)
---
## Форматы страниц (ОБЯЗАТЕЛЬНО)
## Page formats (MANDATORY)
### Frontmatter
```yaml
---
title: Человекочитаемое имя
title: Human-readable name
type: entity | concept | package | summary | contradiction | open-question | overview
tags: [short, tokens]
sources: [concepts/mappa.md]
@@ -195,74 +197,81 @@ updated: 2026-08-24
---
```
Страницы `summaries/` дополнительно несут `ingested: YYYY-MM-DD` и `raw_path: …`.
`contradictions/``status: open | resolved | accepted-divergence` и `affects:`.
`open-questions/``status: open | answered | obsolete` и `touches:`.
`summaries/` pages additionally carry `ingested: YYYY-MM-DD` and `raw_path: …`.
`contradictions/``status: open | resolved | accepted-divergence` and
`affects:`. `open-questions/``status: open | answered | obsolete` and
`touches:`.
### Слаги
### Slugs
- `kebab-case`, **только латиница**. Кириллицу/др. скрипты транслитерируй
(`план переписывания``ozon-client-rewrite`). Оригинальный title — в H1 и frontmatter.
- `kebab-case`, **Latin only**. Transliterate Cyrillic/other scripts
(«план переписывания»`ozon-client-rewrite`). The original title — in H1
and frontmatter.
- `entities/<name>`, `concepts/<name>`, `packages/<name>`, `summaries/<slug>`,
`contradictions/<slug>`, `open-questions/<slug>`.
### Оп-лог — таблица `logs`, не страница
### Op-log — the `logs` table, not a page
File-based `log.md` мёртв (решение 12/15, ратификация 2026-08-24). Сервис пишет
оп-лог сам при каждой write-операции: `mcp__mappa__admin_logs` (фильтры
level/since/component/entity, retention 14d). Ручную `log`-страницу не заводи,
не дописывай, не парси.
File-based `log.md` is dead (decision 12/15, ratified 2026-08-24). The service
writes the op-log itself on every write operation: `mcp__mappa__admin_logs`
(filters level/since/component/entity, retention 14d). Don't create, append,
or parse a manual `log` page.
### `index` — каталог через поиск
### `index` — catalog via search
Каталог = `entity_search(q, type='wiki', project)` (решение 1). `index`-страница
— опциональная опора для ориентации: одна строка на страницу
`- [Title](concepts/foo.md) — hook.`, секции по типам. Обновляй только если
страница уже существует; не плоди каталог-дубли.
Catalog = `entity_search(q, type='wiki', project)` (decision 1). The `index`
page — optional orientation aid: one line per page
`- [Title](concepts/foo.md) — hook.`, sections by type. Update only if the
page already exists; don't proliferate catalog duplicates.
## Quick reference
| Ситуация | Что трогаем |
| Situation | What we touch |
|---|---|
| Ingest одного документа | `summaries/<slug>` (новая) + 315 entities/concepts/packages (+ опционально `index`) |
| Query | (чтение) + возможно новая страница |
| Query реляционный | graph_* (BFS), не чтение |
| Lint | (чтение) + graph_backlinks/stats для сирот |
| Новая вики проекта | первый ingest создаёт `AGENTS` + `CLAUDE`-указатель; оп-лог — автоматический |
| Ingest one document | `summaries/<slug>` (new) + 315 entities/concepts/packages (+ optional `index`) |
| Query | (read) + possibly a new page |
| Query relational | graph_* (BFS), not reading |
| Lint | (read) + graph_backlinks/stats for orphans |
| New project wiki | the first ingest creates `AGENTS` + `CLAUDE` pointer; op-log — automatic |
## Частые ошибки
## Common mistakes
- **Правка `summaries/`.** Нельзя. Только статус-блок по явной просьбе.
- **Дамп сырья в `summaries/`.** Резюме — это резюме. Ссылайся на raw, не копируй.
- **Молчаливые перезаписи.** Новый источник противоречит странице — пометь
блоком `> **Противоречие:**`; не затирай.
- **Нарративный оп-лог.** Не веди его руками: сервис пишет logs сам (admin.logs).
- **Не-ASCII слаги.** Ломают grep и кросс-платформенность. Транслитерируй.
- **Пропущенные противоречия в lint.** Ценность вики — во вскрытых напряжениях,
а не в ложном консенсусе.
- **Запись без version (update).** `wiki_update` без свежей version
last-write-wins, риск затирания чужого; бери version из `wiki_get`, 409 → retry.
- **Держать claim на чтение/раздумья.** Claim — на время работы; чтение — карв-аут.
- **Реляционный вопрос чтением одной страницы.** Это тот самый 0%-recall
провал — зови graph_*.
- **Слаги/пути в graph-тулы.** Только internal id, и только свежие (удалённая
сущность → ошибка).
- **Тащить всю вики в контекст**, чтобы «проследить» связи руками — сервис
делает это за ноль токенов.
- **Editing `summaries/`.** Not allowed. Only a status block on an explicit request.
- **Dumping raw content into `summaries/`.** A summary is a summary. Reference
the raw, don't copy it.
- **Silent overwrites.** A new source contradicts a page — mark with a
`> **Contradiction:**` block; don't wipe it.
- **Narrative op-log.** Don't maintain it by hand: the service writes logs
itself (admin.logs).
- **Non-ASCII slugs.** Break grep and cross-platform compatibility. Transliterate.
- **Missed contradictions in lint.** The wiki's value is in exposed tensions,
not in false consensus.
- **Writing without version (update).** `wiki_update` without a fresh version →
last-write-wins, risk of wiping someone else's work; take the version from
`wiki_get`, 409 → retry.
- **Holding a claim for reading/thinking.** A claim is for the duration of
work; reading — carve-out.
- **Answering a relational question by reading one page.** That's the exact
0%-recall failure — call graph_*.
- **Slugs/paths into graph tools.** Only internal ids, and only fresh ones (a
deleted entity → error).
- **Dragging the whole wiki into context** to "trace" links by hand — the
service does it for zero tokens.
## Red flags
- Реляционный вопрос → читаешь страницу вместо `graph_*`.
- Правка `summaries/` или молчаливая перезапись противоречия.
- Wiki-мутация update без version (last-write-wins) или create с выдуманным claim.
- Нарративный оп-лог руками.
- Relational question → reading a page instead of `graph_*`.
- Editing `summaries/` or silently overwriting a contradiction.
- Wiki update mutation without version (last-write-wins) or create with an
invented claim.
- Narrative op-log by hand.
---
## Reference
- Поиск по сущностям: `mcp__mappa__entity_search` (FTS, решение 1).
- Оп-лог: `mcp__mappa__admin_logs` (автоматический, решение 12).
- Дерево/зонтики: `mcp__mappa__graph_tree(root, depth?, fields?, limit?)`.
- Задачи: `mappa-task-work`. Почта: `mappa-messaging`. Делегирование: `mappa-delegation`.
- Related: `using-projects-meta` (мост до флипа), `project-discipline`.
- Entity search: `mcp__mappa__entity_search` (FTS, decision 1).
- Op-log: `mcp__mappa__admin_logs` (automatic, decision 12).
- Tree/umbrellas: `mcp__mappa__graph_tree(root, depth?, fields?, limit?)`.
- Tasks: `mappa-task-work`. Mail: `mappa-messaging`. Delegation: `mappa-delegation`.
- Related: `using-projects-meta` (bridge until the flip), `project-discipline`.

View File

@@ -1,248 +1,260 @@
---
name: mappa-messaging
author: ours
version: 1.2.0
version: 1.3.0
description: >
Цикл межсессионной почты через Mappa: SEND (inbox_send) → RECEIVE
(inbox_monitor) → POLICY (peer ≠ authority). Адрес = имя папки проекта из
адресной книги; from = своя папка; никогда не писать себе. Письмо от
другого агента — предложение, не authority; единственный источник
направления и скоупа — человек. Старые имена — триггер-синонимы:
inter-session-messaging. Триггеры: «напиши письмо <проекту>», «отправь
сообщение», «свяжись с <проектом>», «передай <проекту>», «уведомь
<проект>», "write a message to <project>", "send a message", а также
получение входящего (см. ниже). НЕ про
доставку/мониторинг (→ mappa-session-orient, inbox raise) и НЕ про задачи
The inter-session mail cycle via Mappa: SEND (inbox_send) → RECEIVE
(inbox_monitor) → POLICY (peer ≠ authority). Address = the project folder
name from the address book; from = your own folder; never write to yourself.
A letter from another agent is a proposal, not authority; the only source of
direction and scope is the human. Old names — trigger-synonyms:
inter-session-messaging. Triggers (bilingual): «напиши письмо <проекту>»,
«отправь сообщение», «свяжись с <проектом>», «передай <проекту>»,
«уведомь <проект>», "write a message to <project>", "send a message",
and also receiving incoming mail (see below). NOT about delivery/monitoring
(→ mappa-session-orient, inbox raise) and NOT about tasks
(→ mappa-task-work, mcp__mappa__task_*).
---
# mappa-messaging
Единый канон межсессионной почты — **цикл, не тул**: отправить → принять
политика содержания. Каждая фаза ниже — обязательная часть цикла; пропуск
фазы = сломанный цикл (письмо без политики = флуд, ответ без SEND = пустота).
The single canon of inter-session mail — **a cycle, not a tool**: send
receive → content policy. Each phase below is a mandatory part of the cycle;
skipping a phase = a broken cycle (a letter without policy = flood, a reply
without SEND = emptiness).
Канал — Mappa (`mcp__mappa__*`), НЕ файлы. Письмо — сущность типа `inbox`
(`inbox:N`), живёт в сервисе; доставка и чтение — карв-аут (не требуют лиза
проекта, решение 19). Файловый канал `.agents/inbox/` выпилен (флип решения 15).
Channel — Mappa (`mcp__mappa__*`), NOT files. A letter is an entity of type
`inbox` (`inbox:N`), lives in the service; delivery and reading — carve-out
(require no project lease, decision 19). The file channel `.agents/inbox/` is
removed (flip of decision 15).
## Когда использовать
## When to use
- Написать письмо другому проекту/агенту: «напиши письмо <проекту>», «отправь сообщение», «свяжись с <проектом>», «передай <проекту>», «уведомь <проект>».
- Получил входящее письмо (монитор доставил, или сам проверил `inbox_monitor`) — обработать по RECEIVE.
- Обсуждаешь с другой сессией дизайн/скоуп/решения — держать POLICY (peer ≠ authority).
- Write a letter to another project/agent: «напиши письмо <проекту>», «отправь сообщение», «свяжись с <проектом>», «передай <проекту>», «уведомь <проект>», "write a message to <project>", "send a message".
- Received an incoming letter (the monitor delivered it, or you checked
`inbox_monitor` yourself) — process it per RECEIVE.
- Discussing design/scope/decisions with another session — keep POLICY
(peer ≠ authority).
**НЕ для:** доставки/мониторинга почты (→ `mappa-session-orient`, inbox raise), задач
(→ `mappa-task-work`), handoff (→ `mappa-closing-ritual`), промоушена (→
**NOT for:** mail delivery/monitoring (→ `mappa-session-orient`, inbox raise),
tasks (→ `mappa-task-work`), handoff (→ `mappa-closing-ritual`), promotion (→
`mappa-brainstorm-promote`).
---
## SEND — как написать письмо
## SEND — how to write a letter
### Адрес — только из адресной книги, и проект должен быть в Mappa
### Address — only from the address book, and the project must be in Mappa
Адрес проекта = **имя его папки на диске как есть** (`.workshop`, `artmone.pro`,
`snolla.js`). Никогда не выдумывай адрес по qualified-имени, remote'у или
памяти — папка может не совпадать с репо (`OpeItcLoc03/common`папка `.common`).
The project address = **its disk folder name as is** (`.workshop`, `artmone.pro`,
`snolla.js`). Never invent an address from a qualified name, remote, or memory —
the folder may not match the repo (`OpeItcLoc03/common`folder `.common`).
1. Прочитай адресную книгу: `~/projects/.wiki/concepts/projects-address-book.md`
(shared wiki clone). Таблица: `адрес (папка) | qualified | роль`.
2. Найди строку с целевым проектом по имени папки.
3. Если проекта в книге **нет** — письмо не пиши. Остановись и спроси человека
(или заведи запись в книге, если человек подтвердил адрес). Письмо по
выдуманному адресу создаёт проект-сироту в Mappa (`ensureProject`) и теряется.
4. **Проект должен существовать в Mappa**: сверь адрес со списком проектов
(`mcp__mappa__admin_status``projects[]` или `entity_search` type=project).
Несуществующего адреса нет в списке — остановись и спроси (или заведи проект).
1. Read the address book: `~/projects/.wiki/concepts/projects-address-book.md`
(shared wiki clone). Table: `address (folder) | qualified | role`.
2. Find the row with the target project by folder name.
3. If the project is **not** in the book — don't write the letter. Stop and ask
the human (or add a book entry if the human confirmed the address). A letter
to an invented address creates an orphan project in Mappa (`ensureProject`)
and gets lost.
4. **The project must exist in Mappa**: cross-check the address against the
project list (`mcp__mappa__admin_status``projects[]` or
`entity_search` type=project). A non-existent address is absent from the
list — stop and ask (or create the project).
### Вызов отправки
### The send call
```
mcp__mappa__inbox_send(
project: <адрес получателя>, # имя папки проекта (из адресной книги)
from: <адрес отправителя>, # СВОЁ имя папки (только имя, без owner/темы)
subject: <тема>, # опционально — короткая тема
body: <markdown-тело> # свободный markdown
project: <recipient address>, # project folder name (from the address book)
from: <sender address>, # YOUR folder name (just the name, no owner/topic)
subject: <topic>, # optional — short topic
body: <markdown body> # free markdown
)
```
- `from`**только имя своей папки**. Без owner, без описания. НЕ
`reviewer-command-index-done-ack` (тема письма — не адрес). На письмо с
выдуманным `from` нельзя ответить.
- Ответ на письмо: `inbox_send(project=<from полученного>, from=<своя папка>)`.
В `subject`префикс `Re: `, в теле первая строка — ссылка на исходное
письмо (`inbox:<номер>` или его subject). Поля `in_reply_to`/`event` в Mappa нет —
вместо них subject-префиксы `Re:` и `[event: closed]` при lifecycle-письмах.
- `from`**only your folder name**. No owner, no description. NOT
`reviewer-command-index-done-ack` (the letter topic is not an address). A
letter with an invented `from` cannot be replied to.
- Reply to a letter: `inbox_send(project=<from of the received>, from=<your folder>)`.
In `subject`the `Re: ` prefix, in the body the first line is a reference
to the original letter (`inbox:<number>` or its subject). There are no
`in_reply_to`/`event` fields in Mappa — instead subject-prefixes `Re:` and
`[event: closed]` for lifecycle letters.
### Реф-формат: слаг/имя первым, полное имя рефа как якорь
### Ref format: slug/name first, full ref name as anchor
Конвенция на прозу и ссылки: **имя/слаг первым, реф как якорь**«письмо
про деплой (inbox:2046)», «таска `session-live-ingest-impl` (task:1022)».
Рефы писать **полными именами**: `task:`/`wiki:`/`inbox:`/`session:`/
`handoff:`/`storm:`/`repo:`/`commit:`/`project:` (короткие `t:`/`w:`/`i:`/
парсер принимает, но писать полные). Вики-реф единый `wiki:NNNN` для всех
бакетов (подтип — в слаге: `wiki:2604` = concepts/session-live-ingest).
Convention for prose and links: **name/slug first, ref as anchor**"the
letter about the deploy (inbox:2046)", "the task `session-live-ingest-impl`
(task:1022)".
Write refs **by full names**: `task:`/`wiki:`/`inbox:`/`session:`/`handoff:`/
`storm:`/`repo:`/`commit:`/`project:` (short `t:`/`w:`/`i:`/… are accepted by
the parser, but write full). The wiki ref is a single `wiki:NNNN` for all
buckets (subtype — in the slug: `wiki:2604` = concepts/session-live-ingest).
### Ссылки на задачи — по глобальному номеру (формат v2)
### Task references — by global number (v2 format)
Ссылка на задачу в письме — **по глобальному номеру**: `#452` (формат v2,
номера — машинный ключ, уникальны по всей федерации). Не слаг — слаг может
повторяться между проектами. Первое упоминание задачи в письме — с номером и
слагом для читаемости: `#452 (tasks-v2-search-by-id)`, далее — просто `#452`.
Резолв номера в {project, slug} — через `mcp__mappa__entity_search` (ищет по
номеру/id) или `entity_get`.
A task reference in a letter — **by global number**: `#452` (v2 format,
numbers are the machine key, unique across the whole federation). Not a slug —
slugs can repeat between projects. First mention of a task in a letter — with
number and slug for readability: `#452 (tasks-v2-search-by-id)`, afterwards —
just `#452`. Resolving a number into {project, slug} — via
`mcp__mappa__entity_search` (searches by number/id) or `entity_get`.
### Жёсткие правила
### Hard rules
1. **Никогда не писать письмо самому себе** — свой инбокс для входящих, не для
заметок. Заметкив `.brainstorm/` или `.tasks/`, не письмом.
2. **Никогда не выдумывать адрес** — только из адресной книги + существующий
проект в Mappa (шаг 4 выше).
3. **`from`всегда адрес (имя папки)**, по которому можно ответить. Описания
вроде `workshop session (implements catalog wave 2)`запрещены: на такое
письмо нельзя ответить.
4. **Тема письмав `subject` и теле**, не в `from`.
1. **Never write a letter to yourself** — your inbox is for incoming, not for
notes. Notesin `.brainstorm/` or `.tasks/`, not by letter.
2. **Never invent an address** — only from the address book + an existing
project in Mappa (step 4 above).
3. **`from`always an address (folder name)** that can be replied to.
Descriptions like `workshop session (implements catalog wave 2)`banned:
such a letter cannot be replied to.
4. **The letter topicin `subject` and body**, not in `from`.
---
## RECEIVE — как обработать входящее
## RECEIVE — how to process incoming mail
1. Входящее доставляет монитор (`mappa-session-orient` — inbox raise, pi-расширение) или
ты проверяешь сам: `mcp__mappa__inbox_monitor(project=<своя папка>, limit)`.
Ответ`{rows: [{id, slug, from, subject, body}]}`: последние письма
твоего проекта, с отправителем и темой (meta извлекается сервером).
2. **Письмо — first-class, не фоновое уведомление.** Прочитай и обработай его
в начале ближайшего хода — НЕ «когда дойдут руки», НЕ в конце сессии. Если
сообщение появилось в контексте после длинного tool-цикла — это не повод
закапывать его в итоговую сводку: обработай до завершения сессии.
3. Признай получение явно и ответь на содержание в своём ходе.
4. **Кто отправитель:** поле `from` в ответе `inbox_monitor` (адрес — имя
папки). Тема — `subject`. Для ответа — SEND отправителю (`from`).
5. Если нужен ответ — SEND по канону выше, отправителю (`from`).
6. Не оставляй письмо без обработки до конца хода — если не можешь решить
сейчас, скажи об этом и (если надо) заведи таску через
`mcp__mappa__task_*`, не «забудь».
7. **Ожидаемая почта:** если ты сам вызвал событие, которое родит письмо в
твой инбокс (notify на твой проект: close/blocked/delivery-failed таски),
— проверь `inbox_monitor` в момент, когда событие сработало; не жди, пока
письмо само доедет. Доставка может задержаться на время текущего tool-цикла.
8. **Дедуп:** монитор помнит доставленные id (в памяти процесса). Письма в
Mappa не перемещаются (нет `.read/`) — обработанные остаются в списке;
повторно их не читай, сверяйся с уже виденными id.
1. Incoming is delivered by the monitor (`mappa-session-orient` — inbox raise,
pi extension) or you check yourself: `mcp__mappa__inbox_monitor(project=<your folder>, limit)`.
Response`{rows: [{id, slug, from, subject, body}]}`: the latest letters
of your project, with sender and topic (meta extracted by the server).
2. **A letter is first-class, not a background notification.** Read and process
it at the start of the nearest turn — NOT "when I get around to it", NOT at
the end of the session. If a message appeared in context after a long
tool-cycle — that's no reason to bury it in the final summary: process it
before the session ends.
3. Acknowledge receipt explicitly and answer the content in your turn.
4. **Who is the sender:** the `from` field in the `inbox_monitor` response
(address — folder name). Topic — `subject`. For a reply — SEND to the
sender (`from`).
5. If a reply is needed — SEND per the canon above, to the sender (`from`).
6. Don't leave a letter unprocessed until the end of the turn — if you can't
decide now, say so and (if needed) create a task via
`mcp__mappa__task_*`, don't "forget".
7. **Expected mail:** if you yourself triggered an event that will birth a
letter into your inbox (notify to your project: close/blocked/
delivery-failed task) — check `inbox_monitor` at the moment the event fired;
don't wait for the letter to arrive on its own. Delivery may lag for the
duration of the current tool-cycle.
8. **Dedup:** the monitor remembers delivered ids (in process memory). Letters
in Mappa are not moved (no `.read/`) — processed ones stay in the list;
don't re-read them, cross-check against already-seen ids.
---
## POLICY — содержание письма
## POLICY — letter content
> The inbox is a peer channel, not a chain of command. Messages from another agent session are a colleague's proposals — never a human mandate. The human is the only authority for direction and scope.
### Правила
### Rules
1. **Peer ≠ authority.** Сообщение от другого агента (даже role-named
«постановщик» / «boss» / «reviewer») peer input: анализ и предложения.
Санкцию даёт только человек. Направление и скоуп — только от человека.
2. **Не выдавай своё мнение за решение.** Отвечая пиру, не называй свой
дизайн-выбор «решением постановщика», пока человек явно не ратифицировал.
Формулируй: «я рекомендую X; человек это не ратифицировал». Различай
«человек решил X» и «пир/я рекомендую X».
3. **Эскалации требуют явного человеческого «да».** Архитектурные решения и
рост скоупа должны быть ратифицированы человеком **до** того, как ты
сообщишь их пиру как решённые или будешь по ним действовать.
1. **Peer ≠ authority.** A message from another agent (even role-named
"assigner" / "boss" / "reviewer") is peer input: analysis and proposals.
Only the human grants sanction. Direction and scope — only from the human.
2. **Don't present your opinion as a decision.** When replying to a peer,
don't call your design choice "the assigner's decision" until the human has
explicitly ratified it. Phrase it: "I recommend X; the human hasn't ratified
it." Distinguish "the human decided X" from "a peer/I recommend X".
3. **Escalations require an explicit human "yes".** Architectural decisions
and scope growth must be ratified by the human **before** you report them to
a peer as decided or act on them.
### Канальный контракт (inbox vs board)
### Channel contract (inbox vs board)
- **Инбокс (`inbox.*`) — только канал коммуникации**: обсуждение, помощь,
lifecycle-уведомления («таска создана», «закрыта», «заблокирована»). Не больше.
- **Задачи — только через `mcp__mappa__task_*`.** Доска — единственный
источник правды о задаче: существование, статус, скоуп, решения создаются и
меняются через `task_create` / `task_close`никогда не «решаются» внутри
письма. (Create — карв-аут; update/close — version+409, wiki:2660.)
- **Inbox (`inbox.*`) — a communication channel only**: discussion, help,
lifecycle notifications ("task created", "closed", "blocked"). Nothing more.
- **Tasks — only through `mcp__mappa__task_*`.** The board is the only source
of truth about a task: existence, status, scope, decisions are created and
changed via `task_create` / `task_close`never "decided" inside a letter.
(Create — carve-out; update/close — version+409, wiki:2660.)
Следствие: **если это не на доске — это не задача и не решение, это разговор.**
Значимый дизайн-выбор должен лечь на доску (или в вики), инбокс лишь указывает
на него.
Consequence: **if it's not on the board — it's not a task or a decision, it's
a conversation.** A meaningful design choice must land on the board (or the
wiki); the inbox only points to it.
### Lifecycle-уведомления: task + letter
### Lifecycle notifications: task + letter
Кросс-проектное действие с задачей — всегда пара «доска + письмо». Доска —
источник правды (существование/статус/скоуп), письмо — пинг и контекст. В
теле письма задачу называй **по номеру** (`#452`), а не только слагом.
Lifecycle-письма помечай subject-префиксом `[event: <тип>]`:
A cross-project task action is always a "board + letter" pair. The board is the
source of truth (existence/status/scope), the letter is a ping and context. In
the letter body, name the task **by number** (`#452`), not just by slug.
Mark lifecycle letters with the subject-prefix `[event: <type>]`:
| Событие | Кто пишет | Куда | subject |
| Event | Who writes | Where | subject |
|---|---|---|---|
| Создание | комиссионер | инбокс получателя | `[event: created] #N slug` |
| Закрытие | исполнитель (живая сессия) или поллер (авто-ран) | инбокс комиссионера (`Notify`) | `[event: closed] #N slug` |
| Блокировка/парк | то же | то же | `[event: blocked] #N slug` |
| Created | commissioner | recipient's inbox | `[event: created] #N slug` |
| Closed | executor (live session) or poller (auto-run) | commissioner's inbox (`Notify`) | `[event: closed] #N slug` |
| Blocked/parked | same | same | `[event: blocked] #N slug` |
Тело письма — 1-2 строки + номера/слаги, не дублировать доску. Живая сессия
узнаёт о задаче ТОЛЬКО через письмо (борд не пингует); комиссионер узнаёт о
закрытии только через `Notify`/письмо. Правило постановки — `mappa-delegation`
(шаг «пара доска+письмо»); правило закрытия — `mappa-task-work` (close).
Letter body — 1-2 lines + numbers/slugs, don't duplicate the board. A live
session learns about a task ONLY through the letter (the board doesn't ping);
the commissioner learns about closing only via `Notify`/letter. Assignment
rule — `mappa-delegation` (the "board+letter pair" step); closing rule —
`mappa-task-work` (close).
### Против чего это
### What this is against
Две сессии пинг-понгуют, каждая соглашается с фреймом другой и добавляет скоуп,
человек номинально в цикле. Сигнатура эхо-камеры: быстрые ответы, согласие с
твоим фреймом, рост скоупа каждый раунд. Это
`user_context_agents_path_of_least_resistance` уровнем выше: сессии обходят
человеческую ратификацию — фейковое «решено» через взаимное согласие.
Two sessions ping-pong, each agrees with the other's frame and adds scope, the
human is nominally in the loop. Echo-chamber signature: fast replies, agreement
with your frame, scope growth every round. This is
`user_context_agents_path_of_least_resistance` one level up: sessions bypass
human ratification — fake "decided" through mutual agreement.
### Circuit-breaker
Заметив рост скоупа без явного человеческого «да» — **остановись и спроси
человека**: «Я пир-сессия, не человек-авторитет; я эскалирую скоуп здесь; ты
реально хочешь, чтобы это ушло как решённое?»
Noticing scope growth without an explicit human "yes" — **stop and ask the
human**: "I'm a peer session, not a human authority; I'm escalating scope here;
do you really want this to go out as decided?"
**Multi-session caveat — не кричи «override» с частичного зрения.** Когда человек
ведёт несколько сессий, твой обзор того, что он ратифицировал, частичен. Пир,
действующий по «нератифицированному», может иметь реальную человеческую санкцию
из канала, который ты не видишь. При кажущемся нарушении — **спроси «ты
ратифицировал это в другом канале?»**, а не обвиняй. Урок 2026-06-16: workshop
назвал close в common «фейковой атрибуцией ратификации»; на деле человек
одобрил напрямую в common-канале, пока workshop ещё обсуждал. Всплыви пробел
вопросом — человек сверит каналы.
**Multi-session caveat — don't shout "override" from partial sight.** When the
human runs several sessions, your view of what they ratified is partial. A peer
acting on the "unratified" may have real human sanction from a channel you
don't see. On an apparent violation — **ask "did you ratify this in another
channel?"**, don't accuse. Lesson 2026-06-16: workshop called the close in
common a "fake attribution of ratification"; in reality the human approved
directly in the common channel while workshop was still discussing. Surface the
gap with a question — the human reconciles the channels.
### Почему это существует
### Why this exists
Возникло 2026-06-16: workshop и common вели многораундовый дизайн-обмен по
инбоксу; workshop эскалировал дизайн (tamper-guard → prevention → oracle-integrity
→ runner-owns-verifier → close-moves) и докладывал каждый шаг как «решение
постановщика» — подразумевая человеческую санкцию, которой не было. common
распознал эхо-камеру, прочитал свой stop-hook и корректно отказался
имплементировать нератифицированный редизайн, спросив человека. Методология
живёт в скиле, не в per-session памяти.
Arose 2026-06-16: workshop and common ran a multi-round design exchange over
the inbox; workshop escalated the design (tamper-guard → prevention →
oracle-integrity → runner-owns-verifier → close-moves) and reported every step
as "the assigner's decision" — implying human sanction that didn't exist.
common recognized the echo chamber, read its own stop-hook, and correctly
refused to implement the unratified redesign, asking the human. The methodology
lives in the skill, not in per-session memory.
---
## What NOT to do
| Искушение | Реальность |
| Temptation | Reality |
|---|---|
| «Письмо — быстрый способ решить вопрос, потом оформлю» | Если это не на доске — это не задача и не решение, это разговор. Дизайн-выбор → доска/вики, письмо только пингует. |
| «Напишу в .common-канал, там одобрят» | Пир-письмо — предложение, не санкция. Человек — единственный авторитет направления и скоупа. |
| «Слаг уникален, сошлюсь на него» | Слаг повторяется между проектами — ссылка по глобальному номеру `#452`. |
| «Отвечу письмом в конце сессии, соберу всё разом» | Письмо — first-class: обработай в начале ближайшего хода, не «когда дойдут руки». |
| «У меня нет адреса — напишу по памяти/qualified» | Адрес — только из адресной книги; выдуманный адрес плодит проект-сироту и письмо теряется. |
| "A letter is a quick way to settle it, I'll formalize later" | If it's not on the board — it's not a task or a decision, it's a conversation. Design choice → board/wiki, the letter only pings. |
| "I'll write to the .common channel, they'll approve" | A peer letter is a proposal, not a sanction. The human is the only authority for direction and scope. |
| "The slug is unique, I'll reference it" | Slugs repeat between projects — reference by global number `#452`. |
| "I'll reply at the end of the session, collect everything at once" | A letter is first-class: process at the start of the nearest turn, not "when I get around to it". |
| "I don't have the address — I'll write from memory/qualified" | Address — only from the address book; an invented address breeds an orphan project and the letter gets lost. |
## Red flags
- Пишешь письмо сам себе / на выдуманный адрес / с `from`-описанием.
- Пинг-понг: быстрые согласия, рост скоупа каждый раунд, человек номинально в цикле.
- Называешь свой выбор «решением постановщика» без явной человеческой ратификации.
- Письмо «решает» задачу, а на доске её нет.
- Writing a letter to yourself / to an invented address / with a `from`-description.
- Ping-pong: fast agreements, scope growth every round, the human nominally in the loop.
- Calling your choice "the assigner's decision" without explicit human ratification.
- A letter "decides" a task while it's absent from the board.
Все эти флаги = **стоп и спроси человека** (или заведи таску/вики-страницу).
All these flags = **stop and ask the human** (or create a task/wiki page).
---
## Reference
- Доставка/мониторинг входящих: `mappa-session-orient` (inbox raise; pi-расширение inbox-monitor).
- Адресная книга: `~/projects/.wiki/concepts/projects-address-book.md` (shared wiki).
- Список проектов Mappa: `mcp__mappa__admin_status` (карв-аут, без лиза).
- Задачи: `mappa-task-work` (борд = `mcp__mappa__task_*`).
- Incoming delivery/monitoring: `mappa-session-orient` (inbox raise; pi extension inbox-monitor).
- Address book: `~/projects/.wiki/concepts/projects-address-book.md` (shared wiki).
- Mappa project list: `mcp__mappa__admin_status` (carve-out, no lease).
- Tasks: `mappa-task-work` (board = `mcp__mappa__task_*`).
- Handoff: `mappa-closing-ritual` (write) / `mappa-session-orient` (read).
- Делегирование: `mappa-delegation` (пара «доска + covering-письмо»).
- Related: `recommend-dont-menu` (стиль ответа), `project-discipline`.
- Delegation: `mappa-delegation` (the "board + covering letter" pair).
- Related: `recommend-dont-menu` (response style), `project-discipline`.

View File

@@ -1,146 +1,152 @@
---
name: mappa-session-orient
author: ours
version: 1.0.1
version: 1.1.0
description: >
Старт-фаза форкфлоу: контракт + чтение (pull --ff-only → handoff read →
inbox raise → liveness-сводка «живо/мертво» → live-ingest query). Нужен и
для ad-hoc, где нет AGENTS.md-контракта. Поглощает pulling-before-work,
session-handoff(read), session-inbox-monitor(raise), using-system-snapshot
(liveness) + live-ingest query (старые имена — триггер-синонимы). Граница:
orient отвечает «живо/мертво» одной строкой; глубокая диагностика — вне
suite (эскалация человеку/диагностической сессии). Триггеры: «что на
сессии», «кто последним работал», «продолжи с места», «orient me»,
session-start ритуал, «pull remote before work».
Start phase of the forkflow: contract + reading (pull --ff-only → handoff
read → inbox raise → liveness summary "alive/dead" → live-ingest query).
Also needed for ad-hoc sessions without an AGENTS.md contract. Absorbs
pulling-before-work, session-handoff(read), session-inbox-monitor(raise),
using-system-snapshot (liveness) + live-ingest query (old names are
trigger-synonyms). Boundary: orient answers "alive/dead" in one line; deep
diagnosis is outside the suite (escalate to a human / a diagnostic session).
Triggers (bilingual): «что на сессии», «кто последним работал», «продолжи с
места», «orient me», "what's on the session", "who worked last", "continue
from where I stopped", "orient me", session-start ritual, «pull remote before
work», "pull remote before work".
---
# mappa-session-orient
Старт-фаза цикла агента: **контракт + чтение**, тонкий слой — отвечает на
вопрос «живо/мертво» (одна строка на секцию), не углубляется. Нужен и для
ad-hoc-сессий (где нет AGENTS.md-контракта — ориентация всё равно обязательна).
Start phase of the agent cycle: **contract + reading**, a thin layer — answers
the question "alive/dead" (one line per section), does not go deep. Also needed
for ad-hoc sessions (where there is no AGENTS.md contract — orientation is
still mandatory).
> **Граница session-orient / ops (w:2605, round 3):** orient — «живо/мертво»;
> ops — «почему и что дальше». Проблема на старте → **не углубляться**:
> передать человеку или диагностической сессии (вне suite).
> **Boundary session-orient / ops (w:2605, round 3):** orient = "alive/dead";
> ops = "why and what's next". A problem at start → **do not dig deeper**:
> hand it to the human or to a diagnostic session (outside the suite).
## Когда использовать
## When to use
- Старт сессии (ритуал, порядок строго по Steps).
- «что на сессии», «кто последним работал», «продолжи с места», «orient me».
- Ad-hoc-сессия без трека/таски — ориентация всё равно (контракт + чтение).
- Session start (ritual, order strictly per Steps).
- «что на сессии», «кто последним работал», «продолжи с места», «orient me»,
"what's on the session", "who worked last", "continue from where I stopped".
- Ad-hoc session without a track/task — orientation anyway (contract + reading).
## Steps (порядок — ритуал)
## Steps (order — the ritual)
### 1. Контракт
### 1. Contract
Прочитать `AGENTS.md` проекта (canon; `CLAUDE.md` — legacy-указатель). Если
AGENTS.md нет — ad-hoc: контракта нет, но ориентация продолжается (шаги 26
не зависят от него).
Read the project's `AGENTS.md` (canon; `CLAUDE.md` — legacy pointer). If there
is no AGENTS.md — ad-hoc: no contract, but orientation continues (steps 26
don't depend on it).
### 2. Pull (pulling-before-work, полный цикл)
### 2. Pull (pulling-before-work, full cycle)
`git pull --ff-only`один раз на старте. Проверки по порядку: git-work-tree?
(нет → silent exit), политика pull (`pull.rebase=true` + `pull.ff=only`,
set-if-absent), origin remote? (нет → skip), дерево чистое? (грязно → skip,
не stash), HEAD attached? (нет → skip), upstream? (нет → skip), `git pull --ff-only`.
**Никогда auto-merge/rebase, никогда stash.** Повторный pull — только по явному
«sync».
`git pull --ff-only`once at start. Checks in order: git work-tree? (no →
silent exit), pull policy (`pull.rebase=true` + `pull.ff=only`, set-if-absent),
origin remote? (no → skip), clean tree? (dirty → skip, no stash), HEAD
attached? (no → skip), upstream? (no → skip), `git pull --ff-only`.
**Never auto-merge/rebase, never stash.** Repeat pull — only on explicit
"sync".
### 3. Handoff read (session-handoff read-часть)
### 3. Handoff read (session-handoff read part)
1. `mcp__mappa__entity_search(q='', type='handoff', project=<имя>, limit=1)`
если пусто, silent exit (первая сессия проекта).
2. **Staleness:** `meta.date` > 7 дней → спросить user'а «handoff устарел,
оверрайдить или продолжить?».
3. **Summarize + Orient:** пересказать одним блоком (summary / open_treks /
ask_user / guards / recent_commits): «прошлая сессия предложила X. Делаем?»
4. **Wait.** Никаких действий до подтверждения user'ом. Default = orient + ask,
никакого auto-execute.
1. `mcp__mappa__entity_search(q='', type='handoff', project=<name>, limit=1)`
if empty, silent exit (project's first session).
2. **Staleness:** `meta.date` > 7 days → ask the user "the handoff is stale,
override or continue?".
3. **Summarize + Orient:** retell in one block (summary / open_treks /
ask_user / guards / recent_commits): "the previous session proposed X. Do we do it?"
4. **Wait.** No actions until the user confirms. Default = orient + ask, no
auto-execute.
### 4. Inbox raise + sweep (session-inbox-monitor)
Поднять персистентный монитор на инбокс проекта (pi: расширение inbox-monitor
поллит `GET /inbox?project=<cwd>`; opt-in — строка `inbox monitor: raise on
start` в AGENTS.md, live re-check каждый тик). Свип: `mcp__mappa__inbox_monitor(project=<имя>)`
— непрочитанные письма могут менять план; обработай каждое по `mappa-messaging`
(письмо — first-class, в начале ближайшего хода).
Raise the persistent monitor on the project's inbox (pi: the inbox-monitor
extension polls `GET /inbox?project=<cwd>`; opt-in — the string
`inbox monitor: raise on start` in AGENTS.md, live re-check every tick). Sweep:
`mcp__mappa__inbox_monitor(project=<name>)` — unread letters may change the
plan; handle each per `mappa-messaging` (a letter is first-class, at the start
of the nearest turn).
### 5. Liveness-сводка (using-system-snapshot) — «живо/мертво»
### 5. Liveness summary (using-system-snapshot) — "alive/dead"
Один-два зонда в текущем turn, сжать в 34 строки, не raw-дампить:
One or two probes in the current turn, compress into 34 lines, no raw dumps:
```
mcp__mappa__meta_health → 🟢/🔴 Mappa alive (заголовок при падении)
mcp__mappa__admin_status → счётчики по типам/проектам (нагрузка)
mcp__projects-meta__meta_system_snapshot → poller (running? + проекты) / docker (N/N up,
иначе проблемные) / tasks (Σ active/blocked,
кэш — может быть stale)
mcp__mappa__meta_health → 🟢/🔴 Mappa alive (header on outage)
mcp__mappa__admin_status → counters by type/project (load)
mcp__projects-meta__meta_system_snapshot → poller (running? + projects) / docker (N/N up,
else the problematic ones) / tasks (Σ active/blocked,
cache — may be stale)
```
**Never assert liveness по памяти** — только вызов тула в этом же turn. Если
snapshot показал проблему → **эскалация, не углубление**: «проблема на старте,
не разбираю — передаю человеку/диагностической сессии» (ops вне suite).
**Never assert liveness from memory** — only a tool call in this same turn. If
the snapshot shows a problem → **escalate, don't dig**: "problem at start, not
investigating — handing to the human / a diagnostic session" (ops outside the
suite).
### 6. Live-ingest query (потребитель session-live-ingest, #1022/#1024)
### 6. Live-ingest query (consumer of session-live-ingest, #1022/#1024)
Зависимость: сервер #1022 (v0.8.0) + клиентская часть #1024 (pi session-sync,
.session пишется клиентом). Контракт — w:2604.
Dependency: server #1022 (v0.8.0) + client part #1024 (pi session-sync,
.session written by the client). Contract — w:2604.
1. `mcp__mappa__session_list(project=<имя>, stale_minutes?)`последние сессии
проекта, latest-first (`updated_at DESC`), с end-state/ts/meta-тройкой
{project, runtime, machine, folder}.
2. **Stale-active детект:** end-state≠clean AND updated_at < nowX → «вёл
<runtime>@<machine>, не завершена» (краш-детект).
3. **«Другая связка + не завершена»** → предложить (peer-канон, решение за
человеком): забить / дернуть письмом (`mappa-messaging`: письмо той связке)
/ продолжить самому.
4. **Same-triple (`/resume`):** та же связка {runtime, machine, folder} → догрузить
остаток (пи-нативный resume или бриф из mappa).
1. `mcp__mappa__session_list(project=<name>, stale_minutes?)`the project's
latest sessions, latest-first (`updated_at DESC`), with
end-state/ts/meta-triple {project, runtime, machine, folder}.
2. **Stale-active detect:** end-state≠clean AND updated_at < nowX →
"<runtime>@<machine> was running, not finished" (crash-detect).
3. **"Different triple + not finished"** → propose (peer canon, human's
decision): ignore / nudge by letter (`mappa-messaging`: letter to that
triple) / continue yourself.
4. **Same-triple (`/resume`):** same triple {runtime, machine, folder} → load
the remainder (pi-native resume or a brief from mappa).
**Замечание (2026-08-24):** роуты `/session` ещё не задеплоены на прод
(сервер #1022 в репо, деплой ждёт #1055) — при 404/«no route» live-ingest query
пропускается без фейла: orient продолжается (шаги 15), query-часть — по факту
доступности.
**Note (2026-08-24):** the `/session` routes are not yet deployed to prod
(server #1022 in repo, deploy awaits #1055) — on 404/"no route" the live-ingest
query is skipped without failing: orient continues (steps 15), the query part
— per actual availability.
## Failure modes
- **Проблема на старте** (сервис упал, snapshot красный, конфликт pull) → не
углубляться: эскалация человеку/диагностической сессии (ops вне suite).
- **Pull diverged** → `⚠️ diverged — resolve manually`; не auto-merge/rebase.
- **Handoff stale (>7 дней)** → спросить user'а, не оверрайдить молча.
- **Live-ingest недоступен** (404 no route / нет клиента #1024) → пропустить
шаг 6, не блокировать ориентацию.
- **Проект не в mappa** (нет handoff/session-сущностей) → silent exit по
соответствующим шагам; первая сессия проекта — норм.
- **Problem at start** (service down, red snapshot, pull conflict) → don't dig:
escalate to the human / a diagnostic session (ops outside the suite).
- **Pull diverged** → "⚠️ diverged — resolve manually"; no auto-merge/rebase.
- **Handoff stale (>7 days)** → ask the user, don't silently override.
- **Live-ingest unavailable** (404 no route / no client #1024) → skip step 6,
don't block orientation.
- **Project not in mappa** (no handoff/session entities) → silent exit on the
corresponding steps; the project's first session — normal.
## Side effects
- Ничего не пишет, ничего не мутирует (ориентация read-only: pull — локальный
ff, inbox-raise — монитор, liveness — зонды, live-ingest — чтение).
- Поднимает персистентный inbox-монитор (живёт до конца сессии).
- Writes nothing, mutates nothing (orientation read-only: pull — local ff,
inbox-raise — monitor, liveness — probes, live-ingest — read).
- Raises the persistent inbox monitor (lives until the end of the session).
## What NOT to do
- **Не auto-execute** из handoff'а — orient + ask, никакого авто-действия.
- **Не углубляться в диагностику** — orient = «живо/мертво»; «почему» — вне suite.
- **Не assert liveness по памяти** — только зонд в этом же turn.
- **Не stash/не auto-merge/не auto-rebase** при pull — только `--ff-only`.
- **Не повторять pull** в сессии без явного «sync».
- **Не ходить по многохоповым цепочкам** live-ingest — одна строка «кто
последним», предложение — человеку.
- **Не писать** (handoff/вики/таски) на ориентации — это финиш-фаза
- **No auto-execute** from the handoff — orient + ask, no auto-action.
- **Don't dig into diagnosis** — orient = "alive/dead"; "why" is outside the suite.
- **Don't assert liveness from memory** — only a probe in this same turn.
- **No stash / no auto-merge / no auto-rebase** on pull — only `--ff-only`.
- **No repeated pull** in the session without an explicit "sync".
- **No multi-hop live-ingest chains** — one line "who worked last", the
proposal goes to the human.
- **Don't write** (handoff/wiki/tasks) at orientation — that's the finish phase
(`mappa-closing-ritual`).
## Reference
- Финиш-фаза: `mappa-closing-ritual` (handoff write + PROPOSE).
- Задачи: `mappa-task-work` (борд после ориентации).
- Почта: `mappa-messaging` (ответы на письма, дернуть связку).
- Знание: `mappa-knowledge`. Делегирование: `mappa-delegation`.
- Live-ingest спека: `concepts/session-live-ingest` (wiki:2604).
- **CC-хук + operator (task:1061):** в одно-юзерной установке CC-сессии несут
`operator:vitya` даже в headless (claude -p) — надёжного hook-сигнала нет
(детект по CLAUDE_INTERACTIVE, если CC выставляет). Не трактуй operator как
признак «человек у руля»; источник истины — end-state + liveness.
- Глубокая диагностика (вне suite): `using-vds-ops` (контейнеры VDS).
- Finish phase: `mappa-closing-ritual` (handoff write + PROPOSE).
- Tasks: `mappa-task-work` (board after orientation).
- Mail: `mappa-messaging` (letter replies, nudge a triple).
- Knowledge: `mappa-knowledge`. Delegation: `mappa-delegation`.
- Live-ingest spec: `concepts/session-live-ingest` (wiki:2604).
- **CC hook + operator (task:1061):** in a single-user setup CC sessions carry
`operator:vitya` even headless (`claude -p`) — there is no reliable hook
signal (detect via CLAUDE_INTERACTIVE if CC sets it). Do not treat operator
as a sign "a human is at the wheel"; source of truth — end-state + liveness.
- Deep diagnosis (outside the suite): `using-vds-ops` (VDS containers).

View File

@@ -1,238 +1,247 @@
---
name: mappa-task-work
author: ours
version: 1.2.0
version: 1.3.0
description: >
Центральный цикл работы с тасками в Mappa: ориентация → выбор работы
(priority/due) → исполнение → сдача (close + review-umbrella) +
loop-mode «поработай очередь». Борд = сущности mappa (решения 14/15/19/20);
create — карв-аут, update/close — optimistic concurrency (version+409),
owner = персистентный assignee, живость — из session владельца
(interactive contract, wiki:2660). Поглощает 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).
The central cycle of working with tasks in Mappa: orientation → work
selection (priority/due) → execution → handover (close + review-umbrella) +
loop-mode "work the queue". Board = mappa entities (decisions 14/15/19/20);
create — carve-out, update/close — optimistic concurrency (version+409),
owner = persistent assignee, liveness — from the owner's session (interactive
contract, wiki:2660). Absorbs using-tasks + task-format + task-loop (loop-mode
INSIDE) + priority-due section (old names — trigger-synonyms).
Triggers (bilingual): «что на досках», «возьми таску», «какой статус»,
«update status», «pause», «switch to X», «где мы остановились», "work the
queue", «поработай очередь», «прогони доску», "what's on the boards", "take
a task". Priority = the human's territory: agents set P0-P2/deadline only at
creation, default P1; overdue → notify, no auto-bump. NOT about delegation
(→ mappa-delegation), NOT about board overview (→ ops/using-system-snapshot).
---
# mappa-task-work
Центральный цикл работы с задачами: **ориентация → выбор работы → исполнение
сдача**. Борд — сущности mappa (`type=task`, `task:N`): чтение — карв-аут;
**create — карв-аут без лиза; update/close — optimistic concurrency
(version+409 → retry)**; **owner = персистентный assignee; живость таски —
из session владельца** (interactive contract, wiki:2660; поллер вне mappa).
Скилл = цикл, не тул: одна механика на выбор/исполнение/сдачу, плюс
**loop-mode** («поработай очередь») внутри — отдельный скил не создаётся.
The central cycle of working with tasks: **orientation → work selection
execution → handover**. Board — mappa entities (`type=task`, `task:N`): read —
carve-out; **create — carve-out without a lease; update/close — optimistic
concurrency (version+409 → retry)**; **owner = persistent assignee; task
liveness — from the owner's session** (interactive contract, wiki:2660; poller
outside mappa). The skill is a cycle, not a tool: one mechanics for
selection/execution/handover, plus **loop-mode** («поработай очередь») inside —
no separate skill is created.
> **Переходное (file channel).** Пока поллер/кэш читают файловые борды
> (`.tasks/STATUS.md`), legacy-канал живёт: блоки в файле обязаны строгому
> формату (см. «Формат таски» ниже), мутации — через `mcp__projects-meta__tasks_*`
> (Gitea-коммиты). Новые таски — через `mcp__mappa__task_create`. Не смешивай.
> **Transitional (file channel).** While the poller/cache reads file boards
> (`.tasks/STATUS.md`), the legacy channel lives: file blocks must obey a
> strict format (see "Task format" below), mutations — via
> `mcp__projects-meta__tasks_*` (Gitea commits). New tasks — via
> `mcp__mappa__task_create`. Don't mix.
## Когда использовать
## When to use
- «что на досках», «возьми таску», «какой статус», «update status», «pause», «switch to X», «где мы остановились».
- «work the queue», «поработай очередь», «прогони доску» → **loop-mode**.
- Смена задачи / пауза / конец сессии — держать борд консистентным.
- «что на досках», «возьми таску», «какой статус», «update status», «pause», «switch to X», «где мы остановились», "what's on the boards", "take a task".
- "work the queue", «поработай очередь», «прогони доску» → **loop-mode**.
- Task switch / pause / session end — keep the board consistent.
**НЕ для:** делегирования другому агенту/проекту (→ `mappa-delegation`),
промоушена (→ `mappa-brainstorm-promote`), инфра-диагностики (→ `using-vds-ops`),
кросс-проектного обзора (→ `using-system-snapshot`).
**NOT for:** delegating to another agent/project (→ `mappa-delegation`),
promotion (→ `mappa-brainstorm-promote`), infra diagnosis (→ `using-vds-ops`),
cross-project overview (→ `using-system-snapshot`).
## MCP-поверхность
## MCP surface
| Операция | Тул | Примечание |
| Operation | Tool | Note |
|---|---|---|
| Взять следующую ready-таску | `mcp__mappa__task_update(project, id, owner=<своя>, status=active…, version)` | **conditional update**: status→active, owner=X, version+409 (кто первый с верной version — тот и взял) |
| Создать таску | `mcp__mappa__task_create(project, slug, title?, description?, status?, priority?, due?)` | **карв-аут без лиза**; per-type номер (решение 20) |
| Закрыть таску | `mcp__mappa__task_close(project, id, version)` | **version-based**: конфликт → 409 → retry со свежей version из task_get |
| Обновить таску | `mcp__mappa__task_update(project, id, parent?, owner?, status?, reason?, version)` | version-based; 409 → retry |
| Прочитать таску | `mcp__mappa__entity_get(id)` | id internal из search |
| Список борда | `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__session_list(project, stale_minutes?)` | сессия владельца жива/стала stale → таска active or not |
| Уведомление при закрытии | `mcp__mappa__inbox_send(project=<notify>, from=<своя>, subject, body)` | письмо комиссионеру |
| Take the next ready task | `mcp__mappa__task_update(project, id, owner=<yours>, status=active…, version)` | **conditional update**: status→active, owner=X, version+409 (whoever is first with the right version — takes it) |
| Create a task | `mcp__mappa__task_create(project, slug, title?, description?, status?, priority?, due?)` | **carve-out without a lease**; per-type number (decision 20) |
| Close a task | `mcp__mappa__task_close(project, id, version)` | **version-based**: conflict → 409 → retry with the fresh version from task_get |
| Update a task | `mcp__mappa__task_update(project, id, parent?, owner?, status?, reason?, version)` | version-based; 409 → retry |
| Read a task | `mcp__mappa__entity_get(id)` | internal id from search |
| Board list | `mcp__mappa__entity_search(q, type='task', project=<name>, limit)` | all statuses |
| parent_of tree | `mcp__mappa__graph_tree(root, depth?, fields?, limit?)` | umbrellas/hierarchy |
| Related entities | `mcp__mappa__graph_neighbors/backlinks(id)` | refs to the task |
| Overdue | `mcp__mappa__admin_overdue_scan(project?)` | P2 job: notify to inbox, no mutations |
| Owner liveness | `mcp__mappa__session_list(project, stale_minutes?)` | owner's session alive/stale → task active or not |
| Close notification | `mcp__mappa__inbox_send(project=<notify>, from=<yours>, subject, body)` | letter to the commissioner |
**Owner = персистентный assignee; живость — из session (interactive contract,
wiki:2660).** Нет TTL/claim/таймера на таске: «взял таску» = conditional
update (`status→active, owner=X` + version, 409 при конфликте — кто первый,
тот и взял). Живость владельца — сессия: таска с owner=X активна, пока сессия
X жива (end-state≠clean, не stale; `session_list(project)`); краш = stale
решает человек/оркестратор (поллер вне mappa, работает на файловых досках
projects-meta). Write-пути (create/update/close) лизов/claim не требуют —
только version на update/close (409 при конфликте).
**Owner = persistent assignee; liveness — from the session (interactive
contract, wiki:2660).** No TTL/claim/timer on the task: "took a task" =
conditional update (`status→active, owner=X` + version, 409 on conflict —
whoever is first takes it). Owner liveness — the session: a task with owner=X
is active while session X is alive (end-state≠clean, not stale;
`session_list(project)`); crash = stale → the human/orchestrator decides
(poller outside mappa, works on projects-meta file boards). Write paths
(create/update/close) require no leases/claims — only version on update/close
(409 on conflict).
**Рефы и id (#1037/#1028).** Таски наружу несут `ref: "t:N"` полным именем
первым полем (`task:N`, конвенция #1028), `num` следом, глобальный `id`
internal (последним, для addressing в тулах). Ссылайся на таску
`[[task:N]]`/`task:N` в прозе (слаг/имя первым, реф как якорь: «таска
`mappa-task-work` (task:1062)»), никогда `#<глобальный id>`.
**Refs and ids (#1037/#1028).** Tasks carry `ref: "t:N"` by full name as the
first field (`task:N`, convention #1028), `num` next, the global `id`
internal (last, for addressing in tools). Reference a task as
`[[task:N]]`/`task:N` in prose (slug/name first, ref as anchor: "the task
`mappa-task-work` (task:1062)"), never `#<global id>`.
## Статусы (эмодзи для презентации)
## Statuses (emojis for presentation)
| Эмодзи | Статус | Значение |
| Emoji | Status | Meaning |
|---|---|---|
| ⚪ | `ready` | не начата, полностью определена |
| 🔴 | `active` | в работе (обычно одна) |
| 🟡 | `paused` | в процессе, возобновляема |
| 🔵 | `blocked` | ждёт внешнего входа |
| 🟢 | `done` | закрыта |
| ⚪ | `ready` | not started, fully defined |
| 🔴 | `active` | in progress (usually one) |
| 🟡 | `paused` | in progress, resumable |
| 🔵 | `blocked` | waiting on external input |
| 🟢 | `done` | closed |
Не путай: 🟢 — *done*, не «готово». Ready ⚪.
Don't confuse: 🟢 — *done*, not "ready". Ready is ⚪.
---
## Цикл
## The cycle
### Фаза 0 — Ориентация
### Phase 0 — Orientation
1. **Инбокс-свип**`mcp__mappa__inbox_monitor(project=<имя>)`: непрочитанные
письма могут менять план. Обработай каждое по `mappa-messaging`.
2. **Борд**`entity_search(q, type='task', project=<имя>, limit=50)`: отсортируй
по статусу (🔴 → 🟡 → ⚪), по одной строке на таску, цитируй slug.
3. Если user назвал таску`entity_get(id)` по её рефу/номеру.
4. Подтверди одним предложением: «Мы в середине X, следующий шаг — Y».
5. Спроси, верен ли план, перед действиями.
1. **Inbox sweep**`mcp__mappa__inbox_monitor(project=<name>)`: unread
letters may change the plan. Handle each per `mappa-messaging`.
2. **Board**`entity_search(q, type='task', project=<name>, limit=50)`: sort
by status (🔴 → 🟡 → ⚪), one line per task, quote the slug.
3. If the user named a task`entity_get(id)` by its ref/number.
4. Confirm in one sentence: "We're in the middle of X, next step — Y".
5. Ask whether the plan is right before acting.
### Фаза 1 — Выбор работы (priority/due)
### Phase 1 — Work selection (priority/due)
1. **Выбор таски — из списка борда** (`entity_search(q, type='task', project)`):
порядок — **P0-пул первый, внутри по дедлайну (просроченные первыми),
потом P1, потом P2**; отсутствующий priority = P1 (task-priority-due).
«Взять таску» = `task_update(project, id, owner=<своя>, status='active',
version)` — conditional update: status→active, owner=X, version+409; кто
первый с верной version — тот и взял (interactive contract, wiki:2660).
`owner` = `<machine>:<runtime>:<session>`.
2. **Локально-первая рекомендация** — борд cwd первым; кросс-проект — футонота
(`Cross-project: N 🔴 active — см. tasks_aggregate`) только если N>0 и в cwd
нет активной 🔴. Кросс-проектные ургенты — информация, не драйвер «что делать здесь».
3. **Priority/Due — территория человека (раздел task-priority-due):**
- Агент ставит `priority`/`due` **только при создании** таски (явные параметры
или строки `**Priority:** P0|P1|P2` / `**Due:** yyyy-mm-dd` в description).
Отсутствует → дефолт P1, без дедлайна.
- **После создания агент не меняет** приоритет/дедлайн — прецедент человека
структурный (update агентами отклоняется сервером). Обнаружил, что таска
на самом деле P0 → паркуй вопрос человеку, не бампай сам.
- **Просрочка:** due < today при ready/active → `admin_overdue_scan` уведомляет
в инбокс **однократно, без мутаций** — никакого авто-бампа/авто-смены приоритета.
1. **Task selection — from the board list** (`entity_search(q, type='task', project)`):
order — **P0 pool first, within it by deadline (overdue first),
then P1, then P2**; missing priority = P1 (task-priority-due).
"Take a task" = `task_update(project, id, owner=<yours>, status='active',
version)` — conditional update: status→active, owner=X, version+409;
whoever is first with the right version takes it (interactive contract,
wiki:2660). `owner` = `<machine>:<runtime>:<session>`.
2. **Local-first recommendation** — cwd board first; cross-project — a footnote
(`Cross-project: N 🔴 active — see tasks_aggregate`) only if N>0 and there's
no active 🔴 in cwd. Cross-project urgencies are information, not a driver
for "what to do here".
3. **Priority/Due — the human's territory (task-priority-due section):**
- An agent sets `priority`/`due` **only at task creation** (explicit
parameters or `**Priority:** P0|P1|P2` / `**Due:** yyyy-mm-dd` lines in
the description). Absent → default P1, no deadline.
- **After creation an agent doesn't change** priority/deadline — the
human's precedent is structural (agent updates are rejected by the
server). If you discover the task is actually P0 → park the question to
the human, don't bump it yourself.
- **Overdue:** due < today with ready/active → `admin_overdue_scan`
notifies the inbox **once, without mutations** — no auto-bump/auto-priority
change.
### Фаза 2 — Исполнение
### Phase 2 — Execution
- **Одна активная таска** 🔴 на проект. Не параллель.
- Читай description + per-task файл (`<slug>.md`, где есть) до старта.
- Живость — из сессии владельца (`session_list(project)`), не таймер; долгие
таски heartbeat не требуют (interactive contract, wiki:2660).
- **`session_break` gate** (из task-loop): если в description таски есть маркер
`session_break` — после close НЕ клейми следующую: печатай
`🔚 SESSION BOUNDARY …` и останавливайся (домен-свитч / milestone / тяжёлая инфра).
- **One active task** 🔴 per project. No parallelism.
- Read description + per-task file (`<slug>.md`, where present) before starting.
- Liveness — from the owner's session (`session_list(project)`), not a timer;
long tasks need no heartbeat (interactive contract, wiki:2660).
- **`session_break` gate** (from task-loop): if the task description has the
`session_break` marker — after close DON'T claim the next one: print
`🔚 SESSION BOUNDARY …` and stop (domain-switch / milestone / heavy infra).
### Фаза 3 — Сдача (close + review-umbrella)
### Phase 3 — Handover (close + review-umbrella)
1. **Pre-close coverage check.** Собери acceptance criteria из description. Для
каждого — evidence: тест в диффе, артефакт, ссылка на дизайн. Нет evidence на
критерий → спроси user'а «закрывать или подождать coverage'а».
2. Resolve/drop открытые вопросы.
3. `task_close(project, id, version)` → статус `done`. `version`
свежая из `entity_get(id)`/`task_get(id)`; конфликт (409) → re-GET → retry.
4. **Notify-письмо (кросс-проектные таски).** Если таска пришла из другого
проекта (в description/meta есть `from:`/`notify:`) — `inbox_send`
комиссионеру: `project=<notify>`, `subject="[event: closed] <slug>"`,
body = итог (сделано, acceptance, ссылки). Живая сессия пишет сама.
Таска 🟢 ≠ комиссионер узнал.
5. **Review-umbrella для impl-тасок** (канон `mappa-delegation`): если таска
имплементационная и закрыта — парная `<slug>-review` уже должна быть
создана при постановке (status=blocked, blocker=impl#); закрытие impl
разблокирует ревью. Не создавай review сам, если её не было — это работа
постановщика; упомяни в close-note.
6. Дополни summary-строку в handoff/вики при наличии.
1. **Pre-close coverage check.** Collect acceptance criteria from the
description. For each — evidence: a test in the diff, an artifact, a design
reference. No evidence for a criterion → ask the user "close or wait for
coverage".
2. Resolve/drop open questions.
3. `task_close(project, id, version)` → status `done`. `version` — fresh from
`entity_get(id)`/`task_get(id)`; conflict (409) → re-GET → retry.
4. **Notify letter (cross-project tasks).** If the task came from another
project (there's `from:`/`notify:` in description/meta) — `inbox_send` to
the commissioner: `project=<notify>`, `subject="[event: closed] <slug>"`,
body = the outcome (done, acceptance, references). A live session writes it
itself. Task 🟢 ≠ commissioner learned.
5. **Review-umbrella for impl tasks** (canon `mappa-delegation`): if the task
is implementation and closed — the paired `<slug>-review` should already
have been created at assignment (status=blocked, blocker=impl#); closing the
impl unblocks the review. Don't create a review yourself if it didn't exist
— that's the assigner's job; mention it in the close-note.
6. Add a summary line to the handoff/wiki if present.
### Пауза / переключение / конец сессии
### Pause / switch / session end
1. Текущая 🔴 → `task_close` если завершена (см. Фазу 3), иначе пометь
`status=paused` (owner остаётся; «where stopped»в description или handoff).
2. **Инбокс-свип** на границе тасок (`inbox_monitor`).
3. Возьми следующую: `task_update(owner, status='active', version)`прежняя
остаётся 🟡.
4. Подтверди ориентацию перед стартом.
1. Current 🔴 → `task_close` if finished (see Phase 3), otherwise mark
`status=paused` (owner stays; "where stopped"in the description or handoff).
2. **Inbox sweep** at the task boundary (`inbox_monitor`).
3. Take the next one: `task_update(owner, status='active', version)`the
previous stays 🟡.
4. Confirm the orientation before starting.
> **Never lose Where I stopped** — критичное поле: в description (последний
> абзац) или в handoff-сущности (`mappa-closing-ritual`). Перед концом сессии
> обязательно запиши handoff.
> **Never lose Where I stopped** — critical field: in the description (last
> paragraph) or in the handoff entity (`mappa-closing-ritual`). Before the end
> of the session, definitely write the handoff.
---
## Loop-mode — «поработай очередь»
Один триггер-сёрфейс: «поработай очередь» / «work the queue» / «прогони доску»
этот режим. Work the board **в этой сессии**: взять → работа → close → взять,
пока очередь не пуста или user не сказал стоп. **Интерактивный цикл, не демон.**
One trigger surface: «поработай очередь» / "work the queue" / «прогони доску»
this mode. Work the board **in this session**: take → work → close → take,
until the queue is empty or the user said stop. **Interactive cycle, not a daemon.**
```
task_update(owner, status=active, version) → 409? re-GET → retry → пусто? → STOP «борд пуст»
таска
работа в этой сессии (read description + <slug>.md)
task_update(owner, status=active, version) → 409? re-GET → retry → empty? → STOP "board is empty"
task
work in this session (read description + <slug>.md)
завершена? нет → park: blocked (внешний) | paused (возобновляемо) → следующая
да
consult_policy: human-only/strict-human → STOP перед close/commit, спросить user
finished? no → park: blocked (external) | paused (resumable) → next
yes
consult_policy: human-only/strict-human → STOP before close/commit, ask the user
↓ auto
pre-close coverage check → task_close
session_break на таске? → да: печатай 🔚 SESSION BOUNDARY, STOP
нет
следующая
session_break on the task? → yes: print 🔚 SESSION BOUNDARY, STOP
no
next
```
- **Пустая очередь — естественный стоп, не wait-loop.** Нет `CronCreate`, нет
спавна субагента, нет коротких поллеров — это работа отдельного поллера.
Длинный watch («работай пока не скажу стоп» + явно «продолжай проверять») —
только один `ScheduleWakeup` с интервалом ≥1200s, никогда `CronCreate`.
- **Не завершаемая таска:** внешний блокер`status=blocked` + blocker
(конкретный факт + что нужно); прервал ты (бюджет/стоп) → `status=paused` +
where_stopped. Одна упавшая таска не останавливает цикл — паркуй и дальше.
- **Heartbeat не нужен** — живость владельца из session (wiki:2660); долгая
таска с живой сессией не «протухает».
- **Consult-гейт:** `auto` → автопилот до close; `human-only`/`strict-human`
работай, затем **STOP перед close/commit** и спроси user. Push никогда не
автоматический (project-discipline Rule 4: commit freely, push по явному
гранту).
- **An empty queue is a natural stop, not a wait-loop.** No `CronCreate`, no
subagent spawn, no short pollers — that's the work of a separate poller.
Long watch ("keep working until I say stop" + explicit "keep checking") —
only one `ScheduleWakeup` with an interval ≥1200s, never `CronCreate`.
- **Non-finishable task:** external blocker`status=blocked` + blocker
(concrete fact + what's needed); you interrupted (budget/stop) →
`status=paused` + where_stopped. One fallen task doesn't stop the cycle —
park and continue.
- **No heartbeat needed** — owner liveness from the session (wiki:2660); a
long task with a live session doesn't "expire".
- **Consult gate:** `auto` → autopilot up to close; `human-only`/`strict-human`
→ work, then **STOP before close/commit** and ask the user. Push is never
automatic (project-discipline Rule 4: commit freely, push on explicit grant).
---
## Формат таски (из task-format)
## Task format (from task-format)
### Primary: mappa task_create
Создание задач — **через тул, не руками** (решение 20): карв-аут, лиз/claim
для create НЕ нужен (wiki:2660). Номер `task:N` назначает сервер — не выдумывай.
Task creation — **via the tool, not by hand** (decision 20): carve-out, no
lease/claim needed for create (wiki:2660). The number `task:N` is assigned by
the server — don't invent it.
```
mcp__mappa__task_create(
project: <имя проекта>, // обязателен
slug: <kebab-case>, // обязателен, латиница
title: <одна строка>, // опционально
description: <markdown>, // тело; [[refs]] → рёбра (решение 4)
status: ready | active | paused | blocked | done, // по умолчанию ready
priority: P0 | P1 | P2, // только при создании; отсутствует → P1
due: yyyy-mm-dd // только при создании; отсутствует = нет
project: <project name>, // required
slug: <kebab-case>, // required, latin
title: <one line>, // optional
description: <markdown>, // body; [[refs]] → edges (decision 4)
status: ready | active | paused | blocked | done, // default ready
priority: P0 | P1 | P2, // only at creation; absent → P1
due: yyyy-mm-dd // only at creation; absent = none
)
```
Slug-правила: короткий, lowercase, kebab-case, латиница. Description — markdown,
`[[refs]]` на связанное. Priority/Due — при создании ИЛИ строками в description
(`**Priority:** P0|P1|P2`, `**Due:** yyyy-mm-dd`; явные параметры переопределяют).
Slug rules: short, lowercase, kebab-case, latin. Description — markdown,
`[[refs]]` to related. Priority/Due — at creation OR as lines in the
description (`**Priority:** P0|P1|P2`, `**Due:** yyyy-mm-dd`; explicit
parameters override).
### Legacy: блок .tasks/STATUS.md (интерм до флипа поллера)
### Legacy: .tasks/STATUS.md block (interim until the poller flips)
Пока файловый поллер не переключён на mappa (#984), блоки в `.tasks/STATUS.md`
обязаны строгому формату — иначе поллер молча пропускает:
While the file poller is not switched to mappa (#984), blocks in
`.tasks/STATUS.md` must obey a strict format — otherwise the poller silently
skips:
```markdown
## ⚪ [#1234 my-task-slug] — One-line description.
@@ -249,65 +258,68 @@ Slug-правила: короткий, lowercase, kebab-case, латиница.
---
```
Три load-bearing правила: **(1)** шапка точно `## <emoji> [#<n> <slug>] — <desc>`
(h2, один emoji, `[#<n> <slug>]`, разделитель ` — `); **(2)** поля — строки
`**Label:** value`, буллеты игнорируются; **(3)** `**Created:**` обязателен.
Three load-bearing rules: **(1)** the header is exactly `## <emoji> [#<n>
<slug>] — <desc>` (h2, one emoji, `[#<n> <slug>]`, separator ` — `); **(2)**
fields are `**Label:** value` lines, bullets are ignored; **(3)**
`**Created:**` is mandatory.
Поля, которые разбирает поллер: `**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-штамп, пишет и чистит поллер; залипший
штамп на ⚪ блокирует поллер.
Fields the poller parses: `**Weight:**` (cheap-ok | needs-claude |
needs-human — **mandatory** for auto-claim), `**Notify:**` (<owner>/<repo>),
`**Requirements:**`, `**Runtime allowed:**`, `**Consult policy:**`,
`**Blocker:**` (only on 🔵), `**Priority:**`/`**Due:**` (as above).
`**Owner:**/`**Claim token:**/`**Claim expires at:**` — claim stamp, written
and cleared by the poller; a stuck stamp on ⚪ blocks the poller.
**Weight — поле, решающее взятие:** без `**Weight:**` поллер паркует в 🔵
(`no backend for weight_tier: unknown`). Обычный код → `needs-claude`;
критикал-инфра (поллер, MCP-серверы, деплой, CI, git-хуки) → `needs-human`.
**Weight — the field that decides the take:** without `**Weight:**` the poller
parks to 🔵 (`no backend for weight_tier: unknown`). Ordinary code →
`needs-claude`; critical infra (poller, MCP servers, deploy, CI, git hooks) →
`needs-human`.
---
## Failure modes
- **version conflict** на update/close → version устарела; re-GET свежую version,
retry. Не «решай конфликт» перезаписью без version (last-write-wins).
- **task_close на незавершённую** → никогда. Park (blocked/paused).
- **owner на таске без живой сессии** → таска формально active, но владелец
stale; спроси человека (адvisory, не замок).
- **notify не указан (legacy)** → без него boss не узнает о завершении.
- **weight не указан (legacy)** → поллер паркует (no backend for weight_tier).
- **update Priority/Due после создания** → сервер отклоняет; паркуй вопрос
человеку, не бампай сам.
- **version conflict** on update/close → version is stale; re-GET the fresh
version, retry. Don't "resolve the conflict" by overwriting without version
(last-write-wins).
- **task_close on an unfinished task** → never. Park (blocked/paused).
- **owner on a task without a live session** → the task is formally active but
the owner is stale; ask the human (advisory, not a lock).
- **notify not specified (legacy)** → without it the boss won't learn about completion.
- **weight not specified (legacy)** → the poller parks (no backend for weight_tier).
- **update Priority/Due after creation** → the server rejects; park the
question to the human, don't bump yourself.
## What NOT to do
- **Не выдумывай номера** — `task:N` назначает сервер (решение 20).
- **Version-дисциплина:** update/close — всегда с version (409 при конфликте
re-GET → retry). Create — карв-аут без лиза (wiki:2660).
- **Одна активная таска** — только одна 🔴 на проект.
- **Never close без coverage check** — evidence на каждый acceptance criterion.
- **Не закрывай незавершённое** — park, не close.
- **Не бампай priority/due после создания** — территория человека.
- **Не «решай» задачи письмом/в чате** — борд — единственный источник правды
(канон mappa-messaging: «если это не на доске — это не задача»).
- **Не полли пустую очередь** — пусто = стоп и отчёт; без демона/CronCreate.
- **Не автопилоть human-only/strict-human** через close/commit; push — только по гранту.
- **Не батчи tasks_create в один репо** — sha-lock конфликты; сериализуй.
- **Don't invent numbers** — `task:N` is assigned by the server (decision 20).
- **Version discipline:** update/close — always with version (409 on conflict
re-GET → retry). Create — carve-out without a lease (wiki:2660).
- **One active task** — only one 🔴 per project.
- **Never close without a coverage check** — evidence for every acceptance criterion.
- **Don't close unfinished work** — park, not close.
- **Don't bump priority/due after creation** — the human's territory.
- **Don't "settle" tasks by letter/in chat** — the board is the only source of
truth (mappa-messaging canon: "if it's not on the board — it's not a task").
- **Don't poll an empty queue** — empty = stop and report; no daemon/CronCreate.
- **Don't autopilot human-only/strict-human** through close/commit; push — only on grant.
- **Don't batch tasks_create into one repo** — sha-lock conflicts; serialize.
## Red flags — STOP
- «Поставлю таймер проверять новые таски» → нет. Стоп на пустой очереди.
- «Спавну фонового воркера гнать доску» → нет. Один цикл, эта сессия.
- «Таска не готова, но закрою и отмечу» → никогда. Park.
- «Приоритет у таски явно P0, сам бампну» → нет. Вопрос человеку.
- "I'll set a timer to check for new tasks" → no. Stop on an empty queue.
- "I'll spawn a background worker to run the board" → no. One cycle, this session.
- "The task isn't ready, but I'll close and mark it" → never. Park.
- "The task is clearly P0, I'll bump it myself" → no. Ask the human.
---
## Reference
- Делегирование (постановка на агентов): `mappa-delegation`.
- Почта (covering-письма, notify): `mappa-messaging`.
- Знание (wiki-ингест после закрытия): `mappa-knowledge`.
- Финиш сессии (handoff write): `mappa-closing-ritual`.
- Старт сессии (pull/handoff/inbox/snapshot): `mappa-session-orient`.
- Промоушен: `mappa-brainstorm-promote` (review-umbrella для промоушенов).
- Кросс-проектный обзор: `using-system-snapshot` (liveness) / `mcp__projects-meta__tasks_aggregate`.
- Delegation (assigning to agents): `mappa-delegation`.
- Mail (covering letters, notify): `mappa-messaging`.
- Knowledge (wiki-ingest after closing): `mappa-knowledge`.
- Session finish (handoff write): `mappa-closing-ritual`.
- Session start (pull/handoff/inbox/snapshot): `mappa-session-orient`.
- Promotion: `mappa-brainstorm-promote` (review-umbrella for promotions).
- Cross-project overview: `using-system-snapshot` (liveness) / `mcp__projects-meta__tasks_aggregate`.

View File

@@ -1,113 +1,117 @@
---
name: report-mappa-issue
author: ours
version: 0.1.0
version: 0.2.0
description: >
Use when working with mappa (MCP tools `mcp__mappa__*`, HTTP-роуты, скилы на
mappa) and anything deviates from the expected workflow: 500/5xx, «entity not
found» для id, который должен существовать, неожиданная форма ответа,
таймауты, молчаливые сбои, неверный статус, нестабильность. Report it by
mail to `mappa` AND `.workshop` — never swallow, never only-local-log, never
only in-chat. TEMPORARY skill: active while mappa is unstable; retire when
stabilized. Triggers: «маппа отдала 500», «entity not found», «неожиданный
Use when working with mappa (MCP tools `mcp__mappa__*`, HTTP routes, mappa
skills) and anything deviates from the expected workflow: 500/5xx, "entity
not found" for an id that must exist, unexpected response shape, timeouts,
silent failures, wrong status, instability. Report it by mail to `mappa` AND
`.workshop` — never swallow, never only-local-log, never only in-chat.
TEMPORARY skill: active while mappa is unstable; retire when stabilized.
Triggers (bilingual): «маппа отдала 500», «entity not found», «неожиданный
ответ от mappa», «mappa вернула», "mappa returned 500", "entity not found",
unexpected mappa response.
"unexpected mappa response".
---
# report-mappa-issue
Любое отклонение от ожидаемого mappa-воркфлоу репортится **почтой в `mappa`
и `.workshop`** — немедленно, с evidence. Не глотать, не прятать в локальный
лог, не откладывать «до сборника».
Any deviation from the expected mappa workflow is reported **by mail to `mappa`
and `.workshop`** — immediately, with evidence. Never swallow, never hide it in
a local log, never postpone "until a digest".
> ⚠️ **TEMPORARY (временный скил):** действует, пока mappa нестабильна. Это
> stopgap для сбора сигналов к стабилизации. Когда mappa стабилизируется
> (неделя без репортов) — скил отзывается: репорты становятся обычными
> баг-тасками. Владелец решения об отзыве — workshop.
> ⚠️ **TEMPORARY skill:** active while mappa is unstable. It is a stopgap for
> collecting signals toward stabilization. When mappa stabilizes (a week
> without reports) — this skill is retired: reports become ordinary bug tasks.
> The owner of the retirement decision is workshop.
## When to use
Репортить, когда в ходе работы с mappa произошло **любое** из:
Report when, during work with mappa, **any** of the following happens:
- **5xx / 500 / 502** на любом вызове (`task_*`, `wiki_*`, `inbox_*`, `entity_*`,
`admin_*`, `graph_*`, HTTP-роуты).
- **«Entity not found» / 404** для id/ref, который **должен** существовать
(знаешь, что создавал; видишь в свежем ответе; ссылается другое письмо/таска).
- **Неожиданная форма ответа** — поля не совпадают с документированными,
пустой `rows` где ожидались данные, новый/неожиданный тип в ответе.
- **Таймауты / зависания** вызова.
- **Молчаливый сбой** — вызов «успешен», но эффекта нет (таска не создалась,
письмо не ушло, статус не поменялся).
- **Ретрай сработал** — даже если повторный вызов прошёл: сама нестабильность
— сигнал для стабилизации (пометь `retry: resolved`).
- **Неверный/неожиданный статус** сущности, рассинхрон борда и реальности.
- **5xx / 500 / 502** on any call (`task_*`, `wiki_*`, `inbox_*`, `entity_*`,
`admin_*`, `graph_*`, HTTP routes).
- **"Entity not found" / 404** for an id/ref that **must** exist (you know you
created it; you see it in a fresh response; another letter/task references it).
- **Unexpected response shape** — fields don't match the documented ones,
empty `rows` where data was expected, a new/unexpected type in the response.
- **Timeouts / hangs** on a call.
- **Silent failure** — the call "succeeded" but had no effect (task not
created, letter not delivered, status unchanged).
- **Retry worked** — even if the repeated call succeeded: the instability
itself is a signal for stabilization (mark `retry: resolved`).
- **Wrong/unexpected entity status**, board vs reality desync.
**Ретраи допустимы** (12 с паузой), но репорт — независимо от исхода ретрая:
случай 500 → репорт; случай 500→ретрай→ок → репорт с `retry: resolved`.
**Retries are allowed** (12 with a pause), but the report happens regardless
of the retry outcome: case 500 → report; case 500 → retry → ok → report with
`retry: resolved`.
## When NOT to use
- **Ожидаемый 404** — сущность действительно не существует и не должна
(никогда не создавалась; удалена по дизайну). Проверь перед репортом, что
сущность обязана была быть.
- **Документированные известные ограничения** (например, «verify на проде
невозможен по дизайну», «прод stale до редеплоя» — если это задокументировано
и известно команде mappa).
- **Отклонения НЕ от mappa** — VDS/docker (→ using-vds-ops), projects-meta кэш
(документированная сталезность), провайдеры моделей. Только mappa.
- **Уже зарепорченный тот же инцидент** — не дублируй (см. Dedup).
- **Expected 404** — the entity genuinely does not exist and should not
(never created; deleted by design). Before reporting, check that the entity
was required to exist.
- **Documented known limitations** (e.g. "verify on prod is impossible by
design", "prod is stale until redeploy" — if documented and known to the
mappa team).
- **Deviations NOT from mappa** — VDS/docker (→ using-vds-ops), projects-meta
cache (documented staleness), model providers. Only mappa.
- **The same incident already reported** — don't duplicate (see Dedup).
## Core pattern — репорт
## Core pattern — the report
Каждый вызов: `mcp__mappa__inbox_send` в **оба** адреса (`mappa` и `.workshop`,
адреса из адресной книги `~/projects/.wiki/concepts/projects-address-book.md`),
`from` = своё имя папки. Формат письма:
Each call: `mcp__mappa__inbox_send` to **both** addresses (`mappa` and
`.workshop`, addresses from the address book
`~/projects/.wiki/concepts/projects-address-book.md`), `from` = your own
folder name. Letter format:
```
Subject: [mappa-issue] <симптом> @ <тул/эндпоинт> (<дата>)
Subject: [mappa-issue] <symptom> @ <tool/endpoint> (<date>)
Body:
- Expected: <что должно было произойти по воркфлоу/докам>
- Actual: <ошибка/статус/ответ — текст сообщения или короткий сниппет>
- Call: <тул + ключевые параметры / эндпоинт + project>
- Retry: <сработал ли ретрай, сколько попыток>
- Recurrence: <первый раз / повторяется — сколько раз за сессию>
- Context: <проект, сессия, какой флоу шёл>
- Expected: <what should have happened per workflow/docs>
- Actual: <error/status/response — message text or a short snippet>
- Call: <tool + key parameters / endpoint + project>
- Retry: <did the retry work, how many attempts>
- Recurrence: <first time / repeats — how many times this session>
- Context: <project, session, which flow was running>
```
Одно письмо = **один инцидент** (симптом × эндпоинт). Рекуррентность — в том же
письме (`recurrence: 5 раз за 2 часа`), не новый репорт на каждый вызов.
One letter = **one incident** (symptom × endpoint). Recurrence goes in the same
letter (`recurrence: 5 times in 2 hours`), not a new report per call.
## Common mistakes / rationalizations
| Рационализация | Реальность |
| Rationalization | Reality |
|---|---|
| «Mappa упала — письмо не дойдёт, зачем писать» | Письмо — сущность в Mappa (карв-аут, без лиза). При оживлении сервиса оно будет в инбоксе получателя. Пиши всегда. |
| «Расскажу человеку в чате» | Человек не всегда в сессии, команда mappa чат не видит. Письмо — durable и кросс-сессионно. |
| «Запишу в локальный лог» | Локальный лог не виден команде mappa. Цель репорта — видимость у получателей. (Локальная запись — дополнительно, не вместо.) |
| «Ретрай сработал — значит ок» | Нестабильность — сам по себе сигнал. Репорть с `retry: resolved`. |
| «Это мелочь, не буду спамить» | Пока mappa нестабильна — любой сигнал материал для стабилизации. Dedup защищает от спама, не молчание. |
| «Соберу несколько и отпишусь разом» | Первое вхождение — немедленно. Рекуррентность докидывай в то же письмо. |
| «Это наверняка уже известно mappa» | Неизвестно, пока не зарепорчено. Репорт — это и есть способ сделать известным. |
| "Mappa is down — the letter won't arrive, why write" | A letter is an entity in Mappa (carve-out, no lease). When the service revives, it will be in the recipient's inbox. Always write. |
| "I'll tell the human in chat" | The human is not always in session; the mappa team doesn't see chat. A letter is durable and cross-session. |
| "I'll write it in the local log" | The local log is invisible to the mappa team. The goal of the report is visibility for recipients. (Local recording is extra, not instead.) |
| "The retry worked — so it's fine" | The instability itself is a signal. Report with `retry: resolved`. |
| "It's a small thing, I won't spam" | While mappa is unstable — any signal is material for stabilization. Dedup protects against spam, silence does not. |
| "I'll collect several and report at once" | First occurrence — immediately. Recurrence gets appended to the same letter. |
| "Mappa surely already knows this" | Unknown until reported. The report is exactly how it becomes known. |
## Red flags — STOP
- Поймал ошибку mappa и продолжил молча (без репорта).
- Записал только локально / сказал только в чате — письма нет.
- Пропустил «entity not found», не проверив, должен ли id существовать.
- Отложил репорт «на потом» без письма и без таски.
- Зарепортил, но не в оба адреса (`mappa` и `.workshop`).
- Caught a mappa error and silently continued (no report).
- Recorded only locally / said only in chat — no letter.
- Skipped "entity not found" without checking whether the id must exist.
- Postponed the report "for later" without a letter and without a task.
- Reported but not to both addresses (`mappa` and `.workshop`).
## Cross-agent
Канал — mappa inbox (`inbox_send` / `inbox.monitor`), общий для всех агентов
(pi: `mcp__mappa__inbox_send`; Claude Code: те же MCP-тулы; headless — то же).
Адресация — строго из адресной книги (`inter-session-messaging` канон).
Channel — mappa inbox (`inbox_send` / `inbox.monitor`), shared by all agents
(pi: `mcp__mappa__inbox_send`; Claude Code: the same MCP tools; headless — the
same). Addressing strictly from the address book (`inter-session-messaging`
canon).
## Out of scope
- **Не чинит mappa** — диагностика/починка сервиса отдельно; скил только
репортит. (Глубокий диагноз`diagnosing-bugs` / `using-vds-ops` для инфры.)
- **Не репортит чужие сервисы** — только отклонения от mappa-воркфлоу.
- **Не заменяет** `inter-session-messaging` (механика отправки — там, этот скил
задаёт политику «что считать инцидентом»).
- **Does not fix mappa** — service diagnosis/repair is separate; this skill
only reports. (Deep diagnosis`diagnosing-bugs` / `using-vds-ops` for
infra.)
- **Does not report other services** — only deviations from the mappa workflow.
- **Does not replace** `inter-session-messaging` (the send mechanics live
there; this skill defines the policy "what counts as an incident").