@hyperscale0/udl 2.6.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/CHANGELOG.md +2 -330
  2. package/LICENSING.md +1 -2
  3. package/README.md +3 -114
  4. package/TRADEMARKS.md +2 -2
  5. package/dist/diagnostics.d.ts +21 -201
  6. package/dist/diagnostics.d.ts.map +1 -1
  7. package/dist/diagnostics.js +29 -193
  8. package/dist/diagnostics.js.map +1 -1
  9. package/dist/evolution.d.ts +3 -132
  10. package/dist/evolution.d.ts.map +1 -1
  11. package/dist/evolution.js +29 -633
  12. package/dist/evolution.js.map +1 -1
  13. package/dist/finance.d.ts +5 -72
  14. package/dist/finance.d.ts.map +1 -1
  15. package/dist/finance.js +233 -737
  16. package/dist/finance.js.map +1 -1
  17. package/dist/index.d.ts +7 -18
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +7 -12
  20. package/dist/index.js.map +1 -1
  21. package/dist/instrument-references.d.ts.map +1 -1
  22. package/dist/instrument-references.js +3 -2
  23. package/dist/instrument-references.js.map +1 -1
  24. package/dist/schema.d.ts +2079 -4564
  25. package/dist/schema.d.ts.map +1 -1
  26. package/dist/schema.js +270 -1609
  27. package/dist/schema.js.map +1 -1
  28. package/dist/validation.d.ts +8 -104
  29. package/dist/validation.d.ts.map +1 -1
  30. package/dist/validation.js +653 -3271
  31. package/dist/validation.js.map +1 -1
  32. package/docs/README.md +147 -14
  33. package/package.json +7 -11
  34. package/spec/README.md +151 -164
  35. package/spec/darb.udl.json +249 -0
  36. package/spec/udl.schema.json +4104 -3202
  37. package/src/diagnostics.ts +51 -246
  38. package/src/evolution.ts +39 -1045
  39. package/src/finance.ts +287 -1127
  40. package/src/index.ts +18 -142
  41. package/src/instrument-references.ts +3 -2
  42. package/src/schema.ts +285 -1843
  43. package/src/validation.ts +870 -5611
  44. package/conformance/README.md +0 -82
  45. package/conformance/evolution/action-contract.expected.json +0 -10
  46. package/conformance/evolution/action-contract.live.udl +0 -44
  47. package/conformance/evolution/action-contract.next.udl +0 -45
  48. package/conformance/evolution/product-identity.expected.json +0 -10
  49. package/conformance/evolution/product-identity.live.udl +0 -55
  50. package/conformance/evolution/product-identity.next.udl +0 -55
  51. package/conformance/evolution/version-required.expected.json +0 -10
  52. package/conformance/evolution/version-required.live.udl +0 -55
  53. package/conformance/evolution/version-required.next.udl +0 -58
  54. package/conformance/invalid/action-without-transition.expected.json +0 -10
  55. package/conformance/invalid/action-without-transition.udl +0 -60
  56. package/conformance/invalid/agent-description-too-long.expected.json +0 -10
  57. package/conformance/invalid/agent-description-too-long.udl +0 -57
  58. package/conformance/invalid/blank-title.expected.json +0 -10
  59. package/conformance/invalid/blank-title.udl +0 -54
  60. package/conformance/invalid/call-binds-results.expected.json +0 -10
  61. package/conformance/invalid/call-binds-results.udl +0 -314
  62. package/conformance/invalid/call-unknown-action.expected.json +0 -10
  63. package/conformance/invalid/call-unknown-action.udl +0 -314
  64. package/conformance/invalid/composition-dial-duplicate-key.expected.json +0 -10
  65. package/conformance/invalid/composition-dial-duplicate-key.udl +0 -73
  66. package/conformance/invalid/depth-budget.expected.json +0 -10
  67. package/conformance/invalid/depth-budget.udl +0 -49
  68. package/conformance/invalid/duplicate-subject.expected.json +0 -10
  69. package/conformance/invalid/duplicate-subject.udl +0 -230
  70. package/conformance/invalid/forged-effects.expected.json +0 -10
  71. package/conformance/invalid/forged-effects.udl +0 -76
  72. package/conformance/invalid/format-version.expected.json +0 -10
  73. package/conformance/invalid/format-version.udl +0 -54
  74. package/conformance/invalid/instrument-id-not-snake-case.expected.json +0 -10
  75. package/conformance/invalid/instrument-id-not-snake-case.udl +0 -54
  76. package/conformance/invalid/invalid-aggregate-gate-shape.expected.json +0 -10
  77. package/conformance/invalid/invalid-aggregate-gate-shape.udl +0 -1819
  78. package/conformance/invalid/invalid-check-duration.expected.json +0 -10
  79. package/conformance/invalid/invalid-check-duration.udl +0 -219
  80. package/conformance/invalid/invalid-dial-anchor.expected.json +0 -10
  81. package/conformance/invalid/invalid-dial-anchor.udl +0 -219
  82. package/conformance/invalid/invalid-exception-parent-ref.expected.json +0 -10
  83. package/conformance/invalid/invalid-exception-parent-ref.udl +0 -2487
  84. package/conformance/invalid/invalid-exposure-shape.expected.json +0 -10
  85. package/conformance/invalid/invalid-exposure-shape.udl +0 -1819
  86. package/conformance/invalid/invalid-journeys.expected.json +0 -10
  87. package/conformance/invalid/invalid-journeys.udl +0 -77
  88. package/conformance/invalid/invalid-remainder.expected.json +0 -10
  89. package/conformance/invalid/invalid-remainder.udl +0 -220
  90. package/conformance/invalid/invalid-schema-keyword.expected.json +0 -10
  91. package/conformance/invalid/invalid-schema-keyword.udl +0 -220
  92. package/conformance/invalid/invalid-utf8.expected.json +0 -10
  93. package/conformance/invalid/invalid-utf8.udl +0 -1
  94. package/conformance/invalid/leaf-effect-mismatch.expected.json +0 -10
  95. package/conformance/invalid/leaf-effect-mismatch.udl +0 -314
  96. package/conformance/invalid/malformed-json.expected.json +0 -10
  97. package/conformance/invalid/malformed-json.udl +0 -1
  98. package/conformance/invalid/missing-create-action.expected.json +0 -10
  99. package/conformance/invalid/missing-create-action.udl +0 -48
  100. package/conformance/invalid/missing-exception-amount-field.expected.json +0 -10
  101. package/conformance/invalid/missing-exception-amount-field.udl +0 -2487
  102. package/conformance/invalid/missing-exception-contract.expected.json +0 -14
  103. package/conformance/invalid/missing-exception-contract.udl +0 -2523
  104. package/conformance/invalid/missing-exception-reason-field.expected.json +0 -10
  105. package/conformance/invalid/missing-exception-reason-field.udl +0 -2487
  106. package/conformance/invalid/not-an-object.expected.json +0 -10
  107. package/conformance/invalid/not-an-object.udl +0 -1
  108. package/conformance/invalid/payout-reconcile-not-a-bank-debit.expected.json +0 -10
  109. package/conformance/invalid/payout-reconcile-not-a-bank-debit.udl +0 -259
  110. package/conformance/invalid/piece-plan-without-partition.expected.json +0 -10
  111. package/conformance/invalid/piece-plan-without-partition.udl +0 -305
  112. package/conformance/invalid/private-action-independent-approval.expected.json +0 -10
  113. package/conformance/invalid/private-action-independent-approval.udl +0 -314
  114. package/conformance/invalid/quote-freeze-set-incomplete.expected.json +0 -10
  115. package/conformance/invalid/quote-freeze-set-incomplete.udl +0 -261
  116. package/conformance/invalid/quote-named-reference-gate.expected.json +0 -10
  117. package/conformance/invalid/quote-named-reference-gate.udl +0 -50
  118. package/conformance/invalid/reconcile-named-reference-gate.expected.json +0 -10
  119. package/conformance/invalid/reconcile-named-reference-gate.udl +0 -50
  120. package/conformance/invalid/unfund-order-not-reversed.expected.json +0 -10
  121. package/conformance/invalid/unfund-order-not-reversed.udl +0 -314
  122. package/conformance/invalid/unknown-key.expected.json +0 -10
  123. package/conformance/invalid/unknown-key.udl +0 -55
  124. package/conformance/invalid/unknown-reference-gate-field.expected.json +0 -10
  125. package/conformance/invalid/unknown-reference-gate-field.udl +0 -1819
  126. package/conformance/invalid/unknown-required-field.expected.json +0 -10
  127. package/conformance/invalid/unknown-required-field.udl +0 -220
  128. package/conformance/invalid/unreachable-state.expected.json +0 -10
  129. package/conformance/invalid/unreachable-state.udl +0 -55
  130. package/conformance/invalid/wrong-exception-amount-field.expected.json +0 -10
  131. package/conformance/invalid/wrong-exception-amount-field.udl +0 -2487
  132. package/conformance/invalid/wrong-exception-reason-field.expected.json +0 -10
  133. package/conformance/invalid/wrong-exception-reason-field.udl +0 -2487
  134. package/conformance/valid/agent-description.expected.json +0 -6
  135. package/conformance/valid/agent-description.udl +0 -66
  136. package/conformance/valid/attested.expected.json +0 -6
  137. package/conformance/valid/attested.udl +0 -251
  138. package/conformance/valid/cards.expected.json +0 -6
  139. package/conformance/valid/cards.udl +0 -1579
  140. package/conformance/valid/commerce-escrow.expected.json +0 -6
  141. package/conformance/valid/commerce-escrow.udl +0 -1512
  142. package/conformance/valid/compiled-crowdfunding.expected.json +0 -6
  143. package/conformance/valid/compiled-crowdfunding.udl +0 -1843
  144. package/conformance/valid/compiled-watch-club.expected.json +0 -6
  145. package/conformance/valid/compiled-watch-club.udl +0 -2486
  146. package/conformance/valid/complete-contract.expected.json +0 -6
  147. package/conformance/valid/complete-contract.udl +0 -218
  148. package/conformance/valid/effect-signatures.expected.json +0 -6
  149. package/conformance/valid/effect-signatures.udl +0 -75
  150. package/conformance/valid/hand-edited.expected.json +0 -6
  151. package/conformance/valid/hand-edited.udl +0 -1
  152. package/conformance/valid/insured-car-marketplace.expected.json +0 -6
  153. package/conformance/valid/insured-car-marketplace.udl +0 -1050
  154. package/conformance/valid/insured-travel.expected.json +0 -6
  155. package/conformance/valid/insured-travel.udl +0 -3469
  156. package/conformance/valid/minimal.expected.json +0 -6
  157. package/conformance/valid/minimal.udl +0 -62
  158. package/conformance/valid/piece-plan-calls.expected.json +0 -6
  159. package/conformance/valid/piece-plan-calls.udl +0 -314
  160. package/conformance/valid/protection.expected.json +0 -6
  161. package/conformance/valid/protection.udl +0 -1551
  162. package/conformance/valid/string-escaping.expected.json +0 -6
  163. package/conformance/valid/string-escaping.udl +0 -54
  164. package/conformance/valid/vocabulary.expected.json +0 -6
  165. package/conformance/valid/vocabulary.udl +0 -2012
  166. package/dist/allocation.d.ts +0 -60
  167. package/dist/allocation.d.ts.map +0 -1
  168. package/dist/allocation.js +0 -177
  169. package/dist/allocation.js.map +0 -1
  170. package/dist/check-profiles.d.ts +0 -57
  171. package/dist/check-profiles.d.ts.map +0 -1
  172. package/dist/check-profiles.js +0 -62
  173. package/dist/check-profiles.js.map +0 -1
  174. package/dist/distribution.d.ts +0 -15
  175. package/dist/distribution.d.ts.map +0 -1
  176. package/dist/distribution.js +0 -49
  177. package/dist/distribution.js.map +0 -1
  178. package/dist/effects.d.ts +0 -58
  179. package/dist/effects.d.ts.map +0 -1
  180. package/dist/effects.js +0 -1126
  181. package/dist/effects.js.map +0 -1
  182. package/dist/reference.d.ts +0 -3
  183. package/dist/reference.d.ts.map +0 -1
  184. package/dist/reference.js +0 -28
  185. package/dist/reference.js.map +0 -1
  186. package/dist/vocabulary.d.ts +0 -23
  187. package/dist/vocabulary.d.ts.map +0 -1
  188. package/dist/vocabulary.js +0 -1052
  189. package/dist/vocabulary.js.map +0 -1
  190. package/docs/funding-custody.md +0 -165
  191. package/docs/guide/01-a-document.md +0 -37
  192. package/docs/guide/02-money-steps.md +0 -23
  193. package/docs/guide/03-laws.md +0 -18
  194. package/docs/guide/04-fees-and-remainder.md +0 -36
  195. package/docs/guide/05-checks-updates-dials.md +0 -7
  196. package/docs/guide/06-effects.md +0 -11
  197. package/docs/guide/07-evolution.md +0 -11
  198. package/docs/guide/08-implementing.md +0 -13
  199. package/docs/guide/09-schedules-and-allocation.md +0 -132
  200. package/docs/llms-full.txt +0 -2008
  201. package/docs/llms.txt +0 -14
  202. package/docs/piece-plans.md +0 -148
  203. package/docs/reference/canonical.md +0 -16
  204. package/docs/reference/clauses.md +0 -1585
  205. package/docs/reference/cli.md +0 -24
  206. package/docs/reference/diagnostics.md +0 -38
  207. package/skills/udl/SKILL.md +0 -28
  208. package/src/allocation.ts +0 -259
  209. package/src/check-profiles.ts +0 -80
  210. package/src/distribution.ts +0 -61
  211. package/src/effects.ts +0 -1920
  212. package/src/reference.ts +0 -31
  213. package/src/vocabulary.ts +0 -1635
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperscale0/udl",
3
- "version": "2.6.1",
3
+ "version": "3.0.0",
4
4
  "description": "The Universal Domain Language: format spec, parser, validator, canonical serializer, and evolution diff.",
5
5
  "keywords": [
6
6
  "udl",
@@ -35,7 +35,6 @@
35
35
  "default": "./dist/index.js"
36
36
  },
37
37
  "./spec/*": "./spec/*",
38
- "./conformance/*": "./conformance/*",
39
38
  "./package.json": "./package.json"
40
39
  },
41
40
  "files": [
@@ -46,10 +45,8 @@
46
45
  "README.md",
47
46
  "SECURITY.md",
48
47
  "TRADEMARKS.md",
49
- "conformance",
50
48
  "docs",
51
49
  "dist",
52
- "skills",
53
50
  "spec",
54
51
  "src"
55
52
  ],
@@ -60,18 +57,17 @@
60
57
  "access": "public"
61
58
  },
62
59
  "scripts": {
63
- "build": "bun run docs:build && tsc -p tsconfig.build.json",
64
- "check": "bun run spec:check && bun run docs:build && bun test && tsc -p tsconfig.json && bun run build && bun scripts/check-bin.ts",
65
- "docs:build": "bun scripts/docs/build.ts",
60
+ "build": "tsc -p tsconfig.build.json",
61
+ "typecheck": "tsc -p tsconfig.json",
62
+ "test": "bun test test",
63
+ "docs:build": "bun scripts/docs.ts",
66
64
  "postpack": "bun scripts/pack-exports.ts restore",
67
65
  "prepack": "bun run build && bun scripts/pack-exports.ts apply",
68
66
  "spec": "bun scripts/emit-spec.ts --write",
69
67
  "spec:check": "bun scripts/emit-spec.ts --check",
70
- "test": "bun test",
71
- "typecheck": "tsc -p tsconfig.json"
68
+ "check": "bun run spec:check && bun run docs:build && bun run typecheck && bun run build && bun test test"
72
69
  },
73
70
  "dependencies": {
74
- "@cfworker/json-schema": "4.1.1",
75
71
  "zod": "4.5.4"
76
72
  },
77
73
  "devDependencies": {
@@ -83,5 +79,5 @@
83
79
  "Amir Ayub",
84
80
  "Sara AlBakaawi"
85
81
  ],
86
- "gitHead": "2d3b3700d68ef565737e206897921f64f4d2cb16"
82
+ "gitHead": "b84ee93a5016401a5f8287745a2fb23ba5647e43"
87
83
  }
package/spec/README.md CHANGED
@@ -1,164 +1,151 @@
1
- # The UDL specification
2
-
3
- A UDL document describes one product in business terms: its subjects, its
4
- instruments, each instrument's lifecycle, the actions that move instances through it, and how
5
- money moves while they do. Everything generated from a document (SDKs, docs,
6
- tool surfaces, the running engine) is downstream of what is written here.
7
-
8
- UDL is the platform's header files and driver framework. Its grammar is the
9
- complete instrument vocabulary and the ABI between authored programs and the
10
- engine. An instrument definition can round-trip through UDL without dropping a
11
- clause.
12
-
13
- ## The schema is the authority
14
-
15
- `udl.schema.json` in this directory is JSON Schema 2020-12, generated from the
16
- Zod grammar in `src/schema.ts` by `scripts/emit-spec.ts`. It is checked in so
17
- that an implementation in any language can read it, and `bun run spec:check`
18
- fails the build when the committed bytes stop matching the generator.
19
-
20
- Prose copies of a machine-checked format drift. This document therefore carries
21
- no grammar tables and no field lists. Where anything below disagrees with
22
- `udl.schema.json`, the schema wins.
23
-
24
- The schema is generated from the _input_ view of the grammar, so it describes a
25
- document as an author writes it, before any default is filled in. A action may
26
- omit `moves`; a parser hands it back as `[]`.
27
-
28
- ## The ten laws
29
-
30
- These are judgment, not shape. They explain why the schema refuses what it
31
- refuses.
32
-
33
- 1. **One-sentence law.** Every concept in UDL is explainable in one sentence to
34
- someone who has never seen it. A concept that needs a paragraph is
35
- machinery, and machinery does not belong in the language.
36
- 2. **Purity law.** No bank or provider legacy enters UDL: no provider statement
37
- schemas, file drops, polling, batch windows, cutoff times, scheme names,
38
- currency reconciliation rules, or ISO or SWIFT message types. A `reconcile`
39
- clause names settlement evidence against a declared provider-side row. It
40
- does not model the provider file, transport, or matching machinery.
41
- 3. **Event law.** Every state change emits an event. Names derive from the instrument
42
- and the action's past tense (`escrow_order.released`); the optional
43
- `eventName` overrides only where the honest past tense is irregular.
44
- 4. **One-spine law.** Internal ledger money moves through four instructions and no others:
45
- `internal_transfer.create`, `.reserve`, `.post`, `.void`. Three account
46
- instructions (`account.escrow.provision`, `account.freeze`,
47
- `account.unfreeze`) complete the sealed kernel set. A `payout` intent hands
48
- a stored amount and beneficiary reference to the execution core. It is not
49
- a kernel instruction and cannot disguise an internal ledger move.
50
- 5. **Uniform object law.** Every instance carries an opaque prefixed id (from
51
- the instrument's `idPrefix`), a `status` drawn from its declared lifecycle, a
52
- creation timestamp, and a caller-owned metadata bag. Amounts are
53
- string-encoded integer minor units paired with a currency code, never JSON
54
- numbers.
55
- 6. **Requirements-as-data law.** Anything a caller must satisfy before a action
56
- unlocks is declared data: `due`, `deadline`, `requiresRefs`,
57
- `requiresChecks`, `requiresExposure`, `requiresAggregate`, `remainder`,
58
- `requiresDrainedAccount`, `commit`, `reconcile`. A reconcile declares one
59
- expectation: the amount, the currency, `credit` or `debit`, the ref naming
60
- the provider-side row, exactly one evidence source, a match law of `exact`,
61
- `tolerance` bounded by a named dial, or `window`, and a window given as a
62
- fixed duration or a stored deadline field. It ends matched or as a capped
63
- exception child, never as a silent wait; the child is declared and validated
64
- at admission rather than materialized, so past the window the transition
65
- refuses naming it. Settlement evidence for a payout is one reconcile against
66
- a debit statement line under any match law, reading the reference an earlier
67
- payout intent captured. No caller may assert that match. An action that carries `quote` prices a base into a charge
68
- and a net, names the fields the price depends on, and declares when the offer
69
- dies, as a fixed duration or a stored deadline field. Exactly one other
70
- action names it back through `commit`, and that action is the only one that
71
- may move the net. A `commit` gate reads the offer the quoting action priced
72
- and refuses once its deadline has passed or any frozen field has changed. It
73
- spends what the quote wrote instead of pricing again, so the number a caller
74
- is shown is the number they pay.
75
- 7. **Append-only evolution law.** Once a definition has live instances, adding
76
- states, transitions, optional fields, and actions is legal; removing,
77
- renaming, tightening, or changing a money step is not.
78
- `diffValidatedUdlEvolution` decides, on two documents the validator has
79
- already admitted; `udl diff` parses both files and then calls it. Evolution
80
- snapshots retain navigation, update examples, and parked-state reasons for
81
- complete inspection. The diff treats their prose as editable presentation
82
- and protects the parked state keys that carry lifecycle meaning.
83
- 8. **Naming law.** Instruments are `snake_case`, singular, plain business English.
84
- Actions are single words in imperative present. Operations are `instrument.action`.
85
- Fields are `camelCase`. The patterns live in the schema.
86
- 9. **Time law.** Delays the world imposes (settlement windows, activation
87
- periods, renewal cycles, retries) surface as honest statuses and timestamps
88
- on objects, never as processes the caller has to operate.
89
- 10. **Closure law.** A document is self-contained. Every lifecycle state is
90
- reachable from `create`, every reference resolves inside the document,
91
- every gate names a state that exists, and every funded balance is drained
92
- on every terminal path.
93
-
94
- ## Admission budgets and derived effects
95
-
96
- The 10,000-node limit bounds authored program complexity. The compiler derives
97
- `effects` rows from action clauses, so the node counter excludes every action
98
- `effects` subtree. Admission still checks those subtrees for shape, exact
99
- agreement with the clauses including row order, nesting depth, string limits,
100
- JSON-only values, and cycles.
101
-
102
- The canonical 33-instrument catalog measured 10,270 nodes with derived effects
103
- and 9,321 authored nodes without the effect subtrees on 2026-09-02. The fixed
104
- 10,000-node limit therefore leaves 679 authored nodes of headroom. The full
105
- effectful document validates as one document.
106
-
107
- ## What the schema cannot say
108
-
109
- JSON Schema pins shape. It does not pin meaning, and four classes of law live
110
- outside it:
111
-
112
- - **Blankness.** The schema says `"type": "string"` where the grammar says a
113
- string whose trimmed length is non-zero. A title of `" "` passes the schema
114
- and is refused by the parser.
115
- - **Closure and reference resolution.** Reachability, gate targets, party
116
- bindings, aggregate links, and authored-example validation are whole-document
117
- properties.
118
- - **Money-graph admission.** Whether a debit can be reached before its funding,
119
- and whether any terminal path strands value, is decided by walking the
120
- lifecycle.
121
- - **Evolution.** The legality of a change is a property of two documents, not
122
- one.
123
-
124
- All four are pinned as data in `../conformance/`. An implementation that reads
125
- only `udl.schema.json` will admit documents this one refuses; the conformance
126
- suite is what makes two implementations agree.
127
-
128
- ## Issue codes
129
-
130
- A refusal reports one or more issues, each with a stable `UDL####` code, a
131
- category, a fix, and a JSON path. Codes and paths form the conformance
132
- contract. Messages may become clearer. The generated
133
- [diagnostic reference](../docs/reference/diagnostics.md) lists every code.
134
-
135
- Paths are `$`-rooted with dotted keys and bracketed array indices:
136
- `$.instruments[0].lifecycle.states[2]`.
137
-
138
- ## Canonical form
139
-
140
- Every admitted document has one canonical byte sequence. The normative
141
- [canonical bytes law](../docs/reference/canonical.md) defines key and array
142
- order, number and string encoding, empty containers, the trailing line feed,
143
- and the SHA-256 digest. Serialization validates first, so an invalid document
144
- has no canonical form.
145
-
146
- ## Two version numbers
147
-
148
- **Format version** is what a document declares, as the literal `"udl": 1`. A
149
- document that declares any other value is refused. Format 1 is the only format
150
- that exists.
151
-
152
- **Package version** is the semver of `@hyperscale0/udl`, declared in
153
- `package.json`.
154
-
155
- They move independently. In development mode, contract and schema shapes change
156
- without deprecation paths or frozen compatibility promises.
157
-
158
- The evolution diff API (`diffValidatedUdlEvolution` and `diffInstrumentEvolution`)
159
- evaluates append-only rules between two admitted documents. The evolution codes
160
- are explicit. `UDL7001` protects stored identities, subjects, instruments, fields,
161
- lifecycles, actions, money clauses, gates, and policy against removal, renaming,
162
- or tightening. `UDL7002` requires a version increase for a semantic change.
163
- Evolution comparison admits the previous document first. A stored document
164
- that fails admission is `invalid_previous`, not an evolution issue.
1
+ # UDL 3
2
+
3
+ UDL is the typed contract between an HSX program and its executor. The grammar
4
+ lives in `src/schema.ts`. Generate `udl.schema.json` with
5
+ `bun scripts/emit-spec.ts --write`. There is no migration reader for earlier UDL.
6
+
7
+ ## Accounts and money
8
+
9
+ A document declares SAR once. Money is a nonnegative minor-unit decimal string
10
+ of at most 18 digits. Percentages are basis points, durations are positive integer
11
+ milliseconds, and dates are timestamps with explicit offsets.
12
+
13
+ An account field either binds a party or declares an account owned by the
14
+ instrument. `owner: "self"` replaces the separate custody concept. Wallet, pool,
15
+ receivable and entitlement are uses of accounts, not different field types.
16
+ An external account is marked `external: true`. Its bank binding belongs to the
17
+ executor; programs and callers never supply bank beneficiary ids. Account
18
+ creation and binding are executor work, not caller-controlled instructions.
19
+ Accounts declare `book: "cash" | "claim"`, defaulting to cash. Moves never cross books.
20
+ Only claim accounts may declare `contra: true` and permit a negative balance.
21
+ An external account must bind a party and use the cash book. A party-bound field
22
+ is keyed by owner, book and key across the product. Its optional key defaults
23
+ to `balance`, so payer and borrower can alias the same party account. An account
24
+ owned by self defaults its key to the field name and is provisioned per instance.
25
+ Named capital, profit income, debt and loss accounts declare explicit keys. `party.buyer` is the buyer's default cash account.
26
+
27
+ Disbursement moves cash from lender to borrower and claims from borrower debt to
28
+ principal and profit receivables. Repayment moves borrower cash to lender cash and
29
+ the same claim amounts from receivables back to borrower debt. Unearned profit is
30
+ cancelled by the claim move alone. Write-off moves the principal claim to the
31
+ lender's loss account, with no cash movement. Profit is earned when a piece is paid.
32
+ These are ordinary paired moves in HSX, not executor loan rules.
33
+
34
+ A value is `{literal: value}` or `{field: path}`. Paths start with `self`, `input`
35
+ or `party`. Reference fields allow typed traversal. Account paths expose locked,
36
+ read-only `.balance` and `.reserved` money values. `self.id`, `self.status`,
37
+ `self.createdAt` and `self.now` are sealed executor values. Callers cannot set
38
+ constants, calculated fields, account bindings or capture fields. Create supplies
39
+ declared typed references; later actions cannot replace them. A ref target is one
40
+ instrument id or a list of 1 to 16 distinct instrument ids. The stored value is
41
+ one instance id from any listed instrument. Path traversal exposes only fields
42
+ with compatible types on every target. Nested refs combine their target sets;
43
+ accounts must agree on owner, key, book, contra and external flags, and enums must
44
+ have the same values. A comparison or selection anchor must share a possible
45
+ reference target. An invoked input must accept every possible supplied target.
46
+ The executor checks the actual instance type at admission.
47
+
48
+ ## Calculation and movement
49
+
50
+ Calculations form a finite dependency graph. `sum`, `subtract` and `minimum`
51
+ operate on money or integer fields, with operands of the same type as the target.
52
+ `rate`, `multiply`, `divide` and `shift` retain their typed operands. Rate and
53
+ division round down. Weighted shares also round down; residual minor units go
54
+ to the declared residual account. Cash and loss use this one rounding rule.
55
+ There is no largest-remainder allocation. Subtraction refuses a negative result. Integer results must
56
+ be safe integers. No calculation evaluates source text.
57
+
58
+ The only move instructions are `internal_transfer.create`,
59
+ `internal_transfer.reserve`, `internal_transfer.post` and `internal_transfer.void`.
60
+ Create and reserve declare an amount, from account and to account. Reserve captures
61
+ its executor-produced transfer identity into a declared self text field. Post and
62
+ void consume that identity. Callers cannot create or replace captured identities.
63
+ An external destination uses the same move vocabulary.
64
+
65
+ A loan, refund, payoff, write-off or distribution is library behavior built from
66
+ accounts, calculations and ordered moves. None has a privileged executor clause.
67
+ Outstanding principal is an account balance. A schedule consists of explicit
68
+ dated child records, with positions 1 through n in declaration order. The library
69
+ uses ordinary comparisons and aggregates to constrain those records. There is no
70
+ recurrence process, allocation bucket, partition expander or schedule requirement
71
+ in the UDL kernel.
72
+
73
+ ## Admission and lifecycle
74
+
75
+ Actions declare typed input lists, requirements, an actor and an event. Lifecycle
76
+ edges name their source and destination states. Requirements and effects execute
77
+ atomically under the same account and reference locks. `set` copies typed values
78
+ to mutable fields. Action calculations read the locked snapshot and populate the
79
+ action draft before requirements. Ordered moves and invocations follow admission;
80
+ invariants check the completed transaction. A due instant
81
+ is inclusive; a deadline is exclusive. Clock delays never extend deadlines.
82
+
83
+ Requirements are compare, state, unique, aggregate, approval, evidence and hours.
84
+ A typed selection names one instrument or a bounded union, a reference field,
85
+ anchor, accepted states and row limit. Exceeding the limit refuses rather than
86
+ truncates. Optional equality filters apply to every selected type. An optional
87
+ window selects date values between `self.now - milliseconds` and `self.now`.
88
+ Aggregate sums use typed paths on the selected records, including account
89
+ balances. An invariant holds before and after every affected transaction.
90
+
91
+ `hours` converts a date path to its literal IANA timezone and accepts `[start,end)`.
92
+ A start greater than end wraps midnight; equal endpoints admit no time.
93
+ `evidence` declares subject, family, check, result and maxAge. The subject is an
94
+ account or text id. The executor selects the newest completed check for that
95
+ subject, family and kind, refuses stale evidence and requires the declared result.
96
+
97
+ An approval freezes target, action, material input, authenticated party, expiry
98
+ and Build identity. The executor produces the digest. A requirement consumes the
99
+ matching approved or declined decision once in the same transaction. `target`
100
+ defaults to self and `action` to the current action. `invoke` supplies typed inputs
101
+ to a linked action or bounded selection. Its graph is acyclic and bounded.
102
+ Public names grant no authority; clock and parent actors remain executor-owned.
103
+
104
+ A captured move exposes a sealed `.status` path with reserved, posted, settled,
105
+ reversed or voided. Voided means a reservation was released. Settled means the outbox received provider confirmation. Reversed
106
+ means provider failure produced a reversal move. A library payout reconciliation
107
+ compares that status with settled at its deadline. Account balances reflect the
108
+ immediate internal ledger and cannot prove an individual bank completion.
109
+ Aggregate bank reconciliation is an operations concern outside the contract.
110
+ There is no special reconciliation or exception-creation clause.
111
+
112
+ ## Ten laws
113
+
114
+ 1. Each concept has one concrete meaning.
115
+ 2. Provider transport and credentials stay outside the contract.
116
+ 3. Every state change emits its declared event.
117
+ 4. Four transfer instructions are the only money movement vocabulary.
118
+ 5. Instances have an opaque identity and money uses integer minor units.
119
+ 6. Typed requirements and effects execute atomically.
120
+ 7. Live additions preserve existing meaning; development estates may recreate.
121
+ 8. Business names identify instruments and their public actions.
122
+ 9. Waiting uses lifecycle states and clocks. Before admission, the executor
123
+ catches up actor: clock actions with due instants on the locked instance,
124
+ references and selected rows, in chronological order. Catch-up commits its
125
+ own transaction and events before the caller request is evaluated. A refused
126
+ caller request rolls back only its own drafts. Caller deadlines are refusal
127
+ boundaries; they never execute the caller action. Separate clock actions
128
+ carry expiry consequences. Requirements and selections observe the resulting
129
+ clock state.
130
+ 10. References close, states are reachable, captures are linear, and terminal
131
+ instances have no remaining owned-account balances or reservations.
132
+
133
+ ## Clause inventory
134
+
135
+ Document: `udl`, `version`, `product`, `title`, `currency`, `parties`, `instruments`.
136
+ Instrument: `id`, `title`, `summary`, `fields`, `calculate`, `lifecycle`, `actions`,
137
+ `actionOrder`, `invariants`, `examples`.
138
+ Action: `summary`, `publicAction`, `event`, `actor`, `input`, `requires`, `due`,
139
+ `deadline`, `set`, `calculate`, `moves`, `invoke`, `approval`.
140
+
141
+ The owner reduced the kernel on 17 September 2026. `allocation`, `allocate`,
142
+ `distribute`, `payout`, `reconcile`, `partitions`, `steps`, `drained`, the allocation
143
+ requirement and the schedule requirement were removed. They described library
144
+ work or duplicated accounts, comparisons and moves. The earlier UDL dialect's
145
+ JSON Schema fields, x-extensions, bind maps, pieceStage, contributionStage,
146
+ templateBinding, piecePlan, signedSum, engineOwned and captureEngine are absent.
147
+ Typed fields, calculations, account ownership and linear captures replace them.
148
+
149
+ `calculate.aggregate` reads a typed selection and yields its count or a money sum. `calculate.ratio` computes floor(amount * numerator / denominator) with arbitrary-precision intermediates and refuses a zero denominator. Numerator and denominator share a numeric type. Selection order is a list of typed ascending paths, followed by identity as the final tie-break. `invoke {instrument, action: "create", input}` creates a child record in the same transaction; its inputs resolve in the caller, and the ordinary create actor and requirements still apply.
150
+
151
+ `calculate.at` reads a typed list at a one-based position and refuses an out-of-range index. Its result has the list item type. Integer divide accepts integer operands and rounds down. Every move may capture its transfer identity into a declared self text field; reserve requires a capture. Captures and their status paths are executor-owned.