Skip to content

Latest commit

 

History

History
802 lines (622 loc) · 76.3 KB

File metadata and controls

802 lines (622 loc) · 76.3 KB

Аудит и документирование API

Что это

Сверка синхронизации между бэкендом Пачки (приватный репозиторий, клонируется локально) и TypeSpec-спецификацией этого репозитория (packages/spec/typespec.tsp).

Триггер — фраза агенту «Проверь API» / «Запусти аудит API».

Точка отсчёта — последняя дата среди файлов apps/docs/content/updates/<YYYY-MM-DD>.md (имя файла = дата). Аудит просматривает изменения публичного API бэкенда начиная с этой даты включительно. Уже отражённое в spec / уже выпущенное агент видит и повторно не вносит.


Процесс аудита

0. Проверка последних изменений бэкенда

Точка отсчёта — последняя дата среди файлов apps/docs/content/updates/<YYYY-MM-DD>.md (имя файла = дата). Аудит просматривает изменения публичного API бэкенда начиная с этой даты включительно. Отдельный чекпоинт по коммиту не ведётся.

  1. Перейти в локальный клон репозитория бэкенда Пачки
  2. Взять последнюю дату из имён файлов apps/docs/content/updates/*.md
  3. Сравнивать ТОЛЬКО с основной (дефолтной) веткой бэкенда, не с HEAD/--all:
    git fetch origin <основная-ветка>
    git log --since="YYYY-MM-DD" origin/<основная-ветка> --oneline
    НЕ использовать git log --all — он показывает коммиты со stage и фича-веток, которых на проде нет. Документация = основная ветка, иначе ставите в spec несуществующее поведение.
  4. Если изменений публичного API с этой даты нет — пропустить, перейти к шагу 1
  5. Если есть — просмотреть изменения через git log/diff --since="YYYY-MM-DD" origin/<основная-ветка>, ограничившись публичным API:
    • контроллеры публичного API бэкенда
    • сериализаторы ответов публичного API и сериализаторы исходящих вебхуков
    • интеракторы/бизнес-логика, вызываемые из публичного API
    • определение роутов бэкенда
  6. Перед записью каждой находки в spec — двойная проверка, что коммит реально в основной ветке (не stage/фича-ветка): git merge-base --is-ancestor <sha> origin/<основная-ветка>
  7. Захват «уже сделанного» — это нормально: если изменение уже отражено в spec / уже вышло в релиз, агент это видит и повторно не вносит. Дублирования не будет, поэтому брать дату с запасом (включительно) безопасно.
  8. Зафиксировать найденные расхождения и учесть их в дальнейших шагах аудита
  9. По возможности — валидировать API-запросом против прода после внесения правок (особенно для новых полей в ответах). Можно попросить у пользователя read-only токен.
  10. По завершении — новые user-facing изменения попадут в новый файл apps/docs/content/updates/<текущая-дата>.md. Эта дата станет точкой отсчёта для следующего аудита. Хранить отдельный коммит/дату сверки не нужно.

1. Проверка эндпоинтов

Сверить определение роутов бэкенда (только публичный API) с интерфейсами в typespec.tsp:

  • Новые эндпоинты в бэкенде → задокументировать в TypeSpec
  • Удалённые эндпоинты → убрать из TypeSpec
  • Обновить Таблицу покрытия API ниже

2. Проверка параметров запросов

Для каждого эндпоинта сверить метод strong-params соответствующего контроллера публичного API с Request-моделями в typespec.tsp:

  • Новые параметры в контроллере → добавить в TypeSpec
  • Удалённые параметры → убрать из TypeSpec
  • Проверить обязательность (? vs required)
  • Обновить Таблицу параметров запросов ниже

3. Проверка полей ответов

Для каждого эндпоинта сверить сериализатор ответа публичного API с response-моделями в typespec.tsp:

  • Новые поля в сериализаторе → добавить в модель
  • Удалённые поля → убрать из модели
  • Nullable: для каждого поля свериться со схемой БД бэкенда И с валидациями модели:
    • Колонка в схеме без NOT NULL (допускает NULL) + нет presence-валидации → type | null в TypeSpec
    • Метод сериализатора явно может вернуть null (например, сериализатор сотрудника, скрывающий персональные данные — email/телефон — для чужих профилей у ботов без соответствующих прав) → type | null
    • Тесты публичного API — если фикстура содержит field: null для штатного сценария → точно nullable
    • Не доверять только схеме на уровне кода: даже у NOT NULL-колонки колбэк перед валидацией может присвоить значение, и тогда поле не nullable в API

3a. Проверка payload исходящих вебхуков

Сверить сериализаторы исходящих вебхуков с моделями *WebhookPayload в typespec.tsp:

Источник payload в бэкенде (по назначению) Модель в TypeSpec
Сериализатор/триггер вебхука сообщения (payload может собираться inline) MessageWebhookPayload
Сериализатор вебхука реакции ReactionWebhookPayload
Обработчик нажатия кнопки (payload собирается inline) ButtonWebhookPayload
Обработчик отправки формы (payload собирается inline) ViewSubmitWebhookPayload
Сериализаторы вебхука членства в чате ChatMemberWebhookPayload
Сериализаторы вебхука членства в компании CompanyMemberWebhookPayload
Сериализаторы вебхука «поделились ссылкой» LinkSharedWebhookPayload

Проверять:

  • Новые поля в payload → добавить в TypeSpec и в WebhookPayloadUnion
  • Удалённые → убрать
  • При добавлении поля учесть TTL хранилищ (например, хранилище сохранённых форм держит view ограниченное время — поле может вернуться null для старых сохранённых view)

Источник истины: коммиты, относящиеся к вебхукам/изменениям API — всегда сверять с сериализаторами исходящих вебхуков и с обработчиками button/view payload в бэкенде.

4. Проверка ошибок API

Для каждого эндпоинта сверить обработку ошибок в контроллерах и интеракторах публичного API с описанием ошибок в typespec.tsp.

Важно: при каждом аудите активно искать новые code-значения в бэкенде. Источники кодов:

  • Интеракторы/бизнес-логика — кастомные хеши ошибок и явные вызовы добавления ошибки
  • Библиотека валидации параметров — автоматические коды required, in, min_length, max_length из деклараций обязательных параметров
  • Контроллеры публичного API — явная сериализация ошибок с {code: ...}
  • Модели и их валидации — валидации на уровне модели
  • Политики доступа — кастомные коды ошибок авторизации
  • Базовый контроллер публичного API — глобальные обработчики (не найдена запись, нет обязательного параметра, отказ авторизации, ошибки валидатора параметров)

Если найден новый code в бэкенде, которого нет в TypeSpec — сразу добавить в enum ValidationErrorCode и обновить таблицы.

Конкретные проверки:

  • HTTP-коды ошибок: Какие статус-коды (400, 401, 402, 403, 404, 409, 410, 422) возвращает бэкенд → все ли объявлены в TypeSpec для данного эндпоинта
  • Модели ошибок: Правильный ли тип используется (ApiError для бизнес-ошибок, OAuthError для 401)
  • Коды валидации (ValidationErrorCode): Какие значения code бэкенд возвращает в errors[].code → все ли есть в enum ValidationErrorCode
  • Коды валидации по эндпоинтам: Для каждого эндпоинта собрать конкретные code-значения, которые он может вернуть (из контроллера, интерактора, модели) → обновить таблицу
  • Обновить Таблицу ошибок по эндпоинтам, Таблицу кодов валидации и Таблицу errors[].code по эндпоинтам ниже

4a. Проверка версий пакетов и changelog'ов

Если в результате аудита появляются изменения в OpenAPI-спецификации, влияющие на пользователей публикуемых пакетов — нужно поднять версии и записи в changelog'ах. Иначе изменения уйдут в публикацию без указания в release notes, и пользователи не узнают о них.

Когда какой пакет требует bump'а

Пакет Файлы версии и changelog Когда обновлять
n8n integrations/n8n/package.json (version), integrations/n8n/CHANGELOG.md (вручную), apps/docs/data/releases.json (product: n8n) Любые изменения в моделях ответа (User, Message, Chat и т.д.), новые/изменённые webhook payloads (Trigger выводит их пользователю), новые/изменённые эндпоинты, изменения в form blocks, изменения в n8n-node build-скриптах
CLI packages/cli/src/data/changelog.json (источник, генерирует CHANGELOG.md), packages/cli/package.json (фактически 0.0.0 — версия публикации берётся из changelog.json), apps/docs/data/releases.json (product: cli) Изменения моделей ответа (новые поля в users/chats/messages и т.д. видны в JSON-выводе), новые/удалённые/переименованные команды, изменения параметров команд, исправления багов
SDK (Go, Python, TS, Kotlin, Swift, C#) sdk/<lang>/package.json, apps/docs/data/releases.json (product: sdk — единая запись на все языки) Любые изменения OpenAPI-спецификации (модели запросов/ответов, эндпоинты, enum'ы, ошибки) — SDK генерируются из OpenAPI
Generator packages/generator/package.json, apps/docs/data/releases.json (product: generator) Изменения шаблонов генерации, новые языки, исправления генератора
Docs (обновления) apps/docs/content/updates/<дата>.md (один файл на дату) Любые user-facing изменения API: новые методы, новые поля моделей, изменения nullable-типов, изменения вебхуков

Перед bump'ом — ОБЯЗАТЕЛЬНО проверить, что реально опубликовано

changelog.json/releases.json/CHANGELOG.md могут содержать ещё не выпущенную версию: отдельный chore(release)-коммит готовит запись заранее, а CI публикует позже. Прежде чем поднимать версию — проверить публикацию для каждого пакета отдельно (статусы независимы):

npm view @pachca/cli version          # CLI
npm view n8n-nodes-pachca version     # n8n
git tag --list 'v*' --sort=-v:refname | head -1   # SDK (CI публикует по тегам)
git tag --list 'n8n-v*' --sort=-v:refname | head -1

Если верхняя запись в changelog новее опубликованного (npm/тег latest < changelog top) — релиз ещё не вышел. Тогда новое изменение вкладывается в эту pending-запись (дату при необходимости сдвинуть на дату изменения), новую версию НЕ создавать: иначе в releases.json/changelog окажется версия, которую gate не сможет опубликовать (она должна быть точным следующим шагом от latest dist-tag — см. docs/releases.md). Если опубликовано — новая версия корректна.

То же правило для обновлений: если файл apps/docs/content/updates/<дата>.md за прошлую дату относится к ещё не выпущенному релизу — дописать в него и при необходимости переименовать на дату релиза (см. updates-format §1, §10), не плодить второй файл-дату.

Чеклист для каждого изменения API

  • Изменения внесены в typespec.tsp и скомпилированы
  • Файл обновления apps/docs/content/updates/<дата>.md (если файл с этой датой уже есть — дописать в него, иначе создать новый)
  • Если затронуты модели ответа → bump n8n + CLI + SDK
  • Если затронуты только webhook payloads → bump n8n
  • Если затронут процесс генерации → bump generator
  • Соответствующие записи в apps/docs/data/releases.json (по продукту) — CI не обновляет этот файл, добавлять вручную
  • Соответствующие записи в */CHANGELOG.md или */changelog.json пакета
  • Версия в package.json пакета поднята согласно semver:
    • + (новый функционал, обратно совместим) → minor
    • ~ (изменение существующего поведения, обратно совместим) → minor
    • - (фикс) → patch
    • Breaking change → major

Файлы с захардкоженными версиями (всегда проверять при bump'е!)

Эти файлы не регенерируются автоматически и часто забываются:

Файл Что обновлять Триггер
integrations/n8n/README.md URL releases/download/n8n-vX.X.X/... (wget install) Каждый bump n8n
integrations/n8n/docs/DEVELOPMENT.md Текст «current: X.X.X» Каждый bump n8n
apps/docs/content/guides/cli.mdx Пример вывода pachca doctor (строка # ✔ CLI v2026.X.X) Каждый bump CLI (исторически часто пропускают)
apps/docs/data/releases.json Новая запись {product, version, date, changes} Каждая публикация любого пакета (CI это НЕ делает)

Файлы, регенерируемые автоматически

Эти файлы перегенерируются при npx turbo build — править руками не нужно:

  • apps/docs/public/updates.md и public/updates/<дата>.md — из content/updates/*.md
  • apps/docs/public/llms-full.txt, llms-en.txt — из content/
  • apps/docs/public/guides/*.md — из content/guides/*.mdx
  • apps/docs/public/skills/* — копии скиллов
  • packages/cli/CHANGELOG.md — из src/data/changelog.json (через patch-manifest.js)
  • apps/docs/sitemap.xml, feed.xml — динамические роуты

После bump'а пакета прогнать npx turbo build и проверить diff этих файлов: если там «зависшая» старая версия — значит регенерация не сработала, нужно дебажить.

Кто задаёт версию пакета: мы вручную в releases.json

Версия больше не считается авто-инкрементом из npm. Её объявляем мы: запись в apps/docs/data/releases.json (для CLI/n8n — продублировать в собственном changelog), а scripts/check-release.mjs валидирует её на публикации. Полные правила и алгоритм шага — docs/releases.md.

Пакет Источник версии Что от нас требуется
n8n releases.json (product n8n) + integrations/n8n/CHANGELOG.md Указать следующую semver-версию (patch+1 / minor+1.0 / major+1.0.0 от latest dist-tag) в обоих местах
CLI releases.json (product cli) + packages/cli/src/data/changelog.json Указать следующую CalVer-версию (ГГГГ.М.патч+1 в том же месяце или .0 в новом) в обоих местах. package.json остаётся 0.0.0 (версию на публикации проставляет CI из changelog)
SDK releases.json (product sdk, одна запись на все языки) Указать следующую semver-версию в releases.json (как у всех; от latest dist-tag @pachca/sdk = 1.0.191.0.20). Per-language package.json не трогать (placeholder 0.0.0). Публикуются все 6 языков вместе. typespec.tsp — источник спеки, не версии SDK
Generator releases.json (product generator) Указать следующую semver-версию

Обязательные правила

  • В тот же аудит добавлять запись в apps/docs/data/releases.json для каждой публикации (CI это не делает) — версия = точный следующий шаг от latest dist-tag.
  • Версия в releases.json и в собственном changelog пакета (CLI/n8n) обязаны совпадать — иначе сборка упадёт на check-changelog-sync.
  • Менять код пакета без записи в changelog (и наоборот) нельзя — сборка упадёт.
  • При каждом bump'е n8n сверять install URL в integrations/n8n/README.md с актуальной версией.
  • Один файл apps/docs/content/updates/<дата>.md на дату; дубликаты дат запрещены.
  • Dev-only фиксы (prebuild-скрипты для локальных E2E, чистка артефактов разработки, фиксы CI/линтеров без эффекта на пользователя) не попадают ни в один changelog — ни в apps/docs/data/releases.json, ни в integrations/<pkg>/CHANGELOG.md, ни в packages/cli/src/data/changelog.json. Контрольный вопрос перед добавлением: «Заметит ли это пользователь, который скачал пакет из npm?» — если нет, не пишем нигде.

Формат версий

Пакет Схема Пример
n8n semver 2.0.6
CLI calver YYYY.M.PATCH 2026.5.0
SDK semver (общий для всех языков) 1.0.14
Generator semver 1.1.3

Стиль текста записей changelog (по продуктам)

Запись описывает изменение на языке поверхности своего продукта, а не сырыми путями API. Не писать (POST /bots), (PUT /bot/webhook) и т.п. в тексте записи — у каждого пакета своя система имён:

Продукт Где Как писать Пример
CLI releases.json `pachca <команда>` — суть + флаги --flag `pachca bots create` — создание бота и получение его `access_token`
CLI packages/cli/src/data/changelog.json поле command = команда; в description суть (без пути), для новой команды — «Новая команда — …» command: "bots create", description: "Новая команда — создание бота…"
SDK releases.json Метод `Русское-название` (`METHOD /path`) либо «поле field» Метод `Саморегистрация вебхука бота` (`PUT /bot/webhook`)
n8n releases.json + integrations/n8n/CHANGELOG.md `Resource`: операция `Create`/`Get Many`/`Update Webhook` + параметры в коде; без сырых API-путей Bot: новая операция `Update Webhook` — саморегистрация вебхука бота

Сырой METHOD /path уместен только у SDK в скобках после названия метода (SDK оперирует методами API напрямую). У CLI поверхность — команды и флаги, у n8n — ресурсы и операции; пути в их записи — стилевая ошибка.

5. Проверка типов аудит-событий

Сверить список event_key в бэкенде с документацией в apps/docs/content/guides/audit-events.mdx:

  1. Найти все вызовы регистрации аудит-события (audit_event(key: '...') / perform_audit_event(key: '...') и аналогичные) в бэкенде — искать во всех директориях бэкенда, не только в коде публичного API: сервисы, интеракторы/бизнес-логика, контроллеры, модели, вспомогательные библиотеки, конфигурация
  2. Сравнить с разделом «Реализованные типы событий» в audit-events.mdx
  3. Новые event_key в бэкенде → добавить в MDX (решить, документировать или игнорировать)
  4. Удалённые event_key → убрать из MDX
  5. Обновить Таблицу типов аудит-событий ниже

Важно: event_key — свободные строки (нет enum), разбросаны по всему бэкенду (не только по коду публичного API). Проверять все вызовы регистрации аудит-события.

5a. Проверка DLP-системы и схем, документируемых только в гайдах

Часть публичного контракта API живёт не в REST-эндпоинтах, а в фичах, которыми пользователь управляет через интерфейс, но структуру и поведение которых мы документируем. Главный пример — DLP-система: правила не создаются через публичный эндпоинт, поэтому шаги 1–4 их не покрывают. При этом структура правила (наборы типов условий и действий, параметры контекста применения, обязательность полей, значения по умолчанию) и порядок обработки — это публичный контракт, который пользователь пишет руками. Такие фичи легко выпадают из аудита — проверять их нужно отдельно при каждом аудите.

Ключевое отличие от обычного эндпоинта: источник истины для этих схем — не typespec.tsp. Схемы лежат в apps/docs/lib/schemas/guides/*.json и рендерятся в гайдах через <SchemaBlock>. Они не генерируются из TypeSpec и в шагах 1–4 не участвуют, поэтому правки при изменении бэкенда могут затронуть не .tsp, а .json-схему и текст .mdx-гайда.

DLP документирован в трёх местах — при изменении бэкенда сверять каждое:

Что меняется в бэкенде Где править в репозитории
Структура правила: наборы типов условий и действий, параметры контекста, поля, их обязательность и дефолты apps/docs/lib/schemas/guides/DlpRule.json (рендерится в гайде через <SchemaBlock>)
Поведение: порядок обработки правил, семантика действий, ограничения движка условий, что видит пользователь apps/docs/content/guides/dlp.mdx (текст, таблицы, примеры)
Состав полей в деталях записи журнала аудита при срабатывании правила модель деталей DLP-события в packages/spec/typespec.tsp + соответствующий event_key (см. §5)

Что сверять с логикой обработки правил в бэкенде:

  • Появились/исчезли типы действий правила → обновить и таблицу действий в гайде, и enum действия в JSON-схеме
  • Появились/исчезли типы условий или их параметры (включая ограничения и значения по умолчанию) → JSON-схема + соответствующий раздел гайда
  • Изменились параметры контекста применения или их допустимые значения → JSON-схема + гайд
  • Изменился порядок или семантика обработки правил → текст гайда
  • Изменился состав полей в деталях аудит-события → модель деталей в TypeSpec (§5)

Тот же подход применять к остальным схемам из каталога apps/docs/lib/schemas/guides/*.json: при изменении соответствующей логики бэкенда сверять и JSON-схему, и текст гайда — они вне TypeSpec и в шагах 1–4 не видны.

6. Внесение изменений

Если найдены расхождения:

  1. Внести правки в typespec.tsp (см. Справочник TypeSpec ниже)
  2. Обновить примеры в @opExample если затронуты
  3. Если найдены новые code в ошибках бэкенда — добавить в enum ValidationErrorCode
  4. Если найдены новые HTTP-коды ошибок — добавить в union ответа операции
  5. Если изменились скоупы токенов (новые скоупы, изменение набора bot-скоупов) — обновить apps/docs/content/guides/authorization.mdx: секции «Скоупы персональных токенов», «Скоупы токенов ботов» и Warning-блок с перечнем недоступных ботам скоупов
  6. Проверить страницу «Модели» (apps/docs/content/api/models.mdx) — страница содержит справочник всех моделей API. Таблицы свойств моделей (<ModelSchema>) генерируются из OpenAPI автоматически, но остальное прописано вручную:
    • Список моделей в <CardGroup> — если появилась новая модель или удалена старая, обновить список карточек (title, href, methods)
    • Списки методов под каждой моделью — если добавлен/удалён/переименован метод, обновить соответствующий список ссылок [Название](METHOD /path)
    • Атрибут methods в <Card> — если у модели изменился набор HTTP-методов (например, добавился DELETE), обновить атрибут
    • Info-блок в начале — если появился новый метод, не возвращающий модель данных, добавить его в список исключений
  7. Добавить файл обновления apps/docs/content/updates/<дата>.md (формат — updates-format)
  8. Запустить cd packages/spec && bun run generate
  9. Запустить bun turbo check
  10. Обновить таблицы в этом документе
  11. Проверить скиллы — если затронуты API-методы, перейти к шагу ниже

6a. Проверка скиллов и workflow

После внесения изменений в TypeSpec/docs — проверить, нужно ли обновить скиллы.

ОБЯЗАТЕЛЬНО оценить необходимость нового сценария. Если изменение вводит неочевидный / многошаговый / ограниченный флоу — обязательные связки параметров, предусловия, порядок вызовов, специфичные ошибки — недостаточно того, что метод попал в endpoints.md: обязательно вынести его в workflow (packages/spec/workflows.ts). Контрольный вопрос: «сможет ли агент выполнить это с первого раза только по описанию метода, или ему нужен пошаговый сценарий с предусловиями и обработкой ошибок?» Если второе — workflow обязателен.

Архитектура генерации скиллов

typespec.tsp → openapi.yaml → generate.ts + config.ts + workflows.ts → SKILL.md + endpoints.md

Автогенерируемые файлы (НИКОГДА не редактировать вручную):

  • skills/*/SKILL.md — генерируется из config + workflows + OpenAPI
  • skills/*/references/endpoints.md — генерируется из OpenAPI (параметры, схемы, примеры curl)
  • skills/pachca-bots/references/webhook-events.md — генерируется из generate.ts (хардкод)
  • apps/docs/public/.well-known/skills/* — копии вышеуказанных файлов

Файлы-источники (редактировать для изменения скиллов):

Файл Что содержит Когда редактировать
apps/docs/scripts/skills/config.ts Определения скиллов: теги, триггеры, описания, ошибки Новый скилл, новый тег OpenAPI, изменение описания, новые специфичные ошибки
packages/spec/workflows.ts Пошаговые сценарии (title, steps, notes, curl) Новый/изменённый workflow, новый параметр влияющий на шаги
apps/docs/scripts/skills/generate.ts Логика генерации, хардкод вебхук-событий Новый тип скилла, изменение формата вывода
packages/spec/typespec.tsp Эндпоинты, параметры, схемы, описания Любые изменения API (отражаются в endpoints.md автоматически)

Команда регенерации: npx turbo build (компилирует TypeSpec И генерирует скиллы)

Привязка эндпоинтов к скиллам

Привязка работает через OpenAPI-теги (@tag в TypeSpec) → config.ts: SKILL_TAG_MAP[].tags + исключения в COMMON_ENDPOINT_MAP.

Скилл Теги в config.ts Доп. маппинг (COMMON_ENDPOINT_MAP) Эндпоинты
pachca-profile Profile /custom_properties → pachca-profile GET /profile, GET/PUT/DELETE /profile/status, GET /custom_properties
pachca-users Users, Group tags CRUD /users, GET/PUT/DELETE /users/{id}/status, CRUD /group_tags, GET /group_tags/{id}/users
pachca-chats Chats, Members /chats/exports → pachca-chats CRUD /chats, archive/unarchive, CRUD members, tags, leave, exports
pachca-messages Messages, Thread, Reactions, Read member /uploads, /direct_url → pachca-messages CRUD /messages, pin, reactions, read_members, threads, uploads
pachca-bots Bots, Link Previews PUT /bots/{id}, POST /messages/{id}/link_previews, GET/DELETE /webhooks/events
pachca-forms Views POST /views/open
pachca-tasks Tasks CRUD /tasks
pachca-search Search GET /search/users, GET /search/chats, GET /search/messages
pachca-security Security GET /audit-events

Что обновлять при разных типах изменений бэкенда

Тип изменения в бэкенде Что обновить Файл
Новый эндпоинт в существующем ресурсе 1) Добавить в TypeSpec (→ попадёт в endpoints.md автоматически). 2) Если нужен новый workflow — добавить в workflows.ts. 3) Если тег эндпоинта не совпадает с существующим — добавить в COMMON_ENDPOINT_MAP typespec.tsp, workflows.ts, config.ts
Новый ресурс (совсем новый API) 1) Добавить в TypeSpec с @tag("NewTag"). 2) Решить: новый скилл или расширить существующий. 3) Добавить скилл в SKILL_TAG_MAP (config.ts): name, tags, description, triggers, negativeTriggers. 4) Добавить workflows в workflows.ts. 5) Обновить CLAUDE.md — таблицу скиллов typespec.tsp, config.ts, workflows.ts, CLAUDE.md
Удалённый эндпоинт 1) Убрать из TypeSpec. 2) Удалить workflow, если он полностью завязан на этот эндпоинт. 3) Обновить шаги в других workflow, если они ссылались на этот эндпоинт typespec.tsp, workflows.ts
Новый параметр запроса 1) Добавить в TypeSpec (→ endpoints.md обновится). 2) Если параметр важный для сценариев — обновить steps/notes в workflows.ts (например, новый фильтр) typespec.tsp, workflows.ts
Удалённый параметр 1) Убрать из TypeSpec. 2) Убрать из шагов/notes в workflows.ts, если упоминается typespec.tsp, workflows.ts
Новое поле в ответе 1) Добавить в TypeSpec (→ endpoints.md обновится). 2) Если поле полезно для workflow — обновить notes typespec.tsp, workflows.ts
Новый код ошибки (специфичный для скилла) 1) Добавить в enum ValidationErrorCode (TypeSpec). 2) Если ошибка важна для пользователя — добавить в config.ts → errors[] соответствующего скилла (отображается в секции «Ограничения и gotchas» SKILL.md) typespec.tsp, config.ts
Изменение скоупа/тарифа 1) Обновить в TypeSpec. 2) Если скилл стал botOnly или перестал — обновить botOnly в config.ts typespec.tsp, config.ts
Новый тип вебхук-события 1) Обновить хардкод в generate.ts (функция generateWebhookEventsMd). 2) Если нужен новый workflow — добавить в workflows.ts для pachca-bots generate.ts, workflows.ts
Изменение описания/триггеров скилла Обновить в config.ts: description, triggers, negativeTriggers, nearestAlternatives config.ts

Секции SKILL.md и откуда они берутся

Понимание структуры SKILL.md помогает определить, какой файл редактировать:

Секция в SKILL.md Источник данных Файл-источник
Frontmatter (name, description) config.ts → SKILL_TAG_MAP config.ts
Заголовок (Base URL, авторизация, botOnly) config.tsbotOnly + хардкод config.ts
«Когда НЕ использовать» config.tsnegativeTriggers, nearestAlternatives config.ts
«Пошаговые сценарии» workflows.ts → WORKFLOWS[skillName] workflows.ts
«Ограничения и gotchas» OpenAPI-схемы (enum, maxLength, maximum) + config.tserrors[] typespec.tsp, config.ts
Таблица эндпоинтов OpenAPI-эндпоинты (метод, путь, скоуп, тариф) typespec.tsp
Ссылка на references/endpoints.md Автогенерация
Секция в references/endpoints.md Источник данных Файл-источник
Описание эндпоинта @doc() в TypeSpec typespec.tsp
Параметры (query, path) Параметры операции в TypeSpec typespec.tsp
Тело запроса (schema) Request-модель в TypeSpec (рекурсивно до глубины 3) typespec.tsp
Пример curl Автогенерация из @opExample typespec.tsp
Ответ (schema) Response-модель в TypeSpec (рекурсивно до глубины 3) typespec.tsp

Чеклист обновления скиллов

  • Определить затронутые скиллы по таблице привязки выше
  • Оценить необходимость нового сценария (см. callout в начале §6a): изменение вводит неочевидный/многошаговый/ограниченный флоу (обязательные связки параметров, предусловия, порядок вызовов, специфичные ошибки)? Если да — workflow обязателен, не ограничиваться endpoints.md
  • Если новый эндпоинт с новым тегом — добавить тег в config.tsSKILL_TAG_MAP[].tags или в COMMON_ENDPOINT_MAP
  • Если нужен новый workflow — добавить в packages/spec/workflows.tsWORKFLOWS[skillName]
  • Если изменились шаги существующего workflow — обновить steps[] и notes в workflows.ts
  • Если появилась специфичная ошибка — добавить в config.tserrors[] соответствующего скилла
  • Запустить npx turbo build для регенерации
  • Источник истины: packages/spec/openapi.yaml (генерируется из typespec.tsp)

Валидация workflows против спецификации

ОБЯЗАТЕЛЬНО при каждом аудите — проверить все workflows.ts на соответствие openapi.yaml/typespec.tsp. Нельзя придумывать параметры, методы или значения enum'ов.

Что проверять в steps[], notes и curl каждого workflow:

  • Названия query-параметров совпадают с реальными в спецификации (пример ошибки: created_at[from] вместо start_time)
  • Значения enum'ов совпадают с реальными (пример ошибки: user_signed_in вместо user_login)
  • Curl-примеры используют правильные параметры и пути
  • Имена полей в body совпадают с request-моделями в TypeSpec
  • Лимиты и дефолты (limit до 50/100/200/300) совпадают с @maxValue в спецификации
  • HTTP-методы в шагах (GET, POST, PUT, DELETE) совпадают с реальными
  • Пути эндпоинтов корректны (например, /audit_events, не /audit-events)

Как проверять:

  1. Открыть workflows.ts и для каждого скилла пройтись по всем steps[], notes и curl
  2. Каждый упомянутый параметр, значение или путь — сверить с определением в typespec.tsp (или openapi.yaml)
  3. Исправления вносить только в workflows.ts — SKILL.md генерируется автоматически

Референсы с рынка

При создании новых workflow можно ориентироваться на типовые агентские и automation-сценарии, распространённые на рынке dev-инструментов — но только как источник идей для workflow (какие задачи агент может решать). Реализовывать только то, что реально поддерживает Пачка API: если сценарий требует параметров, которых нет в нашем API — не добавлять.

Обновить таблицу

Обновить Таблицу скиллов и workflow в конце этого документа

6b. Формат записи обновления

Запись обновления — это отдельный файл apps/docs/content/updates/<дата>.md с YAML-frontmatter (date, title) и markdown-телом. Полные правила (структура, ссылки на методы, поведенческие нюансы в гайд, якоря-транслит, даты в <Info>/<Warning>) — в updates-format. Кратко: один файл на дату, title в frontmatter (не ## в теле), ссылки на методы [Название](METHOD /path).

7. Отчёт

Вывести краткий отчёт:

  • Что нового найдено
  • Что задокументировано
  • Что игнорируется
  • Текущее покрытие

Область аудита — что проверять, что нет

Проверять (только публичный API)

Что Где в бэкенде (по роли)
Контроллеры Контроллеры публичного API — ТОЛЬКО публичный namespace
Сериализаторы Только сериализаторы ответов публичного API
Роуты Определение роутов бэкенда → только публичный namespace
Интеракторы/бизнес-логика Только вызываемые из контроллеров публичного API (проверять по вызовам)
Модели Только валидации, затрагивающие поля публичного API
Базовый контроллер Базовый контроллер публичного API — глобальные обработчики ошибок
Error codes Только коды, реально попадающие в ответы эндпоинтов публичного API

НЕ проверять (игнорировать)

Что Причина
Внутренний (непубличный) API Не документируется
Старый внутренний API Старая версия, не документируется
Внутренние (не относящиеся к публичному API) сериализаторы Используются только внутренним API
Интеракторы только для внутреннего API Не вызываются из публичного API
Push-уведомления Error codes в push-уведомлениях — НЕ API-ответы
Внутренние коды ошибок непубличного API Не документируются
Внутренние, бета- и служебные эндпоинты Не входят в публичный API; в область аудита не входят и не документируются

Игнорируемые элементы

Не помечать как расхождения:

Элемент Причина
GET /files/{id} Не публичный эндпоинт — нигде не возвращаются ID файлов, пользователи не смогут воспользоваться
Внутренние, бета- и служебные эндпоинты Не входят в публичный API; в область аудита не входят и не документируются
POST /messages — uuid Подставляется автоматически (генерируется случайно на бэкенде)
POST /messages — created_at Внутренний параметр, не для публичного API (создание сообщений в прошлое)
POST /messages — files[].blurhash, files[].thumbhash, files[].thumb_key Внутренние параметры файлов
PUT /messages — files[].id Не документируется
POST /messages — 409 Conflict Детектор дублей запросов (короткий TTL) — из публичного API маловероятен, т.к. uuid каждый раз новый
Message — display_avatar_url/display_name conditional Сериализатор включает эти поля только для ботов. Текущая документация: string | null (всегда). Решено: бэкенд поправит, чтобы поля были всегда
UserStatus.away_message — markup Поле markup (массив rich text разметки) есть только в непубличном сериализаторе. Сериализатор публичного API не включает markup
SearchController — search[query] Альтернативный вложенный параметр (legacy), не документируем

Справочник TypeSpec

Архитектура

Бэкенд → TypeSpec (packages/spec/typespec.tsp) → OpenAPI YAML → Next.js docs (автоматически).

Всё описание API находится в одном файле: packages/spec/typespec.tsp. После внесения изменений компилятор генерирует openapi.yaml, а docs-сайт автоматически подхватывает новые страницы, навигацию и поиск.

Где что искать в бэкенде

Что искать Где в бэкенде (по роли)
HTTP-метод, путь, параметры Определение роутов бэкенда и контроллер публичного API
Strong params (что принимает) Метод strong-params соответствующего контроллера
Обязательность полей Валидации в модели и в интеракторе/бизнес-логике
Поля ответа Сериализатор ответа публичного API
Пагинация (page/per или limit/cursor) Контроллер — смотреть, какой механизм пагинации используется (постраничный или курсорный пагинатор)
Коды ошибок (ValidationErrorCode) Контроллер, интерактор, модель — места добавления/возбуждения ошибок
Структура ошибок (ApiError/OAuthError) Базовые обработчики ошибок публичного API
Примеры запросов/ответов Тесты публичного API

Важно: не доверяй только названиям параметров — проверяй обязательность по валидациям модели и интерактора/бизнес-логики.

Шаблоны-аналоги в TypeSpec

Тип операции Пример-шаблон в typespec.tsp
CRUD (list, get, create, update, delete) UserOperations, GroupTagOperations
Список с page/per пагинацией listChatMessages, listReadMembers
Список с cursor пагинацией listUsers, listChats
Удаление (204 No Content) deleteUser, deleteTag
Вложенные ресурсы (/{id}/sub) ChatMemberOperations

Модели ответов (response)

Добавлять в секцию моделей (~строки 400-800):

@doc("Описание сущности")
model EntityName {
  @doc("Описание поля")
  @example(123)
  id: int32;

  @doc("Nullable поле")
  @example(utcDateTime.fromISO("2020-01-01T00:00:00.000Z"))
  some_date: utcDateTime | null;

  @doc("Enum поле")
  @example("value1")
  @extension("x-enum-descriptions", #{
    value1: "Описание значения 1",
    value2: "Описание значения 2"
  })
  status: "value1" | "value2";
}

Правила:

  • Nullable поля: type | null
  • Enum-описания через @extension("x-enum-descriptions", ...)
  • Каждое поле — @doc() + @example()
  • Склонения описаний enum должны согласовываться с сущностью (напоминание → «Выполнено», задача → «Выполнена»)
  • Точки в @doc поля — всё или ничего. Однострочное описание-фрагмент пишем без завершающей точки (как большинство полей: «Идентификатор сообщения»). Но если описание из двух и более предложений, точка должна стоять в конце каждого предложения, включая последнее. Типичная ошибка (возникает постоянно): первое предложение с точкой, последнее — без («…в текст. null, пока не готова» → правильно «…в текст. null, пока не готова**.**»). То же — в EN-переводе в overlay.en.yaml.

Модели запросов (request)

Добавлять рядом с другими Request-моделями (~строки 1000-1400):

@doc("Запрос на создание/обновление сущности")
model EntityCreateRequest {
  @doc("Объект параметров")
  entity: {
    @doc("Обязательное поле")
    field1: string;

    @doc("Опциональное поле")
    field2?: int32;

    @doc("Поле с дефолтом")
    field3?: int32 = 1;
  };
}

Важно:

  • Обязательные поля — без ?
  • Опциональные — с ?
  • Дефолтные значения — = value

Enum'ы

Добавлять в секцию enum'ов (~строки 30-380):

@doc("Описание enum")
@extension("x-enum-descriptions", #{
  value1: "Описание 1",
  value2: "Описание 2",
})
enum EnumName {
  @doc("Описание 1")
  value1: "value1",

  @doc("Описание 2")
  value2: "value2",
}

ОБЯЗАТЕЛЬНО — описание у каждого значения enum. Страница «Возможные значения» в доке рендерится из @extension("x-enum-descriptions", …), а не из @doc на членах enum (member-@doc — декоративный, в x-enum-descriptions не попадает). Поэтому:

  • У каждого значения нового enum должна быть строка в x-enum-descriptions — иначе значение покажется без описания.
  • При добавлении нового значения в существующий enum — дописать его и в x-enum-descriptions (легко забыть: правишь тело enum, а блок @extension выше остаётся со старым набором).
  • Для EN: добавить те же ключи в x-enum-descriptions соответствующего target в packages/spec/overlay.en.yaml (иначе overlay:validate упадёт).

Операции (endpoints)

Добавлять внутри соответствующего interface или создать новый:

@route("/entities")
@tag("TagName")
interface EntityOperations {
  @doc("""
Заголовок

Описание метода. Поддерживается markdown.
""")
  @get
  @route("/{id}")
  @opExample(#{
    parameters: #{
      id: 123
    },
    returnType: #{
      _: 200,
      body: #{
        data: #{ /* пример объекта */ }
      }
    }
  })
  operationName(
    @doc("Описание параметра")
    @path id: int32
  ): {
    @statusCode _: 200;
    @body body: DataResponse<Entity>;
  } | {
    @statusCode _: 401;
    @body body: OAuthError;
  } | {
    @statusCode _: 404;
    @body body: ApiError;
  };
}

Шаблоны типовых операций

Список с page/per:

  listEntities(
    @doc("Количество возвращаемых сущностей за один запрос")
    @query @minValue(1) @maxValue(50)
    per?: int32 = 50,
    @doc("Страница выборки")
    @query page?: int32 = 1
  ): {
    @statusCode _: 200;
    @body body: DataResponse<Entity[]>;
  } | { ... };

Список с cursor:

  listEntities(
    @doc("Количество возвращаемых сущностей за один запрос")
    @query @minValue(1) @maxValue(50)
    limit?: int32 = 50,
    @doc("Курсор для пагинации (из meta.paginate.next_page)")
    @query cursor?: string
  ): {
    @statusCode _: 200;
    @body body: PaginatedDataResponse<Entity[]>;
  } | { ... };

Удаление:

  @delete
  deleteEntity(@doc("Идентификатор") @path id: int32): {
    @statusCode _: 204;
    @body body: EmptyResponse;
  } | {
    @statusCode _: 401;
    @body body: OAuthError;
  } | {
    @statusCode _: 404;
    @body body: ApiError;
  };

Стандартные коды ошибок

Код Когда использовать Тип
400 Невалидный запрос ApiError
401 Не авторизован (токен отсутствует/невалидный) OAuthError
402 Требуется оплата ApiError
403 Недостаточно прав OAuth-скоупа OAuthError
403 Нет доступа (бизнес-логика, политика) ApiError
404 Не найдено ApiError
409 Конфликт ApiError
410 Ресурс удалён ApiError
422 Ошибка валидации ApiError

Важно про 403: Любой эндпоинт может вернуть 403 в формате OAuthError ({"error": "insufficient_scope", "error_description": "..."}) если у токена бота нет нужного скоупа. Дополнительно некоторые эндпоинты (экспорт) могут вернуть 403 в формате ApiError (бизнес-логика запрета). В TypeSpec используется ApiError | OAuthError для таких случаев.

Обёртки ответов

  • Одиночный объект: DataResponse<Entity>
  • Массив (page/per): DataResponse<Entity[]>
  • Массив (cursor): PaginatedDataResponse<Entity[]>
  • Пустой ответ (204): EmptyResponse

Запись в «Последние обновления»

Отдельный файл apps/docs/content/updates/<ГГГГ-ММ-ДД>.md с frontmatter (date, title) и markdown-телом:

---
date: "ГГГГ-ММ-ДД"
title: "Заголовок обновления"
---

Описание изменений.

- [Название метода](HTTP_МЕТОД /путь)
- [Название метода](HTTP_МЕТОД /путь/{id})

Правила:

  • Дата = имя файла и поле date в frontmatter; title — в frontmatter, не ## в теле
  • Ссылки на методы в формате [Текст](МЕТОД /путь) — автоматически становятся кликабельными ссылками на страницы документации
  • Обновления с датой < 7 дней назад получают badge «Новое» на сайте (порог — isNewUpdate() в apps/docs/lib/updates-parser.ts)
  • Допустимые HTTP-методы в ссылках: GET, POST, PUT, DELETE
  • Полные правила — updates-format

Компиляция и проверка

# Компиляция TypeSpec в openapi.yaml
cd packages/spec && bun run generate

# Все проверки (lint, typecheck, knip, prettier)
bun turbo check

# Если prettier ругается:
cd apps/docs && bunx prettier --write <файлы из вывода ошибки>

Чеклист при добавлении нового эндпоинта

  • Изучены контроллер, модель, сериализатор, интерактор/бизнес-логика и тесты в бэкенде
  • Проверена обязательность каждого поля (валидации модели + интерактор)
  • Проверена nullable полей в ответе (сериализатор)
  • Определён тип пагинации (page/per или limit/cursor)
  • Добавлена/обновлена response-модель (если нужно)
  • Добавлена request-модель (для POST/PUT)
  • Проверены все HTTP-коды ошибок бэкенда (400, 401, 403, 404, 409, 410, 422) — все объявлены в TypeSpec
  • Проверены значения code в ошибках бэкенда — все есть в enum ValidationErrorCode
  • Добавлены операции в interface с @doc, @opExample и кодами ошибок
  • Примеры в @opExample содержат все обязательные поля модели
  • Добавлен файл apps/docs/content/updates/<дата>.md со ссылками на новые/обновлённые методы
  • bun run generate проходит без ошибок
  • bun turbo check проходит без ошибок (lint, typecheck, knip, prettier)
  • Новые страницы отображаются на docs-сайте