@hyperscale0/udl 1.0.0-alpha.5 → 1.0.0-rc.1

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 +79 -25
  2. package/README.md +39 -102
  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 +85 -54
  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,39 +10,91 @@ here under the release that made it.
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [1.0.0-rc.1] - 2026-09-02
14
+
15
+ ### Added
16
+
17
+ - Stable `UDL####` diagnostics include a category, fix, message, and JSON path.
18
+ - Evolution comparison returns coded issues instead of strings.
19
+ - `canonicalDigest` returns a promise for SHA-256 over canonical UTF-8 bytes.
20
+ - `udl canon` prints canonical bytes or their digest, and `udl explain` prints
21
+ the catalog entry for one code.
22
+ - The conformance runner has evolution pairs and pins canonical digests, issue
23
+ codes, and paths.
24
+ - Eight guides, generated clause, diagnostic, and command references,
25
+ `llms.txt`, `llms-full.txt`, and an agent skill now ship with the package.
26
+
27
+ - `reconcile` declares one expectation about money an action cannot see yet:
28
+ the amount path, currency field, direction, the ref naming the provider-side
29
+ row, exactly one evidence source, a match law, and a window. The match law is
30
+ `exact`, `tolerance` bounded by a `reconcile_tolerance` dial, or `window`. An
31
+ expectation that never matches by the end of its window is carried by the
32
+ named exception child, capped by `maxOpen`. The child is declared and
33
+ validated at admission, not materialized: the transition refuses with
34
+ `state_conflict` naming it and the due sweep re-arms the expectation.
35
+ `UdlReconcile` exposes the clause type.
36
+ - An action may declare a `quote` offer and exactly one other action may spend
37
+ it through `commit`. Both the base money field and the net destination are
38
+ mandatory members of `fixes`, and `fixes` and `updates` may not overlap.
39
+
40
+ ### Removed
41
+
42
+ - `requiresSettlement` and `UdlRequiresSettlement` are gone. A payout-owning
43
+ instrument now declares exactly one action carrying `reconcile` against a
44
+ debit statement line, under any match law. There is no wrapper and no alias.
45
+
46
+ ### Changed
47
+
48
+ - The npm package includes `docs/` and `skills/`.
49
+ - Derived effect rows for `quote`, `commit`, and `reconcile` change the canonical
50
+ bytes of stored documents that carry `effects` for those clauses.
51
+ - Format 1 now carries every instrument contract clause. Instrument metadata
52
+ includes archetype template, navigation, visibility, dials, caller-parked
53
+ states, and extensible subjects. Actions include check gates, remainder
54
+ arithmetic, field updates, principal policy, and sandbox failure points.
55
+ - Remainder clauses declare their computed money refs, so computed money has
56
+ one authored source.
57
+ - Evolution snapshots protect lifted clauses that affect composition,
58
+ admission, or execution. Navigation, examples, and parked-state reason text
59
+ remain editable presentation.
60
+
61
+ ## [1.0.0-beta.1] - 2026-08-29
62
+
63
+ - This is the first beta and has no package behavior changes from
64
+ 1.0.0-alpha.5.
65
+
13
66
  ## [1.0.0-alpha.5] - 2026-08-29
14
67
 
15
68
  ### Added
16
69
 
17
- - Verbs may declare a camelCase `publicIntent` as their author-approved public
18
- name while the verb key remains the lifecycle and execution identity. System
19
- due verbs must omit it. `udlPublicIntentSchema` is exported for consumers
70
+ - Actions may declare a camelCase `publicAction` as their author-approved public
71
+ name while the action key remains the lifecycle and execution identity. System
72
+ due actions must omit it. `udlPublicActionSchema` is exported for consumers
20
73
  that admit the same name outside a complete document.
21
- - `captureInput` maps declared verb input fields into durable receipt refs.
22
- Declare only input properties from that verb and allocate each captured key
23
- in the noun's shared ref namespace.
74
+ - `captureInput` maps declared action input fields into durable receipt refs.
75
+ Declare only input properties from that action and allocate each captured key
76
+ in the instrument's shared ref namespace.
24
77
  - `signedSum` computes stored add and subtract subtotals over typed child money,
25
- then captures one net amount for exactly one payout or noun transfer. Authors
78
+ then captures one net amount for exactly one payout or instrument transfer. Authors
26
79
  must declare the child reference, money field, currency, admitted statuses,
27
80
  and explicit negative and zero policies.
28
81
  - `requiresExposure` gates a child amount against a stored cap, with an optional
29
82
  anchor-specific cap, and `setsAt.marker` records occurrence timestamps that
30
83
  cannot drive a due condition or deadline. Use these clauses for bounded
31
84
  installment writes and per-anchor occurrence markers.
32
- - `distribute` allocates one parent money field or computed money ref across
33
- typed children selected by status and stored weight. Declare runtime-owned
34
- money refs in `computedMoneyRefs`; the validator resolves the parent,
35
- weight, statuses, and pool before admitting the document.
36
- - Nouns may declare up to four `derivedAmounts`. Each rule computes a declared
85
+ - `distribute` allocates one parent money field or remainder-computed money ref
86
+ across typed children selected by status and stored weight. The validator
87
+ resolves the parent, weight, statuses, and pool before admitting the document.
88
+ - Instruments may declare up to four `derivedAmounts`. Each rule computes a declared
37
89
  money field as 1 through 9,999 basis points of another declared money field
38
90
  with floor rounding. Callers supply neither the result nor a fixed or tiered
39
91
  rule, and a rule may not derive a field from itself.
40
- - Verbs may declare one `payout` intent that reads stored money, currency,
92
+ - Actions may declare one `payout` intent that reads stored money, currency,
41
93
  source-account, and beneficiary values and captures the payout reference.
42
94
  This does not add an operation to the seven-instruction kernel.
43
95
  - A system-only `requiresSettlement` transition may read a captured payout
44
96
  reference and capture the durable evidence record that matched it. The
45
- validator rejects caller input, decision ports, public intents, due and
97
+ validator rejects caller input, decision ports, public actions, due and
46
98
  deadline triggers, kernel steps, and money moves on that transition.
47
99
  - `validateUdlJsonSchema` validates one schema against UDL's sealed JSON Schema
48
100
  subset without applying it to a value. `UdlPayout` and
@@ -50,12 +102,12 @@ here under the release that made it.
50
102
 
51
103
  ### Changed
52
104
 
53
- - Evolution snapshots freeze public intents, captured receipt input,
54
- distribution rules, exposure gates, signed sums, computed money refs, and
55
- derived-amount arithmetic once a noun has live instances. Older snapshots
105
+ - Evolution snapshots freeze public actions, captured receipt input,
106
+ distribution rules, exposure gates, signed sums, computed money clauses, and
107
+ derived-amount arithmetic once a instrument has live instances. Older snapshots
56
108
  remain readable when those keys are absent.
57
109
  - Evolution snapshots freeze both payout intents and settlement evidence gates.
58
- - Receipt refs written by the new clauses share the noun ref namespace with
110
+ - Receipt refs written by the new clauses share the instrument ref namespace with
59
111
  kernel captures, input captures, signed sums, subject refs, and unwind refs.
60
112
 
61
113
  ## [1.0.0-alpha.4] - 2026-08-26
@@ -78,16 +130,16 @@ here under the release that made it.
78
130
  refuses now throws a `UdlError` carrying that document's issues where
79
131
  alpha.2 returned evolution violations. Measured on
80
132
  `conformance/valid/protection.udl` with the product renamed to
81
- `protection_v2` and the live `claim` noun dropped: alpha.2 returned three
82
- violations, naming the rename, the removed live noun, and the version that
133
+ `protection_v2` and the live `claim` instrument dropped: alpha.2 returned three
134
+ violations, naming the rename, the removed live instrument, and the version that
83
135
  did not move; this release throws `invalid_semantics` at
84
- `$.nouns[0].aggregateInvariants[0].childNounId`, because another noun's
136
+ `$.instruments[0].aggregateInvariants[0].childInstrumentId`, because another instrument's
85
137
  aggregate still references `claim`.
86
138
 
87
139
  Read the throw as a refusal to judge, not as a verdict of no violations.
88
140
  A `catch` that treats it as a schema problem and carries on has skipped the
89
141
  append-only check entirely, and the candidate above is exactly the kind that
90
- then sails through: dropping a live noun is the headline violation the law
142
+ then sails through: dropping a live instrument is the headline violation the law
91
143
  exists to catch. Fix the document, or use `diffValidatedUdlEvolution`.
92
144
 
93
145
  The parameters are `unknown` because the function now accepts input nobody
@@ -106,8 +158,8 @@ here under the release that made it.
106
158
  - The comparison key behind every diff carries the same depth budget the
107
159
  validator applies to a document (`UDL_LIMITS.maxDepth`, 24 levels; the
108
160
  deepest conformance document reaches 12). This is what protects
109
- `diffNounEvolution`, which takes snapshots no validator has seen, and it
110
- closes the case where `snapshotUdlNoun` returns a cyclic snapshot that only
161
+ `diffInstrumentEvolution`, which takes snapshots no validator has seen, and it
162
+ closes the case where `snapshotUdlInstrument` returns a cyclic snapshot that only
111
163
  detonates when something later stringifies it.
112
164
 
113
165
  ### Added
@@ -152,7 +204,9 @@ First public release. Format version 1.
152
204
  - `bun run spec:check`, which fails when the committed schema drifts from the
153
205
  grammar it was generated from.
154
206
 
155
- [Unreleased]: https://github.com/hyperscale0/hyperscale-udl/compare/v1.0.0-alpha.5...HEAD
207
+ [Unreleased]: https://github.com/hyperscale0/hyperscale-udl/compare/v1.0.0-rc.1...HEAD
208
+ [1.0.0-rc.1]: https://github.com/hyperscale0/hyperscale-udl/compare/v1.0.0-beta.1...v1.0.0-rc.1
209
+ [1.0.0-beta.1]: https://github.com/hyperscale0/hyperscale-udl/compare/v1.0.0-alpha.5...v1.0.0-beta.1
156
210
  [1.0.0-alpha.5]: https://github.com/hyperscale0/hyperscale-udl/compare/v1.0.0-alpha.4...v1.0.0-alpha.5
157
211
  [1.0.0-alpha.4]: https://github.com/hyperscale0/hyperscale-udl/compare/v1.0.0-alpha.3...v1.0.0-alpha.4
158
212
  [1.0.0-alpha.3]: https://github.com/hyperscale0/hyperscale-udl/compare/v1.0.0-alpha.2...v1.0.0-alpha.3
package/README.md CHANGED
@@ -1,22 +1,10 @@
1
1
  # UDL
2
2
 
3
- UDL is the Universal Domain Language: a JSON document format for describing
4
- a financial product in business terms. One `.udl` file declares the product's
5
- subjects, its nouns, each noun's lifecycle, the verbs that move instances
6
- through that lifecycle, and how money moves while they do. Everything else,
7
- SDKs, docs, tool surfaces, the running engine, is generated from it or checked
8
- against it.
9
-
10
- The language deliberately cannot say certain things. There is no statement
11
- format, no file drop, no polling loop, no cutoff time, no scheme name, no
12
- reconciliation vocabulary. Those are real and they are somebody's problem, but
13
- they are not the product, so they are absorbed below the language and never
14
- surface in a document. What is left is small enough to hold in your head.
15
-
16
- This package is the reference implementation: parser, semantic validator,
17
- canonical serializer, evolution diff, and the `udl` command. The format itself
18
- is 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 @@ is 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-rc.1` freezes the candidate format for one full wave of use. Pin the release candidate while testing another implementation. Format 1 freezes when the package reaches `1.0.0`.
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,103 +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 version that already has live instances?
83
- const live = parseUdl(await readFile("note.live.udl"));
84
- const violations = diffValidatedUdlEvolution(live, document);
68
+ const previous = parseUdl(await readFile("note.previous.udl"));
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
- live noun sails through a diff that never checked whether the noun was
95
- referenced. The `UdlDocument` parameter types are what keeps the compiler on
96
- 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.
97
75
 
98
- ## 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.
99
77
 
100
- ```bash
101
- udl validate product.udl # parse and report every issue found
102
- udl fmt product.udl # print the canonical form
103
- udl fmt product.udl --write # rewrite the file in place
104
- udl diff live.udl product.udl # is the change additive, or does it break?
105
- ```
106
-
107
- Exit codes: `0` the document is admissible or the change is additive, `1` the
108
- document was refused or the change breaks the append-only law, `2` the command
109
- line was wrong or a file could not be read. That split matters in CI: a broken
110
- 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.
111
79
 
112
- ## The two things worth knowing
80
+ ## Command line
113
81
 
114
- **Evolution is append-only.** Once a definition has live instances, you may add
115
- states, transitions, optional fields, and verbs. You may not remove, rename,
116
- tighten, or change a money step. `udl diff` is not advice; it is the same
117
- 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
+ ```
118
90
 
119
- **Internal ledger money moves through four instructions and no others.**
120
- `internal_transfer.create`, `.reserve`, `.post`, `.void`. Three account
121
- instructions complete the sealed set. A noun cannot invent a fifth money path,
122
- which is why a document can be checked for stranded value before anything runs.
123
- The separate `payout` intent carries a stored amount to the execution core and
124
- captures its durable reference. A later `requiresSettlement` transition stays
125
- 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.
126
92
 
127
- ## The spec
93
+ ## Documentation
128
94
 
129
- - [`spec/README.md`](./spec/README.md) is the specification: the ten laws, the
130
- canonical form, the issue codes, and what the schema deliberately cannot say.
131
- - [`spec/udl.schema.json`](./spec/udl.schema.json) is JSON Schema 2020-12,
132
- generated from the grammar. Where prose and schema disagree, the schema wins.
133
- - [`conformance/`](./conformance/README.md) is the semantic spec: `.udl` inputs
134
- with expected verdicts, canonical bytes, and issue codes, runnable from any
135
- 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)
136
101
 
137
102
  ## Versioning
138
103
 
139
- Two numbers move independently.
140
-
141
- **Format version** is the literal `"udl": 1` inside a document. Format 1 is the
142
- only format that exists.
143
-
144
- **Package version** is this package's semver, in `package.json`.
145
-
146
- Format 1 is unstable until the package reaches 1.0.0. Until then an alpha
147
- release may change what format 1 accepts, and every such change is listed in
148
- [`CHANGELOG.md`](./CHANGELOG.md). After 1.0.0, format 1 is frozen and an
149
- incompatible change bumps the literal to `2`.
150
-
151
- ## Status
152
-
153
- Alpha. The format is in use, the API surface is settled enough to build on, and
154
- the version number is honest about the rest. Breaking changes go in the
155
- changelog, not in a footnote.
156
-
157
- ## 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.
158
105
 
159
- Issues only. Hyperscale makes the changes to the format and the package; you
160
- propose them in an issue carrying the use case and the conformance case it
161
- would add. [`CONTRIBUTING.md`](./CONTRIBUTING.md) has that model in full, plus
162
- the dev setup, the test commands, and how to regenerate the spec. Conduct:
163
- [`CODE_OF_CONDUCT.md`](./CODE_OF_CONDUCT.md). Vulnerabilities:
164
- [`SECURITY.md`](./SECURITY.md).
106
+ ## Contributing and license
165
107
 
166
- ## 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.
167
109
 
168
- AGPL-3.0-only, with a commercial license available from Hyperscale LLC for
169
- organisations that cannot accept the AGPL. See [`LICENSE`](./LICENSE) for the
170
- text and [`LICENSING.md`](./LICENSING.md) for which one you want and how to ask
171
- for the commercial one. The marks are not covered by either; see
172
- [`TRADEMARKS.md`](./TRADEMARKS.md), which also carries the rule for claiming
173
- 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
+ }