How OpenMAIC DSL Contract Enforcement Works at Runtime: A Technical Deep Dive

The OpenMAIC DSL contract is enforced at runtime through zero-dependency validator functions in the @openmaic/dsl package that check structural validity, version stamps, and timestamp formats before any data reaches storage.

The OpenMAIC framework defines a domain-specific language (DSL) for structuring educational content—stages, scenes, actions—and a separate runtime envelope for tracking learner sessions and records. This article explains exactly how the OpenMAIC DSL contract enforcement at runtime prevents invalid data from persisting, based on the source code in the THU-MAIC/OpenMAIC repository.


Document-Side Contract Validation

When a course is created, imported, or edited, structural validators ensure the DSL objects conform to the schema.

The core validation logic lives in packages/@openmaic/dsl/src/validate.ts. Functions like validateScene and validateAction walk the object graph, check required fields, verify discriminants, and return a ValidationResult object.

import { validateStage } from '@openmaic/dsl';

// Generated from PPTX import or manual authoring
const stage = await importPptx(fileBuffer);

const validation = validateStage(stage);
if (!validation.valid) {
  console.error('Generated stage violates DSL:', validation.errors);
  // Abort import before any persistence occurs
}

Key source locations:

These validators run in-process with no external dependencies, making them suitable for Node.js, Deno, browsers, or test environments.


Runtime-Side Contract Validation

The runtime layer maintains its own envelope with stricter requirements. Every RuntimeSession and RuntimeRecord must carry:

  • A version stamp (runtimeDslVersion) distinct from document-side dslVersion
  • Valid ISO-8601 timestamps for createdAt and updatedAt
  • Required identity fields (id, learnerKey, stageId)

The validateRuntimeSession and validateRuntimeRecord functions enforce these rules:

import {
  validateRuntimeSession,
  RUNTIME_DSL_VERSION,
} from '@openmaic/dsl';

const session = {
  id: 'sess-123',
  kind: 'chat',
  stageId: 'stage-42',
  learnerKey: 'user-99',
  status: 'active',
  createdAt: '2026-09-06T12:00:00Z',
  updatedAt: '2026-09-06T12:30:00Z',
  runtimeDslVersion: RUNTIME_DSL_VERSION,
};

const result = validateRuntimeSession(session);
if (!result.valid) {
  throw new Error(`Invalid session: ${result.errors.map(e => e.message).join('; ')}`);
}

Source: validateRuntimeSession (lines 53-84) and validateRuntimeRecord (lines 35-73) in validate.ts.


Version and Migration Guards

A critical defense mechanism prevents cross-line data corruption. The runtime contract uses runtimeDslVersion while documents use dslVersion. The runLadder helper in src/runtime.ts (lines 30-45) throws a hard error if these stamps appear on the wrong object type.

This prevents "lift-and-merge" accidents where legacy document data might be incorrectly treated as runtime session data, or vice versa. The validation block at lines 76-98 of validateRuntimeSession implements the stamp check explicitly.


Storage Layer Enforcement

All persistence operations invoke validators before writing. The HTTP-based store in packages/@openmaic/storage/src/runtime/http.ts demonstrates this pattern:

import { validateRuntimeRecord } from '@openmaic/dsl';

async function saveRecord(record) {
  const { valid, errors } = validateRuntimeRecord(record);
  if (!valid) {
    // Returns HTTP 400 before touching the database
    return { status: 400, body: { errors } };
  }
  // Proceed to persistence only after validation passes
}

Concrete call sites:

  • validateRuntimeSession(session) — Line 96 in http.ts
  • validateRuntimeRecord(record) — Line 108 in http.ts

The PostgreSQL implementation (src/runtime/pg.ts) applies identical guards, ensuring consistent enforcement across storage backends.


Zero-Dependency Design Guarantees

The validators are implemented as pure TypeScript/JavaScript functions with no reliance on:

  • Native Date parsing (ISO-8601 strings are validated character-by-character)
  • External schema libraries
  • Runtime type systems

JSON Schema artifacts exist only for non-TypeScript consumers. This design ensures OpenMAIC DSL contract enforcement at runtime functions identically in any JavaScript environment without dependency bloat.


Summary

  • Structural validation via validateStage, validateScene, validateAction in validate.ts guards document-side DSL objects
  • Runtime envelope validation via validateRuntimeSession and validateRuntimeRecord ensures sessions and records carry correct stamps and timestamps
  • Version separation between dslVersion and runtimeDslVersion prevents accidental cross-line data merging
  • Storage-layer guards in HTTP and PostgreSQL implementations reject invalid payloads with 400-series errors before persistence
  • Zero-dependency design enables universal runtime deployment without external requirements

Frequently Asked Questions

What happens if a runtime session has the wrong version stamp?

The validator throws a hard error during validateRuntimeSession execution. Lines 76-98 in validate.ts explicitly check that runtimeDslVersion is present and valid; if a document-side dslVersion appears instead, validation fails with a descriptive error message. The runLadder helper in runtime.ts provides additional cross-line guards.

Can I use the OpenMAIC validators outside of Node.js?

Yes. The @openmaic/dsl package has zero dependencies and uses only pure JavaScript/TypeScript. The validators run identically in browsers, Deno, serverless edge runtimes, and test harnesses without modification or additional packages.

Where does validation occur relative to database writes?

Validation occurs immediately before any persistence operation. In the HTTP storage implementation (http.ts), validateRuntimeSession is called at line 96 and validateRuntimeRecord at line 108—both returning 400-level responses if validation fails, preventing any database interaction with invalid data.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →