CFDL

Contracts & Packs

Streams are the raw building block; contracts are how models stay readable.

Streams vs contracts

SituationUse
Formal agreement with another party (lease, loan, PPA)Contract
Individual expense/revenue line itemsStream
If in doubtStart with a stream

A stream is explicit about everything:

stream ops.revenue on entity operating.company inflow currency USD {
  schedule every monthly from 2026-01 to 2027-12
  amount = 30000
}

A contract declares business terms and lets the pack's templates expand them into streams at compile time:

use pack "cre" version "0.1.0"

contract cre.lease on entity asset.tower {
  term 2026-07..2031-12
  terms {
    base_rent = 25000
  }
}

What the pack does with it

At compile time the pack's lowering rules fill in schedule and amount expressions from the terms (with validated defaults), producing ordinary streams in the IR — there is no runtime magic, and cfdl compile output shows exactly what was generated. Missing or malformed terms fail compilation with named diagnostics.

Instances

Templates support instances via a dotted id — each gets its own streams:

contract cre.lease_unit.tenant_a on entity asset.tower { ... }
contract cre.lease_unit.tenant_b on entity asset.tower { ... }

Migrating a hand-built model onto a pack

  1. Keep the timeline and entities unchanged.
  2. Add use pack "<id>" version "<ver>".
  3. Replace stream groups with the equivalent contract, one at a time.
  4. Compile with --packs and resolve diagnostics.
  5. Diff the IR/results and confirm the deltas are intended.