Skip to content

Studio Backend — Agent Context

Architecture

  • Runtime: Node.js 22.x, Express 5.x, TypeScript
  • Database: MongoDB (dual-DB: Studio + Rendarigo), Redis (caching + RBAC)
  • Realtime: Socket.IO (legacy, deprecated for new flows) + JaduSpine/Centrifugo (new)
  • Monitoring: Sentry (errors + logs)
  • Testing: Vitest, MongoMemoryServer (shared instance, unique DB per file)
  • Linting: Biome (format + lint), Lefthook (pre-commit + pre-push)
  • CI: GitHub Actions (test + coverage on PR, build check on staging→main)

Commands

npm run dev              # nodemon + ts-node (auto-reload)
npm run build            # tsc → dist/
npm test                 # vitest run (all tests, ~12s)
npx vitest run tests/path/to/file.test.ts  # single test file
npm run lint             # biome lint (check only)
npm run lint:staged      # biome lint staged files (auto-fix)
npm run format:staged    # biome format staged files (auto-fix)

Git Hooks (Lefthook)

pre-commit (runs on every commit): 1. biome-format-staged — auto-formats + re-stages 2. biome-lint-staged — blocks commit on lint errors 3. no-console-log — blocks console.log (only when CHECK_CONSOLE_LOG=1)

commit-msg: If changing python_eval/ or src/promptTemplates/, message must include PROMPTS_TESTED=true or PROMPTS_TESTED=skip.

pre-push: tsc build must pass.

Project Structure

src/
├── [feature]/                    # Feature modules (router, controller, service, validator, middleware)
├── models/                       # MongoDB models (singleton per collection)
├── shared/                       # Types, constants, helpers, middleware, errorTypes
├── thirdPartyServices/           # External API integrations (B2, ElevenLabs, Replicate, FalAI, etc.)
├── utilityServices/              # Standalone utility services
├── webhooks/                     # Webhook handlers for external service callbacks
├── logger.ts                     # Winston logger with Sentry transports
├── instrument.ts                 # Sentry SDK initialization
├── index.ts                      # App entry point
├── router.ts                     # Main router
├── mongoDBClient.ts              # MongoDB connection management
├── redisClient.ts                # Redis singleton
└── socketService.ts              # Socket.IO singleton (legacy)

Logging System

Overview

Single Winston logger (src/logger.ts) with 3 transports:

Transport What it does Levels
Console Dev: colorized. Prod: JSON all
SentryLogsTransport Sends to Sentry Logs Explorer error, warn, info
SentryIssuesTransport Auto-creates Sentry Issues when err field present error, warn

How Sentry Issues are Created

The SentryIssuesTransport checks for err instanceof Error. If true, it calls Sentry.captureException() with the error object and forwards errorType as a Sentry tag.

  • logger.error(msg, { err }) → Sentry Issue with severity error
  • logger.warn(msg, { err }) → Sentry Issue with severity warning
  • No err field (or err is not an Error) → NO Sentry Issue, log only

Required Fields

Every logger.error() and logger.warn() call MUST include:

logger.error('Descriptive message', {
  err: error,                              // MUST be Error instance
  errorType: ErrorType.SomeSpecificError,  // MUST be from ErrorType
  // ...additional context
});

Rules

  1. err MUST be an Error instance — never a string, never a plain object
  2. errorType MUST come from src/shared/errorTypes.ts — autocomplete will guide you
  3. Do NOT use sentryTagserrorType is auto-forwarded as a Sentry tag by the transport
  4. Do NOT use console.log/warn/error — pre-commit hook blocks it
  5. For info-level logs, errorType is not required (they don't create Issues)

ErrorType System

Error types live in src/shared/errorTypes.ts, grouped by domain:

Group Domain
GuruErrors Agentic guru, LLM chat, GoToVersion
MinimaticsErrors Minimatics pipeline
MiniStoryErrors MiniStory feature
AssetGenerationErrors Asset gen jobs, quality checks
WorkbenchErrors Workbench operations, sequences
ShotImageEditErrors Shot image edit guru
SketchErrors Sketch analysis
PoseMakerErrors PoseMaker
ThirdPartyErrors External services (B2, ElevenLabs, Replicate, FalAI, etc.)
DirectorialPreferenceErrors Directorial preferences
InfraErrors DB, Redis, sockets, unhandled exceptions
SharedErrors Auth, credits, validation, activities, configuration
JaduCutErrors JaduCut editor, voice generation
WebhookErrors Webhook signature/processing

To add a new error type: add it to the appropriate domain group. The merged ErrorType const at the bottom auto-includes all groups.

In dev, passing an unknown errorType string triggers a console.warn — this tells you to register it in the file.

Request Logging

src/shared/middleware/requestLogger.middleware.ts auto-logs every request on completion: - Attaches req.requestId (from x-request-id header or generated UUID) - Attaches req.log — child logger with { requestId, method, path } context - On response finish: logs statusCode + durationMs at appropriate level (5xx=error, 4xx=warn, else=info)

Use req.log in controllers/services when you have access to req for per-request context:

req.log.info('Processing payment', { orderId });

Log Levels

Level When to use
error Operation failed, requires attention. Usually paired with err field
warn Degraded state, retries, fallbacks. May or may not have err
info Significant business events (server started, job completed, config loaded)
debug Developer-only detail (not sent to Sentry, dev-only)

What Reaches Sentry in Production

Destination What gets there
Sentry Logs Explorer error + warn + info (structured log lines)
Sentry Issues Only error/warn WITH err field (actual exceptions)
Console/stdout Everything at or above LOG_LEVEL

debug logs never reach Sentry. In prod, LOG_LEVEL defaults to info.

Code Conventions

  • Feature-based folder structure: [feature].router.ts, [feature].controller.ts, [feature].service.ts, [feature].validator.ts
  • Models are singletons (one class per collection, open/closed principle)
  • camelCase for files, classes, functions, variables
  • Zod for request validation
  • Static service classes for business logic
  • Prefer Error subclasses over generic errors
  • Pre-commit: Biome format → Biome lint → no-console-log check
  • Pre-push: TypeScript build check

Biome Config Gotchas

  • lineWidth: 180 (not default 80)
  • noExplicitAny: offany is allowed
  • noConsole: warn — use logger, not console
  • useNodejsImportProtocol: warn — use node:http not http
  • noUnusedImports / noUnusedVariables: warn — auto-fixable by lint:staged
  • organizeImports: on — imports auto-sorted on save/commit

Testing

  • Framework: Vitest (globals enabled — no need to import describe/it/expect)
  • Shared MongoMemoryServer (one mongod, unique DB name per test file via crypto.randomBytes)
  • Global setup: tests/globalSetup.ts writes mongod URI to tests/.mongo-uri
  • Setup files: tests/mockers/jaduAuth.setup.ts
  • Test helpers: tests/testHelpers.tsbeforeAll() connects + corrupts env, afterAll() drops DB + disconnects
  • Mockers: tests/mockers/ (reusable mock objects)
  • testTimeout: 15000 (15s per test)
  • Env corruption: TestHelpers.corruptEnv() prefixes all env vars with TEST_CORRUPTED_ to prevent real third-party calls

Writing Tests

import TestHelpers from '../testHelpers';

describe('MyFeature', () => {
  beforeAll(() => TestHelpers.beforeAll());
  afterAll(() => TestHelpers.afterAll());

  it('does something', async () => {
    const db = TestHelpers.getDb();
    // ...
  });
});

Environment

Key env vars: - SENTRY_ENVIRONMENT / NODE_ENV — controls log format + Sentry environment - LOG_LEVEL — minimum log level (default: debug dev, info prod) - CENTRIFUGO_API_URL / CENTRIFUGO_API_KEY — JaduSpine realtime - JADU_AUTH_SERVER_URL / JADU_AUTH_APP_ID — JaduAuth token verification - GITHUB_PKG_TOKEN — for private @scenarix/* packages (required for npm install, see .npmrc) - See .env.local for full list

CI

Workflow Trigger Does
test.yml Push to main/staging, PRs Vitest + coverage report on PR
staging_to_main_validation.yml PR staging→main tsc build check
staging-to-main-pr.yml Weekly + manual Auto-creates release PR
sentry_release.yml Sentry release tracking

CI uses Node 24 and requires SCENARIX_GITHUB_PKG_TOKEN secret for private packages.