@hyperscale0/udl 2.3.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 (86) hide show
  1. package/CHANGELOG.md +26 -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.map +1 -1
  26. package/dist/effects.js +23 -1
  27. package/dist/effects.js.map +1 -1
  28. package/dist/evolution.d.ts +12 -0
  29. package/dist/evolution.d.ts.map +1 -1
  30. package/dist/evolution.js +42 -1
  31. package/dist/evolution.js.map +1 -1
  32. package/dist/finance.d.ts +18 -1
  33. package/dist/finance.d.ts.map +1 -1
  34. package/dist/finance.js +158 -27
  35. package/dist/finance.js.map +1 -1
  36. package/dist/index.d.ts +8 -2
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +5 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/instrument-references.d.ts +5 -0
  41. package/dist/instrument-references.d.ts.map +1 -0
  42. package/dist/instrument-references.js +69 -0
  43. package/dist/instrument-references.js.map +1 -0
  44. package/dist/limits.d.ts +3 -3
  45. package/dist/limits.d.ts.map +1 -1
  46. package/dist/limits.js +3 -7
  47. package/dist/limits.js.map +1 -1
  48. package/dist/reference.d.ts +3 -0
  49. package/dist/reference.d.ts.map +1 -0
  50. package/dist/reference.js +28 -0
  51. package/dist/reference.js.map +1 -0
  52. package/dist/schema.d.ts +1232 -83
  53. package/dist/schema.d.ts.map +1 -1
  54. package/dist/schema.js +398 -60
  55. package/dist/schema.js.map +1 -1
  56. package/dist/validation.d.ts +14 -0
  57. package/dist/validation.d.ts.map +1 -1
  58. package/dist/validation.js +75 -131
  59. package/dist/validation.js.map +1 -1
  60. package/dist/vocabulary.d.ts +23 -0
  61. package/dist/vocabulary.d.ts.map +1 -0
  62. package/dist/vocabulary.js +965 -0
  63. package/dist/vocabulary.js.map +1 -0
  64. package/docs/README.md +2 -1
  65. package/docs/funding-custody.md +165 -0
  66. package/docs/guide/09-schedules-and-allocation.md +130 -0
  67. package/docs/llms-full.txt +620 -101
  68. package/docs/llms.txt +1 -1
  69. package/docs/reference/clauses.md +451 -63
  70. package/docs/reference/cli.md +3 -1
  71. package/docs/reference/diagnostics.md +33 -38
  72. package/package.json +5 -6
  73. package/spec/udl.schema.json +1095 -118
  74. package/src/allocation.ts +259 -0
  75. package/src/diagnostics.ts +0 -32
  76. package/src/distribution.ts +61 -0
  77. package/src/effects.ts +27 -1
  78. package/src/evolution.ts +63 -3
  79. package/src/finance.ts +218 -24
  80. package/src/index.ts +30 -3
  81. package/src/instrument-references.ts +98 -0
  82. package/src/limits.ts +3 -7
  83. package/src/reference.ts +31 -0
  84. package/src/schema.ts +417 -66
  85. package/src/validation.ts +112 -182
  86. package/src/vocabulary.ts +1508 -0
package/dist/schema.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { referencePatternPrefix } from "./reference.js";
1
2
  import { z } from "zod";
2
3
  import { udlCheckEvidenceProfiles } from "./check-profiles.js";
3
4
  export const udlEffectKinds = [
@@ -17,6 +18,9 @@ const eventNamePattern = /^[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)+$/;
17
18
  const udlSnakeCaseSchema = z
18
19
  .string()
19
20
  .regex(snakeCasePattern, "must be a snake_case identifier");
21
+ const udlInstrumentIdSchema = udlSnakeCaseSchema.meta({
22
+ "x-udl-reference": "instrument",
23
+ });
20
24
  const udlActionNameSchema = z
21
25
  .string()
22
26
  .regex(snakeCasePattern, "must be a snake_case action identifier");
@@ -30,10 +34,6 @@ export const udlInstrumentActionIdSchema = z
30
34
  .string()
31
35
  .max(160)
32
36
  .regex(/^[a-z][a-z0-9_]*\.[a-z][a-z0-9_]*$/, "must name an instrument action as instrument_id.action_key");
33
- const udlJourneyOperationNameSchema = z
34
- .string()
35
- .max(160)
36
- .regex(/^[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)+$/, "must name a dotted operation");
37
37
  const nonEmptyTextSchema = z
38
38
  .string()
39
39
  .refine((value) => value.trim().length > 0, "must not be blank");
@@ -85,18 +85,6 @@ const udlExampleSchema = z.strictObject({
85
85
  name: nonEmptyTextSchema,
86
86
  output: z.json().optional(),
87
87
  });
88
- const udlJourneyStepSchema = z.strictObject({
89
- bind: z.record(fieldPathSchema, udlSnakeCaseSchema),
90
- example: udlSnakeCaseSchema,
91
- id: udlSnakeCaseSchema.optional(),
92
- operation: udlJourneyOperationNameSchema,
93
- });
94
- export const udlJourneySchema = z.strictObject({
95
- id: udlSnakeCaseSchema,
96
- label: nonEmptyTextSchema,
97
- steps: z.array(udlJourneyStepSchema).min(1),
98
- summary: nonEmptyTextSchema,
99
- });
100
88
  const udlLifecycleTransitionSchema = z.strictObject({
101
89
  from: z.array(udlSnakeCaseSchema).min(1),
102
90
  to: udlSnakeCaseSchema,
@@ -117,10 +105,26 @@ const udlDateComparisonSchema = z.strictObject({
117
105
  operator: udlDateComparisonOperatorSchema,
118
106
  referencedPath: fieldPathSchema,
119
107
  });
108
+ const udlStoredFieldSchema = z.strictObject({ field: udlFieldNameSchema });
109
+ const udlOffsetSchema = z.union([nonEmptyTextSchema, udlStoredFieldSchema]);
110
+ const udlAttestationSchema = z.strictObject({
111
+ forAction: udlActionNameSchema.optional(),
112
+ action: instanceValuePathSchema,
113
+ digest: instanceValuePathSchema,
114
+ role: instanceValuePathSchema,
115
+ expiresAt: instanceValuePathSchema,
116
+ instrument: instanceValuePathSchema,
117
+ party: z.string().regex(/^[a-z][A-Za-z0-9_]*$/),
118
+ consume: udlActionNameSchema,
119
+ });
120
120
  const udlGateSchema = z.strictObject({
121
+ attests: udlAttestationSchema.optional(),
121
122
  /** Local field key <- referenced instance path; create actions only. */
122
123
  bind: z.record(udlFieldNameSchema, fieldPathSchema).optional(),
123
124
  dateComparison: udlDateComparisonSchema.optional(),
125
+ dueBefore: z
126
+ .strictObject({ field: udlFieldNameSchema, offset: nonEmptyTextSchema })
127
+ .optional(),
124
128
  field: udlFieldNameSchema,
125
129
  /** Local instance path === referenced instance path at admission. */
126
130
  match: z.record(fieldPathSchema, fieldPathSchema).optional(),
@@ -128,7 +132,15 @@ const udlGateSchema = z.strictObject({
128
132
  optional: z.literal(true).optional(),
129
133
  statuses: z.array(udlSnakeCaseSchema).min(1),
130
134
  /** One dependent ever per referenced instance; create actions only. */
131
- unique: z.literal(true).optional(),
135
+ unique: z
136
+ .union([
137
+ z.literal(true),
138
+ z.strictObject({
139
+ namespace: udlSnakeCaseSchema,
140
+ byFields: z.array(udlFieldNameSchema).min(1).max(8),
141
+ }),
142
+ ])
143
+ .optional(),
132
144
  });
133
145
  const udlBindingSchema = z.discriminatedUnion("from", [
134
146
  z.strictObject({ from: z.literal("const"), value: z.string() }),
@@ -178,19 +190,19 @@ const udlDueRecurrenceSchema = z.strictObject({
178
190
  untilField: udlFieldNameSchema.optional(),
179
191
  });
180
192
  const udlDueParentStatusSchema = z.strictObject({
181
- instrumentId: udlSnakeCaseSchema,
193
+ instrumentId: udlInstrumentIdSchema,
182
194
  refField: udlFieldNameSchema,
183
195
  statuses: z.array(udlSnakeCaseSchema).min(1),
184
196
  });
185
197
  const udlDueSchema = z.strictObject({
186
198
  every: udlDueRecurrenceSchema.optional(),
187
199
  field: udlFieldNameSchema,
188
- offset: nonEmptyTextSchema.optional(),
200
+ offset: udlOffsetSchema.optional(),
189
201
  whenParentStatus: udlDueParentStatusSchema.optional(),
190
202
  });
191
203
  const udlDeadlineSchema = z.strictObject({
192
204
  field: udlFieldNameSchema,
193
- offset: nonEmptyTextSchema.optional(),
205
+ offset: udlOffsetSchema.optional(),
194
206
  });
195
207
  const udlSetsAtSchema = z.strictObject({
196
208
  field: udlFieldNameSchema,
@@ -294,6 +306,29 @@ const udlAggregateCheckSchema = z.discriminatedUnion("kind", [
294
306
  kind: z.literal("count_at_least"),
295
307
  targetField: udlFieldNameSchema,
296
308
  }),
309
+ z.strictObject({
310
+ kind: z.literal("count_exactly"),
311
+ value: z.number().int().min(0),
312
+ }),
313
+ z.strictObject({
314
+ kind: z.literal("ordered"),
315
+ field: udlFieldNameSchema,
316
+ positionField: udlFieldNameSchema,
317
+ }),
318
+ z.strictObject({
319
+ kind: z.literal("schedule"),
320
+ positionField: udlFieldNameSchema,
321
+ dateField: udlFieldNameSchema,
322
+ datesField: udlFieldNameSchema,
323
+ amounts: z
324
+ .array(z.strictObject({
325
+ amountField: udlFieldNameSchema,
326
+ totalField: udlFieldNameSchema,
327
+ }))
328
+ .min(1)
329
+ .max(8),
330
+ remainder: z.literal("first"),
331
+ }),
297
332
  z.strictObject({ kind: z.literal("all_in") }),
298
333
  z.strictObject({
299
334
  field: udlFieldNameSchema,
@@ -301,18 +336,28 @@ const udlAggregateCheckSchema = z.discriminatedUnion("kind", [
301
336
  }),
302
337
  ]);
303
338
  const udlAggregateConditionSchema = z.strictObject({
339
+ anchorField: udlFieldNameSchema.optional(),
340
+ dueBefore: z
341
+ .strictObject({
342
+ field: udlFieldNameSchema,
343
+ offset: udlOffsetSchema.optional(),
344
+ })
345
+ .optional(),
304
346
  check: udlAggregateCheckSchema,
305
- instrumentId: udlSnakeCaseSchema.optional(),
347
+ instrumentId: udlInstrumentIdSchema.optional(),
306
348
  over: z.enum(["children", "siblings"]),
307
349
  refField: udlFieldNameSchema,
308
350
  statuses: z.array(udlSnakeCaseSchema).min(1),
309
351
  });
310
352
  const udlExposureRequirementSchema = z.strictObject({
353
+ groupField: udlFieldNameSchema.optional(),
354
+ minimumField: udlFieldNameSchema.optional(),
355
+ measure: z.strictObject({ allocation: udlSnakeCaseSchema }).optional(),
311
356
  amountField: udlFieldNameSchema,
312
357
  anchorField: udlFieldNameSchema,
313
358
  capField: udlFieldNameSchema,
314
359
  capOnAnchor: z.literal(true).optional(),
315
- childInstrumentId: udlSnakeCaseSchema,
360
+ childInstrumentId: udlInstrumentIdSchema,
316
361
  statuses: z.array(udlSnakeCaseSchema).min(1),
317
362
  });
318
363
  // The tenant-backend decision port: the action's caller asserts the acting
@@ -321,6 +366,7 @@ const udlExposureRequirementSchema = z.strictObject({
321
366
  const udlPartyRoleSchema = z.string().regex(/^[a-z][A-Za-z0-9_]*$/);
322
367
  const udlPortSchema = z.strictObject({
323
368
  allowedParties: z.array(udlPartyRoleSchema).min(1),
369
+ capture: udlFieldNameSchema.optional(),
324
370
  });
325
371
  const udlPayoutSchema = z.strictObject({
326
372
  amount: instanceValuePathSchema,
@@ -357,7 +403,7 @@ const udlReconcileSchema = z.strictObject({
357
403
  */
358
404
  exception: z.strictObject({
359
405
  amountField: udlFieldNameSchema,
360
- childInstrumentId: udlSnakeCaseSchema,
406
+ childInstrumentId: udlInstrumentIdSchema,
361
407
  maxOpen: z.number().int().min(1).max(1_000),
362
408
  reasonField: udlFieldNameSchema,
363
409
  refField: udlFieldNameSchema,
@@ -384,7 +430,7 @@ const udlReconcileSchema = z.strictObject({
384
430
  });
385
431
  const udlSignedSumSourceSchema = z.strictObject({
386
432
  amountField: udlFieldNameSchema,
387
- instrumentId: udlSnakeCaseSchema,
433
+ instrumentId: udlInstrumentIdSchema,
388
434
  refField: udlFieldNameSchema,
389
435
  sign: z.enum(["add", "subtract"]),
390
436
  statuses: z.array(udlSnakeCaseSchema).min(1),
@@ -409,7 +455,7 @@ const udlDistributeSchema = z.strictObject({
409
455
  });
410
456
  const udlRemainderCollectedSchema = z.strictObject({
411
457
  amountField: udlFieldNameSchema,
412
- instrumentId: udlSnakeCaseSchema,
458
+ instrumentId: udlInstrumentIdSchema,
413
459
  path: z.literal("refs").optional(),
414
460
  refField: udlFieldNameSchema,
415
461
  statuses: z.array(udlSnakeCaseSchema).min(1),
@@ -421,6 +467,7 @@ const udlRemainderSchema = z.strictObject({
421
467
  inputKey: udlFieldNameSchema.optional(),
422
468
  onZero: z.enum(["refuse", "skip_steps"]),
423
469
  totalPath: instanceValuePathSchema,
470
+ subtractPaths: z.array(instanceValuePathSchema).min(1).max(16).optional(),
424
471
  });
425
472
  const udlCheckRequirementVariants = udlCheckEvidenceProfiles.map((profile) => z.strictObject({
426
473
  checkKind: z.literal(profile.checkKind),
@@ -486,10 +533,16 @@ const udlPartitionSchema = z.strictObject({
486
533
  const udlDerivedAmountSchema = z.strictObject({
487
534
  field: udlFieldNameSchema,
488
535
  rounding: z.literal("floor"),
489
- rule: z.strictObject({
490
- bps: z.number().int().min(1).max(9_999),
491
- kind: z.literal("percentage_of"),
492
- }),
536
+ rule: z.discriminatedUnion("kind", [
537
+ z.strictObject({
538
+ bps: z.union([z.number().int().min(1).max(9_999), udlStoredFieldSchema]),
539
+ kind: z.literal("percentage_of"),
540
+ }),
541
+ z.strictObject({
542
+ kind: z.literal("minimum"),
543
+ capField: udlFieldNameSchema,
544
+ }),
545
+ ]),
493
546
  sourceField: udlFieldNameSchema,
494
547
  });
495
548
  const udlFeeLeafRuleSchema = z.discriminatedUnion("kind", [
@@ -620,7 +673,162 @@ export const udlActionLibraryModuleSchema = z.strictObject({
620
673
  actions: z.record(udlSnakeCaseSchema, udlPrivateActionSchema),
621
674
  });
622
675
  export const udlActionLibrarySchema = z.record(udlSnakeCaseSchema, udlActionLibraryModuleSchema);
676
+ const templateParametersSchema = z.record(udlFieldNameSchema, z.union([z.string(), z.number().int(), z.boolean()]));
677
+ const udlAllocationBucketSchema = z.strictObject({
678
+ key: z.enum(["principal", "profit", "cost", "fine"]),
679
+ source: z.union([
680
+ z.strictObject({
681
+ from: z.literal("slice"),
682
+ amountField: udlFieldNameSchema,
683
+ destinationField: udlFieldNameSchema,
684
+ }),
685
+ z.strictObject({
686
+ from: z.literal("children"),
687
+ instrumentId: udlInstrumentIdSchema,
688
+ refField: udlFieldNameSchema,
689
+ statuses: z.array(udlSnakeCaseSchema).min(1),
690
+ amountField: udlFieldNameSchema,
691
+ destinationField: udlFieldNameSchema,
692
+ }),
693
+ z.strictObject({
694
+ from: z.literal("children"),
695
+ template: udlSnakeCaseSchema,
696
+ parameters: templateParametersSchema,
697
+ refField: udlFieldNameSchema,
698
+ statuses: z.array(udlSnakeCaseSchema).min(1),
699
+ amountField: udlFieldNameSchema,
700
+ destinationField: udlFieldNameSchema,
701
+ }),
702
+ ]),
703
+ });
704
+ const udlAllocationSchema = z.strictObject({
705
+ sliceInstrumentId: udlInstrumentIdSchema,
706
+ sliceRefField: udlFieldNameSchema,
707
+ sliceStatuses: z.array(udlSnakeCaseSchema).min(1),
708
+ dueField: udlFieldNameSchema,
709
+ positionField: udlFieldNameSchema,
710
+ earningRuleField: udlFieldNameSchema,
711
+ buckets: z.array(udlAllocationBucketSchema).min(2).max(4),
712
+ });
713
+ const udlAllocateSchema = z.union([
714
+ z.strictObject({
715
+ refField: udlFieldNameSchema.optional(),
716
+ mode: z.literal("refund"),
717
+ action: udlActionNameSchema.optional(),
718
+ assessmentField: udlFieldNameSchema.optional(),
719
+ capture: udlFieldNameSchema,
720
+ }),
721
+ z.strictObject({
722
+ refField: udlFieldNameSchema,
723
+ mode: z.literal("payment"),
724
+ assessment: z.literal("self").optional(),
725
+ amountField: udlFieldNameSchema.optional(),
726
+ sourceAccountField: udlFieldNameSchema,
727
+ paymentIdentityField: udlFieldNameSchema,
728
+ capture: udlFieldNameSchema,
729
+ }),
730
+ z.strictObject({
731
+ refField: udlFieldNameSchema.optional(),
732
+ mode: z.literal("payoff"),
733
+ amountField: udlFieldNameSchema.optional(),
734
+ sourceAccountField: udlFieldNameSchema,
735
+ paymentIdentityField: udlFieldNameSchema,
736
+ capture: udlFieldNameSchema,
737
+ }),
738
+ z.strictObject({
739
+ refField: udlFieldNameSchema.optional(),
740
+ mode: z.literal("write_off"),
741
+ capture: udlFieldNameSchema,
742
+ }),
743
+ ]);
744
+ const udlContributionsSchema = z.strictObject({
745
+ field: udlFieldNameSchema,
746
+ amountKey: udlFieldNameSchema,
747
+ accountKey: udlFieldNameSchema,
748
+ totalField: udlFieldNameSchema,
749
+ });
750
+ // These clauses are opt-in. Absent clauses never add defaults to older documents.
751
+ const udlFundingSchema = z.strictObject({
752
+ obligationField: udlFieldNameSchema,
753
+ ticketInstrumentId: udlInstrumentIdSchema,
754
+ ticketRefField: udlFieldNameSchema,
755
+ ticketAmountField: udlFieldNameSchema,
756
+ ticketInvestorField: udlFieldNameSchema,
757
+ ticketAccountField: udlFieldNameSchema,
758
+ ticketStatus: udlSnakeCaseSchema,
759
+ collectAction: udlActionNameSchema,
760
+ principalField: udlFieldNameSchema,
761
+ sourceAccountPath: instanceValuePathSchema,
762
+ destinationAccountField: udlFieldNameSchema,
763
+ terms: z.record(udlFieldNameSchema, udlFieldNameSchema),
764
+ capture: udlFieldNameSchema,
765
+ });
766
+ const udlReceiptDistributionSchema = z.strictObject({
767
+ roundField: udlFieldNameSchema,
768
+ snapshotRef: udlFieldNameSchema,
769
+ receiptField: udlFieldNameSchema,
770
+ receiptPath: instanceValuePathSchema,
771
+ mode: z.enum(["cash", "loss"]),
772
+ feeBps: z.number().int().min(0).max(10000),
773
+ vatBps: z.number().int().min(0).max(10000),
774
+ feeAccountField: udlFieldNameSchema,
775
+ taxAccountField: udlFieldNameSchema,
776
+ residualAccountField: udlFieldNameSchema,
777
+ capture: udlFieldNameSchema,
778
+ });
779
+ const udlRequestAuthoritySchema = z.strictObject({
780
+ instrumentField: udlFieldNameSchema,
781
+ instanceField: udlFieldNameSchema,
782
+ actionField: udlFieldNameSchema,
783
+ inputField: udlFieldNameSchema,
784
+ digestField: udlFieldNameSchema,
785
+ roleField: udlFieldNameSchema,
786
+ party: udlPartyRoleSchema,
787
+ expiresField: udlFieldNameSchema,
788
+ });
623
789
  const udlActionShape = {
790
+ requestAuthority: udlRequestAuthoritySchema.optional(),
791
+ funding: udlFundingSchema.optional(),
792
+ receiptDistribution: udlReceiptDistributionSchema.optional(),
793
+ requiresAllocation: z
794
+ .strictObject({
795
+ refField: udlFieldNameSchema.optional(),
796
+ slice: z.union([z.literal("self"), udlStoredFieldSchema]),
797
+ buckets: z
798
+ .array(z.enum(["principal", "profit", "cost", "fine"]))
799
+ .min(1)
800
+ .max(4),
801
+ check: z.enum(["settled", "outstanding"]),
802
+ })
803
+ .optional(),
804
+ unique: z
805
+ .strictObject({
806
+ namespace: udlSnakeCaseSchema,
807
+ byFields: z.array(udlFieldNameSchema).min(1).max(8),
808
+ })
809
+ .optional(),
810
+ requiresInput: z
811
+ .record(udlFieldNameSchema, instanceValuePathSchema)
812
+ .optional(),
813
+ engineOwned: z.literal(true).optional(),
814
+ captureEngine: z
815
+ .record(udlFieldNameSchema, z.enum(["operationId"]))
816
+ .optional(),
817
+ allocate: udlAllocateSchema.optional(),
818
+ contributionStage: z
819
+ .strictObject({
820
+ stage: z.enum(["fund", "refund"]),
821
+ accountPath: instanceValuePathSchema,
822
+ })
823
+ .optional(),
824
+ transitionsRefs: z
825
+ .array(z.strictObject({
826
+ field: udlFieldNameSchema,
827
+ action: udlActionNameSchema,
828
+ }))
829
+ .min(1)
830
+ .max(16)
831
+ .optional(),
624
832
  agentDescription: udlAgentDescriptionSchema.optional(),
625
833
  calls: z.array(udlCallSchema).min(1).optional(),
626
834
  captureInput: z.record(udlFieldNameSchema, udlFieldNameSchema).optional(),
@@ -634,7 +842,11 @@ const udlActionShape = {
634
842
  due: udlDueSchema.optional(),
635
843
  earnable: z.boolean().optional(),
636
844
  effects: udlEffectsSchema.optional(),
637
- eventName: z.string().regex(eventNamePattern).optional(),
845
+ eventName: z
846
+ .string()
847
+ .regex(eventNamePattern)
848
+ .meta({ "x-udl-reference": "instrument_event" })
849
+ .optional(),
638
850
  examples: z.array(udlExampleSchema).min(1).optional(),
639
851
  input: jsonObjectSchema.optional(),
640
852
  moves: z.array(udlMoveSchema).default([]),
@@ -662,7 +874,7 @@ const udlActionShape = {
662
874
  };
663
875
  const udlActionSchema = z.strictObject(udlActionShape);
664
876
  const udlAggregateBaseShape = {
665
- childInstrumentId: udlSnakeCaseSchema,
877
+ childInstrumentId: udlInstrumentIdSchema,
666
878
  childRefField: udlFieldNameSchema,
667
879
  childStatuses: z.array(udlSnakeCaseSchema).min(1),
668
880
  parentField: udlFieldNameSchema,
@@ -687,6 +899,23 @@ const udlAggregateSchema = z.union([
687
899
  }),
688
900
  ]);
689
901
  const udlInstrumentShape = {
902
+ templateBinding: z
903
+ .strictObject({
904
+ id: udlSnakeCaseSchema,
905
+ parameters: templateParametersSchema,
906
+ })
907
+ .optional(),
908
+ allocation: udlAllocationSchema.optional(),
909
+ contributions: udlContributionsSchema.optional(),
910
+ dateOrder: z
911
+ .array(z.strictObject({
912
+ beforeField: udlFieldNameSchema,
913
+ afterField: udlFieldNameSchema,
914
+ operator: z.enum(["<", "<="]),
915
+ }))
916
+ .min(1)
917
+ .max(16)
918
+ .optional(),
690
919
  actionLibrary: udlActionLibrarySchema.optional(),
691
920
  actionOrder: z.array(udlActionNameSchema).min(1),
692
921
  agentDescription: udlAgentDescriptionSchema.optional(),
@@ -697,15 +926,14 @@ const udlInstrumentShape = {
697
926
  description: nonEmptyTextSchema.optional(),
698
927
  dials: z.array(udlDialSchema).optional(),
699
928
  distinctParties: z.literal(true).optional(),
700
- derivedAmounts: z.array(udlDerivedAmountSchema).min(1).max(4).optional(),
929
+ derivedAmounts: z.array(udlDerivedAmountSchema).min(1).max(64).optional(),
701
930
  feeRules: z.array(udlFeeRuleSchema).min(1).max(4).optional(),
702
931
  fields: z.record(udlFieldNameSchema, jsonObjectSchema),
703
- id: udlSnakeCaseSchema,
932
+ id: udlInstrumentIdSchema,
704
933
  idPrefix: z
705
934
  .string()
706
935
  .regex(idPrefixPattern, "must contain 2 to 8 lowercase letters"),
707
936
  lifecycle: udlLifecycleSchema,
708
- journeys: z.array(udlJourneySchema).min(1).optional(),
709
937
  nav: z.array(nonEmptyTextSchema).min(1).optional(),
710
938
  parties: z.record(udlPartyRoleSchema, udlFieldNameSchema).optional(),
711
939
  partitions: z.array(udlPartitionSchema).min(1).optional(),
@@ -721,6 +949,105 @@ const udlInstrumentShape = {
721
949
  };
722
950
  const udlInstrumentSchema = z.strictObject(udlInstrumentShape);
723
951
  export const udlClauseVocabulary = [
952
+ {
953
+ scope: "action",
954
+ spelling: "funding",
955
+ target: "funding",
956
+ cardinality: "one",
957
+ effects: [
958
+ {
959
+ kind: "moves",
960
+ per: "clause",
961
+ signature: { fixed: "transfer.internal" },
962
+ },
963
+ ],
964
+ },
965
+ {
966
+ scope: "action",
967
+ spelling: "receipt distribution",
968
+ target: "receiptDistribution",
969
+ cardinality: "one",
970
+ effects: [
971
+ { kind: "decides", per: "clause", signature: { fixed: "allocation" } },
972
+ { kind: "moves", per: "clause", signature: { fixed: "allocation" } },
973
+ ],
974
+ },
975
+ {
976
+ cardinality: "one",
977
+ scope: "action",
978
+ spelling: "requires allocation",
979
+ target: "requiresAllocation",
980
+ effects: [
981
+ { kind: "decides", per: "clause", signature: { fixed: "allocation" } },
982
+ ],
983
+ },
984
+ {
985
+ cardinality: "one",
986
+ scope: "instrument",
987
+ spelling: "allocation",
988
+ target: "allocation",
989
+ },
990
+ {
991
+ cardinality: "one",
992
+ scope: "instrument",
993
+ spelling: "contributions",
994
+ target: "contributions",
995
+ },
996
+ {
997
+ cardinality: "one",
998
+ scope: "action",
999
+ spelling: "allocate",
1000
+ target: "allocate",
1001
+ effects: [
1002
+ { kind: "decides", per: "clause", signature: { fixed: "allocation" } },
1003
+ { kind: "moves", per: "clause", signature: { fixed: "allocation" } },
1004
+ ],
1005
+ },
1006
+ {
1007
+ cardinality: "one",
1008
+ scope: "action",
1009
+ spelling: "contribution stage",
1010
+ target: "contributionStage",
1011
+ effects: [
1012
+ {
1013
+ kind: "moves",
1014
+ per: "clause",
1015
+ signature: { fixed: "transfer.internal" },
1016
+ },
1017
+ ],
1018
+ },
1019
+ {
1020
+ cardinality: "many",
1021
+ scope: "instrument",
1022
+ spelling: "date order",
1023
+ target: "dateOrder",
1024
+ },
1025
+ {
1026
+ cardinality: "one",
1027
+ scope: "action",
1028
+ spelling: "unique",
1029
+ target: "unique",
1030
+ effects: [
1031
+ {
1032
+ kind: "decides",
1033
+ per: "clause",
1034
+ signature: { fixed: "subject_unique" },
1035
+ },
1036
+ ],
1037
+ },
1038
+ {
1039
+ cardinality: "many",
1040
+ scope: "action",
1041
+ spelling: "transitions refs",
1042
+ target: "transitionsRefs",
1043
+ effects: [
1044
+ {
1045
+ kind: "decides",
1046
+ per: "element",
1047
+ signature: { fixed: "referenced_transition" },
1048
+ },
1049
+ ],
1050
+ },
724
1051
  {
725
1052
  cardinality: "one",
726
1053
  literalKeys: true,
@@ -741,6 +1068,26 @@ export const udlClauseVocabulary = [
741
1068
  spelling: "calls",
742
1069
  target: "calls",
743
1070
  },
1071
+ {
1072
+ cardinality: "one",
1073
+ scope: "action",
1074
+ spelling: "requires input",
1075
+ target: "requiresInput",
1076
+ literalKeys: true,
1077
+ },
1078
+ {
1079
+ cardinality: "one",
1080
+ scope: "action",
1081
+ spelling: "engine owned",
1082
+ target: "engineOwned",
1083
+ },
1084
+ {
1085
+ cardinality: "one",
1086
+ scope: "action",
1087
+ spelling: "capture engine",
1088
+ target: "captureEngine",
1089
+ literalKeys: true,
1090
+ },
744
1091
  {
745
1092
  cardinality: "one",
746
1093
  scope: "action",
@@ -1106,12 +1453,6 @@ export const udlClauseVocabulary = [
1106
1453
  spelling: "id prefix",
1107
1454
  target: "idPrefix",
1108
1455
  },
1109
- {
1110
- cardinality: "many",
1111
- scope: "instrument",
1112
- spelling: "journeys",
1113
- target: "journeys",
1114
- },
1115
1456
  {
1116
1457
  cardinality: "many",
1117
1458
  scope: "instrument",
@@ -1142,6 +1483,12 @@ export const udlClauseVocabulary = [
1142
1483
  spelling: "surface visibility",
1143
1484
  target: "surfaceVisibility",
1144
1485
  },
1486
+ {
1487
+ cardinality: "one",
1488
+ scope: "instrument",
1489
+ spelling: "template binding",
1490
+ target: "templateBinding",
1491
+ },
1145
1492
  {
1146
1493
  cardinality: "one",
1147
1494
  scope: "instrument",
@@ -1160,6 +1507,13 @@ export const udlClauseVocabulary = [
1160
1507
  spelling: "update",
1161
1508
  target: "update",
1162
1509
  },
1510
+ {
1511
+ cardinality: "one",
1512
+ effects: [],
1513
+ scope: "action",
1514
+ spelling: "request authority",
1515
+ target: "requestAuthority",
1516
+ },
1163
1517
  ];
1164
1518
  const udlDocumentShapeSchema = z.strictObject({
1165
1519
  dials: z.array(udlCompositionDialSchema).min(1).optional(),
@@ -1175,26 +1529,10 @@ function isMoneyField(schema) {
1175
1529
  return (typeof schema?.pattern === "string" && moneyPatterns.has(schema.pattern));
1176
1530
  }
1177
1531
  function referenceTargets(schema, instruments) {
1178
- if (schema.type !== "string" || typeof schema.pattern !== "string")
1179
- return [];
1180
- // Parse the scoped-ID pattern. Executing an author regex here would bypass
1181
- // the semantic validator's branch and quantifier budgets.
1182
- const match = /^\^([a-z]{2,8})_\(sandbox\|live\)_\[a-z0-9\]\{8,64\}\$$/.exec(schema.pattern);
1183
- if (!match)
1184
- return [];
1185
- const prefix = match[1];
1186
- const probe = `${prefix}_sandbox_0123456789abcdef`;
1187
- if (typeof schema.const === "string" && schema.const !== probe)
1188
- return [];
1189
- if (Array.isArray(schema.enum) && !schema.enum.includes(probe))
1190
- return [];
1191
- if (typeof schema.minLength === "number" && probe.length < schema.minLength) {
1192
- return [];
1193
- }
1194
- if (typeof schema.maxLength === "number" && probe.length > schema.maxLength) {
1195
- return [];
1196
- }
1197
- return instruments.filter((instrument) => instrument.idPrefix === prefix);
1532
+ const prefix = referencePatternPrefix(schema);
1533
+ return prefix
1534
+ ? instruments.filter((instrument) => instrument.idPrefix === prefix)
1535
+ : [];
1198
1536
  }
1199
1537
  function declaredMoneyRefKeys(instrument) {
1200
1538
  return new Set([