Top-level IR document produced by graph.exportModel().

Realises the engine's contract with the bounded model checker: property-based fuzz alone is statistical, so the engine emits this snapshot for an exhaustive bounded enumeration that aims to lift runtime-detection rows (stale-async, dynamic-dependency cleanup, cycle reachability) into compile-time-equivalent CI gates. Hosts cap the commit log via ExportModelOptions to bound checker workload — the same trade kani and loom make: out-of-bounds programs (more nodes, more commits, longer message chains than the configured bounds) are not proven by the checker and rely on the property-based fuzz suite plus runtime guards.

The shape is closed at two kind constants by construction (iasbuilt/causl#359). What used to be optional resources, conflicts, and msgs arrays — each carrying its own kind discriminator — was the eleven-NodeKind taxonomy §4 spent its budget collapsing. Resource state is already an Input value; conflicts are a derived view over an Input map; the Msg union is an application-checker concern. Adapter packages that need richer model state run their own projector against a public Graph view and emit a sibling document that the checker reads alongside the engine IR — they do not extend CauslModel.

interface CauslModel {
    bridges: readonly IRBridge[];
    commits: readonly IRCommit[];
    events: readonly IREvent[];
    nodes: readonly IRNode[];
    readsTruncated?: boolean;
    schema: 3;
    scopes: readonly IRScope[];
    time: number;
}

Properties

bridges: readonly IRBridge[]

Sanctioned cross-graph dependency declarations. Empty under single-graph IR documents (the common case); populated by a future cross-graph aggregator. EPIC-2's CrossGraphRead pass refuses any cross-graph dep not in this list.

commits: readonly IRCommit[]

Optional commit log, capped by host caller. The checker uses this for replay-based determinism checks.

events: readonly IREvent[]

Lifecycle event stream — the closed six-arm discriminated union EPIC-2's lint passes consume. PR-B1 widens this from PR-A's readonly never[] placeholder; the engine drains its subscriber registry, disposal tombstones, tx-set log, and read-trace map into the array at exportModel() time.

nodes: readonly IRNode[]

Snapshot of every registered node at export time.

readsTruncated?: boolean

Whether the IR's read-set capture was truncated during serialisation. Per the EPIC-1 brutal-critical review's recommendation #4 (#584 A17-4): when the exporter caps a derived's IRRead summary at the K=256 retention bound, downstream consumers (the checker, the bounded enumerator, SARIF dashboards) need to know they're reasoning over a partial picture. A false here is the load-bearing "no truncation occurred — every read of every derived is preserved" claim; a true is the honest acknowledgement that the checker may emit false negatives on rows whose proof requires reads past the cap.

Defaults to false when omitted (older IR documents and adopters that construct CauslModel by hand are treated as complete).

schema: 3

Schema version pinned to CAUSL_MODEL_SCHEMA.

scopes: readonly IRScope[]

Lifecycle scopes referenced by IRSubscribe.scopeId, IRUnsubscribe.scopeId, and IRDispose.scopeId. The exporter always emits at least one default scope per graph ({ id: 'g.<graphId>:default', kind: 'infinite', ... }) so every scopeId resolves. Adopters that need finer scoping pass an explicit scope option to subscribe(); PR-B1 reserves the wire shape and a future PR adds the option.

time: number

Engine GraphTime at export time.