Timelinx

Invariants

Whole-state validation rules that protect committed timeline state

Invariants are whole-document checks that run after a transaction has produced a proposed state. They catch errors that may not be obvious while validating a single operation.

Violation Categories

AreaExamples
Identity and schemaSCHEMA_VERSION_MISMATCH and DUPLICATE_ID protect migrations and stable references.
Track structureTRACK_NOT_SORTED, OVERLAP, TRACK_TYPE_MISMATCH, INVALID_OPACITY, link group references, and track group references.
Clip timeMEDIA_BOUNDS_INVALID, DURATION_MISMATCH, CLIP_BEYOND_TIMELINE, SPEED_INVALID, and INVALID_RANGE.
Editorial metadataMARKER_OUT_OF_BOUNDS, IN_OUT_INVALID, BEAT_GRID_INVALID, CAPTION_OUT_OF_BOUNDS, and CAPTION_OVERLAP.
EffectsEFFECT_NOT_FOUND, KEYFRAME_NOT_FOUND, KEYFRAME_ORDER_VIOLATION, EFFECT_INDEX_OUT_OF_RANGE, and INVALID_RENDER_STAGE.

Why Invariants Exist

Per-operation validation answers “can this operation apply right now?” Invariants answer “is the resulting document still a valid Timelinx timeline?” Both are needed.

Examples:

  • An INSERT_CLIP validator can reject a clip that overlaps existing clips on the target track.
  • The invariant checker can still detect a malformed imported project where two clips already overlap.
  • A SET_TIMELINE_DURATION validator can reject shrinking the timeline below an existing clip.
  • The invariant checker can still catch a state object loaded from storage with a clip beyond the timeline duration.

Important Checks

The checker currently protects:

  • State shape and schema version.
  • Duplicate track, clip, marker, and asset IDs.
  • Track clip sort order.
  • No overlapping clips on the same track.
  • Clip trackId consistency with the containing track.
  • Finite, integer, non-negative frame values.
  • Nonzero clip duration and valid timeline bounds.
  • Referenced assets exist in assetRegistry.
  • Asset media type matches track type.
  • Media bounds stay inside the referenced asset duration.
  • Timeline duration, clip duration, media duration, and speed remain coherent.
  • Markers, captions, beat grids, in/out points, link groups, track groups, effects, keyframes, render stages, ranges, and opacity stay structurally valid.

Rejection Surface

When invariant checks fail through dispatch(), the dispatcher returns:

{
  accepted: false,
  reason: 'INVARIANT_VIOLATED',
  message: '...collected violation messages...'
}

Use checkInvariants(state) directly in tests, import pipelines, migrations, or debugging utilities when you need the individual InvariantViolation[] records.

import { checkInvariants } from '@timelinx/core';

const violations = checkInvariants(importedState);

if (violations.length > 0) {
  throw new Error(
    violations.map((violation) => violation.message).join('\n'),
  );
}

What Invariants Do Not Check

Invariants protect the timeline model. They do not verify everything a product might care about:

  • Whether a file path exists on disk.
  • Whether remote media is reachable.
  • Whether a clip looks or sounds creatively correct.
  • Whether audio loudness meets delivery standards.
  • Whether your app has permission to read a local file.
  • Whether an export preset is acceptable for a distributor.

Those checks belong in the application, import/export, media, or delivery layers. Keep them separate so the core remains deterministic and testable.

In tests that create or transform raw state, run checkInvariants() after the mutation. In production code, prefer dispatch() so the invariant pass is automatic.

On this page