diff --git a/.tasks/2026-08-24-01064-mappa-session-orient.md b/.tasks/2026-08-24-01064-mappa-session-orient.md index 6a528f9..6a419b0 100644 --- a/.tasks/2026-08-24-01064-mappa-session-orient.md +++ b/.tasks/2026-08-24-01064-mappa-session-orient.md @@ -17,4 +17,15 @@ ## Completed steps +- [x] skills/mappa-session-orient/SKILL.md v1.0.0 — старт-фаза: контракт → pull (--ff-only, полный цикл) → handoff read (staleness >7д → ask; orient+ask, без auto-execute) → inbox raise+sweep → liveness-сводка (meta_health/admin_status/snapshot, «живо/мертво») → live-ingest query (session_list, stale-active краш-детект, «другая связка → предложить») +- [x] Граница orient/ops (w:2605 round 3): «живо/мертво»; проблема на старте → эскалация человеку/диагностической сессии, не углубление +- [x] Поглощены: pulling-before-work, session-handoff(read), session-inbox-monitor(raise), using-system-snapshot (liveness); старые имена — триггер-синонимы +- [x] Live-ingest 404-skip задокументирован (роуты /session не задеплоены — сервер #1022 в репо, деплой ждёт #1055); контракт — w:2604 +- [x] mappa-messaging: ссылки session-inbox-monitor → mappa-session-orient (inbox raise) обновлены (3 места) +- [x] lint clean (63 skills, 0 violations); build.sh → dist/mappa-session-orient.skill; install.sh → dual; поглощённые удалены из живых диров +- [x] GREEN micro-test: свежий pi -p на «начало сессии» → ритуал по шагам (контракт→pull→handoff→inbox→liveness→live-ingest), граница «живо/мертво» + эскалация, 404-skip, read-only + ## Notes + +- #1064 была 🔵 blocked ← #1024 (клиент session-sync). Скил-документ завершён по контракту w:2604/w:2605; live-ingest E2E (шаг 6) отложен: сервер #1022 не задеплоен (#1055), клиент #1024 (.common) открыт. 404-skip в скиле — ориентация не блокируется. +- RED-базис: контент унаследован из 4 поглощённых скилов (все прошли ревью); дельта = слияние + live-ingest query + граница orient/ops. Полный behavioral smoke — за #1065. diff --git a/dist/mappa-messaging.skill b/dist/mappa-messaging.skill index 717cce0..d34ef04 100644 Binary files a/dist/mappa-messaging.skill and b/dist/mappa-messaging.skill differ diff --git a/dist/mappa-session-orient.skill b/dist/mappa-session-orient.skill new file mode 100644 index 0000000..f359972 Binary files /dev/null and b/dist/mappa-session-orient.skill differ diff --git a/dist/pulling-before-work.skill b/dist/pulling-before-work.skill deleted file mode 100644 index 4107f54..0000000 Binary files a/dist/pulling-before-work.skill and /dev/null differ diff --git a/dist/session-inbox-monitor.skill b/dist/session-inbox-monitor.skill deleted file mode 100644 index 162bd23..0000000 Binary files a/dist/session-inbox-monitor.skill and /dev/null differ diff --git a/dist/using-system-snapshot.skill b/dist/using-system-snapshot.skill deleted file mode 100644 index 8cec217..0000000 Binary files a/dist/using-system-snapshot.skill and /dev/null differ diff --git a/skills/mappa-messaging/SKILL.md b/skills/mappa-messaging/SKILL.md index 4ee8246..332cdc4 100644 --- a/skills/mappa-messaging/SKILL.md +++ b/skills/mappa-messaging/SKILL.md @@ -11,7 +11,7 @@ description: > inter-session-messaging. Триггеры: «напиши письмо <проекту>», «отправь сообщение», «свяжись с <проектом>», «передай <проекту>», «уведомь <проект>», а также получение входящего (см. ниже). НЕ про - доставку/мониторинг (→ session-inbox-monitor) и НЕ про задачи + доставку/мониторинг (→ mappa-session-orient, inbox raise) и НЕ про задачи (→ mappa-task-work, mcp__mappa__task_*). --- @@ -31,7 +31,7 @@ description: > - Получил входящее письмо (монитор доставил, или сам проверил `inbox_monitor`) — обработать по RECEIVE. - Обсуждаешь с другой сессией дизайн/скоуп/решения — держать POLICY (peer ≠ authority). -**НЕ для:** доставки/мониторинга почты (→ `session-inbox-monitor`), задач +**НЕ для:** доставки/мониторинга почты (→ `mappa-session-orient`, inbox raise), задач (→ `mappa-task-work`), handoff (→ `mappa-closing-ritual`), промоушена (→ `mappa-brainstorm-promote`). @@ -107,7 +107,7 @@ mcp__mappa__inbox_send( ## RECEIVE — как обработать входящее -1. Входящее доставляет монитор (`session-inbox-monitor`, pi-расширение) или +1. Входящее доставляет монитор (`mappa-session-orient` — inbox raise, pi-расширение) или ты проверяешь сам: `mcp__mappa__inbox_monitor(project=<своя папка>, limit)`. Ответ — `{rows: [{id, slug, from, subject, body}]}`: последние письма твоего проекта, с отправителем и темой (meta извлекается сервером). @@ -238,7 +238,7 @@ Lifecycle-письма помечай subject-префиксом `[event: <ти ## Reference -- Доставка/мониторинг входящих: `session-inbox-monitor` (вне suite, не переименован). +- Доставка/мониторинг входящих: `mappa-session-orient` (inbox raise; pi-расширение inbox-monitor). - Адресная книга: `~/projects/.wiki/concepts/projects-address-book.md` (shared wiki). - Список проектов Mappa: `mcp__mappa__admin_status` (карв-аут, без лиза). - Задачи: `mappa-task-work` (борд = `mcp__mappa__task_*`). diff --git a/skills/mappa-session-orient/SKILL.md b/skills/mappa-session-orient/SKILL.md new file mode 100644 index 0000000..fac8284 --- /dev/null +++ b/skills/mappa-session-orient/SKILL.md @@ -0,0 +1,142 @@ +--- +name: mappa-session-orient +author: ours +version: 1.0.0 +description: > + Старт-фаза форкфлоу: контракт + чтение (pull --ff-only → handoff read → + inbox raise → liveness-сводка «живо/мертво» → live-ingest query). Нужен и + для ad-hoc, где нет AGENTS.md-контракта. Поглощает pulling-before-work, + session-handoff(read), session-inbox-monitor(raise), using-system-snapshot + (liveness) + live-ingest query (старые имена — триггер-синонимы). Граница: + orient отвечает «живо/мертво» одной строкой; глубокая диагностика — вне + suite (эскалация человеку/диагностической сессии). Триггеры: «что на + сессии», «кто последним работал», «продолжи с места», «orient me», + session-start ритуал, «pull remote before work». +--- + +# mappa-session-orient + +Старт-фаза цикла агента: **контракт + чтение**, тонкий слой — отвечает на +вопрос «живо/мертво» (одна строка на секцию), не углубляется. Нужен и для +ad-hoc-сессий (где нет AGENTS.md-контракта — ориентация всё равно обязательна). + +> **Граница session-orient / ops (w:2605, round 3):** orient — «живо/мертво»; +> ops — «почему и что дальше». Проблема на старте → **не углубляться**: +> передать человеку или диагностической сессии (вне suite). + +## Когда использовать + +- Старт сессии (ритуал, порядок строго по Steps). +- «что на сессии», «кто последним работал», «продолжи с места», «orient me». +- Ad-hoc-сессия без трека/таски — ориентация всё равно (контракт + чтение). + +## Steps (порядок — ритуал) + +### 1. Контракт + +Прочитать `AGENTS.md` проекта (canon; `CLAUDE.md` — legacy-указатель). Если +AGENTS.md нет — ad-hoc: контракта нет, но ориентация продолжается (шаги 2–6 +не зависят от него). + +### 2. Pull (pulling-before-work, полный цикл) + +`git pull --ff-only` — один раз на старте. Проверки по порядку: git-work-tree? +(нет → silent exit), политика pull (`pull.rebase=true` + `pull.ff=only`, +set-if-absent), origin remote? (нет → skip), дерево чистое? (грязно → skip, +не stash), HEAD attached? (нет → skip), upstream? (нет → skip), `git pull --ff-only`. +**Никогда auto-merge/rebase, никогда stash.** Повторный pull — только по явному +«sync». + +### 3. Handoff read (session-handoff read-часть) + +1. `mcp__mappa__entity_search(q='', type='handoff', project=<имя>, limit=1)` — + если пусто, silent exit (первая сессия проекта). +2. **Staleness:** `meta.date` > 7 дней → спросить user'а «handoff устарел, + оверрайдить или продолжить?». +3. **Summarize + Orient:** пересказать одним блоком (summary / open_treks / + ask_user / guards / recent_commits): «прошлая сессия предложила X. Делаем?» +4. **Wait.** Никаких действий до подтверждения user'ом. Default = orient + ask, + никакого auto-execute. + +### 4. Inbox raise + sweep (session-inbox-monitor) + +Поднять персистентный монитор на инбокс проекта (pi: расширение inbox-monitor +поллит `GET /inbox?project=`; opt-in — строка `inbox monitor: raise on +start` в AGENTS.md, live re-check каждый тик). Свип: `mcp__mappa__inbox_monitor(project=<имя>)` +— непрочитанные письма могут менять план; обработай каждое по `mappa-messaging` +(письмо — first-class, в начале ближайшего хода). + +### 5. Liveness-сводка (using-system-snapshot) — «живо/мертво» + +Один-два зонда в текущем turn, сжать в 3–4 строки, не raw-дампить: + +``` +mcp__mappa__meta_health → 🟢/🔴 Mappa alive (заголовок при падении) +mcp__mappa__admin_status → счётчики по типам/проектам (нагрузка) +mcp__projects-meta__meta_system_snapshot → poller (running? + проекты) / docker (N/N up, + иначе проблемные) / tasks (Σ active/blocked, + кэш — может быть stale) +``` + +**Never assert liveness по памяти** — только вызов тула в этом же turn. Если +snapshot показал проблему → **эскалация, не углубление**: «проблема на старте, +не разбираю — передаю человеку/диагностической сессии» (ops вне suite). + +### 6. Live-ingest query (потребитель session-live-ingest, #1022/#1024) + +Зависимость: сервер #1022 (v0.8.0) + клиентская часть #1024 (pi session-sync, +.session пишется клиентом). Контракт — w:2604. + +1. `mcp__mappa__session_list(project=<имя>, stale_minutes?)` — последние сессии + проекта, latest-first (`updated_at DESC`), с end-state/ts/meta-тройкой + {project, runtime, machine, folder}. +2. **Stale-active детект:** end-state≠clean AND updated_at < now−X → «вёл + @, не завершена» (краш-детект). +3. **«Другая связка + не завершена»** → предложить (peer-канон, решение за + человеком): забить / дернуть письмом (`mappa-messaging`: письмо той связке) + / продолжить самому. +4. **Same-triple (`/resume`):** та же связка {runtime, machine, folder} → догрузить + остаток (пи-нативный resume или бриф из mappa). + +**Замечание (2026-08-24):** роуты `/session` ещё не задеплоены на прод +(сервер #1022 в репо, деплой ждёт #1055) — при 404/«no route» live-ingest query +пропускается без фейла: orient продолжается (шаги 1–5), query-часть — по факту +доступности. + +## Failure modes + +- **Проблема на старте** (сервис упал, snapshot красный, конфликт pull) → не + углубляться: эскалация человеку/диагностической сессии (ops вне suite). +- **Pull diverged** → `⚠️ diverged — resolve manually`; не auto-merge/rebase. +- **Handoff stale (>7 дней)** → спросить user'а, не оверрайдить молча. +- **Live-ingest недоступен** (404 no route / нет клиента #1024) → пропустить + шаг 6, не блокировать ориентацию. +- **Проект не в mappa** (нет handoff/session-сущностей) → silent exit по + соответствующим шагам; первая сессия проекта — норм. + +## Side effects + +- Ничего не пишет, ничего не мутирует (ориентация read-only: pull — локальный + ff, inbox-raise — монитор, liveness — зонды, live-ingest — чтение). +- Поднимает персистентный inbox-монитор (живёт до конца сессии). + +## What NOT to do + +- **Не auto-execute** из handoff'а — orient + ask, никакого авто-действия. +- **Не углубляться в диагностику** — orient = «живо/мертво»; «почему» — вне suite. +- **Не assert liveness по памяти** — только зонд в этом же turn. +- **Не stash/не auto-merge/не auto-rebase** при pull — только `--ff-only`. +- **Не повторять pull** в сессии без явного «sync». +- **Не ходить по многохоповым цепочкам** live-ingest — одна строка «кто + последним», предложение — человеку. +- **Не писать** (handoff/вики/таски) на ориентации — это финиш-фаза + (`mappa-closing-ritual`). + +## Reference + +- Финиш-фаза: `mappa-closing-ritual` (handoff write + PROPOSE). +- Задачи: `mappa-task-work` (борд после ориентации). +- Почта: `mappa-messaging` (ответы на письма, дернуть связку). +- Знание: `mappa-knowledge`. Делегирование: `mappa-delegation`. +- Live-ingest спека: `concepts/session-live-ingest` (wiki:2604). +- Глубокая диагностика (вне suite): `using-vds-ops` (контейнеры VDS). diff --git a/skills/pulling-before-work/README.md b/skills/pulling-before-work/README.md deleted file mode 100644 index 1cad6bd..0000000 --- a/skills/pulling-before-work/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# pulling-before-work - -Policy skill that pulls the current branch from `origin` once at session start -and on explicit re-sync requests. Designed to remove the "edited on stale base" -footgun without trampling dirty work-trees or auto-merging. - -## When it triggers - -- **Session start** — when `AGENTS.md` contains the line `pull remote before work` (added by `project-bootstrap` v2.0.0+). -- **In-chat** — when the user says `sync`, `resync`, `pull`, `обнови репо`, `git pull please`, or close variants. - -Stays silent in non-git folders. Prints one informational line and exits in: -no `origin` remote, no upstream tracking, dirty work-tree, detached HEAD. - -## What it does - -`git pull --ff-only` against the configured upstream — never auto-merges, never -auto-rebases, never stashes, never commits, never pushes. On divergence it prints -a warning with manual-resolution hints and exits. - -## Prerequisites - -None. The skill is a no-op outside git repos and folders without an `origin` -remote, so it's safe to leave activated everywhere. - -## Related - -- `project-bootstrap` (v2.0.0+) — adds the trigger line to new and existing projects' `AGENTS.md`. -- `.wiki/concepts/pulling-before-work-design.md` (in projects bootstrapped from this repo: this design lives in `skills`) — full design rationale. diff --git a/skills/pulling-before-work/SKILL.md b/skills/pulling-before-work/SKILL.md deleted file mode 100644 index a847122..0000000 --- a/skills/pulling-before-work/SKILL.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -name: pulling-before-work -author: ours -version: 1.1.0 -description: > - Pulls the current branch from origin once at session start and on explicit - re-sync requests. Use when AGENTS.md contains the trigger line "pull remote - before work", or when the user says "sync", "resync", "pull", "обнови репо", - "git pull please", or close variants asking to refresh from the remote. - Runs `git pull --ff-only` — never auto-merges or rebases. Also sets the - repo-local pull policy (`pull.rebase=true` + `pull.ff=only`, set-if-absent) - so plain manual `git pull` never creates merge commits either. Stays silent - in non-git folders. Prints one informational line and exits when there is no - origin remote, no upstream tracking, the working tree is dirty, or HEAD is - detached. Does not stash, commit, or push. Activated by `project-bootstrap` - v1.4.0+ via the canonical AGENTS.md template. ---- - -# pulling-before-work - -> Pull from `origin` once when work starts. Don't auto-merge. Don't trample dirty work-trees. Don't ask twice in the same session unless asked. - -## When this runs - -**At session start** — once, when the skill is activated by the `pull remote before work` line in `AGENTS.md`. The cycle below runs immediately. - -**On explicit re-sync** — when the user says any of: `sync`, `resync`, `pull`, `обнови репо`, `pull please`, `git pull`, `подтяни`, `pull from origin`. Re-runs the full cycle. There is no per-session counter; the user is always allowed to ask. - -**Never** before each commit, before each tool call, on every message, or in any other implicit cadence. Mode-3 ("start + on-demand") was the explicit design choice — see `.wiki/concepts/pulling-before-work-design.md`. - -## The pull cycle - -Run these checks in order. Print at most one line of chat output per run. - -### 1. Inside a git work-tree? - -```bash -git rev-parse --is-inside-work-tree 2>/dev/null -``` - -If the command fails or prints anything other than `true` → **exit silently, no chat output.** This is the not-a-git-repo case; the skill must not be noisy in random folders. - -### 1b. Ensure the repo's local pull policy (set-if-absent) - -```bash -git config --local --get pull.rebase >/dev/null 2>&1 || git config --local pull.rebase true -git config --local --get pull.ff >/dev/null 2>&1 || git config --local pull.ff only -``` - -Sets the repo-local pull policy to the snolla baseline: plain `git pull` rebases instead of creating a merge commit (`pull.rebase=true`), and the merge path refuses non-fast-forwards (`pull.ff=only`). **Set-if-absent only** — an explicit local override the user wrote is never clobbered. Silent: no chat output, idempotent, works in dirty trees. Git config is untracked, so this never dirties `git status`. The skill's own pull below still uses `--ff-only` on purpose: a silent session-start pull never leaves the repo mid-rebase; divergence is always resolved by the human. - -### 2. Has an `origin` remote? - -```bash -git remote get-url origin 2>/dev/null -``` - -If the command fails (no such remote) → print one line and exit: - -``` -no origin remote — skip pull -``` - -### 3. Is the working tree clean? - -```bash -git status --porcelain -``` - -If the output is non-empty → print one line and exit: - -``` -working tree dirty — skipping pull. commit/stash, потом скажи "sync" -``` - -Never stash automatically. Stash-pop conflicts are exactly the friction this skill exists to remove. - -### 4. Is HEAD attached? - -```bash -git symbolic-ref -q HEAD -``` - -If the command fails (empty output, exit 1) → detached HEAD. Print: - -``` -detached HEAD — skip pull -``` - -### 5. Does the current branch have an upstream? - -```bash -git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null -``` - -Capture the upstream name (e.g. `origin/master`). If the command fails → no upstream tracking. Print: - -``` -no upstream tracking for — skip pull -``` - -(Where `` is `git rev-parse --abbrev-ref HEAD`.) - -### 6. Pull, fast-forward only - -```bash -git pull --ff-only -``` - -(No args — uses the configured upstream captured above.) - -Classify by exit code and stdout: - -| Result | Print | -|---|---| -| Already up to date | `✅ already up to date with ` | -| Fast-forward, N commits | `✅ pulled N commits from ` | -| Non-fast-forward / diverged (exit non-zero with "diverged" or "non-fast-forward" in output) | `⚠️ diverged from — resolve manually (plain git pull rebases by default; git pull --no-ff for a merge commit); skill never auto-merges/rebases` | - -### Out of scope - -The skill never: - -- commits, stashes, or pushes -- recurses into submodules -- pulls from non-`origin` remotes -- pulls on detached HEAD -- runs auto-merge or auto-rebase -- overwrites an existing local pull policy (set-if-absent only) -- runs more than once per session unless the user asks - -## Recovery hints - -If the skill skipped because of a dirty tree: - -```powershell -# Windows / PowerShell -git status # see what's dirty -git add . ; git commit -m "wip" -# then ask the agent: "sync" -``` - -```bash -# Linux / macOS -git status -git add . && git commit -m "wip" -# then say "sync" -``` - -If the skill reported `diverged`: - -```bash -# Option A (repo default, set by step 1b): rebase your local commits on top of origin -git pull - -# Option B: explicit merge commit -git pull --no-ff -``` - -The skill stays out of these decisions on purpose — both options have valid use cases and the user owns the choice. - -## Why this exists - -Stale local branches are a silent footgun: edits land on top of yesterday's `origin`, the divergence shows up at push time, and by then there's a chunk of work to rebase or merge on the wrong base. One pull at start covers the common case; an explicit re-sync trigger handles long sessions where someone pushed mid-flight. - -Full design rationale (mode choice, dirty-tree skip vs stash, `--ff-only` vs auto-merge, the upstream-check) lives in `.wiki/concepts/pulling-before-work-design.md`. diff --git a/skills/session-inbox-monitor/SKILL.md b/skills/session-inbox-monitor/SKILL.md deleted file mode 100644 index 8f7ebd4..0000000 --- a/skills/session-inbox-monitor/SKILL.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -name: session-inbox-monitor -author: ours -version: 1.0.0 -description: > - Raises a persistent Monitor on the project's Mappa inbox (poll - `mcp__mappa__inbox_monitor` / HTTP `GET /inbox?project=`), so - inter-session messages page the session in real time; the monitor dies on - session end on its own. Pi (pi-coding-agent) sessions: a global extension - polls the same HTTP endpoint, session-scoped (own project only). Triggers: - AGENTS.md line `inbox monitor: raise on start`, or «подними монитор почты», - «настрой авто-монитор инбокса», «raise inbox monitor», «auto-arm inbox - watcher». Headless (`claude -p` / `pi -p`): does NOT raise — rely on the - Stop-hook / agent_settled pull pickup. File channel `.agents/inbox/` is - gone (flip, решение 15) — letters live in Mappa as entities `i:N`, delivery - is a carve-out (no lease, решение 19). NOT for how to handle a received - message (→ inter-session-messaging). ---- - -# session-inbox-monitor - -Auto-raises a session-length Monitor on the project's **Mappa inbox** at -interactive-session start, so inter-session messages page the session in real -time. Tears down for free on session end. Headless sessions skip it and rely -on the pull-model (Stop-hook / agent_settled pickup). - -Канал — Mappa, НЕ файлы: письма — сущности `inbox` (`i:N`) в сервисе, читаются -через `mcp__mappa__inbox_monitor(project, limit)` (или HTTP `GET -/inbox?project=<имя-папки>&limit=N`). Доставка/чтение — карв-аут (решение 19): -не блокируются лизом, работают всегда. Файловые `.agents/inbox/` и `.read/` -выпилены. - -## When to use - -- **Automatic (the common path).** A paired SessionStart hook injects an - instruction at the start of every interactive session of an opted-in project. - You act on that injection — raise the monitor as your first action — without a - user phrase. -- **On request.** AGENTS.md line `inbox monitor: raise on start`, or «подними - монитор почты», «настрой авто-монитор инбокса», «raise inbox monitor», - «auto-arm inbox watcher». -- **NOT for** handling the content of a received message (→ - `inter-session-messaging`). This skill is only the monitor's *lifecycle*. - -## Inputs - -- `` — своё имя папки (cwd basename), адрес в Mappa. -- Письма: `mcp__mappa__inbox_monitor(project=<своя папка>, limit)` → - `{rows: [{id, slug, body}]}` (последние N). `from`/`subject` — в meta, через - `entity_get(id)` при ответе. -- Мопitor-команда при CC — HTTP-опрос `GET /inbox?project=...` (без токена на - текущем проде; если `MAPPA_API_TOKEN` задан — `x-api-token` header). - -## Steps - -1. **Mode check.** Headless / non-interactive (`claude -p`, `pi -p`, - `ctx.hasUI === false`): **STOP — do not raise a monitor.** The pull pickup - (CC Stop-hook / pi `agent_settled`) plus `Notify:` cover delivery there; a - Monitor can't idle-watch in headless. -2. **Raise exactly one persistent Monitor** on the Mappa inbox: - `mcp__mappa__inbox_monitor(project=, limit=50)`, poll - interval ~15s, `description: "inbox watcher"`. Dedup by письменному id: - новое письмо — это id, которого ещё не было в предыдущих ответах. -3. **Do not sweep yourself.** Killing orphans is the hook's job (it runs before - you, at SessionStart). -4. **On an event** (новый id в ответе inbox_monitor), прочитай письмо (body) и - обработай per `inter-session-messaging`. Для ответа — `entity_get(id)` → - `meta.from`. Письма не перемещаются (нет `.read/`) — обработанные остаются - в списке; дедуп по id в памяти монитора. -5. **Teardown is automatic.** The Monitor dies at session end. Do **not** add a - SessionEnd teardown. - -## Pi (pi-coding-agent) support — session-scoped global extension - -Same contract, pi-native, session-scoped: the extension polls ONLY the current -session's project inbox (`GET /inbox?project=`); it never reads -other projects' inboxes (vitya's rule: an agent may only read its own -project's inbox). Installed globally so *every* pi session has the capability, -but each session only ever touches its own project. - -- **Source of truth:** `~/projects/pi-extensions/extensions/inbox-monitor.ts` - (репо `OpeItcLoc03/pi-extensions`, Gitea — дом pi-расширений). Deploy: - `just install` в клоне репо — копирует с затираанием в - `~/.pi/agent/extensions/inbox-monitor.ts` (global → every pi, every - directory), hot-reload with `/reload`. -- **Opt-in per project:** AGENTS.md / CLAUDE.md line - `inbox monitor: raise on start`. (Файловой директории `.agents/inbox/` больше - нет — триггер только строка.) -- **PUSH:** ~15s poll of the session's own Mappa inbox (interactive only). - **PULL:** `agent_settled` sweep — same poll, backstop. Both share one dedup - set per process (by letter id). -- **Headless (`pi -p`, `ctx.hasUI === false`):** NO delivery — no watcher, no - sweep. Messages sit in Mappa until an interactive session picks them up. -- **Delivery:** `pi.sendUserMessage(body, { deliverAs: "followUp", triggerTurn: - true })` — paged into the transcript as a user message; the agent handles it - per `inter-session-messaging`. - -## Failure modes - -- **Нет opt-in строки** → монитор не поднимается; ожидаемо. -- **Сервис mappa недоступен** → poll-запрос падает; монитор ретраит следующий - тик. Письма в сервисе не теряются (они — сущности), доставятся когда сервис - вернётся. НЕ дублируй в файлы — фолбэк-канал выпилен. -- **Two live interactive sessions on the same project** → оба поллят один - инбокс; дедуп по id делает доставку first-wins (кто первый прочитал id — - тот и доставил; второй пропускает). Real-time paging теряет только тот, кто - опоздал; письма не теряются (pull-свип в конце хода). -- **Monitor auto-stopped** → harness останавливает мониторы с переизбытком - событий; дедуп по id держит частоту пейджинга под баром. -- **Обработанное письмо вернулось в списке** — не баг: в Mappa нет `.read/`, - письма не перемещаются. Сверяйся с уже виденными id, не перечитывай. - -## What NOT to do - -- **Don't watch the file inbox** (`.agents/inbox/`) — канал выпилен; пиши и - читай через Mappa. -- **Don't watch the inbox with a background Bash** (`run_in_background`) — it - leaks across `/clear` and accumulates zombies. Use the Monitor tool. -- **Don't add a SessionEnd teardown hook** — the Monitor self-terminates. -- **Don't raise more than one monitor.** The hook guarantees a clean slate - before you raise. -- **Don't handle message content here** — that's `inter-session-messaging`. -- **Don't rely on this in headless** — use the pull model (Stop-hook + - Notify). diff --git a/skills/session-inbox-monitor/hooks/inbox-monitor.ps1 b/skills/session-inbox-monitor/hooks/inbox-monitor.ps1 deleted file mode 100644 index eac16e0..0000000 --- a/skills/session-inbox-monitor/hooks/inbox-monitor.ps1 +++ /dev/null @@ -1,103 +0,0 @@ -# SessionStart inbox-monitor injector hook (session-inbox-monitor skill, v1.0.0). -# -# Channel is Mappa, NOT files (flip, решение 15): letters are entities `inbox` -# (`i:N`) in the mappa service, read via HTTP `GET /inbox?project=`. -# Two jobs, run on every SessionStart (startup / resume / clear / compact): -# (a) SWEEP - kill orphaned inbox-monitor OS processes of THIS project. -# A `/clear` does NOT fire SessionEnd, so a Monitor's underlying -# poll process can outlive the session it belonged to. Without a -# sweep, re-raising would stack duplicates. Match is by a sentinel -# string (CLAUDE_INBOX_MONITOR) baked into the poll command PLUS -# this project's directory - so we never touch unrelated processes. -# (b) INJECT - additionalContext telling the agent to raise a persistent -# Monitor (Monitor TOOL, not background Bash) polling the Mappa -# inbox of this project (HTTP GET /inbox). -# -# Opt-in per project: AGENTS.md or CLAUDE.md line `inbox monitor: raise on start`. -# (The `.agents/inbox/` dir trigger is gone - no file channel anymore.) -# -# Headless (`claude -p`): there is NO reliable hook-level signal to detect it -# (verified 2026-06-17 - `source` and CLAUDE_* env vars don't distinguish it). -# So the hook injects unconditionally and the SKILL instructs the agent to skip -# when headless. A Monitor raised in headless is harmless (killed ~5s after the -# run ends); a false-skip in an interactive session would silently lose the -# feature - so the default errs toward raising. -# -# Machine-local deploy target: ~/.claude/hooks/inbox-monitor.ps1 (registered in -# ~/.claude/settings.json SessionStart). Versioned here for multi-machine rollout. - -param( - [string]$ProjectDir = $env:CLAUDE_PROJECT_DIR, - [string]$Endpoint = $env:MAPPA_CORE_URL -) - -if (-not $ProjectDir) { exit 0 } -if (-not $Endpoint) { $Endpoint = 'https://mappa.vds.kzntsv.site' } - -# UTF-8 stdout guard. This hook emits JSON (additionalContext) to a redirected -# pipe under WinPS 5.1 - the same context that mojibaked stop-dispatcher output. -# The project name (user-data) is interpolated into stdout, so set UTF-8 as a -# forward-guard. Idempotent. -[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 -$OutputEncoding = [System.Text.Encoding]::UTF8 - -$projectName = Split-Path $ProjectDir -Leaf -$agentsMd = Join-Path $ProjectDir 'AGENTS.md' -$claudeMd = Join-Path $ProjectDir 'CLAUDE.md' - -# --- opt-in gate (line in AGENTS.md or CLAUDE.md) --------------------------- -$optedIn = $false -foreach ($md in @($agentsMd, $claudeMd)) { - if (Test-Path $md) { - if (Select-String -Path $md -SimpleMatch 'inbox monitor: raise on start' -Quiet -ErrorAction SilentlyContinue) { - $optedIn = $true - break - } - } -} -if (-not $optedIn) { exit 0 } - -# Forward-slash project dir: the Monitor poll command (Git Bash) uses this form, -# so both the sweep match and the injected command share one literal. -$dirFwd = ($ProjectDir -replace '\\', '/') - -# --- (a) sweep orphaned monitors of THIS project ---------------------------- -# Match = sentinel AND this project's dir in the same process command line. -try { - Get-CimInstance Win32_Process -ErrorAction Stop | - Where-Object { - $_.CommandLine -and - $_.CommandLine -match 'CLAUDE_INBOX_MONITOR' -and - $_.CommandLine -like "*$dirFwd*" - } | - ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue } -} catch { } - -# --- (b) build the canonical Monitor poll command --------------------------- -# `: CLAUDE_INBOX_MONITOR` is a bash no-op carrying the sweep sentinel in the -# process command line without polluting the event stream. Polls the Mappa -# inbox of this project over HTTP, extracts letter ids via node (present on -# every machine that runs the mappa MCP), de-dups by id so a sitting letter -# pages once, not every 15s (a noisy monitor is auto-stopped). -$auth = '' -if ($env:MAPPA_API_TOKEN) { $auth = "-H 'x-api-token: $($env:MAPPA_API_TOKEN)'" } -$cmd = @" -: CLAUDE_INBOX_MONITOR; s=' '; while true; do ids=`$(curl -s -m 10 $auth '__ENDPOINT__/inbox?project=__PROJECT__&limit=50' | node -e "let d='';process.stdin.on('data',c=>d+=c).on('end',()=>{try{const r=JSON.parse(d).rows||[];for(let i=r.length-1;i>=0;i--)console.log(r[i].id)}catch(e){}})"); for id in `$ids; do case "`$s" in *" `$id "*) continue;; esac; s="`$s`$id "; echo "New inter-session message in Mappa inbox (letter id `$id) - read it via inbox_monitor and handle now"; done; sleep 15; done -"@ -$cmd = $cmd.Trim().Replace('__ENDPOINT__', $Endpoint.TrimEnd('/')).Replace('__PROJECT__', $projectName) - -# --- (c) inject the raise-instruction -------------------------------------- -$ctx = @" -[session-inbox-monitor] This project participates in inter-session messaging. As your FIRST action, raise a persistent inbox watcher so messages from other sessions page you in real time. - -Use the Monitor tool with persistent: true, description "inbox watcher", and this EXACT command: - -$cmd - -Do NOT use a background Bash for this - it leaks across /clear. The Monitor tool is session-bound and tears down on its own at session end. The paired SessionStart hook already swept any orphaned watcher before this, so raise exactly one. - -If you are running headless (claude -p / non-interactive), SKIP this - the Stop-hook inbox pickup plus Notify cover delivery there. See the session-inbox-monitor skill for the full contract. -"@ - -@{ hookSpecificOutput = @{ hookEventName = 'SessionStart'; additionalContext = $ctx } } | ConvertTo-Json -Compress -Depth 5 -exit 0 diff --git a/skills/using-system-snapshot/SKILL.md b/skills/using-system-snapshot/SKILL.md deleted file mode 100644 index 3b637ce..0000000 --- a/skills/using-system-snapshot/SKILL.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -name: using-system-snapshot -author: ours -version: 1.0.0 -description: "Use at the start of an ops-context session, and ALWAYS before asserting anything about the mappa meta-service, the agent poller, local docker containers, or cross-project task load — call `mcp__mappa__meta_health` + `mcp__mappa__admin_status` (сервис жив, нагрузка) and `mcp__projects-meta__meta_system_snapshot` (поллер/докер/таски) instead of running `tasklist` / `docker ps` / guessing by hand. Triggers on «что запущено», «что сейчас крутится», «состояние системы», «состояние машины», «поллер работает?», «поллер живой?», «что с докером», «сводка по задачам», «what's running», «system status», «system snapshot», «is the poller up», «is the runner alive», «what containers are up». Read-only — no per-session grant needed. Skip for deep single-container docker diagnosis (that's using-vds-ops for the VDS / docker logs locally) and for mutating or precise per-task work (that's using-projects-meta / mappa task.*)." ---- - -# using-system-snapshot - -## Overview - -Two layers, one mental model: **mappa meta-service** (решение 14/15 — мета в сервисе) + **машина** (поллер/докер/таск-нагрузка — внешняя инфра). - -1. Сервис жив и как загружен: `mcp__mappa__meta_health` (no args → `{ok}`) + `mcp__mappa__admin_status` (счётчики сущностей по типам/проектам — живая нагрузка). -2. Машина: `mcp__projects-meta__meta_system_snapshot` — поллер agents-task-runner, локальные docker-контейнеры, сводка таск-нагрузки из кэша. - -Заменяет старый разброс `tasklist` / `docker ps` / ручного `meta_status` одной-двумя зондами. - -**Core rule: never assert состояние сервиса, поллера, контейнеров или таск-нагрузки без вызова тула в этом же turn.** Память и «он же работал ранее» — не evidence. - -## When to use - -- Session start в **ops-контексте** — ориентация перед infra/раннер/борд-работой. -- User спрашивает что живо: «что запущено», «состояние системы», «поллер работает?», «что с докером», «what's running», «is the poller up». -- **Перед любым claim** о живости mappa-сервиса, поллера, контейнера или числа активных/блокированных тасок. -- Быстрый взгляд на таск-нагрузку (где сейчас сконцентрирована работа). - -## When NOT to use - -- Глубокая диагностика **одного** контейнера (logs/inspect/stats/restart-loop) — это using-vds-ops для Rusonyx VDS или `docker logs` локально. -- **Мутации** тасок или чтение полного борда/тела задачи — using-projects-meta (и mappa task.* / локальный `.tasks/` для текущего проекта). -- Библиотечные доки, поиск по коду — не сюда. - -## Prerequisites - -- `mcp__mappa__*` — mappa MCP (сервис meta; установка — setup-проект mappa). -- `mcp__projects-meta__meta_system_snapshot` — projects-meta-mcp (поллер/докер/кэш-таски). Если тула нет — `setup-projects-meta`. - -## The calls - -`mcp__mappa__meta_health` — no args, read-only. `{ok: true, service: "mappa"}` — сервис жив. -`mcp__mappa__admin_status` — no args, read-only. Счётчики по типам и проектам. -`mcp__projects-meta__meta_system_snapshot` — no args, read-only. Три ключа: - -| Key | Shape | Liveness | -|---|---|---| -| `poller` | `{ running: bool, projects: "" }` | **live** на момент вызова | -| `docker` | `[{ name, status }]` — локальные контейнеры | **live** на момент вызова | -| `tasks` | `{ "/": { active, blocked }, … }` | **из кэша** projects-meta — может быть stale | - -`docker` — **локальная** машина (включая `agents-task-runner-*`), НЕ VDS. `tasks` — кэш; для точного состояния — using-projects-meta Step 0 или mappa/локальный `.tasks/`. - -## Output format — одна строка на секцию - -Сожми в 3–4 строки, не дампи raw-object. - -``` -🟢 Mappa alive — 42 wiki / 18 tasks, 12 проектов -🟢 Poller running — OpeItcLoc03/skills (🔴 если running:false) -🟢 Docker — 8/8 up (иначе только проблемные) -📋 Tasks — 23 active / 41 blocked, 17 проектов (назвать 2–3 самых загруженных) -``` - -Правила на строку: - -- **Mappa** — 🟢/🔴 + admin_status-сводка (счётчики по типам или проектам — кратко). Сервис упал = заголовок. -- **Poller** — 🟢/🔴 + running-flag + projects-строка. Остановлен — скажи прямо. -- **Docker** — если всё `Up` (вкл. `Up … (healthy)`) — `N/N up`. Иначе только проблемные (`Restarting`, `Exited`, `(unhealthy)`, `Created`, `Paused`) по имени + статусу. -- **Tasks** — суммы (Σ active / Σ blocked) + 2–3 проекта с наибольшей активностью. Полный расклад — только по запросу. - -## What NOT to do - -- **НЕ** утверждай «поллер работает» / «все контейнеры up» / «N активных тасок» по памяти или прошлому снапшоту. Зови тулы в текущем turn — liveness требует свежих данных. -- **НЕ** падай на `tasklist` / `docker ps` / ручной `meta_status` — это scatter, который скил заменяет. (`docker logs` — только для глубокой диагностики одного контейнера.) -- **НЕ** вставляй raw-JSON. Строки, по одной на секцию. -- **НЕ** подавай `tasks`-счётчики как точные — они из кэша. При важности точности — флаг сталезности + using-projects-meta. - -## Common mistakes - -| Mistake | Fix | -|---|---| -| «Сервис жив» без вызова в этом turn | Зови `meta_health`/`admin_status` — liveness требует текущих данных. | -| «Poller up» без вызова | `meta_system_snapshot` в текущем turn. | -| `docker ps` / `tasklist` вместо зондов | Один-два вызова тула; в этом смысл. | -| Читать снапшот-`docker` как VDS-флот | Это **локальная** машина. VDS — using-vds-ops. | -| `tasks`-счётчики как авторитетные | Они кэшированы. Точность — using-projects-meta Step 0 / mappa / `.tasks/`. | -| Дампить raw-объект | Сожми в строки (mappa / poller / docker / tasks). |