@hyperscale0/hsx 1.0.0 → 2.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 (166) hide show
  1. package/CHANGELOG.md +15 -4
  2. package/README.md +58 -38
  3. package/dist/src/ast.d.ts +4 -2
  4. package/dist/src/ast.d.ts.map +1 -1
  5. package/dist/src/cli.d.ts +1 -1
  6. package/dist/src/cli.js.map +1 -1
  7. package/dist/src/compile.d.ts +8 -2
  8. package/dist/src/compile.d.ts.map +1 -1
  9. package/dist/src/compile.js +8 -1
  10. package/dist/src/compile.js.map +1 -1
  11. package/dist/src/cost.d.ts +3 -1
  12. package/dist/src/cost.d.ts.map +1 -1
  13. package/dist/src/cost.js +124 -94
  14. package/dist/src/cost.js.map +1 -1
  15. package/dist/src/diagnostics.d.ts.map +1 -1
  16. package/dist/src/diagnostics.js +89 -5
  17. package/dist/src/diagnostics.js.map +1 -1
  18. package/dist/src/emit.d.ts +3 -1
  19. package/dist/src/emit.d.ts.map +1 -1
  20. package/dist/src/emit.js +27 -10
  21. package/dist/src/emit.js.map +1 -1
  22. package/dist/src/format.js.map +1 -1
  23. package/dist/src/index.d.ts +2 -2
  24. package/dist/src/index.d.ts.map +1 -1
  25. package/dist/src/index.js +1 -1
  26. package/dist/src/index.js.map +1 -1
  27. package/dist/src/ir.d.ts.map +1 -1
  28. package/dist/src/lex.d.ts.map +1 -1
  29. package/dist/src/lsp/server.js.map +1 -1
  30. package/dist/src/modules.js +7 -3
  31. package/dist/src/modules.js.map +1 -1
  32. package/dist/src/parse.js +3 -3
  33. package/dist/src/parse.js.map +1 -1
  34. package/dist/src/std-bundle.js +41 -41
  35. package/dist/src/std-bundle.js.map +1 -1
  36. package/dist/src/std-library.js +2 -2
  37. package/dist/src/typecheck.d.ts.map +1 -1
  38. package/dist/src/typecheck.js +859 -156
  39. package/dist/src/typecheck.js.map +1 -1
  40. package/dist/src/version.d.ts +1 -1
  41. package/dist/src/version.js +1 -1
  42. package/docs/README.md +10 -1
  43. package/docs/assets/hsx.svg +18 -0
  44. package/docs/guide/01-first-program.md +1 -1
  45. package/docs/guide/02-money.md +1 -1
  46. package/docs/guide/03-instruments.md +44 -3
  47. package/docs/guide/04-lifecycles.md +18 -3
  48. package/docs/guide/05-fees-and-splits.md +2 -2
  49. package/docs/guide/06-schedules.md +1 -1
  50. package/docs/guide/07-composition.md +8 -2
  51. package/docs/guide/08-writing-a-module.md +13 -2
  52. package/docs/guide/09-cost.md +3 -3
  53. package/docs/guide/10-diagnostics.md +1 -1
  54. package/docs/llms-full.txt +453 -190
  55. package/docs/llms.txt +2 -2
  56. package/docs/playground.md +4 -3
  57. package/docs/reference/cli.md +2 -2
  58. package/docs/reference/diagnostics.md +223 -37
  59. package/docs/reference/grammar.md +2 -2
  60. package/docs/reference/std/advance.md +2 -2
  61. package/docs/reference/std/cancellable_booking.md +10 -10
  62. package/docs/reference/std/captured_payment.md +14 -14
  63. package/docs/reference/std/conditional_disbursement.md +3 -3
  64. package/docs/reference/std/credit_facility.md +2 -2
  65. package/docs/reference/std/held_payment.md +37 -33
  66. package/docs/reference/std/instant_transfer.md +8 -8
  67. package/docs/reference/std/metered.md +2 -2
  68. package/docs/reference/std/pooled_split.md +2 -2
  69. package/docs/reference/std/premium_forward.md +13 -13
  70. package/docs/reference/std/reconciled_payout.md +2 -2
  71. package/docs/reference/std/recurring_collection.md +2 -2
  72. package/docs/reference/std/rotating_pool.md +2 -2
  73. package/docs/reference/std/scheduled.md +18 -18
  74. package/docs/reference/std/security_deposit.md +14 -14
  75. package/docs/reference/std/settlement_batch.md +2 -2
  76. package/docs/reference/std/swap.md +18 -18
  77. package/docs/reference/std/threshold_pool.md +2 -2
  78. package/docs/reference/std/weighted_distribution.md +2 -2
  79. package/docs/reference/types.md +1 -1
  80. package/docs/reference/udl-output.md +1 -1
  81. package/examples/01-first-program/README.md +1 -1
  82. package/examples/01-first-program/tip-jar.hsx +1 -1
  83. package/examples/02-imports-and-archetypes/photo-booth.hsx +1 -1
  84. package/examples/03-diagnostics/corner-shop-fixed.hsx +1 -1
  85. package/examples/03-diagnostics/corner-shop.hsx +1 -1
  86. package/examples/04-complete-product/study-hall.hsx +1 -1
  87. package/examples/05-watch-club/README.md +0 -3
  88. package/examples/05-watch-club/watch-club.hsx +1 -1
  89. package/examples/README.md +35 -4
  90. package/examples/advance/advance.hsx +1 -1
  91. package/examples/advance/advance.udl +10 -5
  92. package/examples/cancellable_booking/cancellable_booking.hsx +1 -1
  93. package/examples/cancellable_booking/cancellable_booking.udl +3 -1
  94. package/examples/captured_payment/captured_payment.hsx +1 -1
  95. package/examples/captured_payment/captured_payment.udl +4 -1
  96. package/examples/conditional_disbursement/conditional_disbursement.hsx +1 -1
  97. package/examples/conditional_disbursement/conditional_disbursement.udl +5 -3
  98. package/examples/cost-table.json +1602 -382
  99. package/examples/credit_facility/credit_facility.hsx +1 -1
  100. package/examples/credit_facility/credit_facility.udl +16 -9
  101. package/examples/held_payment/held_payment.hsx +1 -1
  102. package/examples/held_payment/held_payment.udl +38 -3
  103. package/examples/instant_transfer/instant_transfer.hsx +1 -1
  104. package/examples/instant_transfer/instant_transfer.udl +11 -5
  105. package/examples/metered/metered.hsx +1 -1
  106. package/examples/metered/metered.udl +4 -2
  107. package/examples/pooled_split/pooled_split.hsx +1 -1
  108. package/examples/pooled_split/pooled_split.udl +6 -3
  109. package/examples/premium_forward/premium_forward.hsx +1 -1
  110. package/examples/premium_forward/premium_forward.udl +10 -4
  111. package/examples/reconciled_payout/reconciled_payout.hsx +1 -1
  112. package/examples/reconciled_payout/reconciled_payout.udl +4 -2
  113. package/examples/recurring_collection/recurring_collection.hsx +1 -1
  114. package/examples/recurring_collection/recurring_collection.udl +12 -7
  115. package/examples/rotating_pool/rotating_pool.hsx +1 -1
  116. package/examples/rotating_pool/rotating_pool.udl +14 -4
  117. package/examples/scheduled/scheduled.hsx +1 -1
  118. package/examples/scheduled/scheduled.udl +9 -4
  119. package/examples/security_deposit/security_deposit.hsx +1 -1
  120. package/examples/security_deposit/security_deposit.udl +17 -3
  121. package/examples/settlement_batch/settlement_batch.hsx +1 -1
  122. package/examples/swap/swap.hsx +1 -1
  123. package/examples/swap/swap.udl +12 -6
  124. package/examples/threshold_pool/threshold_pool.hsx +1 -1
  125. package/examples/threshold_pool/threshold_pool.udl +4 -2
  126. package/examples/weighted_distribution/weighted_distribution.hsx +1 -1
  127. package/examples/weighted_distribution/weighted_distribution.udl +4 -2
  128. package/package.json +6 -5
  129. package/skills/hsx/SKILL.md +26 -26
  130. package/src/ast.ts +4 -2
  131. package/src/cli.ts +4 -4
  132. package/src/compile.ts +14 -3
  133. package/src/cost.ts +176 -104
  134. package/src/diagnostics.ts +89 -4
  135. package/src/emit.ts +32 -18
  136. package/src/index.ts +2 -1
  137. package/src/modules.ts +9 -3
  138. package/src/parse.ts +3 -3
  139. package/src/std-bundle.ts +41 -41
  140. package/src/std-library.ts +2 -2
  141. package/src/typecheck.ts +1147 -235
  142. package/src/version.ts +1 -1
  143. package/std/SEMANTICS.md +12 -3
  144. package/std/{settlements → money_flows}/advance.hsx +1 -1
  145. package/std/{settlements → money_flows}/cancellable_booking.hsx +4 -3
  146. package/std/{settlements → money_flows}/captured_payment.hsx +35 -6
  147. package/std/{settlements → money_flows}/conditional_disbursement.hsx +17 -16
  148. package/std/{settlements → money_flows}/credit_facility.hsx +1 -2
  149. package/std/{settlements → money_flows}/held_payment.hsx +59 -25
  150. package/std/money_flows/index.hsx +3 -0
  151. package/std/{settlements → money_flows}/instant_transfer.hsx +2 -3
  152. package/std/{settlements → money_flows}/metered.hsx +1 -1
  153. package/std/{settlements → money_flows}/pooled_split.hsx +3 -3
  154. package/std/{settlements → money_flows}/premium_forward.hsx +22 -23
  155. package/std/{settlements → money_flows}/reconciled_payout.hsx +3 -3
  156. package/std/{settlements → money_flows}/recurring_collection.hsx +1 -1
  157. package/std/{settlements → money_flows}/rotating_pool.hsx +8 -6
  158. package/std/{settlements → money_flows}/scheduled.hsx +22 -27
  159. package/std/{settlements → money_flows}/security_deposit.hsx +38 -14
  160. package/std/{settlements → money_flows}/settlement_batch.hsx +3 -3
  161. package/std/{settlements → money_flows}/swap.hsx +41 -16
  162. package/std/{settlements → money_flows}/threshold_pool.hsx +5 -5
  163. package/std/{settlements → money_flows}/weighted_distribution.hsx +6 -6
  164. package/docs/sessions/2026-09-02-two-instruments.hsx +0 -21
  165. package/docs/sessions/2026-09-02-two-instruments.md +0 -41
  166. package/std/settlements/index.hsx +0 -3
package/src/version.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Package version reported by the command line. */
2
- export const HSX_VERSION = "1.0.0";
2
+ export const HSX_VERSION = "2.0.0";
3
3
 
4
4
  /** Canonical UDL contract version emitted by this compiler. */
5
5
  export const HSX_TARGET_UDL_VERSION = 1;
package/std/SEMANTICS.md CHANGED
@@ -1,9 +1,8 @@
1
1
  # Settlement semantics frozen before legacy teardown
2
2
 
3
3
  This note records the behavior that the former archetype branches emitted on
4
- 2026-09-01. The 48 canonical documents under
5
- `test/fixtures/general-path-oracle/` are the byte-level authority. This note is
6
- the human porting index.
4
+ 2026-09-01. The in-place compiler family specs under `test/` are the semantic
5
+ authority. This note is the human porting index.
7
6
 
8
7
  ## Direct and held payments
9
8
 
@@ -34,6 +33,12 @@ the human porting index.
34
33
  amount. Void and expiry release the reserve. Correction and external reversal
35
34
  use their declared ports and windows. Derived fees use the same floor,
36
35
  partition, bearer, and position rules as direct payment.
36
+ - `cancellable_booking` holds the booking amount in escrow away from guest and
37
+ host until the booking ends or is cancelled. Cancellation quotes a penalty
38
+ against the time remaining before the start date, frozen with offer life.
39
+ Lifecycle moves through `created`, `held`, `cancellation_quoted`, `canceled`,
40
+ `settled`, and `completed`. Penalty retention moves penalty funds to host and
41
+ remainder to guest; completion releases full amount to host.
37
42
  - `premium_forward` holds premium pieces, then forwards the net pieces after a
38
43
  bind decision. It can abandon unbound custody. Policy reference, renewal due,
39
44
  endorsement evidence, and lapse actions appear only when declared. Its fee
@@ -128,3 +133,7 @@ the human porting index.
128
133
  beneficiary for that cycle. The final close requires the escrow account to be
129
134
  drained. Its referenced-membership form delegates those member cycles to a
130
135
  published membership instrument and emits only the parent.
136
+ - `reconciled_payout` instructs an external payout with a tolerance dial,
137
+ waits for a matching bank debit statement line, and treats unmatched amounts
138
+ at the settle date as break rows. Lifecycle moves through `created`,
139
+ `instructed`, and `settled`.
@@ -1,4 +1,4 @@
1
- module std.settlements.advance
1
+ module std.money_flows.advance
2
2
 
3
3
  export instrument advance<C>(
4
4
  funder: party,
@@ -1,4 +1,4 @@
1
- module std.settlements.cancellable_booking
1
+ module std.money_flows.cancellable_booking
2
2
 
3
3
  export instrument cancellable_booking<C>(
4
4
  guest: party,
@@ -19,8 +19,8 @@ export instrument cancellable_booking<C>(
19
19
  amount { type: money<C>; description: concat("Booking price in minor units, held away from both the ", words(guest), " and the ", words(host), " until the booking ends"); }
20
20
  currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
21
21
  starts_at { type: date; description: "When the booking starts; the cancellation penalty is priced against the time left before it"; }
22
- [guest_account_field] { type: account<C>; description: concat("The ", words(guest), " account the booking is taken from and the refund returns to"); pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; [reference_filter_key]: { column: role; values: [customer_balance]; }; }
23
- [host_account_field] { type: account<C>; description: concat("The ", words(host), " account the booking and any penalty end up in"); pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; [reference_filter_key]: { column: role; values: [customer_balance]; }; }
22
+ [guest_account_field] { type: account<C>; description: concat("The ", words(guest), " account the booking is taken from and the refund returns to"); [reference_filter_key]: { column: role; values: [customer_balance]; }; }
23
+ [host_account_field] { type: account<C>; description: concat("The ", words(host), " account the booking and any penalty end up in"); [reference_filter_key]: { column: role; values: [customer_balance]; }; }
24
24
  }
25
25
  lifecycle {
26
26
  states created held cancellation_quoted canceled settled completed;
@@ -48,6 +48,7 @@ export instrument cancellable_booking<C>(
48
48
  agent_description: concat("Move the whole booking price out of the ", words(guest), " account into the holding account. This moves money. Only a created booking takes. Neither party can spend the money until the booking completes or a cancellation is confirmed.");
49
49
  summary: concat("Take the booking price from the ", words(guest), " into the holding account");
50
50
  moves: [{ key: booking; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", amount); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.bookingHoldingAccountId; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", guest_account_field); }; }; capture: { bookingTransferId: transferId; }; }];
51
+ sandbox_failure_point: funding;
51
52
  steps: [];
52
53
  }
53
54
  action complete {
@@ -1,4 +1,4 @@
1
- module std.settlements.captured_payment
1
+ module std.money_flows.captured_payment
2
2
 
3
3
  export instrument captured_payment<C>(payer: party, payee: party, amount: money<C>, reserve_until: date, correction: condition, external_reversal: condition, capture_mode: text, correction_mode: text, negative_position: text, timeout: text, derived_amount: optional<block>) {
4
4
  let(correction_name): correction;
@@ -22,7 +22,6 @@ export instrument captured_payment<C>(payer: party, payee: party, amount: money<
22
22
  platformAccountId {
23
23
  type: account<C>;
24
24
  description: "The product fee collection account";
25
- pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$";
26
25
  [fee_collection_key]: true;
27
26
  [reference_filter_key]: { column: role; values: [customer_balance, product_revenue]; };
28
27
  }
@@ -69,6 +68,7 @@ export instrument captured_payment<C>(payer: party, payee: party, amount: money<
69
68
  agent_description: "Reserve the {amount} against the {payer} in the {payee}'s favor until {reserve_until}. The payment must be created. The money is held, not paid: capture and settle post it, void and expiry release it.";
70
69
  summary: "Reserve the {amount} until {reserve_until}";
71
70
  moves: [{ key: reservation; amount: amount; from: payer; to: beneficiary; operation: reserve; }];
71
+ sandbox_failure_point: funding;
72
72
  steps: [];
73
73
  }
74
74
  action capture {
@@ -77,6 +77,7 @@ export instrument captured_payment<C>(payer: party, payee: party, amount: money<
77
77
  deadline { field: reserve_until; };
78
78
  input { type: object; additional_properties: false; properties: { captureAmount: { description: "Positive partial capture amount in minor units"; pattern: "^[1-9][0-9]{0,17}$"; type: string; }; }; required: [captureAmount]; }
79
79
  moves: [{ key: post; operation: internal_transfer.post; bind: { amount: { from: input; path: captureAmount; }; currency: { from: instance; path: fields.currency; }; postMode: { from: const; value: partial_only; }; transferId: { from: instance; path: refs.authorizeReservationTransferId; }; }; capture: { capturedAmount: postedAmount; }; }];
80
+ sandbox_failure_point: "release";
80
81
  steps: [];
81
82
  }
82
83
  action capture_more {
@@ -122,7 +123,22 @@ export instrument captured_payment<C>(payer: party, payee: party, amount: money<
122
123
  let(correction_capture): camel(concat(correction_name, "_transfer_transfer_id"));
123
124
  agent_description: "Return the whole captured amount to the {payer} after the payment settled. Only a settled payment corrects, and only the party the product authorized may call it. The money moves back and there is no undo.";
124
125
  summary: "Return the full captured amount on payee correction";
125
- port { allowed_parties: [if_eq(at(correction_allowed, 1), payee, "beneficiary", "payer")]; };
126
+ when(correction_fields) {
127
+ input {
128
+ type: object;
129
+ additional_properties: false;
130
+ properties {
131
+ for input_name in keys(correction_fields) {
132
+ [input_name]: get(correction_fields, input_name);
133
+ }
134
+ }
135
+ required: keys(correction_fields);
136
+ }
137
+ capture_input: {
138
+ for item in keys(correction_fields) { [item]: item; }
139
+ };
140
+ }
141
+ port { allowed_parties: correction_allowed; };
126
142
  moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: refs.capturedAmount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payer, "AccountId"); }; [metadata_clawback]: { from: instance; path: refs.authorizeReservationTransferId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: correction_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payee, "AccountId"); }; }; capture: { [correction_capture]: transferId; }; }];
127
143
  steps: [];
128
144
  }
@@ -134,10 +150,23 @@ export instrument captured_payment<C>(payer: party, payee: party, amount: money<
134
150
  let(reversal_capture): camel(concat(reversal_name, "_transfer_transfer_id"));
135
151
  agent_description: "Return the whole captured amount to the {payer} on an external reversal. The payment must be settled and the call must land before reversalUntil. The caller passes externalReference, which stays in the receipt. There is no undo.";
136
152
  summary: "Return the full captured amount on an external reversal";
137
- capture(input) { externalReference: externalReference; };
138
153
  deadline { field: reversalUntil; };
139
- input { type: object; additional_properties: false; properties: { externalReference: { description: "Required decision reference retained in the operation receipt"; max_length: 180; min_length: 1; type: string; }; }; required: [externalReference]; }
140
- port { allowed_parties: [if_eq(at(external_reversal_allowed, 1), payee, "beneficiary", "payer")]; };
154
+ when(external_reversal_fields) {
155
+ capture_input: {
156
+ for item in keys(external_reversal_fields) { [item]: item; }
157
+ };
158
+ input {
159
+ type: object;
160
+ additional_properties: false;
161
+ properties {
162
+ for input_name in keys(external_reversal_fields) {
163
+ [input_name]: get(external_reversal_fields, input_name);
164
+ }
165
+ }
166
+ required: keys(external_reversal_fields);
167
+ }
168
+ }
169
+ port { allowed_parties: external_reversal_allowed; };
141
170
  moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: refs.capturedAmount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payer, "AccountId"); }; [metadata_clawback]: { from: instance; path: refs.authorizeReservationTransferId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: reversal_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payee, "AccountId"); }; }; capture: { [reversal_capture]: transferId; }; }];
142
171
  steps: [];
143
172
  }
@@ -1,4 +1,4 @@
1
- module std.settlements.conditional_disbursement
1
+ module std.money_flows.conditional_disbursement
2
2
 
3
3
  export instrument conditional_disbursement<C>(
4
4
  source: party,
@@ -48,21 +48,22 @@ export instrument conditional_disbursement<C>(
48
48
  action deny {
49
49
  agent_description: "Refuse the whole disbursement without moving money. Every child amount under it must still be unapproved, so call this before any approval lands. The caller passes evidenceReference, which stays in the receipt. Denial is final.";
50
50
  summary: "Record a denial without moving money";
51
- input: {
52
- additional_properties: false;
53
- properties: {
54
- evidence_reference: {
55
- description: "Required decision reference retained in the operation receipt";
56
- max_length: 180;
57
- min_length: 1;
58
- type: "string";
59
- };
51
+ when(decision_fields) {
52
+ input {
53
+ type: object;
54
+ additional_properties: false;
55
+ properties {
56
+ for input_name in keys(decision_fields) {
57
+ [input_name]: get(decision_fields, input_name);
58
+ }
59
+ }
60
+ required: keys(decision_fields);
61
+ }
62
+ capture_input: {
63
+ for item in keys(decision_fields) { [item]: item; }
60
64
  };
61
- required: [evidenceReference];
62
- type: "object";
63
- };
64
- capture_input: { decision_evidence_reference: evidenceReference; };
65
- port: { allowed_parties: [payer]; };
65
+ }
66
+ port { allowed_parties: decision_allowed; };
66
67
  requires aggregate: {
67
68
  check: { kind: all_in; };
68
69
  instrument_id: child_id;
@@ -144,7 +145,7 @@ export instrument conditional_disbursement<C>(
144
145
  type: "object";
145
146
  };
146
147
  capture_input: { decision_evidence_reference: evidenceReference; };
147
- port: { allowed_parties: [payer]; };
148
+ port: { allowed_parties: decision_allowed; };
148
149
  requires exposure: {
149
150
  amount_field: child_amount;
150
151
  anchor_field: parent_field;
@@ -1,4 +1,4 @@
1
- module std.settlements.credit_facility
1
+ module std.money_flows.credit_facility
2
2
 
3
3
  export instrument credit_facility<C>(
4
4
  lender: party,
@@ -32,7 +32,6 @@ export instrument credit_facility<C>(
32
32
  [borrower_field] {
33
33
  type: account<C>;
34
34
  description: "The {borrower} account";
35
- pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$";
36
35
  [reference_filter]: {
37
36
  column: role;
38
37
  values: [customer_balance];
@@ -1,13 +1,8 @@
1
- module std.settlements.held_payment
1
+ module std.money_flows.held_payment
2
2
 
3
3
  export instrument held_payment<C>(payer: party, payee: party, amount: money<C>, release: condition, fees: optional<block>, on_cancel: optional<block>, derived_amount: optional<block>, release_to: optional<party>, whole_amount: optional<block>, release_action: optional<text>, whole_fee: optional<money<C>>, reference: optional<text>, upstream: optional<ref>, id_prefix_override: optional<text>) {
4
4
  let(release_name): release;
5
- let(release_actor): at(release_allowed, 1);
6
- let(release_actor_2): at(release_allowed, 2);
7
- let(release_multiple): lt(1, len(release_allowed));
8
- let(release_role): if_eq(release_actor, payer, "payer", "beneficiary");
9
- let(release_role_2): if_eq(release_actor_2, payer, "payer", "beneficiary");
10
- let(release_actor_text): if_eq(len(release_allowed), 2, concat(words(release_actor), " or ", words(release_actor_2)), words(release_actor));
5
+ let(release_actor_text): if_eq(len(release_allowed), 1, words(at(release_allowed, 1)), concat("a party allowed by ", release));
11
6
  let(payer_account_field): camel(concat(payer, "_account_id"));
12
7
  let(payee_account_field): camel(concat(payee, "_account_id"));
13
8
  let(release_to_account_field): camel(concat(release_to, "_account_id"));
@@ -54,12 +49,11 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
54
49
  let(fee_collection_key): "x-hyperscale-fee-collection-port";
55
50
  let(reference_filter_key): "x-hyperscale-reference-filter";
56
51
  when(release_to) {
57
- [release_to_account_field] { type: account<C>; description: concat("The ", words(release_to), " account"); pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; [reference_filter_key]: { column: role; values: [customer_balance]; }; }
52
+ [release_to_account_field] { type: account<C>; description: concat("The ", words(release_to), " account"); [reference_filter_key]: { column: role; values: [customer_balance]; }; }
58
53
  }
59
54
  platformAccountId {
60
55
  type: account<C>;
61
56
  description: "The product fee collection account";
62
- pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$";
63
57
  [fee_collection_key]: true;
64
58
  [reference_filter_key]: { column: role; values: [customer_balance, product_revenue]; };
65
59
  }
@@ -107,18 +101,18 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
107
101
  lifecycle {
108
102
  when_not(payee_fee) {
109
103
  when_not(cancel_splits) {
110
- states created funded released;
104
+ states created funded disputed released;
111
105
  when(on_cancel) { states cancelled; }
112
106
  states abandoned;
113
107
  }
114
- when(cancel_splits) { states created funding_1 funded releasing_1 released cancelling_1 cancelled abandoned; }
108
+ when(cancel_splits) { states created funding_1 funded disputed releasing_1 released cancelling_1 cancelled abandoned; }
115
109
  }
116
110
  when(payee_fee) {
117
- when_not(on_cancel) { states created funding_1 funded releasing_1 released abandoned; }
111
+ when_not(on_cancel) { states created funding_1 funded disputed releasing_1 released abandoned; }
118
112
  when(on_cancel) {
119
- when_eq(payer_fee_kind, percent) { states created funding_1 funding_2 funding_3 funded releasing_1 releasing_2 released cancelling_1 cancelling_2 cancelled abandoning_1 abandoned abandoning_2; }
120
- when_eq(payer_fee_kind, none) { states created funding_1 funding_2 funded releasing_1 releasing_2 released cancelling_1 cancelling_2 cancelled abandoning_1 abandoned; }
121
- when_eq(payer_fee_kind, binding) { states created funding_1 funding_2 funded releasing_1 releasing_2 released cancelling_1 cancelling_2 cancelled abandoning_1 abandoned; }
113
+ when_eq(payer_fee_kind, percent) { states created funding_1 funding_2 funding_3 funded disputed releasing_1 releasing_2 released cancelling_1 cancelling_2 cancelled abandoning_1 abandoned abandoning_2; }
114
+ when_eq(payer_fee_kind, none) { states created funding_1 funding_2 funded disputed releasing_1 releasing_2 released cancelling_1 cancelling_2 cancelled abandoning_1 abandoned; }
115
+ when_eq(payer_fee_kind, binding) { states created funding_1 funding_2 funded disputed releasing_1 releasing_2 released cancelling_1 cancelling_2 cancelled abandoning_1 abandoned; }
122
116
  }
123
117
  }
124
118
  initial created;
@@ -172,6 +166,8 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
172
166
  on unfund_piece_1: funding_1 | abandoning_1 -> abandoned;
173
167
  }
174
168
  }
169
+ on dispute: funded -> disputed;
170
+ on resume: disputed -> funded;
175
171
  on abandon: created -> abandoned;
176
172
  }
177
173
  parties {
@@ -193,6 +189,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
193
189
  agent_description: concat("Move piece 2 of the split amount from the ", words(payer), " into escrow. Piece 1 must be in escrow first. It debits the ", words(payer), " for real, and the hold is still short of funded until piece 3 lands.");
194
190
  summary: "Fund piece 2 of the held amount into escrow";
195
191
  moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece2Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece2TransferTransferId: transferId; }; }];
192
+ sandbox_failure_point: funding;
196
193
  steps: [];
197
194
  }
198
195
  action fund_piece_3 {
@@ -202,6 +199,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
202
199
  agent_description: concat("Move piece 3 of the split amount from the ", words(payer), " into escrow. Pieces 1 and 2 must be in escrow first. It debits the ", words(payer), " for real. Where the product charges a percentage payer fee, one more call collects that fee before the hold counts as funded.");
203
200
  summary: "Fund piece 3 of the held amount into escrow";
204
201
  moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece3Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_3; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece3TransferTransferId: transferId; }; }];
202
+ sandbox_failure_point: funding;
205
203
  steps: [];
206
204
  }
207
205
  when_eq(payer_fee_kind, percent) {
@@ -346,6 +344,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
346
344
  }
347
345
  }
348
346
  when_eq(payer_fee_kind, binding) { moves: [{ key: service_fee; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", payer_fee); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: fields.platformAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_1; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece1ServiceFeeTransferId: transferId; }; }]; }
347
+ sandbox_failure_point: funding;
349
348
  steps: [];
350
349
  }
351
350
  action [release_name] {
@@ -362,14 +361,13 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
362
361
  additional_properties: false;
363
362
  properties {
364
363
  for input_name in keys(release_fields) {
365
- [input_name] { type: string; description: "Required decision reference retained in the operation receipt"; max_length: 180; min_length: 1; }
364
+ [input_name]: get(release_fields, input_name);
366
365
  }
367
366
  }
368
367
  required: keys(release_fields);
369
368
  }
370
369
  }
371
- when_not(release_multiple) { port { allowed_parties: [release_role]; }; }
372
- when(release_multiple) { port { allowed_parties: [release_role, release_role_2]; }; }
370
+ port { allowed_parties: release_allowed; };
373
371
  when_not(payee_fee) {
374
372
  when_not(cancel_splits) {
375
373
  when_not(release_to) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", amount); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: release_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { [release_capture]: transferId; }; }]; }
@@ -378,6 +376,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
378
376
  when(cancel_splits) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece1Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: release_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { [release_capture]: transferId; }; }]; }
379
377
  }
380
378
  when(payee_fee) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece1Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: release_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { [release_capture]: transferId; }; }]; }
379
+ sandbox_failure_point: "release";
381
380
  steps: [];
382
381
  }
383
382
  when(release_deadline) {
@@ -422,6 +421,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
422
421
  summary: "Fund piece 2 of the held amount into escrow";
423
422
  when_eq(payee_fee_kind, percent) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece2Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece2TransferTransferId: transferId; }; }]; }
424
423
  when_eq(payee_fee_kind, binding) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", payee_fee); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece2TransferTransferId: transferId; }; }]; }
424
+ sandbox_failure_point: funding;
425
425
  steps: [];
426
426
  }
427
427
  action release_piece_2 {
@@ -454,6 +454,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
454
454
  agent_description: concat("Move piece 2 of the split amount from the ", words(payer), " into escrow, which completes funding. Piece 1 must be in escrow first. It debits the ", words(payer), " for real, and the whole held amount then sits in escrow.");
455
455
  summary: "Fund piece 2 of the held amount into escrow";
456
456
  moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece2Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece2TransferTransferId: transferId; }; }];
457
+ sandbox_failure_point: funding;
457
458
  steps: [];
458
459
  }
459
460
  action release_piece_2 {
@@ -492,6 +493,20 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
492
493
  moves: [];
493
494
  steps: [];
494
495
  }
496
+ action dispute {
497
+ agent_description: concat("Freeze a funded ", words(instrument), " so neither release nor refund can run until the parties settle the disagreement. No money moves and the whole balance stays put. Only a funded ", words(instrument), " disputes.");
498
+ description: concat("Freezes funded ", words(instrument), ", preserving the entire balance while the parties decide what happens next.");
499
+ summary: concat("Dispute a ", words(instrument), " settlement");
500
+ moves: [];
501
+ steps: [];
502
+ }
503
+ action resume {
504
+ agent_description: concat("Lift the dispute hold and put the ", words(instrument), " back where it was, funded and ready to release. No money moves and the balance was never touched. Only a disputed ", words(instrument), " resumes. Use this when the parties settled without a refund.");
505
+ description: concat("Ends the dispute hold without moving money and returns the fully funded ", words(instrument), " to its first milestone.");
506
+ summary: concat("Resume a ", words(instrument), " settlement");
507
+ moves: [];
508
+ steps: [];
509
+ }
495
510
  }
496
511
  when(whole_amount) {
497
512
  let(fee_collection_key): "x-hyperscale-fee-collection-port";
@@ -502,26 +517,31 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
502
517
  summary: concat("Escrow-held payment from ", words(payer), " to ", words(payee));
503
518
  description: concat("Held payment: the ", words(payer), " funds ", amount, " into this settlement's own escrow; ", release_actor_text, " confirms through ", release, " to release");
504
519
  distinct_parties: true;
505
- caller_parked_states: { created: "The tenant either funds the hold or abandons it before any money moves."; };
520
+ caller_parked_states: {
521
+ created: "The tenant either funds the hold or abandons it before any money moves.";
522
+ disputed: "Dispute resolution is a human judgment; the parties resume the hold to continue.";
523
+ };
506
524
  fields {
507
525
  currency { type: text; description: "One currency for the hold and its fee"; const: C; }
508
526
  amount { type: money<C>; description: "Amount held in custody, in {C} minor units"; }
509
- [payee]AccountId { type: account<C>; description: "Party the hold protects and pays on release"; pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; [reference_filter_key]: { column: role; values: [customer_balance]; }; }
510
- [payer]AccountId { type: account<C>; description: "Party whose money is held until the hold resolves"; pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; [reference_filter_key]: { column: role; values: [customer_balance]; }; }
511
- platformAccountId { type: account<C>; description: "Product revenue or customer balance account collecting the custody fee"; pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; [fee_collection_key]: true; [reference_filter_key]: { column: role; values: [customer_balance, product_revenue]; }; }
527
+ [payee]AccountId { type: account<C>; description: "Party the hold protects and pays on release"; [reference_filter_key]: { column: role; values: [customer_balance]; }; }
528
+ [payer]AccountId { type: account<C>; description: "Party whose money is held until the hold resolves"; [reference_filter_key]: { column: role; values: [customer_balance]; }; }
529
+ platformAccountId { type: account<C>; description: "Product revenue or customer balance account collecting the custody fee"; [fee_collection_key]: true; [reference_filter_key]: { column: role; values: [customer_balance, product_revenue]; }; }
512
530
  when(reference) { reference { type: text; description: "The tenant's own identifier for what is held: invoice number, project code, contract reference"; optional: true; max_length: 180; min_length: 1; } }
513
531
  [release_deadline] { type: date; description: "Date the hold releases to the payee on its own; fixed at creation and the cutoff for both the release port and cancellation"; }
514
532
  whole_fee { type: money<C>; description: "Custody fee charged on top at funding and never placed in escrow"; }
515
533
  when(upstream) { upstream { type: ref<instrument>; description: "Hold this one is carved out of, when a chain of holds passes the same money down"; optional: true; pattern: concat("^", id_prefix_override, "_(sandbox|live)_[a-z0-9]{8,64}$"); } }
516
534
  }
517
535
  lifecycle {
518
- states created funded released cancelled abandoned;
536
+ states created funded disputed released cancelled abandoned;
519
537
  initial created;
520
538
  on abandon: created -> abandoned;
521
539
  on cancel: funded -> cancelled;
540
+ on dispute: funded -> disputed;
522
541
  on fund: created -> funded;
523
542
  on [release_action]: funded -> released;
524
543
  on release_on_deadline: funded -> released;
544
+ on resume: disputed -> funded;
525
545
  }
526
546
  parties { beneficiary: payee_account_field; payer: payer_account_field; }
527
547
  required: [payer_account_field, payee_account_field, platformAccountId, amount, whole_fee_field, currency, release_deadline];
@@ -544,10 +564,10 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
544
564
  steps: [];
545
565
  }
546
566
  action [release_action] {
547
- agent_description: concat("Pay the ", words(payee), " the whole ", amount, " out of escrow ahead of ", release_deadline, ". This moves money and does not reverse. Only a funded hold releases, the caller must act as the ", release_actor_text, ", and the call is refused once ", release_deadline, " passes. The custody fee stays posted.");
567
+ agent_description: concat("Pay the ", words(payee), " the whole ", amount, " out of escrow ahead of ", release_deadline, ". This moves money and does not reverse. Only a funded hold releases, the caller must act as ", words(at(release_allowed, 1)), ", and the call is refused once ", release_deadline, " passes. The custody fee stays posted.");
548
568
  summary: concat("Release the hold to the ", words(payee), " early");
549
569
  deadline: { field: release_deadline; };
550
- port: { allowed_parties: [release_role]; };
570
+ port: { allowed_parties: [at(release_allowed, 1)]; };
551
571
  moves: [{ key: release_action; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", amount); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; "metadata.instrumentId": { from: const; value: instrument; }; "metadata.instrumentInstanceId": { from: instance; path: instrumentInstanceId; }; "metadata.phase": { from: const; value: release_action; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { releaseTransferId: transferId; }; }];
552
572
  sandbox_failure_point: "release";
553
573
  steps: [];
@@ -574,5 +594,19 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
574
594
  requires_drained: { path: refs.escrowAccountId; };
575
595
  steps: [];
576
596
  }
597
+ action dispute {
598
+ agent_description: concat("Freeze a funded ", words(instrument), " so neither release nor refund can run until the parties settle the disagreement. No money moves and the whole balance stays put. Only a funded ", words(instrument), " disputes.");
599
+ description: concat("Freezes funded ", words(instrument), ", preserving the entire balance while the parties decide what happens next.");
600
+ summary: concat("Dispute a ", words(instrument), " settlement");
601
+ moves: [];
602
+ steps: [];
603
+ }
604
+ action resume {
605
+ agent_description: concat("Lift the dispute hold and put the ", words(instrument), " back where it was, funded and ready to release. No money moves and the balance was never touched. Only a disputed ", words(instrument), " resumes. Use this when the parties settled without a refund.");
606
+ description: concat("Ends the dispute hold without moving money and returns the fully funded ", words(instrument), " to its first milestone.");
607
+ summary: concat("Resume a ", words(instrument), " settlement");
608
+ moves: [];
609
+ steps: [];
610
+ }
577
611
  }
578
612
  }
@@ -0,0 +1,3 @@
1
+ module std.money_flows
2
+
3
+ // Money flows are imported by their full std.money_flows.<name> path.
@@ -1,4 +1,4 @@
1
- module std.settlements.instant_transfer
1
+ module std.money_flows.instant_transfer
2
2
 
3
3
  export instrument instant_transfer<C>(payer: party, payee: party, amount: money<C>, fees: optional<block>, derived_amount: optional<block>) {
4
4
  let(payer_fee): get(fees, payer);
@@ -32,7 +32,6 @@ export instrument instant_transfer<C>(payer: party, payee: party, amount: money<
32
32
  platformAccountId {
33
33
  type: account<C>;
34
34
  description: "The product fee collection account";
35
- pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$";
36
35
  [fee_collection_key]: true;
37
36
  [reference_filter_key]: { column: role; values: [customer_balance, product_revenue]; };
38
37
  }
@@ -44,7 +43,6 @@ export instrument instant_transfer<C>(payer: party, payee: party, amount: money<
44
43
  platformAccountId {
45
44
  type: account<C>;
46
45
  description: "The product fee collection account";
47
- pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$";
48
46
  [fee_collection_key]: true;
49
47
  [reference_filter_key]: { column: role; values: [customer_balance, product_revenue]; };
50
48
  }
@@ -155,6 +153,7 @@ export instrument instant_transfer<C>(payer: party, payee: party, amount: money<
155
153
  moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.netOfCarvedFeeAmount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: pay_piece_1; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { payPiece1TransferTransferId: transferId; }; }];
156
154
  }
157
155
  }
156
+ sandbox_failure_point: funding;
158
157
  steps: [];
159
158
  }
160
159
  when(fees) {
@@ -1,4 +1,4 @@
1
- module std.settlements.metered
1
+ module std.money_flows.metered
2
2
 
3
3
  export instrument metered(
4
4
  payer: party,
@@ -1,4 +1,4 @@
1
- module std.settlements.pooled_split
1
+ module std.money_flows.pooled_split
2
2
 
3
3
  export instrument pooled_split<C>(payer: party, amount: money<C>, payout_due: date, split: block) {
4
4
  let(recipients): keys_except(split, "remainder_to");
@@ -6,13 +6,13 @@ export instrument pooled_split<C>(payer: party, amount: money<C>, payout_due: da
6
6
  let(remainder_recipient): get(split, "remainder_to");
7
7
  fields {
8
8
  currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
9
- [payer]AccountId { type: account<C>; description: concat("The ", words(payer), " account"); pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
9
+ [payer]AccountId { type: account<C>; description: concat("The ", words(payer), " account"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
10
10
  amount { type: money<C>; description: concat("The pooled period total in {C} minor units; the share fields below partition it exactly"); }
11
11
  payout_due { type: date; description: "The period's payout date; the pool distributes from it"; }
12
12
  for index in recipient_count {
13
13
  let(recipient): at(recipients, index);
14
14
  let(share_bps): get(split, recipient);
15
- [recipient]AccountId { type: account<C>; description: concat("The ", words(recipient), " account"); pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
15
+ [recipient]AccountId { type: account<C>; description: concat("The ", words(recipient), " account"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
16
16
  [recipient]ShareAmount {
17
17
  type: money<C>;
18
18
  description: concat(div_floor(basis_points(share_bps), 100), "% of ", amount, if_eq(recipient, remainder_recipient, " (carries the integer-division remainder): ", ": "), "the ", words(recipient), "'s share. Computed as floor(", amount, " * ", basis_points(share_bps), " / 10000) in {C} minor units");
@@ -1,4 +1,4 @@
1
- module std.settlements.premium_forward
1
+ module std.money_flows.premium_forward
2
2
 
3
3
  export instrument premium_forward<C>(
4
4
  payer: party,
@@ -17,9 +17,8 @@ export instrument premium_forward<C>(
17
17
  let(amount_field): concat(amount);
18
18
  let(net_bps): sub(10000, commission);
19
19
  let(fee_bps): add(commission, 0);
20
- let(net_percent): if_eq(net_bps, 9500, "95%", if_eq(net_bps, 9800, "98%", "87.5%"));
21
- let(fee_percent): if_eq(fee_bps, 500, "5%", if_eq(fee_bps, 200, "2%", "12.5%"));
22
- let(bind_roles): if_eq(len(bind_allowed), 2, ["payer", "beneficiary"], ["beneficiary"]);
20
+ let(net_percent): percent_text(net_bps);
21
+ let(fee_percent): percent_text(fee_bps);
23
22
  let(owner_id_key): "owner.id";
24
23
  let(owner_type_key): "owner.type";
25
24
  let(metadata_instrument_key): "metadata.instrumentId";
@@ -39,7 +38,6 @@ export instrument premium_forward<C>(
39
38
  platform_account_id: {
40
39
  type: account;
41
40
  description: "The product fee collection account";
42
- pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$";
43
41
  [fee_port_key]: true;
44
42
  [reference_filter_key]: { column: role; values: [customer_balance, product_revenue]; };
45
43
  }
@@ -122,6 +120,7 @@ export instrument premium_forward<C>(
122
120
  };
123
121
  capture: { fund_piece_1_transfer_transfer_id: transferId; };
124
122
  }];
123
+ sandbox_failure_point: funding;
125
124
  steps: [];
126
125
  }
127
126
 
@@ -142,6 +141,7 @@ export instrument premium_forward<C>(
142
141
  };
143
142
  capture: { fund_piece_2_transfer_transfer_id: transferId; };
144
143
  }];
144
+ sandbox_failure_point: funding;
145
145
  steps: [];
146
146
  }
147
147
 
@@ -152,16 +152,13 @@ export instrument premium_forward<C>(
152
152
  additional_properties: false;
153
153
  properties: {
154
154
  for item in keys(bind_fields) {
155
- [item]: {
156
- description: "Required decision reference retained in the operation receipt";
157
- max_length: 180; min_length: 1; type: string;
158
- }
155
+ [item]: get(bind_fields, item);
159
156
  }
160
157
  };
161
158
  required: keys(bind_fields);
162
159
  type: object;
163
160
  };
164
- port: { allowed_parties: bind_roles; };
161
+ port: { allowed_parties: bind_allowed; };
165
162
  moves: [{
166
163
  key: transfer; operation: "internal_transfer.create";
167
164
  bind: {
@@ -176,6 +173,7 @@ export instrument premium_forward<C>(
176
173
  };
177
174
  capture: { [bind_capture_key]: transferId; };
178
175
  }];
176
+ sandbox_failure_point: "release";
179
177
  steps: [];
180
178
  }
181
179
 
@@ -230,21 +228,22 @@ export instrument premium_forward<C>(
230
228
  action [endorsement] {
231
229
  agent_description: "Record one endorsement against a policy that already forwarded, from evidence the caller supplies. The forward must be released first, and only the {carrier} may call it. It moves no money and it does not change the premium.";
232
230
  summary: "Record one non-money endorsement from external evidence";
233
- input: {
234
- additional_properties: false;
235
- properties: {
236
- for item in keys(endorsement_fields) {
237
- [item]: {
238
- description: "Required decision reference retained in the operation receipt";
239
- max_length: 180; min_length: 1; type: string;
231
+ when(endorsement_fields) {
232
+ input: {
233
+ additional_properties: false;
234
+ properties: {
235
+ for item in keys(endorsement_fields) {
236
+ [item]: get(endorsement_fields, item);
240
237
  }
241
- }
238
+ };
239
+ required: keys(endorsement_fields);
240
+ type: object;
242
241
  };
243
- required: keys(endorsement_fields);
244
- type: object;
245
- };
246
- capture_input: { endorsement_evidence_reference: evidenceReference; };
247
- port: { allowed_parties: [beneficiary]; };
242
+ capture_input: {
243
+ for item in keys(endorsement_fields) { [item]: item; }
244
+ };
245
+ }
246
+ port { allowed_parties: endorsement_allowed; };
248
247
  steps: [];
249
248
  }
250
249
  action lapse {