@hyperscale0/udl 1.0.0-alpha.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 (97) hide show
  1. package/AUTHORS +8 -0
  2. package/CHANGELOG.md +36 -0
  3. package/LICENSE +661 -0
  4. package/LICENSING.md +51 -0
  5. package/README.md +163 -0
  6. package/SECURITY.md +42 -0
  7. package/TRADEMARKS.md +35 -0
  8. package/conformance/README.md +79 -0
  9. package/conformance/invalid/blank-title.expected.json +10 -0
  10. package/conformance/invalid/blank-title.udl +50 -0
  11. package/conformance/invalid/depth-budget.expected.json +10 -0
  12. package/conformance/invalid/depth-budget.udl +49 -0
  13. package/conformance/invalid/format-version.expected.json +10 -0
  14. package/conformance/invalid/format-version.udl +50 -0
  15. package/conformance/invalid/invalid-utf8.expected.json +10 -0
  16. package/conformance/invalid/invalid-utf8.udl +1 -0
  17. package/conformance/invalid/malformed-json.expected.json +10 -0
  18. package/conformance/invalid/malformed-json.udl +1 -0
  19. package/conformance/invalid/missing-create-verb.expected.json +10 -0
  20. package/conformance/invalid/missing-create-verb.udl +45 -0
  21. package/conformance/invalid/not-an-object.expected.json +10 -0
  22. package/conformance/invalid/not-an-object.udl +1 -0
  23. package/conformance/invalid/noun-id-not-snake-case.expected.json +10 -0
  24. package/conformance/invalid/noun-id-not-snake-case.udl +50 -0
  25. package/conformance/invalid/unknown-key.expected.json +10 -0
  26. package/conformance/invalid/unknown-key.udl +51 -0
  27. package/conformance/invalid/unreachable-state.expected.json +10 -0
  28. package/conformance/invalid/unreachable-state.udl +51 -0
  29. package/conformance/invalid/verb-without-transition.expected.json +10 -0
  30. package/conformance/invalid/verb-without-transition.udl +55 -0
  31. package/conformance/valid/cards.expected.json +5 -0
  32. package/conformance/valid/cards.udl +1092 -0
  33. package/conformance/valid/commerce-escrow.expected.json +5 -0
  34. package/conformance/valid/commerce-escrow.udl +1011 -0
  35. package/conformance/valid/hand-edited.expected.json +5 -0
  36. package/conformance/valid/hand-edited.udl +1 -0
  37. package/conformance/valid/insured-car-marketplace.expected.json +5 -0
  38. package/conformance/valid/insured-car-marketplace.udl +831 -0
  39. package/conformance/valid/insured-travel.expected.json +5 -0
  40. package/conformance/valid/insured-travel.udl +2621 -0
  41. package/conformance/valid/minimal.expected.json +5 -0
  42. package/conformance/valid/minimal.udl +50 -0
  43. package/conformance/valid/protection.expected.json +5 -0
  44. package/conformance/valid/protection.udl +1136 -0
  45. package/dist/canonical.d.ts +2 -0
  46. package/dist/canonical.d.ts.map +1 -0
  47. package/dist/canonical.js +35 -0
  48. package/dist/canonical.js.map +1 -0
  49. package/dist/cli.d.ts +3 -0
  50. package/dist/cli.d.ts.map +1 -0
  51. package/dist/cli.js +138 -0
  52. package/dist/cli.js.map +1 -0
  53. package/dist/duration.d.ts +3 -0
  54. package/dist/duration.d.ts.map +1 -0
  55. package/dist/duration.js +23 -0
  56. package/dist/duration.js.map +1 -0
  57. package/dist/evolution.d.ts +63 -0
  58. package/dist/evolution.d.ts.map +1 -0
  59. package/dist/evolution.js +345 -0
  60. package/dist/evolution.js.map +1 -0
  61. package/dist/finance.d.ts +53 -0
  62. package/dist/finance.d.ts.map +1 -0
  63. package/dist/finance.js +511 -0
  64. package/dist/finance.js.map +1 -0
  65. package/dist/index.d.ts +11 -0
  66. package/dist/index.d.ts.map +1 -0
  67. package/dist/index.js +8 -0
  68. package/dist/index.js.map +1 -0
  69. package/dist/limits.d.ts +33 -0
  70. package/dist/limits.d.ts.map +1 -0
  71. package/dist/limits.js +33 -0
  72. package/dist/limits.js.map +1 -0
  73. package/dist/parser.d.ts +4 -0
  74. package/dist/parser.d.ts.map +1 -0
  75. package/dist/parser.js +54 -0
  76. package/dist/parser.js.map +1 -0
  77. package/dist/schema.d.ts +757 -0
  78. package/dist/schema.d.ts.map +1 -0
  79. package/dist/schema.js +281 -0
  80. package/dist/schema.js.map +1 -0
  81. package/dist/validation.d.ts +64 -0
  82. package/dist/validation.d.ts.map +1 -0
  83. package/dist/validation.js +1323 -0
  84. package/dist/validation.js.map +1 -0
  85. package/package.json +84 -0
  86. package/spec/README.md +137 -0
  87. package/spec/udl.schema.json +1019 -0
  88. package/src/canonical.ts +41 -0
  89. package/src/cli.ts +148 -0
  90. package/src/duration.ts +26 -0
  91. package/src/evolution.ts +587 -0
  92. package/src/finance.ts +775 -0
  93. package/src/index.ts +59 -0
  94. package/src/limits.ts +32 -0
  95. package/src/parser.ts +61 -0
  96. package/src/schema.ts +339 -0
  97. package/src/validation.ts +2053 -0
package/LICENSING.md ADDED
@@ -0,0 +1,51 @@
1
+ # Licensing
2
+
3
+ The code in this repository is Copyright 2026 Hyperscale LLC and is licensed
4
+ under the GNU Affero General Public License version 3 only
5
+ (`AGPL-3.0-only`). The full text is in [`LICENSE`](./LICENSE).
6
+
7
+ ## What the AGPL asks of you
8
+
9
+ Two obligations matter in practice.
10
+
11
+ **If you distribute a copy in object form**, modified or not, you make its
12
+ Corresponding Source available under the same license.
13
+
14
+ **If you run a modified copy as a network service**, section 13 applies: the
15
+ users interacting with it over the network get an offer of the source of the
16
+ version you are running. Running UDL unmodified behind your own service does
17
+ not trigger this. Changing the parser, the validator, or the canonical
18
+ serializer and then serving that change does.
19
+
20
+ Using `@hyperscale0/udl` as a library inside your own program is a combined
21
+ work under the AGPL. If that does not fit how you ship, take the commercial
22
+ license instead.
23
+
24
+ ## Commercial license
25
+
26
+ Hyperscale LLC sells a commercial license to organisations that cannot accept
27
+ the AGPL, for the usual reasons: a proprietary product that links the library,
28
+ a hosted service you will not open, a procurement policy that refuses copyleft.
29
+ It grants the same code under ordinary commercial terms with no source
30
+ obligation.
31
+
32
+ Ask through <https://hyperscale0.ai>.
33
+
34
+ ## The license covers code, not marks
35
+
36
+ "Hyperscale" and "UDL" are trademarks of Hyperscale LLC. A copyright license
37
+ says nothing about trademarks in either direction, so
38
+ [`TRADEMARKS.md`](./TRADEMARKS.md) draws that boundary.
39
+
40
+ ## The format is not the implementation
41
+
42
+ UDL the format is separate from this implementation of it. The JSON Schema in
43
+ [`spec/`](./spec) and the conformance cases in [`conformance/`](./conformance)
44
+ are data describing a format, and anyone may write their own parser, validator,
45
+ or serializer against them, in any language, under any license, without
46
+ touching this code.
47
+
48
+ What you may then say about it is a trademark question, not a copyright one.
49
+ [`TRADEMARKS.md`](./TRADEMARKS.md) has the rule: claim compatibility with a UDL
50
+ version only when your implementation passes that version's published
51
+ conformance cases unmodified.
package/README.md ADDED
@@ -0,0 +1,163 @@
1
+ # UDL
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).
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ npm install @hyperscale0/udl@alpha
25
+ ```
26
+
27
+ There is no `latest` tag before 1.0.0, so a bare `npm install @hyperscale0/udl`
28
+ fails loudly rather than installing something you did not choose.
29
+
30
+ ## Thirty seconds
31
+
32
+ `note.udl`, the smallest document the format accepts:
33
+
34
+ ```json
35
+ {
36
+ "nouns": [
37
+ {
38
+ "fields": { "reference": { "type": "string" } },
39
+ "id": "note",
40
+ "idPrefix": "note",
41
+ "lifecycle": {
42
+ "initial": "open",
43
+ "states": ["open", "closed"],
44
+ "transitions": { "close": { "from": ["open"], "to": "closed" } }
45
+ },
46
+ "required": ["reference"],
47
+ "summary": "A note a tenant files and later closes.",
48
+ "title": "Note",
49
+ "verbs": {
50
+ "close": { "moves": [], "steps": [], "summary": "Close the note." },
51
+ "create": { "moves": [], "steps": [], "summary": "File the note." }
52
+ }
53
+ }
54
+ ],
55
+ "product": "minimal",
56
+ "subjects": [],
57
+ "title": "Minimal",
58
+ "udl": 1,
59
+ "version": 1
60
+ }
61
+ ```
62
+
63
+ ```ts
64
+ import { readFile, writeFile } from "node:fs/promises";
65
+ import {
66
+ diffUdlEvolution,
67
+ parseUdl,
68
+ serializeUdl,
69
+ validateUdl,
70
+ } from "@hyperscale0/udl";
71
+
72
+ const document = parseUdl(await readFile("note.udl"));
73
+
74
+ // One document, one byte sequence. Sorted keys, two-space indent, final LF.
75
+ await writeFile("note.udl", serializeUdl(document));
76
+
77
+ // Issues carry a stable code and a JSON path, never just a sentence.
78
+ const result = validateUdl(document);
79
+ if (!result.ok) console.error(result.issues);
80
+
81
+ // Is this change legal against the version that already has live instances?
82
+ const live = parseUdl(await readFile("note.live.udl"));
83
+ const violations = diffUdlEvolution(live, document);
84
+ ```
85
+
86
+ `parseUdl` takes a string or a `Uint8Array`; bytes are decoded as strict UTF-8.
87
+ `serializeUdl` validates before it writes, so an invalid document has no
88
+ canonical form.
89
+
90
+ ## The command
91
+
92
+ ```bash
93
+ udl validate product.udl # parse and report every issue found
94
+ udl fmt product.udl # print the canonical form
95
+ udl fmt product.udl --write # rewrite the file in place
96
+ udl diff live.udl product.udl # is the change additive, or does it break?
97
+ ```
98
+
99
+ Exit codes: `0` the document is admissible or the change is additive, `1` the
100
+ document was refused or the change breaks the append-only law, `2` the command
101
+ line was wrong or a file could not be read. That split matters in CI: a broken
102
+ document and a broken invocation are different failures.
103
+
104
+ ## The two things worth knowing
105
+
106
+ **Evolution is append-only.** Once a definition has live instances, you may add
107
+ states, transitions, optional fields, and verbs. You may not remove, rename,
108
+ tighten, or change a money step. `udl diff` is not advice; it is the same
109
+ function the compiler runs before it will accept a new version.
110
+
111
+ **Money moves through four instructions and no others.**
112
+ `internal_transfer.create`, `.reserve`, `.post`, `.void`. Three account
113
+ instructions complete the sealed set. A noun cannot invent a fifth money path,
114
+ which is why a document can be checked for stranded value before anything runs.
115
+
116
+ ## The spec
117
+
118
+ - [`spec/README.md`](./spec/README.md) is the specification: the ten laws, the
119
+ canonical form, the issue codes, and what the schema deliberately cannot say.
120
+ - [`spec/udl.schema.json`](./spec/udl.schema.json) is JSON Schema 2020-12,
121
+ generated from the grammar. Where prose and schema disagree, the schema wins.
122
+ - [`conformance/`](./conformance/README.md) is the semantic spec: `.udl` inputs
123
+ with expected verdicts, canonical bytes, and issue codes, runnable from any
124
+ language.
125
+
126
+ ## Versioning
127
+
128
+ Two numbers move independently.
129
+
130
+ **Format version** is the literal `"udl": 1` inside a document. Format 1 is the
131
+ only format that exists.
132
+
133
+ **Package version** is this package's semver, currently `1.0.0-alpha.1`,
134
+ published only under the `alpha` dist-tag.
135
+
136
+ Format 1 is unstable until the package reaches 1.0.0. Until then an alpha
137
+ release may change what format 1 accepts, and every such change is listed in
138
+ [`CHANGELOG.md`](./CHANGELOG.md). After 1.0.0, format 1 is frozen and an
139
+ incompatible change bumps the literal to `2`.
140
+
141
+ ## Status
142
+
143
+ Alpha. The format is in use, the API surface is settled enough to build on, and
144
+ the version number is honest about the rest. Breaking changes go in the
145
+ changelog, not in a footnote.
146
+
147
+ ## Contributing
148
+
149
+ Issues only. Hyperscale makes the changes to the format and the package; you
150
+ propose them in an issue carrying the use case and the conformance case it
151
+ would add. [`CONTRIBUTING.md`](./CONTRIBUTING.md) has that model in full, plus
152
+ the dev setup, the test commands, and how to regenerate the spec. Conduct:
153
+ [`CODE_OF_CONDUCT.md`](./CODE_OF_CONDUCT.md). Vulnerabilities:
154
+ [`SECURITY.md`](./SECURITY.md).
155
+
156
+ ## License
157
+
158
+ AGPL-3.0-only, with a commercial license available from Hyperscale LLC for
159
+ organisations that cannot accept the AGPL. See [`LICENSE`](./LICENSE) for the
160
+ text and [`LICENSING.md`](./LICENSING.md) for which one you want and how to ask
161
+ for the commercial one. The marks are not covered by either; see
162
+ [`TRADEMARKS.md`](./TRADEMARKS.md), which also carries the rule for claiming
163
+ UDL compatibility.
package/SECURITY.md ADDED
@@ -0,0 +1,42 @@
1
+ # Security
2
+
3
+ ## Reporting a vulnerability
4
+
5
+ Report privately through GitHub, using this repository's
6
+ [private vulnerability reporting form](https://github.com/hyperscale0/hyperscale-udl/security/advisories/new).
7
+ That is the only intake. There is no security email address, and nothing
8
+ security-sensitive belongs in an issue, a pull request, a discussion, or a
9
+ commit message.
10
+
11
+ A report we can act on names the affected version, describes the surface, and
12
+ gives us something to run: a `.udl` file, an input, a snippet. If you can shape
13
+ it as a conformance case, do that; it goes straight into the fix.
14
+
15
+ ## What counts
16
+
17
+ This package parses untrusted documents, so the interesting failures are the
18
+ ones a document can cause:
19
+
20
+ - A document that gets past `validateUdl` but should not, especially one that
21
+ breaks a money-graph law.
22
+ - A document that makes the parser or validator burn unbounded time or memory.
23
+ Admission budgets (source bytes, nesting depth, node count, string length,
24
+ regex search space) exist precisely to make this impossible; a way around one
25
+ is a vulnerability.
26
+ - A document that makes `serializeUdl` produce bytes that reparse into a
27
+ different document.
28
+ - An evolution diff that reports a breaking change as additive.
29
+
30
+ Out of scope: the `udl` command reading a file you told it to read, and
31
+ anything that requires already controlling the machine running it.
32
+
33
+ ## Supported versions
34
+
35
+ Alpha releases are supported at the newest published `alpha` version only.
36
+ Fixes land there; there is no backport branch before 1.0.0.
37
+
38
+ ## Disclosure
39
+
40
+ We will confirm receipt, tell you what we found, and agree a disclosure date
41
+ with you before publishing an advisory. If a fix is not straightforward we will
42
+ say so rather than go quiet.
package/TRADEMARKS.md ADDED
@@ -0,0 +1,35 @@
1
+ # Trademarks
2
+
3
+ "Hyperscale" and "UDL" are trademarks of Hyperscale LLC.
4
+
5
+ The AGPL covers the code in this repository and nothing else. It grants no
6
+ rights to the marks, and it never mentions them: a copyright license says
7
+ nothing about trademarks either way. This file draws that boundary so nobody
8
+ has to guess where it sits.
9
+
10
+ What that means in practice:
11
+
12
+ - **Yes.** Say your project uses UDL, reads UDL, or is compatible with UDL.
13
+ Say it in your README, your docs, your talk, and your package description.
14
+ Fork this repository and keep the notices intact.
15
+ - **Yes.** Publish a UDL implementation in another language, and name it in a
16
+ way that describes what it does: `udl-rs`, `udl-parser`, `python-udl`.
17
+ - **No.** Name your project, company, or product in a way that suggests
18
+ Hyperscale LLC published it or endorses it. `@hyperscale0/*` on npm and the
19
+ `hyperscale0` GitHub organisation are ours.
20
+ - **No.** Use the marks or our logo in a way that implies affiliation,
21
+ sponsorship, or certification we have not given.
22
+
23
+ ## Claiming compatibility
24
+
25
+ An independent implementation may say it "implements UDL version X" or "passes
26
+ the UDL conformance suite version X" only while it passes the published
27
+ conformance cases for that version, unmodified. The cases in
28
+ [`conformance/`](./conformance) are the whole test: no skipped case, no edited
29
+ expectation, no local fork of the fixtures.
30
+
31
+ That claim is a statement about your implementation, so keep the marks out of
32
+ its name and off its logo, and do not present it as endorsement or
33
+ certification by Hyperscale LLC. We certify nothing; the suite does.
34
+
35
+ If you are unsure, open an issue and ask. Nobody has ever regretted asking.
@@ -0,0 +1,79 @@
1
+ # The UDL conformance suite
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.
9
+
10
+ ## Layout
11
+
12
+ ```
13
+ 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
16
+ ```
17
+
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.
21
+
22
+ ## Expected files
23
+
24
+ A valid case:
25
+
26
+ ```json
27
+ {
28
+ "canonical": "minimal.udl",
29
+ "summary": "Compact, reverse-ordered bytes canonicalize to minimal.udl.",
30
+ "verdict": "valid"
31
+ }
32
+ ```
33
+
34
+ `canonical` names the file in `valid/` holding the canonical bytes for this
35
+ input. A document that is already canonical names itself.
36
+
37
+ An invalid case:
38
+
39
+ ```json
40
+ {
41
+ "issues": [{ "code": "invalid_semantics", "path": "$.nouns[0].verbs" }],
42
+ "summary": "Every noun declares create: nothing else can bring an instance into being.",
43
+ "verdict": "invalid"
44
+ }
45
+ ```
46
+
47
+ `summary` is for humans reading a failure. Nothing asserts against it beyond
48
+ requiring it to be present and non-blank.
49
+
50
+ ## The three levels
51
+
52
+ An implementation claims conformance at the highest level it passes.
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.
57
+
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.
62
+
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.
68
+
69
+ ## Adding a case
70
+
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.
76
+
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.
@@ -0,0 +1,10 @@
1
+ {
2
+ "issues": [
3
+ {
4
+ "code": "invalid_shape",
5
+ "path": "$.title"
6
+ }
7
+ ],
8
+ "summary": "Whitespace is not a title. JSON Schema cannot express this check, so it lives here.",
9
+ "verdict": "invalid"
10
+ }
@@ -0,0 +1,50 @@
1
+ {
2
+ "nouns": [
3
+ {
4
+ "fields": {
5
+ "reference": {
6
+ "type": "string"
7
+ }
8
+ },
9
+ "id": "note",
10
+ "idPrefix": "note",
11
+ "lifecycle": {
12
+ "initial": "open",
13
+ "states": [
14
+ "open",
15
+ "closed"
16
+ ],
17
+ "transitions": {
18
+ "close": {
19
+ "from": [
20
+ "open"
21
+ ],
22
+ "to": "closed"
23
+ }
24
+ }
25
+ },
26
+ "required": [
27
+ "reference"
28
+ ],
29
+ "summary": "A note a tenant files and later closes.",
30
+ "title": "Note",
31
+ "verbs": {
32
+ "close": {
33
+ "moves": [],
34
+ "steps": [],
35
+ "summary": "Close the note."
36
+ },
37
+ "create": {
38
+ "moves": [],
39
+ "steps": [],
40
+ "summary": "File the note."
41
+ }
42
+ }
43
+ }
44
+ ],
45
+ "product": "minimal",
46
+ "subjects": [],
47
+ "title": " ",
48
+ "udl": 1,
49
+ "version": 1
50
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "issues": [
3
+ {
4
+ "code": "resource_limit",
5
+ "path": "$.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested"
6
+ }
7
+ ],
8
+ "summary": "Admission budgets are part of the format: nesting past 24 levels is refused before validation.",
9
+ "verdict": "invalid"
10
+ }
@@ -0,0 +1,49 @@
1
+ {
2
+ "nested": {
3
+ "nested": {
4
+ "nested": {
5
+ "nested": {
6
+ "nested": {
7
+ "nested": {
8
+ "nested": {
9
+ "nested": {
10
+ "nested": {
11
+ "nested": {
12
+ "nested": {
13
+ "nested": {
14
+ "nested": {
15
+ "nested": {
16
+ "nested": {
17
+ "nested": {
18
+ "nested": {
19
+ "nested": {
20
+ "nested": {
21
+ "nested": {
22
+ "nested": {
23
+ "nested": {
24
+ "nested": {
25
+ "nested": {}
26
+ }
27
+ }
28
+ }
29
+ }
30
+ }
31
+ }
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
37
+ }
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "issues": [
3
+ {
4
+ "code": "invalid_shape",
5
+ "path": "$.udl"
6
+ }
7
+ ],
8
+ "summary": "A document declares its format version literally; format 2 does not exist yet.",
9
+ "verdict": "invalid"
10
+ }
@@ -0,0 +1,50 @@
1
+ {
2
+ "nouns": [
3
+ {
4
+ "fields": {
5
+ "reference": {
6
+ "type": "string"
7
+ }
8
+ },
9
+ "id": "note",
10
+ "idPrefix": "note",
11
+ "lifecycle": {
12
+ "initial": "open",
13
+ "states": [
14
+ "open",
15
+ "closed"
16
+ ],
17
+ "transitions": {
18
+ "close": {
19
+ "from": [
20
+ "open"
21
+ ],
22
+ "to": "closed"
23
+ }
24
+ }
25
+ },
26
+ "required": [
27
+ "reference"
28
+ ],
29
+ "summary": "A note a tenant files and later closes.",
30
+ "title": "Note",
31
+ "verbs": {
32
+ "close": {
33
+ "moves": [],
34
+ "steps": [],
35
+ "summary": "Close the note."
36
+ },
37
+ "create": {
38
+ "moves": [],
39
+ "steps": [],
40
+ "summary": "File the note."
41
+ }
42
+ }
43
+ }
44
+ ],
45
+ "product": "minimal",
46
+ "subjects": [],
47
+ "title": "Minimal",
48
+ "udl": 2,
49
+ "version": 1
50
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "issues": [
3
+ {
4
+ "code": "invalid_utf8",
5
+ "path": "$"
6
+ }
7
+ ],
8
+ "summary": "A UDL file is strict UTF-8; a lone continuation byte is refused before JSON parsing.",
9
+ "verdict": "invalid"
10
+ }
@@ -0,0 +1 @@
1
+ {"udl": 1, "title": "�"}
@@ -0,0 +1,10 @@
1
+ {
2
+ "issues": [
3
+ {
4
+ "code": "invalid_json",
5
+ "path": "$"
6
+ }
7
+ ],
8
+ "summary": "Bytes that are not JSON at all report a parse failure, not a grammar failure.",
9
+ "verdict": "invalid"
10
+ }
@@ -0,0 +1 @@
1
+ {"udl": 1,
@@ -0,0 +1,10 @@
1
+ {
2
+ "issues": [
3
+ {
4
+ "code": "invalid_semantics",
5
+ "path": "$.nouns[0].verbs"
6
+ }
7
+ ],
8
+ "summary": "Every noun declares create: nothing else can bring an instance into being.",
9
+ "verdict": "invalid"
10
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "nouns": [
3
+ {
4
+ "fields": {
5
+ "reference": {
6
+ "type": "string"
7
+ }
8
+ },
9
+ "id": "note",
10
+ "idPrefix": "note",
11
+ "lifecycle": {
12
+ "initial": "open",
13
+ "states": [
14
+ "open",
15
+ "closed"
16
+ ],
17
+ "transitions": {
18
+ "close": {
19
+ "from": [
20
+ "open"
21
+ ],
22
+ "to": "closed"
23
+ }
24
+ }
25
+ },
26
+ "required": [
27
+ "reference"
28
+ ],
29
+ "summary": "A note a tenant files and later closes.",
30
+ "title": "Note",
31
+ "verbs": {
32
+ "close": {
33
+ "moves": [],
34
+ "steps": [],
35
+ "summary": "Close the note."
36
+ }
37
+ }
38
+ }
39
+ ],
40
+ "product": "minimal",
41
+ "subjects": [],
42
+ "title": "Minimal",
43
+ "udl": 1,
44
+ "version": 1
45
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "issues": [
3
+ {
4
+ "code": "invalid_shape",
5
+ "path": "$"
6
+ }
7
+ ],
8
+ "summary": "Valid JSON that is not a UDL document.",
9
+ "verdict": "invalid"
10
+ }
@@ -0,0 +1 @@
1
+ "a string"
@@ -0,0 +1,10 @@
1
+ {
2
+ "issues": [
3
+ {
4
+ "code": "invalid_shape",
5
+ "path": "$.nouns[0].id"
6
+ }
7
+ ],
8
+ "summary": "Naming law: noun ids are snake_case, singular, plain business English.",
9
+ "verdict": "invalid"
10
+ }