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 severityerrorlogger.warn(msg, { err })→ Sentry Issue with severitywarning- No
errfield (orerris 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¶
errMUST be anErrorinstance — never a string, never a plain objecterrorTypeMUST come fromsrc/shared/errorTypes.ts— autocomplete will guide you- Do NOT use
sentryTags—errorTypeis auto-forwarded as a Sentry tag by the transport - Do NOT use
console.log/warn/error— pre-commit hook blocks it - For info-level logs,
errorTypeis 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
Errorsubclasses 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: off—anyis allowednoConsole: warn— use logger, not consoleuseNodejsImportProtocol: warn— usenode:httpnothttpnoUnusedImports/noUnusedVariables: warn— auto-fixable by lint:stagedorganizeImports: 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 viacrypto.randomBytes) - Global setup:
tests/globalSetup.tswrites mongod URI totests/.mongo-uri - Setup files:
tests/mockers/jaduAuth.setup.ts - Test helpers:
tests/testHelpers.ts—beforeAll()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 withTEST_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.