@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
@@ -0,0 +1,22 @@
1
+ <!-- Generated by scripts/docs/build.ts from @hyperscale0/udl 1.0.0. Edit the source, not cli.md. -->
2
+
3
+ # Command reference
4
+
5
+ The installed `udl` binary exposes the following commands and exit codes.
6
+
7
+ ```text
8
+ udl - the Universal Domain Language toolchain
9
+
10
+ usage:
11
+ udl validate <file> parse <file> and report every issue found
12
+ udl fmt <file> [--write] print the canonical form, or rewrite the file
13
+ udl canon <file> [--digest] print canonical bytes, or their SHA-256 digest
14
+ udl diff <live> <next> check <next> against the append-only law
15
+ udl explain UDL#### explain one stable diagnostic code
16
+ udl help print this
17
+
18
+ exit codes:
19
+ 0 the document is admissible, or the change is additive
20
+ 1 the document was refused, or the change breaks the append-only law
21
+ 2 the command line was wrong, or a file could not be read
22
+ ```
@@ -0,0 +1,32 @@
1
+ <!-- Generated by scripts/docs/build.ts from @hyperscale0/udl 1.0.0. Edit the source, not diagnostics.md. -->
2
+
3
+ # Diagnostic reference
4
+
5
+ Codes are stable. Titles and messages may become clearer without changing the code. Apply the listed fix, then validate the whole document again.
6
+
7
+ | Code | Family | Category | Title | Fix |
8
+ | --------- | --------- | ----------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
9
+ | `UDL1001` | admission | invalid_utf8 | Invalid UTF-8 | Encode the document as valid UTF-8. |
10
+ | `UDL1002` | admission | invalid_json | Invalid JSON | Repair the JSON syntax before validation. |
11
+ | `UDL1003` | admission | invalid_shape | Invalid document shape | Match the published UDL JSON Schema. |
12
+ | `UDL1004` | admission | resource_limit | Resource limit exceeded | Reduce the source size, nesting, values, strings, references, or financial paths named by the message. |
13
+ | `UDL2001` | document | invalid_semantics | Duplicate declaration | Give each declaration a unique name. |
14
+ | `UDL2002` | document | invalid_semantics | Document law violation | Repair the declaration, subject contract, or derived effects named by the message. |
15
+ | `UDL2005` | document | invalid_semantics | Derived effects mismatch | Regenerate the action effects from its clauses. |
16
+ | `UDL3001` | lifecycle | invalid_semantics | Lifecycle is not closed | Declare every state and action transition, and make every state reachable. |
17
+ | `UDL4001` | finance | invalid_semantics | Money graph violation | Balance every funded amount and close every hold on each lifecycle path. |
18
+ | `UDL5001` | gates | invalid_semantics | Reference gate violation | Point the gate at a declared instrument, action, state, field, and reference. |
19
+ | `UDL5002` | gates | invalid_semantics | Check requirement violation | Use a declared check with compatible evidence and recurrence. |
20
+ | `UDL5003` | gates | invalid_semantics | Exposure gate violation | Use declared account and money fields for the exposure gate. |
21
+ | `UDL5004` | gates | invalid_semantics | Aggregate law violation | Point the aggregate at compatible parent and child fields. |
22
+ | `UDL5005` | gates | invalid_semantics | Settlement or payout violation | Use a declared settlement account and a compatible payout statement line. |
23
+ | `UDL5006` | gates | invalid_semantics | Quote and commit violation | Declare one complete quote freeze set and one matching commit action. |
24
+ | `UDL5007` | gates | invalid_semantics | Reconcile exception child violation | Name a declared child whose reference points back to this instrument. |
25
+ | `UDL5008` | gates | invalid_semantics | Action clause violation | Repair the clause fields and keep incompatible clauses separate. |
26
+ | `UDL5009` | gates | invalid_semantics | Reconcile exception amount field is missing or optional | Name the exception child's required money field in amountField. |
27
+ | `UDL5010` | gates | invalid_semantics | Reconcile exception amount field has the wrong type | Point amountField at a money field declared by the exception child. |
28
+ | `UDL5011` | gates | invalid_semantics | Reconcile exception reason field is missing or optional | Name the exception child's required text field in reasonField. |
29
+ | `UDL5012` | gates | invalid_semantics | Reconcile exception reason field has the wrong type | Point reasonField at a required plain text field declared by the exception child. |
30
+ | `UDL6001` | schema | invalid_semantics | Unsupported JSON Schema | Use only the sealed UDL JSON Schema subset. |
31
+ | `UDL7001` | evolution | invalid_evolution | Stored contract changed | Keep stored identities and contracts unchanged, and add only allowed optional declarations. |
32
+ | `UDL7002` | evolution | invalid_evolution | Version was not increased | Increase the product version for every semantic change. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperscale0/udl",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0",
4
4
  "description": "The Universal Domain Language: format spec, parser, validator, canonical serializer, and evolution diff.",
5
5
  "keywords": [
6
6
  "udl",
@@ -47,7 +47,9 @@
47
47
  "SECURITY.md",
48
48
  "TRADEMARKS.md",
49
49
  "conformance",
50
+ "docs",
50
51
  "dist",
52
+ "skills",
51
53
  "spec",
52
54
  "src"
53
55
  ],
@@ -59,7 +61,9 @@
59
61
  },
60
62
  "scripts": {
61
63
  "build": "tsc -p tsconfig.build.json",
62
- "check": "bun run spec:check && bun test && tsc -p tsconfig.json && bun run build && bun scripts/check-bin.ts",
64
+ "check": "bun run spec:check && bun run docs:check && bun test && tsc -p tsconfig.json && bun run build && bun scripts/check-bin.ts",
65
+ "docs:build": "bun scripts/docs/build.ts --write",
66
+ "docs:check": "bun scripts/docs/build.ts --check",
63
67
  "postpack": "bun scripts/pack-exports.ts restore",
64
68
  "prepack": "bun run build && bun scripts/pack-exports.ts apply",
65
69
  "spec": "bun scripts/emit-spec.ts --write",
@@ -0,0 +1,28 @@
1
+ ---
2
+ name: udl
3
+ description: Read, validate, explain, or write Universal Domain Language documents directly. Use for canonical UDL JSON and format-level diagnostics. SDK and MCP consumers should use their generated contracts instead.
4
+ ---
5
+
6
+ # UDL
7
+
8
+ Use UDL when the task concerns the canonical product document, format conformance, or a validator implementation. Use HSX when authoring a product from reusable modules.
9
+
10
+ ## Read first
11
+
12
+ 1. Read `docs/guide/01-a-document.md` for the document frame.
13
+ 2. Read `docs/guide/03-laws.md` before changing lifecycle, money, gates, or references.
14
+ 3. Look up exact fields in `spec/udl.schema.json` and clauses in `docs/reference/clauses.md`.
15
+
16
+ ## Edit loop
17
+
18
+ 1. Change the smallest complete clause.
19
+ 2. Run `udl validate <file>`.
20
+ 3. Look up each `UDL####` code with `udl explain UDL####` or `docs/reference/diagnostics.md`.
21
+ 4. Apply the listed fix. Validate the whole document again.
22
+ 5. Run `udl canon <file> --digest` before pinning bytes or an identity.
23
+
24
+ Never hand-author `effects`. Derive them from action clauses. Never represent money as a JSON number. Use an integer minor-unit string and its declared currency. Never add a money move without checking every terminal lifecycle path for a matching drain or unwind.
25
+
26
+ For a stored definition, validate both documents before `udl diff <live> <next>`. Treat any `UDL7xxx` result as a refused change. A data migration does not make an incompatible document edit additive.
27
+
28
+ Validator work must run the complete `conformance/` contract and follow `docs/reference/canonical.md` byte for byte.
package/spec/README.md CHANGED
@@ -1,10 +1,15 @@
1
1
  # The UDL specification
2
2
 
3
3
  A UDL document describes one product in business terms: its subjects, its
4
- nouns, each noun's lifecycle, the verbs that move instances through it, and how
4
+ instruments, each instrument's lifecycle, the actions that move instances through it, and how
5
5
  money moves while they do. Everything generated from a document (SDKs, docs,
6
6
  tool surfaces, the running engine) is downstream of what is written here.
7
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
+
8
13
  ## The schema is the authority
9
14
 
10
15
  `udl.schema.json` in this directory is JSON Schema 2020-12, generated from the
@@ -17,7 +22,7 @@ no grammar tables and no field lists. Where anything below disagrees with
17
22
  `udl.schema.json`, the schema wins.
18
23
 
19
24
  The schema is generated from the _input_ view of the grammar, so it describes a
20
- document as an author writes it, before any default is filled in. A verb may
25
+ document as an author writes it, before any default is filled in. A action may
21
26
  omit `moves`; a parser hands it back as `[]`.
22
27
 
23
28
  ## The ten laws
@@ -28,12 +33,13 @@ refuses.
28
33
  1. **One-sentence law.** Every concept in UDL is explainable in one sentence to
29
34
  someone who has never seen it. A concept that needs a paragraph is
30
35
  machinery, and machinery does not belong in the language.
31
- 2. **Purity law.** No bank or provider legacy enters UDL: no statement formats,
32
- no file drops, no polling, no batch windows, no cutoff times, no scheme
33
- names, no reconciliation vocabulary, no ISO or SWIFT message types. A
34
- document describes a product, never the plumbing under it.
35
- 3. **Event law.** Every state change emits an event. Names derive from the noun
36
- and the verb's past tense (`escrow_order.released`); the optional
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
37
43
  `eventName` overrides only where the honest past tense is irregular.
38
44
  4. **One-spine law.** Internal ledger money moves through four instructions and no others:
39
45
  `internal_transfer.create`, `.reserve`, `.post`, `.void`. Three account
@@ -42,23 +48,40 @@ refuses.
42
48
  a stored amount and beneficiary reference to the execution core. It is not
43
49
  a kernel instruction and cannot disguise an internal ledger move.
44
50
  5. **Uniform object law.** Every instance carries an opaque prefixed id (from
45
- the noun's `idPrefix`), a `status` drawn from its declared lifecycle, a
51
+ the instrument's `idPrefix`), a `status` drawn from its declared lifecycle, a
46
52
  creation timestamp, and a caller-owned metadata bag. Amounts are
47
53
  string-encoded integer minor units paired with a currency code, never JSON
48
54
  numbers.
49
- 6. **Requirements-as-data law.** Anything a caller must satisfy before a verb
55
+ 6. **Requirements-as-data law.** Anything a caller must satisfy before a action
50
56
  unlocks is declared data: `due`, `deadline`, `requiresRefs`,
51
- `requiresAggregate`, `requiresDrainedAccount`, `requiresSettlement`. A
52
- settlement gate reads a payout reference captured by an earlier payout
53
- intent and records the durable matched evidence reference. No caller may
54
- assert that match.
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.
55
75
  7. **Append-only evolution law.** Once a definition has live instances, adding
56
- states, transitions, optional fields, and verbs is legal; removing,
76
+ states, transitions, optional fields, and actions is legal; removing,
57
77
  renaming, tightening, or changing a money step is not.
58
78
  `diffValidatedUdlEvolution` decides, on two documents the validator has
59
- already admitted; `udl diff` parses both files and then calls it.
60
- 8. **Naming law.** Nouns are `snake_case`, singular, plain business English.
61
- Verbs are single words in imperative present. Operations are `noun.verb`.
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`.
62
85
  Fields are `camelCase`. The patterns live in the schema.
63
86
  9. **Time law.** Delays the world imposes (settlement windows, activation
64
87
  periods, renewal cycles, retries) surface as honest statuses and timestamps
@@ -68,6 +91,19 @@ refuses.
68
91
  every gate names a state that exists, and every funded balance is drained
69
92
  on every terminal path.
70
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
+
71
107
  ## What the schema cannot say
72
108
 
73
109
  JSON Schema pins shape. It does not pin meaning, and four classes of law live
@@ -91,38 +127,21 @@ suite is what makes two implementations agree.
91
127
 
92
128
  ## Issue codes
93
129
 
94
- A refusal reports one or more issues, each with a stable code and a JSON path.
95
- The codes are the cross-implementation contract; the messages are not.
96
-
97
- | Code | Meaning |
98
- | ------------------- | ----------------------------------------------------------------------------------------- |
99
- | `invalid_utf8` | The bytes are not valid UTF-8. |
100
- | `invalid_json` | The bytes are UTF-8 but not JSON. |
101
- | `invalid_shape` | The JSON does not match `udl.schema.json`, or fails a check the schema cannot express. |
102
- | `invalid_semantics` | The shape is right and a whole-document law is broken. |
103
- | `resource_limit` | The document exceeds an admission budget (size, depth, node count, pattern search space). |
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.
104
134
 
105
135
  Paths are `$`-rooted with dotted keys and bracketed array indices:
106
- `$.nouns[0].lifecycle.states[2]`.
136
+ `$.instruments[0].lifecycle.states[2]`.
107
137
 
108
138
  ## Canonical form
109
139
 
110
- Every document has exactly one canonical byte sequence, produced by
111
- `serializeUdl`:
112
-
113
- - UTF-8, JSON, no byte-order mark.
114
- - Object keys sorted ascending by UTF-16 code unit. Sorting is recursive.
115
- - Two spaces of indentation per level of nesting.
116
- - One space after each `:`. Members separated by `,` then a newline.
117
- - An empty object is `{}` and an empty array is `[]`, both on one line.
118
- - Exactly one line feed at the end of the file, and no other trailing
119
- whitespace.
120
- - Non-ASCII characters are written literally, not escaped.
121
-
122
- Two consequences worth stating outright. Serialization validates first, so an
123
- invalid document has no canonical form. And the round trip is byte-stable:
124
- canonicalizing canonical bytes returns them unchanged, which is what lets a
125
- document be diffed, signed, and stored as its own identity.
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.
126
145
 
127
146
  ## Two version numbers
128
147
 
@@ -133,9 +152,21 @@ that exists.
133
152
  **Package version** is the semver of `@hyperscale0/udl`, declared in
134
153
  `package.json`.
135
154
 
136
- They move independently, under one rule: **format 1 is unstable until the
137
- package reaches 1.0.0.** Until then an alpha release may change what format 1
138
- accepts, and the changes are listed in `../CHANGELOG.md`. Once the package
139
- ships 1.0.0, format 1 is frozen and an incompatible format change bumps the
140
- literal to `2`, with both formats readable for as long as the deprecation
141
- window says.
155
+ They move independently, under one rule: **format 1 freezes when the package
156
+ reaches 1.0.0.** The release candidate may still change what format 1 accepts,
157
+ and every change appears in `../CHANGELOG.md`.
158
+
159
+ After 1.0.0, an incompatible grammar change uses a new format literal. Readers
160
+ keep an explicit decoder and validator for each supported old literal. A
161
+ stored format 1 document remains readable under the format 1 rules. It does
162
+ not acquire new required fields from the current package. Passing an old
163
+ document to an evolution comparison can raise the matching `UDL7xxx` removal,
164
+ tightening, lifecycle, or executable-change code, but a package upgrade alone
165
+ does not rewrite its bytes. A product migration must validate and store its new
166
+ document as a separate version before switching instances to it.
167
+
168
+ The evolution codes are explicit. `UDL7001` protects stored identities,
169
+ subjects, instruments, fields, lifecycles, actions, money clauses, gates, and
170
+ policy. `UDL7002` requires a version increase for a semantic change.
171
+ Evolution comparison admits the previous document first. A stored document
172
+ that fails admission is `invalid_previous`, not an evolution issue.