Reading results and IR
cfdl compile produces the IR, the canonical compiled model. cfdl run
evaluates the IR and produces the results. Both are JSON documents with
published schemas.
A small model and its results
version 0.1
model "reading-results"
time calendar monthly from 2026-01 for 12
assume monthly_fee : decimal = 4000
source { publisher "Signed engagement letter" as_of 2025-12-15 }
entity asset studio : Asset.Intangible {
seats = 6
}
stream studio.fees on entity asset.studio inflow currency USD {
schedule every month from 2026-01 to 2026-12
amount = inputs.monthly_fee
}
stream studio.rent on entity asset.studio outflow currency USD {
schedule every month from 2026-01 to 2026-12
amount = 1500
}Run at an 8% discount rate
({ "deterministic": { "annual_discount_rate": 0.08 } }), it produces this
document, trimmed where … stands:
{
"results_version": "0.18",
"model_hash": "3f5e5867a9fe…",
"ledger_hash": "150154d149ec…",
"engine": { "name": "cfdl-engine", "version": "0.14.0" },
"warnings": [],
"run": { "deterministic": { "annual_discount_rate": 0.08 } },
"inputs": {
"assumptions": [
{ "name": "monthly_fee", "shape": "value", "type": "Decimal",
"value": 4000.0, "origin": "model",
"source": { "as_of": "2025-12-15", "publisher": "Signed engagement letter" } }
]
},
"deterministic": {
"status": "ok",
"metrics": {
"model.npv": { "amount": 28782.144631, "currency": "USD" },
"model.total": { "amount": 30000.0, "currency": "USD" },
"model.wal_years": 0.541667,
"run.annual_discount_rate": 0.08,
"stream.studio.fees.total": { "amount": 48000.0, "currency": "USD" },
…
},
"metric_units": { "model.npv": "money", "model.wal_years": "years", … },
"series": {
"stream.studio.fees": {
"index": { "calendar": "monthly", "start": "2026-01-01", "periods": 12 },
"offset": 1.0,
"entity": "asset.studio",
"values": [ { "amount": 4000.0, "currency": "USD" }, … ]
},
…
},
"annual_rollup": { … }
},
"scenarios": { "status": "not_run", "summaries": [] },
"monte_carlo": { "status": "not_run", … },
"statements": { "statements": [ { "id": "by_entity", "label": "Cash by entity", "default": true, … } ] },
"charts": { "charts": [ { "id": "cash", "label": "Cash flow", "kind": "columns", "default": true, "origin": "default", … } ] },
"graph": {
"entities": [
{ "symbol": "asset.studio", "family": "asset", "type": "Asset.Intangible",
"fields": { "seats": { "value": 6 } } }
]
}
}The net cash flow never turns negative, so the run publishes no model.irr
and no payback.
The results, section by section
results_versionstates the document's shape: 0.18.- Provenance.
model_hashidentifies the model: a hash of the compiled IR without itsviews(slices and statements), so two users who look at identical results differently share a model hash. It also leaves out where anything is written: a comment, a blank line, spacing inside an expression, or a declaration moved to another file leaves it as it was. A declaredmetricis a figure the model claims, so it does movemodel_hash.ledger_hashcovers what happened: the series, the journal and the transitions.enginenames the engine and its version. The run configuration is in neither hash; the results publish it asrun, the configuration as the run used it, with each inputs file's content in place of its path. Each scenario and the Monte Carlo section carry their ownledger_hash. Store all of it next to any number you publish, or package it:cfdl packagewrites a manifest of the files' checksums and every hash, whichcfdl verifychecks without running the model andcfdl reproducechecks by running it. warningslists what the run noticed and did not refuse. Read it before the figures.runis the run configuration as the run used it: the discount rate or curve, the as-of date, the parameters, each inputs file's content, each scenario and the Monte Carlo trials.inputs.assumptionsis the review page: one row per assumption, a set's fields each on their own row, with its shape, type, unit, value, origin (the model's statement, the run's override, or the inputs file) and the source it cited: the model's, or the inputs file's; a value the run overrode cites none.deterministic.metricsis a flat map of the engine's metrics and the model's declared ones (metric.*); money metrics carryamountandcurrency.model.npvappears only when the run states a discount rate or a discount series. See Metrics.domain_metricsis a top-level section: the active pack's metrics, each with its lineage. It is present when a pack computed them.deterministic.seriesholds every stream per period, each attributed to its owningentityand itscategory, with its placementoffsetandcash, which isfalsefor a stream the totals leave out. An option's payoff names the entity the option is written on. A series lowered from a contract also names itscontractand itsline, and a stream that moves an account names it inmoves. Beside the streams:model.net_cash_flow, per-entity nets, each account's balance as the non-cash seriesaccount.<name>, each waterfall step's shortfall asshortfall.<waterfall>.<step>, entity fields, the pack'sdomain.<pack>.*subtotals, and a contract's valuation figures asmetric.<contract>.<figure>.deterministic.annual_rollupregroups the series by calendar year.deterministic.journalanddeterministic.transitionsrecord every causal act, with an act's own consequences nested under it aschildren, and every lifecycle transition. An event's firing (fire) and a transition carry the values their condition read asreads; an option's exercise carries its payoffamountand theentityit is credited to. A transition of a refined machine names itsmasterstate, and one that re-entered a state by history is markedresumed.deterministic.tracesays why every period is what it is, as runs of onereason. For each entity with a lifecycle, every period's evaluation: the state, the guards tested and what each read, and whether itmoved,held, or had its statesetby an action. For each event and option, its test each period. For each stream,ranor the gate that stopped it, such asafter_schedule,out_of_stateorinactive. For each field, its source. A reason an act caused names the act's position in the journal asentry.statementsholds every rendered statement, each ending in a line that reconciles to the model's cash. A model that declares none, with no pack, gets its entity hierarchy, marked"default": true.slicesholds each declared slice's selection, matched streams, net series and figures.chartsholds each chart the pack or the run configuration declares, or the defaults when neither declares one, resolved against the run: the series keys each draws, with its role, unit and fold. A model with no pack and no chart gets its cash by category, marked"default": true. What each metric measures isdeterministic.metric_units:money,rate,ratio,multiple,years,countornumber.graphis the model's structure:graph.entities, each with its symbol, family, type,part ofparent and the fields it states, andgraph.contracts, each with its type, parties, terms and the streams lowered from it.scenariosholds one summary per scenario. Each carries its metrics, its series and annual rollup, its journal, transitions and trace, its slices and its statements. A declared metric the scenario could not evaluate is listed underomittedwith the reason, not published as a number.monte_carloholds the trial count and seed;metrics, each metric's summary across trials (mean, stdev, min/max, percentiles p01 to p99, andtrials, the count of trials that published it);aggregates, the NPV's mean, median, standard deviation andp_negative, the share of trials with a negative NPV; the trial summaries, each with its metrics and its ownomitted; ajournalgiving the share of trials in which each act occurred; and atracegiving the same for each actor's reasons, such as a lifecycle moving intodefault.
The Python SDK exposes the series as
results.cashflows() (wide, PeriodIndex) and results.annual().
IR anatomy
The IR is the canonical compiled model, deterministically ordered with stable
IDs. Its sections include the model's time and phases, entities,
assumptions (series and quantiles among them, as shapes of assumption),
contracts, streams (with lowered schedule and expression slots),
events, options, lifecycles, subtotals, views (slices and
statements), runs, required_refs (what the model needs from outside)
and provenance. Useful habits:
- Commit it: IR diffs show exactly what a model change did.
- Inspect pack lowering: contracts appear as the streams they expanded into.
Schemas
Machine-readable schemas are published here: IR schema · Results schema.