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

> Discover how OpenMAIC DSL contract enforcement works at runtime. Learn about zero-dependency validator functions in the @openmaic/dsl package that ensure data integrity before storage.

- Repository: [MAIC/OpenMAIC](https://github.com/THU-MAIC/OpenMAIC)
- Tags: deep-dive
- Published: 2026-09-06

---

**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.

```typescript
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:
- `validateScene` — Lines 10-15 in [`validate.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/validate.ts)
- `checkAction` — Lines 51-64 in [`validate.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/validate.ts)

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:

```typescript
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`](https://github.com/THU-MAIC/OpenMAIC/blob/main/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`](https://github.com/THU-MAIC/OpenMAIC/blob/main/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:

```typescript
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`](https://github.com/THU-MAIC/OpenMAIC/blob/main/http.ts)
- `validateRuntimeRecord(record)` — Line 108 in [`http.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/http.ts)

The PostgreSQL implementation ([`src/runtime/pg.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/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`](https://github.com/THU-MAIC/OpenMAIC/blob/main/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`](https://github.com/THU-MAIC/OpenMAIC/blob/main/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`](https://github.com/THU-MAIC/OpenMAIC/blob/main/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`](https://github.com/THU-MAIC/OpenMAIC/blob/main/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.