@hyperscale0/udl 1.0.0-beta.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. package/CHANGELOG.md +78 -25
  2. package/README.md +37 -99
  3. package/conformance/README.md +44 -41
  4. package/conformance/evolution/action-contract.expected.json +10 -0
  5. package/conformance/evolution/action-contract.live.udl +44 -0
  6. package/conformance/evolution/action-contract.next.udl +45 -0
  7. package/conformance/evolution/product-identity.expected.json +10 -0
  8. package/conformance/evolution/product-identity.live.udl +55 -0
  9. package/conformance/evolution/product-identity.next.udl +55 -0
  10. package/conformance/evolution/version-required.expected.json +10 -0
  11. package/conformance/evolution/version-required.live.udl +55 -0
  12. package/conformance/evolution/version-required.next.udl +58 -0
  13. package/conformance/invalid/action-without-transition.expected.json +10 -0
  14. package/conformance/invalid/{verb-without-transition.udl → action-without-transition.udl} +7 -2
  15. package/conformance/invalid/agent-description-too-long.expected.json +10 -0
  16. package/conformance/invalid/agent-description-too-long.udl +57 -0
  17. package/conformance/invalid/blank-title.expected.json +1 -1
  18. package/conformance/invalid/blank-title.udl +6 -2
  19. package/conformance/invalid/composition-dial-duplicate-key.expected.json +10 -0
  20. package/conformance/invalid/composition-dial-duplicate-key.udl +73 -0
  21. package/conformance/invalid/depth-budget.expected.json +1 -1
  22. package/conformance/invalid/duplicate-subject.expected.json +10 -0
  23. package/conformance/invalid/duplicate-subject.udl +230 -0
  24. package/conformance/invalid/forged-effects.expected.json +10 -0
  25. package/conformance/invalid/forged-effects.udl +76 -0
  26. package/conformance/invalid/format-version.expected.json +1 -1
  27. package/conformance/invalid/format-version.udl +6 -2
  28. package/conformance/invalid/instrument-id-not-snake-case.expected.json +10 -0
  29. package/conformance/invalid/{noun-id-not-snake-case.udl → instrument-id-not-snake-case.udl} +6 -2
  30. package/conformance/invalid/invalid-aggregate-gate-shape.expected.json +10 -0
  31. package/conformance/invalid/invalid-aggregate-gate-shape.udl +1819 -0
  32. package/conformance/invalid/invalid-check-duration.expected.json +10 -0
  33. package/conformance/invalid/invalid-check-duration.udl +219 -0
  34. package/conformance/invalid/invalid-dial-anchor.expected.json +10 -0
  35. package/conformance/invalid/invalid-dial-anchor.udl +219 -0
  36. package/conformance/invalid/invalid-exception-parent-ref.expected.json +10 -0
  37. package/conformance/invalid/invalid-exception-parent-ref.udl +2487 -0
  38. package/conformance/invalid/invalid-exposure-shape.expected.json +10 -0
  39. package/conformance/invalid/invalid-exposure-shape.udl +1819 -0
  40. package/conformance/invalid/invalid-remainder.expected.json +10 -0
  41. package/conformance/invalid/invalid-remainder.udl +220 -0
  42. package/conformance/invalid/invalid-schema-keyword.expected.json +10 -0
  43. package/conformance/invalid/invalid-schema-keyword.udl +220 -0
  44. package/conformance/invalid/invalid-utf8.expected.json +1 -1
  45. package/conformance/invalid/malformed-json.expected.json +1 -1
  46. package/conformance/invalid/missing-create-action.expected.json +10 -0
  47. package/conformance/invalid/{missing-create-verb.udl → missing-create-action.udl} +5 -2
  48. package/conformance/invalid/missing-exception-amount-field.expected.json +10 -0
  49. package/conformance/invalid/missing-exception-amount-field.udl +2487 -0
  50. package/conformance/invalid/missing-exception-contract.expected.json +14 -0
  51. package/conformance/invalid/missing-exception-contract.udl +2523 -0
  52. package/conformance/invalid/missing-exception-reason-field.expected.json +10 -0
  53. package/conformance/invalid/missing-exception-reason-field.udl +2487 -0
  54. package/conformance/invalid/not-an-object.expected.json +1 -1
  55. package/conformance/invalid/payout-reconcile-not-a-bank-debit.expected.json +10 -0
  56. package/conformance/invalid/payout-reconcile-not-a-bank-debit.udl +259 -0
  57. package/conformance/invalid/quote-freeze-set-incomplete.expected.json +10 -0
  58. package/conformance/invalid/quote-freeze-set-incomplete.udl +261 -0
  59. package/conformance/invalid/quote-named-reference-gate.expected.json +10 -0
  60. package/conformance/invalid/quote-named-reference-gate.udl +50 -0
  61. package/conformance/invalid/reconcile-named-reference-gate.expected.json +10 -0
  62. package/conformance/invalid/reconcile-named-reference-gate.udl +50 -0
  63. package/conformance/invalid/unknown-key.expected.json +1 -1
  64. package/conformance/invalid/unknown-key.udl +6 -2
  65. package/conformance/invalid/unknown-reference-gate-field.expected.json +10 -0
  66. package/conformance/invalid/unknown-reference-gate-field.udl +1819 -0
  67. package/conformance/invalid/unknown-required-field.expected.json +10 -0
  68. package/conformance/invalid/unknown-required-field.udl +220 -0
  69. package/conformance/invalid/unreachable-state.expected.json +2 -2
  70. package/conformance/invalid/unreachable-state.udl +6 -2
  71. package/conformance/invalid/wrong-exception-amount-field.expected.json +10 -0
  72. package/conformance/invalid/wrong-exception-amount-field.udl +2487 -0
  73. package/conformance/invalid/wrong-exception-reason-field.expected.json +10 -0
  74. package/conformance/invalid/wrong-exception-reason-field.udl +2487 -0
  75. package/conformance/valid/agent-description.expected.json +6 -0
  76. package/conformance/valid/agent-description.udl +66 -0
  77. package/conformance/valid/cards.expected.json +1 -0
  78. package/conformance/valid/cards.udl +843 -356
  79. package/conformance/valid/commerce-escrow.expected.json +1 -0
  80. package/conformance/valid/commerce-escrow.udl +792 -291
  81. package/conformance/valid/compiled-crowdfunding.expected.json +6 -0
  82. package/conformance/valid/compiled-crowdfunding.udl +1843 -0
  83. package/conformance/valid/compiled-watch-club.expected.json +6 -0
  84. package/conformance/valid/compiled-watch-club.udl +2486 -0
  85. package/conformance/valid/complete-contract.expected.json +6 -0
  86. package/conformance/valid/complete-contract.udl +218 -0
  87. package/conformance/valid/effect-signatures.expected.json +6 -0
  88. package/conformance/valid/effect-signatures.udl +75 -0
  89. package/conformance/valid/hand-edited.expected.json +1 -0
  90. package/conformance/valid/hand-edited.udl +1 -1
  91. package/conformance/valid/insured-car-marketplace.expected.json +1 -0
  92. package/conformance/valid/insured-car-marketplace.udl +534 -315
  93. package/conformance/valid/insured-travel.expected.json +2 -1
  94. package/conformance/valid/insured-travel.udl +1489 -641
  95. package/conformance/valid/minimal.expected.json +1 -0
  96. package/conformance/valid/minimal.udl +18 -14
  97. package/conformance/valid/protection.expected.json +1 -0
  98. package/conformance/valid/protection.udl +719 -304
  99. package/conformance/valid/string-escaping.expected.json +6 -0
  100. package/conformance/valid/string-escaping.udl +54 -0
  101. package/dist/canonical.d.ts +2 -0
  102. package/dist/canonical.d.ts.map +1 -1
  103. package/dist/canonical.js +7 -0
  104. package/dist/canonical.js.map +1 -1
  105. package/dist/check-profiles.d.ts +57 -0
  106. package/dist/check-profiles.d.ts.map +1 -0
  107. package/dist/check-profiles.js +62 -0
  108. package/dist/check-profiles.js.map +1 -0
  109. package/dist/cli.js +48 -4
  110. package/dist/cli.js.map +1 -1
  111. package/dist/diagnostics.d.ts +168 -0
  112. package/dist/diagnostics.d.ts.map +1 -0
  113. package/dist/diagnostics.js +164 -0
  114. package/dist/diagnostics.js.map +1 -0
  115. package/dist/effects.d.ts +41 -0
  116. package/dist/effects.d.ts.map +1 -0
  117. package/dist/effects.js +110 -0
  118. package/dist/effects.js.map +1 -0
  119. package/dist/evolution.d.ts +51 -18
  120. package/dist/evolution.d.ts.map +1 -1
  121. package/dist/evolution.js +244 -102
  122. package/dist/evolution.js.map +1 -1
  123. package/dist/finance.d.ts +17 -13
  124. package/dist/finance.d.ts.map +1 -1
  125. package/dist/finance.js +165 -109
  126. package/dist/finance.js.map +1 -1
  127. package/dist/index.d.ts +12 -8
  128. package/dist/index.d.ts.map +1 -1
  129. package/dist/index.js +8 -5
  130. package/dist/index.js.map +1 -1
  131. package/dist/limits.d.ts +2 -2
  132. package/dist/limits.d.ts.map +1 -1
  133. package/dist/limits.js +4 -3
  134. package/dist/limits.js.map +1 -1
  135. package/dist/parser.d.ts.map +1 -1
  136. package/dist/parser.js +6 -16
  137. package/dist/parser.js.map +1 -1
  138. package/dist/schema.d.ts +2059 -135
  139. package/dist/schema.d.ts.map +1 -1
  140. package/dist/schema.js +759 -73
  141. package/dist/schema.js.map +1 -1
  142. package/dist/validation.d.ts +22 -9
  143. package/dist/validation.d.ts.map +1 -1
  144. package/dist/validation.js +1463 -457
  145. package/dist/validation.js.map +1 -1
  146. package/docs/README.md +14 -0
  147. package/docs/guide/01-a-document.md +37 -0
  148. package/docs/guide/02-money-steps.md +23 -0
  149. package/docs/guide/03-laws.md +18 -0
  150. package/docs/guide/04-fees-and-remainder.md +15 -0
  151. package/docs/guide/05-checks-updates-dials.md +7 -0
  152. package/docs/guide/06-effects.md +11 -0
  153. package/docs/guide/07-evolution.md +11 -0
  154. package/docs/guide/08-implementing.md +13 -0
  155. package/docs/llms-full.txt +1241 -0
  156. package/docs/llms.txt +14 -0
  157. package/docs/reference/canonical.md +16 -0
  158. package/docs/reference/clauses.md +986 -0
  159. package/docs/reference/cli.md +22 -0
  160. package/docs/reference/diagnostics.md +32 -0
  161. package/package.json +6 -2
  162. package/skills/udl/SKILL.md +28 -0
  163. package/spec/README.md +82 -51
  164. package/spec/udl.schema.json +1191 -119
  165. package/src/canonical.ts +11 -0
  166. package/src/check-profiles.ts +80 -0
  167. package/src/cli.ts +49 -3
  168. package/src/diagnostics.ts +221 -0
  169. package/src/effects.ts +163 -0
  170. package/src/evolution.ts +375 -125
  171. package/src/finance.ts +255 -160
  172. package/src/index.ts +50 -14
  173. package/src/limits.ts +4 -3
  174. package/src/parser.ts +15 -18
  175. package/src/schema.ts +855 -81
  176. package/src/validation.ts +2666 -560
  177. package/conformance/invalid/missing-create-verb.expected.json +0 -10
  178. package/conformance/invalid/noun-id-not-snake-case.expected.json +0 -10
  179. package/conformance/invalid/verb-without-transition.expected.json +0 -10
@@ -1,6 +1,104 @@
1
1
  {
2
- "nouns": [
2
+ "instruments": [
3
3
  {
4
+ "actionOrder": [
5
+ "create",
6
+ "reserve",
7
+ "unreserve",
8
+ "complete",
9
+ "delist"
10
+ ],
11
+ "actions": {
12
+ "complete": {
13
+ "agentDescription": "Mark the car sold. Refused unless the referenced escrow has settled, so the seller is already paid before the listing closes. Sold is terminal and does not reverse.",
14
+ "description": "Marks the car sold after the reserving escrow settles to the seller. Pure lifecycle transition over the catalog row.",
15
+ "effects": {
16
+ "reads": [
17
+ {
18
+ "signature": "reads.requires_refs",
19
+ "source": "requiresRefs"
20
+ }
21
+ ]
22
+ },
23
+ "moves": [],
24
+ "requiresRefs": [
25
+ {
26
+ "field": "escrowId",
27
+ "statuses": [
28
+ "settled"
29
+ ]
30
+ }
31
+ ],
32
+ "steps": [],
33
+ "summary": "Complete a vehicle listing sale"
34
+ },
35
+ "create": {
36
+ "agentDescription": "Publish the car to the marketplace at an asking price. No money moves. The listing stays on the market until a buyer's escrow reserves it or the seller delists it.",
37
+ "description": "Publishes the car to the marketplace at an asking price. No money moves until an escrow is opened against it.",
38
+ "moves": [],
39
+ "steps": [],
40
+ "summary": "Create a vehicle listing"
41
+ },
42
+ "delist": {
43
+ "agentDescription": "Withdraw an unsold car from the marketplace. Only a listing still on the market delists. A reserved one has to unreserve first. No money moves.",
44
+ "description": "Withdraws an unsold car from the marketplace. No money has moved, so this is a pure lifecycle transition.",
45
+ "moves": [],
46
+ "steps": [],
47
+ "summary": "Delist a vehicle listing"
48
+ },
49
+ "reserve": {
50
+ "agentDescription": "Take the listing off the market for a buyer. Refused unless the referenced vehicle escrow is funded, so the buyer's money is already held before the car is held. No money moves.",
51
+ "description": "Takes the listing off the market while a buyer's escrow holds funds. The referenced escrow must currently hold the buyer's funds.",
52
+ "effects": {
53
+ "reads": [
54
+ {
55
+ "signature": "reads.requires_refs",
56
+ "source": "requiresRefs"
57
+ }
58
+ ]
59
+ },
60
+ "moves": [],
61
+ "requiresRefs": [
62
+ {
63
+ "field": "escrowId",
64
+ "statuses": [
65
+ "funded"
66
+ ]
67
+ }
68
+ ],
69
+ "steps": [],
70
+ "summary": "Reserve a vehicle listing"
71
+ },
72
+ "unreserve": {
73
+ "agentDescription": "Put a reserved car back on the market. Refused unless the referenced escrow is canceled or its penalty collected, so a live escrow can never lose its reservation. No money moves.",
74
+ "description": "Relists the car after the reserving escrow is canceled. No money has moved on the listing itself.",
75
+ "effects": {
76
+ "reads": [
77
+ {
78
+ "signature": "reads.requires_refs",
79
+ "source": "requiresRefs"
80
+ }
81
+ ]
82
+ },
83
+ "moves": [],
84
+ "requiresRefs": [
85
+ {
86
+ "field": "escrowId",
87
+ "statuses": [
88
+ "canceled",
89
+ "penalty_collected"
90
+ ]
91
+ }
92
+ ],
93
+ "steps": [],
94
+ "summary": "Return a reserved vehicle listing to the market"
95
+ }
96
+ },
97
+ "agentDescription": "Reach for vehicle listing when a seller advertises one car at an asking price and the listing status has to track a buyer's escrow. The listing moves no money at all. The buyer's funds are held by the vehicle escrow it references, and every transition here checks that escrow's status.",
98
+ "callerParkedStates": {
99
+ "listed": "reserving or delisting is a party act on an open listing",
100
+ "reserved": "completing or unreserving a reservation is a party decision"
101
+ },
4
102
  "description": "A vehicle listing advertises one car for sale at an asking price. It is reserved while a vehicle escrow holds the buyer's funds, sold when the escrow settles to the seller, returned to the market if the escrow is canceled, or delisted by the seller before sale.",
5
103
  "fields": {
6
104
  "askingPrice": {
@@ -79,6 +177,10 @@
79
177
  }
80
178
  }
81
179
  },
180
+ "nav": [
181
+ "Blueprints",
182
+ "Vehicle Listings"
183
+ ],
82
184
  "parties": {
83
185
  "subjectHolder": "sellerAccountId"
84
186
  },
@@ -94,6 +196,7 @@
94
196
  ]
95
197
  },
96
198
  "summary": "Marketplace listing for an insured used car offered by a seller.",
199
+ "templateId": "marketplace",
97
200
  "title": "Vehicle listing",
98
201
  "update": {
99
202
  "fields": [
@@ -105,70 +208,218 @@
105
208
  "states": [
106
209
  "listed"
107
210
  ]
108
- },
109
- "verbs": {
110
- "complete": {
111
- "description": "Marks the car sold after the reserving escrow settles to the seller. Pure lifecycle transition over the catalog row.",
112
- "moves": [],
113
- "requiresRefs": [
211
+ }
212
+ },
213
+ {
214
+ "actionOrder": [
215
+ "create",
216
+ "bind",
217
+ "open_claim",
218
+ "settle_claim",
219
+ "deny_claim",
220
+ "expire",
221
+ "cancel"
222
+ ],
223
+ "actions": {
224
+ "bind": {
225
+ "agentDescription": "Put a quote in force. An insurance carrier underwrites it under a 15 second deadline and a timeout declines. On acceptance the full premium settles from the policyholder to the insurer. Only a quoted policy binds. This moves the policyholder's money and does not reverse.",
226
+ "decision": {
227
+ "capability": "insurance_carrier",
228
+ "deadlineMs": 15000,
229
+ "onTimeout": "decline"
230
+ },
231
+ "description": "Underwrites the policy through an insurance carrier under a deadline, then settles the premium from the policyholder into the insurer account and puts the policy in force.",
232
+ "earnable": true,
233
+ "effects": {
234
+ "decides": [
235
+ {
236
+ "signature": "decides.insurance_carrier",
237
+ "source": "decision"
238
+ }
239
+ ],
240
+ "moves": [
241
+ {
242
+ "signature": "moves.transfer.internal",
243
+ "source": "moves[0]"
244
+ }
245
+ ]
246
+ },
247
+ "eventName": "motor_policy.bound",
248
+ "moves": [
114
249
  {
115
- "field": "escrowId",
116
- "statuses": [
117
- "settled"
118
- ]
250
+ "bind": {
251
+ "amount": {
252
+ "from": "instance",
253
+ "path": "fields.premiumAmount"
254
+ },
255
+ "currency": {
256
+ "from": "instance",
257
+ "path": "fields.currency"
258
+ },
259
+ "destinationAccountId": {
260
+ "from": "instance",
261
+ "path": "fields.insurerAccountId"
262
+ },
263
+ "productId": {
264
+ "from": "instance",
265
+ "path": "productId"
266
+ },
267
+ "sourceAccountId": {
268
+ "from": "instance",
269
+ "path": "fields.policyholderAccountId"
270
+ }
271
+ },
272
+ "capture": {
273
+ "premiumTransferId": "transferId"
274
+ },
275
+ "key": "transfer",
276
+ "operation": "internal_transfer.create"
119
277
  }
120
278
  ],
121
279
  "steps": [],
122
- "summary": "Complete a vehicle listing sale"
280
+ "summary": "Bind a motor policy"
123
281
  },
124
- "create": {
125
- "description": "Publishes the car to the marketplace at an asking price. No money moves until an escrow is opened against it.",
282
+ "cancel": {
283
+ "agentDescription": "Close a quote that never bound. Only a quoted policy cancels, no premium has moved, and there is nothing to refund. An in-force policy has no cancellation path here and lapses at its expiry instead.",
284
+ "description": "Closes a quote that never bound. No money has moved, so this is a pure lifecycle transition.",
126
285
  "moves": [],
127
286
  "steps": [],
128
- "summary": "Create a vehicle listing"
287
+ "summary": "Cancel a quoted motor policy"
129
288
  },
130
- "delist": {
131
- "description": "Withdraws an unsold car from the marketplace. No money has moved, so this is a pure lifecycle transition.",
289
+ "create": {
290
+ "agentDescription": "Open a policy quote against one vehicle subject with its premium and coverage amount. No money moves and the quote is not in force. Binding is a separate call that runs carrier underwriting.",
291
+ "description": "Opens the policy quote against a vehicle subject. No money moves until the policy binds.",
132
292
  "moves": [],
133
293
  "steps": [],
134
- "summary": "Delist a vehicle listing"
294
+ "summary": "Quote a motor policy"
135
295
  },
136
- "reserve": {
137
- "description": "Takes the listing off the market while a buyer's escrow holds funds. The referenced escrow must currently hold the buyer's funds.",
138
- "moves": [],
139
- "requiresRefs": [
296
+ "deny_claim": {
297
+ "agentDescription": "Refuse the open claim and void the pending coverage hold, returning the reserved amount to the insurer. The policy must be in claim_open. No money reaches the policyholder. Denied is a terminal state, so a rejected claim cannot be reopened on this policy.",
298
+ "description": "Denies the claim and voids the pending coverage hold, releasing the reserved amount back to the insurer account.",
299
+ "effects": {
300
+ "moves": [
301
+ {
302
+ "signature": "moves.transfer.internal",
303
+ "source": "moves[0]"
304
+ }
305
+ ]
306
+ },
307
+ "moves": [
140
308
  {
141
- "field": "escrowId",
142
- "statuses": [
143
- "funded"
144
- ]
309
+ "bind": {
310
+ "reason": {
311
+ "from": "const",
312
+ "value": "Motor claim denied"
313
+ },
314
+ "transferId": {
315
+ "from": "instance",
316
+ "path": "refs.claimTransferId"
317
+ }
318
+ },
319
+ "key": "transfer",
320
+ "operation": "internal_transfer.void"
145
321
  }
146
322
  ],
147
323
  "steps": [],
148
- "summary": "Reserve a vehicle listing"
324
+ "summary": "Deny a motor claim"
149
325
  },
150
- "unreserve": {
151
- "description": "Relists the car after the reserving escrow is canceled. No money has moved on the listing itself.",
326
+ "expire": {
327
+ "description": "Ends coverage at term with no open claim. The collected premium stays with the insurer, so this is a pure lifecycle transition fired by the maintenance loop under the expiry due-condition.",
328
+ "due": {
329
+ "field": "expiresAt"
330
+ },
331
+ "effects": {
332
+ "schedules": [
333
+ {
334
+ "signature": "schedules.due",
335
+ "source": "due"
336
+ }
337
+ ]
338
+ },
152
339
  "moves": [],
153
- "requiresRefs": [
340
+ "steps": [],
341
+ "summary": "Expire a motor policy"
342
+ },
343
+ "open_claim": {
344
+ "agentDescription": "Open a claim by reserving the coverage amount from the insurer toward the policyholder as a pending ledger hold. The policy must be active. The insurer account must already hold the coverage amount or the reservation is rejected for insufficient balance. Nothing settles until settle_claim.",
345
+ "description": "Places a pending ledger hold for the coverage amount from the insurer account toward the policyholder. The insurer account must be capitalized to the coverage amount, or the hold is rejected for insufficient balance.",
346
+ "effects": {
347
+ "holds": [
348
+ {
349
+ "signature": "holds.reserve",
350
+ "source": "moves[0]"
351
+ }
352
+ ],
353
+ "moves": [
354
+ {
355
+ "signature": "moves.transfer.internal",
356
+ "source": "moves[0]"
357
+ }
358
+ ]
359
+ },
360
+ "moves": [
154
361
  {
155
- "field": "escrowId",
156
- "statuses": [
157
- "canceled",
158
- "penalty_collected"
159
- ]
362
+ "bind": {
363
+ "amount": {
364
+ "from": "instance",
365
+ "path": "fields.coverageAmount"
366
+ },
367
+ "currency": {
368
+ "from": "instance",
369
+ "path": "fields.currency"
370
+ },
371
+ "destinationAccountId": {
372
+ "from": "instance",
373
+ "path": "fields.policyholderAccountId"
374
+ },
375
+ "sourceAccountId": {
376
+ "from": "instance",
377
+ "path": "fields.insurerAccountId"
378
+ }
379
+ },
380
+ "capture": {
381
+ "claimTransferId": "transferId"
382
+ },
383
+ "key": "transfer",
384
+ "operation": "internal_transfer.reserve"
160
385
  }
161
386
  ],
162
387
  "steps": [],
163
- "summary": "Return a reserved vehicle listing to the market"
388
+ "summary": "Open a motor claim"
389
+ },
390
+ "settle_claim": {
391
+ "agentDescription": "Approve the open claim and post the pending hold, landing the coverage amount in the policyholder's account. The policy must be in claim_open. This releases the insurer's money for real and does not reverse. Use deny_claim to void the hold instead.",
392
+ "description": "Approves the claim and posts the held coverage amount, settling the payout into the policyholder account.",
393
+ "effects": {
394
+ "moves": [
395
+ {
396
+ "signature": "moves.transfer.internal",
397
+ "source": "moves[0]"
398
+ }
399
+ ]
400
+ },
401
+ "eventName": "motor_policy.claim_settled",
402
+ "moves": [
403
+ {
404
+ "bind": {
405
+ "transferId": {
406
+ "from": "instance",
407
+ "path": "refs.claimTransferId"
408
+ }
409
+ },
410
+ "key": "transfer",
411
+ "operation": "internal_transfer.post"
412
+ }
413
+ ],
414
+ "steps": [],
415
+ "summary": "Settle a motor claim"
164
416
  }
165
- }
166
- },
167
- {
417
+ },
418
+ "agentDescription": "Reach for motor_policy when the caller quotes, binds, or claims on vehicle insurance for one car. The premium settles to the insurer on bind, and a claim reserves the coverage amount from the insurer toward the policyholder until an adjuster settles or denies it. Vehicle escrow orders capped by this policy are their own instrument.",
168
419
  "aggregateInvariants": [
169
420
  {
170
421
  "childField": "price",
171
- "childNounId": "vehicle_escrow",
422
+ "childInstrumentId": "vehicle_escrow",
172
423
  "childRefField": "policyId",
173
424
  "childStatuses": [
174
425
  "created",
@@ -178,6 +429,10 @@
178
429
  "parentField": "coverageAmount"
179
430
  }
180
431
  ],
432
+ "callerParkedStates": {
433
+ "claim_open": "settling or denying a claim is an adjuster's judgment",
434
+ "quoted": "binding or canceling a quote is the policyholder's decision"
435
+ },
181
436
  "description": "A motor policy insures a vehicle subject: the premium settles into the insurer account on bind, and a claim holds the coverage amount as a pending ledger reservation from the insurer toward the policyholder, posted on settlement or voided on denial. Binding is underwritten by an insurance carrier under a deadline; coverage lapses on expiry.",
182
437
  "fields": {
183
438
  "coverageAmount": {
@@ -281,6 +536,10 @@
281
536
  }
282
537
  }
283
538
  },
539
+ "nav": [
540
+ "Blueprints",
541
+ "Motor Policies"
542
+ ],
284
543
  "parties": {
285
544
  "beneficiary": "insurerAccountId",
286
545
  "payer": "policyholderAccountId"
@@ -299,6 +558,7 @@
299
558
  ]
300
559
  },
301
560
  "summary": "Vehicle insurance policy priced and capped on a declared car value.",
561
+ "templateId": "insurance",
302
562
  "title": "Motor policy",
303
563
  "update": {
304
564
  "fields": [
@@ -307,23 +567,36 @@
307
567
  "states": [
308
568
  "quoted"
309
569
  ]
310
- },
311
- "verbs": {
312
- "bind": {
313
- "decision": {
314
- "capability": "insurance_carrier",
315
- "deadlineMs": 15000,
316
- "onTimeout": "decline"
570
+ }
571
+ },
572
+ {
573
+ "actionOrder": [
574
+ "create",
575
+ "fund",
576
+ "release",
577
+ "quote_cancel",
578
+ "confirm_cancel",
579
+ "collect_penalty"
580
+ ],
581
+ "actions": {
582
+ "collect_penalty": {
583
+ "agentDescription": "Pay the retained cancellation penalty from escrow to the seller, which drains the escrow. Call it only when confirm_cancel returned a positive penalty. A cancellation quoted at zero penalty has nothing to collect.",
584
+ "description": "Pays the computed retained cancellation penalty to the seller. Invoke only when confirm_cancel returns a positive refs.unwindPenalty.",
585
+ "effects": {
586
+ "moves": [
587
+ {
588
+ "signature": "moves.transfer.internal",
589
+ "source": "moves[0]"
590
+ }
591
+ ]
317
592
  },
318
- "description": "Underwrites the policy through an insurance carrier under a deadline, then settles the premium from the policyholder into the insurer account and puts the policy in force.",
319
- "earnable": true,
320
- "eventName": "motor_policy.bound",
593
+ "eventName": "vehicle_escrow.penalty_collected",
321
594
  "moves": [
322
595
  {
323
596
  "bind": {
324
597
  "amount": {
325
598
  "from": "instance",
326
- "path": "fields.premiumAmount"
599
+ "path": "refs.unwindPenalty"
327
600
  },
328
601
  "currency": {
329
602
  "from": "instance",
@@ -331,77 +604,126 @@
331
604
  },
332
605
  "destinationAccountId": {
333
606
  "from": "instance",
334
- "path": "fields.insurerAccountId"
607
+ "path": "fields.sellerAccountId"
335
608
  },
336
- "sourceAccountId": {
609
+ "productId": {
337
610
  "from": "instance",
338
- "path": "fields.policyholderAccountId"
611
+ "path": "productId"
339
612
  },
340
- "workspaceId": {
613
+ "sourceAccountId": {
341
614
  "from": "instance",
342
- "path": "workspaceId"
615
+ "path": "refs.escrowAccountId"
343
616
  }
344
617
  },
345
618
  "capture": {
346
- "premiumTransferId": "transferId"
619
+ "penaltyTransferId": "transferId"
347
620
  },
348
621
  "key": "transfer",
349
622
  "operation": "internal_transfer.create"
350
623
  }
351
624
  ],
352
625
  "steps": [],
353
- "summary": "Bind a motor policy"
626
+ "summary": "Collect a vehicle cancellation penalty"
354
627
  },
355
- "cancel": {
356
- "description": "Closes a quote that never bound. No money has moved, so this is a pure lifecycle transition.",
357
- "moves": [],
628
+ "confirm_cancel": {
629
+ "agentDescription": "Refund the quoted amount, the price minus the scheduled penalty, from escrow to the buyer. Call quote_cancel first, it seeds the amount. This moves money and does not reverse. Any retained penalty stays in escrow until collect_penalty pays it out.",
630
+ "commit": "quote_cancel",
631
+ "description": "Unwinds the escrow, refunding the price minus the scheduled penalty to the buyer. The quote seeded that amount into refs.unwindRefund; this step moves it from the escrow account back to the buyer.",
632
+ "effects": {
633
+ "moves": [
634
+ {
635
+ "signature": "moves.transfer.internal",
636
+ "source": "moves[0]"
637
+ }
638
+ ]
639
+ },
640
+ "moves": [
641
+ {
642
+ "bind": {
643
+ "amount": {
644
+ "from": "instance",
645
+ "path": "refs.unwindRefund"
646
+ },
647
+ "currency": {
648
+ "from": "instance",
649
+ "path": "fields.currency"
650
+ },
651
+ "destinationAccountId": {
652
+ "from": "instance",
653
+ "path": "fields.buyerAccountId"
654
+ },
655
+ "productId": {
656
+ "from": "instance",
657
+ "path": "productId"
658
+ },
659
+ "sourceAccountId": {
660
+ "from": "instance",
661
+ "path": "refs.escrowAccountId"
662
+ }
663
+ },
664
+ "capture": {
665
+ "refundTransferId": "transferId"
666
+ },
667
+ "key": "transfer",
668
+ "operation": "internal_transfer.create"
669
+ }
670
+ ],
358
671
  "steps": [],
359
- "summary": "Cancel a quoted motor policy"
672
+ "summary": "Confirm a vehicle escrow cancellation"
360
673
  },
361
674
  "create": {
362
- "description": "Opens the policy quote against a vehicle subject. No money moves until the policy binds.",
675
+ "agentDescription": "Open the escrow and provision its account. No money moves. The price is admitted against the referenced motor policy's coverage cap, so an underinsured car is refused here rather than at funding.",
676
+ "description": "Opens the escrow and provisions its dedicated product escrow account. The price is admitted against the motor policy's coverage cap.",
363
677
  "moves": [],
364
- "steps": [],
365
- "summary": "Quote a motor policy"
366
- },
367
- "deny_claim": {
368
- "description": "Denies the claim and voids the pending coverage hold, releasing the reserved amount back to the insurer account.",
369
- "moves": [
678
+ "steps": [
370
679
  {
371
680
  "bind": {
372
- "reason": {
681
+ "currency": {
682
+ "from": "instance",
683
+ "path": "fields.currency"
684
+ },
685
+ "owner.id": {
686
+ "from": "instance",
687
+ "path": "productId"
688
+ },
689
+ "owner.type": {
373
690
  "from": "const",
374
- "value": "Motor claim denied"
691
+ "value": "product"
375
692
  },
376
- "transferId": {
693
+ "productId": {
377
694
  "from": "instance",
378
- "path": "refs.claimTransferId"
695
+ "path": "productId"
696
+ },
697
+ "role": {
698
+ "from": "const",
699
+ "value": "product_escrow"
379
700
  }
380
701
  },
381
- "key": "transfer",
382
- "operation": "internal_transfer.void"
702
+ "capture": {
703
+ "escrowAccountId": "accountId"
704
+ },
705
+ "operation": "account.escrow.provision"
383
706
  }
384
707
  ],
385
- "steps": [],
386
- "summary": "Deny a motor claim"
708
+ "summary": "Create a vehicle escrow"
387
709
  },
388
- "expire": {
389
- "description": "Ends coverage at term with no open claim. The collected premium stays with the insurer, so this is a pure lifecycle transition fired by the maintenance loop under the expiry due-condition.",
390
- "due": {
391
- "field": "expiresAt"
710
+ "fund": {
711
+ "agentDescription": "Move the full price from the buyer's account into escrow. This is the first money movement on the escrow, so get the buyer's confirmation. Only an escrow in created can fund.",
712
+ "description": "Moves the price from the buyer account into the escrow account.",
713
+ "effects": {
714
+ "moves": [
715
+ {
716
+ "signature": "moves.collection.pay_in",
717
+ "source": "moves[0]"
718
+ }
719
+ ]
392
720
  },
393
- "moves": [],
394
- "steps": [],
395
- "summary": "Expire a motor policy"
396
- },
397
- "open_claim": {
398
- "description": "Places a pending ledger hold for the coverage amount from the insurer account toward the policyholder. The insurer account must be capitalized to the coverage amount, or the hold is rejected for insufficient balance.",
399
721
  "moves": [
400
722
  {
401
723
  "bind": {
402
724
  "amount": {
403
725
  "from": "instance",
404
- "path": "fields.coverageAmount"
726
+ "path": "fields.price"
405
727
  },
406
728
  "currency": {
407
729
  "from": "instance",
@@ -409,45 +731,149 @@
409
731
  },
410
732
  "destinationAccountId": {
411
733
  "from": "instance",
412
- "path": "fields.policyholderAccountId"
734
+ "path": "refs.escrowAccountId"
735
+ },
736
+ "productId": {
737
+ "from": "instance",
738
+ "path": "productId"
413
739
  },
414
740
  "sourceAccountId": {
415
741
  "from": "instance",
416
- "path": "fields.insurerAccountId"
742
+ "path": "fields.buyerAccountId"
417
743
  }
418
744
  },
419
745
  "capture": {
420
- "claimTransferId": "transferId"
746
+ "fundTransferId": "transferId"
421
747
  },
422
748
  "key": "transfer",
423
- "operation": "internal_transfer.reserve"
749
+ "operation": "internal_transfer.create"
424
750
  }
425
751
  ],
752
+ "sandboxFailurePoint": "funding",
426
753
  "steps": [],
427
- "summary": "Open a motor claim"
754
+ "summary": "Fund a vehicle escrow"
428
755
  },
429
- "settle_claim": {
430
- "description": "Approves the claim and posts the held coverage amount, settling the payout into the policyholder account.",
431
- "eventName": "motor_policy.claim_settled",
756
+ "quote_cancel": {
757
+ "agentDescription": "Price the cancellation. No money moves. It computes the penalty from the schedule and parks the refund amount on the escrow for confirm_cancel to move. Only a funded escrow can be quoted, and once quoted, release is no longer reachable.",
758
+ "description": "Parks the funded escrow for cancellation and quotes the refund under the penalty schedule. No money moves; the confirm executes the refund.",
759
+ "effects": {
760
+ "holds": [
761
+ {
762
+ "signature": "holds.quote",
763
+ "source": "quote"
764
+ }
765
+ ],
766
+ "schedules": [
767
+ {
768
+ "signature": "schedules.expiry",
769
+ "source": "quote"
770
+ }
771
+ ]
772
+ },
773
+ "moves": [],
774
+ "quote": {
775
+ "baseField": "price",
776
+ "chargeRef": "unwindPenalty",
777
+ "charges": [
778
+ {
779
+ "bps": 500,
780
+ "withinOffset": "P7D"
781
+ },
782
+ {
783
+ "bps": 1000
784
+ }
785
+ ],
786
+ "expires": {
787
+ "offset": "PT15M"
788
+ },
789
+ "fixes": [
790
+ "buyerAccountId",
791
+ "price"
792
+ ],
793
+ "netDestinationField": "buyerAccountId",
794
+ "netRef": "unwindRefund"
795
+ },
796
+ "steps": [],
797
+ "summary": "Quote a vehicle escrow cancellation"
798
+ },
799
+ "release": {
800
+ "agentDescription": "Pay the full price from escrow to the seller and close the escrow as settled. Refused unless the motor policy is active at the moment of the call. This does not reverse: there is no clawback window after release.",
801
+ "description": "Clears the full price from the escrow account to the seller while the car's motor policy is active.",
802
+ "earnable": true,
803
+ "effects": {
804
+ "moves": [
805
+ {
806
+ "signature": "moves.transfer.internal",
807
+ "source": "moves[0]"
808
+ }
809
+ ],
810
+ "reads": [
811
+ {
812
+ "signature": "reads.requires_refs",
813
+ "source": "requiresRefs"
814
+ }
815
+ ]
816
+ },
432
817
  "moves": [
433
818
  {
434
819
  "bind": {
435
- "transferId": {
820
+ "amount": {
436
821
  "from": "instance",
437
- "path": "refs.claimTransferId"
822
+ "path": "fields.price"
823
+ },
824
+ "currency": {
825
+ "from": "instance",
826
+ "path": "fields.currency"
827
+ },
828
+ "destinationAccountId": {
829
+ "from": "instance",
830
+ "path": "fields.sellerAccountId"
831
+ },
832
+ "productId": {
833
+ "from": "instance",
834
+ "path": "productId"
835
+ },
836
+ "sourceAccountId": {
837
+ "from": "instance",
838
+ "path": "refs.escrowAccountId"
438
839
  }
439
840
  },
841
+ "capture": {
842
+ "releaseTransferId": "transferId"
843
+ },
440
844
  "key": "transfer",
441
- "operation": "internal_transfer.post"
845
+ "operation": "internal_transfer.create"
846
+ }
847
+ ],
848
+ "requiresRefs": [
849
+ {
850
+ "field": "policyId",
851
+ "statuses": [
852
+ "active"
853
+ ]
442
854
  }
443
855
  ],
856
+ "sandboxFailurePoint": "release",
444
857
  "steps": [],
445
- "summary": "Settle a motor claim"
858
+ "summary": "Release a vehicle escrow"
446
859
  }
447
- }
448
- },
449
- {
860
+ },
861
+ "agentDescription": "Reach for vehicle escrow when a car buyer's money has to be held until the vehicle's motor policy is confirmed active, then paid to the seller in full. Cancellation runs a quote first and a confirm second, and a penalty comes off the buyer's refund. The listing itself is a separate instrument.",
862
+ "callerParkedStates": {
863
+ "cancel_quoted": "a quoted cancellation moves money only when the caller confirms it",
864
+ "canceled": "the residual penalty is collected by the operator as a deliberate follow-up transfer",
865
+ "created": "funding the escrow is the buyer's act",
866
+ "funded": "releasing or quoting a cancellation over held value is a party decision"
867
+ },
450
868
  "description": "A vehicle escrow holds the buyer's funds in a dedicated product escrow account, then releases the full price to the seller while the car's motor policy is active. Before release it can unwind through a quote then a confirm that refunds the price to the buyer under a penalty schedule.",
869
+ "dials": [
870
+ {
871
+ "key": "cancellation_penalty",
872
+ "kind": "unwind_penalty",
873
+ "summary": "Replaces the tiered cancellation penalty schedule quotes price against.",
874
+ "title": "Cancellation penalty schedule"
875
+ }
876
+ ],
451
877
  "fields": {
452
878
  "buyerAccountId": {
453
879
  "description": "Hyperscale account ID",
@@ -539,6 +965,10 @@
539
965
  }
540
966
  }
541
967
  },
968
+ "nav": [
969
+ "Blueprints",
970
+ "Vehicle Escrows"
971
+ ],
542
972
  "parties": {
543
973
  "beneficiary": "sellerAccountId",
544
974
  "payer": "buyerAccountId"
@@ -556,22 +986,8 @@
556
986
  ]
557
987
  },
558
988
  "summary": "Insured-car escrow that clears to the seller while coverage is live.",
989
+ "templateId": "escrow",
559
990
  "title": "Vehicle escrow",
560
- "unwind": {
561
- "confirm": "confirm_cancel",
562
- "penalty": [
563
- {
564
- "bps": 500,
565
- "withinOffset": "P7D"
566
- },
567
- {
568
- "bps": 1000
569
- }
570
- ],
571
- "quote": "quote_cancel",
572
- "refundDestinationField": "buyerAccountId",
573
- "refundableField": "price"
574
- },
575
991
  "update": {
576
992
  "fields": [
577
993
  "memo"
@@ -579,203 +995,6 @@
579
995
  "states": [
580
996
  "created"
581
997
  ]
582
- },
583
- "verbs": {
584
- "collect_penalty": {
585
- "description": "Pays the computed retained cancellation penalty to the seller. Invoke only when confirm_cancel returns a positive refs.unwindPenalty.",
586
- "eventName": "vehicle_escrow.penalty_collected",
587
- "moves": [
588
- {
589
- "bind": {
590
- "amount": {
591
- "from": "instance",
592
- "path": "refs.unwindPenalty"
593
- },
594
- "currency": {
595
- "from": "instance",
596
- "path": "fields.currency"
597
- },
598
- "destinationAccountId": {
599
- "from": "instance",
600
- "path": "fields.sellerAccountId"
601
- },
602
- "sourceAccountId": {
603
- "from": "instance",
604
- "path": "refs.escrowAccountId"
605
- },
606
- "workspaceId": {
607
- "from": "instance",
608
- "path": "workspaceId"
609
- }
610
- },
611
- "capture": {
612
- "penaltyTransferId": "transferId"
613
- },
614
- "key": "transfer",
615
- "operation": "internal_transfer.create"
616
- }
617
- ],
618
- "steps": [],
619
- "summary": "Collect a vehicle cancellation penalty"
620
- },
621
- "confirm_cancel": {
622
- "description": "Unwinds the escrow, refunding the price minus the scheduled penalty to the buyer. The quote seeded that amount into refs.unwindRefund; this step moves it from the escrow account back to the buyer.",
623
- "moves": [
624
- {
625
- "bind": {
626
- "amount": {
627
- "from": "instance",
628
- "path": "refs.unwindRefund"
629
- },
630
- "currency": {
631
- "from": "instance",
632
- "path": "fields.currency"
633
- },
634
- "destinationAccountId": {
635
- "from": "instance",
636
- "path": "fields.buyerAccountId"
637
- },
638
- "sourceAccountId": {
639
- "from": "instance",
640
- "path": "refs.escrowAccountId"
641
- },
642
- "workspaceId": {
643
- "from": "instance",
644
- "path": "workspaceId"
645
- }
646
- },
647
- "capture": {
648
- "refundTransferId": "transferId"
649
- },
650
- "key": "transfer",
651
- "operation": "internal_transfer.create"
652
- }
653
- ],
654
- "steps": [],
655
- "summary": "Confirm a vehicle escrow cancellation"
656
- },
657
- "create": {
658
- "description": "Opens the escrow and provisions its dedicated product escrow account. The price is admitted against the motor policy's coverage cap.",
659
- "moves": [],
660
- "steps": [
661
- {
662
- "bind": {
663
- "currency": {
664
- "from": "instance",
665
- "path": "fields.currency"
666
- },
667
- "owner.id": {
668
- "from": "instance",
669
- "path": "workspaceId"
670
- },
671
- "owner.type": {
672
- "from": "const",
673
- "value": "workspace"
674
- },
675
- "role": {
676
- "from": "const",
677
- "value": "workspace_escrow"
678
- },
679
- "workspaceId": {
680
- "from": "instance",
681
- "path": "workspaceId"
682
- }
683
- },
684
- "capture": {
685
- "escrowAccountId": "accountId"
686
- },
687
- "operation": "account.escrow.provision"
688
- }
689
- ],
690
- "summary": "Create a vehicle escrow"
691
- },
692
- "fund": {
693
- "description": "Moves the price from the buyer account into the escrow account.",
694
- "moves": [
695
- {
696
- "bind": {
697
- "amount": {
698
- "from": "instance",
699
- "path": "fields.price"
700
- },
701
- "currency": {
702
- "from": "instance",
703
- "path": "fields.currency"
704
- },
705
- "destinationAccountId": {
706
- "from": "instance",
707
- "path": "refs.escrowAccountId"
708
- },
709
- "sourceAccountId": {
710
- "from": "instance",
711
- "path": "fields.buyerAccountId"
712
- },
713
- "workspaceId": {
714
- "from": "instance",
715
- "path": "workspaceId"
716
- }
717
- },
718
- "capture": {
719
- "fundTransferId": "transferId"
720
- },
721
- "key": "transfer",
722
- "operation": "internal_transfer.create"
723
- }
724
- ],
725
- "steps": [],
726
- "summary": "Fund a vehicle escrow"
727
- },
728
- "quote_cancel": {
729
- "description": "Parks the funded escrow for cancellation and quotes the refund under the penalty schedule. No money moves; the confirm executes the refund.",
730
- "moves": [],
731
- "steps": [],
732
- "summary": "Quote a vehicle escrow cancellation"
733
- },
734
- "release": {
735
- "description": "Clears the full price from the escrow account to the seller while the car's motor policy is active.",
736
- "earnable": true,
737
- "moves": [
738
- {
739
- "bind": {
740
- "amount": {
741
- "from": "instance",
742
- "path": "fields.price"
743
- },
744
- "currency": {
745
- "from": "instance",
746
- "path": "fields.currency"
747
- },
748
- "destinationAccountId": {
749
- "from": "instance",
750
- "path": "fields.sellerAccountId"
751
- },
752
- "sourceAccountId": {
753
- "from": "instance",
754
- "path": "refs.escrowAccountId"
755
- },
756
- "workspaceId": {
757
- "from": "instance",
758
- "path": "workspaceId"
759
- }
760
- },
761
- "capture": {
762
- "releaseTransferId": "transferId"
763
- },
764
- "key": "transfer",
765
- "operation": "internal_transfer.create"
766
- }
767
- ],
768
- "requiresRefs": [
769
- {
770
- "field": "policyId",
771
- "statuses": [
772
- "active"
773
- ]
774
- }
775
- ],
776
- "steps": [],
777
- "summary": "Release a vehicle escrow"
778
- }
779
998
  }
780
999
  }
781
1000
  ],