feat(session-handoff): v1.0.0 — NEXT_SESSION.md → handoff-сущность mappa (#983)
mcp__mappa__handoff_write (per-project, append-only, versioned-история, поля session_id/date/status/summary/open_treks/ask_user/guards/recent_commits, решение 14/гриллинг Q5). Чтение — entity_search(type=handoff). Без лиза, без файлов, без git-стеджинга. Ритуал закрытия сохранён (propose-only).
This commit is contained in:
@@ -1,90 +0,0 @@
|
|||||||
---
|
|
||||||
name: meta-host-routing
|
|
||||||
author: ours
|
|
||||||
version: 0.3.0
|
|
||||||
description: >
|
|
||||||
Use before any tasks_create / knowledge_ingest / brainstorm-promotion against
|
|
||||||
a project — resolve WHERE that project's meta lives before writing. A
|
|
||||||
github-hosted project (or any project projects-meta reports "not in cache")
|
|
||||||
does NOT carry .tasks/.wiki in its own repo (meta-out-of-repo design: they'd
|
|
||||||
leak on push/PR). Its meta lives in a sibling Gitea-tracked host repo — route
|
|
||||||
MCP calls there, never into the github working tree, never guess. Triggers:
|
|
||||||
"project not in cache" from projects-meta, promoting/creating tasks for a
|
|
||||||
project with a github remote, "заведи таски в <github-проект>", "промоутни
|
|
||||||
<github-проект>". Skip for a normal Gitea project already known to
|
|
||||||
projects-meta — there the route is direct.
|
|
||||||
---
|
|
||||||
|
|
||||||
# meta-host-routing
|
|
||||||
|
|
||||||
> A project's code repo is not always where its meta lives. Before writing tasks or wiki, resolve the **meta-host**. Github-hosted projects keep their `.tasks/`/`.wiki/` in a sibling Gitea repo — never in the github tree. Never guess the target.
|
|
||||||
|
|
||||||
## When this runs
|
|
||||||
|
|
||||||
Before any `mcp__projects-meta__tasks_create`, `mcp__projects-meta__knowledge_ingest`, or brainstorm promotion, when **either**:
|
|
||||||
|
|
||||||
- the target project's local clone has a **github remote**, OR
|
|
||||||
- `projects-meta` returns **"project not in cache"** for the target.
|
|
||||||
|
|
||||||
Both are signals that the project follows the **meta-out-of-repo** design: its meta is intentionally absent from its own repo.
|
|
||||||
|
|
||||||
**Skip** when the target is a normal Gitea project already known to `projects-meta` (`meta_status` lists it / a `tasks_create` dry-run succeeds) — there the route is direct, no resolution needed.
|
|
||||||
|
|
||||||
## Why meta is out of the repo
|
|
||||||
|
|
||||||
Per the `meta-out-of-repo` design: `.tasks/`, `.wiki/`, `.claude/` must not be committed into a repo that gets pushed to a public / shared / forked-upstream remote — the "kitchen" (notes, tasks, local skills, agent instructions) would leak. A global `core.excludesFile` ignores those paths, so github-hosted projects carry **no** meta in-tree by design. The meta still exists — it lives in a Gitea-tracked **host** repo and syncs through `projects-meta`.
|
|
||||||
|
|
||||||
## Steps
|
|
||||||
|
|
||||||
1. **Detect.** Check the target's local remote (`git remote -v`) and/or a `projects-meta` dry-run. Github remote OR "not in cache" → meta-out-of-repo project; continue. Otherwise → direct Gitea route, this skill does not apply.
|
|
||||||
|
|
||||||
2. **Resolve the meta-host**, in priority order:
|
|
||||||
- **(a) Dedicated meta-host (preferred).** Is there a Gitea repo named **`meta-<project>`**, holding only `.wiki/`+`.tasks/` (no code)? That is its meta-host. Once synced, `projects-meta` tracks it as a project `<owner>/meta-<project>` — a `tasks_create` dry-run against that resolves. Canonical example: code `github.com/OpeItcLoc03/yt-tools` → meta-host **Gitea `OpeItcLoc03/meta-yt-tools`**. **Naming is `meta-<project>`, NOT `<project>`** — per the `meta-out-of-repo` design: the bare `<project>` name on Gitea must stay free for a possible code **mirror** of the github repo. (Local clone convention, if ever needed: `~/projects/.meta/<project>/`.)
|
|
||||||
- **(b) Shared host (transitional).** No dedicated host yet → grep sibling Gitea repos, **start with `.common`** (`~/projects/.common/`), for the project name:
|
|
||||||
```
|
|
||||||
grep -ril "<project-name>" ~/projects/.common/.tasks/ ~/projects/.common/.wiki/
|
|
||||||
```
|
|
||||||
The host is whichever Gitea repo already holds that project's tasks/wiki.
|
|
||||||
- **(c) Neither** → the project has no meta-host yet (Failure modes — STOP and ask, or bootstrap one per "Bootstrapping a new meta-host").
|
|
||||||
|
|
||||||
> Note: `.common` was yt-tools' shared host until 2026-05-27, when yt-tools graduated to its own dedicated host (`OpeItcLoc03/meta-yt-tools`). `.common` now holds only yt-tools' done-task archive. Prefer giving a maturing project its own host over piling onto `.common`.
|
|
||||||
|
|
||||||
3. **Route there.** Send every `tasks_create` / `knowledge_ingest` to the host's qualified `<owner>/<repo>` (a dedicated host = `<owner>/meta-<project>`; a shared host = e.g. `OpeItcLoc03/common`). On a shared host, namespace entries with a `<project>-` slug prefix.
|
|
||||||
|
|
||||||
4. **Never** write `.tasks/`/`.wiki/` files into the github working tree, and **never** invent a target when resolution is ambiguous (Failure modes below).
|
|
||||||
|
|
||||||
## Bootstrapping a new meta-host
|
|
||||||
|
|
||||||
When a project graduates to its own dedicated host (or a github project needs one):
|
|
||||||
|
|
||||||
1. Create a Gitea repo named **`meta-<project>`** (meta-only, `auto_init:false`) via the API with the admin token (`~/.config/projects-mcp/auth.toml`). Do **not** use the bare `<project>` name — keep it free for a code mirror.
|
|
||||||
2. Clone it, build canonical `.wiki/` (CLAUDE.md, index.md, log.md, overview.md, raw/, concepts/, entities/, packages/, sources/) + `.tasks/STATUS.md` (emoji legend header).
|
|
||||||
3. **`git add -f .wiki .tasks`** — the global `core.excludesFile` (`~/.config/git/ignore`) ignores `.wiki/`/`.tasks/`. Existing hosts track them because they were added *before* that ignore existed; a fresh clone needs `-f` or `git add -A` silently stages nothing. This is the one gotcha that will waste a commit if missed.
|
|
||||||
4. Commit, push. `projects-meta` picks it up on its next sync (it may not be in cache until then — see Failure modes).
|
|
||||||
5. If migrating off a shared host: move open tasks + design concepts to the new host, leave the done-task archive behind under a relocation marker, and replace moved concept docs with pointer stubs so back-references don't dead-end.
|
|
||||||
|
|
||||||
## Failure modes
|
|
||||||
|
|
||||||
- **No Gitea repo tracks this project** → STOP. Ask the user whether to bootstrap a dedicated host (preferred) or attach to a shared one. Do **not** default to writing into the github repo — that reintroduces the leak meta-out-of-repo exists to prevent.
|
|
||||||
- **Just-created meta-host not yet in `projects-meta` cache** → `tasks_create`/`knowledge_ingest` return "not in cache" until a sync runs. Either trigger a sync, or write the initial `.tasks/STATUS.md` / `.wiki/` content directly via git (as in Bootstrapping) and let the MCP pick it up next sync.
|
|
||||||
- **Multiple Gitea repos reference the project** → STOP, ask which is canonical. Don't pick by guess.
|
|
||||||
- **projects-meta cache stale** ("not in cache" could be staleness, not meta-out-of-repo) → run a sync / `meta_status` freshness check first (see `using-projects-meta` Step 0) before concluding the project is github-only.
|
|
||||||
|
|
||||||
## Interaction with workshop-promote-brainstorm
|
|
||||||
|
|
||||||
`workshop-promote-brainstorm`'s domain branch currently **aborts** on "project not in cache". With this skill active, that abort becomes a resolve step: find the meta-host, then promote into it. This skill is the routing primitive; promote-brainstorm (and ad-hoc `tasks_create`) consult it.
|
|
||||||
|
|
||||||
## What NOT to do
|
|
||||||
|
|
||||||
- Don't write meta into a github working tree "because the project is right there" — that's the exact path-of-least-resistance leak meta-out-of-repo prevents.
|
|
||||||
- Don't treat "project not in cache" as "project doesn't exist" — it means "meta is hosted elsewhere," resolve it.
|
|
||||||
- Don't guess the meta-host when grep is ambiguous — ask.
|
|
||||||
- Don't apply this to normal Gitea projects already in `projects-meta` — adds a pointless resolution step.
|
|
||||||
|
|
||||||
## Cross-agent note
|
|
||||||
|
|
||||||
References Claude Code MCP tool names (`mcp__projects-meta__*`). On non-CC platforms substitute the projects-meta equivalents; the routing logic is platform-independent.
|
|
||||||
|
|
||||||
## Why this exists
|
|
||||||
|
|
||||||
Codified 2026-05-27 after an agent, asked to promote a yt-tools feature, found yt-tools "not in cache" and started writing tasks directly into the github repo — instead of recalling that yt-tools' meta lives in `.common`. The `meta-out-of-repo` design existed only as an archived workshop concept doc (never triggers). This skill makes the routing rule fire at the moment of action.
|
|
||||||
@@ -1,23 +1,33 @@
|
|||||||
---
|
---
|
||||||
name: session-handoff
|
name: session-handoff
|
||||||
author: ours
|
author: ours
|
||||||
version: 0.5.1
|
version: 1.0.0
|
||||||
description: "Sliding handoff between CC sessions via .tasks/NEXT_SESSION.md. Read on session start: orient agent, ask user before action. Write on session-end phrase or substantive commit. On session-end the agent ALSO runs the closing ritual on its own (idea 7: no invitation needed): handoff write + PROPOSE wiki-ingest of session knowledge + PROPOSE task-board closes — mutations only after user confirmation. Session-end phrases: «завершаем сессию», «сворачиваемся», «закругляемся», «wrap up session», «end session», «we're done for now». Trigger-line in AGENTS.md: `session handoff: read on start, write on end`. Skip task-zone phrases: «закрываем эту таску», «pause», «отбой», «разбегаемся»."
|
description: "Sliding handoff между сессиями через handoff-сущность Mappa (решение 14, гриллинг Q5): per-project, versioned-история, замена .tasks/NEXT_SESSION.md. Read on session start: orient agent, ask user before action. Write on session-end phrase or substantive commit. On session-end the agent ALSO runs the closing ritual on its own (idea 7: no invitation needed): handoff write + PROPOSE wiki-ingest + PROPOSE task-board closes — mutations only after user confirmation. Session-end phrases: «завершаем сессию», «сворачиваемся», «закругляемся», «wrap up session», «end session», «we're done for now». Trigger-line in AGENTS.md: `session handoff: read on start, write on end`. Skip task-zone phrases: «закрываем эту таску», «pause», «отбой», «разбегаемся»."
|
||||||
---
|
---
|
||||||
|
|
||||||
# session-handoff
|
# session-handoff
|
||||||
|
|
||||||
Sliding handoff prompt между CC сессиями. На старте — читает `.tasks/NEXT_SESSION.md`, ориентирует агента и спрашивает user'а перед действиями. При substantive commit'е или session-end фразе — перезаписывает handoff для следующей сессии. Sliding overwrite: один файл, история — через `git log -p .tasks/NEXT_SESSION.md`.
|
Sliding handoff между сессиями. Канал — **handoff-сущность Mappa** (`mcp__mappa__handoff_write`, тип `h:`, per-project): поля `session_id`/`date`/`status`/`summary`/`open_treks[]`/`ask_user[]`/`guards[]`/`recent_commits[]`. Каждый write = **новая версия** (append-only, versioned-история) — файлового `.tasks/NEXT_SESSION.md` больше нет, git-история не нужна.
|
||||||
|
|
||||||
Forward-looking, не timeline: handoff = связка новых вещей конкретно для следующего разворота, не overview всего проекта. STATUS.md / MEMORY.md / `.wiki/log.md` остаются авторитетными для своего scope'а.
|
На старте — читает последний handoff проекта, ориентирует агента и спрашивает user'а перед действиями. При substantive commit'е или session-end фразе — пишет новый handoff для следующей сессии.
|
||||||
|
|
||||||
Дизайн-источник: `~/projects/.workshop/.archive/2026-05-24-session-handoff-skill.md` (Round 1 design + Round 2 resolved Q1–Q10).
|
Forward-looking, не timeline: handoff = связка новых вещей конкретно для следующего разворота, не overview всего проекта. STATUS.md-эквиваленты (борд mappa) / вики остаются авторитетными для своего scope'а.
|
||||||
|
|
||||||
|
## MCP-поверхность
|
||||||
|
|
||||||
|
| Операция | Тул | Примечание |
|
||||||
|
|---|---|---|
|
||||||
|
| Запись handoff | `mcp__mappa__handoff_write(project, session_id, status, summary, open_treks?, ask_user?, guards?, recent_commits?)` | append-only; без лиза (как инбокс) |
|
||||||
|
| Чтение последнего | `mcp__mappa__entity_search(q, type='handoff', project=<имя>, limit=1)` | ORDER BY created_at DESC → первый = последний |
|
||||||
|
| Полное чтение версии | `mcp__mappa__entity_get(id)` | id internal из search |
|
||||||
|
|
||||||
|
`status` ∈ `active | paused | done`. Поля-массивы (open_treks/ask_user/guards/recent_commits) — строковые массивы; пустые секции передавать как `[]` (аналог пометки «(нет на этом раунде)» — next агент видит: пусто, не забыто).
|
||||||
|
|
||||||
## When to use
|
## When to use
|
||||||
|
|
||||||
**Read mode (session start):**
|
**Read mode (session start):**
|
||||||
- AGENTS.md проекта содержит trigger-строку `session handoff: read on start, write on end`.
|
- AGENTS.md проекта содержит trigger-строку `session handoff: read on start, write on end`.
|
||||||
- Файл `.tasks/NEXT_SESSION.md` существует.
|
- В mappa есть handoff-сущности проекта (search не пуст).
|
||||||
|
|
||||||
**Write mode (session end / substantive commit):**
|
**Write mode (session end / substantive commit):**
|
||||||
- User'ская фраза из whitelist:
|
- User'ская фраза из whitelist:
|
||||||
@@ -27,131 +37,77 @@ Forward-looking, не timeline: handoff = связка новых вещей к
|
|||||||
- prefix НЕ в (`meta:`|`docs:`|`style:`|`chore:`|`fix typo`)
|
- prefix НЕ в (`meta:`|`docs:`|`style:`|`chore:`|`fix typo`)
|
||||||
- AND (body length > 200 символов OR files changed > 3)
|
- AND (body length > 200 символов OR files changed > 3)
|
||||||
- Плюс: **первый** non-trivial commit сессии — всегда триггерит, даже если ниже порога (старт работы = context shift).
|
- Плюс: **первый** non-trivial commit сессии — всегда триггерит, даже если ниже порога (старт работы = context shift).
|
||||||
- **Optional**: substantive-commit detection может быть автоматизирован harness-side через PostToolUse hook — см. `hooks/README.md` для opt-in инструкций. С enabled hook'ом первая часть becomes deterministic (parser-side, не behavioral memory).
|
|
||||||
|
|
||||||
**Skip (false-positive guards):**
|
**Skip (false-positive guards):**
|
||||||
- «закрываем эту таску» — task close, не session. Это zone `using-tasks`.
|
- «закрываем эту таску» — task close, не session. Это зона using-tasks.
|
||||||
- «pause», «приостанови» — task-pause, не session-end.
|
- «pause», «приостанови» — task-pause, не session-end.
|
||||||
- «отбой», «разбегаемся» — слишком broad, может относиться к другому контексту.
|
- «отбой», «разбегаемся» — слишком broad.
|
||||||
- «сейчас завершу одну задачу и тогда поговорим» — частичное завершение.
|
- «сейчас завершу одну задачу и тогда поговорим» — частичное завершение.
|
||||||
- Не-git папка, или `.tasks/` отсутствует — silent exit.
|
- Проект не в mappa / нет handoff-сущностей — silent exit.
|
||||||
- AGENTS.md проекта НЕ содержит trigger-строку — silent exit.
|
- AGENTS.md проекта НЕ содержит trigger-строку — silent exit.
|
||||||
|
|
||||||
При неоднозначности — **ASK**, не угадывать: «закрываем сессию или таску?»
|
При неоднозначности — **ASK**, не угадывать: «закрываем сессию или таску?»
|
||||||
|
|
||||||
## Inputs
|
|
||||||
|
|
||||||
**Read mode:**
|
|
||||||
- `.tasks/NEXT_SESSION.md` — sliding handoff, должен существовать.
|
|
||||||
- Текущая дата (для staleness check против `_last_updated_`).
|
|
||||||
|
|
||||||
**Write mode:**
|
|
||||||
- `git log --oneline -5` — последние commits сессии.
|
|
||||||
- `.tasks/STATUS.md` — open треки + 🔴 active task'и (для mid-task capture).
|
|
||||||
- Контекст сессии — pending user-decisions, waiting permissions, memory updates, preemptive guards.
|
|
||||||
|
|
||||||
## Steps
|
## Steps
|
||||||
|
|
||||||
### Read mode
|
### Read mode
|
||||||
|
|
||||||
1. **Detect.** Проверить что `.tasks/NEXT_SESSION.md` существует. Нет — silent exit.
|
1. **Detect.** `mcp__mappa__entity_search(q='', type='handoff', project=<имя проекта>, limit=1)` — если пусто, silent exit (первая сессия проекта).
|
||||||
2. **Staleness check.** Прочитать frontmatter `_last_updated_`. Возраст > 7 дней → отметить user'у:
|
2. **Staleness check.** `meta.date` последнего handoff'а. Возраст > 7 дней → отметить user'у:
|
||||||
```
|
```
|
||||||
handoff от <date> (N дней назад) — возможно устарел.
|
handoff от <date> (N дней назад) — возможно устарел.
|
||||||
Оверrайдить или продолжить?
|
Оверрайдить или продолжить?
|
||||||
```
|
```
|
||||||
Дождаться ответа перед продолжением.
|
Дождаться ответа перед продолжением.
|
||||||
3. **Summarize.** Прочитать тело handoff'а — 5 секций (recent commits / open треки / спроси user'а / не делать / memory updates).
|
3. **Summarize.** Прочитать мета последнего: summary / open_treks / ask_user / guards / recent_commits.
|
||||||
4. **Orient.** Пересказать user'у одним блоком: «прошлая сессия предложила X (open треки + ask-items + don't-items + memory updates). Делаем?»
|
4. **Orient.** Пересказать user'у одним блоком: «прошлая сессия предложила X (open треки + ask-items + guards). Делаем?»
|
||||||
5. **Wait.** Не делать никаких действий до подтверждения user'ом. Default = orient + ask, **никакого auto-execute**.
|
5. **Wait.** Не делать никаких действий до подтверждения user'ом. Default = orient + ask, **никакого auto-execute**.
|
||||||
|
|
||||||
### Write mode
|
### Write mode
|
||||||
|
|
||||||
1. **Scope check.** Это текущий проект (cwd с `.tasks/`). Никаких global мутаций, никаких других проектов.
|
1. **Scope check.** Это текущий проект (cwd). Никаких global мутаций, никаких других проектов.
|
||||||
2. **Mid-task capture.** Если в `.tasks/STATUS.md` есть 🔴 active task — захватить:
|
2. **Mid-task capture.** Если есть 🔴 active таска проекта (борд mappa / `.tasks/`) — захватить в summary:
|
||||||
```
|
```
|
||||||
left mid-task: <slug>
|
left mid-task: <slug>
|
||||||
where_stopped: <текст из STATUS.md>
|
where_stopped: <одна строка>
|
||||||
```
|
```
|
||||||
3. **Compose content.** Собрать `.tasks/NEXT_SESSION.md`:
|
3. **Compose content.** Собрать поля handoff:
|
||||||
```markdown
|
- `session_id` — `<ISO дата>` или идентификатор сессии;
|
||||||
---
|
- `status` — `active` (работа продолжается) / `paused` (заморожено) / `done` (завершено);
|
||||||
_last_updated_: <ISO 8601 timestamp>
|
- `summary` — связка: где остановились, mid-task, ключевые решения;
|
||||||
session_id: <hash или дата>
|
- `open_treks` — массив открытых треков (готовность + entry-point);
|
||||||
---
|
- `ask_user` — pending решения / ожидаемые разрешения;
|
||||||
|
- `guards` — «не делать» (preemptive guards);
|
||||||
# Next session handoff
|
- `recent_commits` — 3–5 последних коммитов (`<slug>: <subject>`).
|
||||||
|
4. **Append.** `mcp__mappa__handoff_write(project=<имя>, ...)` — сервис создаёт новую версию `h:N` (versioned-история; предыдущие версии остаются, `entity_search` вернёт свежую).
|
||||||
## Recent commits
|
5. **Closing ritual (idea 7).** На session-end фразе (НЕ на substantive commit) после handoff-write агент сам, без приглашения, предлагает закрытие:
|
||||||
- <slug>: <subject> (3–5 последних)
|
- **(2) Propose wiki-ingest.** Если за сессию появилось durable-знание — ПРЕДЛОЖИТЬ ingest (using-wiki v2: mappa wiki_create/update под лизом), перечислив кандидатов. Ничего не писать без подтверждения.
|
||||||
...
|
- **(3) Propose task-board closes.** Если есть задачи, выглядящие закрытыми — ПРЕДЛОЖИТЬ закрытия (using-tasks / mappa task_close под лизом). Уважать ralph-loop: verifier-задачи закрывать только через verifier.
|
||||||
|
- Формат — один блок: «Ритуал закрытия: (а) заингестить X в вики? (б) закрыть Y? (в) ничего.» Ждать ответа. Отказ = пропуск.
|
||||||
## Open треки
|
|
||||||
| Трек | Готовность | Entry-point |
|
|
||||||
|---|---|---|
|
|
||||||
| ... | ... | ... |
|
|
||||||
|
|
||||||
## Спроси user'а
|
|
||||||
- <pending decision 1>
|
|
||||||
- <waiting permission 2>
|
|
||||||
|
|
||||||
## Не делать (preemptive guards)
|
|
||||||
- <guard 1>
|
|
||||||
- <guard 2>
|
|
||||||
|
|
||||||
## Memory updates за сессию
|
|
||||||
- <что нового сохранилось / обновилось>
|
|
||||||
```
|
|
||||||
Пустую секцию — оставить заголовок + пометка `(нет на этом раунде)`. Чтобы next агент видел: не забыто, а пусто.
|
|
||||||
4. **Sliding overwrite.** `Write` поверх `.tasks/NEXT_SESSION.md` (предыдущее содержимое НЕ архивируется в `.archive/handoff-*.md` — sliding contract). История восстанавливается через `git log -p .tasks/NEXT_SESSION.md`.
|
|
||||||
5. **Stage.** `git add .tasks/NEXT_SESSION.md` — попадает в следующий commit сессии (или в текущий, если запись была вызвана session-end фразой).
|
|
||||||
6. **Closing ritual (idea 7).** На session-end фразе (НЕ на substantive commit) после handoff-write агент сам, без приглашения, предлагает закрытие:
|
|
||||||
- **(2) Propose wiki-ingest.** Если за сессию появилось durable-знание (паттерн, решение, коррекция user'а, процедура) — ПРЕДЛОЖИТЬ ingest (using-wiki: `sources/`+концепты или global через `knowledge_ingest`), перечислив кандидатов. Ничего не писать без подтверждения.
|
|
||||||
- **(3) Propose task-board closes.** Прочитать `.tasks/STATUS.md`: если есть задачи, выглядящие закрытыми (outcome достигнут, все шаги сделаны) — ПРЕДЛОЖИТЬ закрытия. Уважать ralph-loop: verifier-задачи (с `**Verifier:**`) закрывать только через verifier, не по виду.
|
|
||||||
- Формат предложения — один блок: «Ритуал закрытия: (а) заингестить X в вики? (б) закрыть Y? (в) ничего.» Ждать ответа. Отказ = пропуск, не настаивать.
|
|
||||||
|
|
||||||
## Ритуал закрытия (детали)
|
|
||||||
|
|
||||||
**Граница мутаций:** ритуал берёт инициативу в *проверке и предложении* — но НИ ОДНА мутация (wiki-ingest, закрытие таски) не выполняется молча. Каждая — после явного «да». Причина: вики-шум без ревью и закрытие ralph-loop задач без verifier — дороже пропущенного предложения.
|
|
||||||
|
|
||||||
**Skip (silent):**
|
|
||||||
- Нет `.wiki/` в проекте → шаг (2) пропускается молча.
|
|
||||||
- Нет `.tasks/STATUS.md` → шаг (3) пропускается молча.
|
|
||||||
- Не git-папка / нет `.tasks/` → весь ритуал silent exit (совпадает с базовым скилом).
|
|
||||||
|
|
||||||
**Триггер:** ритуал на session-end фразе; на substantive commit'е — только handoff-write, ритуал НЕ гонять (mid-session коммит ≠ конец сессии, иначе спам предложений).
|
|
||||||
|
|
||||||
**Headless (pi):** pi-extension `session-close-ritual` (источник: `~/projects/pi-extensions/extensions/session-close-ritual.ts` — репо `OpeItcLoc03/pi-extensions`, деплой `just install` в клоне → `~/.pi/agent/extensions/`) инжектит «прогони ритуал» один раз за сессию на `agent_end` в print-режиме (`pi -p`), с дедуп-гардом и opt-in (та же trigger-строка + `.tasks/` + git). Почему `agent_end`, а не `agent_settled`: settle = «no follow-up left», процесс в teardown — followUp уже не обработается (проверено live); `agent_end` срабатывает сразу после рана, пока followUps ещё доставляются. Интерактив — фразовый триггер (ниже), не инжекция.
|
|
||||||
|
|
||||||
## Failure modes
|
## Failure modes
|
||||||
|
|
||||||
- **AGENTS.md без trigger-строки** → silent exit, не вмешиваться. Скил project-opt-in.
|
- **AGENTS.md без trigger-строки** → silent exit. Скил project-opt-in.
|
||||||
- **Не git-repo / `.tasks/` отсутствует** → silent exit. Скил требует обоих условий.
|
- **Проект не в mappa / нет handoff-сущностей** → silent exit.
|
||||||
- **`.tasks/NEXT_SESSION.md` отсутствует** в read mode → silent exit (первая сессия проекта, нечего читать).
|
- **Stale handoff (> 7 дней)** в read mode → не silent, **спросить** user'а оверрайдить или продолжить.
|
||||||
- **Неоднозначная фраза** («закругляемся» в контексте отдельной таски, а не сессии) → ASK user'а «закрываем сессию или таску?», не угадывать.
|
- **Неоднозначная фраза** → ASK «закрываем сессию или таску?», не угадывать.
|
||||||
- **Secret detected.** Содержимое handoff'а матчит паттерны секретов (`AKIA...`, `sk-...`, `ghp_...`, `ssh-rsa`, `BEGIN PRIVATE KEY`, JWT в обычном виде, `password=`/`token=` без obfuscation) → **abort write**, не записывать. Файл идёт в git — не место для credentials. Сообщить user'у с указанием подозрительной строки, дать дочистить контекст руками.
|
- **Secret detected.** Контент матчит паттерны секретов (`AKIA...`, `sk-...`, `ghp_...`, `ssh-rsa`, `BEGIN PRIVATE KEY`, `password=`/`token=`) → **abort write**. Сообщить user'у с указанием подозрительной строки.
|
||||||
- **Stale handoff (> 7 дней)** в read mode → не silent, **спросить** user'а оверrайдить или продолжить (Q9 resolved 2026-05-24).
|
- **Mid-task без борда** → писать handoff без mid-task секции, не блокировать.
|
||||||
- **Mid-task без STATUS.md entry** в write mode → записать handoff без mid-task секции, не блокировать.
|
- **Ритуал: user отказал** → пропустить, не настаивать, не повторять в этой сессии.
|
||||||
- **Ритуал: user отказал во всех предложениях** → пропустить, не настаивать, не повторять в этой сессии. Отказ = решение, не приглашение к уговорам.
|
|
||||||
|
|
||||||
## Side effects
|
## Side effects
|
||||||
|
|
||||||
- Записывает / перезаписывает `.tasks/NEXT_SESSION.md` (project-scope only).
|
- Пишет handoff-сущность проекта (append-only, versioned-история). Никаких файлов, никаких git-коммитов за handoff.
|
||||||
- Файл git-tracked, попадает в commit (либо вместе с session work, либо отдельным commit'ом).
|
- Ритуал закрытия предлагает wiki-ingest и закрытия тасок — но НЕ пишет их.
|
||||||
- Никаких других файлов: `.archive/` не плодим (sliding), `.wiki/log.md` не дёргаем (это не promoted event), `STATUS.md` не правим.
|
|
||||||
- Ритуал закрытия предлагает wiki-ingest и закрытия тасок — но НЕ пишет их. Файлы пишет user-подтверждённый следующий шаг (using-wiki / using-tasks), не этот скил.
|
|
||||||
- Никаких global мутаций, никаких других проектов, никаких user-level config writes.
|
- Никаких global мутаций, никаких других проектов, никаких user-level config writes.
|
||||||
|
|
||||||
## What NOT to do
|
## What NOT to do
|
||||||
|
|
||||||
- **Не auto-execute** действия из read handoff'а. Default = orient + ask. Прошлая сессия могла ошибиться; user agency сохраняем.
|
- **Не auto-execute** действия из read handoff'а. Default = orient + ask.
|
||||||
- **Не писать в вики / не закрывать таски по ритуалу молча.** Ритуал = предложения (idea 7, граница мутаций). Каждая мутация — после явного «да».
|
- **Не писать в вики / не закрывать таски по ритуалу молча.** Ритуал = предложения. Каждая мутация — после явного «да».
|
||||||
- **Не гонять ритуал на substantive commit.** Только session-end фраза. Mid-session коммит → handoff-write без ритуала (иначе спам предложений).
|
- **Не гонять ритуал на substantive commit.** Только session-end фраза.
|
||||||
- **Не append-with-archive.** Sliding only. `.archive/handoff-<date>.md` создавать не нужно — это создавало бы N artefact'ов, которые user не хочет. История — через git log.
|
- **Не писать секреты** в handoff. Если контент матчит secret-patterns — abort.
|
||||||
- **Не триггерить на task-zone phrases** («закрываем эту таску», «pause»), broad farewells («отбой», «разбегаемся»), partial completions («сейчас завершу одну задачу и тогда поговорим»).
|
- **Не на каждом commit'е.** Только substantive (см. эвристику).
|
||||||
- **Не писать секреты** в handoff. Если контент матчит secret-patterns — abort, попросить user'а вычистить контекст.
|
- **Не дублировать борд / вики.** Handoff = forward-looking связка, не overview.
|
||||||
- **Не на каждом commit'е.** Только substantive (см. эвристика в When to use). Trivial `chore: bump dep` или `docs: typo` НЕ триггерят, иначе handoff'ы шумят.
|
- **Не cross-project.** Per-project scope.
|
||||||
- **Не дублировать STATUS.md / MEMORY.md.** Handoff = **forward-looking связка** новых вещей для следующего разворота, не overview всего проекта. Open треки — да, но как мостик «вот где остановились», не как replica STATUS.md.
|
|
||||||
- **Не cross-project.** Per-project scope. «Завершаем сессию» в `.workshop/` не трогает `.admin/` и наоборот.
|
|
||||||
- **Не зависеть от harness `SessionEnd` hook** — такого hook'а в Claude Code нет (есть только `SessionStart`, `UserPromptSubmit`, `PreToolUse`, `PostToolUse`, `Stop`, `Notification`). Триггер — фраза или substantive-commit detection в самом agent flow.
|
|
||||||
- **Не считать handoff авторитетным** на стороне читателя. Это рекомендация прошлой сессии, не директива. User может override любую её часть.
|
- **Не считать handoff авторитетным** на стороне читателя. Это рекомендация прошлой сессии, не директива. User может override любую её часть.
|
||||||
|
|||||||
Reference in New Issue
Block a user