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:
- Клиент создаёт incoming item.
- Backend считает checksum, пишет
IncomingItem, ставит job. - Worker вызывает AI-provider, валидирует ответ Zod/JSON Schema, создаёт
TaskDraft. - Актор подтверждает черновик.
- В одной транзакции:
Task,TaskEvent,WorkflowRun,outbox_event. - Worker исполняет handlers и пишет
DeliveryLog. - 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_items | Unique (workspace_id, checksum) против повторного ingest. |
task_drafts | Черновик не становится задачей без approve. |
tasks | Optimistic version для идемпотентных delivery keys. |
workflow_runs | Unique idempotency_key. |
outbox_events | Unique ключ; attempts, available_at. |
delivery_log | Канал, recipient, external id, статус доставки. |
calendar_links | Update-in-place по external_event_id. |
audit_log | Actor, 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.
Резервирование
- Ежедневный encrypted
pg_dumpв S3. - Retention: 14–30 daily, 8–12 weekly, 6–12 monthly.
- Ежемесячный restore drill на отдельной базе.
- Версии образов, Prisma migrations, Caddy/Compose без секретов.
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.
Порядок поставки
- Monorepo, Compose, Prisma, auth, workspace isolation.
- Employees, incoming, tasks, event log — путь ingest → task без AI и каналов.
- AI adapter, schema, quote, human confirmation.
- Messenger: bind chat, send, callbacks, idempotent update_id, delivery log.
- BullMQ, outbox, retries, scheduler, summaries.
- Calendar + строгий post-confirm create.
- Observability, backups, restore tests, staging.
Visual editor и универсальные ноды — только после стабилизации реальных сценариев. Первые workflow регистрируются декларативно и через typed handlers, чтобы не строить graph platform до подтверждения ценности контура.