Compare commits

..

8 Commits

8 changed files with 1239 additions and 1135 deletions

View File

@@ -1,247 +1,252 @@
--- ---
name: mappa-brainstorm-promote name: mappa-brainstorm-promote
author: ours author: ours
version: 1.6.0 version: 1.7.0
description: > description: >
Finalize a matured brainstorm buffer (mappa entity type=brainstorm, status=buffer): Finalize a matured brainstorm buffer (mappa entity type=brainstorm,
read buffer → choose target project → promote via mcp__mappa__brainstorm_promote status=buffer): read buffer → choose target project → promote via
(atomic buffer → wiki-страница в target + archive, решение 7) → extract mcp__mappa__brainstorm_promote (atomic buffer → wiki-page in target +
action-items into target tasks (mcp__mappa__task_create, карв-аут) → review archive, decision 7) → extract action-items into target tasks
umbrella → covering letter. Общий механизм mappa, как task.create/wiki.create — (mcp__mappa__task_create, carve-out) → review umbrella → covering letter.
никакой workshop-специфики. Старое имя — триггер-синоним: A general mappa mechanism, like task.create/wiki.create — no workshop
workshop-promote-brainstorm. Triggers (user): «промоутни брейнсторм», specifics. Old name — trigger-synonym: workshop-promote-brainstorm.
«finalize <topic>», «выкати в вики», «promote <topic>». Triggers (bilingual): «промоутни брейнсторм», «finalize <topic>», «выкати в
вики», «promote <topic>», "promote the brainstorm", "finalize <topic>".
--- ---
# mappa-brainstorm-promote # mappa-brainstorm-promote
Финализация созревшего брейнсторм-буфера, который живёт **как mappa-сущность Finalizing a matured brainstorm buffer that lives **as a mappa entity of type
типа `brainstorm`** (status=buffer). Это общий механизм mappa — ровно как `brainstorm`** (status=buffer). This is a general mappa mechanism — exactly
`task.create` или `wiki.create`: буфер существует в mappa, скил доводит его до like `task.create` or `wiki.create`: the buffer exists in mappa, the skill
конца (промоут контента в вики + action-items тасками). Никакой takes it to the end (promote the content into the wiki + action-items as
workshop-специфики: скил триггерится из любой папки, работает с brainstorm- tasks). No workshop specifics: the skill triggers from any folder, works with
сущностями любого проекта. brainstorm entities of any project.
Процедура линейная (от чтения буфера до промоута и тасок), не цикл: запускается 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.
работой (`mappa-task-work`) и финишем (`mappa-closing-ritual`). In the forkflow it sits between work (`mappa-task-work`) and finish
(`mappa-closing-ritual`).
**Промоут контента — всегда через `mcp__mappa__brainstorm_promote`:** **Content promotion — always via `mcp__mappa__brainstorm_promote`:**
атомарно создаёт wiki-страницу (slug из буфера, body сохраняется) в проекте из atomically creates a wiki page (slug from the buffer, body preserved) in the
вызова и переводит буфер в `archive` (номер/slug стабильны, решение 20; рёбра project from the call and moves the buffer to `archive` (number/slug stable,
parent_of, событие `brainstorm.promoted`). Никаких файловых каналов. Action-items decision 20; parent_of edges, `brainstorm.promoted` event). No file channels.
уходят тасками в target-проект через `mcp__mappa__task_create` (карв-аут без Action-items go as tasks to the target project via `mcp__mappa__task_create`
лиза, #1054; последовательно, не батчем). (carve-out without a lease, #1054; sequentially, not batched).
## When to use ## When to use
- «промоутни брейнсторм», «finalize <topic>», «выкати в вики», «promote - «промоутни брейнсторм», «finalize <topic>», «выкати в вики», «promote
<topic>». <topic>», "promote the brainstorm".
- Пользователь ссылается на brainstorm-сущность (brainstorm:N) или на тему - The user references a brainstorm entity (brainstorm:N) or a buffer topic
буфера, который созрел и готов к промоушену. that matured and is ready for promotion.
## Inputs ## Inputs
- Brainstorm-реф `brainstorm:N` или `<topic>` (slug/тема буфера) + project (если - Brainstorm ref `brainstorm:N` or `<topic>` (buffer slug/topic) + project (if
буфер не в текущем проекте — спросить). the buffer is not in the current project — ask).
- Для skill-ветки дополнительно: `<name>` нового скила (если не указан — - For the skill branch additionally: `<name>` of the new skill (if not
спросить, предложить производное от topic). specified — ask, propose a derivation from the topic).
## Decision flow ## 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>) ├── ordinary project → brainstorm_promote(project=<target>)
│ → wiki-страница (спека) в вики target │ → wiki page (spec) in the target wiki
└── skill → dialog: description (trigger contract) └── skill → dialog: description (trigger contract)
→ preview + confirm → preview + confirm
→ mkdir + Write SKILL.md (каркас) в ~/projects/skills/ → mkdir + Write SKILL.md (skeleton) in ~/projects/skills/
→ git add + commit (local, no push/install) → 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#) 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 ## Steps
1. **Найти буфер в mappa.** `mcp__mappa__entity_search(type='brainstorm', 1. **Find the buffer in mappa.** `mcp__mappa__entity_search(type='brainstorm',
project=<проект>, q=<topic>)` → в результатах brainstorm:N + **internal id**. project=<project>, q=<topic>)` → in the results brainstorm:N + **internal
Прочитать полный буфер: `mcp__mappa__entity_get(id=<internal id>)` — тело = id**. Read the full buffer: `mcp__mappa__entity_get(id=<internal id>)` —
running record (frontmatter + раунды). body = running record (frontmatter + rounds).
Если буфера нет в mappa — создать brainstorm-сущность через If the buffer is not in mappa — create a brainstorm entity via
`mcp__mappa__brainstorm_create` (или HTTP `POST /entities` type=brainstorm, `mcp__mappa__brainstorm_create` (or HTTP `POST /entities` type=brainstorm,
контракт решения 7/#1054). Не изобретать файловые буферы. contract decision 7/#1054). Don't invent file buffers.
2. **Показать summary буфера (≤2 абзаца).** 2. **Show the buffer summary (≤2 paragraphs).**
3. **Спросить target-проект** — куда промоутить контент. По умолчанию — проект, 3. **Ask the target project** — where to promote the content. Default — the
где буфер живёт (брейншторм ведут там, где тема релевантна). Проверить, что project where the buffer lives (brainstorms are run where the topic is
проект существует в mappa: `mcp__mappa__entity_search` type=project relevant). Verify the project exists in mappa:
(или `mcp__mappa__entity_search` с q=<имя проекта>). Если нет — abort с `mcp__mappa__entity_search` type=project (or `mcp__mappa__entity_search`
сообщением. with q=<project name>). If not — abort with a message.
4. **Если target = skill (пользователь хочет оформить как скил):** 4. **If target = skill (the user wants it as a skill):**
- Спросить `<name>` нового скила (если не указан) — валидный slug - Ask `<name>` of the new skill (if not specified) — a valid slug
(`[a-z0-9-]+`). (`[a-z0-9-]+`).
- Валидация (порядок важен): сначала проверить, что `~/projects/skills/` - Validation (order matters): first check that `~/projects/skills/` itself
сам репозиторий существует. Если нет — **abort** с сообщением «клонируй is a repository. If not — **abort** with the message "clone skills via
skills через update-skills или вручную». update-skills or manually".
- Затем: `~/projects/skills/skills/<name>/` НЕ должна существовать. - Then: `~/projects/skills/skills/<name>/` must NOT exist. If it exists —
Если существует — **abort** с сообщением «скил `<name>` уже существует, **abort** with the message "skill `<name>` already exists, update through
обновляйся обычным маршрутом в `~/projects/skills/`, этот скил не для the normal route in `~/projects/skills/`, this skill is not for updates".
апдейтов». - Two-pass skeleton: dialog on `description` (activation trigger contract:
- Двухпроходной каркас: диалог по `description` (триггер-контракт активации: minimum 2-3 phrases, Russian/English pairs; what it does; antipatterns) →
минимум 2-3 фразы, пары русский/английский; что делает; антипаттерны) → preview + confirm → `Write` of the skeleton (header + 6 empty sections) →
preview + confirm → `Write` каркаса (шапка + 6 пустых секций) → локальный local `git commit` in `~/projects/skills/`. **Without** install.sh, push,
`git commit` в `~/projects/skills/`. **Без** install.sh, push, build-hermes — those are in the baseline tasks of step 7. The body of the
build-hermes — это в baseline-тасках шага 7. Тело каркаса дописывается skeleton is written in a second pass by eye (outside this skill's scope).
вторым проходом глазами (вне scope этого скила).
5. **Промоут контента (всегда через `brainstorm_promote`, решение 7):** 5. **Content promotion (always via `brainstorm_promote`, decision 7):**
`mcp__mappa__brainstorm_promote(project=<target>, brainstorm_id=<internal id>)` `mcp__mappa__brainstorm_promote(project=<target>, brainstorm_id=<internal id>)`
- Атомарно: buffer → wiki-страница (slug из буфера, body сохраняется, рёбра - Atomically: buffer → wiki page (slug from the buffer, body preserved,
parent_of buffer→wiki и refs→buffer) + буфер → `archive` + значимое событие parent_of buffer→wiki edges and refs→buffer) + buffer → `archive` +
`brainstorm.promoted`. significant `brainstorm.promoted` event.
- **Frontmatter-summary (wiki:2661):** убедиться, что в теле буфера есть - **Frontmatter-summary (wiki:2661):** make sure the buffer body has
`summary:` одной строкой в frontmatter — карточки `wiki.search` читают его. `summary:` as one line in the frontmatter — `wiki.search` cards read it.
Если нет — дописать через `mcp__mappa__brainstorm_update` (PATCH If missing — append via `mcp__mappa__brainstorm_update` (PATCH
/brainstorm/:id, title/body/status, optimistic version+409) перед промоутом. /brainstorm/:id, title/body/status, optimistic version+409) before the
- Повторный промоут архивированного буфера → ошибка (one-shot, идемпотентно promotion.
через статус). Сверить `brainstorm_id` (internal) из шага 1. - Re-promoting an archived buffer → error (one-shot, idempotent via
- Если `brainstorm_promote` упал (конфликт версии, 409) → retry со свежим status). Cross-check `brainstorm_id` (internal) from step 1.
internal id; при стабильном отказе — abort до создания тасок. - 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 по строкам вида `- [ ] ...` в теле буфера, 6. **Action-items parsing:** regex over lines like `- [ ] ...` in the buffer
секции после `## Следующие шаги`/`## TODO`/`## Next steps`/ body, sections after `## Следующие шаги`/`## TODO`/`## Next steps`/
`## Action items`. Показать список, дать редактировать/удалять/добавлять. `## Action items`. Show the list, allow editing/removing/adding. If 0
Если 0 action-items — продолжить, не блокировать. action-items — continue, don't block.
7. **Создание тасок:** 7. **Task creation:**
> **NB:** таски создавать **ПОСЛЕДОВАТЕЛЬНО**, не батчем. Один > **NB:** create tasks **SEQUENTIALLY**, not batched. One `task_create` →
> `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, `mcp__mappa__task_create(project=<target>, slug=<kebab>, title, description,
status='ready')`. Create — карв-аут, лиз не нужен (wiki:2660/#1054). status='ready')`. Create — carve-out, no lease needed (wiki:2660/#1054).
Описание импл-таски ссылается на спеку (wiki:NNNN из шага 5). The impl task description references the spec (wiki:NNNN from step 5).
- **Skill:** три baseline-таски в `project='skills'`: - **Skill:** three baseline tasks in `project='skills'`:
- `[<name>-install]` — запустить `install.sh` в `~/projects/skills/`, - `[<name>-install]` — run `install.sh` in `~/projects/skills/`,
проверить активацию в новой сессии. verify activation in a new session.
- `[<name>-hermes-mapping]` — запись в `~/projects/skills/hermes/mapping.yaml` - `[<name>-hermes-mapping]` — a record in
(режим `auto` для стилевых, `pending` если трогает тулы/окружение). `~/projects/skills/hermes/mapping.yaml` (mode `auto` for style ones,
- `[<name>-test-trigger]` — прогнать триггер-фразы из description: `pending` if it touches tools/environment).
активируется на своих, не активируется на 2-3 близких чужих. - `[<name>-test-trigger]` — run the trigger phrases from the description:
Плюс content-таски из буфера (если были) — тоже в `project='skills'`, 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>-`. slug-prefix `<name>-`.
- Если N-я таска упала — продолжить остальные, в конце сообщить какие - If the N-th task failed — continue the rest, report at the end which were
созданы / какие нет. Запомнить slug'и для review-umbrella. 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, `mcp__mappa__task_create(project=<target>, slug=<topic>-review,
status='blocked', blocker=<номера импл-тасок через запятую>, description=<чек-лист>)` status='blocked', blocker=<impl task numbers separated by commas>, description=<checklist>)`
- **Кто делает:** не имплементер. Следующая сессия в этом проекте (другая - **Who does it:** not the implementer. The next session in this project (a
модель / другой день / другой агент) с чистым контекстом. «Я только что different model / different day / different agent) with a clean context.
это написал» bias = главный риск. The "I just wrote this" bias is the main risk.
- Чек-лист: прочитать спеку (wiki:NNNN из шага 5), `git log` shipped-коммитов, - Checklist: read the spec (wiki:NNNN from step 5), `git log` of the
для каждой импл-таски прогнать тесты и сверить с acceptance criteria, shipped commits, for each impl task run the tests and cross-check with
findings → follow-up tasks через `task_create`. acceptance criteria, findings → follow-up tasks via `task_create`.
- Закрытие: все findings зафайлены ИЛИ ревьюер подтвердил «нет findings» - Closing: all findings filed OR the reviewer confirmed "no findings" in
в close-note. the close-note.
- Если review-таска упала — сообщить, **продолжить** к шагу 9 (промоут уже - If the review task failed — report, **continue** to step 9 (the promotion
сделан, буфер в archive). 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='Промоушен `mcp__mappa__inbox_send(project=<target>, from=<your folder>, subject='Promotion
<topic>: таски <#N…>', body=<перечень + wiki:NNNN спека>)` <topic>: tasks <#N…>', body=<list + wiki:NNNN spec>)`
10. **Финальный отчёт пользователю:** 10. **Final report to the user:**
- Куда промочено: `wiki:NNNN` (спека в вики target). - Where it was promoted: `wiki:NNNN` (spec in the target wiki).
- Архив: `brainstorm:N` (status=archive, номер стабилен). - Archive: `brainstorm:N` (status=archive, number stable).
- Какие таски созданы (ref, title, проект). - Which tasks were created (ref, title, project).
- **Для skill:** напомнить про второй проход «доведём `<name>`». - **For skill:** remind about the second pass "let's flesh out `<name>`".
## Failure modes ## Failure modes
- Буфер не найден в mappa (нет brainstorm-сущности) → abort, сообщить: создать - Buffer not found in mappa (no brainstorm entity) → abort, report: create via
через `brainstorm_create` (шаг 1) или HTTP POST /entities. `brainstorm_create` (step 1) or HTTP POST /entities.
- `entity_search`/`entity_get` упал (API-ошибка, не пустой результат) → abort - `entity_search`/`entity_get` failed (API error, not an empty result) → abort
с текстом ошибки; не создавать буфер по догадке. with the error text; don't create a buffer by guess.
- Target-проект не существует в mappa → abort до промоута. - Target project doesn't exist in mappa → abort before promotion.
- `brainstorm_promote` упал (409 версия / стабильный отказ) → retry со свежим - `brainstorm_promote` failed (409 version / stable refusal) → retry with the
internal id; при повторном отказе — abort до создания тасок. Буфер остаётся fresh internal id; on a repeated failure — abort before creating tasks. The
в buffer — ретраится позже. buffer stays in buffer — retried later.
- Буфер уже `archive` (повторный вызов) → abort: промоут one-shot, - Buffer already `archive` (repeated call) → abort: promotion is one-shot,
идемпотентность через статус (решение 7). idempotence via status (decision 7).
- `task_create` упал на N-й content-таске → продолжить остальные, сообщить - `task_create` failed on the N-th content task → continue the rest, report
partial. Промоут уже сделан — буфер не откатывается. partial. The promotion is already done — the buffer is not rolled back.
- `task_create` review-umbrella упал → не блокировать, сообщить пользователю - `task_create` review-umbrella failed → don't block, report to the user
(создать вручную из шага 8). (create manually from step 8).
- `inbox_send` (covering-письмо) упал → промоут и таски не откатываются; - `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/` не существует → abort. - **Skill:** `~/projects/skills/` doesn't exist → abort.
- **Skill:** `~/projects/skills/skills/<name>/` уже существует → abort. - **Skill:** `~/projects/skills/skills/<name>/` already exists → abort.
- **Skill:** пользователь не подтвердил preview → abort, состояние не меняется. - **Skill:** user didn't confirm the preview → abort, state unchanged.
- **Skill:** локальный `git commit` в `~/projects/skills/` упал → файл остаётся, - **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 ## Side effects
- **Всегда:** `brainstorm_promote` — атомарно wiki-страница в target + буфер → - **Always:** `brainstorm_promote` — atomically wiki page in the target +
`archive` + рёбра parent_of (wiki→buffer, refs→buffer) + событие buffer → `archive` + parent_of edges (wiki→buffer, refs→buffer) +
`brainstorm.promoted`. `brainstorm.promoted` event.
- **Обычный target:** спека-страница в вики целевого проекта (с - **Ordinary target:** spec page in the target project's wiki (with
frontmatter-summary, wiki:2661) + импл-таски + review-umbrella + covering-письмо. frontmatter-summary, wiki:2661) + impl tasks + review-umbrella + covering letter.
- **Skill:** каркас `~/projects/skills/skills/<name>/SKILL.md` (только шапка + - **Skill:** skeleton `~/projects/skills/skills/<name>/SKILL.md` (only header +
пустой 6-секционный каркас) + локальный коммит в `~/projects/skills/`. empty 6-section skeleton) + local commit in `~/projects/skills/`.
**Без** install.sh, push, build-hermes — это в baseline-тасках. **Without** install.sh, push, build-hermes — those are in the baseline tasks.
- Создаёт N тасок в target через `mcp__mappa__task_create` (карв-аут). - Creates N tasks in the target via `mcp__mappa__task_create` (carve-out).
- Создаёт review-umbrella таску (status=blocked, blocker=impl#). - Creates a review-umbrella task (status=blocked, blocker=impl#).
- Отправляет covering-письмо в инбокс target. - Sends a covering letter to the target's inbox.
## What NOT to do ## What NOT to do
- **Не использовать файловые каналы** — буфер живёт в mappa brainstorm-сущности, - **Don't use file channels** — the buffer lives in a mappa brainstorm entity,
никаких `.brainstorm/`/`.archive/` записей. no `.brainstorm/`/`.archive/` records.
- **Не использовать `mcp__projects-meta__tasks_create` / `knowledge_ingest` / - **Don't use `mcp__projects-meta__tasks_create` / `knowledge_ingest` /
`knowledge_promote`** — файловые каналы выпилены. Таски `knowledge_promote`** — file channels are removed. Tasks
`mcp__mappa__task_create`, вики — `brainstorm_promote` (контент) + `mcp__mappa__task_create`, wiki — `brainstorm_promote` (content) +
`wiki_create`/`wiki_update` (доп. страницы). `wiki_create`/`wiki_update` (extra pages).
- Не делать `git mv` буфера в архив — промоут архивирует сам. - Don't `git mv` the buffer into the archive — the promotion archives it itself.
- Не удалять буфер вместо промоута — теряется граф-история (parent_of, refs). - Don't delete the buffer instead of promoting — the graph history is lost
- Не батчить `task_create` (гонка; инцидент 2026-08-24: 6/7 упали) — только (parent_of, refs).
последовательно. - Don't batch `task_create` (race; incident 2026-08-24: 6/7 failed) — only
- Не забывать covering-письмо — таска на борде не пингует живую сессию. sequentially.
- **Skill:** не переформатировать тело буфера в каркас автоматически — тело - 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 —
- **Skill:** не запускать `install.sh`, не делать push, не править the body is written in a second pass by eye.
`hermes/mapping.yaml` — это baseline-таски. - **Skill:** don't run `install.sh`, don't push, don't edit
- **Skill:** не промоутить в существующий скил (abort). `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 name: mappa-closing-ritual
author: ours author: ours
version: 1.2.0 version: 1.3.0
description: > description: >
Финиш-фаза форкфлоу: handoff write (mcp__mappa__handoff_write, версия h:N) Finish phase of the forkflow: handoff write (mcp__mappa__handoff_write,
+ PROPOSE wiki-ingest + PROPOSE закрытий тасок. Мутации — только после version h:N) + PROPOSE wiki-ingest + PROPOSE task closes. Mutations — only
подтверждения пользователя. Ad-hoc: mode=light — явный вопрос «Сделать after user confirmation. Ad-hoc: mode=light — an explicit question "Write
handoff?» в конце сессии (НЕ автоматический sweep), решение за человеком. handoff?" at the end of the session (NOT an automatic sweep), the decision
Старое имя — триггер-синоним: session-handoff (write-часть). Чтение на is the human's. Old name — trigger-synonym: session-handoff (write part).
старте — mappa-session-orient. Триггеры: «завершаем сессию», «сворачиваемся», Reading at start — mappa-session-orient. Triggers (bilingual): «завершаем
«закругляемся», «wrap up session», «end session», «we're done for now». сессию», «сворачиваемся», «закругляемся», "wrap up session", "end session",
Анти-триггеры (task-зона, не session-end): «закрываем эту таску», «pause», "we're done for now". Anti-triggers (task-zone, not session-end):
«отбой», «разбегаемся». «закрываем эту таску», «pause», «отбой», «разбегаемся».
--- ---
# mappa-closing-ritual # mappa-closing-ritual
Финиш-фаза цикла агента: **записать handoff → предложить wiki-ingest → Finish phase of the agent cycle: **write handoff → propose wiki-ingest →
предложить закрытия тасок**. Старт ≠ финиш: чтение handoff на старте propose task closes**. Start ≠ finish: reading the handoff at start
`mappa-session-orient`; здесь — write-путь с процедурой и подтверждением. `mappa-session-orient`; here — the write path with procedure and confirmation.
Канал — **handoff-сущность Mappa** (`mcp__mappa__handoff_write`, тип `h:`, Channel — the **Mappa handoff entity** (`mcp__mappa__handoff_write`, type
per-project): поля `session_id`/`date`/`status`/`summary`/`open_treks[]`/ `h:`, per-project): fields `session_id`/`date`/`status`/`summary`/
`ask_user[]`/`guards[]`/`recent_commits[]`. Каждый write = **новая версия** `open_treks[]`/`ask_user[]`/`guards[]`/`recent_commits[]`. Each write = a **new
(append-only, versioned-история). Файлового `.tasks/NEXT_SESSION.md` больше нет. version** (append-only, versioned history). The file-based
`.tasks/NEXT_SESSION.md` no longer exists.
## Когда использовать ## When to use
- Session-end фразы: «завершаем сессию», «сворачиваемся», «закругляемся», - Session-end phrases: «завершаем сессию», «сворачиваемся», «закругляемся»,
«wrap up session», «end session», «we're done for now». "wrap up session", "end session", "we're done for now".
- Ad-hoc-сессия без трека/таски в конце: **mode=light**явный вопрос - Ad-hoc session without a track/task at the end: **mode=light**an explicit
«Сделать handoff?» (не автоматический sweep), решение за человеком. question "Write handoff?" (not an automatic sweep), the human decides.
- AGENTS.md проекта содержит trigger-строку `session handoff: read on start, write on end`. - 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), «отбой», «разбегаемся» `mappa-task-work`), «pause», «приостанови» (task-pause), «отбой», «разбегаемся»
(слишком broad), «сейчас завершу одну задачу и тогда поговорим» (частичное (too broad), "let me finish one task first, then we'll talk" (partial finish).
завершение). При неоднозначности — **ASK**: «закрываем сессию или таску?» On ambiguity — **ASK**: "are we closing the session or a task?"
## Steps ## Steps
### 1. Scope check ### 1. Scope check
Это текущий проект (cwd). Никаких global мутаций, никаких других проектов. This is the current project (cwd). No global mutations, no other projects.
### 2. Mid-task capture ### 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> 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 дата>` или идентификатор сессии; - `session_id``<ISO date>` or session identifier;
- `status``active` (работа продолжается) / `paused` (заморожено) / `done` (завершено); - `status``active` (work continues) / `paused` (frozen) / `done` (finished);
- `summary`связка: где остановились, mid-task, ключевые решения; - `summary`the link: where we stopped, mid-task, key decisions;
- `open_treks`массив открытых треков (готовность + entry-point); - `open_treks`array of open tracks (readiness + entry-point);
- `ask_user` — pending решения / ожидаемые разрешения; - `ask_user` — pending decisions / expected resolutions;
- `guards`«не делать» (preemptive guards); - `guards`"don't do" (preemptive guards);
- `recent_commits` — 35 последних коммитов (`<slug>: <subject>`). - `recent_commits` — 35 latest commits (`<slug>: <subject>`).
Forward-looking, не timeline: handoff = связка новых вещей конкретно для Forward-looking, not a timeline: handoff = a link of new things specifically
следующего разворота, не overview всего проекта. Борд mappa / вики остаются for the next turn, not an overview of the whole project. The mappa board / wiki
авторитетными для своего scope'ане дублировать их в handoff. remain authoritative for their own scope — don't duplicate them in the handoff.
### 4. Append ### 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) и при > **Confirm gate:** handoff write is a mutation. In mode=light (ad-hoc) and on
> неоднозначности — сначала явный вопрос «Сделать handoff?», только после «да» > ambiguity — first an explicit question "Write handoff?", only after "yes"
> write. > write.
### 5. Propose wiki-ingest (не писать!) ### 5. Propose wiki-ingest (don't write!)
Если за сессию появилось durable-знание — **ПРЕДЛОЖИТЬ** ingest If durable knowledge appeared during the session — **PROPOSE** the ingest
(`mappa-knowledge`: mappa wiki create — карв-аут, update — version+409, wiki:2660), перечислив кандидатов. (`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!)
Если есть задачи, выглядящие закрытыми — **ПРЕДЛОЖИТЬ** закрытия If there are tasks that look closed — **PROPOSE** the closes
(`mappa-task-work`: task_close с version+409, wiki:2660). Уважать ralph-loop: verifier-задачи (`mappa-task-work`: task_close with version+409, wiki:2660). Respect ralph-loop:
закрывать только через verifier (attempt/harness-record). verifier tasks close only via the verifier (attempt/harness-record).
### 7. Формат предложения — один блок ### 7. Proposal format — one block
``` ```
Ритуал закрытия: Closing ritual:
(а) заингестить X в вики? (a) ingest X into the wiki?
(б) закрыть Y? (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 Ad-hoc session (no task/track, but artifacts may be born): the trace in mappa
пишется всегда (контракт сервиса — session live-ingest), но **структурирование — is always written (service contract — session live-ingest), but
по явному вопросу**: в конце сессии спроси «Сделать handoff?» (решение за **structuring happens only on an explicit question**: at the end of the
человеком). НЕ автоматический sweep: без «да» — ничего не писать, след session ask "Write handoff?" (the human decides). NOT an automatic sweep:
остаётся в mappa как есть. without "yes" — write nothing, the trace stays in mappa as is.
## Failure modes ## Failure modes
- **Secret detected.** Контент матчит паттерны секретов (`AKIA...`, `sk-...`, - **Secret detected.** Content matches secret patterns (`AKIA...`, `sk-...`,
`ghp_...`, `ssh-rsa`, `BEGIN PRIVATE KEY`, `password=`/`token=`) → **abort `ghp_...`, `ssh-rsa`, `BEGIN PRIVATE KEY`, `password=`/`token=`) → **abort
write**. Сообщить user'у с указанием подозрительной строки. write**. Tell the user with the suspicious line indicated.
- **Неоднозначная фраза** → ASK «закрываем сессию или таску?», не угадывать. - **Ambiguous phrase** → ASK "are we closing the session or a task?", don't guess.
- **Mid-task без борда** → handoff без mid-task секции, не блокировать. - **Mid-task without a board** → handoff without the mid-task section, don't block.
- **User отказал на предложения ритуала** → пропустить, не настаивать. - **User refused the ritual proposals** → skip, don't insist.
- **Проект не в mappa** → silent exit (первая сессия). - **Project not in mappa** → silent exit (first session).
## Side effects ## Side effects
- Пишет handoff-сущность проекта (append-only, versioned-история). Никаких - Writes the project's handoff entity (append-only, versioned history). No
файлов, никаких git-коммитов за handoff. files, no git commits for the handoff.
- Ритуал **предлагает** wiki-ingest и закрытия тасок — но НЕ пишет их без «да». - The ritual **proposes** wiki-ingest and task closes — but does NOT write
- Никаких global мутаций, никаких других проектов. them without "yes".
- No global mutations, no other projects.
## What NOT to do ## What NOT to do
- **Не auto-execute** — каждая мутация (handoff write / wiki-ingest / task - **No auto-execute** — every mutation (handoff write / wiki-ingest / task
close) только после явного «да» пользователя. close) only after an explicit "yes" from the user.
- **Не гонять ритуал на substantive commit.** Только session-end фраза (или - **Don't run the ritual on a substantive commit.** Only a session-end phrase
явный запрос user'а). (or an explicit user request).
- **Не sweep автоматически в ad-hoc** — mode=light: явный вопрос «Сделать - **Don't sweep automatically in ad-hoc** — mode=light: an explicit question
handoff?», решение за человеком. "Write handoff?", the human decides.
- **Не писать секреты** в handoff. Матч secret-patterns → abort. - **Don't write secrets** into the handoff. Secret-pattern match → abort.
- **Не писать handoff на каждый commit** — только финиш. - **Don't write a handoff on every commit** — only at the finish.
- **Не дублировать борд / вики** — handoff = forward-looking связка. - **Don't duplicate the board / wiki** — handoff = forward-looking link.
- **Не cross-project** — per-project scope. - **No cross-project** — per-project scope.
- **Не считать handoff авторитетным** на стороне читателя — это рекомендация - **Don't treat the handoff as authoritative** on the reader's side — it's the
прошлой сессии, не директива; user может override. previous session's recommendation, not a directive; the user can override.
## Reference ## Reference
- Чтение handoff на старте: `mappa-session-orient`. - Reading the handoff at start: `mappa-session-orient`.
- Закрытия тасок: `mappa-task-work` (pre-close coverage, notify-письмо). - Task closes: `mappa-task-work` (pre-close coverage, notify letter).
- Wiki-ingest: `mappa-knowledge`. - Wiki-ingest: `mappa-knowledge`.
- Письма: `mappa-messaging`. Делегирование: `mappa-delegation`. - Letters: `mappa-messaging`. Delegation: `mappa-delegation`.
- Session live-ingest (след в mappa пишется всегда): `concepts/session-live-ingest` (wiki:2604). - 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 name: mappa-delegation
author: ours author: ours
version: 1.2.0 version: 1.3.0
description: > description: >
Цикл делегирования задачи другому агенту/проекту: pre-flight gate → The cycle of delegating a task to another agent/project: pre-flight gate →
шаблон тела → dry-run preview → confirm → covering-письмо в инбокс body template → dry-run preview → confirm → covering letter to the
получателя → парная review-таска для impl. Каждая кросс-проектная делегация recipient's inbox → paired review task for impl. Every cross-project
— пара: tasks_create + письмо (event: created) — таска на борде не пингует delegation is a pair: tasks_create + letter (event: created) — a task on the
живую сессию. Старое имя — триггер-синоним: delegate-task. Триггеры: board does not ping a live session. Old name — trigger-synonym:
«делегировать таску», «delegate task», «создать задачу на агента», delegate-task. Triggers (bilingual): «делегировать таску», «создать задачу
«поставить задачу агенту», «tasks_create для». НЕ применимо: self-assigned на агента», «поставить задачу агенту», «tasks_create для», "delegate task",
таски на своей доске («создать задачу себе» → mappa-task-work), работа "create a task for an agent", "assign a task to an agent". NOT applicable:
своими руками, workshop-внутренние таски. self-assigned tasks on your own board («создать задачу себе» →
mappa-task-work), doing work yourself, workshop-internal tasks.
--- ---
# mappa-delegation # mappa-delegation
Унифицированный **цикл постановки задач на агентов**: от pre-flight гейта до Unified **cycle of assigning tasks to agents**: from the pre-flight gate to
covering-письма получателю. Гарантирует, что каждая делегированная задача the covering letter to the recipient. Guarantees that every delegated task
содержит: обязательные скилы (императивный invoke), pre-flight разрешения, carries: mandatory skills (imperative invoke), pre-flight permissions,
steering-loop поля (notify/weight), парную review-таску для impl — и что 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 ## 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".
**Не применяется:** **Not applicable:**
- Работа которую выполняешь сам в текущей сессии. - Work you do yourself in the current session.
- Self-assigned таски на своей доске («создать задачу себе», «task for myself», «поставить себе задачу») → `mappa-task-work`, не делегирование. Дизамбигуатор: «на агента»/«агенту»/«в проект X» = делегирование; «себе»/«myself» = своя доска. - Self-assigned tasks on your own board («создать задачу себе», "task for myself") → `mappa-task-work`, not delegation. Disambiguator: «на агента»/«агенту»/«в проект X» = delegation; «себе»/"myself" = your own board.
- Workshop-internal таски (`.workshop/.tasks/` — workshop-meta, не делегирование). - Workshop-internal tasks (`.workshop/.tasks/` — workshop-meta, not delegation).
- `tasks_create` с `target=agenda` (cross-project agenda — не делегирование агенту). - `tasks_create` with `target=agenda` (cross-project agenda — not delegation to an agent).
## Inputs ## Inputs
- `target_project` — qualified `<owner>/<repo>` (обязательно) - `target_project` — qualified `<owner>/<repo>` (required)
- `slug` — kebab-case latin - `slug` — kebab-case latin
- Краткое описание задачи (цель + acceptance criteria) - Short task description (goal + acceptance criteria)
- `weight``cheap-ok | needs-claude | needs-human` - `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` — **карв-аут без > **Contract (interactive, wiki:2660).** `task_create` — **carve-out without a
> лиза** (create-без-лиза как принцип); update/close — version+409 (409 → > lease** (create-without-lease as a principle); update/close — version+409
> re-GET → retry). file channel — sha-CAS через Gitea. Claim/TTL нет > (409 → re-GET → retry). file channel — sha-CAS via Gitea. No claim/TTL —
> «взять таску» = conditional update по version (поллер вне mappa). > "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. 0. **Critical infrastructure?** — the task changes: poller/agent-runner, MCP
- Если **да**`weight: needs-human` принудительно, без обсуждения. Объяснить пользователю почему. servers (projects-meta, interns), claim/close/heartbeat mechanics, deploy
- Если **нет** → идти дальше. infra (traefik, docker, systemd), CI/CD pipelines, git hooks.
1. **Интерны — разрешены?** (да/нет, per задача) - If **yes** → force `weight: needs-human`, no discussion. Explain to the
2. **Автопуш — разрешён?** (да/нет, per задача) user why.
3. **Контекстные скилы сверх дефолтов?** — предложить по содержанию задачи (например `claude-api` для работы с Anthropic SDK, `frontend-design` для UI, `using-interns` если интерны разрешены), пользователь утверждает. - If **no** → continue.
4. **notify — кому докладывать о завершении/затыке?** (slug проекта; обычно `.workshop` или `OpeItcLoc03/workshop`) 1. **Interns — allowed?** (yes/no, per task)
5. **Session-break после этой задачи?** — нужен ли разрыв сессии после её закрытия (domain-switch, milestone, heavy infra)? 2. **Auto-push — allowed?** (yes/no, per task)
- Если **да** → проставить `session_break` в теле задачи (см. шаблон): `true` или строка-hint с названием следующего трека. `mappa-task-work` остановится после close и предложит завершить сессию, не клеймя следующую задачу. 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 если есть.> <Goal — one or two sentences. Acceptance criteria if any.>
**Спека:** <path к design-решению или .brainstorm/…> — обязательно для задач **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 `tdd-criteria` — before writing code
- invoke `mappa-task-work` — для управления статусом задачи - invoke `mappa-task-work` — for task status management
- invoke `project-discipline` — дисциплина коммитов/пушей - invoke `project-discipline` — commit/push discipline
- invoke `mappa-knowledge` после закрытия — заингесть .wiki/concepts/<slug>.md - invoke `mappa-knowledge` after closing — ingest .wiki/concepts/<slug>.md
[если кросс-проектная: - invoke `using-projects-meta` — cross-project tasks/wiki] [if cross-project: - invoke `using-projects-meta` — cross-project tasks/wiki]
[контекстные скилы из шага 1.3] [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 **weight:** cheap-ok | needs-claude | needs-human
**notify:** <commissioning-project-slug> **notify:** <commissioning-project-slug>
[**allow_upgrade:** true/false] [**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. **Смена домена / репо** — задача завершает один трек перед переходом на несвязанный. 1. **Domain / repo switch** — the task finishes one track before moving to an
2. **Milestone-задача** — последняя в группе sub-tasks одной фичи. unrelated one.
3. **Тяжёлая инфра-задача** — shared checkout, migrations, deploy — где разумно остановиться и проверить состояние. 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):** если решение бьётся на этапы **Staged breakdown:** if the solution splits into stages (1 → 1b → 3), create
(1 → 1b → 3), создавай каждый этап отдельной таской со `status: blocked` + each stage as a separate task with `status: blocked` + `blocker:
`blocker: <номера-предшественников> (#n1, #n2 — номера, не слаги; номер = <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
параллельно (иначе sha-lock конфликт — см. Failure modes). 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 ### 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
После создания **каждая кросс-проектная делегация** дублируется письмом в After creation, **every cross-project delegation** is duplicated by a letter to
инбокс получателя (канон — `mappa-messaging`: канал Mappa, адрес из адресной the recipient's inbox (canon — `mappa-messaging`: Mappa channel, address from
книги `~/projects/.wiki/concepts/projects-address-book.md`): the address book `~/projects/.wiki/concepts/projects-address-book.md`):
``` ```
mcp__mappa__inbox_send( mcp__mappa__inbox_send(
project: <адрес-получателя>, # имя папки, из адресной книги project: <recipient address>, # folder name, from the address book
from: <своя-папка>, from: <your folder>,
subject: "[event: created] #n slug", 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.)
Причина: таска на борде **не пингует живую сессию** получателя. Поллер Reason: a task on the board **does not ping the recipient's live session**.
подхватит по `Weight`/`Notify`, но живая интерактивная сессия узнаёт только The poller will pick it up by `Weight`/`Notify`, but a live interactive
через inbox-монитор — т.е. через письмо. Правило «task + letter, не только session learns only through the inbox monitor — i.e. through the letter. The
task» — общий случай (шаг 7 — его частность для downstream-задач). rule "task + letter, not just task" — is the general case (step 7 is its
particular case for downstream tasks).
Пропуск: self-assigned задачи на своей доске; `target=agenda` (общая доска, Skip: self-assigned tasks on your own board; `target=agenda` (shared board,
конкретного получателя нет — steering-loop через `Notify`). 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 `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 ## Failure modes
- **Пользователь отказывает на pre-flight** → abort, задачу не создавать. - **User refuses the pre-flight** → abort, don't create the task.
- **Пользователь отклоняет dry-run preview** → abort. - **User rejects the dry-run preview** → abort.
- **notify не указан** → переспросить, не пропускать молча. Без notify steering-loop не замыкается. - **notify not specified** → re-ask, don't skip silently. Without notify the
- **weight не указан** → переспросить. Без weight поллер не знает кому отдать задачу. steering-loop doesn't close.
- **tasks_create упал** → различить: **PushRejected** (sha-lock конфликт — - **weight not specified** → re-ask. Without weight the poller doesn't know who
репо уехало между preview и confirm; бывает при параллельном создании в один to give the task to.
репо) → **retry**: повторить confirm — сервер перечитает актуальный base_sha. - **tasks_create failed** → distinguish: **PushRejected** (sha-lock conflict —
Другие ошибки → сообщить пользователю, не делать retry без явного запроса. 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 ## Side effects
- Создаёт таску в target-проекте через `tasks_create` (file channel — Gitea commit; service channel — mappa-сущность, create = карв-аут без лиза wiki:2660). - Creates a task in the target project via `tasks_create` (file channel —
- Опционально создаёт парную review-таску (status=blocked). Gitea commit; service channel — mappa entity, create = carve-out without a
- Covering-письмо в инбокс получателя (кросс-проектная делегация). 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 ## What NOT to do
- Не пропускать pre-flight gate — даже если кажется что всё очевидно. - Don't skip the pre-flight gate — even if everything seems obvious.
- Не использовать пассивные триггер-фразы вместо `invoke` — «tdd-criteria» в тексте слабее чем «invoke `tdd-criteria`». - Don't use passive trigger phrases instead of `invoke` — "tdd-criteria" in
- Не пропускать `notify` — без него boss не узнает о завершении. text is weaker than "invoke `tdd-criteria`".
- Не пропускать `weight` — без него fleet routing слеп. - Don't skip `notify` — without it the boss won't learn about completion.
- Не создавать review-таску для pointers/ops/research задач — только для impl. - Don't skip `weight` — without it fleet routing is blind.
- Не создавать review-таску без `weight` — reconciler/поллер её пропустит. Наследовать от impl, флор `needs-claude` (см. Step 6). - Don't create a review task for pointer/ops/research tasks — impl only.
- Не назначать `weight: cheap-ok` для задач где дисциплина критична (review, security, schema migration) — слабые модели могут игнорировать invoke-инструкции. - Don't create a review task without `weight` — the reconciler/poller will
- Не назначать `weight: needs-claude` или `cheap-ok` задачам, меняющим критическую инфраструктуру (поллер, MCP серверы, deploy, CI/CD) — только `needs-human`. skip it. Inherit from impl, floor `needs-claude` (see Step 6).
- Не ставить `session_break` рутинно на каждую задачу — это маркер реальной границы (domain-switch / milestone / heavy infra), не дефолт; иначе `mappa-task-work` рвёт сессию после каждого close. - 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
- **Не создавать несколько тасок в один репо параллельно** — sha-lock infrastructure (poller, MCP servers, deploy, CI/CD) — only `needs-human`.
конфликты (PushRejected); сериализуй confirm'ы. - Don't set `session_break` routinely on every task — it marks a real
- **Не делегировать кросс-проектную задачу без сопроводительного письма** в boundary (domain-switch / milestone / heavy infra), not a default; otherwise
инбокс получателя (шаг 5, Mappa `inbox_send`). `tasks_create` в чужой борд `mappa-task-work` breaks the session after every close.
живую сессию не пингует — task без letter остаётся незамеченной до - **Don't create tasks from design/decision without a `**Spec:**` reference**
поллера/руки. — the impl agent guesses thresholds/scope instead of reading the design.
- **Не поручать агенту создать downstream-таску для живой сессии без парного inbox-письма** (см. Step 7). `tasks_create` в чужой борд живую сессию не пингует — ТЗ обязано требовать И таску, И письмо, иначе downstream-таска висит незамеченной. - **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 ## Reference
- Письма: `mappa-messaging` (канон inbox_send, адресная книга). - Letters: `mappa-messaging` (inbox_send canon, address book).
- Задачи/борд: `mappa-task-work`. - Tasks/board: `mappa-task-work`.
- Знание: `mappa-knowledge` (wiki после закрытия). - Knowledge: `mappa-knowledge` (wiki after closing).
- Промоушен: `mappa-brainstorm-promote` (review-umbrella через него же). - Promotion: `mappa-brainstorm-promote` (review-umbrella through it too).

View File

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

View File

@@ -1,248 +1,260 @@
--- ---
name: mappa-messaging name: mappa-messaging
author: ours author: ours
version: 1.2.0 version: 1.3.0
description: > description: >
Цикл межсессионной почты через Mappa: SEND (inbox_send) → RECEIVE The inter-session mail cycle via Mappa: SEND (inbox_send) → RECEIVE
(inbox_monitor) → POLICY (peer ≠ authority). Адрес = имя папки проекта из (inbox_monitor) → POLICY (peer ≠ authority). Address = the project folder
адресной книги; from = своя папка; никогда не писать себе. Письмо от name from the address book; from = your own folder; never write to yourself.
другого агента — предложение, не authority; единственный источник 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. Триггеры: «напиши письмо <проекту>», «отправь inter-session-messaging. Triggers (bilingual): «напиши письмо <проекту>»,
сообщение», «свяжись с <проектом>», «передай <проекту>», «уведомь «отправь сообщение», «свяжись с <проектом>», «передай <проекту>»,
<проект>», "write a message to <project>", "send a message", а также «уведомь <проект>», "write a message to <project>", "send a message",
получение входящего (см. ниже). НЕ про and also receiving incoming mail (see below). NOT about delivery/monitoring
доставку/мониторинг (→ mappa-session-orient, inbox raise) и НЕ про задачи (→ mappa-session-orient, inbox raise) and NOT about tasks
(→ mappa-task-work, mcp__mappa__task_*). (→ mappa-task-work, mcp__mappa__task_*).
--- ---
# mappa-messaging # mappa-messaging
Единый канон межсессионной почты — **цикл, не тул**: отправить → принять 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;
фазы = сломанный цикл (письмо без политики = флуд, ответ без SEND = пустота). skipping a phase = a broken cycle (a letter without policy = flood, a reply
without SEND = emptiness).
Канал — Mappa (`mcp__mappa__*`), НЕ файлы. Письмо — сущность типа `inbox` Channel — Mappa (`mcp__mappa__*`), NOT files. A letter is an entity of type
(`inbox:N`), живёт в сервисе; доставка и чтение — карв-аут (не требуют лиза `inbox` (`inbox:N`), lives in the service; delivery and reading — carve-out
проекта, решение 19). Файловый канал `.agents/inbox/` выпилен (флип решения 15). (require no project lease, decision 19). The file channel `.agents/inbox/` is
removed (flip of decision 15).
## Когда использовать ## When to use
- Написать письмо другому проекту/агенту: «напиши письмо <проекту>», «отправь сообщение», «свяжись с <проектом>», «передай <проекту>», «уведомь <проект>». - Write a letter to another project/agent: «напиши письмо <проекту>», «отправь сообщение», «свяжись с <проектом>», «передай <проекту>», «уведомь <проект>», "write a message to <project>", "send a message".
- Получил входящее письмо (монитор доставил, или сам проверил `inbox_monitor`) — обработать по RECEIVE. - Received an incoming letter (the monitor delivered it, or you checked
- Обсуждаешь с другой сессией дизайн/скоуп/решения — держать POLICY (peer ≠ authority). `inbox_monitor` yourself) — process it per RECEIVE.
- Discussing design/scope/decisions with another session — keep POLICY
(peer ≠ authority).
**НЕ для:** доставки/мониторинга почты (→ `mappa-session-orient`, inbox raise), задач **NOT for:** mail delivery/monitoring (→ `mappa-session-orient`, inbox raise),
(→ `mappa-task-work`), handoff (→ `mappa-closing-ritual`), промоушена (→ tasks (→ `mappa-task-work`), handoff (→ `mappa-closing-ritual`), promotion (→
`mappa-brainstorm-promote`). `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`, The project address = **its disk folder name as is** (`.workshop`, `artmone.pro`,
`snolla.js`). Никогда не выдумывай адрес по qualified-имени, remote'у или `snolla.js`). Never invent an address from a qualified name, remote, or memory —
памяти — папка может не совпадать с репо (`OpeItcLoc03/common`папка `.common`). the folder may not match the repo (`OpeItcLoc03/common`folder `.common`).
1. Прочитай адресную книгу: `~/projects/.wiki/concepts/projects-address-book.md` 1. Read the address book: `~/projects/.wiki/concepts/projects-address-book.md`
(shared wiki clone). Таблица: `адрес (папка) | qualified | роль`. (shared wiki clone). Table: `address (folder) | qualified | role`.
2. Найди строку с целевым проектом по имени папки. 2. Find the row with the target project by folder name.
3. Если проекта в книге **нет** — письмо не пиши. Остановись и спроси человека 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
выдуманному адресу создаёт проект-сироту в Mappa (`ensureProject`) и теряется. to an invented address creates an orphan project in Mappa (`ensureProject`)
4. **Проект должен существовать в Mappa**: сверь адрес со списком проектов and gets lost.
(`mcp__mappa__admin_status``projects[]` или `entity_search` type=project). 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( mcp__mappa__inbox_send(
project: <адрес получателя>, # имя папки проекта (из адресной книги) project: <recipient address>, # project folder name (from the address book)
from: <адрес отправителя>, # СВОЁ имя папки (только имя, без owner/темы) from: <sender address>, # YOUR folder name (just the name, no owner/topic)
subject: <тема>, # опционально — короткая тема subject: <topic>, # optional — short topic
body: <markdown-тело> # свободный markdown body: <markdown body> # free markdown
) )
``` ```
- `from`**только имя своей папки**. Без owner, без описания. НЕ - `from`**only your folder name**. No owner, no description. NOT
`reviewer-command-index-done-ack` (тема письма — не адрес). На письмо с `reviewer-command-index-done-ack` (the letter topic is not an address). A
выдуманным `from` нельзя ответить. letter with an invented `from` cannot be replied to.
- Ответ на письмо: `inbox_send(project=<from полученного>, from=<своя папка>)`. - Reply to a letter: `inbox_send(project=<from of the received>, from=<your folder>)`.
В `subject`префикс `Re: `, в теле первая строка — ссылка на исходное In `subject`the `Re: ` prefix, in the body the first line is a reference
письмо (`inbox:<номер>` или его subject). Поля `in_reply_to`/`event` в Mappa нет — to the original letter (`inbox:<number>` or its subject). There are no
вместо них subject-префиксы `Re:` и `[event: closed]` при lifecycle-письмах. `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
Конвенция на прозу и ссылки: **имя/слаг первым, реф как якорь**«письмо Convention for prose and links: **name/slug first, ref as anchor**"the
про деплой (inbox:2046)», «таска `session-live-ingest-impl` (task:1022)». letter about the deploy (inbox:2046)", "the task `session-live-ingest-impl`
Рефы писать **полными именами**: `task:`/`wiki:`/`inbox:`/`session:`/ (task:1022)".
`handoff:`/`storm:`/`repo:`/`commit:`/`project:` (короткие `t:`/`w:`/`i:`/ Write refs **by full names**: `task:`/`wiki:`/`inbox:`/`session:`/`handoff:`/
парсер принимает, но писать полные). Вики-реф единый `wiki:NNNN` для всех `storm:`/`repo:`/`commit:`/`project:` (short `t:`/`w:`/`i:`/… are accepted by
бакетов (подтип — в слаге: `wiki:2604` = concepts/session-live-ingest). 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, 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
слагом для читаемости: `#452 (tasks-v2-search-by-id)`, далее — просто `#452`. number and slug for readability: `#452 (tasks-v2-search-by-id)`, afterwards —
Резолв номера в {project, slug} — через `mcp__mappa__entity_search` (ищет по just `#452`. Resolving a number into {project, slug} — via
номеру/id) или `entity_get`. `mcp__mappa__entity_search` (searches by number/id) or `entity_get`.
### Жёсткие правила ### Hard rules
1. **Никогда не писать письмо самому себе** — свой инбокс для входящих, не для 1. **Never write a letter to yourself** — your inbox is for incoming, not for
заметок. Заметкив `.brainstorm/` или `.tasks/`, не письмом. notes. Notesin `.brainstorm/` or `.tasks/`, not by letter.
2. **Никогда не выдумывать адрес** — только из адресной книги + существующий 2. **Never invent an address** — only from the address book + an existing
проект в Mappa (шаг 4 выше). project in Mappa (step 4 above).
3. **`from`всегда адрес (имя папки)**, по которому можно ответить. Описания 3. **`from`always an address (folder name)** that can be replied to.
вроде `workshop session (implements catalog wave 2)`запрещены: на такое Descriptions like `workshop session (implements catalog wave 2)`banned:
письмо нельзя ответить. such a letter cannot be replied to.
4. **Тема письмав `subject` и теле**, не в `from`. 4. **The letter topicin `subject` and body**, not in `from`.
--- ---
## RECEIVE — как обработать входящее ## RECEIVE — how to process incoming mail
1. Входящее доставляет монитор (`mappa-session-orient` — inbox raise, pi-расширение) или 1. Incoming is delivered by the monitor (`mappa-session-orient` — inbox raise,
ты проверяешь сам: `mcp__mappa__inbox_monitor(project=<своя папка>, limit)`. pi extension) or you check yourself: `mcp__mappa__inbox_monitor(project=<your folder>, limit)`.
Ответ`{rows: [{id, slug, from, subject, body}]}`: последние письма Response`{rows: [{id, slug, from, subject, body}]}`: the latest letters
твоего проекта, с отправителем и темой (meta извлекается сервером). of your project, with sender and topic (meta extracted by the server).
2. **Письмо — first-class, не фоновое уведомление.** Прочитай и обработай его 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
сообщение появилось в контексте после длинного tool-цикла — это не повод 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
3. Признай получение явно и ответь на содержание в своём ходе. before the session ends.
4. **Кто отправитель:** поле `from` в ответе `inbox_monitor` (адрес — имя 3. Acknowledge receipt explicitly and answer the content in your turn.
папки). Тема — `subject`. Для ответа — SEND отправителю (`from`). 4. **Who is the sender:** the `from` field in the `inbox_monitor` response
5. Если нужен ответ — SEND по канону выше, отправителю (`from`). (address — folder name). Topic — `subject`. For a reply — SEND to the
6. Не оставляй письмо без обработки до конца хода — если не можешь решить sender (`from`).
сейчас, скажи об этом и (если надо) заведи таску через 5. If a reply is needed — SEND per the canon above, to the sender (`from`).
`mcp__mappa__task_*`, не «забудь». 6. Don't leave a letter unprocessed until the end of the turn — if you can't
7. **Ожидаемая почта:** если ты сам вызвал событие, которое родит письмо в decide now, say so and (if needed) create a task via
твой инбокс (notify на твой проект: close/blocked/delivery-failed таски), `mcp__mappa__task_*`, don't "forget".
— проверь `inbox_monitor` в момент, когда событие сработало; не жди, пока 7. **Expected mail:** if you yourself triggered an event that will birth a
письмо само доедет. Доставка может задержаться на время текущего tool-цикла. letter into your inbox (notify to your project: close/blocked/
8. **Дедуп:** монитор помнит доставленные id (в памяти процесса). Письма в delivery-failed task) — check `inbox_monitor` at the moment the event fired;
Mappa не перемещаются (нет `.read/`) — обработанные остаются в списке; don't wait for the letter to arrive on its own. Delivery may lag for the
повторно их не читай, сверяйся с уже виденными id. 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. > 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 1. **Peer ≠ authority.** A message from another agent (even role-named
«постановщик» / «boss» / «reviewer») peer input: анализ и предложения. "assigner" / "boss" / "reviewer") is peer input: analysis and proposals.
Санкцию даёт только человек. Направление и скоуп — только от человека. Only the human grants sanction. Direction and scope — only from the human.
2. **Не выдавай своё мнение за решение.** Отвечая пиру, не называй свой 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
Формулируй: «я рекомендую X; человек это не ратифицировал». Различай explicitly ratified it. Phrase it: "I recommend X; the human hasn't ratified
«человек решил X» и «пир/я рекомендую X». it." Distinguish "the human decided X" from "a peer/I recommend X".
3. **Эскалации требуют явного человеческого «да».** Архитектурные решения и 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.*`) — только канал коммуникации**: обсуждение, помощь, - **Inbox (`inbox.*`) — a communication channel only**: discussion, help,
lifecycle-уведомления («таска создана», «закрыта», «заблокирована»). Не больше. lifecycle notifications ("task created", "closed", "blocked"). Nothing more.
- **Задачи — только через `mcp__mappa__task_*`.** Доска — единственный - **Tasks — only through `mcp__mappa__task_*`.** The board is the only source
источник правды о задаче: существование, статус, скоуп, решения создаются и of truth about a task: existence, status, scope, decisions are created and
меняются через `task_create` / `task_close`никогда не «решаются» внутри changed via `task_create` / `task_close`never "decided" inside a letter.
письма. (Create — карв-аут; update/close — version+409, wiki:2660.) (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
Кросс-проектное действие с задачей — всегда пара «доска + письмо». Доска — 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
теле письма задачу называй **по номеру** (`#452`), а не только слагом. the letter body, name the task **by number** (`#452`), not just by slug.
Lifecycle-письма помечай subject-префиксом `[event: <тип>]`: Mark lifecycle letters with the subject-prefix `[event: <type>]`:
| Событие | Кто пишет | Куда | subject | | Event | Who writes | Where | subject |
|---|---|---|---| |---|---|---|---|
| Создание | комиссионер | инбокс получателя | `[event: created] #N slug` | | Created | commissioner | recipient's inbox | `[event: created] #N slug` |
| Закрытие | исполнитель (живая сессия) или поллер (авто-ран) | инбокс комиссионера (`Notify`) | `[event: closed] #N slug` | | Closed | executor (live session) or poller (auto-run) | commissioner's inbox (`Notify`) | `[event: closed] #N slug` |
| Блокировка/парк | то же | то же | `[event: blocked] #N slug` | | Blocked/parked | same | same | `[event: blocked] #N slug` |
Тело письма — 1-2 строки + номера/слаги, не дублировать доску. Живая сессия 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);
закрытии только через `Notify`/письмо. Правило постановки — `mappa-delegation` the commissioner learns about closing only via `Notify`/letter. Assignment
(шаг «пара доска+письмо»); правило закрытия — `mappa-task-work` (close). rule — `mappa-delegation` (the "board+letter pair" step); closing rule —
`mappa-task-work` (close).
### Против чего это ### What this is against
Две сессии пинг-понгуют, каждая соглашается с фреймом другой и добавляет скоуп, 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` уровнем выше: сессии обходят `user_context_agents_path_of_least_resistance` one level up: sessions bypass
человеческую ратификацию — фейковое «решено» через взаимное согласие. human ratification — fake "decided" through mutual agreement.
### Circuit-breaker ### 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» с частичного зрения.** Когда человек **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
ратифицировал это в другом канале?»**, а не обвиняй. Урок 2026-06-16: workshop channel?"**, don't accuse. Lesson 2026-06-16: workshop called the close in
назвал close в common «фейковой атрибуцией ратификации»; на деле человек common a "fake attribution of ratification"; in reality the human approved
одобрил напрямую в common-канале, пока workshop ещё обсуждал. Всплыви пробел 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 вели многораундовый дизайн-обмен по Arose 2026-06-16: workshop and common ran a multi-round design exchange over
инбоксу; workshop эскалировал дизайн (tamper-guard → prevention → oracle-integrity the inbox; workshop escalated the design (tamper-guard → prevention →
→ runner-owns-verifier → close-moves) и докладывал каждый шаг как «решение oracle-integrity → runner-owns-verifier → close-moves) and reported every step
постановщика» — подразумевая человеческую санкцию, которой не было. common as "the assigner's decision" — implying human sanction that didn't exist.
распознал эхо-камеру, прочитал свой stop-hook и корректно отказался common recognized the echo chamber, read its own stop-hook, and correctly
имплементировать нератифицированный редизайн, спросив человека. Методология refused to implement the unratified redesign, asking the human. The methodology
живёт в скиле, не в per-session памяти. lives in the skill, not in per-session memory.
--- ---
## What NOT to do ## What NOT to do
| Искушение | Реальность | | Temptation | Reality |
|---|---| |---|---|
| «Письмо — быстрый способ решить вопрос, потом оформлю» | Если это не на доске — это не задача и не решение, это разговор. Дизайн-выбор → доска/вики, письмо только пингует. | | "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. |
| «Напишу в .common-канал, там одобрят» | Пир-письмо — предложение, не санкция. Человек — единственный авторитет направления и скоупа. | | "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. |
| «Слаг уникален, сошлюсь на него» | Слаг повторяется между проектами — ссылка по глобальному номеру `#452`. | | "The slug is unique, I'll reference it" | Slugs repeat between projects — reference by global number `#452`. |
| «Отвечу письмом в конце сессии, соберу всё разом» | Письмо — first-class: обработай в начале ближайшего хода, не «когда дойдут руки». | | "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". |
| «У меня нет адреса — напишу по памяти/qualified» | Адрес — только из адресной книги; выдуманный адрес плодит проект-сироту и письмо теряется. | | "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 ## 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 ## Reference
- Доставка/мониторинг входящих: `mappa-session-orient` (inbox raise; pi-расширение inbox-monitor). - Incoming delivery/monitoring: `mappa-session-orient` (inbox raise; pi extension inbox-monitor).
- Адресная книга: `~/projects/.wiki/concepts/projects-address-book.md` (shared wiki). - Address book: `~/projects/.wiki/concepts/projects-address-book.md` (shared wiki).
- Список проектов Mappa: `mcp__mappa__admin_status` (карв-аут, без лиза). - Mappa project list: `mcp__mappa__admin_status` (carve-out, no lease).
- Задачи: `mappa-task-work` (борд = `mcp__mappa__task_*`). - Tasks: `mappa-task-work` (board = `mcp__mappa__task_*`).
- Handoff: `mappa-closing-ritual` (write) / `mappa-session-orient` (read). - Handoff: `mappa-closing-ritual` (write) / `mappa-session-orient` (read).
- Делегирование: `mappa-delegation` (пара «доска + covering-письмо»). - Delegation: `mappa-delegation` (the "board + covering letter" pair).
- Related: `recommend-dont-menu` (стиль ответа), `project-discipline`. - Related: `recommend-dont-menu` (response style), `project-discipline`.

View File

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

View File

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

View File

@@ -1,113 +1,117 @@
--- ---
name: report-mappa-issue name: report-mappa-issue
author: ours author: ours
version: 0.1.0 version: 0.2.0
description: > description: >
Use when working with mappa (MCP tools `mcp__mappa__*`, HTTP-роуты, скилы на Use when working with mappa (MCP tools `mcp__mappa__*`, HTTP routes, mappa
mappa) and anything deviates from the expected workflow: 500/5xx, «entity not skills) and anything deviates from the expected workflow: 500/5xx, "entity
found» для id, который должен существовать, неожиданная форма ответа, not found" for an id that must exist, unexpected response shape, timeouts,
таймауты, молчаливые сбои, неверный статус, нестабильность. Report it by silent failures, wrong status, instability. Report it by mail to `mappa` AND
mail to `mappa` AND `.workshop` — never swallow, never only-local-log, never `.workshop` — never swallow, never only-local-log, never only in-chat.
only in-chat. TEMPORARY skill: active while mappa is unstable; retire when TEMPORARY skill: active while mappa is unstable; retire when stabilized.
stabilized. Triggers: «маппа отдала 500», «entity not found», «неожиданный Triggers (bilingual): «маппа отдала 500», «entity not found», «неожиданный
ответ от mappa», «mappa вернула», "mappa returned 500", "entity not found", ответ от mappa», «mappa вернула», "mappa returned 500", "entity not found",
unexpected mappa response. "unexpected mappa response".
--- ---
# report-mappa-issue # report-mappa-issue
Любое отклонение от ожидаемого mappa-воркфлоу репортится **почтой в `mappa` Any deviation from the expected mappa workflow is reported **by mail to `mappa`
и `.workshop`** — немедленно, с evidence. Не глотать, не прятать в локальный and `.workshop`** — immediately, with evidence. Never swallow, never hide it in
лог, не откладывать «до сборника». a local log, never postpone "until a digest".
> ⚠️ **TEMPORARY (временный скил):** действует, пока mappa нестабильна. Это > ⚠️ **TEMPORARY skill:** active while mappa is unstable. It is a stopgap for
> stopgap для сбора сигналов к стабилизации. Когда mappa стабилизируется > collecting signals toward stabilization. When mappa stabilizes (a week
> (неделя без репортов) — скил отзывается: репорты становятся обычными > without reports) — this skill is retired: reports become ordinary bug tasks.
> баг-тасками. Владелец решения об отзыве — workshop. > The owner of the retirement decision is workshop.
## When to use ## When to use
Репортить, когда в ходе работы с mappa произошло **любое** из: Report when, during work with mappa, **any** of the following happens:
- **5xx / 500 / 502** на любом вызове (`task_*`, `wiki_*`, `inbox_*`, `entity_*`, - **5xx / 500 / 502** on any call (`task_*`, `wiki_*`, `inbox_*`, `entity_*`,
`admin_*`, `graph_*`, HTTP-роуты). `admin_*`, `graph_*`, HTTP routes).
- **«Entity not found» / 404** для id/ref, который **должен** существовать - **"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,
пустой `rows` где ожидались данные, новый/неожиданный тип в ответе. 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
— сигнал для стабилизации (пометь `retry: resolved`). itself is a signal for stabilization (mark `retry: resolved`).
- **Неверный/неожиданный статус** сущности, рассинхрон борда и реальности. - **Wrong/unexpected entity status**, board vs reality desync.
**Ретраи допустимы** (12 с паузой), но репорт — независимо от исхода ретрая: **Retries are allowed** (12 with a pause), but the report happens regardless
случай 500 → репорт; случай 500→ретрай→ок → репорт с `retry: resolved`. of the retry outcome: case 500 → report; case 500 → retry → ok → report with
`retry: resolved`.
## When NOT to use ## When NOT to use
- **Ожидаемый 404** — сущность действительно не существует и не должна - **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.
- **Документированные известные ограничения** (например, «verify на проде - **Documented known limitations** (e.g. "verify on prod is impossible by
невозможен по дизайну», «прод stale до редеплоя» — если это задокументировано design", "prod is stale until redeploy" — if documented and known to the
и известно команде mappa). mappa team).
- **Отклонения НЕ от mappa** — VDS/docker (→ using-vds-ops), projects-meta кэш - **Deviations NOT from mappa** — VDS/docker (→ using-vds-ops), projects-meta
(документированная сталезность), провайдеры моделей. Только mappa. cache (documented staleness), model providers. Only mappa.
- **Уже зарепорченный тот же инцидент** — не дублируй (см. Dedup). - **The same incident already reported** — don't duplicate (see Dedup).
## Core pattern — репорт ## Core pattern — the report
Каждый вызов: `mcp__mappa__inbox_send` в **оба** адреса (`mappa` и `.workshop`, Each call: `mcp__mappa__inbox_send` to **both** addresses (`mappa` and
адреса из адресной книги `~/projects/.wiki/concepts/projects-address-book.md`), `.workshop`, addresses from the address book
`from` = своё имя папки. Формат письма: `~/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: Body:
- Expected: <что должно было произойти по воркфлоу/докам> - Expected: <what should have happened per workflow/docs>
- Actual: <ошибка/статус/ответ — текст сообщения или короткий сниппет> - Actual: <error/status/response — message text or a short snippet>
- Call: <тул + ключевые параметры / эндпоинт + project> - Call: <tool + key parameters / endpoint + project>
- Retry: <сработал ли ретрай, сколько попыток> - Retry: <did the retry work, how many attempts>
- Recurrence: <первый раз / повторяется — сколько раз за сессию> - Recurrence: <first time / repeats — how many times this session>
- Context: <проект, сессия, какой флоу шёл> - Context: <project, session, which flow was running>
``` ```
Одно письмо = **один инцидент** (симптом × эндпоинт). Рекуррентность — в том же One letter = **one incident** (symptom × endpoint). Recurrence goes in the same
письме (`recurrence: 5 раз за 2 часа`), не новый репорт на каждый вызов. letter (`recurrence: 5 times in 2 hours`), not a new report per call.
## Common mistakes / rationalizations ## Common mistakes / rationalizations
| Рационализация | Реальность | | Rationalization | Reality |
|---|---| |---|---|
| «Mappa упала — письмо не дойдёт, зачем писать» | Письмо — сущность в 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. |
| «Расскажу человеку в чате» | Человек не всегда в сессии, команда mappa чат не видит. Письмо — durable и кросс-сессионно. | | "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. |
| «Запишу в локальный лог» | Локальный лог не виден команде mappa. Цель репорта — видимость у получателей. (Локальная запись — дополнительно, не вместо.) | | "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.) |
| «Ретрай сработал — значит ок» | Нестабильность — сам по себе сигнал. Репорть с `retry: resolved`. | | "The retry worked — so it's fine" | The instability itself is a signal. Report with `retry: resolved`. |
| «Это мелочь, не буду спамить» | Пока mappa нестабильна — любой сигнал материал для стабилизации. Dedup защищает от спама, не молчание. | | "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» | Неизвестно, пока не зарепорчено. Репорт — это и есть способ сделать известным. | | "Mappa surely already knows this" | Unknown until reported. The report is exactly how it becomes known. |
## Red flags — STOP ## Red flags — STOP
- Поймал ошибку mappa и продолжил молча (без репорта). - Caught a mappa error and silently continued (no report).
- Записал только локально / сказал только в чате — письма нет. - Recorded only locally / said only in chat — no letter.
- Пропустил «entity not found», не проверив, должен ли id существовать. - Skipped "entity not found" without checking whether the id must exist.
- Отложил репорт «на потом» без письма и без таски. - Postponed the report "for later" without a letter and without a task.
- Зарепортил, но не в оба адреса (`mappa` и `.workshop`). - Reported but not to both addresses (`mappa` and `.workshop`).
## Cross-agent ## Cross-agent
Канал — mappa inbox (`inbox_send` / `inbox.monitor`), общий для всех агентов Channel — mappa inbox (`inbox_send` / `inbox.monitor`), shared by all agents
(pi: `mcp__mappa__inbox_send`; Claude Code: те же MCP-тулы; headless — то же). (pi: `mcp__mappa__inbox_send`; Claude Code: the same MCP tools; headless — the
Адресация — строго из адресной книги (`inter-session-messaging` канон). same). Addressing strictly from the address book (`inter-session-messaging`
canon).
## Out of scope ## Out of scope
- **Не чинит mappa** — диагностика/починка сервиса отдельно; скил только - **Does not fix mappa** — service diagnosis/repair is separate; this skill
репортит. (Глубокий диагноз`diagnosing-bugs` / `using-vds-ops` для инфры.) only reports. (Deep diagnosis`diagnosing-bugs` / `using-vds-ops` for
- **Не репортит чужие сервисы** — только отклонения от mappa-воркфлоу. infra.)
- **Не заменяет** `inter-session-messaging` (механика отправки — там, этот скил - **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").