# Lesson Close

> Закрыть текущее занятие. Финализирует lesson/YYYY-MM-DD.md (frontmatter status, метаданные времени, длительность), делает commit + push в репо DS-personal-guide. Триггерит замкнутый контур доставки — после push → GitHub webhook → bot oauth_server.py:/webhook/github/workbook → sync_one_user_to_dt → ЦД обновляется в Neon. Используй когда пилот говорит «закрываем», «всё», «закончили», «закрой урок» — или после явного завершения задания в текущем lesson-файле.

- Skill: `tserentserenov/lesson-close` (Agent Skill)
- Install (CLI): `npx skillmds@latest add tserentserenov/lesson-close`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tserentserenov/lesson-close/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: TserenTserenov (https://skillmd.com/u/tserentserenov)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tserentserenov/lesson-close

---


# /lesson-close — закрыть занятие и замкнуть контур доставки

> ⚡ **Алгоритм, не диалог.** Шаги 1-5 последовательно. Без вопросов.

## When to use

Закрыть текущее занятие. Финализирует lesson/YYYY-MM-DD.md (frontmatter status, метаданные времени, длительность), делает commit + push в репо DS-personal-guide. Триггерит замкнутый контур доставки — после push → GitHub webhook → bot oauth_server.py:/webhook/github/workbook → sync_one_user_to_dt → ЦД обновляется в Neon. Используй когда пилот говорит «закрываем», «всё», «закончили», «закрой урок» — или после явного завершения задания в текущем lesson-файле.

## Контракт скилла

- **Вход:** существующий `lesson/YYYY-MM-DD.md` в репо `DS-personal-guide` со статусом `status: in_progress`. Активный git remote с правом push.
- **Выход:** lesson-файл с финализированным frontmatter (`status: done|partial|skipped`, `finished_at`, `duration_min`) + git commit + git push. На сервере: webhook triggered → `sync_one_user_to_dt(user_id)` → Neon `digital_twins.data['2_collected']` обновлён + событие в `public.domain_event`.
- **Время:** ≤2 мин (быстрая финализация занятия).
- **Не делает:** не оценивает качество ответа (Оценщик R12 — отдельно, асинхронно); не генерирует assignment на завтра (Портной, ночной рендер).

## Algorithm

## Шаг 1. Вызов через Маршрутизатор

```bash
bash "$IWE_SCRIPTS/route-task.sh" --skill lesson-close --args "<YYYY-MM-DD> [--no-push]"
```

Скрипт `lesson-close.sh` выполняет:
1. Находит lesson/YYYY-MM-DD.md
2. Обновляет frontmatter (`status: done`, `finished_at`, `duration_min`)
3. `git add`, `git commit`, `git push`

**Если `--no-push` передан** — push пропускается.

**Если push fails** — скрипт сообщает ошибку; контур замкнётся при ручном `git push`.

## Шаг 2. Сообщить пилоту о замыкании контура

После успешного выполнения:

```
✅ Урок закрыт.
Статус: <status>
Длительность: <duration_min> мин
Коммит: <short_sha>

Контур замкнут:
  lesson/<date>.md → git push → GitHub webhook → Activity Hub → Neon (ЦД)
  Завтрашний assignment Портной отрендерит на свежих данных.
```

Не нужно ждать или проверять, что webhook реально сработал. Он асинхронный, факт push — единственное, что от тебя требуется. Если интересно: проверь логи `[WorkbookWebhook]` в Railway аист-боте.

## Шаг 3. Граничные случаи

| Ситуация | Действие |
|---|---|
| Урок со статусом `done` уже (повторный close) | Сообщи: «Урок уже закрыт. Если нужно переоткрыть — измени status: in_progress вручную или удали finished_at.» |
| Нет git remote / нет прав push | Сообщи и предложи проверить `git remote -v` и `gh auth status`. Финальный коммит локально остаётся. |
| Merge-conflict при push | НЕ разрешать автоматически. Сообщи пилоту: «Кто-то ещё пушил в этот репо. Проверь: `git pull --rebase` и реши конфликт.» |
| Очень короткое занятие (<3 мин) | Записать как обычно, не оценивать. Поле `duration_min` уйдёт в метрику. |
| Длинное занятие (>60 мин) | То же. Метрика покажет реальное время. |
| Пилот хочет сменить тему текущего занятия задним числом | Не редактируй `theme` или `element_id` — это нарушает соответствие assignment ↔ lesson. Если нужно — открой новый lesson вручную. |

## Связь с другими механизмами

- **Открытие занятия** выполняется отдельным процессом; `/lesson-close` требует уже существующий lesson-файл со статусом `in_progress`.
- **GitHub webhook** — handler в бот-репозитории (`oauth_server.py`, роут `POST /webhook/github/workbook`). HMAC-секрет = `GITHUB_WORKBOOK_WEBHOOK_SECRET` в env. Регистрируется один раз в `DS-personal-guide` репо: Settings → Webhooks.
- **Активность в Neon:** webhook записывает событие в `public.domain_event` (source=`iwe`, event_type=`lesson_closed`) и триггерит `sync_one_user_to_dt(user_id)` → пишет в `digital_twins.data['2_collected']`.
- **Профайлер (асинхронно):** отдельный сервис пересчитывает `3_derived` на основе обновлённого `2_collected`. Запускается отдельным cron.
- **Портной (следующий рендер):** утром скилл `/personal-guide-render` (или ИИ-агент-носитель WP-149 Block D) читает свежий ЦД → новый `daily/<завтра>.md`.

## Антипаттерны

- ❌ Не делать commit без push, если `--no-push` не передан — контур не замкнётся
- ❌ Не редактировать тело lesson-файла (только frontmatter) — ответы пилота неприкосновенны
- ❌ Не пересоздавать lesson при повторном close — выйдет дубликат истории
- ❌ Не разрешать merge-conflict автоматически — может затереть незакоммиченные ответы
- ❌ Не «ждать ответ webhook» — он асинхронный, неблокирующий

<!-- USER-SPACE -->
<!-- /USER-SPACE -->

