@hyperscale0/udl 1.0.0-beta.1 → 1.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 (179) hide show
  1. package/CHANGELOG.md +78 -25
  2. package/README.md +37 -99
  3. package/conformance/README.md +44 -41
  4. package/conformance/evolution/action-contract.expected.json +10 -0
  5. package/conformance/evolution/action-contract.live.udl +44 -0
  6. package/conformance/evolution/action-contract.next.udl +45 -0
  7. package/conformance/evolution/product-identity.expected.json +10 -0
  8. package/conformance/evolution/product-identity.live.udl +55 -0
  9. package/conformance/evolution/product-identity.next.udl +55 -0
  10. package/conformance/evolution/version-required.expected.json +10 -0
  11. package/conformance/evolution/version-required.live.udl +55 -0
  12. package/conformance/evolution/version-required.next.udl +58 -0
  13. package/conformance/invalid/action-without-transition.expected.json +10 -0
  14. package/conformance/invalid/{verb-without-transition.udl → action-without-transition.udl} +7 -2
  15. package/conformance/invalid/agent-description-too-long.expected.json +10 -0
  16. package/conformance/invalid/agent-description-too-long.udl +57 -0
  17. package/conformance/invalid/blank-title.expected.json +1 -1
  18. package/conformance/invalid/blank-title.udl +6 -2
  19. package/conformance/invalid/composition-dial-duplicate-key.expected.json +10 -0
  20. package/conformance/invalid/composition-dial-duplicate-key.udl +73 -0
  21. package/conformance/invalid/depth-budget.expected.json +1 -1
  22. package/conformance/invalid/duplicate-subject.expected.json +10 -0
  23. package/conformance/invalid/duplicate-subject.udl +230 -0
  24. package/conformance/invalid/forged-effects.expected.json +10 -0
  25. package/conformance/invalid/forged-effects.udl +76 -0
  26. package/conformance/invalid/format-version.expected.json +1 -1
  27. package/conformance/invalid/format-version.udl +6 -2
  28. package/conformance/invalid/instrument-id-not-snake-case.expected.json +10 -0
  29. package/conformance/invalid/{noun-id-not-snake-case.udl → instrument-id-not-snake-case.udl} +6 -2
  30. package/conformance/invalid/invalid-aggregate-gate-shape.expected.json +10 -0
  31. package/conformance/invalid/invalid-aggregate-gate-shape.udl +1819 -0
  32. package/conformance/invalid/invalid-check-duration.expected.json +10 -0
  33. package/conformance/invalid/invalid-check-duration.udl +219 -0
  34. package/conformance/invalid/invalid-dial-anchor.expected.json +10 -0
  35. package/conformance/invalid/invalid-dial-anchor.udl +219 -0
  36. package/conformance/invalid/invalid-exception-parent-ref.expected.json +10 -0
  37. package/conformance/invalid/invalid-exception-parent-ref.udl +2487 -0
  38. package/conformance/invalid/invalid-exposure-shape.expected.json +10 -0
  39. package/conformance/invalid/invalid-exposure-shape.udl +1819 -0
  40. package/conformance/invalid/invalid-remainder.expected.json +10 -0
  41. package/conformance/invalid/invalid-remainder.udl +220 -0
  42. package/conformance/invalid/invalid-schema-keyword.expected.json +10 -0
  43. package/conformance/invalid/invalid-schema-keyword.udl +220 -0
  44. package/conformance/invalid/invalid-utf8.expected.json +1 -1
  45. package/conformance/invalid/malformed-json.expected.json +1 -1
  46. package/conformance/invalid/missing-create-action.expected.json +10 -0
  47. package/conformance/invalid/{missing-create-verb.udl → missing-create-action.udl} +5 -2
  48. package/conformance/invalid/missing-exception-amount-field.expected.json +10 -0
  49. package/conformance/invalid/missing-exception-amount-field.udl +2487 -0
  50. package/conformance/invalid/missing-exception-contract.expected.json +14 -0
  51. package/conformance/invalid/missing-exception-contract.udl +2523 -0
  52. package/conformance/invalid/missing-exception-reason-field.expected.json +10 -0
  53. package/conformance/invalid/missing-exception-reason-field.udl +2487 -0
  54. package/conformance/invalid/not-an-object.expected.json +1 -1
  55. package/conformance/invalid/payout-reconcile-not-a-bank-debit.expected.json +10 -0
  56. package/conformance/invalid/payout-reconcile-not-a-bank-debit.udl +259 -0
  57. package/conformance/invalid/quote-freeze-set-incomplete.expected.json +10 -0
  58. package/conformance/invalid/quote-freeze-set-incomplete.udl +261 -0
  59. package/conformance/invalid/quote-named-reference-gate.expected.json +10 -0
  60. package/conformance/invalid/quote-named-reference-gate.udl +50 -0
  61. package/conformance/invalid/reconcile-named-reference-gate.expected.json +10 -0
  62. package/conformance/invalid/reconcile-named-reference-gate.udl +50 -0
  63. package/conformance/invalid/unknown-key.expected.json +1 -1
  64. package/conformance/invalid/unknown-key.udl +6 -2
  65. package/conformance/invalid/unknown-reference-gate-field.expected.json +10 -0
  66. package/conformance/invalid/unknown-reference-gate-field.udl +1819 -0
  67. package/conformance/invalid/unknown-required-field.expected.json +10 -0
  68. package/conformance/invalid/unknown-required-field.udl +220 -0
  69. package/conformance/invalid/unreachable-state.expected.json +2 -2
  70. package/conformance/invalid/unreachable-state.udl +6 -2
  71. package/conformance/invalid/wrong-exception-amount-field.expected.json +10 -0
  72. package/conformance/invalid/wrong-exception-amount-field.udl +2487 -0
  73. package/conformance/invalid/wrong-exception-reason-field.expected.json +10 -0
  74. package/conformance/invalid/wrong-exception-reason-field.udl +2487 -0
  75. package/conformance/valid/agent-description.expected.json +6 -0
  76. package/conformance/valid/agent-description.udl +66 -0
  77. package/conformance/valid/cards.expected.json +1 -0
  78. package/conformance/valid/cards.udl +843 -356
  79. package/conformance/valid/commerce-escrow.expected.json +1 -0
  80. package/conformance/valid/commerce-escrow.udl +792 -291
  81. package/conformance/valid/compiled-crowdfunding.expected.json +6 -0
  82. package/conformance/valid/compiled-crowdfunding.udl +1843 -0
  83. package/conformance/valid/compiled-watch-club.expected.json +6 -0
  84. package/conformance/valid/compiled-watch-club.udl +2486 -0
  85. package/conformance/valid/complete-contract.expected.json +6 -0
  86. package/conformance/valid/complete-contract.udl +218 -0
  87. package/conformance/valid/effect-signatures.expected.json +6 -0
  88. package/conformance/valid/effect-signatures.udl +75 -0
  89. package/conformance/valid/hand-edited.expected.json +1 -0
  90. package/conformance/valid/hand-edited.udl +1 -1
  91. package/conformance/valid/insured-car-marketplace.expected.json +1 -0
  92. package/conformance/valid/insured-car-marketplace.udl +534 -315
  93. package/conformance/valid/insured-travel.expected.json +2 -1
  94. package/conformance/valid/insured-travel.udl +1489 -641
  95. package/conformance/valid/minimal.expected.json +1 -0
  96. package/conformance/valid/minimal.udl +18 -14
  97. package/conformance/valid/protection.expected.json +1 -0
  98. package/conformance/valid/protection.udl +719 -304
  99. package/conformance/valid/string-escaping.expected.json +6 -0
  100. package/conformance/valid/string-escaping.udl +54 -0
  101. package/dist/canonical.d.ts +2 -0
  102. package/dist/canonical.d.ts.map +1 -1
  103. package/dist/canonical.js +7 -0
  104. package/dist/canonical.js.map +1 -1
  105. package/dist/check-profiles.d.ts +57 -0
  106. package/dist/check-profiles.d.ts.map +1 -0
  107. package/dist/check-profiles.js +62 -0
  108. package/dist/check-profiles.js.map +1 -0
  109. package/dist/cli.js +48 -4
  110. package/dist/cli.js.map +1 -1
  111. package/dist/diagnostics.d.ts +168 -0
  112. package/dist/diagnostics.d.ts.map +1 -0
  113. package/dist/diagnostics.js +164 -0
  114. package/dist/diagnostics.js.map +1 -0
  115. package/dist/effects.d.ts +41 -0
  116. package/dist/effects.d.ts.map +1 -0
  117. package/dist/effects.js +110 -0
  118. package/dist/effects.js.map +1 -0
  119. package/dist/evolution.d.ts +51 -18
  120. package/dist/evolution.d.ts.map +1 -1
  121. package/dist/evolution.js +244 -102
  122. package/dist/evolution.js.map +1 -1
  123. package/dist/finance.d.ts +17 -13
  124. package/dist/finance.d.ts.map +1 -1
  125. package/dist/finance.js +165 -109
  126. package/dist/finance.js.map +1 -1
  127. package/dist/index.d.ts +12 -8
  128. package/dist/index.d.ts.map +1 -1
  129. package/dist/index.js +8 -5
  130. package/dist/index.js.map +1 -1
  131. package/dist/limits.d.ts +2 -2
  132. package/dist/limits.d.ts.map +1 -1
  133. package/dist/limits.js +4 -3
  134. package/dist/limits.js.map +1 -1
  135. package/dist/parser.d.ts.map +1 -1
  136. package/dist/parser.js +6 -16
  137. package/dist/parser.js.map +1 -1
  138. package/dist/schema.d.ts +2059 -135
  139. package/dist/schema.d.ts.map +1 -1
  140. package/dist/schema.js +759 -73
  141. package/dist/schema.js.map +1 -1
  142. package/dist/validation.d.ts +22 -9
  143. package/dist/validation.d.ts.map +1 -1
  144. package/dist/validation.js +1463 -457
  145. package/dist/validation.js.map +1 -1
  146. package/docs/README.md +14 -0
  147. package/docs/guide/01-a-document.md +37 -0
  148. package/docs/guide/02-money-steps.md +23 -0
  149. package/docs/guide/03-laws.md +18 -0
  150. package/docs/guide/04-fees-and-remainder.md +15 -0
  151. package/docs/guide/05-checks-updates-dials.md +7 -0
  152. package/docs/guide/06-effects.md +11 -0
  153. package/docs/guide/07-evolution.md +11 -0
  154. package/docs/guide/08-implementing.md +13 -0
  155. package/docs/llms-full.txt +1241 -0
  156. package/docs/llms.txt +14 -0
  157. package/docs/reference/canonical.md +16 -0
  158. package/docs/reference/clauses.md +986 -0
  159. package/docs/reference/cli.md +22 -0
  160. package/docs/reference/diagnostics.md +32 -0
  161. package/package.json +6 -2
  162. package/skills/udl/SKILL.md +28 -0
  163. package/spec/README.md +82 -51
  164. package/spec/udl.schema.json +1191 -119
  165. package/src/canonical.ts +11 -0
  166. package/src/check-profiles.ts +80 -0
  167. package/src/cli.ts +49 -3
  168. package/src/diagnostics.ts +221 -0
  169. package/src/effects.ts +163 -0
  170. package/src/evolution.ts +375 -125
  171. package/src/finance.ts +255 -160
  172. package/src/index.ts +50 -14
  173. package/src/limits.ts +4 -3
  174. package/src/parser.ts +15 -18
  175. package/src/schema.ts +855 -81
  176. package/src/validation.ts +2666 -560
  177. package/conformance/invalid/missing-create-verb.expected.json +0 -10
  178. package/conformance/invalid/noun-id-not-snake-case.expected.json +0 -10
  179. package/conformance/invalid/verb-without-transition.expected.json +0 -10
package/CHANGELOG.md CHANGED
@@ -10,6 +10,58 @@ here under the release that made it.
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [1.0.0] - 2026-09-04
14
+
15
+ This release is byte-identical to 1.0.0-rc.1. Later changes bump under the contract-diff tiers.
16
+
17
+ ## [1.0.0-rc.1] - 2026-09-02
18
+
19
+ ### Added
20
+
21
+ - Stable `UDL####` diagnostics include a category, fix, message, and JSON path.
22
+ - Evolution comparison returns coded issues instead of strings.
23
+ - `canonicalDigest` returns a promise for SHA-256 over canonical UTF-8 bytes.
24
+ - `udl canon` prints canonical bytes or their digest, and `udl explain` prints
25
+ the catalog entry for one code.
26
+ - The conformance runner has evolution pairs and pins canonical digests, issue
27
+ codes, and paths.
28
+ - Eight guides, generated clause, diagnostic, and command references,
29
+ `llms.txt`, `llms-full.txt`, and an agent skill now ship with the package.
30
+
31
+ - `reconcile` declares one expectation about money an action cannot see yet:
32
+ the amount path, currency field, direction, the ref naming the provider-side
33
+ row, exactly one evidence source, a match law, and a window. The match law is
34
+ `exact`, `tolerance` bounded by a `reconcile_tolerance` dial, or `window`. An
35
+ expectation that never matches by the end of its window is carried by the
36
+ named exception child, capped by `maxOpen`. The child is declared and
37
+ validated at admission, not materialized: the transition refuses with
38
+ `state_conflict` naming it and the due sweep re-arms the expectation.
39
+ `UdlReconcile` exposes the clause type.
40
+ - An action may declare a `quote` offer and exactly one other action may spend
41
+ it through `commit`. Both the base money field and the net destination are
42
+ mandatory members of `fixes`, and `fixes` and `updates` may not overlap.
43
+
44
+ ### Removed
45
+
46
+ - `requiresSettlement` and `UdlRequiresSettlement` are gone. A payout-owning
47
+ instrument now declares exactly one action carrying `reconcile` against a
48
+ debit statement line, under any match law. There is no wrapper and no alias.
49
+
50
+ ### Changed
51
+
52
+ - The npm package includes `docs/` and `skills/`.
53
+ - Derived effect rows for `quote`, `commit`, and `reconcile` change the canonical
54
+ bytes of stored documents that carry `effects` for those clauses.
55
+ - Format 1 now carries every instrument contract clause. Instrument metadata
56
+ includes archetype template, navigation, visibility, dials, caller-parked
57
+ states, and extensible subjects. Actions include check gates, remainder
58
+ arithmetic, field updates, principal policy, and sandbox failure points.
59
+ - Remainder clauses declare their computed money refs, so computed money has
60
+ one authored source.
61
+ - Evolution snapshots protect lifted clauses that affect composition,
62
+ admission, or execution. Navigation, examples, and parked-state reason text
63
+ remain editable presentation.
64
+
13
65
  ## [1.0.0-beta.1] - 2026-08-29
14
66
 
15
67
  - This is the first beta and has no package behavior changes from
@@ -19,35 +71,34 @@ here under the release that made it.
19
71
 
20
72
  ### Added
21
73
 
22
- - Verbs may declare a camelCase `publicIntent` as their author-approved public
23
- name while the verb key remains the lifecycle and execution identity. System
24
- due verbs must omit it. `udlPublicIntentSchema` is exported for consumers
74
+ - Actions may declare a camelCase `publicAction` as their author-approved public
75
+ name while the action key remains the lifecycle and execution identity. System
76
+ due actions must omit it. `udlPublicActionSchema` is exported for consumers
25
77
  that admit the same name outside a complete document.
26
- - `captureInput` maps declared verb input fields into durable receipt refs.
27
- Declare only input properties from that verb and allocate each captured key
28
- in the noun's shared ref namespace.
78
+ - `captureInput` maps declared action input fields into durable receipt refs.
79
+ Declare only input properties from that action and allocate each captured key
80
+ in the instrument's shared ref namespace.
29
81
  - `signedSum` computes stored add and subtract subtotals over typed child money,
30
- then captures one net amount for exactly one payout or noun transfer. Authors
82
+ then captures one net amount for exactly one payout or instrument transfer. Authors
31
83
  must declare the child reference, money field, currency, admitted statuses,
32
84
  and explicit negative and zero policies.
33
85
  - `requiresExposure` gates a child amount against a stored cap, with an optional
34
86
  anchor-specific cap, and `setsAt.marker` records occurrence timestamps that
35
87
  cannot drive a due condition or deadline. Use these clauses for bounded
36
88
  installment writes and per-anchor occurrence markers.
37
- - `distribute` allocates one parent money field or computed money ref across
38
- typed children selected by status and stored weight. Declare runtime-owned
39
- money refs in `computedMoneyRefs`; the validator resolves the parent,
40
- weight, statuses, and pool before admitting the document.
41
- - Nouns may declare up to four `derivedAmounts`. Each rule computes a declared
89
+ - `distribute` allocates one parent money field or remainder-computed money ref
90
+ across typed children selected by status and stored weight. The validator
91
+ resolves the parent, weight, statuses, and pool before admitting the document.
92
+ - Instruments may declare up to four `derivedAmounts`. Each rule computes a declared
42
93
  money field as 1 through 9,999 basis points of another declared money field
43
94
  with floor rounding. Callers supply neither the result nor a fixed or tiered
44
95
  rule, and a rule may not derive a field from itself.
45
- - Verbs may declare one `payout` intent that reads stored money, currency,
96
+ - Actions may declare one `payout` intent that reads stored money, currency,
46
97
  source-account, and beneficiary values and captures the payout reference.
47
98
  This does not add an operation to the seven-instruction kernel.
48
99
  - A system-only `requiresSettlement` transition may read a captured payout
49
100
  reference and capture the durable evidence record that matched it. The
50
- validator rejects caller input, decision ports, public intents, due and
101
+ validator rejects caller input, decision ports, public actions, due and
51
102
  deadline triggers, kernel steps, and money moves on that transition.
52
103
  - `validateUdlJsonSchema` validates one schema against UDL's sealed JSON Schema
53
104
  subset without applying it to a value. `UdlPayout` and
@@ -55,12 +106,12 @@ here under the release that made it.
55
106
 
56
107
  ### Changed
57
108
 
58
- - Evolution snapshots freeze public intents, captured receipt input,
59
- distribution rules, exposure gates, signed sums, computed money refs, and
60
- derived-amount arithmetic once a noun has live instances. Older snapshots
109
+ - Evolution snapshots freeze public actions, captured receipt input,
110
+ distribution rules, exposure gates, signed sums, computed money clauses, and
111
+ derived-amount arithmetic once a instrument has live instances. Older snapshots
61
112
  remain readable when those keys are absent.
62
113
  - Evolution snapshots freeze both payout intents and settlement evidence gates.
63
- - Receipt refs written by the new clauses share the noun ref namespace with
114
+ - Receipt refs written by the new clauses share the instrument ref namespace with
64
115
  kernel captures, input captures, signed sums, subject refs, and unwind refs.
65
116
 
66
117
  ## [1.0.0-alpha.4] - 2026-08-26
@@ -83,16 +134,16 @@ here under the release that made it.
83
134
  refuses now throws a `UdlError` carrying that document's issues where
84
135
  alpha.2 returned evolution violations. Measured on
85
136
  `conformance/valid/protection.udl` with the product renamed to
86
- `protection_v2` and the live `claim` noun dropped: alpha.2 returned three
87
- violations, naming the rename, the removed live noun, and the version that
137
+ `protection_v2` and the live `claim` instrument dropped: alpha.2 returned three
138
+ violations, naming the rename, the removed live instrument, and the version that
88
139
  did not move; this release throws `invalid_semantics` at
89
- `$.nouns[0].aggregateInvariants[0].childNounId`, because another noun's
140
+ `$.instruments[0].aggregateInvariants[0].childInstrumentId`, because another instrument's
90
141
  aggregate still references `claim`.
91
142
 
92
143
  Read the throw as a refusal to judge, not as a verdict of no violations.
93
144
  A `catch` that treats it as a schema problem and carries on has skipped the
94
145
  append-only check entirely, and the candidate above is exactly the kind that
95
- then sails through: dropping a live noun is the headline violation the law
146
+ then sails through: dropping a live instrument is the headline violation the law
96
147
  exists to catch. Fix the document, or use `diffValidatedUdlEvolution`.
97
148
 
98
149
  The parameters are `unknown` because the function now accepts input nobody
@@ -111,8 +162,8 @@ here under the release that made it.
111
162
  - The comparison key behind every diff carries the same depth budget the
112
163
  validator applies to a document (`UDL_LIMITS.maxDepth`, 24 levels; the
113
164
  deepest conformance document reaches 12). This is what protects
114
- `diffNounEvolution`, which takes snapshots no validator has seen, and it
115
- closes the case where `snapshotUdlNoun` returns a cyclic snapshot that only
165
+ `diffInstrumentEvolution`, which takes snapshots no validator has seen, and it
166
+ closes the case where `snapshotUdlInstrument` returns a cyclic snapshot that only
116
167
  detonates when something later stringifies it.
117
168
 
118
169
  ### Added
@@ -157,7 +208,9 @@ First public release. Format version 1.
157
208
  - `bun run spec:check`, which fails when the committed schema drifts from the
158
209
  grammar it was generated from.
159
210
 
160
- [Unreleased]: https://github.com/hyperscale0/hyperscale-udl/compare/v1.0.0-beta.1...HEAD
211
+ [Unreleased]: https://github.com/hyperscale0/hyperscale-udl/compare/v1.0.0...HEAD
212
+ [1.0.0]: https://github.com/hyperscale0/hyperscale-udl/compare/v1.0.0-rc.1...v1.0.0
213
+ [1.0.0-rc.1]: https://github.com/hyperscale0/hyperscale-udl/compare/v1.0.0-beta.1...v1.0.0-rc.1
161
214
  [1.0.0-beta.1]: https://github.com/hyperscale0/hyperscale-udl/compare/v1.0.0-alpha.5...v1.0.0-beta.1
162
215
  [1.0.0-alpha.5]: https://github.com/hyperscale0/hyperscale-udl/compare/v1.0.0-alpha.4...v1.0.0-alpha.5
163
216
  [1.0.0-alpha.4]: https://github.com/hyperscale0/hyperscale-udl/compare/v1.0.0-alpha.3...v1.0.0-alpha.4
package/README.md CHANGED
@@ -1,22 +1,10 @@
1
1
  # UDL
2
2
 
3
- UDL is the Universal Domain Language: the canonical JSON contract for a
4
- financial product. One `.udl` file declares the subjects, nouns, lifecycles,
5
- verbs, and money steps that define the product in business terms. Hyperscale's
6
- composer admits that document into a frozen Product build. The compiler then
7
- projects API, SDK, documentation, and agent surfaces from the same contract.
8
-
9
- The language deliberately cannot say certain things. There is no statement
10
- format, no file drop, no polling loop, no cutoff time, no scheme name, no
11
- reconciliation vocabulary. Those are real and they are somebody's problem, but
12
- they are not the product, so they are absorbed below the language and never
13
- surface in a document. What is left is small enough to hold in your head.
14
-
15
- This package is the reference implementation: parser, semantic validator,
16
- canonical serializer, append-only evolution diff, and the `udl` command. It
17
- does not compose a company or execute operations. The format itself is
18
- specified in [`spec/`](./spec/README.md) and pinned by
19
- [`conformance/`](./conformance/README.md).
3
+ UDL is the Universal Domain Language, the canonical JSON contract for a financial product. One `.udl` file declares subjects, instruments, lifecycles, actions, and money movement. An engine can admit that document without reading the source language that produced it.
4
+
5
+ UDL keeps provider machinery below the format. It has no file drops, polling loops, cutoff jobs, scheme messages, or provider statement schemas. The `reconcile` clause names settlement evidence against a declared provider-side row. It does not model the provider file or transport.
6
+
7
+ This package contains the parser, semantic validator, canonical serializer, append-only evolution diff, JSON Schema, and conformance corpus.
20
8
 
21
9
  ## Install
22
10
 
@@ -24,18 +12,17 @@ specified in [`spec/`](./spec/README.md) and pinned by
24
12
  npm install @hyperscale0/udl
25
13
  ```
26
14
 
27
- Every release before 1.0.0 is an alpha, and `latest` follows the newest one, so
28
- a bare install gets it. Pin an exact version if you need one: until 1.0.0 a
29
- change to the surface ships as a minor bump, not a major.
15
+ `1.0.0` freezes format 1. Pin version 1.0.0 while testing another implementation.
30
16
 
31
17
  ## Thirty seconds
32
18
 
33
- `note.udl`, the smallest document the format accepts:
19
+ `note.udl` is the smallest admitted document.
34
20
 
35
21
  ```json
36
22
  {
37
- "nouns": [
23
+ "instruments": [
38
24
  {
25
+ "actionOrder": ["close", "create"],
39
26
  "fields": { "reference": { "type": "string" } },
40
27
  "id": "note",
41
28
  "idPrefix": "note",
@@ -47,7 +34,7 @@ change to the surface ships as a minor bump, not a major.
47
34
  "required": ["reference"],
48
35
  "summary": "A note a tenant files and later closes.",
49
36
  "title": "Note",
50
- "verbs": {
37
+ "actions": {
51
38
  "close": { "moves": [], "steps": [], "summary": "Close the note." },
52
39
  "create": { "moves": [], "steps": [], "summary": "File the note." }
53
40
  }
@@ -64,6 +51,7 @@ change to the surface ships as a minor bump, not a major.
64
51
  ```ts
65
52
  import { readFile, writeFile } from "node:fs/promises";
66
53
  import {
54
+ canonicalDigest,
67
55
  diffValidatedUdlEvolution,
68
56
  parseUdl,
69
57
  serializeUdl,
@@ -71,102 +59,52 @@ import {
71
59
  } from "@hyperscale0/udl";
72
60
 
73
61
  const document = parseUdl(await readFile("note.udl"));
74
-
75
- // One document, one byte sequence. Sorted keys, two-space indent, final LF.
76
62
  await writeFile("note.udl", serializeUdl(document));
63
+ console.log(await canonicalDigest(document));
77
64
 
78
- // Issues carry a stable code and a JSON path, never just a sentence.
79
65
  const result = validateUdl(document);
80
66
  if (!result.ok) console.error(result.issues);
81
67
 
82
- // Is this change legal against the frozen version with existing instances?
83
68
  const previous = parseUdl(await readFile("note.previous.udl"));
84
69
  const violations = diffValidatedUdlEvolution(previous, document);
85
70
  ```
86
71
 
87
- `parseUdl` takes a string or a `Uint8Array`; bytes are decoded as strict UTF-8.
88
- `serializeUdl` validates before it writes, so an invalid document has no
89
- canonical form.
72
+ Every issue has a stable `UDL####` code, a category, a JSON path, a message, and a fix. Messages may become clearer. Codes do not change once published.
90
73
 
91
- `diffValidatedUdlEvolution` takes two `UdlDocument`s and assumes both have
92
- already been admitted. Validate first: an evolution verdict on a document
93
- `validateUdl` refuses is not a verdict at all, and a candidate that drops a
94
- referenced noun sails through a diff that never checked the reference graph.
95
- The `UdlDocument` parameter types keep the compiler on your side of that rule.
74
+ `parseUdl` accepts a string or `Uint8Array` and rejects malformed UTF-8. `serializeUdl` validates before writing. It sorts object keys by UTF-16 code unit, keeps array order, uses two-space indentation, and writes one final line feed. `canonicalDigest` hashes those UTF-8 bytes with SHA-256 and returns a promise for the lowercase hexadecimal digest.
96
75
 
97
- ## The command
76
+ The seven kernel operations are `internal_transfer.create`, `internal_transfer.reserve`, `internal_transfer.post`, `internal_transfer.void`, `account.escrow.provision`, `account.freeze`, and `account.unfreeze`. A `payout` is an execution intent, not another kernel operation.
98
77
 
99
- ```bash
100
- udl validate product.udl # parse and report every issue found
101
- udl fmt product.udl # print the canonical form
102
- udl fmt product.udl --write # rewrite the file in place
103
- udl diff frozen.udl product.udl # is the change additive, or does it break?
104
- ```
105
-
106
- Exit codes: `0` the document is admissible or the change is additive, `1` the
107
- document was refused or the change breaks the append-only law, `2` the command
108
- line was wrong or a file could not be read. That split matters in CI: a broken
109
- document and a broken invocation are different failures.
78
+ The compiler derives action `effects` from clauses. The validator rejects a supplied effects object unless every row and its order match. Derived effects do not consume the authored node budget.
110
79
 
111
- ## The two things worth knowing
80
+ ## Command line
112
81
 
113
- **Evolution is append-only.** Once a frozen definition has instances, you may
114
- add states, transitions, optional fields, and verbs. You may not remove,
115
- rename, tighten, or change a money step. `udl diff` is not advice; it is the
116
- same function the compiler runs before it will accept a new version.
82
+ ```bash
83
+ udl validate product.udl
84
+ udl fmt product.udl --write
85
+ udl canon product.udl
86
+ udl canon product.udl --digest
87
+ udl diff frozen.udl product.udl
88
+ udl explain UDL5001
89
+ ```
117
90
 
118
- **Internal ledger money moves through four instructions and no others.**
119
- `internal_transfer.create`, `.reserve`, `.post`, `.void`. Three account
120
- instructions complete the sealed set. A noun cannot invent a fifth money path,
121
- which is why a document can be checked for stranded value before anything runs.
122
- The separate `payout` intent carries a stored amount to the execution core and
123
- captures its durable reference. A later `requiresSettlement` transition stays
124
- system-only and runs only after that payout matches durable settlement evidence.
91
+ Exit code `0` means success. Exit code `1` means the validator or evolution law refused the document. Exit code `2` means the invocation or file read failed.
125
92
 
126
- ## The spec
93
+ ## Documentation
127
94
 
128
- - [`spec/README.md`](./spec/README.md) is the specification: the ten laws, the
129
- canonical form, the issue codes, and what the schema deliberately cannot say.
130
- - [`spec/udl.schema.json`](./spec/udl.schema.json) is JSON Schema 2020-12,
131
- generated from the grammar. Where prose and schema disagree, the schema wins.
132
- - [`conformance/`](./conformance/README.md) is the semantic spec: `.udl` inputs
133
- with expected verdicts, canonical bytes, and issue codes, runnable from any
134
- language.
95
+ - [Guide and reading order](docs/README.md)
96
+ - [Format specification](spec/README.md)
97
+ - [Canonical bytes law](docs/reference/canonical.md)
98
+ - [Stable diagnostics](docs/reference/diagnostics.md)
99
+ - [Conformance runner contract](conformance/README.md)
100
+ - [Agent skill](skills/udl/SKILL.md)
135
101
 
136
102
  ## Versioning
137
103
 
138
- Two numbers move independently.
139
-
140
- **Format version** is the literal `"udl": 1` inside a document. Format 1 is the
141
- only format that exists.
142
-
143
- **Package version** is this package's semver, in `package.json`.
144
-
145
- Format 1 is unstable until the package reaches 1.0.0. Until then an alpha
146
- release may change what format 1 accepts, and every such change is listed in
147
- [`CHANGELOG.md`](./CHANGELOG.md). After 1.0.0, format 1 is frozen and an
148
- incompatible change bumps the literal to `2`.
149
-
150
- ## Status
151
-
152
- Alpha. The format is in use, the API surface is settled enough to build on, and
153
- the version number is honest about the rest. Breaking changes go in the
154
- changelog, not in a footnote.
155
-
156
- ## Contributing
104
+ The literal `"udl": 1` is the format version. The version in `package.json` is the package version. They move independently. After package version `1.0.0`, an incompatible format change uses a new format literal and keeps an explicit reader for stored format 1 documents during its stated support window.
157
105
 
158
- Issues only. Hyperscale makes the changes to the format and the package; you
159
- propose them in an issue carrying the use case and the conformance case it
160
- would add. [`CONTRIBUTING.md`](./CONTRIBUTING.md) has that model in full, plus
161
- the dev setup, the test commands, and how to regenerate the spec. Conduct:
162
- [`CODE_OF_CONDUCT.md`](./CODE_OF_CONDUCT.md). Vulnerabilities:
163
- [`SECURITY.md`](./SECURITY.md).
106
+ ## Contributing and license
164
107
 
165
- ## License
108
+ Hyperscale accepts format proposals as issues with a use case and the conformance case they would add. See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and proof commands.
166
109
 
167
- AGPL-3.0-only, with a commercial license available from Hyperscale LLC for
168
- organisations that cannot accept the AGPL. See [`LICENSE`](./LICENSE) for the
169
- text and [`LICENSING.md`](./LICENSING.md) for which one you want and how to ask
170
- for the commercial one. The marks are not covered by either; see
171
- [`TRADEMARKS.md`](./TRADEMARKS.md), which also carries the rule for claiming
172
- UDL compatibility.
110
+ UDL is AGPL-3.0-only. Hyperscale LLC also offers a commercial license. See [LICENSING.md](LICENSING.md) and [TRADEMARKS.md](TRADEMARKS.md).
@@ -1,23 +1,23 @@
1
1
  # The UDL conformance suite
2
2
 
3
- Every file here is data. Nothing in this directory imports the reference
4
- implementation, so a UDL implementation in any language can run the suite by
5
- reading files and comparing bytes.
6
-
7
- `../test/conformance.spec.ts` is the reference runner. Read it if a rule below
8
- is ambiguous.
3
+ Every file here is data. An implementation in any language can run the suite
4
+ without importing the TypeScript reference implementation.
9
5
 
10
6
  ## Layout
11
7
 
12
8
  ```
13
9
  conformance/
14
- valid/ documents the format accepts, each with its canonical form
15
- invalid/ documents the format refuses, each with the issues it must report
10
+ valid/<case>.udl
11
+ valid/<case>.expected.json
12
+ invalid/<case>.udl
13
+ invalid/<case>.expected.json
14
+ evolution/<case>.live.udl
15
+ evolution/<case>.next.udl
16
+ evolution/<case>.expected.json
16
17
  ```
17
18
 
18
- Every `<case>.udl` has a sibling `<case>.expected.json`. A file without its
19
- sibling is a case that silently stopped running, so the runner fails on
20
- orphans in either direction.
19
+ The runner fails if either half of a case is missing. An evolution case must
20
+ have all three named files.
21
21
 
22
22
  ## Expected files
23
23
 
@@ -26,54 +26,57 @@ A valid case:
26
26
  ```json
27
27
  {
28
28
  "canonical": "minimal.udl",
29
- "summary": "Compact, reverse-ordered bytes canonicalize to minimal.udl.",
29
+ "digest": "c21d198d69e9d4cadb33aaacf37581bb388e4a8964b7fc284606cfa9f65d35bb",
30
+ "summary": "The smallest admitted document.",
30
31
  "verdict": "valid"
31
32
  }
32
33
  ```
33
34
 
34
- `canonical` names the file in `valid/` holding the canonical bytes for this
35
- input. A document that is already canonical names itself.
35
+ `digest` is lowercase SHA-256 over the canonical UTF-8 bytes. A document that
36
+ is already canonical names itself.
36
37
 
37
38
  An invalid case:
38
39
 
39
40
  ```json
40
41
  {
41
- "issues": [{ "code": "invalid_semantics", "path": "$.nouns[0].verbs" }],
42
- "summary": "Every noun declares create: nothing else can bring an instance into being.",
42
+ "issues": [{ "code": "UDL3001", "path": "$.instruments[0].actions" }],
43
+ "summary": "Every instrument declares create.",
43
44
  "verdict": "invalid"
44
45
  }
45
46
  ```
46
47
 
47
- `summary` is for humans reading a failure. Nothing asserts against it beyond
48
- requiring it to be present and non-blank.
48
+ The runner requires a non-blank `summary`. It never compares messages.
49
49
 
50
- ## The three levels
50
+ ## Conformance levels
51
51
 
52
- An implementation claims conformance at the highest level it passes.
52
+ An implementation claims the highest level it passes.
53
53
 
54
- **Level 1, verdict.** Read the file as bytes. Every `valid/` case is admitted
55
- and every `invalid/` case is refused. This is the whole contract for a
56
- validator.
54
+ 1. Verdict. Admit every `valid/` document and refuse every `invalid/` document.
55
+ 2. Canonical bytes. Serialize every valid document byte for byte as its named
56
+ canonical file, including the trailing line feed, and match its digest.
57
+ 3. Diagnostics. Report every listed code and path for each invalid document.
58
+ The implementation may report extra issues.
59
+ 4. Evolution. Admit both documents, then compare the live and next definitions.
60
+ Report every listed UDL7xxx code and path. The implementation may report
61
+ extra issues.
57
62
 
58
- **Level 2, canonical bytes.** For each `valid/` case, serialize the parsed
59
- document and compare it byte for byte against the file named by `canonical`.
60
- Any implementation that writes UDL must pass this, or two tools will disagree
61
- about what the same document is.
63
+ ## Path grammar
62
64
 
63
- **Level 3, issues.** For each `invalid/` case, every listed `code` and `path`
64
- pair appears among the reported issues. Extra issues are allowed, because
65
- implementations legitimately differ on how many problems they report before
66
- giving up. Messages are never compared; they are prose and they are free to
67
- change.
65
+ Issue paths use `$` for the document root, `.name` for an object member, and
66
+ `[n]` for a zero-based array index. Evolution paths use the instrument index
67
+ from the live document and continue to the changed member, such as
68
+ `$.instruments[3].actions.reconcile`. A direct `diffInstrumentEvolution` call
69
+ has no document index, so its paths start at `$.instruments`.
68
70
 
69
- ## Adding a case
71
+ ## Runner exit rule
70
72
 
71
- Write the `.udl` and its `.expected.json`, then run `bun test`. Keep new cases
72
- small and single-purpose: the point of `invalid/blank-title.udl` is that it
73
- differs from `valid/minimal.udl` in exactly one key. `valid/minimal.udl` is the
74
- smallest document both the grammar and the semantic laws accept, and it is the
75
- right base to mutate.
73
+ The runner exits 0 only if every case at the claimed level passes. A missing
74
+ file, malformed expected file, unexpected verdict, byte mismatch, digest
75
+ mismatch, or missing issue pair makes it exit nonzero.
76
+
77
+ ## Adding a case
76
78
 
77
- The five domain documents in `valid/` are the real thing, projected from a
78
- shipped product catalog. They are large on purpose: they are what catches a
79
- regression the minimal case cannot see.
79
+ Start from the smallest admitted document that carries the needed clause.
80
+ Change one law where possible. Pin codes and paths, never messages. Valid cases
81
+ must keep one example of every target in `udlClauseVocabulary`, including
82
+ quote, commit, reconcile, checks, updates, dials, effects, and money clauses.
@@ -0,0 +1,10 @@
1
+ {
2
+ "issues": [
3
+ {
4
+ "code": "UDL7001",
5
+ "path": "$.instruments[0].actions.close"
6
+ }
7
+ ],
8
+ "summary": "Evolution identifies the changed action beneath the previous instrument index.",
9
+ "verdict": "invalid"
10
+ }
@@ -0,0 +1,44 @@
1
+ {
2
+ "instruments": [
3
+ {
4
+ "actionOrder": ["close", "create"],
5
+ "actions": {
6
+ "close": {
7
+ "moves": [],
8
+ "steps": [],
9
+ "summary": "Close the note."
10
+ },
11
+ "create": {
12
+ "moves": [],
13
+ "steps": [],
14
+ "summary": "File the note."
15
+ }
16
+ },
17
+ "fields": {
18
+ "reference": {
19
+ "type": "string"
20
+ }
21
+ },
22
+ "id": "note",
23
+ "idPrefix": "note",
24
+ "lifecycle": {
25
+ "initial": "open",
26
+ "states": ["open", "closed"],
27
+ "transitions": {
28
+ "close": {
29
+ "from": ["open"],
30
+ "to": "closed"
31
+ }
32
+ }
33
+ },
34
+ "required": ["reference"],
35
+ "summary": "A note a tenant files and later closes.",
36
+ "title": "Note"
37
+ }
38
+ ],
39
+ "product": "minimal",
40
+ "subjects": [],
41
+ "title": "Minimal",
42
+ "udl": 1,
43
+ "version": 1
44
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "instruments": [
3
+ {
4
+ "actionOrder": ["close", "create"],
5
+ "actions": {
6
+ "close": {
7
+ "eventName": "note.closed",
8
+ "moves": [],
9
+ "steps": [],
10
+ "summary": "Close the note."
11
+ },
12
+ "create": {
13
+ "moves": [],
14
+ "steps": [],
15
+ "summary": "File the note."
16
+ }
17
+ },
18
+ "fields": {
19
+ "reference": {
20
+ "type": "string"
21
+ }
22
+ },
23
+ "id": "note",
24
+ "idPrefix": "note",
25
+ "lifecycle": {
26
+ "initial": "open",
27
+ "states": ["open", "closed"],
28
+ "transitions": {
29
+ "close": {
30
+ "from": ["open"],
31
+ "to": "closed"
32
+ }
33
+ }
34
+ },
35
+ "required": ["reference"],
36
+ "summary": "A note a tenant files and later closes.",
37
+ "title": "Note"
38
+ }
39
+ ],
40
+ "product": "minimal",
41
+ "subjects": [],
42
+ "title": "Minimal",
43
+ "udl": 1,
44
+ "version": 2
45
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "issues": [
3
+ {
4
+ "code": "UDL7001",
5
+ "path": "$.product"
6
+ }
7
+ ],
8
+ "summary": "Evolution refusal for product id changed from minimal to renamed.",
9
+ "verdict": "invalid"
10
+ }