IR schema
The shape of a cfdl compile IR document. This is the published contract,
also served at cfdl.dev/schemas; every committed IR golden is validated
against it by make ir-schema.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://cfdl.dev/schemas/CFDL_v0_1_IR.schema.json",
"title": "CFDL v0.1 Canonical IR",
"type": "object",
"additionalProperties": false,
"required": [
"ir_version",
"model",
"time",
"phases",
"entities",
"assumptions",
"contracts",
"streams",
"events",
"options",
"runs",
"required_refs",
"provenance"
],
"properties": {
"ir_version": {
"type": "string",
"const": "0.1"
},
"model": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"currency"
],
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"currency": {
"$ref": "#/$defs/Currency"
}
}
},
"time": {
"type": "object",
"additionalProperties": false,
"required": [
"calendar",
"start",
"periods"
],
"properties": {
"calendar": {
"$ref": "#/$defs/Frequency"
},
"start": {
"$ref": "#/$defs/Date"
},
"periods": {
"type": "integer",
"minimum": 1
},
"projection": {
"type": "integer",
"minimum": 0
}
}
},
"phases": {
"type": "array",
"minItems": 0,
"items": {
"$ref": "#/$defs/Phase"
}
},
"entities": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/Entity"
}
},
"assumptions": {
"$ref": "#/$defs/Assumptions"
},
"quantile_inputs": {
"type": "array",
"minItems": 0,
"items": {
"$ref": "#/$defs/QuantileCall"
},
"description": "Every quantile call site in the document, resolved and deduplicated. The audit record for a nonlinear input: publishing the declaration alone would say a distribution existed, not which slice of it struck a number."
},
"accounts": {
"type": "array",
"items": {
"$ref": "#/$defs/Account"
},
"description": "Declared cash locations. Omitted when a model declares none, so existing IR stays byte-identical."
},
"lifecycles": {
"type": "array",
"items": {
"$ref": "#/$defs/Lifecycle"
},
"description": "Every machine an entity binds — pack-declared and model-declared resolved to the same shape. Absent when no entity has one."
},
"metrics": {
"type": "array",
"minItems": 0,
"items": {
"$ref": "#/$defs/Metric"
},
"description": "Reserved. Metrics are computed at run time by the engine and by the active pack, so a compile output does not carry them; no compiler emits this field."
},
"waterfalls": {
"type": "array",
"minItems": 0,
"description": "Ordered allocations of a pot — a priority of payments. Steps run in declaration order after the period's fields and streams are known; each takes min(max(0, its amount), what remains). Omitted when a model declares none.",
"items": {
"$ref": "#/$defs/Waterfall"
}
},
"subtotals": {
"type": "array",
"items": {
"$ref": "#/$defs/Subtotal"
},
"description": "Per-period subtotals declared by the active pack, in dependency order. Omitted when the pack declares none."
},
"contracts": {
"type": "array",
"minItems": 0,
"items": {
"$ref": "#/$defs/Contract"
}
},
"streams": {
"type": "array",
"minItems": 0,
"items": {
"$ref": "#/$defs/Stream"
}
},
"stream_inputs": {
"type": "array",
"items": {
"$ref": "#/$defs/StreamInputs"
},
"description": "Per-stream record of what each pack rule consumed. Omitted when nothing was lowered from a pack."
},
"events": {
"type": "array",
"minItems": 0,
"items": {
"$ref": "#/$defs/Event"
}
},
"options": {
"type": "array",
"minItems": 0,
"items": {
"$ref": "#/$defs/Option"
}
},
"runs": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/Run"
}
},
"required_refs": {
"type": "array",
"description": "What the model needs from outside, by full name and type: every assumption whose type slot names a pack observable, and every bodiless assumption, sorted by name. A pack, a connector or a reviewer reads it as the list of inputs the model is built on; the inputs file is keyed by these names.",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"type",
"fallback"
],
"properties": {
"name": {
"type": "string",
"description": "The assumption's full name."
},
"type": {
"type": "string",
"description": "The pack observable the type slot named, or the language type of a bodiless assumption typed by one."
},
"fallback": {
"type": "boolean",
"description": "Whether the model states a value the run may replace. A bodiless assumption has none: the run supplies it, or is refused (E5045)."
}
}
},
"uniqueItems": true
},
"provenance": {
"$ref": "#/$defs/Provenance"
},
"views": {
"$ref": "#/$defs/Views",
"description": "Lenses on a completed result — never part of the model. `model_hash` is taken over this document WITHOUT `views`, so adding a slice or a statement changes no identity: two users who look at identical results differently are running the same model. A declared metric is not here; it is a figure the model claims."
},
"warnings": {
"type": "array",
"items": {
"$ref": "#/$defs/CompileWarning"
},
"description": "Warnings the compile raised and kept. Absent when there are none. The engine republishes them in results `warnings`, so a run that started from a questioned model says so."
}
},
"$defs": {
"Id": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"Qname": {
"type": "string",
"pattern": "^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*$"
},
"Date": {
"type": "string",
"description": "ISO date YYYY-MM-DD",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"DateRange": {
"type": "object",
"additionalProperties": false,
"required": [
"start",
"end"
],
"properties": {
"start": {
"$ref": "#/$defs/Date"
},
"end": {
"$ref": "#/$defs/Date"
}
}
},
"Frequency": {
"type": "string",
"enum": [
"daily",
"weekly",
"monthly",
"quarterly",
"annual"
]
},
"Currency": {
"type": "string",
"description": "ISO 4217",
"pattern": "^[A-Z]{3}$"
},
"Decimal": {
"type": "number"
},
"Money": {
"type": "object",
"additionalProperties": false,
"required": [
"amount",
"currency"
],
"properties": {
"amount": {
"$ref": "#/$defs/Decimal"
},
"currency": {
"$ref": "#/$defs/Currency"
}
}
},
"Rate": {
"type": "object",
"additionalProperties": false,
"required": [
"value"
],
"properties": {
"value": {
"$ref": "#/$defs/Decimal"
},
"basis": {
"type": "string",
"description": "Optional semantic basis label (e.g., 'annual')",
"minLength": 1
}
}
},
"Expr": {
"type": "object",
"additionalProperties": false,
"required": [
"lang",
"src"
],
"properties": {
"lang": {
"type": "string",
"enum": [
"cfdl"
]
},
"src": {
"type": "string",
"minLength": 1
}
}
},
"Phase": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"range",
"name"
],
"properties": {
"id": {
"$ref": "#/$defs/Id"
},
"range": {
"$ref": "#/$defs/DateRange"
},
"name": {
"$ref": "#/$defs/Id"
}
}
},
"EntityRef": {
"type": "object",
"additionalProperties": false,
"required": [
"symbol"
],
"properties": {
"symbol": {
"type": "string",
"pattern": "^([A-Za-z_][A-Za-z0-9_]*\\.[A-Za-z_][A-Za-z0-9_]*|contract(\\.[A-Za-z_][A-Za-z0-9_]*)+)$",
"description": "Entity symbol like 'asset.Sunset' A contract's own node, `contract.<name>`, is a reference too: the target of a status write on a contract's machine (`docs/01` §8.6)."
}
}
},
"Entity": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"symbol",
"family",
"type",
"fields"
],
"properties": {
"id": {
"$ref": "#/$defs/Id"
},
"symbol": {
"type": "string",
"pattern": "^[A-Za-z_][A-Za-z0-9_]*\\.[A-Za-z_][A-Za-z0-9_]*$"
},
"family": {
"type": "string",
"enum": [
"asset",
"party",
"container",
"reference"
],
"description": "The family the compiler resolved: the root of the type's refinement chain, or the declaration's own word for an untyped entity. Checked against the declaration's word at compile (E1319, E1323); the results republish it as it stands."
},
"type": {
"$ref": "#/$defs/Qname"
},
"fields": {
"type": "object",
"description": "Field values declared in the entity's block, checked against the fields its ontology type declares. Literals here: a field stated with '=' is a fact about the thing. A field that moves carries an 'init'/'next' rule instead.",
"additionalProperties": {
"$ref": "#/$defs/TypedValue"
}
},
"state": {
"type": "object",
"description": "Mutable runtime state fields (may be empty at compile time)",
"additionalProperties": {
"$ref": "#/$defs/TypedValue"
}
},
"parent": {
"type": "string",
"description": "The entity this one is part of. ALWAYS OPTIONAL, and absent for most entities: hierarchy is available at every grain and required at none. A pool models collective behavior with no loans under it; a building needs no units. The modeler chooses the grain, and the language does not prefer one."
},
"initial_state": {
"type": "string",
"description": "The lifecycle state this entity starts in, overriding its type's declared initial. Absent when the type declares no lifecycle. An entity WITH a lifecycle is always in exactly one of its states — there is no null state and no undeclared state, which is what makes a misspelled status a compile error rather than a wrong answer."
},
"rules": {
"type": "object",
"description": "Fields that MOVE, as recurrences owned by this entity. A field stated with '=' is a fact and lives in `fields`; a field with an 'init'/'next' rule lives here. A rule with no 'next' in source is written out as `next prev`, because a field with no rule holds.",
"additionalProperties": {
"type": "object",
"required": [
"init",
"next"
],
"additionalProperties": false,
"properties": {
"init": {
"$ref": "#/$defs/Expr"
},
"next": {
"$ref": "#/$defs/Expr"
},
"schedule": {
"$ref": "#/$defs/Schedule"
}
},
"description": "A field's recurrence. `schedule` is present only on a field a PACK emitted: it inherits the contract's payment rhythm, so a monthly-paying pool on a daily book compounds twelve times a year rather than 365. A field a modeler wrote has none and steps every period."
}
},
"lifecycle": {
"type": "string",
"description": "The machine this entity is governed by — an id into `lifecycles`. Absent for the many entities that have none."
},
"field_roles": {
"type": "object",
"description": "Field ROLES this entity's contracts fill, role → the lowered fields that play it: `balance` → the survival factors a pool's streams read. An arrival action naming the role sets each. Absent when no rule on the entity fills one.",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
},
"field_types": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/FieldType"
},
"description": "Each stated field's declared type and unit, keyed by field name. Absent for an untyped entity; a field its type does not declare has no entry."
}
}
},
"TypedValue": {
"description": "Strongly-typed value union used for fields/terms/state. anyOf, not oneOf: the members overlap structurally — an Expr {lang, src} is also a valid Map of strings — so requiring exactly one match can never hold for an untagged union.",
"anyOf": [
{
"type": "string"
},
{
"type": "boolean"
},
{
"type": "integer"
},
{
"type": "number"
},
{
"$ref": "#/$defs/Date"
},
{
"$ref": "#/$defs/Money"
},
{
"$ref": "#/$defs/Rate"
},
{
"$ref": "#/$defs/Expr"
},
{
"type": "array",
"items": {
"$ref": "#/$defs/TypedValue"
}
},
{
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/TypedValue"
}
}
]
},
"QuantileCall": {
"type": "object",
"required": [
"quantile",
"function"
],
"additionalProperties": false,
"properties": {
"quantile": {
"type": "string",
"description": "The quantile-shaped assumption named at the call site, by full name."
},
"function": {
"type": "string",
"enum": [
"quantile_at",
"quantile_mean",
"quantile_of"
]
},
"args": {
"type": "array",
"items": {
"type": "number"
},
"description": "The literal arguments after the name, in source order. Absent — not empty — when they were not literals; `unresolved` says so."
},
"unresolved": {
"type": "boolean",
"description": "True when an argument is not a compile-time literal: it reads the period (`time.date`), a run input (`inputs.*`), or another computed value. No single resolved figure exists for such a call, so `args` and `value` are both absent and the call is listed on its name alone. Published rather than left implicit because the old shape was an empty `args` array, and every quantile function takes a fixed arity with at least one argument after the name — `quantile_mean` three, `quantile_of` and `quantile_at` two — so `[]` could never mean \"a call that took no arguments\" and read as exactly that."
},
"value": {
"type": "number",
"description": "What the call resolves to, rounded to the engine's published-number policy so it agrees exactly with the ledger figure it explains. ABSENT when an argument is not a literal — the call is still listed, because a silently omitted call site would read as a model that never made one."
}
}
},
"Assumptions": {
"type": "object",
"additionalProperties": false,
"required": [
"constants",
"random"
],
"properties": {
"constants": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/AssumeConstant"
}
},
"random": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/AssumeRandom"
}
},
"names": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "A set field that names another set, by full path to the set it names (`market.renewal` → `standard_renewal`). Every read through it is already rewritten to the named set; the run uses this to say where an override belongs. Absent when no set names another."
}
}
},
"AssumeConstant": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"type"
],
"properties": {
"name": {
"$ref": "#/$defs/Id",
"description": "The full name a read uses: a scalar's name, or a set field's dotted path."
},
"expr": {
"$ref": "#/$defs/Expr",
"description": "The value's expression. Absent on a series-shaped assumption, which carries `series` instead."
},
"series": {
"$ref": "#/$defs/AssumeSeries"
},
"quantile": {
"$ref": "#/$defs/AssumeQuantile"
},
"type": {
"$ref": "#/$defs/ValueTypeId",
"description": "The language type (docs/01 §5.7). `Fraction` is 0 to 1 by definition and `Duration` is whole and non-negative; `Rate`, `Decimal` and `Int` carry no domain beyond integrality. The domain is checked wherever the value arrives."
},
"observable": {
"type": "string",
"description": "The pack `[[references]]` entry the type slot named (`energy.power_price`), which gives the assumption its unit and enters it in `required_refs`. Absent on an assumption typed by a language type or untyped."
},
"within": {
"type": "array",
"items": {
"type": "number"
},
"minItems": 2,
"maxItems": 2,
"description": "The modeler's own bound, `within [lo, hi]` (docs/01 §12.1): checked wherever the value arrives — a literal at compile time, an override, a scenario value or a draw at run start — and refused when outside, never clamped. `clip` on a distribution truncates draws; this refuses a value."
},
"unit": {
"type": "string",
"description": "The unit the value was stated in, as written (`60 \"USD/sf\"`). Asserted against a pack set type's declared unit where the assumption is a field of a typed set, never converted; recorded and checked by nothing otherwise."
},
"source": {
"$ref": "#/$defs/AssumeSource"
},
"set": {
"$ref": "#/$defs/AssumeSet"
},
"bound": {
"type": "boolean",
"description": "No body: the model states no value. The run supplies it by full name — an override or a value entry of the inputs file for a value, a series or quantile body of the file for those shapes — or the run is refused before any period exists (E5045)."
}
},
"oneOf": [
{
"required": [
"expr"
]
},
{
"required": [
"series"
]
},
{
"required": [
"quantile"
]
},
{
"required": [
"bound"
]
}
],
"description": "A deterministic assumption: a value (`expr`), a series (`series`), a quantile function (`quantile`), or no body at all (`bound`), one of the four. A series is read at the reader's period as `inputs.<name>` and at a date through `curve_value(inputs.<name>, <date>)`; a quantile through `quantile_at`, `quantile_mean` and `quantile_of` with the assumption as the first argument, and never bare."
},
"AssumeRandom": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"dist",
"type"
],
"properties": {
"name": {
"$ref": "#/$defs/Id",
"description": "The full name a read uses: a scalar's name, or a set field's dotted path."
},
"dist": {
"$ref": "#/$defs/Distribution"
},
"type": {
"$ref": "#/$defs/ValueTypeId"
},
"within": {
"type": "array",
"items": {
"type": "number"
},
"minItems": 2,
"maxItems": 2,
"description": "The modeler's own bound, `within [lo, hi]` (docs/01 §12.1): checked wherever the value arrives — a literal at compile time, an override, a scenario value or a draw at run start — and refused when outside, never clamped. `clip` on a distribution truncates draws; this refuses a value."
},
"unit": {
"type": "string",
"description": "The unit the value was stated in, as written (`60 \"USD/sf\"`). Asserted against a pack set type's declared unit where the assumption is a field of a typed set, never converted; recorded and checked by nothing otherwise."
},
"source": {
"$ref": "#/$defs/AssumeSource"
},
"set": {
"$ref": "#/$defs/AssumeSet"
}
}
},
"ValueTypeId": {
"type": "string",
"enum": [
"String",
"Bool",
"Int",
"Decimal",
"Date",
"Money",
"Rate",
"Fraction",
"Duration"
]
},
"Distribution": {
"type": "object",
"additionalProperties": false,
"required": [
"kind",
"params"
],
"properties": {
"kind": {
"type": "string",
"enum": [
"Normal",
"LogNormal",
"Uniform",
"Triangular"
]
},
"params": {
"type": "object",
"additionalProperties": {
"type": [
"number",
"string",
"boolean"
]
}
},
"clip": {
"type": "array",
"items": {
"type": "number"
},
"minItems": 2,
"maxItems": 2
}
}
},
"Contract": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"name",
"type",
"subject",
"term",
"currency",
"terms",
"effects",
"provenance"
],
"properties": {
"id": {
"$ref": "#/$defs/Id"
},
"name": {
"$ref": "#/$defs/Id"
},
"type": {
"$ref": "#/$defs/Qname",
"description": "The ontology type the contract IS — `CRE.Contract.UnitLease` — resolved once at declaration from the pack type it names. `core.Contract` only where no type could be resolved: a contract with no pack active."
},
"contract_name": {
"$ref": "#/$defs/Qname",
"description": "The pack contract type as the model names it — the lowering rule name, `cre.lease_unit`."
},
"master": {
"$ref": "#/$defs/Qname",
"description": "The master at the root of the type's refinement chain — `Contract.Lease`."
},
"settlement_date_basis": {
"type": "boolean",
"description": "Present and true where the agreement is quoted on a settlement-date basis (docs/01 §15.3): the engine measures its series from the settlement date on its `day_count`, as a security's lives, prices, yields and spreads are. Absent, the series are measured by period."
},
"instance": {
"$ref": "#/$defs/Id",
"description": "The instance token where the contract's name carries one — `tenant_a` in `cre.lease_unit.tenant_a` or `contract cre.lease_unit tenant_a`."
},
"subject": {
"$ref": "#/$defs/EntityRef"
},
"term": {
"description": "The agreement's window — its DECLARED dates, never the grid's: two dates, or the state entry it opens at (docs/01 §8.1) with the field names a StateEnter schedule uses. An anchored term resolves during the walk: each entry of the entity into the state opens a window of `anchor_periods` grid periods, and every stream the contract lowers runs in it.",
"oneOf": [
{
"$ref": "#/$defs/DateRange"
},
{
"type": "object",
"additionalProperties": false,
"required": [
"anchor_entity",
"anchor_state",
"anchor_periods"
],
"properties": {
"anchor_entity": {
"type": "string"
},
"anchor_state": {
"type": "string"
},
"anchor_periods": {
"type": "integer",
"minimum": 1
}
}
}
]
},
"currency": {
"$ref": "#/$defs/Currency"
},
"parties": {
"type": "array",
"description": "Who the contract is between, by role. `role` is the pack's word as the model bound it (`landlord`); `master_role` is what the master calls it (`lessor`), so a consumer can find every lender without knowing each pack's word for one.",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"role",
"entity"
],
"properties": {
"role": {
"type": "string"
},
"master_role": {
"type": "string"
},
"entity": {
"$ref": "#/$defs/EntityRef"
}
}
}
},
"tags": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/TypedValue"
}
},
"terms": {
"type": "object",
"description": "Contract terms; pack may validate and type-check",
"additionalProperties": {
"$ref": "#/$defs/TypedValue"
}
},
"effects": {
"$ref": "#/$defs/Effects"
},
"on_start": {
"type": "array",
"items": {
"$ref": "#/$defs/Action"
},
"description": "What the agreement does to its subject when its term starts (docs/01 §8.5): `SetEntityField` actions on the contract's subject, each `contract.<term>` read spliced. Run in the first period of the term inside the run — period 0 for a term already running — before the period's events, and after every contract's `on_end`; a status write is checked against the subject's machine as an event's is. Absent when the contract states none."
},
"on_end": {
"type": "array",
"items": {
"$ref": "#/$defs/Action"
},
"description": "What the agreement does to its subject when its term ends (docs/01 §8.5), run in the first period after the term, before every contract's `on_start` in that period. Absent when the contract states none."
},
"lifecycle": {
"type": "string",
"description": "The machine governing the agreement itself (docs/01 §8.6), an id into `lifecycles`. A contract is an entity: the engine walks its machine under the node `contract.<name>`, and the machine's guards read the agreement's terms as `contract.<term>`. Absent when the contract binds none."
},
"provenance": {
"$ref": "#/$defs/NodeProvenance"
},
"term_bounds": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/TermBound"
},
"description": "Bounds on the terms this contract defers to the run, keyed by term name; absent when it defers no bounded term."
},
"term_types": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/FieldType"
},
"description": "Each stated term's declared type and unit, keyed by term name. Absent for a contract no pack types; a term its type does not declare has no entry."
}
}
},
"Effects": {
"type": "object",
"additionalProperties": false,
"required": [
"streams"
],
"properties": {
"streams": {
"type": "array",
"items": {
"$ref": "#/$defs/Stream"
}
}
}
},
"Stream": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"name",
"owner",
"direction",
"currency",
"schedule",
"amount",
"active_when",
"provenance"
],
"properties": {
"id": {
"$ref": "#/$defs/Id"
},
"name": {
"$ref": "#/$defs/Id"
},
"owner": {
"$ref": "#/$defs/EntityRef"
},
"direction": {
"$ref": "#/$defs/Direction"
},
"currency": {
"$ref": "#/$defs/Currency"
},
"category": {
"type": "string",
"description": "What this stream is economically (revenue, opex, debt_service, ...). Aggregation reads this rather than pattern-matching the name, so the meaning is declared once at the point of emission instead of being re-derived by every consumer. Must name a category the active pack declares (E5022). Absent when the stream is unclassified, which is legal and leaves it out of every category fold."
},
"moves": {
"type": "string",
"description": "The account this stream's amount moves, by its declared name — `asset.loan.balance` for a claim declared in the entity's block, or a structure account's own name. Absent on a stream that changes nothing owed, which is most of them. A cash stream raises a liability its owner owes and lowers a receivable its owner is due; an accrual raises and a write-off lowers, whichever side."
},
"schedule": {
"$ref": "#/$defs/Schedule"
},
"amount": {
"$ref": "#/$defs/Expr"
},
"active_when": {
"description": "If omitted in source, compiler should emit true",
"$ref": "#/$defs/Expr"
},
"closed_by": {
"type": "array",
"description": "The closing states of the machines on the stream's owner and on everything the owner is part of (`closes`, docs/07 §6.1). Once one of those nodes has entered one, the stream pays nothing in a later period of the cash horizon; the projection tail reads the states as the horizon ends. Absent where no such machine is bound.",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"node",
"states"
],
"properties": {
"node": {
"type": "string",
"description": "The owner, or a node it is part of."
},
"states": {
"type": "array",
"items": {
"type": "string"
},
"description": "The states that close it."
},
"held_periods": {
"type": "integer",
"minimum": 1,
"description": "A sold asset's own tail (docs/01 §7.3.3): the periods after the node closes that the stream is still evaluated, without cash, for a valuation of the node that folds past the sale. Absent where no such valuation reads the node."
}
}
}
},
"runs_in": {
"type": "array",
"description": "The states this stream may run in, per machine that governs its line: its contract's own machine (`contract.<name>`) and its owner's, where either lists the stream's line role in an `in <state> run …`. Every entry must hold, as the node stands in the period. Absent where no machine governs the line.",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"node",
"states"
],
"properties": {
"node": {
"type": "string",
"description": "The governing node: an entity's symbol, or `contract.<name>` for a contract's own machine."
},
"states": {
"type": "array",
"items": {
"type": "string"
},
"description": "The states in which the line runs at that node."
}
}
}
},
"provenance": {
"$ref": "#/$defs/NodeProvenance"
}
}
},
"Direction": {
"type": "string",
"enum": [
"inflow",
"outflow",
"accrual",
"writeoff"
],
"description": "`inflow` and `outflow` are cash. `accrual` and `writeoff` are not: a claim raised or extinguished with no money moving. They publish as series, move the account they name, and are excluded from every cash total, category fold and valuation."
},
"Schedule": {
"type": "object",
"additionalProperties": false,
"required": [
"kind"
],
"properties": {
"kind": {
"type": "string",
"enum": [
"OnDate",
"Every",
"PhaseEnter",
"EveryPhase",
"StateEnter"
]
},
"on": {
"$ref": "#/$defs/Date"
},
"every": {
"$ref": "#/$defs/Frequency"
},
"from": {
"$ref": "#/$defs/Date"
},
"to": {
"$ref": "#/$defs/Date"
},
"on_rule": {
"description": "Optional rule for day-of-month or weekday sets",
"$ref": "#/$defs/OnRule"
},
"convention": {
"type": "string",
"enum": [
"none",
"following",
"modified_following",
"preceding",
"modified_preceding"
]
},
"calendar": {
"type": "string"
},
"phase": {
"type": "string",
"description": "Phase name for PhaseEnter/EveryPhase"
},
"except_dates": {
"type": "array",
"items": {
"type": "string"
}
},
"also_dates": {
"type": "array",
"items": {
"type": "string"
}
},
"net_days": {
"description": "Days between a flow being earned and its cash moving. Omitted when cash lands in the period that earned it.",
"type": "integer",
"minimum": 0
},
"net_months": {
"description": "Months between a flow being earned and its cash moving, stepped by the calendar rather than as 30-day units.",
"type": "integer",
"minimum": 0
},
"placement": {
"type": "string",
"enum": [
"start",
"mid",
"end"
],
"description": "Where in its period the flow sits. One axis with three positions, so two placements cannot both be stated. Omitted for the form's default, which differs by form: a one-shot (`OnDate`) opens its period, a recurrence closes it (an ordinary annuity — the interval elapses, then payment falls). `start` is an annuity due and what expense-like streams want; `mid` is the project-finance convention, half a period on every calendar, a convention rather than a date; `end` is what a disposal needs, since a reversion is taken at the close of the holding period. Mutually exclusive with a day rule and with payment terms (E2109). See 12_payment_timing.md."
},
"anchor_entity": {
"type": "string",
"description": "state_enter anchor: the entity whose entries open the windows. Present only for kind StateEnter."
},
"anchor_state": {
"type": "string",
"description": "The state whose entry anchors the window; a re-entered state re-anchors, and a self-edge is an entry."
},
"anchor_periods": {
"type": "integer",
"minimum": 1,
"description": "Window length in grid periods from each entry."
}
},
"allOf": [
{
"if": {
"properties": {
"kind": {
"const": "OnDate"
}
}
},
"then": {
"required": [
"on"
]
}
},
{
"if": {
"properties": {
"kind": {
"const": "Every"
}
}
},
"then": {
"required": [
"every",
"from",
"to"
]
}
},
{
"if": {
"properties": {
"kind": {
"const": "PhaseEnter"
}
}
},
"then": {
"required": [
"phase"
]
}
},
{
"if": {
"properties": {
"kind": {
"const": "EveryPhase"
}
}
},
"then": {
"required": [
"every",
"phase"
]
}
}
]
},
"OnRule": {
"type": "object",
"additionalProperties": false,
"required": [
"kind"
],
"properties": {
"kind": {
"type": "string",
"enum": [
"DayOfMonth",
"EndOfMonth"
]
},
"day": {
"type": "integer",
"minimum": 1,
"maximum": 31
}
},
"allOf": [
{
"if": {
"properties": {
"kind": {
"const": "DayOfMonth"
}
}
},
"then": {
"required": [
"day"
]
}
}
]
},
"BusinessDayConvention": {
"type": "string",
"enum": [
"none",
"following",
"modified_following",
"preceding",
"modified_preceding"
]
},
"Event": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"name",
"actions",
"provenance"
],
"properties": {
"id": {
"$ref": "#/$defs/Id"
},
"name": {
"$ref": "#/$defs/Id"
},
"schedule": {
"$ref": "#/$defs/Schedule",
"description": "The occurrences this event is tested at. Absent means the condition's own dynamics supply them (rising edge). Present with `when`, the event fires at each scheduled occurrence the condition admits — a quarterly covenant test that fails four times running is four breach events, because the model declared quarterly testing."
},
"when": {
"$ref": "#/$defs/Expr",
"description": "Absent means every scheduled occurrence fires. Present with no schedule, occurrences are this expression's rising edges."
},
"actions": {
"type": "array",
"items": {
"$ref": "#/$defs/Action"
}
},
"provenance": {
"$ref": "#/$defs/NodeProvenance"
}
},
"description": "An event is something that happens, and nothing restricts it to happening once. It fires on EACH occurrence: `schedule` supplies the occurrences and `when` filters them; with no schedule, an occurrence is the rising edge of the condition — true having been false, re-arming when it falls. At least one of `schedule` and `when` is present. There is no latch and no `once` flag: once-ness is declared, as a schedule whose occurrence is singular or a topology with no way back. A consumer MUST NOT infer fired-once semantics from the absence of a schedule.",
"anyOf": [
{
"required": [
"schedule"
]
},
{
"required": [
"when"
]
}
]
},
"Action": {
"type": "object",
"additionalProperties": false,
"required": [
"kind"
],
"properties": {
"kind": {
"type": "string",
"enum": [
"SetEntityField",
"ActivateStream",
"DeactivateStream",
"ExerciseOption"
]
},
"entity": {
"$ref": "#/$defs/EntityRef"
},
"field": {
"$ref": "#/$defs/Id"
},
"value": {
"$ref": "#/$defs/TypedValue"
},
"stream": {
"$ref": "#/$defs/Id"
},
"option": {
"$ref": "#/$defs/Id"
}
},
"allOf": [
{
"if": {
"properties": {
"kind": {
"const": "SetEntityField"
}
}
},
"then": {
"required": [
"entity",
"field",
"value"
]
}
},
{
"if": {
"properties": {
"kind": {
"const": "ActivateStream"
}
}
},
"then": {
"required": [
"stream"
]
}
},
{
"if": {
"properties": {
"kind": {
"const": "DeactivateStream"
}
}
},
"then": {
"required": [
"stream"
]
}
},
{
"if": {
"properties": {
"kind": {
"const": "ExerciseOption"
}
}
},
"then": {
"required": [
"option"
]
}
}
]
},
"Option": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"name",
"type",
"exercise_when",
"payoff",
"provenance"
],
"properties": {
"id": {
"$ref": "#/$defs/Id"
},
"name": {
"$ref": "#/$defs/Id"
},
"type": {
"$ref": "#/$defs/Qname"
},
"exercisable_in_phase": {
"$ref": "#/$defs/Id"
},
"schedule": {
"$ref": "#/$defs/Schedule",
"description": "The occasions the election is tested at — a Bermudan right, exercisable on stated dates. The schedule supplies the occasions and `exercise_when` filters them. Absent, an occasion is the election's rising edge while the option is held (inside its window): true having been false, so a right that stays in the money is not re-exercised every period."
},
"exercises": {
"type": "integer",
"minimum": 1,
"description": "How many times the right may be exercised — a lease with two renewals is exercised twice. Absent means once. Each exercise pays the payoff and runs the actions; the payoff series accumulates."
},
"actions": {
"type": "array",
"items": {
"$ref": "#/$defs/Action"
},
"description": "What the exercise DOES beyond paying, in the event's action vocabulary, run on each exercise through the same stores an event writes and visible at t+1. A prepayment option ends the loan; a renewal extends the lease. Absent or empty, the exercise only pays."
},
"exercise_when": {
"$ref": "#/$defs/Expr"
},
"payoff": {
"$ref": "#/$defs/Expr"
},
"category": {
"type": "string",
"description": "What the payoff IS, economically — the stream's `category` clause on an election: a dotted path rooted in operating, investing or financing. Optional; a categorized payoff folds into the pack's subtotals and a slice by category, an uncategorized one into none."
},
"provenance": {
"$ref": "#/$defs/NodeProvenance"
},
"owner": {
"$ref": "#/$defs/EntityRef",
"description": "The asset this option is written on. AN OPTION IS A CONTRACT WITH AN ELECTION, so it attaches to something the way every other contract does. Absent on an option written before options had owners; without one its payoff belongs to no entity and falls out of every per-entity total. An option written `on contract` takes that contract's entity."
},
"contract": {
"$ref": "#/$defs/Qname",
"description": "The contract this option is written against (`on contract <name>`): a renewal over a lease, a prepayment over a loan. The election's subject is the agreement; its stated terms are readable in the election and the payoff as `contract.<term>` where the option states none of its own. Absent on an option written on an entity."
},
"terms": {
"type": "object",
"description": "What the option states, checked against the election type's effective fields exactly as a contract's terms are (`Contract.Option` declares `strike`; a pack election adds its own). The election and the payoff carry the values already spliced.",
"additionalProperties": {
"$ref": "#/$defs/TypedValue"
}
},
"parties": {
"type": "array",
"description": "Who the option is between, by role. The role is named by the contract TYPE rather than by the party, because the same party is lessor in one agreement and lender in another — the role belongs to the agreement.",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"role",
"entity"
],
"properties": {
"role": {
"type": "string"
},
"entity": {
"$ref": "#/$defs/EntityRef"
}
}
}
}
}
},
"Run": {
"type": "object",
"additionalProperties": false,
"required": [
"kind"
],
"properties": {
"kind": {
"type": "string",
"enum": [
"deterministic",
"monte_carlo"
]
},
"trials": {
"type": "integer",
"minimum": 1
},
"seed": {
"type": "integer",
"minimum": 0
}
},
"allOf": [
{
"if": {
"properties": {
"kind": {
"const": "monte_carlo"
}
}
},
"then": {
"required": [
"trials",
"seed"
]
}
}
]
},
"Metric": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"expr"
],
"properties": {
"name": {
"type": "string",
"description": "Published as `metric.<name>`."
},
"expr": {
"$ref": "#/$defs/Expr",
"description": "Evaluated at the horizon over the finished projection. It may read series (including the projection tail), entity fields, inputs, the engine's `model.*` metrics, and `metric.<name>` for any metric declared above it."
},
"provenance": {
"$ref": "#/$defs/NodeProvenance"
},
"per_period": {
"type": "boolean",
"description": "A valuation figure published every period: evaluated at each period of the cash horizon with `time.t` the valuation date, and published as the series `metric.<name>` rather than a scalar. Absent means false."
}
}
},
"Provenance": {
"type": "object",
"additionalProperties": false,
"required": [
"sources",
"compiler"
],
"properties": {
"sources": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"minItems": 1
},
"compiler": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"version",
"hash"
],
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"version": {
"type": "string",
"minLength": 1
},
"hash": {
"type": "string",
"minLength": 8
},
"notes": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
}
}
}
}
},
"NodeProvenance": {
"type": "object",
"additionalProperties": false,
"required": [
"source_file",
"source_span"
],
"properties": {
"source_file": {
"type": "string",
"minLength": 1
},
"source_span": {
"type": "object",
"additionalProperties": false,
"required": [
"start_line",
"start_col",
"end_line",
"end_col"
],
"properties": {
"start_line": {
"type": "integer",
"minimum": 1
},
"start_col": {
"type": "integer",
"minimum": 1
},
"end_line": {
"type": "integer",
"minimum": 1
},
"end_col": {
"type": "integer",
"minimum": 1
}
}
},
"notes": {
"type": "string"
},
"generated_by": {
"$ref": "#/$defs/GeneratedBy"
},
"contract": {
"$ref": "#/$defs/Qname",
"description": "The contract a stream was declared in, when it was written in that contract's `effects` block (docs/01 §8.3). The model's own attribution, beside `generated_by.contract`, which a pack rule sets; the results read either. Absent on every other node."
},
"line": {
"type": "string",
"description": "The line an `effects` stream states it is (`line <role>`, docs/01 §8.3), beside `generated_by.line`, which a pack rule sets. Absent on every other node."
}
}
},
"GeneratedBy": {
"type": "object",
"additionalProperties": false,
"required": [
"pack",
"rule_id"
],
"properties": {
"pack": {
"$ref": "#/$defs/PackRef"
},
"rule_id": {
"type": "string",
"minLength": 1
},
"contract": {
"$ref": "#/$defs/Qname",
"description": "The contract this stream was lowered from, by its qualified name (`cre.lease_unit.tenant_a`). A rule serves every instance of its type; this says which one."
},
"line": {
"type": "string",
"description": "The line the rule emits, by the role its contract's master names (`interest`, `rent`, `proceeds`)."
}
}
},
"PackRef": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"version"
],
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"version": {
"type": "string",
"minLength": 1
}
}
},
"StreamInputs": {
"type": "object",
"additionalProperties": false,
"required": [
"stream",
"contract",
"terms"
],
"description": "What a pack lowering rule CONSUMED to strike one stream: the placeholders its templates actually substituted, plus the rule defaults that filled a gap. Not the contract's whole term map — a contract lowers to several streams and each reads a different subset, so 'the contract's terms' is not an answer to 'what struck this line'. Pack, rule id and source span are already on the stream's own provenance and are not repeated here. Absent for hand-written streams, which no rule struck.",
"properties": {
"stream": {
"$ref": "#/$defs/Id"
},
"contract": {
"type": "string",
"description": "The contract instance the rule matched, including any suffix."
},
"terms": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Resolved placeholder values, as the strings the templates substituted. Not coerced: a term's payload is text plus a span, which is the contract packs already work against."
},
"defaults_applied": {
"type": "array",
"items": {
"type": "string"
},
"description": "Keys the contract did not supply, filled from the rule's own defaults. Separated because 'the model said 0' and 'the pack assumed 0' are different facts, and a reader tracing a number needs to tell them apart."
}
}
},
"Subtotal": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"kind",
"op"
],
"description": "A per-period subtotal: a named fold over the ledger, lowered from the active pack. Where a metric reduces to one lifetime scalar, this produces a value per period — the middle rows of a statement. Folds CATEGORIES by preference rather than stream names, so net operating income is everything under `operating.*` and nothing enumerates which streams those are. Array order is DEPENDENCY order: an entry may reference only ones before it, which makes a cycle unexpressible rather than merely rejected. A subtotal is a fold OF the cash and never counts as cash: it is excluded from model.total, model.npv, model.net_cash_flow and the per-stream annual rollup, by the same construction that keeps a field out of the cash.",
"properties": {
"id": {
"type": "string",
"description": "Output series key; must start with `domain.`."
},
"kind": {
"enum": [
"money",
"number"
]
},
"op": {
"enum": [
"sum",
"negated_sum",
"cumulative",
"negated_cumulative",
"ratio"
],
"description": "How the subtotal folds. `sum` and `negated_sum` total one period; `cumulative` and `negated_cumulative` carry a running total, which is how a stock is derived from a flow — principal paid to date, capital called to date. `ratio` divides two money subtotals."
},
"categories": {
"type": "array",
"items": {
"type": "string"
},
"description": "Category path selectors, e.g. `operating.revenue.*`."
},
"streams": {
"type": "array",
"items": {
"type": "string"
},
"description": "Stream-name selectors, for what a category cannot express."
},
"subtotals": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ids of subtotals declared earlier."
},
"numerator": {
"type": "string"
},
"denominator": {
"type": "string"
},
"formula": {
"type": "string",
"description": "Human-readable lineage, emitted verbatim."
}
}
},
"Waterfall": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"entity",
"source",
"steps"
],
"properties": {
"name": {
"type": "string"
},
"entity": {
"type": "string",
"description": "The entity whose cash this allocates."
},
"schedule": {
"$ref": "#/$defs/Schedule"
},
"source": {
"$ref": "#/$defs/Expr"
},
"steps": {
"type": "array",
"minItems": 0,
"items": {
"$ref": "#/$defs/WaterfallStep"
}
}
}
},
"WaterfallStep": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"payee",
"amount"
],
"properties": {
"name": {
"type": "string"
},
"payee": {
"type": "string",
"description": "The entity this step pays."
},
"amount": {
"$ref": "#/$defs/Expr",
"description": "What the step is owed. `remaining`, `paid.<step>` and `owed.<step>` are bound on top of the ordinary expression environment."
},
"payee_is_account": {
"type": "boolean",
"description": "The payee is an ACCOUNT rather than a party. Allocating to a party allocates into that party's account when it has one; naming an account directly is the explicit form, and is how a reserve is funded. Omitted when false, so existing IR stays byte-identical."
},
"contract": {
"$ref": "#/$defs/Qname",
"description": "The agreement this step pays, by qualified name — `for contract credit.note.a2`. Present only when the model says so; the step's series then carries the contract and the results graph lists the step under it."
},
"line": {
"type": "string",
"description": "Which of the contract's lines this step pays (`principal`, `interest`), which the contract's type must declare as allocated (E1377)."
}
}
},
"Account": {
"type": "object",
"additionalProperties": false,
"required": [
"name"
],
"description": "A declared cash location whose balance carries across periods. `available` is unchanged and still means this period's netted cash; an account is the ACCUMULATED cash, and is what a waterfall draws its pot from when its schedule names a period. There is no currency: an account is denominated by the model, so the only thing a clause could express is a restatement of what the model already declares, or a second unit that must be refused.",
"properties": {
"name": {
"type": "string"
},
"owner": {
"type": "string",
"description": "The party this account belongs to, when it belongs to one. A general account has none — a collection account, a reserve — and a party-owned one holds what has been ALLOCATED to that party. It is not an obligation: it holds cash that exists, and what is still owed is simply not yet allocated."
},
"inflow": {
"$ref": "#/$defs/Expr",
"description": "What flows in each period. May be negative: an account fed a deal's whole net cash IS the deal's cumulative position, negative through the J-curve and positive after."
},
"owner_entity": {
"type": "string",
"description": "The entity whose claim this is, when the account was declared in its block — `asset.loan` for `asset.loan.balance`. Absent on a structure or party account."
},
"side": {
"type": "string",
"enum": [
"owed",
"due"
],
"description": "From the owner's view: `owed` is a liability (an inflow that moves it raises it), `due` a receivable (an inflow lowers it). Required on an account a cash stream moves; a pack contract's account takes it from the master."
},
"init": {
"$ref": "#/$defs/Expr",
"description": "The balance at the timeline's first period. Absent means zero: a balance created during the run is raised from zero by the cash that creates it, and `prev.<account>` is never absent."
},
"fold": {
"type": "boolean",
"description": "A relation fold: this container's account of this name is the sum, opening and closing, of its members' accounts of the same name through `part of`. Synthesized by the compiler for every ancestor of an entity that declares a claim; it carries no side, init, inflow or movement of its own, and a declaration of the same name on the container is refused (E1383). Absent means a declared account."
}
}
},
"Lifecycle": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"initial",
"states"
],
"description": "A declared finite state machine. The states are enumerated ahead of time — the finite set is what makes a misspelled state a compile error rather than a phantom state. The edges are declared only as used: declaring one brings it into existence, an undeclared edge does not exist, and absence is the prohibition. Only machines an entity binds are published.",
"properties": {
"id": {
"type": "string"
},
"initial": {
"type": "string",
"description": "The state the machine opens in; an entity's initial_state overrides it."
},
"states": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
},
"edges": {
"type": "array",
"items": {
"$ref": "#/$defs/LifecycleEdge"
},
"description": "Empty or absent means the machine is unconstrained — permits()'s empty-means-open rule."
},
"entry_actions": {
"type": "array",
"items": {
"$ref": "#/$defs/StateEntry"
},
"description": "What is true of a STATE however it was reached, and the primary domain spelling: an entry action holds for every edge that arrives, including one added later. A pack's types.toml machine declares it once and every model using the type inherits it. Runs BEFORE the taken edge's actions."
},
"master_states": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Each state's master word, for a master machine such as `core.debt` or a pack's refinement of one: `construction` → `drawing`. Published on each status transition as `master`. Absent otherwise."
},
"composites": {
"type": "object",
"description": "Each state that includes a nested machine, flattened: the innermost states inside it, and the one first entry opens at. `states` holds only innermost states; an edge out of a composite is listed from each state inside it, before the nested machine's own edges, and an edge into one keeps the composite's name, resolved at run through history.",
"additionalProperties": {
"type": "object",
"additionalProperties": false,
"required": [
"leaves",
"initial"
],
"properties": {
"leaves": {
"type": "array",
"items": {
"type": "string"
}
},
"initial": {
"type": "string"
}
}
}
}
}
},
"LifecycleEdge": {
"type": "object",
"additionalProperties": false,
"required": [
"from",
"to",
"author"
],
"description": "One edge, from -> to. A self-edge (from = to) is a real transition: it journals and re-anchors. A guard-less edge is a permission only — an event's write may take it, but the machine never fires it on its own. A model may restate a pack machine's edge, which replaces the pack's guard and all; the states stay the pack's.",
"properties": {
"from": {
"type": "string"
},
"to": {
"type": "string"
},
"guard": {
"$ref": "#/$defs/Expr",
"description": "Evaluated each period the entity is in `from`, reading state as the period opened and series strictly backward. There is no latch: edge availability is the memory."
},
"actions": {
"type": "array",
"items": {
"$ref": "#/$defs/StateAction"
},
"description": "What is true of the PATH taken, run on every traversal — whatever took the edge, including a named event's status write. Runs AFTER the target state's entry actions: the specific refines the general."
},
"author": {
"type": "string",
"enum": [
"pack",
"model"
],
"description": "Who declared the edge as it stands. A pack edge a model restated carries `model`, because the condition that fires it is the model's; the pack's actions on it survive and carry their own author, which is why this is stored rather than inferred from them."
},
"replaced_guard": {
"$ref": "#/$defs/Expr",
"description": "The pack guard a model's restatement displaced, kept so the warning and the transitions journal can name it beside the guard that actually fired. Absent unless a replacement occurred."
}
}
},
"StateAction": {
"type": "object",
"additionalProperties": false,
"required": [
"kind",
"author",
"field",
"value"
],
"description": "One arrival action. The vocabulary is `set` only and the target is a FIELD on the entity that transitioned — never `status`, which would fire a second transition inside the same period. The field name is entity-relative because one lifecycle is bound by many entities. Evaluated in the guard's environment (state as the period opened, series strictly backward), so arrival actions add no cycle risk; the write settles the field for the period and the recurrence resumes from it next period. `author` is REQUIRED rather than inferred from the presence of provenance: a pack action whose stamp was forgotten would otherwise journal as the model's, and an unattributable `overridden` line is the one thing this record exists to prevent.",
"properties": {
"kind": {
"type": "string",
"enum": [
"SetField",
"SetRole"
],
"description": "`SetField` writes the named field on the entity that transitioned. `SetRole` names a FIELD ROLE a master declares (e.g. `balance`): the engine writes every field on the entity that plays it — the entity's `field_roles` — and nothing where none does."
},
"author": {
"type": "string",
"enum": [
"pack",
"model"
],
"description": "Who wrote this action. Within one state's or edge's list every `pack` action precedes every `model` action — the pack's machine is the general case and the model's augmentation refines it — and the journal names this as the actor, so a same-field conflict records which author's value was `overridden`."
},
"field": {
"$ref": "#/$defs/Id",
"description": "Entity-relative field name, resolved against the entity bound to this machine."
},
"value": {
"$ref": "#/$defs/Expr"
}
}
},
"StateEntry": {
"type": "object",
"additionalProperties": false,
"required": [
"state",
"actions"
],
"description": "The arrival actions for one state. At most one entry per state per author; a model augmenting a pack-declared machine contributes a SEPARATE list that runs after the pack's, additively — the model may not add or remove states or edges, alter the topology, or remove the pack's actions.",
"properties": {
"state": {
"type": "string"
},
"actions": {
"type": "array",
"items": {
"$ref": "#/$defs/StateAction"
}
}
}
},
"Slice": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"provenance"
],
"properties": {
"name": {
"type": "string"
},
"entities": {
"type": "array",
"items": {
"type": "string"
},
"description": "Entity references; each selects the entity and its part_of descendants."
},
"types": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ontology types, matched transitively through refines."
},
"lines": {
"type": "array",
"items": {
"type": "string"
},
"description": "Lines by role — `interest` selects every stream a rule emits as its type's interest line, whichever pack. Intersects with `types`."
},
"categories": {
"type": "array",
"items": {
"type": "string"
},
"description": "Category selectors — series_sum's dialect."
},
"streams": {
"type": "array",
"items": {
"type": "string"
},
"description": "Stream-name selectors — same dialect."
},
"except_streams": {
"type": "array",
"items": {
"type": "string"
}
},
"except_categories": {
"type": "array",
"items": {
"type": "string"
}
},
"except_entities": {
"type": "array",
"items": {
"type": "string"
}
},
"type_streams": {
"type": "array",
"items": {
"type": "string"
},
"description": "The streams the type and line clauses matched — the compiler's expansion, because only it holds the ontology and the rules."
},
"provenance": {
"$ref": "#/$defs/NodeProvenance"
},
"window": {
"$ref": "#/$defs/DateRange",
"description": "A reporting window, inclusive. Only periods inside it are selected, so total, npv and irr are folds over it. Absent when the slice spans the whole horizon. Not a phase: a phase is a lifecycle anchor that drives schedules, and a window is a reporting bound."
}
}
},
"Statement": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"provenance"
],
"description": "A declared presentation: which hierarchy to show, and to what level. It carries no rows — the rows are generated from the structure after the run, and depth decides which are shown, so an interior node is a subtotal by virtue of where it sits. A statement is authored or generated, never both and never neither (E1369): a generated statement partitions the cash by construction, an authored one by the author's care, and mixed neither guarantee holds.",
"properties": {
"name": {
"type": "string"
},
"label": {
"type": "string"
},
"structure": {
"type": "string",
"enum": [
"entity",
"category"
],
"description": "Which existing hierarchy to present: the part_of tree, or the dotted category path."
},
"depth": {
"type": "integer",
"minimum": 1,
"description": "The level of aggregation. Absent means the whole tree."
},
"grain": {
"type": "string"
},
"slice": {
"type": "string",
"description": "An optional filter, orthogonal to the structure."
},
"metrics": {
"type": "array",
"items": {
"type": "string"
},
"description": "Declared metrics published beside the statement."
},
"provenance": {
"$ref": "#/$defs/NodeProvenance"
},
"rows": {
"type": "array",
"items": {
"$ref": "#/$defs/StatementRow"
},
"description": "Authored rows, for a statement that enumerates rather than generates. A statement does one or the other, never both (E1369): a generated statement partitions the cash by construction, an authored one by the author's care."
},
"default": {
"type": "boolean",
"description": "The statement a consumer shows when it asks for \"the\" statement. A pack's statement states it; absent means false."
}
},
"oneOf": [
{
"required": [
"structure"
],
"description": "A GENERATED statement: the rows follow from the named hierarchy, down to `depth`."
},
{
"required": [
"rows"
],
"description": "An AUTHORED statement: the rows are stated, with their own labels and display signs."
}
]
},
"Views": {
"type": "object",
"additionalProperties": false,
"description": "Slices filter and statements organize. Anything added here is outside the model's identity by construction.",
"properties": {
"slices": {
"type": "array",
"items": {
"$ref": "#/$defs/Slice"
}
},
"statements": {
"type": "array",
"items": {
"$ref": "#/$defs/Statement"
}
}
}
},
"StatementRow": {
"type": "object",
"additionalProperties": false,
"required": [
"kind"
],
"properties": {
"kind": {
"type": "string",
"enum": [
"line",
"subtotal",
"ratio",
"spacer"
]
},
"label": {
"type": "string"
},
"depth": {
"type": "integer",
"minimum": 0,
"description": "Indent, for presentation. Distinct from the statement's own depth, which is a level of aggregation."
},
"categories": {
"type": "array",
"items": {
"type": "string"
}
},
"streams": {
"type": "array",
"items": {
"type": "string"
}
},
"types": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ontology types the row draws, matched transitively through refines, as on a slice."
},
"lines": {
"type": "array",
"items": {
"type": "string"
},
"description": "Lines by role the row draws, as on a slice."
},
"type_streams": {
"type": "array",
"items": {
"type": "string"
},
"description": "What the type and line clauses matched, expanded by the compiler into exact stream names; the evaluator claims them beside `streams`."
},
"slice": {
"type": "string",
"description": "A declared slice whose streams the row draws."
},
"series": {
"type": "string",
"description": "A published series key the row presents (`domain.cre.noi`, `model.net_cash_flow`). A fold OF the ledger rather than cash in it, so the row claims no streams and its figure stays out of the bottom line; a claim clause beside it is refused (E1370). An absent key publishes no values and no total."
},
"entity": {
"type": "string"
},
"numerator": {
"type": "string",
"description": "ratio rows: the declared slice on top."
},
"denominator": {
"type": "string",
"description": "ratio rows: the declared slice underneath. A zero denominator publishes null, not zero."
},
"display": {
"type": "string",
"description": "How to RENDER the sign. Never changes what is summed: values carries the signed amount, so a consumer ignoring this still adds up."
},
"itemize": {
"type": "string",
"description": "One line per instance: the stream selector whose matches each render as their own line (a pack's row)."
},
"items": {
"type": "array",
"items": {
"type": "array",
"prefixItems": [
{
"type": "string"
},
{
"type": "string"
}
],
"minItems": 2,
"maxItems": 2
},
"description": "Conventional instances of an itemized row, as [stream, label] pairs, in the order a pro forma reads them."
}
}
},
"TermBound": {
"type": "object",
"additionalProperties": false,
"required": [
"reads",
"code"
],
"description": "The bound a DEFERRED contract term must meet when the run supplies its value. A literal is checked at compile time against the pack's validations; a term reading `inputs.` or `cfg.` cannot be, so the bound travels here under the validation's own code and the engine checks the value at run start (E5041).",
"properties": {
"reads": {
"type": "string",
"description": "`inputs.<name>` — the assumption the term reads."
},
"min": {
"type": "number"
},
"max": {
"type": "number"
},
"exclusive_min": {
"type": "number"
},
"exclusive_max": {
"type": "number"
},
"code": {
"type": "string",
"description": "The pack validation's code, cited by the run-time refusal."
},
"severity": {
"type": "string",
"enum": [
"error",
"warning",
"info"
],
"description": "The validation's severity: `warning` questions the value at run start and the run proceeds under the warning; `error` refuses it (E5041)."
}
}
},
"CompileWarning": {
"type": "object",
"additionalProperties": false,
"required": [
"code",
"severity",
"message"
],
"description": "A warning the compile raised and kept (docs/08 §3.2): the diagnostic object, severity `warning` or `info`. A pack convention questioned — psa_speed above 1000% — never an error, which would have failed the compile instead.",
"properties": {
"code": {
"type": "string"
},
"severity": {
"type": "string",
"enum": [
"warning",
"info"
]
},
"message": {
"type": "string"
},
"file": {
"type": [
"string",
"null"
]
},
"span": {},
"path": {
"type": [
"string",
"null"
]
},
"hint": {
"type": [
"string",
"null"
]
},
"notes": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"AssumeSource": {
"type": "object",
"additionalProperties": false,
"description": "Where the number came from. Every field is optional and an unknown one is refused at compile time. Part of what the model states, so part of the IR the model hash covers. An assumption with no source is recorded as stated in the model.",
"properties": {
"publisher": {
"type": "string",
"description": "Who produced the data."
},
"series": {
"type": "string",
"description": "The publisher's own identifier for it."
},
"as_of": {
"$ref": "#/$defs/Date",
"description": "The date the data describes."
},
"retrieved": {
"$ref": "#/$defs/Date",
"description": "When it was taken."
},
"url": {
"type": "string",
"description": "Where it was taken from."
},
"note": {
"type": "string",
"description": "Anything else a reviewer needs."
}
}
},
"AssumeSet": {
"type": "object",
"additionalProperties": false,
"required": [
"name"
],
"description": "The set this assumption is a field of. A set's fields are entered by full path — `market.new.ti_psf` — and the set itself carries no entry: it is read one field at a time.",
"properties": {
"name": {
"$ref": "#/$defs/Id",
"description": "The top-level set's name."
},
"type": {
"type": "string",
"description": "The pack set type (`CRE.Reference.MarketLeasing`) that typed the set, when one did."
}
}
},
"AssumeSeries": {
"type": "object",
"additionalProperties": false,
"required": [
"points"
],
"description": "The series shape of an assumption: the curve body one level down. Points sorted ascending by date, one value per date, every point inside the effective dates.",
"properties": {
"interpolation": {
"type": "string",
"enum": [
"step",
"linear"
],
"description": "`step` (flat-forward: the last point at or before the date, the first value before the first point) or `linear` (by calendar day between bracketing points)."
},
"effective_from": {
"type": "string",
"description": "First date the series has a value (YYYY-MM-DD). A read before it has no value and refuses the run (E5040); absent means the first point's value holds before it."
},
"effective_to": {
"type": "string",
"description": "Last date the series has a value (YYYY-MM-DD). A read after it has no value and refuses the run (E5040); absent means the last point's value holds flat past it, with W5024 where a reader runs past."
},
"points": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": [
"date",
"value"
],
"additionalProperties": false,
"properties": {
"date": {
"type": "string"
},
"value": {
"type": "number"
}
}
}
}
}
},
"AssumeQuantile": {
"type": "object",
"additionalProperties": false,
"required": [
"points"
],
"description": "The quantile shape of an assumption: values indexed by cumulative share. Always ascending by share: a declaration written `by exceedance` is reversed at compile time, so this document carries one orientation and no reader has to know which way the source was written.",
"properties": {
"interpolation": {
"type": "string",
"enum": [
"step",
"linear"
],
"description": "The same two words a series uses. It also fixes quadrature: `quantile_mean` is the exact integral of the interpolated function, so no separate mode is declared."
},
"points": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": [
"share",
"value"
],
"additionalProperties": false,
"properties": {
"share": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"value": {
"type": "number"
}
}
}
}
}
},
"FieldType": {
"type": "object",
"additionalProperties": false,
"required": [
"type"
],
"description": "A stated term's or field's declared type and unit, read off the pack field that admitted it. Carried because the engine is pack-free: what a number IS travels beside it, and the results publish both.",
"properties": {
"type": {
"type": "string",
"description": "The field's `field_type`: `decimal`, `integer`, `number`, `string`, `date`, `account` or `contract`."
},
"unit": {
"type": "string",
"description": "The field's declared unit (`USD/yr`, `ratio`, `months`), absent when the pack states none."
}
}
}
}
}