sanatash
Открыть доступ

Technical contract

Стартовая сборка платформы исполнения сценариев

Контракт описывает ядро sanatash: модульный монолит, transactional outbox, идемпотентные side-effects и изолированные workspace. Сценарий поставляется как отдельный проект поверх этого runtime, а не как граф в визуальном конструкторе.

Назначение

Платформа — event-driven runtime управленческой операционки. Система ингестит факты, порождает workflow run, исполняет зарегистрированные action handlers через интеграции и персистит результат. Первая поставка не является универсальным iPaaS и не содержит visual canvas / graph interpreter.

Референсный контур исполнения: неструктурированный вход → AI-извлечение черновиков → human-in-the-loop confirmation → агрегат задачи → доставка в канал → мутации статуса → периодические сводки. Workflow — исполняемый процесс из триггера, предикатов, шагов обработки и эффектов. Один запуск учитывается как единый workflow_run независимо от числа внутренних handlers.

Границы релиза

In-scope

  • Изолированные workspace и RBAC.
  • Ингест текста, checksum, дедупликация.
  • AI-парсинг в черновики по JSON Schema.
  • Approve / reject черновиков.
  • Реестр задач: assignee, due, priority, status machine.
  • Канал уведомлений с callback-кнопками.
  • Календарные события только после confirmation.
  • Scheduler сводок.
  • BullMQ, retries, dead-letter, audit trail.
  • Logical dump PostgreSQL во внешнее object storage.

Out-of-scope

  • Drag-and-drop editor и произвольные user-defined nodes.
  • Выполнение пользовательского JavaScript в runtime.
  • Маркетплейс интеграций.
  • CRM / воронки / recruiting / marketing automation.
  • Speech-to-text и массовый ingest документов.
  • SSO / SAML / LDAP / MDM.
  • Billing и multi-tenant SaaS для внешних арендаторов.
  • Авторассылка без явного confirmation агрегата.

Внешний iPaaS допустим только как временный стенд прототипирования адаптеров. Он не является execution kernel и не задаёт квоты пользовательских сценариев.

Архитектурный подход

Модульный монолит: один backend deployable с жёсткими доменными границами модулей, без сети микросервисов на первом этапе. HTTP-контроллер не оркестрирует внешние I/O. Контроллер персистит инвариант в PostgreSQL и эмитит outbox-запись; worker выполняет side-effect асинхронно. Это сохраняет write-ahead целостность при рестарте процесса и деградации Telegram / Calendar / AI provider.

Канонический pipeline:

  1. Клиент создаёт incoming item.
  2. Backend считает checksum, пишет IncomingItem, ставит job.
  3. Worker вызывает AI-provider, валидирует ответ Zod/JSON Schema, создаёт TaskDraft.
  4. Актор подтверждает черновик.
  5. В одной транзакции: Task, TaskEvent, WorkflowRun, outbox_event.
  6. Worker исполняет handlers и пишет DeliveryLog.
  7. Scheduler эмитит jobs периодических сводок.

Transactional outbox обязателен для критичных эффектов: мутация агрегата и запись outbox коммитятся атомарно. Poller читает status=pending, исполняет, маркирует processed. Dual-write «сначала задача, потом HTTP в канал» запрещён.

Рекомендуемый стек

Frontend: React, TypeScript, Vite, React Router, TanStack Query, React Hook Form + Zod. Tailwind либо компонентная библиотека для admin surface.

Backend: Node.js LTS, TypeScript, Fastify (Express допустим), Zod, Prisma, PostgreSQL 16+, Redis 7+, BullMQ, Pino, OpenAPI, Vitest.

Адаптеры: Telegram Bot API (Telegraf/grammY), Google Calendar + OAuth 2.0, pluggable AI provider, S3-compatible object storage. SMTP — следующая итерация.

Инфра: Ubuntu 24.04 LTS, Docker Compose, Caddy (TLS), CI (GitHub Actions / GitLab), registry, UFW, Fail2ban, Sentry/GlitchTip, health probes.

Структура репозитория

Monorepo (pnpm workspaces / Turborepo):

operflow/
  apps/web | api | worker
  packages/contracts | config | ui
  prisma/schema.prisma + migrations
  infra/docker | caddy | compose
  docs/architecture.md api.md runbook.md security.md

web — панели workspace и admin. api — HTTP, auth, use-cases, webhook, OpenAPI. worker — AI, delivery, calendar, summaries, retries, backup. contracts — Zod-схемы, DTO, enum, event contracts. config — fail-fast валидация env.

Изоляция данных

Мультиарендность закладывается в схему с первого коммита, даже если эксплуатация идёт в одном organization. Иерархия: Organization → Workspace → Member / Employee / IncomingItem / Task / WorkflowRun. Workspace — изолированный кабинет. Каждая бизнес-таблица несёт organization_id и workspace_id. Фильтрация контекста выполняется на backend; клиентский workspaceId не является источником истины.

Роли: SUPER_ADMIN, ORG_ADMIN, MANAGER (confirmation), ASSISTANT (ingest без approve), EMPLOYEE (собственный статус через канал).

Модель данных

Минимальный набор агрегатов PostgreSQL:

СущностьИнварианты
incoming_itemsUnique (workspace_id, checksum) против повторного ingest.
task_draftsЧерновик не становится задачей без approve.
tasksOptimistic version для идемпотентных delivery keys.
workflow_runsUnique idempotency_key.
outbox_eventsUnique ключ; attempts, available_at.
delivery_logКанал, recipient, external id, статус доставки.
calendar_linksUpdate-in-place по external_event_id.
audit_logActor, action, entity, metadata без секретов.

Полный перечень полей (users, workspace_members, employees, settings, task_events, workflow_definitions) фиксируется в Prisma schema и миграциях; эволюция только через versioned migration, не через hot-alter в runtime.

Workflow runtime первой версии

Не интерпретатор произвольного графа. Event bus с заранее зарегистрированными handlers. События публикуются в outbox: incoming.*, draft.*, task.*, summary.*, notification.failed, backup.requested.

Handlers: extract_tasks_from_text, send_telegram_task, create_google_calendar_event, generate_summary_text, create_backup и аналоги.

interface WorkflowActionHandler<TPayload> {
  type: string;
  execute(context: WorkflowContext, payload: TPayload): Promise<ActionResult>;
}

WorkflowContext содержит идентификаторы, workspace config, trace id, idempotency key. Секреты и сырые HTTP-объекты в контекст не попадают. definition_json может эволюционировать к декларативному trigger → filters → actions до появления canvas: контракт данных первичнее UI.

HTTP API

Минимальный REST: auth (login/refresh/me), workspaces, employees, incoming (+ process), drafts (patch/approve/reject), tasks (complete/reschedule), workflow-runs, delivery-log, audit-log, settings, POST /webhooks/telegram, /health, /ready.

Мутирующие операции принимают Idempotency-Key либо получают серверный operational key. Для approve, create task и внешних эффектов ключ обязателен.

AI-интеграция

LLM применяется только к неструктурированному входу: извлечение задач, противоречий, формулировок, сводок из уже посчитанных агрегатов. Модель не создаёт confirmed tasks, не мутирует due, не резолвит неоднозначного assignee, не отправляет сообщения и не выполняет HTTP от имени системы.

Ответ обязан удовлетворять Zod/JSON Schema. Невалидный payload не частично коммитится: статус ошибки ingest + возможность replay. Сопоставление responsibleName — отдельный детерминированный matcher (нормализованное имя, алиасы); при коллизии assignee остаётся null и уходит в ambiguities. Confirmation срока, приоритета и исполнителя — за человеком.

Каналы: messenger и calendar

На MVP допустим один bot process; изоляция — резолв chat_id → employee → workspace. Webhook: HTTPS, secret header, идемпотентный update_id, быстрый ACK, тяжёлая логика в очередь, unknown chat drop, запрет cross-workspace leak.

Callback payload — короткий подписанный action id, без PII и полного текста задачи. Calendar: событие только после confirmation и при заполненных due / calendar / calendar_required. Изменение due обновляет существующий external_event_id, не создаёт дубль. Domain-wide delegation не требуется; минимальные OAuth scopes, отдельный client, тестовый календарь workspace.

Очереди, ретраи, дедупликация

Очереди: incoming-processing, ai-processing, workflow-execution, telegram-delivery, calendar-sync, summaries, backup, dead-letter.

Exponential backoff на transient errors: immediate → 1m → 5m → 20m → 60m → failed + alert. Не ретраить: validation, unknown chat, OAuth scope mismatch, missing calendar, AI schema error, действие без confirmation.

Идемпотентные ключи вида:

telegram:task-assigned:{taskId}:{employeeId}:v{taskVersion}
calendar:create:{taskId}:{dueAt}
summary:morning:{workspaceId}:{date}
ai:extract:{incomingItemId}:{checksum}

Unique index обязателен. Повторный worker, увидев successful delivery, завершается no-op.

Безопасность

Секреты — только env / CI vault. Запрещены Git, UI, логи, таблицы, клиентский бандл. Google refresh tokens шифруются отдельным ключом. PostgreSQL и Redis не публикуются наружу; ingress только 80/443 через Caddy; SSH — ключи, без пароля.

Логи: request/trace id, без API keys, OAuth, полного текста чувствительных документов. Retention сырого ingest настраивается на workspace: хранить ровно столько, сколько нужно для аудита и жизни задач.

Обязательные переменные включают DATABASE_URL, REDIS_URL, JWT secrets, Telegram/Google/AI/S3 credentials, BACKUP_ENCRYPTION_KEY, SENTRY_DSN.

Инфраструктура

Compose: caddy, web, api, worker, scheduler, postgres, redis, опционально backup. Restart unless-stopped. Named volume Postgres + внешний backup: volume на том же VPS не считается копией.

Пилотный capacity envelope: 4 vCPU, 8 GB RAM, 80–120 GB NVMe, отдельный S3 20–100 GB, один IPv4, 80/443.

Резервирование

WAL/PITR — следующий этап. Бэкап внешних офисных дисков в MVP не входит.

CI/CD и окружения

Local / Staging / Production. Pipeline: install → tsc → lint → unit → build → prisma generate → migration check → image → staging smoke (/health, auth, webhook) → ручной promote в production.

Миграции — отдельный шаг до старта новой ревизии api/worker. Автоматический rollback Prisma в production без data-migration плана запрещён.

Верификация

Unit: status machine, RBAC, idempotency key, due/reminder rules, name matcher, AI schema, workspace filter, message templates.

Integration: ingest + drafts, duplicate checksum, approve, outbox в той же транзакции, replay, Telegram failure/retry, calendar upsert, callback, summaries, cross-workspace deny.

Pre-pilot e2e: демонстрационный ingest → правки черновика → два approve → доставка нужным recipient → callback статусов → сверка панели → calendar event → ручной прогон сводок → повтор без дублей → restore dump.

Порядок поставки

  1. Monorepo, Compose, Prisma, auth, workspace isolation.
  2. Employees, incoming, tasks, event log — путь ingest → task без AI и каналов.
  3. AI adapter, schema, quote, human confirmation.
  4. Messenger: bind chat, send, callbacks, idempotent update_id, delivery log.
  5. BullMQ, outbox, retries, scheduler, summaries.
  6. Calendar + строгий post-confirm create.
  7. Observability, backups, restore tests, staging.

Visual editor и универсальные ноды — только после стабилизации реальных сценариев. Первые workflow регистрируются декларативно и через typed handlers, чтобы не строить graph platform до подтверждения ценности контура.