@hyperscale0/udl 2.2.0 → 2.4.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 (88) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +5 -1
  3. package/conformance/invalid/invalid-journeys.expected.json +3 -19
  4. package/conformance/invalid/invalid-journeys.udl +47 -49
  5. package/conformance/valid/attested.expected.json +6 -0
  6. package/conformance/valid/attested.udl +251 -0
  7. package/conformance/valid/hand-edited.expected.json +1 -1
  8. package/conformance/valid/hand-edited.udl +1 -1
  9. package/conformance/valid/minimal.expected.json +1 -1
  10. package/conformance/valid/minimal.udl +0 -15
  11. package/conformance/valid/vocabulary.expected.json +6 -0
  12. package/conformance/valid/vocabulary.udl +1999 -0
  13. package/dist/allocation.d.ts +60 -0
  14. package/dist/allocation.d.ts.map +1 -0
  15. package/dist/allocation.js +177 -0
  16. package/dist/allocation.js.map +1 -0
  17. package/dist/diagnostics.d.ts +1 -31
  18. package/dist/diagnostics.d.ts.map +1 -1
  19. package/dist/diagnostics.js +0 -30
  20. package/dist/diagnostics.js.map +1 -1
  21. package/dist/distribution.d.ts +15 -0
  22. package/dist/distribution.d.ts.map +1 -0
  23. package/dist/distribution.js +49 -0
  24. package/dist/distribution.js.map +1 -0
  25. package/dist/effects.d.ts +0 -6
  26. package/dist/effects.d.ts.map +1 -1
  27. package/dist/effects.js +84 -22
  28. package/dist/effects.js.map +1 -1
  29. package/dist/evolution.d.ts +12 -0
  30. package/dist/evolution.d.ts.map +1 -1
  31. package/dist/evolution.js +42 -1
  32. package/dist/evolution.js.map +1 -1
  33. package/dist/finance.d.ts +18 -1
  34. package/dist/finance.d.ts.map +1 -1
  35. package/dist/finance.js +158 -27
  36. package/dist/finance.js.map +1 -1
  37. package/dist/index.d.ts +9 -3
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +6 -2
  40. package/dist/index.js.map +1 -1
  41. package/dist/instrument-references.d.ts +5 -0
  42. package/dist/instrument-references.d.ts.map +1 -0
  43. package/dist/instrument-references.js +69 -0
  44. package/dist/instrument-references.js.map +1 -0
  45. package/dist/limits.d.ts +3 -3
  46. package/dist/limits.d.ts.map +1 -1
  47. package/dist/limits.js +3 -7
  48. package/dist/limits.js.map +1 -1
  49. package/dist/reference.d.ts +3 -0
  50. package/dist/reference.d.ts.map +1 -0
  51. package/dist/reference.js +28 -0
  52. package/dist/reference.js.map +1 -0
  53. package/dist/schema.d.ts +1232 -83
  54. package/dist/schema.d.ts.map +1 -1
  55. package/dist/schema.js +398 -60
  56. package/dist/schema.js.map +1 -1
  57. package/dist/validation.d.ts +27 -1
  58. package/dist/validation.d.ts.map +1 -1
  59. package/dist/validation.js +327 -187
  60. package/dist/validation.js.map +1 -1
  61. package/dist/vocabulary.d.ts +23 -0
  62. package/dist/vocabulary.d.ts.map +1 -0
  63. package/dist/vocabulary.js +965 -0
  64. package/dist/vocabulary.js.map +1 -0
  65. package/docs/README.md +5 -1
  66. package/docs/funding-custody.md +165 -0
  67. package/docs/guide/09-schedules-and-allocation.md +130 -0
  68. package/docs/llms-full.txt +623 -101
  69. package/docs/llms.txt +1 -1
  70. package/docs/piece-plans.md +148 -0
  71. package/docs/reference/clauses.md +451 -63
  72. package/docs/reference/cli.md +3 -1
  73. package/docs/reference/diagnostics.md +33 -38
  74. package/package.json +5 -6
  75. package/spec/udl.schema.json +1095 -118
  76. package/src/allocation.ts +259 -0
  77. package/src/diagnostics.ts +0 -32
  78. package/src/distribution.ts +61 -0
  79. package/src/effects.ts +116 -22
  80. package/src/evolution.ts +63 -3
  81. package/src/finance.ts +218 -24
  82. package/src/index.ts +31 -3
  83. package/src/instrument-references.ts +98 -0
  84. package/src/limits.ts +3 -7
  85. package/src/reference.ts +31 -0
  86. package/src/schema.ts +417 -66
  87. package/src/validation.ts +452 -243
  88. package/src/vocabulary.ts +1508 -0
@@ -1,4 +1,4 @@
1
- <!-- Generated by scripts/docs/build.ts from @hyperscale0/udl 2.2.0. Edit the source, not llms-full.txt. -->
1
+ <!-- Generated by scripts/docs/build.ts from @hyperscale0/udl 2.4.0. Edit the source, not llms-full.txt. -->
2
2
 
3
3
  # UDL complete reference
4
4
 
@@ -16,8 +16,12 @@ UDL is the versioned JSON contract between a product definition and an engine th
16
16
  6. [Effects](guide/06-effects.md)
17
17
  7. [Evolution](guide/07-evolution.md)
18
18
  8. [Implementing UDL](guide/08-implementing.md)
19
+ 9. [Schedules, allocation and referenced state](guide/09-schedules-and-allocation.md)
19
20
 
20
- The [clause reference](reference/clauses.md) and [diagnostic reference](reference/diagnostics.md) come from the package tables. The [command reference](reference/cli.md) comes from the CLI usage text. Do not edit those generated files by hand.
21
+ [Piece plans and private action composition](piece-plans.md) is a worked
22
+ instrument fragment for `piecePlan`, `pieceStage`, `calls` and `actionLibrary`.
23
+
24
+ The [clause reference](reference/clauses.md) and [diagnostic reference](reference/diagnostics.md) come from the package tables. The [command reference](reference/cli.md) comes from the CLI usage text. Run `bun run docs:build` in a source checkout. Package builds and prepack generate these ignored files automatically and validate local links.
21
25
 
22
26
  <!-- source: guide/01-a-document.md -->
23
27
 
@@ -199,6 +203,139 @@ Do not key behavior on diagnostic messages. Titles, details, and fixes can becom
199
203
 
200
204
  Keep parsing, validation, canonicalization, and evolution comparison separate. That split prevents a diff from judging a candidate that the validator would refuse on its own.
201
205
 
206
+ <!-- source: guide/09-schedules-and-allocation.md -->
207
+
208
+ # Schedules, allocation and referenced state
209
+
210
+ These clauses describe product laws. An engine must implement their admission semantics before accepting a document that uses them. The pure planners exported by this package calculate from trusted snapshots. They do not authenticate a caller, acquire locks, post money or claim an operation identity.
211
+
212
+ ## Fields and dates
213
+
214
+ `derivedAmounts` retains its ordered list of computations. `percentage_of.bps` accepts a literal rate or `{field}`. The field must be required, immutable and bounded to integer basis points between 1 and 9999. `minimum` reads `sourceField` and `capField` in one currency. Both operands must be immutable money available at creation, supplied as required fields, derived earlier or bound from a reference. Bind a referenced cap into a local field through `requiresRefs.bind`. Floor rounding still applies. A derived operand must appear before its consumer.
215
+
216
+ For a SAR 200 premium, 1000 bps produces SAR 20 and 500 bps produces SAR 10. Costs of SAR 12, 25 and 40 with a stored SAR 25 cap produce SAR 12, 25 and 25.
217
+
218
+ `due.offset`, `deadline.offset` and an aggregate's `dueBefore.offset` accept a literal duration or `{field}`. A stored duration must enumerate positive fixed ISO durations. It cannot contain months or a runtime expression. The enclosing record supplies the field: a due-clock aggregate reads each candidate child's date and duration. The engine compares date plus offset to one authoritative admission time, including when the scheduler has not updated the child's status.
219
+
220
+ `dateOrder` compares two immutable date fields on one instrument with `<` or `<=`. Distinct `requiresRefs` gates may constrain the same reference. Engines apply all gates as a conjunction, including both interval bounds. Repeating an identical gate remains invalid.
221
+
222
+ ## Aggregates and exact schedules
223
+
224
+ `requiresAggregate.anchorField` selects the owner's parent reference. Candidate rows have their own `refField` pointing to that same parent. This lets a claim inspect premium slices of its cover without pretending those slices reference the claim. The engine locks the shared parent and the relevant row set. A `dueBefore` predicate applies before the aggregate check. A zero `count_exactly` over unpaid due slices refuses cover immediately at the clock boundary.
225
+
226
+ `count_at_least.targetField` is an integer count. `ordered` checks strictly ascending dates in contiguous integer position order starting at 1. `schedule` additionally requires exact membership in the parent's immutable signed date list. No missing, extra or duplicate position is admitted. Every signed date must be strictly later than its predecessor. The engine reads the complete child set and checks statuses without discarding extra rows.
227
+
228
+ For every declared schedule amount, each child receives integer division of the parent total by the signed date count. The first position receives the remainder. SAR 48,000 principal and SAR 2,400 profit over six dates therefore produce six SAR 8,000 principal and SAR 400 profit slices. Adding one minor unit to principal adds it to the first slice only. The date list is bounded to 366 entries.
229
+
230
+ ## Held partitions and contributions
231
+
232
+ A `partitions` declaration is an exact equality, not a spending allowance. Admission verifies numeric equality and immutability. The finance proof can replace one complete funded partition with another partition of the same total on the same held account. Partial funding and already spent pieces do not establish the total. The proof permits one substitution per funded batch, with no active holds or unrelated balance. Draining the account resets that limit.
233
+
234
+ `contributions` declares a required immutable list, its positive amount and origin-account keys, and a total money field. The list contains at most 256 entries. `contributionStage` funds every entry into one held account atomically or refunds every entry from that account to its original origin. Origins cannot equal the held account. The sum must equal the declared total. The engine derives operation identity from the instrument, stage and entry index and records the same entries for recovery.
235
+
236
+ The SAR 60,000 witness receives SAR 12,000 and SAR 48,000. Its outgoing partition is SAR 59,310 seller net, SAR 600 fee and SAR 90 tax. A refund returns SAR 12,000 and SAR 48,000 to the respective origins. Evidence attachment remains a separate action; do not update financial inputs during a cash action.
237
+
238
+ ## Shared allocation and refunds
239
+
240
+ An obligation's `allocation` declares its slice instrument, reference and eligible statuses, due date, position, earning-rule field and bucket sources. Principal and profit come from the slice. Cost and fine may come from separately assessed child records of that slice. Bucket array order is payment priority. Engines always walk slices by due date ascending, then declared position. There is no company-supplied sorting option. Multiple assessments of the same bucket use ascending stable assessment identity.
241
+
242
+ A children bucket can select a concrete `instrumentId` or declare `template` and `parameters`. A template selector matches every instrument whose `templateBinding.id` equals that template and whose bound parameters include the requested scalar values. HSX retains string, integer and boolean arguments in `templateBinding.parameters` when it instantiates a template. Every matching alias must supply the declared slice reference, statuses, amount and destination fields. An uninstantiated selector contributes no assessments to a catalogue. The composed document validates the matching product instances.
243
+
244
+ For `payoff` and `write_off`, omitting `allocate.refField` selects the executing instance. That instance must declare `allocation`. This does not introduce a caller-supplied self reference. Refund may also omit the reference and name its own earlier allocating action. Payment requires an explicit reference.
245
+
246
+ An action's `allocate` references this declaration. Payment and payoff resolve amount, source account and payment identity from required immutable fields. Self payoff and assessment payment may instead use required action input keys. An operand must resolve to exactly one source. Self payoff may omit amountField so the planner computes the amount; a supplied amount must equal that figure. Assessment payment may omit amountField so the planner collects the assessment's whole remaining balance; a supplied amount above that balance refuses. The engine claims the identity under the obligation, reads remaining balances from prior allocation records and posts only positive allocations. It commits postings, consumption records, receipt and lifecycle state together. Recovery replays the recorded result. Direct collection and agency recovery must use the same declaration and assessment identities. A new channel or alias must not restore debt already consumed.
247
+
248
+ A SAR 4,000 payment against an SAR 8,400 slice consumes principal only. A later SAR 4,400 consumes the remaining SAR 4,000 principal and SAR 400 profit. Cost and fine follow when the authored priority puts them after principal and profit. An overpayment refuses rather than disappearing into an unspecified balance.
249
+
250
+ `allocate.mode: refund` names the original allocating action on self or through a typed instrument reference. An assessment record may omit `action` and reference its allocation owner instead: the engine reverses every consumption recorded against that assessment across the receipts that consumed it, its own collection, a repayment, a recovery or a payoff, each amount back to that receipt's payer, once. An optional immutable assessment identity selects one assessment's recorded allocation. The engine reverses those recorded postings to the original payer once. It does not accept new destinations or amounts and does not reopen consumed assessment debt. The refund identity is the original receipt plus assessment identity. A second refund refuses across aliases and lifecycle states.
251
+
252
+ ## Earning and noncash cancellation
253
+
254
+ The immutable earning-rule field chooses one of two language-defined rules:
255
+
256
+ - `per_slice_on_due`: profit earns when its slice becomes due or is paid, whichever comes first.
257
+ - `on_disbursement`: all profit earns when the obligation advances.
258
+
259
+ The engine proves the advance and supplies trusted paid/consumed facts to the planner. `payoff` collects outstanding principal, earned profit and assessed charges. It cancels unearned future profit without a cash refund. After one SAR 8,400 slice is paid, the six-slice example has SAR 40,000 principal and SAR 2,000 future profit remaining. Under `per_slice_on_due`, payoff collects SAR 40,000 and cancels SAR 2,000. Under `on_disbursement`, payoff collects SAR 42,000.
260
+
261
+ `write_off` posts no money. Cancellation rows distinguish principal loss and earned charges from unearned-profit cancellation. No daily accrual rule exists: UDL does not yet define an authoritative day-count convention.
262
+
263
+ `remainder.subtractPaths` is a bounded list of immutable money operands subtracted alongside existing collected sums from `totalPath`. Negative results refuse; zero follows `onZero`. It is not unrestricted arithmetic over money.
264
+
265
+ ## Referenced state and admission authority
266
+
267
+ `transitionsRefs` invokes named noncash lifecycle actions on referenced instruments in the same transaction. The engine applies every target gate, authority check and lifecycle precondition. Cycles, repeated target actions and cash-bearing or input-dependent target actions refuse. A child can lapse its cover; a refund can cancel a referenced funding record and prevent future actions through that record's lifecycle gates. This does not replace durable approval consumption tied to a downstream operation.
268
+
269
+ `requiresRefs.unique` accepts `{namespace, byFields}`. The uniqueness key contains the Product, namespace, referenced instance identity and immutable key values. It excludes instrument aliases and current status. A waived or refunded fine still occupies its key; cost uses a different kind value. The engine enforces this key under a shared constraint or lock.
270
+
271
+ Decision authentication, attestation verification and request hashing are admission concerns. The decision record must carry the requested instrument kind, instance, action, canonical input digest, deciding role and actor, expiry, verdict and evidence references. The engine computes the digest from the admitted request, authenticates the authority, checks expiry and revocation under locks, and binds consumption to the durable operation identity. Submitted actor and operation fields are not proof.
272
+
273
+ ## Effects and classification budget
274
+
275
+ Canonical JSON preserves every clause and array order. Allocation and referenced transitions add explicit effects. Hosts must price `decides.allocation`, `moves.allocation` and `decides.referenced_transition` before compiling products that use them. Write-off has no movement effect. Contribution transfers use the existing internal-transfer effect signature.
276
+
277
+ Reference classification reads the sealed ID pattern text; it never executes an authored regular expression or compiles a JSON Schema validator per candidate prefix. HSX supplies the published prefix when a typed reference omits its pattern. The 131,072 distinct-pair budget is a safety limit, not a catalogue-size target. The memo counts a schema/prefix pair once and fails closed after the limit.
278
+
279
+ ## Attested requests
280
+
281
+ An action may declare one `requiresRefs` gate with `attests`. The gate must
282
+ require a status and match `instrumentInstanceId` to a field on the referenced
283
+ record. It cannot be optional. The block declares `action`, `digest`, `role`,
284
+ `expiresAt` and `consume`. HSX accepts `expires_at` and emits `expiresAt`.
285
+ Action and role paths name text fields, digest names a lowercase SHA-256 field
286
+ with pattern `^[a-f0-9]{64}$`, and expiry names a date field.
287
+
288
+ Admission compares the stored action to the admitted lifecycle action name.
289
+ It computes the stored digest's expected value as
290
+ `bindResolvedRequestHash(operationExecutionIdentity({ name, environment, body }))`
291
+ for the downstream operation as admitted. The role must belong to the
292
+ authenticated deciding principal. A caller-supplied actor or role is not proof.
293
+ Admission refuses at or after expiry.
294
+
295
+ Stored terms are absent from the request body. An attested action therefore
296
+ puts its material terms in required input and declares `requiresInput`, a map
297
+ from input key to stored `fields.*` path. Admission checks equality before
298
+ updates or money movement. Changing a premium from 240000 to 180000 minor
299
+ units changes the request digest and fails the stored-term equality law.
300
+
301
+ The `consume` action must be reachable from every admitted status and declare
302
+ `engineOwned: true`. It cannot be public, accept input or update caller fields.
303
+ `captureEngine: { consumedByOperationId: "operationId" }` declares a ref written
304
+ from the admitting operation identity. These keys cannot be fields, input,
305
+ caller captures or move captures. The marker controls invocation authority;
306
+ the capture map names engine facts and their destination keys.
307
+
308
+ Consumption reuses referenced-transition effects and cycle checks. An ordinary
309
+ `transitionsRefs` entry cannot invoke an engine-owned action. The engine must
310
+ lock the decision and execute its transition inside the admitting transaction,
311
+ so a refusal rolls back consumption and one approval cannot authorize two
312
+ operations. This ABI declares those obligations; validation is not runtime
313
+ proof of their enforcement.
314
+
315
+ ## Subject uniqueness
316
+
317
+ An action's `unique: { namespace, byFields }` claims one key at creation without a reference gate. Keys contain one to eight distinct required immutable string, account or integer fields. Every use of a namespace must carry the same ordered field names and types. Reference-keyed and subject-keyed claims cannot share a namespace. The engine must claim the key tenant-wide, across instrument aliases, and never release it on closure. Raising a limit updates the existing record. The effect is `decides.subject_unique`, priced at zero without a price-version change.
318
+
319
+ ## Allocation exposure
320
+
321
+ `requiresExposure.measure: { allocation: "principal" }` selects a bucket on the child instrument. Its gross operand is the parent money field named by an exact `requiresAggregate.check.kind: "schedule"` mapping to that bucket's slice amount field. The relation must use the allocation's slice instrument and reference. A bucket sourced from other children has no such mapping and cannot be measured by this clause.
322
+
323
+ For qualifying children, the engine sums `amountField` and subtracts consumed amounts of the selected bucket over those same children. Payment, payoff and write-off share that consumption record. Refunds must be reflected by that record. The admitted instance contributes its full `amountField`. Omitting `measure` preserves the existing gross sum.
324
+
325
+ ## Recorded decider
326
+
327
+ `attests.instrument` is a required text-field path on the referenced decision. Admission compares that stored value with the admitted instrument's id. Required `attests.party` names a declared party on the referenced decision. Admission compares the recorded deciding account with that party's bound account.
328
+
329
+ A decision action declares `port.capture` as the name of an optional account field on its own instrument. The engine writes the admitted actor account there after checking `allowedParties`. Callers cannot populate it through `captureInput`, action input, step captures or updates. It cannot be required at creation. This records the tenant backend's assertion of the actor account; it does not independently authenticate that account.
330
+
331
+ ## Slice consumption and assessment collection
332
+
333
+ `requiresAllocation: { refField, slice: "self", buckets, check }` reads the referenced obligation's shared consumption for the executing slice. `settled` requires every listed bucket to equal its gross amount. `outstanding` requires at least one bucket below gross. The reference is required and immutable, and the owner must allocate over this slice instrument or its template alias.
334
+
335
+ Payment on an assessment may declare `assessment: "self"`. Admission matches the executing record to exactly one children bucket, then restricts payment to that record's remaining balance. An amount above that balance refuses. Refund without `action` reverses the assessment's consumption across every receipt that consumed it, once, and never reopens its consumed debt.
336
+
337
+ An anchor exposure cap may be updatable only when its anchor update clause names the cap field and the exposure declares `measure`. Other exposure caps remain immutable.
338
+
202
339
  <!-- source: reference/canonical.md -->
203
340
 
204
341
  # Canonical bytes
@@ -224,6 +361,256 @@ An admitted UDL document has one canonical byte sequence.
224
361
 
225
362
  This page lists every entry in `udlClauseVocabulary`. The examples are copied from admitted documents under `conformance/valid`.
226
363
 
364
+ ## funding
365
+
366
+ - Scope: action
367
+ - UDL target: `funding`
368
+ - Cardinality: one
369
+ - Linear outputs: none
370
+ - Effects: moves.transfer.internal per clause
371
+ - Law: Requirements-as-data law and closure law
372
+ - Conformance source: `vocabulary.udl`
373
+
374
+ ```json
375
+ {
376
+ "capture": "snapshot",
377
+ "collectAction": "collect",
378
+ "destinationAccountField": "fundingAccount",
379
+ "obligationField": "obligationId",
380
+ "principalField": "principal",
381
+ "sourceAccountPath": "refs.escrow",
382
+ "terms": {
383
+ "currency": "currency",
384
+ "principal": "principal"
385
+ },
386
+ "ticketAccountField": "wallet",
387
+ "ticketAmountField": "amount",
388
+ "ticketInstrumentId": "ticket",
389
+ "ticketInvestorField": "investor",
390
+ "ticketRefField": "roundId",
391
+ "ticketStatus": "open"
392
+ }
393
+ ```
394
+
395
+ ## receipt distribution
396
+
397
+ - Scope: action
398
+ - UDL target: `receiptDistribution`
399
+ - Cardinality: one
400
+ - Linear outputs: none
401
+ - Effects: decides.allocation per clause; moves.allocation per clause
402
+ - Law: Requirements-as-data law and closure law
403
+ - Conformance source: `vocabulary.udl`
404
+
405
+ ```json
406
+ {
407
+ "capture": "receipt",
408
+ "feeAccountField": "fee",
409
+ "feeBps": 100,
410
+ "mode": "cash",
411
+ "receiptField": "receiptId",
412
+ "receiptPath": "refs.allocationReceipt",
413
+ "residualAccountField": "residual",
414
+ "roundField": "roundId",
415
+ "snapshotRef": "snapshot",
416
+ "taxAccountField": "tax",
417
+ "vatBps": 1500
418
+ }
419
+ ```
420
+
421
+ ## requires allocation
422
+
423
+ - Scope: action
424
+ - UDL target: `requiresAllocation`
425
+ - Cardinality: one
426
+ - Linear outputs: none
427
+ - Effects: decides.allocation per clause
428
+ - Law: Requirements-as-data law and closure law
429
+ - Conformance source: `vocabulary.udl`
430
+
431
+ ```json
432
+ {
433
+ "buckets": [
434
+ "principal",
435
+ "profit"
436
+ ],
437
+ "check": "settled",
438
+ "refField": "coverId",
439
+ "slice": "self"
440
+ }
441
+ ```
442
+
443
+ ## allocation
444
+
445
+ - Scope: instrument
446
+ - UDL target: `allocation`
447
+ - Cardinality: one
448
+ - Linear outputs: none
449
+ - Effects: none
450
+ - Law: Requirements-as-data law and closure law
451
+ - Conformance source: `vocabulary.udl`
452
+
453
+ ```json
454
+ {
455
+ "buckets": [
456
+ {
457
+ "key": "principal",
458
+ "source": {
459
+ "amountField": "principal",
460
+ "destinationField": "receiver",
461
+ "from": "slice"
462
+ }
463
+ },
464
+ {
465
+ "key": "profit",
466
+ "source": {
467
+ "amountField": "profit",
468
+ "destinationField": "receiver",
469
+ "from": "slice"
470
+ }
471
+ },
472
+ {
473
+ "key": "cost",
474
+ "source": {
475
+ "amountField": "amount",
476
+ "destinationField": "receiver",
477
+ "from": "children",
478
+ "parameters": {
479
+ "cost": true
480
+ },
481
+ "refField": "sliceId",
482
+ "statuses": [
483
+ "open"
484
+ ],
485
+ "template": "assessment"
486
+ }
487
+ }
488
+ ],
489
+ "dueField": "dueAt",
490
+ "earningRuleField": "earningRule",
491
+ "positionField": "position",
492
+ "sliceInstrumentId": "slice",
493
+ "sliceRefField": "coverId",
494
+ "sliceStatuses": [
495
+ "open"
496
+ ]
497
+ }
498
+ ```
499
+
500
+ ## contributions
501
+
502
+ - Scope: instrument
503
+ - UDL target: `contributions`
504
+ - Cardinality: one
505
+ - Linear outputs: none
506
+ - Effects: none
507
+ - Law: Requirements-as-data law and closure law
508
+ - Conformance source: `vocabulary.udl`
509
+
510
+ ```json
511
+ {
512
+ "accountKey": "origin",
513
+ "amountKey": "amount",
514
+ "field": "contributions",
515
+ "totalField": "price"
516
+ }
517
+ ```
518
+
519
+ ## allocate
520
+
521
+ - Scope: action
522
+ - UDL target: `allocate`
523
+ - Cardinality: one
524
+ - Linear outputs: none
525
+ - Effects: decides.allocation per clause; moves.allocation per clause
526
+ - Law: Requirements-as-data law and closure law
527
+ - Conformance source: `vocabulary.udl`
528
+
529
+ ```json
530
+ {
531
+ "capture": "payoffReceipt",
532
+ "mode": "payoff",
533
+ "paymentIdentityField": "paymentIdentity",
534
+ "sourceAccountField": "payerAccount"
535
+ }
536
+ ```
537
+
538
+ ## contribution stage
539
+
540
+ - Scope: action
541
+ - UDL target: `contributionStage`
542
+ - Cardinality: one
543
+ - Linear outputs: none
544
+ - Effects: moves.transfer.internal per clause
545
+ - Law: Requirements-as-data law and closure law
546
+ - Conformance source: `vocabulary.udl`
547
+
548
+ ```json
549
+ {
550
+ "accountPath": "refs.hold",
551
+ "stage": "fund"
552
+ }
553
+ ```
554
+
555
+ ## date order
556
+
557
+ - Scope: instrument
558
+ - UDL target: `dateOrder`
559
+ - Cardinality: many
560
+ - Linear outputs: none
561
+ - Effects: none
562
+ - Law: Requirements-as-data law and closure law
563
+ - Conformance source: `vocabulary.udl`
564
+
565
+ ```json
566
+ [
567
+ {
568
+ "afterField": "end",
569
+ "beforeField": "start",
570
+ "operator": "<"
571
+ }
572
+ ]
573
+ ```
574
+
575
+ ## unique
576
+
577
+ - Scope: action
578
+ - UDL target: `unique`
579
+ - Cardinality: one
580
+ - Linear outputs: none
581
+ - Effects: decides.subject_unique per clause
582
+ - Law: Requirements-as-data law and closure law
583
+ - Conformance source: `vocabulary.udl`
584
+
585
+ ```json
586
+ {
587
+ "byFields": [
588
+ "borrower",
589
+ "currency"
590
+ ],
591
+ "namespace": "borrower_limit"
592
+ }
593
+ ```
594
+
595
+ ## transitions refs
596
+
597
+ - Scope: action
598
+ - UDL target: `transitionsRefs`
599
+ - Cardinality: many
600
+ - Linear outputs: none
601
+ - Effects: decides.referenced_transition per element
602
+ - Law: Requirements-as-data law and closure law
603
+ - Conformance source: `vocabulary.udl`
604
+
605
+ ```json
606
+ [
607
+ {
608
+ "action": "close",
609
+ "field": "coverId"
610
+ }
611
+ ]
612
+ ```
613
+
227
614
  ## action library
228
615
 
229
616
  - Scope: instrument
@@ -237,7 +624,10 @@ This page lists every entry in `udlClauseVocabulary`. The examples are copied fr
237
624
  ```json
238
625
  {
239
626
  "transfers": {
240
- "actionOrder": ["execute_fund", "execute_release"],
627
+ "actionOrder": [
628
+ "execute_fund",
629
+ "execute_release"
630
+ ],
241
631
  "actions": {
242
632
  "execute_fund": {
243
633
  "approval": "inherit",
@@ -260,7 +650,9 @@ This page lists every entry in `udlClauseVocabulary`. The examples are copied fr
260
650
  "operation": "internal_transfer.create"
261
651
  }
262
652
  ],
263
- "order": ["leaf_fund"],
653
+ "order": [
654
+ "leaf_fund"
655
+ ],
264
656
  "parameters": {
265
657
  "inst": {
266
658
  "kind": "instance"
@@ -293,7 +685,9 @@ This page lists every entry in `udlClauseVocabulary`. The examples are copied fr
293
685
  "operation": "internal_transfer.create"
294
686
  }
295
687
  ],
296
- "order": ["leaf_rel"],
688
+ "order": [
689
+ "leaf_rel"
690
+ ],
297
691
  "parameters": {
298
692
  "inst": {
299
693
  "kind": "instance"
@@ -347,6 +741,52 @@ This page lists every entry in `udlClauseVocabulary`. The examples are copied fr
347
741
  ]
348
742
  ```
349
743
 
744
+ ## requires input
745
+
746
+ - Scope: action
747
+ - UDL target: `requiresInput`
748
+ - Cardinality: one
749
+ - Linear outputs: none
750
+ - Effects: none
751
+ - Law: Requirements-as-data law and closure law
752
+ - Conformance source: `attested.udl`
753
+
754
+ ```json
755
+ {
756
+ "amount": "fields.amount"
757
+ }
758
+ ```
759
+
760
+ ## engine owned
761
+
762
+ - Scope: action
763
+ - UDL target: `engineOwned`
764
+ - Cardinality: one
765
+ - Linear outputs: none
766
+ - Effects: none
767
+ - Law: Requirements-as-data law and closure law
768
+ - Conformance source: `attested.udl`
769
+
770
+ ```json
771
+ true
772
+ ```
773
+
774
+ ## capture engine
775
+
776
+ - Scope: action
777
+ - UDL target: `captureEngine`
778
+ - Cardinality: one
779
+ - Linear outputs: none
780
+ - Effects: none
781
+ - Law: Requirements-as-data law and closure law
782
+ - Conformance source: `attested.udl`
783
+
784
+ ```json
785
+ {
786
+ "consumedByOperationId": "operationId"
787
+ }
788
+ ```
789
+
350
790
  ## capture input
351
791
 
352
792
  - Scope: action
@@ -401,7 +841,10 @@ A commit has no separate effect row. The committing action consumes the quote th
401
841
  "expires": {
402
842
  "offset": "PT15M"
403
843
  },
404
- "fixes": ["amount", "buyerAccountId"],
844
+ "fixes": [
845
+ "amount",
846
+ "buyerAccountId"
847
+ ],
405
848
  "netDestinationField": "buyerAccountId",
406
849
  "netRef": "unwindRefund"
407
850
  }
@@ -478,7 +921,10 @@ A commit has no separate effect row. The committing action consumes the quote th
478
921
  "path": "fields.distributableAmount"
479
922
  },
480
923
  "refField": "repaymentDistributionId",
481
- "statuses": ["recorded", "paid"],
924
+ "statuses": [
925
+ "recorded",
926
+ "paid"
927
+ ],
482
928
  "weightField": "committedWeight"
483
929
  }
484
930
  ```
@@ -571,19 +1017,21 @@ true
571
1017
  - Linear outputs: none
572
1018
  - Effects: none
573
1019
  - Law: Requirements-as-data law and closure law
574
- - Conformance source: `cards.udl`
1020
+ - Conformance source: `attested.udl`
575
1021
 
576
1022
  ```json
577
1023
  {
578
1024
  "additionalProperties": false,
579
1025
  "properties": {
580
- "reason": {
581
- "maxLength": 180,
582
- "minLength": 1,
583
- "type": "string"
1026
+ "amount": {
1027
+ "pattern": "^[1-9][0-9]{0,17}$",
1028
+ "type": "string",
1029
+ "x-hyperscale-currency": "SAR"
584
1030
  }
585
1031
  },
586
- "required": ["reason"],
1032
+ "required": [
1033
+ "amount"
1034
+ ],
587
1035
  "type": "object"
588
1036
  }
589
1037
  ```
@@ -636,7 +1084,10 @@ true
636
1084
 
637
1085
  ```json
638
1086
  {
639
- "fund_order": ["p1", "p2"],
1087
+ "fund_order": [
1088
+ "p1",
1089
+ "p2"
1090
+ ],
640
1091
  "id": "split_plan",
641
1092
  "pieces": [
642
1093
  {
@@ -652,10 +1103,18 @@ true
652
1103
  "release_to": "p2Release"
653
1104
  }
654
1105
  ],
655
- "refund_order": ["p1"],
656
- "release_order": ["p1", "p2"],
1106
+ "refund_order": [
1107
+ "p1"
1108
+ ],
1109
+ "release_order": [
1110
+ "p1",
1111
+ "p2"
1112
+ ],
657
1113
  "total": "totalAmount",
658
- "unfund_order": ["p2", "p1"]
1114
+ "unfund_order": [
1115
+ "p2",
1116
+ "p1"
1117
+ ]
659
1118
  }
660
1119
  ```
661
1120
 
@@ -684,11 +1143,14 @@ true
684
1143
  - Linear outputs: none
685
1144
  - Effects: decides.tenant_port per clause
686
1145
  - Law: Requirements-as-data law and closure law
687
- - Conformance source: `compiled-crowdfunding.udl`
1146
+ - Conformance source: `attested.udl`
688
1147
 
689
1148
  ```json
690
1149
  {
691
- "allowedParties": ["beneficiary"]
1150
+ "allowedParties": [
1151
+ "decider"
1152
+ ],
1153
+ "capture": "decidedBy"
692
1154
  }
693
1155
  ```
694
1156
 
@@ -797,7 +1259,9 @@ true
797
1259
  "instrumentId": "borrower_loan_installment_1_payment",
798
1260
  "over": "children",
799
1261
  "refField": "borrowerLoanId",
800
- "statuses": ["paid"]
1262
+ "statuses": [
1263
+ "paid"
1264
+ ]
801
1265
  }
802
1266
  ]
803
1267
  ```
@@ -818,7 +1282,9 @@ true
818
1282
  "checkKind": "identity_verification",
819
1283
  "family": "national_identity",
820
1284
  "maxAge": "P30D",
821
- "statuses": ["completed"],
1285
+ "statuses": [
1286
+ "completed"
1287
+ ],
822
1288
  "subjectField": "subjectId"
823
1289
  }
824
1290
  ]
@@ -858,7 +1324,9 @@ true
858
1324
  "capField": "installment1Amount",
859
1325
  "capOnAnchor": true,
860
1326
  "childInstrumentId": "borrower_loan_installment_1_payment",
861
- "statuses": ["paid"]
1327
+ "statuses": [
1328
+ "paid"
1329
+ ]
862
1330
  }
863
1331
  ]
864
1332
  ```
@@ -871,13 +1339,27 @@ true
871
1339
  - Linear outputs: none
872
1340
  - Effects: reads.requires_refs per clause
873
1341
  - Law: Requirements-as-data law and closure law
874
- - Conformance source: `cards.udl`
1342
+ - Conformance source: `attested.udl`
875
1343
 
876
1344
  ```json
877
1345
  [
878
1346
  {
879
- "field": "cardholderId",
880
- "statuses": ["active"]
1347
+ "attests": {
1348
+ "action": "fields.actionName",
1349
+ "consume": "consume",
1350
+ "digest": "fields.digest",
1351
+ "expiresAt": "fields.expiry",
1352
+ "instrument": "fields.requestInstrument",
1353
+ "party": "decider",
1354
+ "role": "fields.role"
1355
+ },
1356
+ "field": "approvalId",
1357
+ "match": {
1358
+ "instrumentInstanceId": "fields.requestId"
1359
+ },
1360
+ "statuses": [
1361
+ "approved"
1362
+ ]
881
1363
  }
882
1364
  ]
883
1365
  ```
@@ -935,7 +1417,9 @@ true
935
1417
  "instrumentId": "seller_payout_capture_entry",
936
1418
  "refField": "sellerPayoutId",
937
1419
  "sign": "add",
938
- "statuses": ["accrued"],
1420
+ "statuses": [
1421
+ "accrued"
1422
+ ],
939
1423
  "subtotalRef": "grossCaptureAmount"
940
1424
  },
941
1425
  {
@@ -943,7 +1427,9 @@ true
943
1427
  "instrumentId": "seller_payout_credit_adjustment",
944
1428
  "refField": "sellerPayoutId",
945
1429
  "sign": "add",
946
- "statuses": ["applied"],
1430
+ "statuses": [
1431
+ "applied"
1432
+ ],
947
1433
  "subtotalRef": "creditAdjustmentAmount"
948
1434
  },
949
1435
  {
@@ -951,7 +1437,9 @@ true
951
1437
  "instrumentId": "seller_payout_debit_adjustment",
952
1438
  "refField": "sellerPayoutId",
953
1439
  "sign": "subtract",
954
- "statuses": ["applied"],
1440
+ "statuses": [
1441
+ "applied"
1442
+ ],
955
1443
  "subtotalRef": "debitAdjustmentAmount"
956
1444
  }
957
1445
  ]
@@ -997,7 +1485,9 @@ true
997
1485
  - Conformance source: `commerce-escrow.udl`
998
1486
 
999
1487
  ```json
1000
- ["escrowOrderId"]
1488
+ [
1489
+ "escrowOrderId"
1490
+ ]
1001
1491
  ```
1002
1492
 
1003
1493
  ## notify
@@ -1051,14 +1541,18 @@ true
1051
1541
  "childField": "amount",
1052
1542
  "childInstrumentId": "borrower_loan_installment_1_payment",
1053
1543
  "childRefField": "borrowerLoanId",
1054
- "childStatuses": ["paid"],
1544
+ "childStatuses": [
1545
+ "paid"
1546
+ ],
1055
1547
  "parentField": "installment1Amount"
1056
1548
  },
1057
1549
  {
1058
1550
  "childField": "amount",
1059
1551
  "childInstrumentId": "borrower_loan_installment_2_payment",
1060
1552
  "childRefField": "borrowerLoanId",
1061
- "childStatuses": ["paid"],
1553
+ "childStatuses": [
1554
+ "paid"
1555
+ ],
1062
1556
  "parentField": "installment2Amount"
1063
1557
  }
1064
1558
  ]
@@ -1233,34 +1727,6 @@ true
1233
1727
  "note"
1234
1728
  ```
1235
1729
 
1236
- ## journeys
1237
-
1238
- - Scope: instrument
1239
- - UDL target: `journeys`
1240
- - Cardinality: many
1241
- - Linear outputs: none
1242
- - Effects: none
1243
- - Law: Requirements-as-data law and closure law
1244
- - Conformance source: `hand-edited.udl`
1245
-
1246
- ```json
1247
- [
1248
- {
1249
- "id": "file_note",
1250
- "label": "File a note",
1251
- "steps": [
1252
- {
1253
- "bind": {},
1254
- "example": "default",
1255
- "id": "note",
1256
- "operation": "note.create"
1257
- }
1258
- ],
1259
- "summary": "File a note."
1260
- }
1261
- ]
1262
- ```
1263
-
1264
1730
  ## nav
1265
1731
 
1266
1732
  - Scope: instrument
@@ -1272,7 +1738,10 @@ true
1272
1738
  - Conformance source: `cards.udl`
1273
1739
 
1274
1740
  ```json
1275
- ["Blueprints", "Cardholders"]
1741
+ [
1742
+ "Blueprints",
1743
+ "Cardholders"
1744
+ ]
1276
1745
  ```
1277
1746
 
1278
1747
  ## partitions
@@ -1288,7 +1757,10 @@ true
1288
1757
  ```json
1289
1758
  [
1290
1759
  {
1291
- "pieceFields": ["installment1Amount", "installment2Amount"],
1760
+ "pieceFields": [
1761
+ "installment1Amount",
1762
+ "installment2Amount"
1763
+ ],
1292
1764
  "totalField": "principalAmount"
1293
1765
  }
1294
1766
  ]
@@ -1306,7 +1778,10 @@ true
1306
1778
 
1307
1779
  ```json
1308
1780
  {
1309
- "kinds": ["vehicle", "merchandise"]
1781
+ "kinds": [
1782
+ "vehicle",
1783
+ "merchandise"
1784
+ ]
1310
1785
  }
1311
1786
  ```
1312
1787
 
@@ -1338,6 +1813,25 @@ true
1338
1813
  "public"
1339
1814
  ```
1340
1815
 
1816
+ ## template binding
1817
+
1818
+ - Scope: instrument
1819
+ - UDL target: `templateBinding`
1820
+ - Cardinality: one
1821
+ - Linear outputs: none
1822
+ - Effects: none
1823
+ - Law: Requirements-as-data law and closure law
1824
+ - Conformance source: `vocabulary.udl`
1825
+
1826
+ ```json
1827
+ {
1828
+ "id": "assessment",
1829
+ "parameters": {
1830
+ "cost": true
1831
+ }
1832
+ }
1833
+ ```
1834
+
1341
1835
  ## template id
1342
1836
 
1343
1837
  - Scope: instrument
@@ -1389,8 +1883,39 @@ true
1389
1883
  "name": "update_cardholder_contact"
1390
1884
  }
1391
1885
  ],
1392
- "fields": ["displayName", "email", "phoneNumber"],
1393
- "states": ["pending", "active", "suspended"]
1886
+ "fields": [
1887
+ "displayName",
1888
+ "email",
1889
+ "phoneNumber"
1890
+ ],
1891
+ "states": [
1892
+ "pending",
1893
+ "active",
1894
+ "suspended"
1895
+ ]
1896
+ }
1897
+ ```
1898
+
1899
+ ## request authority
1900
+
1901
+ - Scope: action
1902
+ - UDL target: `requestAuthority`
1903
+ - Cardinality: one
1904
+ - Linear outputs: none
1905
+ - Effects: none
1906
+ - Law: Requirements-as-data law and closure law
1907
+ - Conformance source: `attested.udl`
1908
+
1909
+ ```json
1910
+ {
1911
+ "actionField": "actionName",
1912
+ "digestField": "digest",
1913
+ "expiresField": "expiry",
1914
+ "inputField": "requestInput",
1915
+ "instanceField": "requestId",
1916
+ "instrumentField": "requestInstrument",
1917
+ "party": "decider",
1918
+ "roleField": "role"
1394
1919
  }
1395
1920
  ```
1396
1921
 
@@ -1400,48 +1925,45 @@ true
1400
1925
 
1401
1926
  Codes are stable. Titles and messages may become clearer without changing the code. Apply the listed fix, then validate the whole document again.
1402
1927
 
1403
- | Code | Family | Category | Title | Fix |
1404
- | ---------------------------- | --------- | ----------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
1405
- | `journey_duplicate_step_id` | journey | invalid_semantics | Journey step id is duplicated | Give every named step in the journey a unique id. |
1406
- | `journey_invalid_transition` | journey | invalid_semantics | Journey lifecycle transition is invalid | Order the steps so each action starts from the state produced by earlier steps. |
1407
- | `journey_unbound_reference` | journey | invalid_semantics | Journey reference is unbound or has the wrong kind | Bind every reference input to an earlier step that creates the required kind. |
1408
- | `journey_unknown_example` | journey | invalid_semantics | Journey example does not exist | Name an authored example on the journey operation. |
1409
- | `journey_unknown_operation` | journey | invalid_semantics | Journey operation is not in the composition | Name an operation in the composition closure. |
1410
- | `UDL1001` | admission | invalid_utf8 | Invalid UTF-8 | Encode the document as valid UTF-8. |
1411
- | `UDL1002` | admission | invalid_json | Invalid JSON | Repair the JSON syntax before validation. |
1412
- | `UDL1003` | admission | invalid_shape | Invalid document shape | Match the published UDL JSON Schema. |
1413
- | `UDL1004` | admission | resource_limit | Resource limit exceeded | Reduce the source size, nesting, values, strings, references, or financial paths named by the message. |
1414
- | `UDL2001` | document | invalid_semantics | Duplicate declaration | Give each declaration a unique name. |
1415
- | `UDL2002` | document | invalid_semantics | Document law violation | Repair the declaration, subject contract, or derived effects named by the message. |
1416
- | `UDL2005` | document | invalid_semantics | Derived effects mismatch | Regenerate the action effects from its clauses. |
1417
- | `UDL2010` | document | invalid_semantics | Action graph violation | Order all actions and calls without cycles or collisions, within depth and count limits. |
1418
- | `UDL2011` | document | invalid_semantics | Action binding violation | Bind parameters to declared instance, piece, or caller variables without forward references or duplicate captures. |
1419
- | `UDL2012` | document | invalid_semantics | Action authority violation | Match principal, approval, and recovery policies and keep field paths within tenant boundary. |
1420
- | `UDL2013` | document | invalid_semantics | Action effect or evidence violation | Provide valid effect kind, matching effect signature, and non-empty evidence for every leaf. |
1421
- | `UDL3001` | lifecycle | invalid_semantics | Lifecycle is not closed | Declare every state and action transition, and make every state reachable. |
1422
- | `UDL4001` | finance | invalid_semantics | Money graph violation | Balance every funded amount and close every hold on each lifecycle path. |
1423
- | `UDL4002` | finance | invalid_semantics | Piece partition violation | Match piece plan total, amounts, and payees to required immutable fields and declared partition. |
1424
- | `UDL5001` | gates | invalid_semantics | Reference gate violation | Point the gate at a declared instrument, action, state, field, and reference. |
1425
- | `UDL5002` | gates | invalid_semantics | Check requirement violation | Use a declared check with compatible evidence and recurrence. |
1426
- | `UDL5003` | gates | invalid_semantics | Exposure gate violation | Use declared account and money fields for the exposure gate. |
1427
- | `UDL5004` | gates | invalid_semantics | Aggregate law violation | Point the aggregate at compatible parent and child fields. |
1428
- | `UDL5005` | gates | invalid_semantics | Settlement or payout violation | Use a declared settlement account and a compatible payout statement line. |
1429
- | `UDL5006` | gates | invalid_semantics | Quote and commit violation | Declare one complete quote freeze set and one matching commit action. |
1430
- | `UDL5007` | gates | invalid_semantics | Reconcile exception child violation | Name a declared child whose reference points back to this instrument. |
1431
- | `UDL5008` | gates | invalid_semantics | Action clause violation | Repair the clause fields and keep incompatible clauses separate. |
1432
- | `UDL5009` | gates | invalid_semantics | Reconcile exception amount field is missing or optional | Name the exception child's required money field in amountField. |
1433
- | `UDL5010` | gates | invalid_semantics | Reconcile exception amount field has the wrong type | Point amountField at a money field declared by the exception child. |
1434
- | `UDL5011` | gates | invalid_semantics | Reconcile exception reason field is missing or optional | Name the exception child's required text field in reasonField. |
1435
- | `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. |
1436
- | `UDL5013` | gates | invalid_semantics | Piece stage violation | Reference a declared piece plan and stage in the containing instrument. |
1437
- | `UDL6001` | schema | invalid_semantics | Unsupported JSON Schema | Use only the sealed UDL JSON Schema subset. |
1438
- | `UDL7001` | evolution | invalid_evolution | Stored contract changed | Keep stored identities and contracts unchanged, and add only allowed optional declarations. |
1439
- | `UDL7002` | evolution | invalid_evolution | Version was not increased | Increase the product version for every semantic change. |
1928
+ | Code | Family | Category | Title | Fix |
1929
+ | --------- | --------- | ----------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
1930
+ | `UDL1001` | admission | invalid_utf8 | Invalid UTF-8 | Encode the document as valid UTF-8. |
1931
+ | `UDL1002` | admission | invalid_json | Invalid JSON | Repair the JSON syntax before validation. |
1932
+ | `UDL1003` | admission | invalid_shape | Invalid document shape | Match the published UDL JSON Schema. |
1933
+ | `UDL1004` | admission | resource_limit | Resource limit exceeded | Reduce the source size, nesting, values, strings, references, or financial paths named by the message. |
1934
+ | `UDL2001` | document | invalid_semantics | Duplicate declaration | Give each declaration a unique name. |
1935
+ | `UDL2002` | document | invalid_semantics | Document law violation | Repair the declaration, subject contract, or derived effects named by the message. |
1936
+ | `UDL2005` | document | invalid_semantics | Derived effects mismatch | Regenerate the action effects from its clauses. |
1937
+ | `UDL2010` | document | invalid_semantics | Action graph violation | Order all actions and calls without cycles or collisions, within depth and count limits. |
1938
+ | `UDL2011` | document | invalid_semantics | Action binding violation | Bind parameters to declared instance, piece, or caller variables without forward references or duplicate captures. |
1939
+ | `UDL2012` | document | invalid_semantics | Action authority violation | Match principal, approval, and recovery policies and keep field paths within tenant boundary. |
1940
+ | `UDL2013` | document | invalid_semantics | Action effect or evidence violation | Provide valid effect kind, matching effect signature, and non-empty evidence for every leaf. |
1941
+ | `UDL3001` | lifecycle | invalid_semantics | Lifecycle is not closed | Declare every state and action transition, and make every state reachable. |
1942
+ | `UDL4001` | finance | invalid_semantics | Money graph violation | Balance every funded amount and close every hold on each lifecycle path. |
1943
+ | `UDL4002` | finance | invalid_semantics | Piece partition violation | Match piece plan total, amounts, and payees to required immutable fields and declared partition. |
1944
+ | `UDL5001` | gates | invalid_semantics | Reference gate violation | Point the gate at a declared instrument, action, state, field, and reference. |
1945
+ | `UDL5002` | gates | invalid_semantics | Check requirement violation | Use a declared check with compatible evidence and recurrence. |
1946
+ | `UDL5003` | gates | invalid_semantics | Exposure gate violation | Use declared account and money fields for the exposure gate. |
1947
+ | `UDL5004` | gates | invalid_semantics | Aggregate law violation | Point the aggregate at compatible parent and child fields. |
1948
+ | `UDL5005` | gates | invalid_semantics | Settlement or payout violation | Use a declared settlement account and a compatible payout statement line. |
1949
+ | `UDL5006` | gates | invalid_semantics | Quote and commit violation | Declare one complete quote freeze set and one matching commit action. |
1950
+ | `UDL5007` | gates | invalid_semantics | Reconcile exception child violation | Name a declared child whose reference points back to this instrument. |
1951
+ | `UDL5008` | gates | invalid_semantics | Action clause violation | Repair the clause fields and keep incompatible clauses separate. |
1952
+ | `UDL5009` | gates | invalid_semantics | Reconcile exception amount field is missing or optional | Name the exception child's required money field in amountField. |
1953
+ | `UDL5010` | gates | invalid_semantics | Reconcile exception amount field has the wrong type | Point amountField at a money field declared by the exception child. |
1954
+ | `UDL5011` | gates | invalid_semantics | Reconcile exception reason field is missing or optional | Name the exception child's required text field in reasonField. |
1955
+ | `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. |
1956
+ | `UDL5013` | gates | invalid_semantics | Piece stage violation | Reference a declared piece plan and stage in the containing instrument. |
1957
+ | `UDL6001` | schema | invalid_semantics | Unsupported JSON Schema | Use only the sealed UDL JSON Schema subset. |
1958
+ | `UDL7001` | evolution | invalid_evolution | Stored contract changed | Keep stored identities and contracts unchanged, and add only allowed optional declarations. |
1959
+ | `UDL7002` | evolution | invalid_evolution | Version was not increased | Increase the product version for every semantic change. |
1440
1960
 
1441
1961
  <!-- source: reference/cli.md -->
1442
1962
 
1443
1963
  # Command reference
1444
1964
 
1965
+ In the full repository checkout, run `bun open/udl/src/cli.ts validate <file.udl.json>` from the repository root. The entrypoint is `open/udl/src/cli.ts`; in a standalone package checkout it is `src/cli.ts`. Use this local validator for local HSX output.
1966
+
1445
1967
  The installed `udl` binary exposes the following commands and exit codes.
1446
1968
 
1447
1969
  ```text